import sys import os import statistics import logging sys.path.append('/app/backend') from src.services.postgresql_manager import PostgreSQLManager from src.utils.config import config from src.models.activity import Activity from src.models.bike_setup import BikeSetup from src.services.parsers import extract_activity_data from src.services.bike_matching import WHEEL_CIRCUMFERENCE_M logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) # Re-implement logic to capture samples def analyze_streams_debug(speed_stream, cadence_stream, window_size=10): if not speed_stream or not cadence_stream or len(speed_stream) != len(cadence_stream): print(" - Streams missing or mismatched length") return [], 0.0 ratios = [] samples = [] rejected_reasons = {'none': 0, 'threshold': 0, 'variance': 0} n = len(speed_stream) for i in range(0, n - window_size, 5): window_speeds = speed_stream[i:i+window_size] window_cadences = cadence_stream[i:i+window_size] if any(v is None for v in window_speeds) or any(c is None for c in window_cadences): rejected_reasons['none'] += 1 continue if all(c > 55 for c in window_cadences) and all(v > 2.5 for v in window_speeds): try: cad_std = statistics.stdev(window_cadences) spd_std = statistics.stdev(window_speeds) if cad_std < 5 and spd_std < 0.5: avg_speed = statistics.mean(window_speeds) avg_cadence = statistics.mean(window_cadences) ratio = (avg_speed * 60) / (avg_cadence * WHEEL_CIRCUMFERENCE_M) ratios.append(ratio) if len(samples) < 10: samples.append({ 'time_idx': i, 'avg_spd': avg_speed, 'avg_cad': avg_cadence, 'ratio': ratio, 'cad_std': cad_std }) else: rejected_reasons['variance'] += 1 except statistics.StatisticsError: pass else: rejected_reasons['threshold'] += 1 if not ratios: print(f" - No steady segments. Rejections: {rejected_reasons}") return [], 0.0 return samples, statistics.median(ratios) def main(): target_ids = ['21072264737', '18469350198', '18349164690'] db = PostgreSQLManager(config.DATABASE_URL).SessionLocal() # Load all setups setups = db.query(BikeSetup).all() print(f"Loaded {len(setups)} bike setups.") for s in setups: if s.rear_cog == 0: print(f" - {s.name or s.frame}: No gears configured (skipped)") continue mech_ratio = s.chainring / s.rear_cog print(f" - {s.name or s.frame}: {s.chainring}/{s.rear_cog} = {mech_ratio:.3f} (Active: {s.purchase_date} to {s.retirement_date})") print("\n" + "="*80) # Add a control activity to verify script works print("\n" + "="*80) print("CONTROL CHECK: Finding a random activity WITH cadence to verify script logic...") control_activity = db.query(Activity).filter(Activity.avg_cadence > 0, Activity.file_content != None).first() if control_activity: target_ids.append(control_activity.garmin_activity_id) else: print("No control activity found!") for gid in target_ids: print(f"\nAnalyzing Activity Garmin ID: {gid}") activity = db.query(Activity).filter(Activity.garmin_activity_id == str(gid)).first() if not activity: print(" - Not found in DB") continue print(f" - Type: {activity.activity_type}") print(f" - Date: {activity.start_time}") print(f" - Global Avg Speed: {activity.avg_speed:.2f} m/s" if activity.avg_speed else " - Global Avg Speed: None") print(f" - Global Avg Cadence: {activity.avg_cadence:.1f} rpm" if activity.avg_cadence else " - Global Avg Cadence: None") if not activity.file_content: print(" - No file content available") continue data = extract_activity_data(activity.file_content, activity.file_type) speeds = data.get('speed') or [] cadences = data.get('cadence') or [] # Check if actual data exists valid_speeds = [x for x in speeds if x is not None] valid_cadences = [x for x in cadences if x is not None] if len(valid_cadences) < 10: print(" - CRITICAL: No cadence data stream found in file.") print(" - Result: IMPOSSIBLE TO MATCH GEAR RATIO.") continue samples, observed_ratio = analyze_streams_debug(speeds, cadences) print(f" - Steady Segments Found: {len(samples) if samples else 0}") print(f" - Observed Ratio (Median): {observed_ratio:.3f}") if samples: print(" - First 10 Steady Samples:") for s in samples: print(f" - T={s['time_idx']}s | Spd={s['avg_spd']:.1f} | Cad={s['avg_cad']:.1f} | R={s['ratio']:.3f} (std_cad={s['cad_std']:.1f})") # ... logic continues ... print("\n - Matching Against Setups:") if observed_ratio > 0: for bike in setups: if bike.rear_cog == 0: continue # Date Check active = True if bike.purchase_date and activity.start_time.date() < bike.purchase_date: active = False if bike.retirement_date and activity.start_time.date() > bike.retirement_date: active = False status_str = "ACTIVE" if active else "INACTIVE" mech_ratio = bike.chainring / bike.rear_cog diff = abs(observed_ratio - mech_ratio) error_pct = diff / mech_ratio confidence = max(0.0, 1.0 - error_pct) marker = "<<< BEST MATCH" if confidence > 0.9 else "" if not active: marker = "(Date Mismatch)" print(f" - {bike.name or bike.frame} ({bike.chainring}/{bike.rear_cog}): Mech={mech_ratio:.3f} | Diff={diff:.3f} | Conf={confidence:.3f} [{status_str}] {marker}") else: print(" - Could not calculate valid observed ratio from streams.") db.close() if __name__ == "__main__": main()