export_meta_schema.py 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. """导出元知识图谱 Schema 为前端展示用 JSON。
  2. 输出:
  3. output/meta_graph_schema.json # 完整 schema(含规划中节点)
  4. output/meta_graph_schema_display.json # 仅已启用节点/关系,适合前端展示
  5. 内容包括:
  6. - nodes: 节点/实体类型,含属性列表、部门、是否中心节点、是否建议节点、是否已启用
  7. - relations: 节点间连接关系,含源节点、目标节点、关系名、关联键、基数、说明、是否已启用
  8. """
  9. from __future__ import annotations
  10. import json
  11. import re
  12. import sys
  13. from datetime import date
  14. from pathlib import Path
  15. from step2_graph_building.meta.schema import ENTITIES, RELATIONS
  16. sys.stdout.reconfigure(encoding="utf-8")
  17. ROOT = Path(__file__).resolve().parents[1]
  18. OUT = ROOT / "output" / "meta_graph_schema.json"
  19. OUT_DISPLAY = ROOT / "output" / "meta_graph_schema_display.json"
  20. def split_attributes(desc: str) -> list[str]:
  21. """从 EntitySpec.desc 中提取属性名。
  22. desc 基本是“属性1 / 属性2 / ...”格式;括号内的斜杠不拆分。
  23. """
  24. if not desc:
  25. return []
  26. # 先保护括号内的“/”,例如 合同状况(线上/线下)、服务状态(服务中/历史)
  27. protected = re.sub(
  28. r"([((][^))]*)/([^))]*[))])",
  29. lambda m: m.group(0).replace("/", "__SLASH__"),
  30. desc,
  31. )
  32. parts = re.split(r"\s*/\s*|;|;", protected)
  33. result: list[str] = []
  34. for p in parts:
  35. p = p.strip().replace("__SLASH__", "/")
  36. if not p:
  37. continue
  38. # 去掉解释性括号,但保留括号内容作为属性说明
  39. result.append(re.sub(r"\s+", "", p))
  40. return result
  41. def build_payload() -> dict:
  42. active_names = {e.name for e in ENTITIES if e.active}
  43. nodes = []
  44. for e in ENTITIES:
  45. dept = [e.department] if isinstance(e.department, str) else list(e.department)
  46. nodes.append({
  47. "id": e.name,
  48. "name": e.name,
  49. "department": dept,
  50. "is_hub": e.is_hub,
  51. "suggested": e.suggested,
  52. "active": e.active,
  53. "attributes": split_attributes(e.desc),
  54. "description": e.desc,
  55. })
  56. relations = []
  57. for r in RELATIONS:
  58. relations.append({
  59. "id": f"{r.source}__{r.rel_type}__{r.target}",
  60. "source": r.source,
  61. "target": r.target,
  62. "type": r.rel_type,
  63. "key": r.key,
  64. "cardinality": r.cardinality,
  65. "description": r.desc,
  66. "active": r.source in active_names and r.target in active_names,
  67. })
  68. return {
  69. "meta": {
  70. "title": "申勤物业元知识图谱 Schema",
  71. "source": "src/step2_graph_building/meta/schema.py",
  72. "generated_at": date.today().isoformat(),
  73. "entity_count": len(nodes),
  74. "active_entity_count": sum(1 for n in nodes if n["active"]),
  75. "relation_count": len(relations),
  76. "active_relation_count": sum(1 for r in relations if r["active"]),
  77. },
  78. "nodes": nodes,
  79. "relations": relations,
  80. }
  81. def main() -> None:
  82. from step2_graph_building.meta.production import load_production_schema, export_schema
  83. production = load_production_schema()
  84. if production is not None:
  85. export_schema(production, OUT.parent)
  86. print("已重新导出当前生产元图谱;没有使用历史固定 Schema 覆盖。")
  87. return
  88. payload = build_payload()
  89. OUT.parent.mkdir(parents=True, exist_ok=True)
  90. OUT.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8")
  91. display_nodes = [n for n in payload["nodes"] if n["active"]]
  92. display_relations = [r for r in payload["relations"] if r["active"]]
  93. display = {
  94. "meta": {
  95. **payload["meta"],
  96. "title": "申勤物业元知识图谱 Schema(仅已启用节点/关系)",
  97. "entity_count": len(display_nodes),
  98. "active_entity_count": len(display_nodes),
  99. "relation_count": len(display_relations),
  100. "active_relation_count": len(display_relations),
  101. },
  102. "nodes": display_nodes,
  103. "relations": display_relations,
  104. }
  105. OUT_DISPLAY.write_text(json.dumps(display, ensure_ascii=False, indent=2), encoding="utf-8")
  106. print("完整 schema:", OUT)
  107. print(f" 节点: {payload['meta']['entity_count']}(启用 {payload['meta']['active_entity_count']})")
  108. print(f" 关系: {payload['meta']['relation_count']}(启用 {payload['meta']['active_relation_count']})")
  109. print("展示版 schema:", OUT_DISPLAY)
  110. print(f" 节点: {len(display_nodes)}")
  111. print(f" 关系: {len(display_relations)}")
  112. if __name__ == "__main__":
  113. main()