Change file structure
All checks were successful
Build Formula10 Docker Image / build-docker (push) Successful in 50s

This commit is contained in:
2024-02-24 16:22:48 +01:00
parent 0f15369685
commit fc8a890511
9 changed files with 190 additions and 191 deletions

View File

@ -2,9 +2,9 @@ from typing import Dict, List, cast
from urllib.parse import quote
from flask import redirect
from werkzeug import Response
from database_utils import race_has_result, user_exists
from model import PodiumDrivers, RaceResult, SeasonGuess, TeamWinners, User, db, RaceGuess
from validation_utils import any_is_none, positions_are_contiguous
from app.database.database_utils import race_has_result, user_exists
from app.database.model import PodiumDrivers, RaceResult, SeasonGuess, TeamWinners, User, db, RaceGuess
from app.database.validation_utils import any_is_none, positions_are_contiguous
def find_or_create_race_guess(user_name: str, race_name: str) -> RaceGuess:
@ -127,13 +127,13 @@ def update_season_guess(user_name: str, guesses: List[str | None], team_winner_g
# Pylance marks type errors here, but those are intended. Columns are marked nullable.
season_guess: SeasonGuess = find_or_create_season_guess(user_name)
season_guess.hot_take = guesses[0]
season_guess.p2_team_name = guesses[1]
season_guess.overtake_driver_name = guesses[2]
season_guess.dnf_driver_name = guesses[3]
season_guess.gained_driver_name = guesses[4]
season_guess.lost_driver_name = guesses[5]
season_guess.team_winners.teamwinner_driver_names = team_winner_guesses
season_guess.hot_take = guesses[0] # type: ignore
season_guess.p2_team_name = guesses[1] # type: ignore
season_guess.overtake_driver_name = guesses[2] # type: ignore
season_guess.dnf_driver_name = guesses[3] # type: ignore
season_guess.gained_driver_name = guesses[4] # type: ignore
season_guess.lost_driver_name = guesses[5] # type: ignore
season_guess.team_winners.teamwinner_driver_names = team_winner_guesses # type: ignore
season_guess.podium_drivers.podium_driver_names = podium_driver_guesses
db.session.commit()

View File

@ -1,4 +1,4 @@
from model import User, db, RaceResult
from app.database.model import User, db, RaceResult
def race_has_result(race_name: str) -> bool:

View File

@ -1,7 +1,7 @@
import csv
import os.path
from typing import List, Any
from model import Team, Driver, Race, User, RaceResult, RaceGuess, TeamWinners, PodiumDrivers, SeasonGuess, db
from app.database.model import Team, Driver, Race, User, RaceResult, RaceGuess, TeamWinners, PodiumDrivers, SeasonGuess, db
def load_csv(filename: str) -> List[List[str]]:

174
app/frontend/controller.py Normal file
View File

@ -0,0 +1,174 @@
from typing import List
from urllib.parse import unquote
from flask import Flask, render_template, request, redirect
from werkzeug import Response
from app.database.model import Team, db
from app.database.file_utils import reload_static_data, reload_dynamic_data, export_dynamic_data
from app.frontend.template_model import TemplateModel
from app.database.backend_model import delete_race_guess, update_race_guess, update_race_result, update_season_guess, update_user
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = "sqlite:///formula10.db"
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.url_map.strict_slashes = False
db.init_app(app)
# TODO
# General
# - Choose "place to guess" late before the race? Make a page for this
# - Rules page
# - Make user order changeable using drag'n'drop?
# - Show place when entering race result (would require updating the drag'n'drop code...)
# - Show cards of previous race results, like with season guesses?
# - Make the season card grid left-aligned? So e.g. 2 cards are not spread over the whole screen with large gaps?
# Statistics
# - Auto calculate points
# - Order user table by points + display points somewhere
# - Show current values for some season guesses (e.g. current most dnfs)
# - Generate static diagram using chart.js + templating the js (funny yikes)
@app.route("/")
def root() -> Response:
return redirect("/race/Everyone")
@app.route("/save/all")
def save() -> Response:
export_dynamic_data()
return redirect("/")
@app.route("/load/all")
def load() -> Response:
reload_static_data()
reload_dynamic_data()
return redirect("/")
@app.route("/load/static")
def load_static() -> Response:
reload_static_data()
return redirect("/")
@app.route("/load/dynamic")
def load_dynamic() -> Response:
reload_dynamic_data()
return redirect("/")
@app.route("/race")
def race_root() -> Response:
return redirect("/race/Everyone")
@app.route("/race/<user_name>")
def race_active_user(user_name: str) -> str:
user_name = unquote(user_name)
model = TemplateModel()
return render_template("race.jinja",
active_user=model.user_by(user_name=user_name, ignore=["Everyone"]),
model=model)
@app.route("/race-guess/<race_name>/<user_name>", methods=["POST"])
def race_guess_post(race_name: str, user_name: str) -> Response:
race_name = unquote(race_name)
user_name = unquote(user_name)
pxx: str | None = request.form.get("pxxselect")
dnf: str | None = request.form.get("dnfselect")
return update_race_guess(race_name, user_name, pxx, dnf)
@app.route("/race-guess-delete/<race_name>/<user_name>", methods=["POST"])
def race_guess_delete_post(race_name: str, user_name: str) -> Response:
race_name = unquote(race_name)
user_name = unquote(user_name)
return delete_race_guess(race_name, user_name)
@app.route("/season")
def season_root() -> Response:
return redirect("/season/Everyone")
@app.route("/season/<user_name>")
def season_active_user(user_name: str) -> str:
user_name = unquote(user_name)
model = TemplateModel()
return render_template("season.jinja",
active_user=model.user_by(user_name=user_name, ignore=["Everyone"]),
model=model)
@app.route("/season-guess/<user_name>", methods=["POST"])
def season_guess_post(user_name: str) -> Response:
user_name = unquote(user_name)
guesses: List[str | None] = [
request.form.get("hottakeselect"),
request.form.get("p2select"),
request.form.get("overtakeselect"),
request.form.get("dnfselect"),
request.form.get("gainedselect"),
request.form.get("lostselect")
]
team_winner_guesses: List[str | None] = [
request.form.get(f"teamwinner-{team.name}") for team in db.session.query(Team).all()
]
podium_driver_guesses: List[str] = request.form.getlist("podiumdrivers")
return update_season_guess(user_name, guesses, team_winner_guesses, podium_driver_guesses)
@app.route("/result")
def result_root() -> Response:
return redirect("/result/Current")
@app.route("/result/<race_name>")
def result_active_race(race_name: str) -> str:
race_name = unquote(race_name)
model = TemplateModel()
return render_template("enter.jinja",
active_result=model.race_result_by(race_name=race_name),
model=model)
@app.route("/result-enter/<race_name>", methods=["POST"])
def result_enter_post(race_name: str) -> Response:
race_name = unquote(race_name)
pxxs: List[str] = request.form.getlist("pxx-drivers")
first_dnfs: List[str] = request.form.getlist("first-dnf-drivers")
dnfs: List[str] = request.form.getlist("dnf-drivers")
excluded: List[str] = request.form.getlist("excluded-drivers")
return update_race_result(race_name, pxxs, first_dnfs, dnfs, excluded)
@app.route("/user")
def user_root() -> str:
model = TemplateModel()
return render_template("users.jinja",
model=model)
@app.route("/user-add", methods=["POST"])
def user_add_post() -> Response:
username: str | None = request.form.get("select-add-user")
return update_user(username, add=True)
@app.route("/user-delete", methods=["POST"])
def user_delete_post() -> Response:
username: str | None = request.form.get("select-delete-user")
return update_user(username, delete=True)

View File

@ -1,7 +1,7 @@
from typing import List, Callable, Dict, overload
from sqlalchemy import desc
from model import User, RaceResult, RaceGuess, Race, Driver, Team, SeasonGuess, db
from validation_utils import find_first_or_none, find_multiple, find_single, find_single_or_none
from app.database.model import User, RaceResult, RaceGuess, Race, Driver, Team, SeasonGuess, db
from app.database.validation_utils import find_first_or_none, find_multiple, find_single, find_single_or_none
# This could also be moved to database_utils (at least partially), but I though the template should cache the database responses

View File

View File

@ -1,179 +1,4 @@
from typing import List
from urllib.parse import unquote
from flask import Flask, render_template, request, redirect
from werkzeug import Response
from model import Team, db
from file_utils import reload_static_data, reload_dynamic_data, export_dynamic_data
from template_model import TemplateModel
from backend_model import delete_race_guess, update_race_guess, update_race_result, update_season_guess, update_user
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = "sqlite:///formula10.db"
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.url_map.strict_slashes = False
db.init_app(app)
# TODO
# General
# - Choose "place to guess" late before the race? Make a page for this
# - Make user order changeable using drag'n'drop?
# - Show place when entering race result (would require updating the drag'n'drop code...)
# - Show cards of previous race results, like with season guesses?
# - Make the season card grid left-aligned? So e.g. 2 cards are not spread over the whole screen with large gaps?
# Statistics
# - Auto calculate points
# - Order user table by points + display points somewhere
# - Show current values for some season guesses (e.g. current most dnfs)
# - Generate static diagram using chart.js + templating the js (funny yikes)
# Rules page
@app.route("/")
def root() -> Response:
return redirect("/race/Everyone")
@app.route("/save/all")
def save() -> Response:
export_dynamic_data()
return redirect("/")
@app.route("/load/all")
def load() -> Response:
reload_static_data()
reload_dynamic_data()
return redirect("/")
@app.route("/load/static")
def load_static() -> Response:
reload_static_data()
return redirect("/")
@app.route("/load/dynamic")
def load_dynamic() -> Response:
reload_dynamic_data()
return redirect("/")
@app.route("/race")
def race_root() -> Response:
return redirect("/race/Everyone")
@app.route("/race/<user_name>")
def race_active_user(user_name: str) -> str:
user_name = unquote(user_name)
model = TemplateModel()
return render_template("race.jinja",
active_user=model.user_by(user_name=user_name, ignore=["Everyone"]),
model=model)
@app.route("/race-guess/<race_name>/<user_name>", methods=["POST"])
def race_guess_post(race_name: str, user_name: str) -> Response:
race_name = unquote(race_name)
user_name = unquote(user_name)
pxx: str | None = request.form.get("pxxselect")
dnf: str | None = request.form.get("dnfselect")
return update_race_guess(race_name, user_name, pxx, dnf)
@app.route("/race-guess-delete/<race_name>/<user_name>", methods=["POST"])
def race_guess_delete_post(race_name: str, user_name: str) -> Response:
race_name = unquote(race_name)
user_name = unquote(user_name)
return delete_race_guess(race_name, user_name)
@app.route("/season")
def season_root() -> Response:
return redirect("/season/Everyone")
@app.route("/season/<user_name>")
def season_active_user(user_name: str) -> str:
user_name = unquote(user_name)
model = TemplateModel()
return render_template("season.jinja",
active_user=model.user_by(user_name=user_name, ignore=["Everyone"]),
model=model)
@app.route("/season-guess/<user_name>", methods=["POST"])
def season_guess_post(user_name: str) -> Response:
user_name = unquote(user_name)
guesses: List[str | None] = [
request.form.get("hottakeselect"),
request.form.get("p2select"),
request.form.get("overtakeselect"),
request.form.get("dnfselect"),
request.form.get("gainedselect"),
request.form.get("lostselect")
]
team_winner_guesses: List[str | None] = [
request.form.get(f"teamwinner-{team.name}") for team in db.session.query(Team).all()
]
podium_driver_guesses: List[str] = request.form.getlist("podiumdrivers")
return update_season_guess(user_name, guesses, team_winner_guesses, podium_driver_guesses)
@app.route("/result")
def result_root() -> Response:
return redirect("/result/Current")
@app.route("/result/<race_name>")
def result_active_race(race_name: str) -> str:
race_name = unquote(race_name)
model = TemplateModel()
return render_template("enter.jinja",
active_result=model.race_result_by(race_name=race_name),
model=model)
@app.route("/result-enter/<race_name>", methods=["POST"])
def result_enter_post(race_name: str) -> Response:
race_name = unquote(race_name)
pxxs: List[str] = request.form.getlist("pxx-drivers")
first_dnfs: List[str] = request.form.getlist("first-dnf-drivers")
dnfs: List[str] = request.form.getlist("dnf-drivers")
excluded: List[str] = request.form.getlist("excluded-drivers")
return update_race_result(race_name, pxxs, first_dnfs, dnfs, excluded)
@app.route("/user")
def user_root() -> str:
model = TemplateModel()
return render_template("users.jinja",
model=model)
@app.route("/user-add", methods=["POST"])
def user_add_post() -> Response:
username: str | None = request.form.get("select-add-user")
return update_user(username, add=True)
@app.route("/user-delete", methods=["POST"])
def user_delete_post() -> Response:
username: str | None = request.form.get("select-delete-user")
return update_user(username, delete=True)
from app.frontend.controller import app
if __name__ == "__main__":
app.run(debug=True, host="0.0.0.0")
app.run(debug=True, host="0.0.0.0")