aboutsummaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorKaran Jayachandra <mail@karanjayachandra.com>2025-07-14 09:23:32 +0200
committerKaran Jayachandra <mail@karanjayachandra.com>2025-07-14 09:23:32 +0200
commitec3af3f76ae1785bfb8c1dfbc69788e95fc7954d (patch)
treed21468b10051184a5cb918d618e6b720539a3312 /app
parentd5d64f2b1047f864811d23f19f109a6513c22a2a (diff)
Working app, guests implementation pending
Diffstat (limited to 'app')
-rw-r--r--app/config.py4
-rw-r--r--app/controller.py24
-rw-r--r--app/forms.py3
-rw-r--r--app/model.py44
-rw-r--r--app/routes.py29
-rw-r--r--app/utilities.py14
6 files changed, 84 insertions, 34 deletions
diff --git a/app/config.py b/app/config.py
index a6a60be..fa16e1f 100644
--- a/app/config.py
+++ b/app/config.py
@@ -5,9 +5,9 @@ from os.path import abspath, dirname, join
class Config:
SECRET_KEY = environ.get("SECRET_KEY") or None
DATABASE_CSV = environ.get("DATABASE_CSV") or None
- COURT_COUNT = environ.get("COURT_COUNT") or None
SQLALCHEMY_DATABASE_URI = "sqlite:///" + join(abspath(dirname(__file__)), "app.db")
+ COURT_COUNT = 12
PLAYERS_PER_COURT = 4
GUEST_LEVELS = [1, 3, 5]
MIN_PLAYER_LEVEL = 1
- MAX_PLAYER_LEVEL = 10 \ No newline at end of file
+ MAX_PLAYER_LEVEL = 10
diff --git a/app/controller.py b/app/controller.py
index 5bdeba7..d8a178f 100644
--- a/app/controller.py
+++ b/app/controller.py
@@ -2,7 +2,7 @@ from math import floor
from app import app, db
from datetime import date
from operator import attrgetter
-from sqlalchemy import select, func, Date
+from sqlalchemy import func, Date
from app.model import (
Player,
Players,
@@ -29,6 +29,7 @@ class Session:
def __init__(self):
with app.app_context():
+ games = db.session.query(Game)
db.session.query(Game).delete()
id = self.players.default_id()
player_data = {f"player_{i+1}": id for i in range(4)}
@@ -38,9 +39,10 @@ class Session:
db.session.commit()
def round(self) -> int:
- games = db.session.query(Game).filter(func.date(Game.date) == date.today()).all()
+ games = (
+ db.session.query(Game).filter(func.date(Game.date) == date.today()).all()
+ )
round_id = max([game.round_id for game in games])
- print(round_id)
if round_id is None:
raise ValueError(f"Database isn't initialized")
return round_id
@@ -60,7 +62,7 @@ class Session:
p = db.session.get(Player, getattr(game, f"player_{i+1}"))
if p is None:
raise ValueError(f"Player id mismatch.")
- dp.append(p)
+ dp.append(p)
t1 = DisplayTeam(DisplayPlayer(dp[0]), DisplayPlayer(dp[1]))
t2 = DisplayTeam(DisplayPlayer(dp[2]), DisplayPlayer(dp[3]))
db.session.get(Player, game.player_2)
@@ -72,8 +74,6 @@ class Session:
reserved_games = get_placeholder_games(reserved_courts, "RESERVED")
active_courts = self.courts.active()
active_players = self.players.active()
- for player in active_players:
- print(player)
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]
@@ -93,8 +93,18 @@ class Session:
def confirm(self) -> list[DisplayGame]:
if len(self.proposal) == 0:
return self.all()
+ round_number = self.round() + 1
for game in self.proposal:
- db.session.add(game)
+ db.session.add(
+ Game(
+ round_number,
+ game.court_id,
+ game.team_1.player_1.id,
+ game.team_1.player_2.id,
+ game.team_2.player_1.id,
+ game.team_2.player_2.id,
+ )
+ )
db.session.commit()
self.proposal = []
return self.all()
diff --git a/app/forms.py b/app/forms.py
index 504824a..2e0929a 100644
--- a/app/forms.py
+++ b/app/forms.py
@@ -2,8 +2,9 @@ from flask_wtf import FlaskForm
from wtforms.validators import DataRequired
from wtforms import StringField, PasswordField, BooleanField, SubmitField
+
class LoginForm(FlaskForm):
username = StringField("Username", validators=[DataRequired()])
password = PasswordField("Password", validators=[DataRequired()])
remember_me = BooleanField("Remember Me")
- submit = SubmitField("Sign In") \ No newline at end of file
+ submit = SubmitField("Sign In")
diff --git a/app/model.py b/app/model.py
index 56921f4..2f8c609 100644
--- a/app/model.py
+++ b/app/model.py
@@ -74,7 +74,15 @@ class Player(db.Model):
status: Mapped[bool] = mc(Boolean)
level: Mapped[int] = mc(Integer)
- def __init__(self, id, first_name, last_name, team = 0, status = False, level = int(app.config["MIN_PLAYER_LEVEL"])):
+ def __init__(
+ self,
+ id,
+ first_name,
+ last_name,
+ team=0,
+ status=False,
+ level=int(app.config["MIN_PLAYER_LEVEL"]),
+ ):
self.id = id
self.first_name = first_name
self.last_name = last_name
@@ -124,11 +132,15 @@ class Players:
if app.config["DATABASE_CSV"] is not None:
data = read_csv(app.config["DATABASE_CSV"])
db.session.query(Player).delete()
- db.session.add(Player(**{"id": 0, "first_name": "---", "last_name": ""}))
- db.session.add(Player(**{"id": 1, "first_name": "RESERVED", "last_name": ""}))
+ db.session.add(
+ Player(**{"id": 0, "first_name": "---", "last_name": ""})
+ )
+ db.session.add(
+ Player(**{"id": 1, "first_name": "RESERVED", "last_name": ""})
+ )
for entry in data.to_dict("records"):
args = entry | {"status": False}
- db.session.add(Player(**args))
+ db.session.add(Player(**args)) # type: ignore
db.session.commit()
else:
if len(self.all()) == 0:
@@ -163,12 +175,13 @@ class Players:
return player
def all(self) -> Tuple[list[Player], list[Player]]:
- regulars = [
- p for p in db.session.query(Player).where(Player.first_name != "Guest")
- ]
- guests = [
- p for p in db.session.query(Player).where(Player.first_name == "Guest")
- ]
+ players = (
+ db.session.query(Player)
+ .where(Player.id > 1)
+ .order_by(Player.first_name, Player.last_name)
+ )
+ regulars = [p for p in players if p.first_name != "Guest"]
+ guests = [p for p in players if p.first_name == "Guest"]
guests.sort(key=lambda x: int(x.last_name))
return regulars, guests
@@ -196,7 +209,15 @@ class Game(db.Model):
player_4: Mapped[int] = mc(ForeignKey(Player.id), index=True)
date: Mapped[datetime] = mc(index=True, default=lambda: date.today())
- def __init__(self, round_id: int, court_id: int, player_1: int, player_2: int, player_3: int, player_4: int):
+ def __init__(
+ self,
+ round_id: int,
+ court_id: int,
+ player_1: int,
+ player_2: int,
+ player_3: int,
+ player_4: int,
+ ):
self.round_id = round_id
self.court_id = court_id
self.player_1 = player_1
@@ -205,7 +226,6 @@ class Game(db.Model):
self.player_4 = player_4
-
class DisplayPlayer:
def __init__(self, player: Player):
diff --git a/app/routes.py b/app/routes.py
index bc85e48..cffbae9 100644
--- a/app/routes.py
+++ b/app/routes.py
@@ -1,14 +1,25 @@
from app import app
from app.forms import LoginForm
from app.controller import Session
-from flask import render_template, request, url_for, Response, flash, redirect, get_template_attribute, render_template_string
+from flask import (
+ render_template,
+ request,
+ url_for,
+ Response,
+ flash,
+ redirect,
+ get_template_attribute,
+)
+from flask import render_template_string as render_string
g = Session()
+
@app.route("/", methods=["GET"])
def home():
return render_template("index.j2", url_for=url_for)
+
@app.route("/login", methods=["GET", "POST"])
def login():
form = LoginForm()
@@ -17,6 +28,7 @@ def login():
return redirect("/")
return render_template("login.j2", form=form)
+
@app.route("/control", methods=["GET"])
def get_controls():
return render_template("controls.j2", courts=g.courts.all())
@@ -26,9 +38,7 @@ def get_controls():
def get_random_games():
g.levels = int(request.form["levels"].split()[0])
g.teams = request.form["team"] == "Teams"
- return render_template(
- "games.j2", games=g.propose(), title="Proposal"
- )
+ return render_template("games.j2", games=g.propose(), title="Proposal")
@app.route("/confirm", methods=["POST"])
@@ -51,14 +61,15 @@ def toggle_player(player_request):
id = int(player_request)
player = g.players.toggle(id)
button = get_template_attribute("macros.j2", "player_button")
- t = '{% from "macros.j2" import player_button %}'
- return render_template_string("{{player_button(player)}}", player=player, player_button=button)
+ return render_string("{{button(player)}}", player=player, button=button)
@app.route("/players", methods=["GET"])
def get_list_of_players():
regulars, guests = g.players.all()
- return render_template("players.j2", regulars=regulars, guests=guests, url_for=url_for)
+ return render_template(
+ "players.j2", regulars=regulars, guests=guests, url_for=url_for
+ )
@app.route("/reset_players", methods=["POST"])
@@ -73,8 +84,8 @@ def reset_all_players():
def toggle_court(court_number):
id = int(court_number)
status = g.courts.toggle(id)
- t = '{% from "macros.j2" import court_button %}{{ court_button(id, status) }}'
- return render_template(t, id=id, status=status)
+ button = get_template_attribute("macros.j2", "court_button")
+ return render_string("{{button(id, status)}}", id=id, status=status, button=button)
@app.route("/courts", methods=["GET"])
diff --git a/app/utilities.py b/app/utilities.py
index c84e1bf..133f9e1 100644
--- a/app/utilities.py
+++ b/app/utilities.py
@@ -1,7 +1,9 @@
from app import app, db
+from datetime import date
+from sqlalchemy import func
from typing import Tuple, Sequence
from random import shuffle, sample
-from app.model import Player, DisplayPlayer, Court, DisplayGame, DisplayTeam
+from app.model import Player, DisplayPlayer, Court, Game, DisplayGame, DisplayTeam
def _shuffle_two_lists(a: list, b: list) -> Tuple[list, list]:
@@ -26,13 +28,19 @@ def sample_list(indices: list, cost: list[int], count=None) -> list:
def _normalize_skill(level: int, new_max: int) -> int:
- new_level = round((new_max - 1) * (level / app.config["MAX_PLAYER_LEVEL"])) + 1
+ scaling = (new_max - 1) / int(app.config["MAX_PLAYER_LEVEL"])
+ new_level = round(scaling * level) + 1
return new_level
def select_players(players: Sequence[Player], count: int) -> list[Player]:
+ games = db.session.query(Game).filter(func.date(Game.date) == date.today()).all()
+ player_identifiers = []
+ for game in games:
+ for i in range(4):
+ player_identifiers.append(getattr(game, f"player_{i+1}"))
identifiers = [player.id for player in players]
- play_count = [int(player.id) for player in players]
+ play_count = [player_identifiers.count(player.id) 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