Files
life-echo/api/app/features/memoir/chapter_markdown_compose.py

68 lines
2.5 KiB
Python
Raw Normal View History

2026-03-20 15:15:35 +08:00
"""
chapter_story_links 顺序将各 story 正文物化为单一 markdown LLM
保留 story asset:// 引用不变
章节级 canonical仅正文拼接故事间用 ---故事标题仅存 stories.title
PDF 导出可单独物化## 标题 + 正文」版本。
2026-03-20 15:15:35 +08:00
"""
from typing import Any
from app.features.memoir.markdown_sanitize import sanitize_story_for_chapter_compose
2026-03-20 15:15:35 +08:00
def _gather_title_body_pairs(chapter: Any) -> list[tuple[str, str]]:
2026-03-20 15:15:35 +08:00
links = sorted(
list(getattr(chapter, "story_links", None) or []),
key=lambda x: getattr(x, "order_index", 0),
)
pairs: list[tuple[str, str]] = []
for link in links:
st = getattr(link, "story", None)
if st is None:
continue
title = (getattr(st, "title", None) or "").strip()
body = (getattr(st, "canonical_markdown", None) or "").strip()
pairs.append((title, body))
return pairs
def compose_ordered_stories_to_markdown(ordered: list[tuple[str, str]]) -> str:
"""
:param ordered: (story_title, canonical_markdown) 已按阅读顺序排好title 仅用于清洗去重
:return: 章节级 markdown仅各故事正文非空块之间用 \\n\\n---\\n\\n 分隔
"""
bodies: list[str] = []
for title, md in ordered:
raw = (md or "").strip()
if not raw:
continue
cleaned = sanitize_story_for_chapter_compose(raw, title)
if cleaned:
bodies.append(cleaned)
return "\n\n---\n\n".join(bodies)
def compose_ordered_stories_to_pdf_markdown(ordered: list[tuple[str, str]]) -> str:
"""PDF每故事 ## 标题 + 正文,块间 ---(标题来自元数据,不写回章节 canonical"""
parts: list[str] = []
for title, md in ordered:
t = (title or "").strip() or "故事"
raw = (md or "").strip()
if not raw:
continue
body = sanitize_story_for_chapter_compose(raw, title)
if not body:
continue
parts.append(f"## {t}\n\n{body}")
return "\n\n---\n\n".join(parts)
def materialize_chapter_markdown_from_loaded_chapter(chapter: Any) -> str:
"""要求 chapter.story_links 已 eager-load且各 link.story 可用。"""
return compose_ordered_stories_to_markdown(_gather_title_body_pairs(chapter))
def materialize_chapter_pdf_markdown_from_loaded_chapter(chapter: Any) -> str:
"""PDF 专用:含每段 ## 故事名。"""
return compose_ordered_stories_to_pdf_markdown(_gather_title_body_pairs(chapter))