initial: novel-app snapshot

This commit is contained in:
omo
2026-08-17 17:11:30 +08:00
commit 1ae06209ac
34 changed files with 2821 additions and 0 deletions

View File

@@ -0,0 +1,83 @@
"""Character-related queries.
Functions take an explicit sqlite3 connection with row_factory=sqlite3.Row
and return plain dicts. No Flask imports.
"""
from datetime import datetime
CHARACTER_COLUMNS = (
"id, novel_id, name, role, description, first_appearance_chapter, "
"status, created_at, updated_at"
)
ALLOWED_UPDATE_FIELDS = {
"name",
"role",
"description",
"first_appearance_chapter",
"status",
}
def _rows(cur):
return [dict(row) for row in cur.fetchall()]
def _now():
return datetime.now().isoformat(timespec="seconds")
def list_characters(db, novel_id):
"""All characters of a novel ordered by id."""
return _rows(
db.execute(
f"SELECT {CHARACTER_COLUMNS} FROM characters WHERE novel_id = ? ORDER BY id",
(novel_id,),
)
)
def get_character(db, character_id):
"""Single character row as dict, or None."""
row = db.execute(
f"SELECT {CHARACTER_COLUMNS} FROM characters WHERE id = ?", (character_id,)
).fetchone()
return dict(row) if row is not None else None
def create_character(db, novel_id, name, role=None, description=None, first_appearance_chapter=None):
"""Insert a new character and return its id."""
now = _now()
cur = db.execute(
"INSERT INTO characters (novel_id, name, role, description, first_appearance_chapter, status, created_at, updated_at) "
"VALUES (?, ?, ?, ?, ?, 'alive', ?, ?)",
(novel_id, name, role, description, first_appearance_chapter, now, now),
)
db.commit()
return cur.lastrowid
def update_character(db, character_id, **fields):
"""Update allowed character 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 characters SET {assignments} WHERE id = ?",
(*updates.values(), character_id),
)
db.commit()
def find_characters_in_text(db, novel_id, text):
"""Characters whose name appears as a substring of text."""
characters = _rows(
db.execute(
f"SELECT {CHARACTER_COLUMNS} FROM characters WHERE novel_id = ?",
(novel_id,),
)
)
return [c for c in characters if c["name"] and c["name"] in text]