|
|
|
|
@ -603,7 +603,7 @@ func (db *DB) getLaneData(lane int, racerID int64, heatNum int, groupID int64) (
|
|
|
|
|
|
|
|
|
|
// GetFinalResults calculates the final results for a group
|
|
|
|
|
// The final time is the average of the fastest 3 times, discarding the slowest time
|
|
|
|
|
// Racers with a time of 9.999 are marked as DNF and placed at the end
|
|
|
|
|
// Racers with all times as 9.999 are marked as DNF and placed at the end
|
|
|
|
|
func (db *DB) GetFinalResults(groupID int64) ([]models.FinalResult, error) {
|
|
|
|
|
// Get all racers in the group
|
|
|
|
|
racers, err := db.GetRacersByGroup(groupID)
|
|
|
|
|
@ -611,19 +611,6 @@ func (db *DB) GetFinalResults(groupID int64) ([]models.FinalResult, error) {
|
|
|
|
|
return nil, fmt.Errorf("failed to get racers: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get all heat results for the group
|
|
|
|
|
rows, err := db.Query(`
|
|
|
|
|
SELECT heat_number, lane1_id, lane1_time, lane2_id, lane2_time,
|
|
|
|
|
lane3_id, lane3_time, lane4_id, lane4_time
|
|
|
|
|
FROM heat_results
|
|
|
|
|
WHERE group_id = ?
|
|
|
|
|
ORDER BY heat_number
|
|
|
|
|
`, groupID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("failed to query heat results: %w", err)
|
|
|
|
|
}
|
|
|
|
|
defer rows.Close()
|
|
|
|
|
|
|
|
|
|
// Map to store times for each racer
|
|
|
|
|
racerTimes := make(map[int64][]float64)
|
|
|
|
|
|
|
|
|
|
@ -632,19 +619,38 @@ func (db *DB) GetFinalResults(groupID int64) ([]models.FinalResult, error) {
|
|
|
|
|
racerTimes[racer.ID] = []float64{}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Process each heat result
|
|
|
|
|
// Get all heats and heat results for the group
|
|
|
|
|
rows, err := db.Query(`
|
|
|
|
|
SELECT
|
|
|
|
|
h.heat_num,
|
|
|
|
|
h.lane1_id, h.lane2_id, h.lane3_id, h.lane4_id,
|
|
|
|
|
hr.lane1_time, hr.lane2_time, hr.lane3_time, hr.lane4_time
|
|
|
|
|
FROM heats h
|
|
|
|
|
LEFT JOIN heat_results hr ON h.group_id = hr.group_id AND h.heat_num = hr.heat_number
|
|
|
|
|
WHERE h.group_id = ?
|
|
|
|
|
ORDER BY h.heat_num
|
|
|
|
|
`, groupID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("failed to query heats and results: %w", err)
|
|
|
|
|
}
|
|
|
|
|
defer rows.Close()
|
|
|
|
|
|
|
|
|
|
// Process each heat and its results
|
|
|
|
|
for rows.Next() {
|
|
|
|
|
var heatNum int
|
|
|
|
|
var lane1ID, lane2ID, lane3ID, lane4ID sql.NullInt64
|
|
|
|
|
var lane1Time, lane2Time, lane3Time, lane4Time sql.NullFloat64
|
|
|
|
|
|
|
|
|
|
err := rows.Scan(&heatNum, &lane1ID, &lane1Time, &lane2ID, &lane2Time,
|
|
|
|
|
&lane3ID, &lane3Time, &lane4ID, &lane4Time)
|
|
|
|
|
err := rows.Scan(
|
|
|
|
|
&heatNum,
|
|
|
|
|
&lane1ID, &lane2ID, &lane3ID, &lane4ID,
|
|
|
|
|
&lane1Time, &lane2Time, &lane3Time, &lane4Time,
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("failed to scan heat result: %w", err)
|
|
|
|
|
return nil, fmt.Errorf("failed to scan heat and result: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add times for each lane if a racer was assigned
|
|
|
|
|
// Add times for each lane if a racer was assigned and has a time
|
|
|
|
|
if lane1ID.Valid && lane1Time.Valid {
|
|
|
|
|
racerTimes[lane1ID.Int64] = append(racerTimes[lane1ID.Int64], lane1Time.Float64)
|
|
|
|
|
}
|
|
|
|
|
@ -670,48 +676,70 @@ func (db *DB) GetFinalResults(groupID int64) ([]models.FinalResult, error) {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if racer has DNF (time of 9.999)
|
|
|
|
|
dnf := false
|
|
|
|
|
// Make a copy of times for display (original array will be modified by sort)
|
|
|
|
|
displayTimes := make([]float64, len(times))
|
|
|
|
|
copy(displayTimes, times)
|
|
|
|
|
|
|
|
|
|
// Check if all times are DNF
|
|
|
|
|
allDNF := true
|
|
|
|
|
for _, time := range times {
|
|
|
|
|
if time >= 9.999 {
|
|
|
|
|
dnf = true
|
|
|
|
|
if time < 9.999 {
|
|
|
|
|
allDNF = false
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var avgTime float64
|
|
|
|
|
|
|
|
|
|
if dnf {
|
|
|
|
|
// If DNF, set average time to a high value for sorting
|
|
|
|
|
if allDNF {
|
|
|
|
|
// If all runs are DNF, mark as DNF
|
|
|
|
|
avgTime = 999.999
|
|
|
|
|
} else {
|
|
|
|
|
// Sort times to find fastest
|
|
|
|
|
sort.Float64s(times)
|
|
|
|
|
// Filter out DNF times for calculation
|
|
|
|
|
validTimes := []float64{}
|
|
|
|
|
for _, time := range times {
|
|
|
|
|
if time < 9.999 {
|
|
|
|
|
validTimes = append(validTimes, time)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Sort valid times to find fastest
|
|
|
|
|
sort.Float64s(validTimes)
|
|
|
|
|
|
|
|
|
|
// Calculate average of fastest 3 times (or fewer if not enough runs)
|
|
|
|
|
numTimes := len(times)
|
|
|
|
|
numTimes := len(validTimes)
|
|
|
|
|
if numTimes >= 4 {
|
|
|
|
|
// Discard the slowest time
|
|
|
|
|
avgTime = (times[0] + times[1] + times[2]) / 3.0
|
|
|
|
|
avgTime = (validTimes[0] + validTimes[1] + validTimes[2]) / 3.0
|
|
|
|
|
} else if numTimes == 3 {
|
|
|
|
|
avgTime = (times[0] + times[1] + times[2]) / 3.0
|
|
|
|
|
avgTime = (validTimes[0] + validTimes[1] + validTimes[2]) / 3.0
|
|
|
|
|
} else if numTimes == 2 {
|
|
|
|
|
avgTime = (times[0] + times[1]) / 2.0
|
|
|
|
|
avgTime = (validTimes[0] + validTimes[1]) / 2.0
|
|
|
|
|
} else if numTimes == 1 {
|
|
|
|
|
avgTime = validTimes[0]
|
|
|
|
|
} else {
|
|
|
|
|
avgTime = times[0]
|
|
|
|
|
// No valid times (all DNF)
|
|
|
|
|
avgTime = 999.999
|
|
|
|
|
allDNF = true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
results = append(results, models.FinalResult{
|
|
|
|
|
Racer: racer,
|
|
|
|
|
Times: times,
|
|
|
|
|
Times: displayTimes,
|
|
|
|
|
AverageTime: avgTime,
|
|
|
|
|
DNF: dnf,
|
|
|
|
|
DNF: allDNF,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Sort results by average time (DNF racers will be at the end)
|
|
|
|
|
sort.Slice(results, func(i, j int) bool {
|
|
|
|
|
if results[i].DNF && !results[j].DNF {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
if !results[i].DNF && results[j].DNF {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
return results[i].AverageTime < results[j].AverageTime
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|