Files
gitea 2dbd3410e9 Zbiorcza tabela dat z technik (LOG-14)
engine/timeline.py: scala w jedną posortowaną oś czasu (technique | significator |
start | exact | end):
- profekcje roczne (LOG-10) — Władca Roku per rok życia,
- Solar Return (LOG-12) — moment powrotu Słońca,
- dyrekcje solar-arc — daty dokładnych aspektów kierowanych planet do punktów
  natalnych (klucz Naiboda 0°59'08"/rok, konfigurowalny; okno orbowe ±1 rok).
Endpoint /chart/timeline (zakres from_date..to_date, wybór technik).

Walidacja:
- profekcje spójne z tabelą notes3; dyrekcja Sun koniunkcja MC (łuk 312°) słusznie
  poza życiem; oś posortowana po dacie dokładnej; wiek = łuk/klucz spójny z datą.
- E2E: dla 2025-2026 zwraca 19 zdarzeń (m.in. Władca Roku 42 Saturn 30.04.2026,
  Solar Return, dyr. Venus koniunkcja North Node 27.05.2025). 66 testów przechodzi.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 22:33:15 +02:00

72 lines
2.6 KiB
Python

"""Zbiorcza oś czasu z technik (LOG-14)."""
import datetime as dt
from app.engine.timeline import (
NAIBOD_KEY,
build_timeline,
profection_events,
solar_arc_directions,
)
BIRTH = dt.datetime(1984, 4, 30, 7, 35, tzinfo=dt.timezone.utc)
# natalne długości (z horoskopu referencyjnego)
NATAL = {
"Asc": 112.18, "MC": 352.59, "Sun": 40.14, "Moon": 30.55, "Mercury": 27.38,
"Venus": 27.68, "Mars": 234.54, "Jupiter": 282.96, "Saturn": 223.31,
"Uranus": 252.81, "Neptune": 271.22, "Pluto": 210.48,
}
def _win(y0, y1):
return (dt.datetime(y0, 1, 1, tzinfo=dt.timezone.utc),
dt.datetime(y1, 12, 31, tzinfo=dt.timezone.utc))
def test_profection_events_in_window():
lo, hi = _win(2024, 2026)
rows = profection_events(NATAL["Asc"], BIRTH, lo, hi)
# rok profekcyjny wiek 42 zaczyna się 30.04.2026 -> Asc Capricorn
ages = [r["significator"] for r in rows]
assert any("Asc Capricorn" in a and "wiek 42" in a for a in ages)
def test_solar_arc_exact_matches_arc_over_key():
lo, hi = _win(2020, 2030)
rows = solar_arc_directions(NATAL, BIRTH, lo, hi, key=NAIBOD_KEY)
assert rows, "brak dyrekcji w oknie"
# dla każdej dyrekcji: łuk = (wiek * klucz), a data = urodziny + wiek -> spójne
for r in rows[:5]:
exact = dt.date.fromisoformat(r["exact"])
age_years = (dt.datetime(exact.year, exact.month, exact.day, tzinfo=dt.timezone.utc)
- BIRTH).days / 365.2422
assert 36 <= age_years <= 47 # okno 2020-2030 = wiek ~36-46
assert r["technique"] == "solar_arc" and "dyr." in r["significator"]
def test_directed_sun_conjunct_natal_mc_date():
# Sun natal 40.14 -> MC natal 352.59: łuk koniunkcji = (352.59-40.14)%360 = 312.45
# to > lifespan przy Naibod (~317 lat) -> NIE powinno być w oknie życia
lo, hi = _win(1984, 2084)
rows = solar_arc_directions(NATAL, BIRTH, lo, hi)
sun_mc = [r for r in rows if r["significator"] == "dyr. Sun koniunkcja MC"]
assert not sun_mc # łuk 312° = poza życiem
def test_build_timeline_sorted_and_merged():
events = build_timeline(_FakeEngine(), _FakeNatal(), NATAL,
"2025-01-01", "2027-01-01",
techniques=["profection", "solar_arc"])
assert events
dates = [e["exact"] for e in events]
assert dates == sorted(dates) # posortowane po dacie dokładnej
techs = {e["technique"] for e in events}
assert "profection" in techs and "solar_arc" in techs
class _FakeNatal:
when_utc = BIRTH
class _FakeEngine:
"""Silnik-atrapa — build_timeline z solar_return by go użył, tu go pomijamy."""