aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaran Jayachandra <mail@karanjayachandra.com>2024-12-02 19:33:48 +0100
committerKaran Jayachandra <mail@karanjayachandra.com>2024-12-02 19:33:48 +0100
commitbeea33bc183837a3feceaf1b5127c2e21e16fc36 (patch)
treeed0d9b5f4e3ad600f8d498227323576800983608
parent6ebbee2016b0232573ad1d322b4d4ca6287b3599 (diff)
Added team based games
-rw-r--r--src/match_up/controller.py58
-rw-r--r--src/match_up/data.py7
-rw-r--r--src/match_up/model.py7
-rw-r--r--src/match_up/routes.py5
-rw-r--r--src/match_up/templates/controls.j212
-rw-r--r--src/match_up/utilities.py43
6 files changed, 86 insertions, 46 deletions
diff --git a/src/match_up/controller.py b/src/match_up/controller.py
index 0e08cae..585160f 100644
--- a/src/match_up/controller.py
+++ b/src/match_up/controller.py
@@ -2,7 +2,7 @@ from typing import Tuple
from operator import attrgetter
from match_up.data import Game, PLAYER_PER_COURT
from match_up.model import Player, PlayerList, CourtList
-from match_up.utilities import _create_placeholders, _select_players, _create_games
+from match_up.utilities import _create_placeholders, _select_players, _create_games, _create_team_games
class RoundGenerator:
@@ -30,21 +30,44 @@ class RoundGenerator:
return games
def _create_active_games(
- self, total_levels: int
+ self, total_levels: int, group_by_team: bool
) -> Tuple[list[Game], list[Player]]:
- active_courts, _ = self.courts.separate_courts()
- used_courts, _ = self._separate_courts(active_courts)
+ active_courts = self.courts.separate_courts()[0]
+ used_courts = self._separate_courts(active_courts)[0]
required_player_count = len(used_courts) * PLAYER_PER_COURT
active_players = self.players.get_players()
- selected_players = _select_players(active_players, required_player_count)
- games = _create_games(used_courts, selected_players, total_levels)
- return games, selected_players
+ if group_by_team:
+ unique_team_id = {player.team for player in active_players}
+ grouped_players_by_team = {team_id: [] for team_id in unique_team_id}
+ for player in active_players:
+ team = player.team
+ grouped_players_by_team[team].append(player)
+ active_teams = {}
+ players_without_team = []
+ for team_id in grouped_players_by_team:
+ if team_id == 0:
+ players_without_team += [player for player in grouped_players_by_team[team_id]]
+ else:
+ if len(grouped_players_by_team[team_id]) >= 4:
+ active_teams[team_id] = grouped_players_by_team[team_id]
+ else:
+ players_without_team += [player for player in grouped_players_by_team[team_id]]
+
+ regular_courts = len(used_courts) - len(active_teams)
+ reg_courts = {A:N for (A,N) in [x for x in used_courts.items()][:regular_courts]}
+ team_courts = {A:N for (A,N) in [x for x in used_courts.items()][regular_courts:]}
- def _generate_proposal(self, total_levels: int) -> Tuple[list[Game], list[Player]]:
- inactive_games = self._create_inactive_games()
- active_games, selected_players = self._create_active_games(total_levels)
- games = active_games + inactive_games
- games.sort(key=attrgetter("court"))
+ required_player_count = required_player_count - (4 * len(active_teams))
+ selected_players_without_team = _select_players(players_without_team, required_player_count)
+ regular_games = _create_games(reg_courts, selected_players_without_team, total_levels)
+
+ team_games, selected_players_with_team = _create_team_games(team_courts, active_teams)
+
+ selected_players = selected_players_without_team + selected_players_with_team
+ games = regular_games + team_games
+ else:
+ selected_players = _select_players(active_players, required_player_count)
+ games = _create_games(used_courts, selected_players, total_levels)
return games, selected_players
def update_player_list(self, csv_location):
@@ -55,10 +78,13 @@ class RoundGenerator:
self.proposed_players = []
return self.games
- def propose(self, levels: int) -> list[Game]:
- proposal = self._generate_proposal(levels)
- self.proposed_games = proposal[0]
- self.proposed_players = proposal[1]
+ def propose(self, levels: int, group_by_team: bool) -> list[Game]:
+ inactive_games = self._create_inactive_games()
+ active_games, selected_players = self._create_active_games(levels, group_by_team)
+ games = active_games + inactive_games
+ games.sort(key=attrgetter("court"))
+ self.proposed_games = games
+ self.proposed_players = selected_players
return self.proposed_games
def confirm(self) -> list[Game]:
diff --git a/src/match_up/data.py b/src/match_up/data.py
index 636a5eb..d8996fe 100644
--- a/src/match_up/data.py
+++ b/src/match_up/data.py
@@ -18,15 +18,18 @@ class Player(BASE):
first = Column("first", String)
last = Column("last", String)
level = Column("level", Integer)
+ team = Column("team", Integer)
status = Column("status", Integer)
count = Column("count", Integer)
+
def __init__(
self,
id: int,
first: str = "",
last: str = "",
level: int = 0,
+ team: int = 0,
status: bool = True,
count: int = 0,
):
@@ -34,12 +37,14 @@ class Player(BASE):
self.first = first
self.last = last
self.status = status
+ self.team = team
self.level = level
self.count = count
def __repr__(self):
+ team = f" of Team {self.team}" if self.team else ""
status = "active" if self.status else "inactive"
- return f"{self.first} {self.last} (Level {self.level}) is {status} and has played {self.count} games"
+ return f"{self.first} {self.last} (Level {self.level}){team} is {status} and has played {self.count} games"
class DisplayPlayer:
diff --git a/src/match_up/model.py b/src/match_up/model.py
index 4ac8c54..52d72b2 100644
--- a/src/match_up/model.py
+++ b/src/match_up/model.py
@@ -5,13 +5,8 @@ from dataclasses import dataclass
from importlib.resources import path
from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine, not_
+from match_up.data import PLAYER_PER_COURT, BASE, Player, DisplayPlayer
from match_up.utilities import _init_database_from_csv, _init_random_database
-from match_up.data import (
- Player,
- DisplayPlayer,
- PLAYER_PER_COURT,
- BASE,
-)
@dataclass
diff --git a/src/match_up/routes.py b/src/match_up/routes.py
index 8ae18ce..f9cd2fa 100644
--- a/src/match_up/routes.py
+++ b/src/match_up/routes.py
@@ -17,8 +17,9 @@ def get_controls():
@_app.route("/propose", methods=["POST"])
def get_random_games():
- levels = int(request.form["type"].split()[0])
- return render_template("games.j2", games=_rg.propose(levels), title="Proposal")
+ levels = int(request.form["levels"].split()[0])
+ team_practice = request.form["team"] == "Teams"
+ return render_template("games.j2", games=_rg.propose(levels, team_practice), title="Proposal")
@_app.route("/confirm", methods=["POST"])
diff --git a/src/match_up/templates/controls.j2 b/src/match_up/templates/controls.j2
index 95584f7..0183c97 100644
--- a/src/match_up/templates/controls.j2
+++ b/src/match_up/templates/controls.j2
@@ -9,7 +9,7 @@
<div class="columns">
<div class="column">
<div class="select is-responsive">
- <select name="type">
+ <select name="levels">
<option>10 Levels</option>
<option>8 Levels</option>
<option>6 Levels</option>
@@ -20,10 +20,18 @@
</div>
</div>
<div class="column">
+ <div class="select is-responsive">
+ <select name="team">
+ <option>Random</option>
+ <option>Teams</option>
+ </select>
+ </div>
+ </div>
+ <div class="column">
<button class="button is-dark is-responsive"
hx-post="/propose"
hx-target="#games"
- hx-include="[name='type']">Generate</button>
+ hx-include="[name='levels'], [name='team']">Generate</button>
</div>
<div class="column"></div>
<div class="column">
diff --git a/src/match_up/utilities.py b/src/match_up/utilities.py
index 9fc31fa..dd9568c 100644
--- a/src/match_up/utilities.py
+++ b/src/match_up/utilities.py
@@ -1,13 +1,11 @@
+from math import floor
from typing import Tuple
-from pandas import read_csv
from random import shuffle
+from pandas import read_csv
from random import randint, getrandbits
from names import get_first_name, get_last_name
+from match_up.data import Player, DisplayPlayer, Team, Game
from match_up.data import (
- Player,
- DisplayPlayer,
- Team,
- Game,
MIN_LEVEL,
MAX_LEVEL,
PLAYER_PER_COURT,
@@ -86,27 +84,27 @@ def _create_games(courts: dict, players: list[Player], levels: int) -> list[Game
games.append(Game(court, team1, team2))
return games
-
-def _textualize_guest(level, value) -> str:
- match level:
- case 1:
- text = "Beginners"
- case 3:
- text = "Novices"
- case 5:
- text = "Intermediates"
- if value == 1:
- text = text[:-1]
- return f"{value} {text}"
+def _create_team_games(courts: dict, teams: dict) -> tuple[list[Game], list[Player]]:
+ games = []
+ selected_players = []
+ for court, (_, players) in zip(courts, teams.items()):
+ selected_players.append(players[:3])
+ team1 = Team(
+ player1=DisplayPlayer(players[0]), player2=DisplayPlayer(players[3])
+ )
+ team2 = Team(
+ player1=DisplayPlayer(players[1]), player2=DisplayPlayer(players[2])
+ )
+ games.append(Game(court, team1, team2))
+ return games, selected_players
def _init_guests(session) -> None:
for level_id, level in enumerate(GUEST_LEVELS):
for player_id in range(GUESTS_PER_LEVEL):
unique_id = (level_id * GUESTS_PER_LEVEL) + player_id + 1
- id = 1000 + unique_id
player = Player(
- id=id,
+ id=-unique_id,
first="Guest",
last=str(unique_id),
level=level,
@@ -135,12 +133,19 @@ def _init_database_from_csv(session, csv_location: str) -> None:
def _init_random_database(session, player_count: int = 80) -> None:
+ total_teams = 5
+ players_per_team = 5
for i in range(player_count):
+ if i < total_teams * players_per_team:
+ team = floor(i / players_per_team) + 1
+ else:
+ team = 0
player = Player(
id=i,
first=get_first_name(),
last=get_last_name(),
level=randint(MIN_LEVEL, MAX_LEVEL),
+ team=team,
status=bool(getrandbits(1)),
count=0,
)