Files
astrololo/services/data/app/cache/frame_cache.py
T
gitea 16d35c16dc Szkielet aplikacji trójwarstwowej (prezentacja / logika / dane)
Trzy niezależne usługi FastAPI komunikujące się przez HTTP/JSON, każda
zna tylko adres warstwy bezpośrednio pod nią:

- presentation (:8000) — strona WWW + formularz
- logic (:8001) — reguły biznesowe, pośrednik
- data (:8002) — wyszukiwanie danych za interfejsem DataProvider

Warstwa danych: czytanie setek plików .xlsx z wykrywaniem nagłówka i
mapowaniem układu kolumn na schemat kanoniczny, z 4-poziomowym cache
(schemat L1, Parquet L2, wyniki zapytań L3, odwrócony indeks L4) i
unieważnianiem po odcisku pliku. Gotowa ścieżka migracji do SQL
(ingest/to_sql.py + SqlDataProvider, przełączane przez DATA_PROVIDER).

Zawiera docker-compose, Makefile, generator danych przykładowych.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 17:35:09 +02:00

36 lines
1.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Cache danych: znormalizowany arkusz zapisany jako Parquet.
POZIOM 2 cache — największy zysk wydajności. Parsowanie .xlsx jest wolne
(dziesiątkisetki ms na duży plik). Po pierwszym wczytaniu zapisujemy
znormalizowaną ramkę jako Parquet (kolumnowy, kompresowany), kluczowaną odciskiem
pliku. Kolejne odczyty ładują Parquet — zwykle 10100x szybciej niż .xlsx i bez
ponownego wykrywania nagłówka.
Plik Parquet jest też naturalnym formatem pośrednim przy migracji do SQL.
"""
from __future__ import annotations
from pathlib import Path
import pandas as pd
class FrameCache:
def __init__(self, cache_dir: Path) -> None:
self.dir = cache_dir / "frames"
self.dir.mkdir(parents=True, exist_ok=True)
def _path(self, fp: str, sheet: str) -> Path:
safe = fp.replace(":", "_") + "__" + str(sheet).replace("/", "_")
return self.dir / f"{safe}.parquet"
def get(self, fp: str, sheet: str) -> pd.DataFrame | None:
p = self._path(fp, sheet)
if p.exists():
return pd.read_parquet(p)
return None
def put(self, fp: str, sheet: str, frame: pd.DataFrame) -> None:
# astype(str) na kolumnach object zapewnia stabilny zapis Parquet
frame.to_parquet(self._path(fp, sheet), index=False)