aboutsummaryrefslogtreecommitdiff
path: root/src/match_up
diff options
context:
space:
mode:
authorKaran Jayachandra <mail@karanjayachandra.com>2024-11-15 16:54:29 +0100
committerKaran Jayachandra <mail@karanjayachandra.com>2024-11-15 16:54:29 +0100
commit765fe232ac9fded73387d5cb9ba9b0a07f05b4e4 (patch)
tree80f427f52f46bf258c491d45f3bb1e20b4d36c96 /src/match_up
parent6ca047f36fb688cdb72f0170e0fb3aaf3701ff95 (diff)
Added the history and reset functionality
Diffstat (limited to 'src/match_up')
-rw-r--r--src/match_up/__init__.py12
-rw-r--r--src/match_up/controller.py112
-rw-r--r--src/match_up/data.py70
-rw-r--r--src/match_up/model.py202
-rw-r--r--src/match_up/routes.py55
-rw-r--r--src/match_up/templates/controls.j25
-rw-r--r--src/match_up/utilities.py136
7 files changed, 307 insertions, 285 deletions
diff --git a/src/match_up/__init__.py b/src/match_up/__init__.py
index e6c472a..bccd0e7 100644
--- a/src/match_up/__init__.py
+++ b/src/match_up/__init__.py
@@ -3,17 +3,16 @@ from waitress import serve
from argparse import ArgumentParser
from importlib.resources import path
from match_up.controller import RoundGenerator
-from match_up.model import CourtList, PlayerList
with path("match_up", "static") as p:
static_folder = p
with path("match_up", "templates") as p:
template_folder = p
-app = Flask(
+_app = Flask(
"Match Up! Backend", static_folder=static_folder, template_folder=template_folder
)
-rg = RoundGenerator()
+_rg = RoundGenerator()
from match_up import routes
@@ -21,10 +20,7 @@ from match_up import routes
def main():
parser = ArgumentParser()
- parser.add_argument("-c", "--courts", help="Total courts", type=int, default=12)
parser.add_argument("-d", "--database", help="CSV database", default=None)
args = parser.parse_args()
- global rg
- rg.courts = CourtList(total=args.courts)
- rg.players = PlayerList(csv_location=args.database)
- serve(app, port=80)
+ _rg.update_player_list(csv_location=args.database)
+ serve(_app, port=80)
diff --git a/src/match_up/controller.py b/src/match_up/controller.py
index aa72b12..8e89ffd 100644
--- a/src/match_up/controller.py
+++ b/src/match_up/controller.py
@@ -1,78 +1,18 @@
from typing import Tuple
from operator import attrgetter
-from dataclasses import dataclass, field
-from match_up.utilities import sample_list
-from match_up.model import (
- Team,
- Game,
- Player,
- DisplayPlayer,
- PlayerList,
- CourtList,
- MAX_LEVEL,
- PLAYER_PER_COURT,
-)
+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
-def _create_placeholders(courts: dict, name: str) -> list[Game]:
- dummy_player = Player(first=name)
- dummy_team = Team(
- player1=DisplayPlayer(dummy_player), player2=DisplayPlayer(dummy_player)
- )
- games = [Game(court, dummy_team, dummy_team) for court in courts]
- return games
-
-
-def _normalize_skill(level: int, new_max: int) -> int:
- new_level = round((new_max - 1) * (level / MAX_LEVEL)) + 1
- return new_level
-
-
-def _select_players(players: list[Player], count: int) -> dict:
- identifiers = [player.id for player in players]
- play_count = [player.count for player in players]
- ids = sample_list(identifiers, play_count, count)
- selected_players = [player for player in players if player.id in ids]
- return selected_players
-
-
-def _create_groups_of_four_players(
- players: list[Player], new_max: int
-) -> list[list[Player]]:
- identifiers = [player.id for player in players]
- player_levels = [_normalize_skill(player.level, new_max) for player in players]
- identifiers = sample_list(identifiers, player_levels)
- court_players = []
- for i in range(0, len(players), PLAYER_PER_COURT):
- court_ids = identifiers[i : i + PLAYER_PER_COURT]
- selected_players = [player for player in players if player.id in court_ids]
- court_players.append(selected_players)
- return court_players
-
-
-def _create_games(courts: dict, players: list[Player], levels: int):
- court_players = _create_groups_of_four_players(players, levels)
- games = []
- for court, players in zip(courts, court_players):
- 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
-
-
-@dataclass
class RoundGenerator:
- courts: CourtList = field(default_factory=lambda: CourtList())
- players: PlayerList = field(default_factory=lambda: PlayerList())
- round: int = 0
- proposal: tuple = tuple()
+ proposed_games: list[Game] = []
+ proposed_players: list[Player] = []
- def __post_init__(self) -> None:
- self.games = _create_placeholders(self.courts.get_courts(), "---")
+ def __init__(self) -> None:
+ self.courts = CourtList()
+ self.players = PlayerList()
+ self.reset()
def _separate_courts(self, courts: dict) -> Tuple[dict, dict]:
possible_games = min(len(courts), self.players.get_possible_game_count())
@@ -89,7 +29,9 @@ class RoundGenerator:
games = reserved_games + blank_games
return games
- def _create_active_games(self, total_levels: int):
+ def _create_active_games(
+ self, total_levels: int
+ ) -> Tuple[list[Game], list[Player]]:
active_courts, _ = self.courts.separate_courts()
used_courts, _ = self._separate_courts(active_courts)
required_player_count = len(used_courts) * PLAYER_PER_COURT
@@ -98,28 +40,38 @@ class RoundGenerator:
games = _create_games(used_courts, selected_players, total_levels)
return games, selected_players
- def _generate_proposal(self, total_levels: int) -> Tuple[list[Game], dict]:
+ 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"))
return games, selected_players
+ def update_player_list(self, csv_location):
+ self.players = PlayerList(csv_location)
+
def clear(self) -> list[Game]:
- self.proposal = tuple()
+ self.proposed_games = []
+ self.proposed_players = []
return self.games
def propose(self, levels: int) -> list[Game]:
- self.proposal = self._generate_proposal(levels)
- return self.proposal[0]
+ proposal = self._generate_proposal(levels)
+ self.proposed_games = proposal[0]
+ self.proposed_players = proposal[1]
+ return self.proposed_games
def confirm(self) -> list[Game]:
- proposed_games, proposed_players = self.proposal
- if self.games == proposed_games or len(proposed_games) == 0:
+ if len(self.proposed_games) == 0:
return self.games
- self.games = proposed_games
- identifiers = [player.id for player in proposed_players]
+ self.games = self.proposed_games
+ identifiers = [int(player.id) for player in self.proposed_players]
self.players.increment_game_count(identifiers)
- self.proposal = tuple()
self.round += 1
- return self.games
+ return self.clear()
+
+ def reset(self):
+ self.round = 0
+ self.games = _create_placeholders(self.courts.get_courts(), "---")
+ self.players.reset_game_count()
+ return self.games
diff --git a/src/match_up/data.py b/src/match_up/data.py
new file mode 100644
index 0000000..62aa5d8
--- /dev/null
+++ b/src/match_up/data.py
@@ -0,0 +1,70 @@
+from dataclasses import dataclass
+from sqlalchemy import Column, String, Integer
+from sqlalchemy.ext.declarative import declarative_base
+
+
+MIN_LEVEL = 1
+MAX_LEVEL = 10
+PLAYER_PER_COURT = 4
+
+BASE = declarative_base()
+
+
+class Player(BASE):
+ __tablename__ = "players"
+ id = Column("id", Integer, primary_key=True)
+ first = Column("first", String)
+ last = Column("last", String)
+ level = Column("level", Integer)
+ status = Column("status", Integer)
+ count = Column("count", Integer)
+
+ def __init__(
+ self,
+ id: int,
+ first: str = "",
+ last: str = "",
+ level: int = 0,
+ status: bool = True,
+ count: int = 0,
+ ):
+ self.id = id
+ self.first = first
+ self.last = last
+ self.status = status
+ self.level = level
+ self.count = count
+
+ def __repr__(self):
+ status = "active" if self.status else "inactive"
+ return f"{self.first} {self.last} (Level {self.level}) is {status} and has played {self.count} games"
+
+
+class DisplayPlayer:
+ id: int
+ first: str
+ last: str
+ status: bool
+
+ def __init__(self, player: Player):
+ self.id = int(player.id)
+ self.first = str(player.first)
+ self.last = str(player.last)
+ self.status = bool(player.status)
+
+ def __repr__(self):
+ status = "active" if self.status else "inactive"
+ return f"{self.first} {self.last} is {status}"
+
+
+@dataclass
+class Team:
+ player1: DisplayPlayer
+ player2: DisplayPlayer
+
+
+@dataclass
+class Game:
+ court: int
+ team1: Team
+ team2: Team
diff --git a/src/match_up/model.py b/src/match_up/model.py
index 9b2dce7..abce73c 100644
--- a/src/match_up/model.py
+++ b/src/match_up/model.py
@@ -3,85 +3,25 @@ from typing import Tuple
from os.path import join, isfile
from dataclasses import dataclass
from importlib.resources import path
-from pandas import read_csv, DataFrame
from sqlalchemy.orm import sessionmaker
-from random import randint, getrandbits
-from names import get_first_name, get_last_name
-from sqlalchemy.ext.declarative import declarative_base
-from sqlalchemy import create_engine, Column, String, Integer, not_
-
-MIN_LEVEL = 1
-MAX_LEVEL = 10
-DEFAULT_COURTS = 12
-PLAYER_PER_COURT = 4
-
-
-Base = declarative_base()
-
-
-class Player(Base):
- __tablename__ = "players"
- id = Column("id", Integer, primary_key=True)
- first = Column("first", String)
- last = Column("last", String)
- level = Column("level", Integer)
- status = Column("status", Integer)
- count = Column("count", Integer)
-
- def __init__(
- self,
- id: int = None,
- first: str = "",
- last: str = "",
- level: int = None,
- status: bool = None,
- count: int = None,
- ):
- self.id = id
- self.first = first
- self.last = last
- self.status = status
- self.level = level
- self.count = count
-
- def __repr__(self):
- status = "active" if self.status else "inactive"
- return f"{self.first} {self.last} (Level {self.level}) is {status} and has played {self.count} games"
-
-
-class DisplayPlayer:
- id: int
- first: str
- last: str
- status: bool
-
- def __init__(self, player: Player):
- self.id = player.id
- self.first = player.first
- self.last = player.last
- self.status = player.status
-
- def __repr__(self):
- status = "active" if self.status else "inactive"
- return f"{self.first} {self.last} is {status}"
-
-
-@dataclass
-class Team:
- player1: DisplayPlayer
- player2: DisplayPlayer
-
-
-@dataclass
-class Game:
- court: int
- team1: Team
- team2: Team
+from sqlalchemy import create_engine, not_
+from match_up.utilities import (
+ generate_guests,
+ _init_database_from_csv,
+ _init_random_database,
+ _textualize_guest,
+)
+from match_up.data import (
+ Player,
+ DisplayPlayer,
+ PLAYER_PER_COURT,
+ BASE,
+)
@dataclass
class CourtList:
- total: int = DEFAULT_COURTS
+ total: int = 12
def __post_init__(self) -> None:
self.courts = {i + 1: True for i in range(self.total)}
@@ -89,7 +29,7 @@ class CourtList:
def get_court_status(self, court_number: int) -> bool:
return self.courts[court_number]
- def toggle_court_status(self, court_number: int) -> None:
+ def toggle_court_status(self, court_number: int) -> bool:
new_status = not self.courts[court_number]
self.courts[court_number] = new_status
return new_status
@@ -103,46 +43,36 @@ class CourtList:
return active_courts, inactive_courts
-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}"
-
-
class PlayerList:
- guests: dict = {1: 0, 3: 0, 5: 0}
+ guest_count: dict = {1: 0, 3: 0, 5: 0}
- def __init__(self, csv_location: str = None) -> None:
+ def __init__(self, csv_location=None) -> None:
+ with path("match_up") as p:
+ self.db_location = join(p, "players.db")
+ db_file_found = True
+ if not isfile(self.db_location):
+ db_file_found = False
self._init_database()
if csv_location is not None:
- self._init_database_from_csv(read_csv(csv_location))
+ _init_database_from_csv(self.session, csv_location)
else:
- if not isfile(self.db_location):
- self._init_random_database()
+ if not db_file_found:
+ _init_random_database(self.session)
def _init_database(self):
- with path("match_up") as p:
- self.db_location = join(p, "players.db")
engine = create_engine("sqlite:///" + self.db_location, echo=True)
- Base.metadata.create_all(bind=engine)
+ BASE.metadata.create_all(bind=engine)
self.session = sessionmaker(bind=engine)()
def increment_guests(self, level: int):
- self.guests[level] = self.guests[level] + 1
- return _textualize_guest(level, self.guests[level])
+ self.guest_count[level] = self.guest_count[level] + 1
+ return _textualize_guest(level, self.guest_count[level])
def decrement_guests(self, level: int):
- if self.guests[level] == 0:
+ if self.guest_count[level] == 0:
return 0
- self.guests[level] = self.guests[level] - 1
- return _textualize_guest(level, self.guests[level])
+ self.guest_count[level] = self.guest_count[level] - 1
+ return _textualize_guest(level, self.guest_count[level])
def get_player_status(self, id: int) -> bool:
return self.session.query(Player).filter(Player.id == id).first().status
@@ -153,7 +83,7 @@ class PlayerList:
self.session.commit()
return DisplayPlayer(player)
- def get_all_players(self) -> list[Player]:
+ def get_all_players(self) -> list[DisplayPlayer]:
all_players = [
DisplayPlayer(p)
for p in self.session.query(Player)
@@ -167,51 +97,20 @@ class PlayerList:
possible_games = floor(total_players / PLAYER_PER_COURT)
return possible_games
- def get_players(self, rounds: int = 0) -> list[Player]:
+ def get_players(self) -> list[Player]:
active_players = (
self.session.query(Player)
.order_by(Player.first, Player.last)
.filter(not_(Player.status == 0))
.all()
)
- low_guests = [
- Player(
- id=1000 + i,
- first=f"Beginner",
- last=f"{i}",
- level=1,
- status=True,
- count=rounds,
- )
- for i in range(1, self.guests[1] + 1)
- ]
- mid_guests = [
- Player(
- id=2000 + i,
- first=f"Novice",
- last=f"{i}",
- level=4,
- status=True,
- count=rounds,
- )
- for i in range(1, self.guests[3] + 1)
- ]
- high_guests = [
- Player(
- id=3000 + i,
- first=f"Intermediate",
- last=f"{i}",
- level=7,
- status=True,
- count=rounds,
- )
- for i in range(1, self.guests[5] + 1)
- ]
- return active_players + low_guests + mid_guests + high_guests
+ guests = generate_guests(self.guest_count)
+ return active_players + guests
def get_guests(self):
return {
- level: _textualize_guest(level, self.guests[level]) for level in self.guests
+ level: _textualize_guest(level, self.guest_count[level])
+ for level in self.guest_count
}
def increment_game_count(self, player_ids: list[int]):
@@ -231,30 +130,3 @@ class PlayerList:
for player in players:
player.status = 0
self.session.commit()
-
- def _init_database_from_csv(self, data: DataFrame) -> None:
- self.session.query(Player).delete()
- for _, entry in data.iterrows():
- player = Player(
- id=entry["id"],
- first=entry["first"],
- last=entry["last"],
- level=entry["skill"],
- status=False,
- count=0,
- )
- self.session.add(player)
- self.session.commit()
-
- def _init_random_database(self, player_count: int = 80) -> None:
- for i in range(player_count):
- player = Player(
- id=i,
- first=get_first_name(),
- last=get_last_name(),
- level=randint(MIN_LEVEL, MAX_LEVEL),
- status=getrandbits(1),
- count=0,
- )
- self.session.add(player)
- self.session.commit()
diff --git a/src/match_up/routes.py b/src/match_up/routes.py
index 14b36d2..c0e77aa 100644
--- a/src/match_up/routes.py
+++ b/src/match_up/routes.py
@@ -1,94 +1,99 @@
-from match_up import app, rg
+from match_up import _app, _rg
from jinja2 import Environment, PackageLoader
from flask import render_template, request, url_for, Response
re = Environment(loader=PackageLoader("match_up"))
-@app.route("/", methods=["GET"])
+@_app.route("/", methods=["GET"])
def home():
return re.get_template("index.j2").render(url_for=url_for)
-@app.route("/control", methods=["GET"])
+@_app.route("/control", methods=["GET"])
def get_controls():
- return render_template("controls.j2", courts=rg.courts.get_courts())
+ return render_template("controls.j2", courts=_rg.courts.get_courts())
-@app.route("/increment_guest", methods=["POST"])
+@_app.route("/increment_guest", methods=["POST"])
def increment_guests():
level = int(request.form["level"].split()[0])
t = re.from_string(
'{% from "macros.j2" import guest_control %}{{guest_control(level, value)}}'
)
- value = rg.players.increment_guests(level)
+ value = _rg.players.increment_guests(level)
return render_template(t, level=level, value=value)
-@app.route("/decrement_guest", methods=["POST"])
+@_app.route("/decrement_guest", methods=["POST"])
def decrement_guests():
print(request.form["level"])
level = int(request.form["level"])
t = re.from_string(
'{% from "macros.j2" import guest_control %}{{guest_control(level, value)}}'
)
- value = rg.players.decrement_guests(level)
+ value = _rg.players.decrement_guests(level)
return render_template(t, level=level, value=value)
-@app.route("/propose", methods=["POST"])
+@_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")
+ return render_template("games.j2", games=_rg.propose(levels), title="Proposal")
-@app.route("/confirm", methods=["POST"])
+@_app.route("/confirm", methods=["POST"])
def confirm_games():
- return render_template("games.j2", games=rg.confirm(), title=f"Round: {rg.round}")
+ return render_template("games.j2", games=_rg.confirm(), title=f"Round: {_rg.round}")
-@app.route("/clear", methods=["POST"])
+@_app.route("/clear", methods=["POST"])
def clear_games():
- return render_template("games.j2", games=rg.clear(), title=f"Round: {rg.round}")
+ return render_template("games.j2", games=_rg.clear(), title=f"Round: {_rg.round}")
-@app.route("/player-toggle/<player_request>", methods=["POST"])
+@_app.route("/reset", methods=["POST"])
+def reset_games():
+ return render_template("games.j2", games=_rg.reset(), title=f"Round: {_rg.round}")
+
+
+@_app.route("/player-toggle/<player_request>", methods=["POST"])
def toggle_player(player_request):
id = int(player_request)
- player = rg.players.toggle_player_status(id)
+ player = _rg.players.toggle_player_status(id)
t = re.from_string(
'{% from "macros.j2" import player_button %}{{player_button(player)}}'
)
return render_template(t, player=player)
-@app.route("/players", methods=["GET"])
+@_app.route("/players", methods=["GET"])
def get_list_of_players():
- all_players = rg.players.get_all_players()
- guests = rg.players.get_guests()
+ all_players = _rg.players.get_all_players()
+ guests = _rg.players.get_guests()
return re.get_template("players.j2").render(
players=all_players, guests=guests, url_for=url_for
)
-@app.route("/reset_players", methods=["POST"])
+@_app.route("/reset_players", methods=["POST"])
def reset_all_players():
- rg.players.reset_all_players()
+ _rg.players.reset_all_players()
response = Response("Players reset!")
response.headers["HX-Refresh"] = "true"
return response
-@app.route("/court-toggle/<court_number>", methods=["POST"])
+@_app.route("/court-toggle/<court_number>", methods=["POST"])
def toggle_court(court_number):
id = int(court_number)
- status = rg.courts.toggle_court_status(id)
+ status = _rg.courts.toggle_court_status(id)
t = re.from_string(
'{% from "macros.j2" import court_button %}{{ court_button(id, status) }}'
)
return render_template(t, id=id, status=status)
-@app.route("/courts", methods=["GET"])
+@_app.route("/courts", methods=["GET"])
def get_list_of_courts():
- return render_template("courts.j2", courts=rg.courts.get_courts())
+ return render_template("courts.j2", courts=_rg.courts.get_courts())
diff --git a/src/match_up/templates/controls.j2 b/src/match_up/templates/controls.j2
index e9e37f3..57a605e 100644
--- a/src/match_up/templates/controls.j2
+++ b/src/match_up/templates/controls.j2
@@ -31,4 +31,9 @@
hx-post="/confirm"
hx-target="#games">Start Round</button>
</div>
+ <div class="column">
+ <button class="button is-dark is-responsive"
+ hx-post="/reset"
+ hx-target="#games">Reset!</button>
+ </div>
</div> \ No newline at end of file
diff --git a/src/match_up/utilities.py b/src/match_up/utilities.py
index 56fb78e..db7c76a 100644
--- a/src/match_up/utilities.py
+++ b/src/match_up/utilities.py
@@ -1,10 +1,17 @@
-from time import time
from typing import Tuple
-from random import shuffle
-
-
-SECS_IN_MIN = 60
-DEFAULT_TIMER = 12.0
+from pandas import read_csv
+from random import shuffle, choice
+from random import randint, getrandbits
+from names import get_first_name, get_last_name
+from match_up.data import (
+ Player,
+ DisplayPlayer,
+ Team,
+ Game,
+ MIN_LEVEL,
+ MAX_LEVEL,
+ PLAYER_PER_COURT,
+)
def _shuffle_two_lists(a: list, b: list) -> Tuple[list, list]:
@@ -20,9 +27,124 @@ def _pick_from_list_after_sorting_other(a: list, b: list[int]) -> list:
return [a[index] for index in indices]
-def sample_list(indices: list, cost: list[int], count: int = None) -> list:
+def sample_list(indices: list, cost: list[int], count=None) -> list:
if count is None:
count = len(indices)
indices, cost = _shuffle_two_lists(indices, cost)
indices = _pick_from_list_after_sorting_other(indices, cost)[:count]
return indices
+
+
+def _create_placeholders(courts: dict, name: str) -> list[Game]:
+ dummy_player = Player(id=-1, first=name)
+ dummy_team = Team(
+ player1=DisplayPlayer(dummy_player), player2=DisplayPlayer(dummy_player)
+ )
+ games = [Game(court, dummy_team, dummy_team) for court in courts]
+ return games
+
+
+def _normalize_skill(level: int, new_max: int) -> int:
+ new_level = round((new_max - 1) * (level / MAX_LEVEL)) + 1
+ return new_level
+
+
+def _select_players(players: list[Player], count: int) -> list[Player]:
+ identifiers = [player.id for player in players]
+ play_count = [int(player.count) for player in players]
+ ids = sample_list(identifiers, play_count, count)
+ selected_players = [player for player in players if player.id in ids]
+ return selected_players
+
+
+def _create_groups_of_four_players(
+ players: list[Player], new_max: int
+) -> list[list[Player]]:
+ identifiers = [player.id for player in players]
+ player_levels = [_normalize_skill(int(player.level), new_max) for player in players]
+ identifiers = sample_list(identifiers, player_levels)
+ court_players = []
+ for i in range(0, len(players), PLAYER_PER_COURT):
+ court_ids = identifiers[i : i + PLAYER_PER_COURT]
+ selected_players = [player for player in players if player.id in court_ids]
+ court_players.append(selected_players)
+ return court_players
+
+
+def _create_games(courts: dict, players: list[Player], levels: int) -> list[Game]:
+ court_players = _create_groups_of_four_players(players, levels)
+ games = []
+ for court, players in zip(courts, court_players):
+ 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
+
+
+def generate_guest_type(count: int, level: int, description: str):
+ base_id = level * 10000
+ return [
+ Player(
+ id=base_id + i,
+ first=description,
+ last=f"{i + 1}",
+ level=level,
+ status=True,
+ count=choice([0, 1000]),
+ )
+ for i in range(count)
+ ]
+
+
+def generate_guests(guests):
+ beginner_guests = generate_guest_type(guests[1], 1, "Beginner")
+ novice_guests = generate_guest_type(guests[3], 3, "Novice")
+ intermediate_guests = generate_guest_type(guests[5], 3, "Intermediate")
+ return beginner_guests + novice_guests + intermediate_guests
+
+
+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 _init_database_from_csv(session, csv_location: str) -> None:
+ data = read_csv(csv_location)
+ session.query(Player).delete()
+ for _, entry in data.iterrows():
+ player = Player(
+ id=entry["id"],
+ first=entry["first"],
+ last=entry["last"],
+ level=entry["skill"],
+ status=False,
+ count=0,
+ )
+ session.add(player)
+ session.commit()
+
+
+def _init_random_database(session, player_count: int = 80) -> None:
+ for i in range(player_count):
+ player = Player(
+ id=i,
+ first=get_first_name(),
+ last=get_last_name(),
+ level=randint(MIN_LEVEL, MAX_LEVEL),
+ status=bool(getrandbits(1)),
+ count=0,
+ )
+ session.add(player)
+ session.commit()