Spaces:
Running
Running
| # src/tools/viz_executor.py | |
| import matplotlib | |
| matplotlib.use('Agg') | |
| import matplotlib.pyplot as plt | |
| from typing import List, Dict, Any | |
| from domain.training.charts import ChartSpec, ChartType | |
| from .interfaces import IVisualizationExecutor | |
| from src.tools.runner_ai import calculate_pace_s_per_km | |
| class TemplateVisualizationExecutor(IVisualizationExecutor): | |
| def render_chart(self, chart_spec: ChartSpec, features: List[Dict[str, Any]]) -> plt.Figure: | |
| if chart_spec.chart_type == ChartType.PACE: | |
| return self._render_pace_chart(features, chart_spec.title or "Pace over Time") | |
| elif chart_spec.chart_type == ChartType.HEART_RATE: | |
| return self._render_hr_chart(features, chart_spec.title or "Heart Rate over Time") | |
| else: | |
| return self._empty_chart(f"Unsupported Chart Type: {chart_spec.chart_type}") | |
| def _render_pace_chart(self, features: List[Dict[str, Any]], title: str) -> plt.Figure: | |
| fig, ax = plt.subplots(figsize=(10, 5)) | |
| dates = [] | |
| paces = [] | |
| for run in features: | |
| dt = run.get("start_time") | |
| pace_s = calculate_pace_s_per_km( | |
| run.get("total_distance_m"), run.get("total_duration_s") | |
| ) | |
| if dt and pace_s: | |
| dates.append(dt) | |
| # Convert s/km to min/km (float) | |
| paces.append(pace_s / 60.0) | |
| if not dates: | |
| return self._empty_chart(title) | |
| ax.plot(dates, paces, marker="o", linestyle="-", color="blue") | |
| ax.set_title(title) | |
| ax.set_ylabel("Pace (min/km)") | |
| ax.set_xlabel("Date") | |
| ax.grid(True) | |
| # Invert y-axis for pace (smaller min/km is faster) | |
| ax.invert_yaxis() | |
| fig.autofmt_xdate() | |
| return fig | |
| def _render_hr_chart(self, features: List[Dict[str, Any]], title: str) -> plt.Figure: | |
| fig, ax = plt.subplots(figsize=(10, 5)) | |
| dates = [] | |
| hrs = [] | |
| for run in features: | |
| dt = run.get("start_time") | |
| hr = run.get("avg_hr_bpm") | |
| if dt and hr: | |
| dates.append(dt) | |
| hrs.append(hr) | |
| if not dates: | |
| return self._empty_chart(title) | |
| ax.plot(dates, hrs, marker="s", linestyle="--", color="red") | |
| ax.set_title(title) | |
| ax.set_ylabel("Heart Rate (bpm)") | |
| ax.set_xlabel("Date") | |
| ax.grid(True) | |
| fig.autofmt_xdate() | |
| return fig | |
| def _empty_chart(self, title: str) -> plt.Figure: | |
| fig, ax = plt.subplots(figsize=(6, 3)) | |
| ax.text(0.5, 0.5, "No Data Available", ha="center", va="center") | |
| ax.set_title(title) | |
| ax.axis("off") | |
| return fig | |