"""Foreshadowing-related queries. Functions take an explicit sqlite3 connection with row_factory=sqlite3.Row and return plain dicts. No Flask imports. """ from datetime import datetime FORESHADOWING_COLUMNS = ( "id, novel_id, description, planted_chapter, resolved_chapter, " "status, created_at, updated_at" ) ALLOWED_UPDATE_FIELDS = { "description", "planted_chapter", "resolved_chapter", "status", } def _rows(cur): return [dict(row) for row in cur.fetchall()] def _now(): return datetime.now().isoformat(timespec="seconds") def list_foreshadowing(db, novel_id): """All foreshadowing entries of a novel ordered by id.""" return _rows( db.execute( f"SELECT {FORESHADOWING_COLUMNS} FROM foreshadowing WHERE novel_id = ? ORDER BY id", (novel_id,), ) ) def create_foreshadowing(db, novel_id, description, planted_chapter=None, resolved_chapter=None, status="pending"): """Insert a new foreshadowing entry and return its id.""" now = _now() cur = db.execute( "INSERT INTO foreshadowing (novel_id, description, planted_chapter, resolved_chapter, status, created_at, updated_at) " "VALUES (?, ?, ?, ?, ?, ?, ?)", (novel_id, description, planted_chapter, resolved_chapter, status, now, now), ) db.commit() return cur.lastrowid def update_foreshadowing(db, fs_id, **fields): """Update allowed foreshadowing fields.""" updates = {k: v for k, v in fields.items() if k in ALLOWED_UPDATE_FIELDS} if not updates: return updates["updated_at"] = _now() assignments = ", ".join(f"{key} = ?" for key in updates) db.execute( f"UPDATE foreshadowing SET {assignments} WHERE id = ?", (*updates.values(), fs_id), ) db.commit() def list_open_foreshadowing(db, novel_id): """Open (pending/planted) foreshadowing entries ordered by id.""" return _rows( db.execute( f"SELECT {FORESHADOWING_COLUMNS} FROM foreshadowing " "WHERE novel_id = ? AND status IN ('pending', 'planted') ORDER BY id", (novel_id,), ) )