feat: 职称评审核验助手首版 v1.0.1(verify-promotion-metric-default)
All checks were successful
技能自动化发布 / release (push) Successful in 7s

This commit is contained in:
chenfuqiang
2026-09-13 16:05:20 +08:00
commit 04026d0372
206 changed files with 29350 additions and 0 deletions

169
scripts/db/connection.py Normal file
View File

@@ -0,0 +1,169 @@
"""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()