59 lines
2.1 KiB
Python
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)
|