From e3bb076af658dce18b13b4451d9346a124601724 Mon Sep 17 00:00:00 2001 From: luxick Date: Tue, 12 Feb 2019 17:49:07 +0100 Subject: [PATCH] Further refactoring. --- app.py | 250 +++++++++++++++++++++++++---------------------------- choices.py | 5 +- config.py | 11 +++ const.py | 6 +- db.py | 102 +++++++++++----------- forms.py | 65 ++++++++------ models.py | 20 ++--- roles.json | 4 + 8 files changed, 238 insertions(+), 225 deletions(-) create mode 100644 config.py create mode 100644 roles.json diff --git a/app.py b/app.py index 7c39afb..1ad07ac 100644 --- a/app.py +++ b/app.py @@ -9,12 +9,13 @@ import db import forms import models import const +from config import Config, roles logging.basicConfig(filename=const.LOG_PATH, level=logging.DEBUG) -logging.info(f'Starting in working dir: {os.getcwd()}') -logging.info(f'App base path: {const.BASE_PATH}') +logging.info(f"Starting in working dir: {os.getcwd()}") +logging.info(f"App base path: {const.BASE_PATH}") def create_app(): @@ -25,15 +26,10 @@ def create_app(): app = create_app() -app.secret_key = 'THIS IS A TEST KEY' - -ROLES = { - '123': 'readonly', - '1234': 'editor' -} +app.config.from_object(Config) -@app.cli.command('initdb') +@app.cli.command("initdb") def init_db_command(): """Initializes the database.""" db.init_db() @@ -41,171 +37,167 @@ def init_db_command(): @app.teardown_appcontext def close_connection(exception): - db = getattr(g, '_database', None) + 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' + g.is_editor = data == "editor" def authorize(func): @functools.wraps(func) def wrapper(*args, **kwargs): try: - set_user_role(session['role']) + set_user_role(session["role"]) except KeyError: - return redirect('/login') + return redirect("/login") return func(*args, **kwargs) + return wrapper -@app.route('/login', methods=['GET', 'POST']) +@app.route("/login", methods=["GET", "POST"]) def login(): - if request.method == 'GET': - return render_template('login.html') + if request.method == "GET": + return render_template("login.html") else: try: - password = request.form['password'] - session['role'] = ROLES[password] - return redirect('/') + password = request.form["password"] + session["role"] = roles()[password] + return redirect("/") except KeyError: - return redirect('login') + return redirect("login") -@app.route('/logout') +@app.route("/logout") def logout(): - session.pop('role', None) - return redirect('login') + session.pop("role", None) + return redirect("login") -@app.route('/') +@app.route("/") @authorize def landing(): - return redirect('/seasons') + return redirect("/seasons") -@app.route('/seasons') +@app.route("/seasons") @authorize def season_list(): sql, args = db.load_season() - results = db. query_db(sql, args, cls=models.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') - ] + "seasons": results, + "columns": [ + ("code", "#"), + ("game", "Game"), + ("description", "Season Description"), + ("start", "Started At"), + ("end", "Ended At"), + ], } - return render_template('seasons.html', model=model) + return render_template("seasons.html", model=model) -@app.route('/seasons/new', methods=['GET']) +@app.route("/seasons/new", methods=["GET"]) @authorize def season_new(): - return render_template('editseason.html', model={}) + return render_template("editseason.html", model={}) -@app.route('/seasons//edit') +@app.route("/seasons//edit") @authorize def season_edit(id: int): sql, args = db.load_season(id) loaded = db.query_db(sql, args, one=True, cls=models.Season) - return render_template('editseason.html', model=loaded) + return render_template("editseason.html", model=loaded) -@app.route('/seasons/save', methods=['POST']) +@app.route("/seasons/save", methods=["POST"]) @authorize def season_save(): try: season = models.Season.from_form(request.form) except AttributeError as err: print(err) - return render_template('editseason.html', model={}) + return render_template("editseason.html", model={}) sql, args = db.save_season_query(season) res = db.update_db(sql, args) - return redirect('/seasons') + return redirect("/seasons") -@app.route('/seasons/', methods=['GET']) +@app.route("/seasons/", methods=["GET"]) @authorize def season_overview(season_id: int): sql, args = db.load_season(season_id) db_season = db.query_db(sql, args, one=True, cls=models.Season) infos = { - 'Number': db_season.code, - 'Game': db_season.game, - 'Start Date': db_season.start, - 'End Date': db_season.end if db_season.end else 'Ongoing' + "Number": db_season.code, + "Game": db_season.game, + "Start Date": db_season.start, + "End Date": db_season.end if db_season.end else "Ongoing", } - model = { - 'title': f'{db_season.code} {db_season.game}', - 'season_info': infos - } - return render_template('seasonoverview.html', model=model) + model = {"title": f"{db_season.code} {db_season.game}", "season_info": infos} + return render_template("seasonoverview.html", model=model) -@app.route('/seasons//episodes', methods=['GET']) +@app.route("/seasons//episodes", methods=["GET"]) @authorize def episode_list(season_id: int): sql, args = db.load_season(season_id) db_season = db.query_db(sql, args, one=True, cls=models.Season) - model = { - 'season_id': season_id, - 'season_code': db_season.code - } - return render_template('episodelist.html', model=model) + model = {"season_id": season_id, "season_code": db_season.code} + return render_template("episodelist.html", model=model) -@app.route('/seasons//new', methods=['GET']) +@app.route("/seasons//new", methods=["GET"]) @authorize def episode_new(season_id: int): model = models.GenericFormModel( - page_title='New Episode', - form_title='Create New Episode', - post_url='/episodes/save') + page_title="New Episode", + form_title="Create New Episode", + post_url="/episodes/save", + ) form = forms.EpisodeForm(request.form) form.season_id.data = season_id - return render_template('generic_form.html', model=model, form=form) + return render_template("generic_form.html", model=model, form=form) -@app.route('/episodes/save', methods=['POST']) +@app.route("/episodes/save", methods=["POST"]) @authorize def episode_save(): form = forms.EpisodeForm(request.form) val = form.validate() - return render_template('editepisode.html', form=form) + return render_template("editepisode.html", form=form) -@app.route('/players/new') +@app.route("/players/new") @authorize def player_new(): form = forms.PlayerForm() model = models.GenericFormModel( - page_title='Players', - form_title='Create a new Player', - post_url='/players/edit/null' + page_title="Players", + form_title="Create a new Player", + post_url="/players/edit/null", ) - return render_template('generic_form.html', model=model, form=form) + return render_template("generic_form.html", model=model, form=form) -@app.route('/players/edit/', methods=['GET', 'POST']) +@app.route("/players/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'/players/edit/{player_id}' + page_title="Players", + form_title=f"Edit Player", + post_url=f"/players/edit/{player_id}", ) # Edit Existing Player - if request.method == 'GET': + if request.method == "GET": sql, args = db.load_players(player_id) player = db.query_db(sql, args, one=True, cls=models.Player) @@ -217,7 +209,7 @@ def player_edit(player_id: int): 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) + return render_template("generic_form.html", model=model, form=form) # Save POSTed data else: @@ -225,45 +217,43 @@ def player_edit(player_id: int): if form.validate_on_submit(): player = models.Player.from_form(form) res = db.save_player(player) - return redirect('/players') + return redirect("/players") - model.form_title = 'Incorrect Data' - return render_template('generic_form.html', model=model, form=form) + model.form_title = "Incorrect Data" + return render_template("generic_form.html", model=model, form=form) -@app.route('/players') +@app.route("/players") @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')] + "player_list": players, + "columns": [ + ("id", "ID"), + ("name", "Player Name"), + ("alias", "Alias"), + ("hex_id", "Hex ID"), + ], } - return render_template('players.html', model=model) + return render_template("players.html", model=model) -@app.route('/drinks') +@app.route("/drinks") @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')] + "drinks": drinks, + "columns": [("id", "ID"), ("name", "Drink Name"), ("vol", "Alcohol %")], + "controls": [("edit", "Edit")], } - return render_template('drinks.html', model=model) + return render_template("drinks.html", model=model) -@app.route('/drinks//edit', methods=['GET']) +@app.route("/drinks//edit", methods=["GET"]) @authorize def drink_edit(drink_id: int): sql, args = db.load_drinks(drink_id) @@ -275,83 +265,77 @@ def drink_edit(drink_id: int): form.vol.data = drink.vol model = models.GenericFormModel( - page_title='Edit Drink', + page_title="Edit Drink", form_title=f'Edit Drink "{drink.name}"', - post_url='/drinks/save' + post_url="/drinks/save", ) - return render_template('generic_form.html', model=model, form=form) + return render_template("generic_form.html", model=model, form=form) -@app.route('/drinks/new', methods=['GET']) +@app.route("/drinks/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' + 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) + return render_template("generic_form.html", model=model, form=form) -@app.route('/drinks/save', methods=['POST']) +@app.route("/drinks/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('/drinks') + return redirect("/drinks") model = models.GenericFormModel( - page_title='Drinks', - form_title='Edit Drink', - post_url='/drinks/save' + page_title="Drinks", form_title="Edit Drink", post_url="/drinks/save" ) - return render_template('generic_form.html', model=model, form=form) + return render_template("generic_form.html", model=model, form=form) -@app.route('/enemies') +@app.route("/enemies") @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) + model = {"enemies": enemies} + return render_template("enemies.html", model=model) -@app.route('/enemies/new', methods=['GET']) +@app.route("/enemies/new", methods=["GET"]) @authorize def enemy_new(preselect_season=None): - sql, args = db.load_season() - seasons = db.query_db(sql, args, cls=models.Season) - seasons = sorted(seasons, key=lambda s: s.code) - 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'/enemies/edit/null' + page_title="Enemies", + form_title="Create a new Enemy", + post_url=f"/enemies/edit/null", ) - return render_template('generic_form.html', model=model, form=form) + return render_template("generic_form.html", model=model, form=form) -@app.route('/enemies/edit/', methods=['GET', 'POST']) +@app.route("/enemies/edit/", methods=["GET", "POST"]) @authorize def enemy_edit(enemy_id: int): model = models.GenericFormModel( - page_title='Enemies', - form_title='Edit Enemy', - post_url=f'/enemies/edit/{enemy_id}' + page_title="Enemies", + form_title="Edit Enemy", + post_url=f"/enemies/edit/{enemy_id}", ) - if request.method == 'GET': + if request.method == "GET": sql, args = db.load_season() seasons = db.query_db(sql, args, cls=models.Season) @@ -365,7 +349,7 @@ def enemy_edit(enemy_id: int): form.enemy_id.data = enemy_id model.form_title = f'Edit Enemy "{enemy.name}"' - return render_template('generic_form.html', model=model, form=form) + return render_template("generic_form.html", model=model, form=form) else: form = forms.EnemyForm() if form.validate_on_submit(): @@ -376,11 +360,11 @@ def enemy_edit(enemy_id: int): if form.submit_continue_button.data: form.name.data = None return enemy_new(preselect_season=enemy.season_id) - return redirect('/enemies') + return redirect("/enemies") - model.form_title = 'Incorrect Data' - return render_template('generic_form.html', model=model, form=form) + model.form_title = "Incorrect Data" + return render_template("generic_form.html", model=model, form=form) -if __name__ == '__main__': +if __name__ == "__main__": app.run() diff --git a/choices.py b/choices.py index 364e5b2..818aab8 100644 --- a/choices.py +++ b/choices.py @@ -8,8 +8,8 @@ def season_choices(): """ sql, args = db.load_season() seasons = db.query_db(sql, args, cls=models.Season) - choices = [(s.id, f'{s.code}: {s.game}') for s in seasons] - choices.insert(0, (-1, 'No Season')) + choices = [(s.id, f"{s.code}: {s.game}") for s in seasons] + choices.insert(0, (-1, "No Season")) return choices @@ -18,6 +18,7 @@ class SeasonChoicesIterable: This is used to declare choices for WTForms SelectFields at class definition time. Be aware that this uses an undocumented WTForms feature and is not guaranteed to work. """ + def __iter__(self): for choice in season_choices(): yield choice diff --git a/config.py b/config.py new file mode 100644 index 0000000..aca523f --- /dev/null +++ b/config.py @@ -0,0 +1,11 @@ +import json +import os + + +def roles(): + with open("roles.json", "r") as f: + return json.load(f) + + +class Config: + SECRET_KEY = os.environ.get("ES_SECRET_KEY") diff --git a/const.py b/const.py index 55ae27c..415f5f9 100644 --- a/const.py +++ b/const.py @@ -2,8 +2,8 @@ import os BASE_PATH = os.path.dirname(os.path.realpath(__file__)) -DATABASE_NAME = 'es_debug.db' +DATABASE_NAME = "es_debug.db" DATABASE_PATH = os.path.join(BASE_PATH, DATABASE_NAME) -LOG_NAME = 'estus-shots.log' -LOG_PATH = os.path.join(BASE_PATH, LOG_NAME) \ No newline at end of file +LOG_NAME = "estus-shots.log" +LOG_PATH = os.path.join(BASE_PATH, LOG_NAME) diff --git a/db.py b/db.py index b70950a..5b8f450 100644 --- a/db.py +++ b/db.py @@ -8,9 +8,9 @@ import const def connect_db(): """Create a new sqlite3 connection and register it in 'g._database'""" - db = getattr(g, '_database', None) + db = getattr(g, "_database", None) if db is None: - log.info(f'Connecting {const.DATABASE_NAME}') + log.info(f"Connecting {const.DATABASE_NAME}") db = g._database = sqlite3.connect(const.DATABASE_PATH) db.row_factory = sqlite3.Row @@ -19,7 +19,7 @@ def connect_db(): def query_db(query, args=(), one=False, cls=None): """Runs an SQL query on an new database connection, returning the fetched rv""" - log.info(f'Running query ({query}) with arguments ({args})') + log.info(f"Running query ({query}) with arguments ({args})") cur = connect_db().execute(query, args) rv = cur.fetchall() cur.close() @@ -45,25 +45,25 @@ def update_db(query, args=()): def init_db(): """Initialize the database from the 'schema.sql' script file""" - file_name = 'schema.sql' + file_name = "schema.sql" print(f'Creating database from file: "{file_name}"') with connect_db() as conn: - with open(file_name, 'r') as f: + with open(file_name, "r") as f: try: conn.cursor().executescript(f.read()) except sqlite3.OperationalError as err: - log.error(f'Cannot create database: {err}') + log.error(f"Cannot create database: {err}") conn.commit() def save_player_query(player): if not player.id: - sql = 'insert into player values (?, ?, ?, ?, ?)' + sql = "insert into player values (?, ?, ?, ?, ?)" args = (None, player.real_name, player.alias, player.hex_id, player.anon) else: - sql = 'update player ' \ - 'set real_name=?, alias=?, hex_id=?, anon=? ' \ - 'where id==?' + sql = ( + "update player " "set real_name=?, alias=?, hex_id=?, anon=? " "where id==?" + ) args = (player.real_name, player.alias, player.hex_id, player.anon, player.id) return sql, args @@ -74,33 +74,31 @@ def save_player(player): def load_players(id=None): - sql = 'select * from player' + sql = "select * from player" args = () if id: - sql += ' where player.id = ?' - args = (id, ) - sql += ' order by player.id' + sql += " where player.id = ?" + args = (id,) + sql += " order by player.id" return sql, args def load_drinks(id=None): - sql = 'select * from drink' + sql = "select * from drink" args = () if id: - sql += ' where drink.id = ?' - args = (id, ) - sql += ' order by drink.id' + sql += " where drink.id = ?" + args = (id,) + sql += " order by drink.id" return sql, args def save_drink_query(drink): if not drink.id: - sql = 'insert into drink values (?, ?, ?)' + sql = "insert into drink values (?, ?, ?)" args = (None, drink.name, drink.vol) else: - sql = 'update drink ' \ - 'set name=?, vol=? ' \ - 'where id==?' + sql = "update drink " "set name=?, vol=? " "where id==?" args = (drink.name, drink.vol, drink.id) return sql, args @@ -111,54 +109,58 @@ def save_drink(drink): def load_enemies(id=None): - sql = 'select * from enemy' + sql = "select * from enemy" args = () if id: - sql += ' where enemy.id = ?' - args = (id, ) - sql += ' order by enemy.id' + sql += " where enemy.id = ?" + args = (id,) + sql += " order by enemy.id" return sql, args def save_enemy(enemy: models.Enemy): if not enemy.id: - sql = 'insert into enemy values (?, ?, ?, ?)' + sql = "insert into enemy values (?, ?, ?, ?)" args = (None, enemy.name, enemy.boss, enemy.season_id) else: - sql = 'update enemy ' \ - 'set name=?, boss=?, season_id=? ' \ - 'where id==?' + sql = "update enemy " "set name=?, boss=?, season_id=? " "where id==?" args = (enemy.name, enemy.boss, enemy.season_id, enemy.id) return sql, args def save_season_query(season: models.Season): if not season.id: - sql = 'insert into season values (?, ?, ?, ?, ?, ?)' - args = (None, - season.game, - season.description, - season.start, - season.end, - season.code) + sql = "insert into season values (?, ?, ?, ?, ?, ?)" + args = ( + None, + season.game, + season.description, + season.start, + season.end, + season.code, + ) else: - sql = 'update season ' \ - 'set game=?, description=?, start=?, end=?, code=? ' \ - 'where id==?' - args = (season.game, - season.description, - season.start, - season.end, - season.code, - season.id) + sql = ( + "update season " + "set game=?, description=?, start=?, end=?, code=? " + "where id==?" + ) + args = ( + season.game, + season.description, + season.start, + season.end, + season.code, + season.id, + ) return sql, args def load_season(id=None): - sql = 'select * from season' + sql = "select * from season" args = () if id: - sql += ' where season.id = ?' - args = (id, ) - sql += ' order by season.start' + sql += " where season.id = ?" + args = (id,) + sql += " order by season.start" return sql, args diff --git a/forms.py b/forms.py index e9177d8..c1be2de 100644 --- a/forms.py +++ b/forms.py @@ -1,42 +1,53 @@ from flask_wtf import FlaskForm -from wtforms import DateField, TimeField, StringField, SubmitField, BooleanField, \ - DecimalField, SelectField +from wtforms import ( + DateField, + TimeField, + StringField, + SubmitField, + BooleanField, + DecimalField, + SelectField, +) from wtforms.validators import DataRequired import choices + class EpisodeForm(FlaskForm): - season_id = StringField('Season ID', render_kw={'readonly': True}) - episode_id = StringField('Episode ID', render_kw={'readonly': True}) - code = StringField('Episode Code', validators=[DataRequired()]) - title = StringField('Title', validators=[DataRequired()]) - description = StringField('Description', validators=[]) - date = DateField('Episode Date', format='%Y-%m-%d', validators=[DataRequired()]) - start = TimeField('Start Time', format='%H:%M', validators=[DataRequired()]) - end = TimeField('End Time', format='%H:%M', validators=[DataRequired()]) - submit_button = SubmitField('Submit') + season_id = StringField("Season ID", render_kw={"readonly": True}) + episode_id = StringField("Episode ID", render_kw={"readonly": True}) + code = StringField("Episode Code", validators=[DataRequired()]) + title = StringField("Title", validators=[DataRequired()]) + description = StringField("Description", validators=[]) + date = DateField("Episode Date", format="%Y-%m-%d", validators=[DataRequired()]) + start = TimeField("Start Time", format="%H:%M", validators=[DataRequired()]) + end = TimeField("End Time", format="%H:%M", validators=[DataRequired()]) + submit_button = SubmitField("Submit") class PlayerForm(FlaskForm): - player_id = StringField('Player ID', render_kw={'readonly': True}) - real_name = StringField('Real Name') - alias = StringField('Player Alias', validators=[DataRequired()]) - hex_id = StringField('Hex ID') - anonymize = BooleanField('Anonymize (Show only player alias)') - submit_button = SubmitField('Submit') + player_id = StringField("Player ID", render_kw={"readonly": True}) + real_name = StringField("Real Name") + alias = StringField("Player Alias", validators=[DataRequired()]) + hex_id = StringField("Hex ID") + anonymize = BooleanField("Anonymize (Show only player alias)") + submit_button = SubmitField("Submit") class DrinkForm(FlaskForm): - drink_id = StringField('Drink ID', render_kw={'readonly': True}) - name = StringField('Name', validators=[DataRequired()]) - vol = DecimalField('Alcohol %', validators=[DataRequired()]) - submit_button = SubmitField('Submit') + drink_id = StringField("Drink ID", render_kw={"readonly": True}) + name = StringField("Name", validators=[DataRequired()]) + vol = DecimalField("Alcohol %", validators=[DataRequired()]) + submit_button = SubmitField("Submit") class EnemyForm(FlaskForm): - enemy_id = StringField('Enemy ID', render_kw={'readonly': True}) - season_id = SelectField('Season', choices=choices.SeasonChoicesIterable(), coerce=int) - name = StringField('Name', validators=[DataRequired()]) - is_boss = BooleanField('Is Boss') - submit_button = SubmitField('Submit') - submit_continue_button = SubmitField('Submit and Continue') + + enemy_id = StringField("Enemy ID", render_kw={"readonly": True}) + season_id = SelectField( + "Season", choices=choices.SeasonChoicesIterable(), coerce=int + ) + name = StringField("Name", validators=[DataRequired()]) + is_boss = BooleanField("Is Boss") + submit_button = SubmitField("Submit") + submit_continue_button = SubmitField("Submit and Continue") diff --git a/models.py b/models.py index 6ad4ed9..bb9aa10 100644 --- a/models.py +++ b/models.py @@ -80,32 +80,32 @@ class Season: @classmethod def from_form(cls, form): - id = form.get('id', None) + id = form.get("id", None) id = int(id) if id else None - game = form.get('game', None) + game = form.get("game", None) if not game: - raise AttributeError(INVALID_STR.format('game')) + raise AttributeError(INVALID_STR.format("game")) game = str(game) - description = form.get('description', None) + description = form.get("description", None) - start = form.get('start', None) + start = form.get("start", None) try: start = datetime.date.fromisoformat(start) except Exception: - raise AttributeError(INVALID_STR.format('start')) + raise AttributeError(INVALID_STR.format("start")) - end = form.get('end', None) + end = form.get("end", None) if end: try: end = datetime.date.fromisoformat(end) except Exception: - raise AttributeError(INVALID_STR.format('end')) + raise AttributeError(INVALID_STR.format("end")) - code = form.get('code', None) + code = form.get("code", None) if not code: - raise AttributeError(INVALID_STR.format('code')) + raise AttributeError(INVALID_STR.format("code")) self = cls(id, game, description, start, end, code) return self diff --git a/roles.json b/roles.json new file mode 100644 index 0000000..372eb9a --- /dev/null +++ b/roles.json @@ -0,0 +1,4 @@ +{ + "": "editor", + "