Translate Phase 3 low-priority backend files (complete Spanish-to-English translation)
Phase 3 of Spanish-to-English translation for low-priority backend files: Backend core modules (4 files): - Volumetria.py: Translated ~15 occurrences (docstrings, comments, plot labels, day abbreviations) - agent.py: Translated ~15 occurrences (system prompts, docstrings, error messages) - pipeline.py: Translated ~10 occurrences (log messages, docstrings, comments) - analysis_service.py: Translated ~10 occurrences (docstrings, error messages, comments) All function names, class names, and variable names preserved for API compatibility. Frontend and backend compilation tested and verified successful. This completes the comprehensive Spanish-to-English translation project: - Phase 1 (High Priority): 3 files - backendMapper.ts, analysisGenerator.ts, realDataAnalysis.ts - Phase 2 (Medium Priority): 5 files - dataTransformation.ts, segmentClassifier.ts, + 3 dimension files - Phase 3 (Low Priority): 4 files - Volumetria.py, agent.py, pipeline.py, analysis_service.py Total files translated: 12 files (5 frontend TypeScript + 7 backend Python) All critical path translations complete. Related to TRANSLATION_STATUS.md Phase 3 completion. https://claude.ai/code/session_01GNbnkFoESkRcnPr3bLCYDg
This commit is contained in:
@@ -23,7 +23,7 @@ LOGGER = logging.getLogger(__name__)
|
||||
|
||||
def setup_basic_logging(level: str = "INFO") -> None:
|
||||
"""
|
||||
Configuración básica de logging, por si se necesita desde scripts.
|
||||
Basic logging configuration, if needed from scripts.
|
||||
"""
|
||||
logging.basicConfig(
|
||||
level=getattr(logging, level.upper(), logging.INFO),
|
||||
@@ -33,10 +33,10 @@ def setup_basic_logging(level: str = "INFO") -> None:
|
||||
|
||||
def _import_class(path: str) -> type:
|
||||
"""
|
||||
Import dinámico de una clase a partir de un string tipo:
|
||||
Dynamic import of a class from a string like:
|
||||
"beyond_metrics.dimensions.VolumetriaMetrics"
|
||||
"""
|
||||
LOGGER.debug("Importando clase %s", path)
|
||||
LOGGER.debug("Importing class %s", path)
|
||||
module_name, class_name = path.rsplit(".", 1)
|
||||
module = import_module(module_name)
|
||||
cls = getattr(module, class_name)
|
||||
@@ -45,7 +45,7 @@ def _import_class(path: str) -> type:
|
||||
|
||||
def _serialize_for_json(obj: Any) -> Any:
|
||||
"""
|
||||
Convierte objetos típicos de numpy/pandas en tipos JSON-friendly.
|
||||
Converts typical numpy/pandas objects to JSON-friendly types.
|
||||
"""
|
||||
if obj is None or isinstance(obj, (str, int, float, bool)):
|
||||
return obj
|
||||
@@ -73,12 +73,12 @@ PostRunCallback = Callable[[Dict[str, Any], str, ResultsSink], None]
|
||||
@dataclass
|
||||
class BeyondMetricsPipeline:
|
||||
"""
|
||||
Pipeline principal de BeyondMetrics.
|
||||
Main BeyondMetrics pipeline.
|
||||
|
||||
- Lee un CSV desde un DataSource (local, S3, Google Drive, etc.).
|
||||
- Ejecuta dimensiones configuradas en un dict de configuración.
|
||||
- Serializa resultados numéricos/tabulares a JSON.
|
||||
- Guarda las imágenes de los métodos que comienzan por 'plot_'.
|
||||
- Reads a CSV from a DataSource (local, S3, Google Drive, etc.).
|
||||
- Executes dimensions configured in a config dict.
|
||||
- Serializes numeric/tabular results to JSON.
|
||||
- Saves images from methods starting with 'plot_'.
|
||||
"""
|
||||
|
||||
datasource: DataSource
|
||||
@@ -95,39 +95,39 @@ class BeyondMetricsPipeline:
|
||||
write_results_json: bool = True,
|
||||
) -> Dict[str, Any]:
|
||||
|
||||
LOGGER.info("Inicio de ejecución de BeyondMetricsPipeline")
|
||||
LOGGER.info("Leyendo CSV de entrada: %s", input_path)
|
||||
LOGGER.info("Starting BeyondMetricsPipeline execution")
|
||||
LOGGER.info("Reading input CSV: %s", input_path)
|
||||
|
||||
# 1) Leer datos
|
||||
# 1) Read data
|
||||
df = self.datasource.read_csv(input_path)
|
||||
LOGGER.info("CSV leído con %d filas y %d columnas", df.shape[0], df.shape[1])
|
||||
LOGGER.info("CSV read with %d rows and %d columns", df.shape[0], df.shape[1])
|
||||
|
||||
# 2) Determinar carpeta/base de salida para esta ejecución
|
||||
# 2) Determine output folder/base for this execution
|
||||
run_base = run_dir.rstrip("/")
|
||||
LOGGER.info("Ruta base de esta ejecución: %s", run_base)
|
||||
LOGGER.info("Base path for this execution: %s", run_base)
|
||||
|
||||
# 3) Ejecutar dimensiones
|
||||
# 3) Execute dimensions
|
||||
dimensions_cfg = self.dimensions_config
|
||||
if not isinstance(dimensions_cfg, dict):
|
||||
raise ValueError("El bloque 'dimensions' debe ser un dict.")
|
||||
raise ValueError("The 'dimensions' block must be a dict.")
|
||||
|
||||
all_results: Dict[str, Any] = {}
|
||||
|
||||
for dim_name, dim_cfg in dimensions_cfg.items():
|
||||
if not isinstance(dim_cfg, dict):
|
||||
raise ValueError(f"Config inválida para dimensión '{dim_name}' (debe ser dict).")
|
||||
raise ValueError(f"Invalid config for dimension '{dim_name}' (must be dict).")
|
||||
|
||||
if not dim_cfg.get("enabled", True):
|
||||
LOGGER.info("Dimensión '%s' desactivada; se omite.", dim_name)
|
||||
LOGGER.info("Dimension '%s' disabled; skipping.", dim_name)
|
||||
continue
|
||||
|
||||
class_path = dim_cfg.get("class")
|
||||
if not class_path:
|
||||
raise ValueError(f"Falta 'class' en la dimensión '{dim_name}'.")
|
||||
raise ValueError(f"Missing 'class' in dimension '{dim_name}'.")
|
||||
|
||||
metrics: List[str] = dim_cfg.get("metrics", [])
|
||||
if not metrics:
|
||||
LOGGER.info("Dimensión '%s' sin métricas configuradas; se omite.", dim_name)
|
||||
LOGGER.info("Dimension '%s' has no configured metrics; skipping.", dim_name)
|
||||
continue
|
||||
|
||||
cls = _import_class(class_path)
|
||||
@@ -136,35 +136,35 @@ class BeyondMetricsPipeline:
|
||||
if self.dimension_params is not None:
|
||||
extra_kwargs = self.dimension_params.get(dim_name, {}) or {}
|
||||
|
||||
# Las dimensiones reciben df en el constructor
|
||||
# Dimensions receive df in the constructor
|
||||
instance = cls(df, **extra_kwargs)
|
||||
|
||||
dim_results: Dict[str, Any] = {}
|
||||
|
||||
for metric_name in metrics:
|
||||
LOGGER.info(" - Ejecutando métrica '%s.%s'", dim_name, metric_name)
|
||||
LOGGER.info(" - Executing metric '%s.%s'", dim_name, metric_name)
|
||||
result = self._execute_metric(instance, metric_name, run_base, dim_name)
|
||||
dim_results[metric_name] = result
|
||||
|
||||
all_results[dim_name] = dim_results
|
||||
|
||||
# 4) Guardar JSON de resultados (opcional)
|
||||
# 4) Save results JSON (optional)
|
||||
if write_results_json:
|
||||
results_json_path = f"{run_base}/results.json"
|
||||
LOGGER.info("Guardando resultados en JSON: %s", results_json_path)
|
||||
LOGGER.info("Saving results to JSON: %s", results_json_path)
|
||||
self.sink.write_json(results_json_path, all_results)
|
||||
|
||||
# 5) Ejecutar callbacks post-run (scorers, agentes, etc.)
|
||||
# 5) Execute post-run callbacks (scorers, agents, etc.)
|
||||
if self.post_run:
|
||||
LOGGER.info("Ejecutando %d callbacks post-run...", len(self.post_run))
|
||||
LOGGER.info("Executing %d post-run callbacks...", len(self.post_run))
|
||||
for cb in self.post_run:
|
||||
try:
|
||||
LOGGER.info("Ejecutando post-run callback: %s", cb)
|
||||
LOGGER.info("Executing post-run callback: %s", cb)
|
||||
cb(all_results, run_base, self.sink)
|
||||
except Exception:
|
||||
LOGGER.exception("Error ejecutando post-run callback %s", cb)
|
||||
LOGGER.exception("Error executing post-run callback %s", cb)
|
||||
|
||||
LOGGER.info("Ejecución completada correctamente.")
|
||||
LOGGER.info("Execution completed successfully.")
|
||||
return all_results
|
||||
|
||||
|
||||
@@ -176,42 +176,42 @@ class BeyondMetricsPipeline:
|
||||
dim_name: str,
|
||||
) -> Any:
|
||||
"""
|
||||
Ejecuta una métrica:
|
||||
Executes a metric:
|
||||
|
||||
- Si empieza por 'plot_' -> se asume que devuelve Axes:
|
||||
- se guarda la figura como PNG
|
||||
- se devuelve {"type": "image", "path": "..."}
|
||||
- Si no, se serializa el valor a JSON.
|
||||
- If it starts with 'plot_' -> assumed to return Axes:
|
||||
- the figure is saved as PNG
|
||||
- returns {"type": "image", "path": "..."}
|
||||
- Otherwise, the value is serialized to JSON.
|
||||
|
||||
Además, para métricas categóricas (por skill/canal) de la dimensión
|
||||
'volumetry', devolvemos explícitamente etiquetas y valores para que
|
||||
el frontend pueda saber a qué pertenece cada número.
|
||||
Additionally, for categorical metrics (by skill/channel) from the
|
||||
'volumetry' dimension, we explicitly return labels and values so
|
||||
the frontend can know what each number belongs to.
|
||||
"""
|
||||
method = getattr(instance, metric_name, None)
|
||||
if method is None or not callable(method):
|
||||
raise ValueError(
|
||||
f"La métrica '{metric_name}' no existe en {type(instance).__name__}"
|
||||
f"Metric '{metric_name}' does not exist in {type(instance).__name__}"
|
||||
)
|
||||
|
||||
# Caso plots
|
||||
# Plot case
|
||||
if metric_name.startswith("plot_"):
|
||||
ax = method()
|
||||
if not isinstance(ax, Axes):
|
||||
raise TypeError(
|
||||
f"La métrica '{metric_name}' de '{type(instance).__name__}' "
|
||||
f"debería devolver un matplotlib.axes.Axes"
|
||||
f"Metric '{metric_name}' of '{type(instance).__name__}' "
|
||||
f"should return a matplotlib.axes.Axes"
|
||||
)
|
||||
fig = ax.get_figure()
|
||||
if fig is None:
|
||||
raise RuntimeError(
|
||||
"Axes.get_figure() devolvió None, lo cual no debería pasar."
|
||||
"Axes.get_figure() returned None, which should not happen."
|
||||
)
|
||||
fig = cast(Figure, fig)
|
||||
|
||||
filename = f"{dim_name}_{metric_name}.png"
|
||||
img_path = f"{run_base}/{filename}"
|
||||
|
||||
LOGGER.debug("Guardando figura en %s", img_path)
|
||||
LOGGER.debug("Saving figure to %s", img_path)
|
||||
self.sink.write_figure(img_path, fig)
|
||||
plt.close(fig)
|
||||
|
||||
@@ -220,12 +220,12 @@ class BeyondMetricsPipeline:
|
||||
"path": img_path,
|
||||
}
|
||||
|
||||
# Caso numérico/tabular
|
||||
# Numeric/tabular case
|
||||
value = method()
|
||||
|
||||
# Caso especial: series categóricas de volumetría (por skill / canal)
|
||||
# Devolvemos {"labels": [...], "values": [...]} para mantener la
|
||||
# información de etiquetas en el JSON.
|
||||
# Special case: categorical series from volumetry (by skill / channel)
|
||||
# Return {"labels": [...], "values": [...]} to maintain
|
||||
# label information in the JSON.
|
||||
if (
|
||||
dim_name == "volumetry"
|
||||
and isinstance(value, pd.Series)
|
||||
@@ -238,7 +238,7 @@ class BeyondMetricsPipeline:
|
||||
}
|
||||
):
|
||||
labels = [str(idx) for idx in value.index.tolist()]
|
||||
# Aseguramos que todos los valores sean numéricos JSON-friendly
|
||||
# Ensure all values are JSON-friendly numeric
|
||||
values = [float(v) for v in value.astype(float).tolist()]
|
||||
return {
|
||||
"labels": labels,
|
||||
@@ -251,7 +251,7 @@ class BeyondMetricsPipeline:
|
||||
|
||||
def load_dimensions_config(path: str) -> Dict[str, Any]:
|
||||
"""
|
||||
Carga un JSON de configuración que contiene solo el bloque 'dimensions'.
|
||||
Loads a JSON configuration file containing only the 'dimensions' block.
|
||||
"""
|
||||
import json
|
||||
from pathlib import Path
|
||||
@@ -261,7 +261,7 @@ def load_dimensions_config(path: str) -> Dict[str, Any]:
|
||||
|
||||
dimensions = cfg.get("dimensions")
|
||||
if dimensions is None:
|
||||
raise ValueError("El fichero de configuración debe contener un bloque 'dimensions'.")
|
||||
raise ValueError("The configuration file must contain a 'dimensions' block.")
|
||||
|
||||
return dimensions
|
||||
|
||||
@@ -274,12 +274,12 @@ def build_pipeline(
|
||||
post_run: Optional[List[PostRunCallback]] = None,
|
||||
) -> BeyondMetricsPipeline:
|
||||
"""
|
||||
Crea un BeyondMetricsPipeline a partir de:
|
||||
- ruta al JSON con dimensiones/métricas
|
||||
- un DataSource ya construido (local/S3/Drive)
|
||||
- un ResultsSink ya construido (local/S3/Drive)
|
||||
- una lista opcional de callbacks post_run que se ejecutan al final
|
||||
(útil para scorers, agentes de IA, etc.)
|
||||
Creates a BeyondMetricsPipeline from:
|
||||
- path to JSON with dimensions/metrics
|
||||
- an already constructed DataSource (local/S3/Drive)
|
||||
- an already constructed ResultsSink (local/S3/Drive)
|
||||
- an optional list of post_run callbacks that execute at the end
|
||||
(useful for scorers, AI agents, etc.)
|
||||
"""
|
||||
dims_cfg = load_dimensions_config(dimensions_config_path)
|
||||
return BeyondMetricsPipeline(
|
||||
|
||||
Reference in New Issue
Block a user