feat: 申报赛道推荐助手首版 v1.0.1(match-guide-track-default)
All checks were successful
技能自动化发布 / release (push) Successful in 6s
All checks were successful
技能自动化发布 / release (push) Successful in 6s
This commit is contained in:
225
scripts/db/connection.py
Normal file
225
scripts/db/connection.py
Normal file
@@ -0,0 +1,225 @@
|
||||
"""SQLite 连接与幂等初始化(申报赛道推荐助手)。
|
||||
|
||||
业务表 3 张:
|
||||
- teacher_profiles(教师画像,可编辑)
|
||||
- guide_tracks(指南赛道,可编辑)
|
||||
- match_reports(推荐报告,只读,匹配自动写入)
|
||||
|
||||
表间禁止 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())
|
||||
|
||||
|
||||
_TEACHER_PROFILES_DDL = """
|
||||
CREATE TABLE IF NOT EXISTS teacher_profiles (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
teacher_name TEXT NOT NULL UNIQUE,
|
||||
department TEXT,
|
||||
title TEXT,
|
||||
research_tags TEXT,
|
||||
recent_papers TEXT,
|
||||
project_history TEXT,
|
||||
sci_q1_count INTEGER,
|
||||
core_paper_count INTEGER,
|
||||
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
||||
updated_at INTEGER NOT NULL DEFAULT (unixepoch())
|
||||
)
|
||||
"""
|
||||
|
||||
_GUIDE_TRACKS_DDL = """
|
||||
CREATE TABLE IF NOT EXISTS guide_tracks (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
year INTEGER NOT NULL,
|
||||
agency TEXT,
|
||||
program_name TEXT,
|
||||
track_name TEXT NOT NULL,
|
||||
track_tags TEXT,
|
||||
priority_keywords TEXT,
|
||||
min_title TEXT,
|
||||
min_sci_q1 INTEGER NOT NULL DEFAULT 0,
|
||||
quota INTEGER,
|
||||
source_doc TEXT,
|
||||
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
||||
updated_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
||||
UNIQUE (year, agency, program_name, track_name)
|
||||
)
|
||||
"""
|
||||
|
||||
_MATCH_REPORTS_DDL = """
|
||||
CREATE TABLE IF NOT EXISTS match_reports (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
batch_no TEXT NOT NULL,
|
||||
teacher_name TEXT,
|
||||
department TEXT,
|
||||
season TEXT,
|
||||
top_json TEXT,
|
||||
tiers_json TEXT,
|
||||
eligible_count INTEGER,
|
||||
excluded_json TEXT,
|
||||
report_file TEXT,
|
||||
created_at INTEGER NOT NULL DEFAULT (unixepoch())
|
||||
)
|
||||
"""
|
||||
|
||||
_MATCH_REPORTS_INDEX = """
|
||||
CREATE INDEX IF NOT EXISTS idx_match_reports_batch
|
||||
ON match_reports(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
|
||||
|
||||
|
||||
_TEACHER_PROFILES_COLUMNS: tuple[dict[str, object], ...] = (
|
||||
_col("id", "编号", "主键", searchable=1),
|
||||
_col("teacher_name", "教师姓名", "教师姓名(唯一)。", searchable=1),
|
||||
_col("department", "所属学院", "二级学院/单位。", searchable=1, editable=1),
|
||||
_col("title", "职称", "讲师/副教授/教授等,用于职称门槛比对。", searchable=1, editable=1, options_json='[{"value":"讲师","label":"讲师"},{"value":"副教授","label":"副教授"},{"value":"教授","label":"教授"}]'),
|
||||
_col("research_tags", "研究标签", "研究方向关键词,逗号分隔(匹配主依据)。", searchable=1, editable=1),
|
||||
_col("recent_papers", "近3年论文", "近 3 年论文标题,分号分隔。", editable=1),
|
||||
_col("project_history", "项目经历", "在研/结题项目名称,分号分隔。", editable=1),
|
||||
_col("sci_q1_count", "SCI一区篇数", "SCI 一区论文数量(硬门槛比对)。", searchable=1, editable=1, display_type="number"),
|
||||
_col("core_paper_count", "核心论文篇数", "核心期刊论文数量。", searchable=1, editable=1, display_type="number"),
|
||||
_col("created_at", "创建时间", "记录创建时间(Unix 秒级,自动生成)。"),
|
||||
_col("updated_at", "更新时间", "记录最后更新时间(Unix 秒级,自动维护)。"),
|
||||
)
|
||||
|
||||
_GUIDE_TRACKS_COLUMNS: tuple[dict[str, object], ...] = (
|
||||
_col("id", "编号", "主键", searchable=1),
|
||||
_col("year", "指南年度", "指南发布/申报年度。", searchable=1),
|
||||
_col("agency", "学部/归口", "如 数理学部、科研处。", searchable=1, editable=1),
|
||||
_col("program_name", "项目类型", "如 面上项目、青年项目。", searchable=1, editable=1),
|
||||
_col("track_name", "赛道名称", "指南中的方向/赛道名。", searchable=1, editable=1),
|
||||
_col("track_tags", "需求标签", "赛道方向关键词,逗号分隔(匹配主依据)。", searchable=1, editable=1),
|
||||
_col("priority_keywords", "优先方向", "优先资助方向关键词,逗号分隔(加分项)。", editable=1),
|
||||
_col("min_title", "职称门槛", "讲师及以上 / 副高及以上 / 正高及以上。", editable=1, options_json='[{"value":"讲师及以上","label":"讲师及以上"},{"value":"副高及以上","label":"副高及以上"},{"value":"正高及以上","label":"正高及以上"}]'),
|
||||
_col("min_sci_q1", "SCI一区门槛", "要求的 SCI 一区最少篇数。", editable=1, display_type="number"),
|
||||
_col("quota", "拟资助数", "该赛道拟资助项数(参考)。", editable=1, display_type="number"),
|
||||
_col("source_doc", "指南来源", "指南文件名/文号。", editable=1),
|
||||
_col("created_at", "创建时间", "记录创建时间(Unix 秒级,自动生成)。"),
|
||||
_col("updated_at", "更新时间", "记录最后更新时间(Unix 秒级,自动维护)。"),
|
||||
)
|
||||
|
||||
_MATCH_REPORTS_COLUMNS: tuple[dict[str, object], ...] = (
|
||||
_col("id", "报告编号", "主键,推荐报告编号。", searchable=1),
|
||||
_col("batch_no", "匹配批次号", "同一次匹配请求的批次号。", searchable=1),
|
||||
_col("teacher_name", "教师姓名", "被匹配教师。", searchable=1),
|
||||
_col("department", "所属学院", "教师所属学院。", searchable=1),
|
||||
_col("season", "指南年度", "匹配所用指南年度。", searchable=1),
|
||||
_col("top_json", "Top3推荐", "Top 3 赛道(JSON,含得分与理由)。", visible=0, display_type="textarea"),
|
||||
_col("tiers_json", "分层清单", "强烈推荐/可尝试/不建议分层(JSON)。", visible=0, display_type="textarea"),
|
||||
_col("eligible_count", "可匹配赛道数", "通过硬门槛过滤后的赛道数。", searchable=1, display_type="number"),
|
||||
_col("excluded_json", "剔除清单", "被硬门槛剔除的赛道及原因(JSON)。", visible=0, display_type="textarea"),
|
||||
_col("report_file", "建议书文件", "《申报策略建议书》HTML 文件路径。", visible=0),
|
||||
_col("created_at", "创建时间", "报告创建时间(Unix 秒级,自动生成)。"),
|
||||
)
|
||||
|
||||
|
||||
def _init_teacher_profiles_metadata(cursor) -> None:
|
||||
cursor.execute(_TEACHER_PROFILES_DDL)
|
||||
init_updated_at_trigger(cursor, "teacher_profiles")
|
||||
upsert_table_metadata(
|
||||
cursor,
|
||||
table_name="teacher_profiles",
|
||||
display_name="教师画像",
|
||||
description="教师学术画像:研究标签、成果与职称,用于赛道匹配。",
|
||||
grid_help="研究标签是匹配主依据(逗号分隔);顶栏按钮可录入,行内可编辑。",
|
||||
visible=1,
|
||||
readonly=0,
|
||||
default_sort_json='[{"column":"id","direction":"desc"}]',
|
||||
default_visible_columns_json='["teacher_name","department","title","research_tags","sci_q1_count"]',
|
||||
)
|
||||
seed_column_metadata_batch(
|
||||
cursor,
|
||||
table_name="teacher_profiles",
|
||||
columns=_TEACHER_PROFILES_COLUMNS,
|
||||
valid_column_names=[str(c["column_name"]) for c in _TEACHER_PROFILES_COLUMNS],
|
||||
)
|
||||
|
||||
|
||||
def _init_guide_tracks_metadata(cursor) -> None:
|
||||
cursor.execute(_GUIDE_TRACKS_DDL)
|
||||
init_updated_at_trigger(cursor, "guide_tracks")
|
||||
upsert_table_metadata(
|
||||
cursor,
|
||||
table_name="guide_tracks",
|
||||
display_name="指南赛道",
|
||||
description="当年申报指南的结构化赛道库:需求标签、优先方向与硬门槛。",
|
||||
grid_help="按年度管理;需求标签为匹配主依据,硬门槛用于过滤不满足条件的教师。",
|
||||
visible=1,
|
||||
readonly=0,
|
||||
default_sort_json='[{"column":"year","direction":"desc"},{"column":"id","direction":"asc"}]',
|
||||
default_visible_columns_json='["year","program_name","track_name","track_tags","min_title","min_sci_q1"]',
|
||||
)
|
||||
seed_column_metadata_batch(
|
||||
cursor,
|
||||
table_name="guide_tracks",
|
||||
columns=_GUIDE_TRACKS_COLUMNS,
|
||||
valid_column_names=[str(c["column_name"]) for c in _GUIDE_TRACKS_COLUMNS],
|
||||
)
|
||||
|
||||
|
||||
def _init_match_reports_metadata(cursor) -> None:
|
||||
cursor.execute(_MATCH_REPORTS_DDL)
|
||||
cursor.execute(_MATCH_REPORTS_INDEX)
|
||||
upsert_table_metadata(
|
||||
cursor,
|
||||
table_name="match_reports",
|
||||
display_name="推荐报告",
|
||||
description="每次匹配的 Top 3 赛道、分层清单与剔除原因;可重建建议书。",
|
||||
grid_help="推荐仅供参考;顶栏按钮可重建《申报策略建议书》。",
|
||||
visible=1,
|
||||
readonly=1,
|
||||
default_sort_json='[{"column":"id","direction":"desc"}]',
|
||||
default_visible_columns_json='["id","season","teacher_name","department","eligible_count"]',
|
||||
)
|
||||
seed_column_metadata_batch(
|
||||
cursor,
|
||||
table_name="match_reports",
|
||||
columns=_MATCH_REPORTS_COLUMNS,
|
||||
valid_column_names=[str(c["column_name"]) for c in _MATCH_REPORTS_COLUMNS],
|
||||
)
|
||||
|
||||
|
||||
def init_db() -> dict[str, int]:
|
||||
"""幂等创建/迁移本地 SQLite 与数据管理元数据。"""
|
||||
conn = get_conn()
|
||||
try:
|
||||
cur = conn.cursor()
|
||||
init_display_metadata(cur)
|
||||
_init_teacher_profiles_metadata(cur)
|
||||
_init_guide_tracks_metadata(cur)
|
||||
_init_match_reports_metadata(cur)
|
||||
conn.commit()
|
||||
return {}
|
||||
finally:
|
||||
conn.close()
|
||||
Reference in New Issue
Block a user