package web import ( "log/slog" "github.com/martinlehoux/biking_home/mountain_pass" "github.com/martinlehoux/biking_home/official_climb" "github.com/martinlehoux/biking_home/ride" "github.com/martinlehoux/biking_home/rideanalysis" ) // analysisFor runs the ride analysis pipeline the same way the ride profile // does, so recorded attempts and displayed climbs always agree. func analysisFor(parsed ride.Ride, passes []mountain_pass.MountainPass, officialClimbs []official_climb.OfficialClimb, matchPolicy official_climb.MatchPolicy) rideanalysis.Result { return rideanalysis.Analyze(parsed, passes, officialClimbs, matchPolicy) } // attemptsFromAnalysis extracts one attempt per official-climb occurrence. // Occurrences are numbered per climb in route order (0, 1, … for laps). func attemptsFromAnalysis(rideID int64, analysis rideanalysis.Result, parsed ride.Ride) []official_climb.Attempt { occurrences := make(map[int64]int) attempts := make([]official_climb.Attempt, 0) for _, analyzed := range analysis.Climbs { if analyzed.OfficialClimbID == 0 { continue } occurrence := occurrences[analyzed.OfficialClimbID] occurrences[analyzed.OfficialClimbID] = occurrence + 1 attempts = append(attempts, official_climb.AttemptFromStats(rideID, occurrence, official_climb.AttemptStats{ OfficialClimbID: analyzed.OfficialClimbID, DistanceM: analyzed.DistanceM, ElevationGainM: elevationGain(analyzed), SlopePercent: analyzed.SlopePercent, Cotacol: analyzed.Cotacol, StartIndex: analyzed.Interval.Start, EndIndex: analyzed.Interval.End, }, parsed.Timestamps())) } return attempts } func elevationGain(analyzed rideanalysis.AnalyzedClimb) float64 { gain := 0.0 for i := 1; i < len(analyzed.Segment.Elevations); i++ { if diff := analyzed.Segment.Elevations[i] - analyzed.Segment.Elevations[i-1]; diff > 0 { gain += diff } } return gain } // recordOfficialClimbAttempts persists the ride's official-climb attempts, // replacing any previously recorded ones. Failures are logged, never fatal: // attempts are an optimization over on-the-fly matching. func (s *Server) recordOfficialClimbAttempts(rideID int64, parsed ride.Ride, analysis rideanalysis.Result) { attempts := attemptsFromAnalysis(rideID, analysis, parsed) if err := official_climb.ReplaceRideAttempts(s.db, rideID, attempts); err != nil { slog.Warn("Failed to record official climb attempts", "ride_id", rideID, "error", err) } }