169 lines
6.6 KiB
Python
169 lines
6.6 KiB
Python
|
|
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()
|