From 4d2f85d2880e7c3274f86b68dafb6c11036b0d69 Mon Sep 17 00:00:00 2001 From: Burathar Date: Wed, 29 Jul 2020 16:24:36 +0200 Subject: [PATCH] move player routes to routes_player.py --- app/game_settings/routes.py | 4 +- app/main/__init__.py | 2 +- app/main/routes.py | 155 ++++-------------------------------- app/main/routes_player.py | 116 +++++++++++++++++++++++++++ app/utils.py | 25 +++++- 5 files changed, 159 insertions(+), 143 deletions(-) create mode 100644 app/main/routes_player.py diff --git a/app/game_settings/routes.py b/app/game_settings/routes.py index bf8b549..d5ae243 100644 --- a/app/game_settings/routes.py +++ b/app/game_settings/routes.py @@ -22,7 +22,7 @@ def create_game(): return redirect(url_for('main.game_dashboard', game_name=game.name)) else: flash_errors(form) - return render_template('game_settings.create_game.html', title='Create Game', form=form) + return render_template('game_settings/create_game.html', title='Create Game', form=form) @bp.route('/game//change_settings', methods=['GET', 'POST']) @login_required @@ -56,7 +56,7 @@ def change_game_settings(game_name): return redirect(url_for('main.game_dashboard', game_name=game.name)) else: flash_errors(form) - return render_template('game_settings.edit_game.html', title='Chage Game Settings', form=form, game=game) + return render_template('game_settings/edit_game.html', title='Chage Game Settings', form=form, game=game) @bp.route('/game//delete') @login_required diff --git a/app/main/__init__.py b/app/main/__init__.py index 0e73c07..baecfa2 100644 --- a/app/main/__init__.py +++ b/app/main/__init__.py @@ -2,4 +2,4 @@ from flask import Blueprint bp = Blueprint('main', __name__) -from app.main import routes, routes_objective \ No newline at end of file +from app.main import routes, routes_objective, routes_player \ No newline at end of file diff --git a/app/main/routes.py b/app/main/routes.py index 9b1acb2..df5507a 100644 --- a/app/main/routes.py +++ b/app/main/routes.py @@ -1,21 +1,13 @@ -import fnmatch import json -from os import listdir -from pathlib import Path from datetime import datetime, timedelta - -from flask import render_template, flash, redirect, url_for, request, abort, send_file, current_app +from flask import render_template, redirect, url_for, request, abort, send_file, current_app from flask_login import current_user, login_required -from werkzeug.utils import secure_filename from werkzeug.security import safe_join -from sqlalchemy import and_ - from app import db from app.main import bp -from app.utils import get_game_if_owner -from app.models import User, Game, Role, GamePlayer, ObjectiveMinimalEncoder,\ - LocationEncoder, PlayerCaughtPlayer, Review, Location -from app.main.forms import PlayerAddForm, UserCreateForm, PlayerUpdateForm, CatchBunnyForm +from app.utils import get_game_if_owner, get_caught_bunny_photo_directory, get_bunny_photo_filename +from app.models import User, Game, Role, GamePlayer, ObjectiveMinimalEncoder, LocationEncoder, \ + PlayerCaughtPlayer, Review, Location @bp.before_app_request def before_request(): @@ -46,60 +38,14 @@ def game_dashboard(game_name): if role == Role.hunter: hunter_delay = current_app.config['HUNTER_LOCATION_DELAY'] return render_template('game_hunter_dashboard.html', title='Game Dashboard', game=game, - hunter_delay=hunter_delay, json=json, location_encoder=LocationEncoder) + hunter_delay=hunter_delay, json=json, + location_encoder=LocationEncoder) if role == Role.none: return render_template('game_hunter_dashboard.html', title='Game Dashboard', game=game, json=json, location_encoder=LocationEncoder) if role is None: abort(403) -@bp.route('/game//catch_bunny', methods=['GET', 'POST']) -@login_required -def catch_bunny(game_name): - game = Game.query.filter_by(name=game_name).first_or_404() - if current_user.role_in_game(game) is not Role.hunter: - flash('Only hunters can catch bunnies!') - abort(403) - if not game.is_active(): - flash("Its not possible to catch a bunny before or after a game, or if the game is not in 'active' mode.") - return redirect(url_for('main.game_dashboard', game_name=game.name)) - - game_bunnies = game.bunnies() - form = CatchBunnyForm() - form.bunny.choices = [(player.user.id, player.user.name) for player in game_bunnies] - - if request.method == 'GET': - parsed_bunny_name = request.args.get('bunny_name', default='', type=str) - if parsed_bunny_name: - bunny = [gameplayer for gameplayer in game_bunnies if gameplayer.user.name == parsed_bunny_name] - if bunny: - # pylint: disable=no-member - form.bunny.default = bunny[0].user.id - form.process() - - if form.validate_on_submit(): - bunny = [gameplayer for gameplayer in game_bunnies if gameplayer.user.id == form.bunny.data] - if not bunny: - flash('Please choose a bunny') - return request.url - bunny = bunny[0] - - pcp = PlayerCaughtPlayer(catching_player=current_user.player_in(game), caught_player=bunny, timestamp=datetime.utcnow()) - save_player_caught_player_photo(form.photo.data, game, pcp) - db.session.add(pcp) - db.session.commit() - flash(f"You caught {bunny.user.name}! The submitted photo will be reviewed by a game owner.") - return redirect(url_for('main.game_dashboard', game_name=game.name)) - return render_template('catch_bunny.html', title='Catch Bunny', form=form, game=game) - -def save_player_caught_player_photo(file_storage, game, pcp): - timestamp = pcp.timestamp.strftime('%Y%m%d%H%M%S') - extension = Path(file_storage.filename).suffix - filename = secure_filename(f'{timestamp}_{pcp.catching_player.user.name}_caught_{pcp.caught_player.user.name}{extension}') - path = get_caught_bunny_photo_directory(game) - path.mkdir(parents=True, exist_ok=True) - file_storage.save(path / filename) - @bp.route('/game//caught_bunny_photo', methods=['GET']) @login_required def caught_bunny_photo(game_name): @@ -120,17 +66,6 @@ def caught_bunny_photo(game_name): #TODO: Implement switch between serve self and serve by webserver return send_file(photo_path, conditional=True, as_attachment=False) -def get_bunny_photo_filename(directory, timestamp, hunter_name, bunny_name): - filename = secure_filename(f'{timestamp}_{hunter_name}_caught_{bunny_name}') + '.*' - matches = fnmatch.filter(listdir(directory), filename) - return matches[0] if matches else '' - -def get_caught_bunny_photo_directory(game): - return Path(current_app.root_path).parent / \ - current_app.config['UPLOAD_FOLDER'] / \ - secure_filename(game.name) / \ - current_app.config['PLAYER_CAUGHT_PLAYER_PHOTO_DIR_NAME'] - @bp.route('/game//review') @login_required def review_caught_bunny_photos(game_name): @@ -144,91 +79,33 @@ def review_caught_bunny_photos(game_name): db.session.commit() return render_template('review_caught_bunny_photos.html', game=game) -@bp.route('/game//adduser', methods=['GET', 'POST']) -@bp.route('/game//addplayer', methods=['GET', 'POST']) -@login_required -def add_player(game_name): - game = get_game_if_owner(game_name) - - form_add = PlayerAddForm() - form_add.role.choices = [(role.value, role.name) for role in Role] - form_create = UserCreateForm() - form_create.role.choices = [(role.value, role.name) for role in Role] - - if form_add.submit_add.data and form_add.validate_on_submit(): - user = User.query.filter_by(name=form_add.name.data).first_or_404() - game.players.append(GamePlayer(user=user, role=Role(form_add.role.data))) - db.session.commit() - return redirect(url_for('main.game_dashboard', game_name=game.name)) - - if form_create.submit_create.data and form_create.validate_on_submit(): - user = User(name=form_create.name.data) - user.set_auth_hash() - game.players.append(GamePlayer(user=user, role=Role(form_create.role.data))) - db.session.commit() - return redirect(url_for('main.game_dashboard', game_name=game.name)) - - return render_template('add_player.html', title=f'Add User for {game_name}', - form_add=form_add, form_create=form_create, game=game) - -@bp.route('/game//removeuser/') -@bp.route('/game//removeplayer/') -@login_required -def remove_player(game_name, username): - game = get_game_if_owner(game_name) - user = User.query.filter(and_(User.name == username, User.games.contains(game))).first_or_404() - game.users.remove(user) - if not user.last_login: - db.session.delete(user) - db.session.commit() - return redirect(url_for('main.game_dashboard', game_name=game.name)) - -@bp.route('/game//user/', methods=['GET', 'POST']) -@bp.route('/game//player/', methods=['GET', 'POST']) -@login_required -def game_player(game_name, username): - game = get_game_if_owner(game_name) - user = User.query.filter((User.name == username) & (User.games.contains(game))).first_or_404() - player = user.player_in(game) - - # pylint: disable=no-member - form = PlayerUpdateForm(role=player.role.name) - form.role.choices = [(role.value, role.name) for role in Role] - if request.method == 'GET': - form.role.default = player.role.value - form.process() - - if form.validate_on_submit(): - player.role = Role(form.role.data) - print(form.role.data) - db.session.commit() - return redirect(url_for('main.game_dashboard', game_name=game.name)) - return render_template('game_player.html', title=f'{user.name} in {game_name}', player=player, form=form, json=json, location_encoder=LocationEncoder) - - @bp.route('/user//send_location', methods=['POST']) @login_required def send_location(username): user = User.query.filter_by(name=username).first_or_404() last_location = user.last_location() - + latitude = request.form.get('lat', default=None, type=float) longitude = request.form.get('long', default=None, type=float) if latitude is None or longitude is None: return '', 400 - - # Check if previous two locations are exactly the same, if so, only update timestamp of last location + + # Check if previous two locations are exactly the same, + # if so, only update timestamp of last location if last_location: if datetime.utcnow() - last_location.timestamp < timedelta(minutes=1): return '', 204 - if latitude == last_location.latitude and longitude == last_location.longitude and len(user.locations) >= 2: + if (latitude == last_location.latitude and + longitude == last_location.longitude and + len(user.locations) >= 2): before_last_location = user.locations[-2] if before_last_location: - if latitude == before_last_location.latitude and longitude == before_last_location.longitude: + if (latitude == before_last_location.latitude and + longitude == before_last_location.longitude): last_location.timestamp = datetime.utcnow() db.session.commit() return '', 204 - + user.locations.append(Location(longitude=longitude, latitude=latitude)) db.session.commit() return '', 204 diff --git a/app/main/routes_player.py b/app/main/routes_player.py new file mode 100644 index 0000000..3cf4ca6 --- /dev/null +++ b/app/main/routes_player.py @@ -0,0 +1,116 @@ +import json +from datetime import datetime +from flask import render_template, flash, redirect, url_for, request, abort +from flask_login import current_user, login_required +from sqlalchemy import and_ +from app import db +from app.main import bp +from app.utils import get_game_if_owner, save_player_caught_player_photo +from app.models import User, Game, Role, GamePlayer, PlayerCaughtPlayer, LocationEncoder +from app.main.forms import PlayerAddForm, UserCreateForm, CatchBunnyForm, PlayerUpdateForm + + +@bp.route('/game//adduser', methods=['GET', 'POST']) +@bp.route('/game//addplayer', methods=['GET', 'POST']) +@login_required +def add_player(game_name): + game = get_game_if_owner(game_name) + + form_add = PlayerAddForm() + form_add.role.choices = [(role.value, role.name) for role in Role] + form_create = UserCreateForm() + form_create.role.choices = [(role.value, role.name) for role in Role] + + if form_add.submit_add.data and form_add.validate_on_submit(): + user = User.query.filter_by(name=form_add.name.data).first_or_404() + game.players.append(GamePlayer(user=user, role=Role(form_add.role.data))) + db.session.commit() + return redirect(url_for('main.game_dashboard', game_name=game.name)) + + if form_create.submit_create.data and form_create.validate_on_submit(): + user = User(name=form_create.name.data) + user.set_auth_hash() + game.players.append(GamePlayer(user=user, role=Role(form_create.role.data))) + db.session.commit() + return redirect(url_for('main.game_dashboard', game_name=game.name)) + + return render_template('add_player.html', title=f'Add User for {game_name}', + form_add=form_add, form_create=form_create, game=game) + +@bp.route('/game//removeuser/') +@bp.route('/game//removeplayer/') +@login_required +def remove_player(game_name, username): + game = get_game_if_owner(game_name) + user = User.query.filter(and_(User.name == username, User.games.contains(game))).first_or_404() + game.users.remove(user) + if not user.last_login: + db.session.delete(user) + db.session.commit() + return redirect(url_for('main.game_dashboard', game_name=game.name)) + +@bp.route('/game//user/', methods=['GET', 'POST']) +@bp.route('/game//player/', methods=['GET', 'POST']) +@login_required +def game_player(game_name, username): + game = get_game_if_owner(game_name) + user = User.query.filter((User.name == username) & (User.games.contains(game))).first_or_404() + player = user.player_in(game) + + # pylint: disable=no-member + form = PlayerUpdateForm(role=player.role.name) + form.role.choices = [(role.value, role.name) for role in Role] + if request.method == 'GET': + form.role.default = player.role.value + form.process() + + if form.validate_on_submit(): + player.role = Role(form.role.data) + print(form.role.data) + db.session.commit() + return redirect(url_for('main.game_dashboard', game_name=game.name)) + return render_template('game_player.html', title=f'{user.name} in {game_name}', + player=player, form=form, json=json, location_encoder=LocationEncoder) + +@bp.route('/game//catch_bunny', methods=['GET', 'POST']) +@login_required +def catch_bunny(game_name): + game = Game.query.filter_by(name=game_name).first_or_404() + if current_user.role_in_game(game) is not Role.hunter: + flash('Only hunters can catch bunnies!') + abort(403) + if not game.is_active(): + flash("Its not possible to catch a bunny before or after a game, " + "or if the game is not in 'active' mode.") + return redirect(url_for('main.game_dashboard', game_name=game.name)) + + game_bunnies = game.bunnies() + form = CatchBunnyForm() + form.bunny.choices = [(player.user.id, player.user.name) for player in game_bunnies] + + if request.method == 'GET': + parsed_bunny_name = request.args.get('bunny_name', default='', type=str) + if parsed_bunny_name: + bunny = [gameplayer for gameplayer in game_bunnies + if gameplayer.user.name == parsed_bunny_name] + if bunny: + # pylint: disable=no-member + form.bunny.default = bunny[0].user.id + form.process() + + if form.validate_on_submit(): + bunny = [gameplayer for gameplayer in game_bunnies if gameplayer.user.id == form.bunny.data] + if not bunny: + flash('Please choose a bunny') + return request.url + bunny = bunny[0] + + pcp = PlayerCaughtPlayer(catching_player=current_user.player_in(game), + caught_player=bunny, timestamp=datetime.utcnow()) + save_player_caught_player_photo(form.photo.data, game, pcp) + db.session.add(pcp) + db.session.commit() + flash(f"You caught {bunny.user.name}! " + "The submitted photo will be reviewed by a game owner.") + return redirect(url_for('main.game_dashboard', game_name=game.name)) + return render_template('catch_bunny.html', title='Catch Bunny', form=form, game=game) diff --git a/app/utils.py b/app/utils.py index 4198643..94da5d7 100644 --- a/app/utils.py +++ b/app/utils.py @@ -1,7 +1,11 @@ +import fnmatch +from os import listdir from io import BytesIO +from pathlib import Path import qrcode -from flask import send_file, flash, abort +from flask import send_file, flash, abort, current_app from flask_login import current_user +from werkzeug.utils import secure_filename from app.models import Game def generate_qr_code(url): @@ -37,3 +41,22 @@ def get_game_if_owner(game_name): if not game.owned_by(current_user): abort(403) return game + +def save_player_caught_player_photo(file_storage, game, pcp): + timestamp = pcp.timestamp.strftime('%Y%m%d%H%M%S') + extension = Path(file_storage.filename).suffix + filename = secure_filename(f'{timestamp}_{pcp.catching_player.user.name}_caught_{pcp.caught_player.user.name}{extension}') + path = get_caught_bunny_photo_directory(game) + path.mkdir(parents=True, exist_ok=True) + file_storage.save(path / filename) + +def get_bunny_photo_filename(directory, timestamp, hunter_name, bunny_name): + filename = secure_filename(f'{timestamp}_{hunter_name}_caught_{bunny_name}') + '.*' + matches = fnmatch.filter(listdir(directory), filename) + return matches[0] if matches else '' + +def get_caught_bunny_photo_directory(game): + return Path(current_app.root_path).parent / \ + current_app.config['UPLOAD_FOLDER'] / \ + secure_filename(game.name) / \ + current_app.config['PLAYER_CAUGHT_PLAYER_PHOTO_DIR_NAME']