mirror of
https://github.com/migatu/astrololo.git
synced 2026-07-14 21:38:37 +00:00
16d35c16dc
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>
44 lines
1.5 KiB
Python
44 lines
1.5 KiB
Python
"""Wczytanie pojedynczego arkusza do znormalizowanej ramki danych.
|
|
|
|
Łączy wykrywanie nagłówka (header_detect) z mapowaniem układu kolumn (layout).
|
|
Zwraca ramkę o KANONICZNYCH nazwach kolumn — gotową do indeksowania, cache'owania
|
|
(parquet) i ewentualnego załadowania do SQL.
|
|
|
|
To jest jedyne miejsce, które "rozumie" format Excela. Reszta systemu jej nie
|
|
widzi.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
|
|
import pandas as pd
|
|
|
|
from app.excel.header_detect import detect_header_row
|
|
from app.excel.layout import build_column_mapping
|
|
|
|
|
|
@dataclass
|
|
class LoadedSheet:
|
|
frame: pd.DataFrame # dane z kanonicznymi kolumnami
|
|
header_row: int # wykryty indeks nagłówka
|
|
column_mapping: dict[str, str] # pole_kanoniczne -> oryginalna_nazwa
|
|
|
|
|
|
def load_sheet(path: str, sheet: str | int = 0, header_scan_rows: int = 15) -> LoadedSheet:
|
|
raw = pd.read_excel(path, sheet_name=sheet, header=None, dtype=object)
|
|
header_row = detect_header_row(raw, max_scan=header_scan_rows)
|
|
|
|
header_cells = [str(c) for c in raw.iloc[header_row].tolist()]
|
|
mapping = build_column_mapping(header_cells)
|
|
|
|
data = raw.iloc[header_row + 1 :].copy()
|
|
data.columns = header_cells
|
|
data = data.dropna(how="all")
|
|
|
|
# przenazwij na kanoniczne pola: {oryginał -> kanoniczne}
|
|
inverse = {orig: canon for canon, orig in mapping.items()}
|
|
data = data.rename(columns=inverse)
|
|
data = data.reset_index(drop=True)
|
|
|
|
return LoadedSheet(frame=data, header_row=header_row, column_mapping=mapping)
|