170 lines
6.2 KiB
Python
170 lines
6.2 KiB
Python
"""SQLite 连接与幂等初始化(职称评审核验助手)。
|
||
|
||
业务表 2 张:
|
||
- threshold_rules(评审门槛库,可编辑)
|
||
- verify_cases(核验案例,只读,核验自动写入)
|
||
|
||
表间禁止 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.seed_data import seed_all
|
||
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())
|
||
|
||
|
||
_THRESHOLD_RULES_DDL = """
|
||
CREATE TABLE IF NOT EXISTS threshold_rules (
|
||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||
rank TEXT NOT NULL,
|
||
metric_key TEXT NOT NULL,
|
||
metric_name TEXT NOT NULL,
|
||
min_value REAL NOT NULL,
|
||
unit TEXT,
|
||
basis_doc TEXT,
|
||
clause TEXT,
|
||
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
||
updated_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
||
UNIQUE (rank, metric_key)
|
||
)
|
||
"""
|
||
|
||
_VERIFY_CASES_DDL = """
|
||
CREATE TABLE IF NOT EXISTS verify_cases (
|
||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||
batch_no TEXT NOT NULL,
|
||
teacher_name TEXT,
|
||
department TEXT,
|
||
apply_rank TEXT,
|
||
metrics_json TEXT,
|
||
verdict TEXT NOT NULL,
|
||
matrix_json TEXT,
|
||
report_file TEXT,
|
||
created_at INTEGER NOT NULL DEFAULT (unixepoch())
|
||
)
|
||
"""
|
||
|
||
_VERIFY_CASES_INDEX = """
|
||
CREATE INDEX IF NOT EXISTS idx_verify_cases_batch
|
||
ON verify_cases(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
|
||
|
||
|
||
_THRESHOLD_RULES_COLUMNS: tuple[dict[str, object], ...] = (
|
||
_col("id", "编号", "主键", searchable=1),
|
||
_col("rank", "申报级别", "副教授 / 教授。", searchable=1),
|
||
_col("metric_key", "指标键", "指标唯一键(presiding_provincial 等)。", searchable=1),
|
||
_col("metric_name", "指标名称", "如 主持省部级项目。", searchable=1, editable=1),
|
||
_col("min_value", "最低要求", "刚性门槛数值。", searchable=1, editable=1, display_type="number"),
|
||
_col("unit", "单位", "项/篇/课时/年 等。", editable=1),
|
||
_col("basis_doc", "依据文件", "如《专业技术职务评聘办法》。", searchable=1, editable=1),
|
||
_col("clause", "条款", "门槛出处条款号。", editable=1),
|
||
_col("created_at", "创建时间", "记录创建时间(Unix 秒级,自动生成)。"),
|
||
_col("updated_at", "更新时间", "记录最后更新时间(Unix 秒级,自动维护)。"),
|
||
)
|
||
|
||
_VERIFY_CASES_COLUMNS: tuple[dict[str, object], ...] = (
|
||
_col("id", "案例编号", "主键,核验记录编号。", searchable=1),
|
||
_col("batch_no", "核验批次号", "同一次核验请求的批次号。", searchable=1),
|
||
_col("teacher_name", "教师姓名", "被核验教师。", searchable=1),
|
||
_col("department", "所属学院", "教师所属学院。", searchable=1),
|
||
_col("apply_rank", "申报级别", "副教授 / 教授。", searchable=1),
|
||
_col("metrics_json", "申报指标", "教师实有指标(JSON,默认隐藏)。", visible=0, display_type="textarea"),
|
||
_col(
|
||
"verdict",
|
||
"核验结论",
|
||
"安全(形式审查通过)/ 边缘(建议补充)/ 硬伤(暂缓提交)。",
|
||
searchable=1,
|
||
options_json='[{"value":"安全","label":"🟢 安全"},{"value":"边缘","label":"🟡 边缘"},{"value":"硬伤","label":"🔴 硬伤"}]',
|
||
),
|
||
_col("matrix_json", "达标矩阵", "逐项达标矩阵(JSON)。", visible=0, display_type="textarea"),
|
||
_col("report_file", "报告文件", "《核验报告》HTML 文件路径。", visible=0),
|
||
_col("created_at", "创建时间", "记录创建时间(Unix 秒级,自动生成)。"),
|
||
)
|
||
|
||
|
||
def _init_threshold_rules_metadata(cursor) -> None:
|
||
cursor.execute(_THRESHOLD_RULES_DDL)
|
||
init_updated_at_trigger(cursor, "threshold_rules")
|
||
upsert_table_metadata(
|
||
cursor,
|
||
table_name="threshold_rules",
|
||
display_name="评审门槛库",
|
||
description="各级别职称申报的刚性门槛(数值须以人事处最新文件为准)。",
|
||
grid_help="按 申报级别+指标键 唯一;行内可编辑最低要求与依据条款。",
|
||
visible=1,
|
||
readonly=0,
|
||
)
|
||
seed_column_metadata_batch(
|
||
cursor,
|
||
table_name="threshold_rules",
|
||
columns=_THRESHOLD_RULES_COLUMNS,
|
||
valid_column_names=[str(c["column_name"]) for c in _THRESHOLD_RULES_COLUMNS],
|
||
)
|
||
|
||
|
||
def _init_verify_cases_metadata(cursor) -> None:
|
||
cursor.execute(_VERIFY_CASES_DDL)
|
||
cursor.execute(_VERIFY_CASES_INDEX)
|
||
upsert_table_metadata(
|
||
cursor,
|
||
table_name="verify_cases",
|
||
display_name="核验案例",
|
||
description="每次核验的达标矩阵与三色结论;可重建《核验报告》。",
|
||
grid_help="🔴硬伤=存在不达门槛项;🟡边缘=达标但余量不足10%;🟢安全=全部超标准10%以上。",
|
||
visible=1,
|
||
readonly=1,
|
||
default_sort_json='[{"column":"id","direction":"desc"}]',
|
||
default_visible_columns_json='["id","teacher_name","department","apply_rank","verdict","created_at"]',
|
||
)
|
||
seed_column_metadata_batch(
|
||
cursor,
|
||
table_name="verify_cases",
|
||
columns=_VERIFY_CASES_COLUMNS,
|
||
valid_column_names=[str(c["column_name"]) for c in _VERIFY_CASES_COLUMNS],
|
||
)
|
||
|
||
|
||
def init_db() -> dict[str, int]:
|
||
"""幂等创建/迁移本地 SQLite、数据管理元数据与门槛种子。"""
|
||
conn = get_conn()
|
||
try:
|
||
cur = conn.cursor()
|
||
init_display_metadata(cur)
|
||
_init_threshold_rules_metadata(cur)
|
||
_init_verify_cases_metadata(cur)
|
||
seeded = seed_all(cur)
|
||
conn.commit()
|
||
return seeded
|
||
finally:
|
||
conn.close()
|