Files
box-box/internal/store/results.go

330 lines
8.3 KiB
Go

package store
import (
"database/sql"
"fmt"
"time"
)
// UpsertDriver inserts or updates a driver by driver_number.
func (s *Store) UpsertDriver(d Driver) error {
if d.UpdatedAt.IsZero() {
d.UpdatedAt = time.Now()
}
_, err := s.db.Exec(`
INSERT INTO drivers (
driver_number, broadcast_name, first_name, full_name, last_name,
name_acronym, headshot_url, team_name, team_colour, updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(driver_number) DO UPDATE SET
broadcast_name = excluded.broadcast_name,
first_name = excluded.first_name,
full_name = excluded.full_name,
last_name = excluded.last_name,
name_acronym = excluded.name_acronym,
headshot_url = excluded.headshot_url,
team_name = excluded.team_name,
team_colour = excluded.team_colour,
updated_at = excluded.updated_at
`,
d.DriverNumber,
nullString(d.BroadcastName),
nullString(d.FirstName),
d.FullName,
nullString(d.LastName),
nullString(d.NameAcronym),
nullString(d.HeadshotURL),
nullString(d.TeamName),
nullString(d.TeamColour),
d.UpdatedAt.Unix(),
)
if err != nil {
return fmt.Errorf("upsert driver: %w", err)
}
return nil
}
// GetDriver returns a driver by number.
func (s *Store) GetDriver(driverNumber int) (Driver, error) {
var d Driver
var updatedAt int64
var broadcastName, firstName, lastName, nameAcronym sql.NullString
var headshotURL, teamName, teamColour sql.NullString
err := s.db.QueryRow(`
SELECT driver_number, broadcast_name, first_name, full_name, last_name,
name_acronym, headshot_url, team_name, team_colour, updated_at
FROM drivers
WHERE driver_number = ?
`, driverNumber).Scan(
&d.DriverNumber,
&broadcastName,
&firstName,
&d.FullName,
&lastName,
&nameAcronym,
&headshotURL,
&teamName,
&teamColour,
&updatedAt,
)
if err != nil {
return Driver{}, err
}
d.BroadcastName = broadcastName.String
d.FirstName = firstName.String
d.LastName = lastName.String
d.NameAcronym = nameAcronym.String
d.HeadshotURL = headshotURL.String
d.TeamName = teamName.String
d.TeamColour = teamColour.String
d.UpdatedAt = time.Unix(updatedAt, 0)
return d, nil
}
// UpsertSessionDriver links a driver to a session.
func (s *Store) UpsertSessionDriver(sd SessionDriver) error {
_, err := s.db.Exec(`
INSERT INTO session_drivers (
session_key, driver_number, meeting_key, broadcast_name, first_name,
full_name, last_name, name_acronym, headshot_url, team_name, team_colour
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(session_key, driver_number) DO UPDATE SET
meeting_key = excluded.meeting_key,
broadcast_name = excluded.broadcast_name,
first_name = excluded.first_name,
full_name = excluded.full_name,
last_name = excluded.last_name,
name_acronym = excluded.name_acronym,
headshot_url = excluded.headshot_url,
team_name = excluded.team_name,
team_colour = excluded.team_colour
`,
sd.SessionKey,
sd.DriverNumber,
sd.MeetingKey,
nullString(sd.BroadcastName),
nullString(sd.FirstName),
nullString(sd.FullName),
nullString(sd.LastName),
nullString(sd.NameAcronym),
nullString(sd.HeadshotURL),
nullString(sd.TeamName),
nullString(sd.TeamColour),
)
if err != nil {
return fmt.Errorf("upsert session driver: %w", err)
}
return nil
}
// ListSessionDrivers returns drivers entered for a session ordered by number.
func (s *Store) ListSessionDrivers(sessionKey int) ([]SessionDriver, error) {
rows, err := s.db.Query(`
SELECT session_key, driver_number, meeting_key, broadcast_name, first_name,
full_name, last_name, name_acronym, headshot_url, team_name, team_colour
FROM session_drivers
WHERE session_key = ?
ORDER BY driver_number ASC
`, sessionKey)
if err != nil {
return nil, err
}
defer rows.Close()
var out []SessionDriver
for rows.Next() {
var sd SessionDriver
var broadcastName, firstName, fullName, lastName, nameAcronym, headshotURL sql.NullString
var teamName, teamColour sql.NullString
if err := rows.Scan(
&sd.SessionKey,
&sd.DriverNumber,
&sd.MeetingKey,
&broadcastName,
&firstName,
&fullName,
&lastName,
&nameAcronym,
&headshotURL,
&teamName,
&teamColour,
); err != nil {
return nil, err
}
sd.BroadcastName = broadcastName.String
sd.FirstName = firstName.String
sd.FullName = fullName.String
sd.LastName = lastName.String
sd.NameAcronym = nameAcronym.String
sd.HeadshotURL = headshotURL.String
sd.TeamName = teamName.String
sd.TeamColour = teamColour.String
out = append(out, sd)
}
return out, rows.Err()
}
// UpsertSessionResult inserts or updates a session classification row.
func (s *Store) UpsertSessionResult(r SessionResult) error {
_, err := s.db.Exec(`
INSERT INTO session_results (
session_key, driver_number, meeting_key, position, points,
number_of_laps, duration_json, gap_to_leader_json, dnf, dns, dsq
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(session_key, driver_number) DO UPDATE SET
meeting_key = excluded.meeting_key,
position = excluded.position,
points = excluded.points,
number_of_laps = excluded.number_of_laps,
duration_json = excluded.duration_json,
gap_to_leader_json = excluded.gap_to_leader_json,
dnf = excluded.dnf,
dns = excluded.dns,
dsq = excluded.dsq
`,
r.SessionKey,
r.DriverNumber,
r.MeetingKey,
r.Position,
r.Points,
nullableZeroInt(r.NumberOfLaps),
nullString(r.DurationJSON),
nullString(r.GapToLeaderJSON),
boolInt(r.DNF),
boolInt(r.DNS),
boolInt(r.DSQ),
)
if err != nil {
return fmt.Errorf("upsert session result: %w", err)
}
return nil
}
// ListSessionResults returns classification rows ordered by finishing position.
func (s *Store) ListSessionResults(sessionKey int) ([]SessionResult, error) {
rows, err := s.db.Query(`
SELECT session_key, driver_number, meeting_key, position, points,
number_of_laps, duration_json, gap_to_leader_json, dnf, dns, dsq
FROM session_results
WHERE session_key = ?
ORDER BY CASE WHEN position > 0 THEN position ELSE 9999 END ASC, driver_number ASC
`, sessionKey)
if err != nil {
return nil, err
}
defer rows.Close()
return scanSessionResults(rows)
}
// UpsertStartingGridEntry inserts or updates a starting grid row.
func (s *Store) UpsertStartingGridEntry(g StartingGridEntry) error {
_, err := s.db.Exec(`
INSERT INTO starting_grid (
session_key, driver_number, meeting_key, position, lap_duration
) VALUES (?, ?, ?, ?, ?)
ON CONFLICT(session_key, driver_number) DO UPDATE SET
meeting_key = excluded.meeting_key,
position = excluded.position,
lap_duration = excluded.lap_duration
`,
g.SessionKey,
g.DriverNumber,
g.MeetingKey,
g.Position,
nullableZeroFloat(g.LapDuration),
)
if err != nil {
return fmt.Errorf("upsert starting grid: %w", err)
}
return nil
}
// ListStartingGrid returns grid rows ordered by position.
func (s *Store) ListStartingGrid(sessionKey int) ([]StartingGridEntry, error) {
rows, err := s.db.Query(`
SELECT session_key, driver_number, meeting_key, position, lap_duration
FROM starting_grid
WHERE session_key = ?
ORDER BY position ASC, driver_number ASC
`, sessionKey)
if err != nil {
return nil, err
}
defer rows.Close()
var out []StartingGridEntry
for rows.Next() {
var g StartingGridEntry
var lapDuration sql.NullFloat64
if err := rows.Scan(
&g.SessionKey,
&g.DriverNumber,
&g.MeetingKey,
&g.Position,
&lapDuration,
); err != nil {
return nil, err
}
if lapDuration.Valid {
g.LapDuration = lapDuration.Float64
}
out = append(out, g)
}
return out, rows.Err()
}
func scanSessionResults(rows *sql.Rows) ([]SessionResult, error) {
var out []SessionResult
for rows.Next() {
var r SessionResult
var numberOfLaps sql.NullInt64
var durationJSON, gapJSON sql.NullString
var dnf, dns, dsq int
if err := rows.Scan(
&r.SessionKey,
&r.DriverNumber,
&r.MeetingKey,
&r.Position,
&r.Points,
&numberOfLaps,
&durationJSON,
&gapJSON,
&dnf,
&dns,
&dsq,
); err != nil {
return nil, err
}
if numberOfLaps.Valid {
r.NumberOfLaps = int(numberOfLaps.Int64)
}
r.DurationJSON = durationJSON.String
r.GapToLeaderJSON = gapJSON.String
r.DNF = dnf != 0
r.DNS = dns != 0
r.DSQ = dsq != 0
out = append(out, r)
}
return out, rows.Err()
}
func boolInt(v bool) int {
if v {
return 1
}
return 0
}
func nullableZeroFloat(v float64) any {
if v == 0 {
return nil
}
return v
}