feat: schema upgrade + import 159 books from reading list
- db.py: add group, isbn, priority, read_reason, read_order, cover_blob fields - Import 159 books from booklist.md with group/priority/status metadata - Include books.db in repo (personal use)
This commit is contained in:
@@ -14,6 +14,12 @@ def _connect():
|
||||
return conn
|
||||
|
||||
|
||||
def _column_exists(conn, table, column):
|
||||
"""检查列是否已存在。"""
|
||||
cols = conn.execute(f"PRAGMA table_info({table})").fetchall()
|
||||
return any(c["name"] == column for c in cols)
|
||||
|
||||
|
||||
def init_db():
|
||||
"""初始化数据库和表结构。"""
|
||||
conn = _connect()
|
||||
@@ -39,6 +45,20 @@ def init_db():
|
||||
updated_at TEXT DEFAULT (datetime('now', 'localtime'))
|
||||
)
|
||||
""")
|
||||
# v2 新增字段 — 兼容已有数据库
|
||||
new_cols = [
|
||||
("\"group\"", "TEXT DEFAULT ''"),
|
||||
("isbn", "TEXT DEFAULT ''"),
|
||||
("priority", "TEXT DEFAULT ''"),
|
||||
("read_reason", "TEXT DEFAULT ''"),
|
||||
("read_order", "INTEGER"),
|
||||
("cover_blob", "BLOB"),
|
||||
("cover_local", "TEXT DEFAULT ''"),
|
||||
]
|
||||
for col_name, col_def in new_cols:
|
||||
plain = col_name.strip('"')
|
||||
if not _column_exists(conn, "books", plain):
|
||||
conn.execute(f"ALTER TABLE books ADD COLUMN {col_name} {col_def}")
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
@@ -46,17 +66,20 @@ def init_db():
|
||||
def add_book(title, *, author=None, translator=None, publisher=None,
|
||||
pub_date=None, cover_url=None, format="paper", status="to-read",
|
||||
rating=None, douban_score=None, goodreads_score=None,
|
||||
tags="", notes=None, start_date=None, finish_date=None):
|
||||
tags="", notes=None, start_date=None, finish_date=None,
|
||||
group="", isbn="", priority="", read_reason="", read_order=None):
|
||||
"""添加一本书,返回新书 ID。"""
|
||||
conn = _connect()
|
||||
cur = conn.execute("""
|
||||
INSERT INTO books (title, author, translator, publisher, pub_date,
|
||||
cover_url, format, status, rating, douban_score,
|
||||
goodreads_score, tags, notes, start_date, finish_date)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
goodreads_score, tags, notes, start_date, finish_date,
|
||||
"group", isbn, priority, read_reason, read_order)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""", (title, author, translator, publisher, pub_date, cover_url,
|
||||
format, status, rating, douban_score, goodreads_score,
|
||||
tags, notes, start_date, finish_date))
|
||||
tags, notes, start_date, finish_date,
|
||||
group, isbn, priority, read_reason, read_order))
|
||||
conn.commit()
|
||||
book_id = cur.lastrowid
|
||||
conn.close()
|
||||
@@ -71,6 +94,8 @@ def update_book(book_id, **kwargs):
|
||||
"title", "author", "translator", "publisher", "pub_date",
|
||||
"cover_url", "format", "status", "rating", "douban_score",
|
||||
"goodreads_score", "tags", "notes", "start_date", "finish_date",
|
||||
"group", "isbn", "priority", "read_reason", "read_order",
|
||||
"cover_blob", "cover_local",
|
||||
}
|
||||
fields = {k: v for k, v in kwargs.items() if k in allowed}
|
||||
if not fields:
|
||||
@@ -100,8 +125,8 @@ def get_book(book_id):
|
||||
return dict(row) if row else None
|
||||
|
||||
|
||||
def list_books(*, status=None, format=None, tag=None):
|
||||
"""查询书目列表,支持按状态/格式/标签过滤。"""
|
||||
def list_books(*, status=None, format=None, tag=None, group=None):
|
||||
"""查询书目列表,支持按状态/格式/标签/分组过滤。"""
|
||||
conn = _connect()
|
||||
query = "SELECT * FROM books WHERE 1=1"
|
||||
params = []
|
||||
@@ -112,9 +137,11 @@ def list_books(*, status=None, format=None, tag=None):
|
||||
query += " AND format = ?"
|
||||
params.append(format)
|
||||
if tag:
|
||||
# 逗号分隔的 tags 字段中模糊匹配
|
||||
query += " AND (',' || tags || ',' LIKE ?)"
|
||||
params.append(f"%,{tag},%")
|
||||
if group:
|
||||
query += ' AND "group" = ?'
|
||||
params.append(group)
|
||||
query += " ORDER BY updated_at DESC"
|
||||
rows = conn.execute(query, params).fetchall()
|
||||
conn.close()
|
||||
@@ -131,11 +158,15 @@ def get_stats():
|
||||
by_format = {}
|
||||
for row in conn.execute("SELECT format, COUNT(*) as cnt FROM books GROUP BY format"):
|
||||
by_format[row["format"]] = row["cnt"]
|
||||
by_group = {}
|
||||
for row in conn.execute('SELECT "group", COUNT(*) as cnt FROM books WHERE "group" != "" GROUP BY "group"'):
|
||||
by_group[row["group"]] = row["cnt"]
|
||||
conn.close()
|
||||
return {
|
||||
"total": total,
|
||||
"by_status": by_status,
|
||||
"by_format": by_format,
|
||||
"by_group": by_group,
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user