Files
portal/knowledge/aleksandra/brain/cards.py
T

73 lines
2.6 KiB
Python

"""Парсер карточки знаний Александры (frontmatter + тело) и сборка справки по дереву."""
from dataclasses import dataclass, field
from pathlib import Path
import re
@dataclass
class Card:
nisha: str
body: str
path: str
roditel: str | None = None
sinonimy: list[str] = field(default_factory=list)
istochnik: str | None = None
obnovleno: str | None = None
trendy_obnovleno: str | None = None
def _parse_frontmatter(text: str):
"""Вернуть (dict метаданных, тело). Поддержка скаляров и списков [a, b]."""
m = re.match(r"^---\s*\n(.*?)\n---\s*\n?(.*)$", text, re.S)
if not m:
if text.lstrip().startswith("---"):
raise ValueError("битый frontmatter: есть открывающий '---', но нет закрывающего")
return {}, text.strip()
meta_raw, body = m.group(1), m.group(2)
meta = {}
for line in meta_raw.splitlines():
if not line.strip() or ":" not in line:
continue
key, _, val = line.partition(":")
key, val = key.strip(), val.strip()
if val.startswith("[") and val.endswith("]"):
items = [x.strip() for x in val[1:-1].split(",") if x.strip()]
meta[key] = items
else:
meta[key] = val
return meta, body.strip()
def parse_card(path) -> Card:
p = Path(path)
meta, body = _parse_frontmatter(p.read_text(encoding="utf-8"))
roditel = meta.get("roditel")
if roditel in ("", "", "-"):
roditel = None
return Card(
nisha=meta.get("nisha", p.stem),
body=body,
path=str(p),
roditel=roditel,
sinonimy=meta.get("sinonimy", []) or [],
istochnik=meta.get("istochnik"),
obnovleno=meta.get("obnovleno"),
trendy_obnovleno=meta.get("trendy_obnovleno"),
)
def assemble_brief(card: Card, cards_by_name: dict[str, Card]) -> str:
"""Склейка справки: общее родителя (вверх по цепочке) + тело ниши.
Цепочку ограничиваем 4 уровнями от зацикливания."""
chain = []
cur, seen, depth = card, set(), 0
while cur is not None and depth < 4:
chain.append(cur)
if cur.roditel in seen or cur.roditel is None:
break
seen.add(cur.roditel)
cur = cards_by_name.get(cur.roditel)
depth += 1
# от корня (домен) к листу (ниша)
return "\n\n".join(c.body for c in reversed(chain)).strip()