diff options
| -rw-r--r-- | config.py | 16 | ||||
| -rw-r--r-- | gen_test_data.py | 32 | ||||
| -rw-r--r-- | pyproject.toml | 34 | ||||
| -rw-r--r-- | src/match_up/__init__.py | 31 | ||||
| -rw-r--r-- | src/match_up/controller.py (renamed from controller.py) | 17 | ||||
| -rw-r--r-- | src/match_up/model.py (renamed from model.py) | 41 | ||||
| -rw-r--r-- | src/match_up/routes.py (renamed from app.py) | 20 | ||||
| -rw-r--r-- | src/match_up/static/bulma.min.css (renamed from static/bulma.min.css) | 0 | ||||
| -rw-r--r-- | src/match_up/static/custom.css (renamed from static/custom.css) | 0 | ||||
| -rw-r--r-- | src/match_up/static/favicon.ico (renamed from static/favicon.ico) | bin | 15406 -> 15406 bytes | |||
| -rw-r--r-- | src/match_up/static/htmx.min.js (renamed from static/htmx.min.js) | 0 | ||||
| -rw-r--r-- | src/match_up/static/logo.png (renamed from static/logo.png) | 0 | ||||
| -rw-r--r-- | src/match_up/templates/controls.j2 (renamed from templates/controls.j2) | 0 | ||||
| -rw-r--r-- | src/match_up/templates/games.j2 (renamed from templates/games.j2) | 0 | ||||
| -rw-r--r-- | src/match_up/templates/index.j2 (renamed from templates/index.j2) | 4 | ||||
| -rw-r--r-- | src/match_up/templates/macros.j2 (renamed from templates/macros.j2) | 4 | ||||
| -rw-r--r-- | src/match_up/templates/players.j2 (renamed from templates/players.j2) | 4 | ||||
| -rw-r--r-- | src/match_up/utilities.py (renamed from utilities.py) | 5 |
18 files changed, 124 insertions, 84 deletions
diff --git a/config.py b/config.py deleted file mode 100644 index 7d01933..0000000 --- a/config.py +++ /dev/null @@ -1,16 +0,0 @@ -class Config: - DEBUG = False - DEVELOPMENT = False - CSRF_ENABLED = True - ASSETS_DEBUG = False - - -class ProductionConfig(Config): - pass - - -class DevelopmentConfig(Config): - DEBUG = True - DEVELOPMENT = True - TEMPLATES_AUTO_RELOAD = True - ASSETS_DEBUG = True diff --git a/gen_test_data.py b/gen_test_data.py deleted file mode 100644 index a49594f..0000000 --- a/gen_test_data.py +++ /dev/null @@ -1,32 +0,0 @@ -from csv import writer -from random import randint -from names import get_full_name -from model import Player, MIN_LEVEL, MAX_LEVEL - -PLAYER_COUNT = 80 - - -def _generate_players(player_count: int) -> list[Player]: - players = [] - for _ in range(player_count): - random_name = get_full_name() - random_skill = randint(MIN_LEVEL, MAX_LEVEL) - players.append(Player(name=random_name, skill=random_skill)) - return players - - -def _write_players_to_csv(player_list: list[Player]) -> None: - with open("database_test.csv", "w", newline="") as file: - data_writer = writer(file) - data_writer.writerow(["name", "skill"]) - for player in player_list: - data_writer.writerow([player.name, player.skill]) - - -def main(player_count: int): - players = _generate_players(player_count) - _write_players_to_csv(players) - - -if __name__ == "__main__": - main(PLAYER_COUNT) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..de1a93f --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,34 @@ +[build-system] +requires = ["setuptools>=64", "setuptools_scm>=8"] +build-backend = "setuptools.build_meta" + +[project] +name = "match_up" +dynamic = ["version"] +dependencies = ["flask", "names"] +authors = [ + { name="Karan Jayachandra", email="mail@karanjayachandra.com" }, +] +description = "A small web application for matchmaking" +readme = "README.md" +requires-python = ">=3.10" +classifiers = [ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", +] + +[project.urls] +Homepage = "https://gitlab.com/KaranJayachandra/match_up" +Issues = "https://gitlab.com/KaranJayachandra/match_up/-/issues" + + +[tool.setuptools.packages.find] +where = ["src"] + +[tool.setuptools.package-data] +"templates" = ["*.j2"] +"static" = ["*.css", "*.js", "*.png", "*.ico"] + +[project.scripts] +match_up = "match_up:main"
\ No newline at end of file diff --git a/src/match_up/__init__.py b/src/match_up/__init__.py new file mode 100644 index 0000000..3aa3b98 --- /dev/null +++ b/src/match_up/__init__.py @@ -0,0 +1,31 @@ +from flask import Flask +from argparse import ArgumentParser +from importlib.resources import path +from match_up.utilities import Timer +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( + "Match Up! Backend", static_folder=static_folder, template_folder=template_folder +) +rg = RoundGenerator() + + +from match_up import routes + + +def main(): + parser = ArgumentParser() + parser.add_argument("-t", "--timer", help="Min per round", type=float, default=12) + 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.timer = Timer(max_mins=args.timer) + rg.players = PlayerList(location=args.database) + app.run() diff --git a/controller.py b/src/match_up/controller.py index e6dc827..a21fced 100644 --- a/controller.py +++ b/src/match_up/controller.py @@ -1,8 +1,15 @@ from typing import Tuple from operator import attrgetter -from dataclasses import dataclass -from utilities import sample_list, Timer -from model import Team, Game, PlayerList, CourtList, MAX_LEVEL, PLAYER_PER_COURT +from dataclasses import dataclass, field +from match_up.utilities import sample_list, Timer +from match_up.model import ( + Team, + Game, + PlayerList, + CourtList, + MAX_LEVEL, + PLAYER_PER_COURT, +) def _create_placeholders(courts: dict, name: str) -> list[Game]: @@ -43,8 +50,8 @@ def _create_games(courts: dict, players: list[dict], levels: int): @dataclass class RoundGenerator: - courts: CourtList - players: PlayerList + courts: CourtList = field(default_factory=lambda: CourtList()) + players: PlayerList = field(default_factory=lambda: PlayerList()) round: int = 0 proposal: tuple = tuple() diff --git a/model.py b/src/match_up/model.py index c652ce8..5d6c0f5 100644 --- a/model.py +++ b/src/match_up/model.py @@ -1,11 +1,13 @@ -from os import getenv from math import floor from csv import reader from typing import Tuple +from names import get_full_name from dataclasses import dataclass +from random import randint, getrandbits MIN_LEVEL = 1 MAX_LEVEL = 10 +DEFAULT_COURTS = 12 PLAYER_PER_COURT = 4 @@ -31,7 +33,7 @@ class Game: @dataclass class CourtList: - total: int = int(getenv("COURTS")) + total: int = DEFAULT_COURTS def __post_init__(self) -> None: self.courts = {i + 1: True for i in range(self.total)} @@ -53,20 +55,37 @@ class CourtList: return active_courts, inactive_courts +def _load_csv_players(file_name: str) -> dict: + with open(file_name, newline="") as file: + pointer = reader(file) + next(pointer, None) + players = {id: Player(row[0], int(row[1])) for id, row in enumerate(pointer)} + return players + + +def _generate_random_players(player_count: int = 80) -> list[Player]: + players = { + i: Player( + name=get_full_name(), + skill=randint(MIN_LEVEL, MAX_LEVEL), + status=bool(getrandbits(1)), + ) + for i in range(player_count) + } + return players + + @dataclass class PlayerList: - location: str = getenv("DATABASE") + location: str = None guests: int = 0 def __post_init__(self) -> None: - with open("data/" + self.location, newline="") as file: - pointer = reader(file) - next(pointer, None) - players = { - id: Player(row[0], int(row[1])) for id, row in enumerate(pointer) - } - self.guest_id_start = len(players) + 1 - self.players = players + if self.location is not None: + self.players = _load_csv_players(self.location) + else: + self.players = _generate_random_players() + self.guest_id_start = len(self.players) + 1 def increment_guests(self): self.guests = self.guests + 1 diff --git a/app.py b/src/match_up/routes.py index cb790f3..319b111 100644 --- a/app.py +++ b/src/match_up/routes.py @@ -1,19 +1,13 @@ -from dotenv import load_dotenv -from controller import RoundGenerator -from model import CourtList, PlayerList -from jinja2 import FileSystemLoader, Environment -from flask import Flask, render_template, request +from match_up import app, rg +from jinja2 import Environment, PackageLoader +from flask import render_template, request, url_for - -load_dotenv() -app = Flask("Match Up! Backend") -rg = RoundGenerator(courts=CourtList(), players=PlayerList()) -re = Environment(loader=FileSystemLoader("templates")) +re = Environment(loader=PackageLoader("match_up")) @app.route("/", methods=["GET"]) def home(): - return render_template("index.j2") + return re.get_template("index.j2").render(url_for=url_for) @app.route("/time", methods=["GET"]) @@ -74,7 +68,9 @@ def toggle_player(player_request): @app.route("/players", methods=["GET"]) def get_list_of_players(): - return render_template("players.j2", players=rg.players.get_players()) + return re.get_template("players.j2").render( + players=rg.players.get_players(), url_for=url_for + ) @app.route("/court-toggle/<court_number>", methods=["POST"]) diff --git a/static/bulma.min.css b/src/match_up/static/bulma.min.css index eaa3fb5..eaa3fb5 100644 --- a/static/bulma.min.css +++ b/src/match_up/static/bulma.min.css diff --git a/static/custom.css b/src/match_up/static/custom.css index 11d583f..11d583f 100644 --- a/static/custom.css +++ b/src/match_up/static/custom.css diff --git a/static/favicon.ico b/src/match_up/static/favicon.ico Binary files differindex 64eab0a..64eab0a 100644 --- a/static/favicon.ico +++ b/src/match_up/static/favicon.ico diff --git a/static/htmx.min.js b/src/match_up/static/htmx.min.js index de5f0f1..de5f0f1 100644 --- a/static/htmx.min.js +++ b/src/match_up/static/htmx.min.js diff --git a/static/logo.png b/src/match_up/static/logo.png index 7d2d8c7..7d2d8c7 100644 --- a/static/logo.png +++ b/src/match_up/static/logo.png diff --git a/templates/controls.j2 b/src/match_up/templates/controls.j2 index 6ba724a..6ba724a 100644 --- a/templates/controls.j2 +++ b/src/match_up/templates/controls.j2 diff --git a/templates/games.j2 b/src/match_up/templates/games.j2 index d5afef1..d5afef1 100644 --- a/templates/games.j2 +++ b/src/match_up/templates/games.j2 diff --git a/templates/index.j2 b/src/match_up/templates/index.j2 index 832dbfb..ce0d531 100644 --- a/templates/index.j2 +++ b/src/match_up/templates/index.j2 @@ -1,10 +1,10 @@ {% from "macros.j2" import header, footer, navbar %} <!doctype html> <html lang="en"> - {{ header() }} + {{ header(url_for) }} <body> <section class="section"> - {{ navbar() }} + {{ navbar(url_for) }} <div hx-post="/clear" hx-swap="outerHTML" hx-trigger="load"></div> <div hx-get="/control" hx-swap="outerHTML" hx-trigger="load"></div> {{ footer() }} diff --git a/templates/macros.j2 b/src/match_up/templates/macros.j2 index 8d05bb0..dedbf48 100644 --- a/templates/macros.j2 +++ b/src/match_up/templates/macros.j2 @@ -22,7 +22,7 @@ </div> {%- endmacro %} -{% macro header() -%} +{% macro header(url_for) -%} <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> @@ -38,7 +38,7 @@ <div class="content has-text-centered">Made by <a href="https://karanjayachandra.com/">Karan</a> using <a href="https://flask.palletsprojects.com">Flask</a>, <a href="https://htmx.org/">HTMX</a> and <a href="https://bulma.io">Bulma</a></div> {%- endmacro %} -{% macro navbar() -%} +{% macro navbar(url_for) -%} <nav class="level"> <div class="level-left"> <div class="level-item"> diff --git a/templates/players.j2 b/src/match_up/templates/players.j2 index ae6afe0..0d1f96c 100644 --- a/templates/players.j2 +++ b/src/match_up/templates/players.j2 @@ -1,10 +1,10 @@ {% from "macros.j2" import header, navbar, footer, player %} <!doctype html> <html lang="en"> - {{ header() }} + {{ header(url_for) }} <body> <section class="section"> - {{ navbar() }} + {{ navbar(url_for) }} <div class="fixed-grid has-4-cols"> <div class="grid"> {% for id, value in players.items() %} diff --git a/utilities.py b/src/match_up/utilities.py index 7a9d55d..8a905ba 100644 --- a/utilities.py +++ b/src/match_up/utilities.py @@ -1,10 +1,11 @@ -from os import getenv from time import time from typing import Tuple from random import shuffle from dataclasses import dataclass + SECS_IN_MIN = 60 +DEFAULT_TIMER = 12.0 def _shuffle_two_lists(a: list, b: list) -> Tuple[list, list]: @@ -30,9 +31,9 @@ def sample_list(indices: list, cost: list[int], count: int = None) -> list: @dataclass class Timer: + max_mins: float = DEFAULT_TIMER ref: float = None running: bool = False - max_mins: float = float(getenv("TIMER")) default: str = ("00", "00") def __post_init__(self): |
