feat: wiele systemów domów naraz — porównanie obok siebie (PRE-05/LOG-05, A2a)
build / build (push) Successful in 1m3s
Testy / Testy warstwy logicznej (silnik) (push) Successful in 11m7s
Testy / Testy warstwy prezentacji (dostęp do baz) (push) Successful in 9m55s
Testy / Build obrazu silnika B (swisseph) (push) Successful in 36s
Testy / Kontrola składni wszystkich warstw (push) Successful in 25s
build / build (push) Successful in 1m3s
Testy / Testy warstwy logicznej (silnik) (push) Successful in 11m7s
Testy / Testy warstwy prezentacji (dostęp do baz) (push) Successful in 9m55s
Testy / Build obrazu silnika B (swisseph) (push) Successful in 36s
Testy / Kontrola składni wszystkich warstw (push) Successful in 25s
Astrolodzy spierają się o systemy domów; teraz można policzyć kilka na raz i zobaczyć, jak różny podział przesuwa planety między domami. Domy to geometria z Asc/MC — osie są WSPÓLNE, różni się tylko podział. Prymarny system zostaje w `cusps`/`house_system`/`positions[].house` (pod kosmogram i wstecznie — nic się nie zmienia dla dotychczasowych ścieżek). Nowość: - logika: `build_chart(..., house_systems=[...])` → `result["house_systems"]` = pełen zestaw (prymarny pierwszy, bez duplikatów), a `positions[].houses[system]` mówi, w którym domu obiekt siedzi wg każdego systemu. Doklejane tylko gdy > 1. Nieznany system (np. placidus — dojdzie przez swisseph osobno, A2b) pomijany, nie wywala horoskopu. - endpoint `/chart/positions`: pole `house_systems`; klient prezentacji przekazuje. - UI: checkboxy „Porównaj systemy domów" (whole sign / equal / porphyry) + tabela kusp obok siebie (12 domów × systemy), stan zaznaczeń przeżywa submit. Na razie 3 systemy z czystej matmy (`houses.py`) — zero zależności, zero walidacji krzyżowej. Egzotyczne (Placidus/Koch/Regiomontanus/Campanus) dojdą przez endpoint `/houses` w silniku B (swisseph) jako A2b — maszyneria „naraz" jest już gotowa, egzotyczne tylko dopiszą kolejne wpisy. Weryfikacja: żywy /chart/positions — prymarny whole_sign, house_systems [whole_sign, equal, porphyry], 12 kusp/system, dom per system per obiekt. Testy: logika +5, prezentacja +6. Logika 270, prezentacja 179. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit was merged in pull request #44.
This commit is contained in:
@@ -35,7 +35,8 @@ def _shift_pos(pdict: dict, off: float) -> None:
|
||||
|
||||
|
||||
def build_chart(engine: EphemerisEngine, moment: ChartMoment, house_system: str = H.WHOLE_SIGN,
|
||||
lots_method: str = "degree", zodiac: str = Z.TROPICAL) -> dict:
|
||||
lots_method: str = "degree", zodiac: str = Z.TROPICAL,
|
||||
house_systems: list[str] | None = None) -> dict:
|
||||
from app.engine import glyphs as GL
|
||||
from app.engine.aspects import find_aspects
|
||||
from app.engine.houses import mean_obliquity
|
||||
@@ -94,6 +95,16 @@ def build_chart(engine: EphemerisEngine, moment: ChartMoment, house_system: str
|
||||
system = house_system if house_system in H.SYSTEMS else H.WHOLE_SIGN
|
||||
cusp_list = H.cusps(asc, mc, system) # tropikalne — geometria domów jest niezmiennicza
|
||||
|
||||
def _cusps_out(cl: list[float]) -> list[dict]:
|
||||
"""Cuspy → wiersze pod UI/kosmogram: znak, stopień w znaku, długość, glif."""
|
||||
return [
|
||||
{"house": i + 1, "sign": SIGNS[sign_index(norm360(c - off))],
|
||||
"in_sign": in_sign(norm360(c - off)),
|
||||
"decimal": round(norm360(c - off), 6), # długość cuspu — pod kosmogram (PRE-12)
|
||||
"sign_glyph": GL.sign_glyph(SIGNS[sign_index(norm360(c - off))])}
|
||||
for i, c in enumerate(cl)
|
||||
]
|
||||
|
||||
result["house_system"] = system
|
||||
result["angles"] = {
|
||||
"Asc": _fmt("Asc", asc, off),
|
||||
@@ -103,16 +114,25 @@ def build_chart(engine: EphemerisEngine, moment: ChartMoment, house_system: str
|
||||
}
|
||||
for a in result["angles"].values(): # glif znaku osi (LOG-22)
|
||||
a["sign_glyph"] = GL.sign_glyph(a["sign"])
|
||||
result["cusps"] = [
|
||||
{"house": i + 1, "sign": SIGNS[sign_index(norm360(c - off))],
|
||||
"in_sign": in_sign(norm360(c - off)),
|
||||
"decimal": round(norm360(c - off), 6), # długość cuspu — pod kosmogram (PRE-12)
|
||||
"sign_glyph": GL.sign_glyph(SIGNS[sign_index(norm360(c - off))])}
|
||||
for i, c in enumerate(cusp_list)
|
||||
]
|
||||
result["cusps"] = _cusps_out(cusp_list) # PRYMARNY system — pod kosmogram i wstecz
|
||||
for pdict, obj in zip(result["positions"], positions):
|
||||
pdict["house"] = H.assign_house(obj.longitude, cusp_list) # dom po długości tropikalnej
|
||||
|
||||
# Wiele systemów domów NARAZ (PRE-05/LOG-05) — do porównania obok siebie.
|
||||
# Osie (Asc/MC) są wspólne; różni się tylko PODZIAŁ na domy. Prymarny zostaje
|
||||
# w `cusps`/`house_system` (kosmogram i wstecz), a `house_systems` niesie pełen
|
||||
# zestaw; `positions[].houses[system]` mówi, w którym domu obiekt siedzi wg
|
||||
# danego systemu. Dokładamy tylko gdy poproszono o więcej niż jeden.
|
||||
requested = [system] + [s for s in (house_systems or []) if s in H.SYSTEMS]
|
||||
ordered = list(dict.fromkeys(requested)) # prymarny pierwszy, bez duplikatów
|
||||
if len(ordered) > 1:
|
||||
result["house_systems"] = []
|
||||
for s in ordered:
|
||||
cl = cusp_list if s == system else H.cusps(asc, mc, s)
|
||||
result["house_systems"].append({"system": s, "cusps": _cusps_out(cl)})
|
||||
for pdict, obj in zip(result["positions"], positions):
|
||||
pdict.setdefault("houses", {})[s] = H.assign_house(obj.longitude, cl)
|
||||
|
||||
# Lots (LOG-08) — wymagają Asc i sekty (dzień/noc)
|
||||
from app.engine.firdaria import is_day_birth
|
||||
from app.engine.lots import compute_lots
|
||||
|
||||
@@ -43,7 +43,8 @@ class PositionsRequest(BaseModel):
|
||||
lat: float = 0.0
|
||||
lon: float = 0.0
|
||||
objects: list[str] | None = None
|
||||
house_system: str = "whole_sign" # whole_sign | equal | porphyry
|
||||
house_system: str = "whole_sign" # whole_sign | equal | porphyry (PRYMARNY — pod kosmogram)
|
||||
house_systems: list[str] | None = None # PRE-05: dodatkowe systemy do porównania obok
|
||||
stations: bool = False # licz stacje (LOG-03; wolniejsze — root-findy)
|
||||
tables: bool = False # tabele dodatkowe (LOG-23; szuka wschodu/zachodu)
|
||||
zodiac: str = "tropical" # LOG-04: tropical | sidereal_{lahiri,fagan_bradley,krishnamurti} | draconic
|
||||
@@ -67,7 +68,8 @@ def chart_positions(req: PositionsRequest) -> dict:
|
||||
engine = get_engine()
|
||||
moment = ChartMoment(when_utc=req.when_utc, lat=req.lat, lon=req.lon)
|
||||
try:
|
||||
chart = build_chart(engine, moment, req.house_system, zodiac=req.zodiac)
|
||||
chart = build_chart(engine, moment, req.house_system, zodiac=req.zodiac,
|
||||
house_systems=req.house_systems)
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=422, detail=str(e))
|
||||
if req.stations:
|
||||
|
||||
@@ -25,3 +25,44 @@ def test_house_systems_available(own_engine, reference_moment):
|
||||
chart = build_chart(own_engine, reference_moment, system)
|
||||
assert chart["house_system"] == system
|
||||
assert len(chart["cusps"]) == 12
|
||||
|
||||
|
||||
# ── wiele systemów domów naraz (PRE-05/LOG-05) ──
|
||||
|
||||
def test_multiple_house_systems_side_by_side(own_engine, reference_moment):
|
||||
"""Prymarny zostaje w cusps/house_system; `house_systems` niesie pełen zestaw
|
||||
do porównania, z prymarnym NA POCZĄTKU i bez duplikatów."""
|
||||
chart = build_chart(own_engine, reference_moment, "whole_sign",
|
||||
house_systems=["equal", "porphyry", "whole_sign"])
|
||||
assert chart["house_system"] == "whole_sign" # prymarny bez zmian
|
||||
assert [h["system"] for h in chart["house_systems"]] == ["whole_sign", "equal", "porphyry"]
|
||||
for h in chart["house_systems"]:
|
||||
assert len(h["cusps"]) == 12
|
||||
|
||||
|
||||
def test_single_system_has_no_comparison_block(own_engine, reference_moment):
|
||||
"""Bez dodatkowych systemów nie zaśmiecamy wyniku."""
|
||||
assert "house_systems" not in build_chart(own_engine, reference_moment, "whole_sign")
|
||||
|
||||
|
||||
def test_object_gets_a_house_in_every_system(own_engine, reference_moment):
|
||||
"""Sedno porównania: każdy obiekt ma dom w KAŻDYM systemie, a prymarny jest
|
||||
spójny z polem `.house`."""
|
||||
chart = build_chart(own_engine, reference_moment, "whole_sign", house_systems=["porphyry"])
|
||||
sun = next(p for p in chart["positions"] if p["name"] == "Sun")
|
||||
assert set(sun["houses"]) == {"whole_sign", "porphyry"}
|
||||
assert sun["houses"]["whole_sign"] == sun["house"]
|
||||
|
||||
|
||||
def test_primary_always_first_even_if_not_listed(own_engine, reference_moment):
|
||||
chart = build_chart(own_engine, reference_moment, "equal", house_systems=["porphyry"])
|
||||
systems = [h["system"] for h in chart["house_systems"]]
|
||||
assert systems[0] == "equal" and "porphyry" in systems
|
||||
|
||||
|
||||
def test_unknown_extra_system_is_ignored(own_engine, reference_moment):
|
||||
"""Nieobsługiwany system (np. placidus — dojdzie przez swisseph osobno) jest
|
||||
po prostu pomijany, nie wywala horoskopu."""
|
||||
chart = build_chart(own_engine, reference_moment, "whole_sign",
|
||||
house_systems=["placidus", "equal"])
|
||||
assert [h["system"] for h in chart["house_systems"]] == ["whole_sign", "equal"]
|
||||
|
||||
Reference in New Issue
Block a user