import logging from sqlalchemy.orm import Session from src.services.postgresql_manager import PostgreSQLManager from src.utils.config import config from src.models.activity import Activity from src.models.segment import Segment from src.services.segment_matcher import SegmentMatcher from src.services.parsers import extract_points_from_file # Configure logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) def debug_matching(): db_manager = PostgreSQLManager(config.DATABASE_URL) with db_manager.get_db_session() as db: # 1. Fetch Segment segment = db.query(Segment).filter(Segment.name.like("%Etiwanda%")).first() if not segment: print("ERROR: Segment 'Etiwanda Climb' not found.") return print(f"DEBUG: Found Segment ID {segment.id}: {segment.name}") print(f"DEBUG: Segment Distance: {segment.distance}") # 2. Fetch Activity # Try finding by ID or Garmin ID act_id = 21072264737 activity = db.query(Activity).filter(Activity.id == act_id).first() if not activity: activity = db.query(Activity).filter(Activity.garmin_activity_id == str(act_id)).first() if not activity: print(f"ERROR: Activity {act_id} not found.") return print(f"DEBUG: Found Activity ID {activity.id} (Garmin: {activity.garmin_activity_id})") # 3. Extract Points if not activity.file_content: print("ERROR: Activity has no file content") return points = extract_points_from_file(activity.file_content, activity.file_type) print(f"DEBUG: Extracted {len(points)} points from activity.") # 4. Trigger Match matcher = SegmentMatcher(db) # Manually invoke match parts to trace # (Copying logic from match_activity wrapper) import json seg_points = json.loads(segment.points) if isinstance(segment.points, str) else segment.points print(f"DEBUG: Segment has {len(seg_points)} points.") print(f"DEBUG: Segment has {len(seg_points)} points.") efforts = matcher.match_activity(activity, points) if efforts: print(f"SUCCESS: match_activity returned {len(efforts)} efforts.") for e in efforts: print(f" - Segment {e.segment_id} (Duration: {e.elapsed_time}s)") else: print("FAILURE: match_activity returned NO efforts.") # --- Deep Trace --- print("\n--- DEEP TRACE ---") ENTRY_RADIUS = 25.0 CORRIDOR_RADIUS = 35.0 from src.utils.geo import haversine_distance, perpendicular_distance start_node = seg_points[0] end_node = seg_points[-1] # Check Start Proximity start_candidates = [] min_start_dist = float('inf') for i, p in enumerate(points): dist = haversine_distance(p[1], p[0], start_node[1], start_node[0]) if dist < min_start_dist: min_start_dist = dist if dist <= ENTRY_RADIUS: start_candidates.append(i) print(f"Min distance to Start Node: {min_start_dist:.2f}m") print(f"Start Candidates: {start_candidates}") if not start_candidates: print("FAIL: Start node never reached within 25m.") return # Trace Candidate 0 (or all) for start_idx in start_candidates: print(f"\nChecking candidate starting at index {start_idx}...") effort_accum_dist = 0.0 deviated = False completed = False max_deviation = 0.0 for j in range(start_idx + 1, len(points)): p = points[j] prev_p = points[j-1] # Accumulate distance step_dist = haversine_distance(p[1], p[0], prev_p[1], prev_p[0]) effort_accum_dist += step_dist # Check deviation dev = matcher._min_dist_to_segment_path(p, seg_points) if dev > max_deviation: max_deviation = dev if dev > CORRIDOR_RADIUS: print(f" DEVIATION at index {j}! Dist {dev:.2f}m > {CORRIDOR_RADIUS}m. AccumDist: {effort_accum_dist:.2f}m") deviated = True break # Check completion d_end = haversine_distance(p[1], p[0], end_node[1], end_node[0]) if d_end <= ENTRY_RADIUS: if effort_accum_dist >= 0.8 * segment.distance: print(f" COMPLETION possible at index {j}. d_end={d_end:.2f}m, dist={effort_accum_dist:.2f}m") completed = True # Don't break immediately, could get closer? # But logic breaks on completion check? # Logic: returns first valid end. if not completed: print(f" Candidate ended without completion. Max Deviation: {max_deviation:.2f}m. Total Dist: {effort_accum_dist:.2f}m vs Target {segment.distance:.2f}m") if __name__ == "__main__": debug_matching()