"""Chapter-related queries. Functions take an explicit sqlite3 connection with row_factory=sqlite3.Row and return plain dicts. No Flask imports. """ from datetime import datetime CHAPTER_COLUMNS = ( "id, novel_id, volume, chapter_number, title, outline, content, " "word_count, status, created_at, updated_at" ) ALLOWED_UPDATE_FIELDS = { "title", "outline", "content", "word_count", "status", "volume", "chapter_number", } def _rows(cur): return [dict(row) for row in cur.fetchall()] def _now(): return datetime.now().isoformat(timespec="seconds") def list_chapters(db, novel_id): """All chapters of a novel ordered by volume, chapter_number.""" return _rows( db.execute( f"SELECT {CHAPTER_COLUMNS} FROM chapters WHERE novel_id = ? " "ORDER BY volume, chapter_number", (novel_id,), ) ) def get_chapter(db, chapter_id): """Single chapter row as dict, or None.""" row = db.execute( f"SELECT {CHAPTER_COLUMNS} FROM chapters WHERE id = ?", (chapter_id,) ).fetchone() return dict(row) if row is not None else None def get_chapter_by_number(db, novel_id, volume, chapter_number): """Chapter identified by novel + volume + chapter_number, or None.""" row = db.execute( f"SELECT {CHAPTER_COLUMNS} FROM chapters " "WHERE novel_id = ? AND volume = ? AND chapter_number = ?", (novel_id, volume, chapter_number), ).fetchone() return dict(row) if row is not None else None def create_chapter(db, novel_id, volume, chapter_number, title=None, outline=None, status="pending"): """Insert a new chapter and return its id.""" now = _now() cur = db.execute( "INSERT INTO chapters (novel_id, volume, chapter_number, title, outline, status, created_at, updated_at) " "VALUES (?, ?, ?, ?, ?, ?, ?, ?)", (novel_id, volume, chapter_number, title, outline, status, now, now), ) db.commit() return cur.lastrowid def update_chapter(db, chapter_id, **fields): """Update allowed chapter 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 chapters SET {assignments} WHERE id = ?", (*updates.values(), chapter_id), ) db.commit() def get_previous_chapters(db, novel_id, volume, chapter_number, limit=2): """Chapters strictly before (volume, chapter_number), newest first, with content.""" return _rows( db.execute( f"SELECT {CHAPTER_COLUMNS} FROM chapters " "WHERE novel_id = ? AND (volume < ? OR (volume = ? AND chapter_number < ?)) " "ORDER BY volume DESC, chapter_number DESC LIMIT ?", (novel_id, volume, volume, chapter_number, limit), ) ) def list_done_chapters(db, novel_id, limit=20): """Done chapters, newest first.""" return _rows( db.execute( f"SELECT {CHAPTER_COLUMNS} FROM chapters " "WHERE novel_id = ? AND status = 'done' " "ORDER BY volume DESC, chapter_number DESC LIMIT ?", (novel_id, limit), ) )