Files
CareerNexus/tests/test_tkg.py
T
RoyirinRosmontis a10442d78c CareerNexus 职联:岗位能力图谱构建与动态演化分析平台(挑战杯 XH-202621)
- 全链路:多源JD接入/清洗交叉验证/结构化抽取(星火+离线双通道)/时序知识图谱/新岗位发现/演化分析/人岗匹配/学习路径
- WebUI 五视图:驾驶舱/图谱工作台/岗位演化/匹配(单人+批量)/学习路径
- 设置中心(config.json)/Token鉴权/SQLite状态库/语义向量通道(BGE-M3本地或联网API)
- 71 个 pytest 用例;数据目录仅保留结构(.gitkeep),数据由 scripts/generate_dataset.py 复现
2026-08-03 13:15:35 +08:00

79 lines
3.8 KiB
Python

"""时序图谱测试:时间点切片、演化追加不覆盖、持久化往返。"""
from __future__ import annotations
from app.graph.tkg import TemporalKG, quarter_of, to_window
from app.models import ChangeEvent, GraphObservation, JobDefinition
def _kg() -> TemporalKG:
kg = TemporalKG()
kg.add_job(JobDefinition(job_id="job_T", title="测试工程师"))
return kg
def test_quarter_and_window_conversion():
from datetime import date
assert quarter_of(date(2022, 2, 15)) == "2022Q1"
assert quarter_of(date(2022, 11, 1)) == "2022Q4"
assert to_window("2022-05-01") == "2022Q2"
assert to_window("2022Q3") == "2022Q3"
def test_snapshot_time_slicing():
"""任意时间点切片:只看到该时刻已生效且未退出的技能。"""
kg = _kg()
kg.observe(GraphObservation(job_id="job_T", skill="Java", importance="required",
weight=1.0, window="2020Q1", source_ids=["s1"]))
kg.observe(GraphObservation(job_id="job_T", skill="Spring Cloud", importance="required",
weight=1.0, window="2023Q1", source_ids=["s2"]))
snap_2020 = kg.snapshot("job_T", "2020-06-01")
assert {s["skill"] for s in snap_2020["skills"]} == {"Java"}
snap_2024 = kg.snapshot("job_T", "2024-01-01")
assert {s["skill"] for s in snap_2024["skills"]} == {"Java", "Spring Cloud"}
def test_close_interval_removes_from_snapshot():
kg = _kg()
kg.observe(GraphObservation(job_id="job_T", skill="Struts", importance="preferred",
weight=1.0, window="2019Q1", source_ids=["s1"]))
kg.close_interval("job_T", "Struts", "2022Q1")
assert {s["skill"] for s in kg.snapshot("job_T", "2021-01-01")["skills"]} == {"Struts"}
assert kg.snapshot("job_T", "2023-01-01")["skills"] == []
def test_evolution_appends_not_overwrites():
"""同一技能退出后重新出现:新区间追加,旧区间保留(历史不覆盖)。"""
kg = _kg()
obs = GraphObservation(job_id="job_T", skill="Kafka", importance="required",
weight=1.0, window="2020Q1", source_ids=["s1"])
kg.observe(obs)
kg.close_interval("job_T", "Kafka", "2021Q1")
kg.observe(GraphObservation(job_id="job_T", skill="Kafka", importance="required",
weight=0.8, window="2023Q1", source_ids=["s2"]))
intervals = kg.g["job_T"]["Kafka"]["intervals"]
assert intervals == [["2020Q1", "2021Q1"], ["2023Q1", None]]
assert kg.snapshot("job_T", "2020-06-01")["skills"] # 旧区间仍可切片
assert kg.snapshot("job_T", "2022-01-01")["skills"] == [] # 退出期间为空
assert kg.snapshot("job_T", "2024-01-01")["skills"] # 重现后恢复
# 事件流只追加
kg.add_event(ChangeEvent(event_id="e1", job_id="job_T", skill="Kafka",
change_type="退出", window="2021Q1", description="x"))
kg.add_event(ChangeEvent(event_id="e2", job_id="job_T", skill="Kafka",
change_type="新增", window="2023Q1", description="y"))
assert [e.event_id for e in kg.events_of("job_T")] == ["e1", "e2"]
def test_save_load_roundtrip(tmp_path):
kg = _kg()
kg.observe(GraphObservation(job_id="job_T", skill="Java", importance="required",
weight=1.0, window="2020Q1", source_ids=["s1"]))
kg.add_event(ChangeEvent(event_id="e1", job_id="job_T", skill="Java",
change_type="新增", window="2020Q1", description="d"))
path = tmp_path / "g.json"
kg.save(path)
kg2 = TemporalKG.load(path)
assert kg2.stats()["edges"] == 1
assert kg2.jobs["job_T"].title == "测试工程师"
assert kg2.snapshot("job_T", "2020-06-01")["skills"][0]["skill"] == "Java"
assert kg2.events_of("job_T")[0].event_id == "e1"