From 0da8dc16fb26e9df5d0efd7c7a463e354d8e00cf Mon Sep 17 00:00:00 2001
From: Karan Jayachandra
Date: Sun, 6 Jul 2025 12:58:07 +0200
Subject: Transitioned back to a traditional flask app
---
app/routes.py | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 80 insertions(+)
create mode 100644 app/routes.py
(limited to 'app/routes.py')
diff --git a/app/routes.py b/app/routes.py
new file mode 100644
index 0000000..e5a3b99
--- /dev/null
+++ b/app/routes.py
@@ -0,0 +1,80 @@
+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"])
+def home():
+ return re.get_template("index.j2").render(url_for=url_for)
+
+
+@_app.route("/control", methods=["GET"])
+def get_controls():
+ return render_template("controls.j2", courts=_rg.courts.get_courts())
+
+
+@_app.route("/propose", methods=["POST"])
+def get_random_games():
+ 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"])
+def confirm_games():
+ return render_template("games.j2", games=_rg.confirm(), title=f"Round: {_rg.round}")
+
+
+@_app.route("/clear", methods=["POST"])
+def clear_games():
+ return render_template("games.j2", games=_rg.clear(), title=f"Round: {_rg.round}")
+
+
+@_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/", methods=["POST"])
+def toggle_player(player_request):
+ id = int(player_request)
+ 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"])
+def get_list_of_players():
+ regulars, guests = _rg.players.get_all_players()
+ return re.get_template("players.j2").render(
+ regulars=regulars, guests=guests, url_for=url_for
+ )
+
+
+@_app.route("/reset_players", methods=["POST"])
+def reset_all_players():
+ _rg.players.reset_all_players()
+ response = Response("Players reset!")
+ response.headers["HX-Refresh"] = "true"
+ return response
+
+
+@_app.route("/court-toggle/", methods=["POST"])
+def toggle_court(court_number):
+ id = int(court_number)
+ 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"])
+def get_list_of_courts():
+ return render_template("courts.j2", courts=_rg.courts.get_courts())
--
cgit v1.3.1
From c773faeea93fe5db2eb493c79c0c82f46336493c Mon Sep 17 00:00:00 2001
From: Karan Jayachandra
Date: Sun, 6 Jul 2025 13:31:37 +0200
Subject: Running application
---
app/__init__.py | 25 +-----------
app/controller.py | 6 +--
app/model.py | 6 +--
app/routes.py | 63 ++++++++++++++----------------
app/templates/controls.j2 | 47 ++++++++++++++++++++++
app/templates/games.j2 | 21 ++++++++++
app/templates/index.j2 | 13 +++++++
app/templates/macros.j2 | 99 +++++++++++++++++++++++++++++++++++++++++++++++
app/templates/players.j2 | 31 +++++++++++++++
app/utilities.py | 4 +-
matchup.py | 1 +
templates/controls.j2 | 47 ----------------------
templates/games.j2 | 21 ----------
templates/index.j2 | 13 -------
templates/macros.j2 | 99 -----------------------------------------------
templates/players.j2 | 31 ---------------
16 files changed, 249 insertions(+), 278 deletions(-)
create mode 100644 app/templates/controls.j2
create mode 100644 app/templates/games.j2
create mode 100644 app/templates/index.j2
create mode 100644 app/templates/macros.j2
create mode 100644 app/templates/players.j2
create mode 100644 matchup.py
delete mode 100644 templates/controls.j2
delete mode 100644 templates/games.j2
delete mode 100644 templates/index.j2
delete mode 100644 templates/macros.j2
delete mode 100644 templates/players.j2
(limited to 'app/routes.py')
diff --git a/app/__init__.py b/app/__init__.py
index bccd0e7..96c8ef5 100644
--- a/app/__init__.py
+++ b/app/__init__.py
@@ -1,26 +1,5 @@
from flask import Flask
-from waitress import serve
-from argparse import ArgumentParser
-from importlib.resources import path
-from match_up.controller import RoundGenerator
+app = Flask(__name__)
-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("-d", "--database", help="CSV database", default=None)
- args = parser.parse_args()
- _rg.update_player_list(csv_location=args.database)
- serve(_app, port=80)
+from app import routes
diff --git a/app/controller.py b/app/controller.py
index b00bf6b..498715d 100644
--- a/app/controller.py
+++ b/app/controller.py
@@ -1,8 +1,8 @@
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 (
+from app.data import Game, PLAYER_PER_COURT
+from app.model import Player, PlayerList, CourtList
+from app.utilities import (
_create_placeholders,
_select_players,
_create_shuffle_games,
diff --git a/app/model.py b/app/model.py
index 467310b..b2b6470 100644
--- a/app/model.py
+++ b/app/model.py
@@ -3,12 +3,11 @@ from typing import Tuple
from os.path import isfile
from pandas import read_csv
from dataclasses import dataclass
-from importlib.resources import path
from random import getrandbits, randint
from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine, not_
from names import get_first_name, get_last_name
-from match_up.data import (
+from app.data import (
MIN_LEVEL,
MAX_LEVEL,
PLAYER_PER_COURT,
@@ -103,8 +102,7 @@ def _init_random_database(session, player_count: int = 80) -> None:
class PlayerList:
def __init__(self, csv_location=None) -> None:
- with path("match_up", "players.db") as p:
- self.db_location = p
+ self.db_location = "players.db"
db_file_found = True
if not isfile(self.db_location):
db_file_found = False
diff --git a/app/routes.py b/app/routes.py
index e5a3b99..c18d93b 100644
--- a/app/routes.py
+++ b/app/routes.py
@@ -1,80 +1,73 @@
-from match_up import _app, _rg
-from jinja2 import Environment, PackageLoader
+from app import app
+from app.controller import RoundGenerator
from flask import render_template, request, url_for, Response
-re = Environment(loader=PackageLoader("match_up"))
+rg = RoundGenerator()
-
-@_app.route("/", methods=["GET"])
+@app.route("/", methods=["GET"])
def home():
- return re.get_template("index.j2").render(url_for=url_for)
+ return render_template("index.j2", 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("/propose", methods=["POST"])
+@app.route("/propose", methods=["POST"])
def get_random_games():
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"
+ "games.j2", games=rg.propose(levels, team_practice), 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("/reset", methods=["POST"])
+@app.route("/reset", methods=["POST"])
def reset_games():
- return render_template("games.j2", games=_rg.reset(), title=f"Round: {_rg.round}")
+ return render_template("games.j2", games=rg.reset(), title=f"Round: {rg.round}")
-@_app.route("/player-toggle/", methods=["POST"])
+@app.route("/player-toggle/", methods=["POST"])
def toggle_player(player_request):
id = int(player_request)
- player = _rg.players.toggle_player_status(id)
- t = re.from_string(
- '{% from "macros.j2" import player_button %}{{player_button(player)}}'
- )
+ player = rg.players.toggle_player_status(id)
+ t = '{% 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():
- regulars, guests = _rg.players.get_all_players()
- return re.get_template("players.j2").render(
- regulars=regulars, guests=guests, url_for=url_for
- )
+ regulars, guests = rg.players.get_all_players()
+ return render_template("players.j2", regulars=regulars, 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/", methods=["POST"])
+@app.route("/court-toggle/", methods=["POST"])
def toggle_court(court_number):
id = int(court_number)
- status = _rg.courts.toggle_court_status(id)
- t = re.from_string(
- '{% from "macros.j2" import court_button %}{{ court_button(id, status) }}'
- )
+ status = rg.courts.toggle_court_status(id)
+ t = '{% 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/app/templates/controls.j2 b/app/templates/controls.j2
new file mode 100644
index 0000000..0183c97
--- /dev/null
+++ b/app/templates/controls.j2
@@ -0,0 +1,47 @@
+{% from "macros.j2" import court_button %}
+
+
+ {% for key, value in courts.items() %}
+ {{ court_button(key, value) }}
+ {% endfor %}
+
+
+
+
+
+
+ 10 Levels
+ 8 Levels
+ 6 Levels
+ 4 Levels
+ 2 Levels
+ 0 Levels
+
+
+
+
+
+
+ Random
+ Teams
+
+
+
+
+ Generate
+
+
+
+ Confirm
+
+
+ RESET!
+
+
\ No newline at end of file
diff --git a/app/templates/games.j2 b/app/templates/games.j2
new file mode 100644
index 0000000..46150f7
--- /dev/null
+++ b/app/templates/games.j2
@@ -0,0 +1,21 @@
+
+
+ {{ title }}
+
+
+ Court
+ Team 1
+ Team 2
+
+
+{% for game in games %}
+
+ {{ game.court }}
+ {{ game.team1.player1.first }} {{ " " + game.team1.player1.last }}
+ {{ game.team1.player2.first }} {{ " " + game.team1.player2.last }}
+ {{ game.team2.player1.first }} {{ " " + game.team2.player1.last }}
+ {{ game.team2.player2.first }} {{ " " + game.team2.player2.last }}
+
+{% endfor %}
+
+
\ No newline at end of file
diff --git a/app/templates/index.j2 b/app/templates/index.j2
new file mode 100644
index 0000000..ce0d531
--- /dev/null
+++ b/app/templates/index.j2
@@ -0,0 +1,13 @@
+{% from "macros.j2" import header, footer, navbar %}
+
+
+ {{ header(url_for) }}
+
+
+ {{ navbar(url_for) }}
+
+
+ {{ footer() }}
+
+
+
diff --git a/app/templates/macros.j2 b/app/templates/macros.j2
new file mode 100644
index 0000000..2974744
--- /dev/null
+++ b/app/templates/macros.j2
@@ -0,0 +1,99 @@
+{% macro court_button(id, status) -%}
+ Court {{ id }}
+{%- endmacro %}
+
+{% macro player_button(player) -%}
+ {{ player.first + " " + player.last}}
+{%- endmacro %}
+
+{% macro guest_control(level, value) -%}
+
+{%- endmacro %}
+
+{% macro header(url_for) -%}
+
+
+
+ Match Up!
+
+
+
+
+
+
+
+
+
+{%- endmacro %}
+
+{% macro footer() -%}
+Made by
Karan using
Flask ,
HTMX ,
Notyf and
Bulma
+{%- endmacro %}
+
+{% macro navbar(url_for) -%}
+
+
+ Games
+
+
+ Players
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{%- endmacro %}
\ No newline at end of file
diff --git a/app/templates/players.j2 b/app/templates/players.j2
new file mode 100644
index 0000000..0beeee9
--- /dev/null
+++ b/app/templates/players.j2
@@ -0,0 +1,31 @@
+{% from "macros.j2" import header, navbar, footer, player_button, guest_control %}
+
+
+ {{ header(url_for) }}
+
+
+ {{ navbar(url_for) }}
+ Members
+
+
+ {% for regular in regulars %}
+
{{ player_button(regular) }}
+ {% endfor %}
+
+
Guests
+
+
+ {% for guest in guests %}
+
{{ player_button(guest) }}
+ {% endfor %}
+
+
+
+
+ RESET!
+
+ {{ footer() }}
+
+
+
\ No newline at end of file
diff --git a/app/utilities.py b/app/utilities.py
index 7140a5f..e324161 100644
--- a/app/utilities.py
+++ b/app/utilities.py
@@ -1,7 +1,7 @@
from typing import Tuple
from random import shuffle, sample
-from match_up.data import Player, DisplayPlayer, Team, Game
-from match_up.data import MAX_LEVEL, PLAYER_PER_COURT
+from app.data import Player, DisplayPlayer, Team, Game
+from app.data import MAX_LEVEL, PLAYER_PER_COURT
def _shuffle_two_lists(a: list, b: list) -> Tuple[list, list]:
diff --git a/matchup.py b/matchup.py
new file mode 100644
index 0000000..e524e69
--- /dev/null
+++ b/matchup.py
@@ -0,0 +1 @@
+from app import app
\ No newline at end of file
diff --git a/templates/controls.j2 b/templates/controls.j2
deleted file mode 100644
index 0183c97..0000000
--- a/templates/controls.j2
+++ /dev/null
@@ -1,47 +0,0 @@
-{% from "macros.j2" import court_button %}
-
-
- {% for key, value in courts.items() %}
- {{ court_button(key, value) }}
- {% endfor %}
-
-
-
-
-
-
- 10 Levels
- 8 Levels
- 6 Levels
- 4 Levels
- 2 Levels
- 0 Levels
-
-
-
-
-
-
- Random
- Teams
-
-
-
-
- Generate
-
-
-
- Confirm
-
-
- RESET!
-
-
\ No newline at end of file
diff --git a/templates/games.j2 b/templates/games.j2
deleted file mode 100644
index 46150f7..0000000
--- a/templates/games.j2
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
- {{ title }}
-
-
- Court
- Team 1
- Team 2
-
-
-{% for game in games %}
-
- {{ game.court }}
- {{ game.team1.player1.first }} {{ " " + game.team1.player1.last }}
- {{ game.team1.player2.first }} {{ " " + game.team1.player2.last }}
- {{ game.team2.player1.first }} {{ " " + game.team2.player1.last }}
- {{ game.team2.player2.first }} {{ " " + game.team2.player2.last }}
-
-{% endfor %}
-
-
\ No newline at end of file
diff --git a/templates/index.j2 b/templates/index.j2
deleted file mode 100644
index ce0d531..0000000
--- a/templates/index.j2
+++ /dev/null
@@ -1,13 +0,0 @@
-{% from "macros.j2" import header, footer, navbar %}
-
-
- {{ header(url_for) }}
-
-
- {{ navbar(url_for) }}
-
-
- {{ footer() }}
-
-
-
diff --git a/templates/macros.j2 b/templates/macros.j2
deleted file mode 100644
index 2974744..0000000
--- a/templates/macros.j2
+++ /dev/null
@@ -1,99 +0,0 @@
-{% macro court_button(id, status) -%}
-
Court {{ id }}
-{%- endmacro %}
-
-{% macro player_button(player) -%}
-
{{ player.first + " " + player.last}}
-{%- endmacro %}
-
-{% macro guest_control(level, value) -%}
-
-{%- endmacro %}
-
-{% macro header(url_for) -%}
-
-
-
-
Match Up!
-
-
-
-
-
-
-
-
-
-{%- endmacro %}
-
-{% macro footer() -%}
-
Made by
Karan using
Flask ,
HTMX ,
Notyf and
Bulma
-{%- endmacro %}
-
-{% macro navbar(url_for) -%}
-
-
- Games
-
-
- Players
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-{%- endmacro %}
\ No newline at end of file
diff --git a/templates/players.j2 b/templates/players.j2
deleted file mode 100644
index 0beeee9..0000000
--- a/templates/players.j2
+++ /dev/null
@@ -1,31 +0,0 @@
-{% from "macros.j2" import header, navbar, footer, player_button, guest_control %}
-
-
- {{ header(url_for) }}
-
-
- {{ navbar(url_for) }}
- Members
-
-
- {% for regular in regulars %}
-
{{ player_button(regular) }}
- {% endfor %}
-
-
Guests
-
-
- {% for guest in guests %}
-
{{ player_button(guest) }}
- {% endfor %}
-
-
-
-
- RESET!
-
- {{ footer() }}
-
-
-
\ No newline at end of file
--
cgit v1.3.1
From ceb264b6bdfed8c51e54deed2b6132fecff3934d Mon Sep 17 00:00:00 2001
From: Karan Jayachandra
Date: Sun, 6 Jul 2025 15:13:27 +0200
Subject: Added the basic login
---
app/__init__.py | 2 ++
app/config.py | 4 ++++
app/forms.py | 9 +++++++++
app/routes.py | 10 +++++++++-
app/templates/index.j2 | 9 +++++++++
app/templates/login.j2 | 25 +++++++++++++++++++++++++
6 files changed, 58 insertions(+), 1 deletion(-)
create mode 100644 app/config.py
create mode 100644 app/forms.py
create mode 100644 app/templates/login.j2
(limited to 'app/routes.py')
diff --git a/app/__init__.py b/app/__init__.py
index 96c8ef5..7b0c03d 100644
--- a/app/__init__.py
+++ b/app/__init__.py
@@ -1,5 +1,7 @@
from flask import Flask
+from app.config import Config
app = Flask(__name__)
+app.config.from_object(Config)
from app import routes
diff --git a/app/config.py b/app/config.py
new file mode 100644
index 0000000..aeb2840
--- /dev/null
+++ b/app/config.py
@@ -0,0 +1,4 @@
+from os import environ
+
+class Config:
+ SECRET_KEY = environ.get("SECRET_KEY") or "you-will-never-guess"
\ No newline at end of file
diff --git a/app/forms.py b/app/forms.py
new file mode 100644
index 0000000..504824a
--- /dev/null
+++ b/app/forms.py
@@ -0,0 +1,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
diff --git a/app/routes.py b/app/routes.py
index c18d93b..b943e16 100644
--- a/app/routes.py
+++ b/app/routes.py
@@ -1,6 +1,7 @@
from app import app
+from app.forms import LoginForm
from app.controller import RoundGenerator
-from flask import render_template, request, url_for, Response
+from flask import render_template, request, url_for, Response, flash, redirect
rg = RoundGenerator()
@@ -8,6 +9,13 @@ rg = RoundGenerator()
def home():
return render_template("index.j2", url_for=url_for)
+@app.route("/login", methods=["GET", "POST"])
+def login():
+ form = LoginForm()
+ if form.validate_on_submit():
+ flash(f"User {form.username} is requesting to login.")
+ return redirect("/")
+ return render_template("login.j2", form=form)
@app.route("/control", methods=["GET"])
def get_controls():
diff --git a/app/templates/index.j2 b/app/templates/index.j2
index ce0d531..656b187 100644
--- a/app/templates/index.j2
+++ b/app/templates/index.j2
@@ -7,6 +7,15 @@
{{ navbar(url_for) }}
+ {% with messages = get_flashed_messages() %}
+ {% if messages %}
+
+ {% for message in messages %}
+ {{ message }}
+ {% endfor %}
+
+ {% endif %}
+ {% endwith %}
{{ footer() }}
+
+ {{ navbar(url_for) }}
+ Sign In
+
+ {{ footer() }}
+
+
+