"""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 [--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 ") 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))