| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162 |
- #!/usr/bin/env python3
- # -*- coding: utf-8 -*-
- import json
- import tempfile
- from data_modules.config import DataModulesConfig
- from data_modules.index_manager import IndexManager, ChapterReadingPowerMeta
- from status_reporter import StatusReporter
- def _write_state(project_root, state: dict):
- webnovel_dir = project_root / ".webnovel"
- webnovel_dir.mkdir(parents=True, exist_ok=True)
- (webnovel_dir / "state.json").write_text(
- json.dumps(state, ensure_ascii=False, indent=2),
- encoding="utf-8",
- )
- def test_foreshadowing_analysis_uses_real_chapters_and_handles_missing_data():
- with tempfile.TemporaryDirectory() as tmpdir:
- project_root = DataModulesConfig.from_project_root(tmpdir).project_root
- state = {
- "progress": {"current_chapter": 120, "total_words": 360000},
- "plot_threads": {
- "foreshadowing": [
- {
- "content": "林家宝库铭文的秘密",
- "status": "未回收",
- "tier": "核心",
- "planted_chapter": 20,
- "target_chapter": 100,
- },
- {
- "content": "神秘玉佩来历",
- "status": "待回收",
- "tier": "支线",
- "added_chapter": 50,
- "target": 150,
- },
- {
- "content": "旧日誓言",
- "status": "未回收",
- "tier": "装饰",
- },
- {
- "content": "已完成伏笔",
- "status": "已回收",
- "planted_chapter": 10,
- "target_chapter": 20,
- },
- ]
- },
- }
- _write_state(project_root, state)
- reporter = StatusReporter(str(project_root))
- assert reporter.load_state() is True
- foreshadowing = reporter.analyze_foreshadowing()
- assert len(foreshadowing) == 3
- records = {item["content"]: item for item in foreshadowing}
- assert records["林家宝库铭文的秘密"]["planted_chapter"] == 20
- assert records["林家宝库铭文的秘密"]["elapsed"] == 100
- assert records["林家宝库铭文的秘密"]["status"] == "🔴 已超期"
- assert records["神秘玉佩来历"]["planted_chapter"] == 50
- assert records["神秘玉佩来历"]["target_chapter"] == 150
- assert records["神秘玉佩来历"]["status"] in {"🟡 轻度超时", "🟢 正常"}
- assert records["旧日誓言"]["planted_chapter"] is None
- assert records["旧日誓言"]["status"] == "⚪ 数据不足"
- urgency = reporter.analyze_foreshadowing_urgency()
- urgency_by_content = {item["content"]: item for item in urgency}
- assert urgency_by_content["林家宝库铭文的秘密"]["urgency"] is not None
- assert urgency_by_content["林家宝库铭文的秘密"]["status"] == "🔴 已超期"
- assert urgency_by_content["旧日誓言"]["urgency"] is None
- assert urgency_by_content["旧日誓言"]["status"] == "⚪ 数据不足"
- def test_pacing_analysis_prefers_real_coolpoint_metadata_over_estimation():
- with tempfile.TemporaryDirectory() as tmpdir:
- config = DataModulesConfig.from_project_root(tmpdir)
- config.ensure_dirs()
- project_root = config.project_root
- state = {
- "progress": {"current_chapter": 3, "total_words": 12000},
- "chapter_meta": {
- "0003": {
- "hook": "下章有变",
- "coolpoint_patterns": ["身份掉马", "反派翻车"],
- }
- },
- }
- _write_state(project_root, state)
- idx = IndexManager(config)
- idx.save_chapter_reading_power(
- ChapterReadingPowerMeta(
- chapter=1,
- hook_type="渴望钩",
- hook_strength="strong",
- coolpoint_patterns=["打脸权威", "身份掉马"],
- )
- )
- idx.save_chapter_reading_power(
- ChapterReadingPowerMeta(
- chapter=2,
- hook_type="悬念钩",
- hook_strength="medium",
- coolpoint_patterns=["身份掉马"],
- )
- )
- reporter = StatusReporter(str(project_root))
- assert reporter.load_state() is True
- reporter.chapters_data = [
- {"chapter": 1, "word_count": 4000, "cool_point": "", "dominant": "", "characters": []},
- {"chapter": 2, "word_count": 3000, "cool_point": "", "dominant": "", "characters": []},
- {"chapter": 3, "word_count": 5000, "cool_point": "", "dominant": "", "characters": []},
- ]
- segments = reporter.analyze_pacing()
- assert len(segments) == 1
- seg = segments[0]
- assert seg["cool_points"] == 5
- assert round(seg["words_per_point"], 2) == 2400.00
- assert seg["missing_chapters"] == 0
- assert seg["dominant_source"] == "chapter_reading_power"
- def test_pacing_analysis_marks_missing_data_instead_of_assuming_one_point_per_chapter():
- with tempfile.TemporaryDirectory() as tmpdir:
- config = DataModulesConfig.from_project_root(tmpdir)
- config.ensure_dirs()
- project_root = config.project_root
- state = {
- "progress": {"current_chapter": 1, "total_words": 2000},
- "chapter_meta": {},
- }
- _write_state(project_root, state)
- reporter = StatusReporter(str(project_root))
- assert reporter.load_state() is True
- reporter.chapters_data = [
- {"chapter": 1, "word_count": 2000, "cool_point": "", "dominant": "", "characters": []}
- ]
- seg = reporter.analyze_pacing()[0]
- assert seg["cool_points"] == 0
- assert seg["words_per_point"] is None
- assert seg["rating"] == "数据不足"
- assert seg["missing_chapters"] == 1
|