184 lines
5.1 KiB
Go
184 lines
5.1 KiB
Go
package db
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/pkg/errors"
|
|
"github.com/uptrace/bun"
|
|
)
|
|
|
|
// SeasonStatus represents the current status of a season
|
|
type SeasonStatus string
|
|
|
|
const (
|
|
// StatusUpcoming means the season has not started yet
|
|
StatusUpcoming SeasonStatus = "upcoming"
|
|
// StatusInProgress means the regular season is active
|
|
StatusInProgress SeasonStatus = "in_progress"
|
|
// StatusFinalsSoon means regular season ended, finals upcoming
|
|
StatusFinalsSoon SeasonStatus = "finals_soon"
|
|
// StatusFinals means finals are in progress
|
|
StatusFinals SeasonStatus = "finals"
|
|
// StatusCompleted means the season has finished
|
|
StatusCompleted SeasonStatus = "completed"
|
|
)
|
|
|
|
type Season struct {
|
|
bun.BaseModel `bun:"table:seasons,alias:s"`
|
|
|
|
ID int `bun:"id,pk,autoincrement" json:"id"`
|
|
Name string `bun:"name,unique,notnull" json:"name"`
|
|
ShortName string `bun:"short_name,unique,notnull" json:"short_name"`
|
|
StartDate time.Time `bun:"start_date,notnull" json:"start_date"`
|
|
EndDate bun.NullTime `bun:"end_date" json:"end_date"`
|
|
FinalsStartDate bun.NullTime `bun:"finals_start_date" json:"finals_start_date"`
|
|
FinalsEndDate bun.NullTime `bun:"finals_end_date" json:"finals_end_date"`
|
|
SlapVersion string `bun:"slap_version,notnull,default:'rebound'" json:"slap_version"`
|
|
|
|
Leagues []League `bun:"m2m:season_leagues,join:Season=League" json:"-"`
|
|
Teams []Team `bun:"m2m:team_participations,join:Season=Team" json:"-"`
|
|
}
|
|
|
|
// NewSeason creats a new season
|
|
func NewSeason(ctx context.Context, tx bun.Tx, name, version, shortname string,
|
|
start time.Time, audit *AuditMeta,
|
|
) (*Season, error) {
|
|
season := &Season{
|
|
Name: name,
|
|
ShortName: strings.ToUpper(shortname),
|
|
StartDate: start.Truncate(time.Hour * 24),
|
|
SlapVersion: version,
|
|
}
|
|
err := Insert(tx, season).
|
|
WithAudit(audit, nil).Exec(ctx)
|
|
if err != nil {
|
|
return nil, errors.WithMessage(err, "db.Insert")
|
|
}
|
|
return season, nil
|
|
}
|
|
|
|
func ListSeasons(ctx context.Context, tx bun.Tx, pageOpts *PageOpts) (*List[Season], error) {
|
|
defaults := &PageOpts{
|
|
1,
|
|
10,
|
|
bun.OrderDesc,
|
|
"start_date",
|
|
}
|
|
return GetList[Season](tx).Relation("Leagues").GetPaged(ctx, pageOpts, defaults)
|
|
}
|
|
|
|
func GetSeason(ctx context.Context, tx bun.Tx, shortname string) (*Season, error) {
|
|
if shortname == "" {
|
|
return nil, errors.New("short_name not provided")
|
|
}
|
|
return GetByField[Season](tx, "short_name", shortname).Relation("Leagues").Relation("Teams").Get(ctx)
|
|
}
|
|
|
|
// Update updates the season struct. It does not insert to the database
|
|
func (s *Season) Update(ctx context.Context, tx bun.Tx, version string,
|
|
start, end, finalsStart, finalsEnd time.Time, audit *AuditMeta,
|
|
) error {
|
|
s.SlapVersion = version
|
|
s.StartDate = start.Truncate(time.Hour * 24)
|
|
if !end.IsZero() {
|
|
s.EndDate.Time = end.Truncate(time.Hour * 24)
|
|
}
|
|
if !finalsStart.IsZero() {
|
|
s.FinalsStartDate.Time = finalsStart.Truncate(time.Hour * 24)
|
|
}
|
|
if !finalsEnd.IsZero() {
|
|
s.FinalsEndDate.Time = finalsEnd.Truncate(time.Hour * 24)
|
|
}
|
|
return Update(tx, s).WherePK().
|
|
Column("slap_version", "start_date", "end_date", "finals_start_date", "finals_end_date").
|
|
WithAudit(audit, nil).Exec(ctx)
|
|
}
|
|
|
|
func (s *Season) MapTeamsToLeagues(ctx context.Context, tx bun.Tx) ([]LeagueWithTeams, error) {
|
|
// For each league, get the teams
|
|
leaguesWithTeams := make([]LeagueWithTeams, len(s.Leagues))
|
|
for i, league := range s.Leagues {
|
|
var teams []*Team
|
|
err := tx.NewSelect().
|
|
Model(&teams).
|
|
Join("INNER JOIN team_participations AS tp ON tp.team_id = t.id").
|
|
Where("tp.season_id = ? AND tp.league_id = ?", s.ID, league.ID).
|
|
Order("t.name ASC").
|
|
Scan(ctx)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "tx.NewSelect")
|
|
}
|
|
leaguesWithTeams[i] = LeagueWithTeams{
|
|
League: &league,
|
|
Teams: teams,
|
|
}
|
|
}
|
|
return leaguesWithTeams, nil
|
|
}
|
|
|
|
type LeagueWithTeams struct {
|
|
League *League
|
|
Teams []*Team
|
|
}
|
|
|
|
// GetStatus returns the current status of the season based on dates
|
|
func (s *Season) GetStatus() SeasonStatus {
|
|
now := time.Now()
|
|
|
|
if now.Before(s.StartDate) {
|
|
return StatusUpcoming
|
|
}
|
|
|
|
if !s.FinalsStartDate.IsZero() {
|
|
if !s.FinalsEndDate.IsZero() && now.After(s.FinalsEndDate.Time) {
|
|
return StatusCompleted
|
|
}
|
|
if now.After(s.FinalsStartDate.Time) {
|
|
return StatusFinals
|
|
}
|
|
if !s.EndDate.IsZero() && now.After(s.EndDate.Time) {
|
|
return StatusFinalsSoon
|
|
}
|
|
return StatusInProgress
|
|
}
|
|
|
|
if !s.EndDate.IsZero() && now.After(s.EndDate.Time) {
|
|
return StatusCompleted
|
|
}
|
|
|
|
return StatusInProgress
|
|
}
|
|
|
|
// GetDefaultTab returns the default tab to show based on the season status
|
|
func (s *Season) GetDefaultTab() string {
|
|
switch s.GetStatus() {
|
|
case StatusInProgress:
|
|
return "table"
|
|
case StatusUpcoming:
|
|
return "teams"
|
|
default:
|
|
return "finals"
|
|
}
|
|
}
|
|
|
|
func (s *Season) HasLeague(league *League) bool {
|
|
for _, league_ := range s.Leagues {
|
|
if league_.ID == league.ID {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (s *Season) GetLeague(leagueShortName string) (*League, error) {
|
|
for _, league := range s.Leagues {
|
|
if league.ShortName == leagueShortName {
|
|
return &league, nil
|
|
}
|
|
}
|
|
return nil, BadRequestNotAssociated("season", "league",
|
|
"id", "short_name", s.ID, leagueShortName)
|
|
}
|