Files
astrololo/services/logic
gitea aec3f84331
Testy / Testy warstwy logicznej (silnik) (pull_request) Successful in 10m29s
Testy / Testy warstwy prezentacji (dostęp do baz) (pull_request) Successful in 9m28s
Testy / Testy warstwy bazodanowej (ochrona baz) (pull_request) Successful in 9m28s
Testy / Build obrazu silnika B (swisseph) (pull_request) Successful in 18s
Testy / Kontrola składni wszystkich warstw (pull_request) Successful in 9s
build-render / build (push) Successful in 5m34s
build / build (push) Successful in 39s
Testy / Testy warstwy logicznej (silnik) (push) Successful in 10m46s
Testy / Testy warstwy prezentacji (dostęp do baz) (push) Successful in 9m31s
Testy / Testy warstwy bazodanowej (ochrona baz) (push) Successful in 9m27s
Testy / Build obrazu silnika B (swisseph) (push) Successful in 19s
Testy / Kontrola składni wszystkich warstw (push) Successful in 11s
feat(domy): topocentric zgodny co do zera — wybór gałęzi liczony, nie zgadywany
Topocentric był wycofany z powodu rozjazdu przy |φ| ≈ 89,9°. Ta diagnoza była
BŁĘDNA: opierała się wyłącznie na zestawie brzegowym, który próbkuje tylko wybrane
szerokości. Przemiał losowy pokazał prawdziwą skalę — błędy od ~70° w górę,
do 50% przypadków blisko biegunów, 6,5% całości. Nie dwa przypadki brzegowe.

Przyczyną nie była jednak konstrukcja, tylko wybór gałęzi: dwa koła wielkie
przecinają się w dwóch punktach antypodycznych. Kolejno zawiodły reguły „po której
stronie MC", „w łuku kwadrantu", „wschodnia połowa horyzontu" i śledzenie ciągłości
krokami (to ostatnie maskuje własną patologię — po korekcie do bliższej gałęzi
zmierzony skok ZAWSZE wychodzi ≤ 90°, więc detektor nigdy się nie zapala).

Wszystkie te reguły rozstrzygają lokalnie, a przy dużych szerokościach kolejność
domów potrafi się odwrócić: przy φ = −79,55° MC wypada na 306,8°, a dom 11 na
291,4°. To jest poprawne — wyrocznia zwraca to samo.

Rozwiązanie: nie wybierać w ogóle. Iloczyn wektorowy zenitu z biegunem ekliptyki
jest ciągłą funkcją parametru rodziny i sam niesie właściwy zwrot; dwuznaczność
wprowadza dopiero atan2. Zostajemy w wektorach, znak ustalamy raz — kotwicząc
rodzinę na MC górującym.

Wynik: 0,000000000° na 240 000 porównań, cała dziedzina, bez iteracji i bez
zawężania szerokości. Żadna granica dziedziny nie jest tu potrzebna, więc żadnej
nie udajemy — test pilnuje, że topocentric liczy się wszędzie i nie fallbackuje.

Morał do frameworka: zestaw brzegowy mówi, CZY system się psuje; dopiero przemiał
losowy mówi JAK BARDZO. Wniosek o skali wyciągnięty z samych brzegów był tu
zaniżony o trzy rzędy wielkości. Odnotowane w tests/oracle/README.md.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 22:24:40 +02:00
..

Warstwa logiczna (logic)

Niezależna usługa pośrednicząca. W górę udostępnia API dla prezentacji, w dół woła warstwę bazodanową. Tu żyją reguły biznesowe — nie w prezentacji i nie w bazie.

API

  • POST /api/queryQueryRequestQueryResponse
  • POST /chart/positions{when_utc, lat, lon, house_system?} → pełny horoskop: pozycje (LOG-01) + osie i domy (LOG-05) + aspekty główne z applying/separating (LOG-06) + opcjonalnie stacje planet (stations:true, LOG-03). Zwraca też sektę i 7 Lots hermetycznych z domami (LOG-08). Obiekty: 10 planet + mean NN/SN/Lilith (LOG-02). house_system: whole_sign (dom.) / equal / porphyry.
  • POST /chart/report{when_utc, lat, lon, limit?} → wynik obliczeń wyszukany w bazie: fasety sygnifikatorów w znaku / w domu / w aspekcie, z rozwinięciem skrótów, odsiewaniem duplikatów (ten sam sygnifikator i opis), rankingiem siły (LOG-21) oraz opcją group (grupowanie identycznych opisów)
  • POST /chart/profections{when_utc, lat, lon, start_age?, count?} → profekcje roczne: wiek, profektowany Asc, Władca Roku (+MC/Su/Mo) (LOG-10)
  • POST /chart/return{when_utc, lat, lon, kind, around?} → Solar/Lunar Return: moment powrotu + pełny horoskop na ten moment (LOG-12)
  • POST /chart/firdaria{when_utc, lat, lon} → Firdaria: sekta (dzień/noc), okresy główne i podokresy time-lordów (LOG-11)
  • POST /chart/timeline{when_utc, lat, lon, from_date, to_date, techniques?} → zbiorcza oś czasu: profekcje + Solar Return + dyrekcje solar-arc + Firdaria, posortowane (technique | significator | start | exact | end); z interpret=true dopina interpretacje z bazy do dat (LOG-14, 1B->2B)
  • POST /chart/compare → jak wyżej → raport różnic dwóch silników (LOG-26; wymaga silnika B)
  • GET /health (sprawdza też warstwę bazodanową)

Silnik efemeryd (LOG-24, „wymienny silnik liczący")

W app/engine/ żyje pluggable silnik za interfejsem EphemerisEngine:

  • SkyfieldEngine — własny, permisywny (Skyfield MIT + dane JPL public domain). Domyślny.
  • RemoteEngine — klient OSOBNEJ, izolowanej usługi engine-swisseph (AGPL), używany tylko w trybie porównawczym.

Wybór: EPHEMERIS_ENGINE=own|swisseph. Silnik B włącza się przez ENGINE_SWISSEPH_URL.

Walidacja (LOG-25/28): app/engine/compare.py zestawia oba silniki z progiem tolerancji; ten sam kontrakt parzystości obowiązuje każdy silnik. Nasz SkyfieldEngine zgadza się ze Swiss Ephemeris co do ~1″ na horoskopie referencyjnym (patrz tests/).

pip install -r requirements-dev.txt
PYTHONPATH=. pytest tests -q            # silnik B pomijany, jeśli ENGINE_SWISSEPH_URL nieustawiony

Zależności w dół

Zna wyłącznie DATA_URL (adres warstwy bazodanowej) i jej kontrakt /search. Nie wie, czy pod spodem jest Excel czy SQL.

Uruchomienie

pip install -r requirements.txt
export DATA_URL=http://localhost:8002
uvicorn app.main:app --port 8001

Gdzie rozbudowywać domenę

service.pyQueryService.handle(): walidacja wejścia, tłumaczenie zapytania, obliczenia i wzbogacanie wyników.