mirror of
https://github.com/AmanTahiliani/box-box.git
synced 2026-08-07 19:56:18 -04:00
Add analytics data foundation
This commit is contained in:
@@ -99,3 +99,102 @@ func parseJSONValue(raw string) interface{} {
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func stintToModel(st store.Stint) models.Stint {
|
||||
return models.Stint{
|
||||
SessionKey: st.SessionKey,
|
||||
DriverNumber: st.DriverNumber,
|
||||
MeetingKey: st.MeetingKey,
|
||||
StintNumber: st.StintNumber,
|
||||
Compound: models.TyreCompound(st.Compound),
|
||||
LapStart: st.LapStart,
|
||||
LapEnd: st.LapEnd,
|
||||
TyreAgeAtStart: st.TyreAgeAtStart,
|
||||
}
|
||||
}
|
||||
|
||||
func pitStopToModel(p store.PitStop) models.Pit {
|
||||
stopDuration := p.StopDuration
|
||||
if stopDuration == 0 {
|
||||
stopDuration = p.PitDuration
|
||||
}
|
||||
return models.Pit{
|
||||
SessionKey: p.SessionKey,
|
||||
DriverNumber: p.DriverNumber,
|
||||
MeetingKey: p.MeetingKey,
|
||||
LapNumber: p.LapNumber,
|
||||
Date: p.Date,
|
||||
PitDuration: p.PitDuration,
|
||||
LaneDuration: p.LaneDuration,
|
||||
StopDuration: stopDuration,
|
||||
}
|
||||
}
|
||||
|
||||
func positionToModel(p store.PositionSample) models.Position {
|
||||
return models.Position{
|
||||
SessionKey: p.SessionKey,
|
||||
DriverNumber: p.DriverNumber,
|
||||
MeetingKey: p.MeetingKey,
|
||||
Date: p.Date,
|
||||
Position: p.Position,
|
||||
}
|
||||
}
|
||||
|
||||
func raceControlToModel(rc store.RaceControlMessage) models.RaceControl {
|
||||
return models.RaceControl{
|
||||
SessionKey: rc.SessionKey,
|
||||
MeetingKey: rc.MeetingKey,
|
||||
Date: rc.Date,
|
||||
Category: models.RaceControlCategory(rc.Category),
|
||||
Flag: models.Flag(rc.Flag),
|
||||
Message: rc.Message,
|
||||
Scope: rc.Scope,
|
||||
DriverNumber: rc.DriverNumber,
|
||||
LapNumber: rc.LapNumber,
|
||||
Sector: rc.Sector,
|
||||
QualifyingPhase: rc.QualifyingPhase,
|
||||
}
|
||||
}
|
||||
|
||||
func weatherToModel(w store.WeatherSample) models.Weather {
|
||||
return models.Weather{
|
||||
SessionKey: w.SessionKey,
|
||||
MeetingKey: w.MeetingKey,
|
||||
Date: w.Date,
|
||||
AirTemperature: w.AirTemperature,
|
||||
TrackTemperature: w.TrackTemperature,
|
||||
Humidity: w.Humidity,
|
||||
Pressure: w.Pressure,
|
||||
Rainfall: w.Rainfall,
|
||||
WindDirection: w.WindDirection,
|
||||
WindSpeed: w.WindSpeed,
|
||||
}
|
||||
}
|
||||
|
||||
func lapToModel(l store.Lap) models.Lap {
|
||||
lap := models.Lap{
|
||||
SessionKey: l.SessionKey,
|
||||
DriverNumber: l.DriverNumber,
|
||||
MeetingKey: l.MeetingKey,
|
||||
LapNumber: l.LapNumber,
|
||||
DateStart: l.DateStart,
|
||||
IsPitOutLap: l.IsPitOutLap,
|
||||
}
|
||||
if l.LapDuration != 0 {
|
||||
d := l.LapDuration
|
||||
lap.LapDuration = &d
|
||||
}
|
||||
if l.DurationSector1 != 0 {
|
||||
d := l.DurationSector1
|
||||
lap.DurationSector1 = &d
|
||||
}
|
||||
if l.DurationSector2 != 0 {
|
||||
d := l.DurationSector2
|
||||
lap.DurationSector2 = &d
|
||||
}
|
||||
if l.DurationSector3 != 0 {
|
||||
d := l.DurationSector3
|
||||
lap.DurationSector3 = &d
|
||||
}
|
||||
return lap
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/AmanTahiliani/box-box/internal/models"
|
||||
"github.com/AmanTahiliani/box-box/internal/store"
|
||||
)
|
||||
|
||||
@@ -150,8 +151,8 @@ func TestGetRaceHubCompleteData(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("GetRaceHub() error = %v", err)
|
||||
}
|
||||
if hub.Source != ResponseSourceLocal {
|
||||
t.Fatalf("Source = %q, want %q", hub.Source, ResponseSourceLocal)
|
||||
if hub.Source != ResponseSourcePartial {
|
||||
t.Fatalf("Source = %q, want %q (core datasets complete, analytics missing)", hub.Source, ResponseSourcePartial)
|
||||
}
|
||||
if len(hub.Results) != 1 {
|
||||
t.Fatalf("Results len = %d, want 1", len(hub.Results))
|
||||
@@ -185,6 +186,47 @@ func TestListMeetingsByYear(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetRaceHubAnalyticsDatasets(t *testing.T) {
|
||||
svc := openTestService(t)
|
||||
seedRaceHubData(t, svc.store)
|
||||
|
||||
sessionKey := 9472
|
||||
meetingKey := 1229
|
||||
|
||||
if err := svc.store.UpsertStint(store.Stint{
|
||||
SessionKey: sessionKey, DriverNumber: 1, MeetingKey: meetingKey,
|
||||
StintNumber: 1, Compound: "MEDIUM", LapStart: 1, LapEnd: 30,
|
||||
}); err != nil {
|
||||
t.Fatalf("UpsertStint() error = %v", err)
|
||||
}
|
||||
if err := svc.store.UpsertPositionSample(store.PositionSample{
|
||||
SessionKey: sessionKey, DriverNumber: 1, MeetingKey: meetingKey,
|
||||
Date: "2025-05-25T13:05:00+00:00", Position: 1,
|
||||
}); err != nil {
|
||||
t.Fatalf("UpsertPositionSample() error = %v", err)
|
||||
}
|
||||
|
||||
hub, err := svc.GetRaceHub(sessionKey)
|
||||
if err != nil {
|
||||
t.Fatalf("GetRaceHub() error = %v", err)
|
||||
}
|
||||
if hub.Datasets["stints"].Status != DatasetStatusAvailable {
|
||||
t.Fatalf("stints status = %+v, want available", hub.Datasets["stints"])
|
||||
}
|
||||
if hub.Datasets["positions"].Status != DatasetStatusAvailable {
|
||||
t.Fatalf("positions status = %+v, want available", hub.Datasets["positions"])
|
||||
}
|
||||
if len(hub.Stints) != 1 || hub.Stints[0].Compound != models.CompoundMedium {
|
||||
t.Fatalf("Stints = %+v, want one MEDIUM stint", hub.Stints)
|
||||
}
|
||||
if len(hub.Positions) != 1 {
|
||||
t.Fatalf("Positions len = %d, want 1", len(hub.Positions))
|
||||
}
|
||||
if hub.Datasets["pit_stops"].Status != DatasetStatusMissing {
|
||||
t.Fatalf("pit_stops status = %+v, want missing", hub.Datasets["pit_stops"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestListDriversRequiresSession(t *testing.T) {
|
||||
svc := openTestService(t)
|
||||
|
||||
|
||||
@@ -46,6 +46,12 @@ type RaceHub struct {
|
||||
Drivers []models.Driver `json:"drivers"`
|
||||
Results []EnrichedResult `json:"results"`
|
||||
StartingGrid []EnrichedGrid `json:"starting_grid"`
|
||||
Stints []models.Stint `json:"stints"`
|
||||
PitStops []models.Pit `json:"pit_stops"`
|
||||
Positions []models.Position `json:"positions"`
|
||||
RaceControl []models.RaceControl `json:"race_control"`
|
||||
Weather []models.Weather `json:"weather"`
|
||||
Laps []models.Lap `json:"laps"`
|
||||
}
|
||||
|
||||
// GetRaceHub loads ingested Race Hub datasets for a session from the local store.
|
||||
@@ -58,10 +64,22 @@ func (s *Service) GetRaceHub(sessionKey int) (RaceHub, error) {
|
||||
"drivers": missingDataset(),
|
||||
"results": missingDataset(),
|
||||
"starting_grid": missingDataset(),
|
||||
"stints": missingDataset(),
|
||||
"pit_stops": missingDataset(),
|
||||
"positions": missingDataset(),
|
||||
"race_control": missingDataset(),
|
||||
"weather": missingDataset(),
|
||||
"laps": missingDataset(),
|
||||
},
|
||||
Drivers: []models.Driver{},
|
||||
Results: []EnrichedResult{},
|
||||
StartingGrid: []EnrichedGrid{},
|
||||
Stints: []models.Stint{},
|
||||
PitStops: []models.Pit{},
|
||||
Positions: []models.Position{},
|
||||
RaceControl: []models.RaceControl{},
|
||||
Weather: []models.Weather{},
|
||||
Laps: []models.Lap{},
|
||||
}
|
||||
|
||||
sess, err := s.store.GetSession(sessionKey)
|
||||
@@ -150,6 +168,78 @@ func (s *Service) GetRaceHub(sessionKey int) (RaceHub, error) {
|
||||
hub.Datasets["starting_grid"] = availableLocal(len(enriched))
|
||||
}
|
||||
|
||||
stints, err := s.store.ListStints(sessionKey)
|
||||
if err != nil {
|
||||
return RaceHub{}, err
|
||||
}
|
||||
if len(stints) > 0 {
|
||||
hub.Stints = make([]models.Stint, 0, len(stints))
|
||||
for _, st := range stints {
|
||||
hub.Stints = append(hub.Stints, stintToModel(st))
|
||||
}
|
||||
hub.Datasets["stints"] = availableLocal(len(hub.Stints))
|
||||
}
|
||||
|
||||
pitStops, err := s.store.ListPitStops(sessionKey)
|
||||
if err != nil {
|
||||
return RaceHub{}, err
|
||||
}
|
||||
if len(pitStops) > 0 {
|
||||
hub.PitStops = make([]models.Pit, 0, len(pitStops))
|
||||
for _, p := range pitStops {
|
||||
hub.PitStops = append(hub.PitStops, pitStopToModel(p))
|
||||
}
|
||||
hub.Datasets["pit_stops"] = availableLocal(len(hub.PitStops))
|
||||
}
|
||||
|
||||
positions, err := s.store.ListPositionSamples(sessionKey)
|
||||
if err != nil {
|
||||
return RaceHub{}, err
|
||||
}
|
||||
if len(positions) > 0 {
|
||||
hub.Positions = make([]models.Position, 0, len(positions))
|
||||
for _, p := range positions {
|
||||
hub.Positions = append(hub.Positions, positionToModel(p))
|
||||
}
|
||||
hub.Datasets["positions"] = availableLocal(len(hub.Positions))
|
||||
}
|
||||
|
||||
raceControl, err := s.store.ListRaceControlMessages(sessionKey)
|
||||
if err != nil {
|
||||
return RaceHub{}, err
|
||||
}
|
||||
if len(raceControl) > 0 {
|
||||
hub.RaceControl = make([]models.RaceControl, 0, len(raceControl))
|
||||
for _, rc := range raceControl {
|
||||
hub.RaceControl = append(hub.RaceControl, raceControlToModel(rc))
|
||||
}
|
||||
hub.Datasets["race_control"] = availableLocal(len(hub.RaceControl))
|
||||
}
|
||||
|
||||
weather, err := s.store.ListWeatherSamples(sessionKey)
|
||||
if err != nil {
|
||||
return RaceHub{}, err
|
||||
}
|
||||
if len(weather) > 0 {
|
||||
hub.Weather = make([]models.Weather, 0, len(weather))
|
||||
for _, w := range weather {
|
||||
hub.Weather = append(hub.Weather, weatherToModel(w))
|
||||
}
|
||||
hub.Datasets["weather"] = availableLocal(len(hub.Weather))
|
||||
}
|
||||
|
||||
laps, err := s.store.ListLaps(sessionKey)
|
||||
if err != nil {
|
||||
return RaceHub{}, err
|
||||
}
|
||||
if len(laps) > 0 {
|
||||
hub.Laps = make([]models.Lap, 0, len(laps))
|
||||
for _, l := range laps {
|
||||
hub.Laps = append(hub.Laps, lapToModel(l))
|
||||
}
|
||||
hub.Datasets["laps"] = availableLocal(len(hub.Laps))
|
||||
}
|
||||
|
||||
hub.Source = responseSource(hub.Datasets)
|
||||
return hub, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user