File size: 2,707 Bytes
557ee65
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# 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