"""Trasy wnoszone przez moduł. Bez tego katalogu w obrazie nie istnieją.""" from __future__ import annotations from typing import Any import httpx from fastapi.responses import JSONResponse, Response from app.moduly.eksport import katalog _logic: Any = None _szablony: Any = None _buduj_utc: Any = None _blad: Any = None _kolo: Any = None def zamontuj(app, *, logic: Any, templates: Any, buduj_utc: Any, blad: Any, security: Any = None, licz_rekordy: Any = None, kolo: Any = None, **_reszta) -> None: """Podpięcie trasy i przekazanie zależności. Idą z zewnątrz, bo zależność ma biec w JEDNĄ stronę: aplikacja wie o modułach opcjonalnych (przez neutralny most), moduł nie wie nic o aplikacji.""" global _logic, _szablony, _buduj_utc, _blad, _kolo _logic, _szablony, _buduj_utc, _blad = logic, templates, buduj_utc, blad _kolo = kolo katalog.ustaw_zaleznosci(logic=logic, security=security, licz_rekordy=licz_rekordy) # Nazwy, których używa przeniesiona trasa. `chartwheel` to czysty rysownik # bez stanu, więc importujemy go wprost — wstrzykiwanie ma sens dla rzeczy # związanych z aplikacją (klient, szablony), nie dla funkcji rysujących. from app import chartwheel as chartwheel_mod _build_utc = buduj_utc _logic_error = blad @app.post("/compile/pdf") def compile_pdf(payload: dict): """Składa raport PDF (PRE-24) — woła usługę render po szyfrowanym łączu. Wejście z przeglądarki, bo część kawałków (interpretacja natalna i predykcje) mieszkają w magazynie lokalnym. Kosmogram i tabele liczymy TU, żeby PDF zawierał dokładnie to, co widać na stronie. Kosmogram idzie w motywie DRUKU: samodzielny konwerter SVG→PDF nie zna naszego arkusza, więc zmienne CSS i font glifów muszą być w samym rysunku. """ from fastapi.responses import Response from app.clients.render_client import RenderClient data = payload.get("data") or {} try: iso_utc, label = _build_utc( str(data.get("date") or ""), str(data.get("time") or ""), float(data.get("tz_offset") or 0.0), ) except (ValueError, TypeError) as e: return JSONResponse({"detail": f"Niepoprawne dane wejściowe: {e}"}, status_code=422) figures: list[dict] = [] warnings: list[str] = [] try: chart = logic.positions( when_utc_iso=iso_utc, 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"), ) # Fallback systemu domów musi dojechać do PDF-a — z samego kosmogramu # nie da się poznać, że podział jest z innego systemu, niż zamówiono. warnings = [w for w in (chart.get("house_warnings") or []) if w] from app import chartwheel # Zasada: co pokazujemy na stronie, ma trafić do PDF-a. Wszystkie rysunki # w motywie DRUKU — samodzielny konwerter SVG→PDF nie zna arkusza, więc # zmienne CSS i font glifów muszą być wprost w rysunku. for svg, caption in ( (chartwheel.render(chart, theme="print", orientation=str(data.get("wheel_orientation") or chartwheel_mod.ASC_LEFT)), "Kosmogram"), (chartwheel.render_aspectarian(chart, theme="print"), "Aspektarian — siatka aspektów"), (chartwheel.render_declination(chart, theme="print"), "Wykres deklinacji"), (chartwheel.render_antiscia(chart, theme="print"), "Oś antyscji"), ): if svg: figures.append({"svg": svg, "caption": caption}) except httpx.HTTPError as e: # Brak rysunków nie może zablokować raportu — tekst jest ważniejszy. data = {**data, "wheel_error": _logic_error(e)} report = { "person": payload.get("person") or "", "data": {**data, "moment_utc": label}, "figures": figures, "warnings": warnings, "natal": payload.get("natal") or {}, "predictions": payload.get("predictions") or [], } try: pdf = RenderClient().pdf(report) except httpx.HTTPError as e: return JSONResponse( {"detail": f"Usługa render niedostępna albo nie złożyła PDF-a: {e}"}, status_code=502, ) return Response(content=pdf, media_type="application/pdf", headers={"Content-Disposition": 'attachment; filename="raport.pdf"'})