feat: 日报事件多新闻源入库(news_event.sources 列)

- ExtractedEvent 增加 sources 字段(主源居首去重,旧产物兜底 [source_id])
- extract_event(_async) 增加 sources 参数;run_event_extraction 读取
  deduped uniques 的 sources 透传
- reporter _load_events_from_dir 读取 sources → EventRow.sources
- news_event 建表加 sources TEXT 列;init_schema 幂等迁移旧表(捕获
  Duplicate column);save_report INSERT 写 JSON 多源
- 新增测试:ExtractedEvent/EventRow.sources 兜底与保序、_load_events_from_dir 多源
This commit is contained in:
2026-08-12 12:56:04 +08:00
parent 182f788857
commit 794ce6c853
10 changed files with 129 additions and 7 deletions
+25
View File
@@ -121,6 +121,31 @@ def test_event_types_constant_includes_common() -> None:
assert must in EVENT_TYPES
# --------------------------------------------------------------------------- #
# ExtractedEvent.sources 多源字段
# --------------------------------------------------------------------------- #
def test_extracted_event_sources_defaults_to_main_source() -> None:
"""未提供 sources 时兜底为 [source_id](兼容旧产物)。"""
ev = ExtractedEvent(
source_id="cls", url="https://x/1", url_hash="h1", title="t",
event=EventExtraction(sentiment="positive", importance=3, event_type="重大合同"),
provider="deepseek", model="m",
)
assert ev.sources == ["cls"]
def test_extracted_event_sources_keeps_main_first_and_dedup() -> None:
"""sources 保主源居首、去重保序。"""
ev = ExtractedEvent(
source_id="cls", url="https://x/1", url_hash="h1", title="t",
sources=["sina", "cls", "eastmoney", "sina"],
event=EventExtraction(sentiment="neutral", importance=2, event_type="其他"),
provider="deepseek", model="m",
)
assert ev.sources == ["cls", "sina", "eastmoney"]
# --------------------------------------------------------------------------- #
# JSON 提取与解析
# --------------------------------------------------------------------------- #
+28
View File
@@ -255,3 +255,31 @@ class TestCollectNewsEventsLookback:
assert "无时间戳" in titles
assert "窗口外旧闻" not in titles
assert "公告排除" not in titles
class TestLoadEventsSources:
"""_load_events_from_dir 读取多源(sources)字段。"""
def test_loads_sources_with_fallback(self, monkeypatch, tmp_path) -> None:
import json as _json
import scheduler.reporter as rep
ev_dir = tmp_path / "data" / "events" / "20260812"
ev_dir.mkdir(parents=True)
(ev_dir / "aaa.json").write_text(_json.dumps({
"source_id": "cls", "sources": ["cls", "sina", "eastmoney"],
"title": "多源新闻", "url": "https://x/1", "event": {},
}, ensure_ascii=False), encoding="utf-8")
(ev_dir / "bbb.json").write_text(_json.dumps({
"source_id": "cls", "title": "旧产物无 sources", "url": "https://x/2",
"event": {},
}, ensure_ascii=False), encoding="utf-8")
monkeypatch.chdir(tmp_path)
events = rep._load_events_from_dir("20260812")
by_url = {e["url"]: e for e in events}
# 新产物:多源完整透传
assert by_url["https://x/1"]["sources"] == ["cls", "sina", "eastmoney"]
# 旧产物:兜底 [source_id]
assert by_url["https://x/2"]["sources"] == ["cls"]
+7
View File
@@ -20,9 +20,16 @@ class TestEventRow:
ev = EventRow(
section="intl", rank=2, importance=4, event_type="地缘政治",
title="t", summary="s", sentiment="negative", source="ForexLive",
sources=["ForexLive", "新浪财经"],
url="https://x.com/1",
)
assert ev.sentiment == "negative"
assert ev.sources == ["ForexLive", "新浪财经"]
def test_sources_optional(self) -> None:
"""sources 为可选项(旧数据无多源记录)。"""
ev = EventRow(section="news", rank=1, title="t")
assert ev.sources is None
def test_missing_title_raises(self) -> None:
with pytest.raises(ValidationError):