diff --git a/app/services/__pycache__/context_generator.cpython-312.pyc b/app/services/__pycache__/context_generator.cpython-312.pyc index 2fc12bc..2e8fc75 100644 Binary files a/app/services/__pycache__/context_generator.cpython-312.pyc and b/app/services/__pycache__/context_generator.cpython-312.pyc differ diff --git a/app/services/__pycache__/graph_generator.cpython-312.pyc b/app/services/__pycache__/graph_generator.cpython-312.pyc index b61675b..480e77b 100644 Binary files a/app/services/__pycache__/graph_generator.cpython-312.pyc and b/app/services/__pycache__/graph_generator.cpython-312.pyc differ diff --git a/app/services/context_generator.py b/app/services/context_generator.py index 925a216..de02bf4 100644 --- a/app/services/context_generator.py +++ b/app/services/context_generator.py @@ -235,19 +235,27 @@ class ContextGenerator: return metrics def _detect_thresholds(self) -> Tuple[Optional[Dict], Optional[Dict]]: - """Detect VT1 and VT2 thresholds""" + """Detect VT1 and VT2 thresholds (matching notebook logic)""" + # VT1: First index where carb burn > fat burn AND remains higher condition = self.pnoe_df["CHO_smoothed"] > self.pnoe_df["FAT_smoothed"] crossover_indices = condition[condition].index vt1 = None if len(crossover_indices) > 0: - vt1_idx = crossover_indices[0] - vt1_row = self.pnoe_df.loc[vt1_idx] - vt1 = { - "HeartRate": vt1_row["HR(bpm)_smoothed"], - "Speed": vt1_row["Speed"], - "Time": vt1_row["T(sec)"], - } + # Find first crossover where carbs remain higher for the rest + for idx in crossover_indices: + if all( + self.pnoe_df.loc[idx:]["CHO_smoothed"] + > self.pnoe_df.loc[idx:]["FAT_smoothed"] + ): + vt1_idx = idx + vt1_row = self.pnoe_df.loc[vt1_idx] + vt1 = { + "HeartRate": vt1_row["HR(bpm)_smoothed"], + "Speed": vt1_row["Speed"], + "Time": vt1_row["T(sec)"], + } + break ve_slope = self.pnoe_df["VE(l/min)_smoothed"].diff() second_derivative = ve_slope.diff() @@ -745,9 +753,13 @@ class ContextGenerator: """Calculate detailed metrics for each heart rate zone based on actual data""" import math - # Get zone boundaries - fat_max_idx = self.pnoe_df["FAT_smoothed"].idxmax() - optimal_row = self.pnoe_df.loc[fat_max_idx] + # Get zone boundaries - use optimal fat burning zone (highest fat:carb ratio) + # matching notebook logic + self.pnoe_df["fat_carb_ratio"] = self.pnoe_df["FAT_smoothed"] / ( + self.pnoe_df["CHO_smoothed"] + 0.00000001 + ) + optimal_fat_idx = self.pnoe_df["fat_carb_ratio"].idxmax() + optimal_row = self.pnoe_df.loc[optimal_fat_idx] # Detect VT1 and VT2 vt1 = pnoe_metrics.get("vt1")