109 lines
3.2 KiB
Python
109 lines
3.2 KiB
Python
"""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),
|
|
)
|
|
) |