Files
verify-promotion-metric-def…/scripts/cli/app.py
chenfuqiang 04026d0372
All checks were successful
技能自动化发布 / release (push) Successful in 7s
feat: 职称评审核验助手首版 v1.0.1(verify-promotion-metric-default)
2026-09-13 16:05:20 +08:00

198 lines
8.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""CLI:argparse 与分发(职称评审核验助手)。
只负责参数解析并转交 service;业务内核在 verify_service / threshold_service,
勿在此按入口复制逻辑。
"""
from __future__ import annotations
import argparse
import sys
from typing import List, Optional
from service.task_service import (
cmd_config_path,
cmd_health,
cmd_import_thresholds,
cmd_init_db,
cmd_list_cases,
cmd_render_html,
cmd_run,
cmd_set_threshold,
cmd_show_case,
cmd_stats,
cmd_verify_batch_input,
cmd_verify_metric,
cmd_version,
cmd_view_thresholds,
)
from util.config_bootstrap import bootstrap_skill_config
from util.argparse_zh import ZhArgumentParser
from util.constants import LOG_LOGGER_NAME, SKILL_SLUG
from util.logging_config import get_skill_logger, setup_skill_logging
def _cli_str_or_none(raw: Optional[str]) -> Optional[str]:
if raw is None:
return None
v = str(raw).strip()
return v or None
def _cli_int_or_none(raw: Optional[str]) -> Optional[int]:
raw = _cli_str_or_none(raw)
if raw is None:
return None
try:
return int(float(raw))
except ValueError:
return None
def _handle_run(args: argparse.Namespace) -> int:
tail = [str(x).strip() for x in (args.run_tail or []) if str(x).strip()]
if len(tail) > 2:
print("参数过多。")
print("用法:python main.py run [target [input_id]] | run [-t target] [-i input_id]")
return 1
t_target: Optional[str] = None
t_input: Optional[str] = None
if len(tail) == 2:
t_target, t_input = tail[0], tail[1]
elif len(tail) == 1:
if tail[0].isdigit():
t_input = tail[0]
else:
t_target = tail[0]
target = _cli_str_or_none(getattr(args, "target", None)) or t_target
input_id = _cli_str_or_none(getattr(args, "input_id", None)) or t_input
return cmd_run(target=target, input_id=input_id)
def _handle_set_threshold(args):
return cmd_set_threshold(
rank=str(args.rank or "").strip(),
metric_key=str(args.metric_key or "").strip(),
metric_name=_cli_str_or_none(args.metric_name) or "",
min_value=float(args.min_value),
unit=_cli_str_or_none(args.unit) or "",
basis_doc=_cli_str_or_none(args.basis_doc) or "",
clause=_cli_str_or_none(args.clause) or "",
)
def _handle_verify(args):
return cmd_verify_metric(
teacher_name=str(args.teacher or "").strip(),
apply_rank=str(args.apply_rank or "").strip(),
metrics_json=str(args.metrics_json or "").strip(),
department=_cli_str_or_none(args.department) or "",
)
def _handle_list_reports(args):
return cmd_list_cases(limit=int(args.limit or 20), verdict=_cli_str_or_none(args.verdict) or "")
def _handle_render_html(args):
report_id = getattr(args, "report_id", None)
return cmd_render_html(report_id=int(report_id) if report_id else None)
def _print_full_usage() -> None:
print("职称评审核验助手(main.py)可用命令:")
print(" python main.py verify-metric --teacher 张三 --apply-rank 副教授 --metrics-json <file|内联|-> [--department 学院]")
print(" python main.py verify-batch-input # 批量核验 input/ 目录申报案例")
print(" python main.py view-thresholds [--rank 副教授]")
print(" python main.py set-threshold --rank 副教授 --metric-key presiding_provincial --metric-name 主持省部级项目 --min-value 1 [--unit 项] [--basis-doc 文件] [--clause 条款]")
print(" python main.py import-thresholds --thresholds-json <file|->")
print(" python main.py list-cases [--limit N] [--verdict 硬伤] | show-case --id N | render-html [--report-id N]")
print(" python main.py stats")
print(" python main.py run [target [input_id]] [-t target] [-i input_id]")
print(" python main.py health | config-path | version | init-db")
def build_parser() -> ZhArgumentParser:
p = ZhArgumentParser(
prog="main.py",
description="职称评审核验助手:维护各级别刚性门槛库,对申报案例逐项比对生成达标矩阵与红黄绿三色结论,出具《核验报告》。",
formatter_class=argparse.RawDescriptionHelpFormatter,
)
sub = p.add_subparsers(dest="cmd", required=True, parser_class=ZhArgumentParser)
sp = sub.add_parser("verify-metric", help="职称硬指标核验(主命令)")
sp.add_argument("--teacher", required=True, metavar="教师姓名")
sp.add_argument("--apply-rank", required=True, metavar="申报级别(副教授/教授)", dest="apply_rank")
sp.add_argument("--metrics-json", required=True, metavar="指标JSON(file|内联|-)", dest="metrics_json")
sp.add_argument("--department", default=None, metavar="所属学院")
sp.set_defaults(handler=_handle_verify)
sp = sub.add_parser("verify-batch-input", help="批量核验 input/ 目录申报案例")
sp.set_defaults(handler=lambda _a: cmd_verify_batch_input())
sp = sub.add_parser("view-thresholds", help="查看评审门槛库")
sp.add_argument("--rank", default=None, metavar="级别筛选")
sp.set_defaults(handler=lambda a: cmd_view_thresholds(rank=_cli_str_or_none(a.rank) or ""))
sp = sub.add_parser("set-threshold", help="录入/更新评审门槛")
sp.add_argument("--rank", required=True, metavar="申报级别", dest="rank")
sp.add_argument("--metric-key", required=True, metavar="指标键", dest="metric_key")
sp.add_argument("--metric-name", default=None, metavar="指标名称", dest="metric_name")
sp.add_argument("--min-value", required=True, type=float, metavar="最低要求", dest="min_value")
sp.add_argument("--unit", default=None, metavar="单位")
sp.add_argument("--basis-doc", default=None, metavar="依据文件", dest="basis_doc")
sp.add_argument("--clause", default=None, metavar="条款")
sp.set_defaults(handler=_handle_set_threshold)
sp = sub.add_parser("import-thresholds", help="批量导入门槛")
sp.add_argument("--thresholds-json", required=True, metavar="JSON(file|-)", dest="thresholds_json")
sp.set_defaults(handler=lambda a: cmd_import_thresholds(str(a.thresholds_json)))
sp = sub.add_parser("list-cases", help="查询核验案例")
sp.add_argument("--limit", default=20, metavar="条数")
sp.add_argument("--verdict", default=None, metavar="结论筛选(安全/边缘/硬伤)")
sp.set_defaults(handler=_handle_list_reports)
sp = sub.add_parser("show-case", help="查看核验案例详情")
sp.add_argument("--id", required=True, type=int, metavar="案例编号", dest="case_id")
sp.set_defaults(handler=lambda a: cmd_show_case(case_id=int(a.case_id)))
sp = sub.add_parser("render-html", help="重建《核验报告》(缺省最近 10 份)")
sp.add_argument("--report-id", default=None, metavar="案例编号", dest="report_id")
sp.set_defaults(handler=_handle_render_html)
sp = sub.add_parser("stats", help="核验统计")
sp.set_defaults(handler=lambda _a: cmd_stats())
sp = sub.add_parser("run", help="任务执行命令骨架")
sp.add_argument("--target", "-t", default=None, metavar="目标")
sp.add_argument("--input-id", "-i", default=None, metavar="输入id", dest="input_id")
sp.add_argument("run_tail", nargs="*", metavar="位置参数")
sp.set_defaults(handler=_handle_run)
sp = sub.add_parser("health", help="健康检查")
sp.set_defaults(handler=lambda _a: cmd_health())
sp = sub.add_parser("config-path", help="输出用户 .env 与模板路径")
sp.set_defaults(handler=lambda _a: cmd_config_path())
sp = sub.add_parser("version", help="版本信息(JSON)")
sp.set_defaults(handler=lambda _a: cmd_version())
sp = sub.add_parser("init-db", help="幂等初始化本地数据库与数据管理元数据")
sp.set_defaults(handler=lambda _a: cmd_init_db())
return p
def main(argv: Optional[List[str]] = None) -> int:
argv = argv if argv is not None else sys.argv[1:]
bootstrap_skill_config()
setup_skill_logging(SKILL_SLUG, LOG_LOGGER_NAME)
get_skill_logger().info("cli_start argv=%s", sys.argv)
if not argv:
_print_full_usage()
return 1
parser = build_parser()
args = parser.parse_args(argv)
return int(args.handler(args))