feat: standardize skill data management and actions
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -113,15 +113,16 @@ def upsert_column_metadata(
|
||||
searchable: int = 1,
|
||||
editable: int = 1,
|
||||
display_type: str | None = None,
|
||||
options_json: str | None = None,
|
||||
) -> 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, display_type
|
||||
display_order, visible, searchable, editable, display_type, options_json
|
||||
)
|
||||
VALUES (?, ?, ?, ?, NULL, ?, ?, ?, ?)
|
||||
VALUES (?, ?, ?, ?, NULL, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT(table_name, column_name) DO UPDATE SET
|
||||
display_name = excluded.display_name,
|
||||
description = excluded.description,
|
||||
@@ -129,9 +130,20 @@ def upsert_column_metadata(
|
||||
visible = excluded.visible,
|
||||
searchable = excluded.searchable,
|
||||
editable = excluded.editable,
|
||||
display_type = excluded.display_type
|
||||
display_type = excluded.display_type,
|
||||
options_json = excluded.options_json
|
||||
""",
|
||||
(table_name, column_name, display_name, description, visible, searchable, editable, display_type),
|
||||
(
|
||||
table_name,
|
||||
column_name,
|
||||
display_name,
|
||||
description,
|
||||
visible,
|
||||
searchable,
|
||||
editable,
|
||||
display_type,
|
||||
options_json,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -156,9 +168,98 @@ def seed_task_logs_display_metadata(cursor) -> None:
|
||||
searchable=int(column["searchable"]),
|
||||
editable=int(column["editable"]),
|
||||
display_type=str(column["display_type"]) if column.get("display_type") else None,
|
||||
options_json=str(column["options_json"]) if column.get("options_json") else None,
|
||||
)
|
||||
|
||||
|
||||
def seed_column_metadata_batch(
|
||||
cursor,
|
||||
*,
|
||||
table_name: str,
|
||||
columns: tuple[dict[str, object], ...],
|
||||
) -> None:
|
||||
for column in columns:
|
||||
upsert_column_metadata(
|
||||
cursor,
|
||||
table_name=table_name,
|
||||
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.get("visible", 1)),
|
||||
searchable=int(column.get("searchable", 1)),
|
||||
editable=int(column.get("editable", 1)),
|
||||
display_type=str(column["display_type"]) if column.get("display_type") else None,
|
||||
options_json=str(column["options_json"]) if column.get("options_json") else None,
|
||||
)
|
||||
|
||||
|
||||
def prune_stale_column_metadata(cursor, table_name: str, valid_column_names: Iterable[str]) -> None:
|
||||
"""删除表中已不存在物理字段的元数据行(幂等初始化后不得残留旧字段)。"""
|
||||
valid = set(valid_column_names)
|
||||
cursor.execute(
|
||||
"SELECT column_name FROM _jiangchang_columns WHERE table_name = ?",
|
||||
(table_name,),
|
||||
)
|
||||
for (column_name,) in cursor.fetchall():
|
||||
if str(column_name) not in valid:
|
||||
cursor.execute(
|
||||
"DELETE FROM _jiangchang_columns WHERE table_name = ? AND column_name = ?",
|
||||
(table_name, column_name),
|
||||
)
|
||||
|
||||
|
||||
def get_table_readonly(cursor, table_name: str) -> int | None:
|
||||
cursor.execute(
|
||||
"SELECT readonly FROM _jiangchang_tables WHERE table_name = ?",
|
||||
(table_name,),
|
||||
)
|
||||
row = cursor.fetchone()
|
||||
if not row:
|
||||
return None
|
||||
return int(row[0] or 0)
|
||||
|
||||
|
||||
def get_column_editable(cursor, table_name: str, column_name: str) -> int | None:
|
||||
cursor.execute(
|
||||
"""
|
||||
SELECT editable FROM _jiangchang_columns
|
||||
WHERE table_name = ? AND column_name = ?
|
||||
""",
|
||||
(table_name, column_name),
|
||||
)
|
||||
row = cursor.fetchone()
|
||||
if not row:
|
||||
return None
|
||||
return int(row[0] or 0)
|
||||
|
||||
|
||||
def list_editable_business_columns(cursor, table_name: str) -> list[str]:
|
||||
"""返回 editable=1 的业务字段名(不含宿主侧主键/generated 等二次过滤)。"""
|
||||
physical = list_physical_column_names(cursor, table_name)
|
||||
cursor.execute(
|
||||
"""
|
||||
SELECT column_name, editable
|
||||
FROM _jiangchang_columns
|
||||
WHERE table_name = ?
|
||||
""",
|
||||
(table_name,),
|
||||
)
|
||||
editable_set = {
|
||||
str(column_name)
|
||||
for column_name, editable in cursor.fetchall()
|
||||
if editable is not None and int(editable) == 1
|
||||
}
|
||||
return [name for name in physical if name in editable_set]
|
||||
|
||||
|
||||
def table_allows_manual_create(cursor, table_name: str) -> bool:
|
||||
"""表级 readonly=0 且至少有一个 editable=1 字段时,宿主才应提供空白新增表单。"""
|
||||
readonly = get_table_readonly(cursor, table_name)
|
||||
if readonly is None or int(readonly) != 0:
|
||||
return False
|
||||
return bool(list_editable_business_columns(cursor, table_name))
|
||||
|
||||
|
||||
def init_display_metadata(cursor) -> None:
|
||||
init_display_metadata_tables(cursor)
|
||||
seed_task_logs_display_metadata(cursor)
|
||||
|
||||
Reference in New Issue
Block a user