added team view to season_leagues
This commit is contained in:
@@ -33,7 +33,7 @@ type Fixture struct {
|
||||
func NewFixture(ctx context.Context, tx bun.Tx, seasonShortName, leagueShortName string,
|
||||
homeTeamID, awayTeamID, round int, audit *AuditMeta,
|
||||
) (*Fixture, error) {
|
||||
season, league, teams, err := GetSeasonLeague(ctx, tx, seasonShortName, leagueShortName)
|
||||
season, league, teams, err := GetSeasonLeagueWithTeams(ctx, tx, seasonShortName, leagueShortName)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetSeasonLeague")
|
||||
}
|
||||
@@ -59,7 +59,7 @@ func NewFixture(ctx context.Context, tx bun.Tx, seasonShortName, leagueShortName
|
||||
func NewRound(ctx context.Context, tx bun.Tx, seasonShortName, leagueShortName string,
|
||||
round int, audit *AuditMeta,
|
||||
) ([]*Fixture, error) {
|
||||
season, league, teams, err := GetSeasonLeague(ctx, tx, seasonShortName, leagueShortName)
|
||||
season, league, teams, err := GetSeasonLeagueWithTeams(ctx, tx, seasonShortName, leagueShortName)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetSeasonLeague")
|
||||
}
|
||||
@@ -71,22 +71,22 @@ func NewRound(ctx context.Context, tx bun.Tx, seasonShortName, leagueShortName s
|
||||
return fixtures, nil
|
||||
}
|
||||
|
||||
func GetFixtures(ctx context.Context, tx bun.Tx, seasonShortName, leagueShortName string) (*Season, *League, []*Fixture, error) {
|
||||
season, league, _, err := GetSeasonLeague(ctx, tx, seasonShortName, leagueShortName)
|
||||
func GetFixtures(ctx context.Context, tx bun.Tx, seasonShortName, leagueShortName string) (*SeasonLeague, []*Fixture, error) {
|
||||
sl, err := GetSeasonLeague(ctx, tx, seasonShortName, leagueShortName)
|
||||
if err != nil {
|
||||
return nil, nil, nil, errors.Wrap(err, "GetSeasonLeague")
|
||||
return nil, nil, errors.Wrap(err, "GetSeasonLeague")
|
||||
}
|
||||
fixtures, err := GetList[Fixture](tx).
|
||||
Where("season_id = ?", season.ID).
|
||||
Where("league_id = ?", league.ID).
|
||||
Where("season_id = ?", sl.SeasonID).
|
||||
Where("league_id = ?", sl.LeagueID).
|
||||
Order("game_week ASC NULLS FIRST", "round ASC", "id ASC").
|
||||
Relation("HomeTeam").
|
||||
Relation("AwayTeam").
|
||||
GetAll(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, nil, errors.Wrap(err, "GetList")
|
||||
return nil, nil, errors.Wrap(err, "GetList")
|
||||
}
|
||||
return season, league, fixtures, nil
|
||||
return sl, fixtures, nil
|
||||
}
|
||||
|
||||
func GetFixture(ctx context.Context, tx bun.Tx, id int) (*Fixture, error) {
|
||||
@@ -98,6 +98,22 @@ func GetFixture(ctx context.Context, tx bun.Tx, id int) (*Fixture, error) {
|
||||
Get(ctx)
|
||||
}
|
||||
|
||||
func GetFixturesForTeam(ctx context.Context, tx bun.Tx, seasonID, leagueID, teamID int) ([]*Fixture, error) {
|
||||
fixtures, err := GetList[Fixture](tx).
|
||||
Where("season_id = ?", seasonID).
|
||||
Where("league_id = ?", leagueID).
|
||||
Where("game_week IS NOT NULL").
|
||||
Where("(home_team_id = ? OR away_team_id = ?)", teamID, teamID).
|
||||
Order("game_week ASC", "round ASC", "id ASC").
|
||||
Relation("HomeTeam").
|
||||
Relation("AwayTeam").
|
||||
GetAll(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetList")
|
||||
}
|
||||
return fixtures, nil
|
||||
}
|
||||
|
||||
func GetFixturesByGameWeek(ctx context.Context, tx bun.Tx, seasonID, leagueID, gameweek int) ([]*Fixture, error) {
|
||||
fixtures, err := GetList[Fixture](tx).
|
||||
Where("season_id = ?", seasonID).
|
||||
@@ -180,13 +196,13 @@ func UpdateFixtureGameWeeks(ctx context.Context, tx bun.Tx, fixtures []*Fixture,
|
||||
}
|
||||
|
||||
func DeleteAllFixtures(ctx context.Context, tx bun.Tx, seasonShortName, leagueShortName string, audit *AuditMeta) error {
|
||||
season, league, _, err := GetSeasonLeague(ctx, tx, seasonShortName, leagueShortName)
|
||||
sl, err := GetSeasonLeague(ctx, tx, seasonShortName, leagueShortName)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GetSeasonLeague")
|
||||
}
|
||||
err = DeleteItem[Fixture](tx).
|
||||
Where("season_id = ?", season.ID).
|
||||
Where("league_id = ?", league.ID).
|
||||
Where("season_id = ?", sl.SeasonID).
|
||||
Where("league_id = ?", sl.LeagueID).
|
||||
WithAudit(audit, nil).
|
||||
Delete(ctx)
|
||||
if err != nil {
|
||||
|
||||
30
internal/db/migrations/20260220174806_team_rosters.go
Normal file
30
internal/db/migrations/20260220174806_team_rosters.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"git.haelnorr.com/h/oslstats/internal/db"
|
||||
"github.com/uptrace/bun"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Migrations.MustRegister(
|
||||
// UP migration
|
||||
func(ctx context.Context, conn *bun.DB) error {
|
||||
// Add your migration code here
|
||||
_, err := conn.NewCreateTable().
|
||||
IfNotExists().
|
||||
Model((*db.TeamRoster)(nil)).
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
// DOWN migration
|
||||
func(ctx context.Context, conn *bun.DB) error {
|
||||
// Add your rollback code here
|
||||
return nil
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -90,3 +90,16 @@ func UpdatePlayerSlapID(ctx context.Context, tx bun.Tx, playerID int, slapID uin
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetPlayersNotOnTeam(ctx context.Context, tx bun.Tx, seasonID, leagueID int) ([]*Player, error) {
|
||||
players, err := GetList[Player](tx).Relation("User").
|
||||
Join("LEFT JOIN team_rosters tr ON tr.player_id = p.id").
|
||||
Where("NOT (tr.season_id = ? and tr.league_id = ?) OR (tr.season_id IS NULL and tr.league_id IS NULL)",
|
||||
seasonID, leagueID).
|
||||
Order("p.name ASC").
|
||||
GetAll(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetList")
|
||||
}
|
||||
return players, nil
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
|
||||
"git.haelnorr.com/h/oslstats/internal/permissions"
|
||||
"github.com/pkg/errors"
|
||||
@@ -15,8 +16,36 @@ type SeasonLeague struct {
|
||||
League *League `bun:"rel:belongs-to,join:league_id=id"`
|
||||
}
|
||||
|
||||
// GetSeasonLeague retrieves a specific season-league combination with teams
|
||||
func GetSeasonLeague(ctx context.Context, tx bun.Tx, seasonShortName, leagueShortName string) (*Season, *League, []*Team, error) {
|
||||
// GetSeasonLeague retrieves a specific season-league combination
|
||||
func GetSeasonLeague(ctx context.Context, tx bun.Tx, seasonShortName, leagueShortName string) (*SeasonLeague, error) {
|
||||
if seasonShortName == "" {
|
||||
return nil, errors.New("season short_name cannot be empty")
|
||||
}
|
||||
if leagueShortName == "" {
|
||||
return nil, errors.New("league short_name cannot be empty")
|
||||
}
|
||||
|
||||
sl := new(SeasonLeague)
|
||||
err := tx.NewSelect().
|
||||
Model(sl).
|
||||
Relation("Season", func(q *bun.SelectQuery) *bun.SelectQuery {
|
||||
return q.Where("season.short_name = ?", seasonShortName)
|
||||
}).
|
||||
Relation("League", func(q *bun.SelectQuery) *bun.SelectQuery {
|
||||
return q.Where("league.short_name = ?", leagueShortName)
|
||||
}).Scan(ctx)
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, BadRequestNotFound("season_league", "season.short_name,league.short_name", seasonShortName+","+leagueShortName)
|
||||
}
|
||||
return nil, errors.Wrap(err, "tx.NewSelect")
|
||||
}
|
||||
|
||||
return sl, nil
|
||||
}
|
||||
|
||||
// GetSeasonLeagueWithTeams retrieves a specific season-league combination with teams
|
||||
func GetSeasonLeagueWithTeams(ctx context.Context, tx bun.Tx, seasonShortName, leagueShortName string) (*Season, *League, []*Team, error) {
|
||||
if seasonShortName == "" {
|
||||
return nil, nil, nil, errors.New("season short_name cannot be empty")
|
||||
}
|
||||
@@ -41,6 +70,9 @@ func GetSeasonLeague(ctx context.Context, tx bun.Tx, seasonShortName, leagueShor
|
||||
Join("INNER JOIN team_participations AS tp ON tp.team_id = t.id").
|
||||
Where("tp.season_id = ? AND tp.league_id = ?", season.ID, league.ID).
|
||||
Order("t.name ASC").
|
||||
Relation("Players", func(q *bun.SelectQuery) *bun.SelectQuery {
|
||||
return q.Where("season_id = ? AND league_id = ?", season.ID, league.ID)
|
||||
}).
|
||||
Scan(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, nil, errors.Wrap(err, "tx.Select teams")
|
||||
|
||||
@@ -24,6 +24,7 @@ func (db *DB) RegisterModels() []any {
|
||||
(*UserRole)(nil),
|
||||
(*SeasonLeague)(nil),
|
||||
(*TeamParticipation)(nil),
|
||||
(*TeamRoster)(nil),
|
||||
(*User)(nil),
|
||||
(*DiscordToken)(nil),
|
||||
(*Season)(nil),
|
||||
|
||||
@@ -17,6 +17,7 @@ type Team struct {
|
||||
|
||||
Seasons []Season `bun:"m2m:team_participations,join:Team=Season" json:"-"`
|
||||
Leagues []League `bun:"m2m:team_participations,join:Team=League" json:"-"`
|
||||
Players []Player `bun:"m2m:team_rosters,join:Team=Player" json:"-"`
|
||||
}
|
||||
|
||||
func NewTeam(ctx context.Context, tx bun.Tx, name, shortName, altShortName, color string, audit *AuditMeta) (*Team, error) {
|
||||
|
||||
176
internal/db/teamroster.go
Normal file
176
internal/db/teamroster.go
Normal file
@@ -0,0 +1,176 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/uptrace/bun"
|
||||
)
|
||||
|
||||
type TeamRoster struct {
|
||||
bun.BaseModel `bun:"table:team_rosters,alias:tr"`
|
||||
TeamID int `bun:",pk,notnull" json:"team_id"`
|
||||
SeasonID int `bun:",pk,notnull,unique:player" json:"season_id"`
|
||||
LeagueID int `bun:",pk,notnull,unique:player" json:"league_id"`
|
||||
PlayerID int `bun:",pk,notnull,unique:player" json:"player_id"`
|
||||
IsManager bool `bun:"is_manager,default:'false'" json:"is_manager"`
|
||||
|
||||
Team *Team `bun:"rel:belongs-to,join:team_id=id" json:"-"`
|
||||
Player *Player `bun:"rel:belongs-to,join:player_id=id" json:"-"`
|
||||
Season *Season `bun:"rel:belongs-to,join:season_id=id" json:"-"`
|
||||
League *League `bun:"rel:belongs-to,join:league_id=id" json:"-"`
|
||||
}
|
||||
|
||||
type TeamWithRoster struct {
|
||||
Team *Team
|
||||
Season *Season
|
||||
League *League
|
||||
Manager *Player
|
||||
Players []*Player
|
||||
}
|
||||
|
||||
func GetTeamRoster(ctx context.Context, tx bun.Tx, seasonShortName, leagueShortName string, teamID int) (*TeamWithRoster, error) {
|
||||
tr := []*TeamRoster{}
|
||||
err := tx.NewSelect().
|
||||
Model(&tr).
|
||||
Relation("Team", func(q *bun.SelectQuery) *bun.SelectQuery {
|
||||
return q.Where("team.id = ?", teamID)
|
||||
}).
|
||||
Relation("Season", func(q *bun.SelectQuery) *bun.SelectQuery {
|
||||
return q.Where("season.short_name = ?", seasonShortName)
|
||||
}).
|
||||
Relation("League", func(q *bun.SelectQuery) *bun.SelectQuery {
|
||||
return q.Where("league.short_name = ?", leagueShortName)
|
||||
}).
|
||||
Relation("Player").Scan(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "tx.NewSelect")
|
||||
}
|
||||
team, err := GetTeam(ctx, tx, teamID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetTeam")
|
||||
}
|
||||
sl, err := GetSeasonLeague(ctx, tx, seasonShortName, leagueShortName)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetSeasonLeague")
|
||||
}
|
||||
var manager *Player
|
||||
players := []*Player{}
|
||||
for _, tp := range tr {
|
||||
if tp.IsManager {
|
||||
manager = tp.Player
|
||||
} else {
|
||||
players = append(players, tp.Player)
|
||||
}
|
||||
}
|
||||
players = append([]*Player{manager}, players...)
|
||||
twr := &TeamWithRoster{
|
||||
team,
|
||||
sl.Season,
|
||||
sl.League,
|
||||
manager,
|
||||
players,
|
||||
}
|
||||
return twr, nil
|
||||
}
|
||||
|
||||
func AddPlayerToTeam(ctx context.Context, tx bun.Tx, seasonID, leagueID, teamID, playerID int, manager bool, audit *AuditMeta) error {
|
||||
season, err := GetByID[Season](tx, seasonID).Get(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GetSeason")
|
||||
}
|
||||
league, err := GetByID[League](tx, leagueID).Get(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GetLeague")
|
||||
}
|
||||
team, err := GetByID[Team](tx, teamID).Get(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GetTeam")
|
||||
}
|
||||
player, err := GetByID[Player](tx, playerID).Get(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GetPlayer")
|
||||
}
|
||||
tr := &TeamRoster{
|
||||
SeasonID: season.ID,
|
||||
LeagueID: league.ID,
|
||||
TeamID: team.ID,
|
||||
PlayerID: player.ID,
|
||||
IsManager: manager,
|
||||
}
|
||||
err = Insert(tx, tr).WithAudit(audit, nil).Exec(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Insert")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ManageTeamRoster replaces the entire roster for a team in a season/league.
|
||||
// It deletes all existing roster entries and inserts the new ones.
|
||||
func ManageTeamRoster(ctx context.Context, tx bun.Tx, seasonID, leagueID, teamID, managerID int, playerIDs []int, audit *AuditMeta) error {
|
||||
// Delete all existing roster entries for this team/season/league
|
||||
_, err := tx.NewDelete().
|
||||
Model((*TeamRoster)(nil)).
|
||||
Where("season_id = ?", seasonID).
|
||||
Where("league_id = ?", leagueID).
|
||||
Where("team_id = ?", teamID).
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "delete existing roster")
|
||||
}
|
||||
|
||||
// Insert manager if provided
|
||||
if managerID > 0 {
|
||||
tr := &TeamRoster{
|
||||
SeasonID: seasonID,
|
||||
LeagueID: leagueID,
|
||||
TeamID: teamID,
|
||||
PlayerID: managerID,
|
||||
IsManager: true,
|
||||
}
|
||||
err = Insert(tx, tr).Exec(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Insert manager")
|
||||
}
|
||||
}
|
||||
|
||||
// Insert players
|
||||
for _, playerID := range playerIDs {
|
||||
if playerID == managerID {
|
||||
continue // Already inserted as manager
|
||||
}
|
||||
tr := &TeamRoster{
|
||||
SeasonID: seasonID,
|
||||
LeagueID: leagueID,
|
||||
TeamID: teamID,
|
||||
PlayerID: playerID,
|
||||
IsManager: false,
|
||||
}
|
||||
err = Insert(tx, tr).Exec(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Insert player")
|
||||
}
|
||||
}
|
||||
|
||||
// Log the roster change
|
||||
details := map[string]any{
|
||||
"season_id": seasonID,
|
||||
"league_id": leagueID,
|
||||
"team_id": teamID,
|
||||
"manager_id": managerID,
|
||||
"player_ids": playerIDs,
|
||||
}
|
||||
info := &AuditInfo{
|
||||
"teams.manage_players",
|
||||
"team_roster",
|
||||
fmt.Sprintf("%d-%d-%d", seasonID, leagueID, teamID),
|
||||
details,
|
||||
}
|
||||
err = LogSuccess(ctx, tx, audit, info)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "LogSuccess")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user