第 1 卷第 1 章:1–9 年
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
# 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]
|
||||
for tag, kind in lg.ID_FIELDS.items():
|
||||
for i in e.ids(tag):
|
||||
if kind == "figure":
|
||||
name = world.figure_name(i)
|
||||
elif kind == "site":
|
||||
name = world.site_name(i)
|
||||
elif kind == "entity":
|
||||
name = world.entity_name(i)
|
||||
else:
|
||||
name = ""
|
||||
if name:
|
||||
chunks.append(f"{tag}={name}")
|
||||
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:
|
||||
seen: set[int] = set()
|
||||
for tag, kind in lg.ID_FIELDS.items():
|
||||
if kind != "figure":
|
||||
continue
|
||||
for i in e.ids(tag):
|
||||
seen.add(i)
|
||||
involvement.update(seen)
|
||||
|
||||
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]
|
||||
Reference in New Issue
Block a user