hackathon-ENGIN/engin/io.py

59 lines
2.1 KiB
Python

"""CSV input adapters. The service depends on the protocol, not pandas I/O directly."""
from __future__ import annotations
from io import BytesIO
from pathlib import Path
from typing import Protocol
import pandas as pd
from .errors import InputDataError
class FrameReader(Protocol):
def read_bytes(self, payload: bytes) -> pd.DataFrame: ...
def read_path(self, path: Path) -> pd.DataFrame: ...
class PandasCsvReader:
def __init__(self, max_bytes: int) -> None:
self.max_bytes = max_bytes
def read_bytes(self, payload: bytes) -> pd.DataFrame:
if not payload:
raise InputDataError("Przesłany plik jest pusty.", code="empty_file")
if len(payload) > self.max_bytes:
limit_mb = self.max_bytes / (1024 * 1024)
raise InputDataError(
f"Plik przekracza limit {limit_mb:.0f} MB.",
hint="Podziel dane na mniejsze kompletne zestawy silników.",
code="file_too_large",
)
try:
return pd.read_csv(BytesIO(payload)).reset_index(drop=True)
except UnicodeDecodeError as exc:
raise InputDataError(
"Plik nie jest poprawnym tekstowym CSV w kodowaniu UTF-8.",
hint="Zapisz plik jako CSV UTF-8 i spróbuj ponownie.",
code="invalid_encoding",
) from exc
except (pd.errors.EmptyDataError, pd.errors.ParserError) as exc:
raise InputDataError(
"Nie udało się odczytać struktury CSV.",
hint="Sprawdź separator, nagłówek oraz liczbę kolumn w każdym wierszu.",
code="malformed_csv",
) from exc
def read_path(self, path: Path) -> pd.DataFrame:
try:
payload = path.read_bytes()
except OSError as exc:
raise InputDataError(
f"Nie można odczytać pliku {path.name!r}.",
hint="Sprawdź, czy plik istnieje i ma uprawnienia do odczytu.",
code="file_unavailable",
) from exc
return self.read_bytes(payload)