feat: 结题报告初稿助手首版 v1.0.1(draft-closing-report-default)
All checks were successful
技能自动化发布 / release (push) Successful in 6s
All checks were successful
技能自动化发布 / release (push) Successful in 6s
This commit is contained in:
262
scripts/db/connection.py
Normal file
262
scripts/db/connection.py
Normal file
@@ -0,0 +1,262 @@
|
||||
"""SQLite 连接与幂等初始化(结题报告初稿助手)。
|
||||
|
||||
业务表 4 张:
|
||||
- closing_projects(结题项目,含预期指标,可编辑)
|
||||
- achievements(成果清单,可编辑)
|
||||
- expense_items(决算明细,可编辑)
|
||||
- report_drafts(初稿记录,只读,生成自动写入)
|
||||
|
||||
表间禁止 SQLite 外键,仅存业务 id(见 references/SCHEMA.md)。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sqlite3
|
||||
|
||||
from db.display_metadata import (
|
||||
init_display_metadata,
|
||||
seed_column_metadata_batch,
|
||||
upsert_table_metadata,
|
||||
)
|
||||
from db.timestamp_columns import init_updated_at_trigger
|
||||
from util.runtime_paths import get_db_path
|
||||
|
||||
|
||||
def get_conn() -> sqlite3.Connection:
|
||||
return sqlite3.connect(get_db_path())
|
||||
|
||||
|
||||
_CLOSING_PROJECTS_DDL = """
|
||||
CREATE TABLE IF NOT EXISTS closing_projects (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
project_no TEXT NOT NULL UNIQUE,
|
||||
project_name TEXT,
|
||||
pi_name TEXT,
|
||||
department TEXT,
|
||||
program TEXT,
|
||||
start_date TEXT,
|
||||
end_date TEXT,
|
||||
expected_json TEXT,
|
||||
annual_report_excerpt TEXT,
|
||||
status TEXT NOT NULL DEFAULT '待结题',
|
||||
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
||||
updated_at INTEGER NOT NULL DEFAULT (unixepoch())
|
||||
)
|
||||
"""
|
||||
|
||||
_ACHIEVEMENTS_DDL = """
|
||||
CREATE TABLE IF NOT EXISTS achievements (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
project_no TEXT NOT NULL,
|
||||
achievement_type TEXT NOT NULL,
|
||||
title TEXT,
|
||||
venue_or_no TEXT,
|
||||
ach_date TEXT,
|
||||
is_representative INTEGER NOT NULL DEFAULT 0,
|
||||
note TEXT,
|
||||
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
||||
updated_at INTEGER NOT NULL DEFAULT (unixepoch())
|
||||
)
|
||||
"""
|
||||
|
||||
_EXPENSE_ITEMS_DDL = """
|
||||
CREATE TABLE IF NOT EXISTS expense_items (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
project_no TEXT NOT NULL,
|
||||
category TEXT NOT NULL,
|
||||
amount REAL NOT NULL,
|
||||
occurred_date TEXT,
|
||||
description TEXT,
|
||||
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
||||
updated_at INTEGER NOT NULL DEFAULT (unixepoch())
|
||||
)
|
||||
"""
|
||||
|
||||
_REPORT_DRAFTS_DDL = """
|
||||
CREATE TABLE IF NOT EXISTS report_drafts (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
batch_no TEXT NOT NULL,
|
||||
project_no TEXT,
|
||||
project_name TEXT,
|
||||
completion_json TEXT,
|
||||
narrative_points_json TEXT,
|
||||
draft_file TEXT,
|
||||
preview_file TEXT,
|
||||
created_at INTEGER NOT NULL DEFAULT (unixepoch())
|
||||
)
|
||||
"""
|
||||
|
||||
_REPORT_DRAFTS_INDEX = """
|
||||
CREATE INDEX IF NOT EXISTS idx_report_drafts_batch
|
||||
ON report_drafts(batch_no)
|
||||
"""
|
||||
|
||||
_TS_COLUMN_NAMES = {"created_at", "updated_at"}
|
||||
|
||||
|
||||
def _col(column_name: str, display_name: str, description: str, **extra: object) -> dict[str, object]:
|
||||
base: dict[str, object] = {
|
||||
"column_name": column_name,
|
||||
"display_name": display_name,
|
||||
"description": description,
|
||||
"visible": 1,
|
||||
"searchable": 0,
|
||||
"editable": 0,
|
||||
}
|
||||
base.update(extra)
|
||||
if column_name in _TS_COLUMN_NAMES:
|
||||
base["display_type"] = "datetime_unix_seconds"
|
||||
return base
|
||||
|
||||
|
||||
_CLOSING_PROJECTS_COLUMNS: tuple[dict[str, object], ...] = (
|
||||
_col("id", "编号", "主键", searchable=1),
|
||||
_col("project_no", "项目编号", "项目唯一编号。", searchable=1),
|
||||
_col("project_name", "项目名称", "项目全称。", searchable=1, editable=1),
|
||||
_col("pi_name", "负责人", "项目负责人姓名。", searchable=1, editable=1),
|
||||
_col("department", "所属学院", "二级学院/单位。", searchable=1, editable=1),
|
||||
_col("program", "项目类型", "如 国家自然科学基金(青年/面上)。", searchable=1, editable=1),
|
||||
_col("start_date", "执行期开始", "YYYY-MM-DD。", searchable=1, editable=1),
|
||||
_col("end_date", "执行期结束", "YYYY-MM-DD。", searchable=1, editable=1),
|
||||
_col("expected_json", "预期成果指标", "如 [{\"type\":\"论文\",\"target\":3,\"unit\":\"篇\"}]。", visible=0, display_type="textarea", editable=1),
|
||||
_col("annual_report_excerpt", "年度报告摘句", "年度/中期报告关键句(分号分隔),供叙事素材。", visible=0, display_type="textarea", editable=1),
|
||||
_col("status", "状态", "待结题 / 初稿已生成 / 已提交。", searchable=1, editable=1, options_json='[{"value":"待结题","label":"待结题"},{"value":"初稿已生成","label":"初稿已生成"},{"value":"已提交","label":"已提交"}]'),
|
||||
_col("created_at", "创建时间", "记录创建时间(Unix 秒级,自动生成)。"),
|
||||
_col("updated_at", "更新时间", "记录最后更新时间(Unix 秒级,自动维护)。"),
|
||||
)
|
||||
|
||||
_ACHIEVEMENTS_COLUMNS: tuple[dict[str, object], ...] = (
|
||||
_col("id", "编号", "主键", searchable=1),
|
||||
_col("project_no", "项目编号", "所属项目编号。", searchable=1),
|
||||
_col("achievement_type", "成果类型", "论文 / 专利 / 人才培养 / 其他。", searchable=1, editable=1, options_json='[{"value":"论文","label":"论文"},{"value":"专利","label":"专利"},{"value":"人才培养","label":"人才培养"},{"value":"其他","label":"其他"}]'),
|
||||
_col("title", "成果名称", "论文标题/专利名/成果描述。", searchable=1, editable=1),
|
||||
_col("venue_or_no", "期刊/专利号", "发表期刊或专利号。", editable=1),
|
||||
_col("ach_date", "日期", "发表/授权日期(YYYY-MM-DD)。", editable=1),
|
||||
_col("is_representative", "代表性成果", "1=代表本项目的标志性成果。", searchable=1, editable=1),
|
||||
_col("note", "备注", "说明。", editable=1),
|
||||
_col("created_at", "创建时间", "记录创建时间(Unix 秒级,自动生成)。"),
|
||||
_col("updated_at", "更新时间", "记录最后更新时间(Unix 秒级,自动维护)。"),
|
||||
)
|
||||
|
||||
_EXPENSE_ITEMS_COLUMNS: tuple[dict[str, object], ...] = (
|
||||
_col("id", "编号", "主键", searchable=1),
|
||||
_col("project_no", "项目编号", "所属项目编号。", searchable=1),
|
||||
_col("category", "科目", "决算科目,如 差旅费、设备费。", searchable=1, editable=1),
|
||||
_col("amount", "金额", "支出金额(元)。", searchable=1, editable=1, display_type="number"),
|
||||
_col("occurred_date", "发生日期", "YYYY-MM-DD。", editable=1),
|
||||
_col("description", "原始摘要", "流水描述,供语义化转换参考。", visible=0, display_type="textarea", editable=1),
|
||||
_col("created_at", "创建时间", "记录创建时间(Unix 秒级,自动生成)。"),
|
||||
_col("updated_at", "更新时间", "记录最后更新时间(Unix 秒级,自动维护)。"),
|
||||
)
|
||||
|
||||
_REPORT_DRAFTS_COLUMNS: tuple[dict[str, object], ...] = (
|
||||
_col("id", "记录编号", "主键,初稿记录编号。", searchable=1),
|
||||
_col("batch_no", "生成批次号", "同一次生成请求的批次号。", searchable=1),
|
||||
_col("project_no", "项目编号", "初稿对应项目。", searchable=1),
|
||||
_col("project_name", "项目名称", "初稿对应项目名。", searchable=1),
|
||||
_col("completion_json", "指标对比", "预期 vs 实际对比(JSON)。", visible=0, display_type="textarea"),
|
||||
_col("narrative_points_json", "叙事素材要点", "从成果与年度报告提取的要点(JSON)。", visible=0, display_type="textarea"),
|
||||
_col("draft_file", "Word 初稿", "生成的 docx 文件路径。", visible=0),
|
||||
_col("preview_file", "预览文件", "HTML 预览文件路径。", visible=0),
|
||||
_col("created_at", "创建时间", "记录创建时间(Unix 秒级,自动生成)。"),
|
||||
)
|
||||
|
||||
|
||||
def _init_closing_projects_metadata(cursor) -> None:
|
||||
cursor.execute(_CLOSING_PROJECTS_DDL)
|
||||
init_updated_at_trigger(cursor, "closing_projects")
|
||||
upsert_table_metadata(
|
||||
cursor,
|
||||
table_name="closing_projects",
|
||||
display_name="结题项目",
|
||||
description="待结题项目与预期成果指标;初稿生成的数据源。",
|
||||
grid_help="预期指标为 JSON 数组(type/target/unit);顶栏按钮可录入,行内可编辑。",
|
||||
visible=1,
|
||||
readonly=0,
|
||||
default_sort_json='[{"column":"id","direction":"desc"}]',
|
||||
default_visible_columns_json='["project_no","project_name","pi_name","program","end_date","status"]',
|
||||
)
|
||||
seed_column_metadata_batch(
|
||||
cursor,
|
||||
table_name="closing_projects",
|
||||
columns=_CLOSING_PROJECTS_COLUMNS,
|
||||
valid_column_names=[str(c["column_name"]) for c in _CLOSING_PROJECTS_COLUMNS],
|
||||
)
|
||||
|
||||
|
||||
def _init_achievements_metadata(cursor) -> None:
|
||||
cursor.execute(_ACHIEVEMENTS_DDL)
|
||||
init_updated_at_trigger(cursor, "achievements")
|
||||
upsert_table_metadata(
|
||||
cursor,
|
||||
table_name="achievements",
|
||||
display_name="成果清单",
|
||||
description="实际取得的论文/专利/人才培养等成果;完成情况章节的数据源。",
|
||||
grid_help="按项目编号关联;代表性成果会优先进入初稿素材。",
|
||||
visible=1,
|
||||
readonly=0,
|
||||
)
|
||||
seed_column_metadata_batch(
|
||||
cursor,
|
||||
table_name="achievements",
|
||||
columns=_ACHIEVEMENTS_COLUMNS,
|
||||
valid_column_names=[str(c["column_name"]) for c in _ACHIEVEMENTS_COLUMNS],
|
||||
)
|
||||
|
||||
|
||||
def _init_expense_items_metadata(cursor) -> None:
|
||||
cursor.execute(_EXPENSE_ITEMS_DDL)
|
||||
init_updated_at_trigger(cursor, "expense_items")
|
||||
upsert_table_metadata(
|
||||
cursor,
|
||||
table_name="expense_items",
|
||||
display_name="决算明细",
|
||||
description="经费流水明细;决算章节的数据源。",
|
||||
grid_help="金额单位元;按科目汇总后自动生成规范描述句。",
|
||||
visible=1,
|
||||
readonly=0,
|
||||
)
|
||||
seed_column_metadata_batch(
|
||||
cursor,
|
||||
table_name="expense_items",
|
||||
columns=_EXPENSE_ITEMS_COLUMNS,
|
||||
valid_column_names=[str(c["column_name"]) for c in _EXPENSE_ITEMS_COLUMNS],
|
||||
)
|
||||
|
||||
|
||||
def _init_report_drafts_metadata(cursor) -> None:
|
||||
cursor.execute(_REPORT_DRAFTS_DDL)
|
||||
cursor.execute(_REPORT_DRAFTS_INDEX)
|
||||
upsert_table_metadata(
|
||||
cursor,
|
||||
table_name="report_drafts",
|
||||
display_name="初稿记录",
|
||||
description="每次生成的初稿记录:指标对比、素材要点与文件路径。",
|
||||
grid_help="Word 文件在 output 目录可下载;HTML 预览在侧栏查看。",
|
||||
visible=1,
|
||||
readonly=1,
|
||||
default_sort_json='[{"column":"id","direction":"desc"}]',
|
||||
default_visible_columns_json='["id","project_no","project_name","created_at"]',
|
||||
)
|
||||
seed_column_metadata_batch(
|
||||
cursor,
|
||||
table_name="report_drafts",
|
||||
columns=_REPORT_DRAFTS_COLUMNS,
|
||||
valid_column_names=[str(c["column_name"]) for c in _REPORT_DRAFTS_COLUMNS],
|
||||
)
|
||||
|
||||
|
||||
def init_db() -> dict[str, int]:
|
||||
"""幂等创建/迁移本地 SQLite 与数据管理元数据。"""
|
||||
conn = get_conn()
|
||||
try:
|
||||
cur = conn.cursor()
|
||||
init_display_metadata(cur)
|
||||
_init_closing_projects_metadata(cur)
|
||||
_init_achievements_metadata(cur)
|
||||
_init_expense_items_metadata(cur)
|
||||
_init_report_drafts_metadata(cur)
|
||||
conn.commit()
|
||||
return {}
|
||||
finally:
|
||||
conn.close()
|
||||
Reference in New Issue
Block a user