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:
Claude
2026-02-07 11:15:47 +00:00
parent 8c7f5fa827
commit 9caa382010
4 changed files with 217 additions and 217 deletions

View File

@@ -14,25 +14,25 @@ from openai import OpenAI
DEFAULT_SYSTEM_PROMPT = (
"Eres un consultor experto en contact centers. "
"Vas a recibir resultados analíticos de un sistema de métricas "
"(BeyondMetrics) en formato JSON. Tu tarea es generar un informe claro, "
"accionable y orientado a negocio, destacando los principales hallazgos, "
"riesgos y oportunidades de mejora."
"You are an expert contact center consultant. "
"You will receive analytical results from a metrics system "
"(BeyondMetrics) in JSON format. Your task is to generate a clear, "
"actionable, business-oriented report, highlighting the main findings, "
"risks, and opportunities for improvement."
)
@dataclass
class ReportAgentConfig:
"""
Configuración básica del agente de informes.
Basic configuration for the report agent.
openai_api_key:
Se puede pasar explícitamente o leer de la variable de entorno OPENAI_API_KEY.
Can be passed explicitly or read from the OPENAI_API_KEY environment variable.
model:
Modelo de ChatGPT a utilizar, p.ej. 'gpt-4.1-mini' o similar.
ChatGPT model to use, e.g. 'gpt-4.1-mini' or similar.
system_prompt:
Prompt de sistema para controlar el estilo del informe.
System prompt to control the report style.
"""
openai_api_key: Optional[str] = None
@@ -42,15 +42,15 @@ class ReportAgentConfig:
class BeyondMetricsReportAgent:
"""
Agente muy sencillo que:
Simple agent that:
1) Lee el JSON de resultados de una ejecución de BeyondMetrics.
2) Construye un prompt con esos resultados.
3) Llama a ChatGPT para generar un informe en texto.
4) Guarda el informe en un PDF en disco, EMBEBIENDO las imágenes PNG
generadas por el pipeline como anexos.
1) Reads the JSON results from a BeyondMetrics execution.
2) Builds a prompt with those results.
3) Calls ChatGPT to generate a text report.
4) Saves the report to a PDF on disk, EMBEDDING the PNG images
generated by the pipeline as attachments.
MVP: centrado en texto + figuras incrustadas.
MVP: focused on text + embedded figures.
"""
def __init__(self, config: Optional[ReportAgentConfig] = None) -> None:
@@ -59,16 +59,16 @@ class BeyondMetricsReportAgent:
api_key = self.config.openai_api_key or os.getenv("OPENAI_API_KEY")
if not api_key:
raise RuntimeError(
"Falta la API key de OpenAI. "
"Pásala en ReportAgentConfig(openai_api_key=...) o "
"define la variable de entorno OPENAI_API_KEY."
"Missing OpenAI API key. "
"Pass it in ReportAgentConfig(openai_api_key=...) or "
"define the OPENAI_API_KEY environment variable."
)
# Cliente de la nueva API de OpenAI
# New OpenAI API client
self._client = OpenAI(api_key=api_key)
# ------------------------------------------------------------------
# API pública principal
# Main public API
# ------------------------------------------------------------------
def generate_pdf_report(
self,
@@ -77,48 +77,48 @@ class BeyondMetricsReportAgent:
extra_user_prompt: str = "",
) -> str:
"""
Genera un informe en PDF a partir de una carpeta de resultados.
Generates a PDF report from a results folder.
Parámetros:
Parameters:
- run_base:
Carpeta base de la ejecución. Debe contener al menos 'results.json'
y, opcionalmente, imágenes PNG generadas por el pipeline.
Base folder for the execution. Must contain at least 'results.json'
and, optionally, PNG images generated by the pipeline.
- output_pdf_path:
Ruta completa del PDF de salida. Si es None, se crea
'beyondmetrics_report.pdf' dentro de run_base.
Full path for the output PDF. If None, creates
'beyondmetrics_report.pdf' inside run_base.
- extra_user_prompt:
Texto adicional para afinar la petición al agente
(p.ej. "enfatiza eficiencia y SLA", etc.)
Additional text to refine the agent's request
(e.g. "emphasize efficiency and SLA", etc.)
Devuelve:
- La ruta del PDF generado.
Returns:
- The path to the generated PDF.
"""
run_dir = Path(run_base)
results_json = run_dir / "results.json"
if not results_json.exists():
raise FileNotFoundError(
f"No se ha encontrado {results_json}. "
"Asegúrate de ejecutar primero el pipeline."
f"{results_json} not found. "
"Make sure to run the pipeline first."
)
# 1) Leer JSON de resultados
# 1) Read results JSON
with results_json.open("r", encoding="utf-8") as f:
results_data: Dict[str, Any] = json.load(f)
# 2) Buscar imágenes generadas
# 2) Find generated images
image_files = sorted(p for p in run_dir.glob("*.png"))
# 3) Construir prompt de usuario
# 3) Build user prompt
user_prompt = self._build_user_prompt(
results=results_data,
image_files=[p.name for p in image_files],
extra_user_prompt=extra_user_prompt,
)
# 4) Llamar a ChatGPT para obtener el texto del informe
# 4) Call ChatGPT to get the report text
report_text = self._call_chatgpt(user_prompt)
# 5) Crear PDF con texto + imágenes embebidas
# 5) Create PDF with text + embedded images
if output_pdf_path is None:
output_pdf_path = str(run_dir / "beyondmetrics_report.pdf")
@@ -127,7 +127,7 @@ class BeyondMetricsReportAgent:
return output_pdf_path
# ------------------------------------------------------------------
# Construcción del prompt
# Prompt construction
# ------------------------------------------------------------------
def _build_user_prompt(
self,
@@ -136,34 +136,34 @@ class BeyondMetricsReportAgent:
extra_user_prompt: str = "",
) -> str:
"""
Construye el mensaje de usuario que se enviará al modelo.
Para un MVP, serializamos el JSON de resultados entero.
Más adelante se puede resumir si el JSON crece demasiado.
Builds the user message to be sent to the model.
For an MVP, we serialize the entire results JSON.
Later, this can be summarized if the JSON grows too large.
"""
results_str = json.dumps(results, indent=2, ensure_ascii=False)
images_section = (
"Imágenes generadas en la ejecución:\n"
"Images generated in the execution:\n"
+ "\n".join(f"- {name}" for name in image_files)
if image_files
else "No se han generado imágenes en esta ejecución."
else "No images were generated in this execution."
)
extra = (
f"\n\nInstrucciones adicionales del usuario:\n{extra_user_prompt}"
f"\n\nAdditional user instructions:\n{extra_user_prompt}"
if extra_user_prompt
else ""
)
prompt = (
"A continuación te proporciono los resultados de una ejecución de BeyondMetrics "
"en formato JSON. Debes elaborar un INFORME EJECUTIVO para un cliente de "
"contact center. El informe debe incluir:\n"
"- Resumen ejecutivo en lenguaje de negocio.\n"
"- Principales hallazgos por dimensión.\n"
"- Riesgos o problemas detectados.\n"
"- Recomendaciones accionables.\n\n"
"Resultados (JSON):\n"
"Below I provide you with the results of a BeyondMetrics execution "
"in JSON format. You must produce an EXECUTIVE REPORT for a contact "
"center client. The report should include:\n"
"- Executive summary in business language.\n"
"- Main findings by dimension.\n"
"- Detected risks or issues.\n"
"- Actionable recommendations.\n\n"
"Results (JSON):\n"
f"{results_str}\n\n"
f"{images_section}"
f"{extra}"
@@ -172,12 +172,12 @@ class BeyondMetricsReportAgent:
return prompt
# ------------------------------------------------------------------
# Llamada a ChatGPT (nueva API)
# ChatGPT call (new API)
# ------------------------------------------------------------------
def _call_chatgpt(self, user_prompt: str) -> str:
"""
Llama al modelo de ChatGPT y devuelve el contenido del mensaje de respuesta.
Implementado con la nueva API de OpenAI.
Calls the ChatGPT model and returns the content of the response message.
Implemented with the new OpenAI API.
"""
resp = self._client.chat.completions.create(
model=self.config.model,
@@ -190,11 +190,11 @@ class BeyondMetricsReportAgent:
content = resp.choices[0].message.content
if not isinstance(content, str):
raise RuntimeError("La respuesta del modelo no contiene texto.")
raise RuntimeError("The model response does not contain text.")
return content
# ------------------------------------------------------------------
# Escritura de PDF (texto + imágenes)
# PDF writing (text + images)
# ------------------------------------------------------------------
def _write_pdf(
self,
@@ -203,11 +203,11 @@ class BeyondMetricsReportAgent:
image_paths: Sequence[Path],
) -> None:
"""
Crea un PDF A4 con:
Creates an A4 PDF with:
1) Texto del informe (páginas iniciales).
2) Una sección de anexos donde se incrustan las imágenes PNG
generadas por el pipeline, escaladas para encajar en la página.
1) Report text (initial pages).
2) An appendix section where the PNG images generated by the
pipeline are embedded, scaled to fit the page.
"""
output_path = str(output_path)
c = canvas.Canvas(output_path, pagesize=A4)
@@ -220,7 +220,7 @@ class BeyondMetricsReportAgent:
c.setFont("Helvetica", 11)
# --- Escribir texto principal ---
# --- Write main text ---
def _wrap_line(line: str, max_chars: int = 100) -> list[str]:
parts: list[str] = []
current: list[str] = []
@@ -248,37 +248,37 @@ class BeyondMetricsReportAgent:
c.drawString(margin_x, y, line)
y -= line_height
# --- Anexar imágenes como figuras ---
# --- Append images as figures ---
if image_paths:
# Nueva página para las figuras
# New page for figures
c.showPage()
c.setFont("Helvetica-Bold", 14)
c.drawString(margin_x, height - margin_y, "Anexo: Figuras")
c.drawString(margin_x, height - margin_y, "Appendix: Figures")
c.setFont("Helvetica", 11)
current_y = height - margin_y - 2 * line_height
for img_path in image_paths:
# Si no cabe la imagen en la página, pasamos a la siguiente
# If the image doesn't fit on the page, move to the next one
available_height = current_y - margin_y
if available_height < 100: # espacio mínimo
if available_height < 100: # minimum space
c.showPage()
c.setFont("Helvetica-Bold", 14)
c.drawString(margin_x, height - margin_y, "Anexo: Figuras (cont.)")
c.drawString(margin_x, height - margin_y, "Appendix: Figures (cont.)")
c.setFont("Helvetica", 11)
current_y = height - margin_y - 2 * line_height
available_height = current_y - margin_y
# Título de la figura
title = f"Figura: {img_path.name}"
# Figure title
title = f"Figure: {img_path.name}"
c.drawString(margin_x, current_y, title)
current_y -= line_height
# Cargar imagen y escalarla
# Load and scale image
try:
img = ImageReader(str(img_path))
iw, ih = img.getSize()
# Escala para encajar en ancho y alto disponibles
# Scale to fit available width and height
max_img_height = available_height - 2 * line_height
scale = min(max_width / iw, max_img_height / ih)
if scale <= 0:
@@ -302,8 +302,8 @@ class BeyondMetricsReportAgent:
current_y = y_img - 2 * line_height
except Exception as e:
# Si falla la carga, lo indicamos en el PDF
err_msg = f"No se pudo cargar la imagen {img_path.name}: {e}"
# If loading fails, indicate it in the PDF
err_msg = f"Could not load image {img_path.name}: {e}"
c.drawString(margin_x, current_y, err_msg)
current_y -= 2 * line_height