mirror of
https://github.com/Horizontal42/Sort_YM.git
synced 2026-11-10 23:52:00 +03:00
render lyrics digest grouped by mood
This commit is contained in:
parent
8eae62fb63
commit
d0682f8341
2 changed files with 123 additions and 0 deletions
|
|
@ -439,6 +439,56 @@ def render_digest(
|
|||
return "\n".join(lines).rstrip() + "\n"
|
||||
|
||||
|
||||
def _lyrics_track_names(tracks_cache: dict[str, dict]) -> dict[str, str]:
|
||||
names: dict[str, str] = {}
|
||||
for t in tracks_cache.values():
|
||||
names[str(t["id"])] = f"{', '.join(t['artists'])} — {t['title']}"
|
||||
return names
|
||||
|
||||
|
||||
def render_lyrics_digest(tracks_cache: dict[str, dict], analysis: dict[str, dict]) -> str:
|
||||
names = _lyrics_track_names(tracks_cache)
|
||||
entries = [(tid, e) for tid, e in analysis.items() if "error" not in e]
|
||||
failed = len(analysis) - len(analyzed_entries(analysis))
|
||||
|
||||
lines = [
|
||||
"# Разбор текстов (RU-треки)",
|
||||
"",
|
||||
f"Разобрано треков: {len(entries)}. Группировка по основному настроению; "
|
||||
"цитата приводится только если сверена с текстом дословно.",
|
||||
"",
|
||||
]
|
||||
|
||||
by_mood: dict[str, list[tuple[str, dict]]] = {}
|
||||
for tid, entry in entries:
|
||||
by_mood.setdefault(entry["mood"][0], []).append((tid, entry))
|
||||
|
||||
for mood, items in sorted(by_mood.items(), key=lambda kv: (-len(kv[1]), kv[0])):
|
||||
lines.append(f"## {mood} ({len(items)})")
|
||||
lines.append("")
|
||||
for tid, entry in sorted(items, key=lambda kv: names.get(kv[0], kv[0])):
|
||||
lines.append(f"### {names.get(tid, f'трек {tid}')}")
|
||||
lines.append(
|
||||
f"- Настроение: {', '.join(entry['mood'])}; арка: {entry['emotional_arc']}; "
|
||||
f"ракурс: {entry['pov']}; тон: {entry['stance']}; речь: {entry['register']}; "
|
||||
f"конкретность: {entry['concreteness']}"
|
||||
)
|
||||
lines.append(f"- Темы: {', '.join(entry['themes'])}")
|
||||
if entry.get("key_line_verified"):
|
||||
lines.append(f"- Цитата: «{entry['key_line']}»")
|
||||
lines.append("")
|
||||
lines.append(entry["summary"])
|
||||
lines.append("")
|
||||
lines.append(f"Чем может цеплять: {entry['resonance']}")
|
||||
lines.append("")
|
||||
|
||||
if failed:
|
||||
lines.append(f"Не разобрано: {failed} (таймаут или сбой модели, повторный `analyze` попробует снова).")
|
||||
lines.append("")
|
||||
|
||||
return "\n".join(lines).rstrip() + "\n"
|
||||
|
||||
|
||||
def write_digest(text: str, out_dir: Path, filename: str = DIGEST_FILE) -> Path:
|
||||
out_dir.mkdir(parents=True, exist_ok=True)
|
||||
out_file = out_dir / filename
|
||||
|
|
|
|||
|
|
@ -330,3 +330,76 @@ def test_render_digest_with_analysis_includes_lyrics_section():
|
|||
text = digest.render_digest(rows, tracks_cache, {}, top_artists=10, top_albums=10, analysis=ANALYSIS)
|
||||
|
||||
assert "## Лирика (RU-подмножество)" in text
|
||||
|
||||
|
||||
LYRICS_TRACKS = {
|
||||
"100:10": {"id": 100, "album_id": 10, "title": "Тоска", "artists": ["Артист"]},
|
||||
"200:20": {"id": 200, "album_id": 20, "title": "Город", "artists": ["Другой"]},
|
||||
}
|
||||
|
||||
FULL_ANALYSIS = {
|
||||
"100": {
|
||||
"mood": ["melancholy", "longing"],
|
||||
"themes": ["city", "memory"],
|
||||
"emotional_arc": "descent",
|
||||
"pov": "confessional",
|
||||
"register": "conversational",
|
||||
"concreteness": "abstract",
|
||||
"stance": "sincere",
|
||||
"confidence": 0.9,
|
||||
"summary": "Герой бродит по городу.",
|
||||
"resonance": "Узнаваемое чувство одиночества.",
|
||||
"key_line": "А я иду, шагаю по Москве",
|
||||
"key_line_verified": True,
|
||||
},
|
||||
"200": {
|
||||
"mood": ["melancholy"],
|
||||
"themes": ["city"],
|
||||
"emotional_arc": "static",
|
||||
"pov": "narrative",
|
||||
"register": "poetic",
|
||||
"concreteness": "concrete",
|
||||
"stance": "ironic",
|
||||
"confidence": 0.8,
|
||||
"summary": "Наблюдение за городом.",
|
||||
"resonance": "Городская меланхолия.",
|
||||
"key_line": "Пересказ вместо цитаты",
|
||||
"key_line_verified": False,
|
||||
},
|
||||
"300": {"error": "TimeoutError: модель зависла"},
|
||||
}
|
||||
|
||||
|
||||
def test_render_lyrics_digest_lists_tracks_with_narrative():
|
||||
text = digest.render_lyrics_digest(LYRICS_TRACKS, FULL_ANALYSIS)
|
||||
|
||||
assert "Артист — Тоска" in text
|
||||
assert "Герой бродит по городу." in text
|
||||
assert "Узнаваемое чувство одиночества." in text
|
||||
assert "city, memory" in text
|
||||
|
||||
|
||||
def test_render_lyrics_digest_omits_unverified_key_line():
|
||||
text = digest.render_lyrics_digest(LYRICS_TRACKS, FULL_ANALYSIS)
|
||||
|
||||
assert "А я иду, шагаю по Москве" in text
|
||||
assert "Пересказ вместо цитаты" not in text, "непроверенная цитата - вероятный пересказ модели"
|
||||
|
||||
|
||||
def test_render_lyrics_digest_reports_failed_tracks_without_rendering_them():
|
||||
text = digest.render_lyrics_digest(LYRICS_TRACKS, FULL_ANALYSIS)
|
||||
|
||||
assert "TimeoutError" not in text
|
||||
assert "Не разобрано: 1" in text
|
||||
|
||||
|
||||
def test_render_lyrics_digest_groups_by_primary_mood():
|
||||
text = digest.render_lyrics_digest(LYRICS_TRACKS, FULL_ANALYSIS)
|
||||
|
||||
assert "## melancholy (2)" in text
|
||||
|
||||
|
||||
def test_render_lyrics_digest_handles_track_missing_from_cache():
|
||||
text = digest.render_lyrics_digest({}, {"100": FULL_ANALYSIS["100"]})
|
||||
|
||||
assert "трек 100" in text
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue