from math import floor from app import app, db from datetime import date from typing import Sequence from pandas import DataFrame from operator import attrgetter from sqlalchemy import func, select, not_ from app.model import ( Player, Court, Game, DisplayGame, ) from app.utilities import ( get_placeholder_games, get_team_games, get_shuffle_games, separate_teams_and_players, initialize_random_database, initialize_default_players, ) class Courts: def __init__(self): with app.app_context(): db.session.query(Court).delete() for _ in range(app.config["COURT_COUNT"]): db.session.add(Court()) db.session.commit() def get(self, id: int) -> Court: court = db.session.get(Court, id) if court is None: raise ValueError(f"Court not found: {id}") return court def toggle(self, id: int) -> Court: court = self.get(id) court.status = not court.status db.session.commit() return court def active(self): return db.session.query(Court).where(Court.status).all() def inactive(self): return db.session.query(Court).where(not_(Court.status)).all() def all(self) -> list[Court]: return db.session.query(Court).order_by(Court.id).all() class Players: def __init__(self) -> None: with app.app_context(): if len(self.regulars()) == 0: initialize_random_database() def update(self, data: DataFrame): initialize_default_players() for entry in data.to_dict("records"): args = entry | {"status": False} db.session.add(Player(**args)) # type: ignore db.session.commit() def get(self, id: int) -> Player: player = db.session.get(Player, id) if player is None: raise ValueError(f"Player not found: {id}") return player def toggle(self, id: int) -> Player: player = self.get(id) player.status = not player.status db.session.commit() return player def regulars(self) -> list[Player]: filter_list = list(app.config["GUESTS"].keys()) + list( app.config["DEFAULTS"].keys() ) return ( db.session.query(Player) .where(Player.id > 1) .where(Player.first_name.not_in(filter_list)) .order_by(Player.first_name, Player.last_name) .all() ) def guests(self) -> list[Player]: return ( db.session.query(Player) .where(Player.id > 1) .where(Player.first_name.in_(app.config["GUESTS"].keys())) .order_by(Player.first_name, Player.last_name) .all() ) def active(self) -> Sequence[Player]: query = ( select(Player) .where(Player.status) .order_by(Player.first_name, Player.last_name) ) return db.session.scalars(query).all() def reset(self): for player in db.session.scalars(select(Player)): player.status = False db.session.commit() class Session: levels: int = 10 teams: bool = False proposal: list[DisplayGame] = [] def _today(self): games = ( db.session.query(Game).filter(func.date(Game.date) == date.today()).all() ) return games def __init__(self): with app.app_context(): db.create_all() self.courts = Courts() self.players = Players() if len(self._today()) == 0: query = select(Player).where(Player.first_name == "---") player = db.session.scalars(query).first() if player is None: raise ValueError("Database isn't initialized.") player_data = {f"player_{i + 1}": player.id for i in range(4)} for court in self.courts.all(): args = {"round_id": 0, "court_id": court.id} | player_data db.session.add(Game(**args)) db.session.commit() def round(self) -> int: games = self._today() round_id = max([game.round_id for game in games]) if round_id is None: raise ValueError("Database isn't initialized") return round_id def current_round(self) -> list[DisplayGame]: games = ( db.session.query(Game) .filter(func.date(Game.date) == date.today()) .filter(Game.round_id == self.round()) .order_by(Game.court_id) .all() ) games = [DisplayGame(game) for game in games] return games def propose(self) -> list[DisplayGame]: reserved_courts = self.courts.inactive() reserved_games = get_placeholder_games(reserved_courts, "RESERVED") active_courts = self.courts.active() active_players = self.players.active() game_count = floor(len(active_players) / app.config["PLAYERS_PER_COURT"]) game_count = min(len(active_courts), game_count) used_courts = active_courts[:game_count] unused_courts = active_courts[game_count:] blank_games = get_placeholder_games(unused_courts, "---") active_teams = {} if self.teams: active_teams, active_players = separate_teams_and_players(active_players) team_courts = used_courts[: len(active_teams)] shuffle_courts = used_courts[len(active_teams) :] team_games = get_team_games(team_courts, active_teams) shuffle_games = get_shuffle_games(shuffle_courts, active_players, self.levels) self.proposal = shuffle_games + team_games + blank_games + reserved_games self.proposal.sort(key=attrgetter("court.id")) return self.proposal def confirm(self) -> list[DisplayGame]: if len(self.proposal) == 0: return self.current_round() round_id = self.round() + 1 for game in self.proposal: db.session.add(game.to_game(round_id)) db.session.commit() self.proposal = [] return self.current_round() def reset(self) -> list[DisplayGame]: db.session.query(Game).filter(func.date(Game.date) == date.today()).delete() db.session.commit() self.__init__() return self.current_round()