From 94c07ab1560aa39517565dc25c8e803899053e1f Mon Sep 17 00:00:00 2001 From: Kayos Date: Thu, 30 Apr 2026 12:02:58 -0700 Subject: [PATCH] Step 4 (partial): drop dead pick_points table + game-system DB methods MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Migration 020 drops cauldron_pick_points (game system stripped from /plan 2026-04-30 — all award_pick_points calls were already removed from server.py). The DB methods household_scoreboard, household_streak, and award_pick_points are deleted now too — they were unused dead code since the strip. delete_plan_slots no longer DELETEs from the dropped table (kept it minimal: just wipe the meal_plan_slots). DEFERRED from Step 4: dropping cauldron_foods. The 229 Sonnet-curated density rows + 2462 USDA seed rows are still useful raw material for a fuzzy backfill into cauldron_food_metadata (only 128 of Mealie's 2895 foods got densities matched on the first exact-name pass). Until we run a fuzzy backfill, holding onto cauldron_foods as a cold archive is cheaper than losing the data. Will revisit once the metadata catalog is more complete. Recovery from 020: revert this migration, restore the CREATE TABLE position-014 entry from db.py history. The points data was never meaningfully populated (jobs 1+3 award attempts were folded into the strip), so loss is essentially zero. --- cauldron/db.py | 126 +++++-------------------------------------------- 1 file changed, 12 insertions(+), 114 deletions(-) diff --git a/cauldron/db.py b/cauldron/db.py index a4f9574..f95e70f 100644 --- a/cauldron/db.py +++ b/cauldron/db.py @@ -346,6 +346,15 @@ MIGRATIONS = [ FOREIGN KEY (job_id) REFERENCES cauldron_consolidate_jobs(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 """, + # 020 — Drop the game-system pick-points table. Stripped from /plan + # 2026-04-30; tables and methods kept around as dead code for a few + # days to let it bake. Now that the simpler base flow is proven, + # the ledger comes out. Recovery path: revert this migration's number + # and re-add the table from db.py history; the points data was never + # widely populated anyway. + """ + DROP TABLE IF EXISTS cauldron_pick_points + """, ] @@ -569,94 +578,7 @@ class DB: ) return cur.rowcount - def household_scoreboard(self, household_id: int) -> list[dict]: - """Per-user lock counts + pick-points + most recent lock time. - - Three numbers per row: - wins — user-locked weeks (excludes auto-locks) - weeks_locked — alias for wins, preserved for older callers - points — sum of cauldron_pick_points for this user/household - - Sort: points desc, then wins desc, then last_win desc — points are - the headline metric in v0.3 (every pick lands → matters daily). - - We compute lock counts and points as separate scalar subqueries so - the JOIN doesn't blow up on the cartesian (members × plans × points). - """ - with self.conn() as c, c.cursor() as cur: - cur.execute( - """ - SELECT - u.authentik_sub AS sub, - u.email AS email, - u.display_name AS display_name, - COALESCE(( - SELECT COUNT(*) FROM cauldron_meal_plans p - WHERE p.locked_by_sub = m.authentik_sub - AND p.household_id = m.household_id - AND p.locked_reason = 'user' - ), 0) AS wins, - ( - SELECT MAX(p.locked_at) FROM cauldron_meal_plans p - WHERE p.locked_by_sub = m.authentik_sub - AND p.household_id = m.household_id - AND p.locked_reason = 'user' - ) AS last_win, - COALESCE(( - SELECT SUM(pp.points) FROM cauldron_pick_points pp - WHERE pp.household_id = m.household_id - AND pp.authentik_sub = m.authentik_sub - ), 0) AS points - FROM cauldron_household_members m - LEFT JOIN cauldron_users u - ON u.authentik_sub = m.authentik_sub - WHERE m.household_id = %s - ORDER BY points DESC, wins DESC, last_win DESC - """, - (household_id,), - ) - out = [] - for r in cur.fetchall(): - d = dict(r) - d["points"] = int(d.get("points") or 0) - d["wins"] = int(d.get("wins") or 0) - d["weeks_locked"] = d["wins"] - out.append(d) - return out - - def household_streak(self, household_id: int) -> dict | None: - """Compute current win streak: walk back from most recent locked week, - counting consecutive weeks won by the same user. Returns - {sub, display_name, count} or None if no locks.""" - with self.conn() as c, c.cursor() as cur: - cur.execute( - """ - SELECT p.week_start, p.locked_by_sub, u.display_name, u.email - FROM cauldron_meal_plans p - LEFT JOIN cauldron_users u ON u.authentik_sub = p.locked_by_sub - WHERE p.household_id = %s - AND p.locked_at IS NOT NULL - AND p.locked_reason = 'user' - ORDER BY p.week_start DESC - """, - (household_id,), - ) - rows = cur.fetchall() - if not rows: - return None - leader = rows[0]["locked_by_sub"] - count = 0 - for r in rows: - if r["locked_by_sub"] != leader: - break - count += 1 - return { - "sub": leader, - "display_name": rows[0]["display_name"] or rows[0]["email"], - "count": count, - } - - # --- plan slots + pick points (v0.3 A4) -------------------------------- + # --- plan slots (v0.3 A4) ---------------------------------------------- # Day order is stable Mon..Sun. Used everywhere we need to render slots # in calendar order. @@ -728,13 +650,10 @@ class DB: return inserted def delete_plan_slots(self, plan_id: int) -> int: - """Wipe slots for a plan (used by re-roll). Also nukes the matching - pick_points so the ledger doesn't double-count on regenerate.""" + """Wipe slots for a plan (used by re-roll).""" with self.conn() as c, c.cursor() as cur: cur.execute("DELETE FROM cauldron_meal_plan_slots WHERE plan_id=%s", (plan_id,)) - slots_removed = cur.rowcount - cur.execute("DELETE FROM cauldron_pick_points WHERE plan_id=%s", (plan_id,)) - return slots_removed + return cur.rowcount def mark_plan_generated(self, plan_id: int, sub: str) -> dict: """Set generated_by_sub + generated_at IF not already set. Returns @@ -766,27 +685,6 @@ class DB: (plan_id,), ) - def award_pick_points( - self, - household_id: int, - plan_id: int, - sub: str, - points: int, - reason: str = "pick_used", - ) -> int: - """Insert one ledger row. Returns the new row id. Reason must be one - of the ENUM values; we don't validate here — DB will reject bad ones.""" - with self.conn() as c, c.cursor() as cur: - cur.execute( - """ - INSERT INTO cauldron_pick_points - (household_id, plan_id, authentik_sub, points, reason) - VALUES (%s, %s, %s, %s, %s) - """, - (household_id, plan_id, sub, int(points), reason), - ) - return cur.lastrowid - def enrich_plan_with_slots(self, plan: dict) -> dict: """In-place: add `slots` key to a plan dict. Returns the same dict for chaining. Empty list if there are no slots yet."""