package query import ( "database/sql" "errors" "github.com/AmanTahiliani/box-box/internal/models" ) // ErrMeetingNotFound is returned when a meeting is not in the local store. var ErrMeetingNotFound = errors.New("meeting not found") // WeekendSession is one session within a meeting with dataset coverage. type WeekendSession struct { Session models.Session `json:"session"` Source string `json:"source"` Datasets map[string]DatasetInfo `json:"datasets"` } // Weekend is the local-first read model for one race weekend. type Weekend struct { Source string `json:"source"` MeetingKey int `json:"meeting_key"` Meeting models.Meeting `json:"meeting"` Sessions []WeekendSession `json:"sessions"` DefaultSessionKey int `json:"default_session_key,omitempty"` } // ListSeasons returns years with ingested meetings, newest first. func (s *Service) ListSeasons() ([]int, error) { return s.store.ListYears() } // GetWeekend loads meeting metadata, sessions, and per-session dataset coverage. func (s *Service) GetWeekend(meetingKey int) (Weekend, error) { meeting, err := s.store.GetMeeting(meetingKey) if err != nil { if errors.Is(err, sql.ErrNoRows) { return Weekend{}, ErrMeetingNotFound } return Weekend{}, err } sessions, err := s.store.ListSessionsByMeeting(meetingKey) if err != nil { return Weekend{}, err } out := Weekend{ Source: ResponseSourceNone, MeetingKey: meetingKey, Meeting: meetingToModel(meeting), Sessions: []WeekendSession{}, } if len(sessions) == 0 { return out, nil } out.Sessions = make([]WeekendSession, 0, len(sessions)) for _, sess := range sessions { counts, err := s.store.CountSessionDatasets(sess.SessionKey) if err != nil { return Weekend{}, err } datasets := datasetsFromCounts(true, true, counts) sessionModel := sessionToModel(sess) if datasets["starting_grid"].Status == DatasetStatusMissing && !isGridExpected(sessionModel.SessionType, sessionModel.SessionName) { datasets["starting_grid"] = skippedNA() } out.Sessions = append(out.Sessions, WeekendSession{ Session: sessionModel, Source: responseSource(datasets), Datasets: datasets, }) } out.Source = weekendSource(out.Sessions) out.DefaultSessionKey = pickDefaultSession(out.Sessions) return out, nil } func weekendSource(sessions []WeekendSession) string { if len(sessions) == 0 { return ResponseSourceNone } hasLocal := false allMissing := true for _, sess := range sessions { if sess.Source == ResponseSourceNone { continue } allMissing = false if sess.Source == ResponseSourceLocal || sess.Source == ResponseSourcePartial { hasLocal = true } } if allMissing { return ResponseSourceNone } for _, sess := range sessions { if sess.Source == ResponseSourcePartial || sess.Source == ResponseSourceNone { return ResponseSourcePartial } } if hasLocal { return ResponseSourceLocal } return ResponseSourceNone } func pickDefaultSession(sessions []WeekendSession) int { if len(sessions) == 0 { return 0 } bestIdx := 0 bestScore := datasetScore(sessions[0].Datasets) for i := 1; i < len(sessions); i++ { score := datasetScore(sessions[i].Datasets) if score >= bestScore { bestScore = score bestIdx = i } } return sessions[bestIdx].Session.SessionKey } func datasetScore(datasets map[string]DatasetInfo) int { score := 0 for _, info := range datasets { if info.Status == DatasetStatusAvailable { score++ } } return score }