Small joys backend: shared daily framework + On This Day engine

- goodnews/daily.py: shared helpers for the daily "small joys" (http_json, date-seeded
  deterministic pick, dedup key) so each joy is a small self-contained module.
- goodnews/onthisday.py: harvest today's MM-DD from Wikimedia's On-this-day feed →
  tone-filter to good/neutral (keyword floor + optional LLM refine) → pool → deterministic
  daily pick (idempotent, respects blocked/featured) → cached row. Network/LLM before any
  DB write. Multi-source ready (source column).
- db.py: onthisday_pool + daily_onthisday tables.
- api.py: GET /api/onthisday/today (edge-cacheable).
- cli.py: cycle step (run after Daily Art; --no-joys to skip), LLM client for tone refine.
- tests/test_onthisday.py: 7 tests (filter+dedup, pick idempotent, blocked/featured,
  never-empty, empty-pool, LLM-narrow). 382 backend tests green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
jay
2026-06-22 16:51:29 -04:00
parent 4739d87f4b
commit a7da8362ab
6 changed files with 351 additions and 2 deletions
+30
View File
@@ -274,6 +274,36 @@ CREATE TABLE IF NOT EXISTS daily_art (
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);
-- "Small joys" daily features. On This Day: a good/neutral thing that happened on
-- today's calendar date, harvested + tone-filtered into a pool, then one picked per day.
CREATE TABLE IF NOT EXISTS onthisday_pool (
id INTEGER PRIMARY KEY AUTOINCREMENT,
source TEXT NOT NULL DEFAULT 'wikimedia', -- multi-source ready (wikimedia | admin | ...)
md TEXT NOT NULL, -- 'MM-DD'
year INTEGER,
ckey TEXT NOT NULL UNIQUE, -- dedup hash so re-harvest never duplicates
text TEXT NOT NULL,
summary TEXT,
image_url TEXT,
page_url TEXT,
shown_at TEXT, -- last date this was the pick (no-soon-repeat)
blocked INTEGER NOT NULL DEFAULT 0, -- admin lever: never pick this
featured INTEGER NOT NULL DEFAULT 0, -- admin lever: prefer this for its date
added_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS daily_onthisday (
feature_date TEXT PRIMARY KEY, -- 'YYYY-MM-DD'
pool_id INTEGER NOT NULL,
source TEXT NOT NULL DEFAULT 'wikimedia',
md TEXT NOT NULL,
year INTEGER,
text TEXT,
summary TEXT,
image_url TEXT,
page_url TEXT,
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);
-- Privacy-respecting, first-party analytics. NO IP / user-agent / referrer / raw
-- URL. visitor_hash is a hash of a random localStorage token (never email/IP).
-- The UNIQUE key dedups to one row per (kind, article, visitor, day) — that both