Spięcie osi czasu z bazą interpretacji i widokiem (LOG-14, 1B->2B)

Realizuje przepływ 1B->2B z notes2: predykcyjne sygnifikatory (z datami)
dopasowane do interpretacji z bazy.

Logika:
- timeline.py: zdarzenia niosą strukturę (directed/aspect/target dla dyrekcji,
  lord/sign dla profekcji) do budowy tokenów.
- significators.interpret_events + _event_tokens: z każdego zdarzenia buduje
  tokeny bazy (dyrekcja: [planeta][aspekt][cel]; profekcja: [władca][znak]) i
  dopina interpretacje reużywając _facet_samples (AND tokenów, dedup, rozwinięcie).
- /chart/timeline: flaga interpret=true.

Prezentacja:
- nowa strona /timeline "Kalendarz": formularz (urodzenie + zakres dat) -> oś
  czasu z technikami, datami i interpretacjami; nawigacja + wspólne now.js.

Walidacja E2E na realnym main_base.xlsx (2025-2026):
- dyr. Saturn kwadratura MC -> 50 interpret. ("...5th house" -> "abortion/miscarriage")
- Władca Roku Saturn (wiek 42) -> 63; strona renderuje badge dat/technik.
71 testów przechodzi (nowy test_timeline_interpret).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-11 12:31:50 +02:00
parent 97ad21d2e4
commit c4a181b810
9 changed files with 247 additions and 9 deletions
+49
View File
@@ -195,3 +195,52 @@ def build_report(
"facets": facets,
})
return {"provider": provider, "objects": items}
def _event_tokens(event: dict) -> list[str]:
"""Tokeny bazy dla zdarzenia osi czasu (spięcie 1B→2B). Pierwszy = planeta."""
from app.engine.aspects import DB_TOKEN
if event.get("technique") == "solar_arc":
p = PLANET_ABBR.get(event.get("directed"))
a = DB_TOKEN.get(event.get("aspect"))
q = PLANET_ABBR.get(event.get("target")) # None dla Asc/MC (nie ma tokenu)
toks = []
if p:
toks.append("[" + p)
if a:
toks.append(a)
if q:
toks.append("[" + q)
return toks if p else []
if event.get("technique") == "profection":
lord = PLANET_ABBR.get(event.get("lord"))
sign = SIGN_TO_ABBR.get(event.get("sign"))
toks = []
if lord:
toks.append("[" + lord)
if sign:
toks.append("[" + sign)
return toks if lord else []
return []
def interpret_events(events: list[dict], data: DataSource, limit: int = 4,
per_object_limit: int = 5000) -> list[dict]:
"""Dopina interpretacje z bazy do zdarzeń osi czasu (predykcyjne 1B → 2B).
Wyszukuje po tokenie planety zdarzenia i zawęża do wszystkich tokenów
(aspekt/druga planeta lub znak), z odsiewaniem szumu i duplikatów.
"""
for ev in events:
tokens = _event_tokens(ev)
if not tokens:
continue
raw = data.search(
key="significator", value=tokens[0], exact=False, limit=per_object_limit,
fields=["significator", "actioneffect", "topicresult", "bodypart"],
)
samples = _facet_samples(raw.get("rows", []), tokens)
ev["interpretations"] = samples[:limit]
ev["interpretations_count"] = len(samples)
return events