feat: Enhance CSV cleaning service with new endpoints for CSV analysis and preview functionality. Introduce support for job management linked to extraction batches, and update the cleaning rules structure. Add new utility functions for handling CSV data and improve documentation for clarity on usage and available endpoints.
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
"""Tipos de columna y resumen de análisis."""
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Optional
|
||||
|
||||
TYPE_INTEGER = "integer"
|
||||
TYPE_DECIMAL = "decimal"
|
||||
TYPE_DATE = "date"
|
||||
TYPE_BOOLEAN = "boolean"
|
||||
TYPE_CATEGORICAL = "categorical"
|
||||
TYPE_TEXT = "text"
|
||||
TYPE_MIXED = "mixed"
|
||||
TYPE_EMPTY = "empty"
|
||||
|
||||
|
||||
@dataclass
|
||||
class ColumnSummary:
|
||||
"""Resumen inteligente para una columna del DataFrame."""
|
||||
name: str
|
||||
inferred_type: str
|
||||
example: str = ""
|
||||
samples: list[str] = field(default_factory=list)
|
||||
null_count: int = 0
|
||||
null_pct: float = 0.0
|
||||
unique_count: int = 0
|
||||
unique_pct: float = 0.0
|
||||
min: Optional[float] = None
|
||||
max: Optional[float] = None
|
||||
mean: Optional[float] = None
|
||||
avg_length: Optional[float] = None
|
||||
top_values: list[tuple[str, int]] = field(default_factory=list)
|
||||
detected_date_format: Optional[str] = None
|
||||
applicable_rules: list[str] = field(default_factory=list)
|
||||
|
||||
def info_text(self) -> str:
|
||||
"""Texto humano con información adicional (para subtítulo de la UI)."""
|
||||
parts: list[str] = []
|
||||
parts.append(f"nulos: {self.null_count} ({self.null_pct:.0f}%)")
|
||||
parts.append(f"únicos: {self.unique_count}")
|
||||
if self.inferred_type in (TYPE_INTEGER, TYPE_DECIMAL):
|
||||
if self.min is not None:
|
||||
parts.append(f"min: {_fmt_num(self.min)}")
|
||||
if self.max is not None:
|
||||
parts.append(f"max: {_fmt_num(self.max)}")
|
||||
if self.mean is not None:
|
||||
parts.append(f"media: {_fmt_num(self.mean)}")
|
||||
elif self.inferred_type == TYPE_DATE:
|
||||
if self.detected_date_format:
|
||||
parts.append(f"formato: {self.detected_date_format}")
|
||||
elif self.inferred_type in (TYPE_TEXT, TYPE_CATEGORICAL):
|
||||
if self.avg_length is not None:
|
||||
parts.append(f"long. prom: {self.avg_length:.1f}")
|
||||
if self.top_values:
|
||||
tops = ", ".join(f'"{v}" ({c})' for v, c in self.top_values[:3])
|
||||
parts.append(f"top: {tops}")
|
||||
return " · ".join(parts)
|
||||
|
||||
|
||||
def _fmt_num(x: float) -> str:
|
||||
if x is None:
|
||||
return ""
|
||||
if isinstance(x, float) and x.is_integer():
|
||||
return str(int(x))
|
||||
if isinstance(x, float):
|
||||
return f"{x:.2f}"
|
||||
return str(x)
|
||||
Reference in New Issue
Block a user