feat: konfigurowalne aspekty i orby (PRE-06)
Testy / Testy warstwy logicznej (silnik) (pull_request) Successful in 10m47s
Testy / Testy warstwy prezentacji (dostęp do baz) (pull_request) Successful in 9m27s
Testy / Build obrazu silnika B (swisseph) (pull_request) Successful in 12s
Testy / Kontrola składni wszystkich warstw (pull_request) Successful in 9s
build / build (push) Successful in 26s
Testy / Testy warstwy logicznej (silnik) (push) Successful in 10m30s
Testy / Testy warstwy prezentacji (dostęp do baz) (push) Successful in 9m28s
Testy / Build obrazu silnika B (swisseph) (push) Successful in 12s
Testy / Kontrola składni wszystkich warstw (push) Successful in 8s
Testy / Testy warstwy logicznej (silnik) (pull_request) Successful in 10m47s
Testy / Testy warstwy prezentacji (dostęp do baz) (pull_request) Successful in 9m27s
Testy / Build obrazu silnika B (swisseph) (pull_request) Successful in 12s
Testy / Kontrola składni wszystkich warstw (pull_request) Successful in 9s
build / build (push) Successful in 26s
Testy / Testy warstwy logicznej (silnik) (push) Successful in 10m30s
Testy / Testy warstwy prezentacji (dostęp do baz) (push) Successful in 9m28s
Testy / Build obrazu silnika B (swisseph) (push) Successful in 12s
Testy / Kontrola składni wszystkich warstw (push) Successful in 8s
Dotąd orb (8°) i aspekty były zaszyte w silniku. Astrolodzy pracują na różnych orbach i lubią dokładać aspekty poboczne — teraz da się to ustawić w UI. Silnik (aspects.py): `find_aspects` przyjmuje orb, luminary_bonus i `minor`. Aspekty poboczne = tylko trzy (półsekstyl 30° / półkwadratura 45° / kwinkunks 150°) — bo mają już glify i barwy w prezentacji i w engine/glyphs.py, więc dokładają się bez ruszania czegokolwiek poza silnikiem. Bazy zwykle ich nie opisują (brak tokenu), więc trafiają na kosmogram i do tabeli, ale NIE tworzą faset — most sygnifikatorów po cichu je pomija (skip przy braku tokenu). Plumbing przez wszystkie warstwy: request logiki, build_chart, klient prezentacji, oba handlery (/, /compile) ORAZ PDF (/compile/pdf). Ustawienia wędrują między zakładkami (formsync) i lecą do PDF-a (compile.js) — żeby podsumowanie liczyło aspekty tym samym orbem co horoskop (ta sama zasada co fix podsumowania #46). UI: orb, bonus dla świateł, checkbox „aspekty poboczne" na obu formularzach. Weryfikacja na żywym /chart/positions: domyślnie 24 aspekty (główne); minor → 52 (dochodzą quincunx/semisextile/semisquare); orb 3 → 13, orb 12 → 32. Testy: logika +3 (minor/orb/bonus), prezentacja +6 (obecność pól, przekazanie przez warstwy, sync, PDF). Logika 273, prezentacja 220. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit was merged in pull request #49.
This commit is contained in:
@@ -55,6 +55,9 @@ class LogicClient:
|
||||
zodiac: str = "tropical",
|
||||
tables: bool = False,
|
||||
house_systems: list[str] | None = None,
|
||||
aspect_orb: float = 8.0,
|
||||
aspect_luminary_bonus: float = 2.0,
|
||||
aspect_minor: bool = False,
|
||||
) -> dict[str, Any]:
|
||||
"""Pełny horoskop dla danego momentu — woła logic /chart/positions."""
|
||||
payload = {
|
||||
@@ -64,6 +67,9 @@ class LogicClient:
|
||||
"objects": objects,
|
||||
"house_system": house_system,
|
||||
"house_systems": house_systems,
|
||||
"aspect_orb": aspect_orb,
|
||||
"aspect_luminary_bonus": aspect_luminary_bonus,
|
||||
"aspect_minor": aspect_minor,
|
||||
"stations": stations,
|
||||
"zodiac": zodiac,
|
||||
"tables": tables,
|
||||
|
||||
@@ -106,14 +106,18 @@ def chart_compute(
|
||||
lon: float = Form(0.0),
|
||||
house_system: str = Form("whole_sign"),
|
||||
house_systems: list[str] = Form([]), # PRE-05: dodatkowe systemy do porównania
|
||||
aspect_orb: float = Form(8.0), # PRE-06: konfigurowalny orb aspektów
|
||||
aspect_luminary_bonus: float = Form(2.0),
|
||||
aspect_minor: bool = Form(False),
|
||||
stations: bool = Form(False),
|
||||
zodiac: str = Form("tropical"),
|
||||
tables: bool = Form(False),
|
||||
):
|
||||
form = {"person": person, "date": date, "time": time, "tz_offset": tz_offset,
|
||||
"lat": lat, "lon": lon, "house_system": house_system,
|
||||
"house_systems": house_systems, "stations": stations,
|
||||
"zodiac": zodiac, "tables": tables}
|
||||
"house_systems": house_systems, "aspect_orb": aspect_orb,
|
||||
"aspect_luminary_bonus": aspect_luminary_bonus, "aspect_minor": aspect_minor,
|
||||
"stations": stations, "zodiac": zodiac, "tables": tables}
|
||||
ctx: dict = {"form": form, "result": None, "error": None, "moment": None}
|
||||
try:
|
||||
iso_utc, label = _build_utc(date, time, tz_offset)
|
||||
@@ -121,6 +125,8 @@ def chart_compute(
|
||||
ctx["result"] = logic.positions(
|
||||
when_utc_iso=iso_utc, lat=lat, lon=lon,
|
||||
house_system=house_system, house_systems=house_systems,
|
||||
aspect_orb=aspect_orb, aspect_luminary_bonus=aspect_luminary_bonus,
|
||||
aspect_minor=aspect_minor,
|
||||
stations=stations, zodiac=zodiac, tables=tables,
|
||||
)
|
||||
from app import chartwheel # kosmogram (PRE-12), SVG po stronie serwera
|
||||
@@ -155,6 +161,9 @@ def compile_build(
|
||||
lon: float = Form(0.0),
|
||||
house_system: str = Form("whole_sign"),
|
||||
house_systems: list[str] = Form([]), # PRE-05 — porównanie domów w podsumowaniu
|
||||
aspect_orb: float = Form(8.0), # PRE-06 — te same ustawienia aspektów
|
||||
aspect_luminary_bonus: float = Form(2.0),
|
||||
aspect_minor: bool = Form(False),
|
||||
stations: bool = Form(False), # LOG-03 — stacje też w podsumowaniu
|
||||
zodiac: str = Form("tropical"),
|
||||
tables: bool = Form(False), # LOG-23 — żywioły/faza/godziny w podsumowaniu
|
||||
@@ -170,8 +179,9 @@ def compile_build(
|
||||
"""
|
||||
form = {"person": person, "date": date, "time": time, "tz_offset": tz_offset,
|
||||
"lat": lat, "lon": lon, "house_system": house_system,
|
||||
"house_systems": house_systems, "stations": stations,
|
||||
"zodiac": zodiac, "tables": tables}
|
||||
"house_systems": house_systems, "aspect_orb": aspect_orb,
|
||||
"aspect_luminary_bonus": aspect_luminary_bonus, "aspect_minor": aspect_minor,
|
||||
"stations": stations, "zodiac": zodiac, "tables": tables}
|
||||
ctx: dict = {"form": form, "result": None, "error": None, "moment": None}
|
||||
try:
|
||||
iso_utc, label = _build_utc(date, time, tz_offset)
|
||||
@@ -179,6 +189,8 @@ def compile_build(
|
||||
ctx["result"] = logic.positions(
|
||||
when_utc_iso=iso_utc, lat=lat, lon=lon,
|
||||
house_system=house_system, house_systems=house_systems,
|
||||
aspect_orb=aspect_orb, aspect_luminary_bonus=aspect_luminary_bonus,
|
||||
aspect_minor=aspect_minor,
|
||||
stations=stations, zodiac=zodiac, tables=tables,
|
||||
)
|
||||
from app import chartwheel
|
||||
@@ -224,6 +236,9 @@ def compile_pdf(payload: dict):
|
||||
lat=float(data.get("lat") or 0.0), lon=float(data.get("lon") or 0.0),
|
||||
house_system=str(data.get("house_system") or "whole_sign"),
|
||||
house_systems=[s for s in (data.get("house_systems") or []) if s],
|
||||
aspect_orb=float(data.get("aspect_orb") or 8.0),
|
||||
aspect_luminary_bonus=float(data.get("aspect_luminary_bonus") or 2.0),
|
||||
aspect_minor=bool(data.get("aspect_minor")),
|
||||
stations=bool(data.get("stations")), tables=bool(data.get("tables")),
|
||||
zodiac=str(data.get("zodiac") or "tropical"),
|
||||
)
|
||||
|
||||
@@ -109,6 +109,8 @@
|
||||
lat: v('lat'), lon: v('lon'),
|
||||
house_system: v('house_system'), zodiac: v('zodiac'),
|
||||
house_systems: multi('house_systems'),
|
||||
aspect_orb: v('aspect_orb'), aspect_luminary_bonus: v('aspect_luminary_bonus'),
|
||||
aspect_minor: checked('aspect_minor'),
|
||||
stations: checked('stations'), tables: checked('tables'),
|
||||
place: (document.querySelector('#geoSearch') || {}).value || ''
|
||||
};
|
||||
|
||||
@@ -36,7 +36,10 @@
|
||||
{ key: 'place', sel: '#geoSearch' },
|
||||
{ key: 'stations', sel: 'input[name=stations]', type: 'check' },
|
||||
{ key: 'tables', sel: 'input[name=tables]', type: 'check' },
|
||||
{ key: 'house_systems', name: 'house_systems', type: 'multi' }
|
||||
{ key: 'house_systems', name: 'house_systems', type: 'multi' },
|
||||
{ key: 'aspect_orb', sel: 'input[name=aspect_orb]' },
|
||||
{ key: 'aspect_luminary_bonus', sel: 'input[name=aspect_luminary_bonus]' },
|
||||
{ key: 'aspect_minor', sel: 'input[name=aspect_minor]', type: 'check' }
|
||||
];
|
||||
|
||||
function elementsOf(f) {
|
||||
|
||||
@@ -64,6 +64,11 @@
|
||||
<label><input type="checkbox" name="house_systems" value="equal" {{ 'checked' if 'equal' in chosen else '' }}> Equal</label>
|
||||
<label><input type="checkbox" name="house_systems" value="porphyry" {{ 'checked' if 'porphyry' in chosen else '' }}> Porphyry</label>
|
||||
</div>
|
||||
<div class="opts" title="Orb = dopuszczalne odchylenie od dokładnego kąta aspektu. „Bonus świateł" powiększa orb dla aspektów ze Słońcem/Księżycem. Aspekty poboczne: półsekstyl 30°, półkwadratura 45°, kwinkunks 150° (PRE-06).">
|
||||
<label>Orb aspektów (°) <input type="number" name="aspect_orb" step="0.5" min="1" max="15" value="{{ form.aspect_orb if form.aspect_orb is not none else 8 }}"></label>
|
||||
<label>Bonus świateł (°) <input type="number" name="aspect_luminary_bonus" step="0.5" min="0" max="5" value="{{ form.aspect_luminary_bonus if form.aspect_luminary_bonus is not none else 2 }}"></label>
|
||||
<label><input type="checkbox" name="aspect_minor" value="true" {{ 'checked' if form.aspect_minor else '' }}> aspekty poboczne (30/45/150°)</label>
|
||||
</div>
|
||||
{% if location_label %}<p class="muted small">Wstępnie wpisano lokalizację: <strong>{{ location_label }}</strong> ({{ form.lat }}, {{ form.lon }}). Zmień pola lub kliknij „Tu i teraz".</p>{% endif %}
|
||||
<div class="actions">
|
||||
<button type="button" id="nowBtn" class="ghost">Tu i teraz</button>
|
||||
|
||||
@@ -70,6 +70,11 @@ zapamiętane predykcje okresowe. Dane pobiera z pozostałych zakładek — nie t
|
||||
<label><input type="checkbox" name="house_systems" value="equal" {{ 'checked' if 'equal' in chosen else '' }}> Equal</label>
|
||||
<label><input type="checkbox" name="house_systems" value="porphyry" {{ 'checked' if 'porphyry' in chosen else '' }}> Porphyry</label>
|
||||
</div>
|
||||
<div class="opts" title="Te same ustawienia aspektów co przy horoskopie (PRE-06). Aspekty poboczne: półsekstyl 30°, półkwadratura 45°, kwinkunks 150°.">
|
||||
<label>Orb aspektów (°) <input type="number" name="aspect_orb" step="0.5" min="1" max="15" value="{{ form.aspect_orb if form.aspect_orb is not none else 8 }}"></label>
|
||||
<label>Bonus świateł (°) <input type="number" name="aspect_luminary_bonus" step="0.5" min="0" max="5" value="{{ form.aspect_luminary_bonus if form.aspect_luminary_bonus is not none else 2 }}"></label>
|
||||
<label><input type="checkbox" name="aspect_minor" value="true" {{ 'checked' if form.aspect_minor else '' }}> aspekty poboczne (30/45/150°)</label>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<button type="submit">Złóż podsumowanie</button>
|
||||
<button type="button" id="pdfBtn" class="ghost"
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
"""Konfigurowalne aspekty i orby w UI (PRE-06).
|
||||
|
||||
Ustawienia muszą: (1) być na obu formularzach (Horoskop i Skompiluj), (2) wędrować
|
||||
między zakładkami (formsync), (3) trafiać do logiki na ekran i do PDF-a — inaczej
|
||||
podsumowanie liczyłoby aspekty innym orbem niż horoskop (ta sama klasa błędu co
|
||||
regresja podsumowania).
|
||||
"""
|
||||
import pathlib
|
||||
|
||||
APP = pathlib.Path(__file__).resolve().parents[1] / "app"
|
||||
CHART = (APP / "templates" / "chart.html").read_text(encoding="utf-8")
|
||||
COMPILE = (APP / "templates" / "compile.html").read_text(encoding="utf-8")
|
||||
MAIN = (APP / "main.py").read_text(encoding="utf-8")
|
||||
CLIENT = (APP / "clients" / "logic_client.py").read_text(encoding="utf-8")
|
||||
SYNC = (APP / "static" / "formsync.js").read_text(encoding="utf-8")
|
||||
COMPILE_JS = (APP / "static" / "compile.js").read_text(encoding="utf-8")
|
||||
|
||||
FIELDS = ("aspect_orb", "aspect_luminary_bonus", "aspect_minor")
|
||||
|
||||
|
||||
def test_both_forms_have_aspect_settings():
|
||||
for f in FIELDS:
|
||||
assert f'name="{f}"' in CHART, f"Horoskop: brak {f}"
|
||||
assert f'name="{f}"' in COMPILE, f"Skompiluj: brak {f}"
|
||||
|
||||
|
||||
def test_handlers_accept_and_pass_aspect_settings():
|
||||
# oba wywołania logic.positions (chart + compile) dostają orb
|
||||
assert MAIN.count("aspect_orb=aspect_orb") == 2
|
||||
assert "aspect_minor: bool = Form(False)" in MAIN
|
||||
|
||||
|
||||
def test_client_forwards_aspect_settings():
|
||||
for f in FIELDS:
|
||||
assert f'"{f}": {f}' in CLIENT, f"klient nie przekazuje {f}"
|
||||
|
||||
|
||||
def test_settings_are_synced_between_tabs():
|
||||
for f in FIELDS:
|
||||
assert f"'{f}'" in SYNC, f"formsync nie synchronizuje {f}"
|
||||
|
||||
|
||||
def test_pdf_payload_includes_aspect_settings():
|
||||
assert "aspect_orb: v('aspect_orb')" in COMPILE_JS
|
||||
assert "aspect_minor: checked('aspect_minor')" in COMPILE_JS
|
||||
|
||||
|
||||
def test_pdf_handler_reads_aspect_settings():
|
||||
assert 'aspect_orb=float(data.get("aspect_orb")' in MAIN
|
||||
assert 'aspect_minor=bool(data.get("aspect_minor"))' in MAIN
|
||||
Reference in New Issue
Block a user