Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1c68ca04f6 | |||
| a7cd539d74 |
@@ -0,0 +1,146 @@
|
||||
# Analiza materiałów od klienta — wrzesień 2026
|
||||
|
||||
Źródła:
|
||||
- `astro parser TO DO LIST 2026-09-05.xlsx` — dalej **TO DO**,
|
||||
- `astro parser_2026-09-13.docx` — dalej **specyfikacja**.
|
||||
|
||||
Architektura (trzy warstwy) zostaje bez zmian. Uwagi klienta o architekturze i stosie technologicznym są tu celowo pominięte.
|
||||
|
||||
Legenda pewności: **[kod]** sprawdzone w kodzie · **[rejestr]** status z `astrololo_wymagania.xlsx`, niezweryfikowany w kodzie · **[hipoteza]** wniosek z objawów, niesprawdzony.
|
||||
|
||||
## TL;DR
|
||||
|
||||
1. **Interpretacje pokazują sygnifikatory, których w horoskopie nie ma.** Klient ma rację, mechanizm potwierdzony w kodzie. To nie nowe wymaganie, tylko błąd w działającej funkcji — i ten sam błąd zasila prompt do LLM.
|
||||
2. **Specyfikacja (docx) to w ~90% wynik AI, którego klient jeszcze nie przejrzał.** W części 2 z 21 stoi znacznik „{DOTĄD SPRAWDZONE}". Nie traktować jej jako wymagań — to skorowidz do sprawdzania algorytmów.
|
||||
3. **Prawdziwą listą priorytetów jest TO DO (xlsx).** Astrologia wedyjska, chińska i majańska występuje wyłącznie w specyfikacji; w TO DO jej nie ma.
|
||||
4. **Trzy zmiany przekrojowe:** interfejs po angielsku, wyłącznie jasny motyw z kolorowaniem, tabele w formie „symbol + skrót".
|
||||
5. **Część pozycji z TO DO już istnieje** (paralele, draconic, stacje, profekcje, zbiorcza tabela predykcji) — klient może o tym nie wiedzieć albo chce innego formatu.
|
||||
|
||||
## 1. Interpretacje działają źle (potwierdzone w kodzie)
|
||||
|
||||
**Zgłoszenie (TO DO, wiersz 11, \*\*\*).** Pod nagłówkiem „Sun w Taurus" pojawia się rekord „Sun in Gemini or Sagittarius, in square or opposition the Moon", choć żaden z warunków nie zachodzi. Fasetka „koniunkcja z Moon — 84 dopasowań" zbiera każdą koniunkcję czegokolwiek z Księżycem. Pokazują się też rekordy o dyrekcjach („by dir."), których nie liczymy, oraz o godnościach („well-dig. at B."), które w tym horoskopie nie są spełnione.
|
||||
|
||||
**Mechanizm [kod].** `_facet_samples` w [significators.py](../services/logic/app/significators.py#L70) przepuszcza rekord, jeśli tekst jego sygnifikatora *zawiera* wszystkie tokeny fasetki ([linia 82](../services/logic/app/significators.py#L82): `if not all(t in low for t in toks)`). Sprawdza więc, czy rekord *wspomina* te tokeny, a nie czy *wszystkie warunki rekordu* są spełnione w horoskopie. Skutki:
|
||||
|
||||
- Nie rozróżnia, **która** planeta jest w aspekcie. Rekord `[Ve [conj [Mo … [Su in 7th H.` trafia do fasetki „Słońce w koniunkcji z Księżycem", bo zawiera `[Su`, `[conj` i `[Mo`.
|
||||
- Ignoruje **dodatkowe warunki** rekordu: inny znak („Gem or Sag"), inny aspekt („sq or opp"), kwalifikatory techniki („by dir.", „tr."), godności („at B.").
|
||||
- „[Su: 5415 rekordów" w nagłówku to `planet_total` — liczba wszystkich rekordów wspominających Słońce, bez żadnego dopasowania.
|
||||
|
||||
Ta sama funkcja obsługuje ścieżkę kalendarza ([`interpret_events`](../services/logic/app/significators.py#L265)), więc wada dotyczy obu ekranów.
|
||||
|
||||
**Dlaczego to pilne.** Generator promptów (LOG-29/30) bierze dopasowania z tego samego `build_report` ([main.py](../services/logic/app/main.py#L338), [prompt.py](../services/logic/app/prompt.py#L66)), a prompt każe modelowi popierać każdą tezę konkretnym sygnifikatorem. Model z przekonaniem cytuje więc świadectwa, których w horoskopie nie ma.
|
||||
|
||||
**Rozbieżność z rejestrem.** LOG-15, LOG-16 i LOG-19 mają status „Zrobione", a docstring modułu sam nazywa ten kod „Zalążkiem LOG-15/16". Statusy do korekty.
|
||||
|
||||
**Co trzeba zrobić.** Prawdziwy ewaluator zamiast dopasowania tokenów: rozbiór sygnifikatora na drzewo warunków (koniunkcja klauzul, alternatywy „or", kwalifikatory techniki i godności) i sprawdzenie każdej klauzuli względem atomów policzonych dla horoskopu. Rekord z warunkiem, którego nie umiemy sprawdzić (dyrekcje, godności), ma zostać odrzucony albo oznaczony „nie do oceny" — nigdy pokazany jako trafienie. Przypadek testowy wprost od klienta: rekord z „by dir." nie może się pojawić w interpretacji natalnej.
|
||||
|
||||
## 2. Czym są te dwa dokumenty
|
||||
|
||||
**TO DO (xlsx)** to właściwa lista robocza: arkusz „LISTA TO DO" z priorytetami (\*\*\* / \*\* / \* / brak = na tym etapie niekonieczne) oraz 16 arkuszy-wzorców z przykładowymi tabelami (tabela stopni, synastria, lista aspektów, profekcje, tranzyty, asteroidy, dane obiektów, godziny planetarne, syzygia, żywioły, orby, obiekty). Ostatnia poprawka: 05.09.2026.
|
||||
|
||||
**Specyfikacja (docx)** to „Kompletna specyfikacja techniczna" w 21 częściach. Według klienta to efekt ponad 20 godzin pracy z agentem AI, wciąż niedokończony; ma być czytana razem z TO DO. Ustalenia z formatowania dokumentu [kod — odczyt XML]:
|
||||
|
||||
- **Legenda kolorów** (cieniowanie akapitów): pomarańczowy = „to jeszcze uzupełnię", żółty = „kompletnie do poprawki", łososiowy = „chyba out".
|
||||
- Flagi występują **tylko w częściach 1–2**. W części 2, zaraz po „Accidental dignities", stoi znacznik **„{DOTĄD SPRAWDZONE}"**. Wszystko dalej — części 3–21: aspekty, Lots, midpointy, astrologia wedyjska, gwiazdy stałe, wszystkie techniki predykcyjne, dashy, numerologia, chiromancja — to **nieprzejrzany wynik AI**.
|
||||
- Kolor „chyba out" nie występuje ani razu: nic nie jest jeszcze wykreślone.
|
||||
- „Do poprawki" dotyczy formatu tabeli orbów (1.2). To zgodne z niespójnością tej tabeli: Słońce i Księżyc mają w niej 8°, a obok stoi „+2° bonusu dla świateł" — nie wiadomo, czy bonus jest już wliczony.
|
||||
- Czerwona czcionka w częściach 1–2 to **specyfikacja kolorów UI** (osłabienia, domy kadentne i ujemne punkty na czerwono), a nie znacznik „bzdura".
|
||||
- Dokument jest urwany: „TU WYWALIŁ BŁĄD, KONTYNUACJA" w części 11 i „TU SIĘ SKOŃCZYŁ LIMIT WĄTKU" w części 21.
|
||||
|
||||
**Specyfikacja zawiera błędy merytoryczne.** Lista aspektów pobocznych w części 1.2 podaje np. „0.5° Vigintile (1/40)" i „11.25° Undecile? (1/32)". W rzeczywistości vigintyl to 18° (1/20), a undecyl 32,73° (1/11) — arkusz „ASPECTS" w TO DO ma te wartości poprawnie. **Przy konflikcie źródłem prawdy jest xlsx; specyfikację traktujemy jako skorowidz do weryfikacji.**
|
||||
|
||||
## 3. Błędy w działającej aplikacji (to nie są nowe wymagania)
|
||||
|
||||
| TO DO | Prio | Zgłoszenie | Ocena |
|
||||
|---|---|---|---|
|
||||
| 11 | \*\*\* | Interpretacje pokazują nieobecne sygnifikatory | **[kod]** potwierdzone — sekcja 1. |
|
||||
| 9 | \*\*\* | Pliki 5 MB i 7,75 MB kończą się „Gateway Timeout" | **[hipoteza]** Odpowiedź 504 zwraca proxy/ingress, nie aplikacja — w warstwach prezentacji i danych nie ma limitu rozmiaru. Do sprawdzenia w repo deploy (timeout ingressu, limit rozmiaru żądania). |
|
||||
| 9 | \*\*\* | Komunikat „przyjęty, ale nie nadaje się do użycia" pojawia się dopiero przy ponownym wgraniu | Walidacja struktury powinna iść od razu po wgraniu, razem z opisem wymaganego układu kolumn (najlepiej jako stała podpowiedź w UI). |
|
||||
| 10 | \*\*\* | Brak listy wgranych plików (usuń / sprawdź / aktywuj) | **[hipoteza]** DAN-27 i PRE-09 mają status „Zrobione". Klient najpewniej pracuje na wariancie albo koncie bez tej zakładki: astroklient jest z założenia „bez administracji" (commit 10970c5), a zakładki są bramkowane uprawnieniami (PRE-27). Ustalić, gdzie to widać. |
|
||||
| 12 | \*\*\* | Ekran „Sygnifikatory" — nie wiadomo, do czego służy | UX: ekran wymaga wpisywania tokenów w składni bazy. Potrzebny opis i podpowiedzi albo przeprojektowanie. |
|
||||
|
||||
## 4. Zmiany przekrojowe
|
||||
|
||||
- **Interfejs po angielsku (TO DO 6, \*\*\*).** Cały obecny interfejs jest po polsku. Dotyczy wszystkich trzech wariantów (astrololo, astroklient, astrodemo) — najlepiej zrobić to jednym mechanizmem i18n przy okazji porządkowania wariantów, a nie trzy razy.
|
||||
- **Jasny motyw i kolorowanie (TO DO 7, \*\*\*).** Bez trybu ciemnego. Kolory komórek dla grup aspektów, żywiołów znaków i planet; tło komórki to bardzo jasna wersja koloru.
|
||||
- **„Symbol + skrót" w tabelach (TO DO 8, \*\*).** `☍ opp`, `☉ Su`, `♐ Sag` — nigdy pełne nazwy. Rozstrzyga pytanie Q-05 i jest zgodne z DAN-18 (Unicode jako tekst, nie emoji).
|
||||
|
||||
## 5. Mapowanie TO DO na rejestr wymagań
|
||||
|
||||
Statusy według rejestru **[rejestr]**, o ile nie zaznaczono inaczej.
|
||||
|
||||
### Istnieje w rejestrze — klient podnosi priorytet albo doprecyzowuje
|
||||
|
||||
| TO DO | Prio | Temat | ID | Status | Co się zmienia |
|
||||
|---|---|---|---|---|---|
|
||||
| 19–20 | \*\*\* | Baza HOROSCOPES: zapis, ulubione, ostatnie, kopiuj/wklej | DAN-04 | Do zrobienia | Realnie pierwsza kolejka. Kopiowanie i wklejanie w formacie czytelnym bez kodu (TSV albo „;"). |
|
||||
| 23 | \*\* | Baza SUBHOROSCOPES (relacje: mąż, dziecko, ślub…) | DAN-05 | Do zrobienia | Klient podaje schemat: ID = ImięNazwiskoData (+ litera przy powtórce), pola TYPE i SUBJECT. |
|
||||
| 24 | \*\*\* | Orby per obiekt × per aspekt | DAN-06, PRE-06 | Do zrobienia / Zrobione | Dziś jedna orba + bonus świateł. Potrzebna macierz; arkusz ORBS dokłada „metody" według autorów i szkół. |
|
||||
| 25–26 | \*\*\* / \*\* | Obiekty do wyświetlania: trzy przełączniki na obiekt, bez odświeżania strony | PRE-08 | Do zrobienia | Pokaż na kole / rysuj aspekty / pokaż w aspektarianie. |
|
||||
| 27, 56–65 | \*\*\* | Ascensional times + minor planetary years + kombinacje | LOG-13, DAN-07 | Do zrobienia | Z „Could" na \*\*\*. Pełny algorytm: reguła Valensa, „model A" Patricka Watsona, ułamki 1/3, 1/2 i 2/3, pomijanie znaków powyżej 66° i 78° szerokości. |
|
||||
| 28–31 | \*\*\* | Synastria — przebudowa | PRE-04 | Zrobione | **[kod]** Dziś bez wyboru lokalizacji (strefa i współrzędne wpisywane ręcznie), jedna orba, aspekty poboczne włączane hurtem. Potrzebne: pełne dane obu osób, orby per aspekt, wynik „obiekt A — aspekt — obiekt B", bi-wheel (A w środku, B na zewnątrz) przełączany z widokiem obok siebie, pełny kwadrat aspektów zamiast trójkąta. |
|
||||
| 35–36 | \*\*\* / \* | Wybór aspektów z grupami + okno aspektów pobocznych | PRE-06 | Zrobione | Przyciski major / hard / soft / minor medical; okno aspektów pobocznych z sortowaniem według stopni i harmonik. |
|
||||
| 41 | \*\*\* | Stacje: poprzednia i następna — dni, data, stopień, link | LOG-03 | Zrobione | Dodatkowe pola, flaga „< 7 dni", hiperłącze do horoskopu na moment stacji. |
|
||||
| 44 | \*\*\* | Lots z `lots astroseek.xlsx` | LOG-08, DAN-08 | Zrobione / W trakcie | Odwracanie formuły w nocy + warunki specjalne (klient uzupełni). |
|
||||
| 52–54 | \*\*\* | Zunifikowana tabela predykcji, kopiowana do Excela jako jedna tabela | LOG-14 | Zrobione | Format kolumn według arkusza „transits, predictive"; typy event / period / mundane. |
|
||||
| 85 | \*\*\* | Firdaria | LOG-11 | W trakcie | Klient odsyła do `fidaria calculator.xls`, którego nie ma w przekazanych plikach. |
|
||||
| 87, 89, 91–93 | \*\*\* | Zodiacal Releasing, Annual Divisions, Days of the Moon, Solar Monthly Revolutions, Lord of the Orb | LOG-11, LOG-12 | W trakcie / Zrobione | Lord of the Orb w dwóch wariantach (mod 84 i mod 12). |
|
||||
| 96 | \*\* | Primary Directions | LOG-09 | Do zrobienia | Pytanie Q-02 nadal otwarte. |
|
||||
|
||||
### Już jest — klient może o tym nie wiedzieć
|
||||
|
||||
| TO DO | Prio | Temat | ID |
|
||||
|---|---|---|---|
|
||||
| 37 | \*\*\* | Paralele i kontrparalele | LOG-07 (Zrobione) |
|
||||
| 40 | — | Zodiak drakoniczny | LOG-04 (Zrobione; **[kod]** draconic w `chart.py`) |
|
||||
| 45 | — | Solariusze, lunariusze | LOG-12 (Zrobione) |
|
||||
|
||||
### Nowe — nie ma ich w rejestrze
|
||||
|
||||
**Dane horoskopu i wejście:**
|
||||
- nazwa strefy czasowej i informacja o DST, np. CEST (TO DO 14, \*\*\*); UT/GMT oraz lokalny czas gwiazdowy (15–16, \*);
|
||||
- wklejanie danych w formacie Zet Astroprocessor (21, \*\*\*);
|
||||
- kilka zapisanych lokalizacji domyślnych z edycją listy (22, \*\*\*).
|
||||
|
||||
**Koło horoskopu (rozszerzenia PRE-12):**
|
||||
- zakotwiczenie: Asc / whole sign / Baran / Rak / Słońce / Księżyc (32);
|
||||
- granice terminów i dekanatów w kolorach, symbol planety po najechaniu kursorem (33);
|
||||
- style linii aspektów: pogrubiona = applying ≤ 3°, bardzo gruba = applying ≤ 1°, przerywana = separating; kolory i grubości konfigurowalne (49, \*\*\*).
|
||||
|
||||
**Tabele:**
|
||||
- tabela stopni 30 × 12 z podglądem „zoom" wybranego stopnia (50, \*\*\*);
|
||||
- tabela aspektów przez cały znak, out-of-sign, dokładność (34, \*\*\*);
|
||||
- midpointy (38, \*);
|
||||
- długość ekliptyczna w kilku formatach (46, \*).
|
||||
|
||||
**Techniki predykcyjne (\*):** progresje sekundarne, ingresy, okresy cienia retrogradacji, ekstrema szerokości ekliptycznej, OOB, fazy Księżyca i zaćmienia (73–83); profekcje ciągłe z wyborem klucza (94, \*\*).
|
||||
|
||||
**Poza astrologią zachodnią** (bez gwiazdek albo z jedną): numerologia (100, \*\*) z gotowym schematem tabeli wynikowej, chiromancja (101, \*), czytanie twarzy i sylwetki (102, \*), atlas medyczny i atlas historycznych krain (103–104), Astro\*Carto\*Graphy (98).
|
||||
|
||||
**Tylko w specyfikacji, nie ma w TO DO:** cała astrologia wedyjska (wargi D-N, 13 systemów dash, karaki, argala, shadbala, ashtakavarga, yogi), chińska (BaZi, Zi Wei, Xiu), majańska (Tzolkin) i setki nazwanych asteroid. **Do potwierdzenia z klientem, czy to w ogóle zakres.** Uwaga techniczna: numerowane asteroidy wymagają plików efemeryd Swiss Ephemeris, czyli silnika `engine-swisseph` (izolacja AGPL według LOG-27) — nasz silnik Skyfield ich nie policzy.
|
||||
|
||||
## 6. Brakujące materiały
|
||||
|
||||
Klient się na nie powołuje, ale nie ma ich w przekazanych plikach:
|
||||
- `wznoszenie znaków ascensional times.xlsx` (TO DO 56) i plik „wrzucony na dysk" (27);
|
||||
- `fidaria calculator.xls` (85);
|
||||
- dokończenie specyfikacji od części 21 (urwana) oraz przegląd części 3–21.
|
||||
|
||||
## 7. Pytania do klienta
|
||||
|
||||
1. Czy część wedyjska, chińska i majańska ze specyfikacji to zakres, czy tylko efekt uboczny pracy z AI? W TO DO jej nie ma.
|
||||
2. Interfejs po angielsku: we wszystkich trzech wariantach czy tylko w wersji dla klienta? Czy dotyczy to też raportów — prompt do LLM jest dziś po polsku (LOG-29)?
|
||||
3. Na którym wariancie i koncie widać brak listy plików (TO DO 10)?
|
||||
4. Tabela orbów: czy bonus +2° dla świateł dolicza się do wartości z tabeli, czy jest już w nich zawarty?
|
||||
5. Kiedy będą przejrzane części 3–21 specyfikacji — i czy do tego czasu traktujemy je jako nieobowiązujące?
|
||||
|
||||
## 8. Proponowana kolejność
|
||||
|
||||
1. **Naprawa dopasowania sygnifikatorów** (sekcja 1). Psuje Interpretacje, Kalendarz i prompt do LLM; dopóki działa źle, każda funkcja budowana na nim dziedziczy błąd.
|
||||
2. **Błędy wgrywania plików** (TO DO 9–10) — klient nie może wgrać własnych baz.
|
||||
3. **Baza HOROSCOPES, kopiuj/wklej, lokalizacje domyślne** (19–22) — codzienna wygoda, priorytet \*\*\*.
|
||||
4. **Synastria** (28–31) i **orby per obiekt × aspekt** (24) — orby są wspólne dla obu.
|
||||
5. **Zmiany przekrojowe UI** (angielski, jasny motyw, „symbol + skrót") razem z porządkowaniem wariantów astrololo / astroklient / astrodemo, żeby nie robić tego trzy razy.
|
||||
6. Dalej techniki z \*\*\* według TO DO; specyfikację dopiero po jej przejrzeniu przez klienta.
|
||||
|
||||
Rejestr `astrololo_wymagania.xlsx` celowo **nie** jest zmieniany w tym PR. Najpierw akceptacja tej analizy, potem naniesienie zmian — zgodnie z zasadą „przegląd → poprawki → akceptacja → kod".
|
||||
Binary file not shown.
Binary file not shown.
@@ -74,12 +74,7 @@ class LogicClient:
|
||||
"""Strumień NDJSON w dół — przekazywany dalej ramka po ramce.
|
||||
|
||||
Idzie przez szyfrowane łącze jak reszta ruchu (PRE-16): bez tego przy
|
||||
włączonym LINK_ENCRYPTION_REQUIRED serwer odrzuciłby żądanie.
|
||||
|
||||
Nie przechodzi przez `_post`, więc pulę konta dokłada sam — tak samo,
|
||||
z kontekstu i z pierwszeństwem przed polem przyniesionym w payloadzie.
|
||||
Bez tego horoskop strumieniowany szukałby w całym udziale."""
|
||||
payload = {**payload, "tenant": KONTO.get("")}
|
||||
włączonym LINK_ENCRYPTION_REQUIRED serwer odrzuciłby żądanie."""
|
||||
with httpx.Client(timeout=httpx.Timeout(None, connect=15.0)) as client:
|
||||
yield from link_crypto.stream_lines(
|
||||
client, f"{self.base_url}{sciezka}",
|
||||
|
||||
@@ -80,66 +80,15 @@ print("WYNIK:" + json.dumps({
|
||||
'''
|
||||
|
||||
|
||||
# Generowania tekstu w tym obrazie nie ma, ale most modułów zostaje — więc jego
|
||||
# transport musi nieść pulę z sesji tak samo jak wywołania ekranów. Osobna sonda,
|
||||
# bo dokłada trasę, której w produkcie nie ma: w sondzie produktu zafałszowałaby
|
||||
# każde pytanie o to, co produkt zawiera.
|
||||
SONDA_MODULU = '''
|
||||
import json, os, hashlib, secrets
|
||||
sol = secrets.token_bytes(16)
|
||||
dk = hashlib.scrypt(b"tajne", salt=sol, n=2**14, r=8, p=1, dklen=32)
|
||||
os.environ.update(ASTROKLIENT_USERS="klient:scrypt$%s$%s" % (sol.hex(), dk.hex()),
|
||||
SESSION_SECRET="t"*64, COOKIES_INSECURE="1", RATE_LIMIT_PER_MIN="0")
|
||||
from fastapi.responses import StreamingResponse
|
||||
from starlette.testclient import TestClient
|
||||
from app.main import app, logic
|
||||
from app import security, session, features
|
||||
from app.clients import logic_client
|
||||
wyslane = {}
|
||||
def call_json(client, method, url, payload=None, **kw):
|
||||
wyslane[url.split("/chart/", 1)[1]] = (payload or {}).get("tenant")
|
||||
return {}
|
||||
def stream_lines(client, url, payload=None, **kw):
|
||||
wyslane[url.split("/chart/", 1)[1]] = (payload or {}).get("tenant")
|
||||
yield '{"type": "result", "result": {}}'
|
||||
logic_client.link_crypto.call_json = call_json
|
||||
logic_client.link_crypto.stream_lines = stream_lines
|
||||
# Trasa w miejscu modułu: woła ten sam transport co moduł generowania, a strumień
|
||||
# konsumuje równie leniwie — dopiero przy wysyłaniu odpowiedzi, nie w obsłudze trasy.
|
||||
features.ROUTES[("POST", "/sonda/modul")] = None
|
||||
@app.post("/sonda/modul")
|
||||
def sonda_modul():
|
||||
podrobione = {"profile": "natal", "tenant": "ktos-inny"}
|
||||
logic.wywolaj("/chart/prompt", podrobione, 5)
|
||||
logic.wywolaj("/chart/horoscope", podrobione, 5)
|
||||
def relay():
|
||||
yield from logic.strumien("/chart/horoscope/stream", podrobione)
|
||||
return StreamingResponse(relay(), media_type="application/x-ndjson")
|
||||
c = TestClient(app, follow_redirects=False)
|
||||
H = {"Cookie": f"{session.COOKIE}={security.issue_session('klient')}"}
|
||||
r = c.post("/sonda/modul", headers=H)
|
||||
print("WYNIK:" + json.dumps({"status": r.status_code, "pule": wyslane}))
|
||||
'''
|
||||
|
||||
|
||||
def _uruchom(drzewo: pathlib.Path, sonda: str) -> dict:
|
||||
@pytest.fixture(scope="module")
|
||||
def wynik(drzewo):
|
||||
"""Aplikacja uruchomiona w OSOBNYM PROCESIE.
|
||||
|
||||
Importy Pythona są zapamiętywane w procesie, więc sprawdzanie „produktu bez
|
||||
modułu" tam, gdzie moduł już się zaimportował, dałoby wynik fałszywie
|
||||
pozytywny."""
|
||||
r = subprocess.run([sys.executable, "-c", sonda], cwd=drzewo, capture_output=True,
|
||||
r = subprocess.run([sys.executable, "-c", SONDA], cwd=drzewo, capture_output=True,
|
||||
text=True, env={"PYTHONPATH": str(drzewo), "PATH": "/usr/bin:/bin"})
|
||||
linia = next((l for l in r.stdout.splitlines() if l.startswith("WYNIK:")), None)
|
||||
assert linia, f"sonda nie wystartowała:\n{r.stdout[-2000:]}\n{r.stderr[-2000:]}"
|
||||
return json.loads(linia[len("WYNIK:"):])
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def wynik(drzewo):
|
||||
return _uruchom(drzewo, SONDA)
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def wynik_modulu(drzewo):
|
||||
return _uruchom(drzewo, SONDA_MODULU)
|
||||
|
||||
@@ -44,12 +44,3 @@ def test_the_pool_comes_from_the_session_not_from_the_form(wynik):
|
||||
"""Pula jedzie z zalogowanego konta. Gdyby brała się z żądania, wystarczyłoby
|
||||
podstawić cudzy login, żeby czytać cudze pliki — sonda właśnie to próbuje."""
|
||||
assert wynik["pula"] == "klient"
|
||||
|
||||
|
||||
def test_module_transport_carries_the_session_pool_too(wynik_modulu):
|
||||
"""Prompt i horoskop idą mostem modułów, a strumień horoskopu obok `_post` —
|
||||
i właśnie tam pula ginęła, więc horoskop szukałby w całym udziale. Sonda
|
||||
podsuwa cudzy login w payloadzie; ma wygrać konto z sesji."""
|
||||
assert wynik_modulu["status"] == 200
|
||||
assert wynik_modulu["pule"] == {"prompt": "klient", "horoscope": "klient",
|
||||
"horoscope/stream": "klient"}
|
||||
|
||||
@@ -294,10 +294,6 @@ class PromptRequest(BaseModel):
|
||||
from_date: str | None = None
|
||||
to_date: str | None = None
|
||||
techniques: list[str] | None = None
|
||||
# Pula konta (PRE-29) — puste = cały udział. Warstwa logiczna niczego tu nie
|
||||
# rozstrzyga, tylko przenosi: kto do jakiej puli ma prawo, wie warstwa
|
||||
# prezentacji, bo to ona zna konta. HoroscopeRequest dziedziczy to pole.
|
||||
tenant: str = ""
|
||||
|
||||
|
||||
@app.post("/chart/prompt")
|
||||
@@ -340,7 +336,7 @@ def chart_prompt(req: PromptRequest) -> dict:
|
||||
report: dict = {"objects": []}
|
||||
try:
|
||||
report = build_report(
|
||||
chart["positions"], DataClient(tenant=req.tenant),
|
||||
chart["positions"], DataClient(),
|
||||
aspects=chart.get("aspects"), parallels=chart.get("parallels"),
|
||||
per_object_limit=req.limit,
|
||||
)
|
||||
@@ -362,7 +358,7 @@ def chart_prompt(req: PromptRequest) -> dict:
|
||||
events = build_timeline(engine, moment, points, req.from_date, req.to_date,
|
||||
req.techniques)
|
||||
try:
|
||||
interpret_events(events, DataClient(tenant=req.tenant))
|
||||
interpret_events(events, DataClient())
|
||||
except httpx.HTTPError as e:
|
||||
data_error = f"Warstwa danych niedostępna: {e}" # oś czasu zostaje
|
||||
out = build_period_prompt(chart, events, req.from_date, req.to_date,
|
||||
@@ -631,7 +627,7 @@ def chart_timeline(req: TimelineRequest) -> dict:
|
||||
if req.interpret:
|
||||
from app.significators import interpret_events
|
||||
try:
|
||||
interpret_events(events, DataClient(tenant=req.tenant))
|
||||
interpret_events(events, DataClient())
|
||||
except httpx.HTTPError as e:
|
||||
out["data_error"] = f"Warstwa danych niedostępna: {e}"
|
||||
out.update(count=len(events), events=events)
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
"""Pula konta (PRE-29) dociera do warstwy danych z KAŻDEJ trasy, która w niej szuka.
|
||||
|
||||
Trasy są wołane przez HTTP, a DataClient podmieniony na szpiega, który zapisuje,
|
||||
z jaką pulą go zbudowano i czy naprawdę o coś zapytał. Celowo nie czytamy źródła:
|
||||
napis `tenant=` w kodzie nie mówi, czy pula przeżyła drogę od żądania do bazy.
|
||||
A pominięta pula nie daje błędu — daje cały udział, czyli cudze pliki.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
|
||||
import pytest
|
||||
from starlette.testclient import TestClient
|
||||
|
||||
from app import link_crypto, main
|
||||
|
||||
POOL = "konto-a"
|
||||
PERIOD = {"from_date": "2024-01-01", "to_date": "2024-06-30"}
|
||||
# Nieznany dostawca kończy horoskop na LLMError zaraz po zbudowaniu promptu —
|
||||
# bez sieci i bez klucza API, a warstwa danych jest już wtedy przepytana.
|
||||
NO_PROVIDER = {"provider": "brak-dostawcy"}
|
||||
|
||||
ROUTES = [
|
||||
pytest.param("/chart/report", {}, id="report"),
|
||||
pytest.param("/chart/timeline", {**PERIOD, "interpret": True}, id="timeline"),
|
||||
pytest.param("/chart/prompt", {"profile": "natal"}, id="prompt-natal"),
|
||||
pytest.param("/chart/prompt", {"profile": "period", **PERIOD}, id="prompt-period"),
|
||||
pytest.param("/chart/horoscope", {"profile": "natal", **NO_PROVIDER}, id="horoscope-natal"),
|
||||
pytest.param("/chart/horoscope", {"profile": "period", **PERIOD, **NO_PROVIDER},
|
||||
id="horoscope-period"),
|
||||
pytest.param("/chart/horoscope/stream", {"profile": "natal", **NO_PROVIDER},
|
||||
id="stream-natal"),
|
||||
pytest.param("/chart/horoscope/stream", {"profile": "period", **PERIOD, **NO_PROVIDER},
|
||||
id="stream-period"),
|
||||
]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def clients(monkeypatch, own_engine) -> list:
|
||||
"""Każdy DataClient zbudowany w trakcie żądania, w kolejności tworzenia."""
|
||||
built: list = []
|
||||
|
||||
class SpyDataClient:
|
||||
def __init__(self, base_url: str | None = None, tenant: str = "") -> None:
|
||||
self.tenant = tenant
|
||||
self.searches = 0
|
||||
built.append(self)
|
||||
|
||||
def search(self, key, value, exact, limit, fields=None):
|
||||
self.searches += 1
|
||||
return {"provider": "szpieg", "total": 0, "rows": []}
|
||||
|
||||
monkeypatch.setattr(main, "DataClient", SpyDataClient)
|
||||
monkeypatch.setattr(main, "get_engine", lambda: own_engine)
|
||||
for env in ("INTERNAL_TOKEN", link_crypto.ENV_REQUIRED, link_crypto.ENV_PRESENTATION_LOGIC):
|
||||
monkeypatch.delenv(env, raising=False)
|
||||
return built
|
||||
|
||||
|
||||
def _call(path: str, body: dict, reference_moment) -> None:
|
||||
payload = {"when_utc": reference_moment.when_utc.isoformat(),
|
||||
"lat": reference_moment.lat, "lon": reference_moment.lon, **body}
|
||||
r = TestClient(main.app).post(path, json=payload)
|
||||
assert r.status_code == 200, r.text
|
||||
if path.endswith("/stream"):
|
||||
# strumień zawsze odpowiada 200 — o porażce mówi dopiero ostatnia ramka
|
||||
last = json.loads(r.text.strip().splitlines()[-1])
|
||||
assert last["type"] == "result", last
|
||||
|
||||
|
||||
@pytest.mark.parametrize("path, body", ROUTES)
|
||||
def test_account_pool_reaches_the_data_layer(clients, reference_moment, path, body):
|
||||
_call(path, {**body, "tenant": POOL}, reference_moment)
|
||||
|
||||
# Bez choćby jednego wyszukiwania test przechodziłby na pusto.
|
||||
assert any(c.searches for c in clients), "trasa nie zapytała warstwy danych"
|
||||
assert {c.tenant for c in clients} == {POOL}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("path, body", ROUTES)
|
||||
def test_without_a_pool_the_route_searches_the_whole_share(clients, reference_moment, path, body):
|
||||
"""Pełna aplikacja nie zna kont i nie wysyła puli — dla niej nic się nie zmienia."""
|
||||
_call(path, body, reference_moment)
|
||||
|
||||
assert any(c.searches for c in clients)
|
||||
assert {c.tenant for c in clients} == {""}
|
||||
Reference in New Issue
Block a user