hierarchy_extract.py 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. """
  2. 按 case_elements_hierarchy.json 的层级结构,将规则抽取结果(merge_dispute_template_fields 等扁平字段)
  3. 填入对应叶子节点;案由分类决定仅展示与填充该案由子树。
  4. """
  5. from __future__ import annotations
  6. import copy
  7. import json
  8. from pathlib import Path
  9. from typing import Any
  10. _HIERARCHY_PATH = Path(__file__).resolve().parent.parent / "data" / "case_elements_hierarchy.json"
  11. # 叶子路径 "案由>一层>二层>…" -> merge_dispute_template_fields / extract 产出的英文 hint 键
  12. LEAF_PATH_TO_HINT_KEY: dict[str, str] = {
  13. # —— 通用要素(各案由顶层,值来自同一组扁平字段)——
  14. "劳动关系纠纷类>申请人信息": "gen_applicant_info",
  15. "劳动关系纠纷类>被申请人信息": "gen_respondent_info",
  16. "劳动关系纠纷类>事实与理由": "gen_facts_and_reasons",
  17. "工伤保险待遇纠纷>申请人信息": "gen_applicant_info",
  18. "工伤保险待遇纠纷>被申请人信息": "gen_respondent_info",
  19. "工伤保险待遇纠纷>事实与理由": "gen_facts_and_reasons",
  20. "经济补偿金纠纷>申请人信息": "gen_applicant_info",
  21. "经济补偿金纠纷>被申请人信息": "gen_respondent_info",
  22. "经济补偿金纠纷>事实与理由": "gen_facts_and_reasons",
  23. "赔偿金纠纷>申请人信息": "gen_applicant_info",
  24. "赔偿金纠纷>被申请人信息": "gen_respondent_info",
  25. "赔偿金纠纷>事实与理由": "gen_facts_and_reasons",
  26. "生育保险待遇纠纷>申请人信息": "gen_applicant_info",
  27. "生育保险待遇纠纷>被申请人信息": "gen_respondent_info",
  28. "生育保险待遇纠纷>事实与理由": "gen_facts_and_reasons",
  29. # —— 劳动关系纠纷类 ——
  30. "劳动关系纠纷类>劳动报酬>劳动报酬发放周期": "lr1_pay_cycle",
  31. "劳动关系纠纷类>劳动报酬>劳动报酬金额": "lr1_pay_amount",
  32. "劳动关系纠纷类>劳动报酬>劳动报酬发放形式": "lr1_pay_form",
  33. "劳动关系纠纷类>社会保险>是否参加社会保险": "lr1_si_joined",
  34. "劳动关系纠纷类>社会保险>保险待遇金额": "lr1_si_benefit_amount",
  35. "劳动关系纠纷类>劳动关系>是否签订劳动合同>签订无固定限期劳动合同": "lr1_open_ended_contract",
  36. "劳动关系纠纷类>劳动关系>是否签订劳动合同>未签订劳动合同的二倍工资": "lr1_double_wage_no_contract",
  37. "劳动关系纠纷类>劳动关系>劳动关系存在时间": "lr1_relation_duration",
  38. # —— 工伤保险待遇纠纷 ——
  39. "工伤保险待遇纠纷>劳动关系>是否签订劳动合同": "wi_lr_contract_signed",
  40. "工伤保险待遇纠纷>劳动关系>劳动关系存在时间": "wi_lr_relation_duration",
  41. "工伤保险待遇纠纷>保险待遇>保险待遇发放时间>一次性伤残补助费": "wi_benefit_disability",
  42. "工伤保险待遇纠纷>保险待遇>保险待遇发放时间>辅助器具费": "wi_benefit_prosthetic",
  43. "工伤保险待遇纠纷>保险待遇>保险待遇发放时间>一次性医疗补助金": "wi_benefit_medical_allowance",
  44. "工伤保险待遇纠纷>保险待遇>保险待遇发放时间>交通食宿费": "wi_benefit_travel",
  45. "工伤保险待遇纠纷>保险待遇>保险待遇发放时间>工伤医疗/康复费用": "wi_benefit_rehab",
  46. "工伤保险待遇纠纷>保险待遇>保险待遇发放时间>住院治疗期间护理费": "wi_benefit_nursing",
  47. "工伤保险待遇纠纷>保险待遇>保险待遇发放时间>住院伙食补助费": "wi_benefit_meal",
  48. "工伤保险待遇纠纷>保险待遇>保险待遇金额": "wi_benefit_amount_total",
  49. "工伤保险待遇纠纷>保险待遇>保险待遇发放形式": "wi_benefit_pay_form",
  50. "工伤保险待遇纠纷>保险待遇>是否认可经济补偿": "wi_recognize_ec",
  51. "工伤保险待遇纠纷>社会保险>保险待遇金额": "wi_si_benefit_amt",
  52. "工伤保险待遇纠纷>社会保险>是否参加社会保险": "wi_si_joined",
  53. # —— 追索劳动报酬 ——
  54. "追索劳动报酬>申请人信息": "gen_applicant_info",
  55. "追索劳动报酬>被申请人信息": "gen_respondent_info",
  56. "追索劳动报酬>事实与理由": "gen_facts_and_reasons",
  57. "追索劳动报酬>劳动报酬>劳动报酬发放周期": "sr_pay_cycle",
  58. "追索劳动报酬>劳动报酬>克扣劳动报酬>主张金额": "sr_claim_deducted_pay",
  59. "追索劳动报酬>劳动报酬>克扣劳动报酬>生活费": "sr_claim_living_allowance",
  60. "追索劳动报酬>劳动报酬>加班工资": "sr_claim_overtime_pay",
  61. "追索劳动报酬>劳动报酬>实际支付工资标准": "sr_actual_pay_standard",
  62. "追索劳动报酬>劳动报酬>高温津贴金额": "sr_high_temp_allowance",
  63. "追索劳动报酬>劳动报酬>约定工资标准": "sr_agreed_pay_standard",
  64. "追索劳动报酬>劳动报酬>带薪年休假工资": "sr_annual_leave_pay",
  65. "追索劳动报酬>劳动报酬>未支付期间": "sr_unpaid_period",
  66. "追索劳动报酬>劳动报酬>加班工资金额": "sr_overtime_amount",
  67. # —— 经济补偿金纠纷 ——
  68. "经济补偿金纠纷>经济补偿金>离职前12个月平均工资": "ec_avg_salary_12m",
  69. "经济补偿金纠纷>经济补偿金>未签订劳动合同的二倍工资": "ec_double_wage_part",
  70. "经济补偿金纠纷>经济补偿金>主张金额>违法解除劳动合同的赔偿金": "ec_illegal_term_part",
  71. "经济补偿金纠纷>经济补偿金>主张金额>违法约定试用期的赔偿金": "ec_illegal_probation_part",
  72. "经济补偿金纠纷>经济补偿金>代通知金": "ec_notice_pay",
  73. "经济补偿金纠纷>经济补偿金>加付赔偿金": "ec_additional_damages",
  74. "经济补偿金纠纷>经济补偿金>劳动合同存在时间": "ec_contract_duration",
  75. "经济补偿金纠纷>劳动合同>离职原因": "ec_leave_reason",
  76. "经济补偿金纠纷>劳动合同>离职时间": "ec_leave_date",
  77. # —— 赔偿金纠纷 ——
  78. "赔偿金纠纷>赔偿金>主张金额": "dm_claim_amount",
  79. "赔偿金纠纷>赔偿金>违法解除劳动合同赔偿金": "dm_illegal_dismissal_damages",
  80. "赔偿金纠纷>劳动合同>劳动合同是否存在": "dm_contract_exists",
  81. "赔偿金纠纷>劳动合同>解除劳动合同原因": "dm_terminate_reason",
  82. "赔偿金纠纷>劳动合同>劳动合同是否继续履行": "dm_contract_continue",
  83. # —— 生育保险待遇纠纷 ——
  84. "生育保险待遇纠纷>金额>主张金额>产假工资/生育津贴": "mi_maternity_allowance_salary",
  85. "生育保险待遇纠纷>金额>主张金额>生育医疗费用": "mi_maternity_medical",
  86. "生育保险待遇纠纷>金额>加付赔偿金": "mi_additional_damages",
  87. "生育保险待遇纠纷>金额>交通食宿费": "mi_travel_accommodation",
  88. "生育保险待遇纠纷>劳动合同>劳动合同是否继续履行": "mi_contract_continue",
  89. "生育保险待遇纠纷>劳动合同>劳动合同解除原因": "mi_terminate_reason",
  90. }
  91. DEFAULT_CAUSE_TYPE = "劳动关系纠纷类"
  92. def load_hierarchy_templates() -> dict[str, Any]:
  93. if not _HIERARCHY_PATH.is_file():
  94. return {}
  95. try:
  96. return json.loads(_HIERARCHY_PATH.read_text(encoding="utf-8"))
  97. except Exception:
  98. return {}
  99. def _path_key(path: tuple[str, ...]) -> str:
  100. return ">".join(path)
  101. def _leaf_value(path: tuple[str, ...], hints: dict[str, Any]) -> Any:
  102. key = _path_key(path)
  103. hk = LEAF_PATH_TO_HINT_KEY.get(key)
  104. if hk and hk in hints:
  105. v = hints[hk]
  106. if v is not None and v != "":
  107. return v
  108. return None
  109. def fill_hierarchy_node(node: Any, path: tuple[str, ...], hints: dict[str, Any]) -> Any:
  110. if node is None:
  111. return _leaf_value(path, hints)
  112. if isinstance(node, dict):
  113. return {k: fill_hierarchy_node(v, path + (k,), hints) for k, v in node.items()}
  114. return node
  115. def build_elements_hierarchy_for_cause(cause_type: str, hints: dict[str, Any]) -> dict[str, Any] | None:
  116. templates = load_hierarchy_templates()
  117. if cause_type not in templates:
  118. return None
  119. subtree = copy.deepcopy(templates[cause_type])
  120. return fill_hierarchy_node(subtree, (cause_type,), hints)
  121. def count_hierarchy_leaves(obj: Any) -> int:
  122. if obj is None:
  123. return 1
  124. if isinstance(obj, dict):
  125. if not obj:
  126. return 0
  127. return sum(count_hierarchy_leaves(v) for v in obj.values())
  128. return 0
  129. def flatten_hierarchy_for_preview(obj: Any, path: tuple[str, ...] = ()) -> list[dict[str, Any]]:
  130. """扁平行:供 case_elements_table 简要预览。"""
  131. rows: list[dict[str, Any]] = []
  132. if obj is None:
  133. rows.append({"path": list(path), "value": None})
  134. elif isinstance(obj, dict):
  135. for k, v in obj.items():
  136. rows.extend(flatten_hierarchy_for_preview(v, path + (k,)))
  137. else:
  138. rows.append({"path": list(path), "value": obj})
  139. return rows