"""特殊批次定向培养计划查询模块。 加载 data/crowd_db/special_programs.json,提供按省份/分数匹配特殊批次推荐的能力。 """ from __future__ import annotations import json from pathlib import Path from typing import Any _SPECIAL_PROGRAMS_PATH = Path(__file__).resolve().parent / "special_programs.json" _SPECIAL_RULES_PATH = ( Path(__file__).resolve().parents[1] / "rules" / "special_programs_rules.json" ) class SpecialProgramsLoader: """加载特殊批次定向培养计划数据。""" def __init__( self, data_path: Path | None = None, rules_path: Path | None = None ) -> None: self._data_path = data_path or _SPECIAL_PROGRAMS_PATH self._rules_path = rules_path or _SPECIAL_RULES_PATH self._data: dict[str, Any] | None = None self._rules: dict[str, Any] | None = None @property def data(self) -> dict[str, Any]: if self._data is None: self._data = json.loads(self._data_path.read_text(encoding="utf-8")) return self._data # type: ignore[return-value] @property def rules(self) -> dict[str, Any]: if self._rules is None: self._rules = json.loads(self._rules_path.read_text(encoding="utf-8")) return self._rules # type: ignore[return-value] def list_programs(self) -> list[dict[str, Any]]: """列出全部 5 类特殊批次项目。""" return self.data.get("programs", []) def get_program(self, program_type: str) -> dict[str, Any] | None: """按 program_type 获取单个项目详情。""" for p in self.list_programs(): if p.get("program_type") == program_type: return p return None def list_programs_for_province(self, province: str) -> list[dict[str, Any]]: """列出某省份适用的特殊批次项目。""" province = province.strip() result = [] for p in self.list_programs(): applicable = p.get("applicable_provinces") or [] if "全国" in applicable or province in applicable: result.append(p) return result def find_matching_schools( self, province: str, score: int, *, program_type: str | None = None, ) -> list[dict[str, Any]]: """根据省份和分数,匹配可捡漏的特殊批次院校。 Args: province: 考试省份。 score: 考生分数。 program_type: 可选,限定项目类型。 Returns: 匹配的院校列表,每个含 school/major/score_min/program_type 等。 """ province = province.strip() program_schools = self.data.get("program_schools", {}) results: list[dict[str, Any]] = [] for ptype, schools in program_schools.items(): if program_type and ptype != program_type: continue for s in schools: s_province = s.get("province", "") # "全国" 适用所有省份 if s_province != "全国" and s_province != province: continue score_min = s.get("score_min", 999) # 考生分数 >= 院校最低分 * 0.9(允许一定弹性) if score >= int(score_min) * 0.85: results.append({ **s, "program_type": ptype, "gap": score - int(score_min), }) # 按分数差升序(越接近的越优先推荐) results.sort(key=lambda x: abs(x.get("gap", 0))) return results def get_applicable_rules(self, program_type: str) -> list[dict[str, Any]]: """获取某项目类型适用的规则列表。""" all_rules = self.rules.get("rules", []) return [r for r in all_rules if program_type in (r.get("applies_to") or [])] def build_recommendation_for_review( self, province: str, score: int, ) -> list[dict[str, Any]]: """为审核/复核场景生成特殊批次推荐摘要。 返回格式适合直接注入 LLM prompt 或 ReviewResultContract。 """ programs = self.list_programs_for_province(province) if not programs: return [] recommendations = [] for p in programs: ptype = p.get("program_type", "") matching_schools = self.find_matching_schools( province, score, program_type=ptype ) if not matching_schools: continue # 取最近的院校 best_school = matching_schools[0] features = p.get("key_features", {}) recommendations.append({ "program_type": ptype, "program_name": p.get("program_name", ""), "description": p.get("description", ""), "batch": p.get("batch", ""), "best_match_school": best_school.get("school", ""), "best_match_major": best_school.get("major", ""), "best_match_score_min": best_school.get("score_min", 0), "tuition": features.get("tuition", ""), "employment": features.get("employment", ""), "service_years": features.get("service_years"), "exam_required": features.get("exam_required", False), "physical_check": features.get("physical_check", ""), "match_score": max(0, 100 - abs(best_school.get("gap", 0))), "warnings": p.get("warnings", []), }) # 按 match_score 降序 recommendations.sort(key=lambda x: x.get("match_score", 0), reverse=True) return recommendations