165 lines
6.5 KiB
Python
165 lines
6.5 KiB
Python
"""匠厂数据管理展示元数据(_jiangchang_tables / _jiangchang_columns)。
|
||
|
||
SQLite 无原生表/字段 COMMENT;中文展示名称通过这两张元数据表提供给宿主「数据管理」页面。
|
||
字段在界面上的顺序由 CREATE TABLE / PRAGMA table_info(cid) 决定,**不**使用 display_order 排序。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from typing import Iterable
|
||
|
||
# 宿主会隐藏这些表,不作为业务表展示(见 jiangchang electron/data-management/types.ts)
|
||
INTERNAL_METADATA_TABLES = frozenset({"_jiangchang_tables", "_jiangchang_columns"})
|
||
LEGACY_METADATA_TABLES = frozenset({"_schema_meta"})
|
||
METADATA_TABLES = INTERNAL_METADATA_TABLES | LEGACY_METADATA_TABLES
|
||
|
||
TASK_LOGS_TABLE = "task_logs"
|
||
|
||
_JIANGCHANG_TABLES_DDL = """
|
||
CREATE TABLE IF NOT EXISTS _jiangchang_tables (
|
||
table_name TEXT PRIMARY KEY,
|
||
display_name TEXT NOT NULL,
|
||
description TEXT,
|
||
sort_order INTEGER,
|
||
visible INTEGER NOT NULL DEFAULT 1,
|
||
readonly INTEGER NOT NULL DEFAULT 0
|
||
)
|
||
"""
|
||
|
||
_JIANGCHANG_COLUMNS_DDL = """
|
||
CREATE TABLE IF NOT EXISTS _jiangchang_columns (
|
||
table_name TEXT NOT NULL,
|
||
column_name TEXT NOT NULL,
|
||
display_name TEXT NOT NULL,
|
||
description TEXT,
|
||
display_order INTEGER,
|
||
visible INTEGER NOT NULL DEFAULT 1,
|
||
searchable INTEGER NOT NULL DEFAULT 1,
|
||
editable INTEGER NOT NULL DEFAULT 1,
|
||
display_type TEXT,
|
||
options_json TEXT,
|
||
PRIMARY KEY (table_name, column_name)
|
||
)
|
||
"""
|
||
|
||
TASK_LOGS_TABLE_METADATA = {
|
||
"table_name": TASK_LOGS_TABLE,
|
||
"display_name": "任务日志",
|
||
"description": "记录技能任务的执行状态和结果",
|
||
"visible": 1,
|
||
"readonly": 0,
|
||
}
|
||
|
||
TASK_LOGS_COLUMN_METADATA: tuple[dict[str, object], ...] = (
|
||
{"column_name": "id", "display_name": "编号", "description": "任务日志唯一编号", "visible": 1, "searchable": 1, "editable": 0},
|
||
{"column_name": "task_type", "display_name": "任务类型", "description": "任务的业务类型", "visible": 1, "searchable": 1, "editable": 1},
|
||
{"column_name": "target_id", "display_name": "目标编号", "description": "任务目标对象编号", "visible": 1, "searchable": 1, "editable": 1},
|
||
{"column_name": "input_id", "display_name": "输入编号", "description": "输入对象编号", "visible": 1, "searchable": 1, "editable": 1},
|
||
{"column_name": "input_title", "display_name": "输入标题", "description": "输入对象标题", "visible": 1, "searchable": 1, "editable": 1},
|
||
{"column_name": "status", "display_name": "状态", "description": "任务当前状态", "visible": 1, "searchable": 1, "editable": 1},
|
||
{"column_name": "error_msg", "display_name": "错误信息", "description": "任务失败时的错误说明", "visible": 1, "searchable": 1, "editable": 1},
|
||
{"column_name": "result_summary", "display_name": "结果摘要", "description": "任务执行结果摘要", "visible": 1, "searchable": 1, "editable": 1},
|
||
{"column_name": "created_at", "display_name": "创建时间", "description": "记录创建时间", "visible": 1, "searchable": 1, "editable": 0},
|
||
{"column_name": "updated_at", "display_name": "更新时间", "description": "记录最后更新时间", "visible": 1, "searchable": 1, "editable": 0},
|
||
)
|
||
|
||
|
||
def init_display_metadata_tables(cursor) -> None:
|
||
cursor.execute(_JIANGCHANG_TABLES_DDL)
|
||
cursor.execute(_JIANGCHANG_COLUMNS_DDL)
|
||
|
||
|
||
def upsert_table_metadata(cursor, *, table_name: str, display_name: str, description: str | None = None, visible: int = 1, readonly: int = 0) -> None:
|
||
cursor.execute(
|
||
"""
|
||
INSERT INTO _jiangchang_tables (table_name, display_name, description, visible, readonly)
|
||
VALUES (?, ?, ?, ?, ?)
|
||
ON CONFLICT(table_name) DO UPDATE SET
|
||
display_name = excluded.display_name,
|
||
description = excluded.description,
|
||
visible = excluded.visible,
|
||
readonly = excluded.readonly
|
||
""",
|
||
(table_name, display_name, description, visible, readonly),
|
||
)
|
||
|
||
|
||
def upsert_column_metadata(
|
||
cursor,
|
||
*,
|
||
table_name: str,
|
||
column_name: str,
|
||
display_name: str,
|
||
description: str | None = None,
|
||
visible: int = 1,
|
||
searchable: int = 1,
|
||
editable: int = 1,
|
||
) -> None:
|
||
# display_order 保留为 NULL:宿主按 PRAGMA table_info cid 展示字段,不读 display_order。
|
||
cursor.execute(
|
||
"""
|
||
INSERT INTO _jiangchang_columns (
|
||
table_name, column_name, display_name, description,
|
||
display_order, visible, searchable, editable
|
||
)
|
||
VALUES (?, ?, ?, ?, NULL, ?, ?, ?)
|
||
ON CONFLICT(table_name, column_name) DO UPDATE SET
|
||
display_name = excluded.display_name,
|
||
description = excluded.description,
|
||
display_order = NULL,
|
||
visible = excluded.visible,
|
||
searchable = excluded.searchable,
|
||
editable = excluded.editable
|
||
""",
|
||
(table_name, column_name, display_name, description, visible, searchable, editable),
|
||
)
|
||
|
||
|
||
def seed_task_logs_display_metadata(cursor) -> None:
|
||
meta = TASK_LOGS_TABLE_METADATA
|
||
upsert_table_metadata(
|
||
cursor,
|
||
table_name=str(meta["table_name"]),
|
||
display_name=str(meta["display_name"]),
|
||
description=str(meta["description"]) if meta.get("description") else None,
|
||
visible=int(meta["visible"]),
|
||
readonly=int(meta["readonly"]),
|
||
)
|
||
for column in TASK_LOGS_COLUMN_METADATA:
|
||
upsert_column_metadata(
|
||
cursor,
|
||
table_name=TASK_LOGS_TABLE,
|
||
column_name=str(column["column_name"]),
|
||
display_name=str(column["display_name"]),
|
||
description=str(column["description"]) if column.get("description") else None,
|
||
visible=int(column["visible"]),
|
||
searchable=int(column["searchable"]),
|
||
editable=int(column["editable"]),
|
||
)
|
||
|
||
|
||
def init_display_metadata(cursor) -> None:
|
||
init_display_metadata_tables(cursor)
|
||
seed_task_logs_display_metadata(cursor)
|
||
|
||
|
||
def iter_user_tables(cursor) -> Iterable[str]:
|
||
cursor.execute(
|
||
"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name"
|
||
)
|
||
for (name,) in cursor.fetchall():
|
||
if name in METADATA_TABLES:
|
||
continue
|
||
yield str(name)
|
||
|
||
|
||
def _quote_identifier(name: str) -> str:
|
||
return '"' + name.replace('"', '""') + '"'
|
||
|
||
|
||
def list_physical_column_names(cursor, table_name: str) -> list[str]:
|
||
cursor.execute(f"PRAGMA table_info({_quote_identifier(table_name)})")
|
||
rows = cursor.fetchall()
|
||
rows.sort(key=lambda row: int(row[0]))
|
||
return [str(row[1]) for row in rows]
|