Files
Chen Yi e0073e90bd 第 1 卷(人物主线):第 1–3 集 + 人物表
主角线:Guspu Frillyknots / Ral Fastenhatchets / Alath Blottedmine
由线索挖掘器自动选定,按因果链切集,经 5 维度自评闸门(≥12 分)后入库。
2026-10-05 19:28:49 +08:00

93 lines
3.7 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# pyright: reportMissingImports=false, reportAttributeAccessIssue=false
# 说明:LSP 的 Python 环境看不到本项目包(未安装进它的 site-packages),
# 会把「包内互相 import」误报为缺失模块。运行时导入已由执行验证通过。
"""生成迷你年表与人物索引(随成品入库的两种辅助文件)。"""
from __future__ import annotations
from collections import Counter
from pathlib import Path
from dfannals import legends as lg
from dfannals import score
from dfannals.legends import Event, World
# 评分标准统一在 dfannals/score.py,年表与切章共用同一套。
significance = score.significance
category = score.category
def event_line(world: World, e: Event) -> str:
"""把一条事件压成一行可读文字。"""
chunks: list[str] = [e.type, *world.render_refs(e)]
return f"- **{e.year}** [{category(e)}] " + " · ".join(chunks)
def build_timeline(world: World, min_score: int = 70, per_decade: int = 14) -> str:
"""按十年分组的重要事件年表。"""
prom = score.prominence(world)
lo, hi = world.event_years()
important = [e for e in world.events if significance(e, prom) >= min_score]
buckets: dict[int, list[Event]] = {}
for e in important:
buckets.setdefault((e.year // 10) * 10, []).append(e)
lines = [
f"# {world.name} · 年表",
"",
f"- 历史跨度:{lo}–{hi} 年" if hi else "- 历史跨度:未知",
f"- 事件总数:{len(world.events)},其中重要事件 {len(important)}",
f"- 历史人物 {len(world.figures)} 位 · 文明与组织 {len(world.entities)} 个 · 地点 {len(world.sites)} 处",
"",
(
"> 本表由 legends 导出数据自动生成,只收录叙事权重较高的事件;"
"完整数据留在本地,不入库。"
),
"",
]
for decade in sorted(buckets):
evs = sorted(buckets[decade], key=lambda e: (e.year, e.seconds72, e.id))
shown = sorted(evs, key=lambda e: -significance(e, prom))[:per_decade]
shown.sort(key=lambda e: (e.year, e.seconds72, e.id))
lines.append(f"## {decade}s")
lines.append("")
lines.extend(event_line(world, e) for e in shown)
if len(evs) > len(shown):
lines.append(f"- …另有 {len(evs) - len(shown)} 条次要事件未列")
lines.append("")
return "\n".join(lines)
def build_figures(world: World, top: int = 120) -> str:
"""人物索引:按参与事件数排序,给出身份与生卒。"""
involvement: Counter[int] = Counter()
for e in world.events:
involvement.update(e.figure_ids())
lines = [
f"# {world.name} · 人物索引",
"",
f"共 {len(world.figures)} 位历史人物,按在史料中出现的次数排序(前 {top} 位)。",
"",
"| 人物 | 种族 | 生卒 | 职业 | 事件数 |",
"|---|---|---|---|---|",
]
ranked = sorted(world.figures.values(), key=lambda f: -involvement.get(f.id, 0))
for fig in ranked[:top]:
name = lg.pretty(fig.name) or f"HF#{fig.id}"
prof = fig.profession or "—"
lines.append(
f"| {name} | {fig.race or '—'} | {fig.alive_span} | {prof} | {involvement.get(fig.id, 0)} |"
)
lines.append("")
return "\n".join(lines)
def write_outputs(world: World, out_dir: Path, min_score: int = 40) -> list[Path]:
out_dir.mkdir(parents=True, exist_ok=True)
timeline = out_dir / "timeline.md"
figures = out_dir / "figures.md"
timeline.write_text(build_timeline(world, min_score=min_score), encoding="utf-8")
figures.write_text(build_figures(world), encoding="utf-8")
return [timeline, figures]