import functools import logging import os from flask import Flask, g, render_template, request, redirect, session, url_for from flask_bootstrap import Bootstrap import db import forms import models import util from config import Config logging.basicConfig(filename=Config.LOG_PATH, level=logging.DEBUG) logging.info(f"Starting in working dir: {os.getcwd()}") def create_app(): app = Flask(__name__) Bootstrap(app) return app app = create_app() app.config.from_object(Config) @app.template_filter("format_time") def format_time(value): """Make the datetime to time string formatting available to jinja2""" if value is None: return "" return util.datetime_time_str(value) @app.template_filter("format_timedelta") def format_timedelta(value): """Make formatting for timedeltas available to jinja2""" if value is None: return "" return util.timedelta_to_str(value) @app.cli.command("initdb") def init_db_command(): """Initializes the database.""" db.init_db() @app.teardown_appcontext def close_connection(exception): db = getattr(g, "_database", None) if db is not None: db.close() def set_user_role(data): """Set the users role in the flask g object for later usage""" g.is_editor = data == "editor" def authorize(func): @functools.wraps(func) def wrapper(*args, **kwargs): try: set_user_role(session["user_type"]) except KeyError: return redirect("/login") return func(*args, **kwargs) return wrapper def get_user_type(password): # TODO password hashing? if password == Config.WRITE_PW: return "editor" if password == Config.READ_PW: return "readonly" return False @app.route("/login", methods=["GET", "POST"]) def login(): if request.method == "GET": return render_template("login.html") else: user_type = get_user_type(request.form.get("password")) if not user_type: return redirect("/login") session["user_type"] = user_type return redirect("/") @app.route("/logout") def logout(): session.pop("role", None) return redirect("login") @app.route("/") @authorize def landing(): return redirect("/season") @app.route("/season") @authorize def season_list(): sql, args = db.load_season() results = db.query_db(sql, args, cls=models.Season) model = { "seasons": results, "columns": [ ("code", "#"), ("game", "Game"), ("description", "Season Description"), ("start", "Started At"), ("end", "Ended At"), ], } return render_template("season_list.html", model=model) @app.route("/season/new", methods=["GET"]) @authorize def season_new(): form = forms.SeasonForm() model = models.GenericFormModel( page_title="New Season", form_title="Create New Season", post_url="/season/edit/null", ) return render_template("generic_form.html", model=model, form=form) @app.route("/season/edit/", methods=["GET", "POST"]) @authorize def season_edit(season_id: int): model = models.GenericFormModel( page_title="Seasons", form_title="Edit Season", post_url=f"/season/edit/{season_id}", ) if request.method == "GET": sql, args = db.load_season(season_id) season: models.Season = db.query_db(sql, args, one=True, cls=models.Season) form = forms.SeasonForm() form.season_id.data = season.id form.code.data = season.code form.game_name.data = season.game form.description.data = season.description form.start.data = season.start form.end.data = season.end model.form_title = f"Edit Season '{season.code}: {season.game}'" return render_template("generic_form.html", model=model, form=form) else: form = forms.SeasonForm() if not form.validate_on_submit(): model.errors = form.errors return render_template("generic_form.html", model=model, form=form) season = models.Season.from_form(form) sql, args = db.save_season_query(season) errors = db.update_db(sql, args) return redirect(url_for("season_list")) @app.route("/season/", methods=["GET"]) @authorize def season_overview(season_id: int): sql, args = db.load_season(season_id) season = db.query_db(sql, args, one=True, cls=models.Season) sql, args = db.load_episodes(season.id) episodes = db.query_db(sql, args, cls=models.Episode) infos = { "Number": season.code, "Game": season.game, "Start Date": season.start, "End Date": season.end if season.end else "Ongoing", } model = { "title": f"{season.code} {season.game}", "season_info": infos, "episodes": episodes, } return render_template("season_overview.html", model=model) @app.route("/season//episode/") @authorize def episode_detail(season_id: int, episode_id: int): sql, args = db.load_season(season_id) season = db.query_db(sql, args, one=True, cls=models.Season) sql, args = db.load_episode(episode_id) episode = db.query_db(sql, args, one=True, cls=models.Episode) sql, args = db.load_episode_players(episode_id) ep_players = db.query_db(sql, args, cls=models.Player) events = {"entries": [], "victory_count": 0, "defeat_count": 0} model = { "title": f"{season.code}{episode.code}", "episode": episode, "season": season, "players": ep_players, "events": events, } return render_template("episode_details.html", model=model) @app.route("/season//episode", methods=["GET"]) @authorize def episode_list(season_id: int): sql, args = db.load_season(season_id) season = db.query_db(sql, args, one=True, cls=models.Season) sql, args = db.load_episodes(season_id) episodes = db.query_db(sql, args, cls=models.Episode) model = {"season_id": season_id, "season_code": season.code} return render_template("episode_list.html", model=model) @app.route("/season//episode/new", methods=["GET"]) @authorize def episode_new(season_id: int): model = models.GenericFormModel( page_title="New Episode", form_title="Create New Episode", post_url=f"/season/{season_id}/episode/null/edit", ) form = forms.EpisodeForm(request.form) form.season_id.data = season_id return render_template("generic_form.html", model=model, form=form) @app.route("/season//episode//edit", methods=["GET", "POST"]) @authorize def episode_edit(season_id: int, episode_id: int): model = models.GenericFormModel( page_title="Edit Episode", form_title="Edit Episode", post_url=f"/season/{season_id}/episode/{episode_id}/edit", ) if request.method == "GET": sql, args = db.load_episode(episode_id) episode: models.Episode = db.query_db(sql, args, one=True, cls=models.Episode) sql, args = db.load_episode_players(episode_id) ep_players = db.query_db(sql, args, cls=models.Player) form = forms.EpisodeForm() form.season_id.data = episode.season_id form.episode_id.data = episode.id form.code.data = episode.code form.date.data = episode.date form.start.data = episode.start form.end.data = episode.end form.title.data = episode.title form.players.data = [p.id for p in ep_players] model.form_title = f"Edit Episode '{episode.code}: {episode.title}'" return render_template("generic_form.html", model=model, form=form) else: form = forms.EpisodeForm() if not form.validate_on_submit(): model.errors = form.errors return render_template("generic_form.html", model=model, form=form) errors = False episode = models.Episode.from_form(form) sql, args = db.save_episode(episode) last_key = db.update_db(sql, args, return_key=True) episode_id = episode.id if episode.id else last_key form_ids = form.players.data sql, args = db.load_episode_players(episode_id) ep_players = db.query_db(sql, args, cls=models.Player) pids = [p.id for p in ep_players] new_ids = [pid for pid in form_ids if pid not in pids] removed_ids = [pid for pid in pids if pid not in form_ids] if removed_ids: sql, args = db.remove_episode_player(episode_id, removed_ids) errors = db.update_db(sql, args) if new_ids: sql, args = db.save_episode_players(episode_id, new_ids) errors = db.update_db(sql, args) if errors: model.errors = {"Error saving episode": [errors]} return render_template("generic_form.html", model=model, form=form) return redirect(url_for("season_overview", season_id=season_id)) @app.route("/season//episode//event/new", methods=["GET"]) @authorize def event_new(s_id: int, ep_id: int): model = { "page_title": "New Event", "form_title": "Create New Event", "post_url": f"/seasons/{s_id}/episodes/{ep_id}/events/null/edit", } return render_template("event_editor.html", model=model) @app.route( "/season//episode//event//edit", methods=["GET", "POST"] ) @authorize def event_edit(s_id: int, ep_id: int, ev_id: int): model = { "page_title": "Edit Event", "form_title": "Edit Event", "post_url": f"/season/{s_id}/episode/{ep_id}/event/{ev_id}/edit", } if request.method == "GET": return render_template("event_editor.html", model=model) else: pass @app.route("/player/new", methods=["GET"]) @authorize def player_new(): form = forms.PlayerForm() model = models.GenericFormModel( page_title="Players", form_title="Create a new Player", post_url="/player/null/edit", ) return render_template("generic_form.html", model=model, form=form) @app.route("/player//edit", methods=["GET", "POST"]) @authorize def player_edit(player_id: int): model = models.GenericFormModel( page_title="Players", form_title=f"Edit Player", post_url=f"/player/{player_id}/edit", ) # Edit Existing Player if request.method == "GET": sql, args = db.load_players(player_id) player = db.query_db(sql, args, one=True, cls=models.Player) form = forms.PlayerForm() form.player_id.data = player.id form.anonymize.data = player.anon form.real_name.data = player.real_name form.alias.data = player.alias form.hex_id.data = player.hex_id model.form_title = f'Edit Player "{player.name}"' return render_template("generic_form.html", model=model, form=form) # Save POSTed data else: form = forms.PlayerForm() if form.validate_on_submit(): player = models.Player.from_form(form) res = db.save_player(player) return redirect("/player") model.form_title = "Incorrect Data" return render_template("generic_form.html", model=model, form=form) @app.route("/player") @authorize def player_list(): sql, args = db.load_players() players = db.query_db(sql, args, cls=models.Player) model = { "player_list": players, "columns": [ ("id", "ID"), ("name", "Player Name"), ("alias", "Alias"), ("hex_id", "Hex ID"), ], } return render_template("player_list.html", model=model) @app.route("/drink") @authorize def drink_list(): sql, args = db.load_drinks() drinks = db.query_db(sql, args, cls=models.Drink) model = { "drinks": drinks, "columns": [("id", "ID"), ("name", "Drink Name"), ("vol", "Alcohol %")], "controls": [("edit", "Edit")], } return render_template("drink_list.html", model=model) @app.route("/drink//edit", methods=["GET"]) @authorize def drink_edit(drink_id: int): sql, args = db.load_drinks(drink_id) drink = db.query_db(sql, args, one=True, cls=models.Drink) form = forms.DrinkForm() form.drink_id.data = drink.id form.name.data = drink.name form.vol.data = drink.vol model = models.GenericFormModel( page_title="Edit Drink", form_title=f'Edit Drink "{drink.name}"', post_url="/drink/save", ) return render_template("generic_form.html", model=model, form=form) @app.route("/drink/new", methods=["GET"]) @authorize def new_drink(): form = forms.DrinkForm() model = models.GenericFormModel( page_title="New Drink", form_title=f"Create a new Drink", post_url="/drinks/save", ) return render_template("generic_form.html", model=model, form=form) @app.route("/drink/save", methods=["POST"]) @authorize def drink_save(): form = forms.DrinkForm() if form.validate_on_submit(): drink = models.Drink.from_form(form) res = db.save_drink(drink) return redirect("/drink") model = models.GenericFormModel( page_title="Drinks", form_title="Edit Drink", post_url="/drink/save" ) return render_template("generic_form.html", model=model, form=form) @app.route("/enemy") @authorize def enemy_list(): sql, args = db.load_enemies() enemies = db.query_db(sql, args, cls=models.Enemy) model = {"enemies": enemies} return render_template("enemies.html", model=model) @app.route("/enemy/new", methods=["GET"]) @authorize def enemy_new(preselect_season=None): form = forms.EnemyForm() if preselect_season: form.season_id.default = preselect_season model = models.GenericFormModel( page_title="Enemies", form_title="Create a new Enemy", post_url=f"/enemy/null/edit", ) return render_template("generic_form.html", model=model, form=form) @app.route("/enemy//edit", methods=["GET", "POST"]) @authorize def enemy_edit(enemy_id: int): model = models.GenericFormModel( page_title="Enemies", form_title="Edit Enemy", post_url=f"/enemy/{enemy_id}/edit", ) if request.method == "GET": sql, args = db.load_enemies(enemy_id) enemy = db.query_db(sql, args, one=True, cls=models.Enemy) form = forms.EnemyForm() form.season_id.data = enemy.season_id if enemy.season_id else -1 form.name.data = enemy.name form.is_boss.data = enemy.boss form.enemy_id.data = enemy_id model.form_title = f'Edit Enemy "{enemy.name}"' return render_template("generic_form.html", model=model, form=form) else: form = forms.EnemyForm() if form.validate_on_submit(): enemy = models.Enemy.from_form(form) sql, args = db.save_enemy(enemy) errors = db.update_db(sql, args) if form.submit_continue_button.data: form.name.data = None return enemy_new(preselect_season=enemy.season_id) return redirect("/enemy") model.form_title = "Incorrect Data" return render_template("generic_form.html", model=model, form=form) if __name__ == "__main__": app.run()