mirror of
https://github.com/migatu/astrololo.git
synced 2026-07-14 21:38:37 +00:00
Napraw lokalne środowisko dev: Parquet mixed-types, brak deps, compose
Trzy usterki uniemożliwiające uruchomienie stosu lokalnie: 1. Warstwa danych wykładała się na starcie przy zapisie Parquet dla realnych plików (np. Encyclopaedia of Medical Astrology) — kolumny o mieszanych typach (int+str+NaN). frame_cache.put() zapisuje teraz ramkę jako string (warstwa i tak wyszukuje po tekście). Dodatkowo warmup() jest odporny: pojedynczy uszkodzony plik nie blokuje startu usługi. 2. Brakowało kroku instalacji zależności — dev-logic/dev-presentation padały na 'No module named httpx'. Nowy cel `make install` instaluje zależności WSZYSTKICH warstw do aktywnego venv. README zaktualizowane (instalowało wcześniej tylko warstwę danych). 3. `make up` zakładał `docker compose`, którego użytkownik nie ma. Makefile wykrywa `docker compose` lub `docker-compose`, a przy braku obu podaje czytelną instrukcję trybu lokalnego. Dodano `make test` i `make clean-cache`. Zweryfikowane end-to-end na realnym środowisku (.env, Python 3.14) i danych: warmup przechodzi (3 pliki), cały stos wstaje, formularz → logika → silnik Skyfield zwraca poprawne pozycje. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,31 +1,56 @@
|
|||||||
.PHONY: help up down sample reindex migrate sql dev-data dev-logic dev-presentation
|
.PHONY: help install up down sample reindex migrate sql test clean-cache \
|
||||||
|
dev-data dev-logic dev-presentation
|
||||||
|
|
||||||
|
# Autodetekcja narzędzia compose: nowe "docker compose" albo stare "docker-compose".
|
||||||
|
COMPOSE := $(shell if docker compose version >/dev/null 2>&1; then echo "docker compose"; \
|
||||||
|
elif command -v docker-compose >/dev/null 2>&1; then echo "docker-compose"; fi)
|
||||||
|
|
||||||
|
# Python aktywnego środowiska (venv). Nadpisz: make install PY=python3.12
|
||||||
|
PY ?= python
|
||||||
|
|
||||||
help:
|
help:
|
||||||
@echo "up - uruchom wszystkie 3 warstwy (docker compose)"
|
@echo "install - zainstaluj zależności WSZYSTKICH warstw do aktywnego venv"
|
||||||
@echo "down - zatrzymaj"
|
@echo "sample - wygeneruj przykładowe pliki .xlsx"
|
||||||
@echo "sample - wygeneruj przykładowe pliki .xlsx"
|
@echo "dev-data - uruchom warstwę danych lokalnie (:8002)"
|
||||||
@echo "reindex - zbuduj cache + indeks warstwy bazodanowej"
|
@echo "dev-logic - uruchom warstwę logiczną lokalnie (:8001)"
|
||||||
@echo "migrate - ETL: Excel -> SQL"
|
@echo "dev-presentation - uruchom warstwę prezentacji lokalnie (:8000)"
|
||||||
@echo "sql - uruchom z warstwą SQL (DATA_PROVIDER=sql)"
|
@echo "test - testy warstwy logicznej (silnik efemeryd)"
|
||||||
@echo "dev-* - uruchom pojedynczą warstwę lokalnie (bez dockera)"
|
@echo "clean-cache - wyczyść regenerowalny cache warstwy danych"
|
||||||
|
@echo "reindex - zbuduj cache + indeks warstwy bazodanowej"
|
||||||
|
@echo "migrate - ETL: Excel -> SQL"
|
||||||
|
@echo "up / down - uruchom / zatrzymaj cały stos (Docker; wykryto: $(COMPOSE))"
|
||||||
|
@echo "sql - Docker z warstwą SQL (DATA_PROVIDER=sql)"
|
||||||
|
@echo ""
|
||||||
|
@echo "Szybki start lokalny (bez Dockera), w 3 terminalach:"
|
||||||
|
@echo " make install"
|
||||||
|
@echo " make sample # opcjonalnie: dane przykładowe"
|
||||||
|
@echo " make dev-data # terminal 1"
|
||||||
|
@echo " make dev-logic # terminal 2"
|
||||||
|
@echo " make dev-presentation # terminal 3 -> http://localhost:8000"
|
||||||
|
|
||||||
up:
|
# --- instalacja zależności do aktywnego środowiska ---
|
||||||
docker compose up --build
|
install:
|
||||||
|
$(PY) -m pip install \
|
||||||
down:
|
-r services/data/requirements.txt \
|
||||||
docker compose down
|
-r services/logic/requirements.txt \
|
||||||
|
-r services/presentation/requirements.txt
|
||||||
|
@echo "OK. Zależności warstw zainstalowane. (Silnik AGPL 'engine-swisseph' instaluje się osobno.)"
|
||||||
|
|
||||||
sample:
|
sample:
|
||||||
cd services/data && python scripts/make_sample_data.py
|
cd services/data && $(PY) scripts/make_sample_data.py
|
||||||
|
|
||||||
reindex:
|
reindex:
|
||||||
cd services/data && python -m app.ingest.build_index
|
cd services/data && $(PY) -m app.ingest.build_index
|
||||||
|
|
||||||
migrate:
|
migrate:
|
||||||
cd services/data && python -m app.ingest.to_sql
|
cd services/data && $(PY) -m app.ingest.to_sql
|
||||||
|
|
||||||
sql:
|
test:
|
||||||
DATA_PROVIDER=sql docker compose up --build
|
cd services/logic && PYTHONPATH=. $(PY) -m pytest tests -q
|
||||||
|
|
||||||
|
clean-cache:
|
||||||
|
rm -rf services/data/.cache/* services/logic/.ephemeris/* 2>/dev/null || true
|
||||||
|
@echo "Cache wyczyszczony."
|
||||||
|
|
||||||
# --- lokalny dev (3 osobne terminale) ---
|
# --- lokalny dev (3 osobne terminale) ---
|
||||||
dev-data:
|
dev-data:
|
||||||
@@ -36,3 +61,20 @@ dev-logic:
|
|||||||
|
|
||||||
dev-presentation:
|
dev-presentation:
|
||||||
cd services/presentation && LOGIC_URL=http://localhost:8001 uvicorn app.main:app --reload --port 8000
|
cd services/presentation && LOGIC_URL=http://localhost:8001 uvicorn app.main:app --reload --port 8000
|
||||||
|
|
||||||
|
# --- Docker (opcjonalnie) ---
|
||||||
|
up:
|
||||||
|
@if [ -z "$(COMPOSE)" ]; then \
|
||||||
|
echo "Nie znaleziono Docker Compose (ani 'docker compose', ani 'docker-compose')."; \
|
||||||
|
echo "Użyj trybu lokalnego: make install, potem dev-data / dev-logic / dev-presentation."; \
|
||||||
|
exit 1; \
|
||||||
|
fi
|
||||||
|
$(COMPOSE) up --build
|
||||||
|
|
||||||
|
down:
|
||||||
|
@if [ -z "$(COMPOSE)" ]; then echo "Brak Docker Compose."; exit 1; fi
|
||||||
|
$(COMPOSE) down
|
||||||
|
|
||||||
|
sql:
|
||||||
|
@if [ -z "$(COMPOSE)" ]; then echo "Brak Docker Compose."; exit 1; fi
|
||||||
|
DATA_PROVIDER=sql $(COMPOSE) up --build
|
||||||
|
|||||||
@@ -28,13 +28,20 @@ make up # zbuduj i uruchom 3 warstwy
|
|||||||
# otwórz http://localhost:8000
|
# otwórz http://localhost:8000
|
||||||
```
|
```
|
||||||
|
|
||||||
## Szybki start (lokalnie, 3 terminale)
|
## Szybki start (lokalnie, bez Dockera — zalecane)
|
||||||
|
```bash
|
||||||
|
python -m venv .env && source .env/bin/activate # venv (jednorazowo)
|
||||||
|
make install # zależności WSZYSTKICH warstw
|
||||||
|
make sample # opcjonalnie: dane przykładowe
|
||||||
|
```
|
||||||
|
Potem w 3 osobnych terminalach (w każdym `source .env/bin/activate`):
|
||||||
```bash
|
```bash
|
||||||
cd services/data && pip install -r requirements.txt && python scripts/make_sample_data.py
|
|
||||||
make dev-data # terminal 1 -> :8002
|
make dev-data # terminal 1 -> :8002
|
||||||
make dev-logic # terminal 2 -> :8001
|
make dev-logic # terminal 2 -> :8001
|
||||||
make dev-presentation # terminal 3 -> :8000
|
make dev-presentation # terminal 3 -> :8000 -> http://localhost:8000
|
||||||
```
|
```
|
||||||
|
> `make install` instaluje zależności wszystkich trzech warstw do aktywnego venv.
|
||||||
|
> Testy silnika: `make test`. Wyczyszczenie cache: `make clean-cache`.
|
||||||
|
|
||||||
## Modułowość — dowód
|
## Modułowość — dowód
|
||||||
- Wymień prezentację (np. na SPA/React) → reszta bez zmian, kontrakt `/api/query` stały.
|
- Wymień prezentację (np. na SPA/React) → reszta bez zmian, kontrakt `/api/query` stały.
|
||||||
|
|||||||
+7
-2
@@ -31,5 +31,10 @@ class FrameCache:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def put(self, fp: str, sheet: str, frame: pd.DataFrame) -> None:
|
def put(self, fp: str, sheet: str, frame: pd.DataFrame) -> None:
|
||||||
# astype(str) na kolumnach object zapewnia stabilny zapis Parquet
|
# Kolumny z heterogenicznych plików Excela bywają mieszane (int + str +
|
||||||
frame.to_parquet(self._path(fp, sheet), index=False)
|
# NaN w jednej kolumnie) — pyarrow tego nie zapisze. Warstwa danych i tak
|
||||||
|
# operuje na tekście (wyszukiwanie po .astype(str)), więc zapisujemy ramkę
|
||||||
|
# jako string (brak wartości -> pusty tekst). Gwarantuje to stabilny zapis
|
||||||
|
# Parquet niezależnie od zawartości pliku źródłowego.
|
||||||
|
safe = frame.astype("string").fillna("")
|
||||||
|
safe.to_parquet(self._path(fp, sheet), index=False)
|
||||||
|
|||||||
@@ -82,7 +82,10 @@ class ExcelDataProvider(DataProvider):
|
|||||||
|
|
||||||
def warmup(self) -> None:
|
def warmup(self) -> None:
|
||||||
for path in self._excel_files():
|
for path in self._excel_files():
|
||||||
self._ensure_indexed(path)
|
try:
|
||||||
|
self._ensure_indexed(path)
|
||||||
|
except Exception as e: # jeden uszkodzony plik nie może zablokować startu
|
||||||
|
print(f"[data] pominięto plik przy indeksowaniu: {path} — {e}")
|
||||||
|
|
||||||
def _excel_files(self) -> list[str]:
|
def _excel_files(self) -> list[str]:
|
||||||
base = Path(self.s.excel_dir)
|
base = Path(self.s.excel_dir)
|
||||||
|
|||||||
Reference in New Issue
Block a user