import json import qrcode from flask import render_template, flash, redirect, url_for, request, abort, send_file from flask_login import current_user, login_required from sqlalchemy import and_ from io import BytesIO from app import db from app.main import bp from app.models import Player, Game, Role, GamePlayer, Objective, ObjectiveMinimalEncoder, LocationEncoder from app.main.forms import CreateGameForm, ObjectiveForm, PlayerAddForm, PlayerCreateForm, PlayerUpdateForm @bp.route('/') @bp.route('/index') @login_required def index(): if len(current_user.games) == 1: return redirect(url_for('main.game_dashboard', game_name=current_user.games[0].name)) return render_template("index.html", title='Home') @bp.route('/create_game', methods=['GET', 'POST']) @login_required def create_game(): print(current_user.is_authenticated) form = CreateGameForm() if form.validate_on_submit(): game = Game(name=form.game_name.data, start_time=form.start_time.data, end_time=form.end_time.data) game.game_players.append(GamePlayer(player=current_user, role=Role['owner'])) db.session.add(game) db.session.commit() flash(f"'{game.name}' had been created!") return redirect(url_for('main.game_dashboard', game_name=game.name)) return render_template('create_game.html', title='Create Game', form=form) @bp.route('/game//dashboard') @login_required def game_dashboard(game_name): game = Game.query.filter_by(name=game_name).first_or_404() role = current_user.get_role_for_game(game) if role == Role.owner: return render_template('game_owner_dashboard.html', title='Game Dashboard', game=game, json=json, objective_encoder=ObjectiveMinimalEncoder, location_encoder=LocationEncoder) if role == Role.bunny: return render_template('game_bunny_dashboard.html', title='Game Dashboard', game=game, json=json, objective_encoder=ObjectiveMinimalEncoder, location_encoder=LocationEncoder) if role == Role.hunter: return render_template('game_hunter_dashboard.html', title='Game Dashboard', game=game, 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//addplayer', methods=['GET', 'POST']) @login_required def add_player(game_name): game = Game.query.filter_by(name=game_name).first_or_404() if not game.owned_by(current_user): abort(403) form_add = PlayerAddForm() form_create = PlayerCreateForm() if form_add.submit_add.data and form_add.validate_on_submit(): player = Player.query.filter_by(form_add.name.data).first_or_404() game.game_players.append(GamePlayer(player=player, role=Role[form_create.role.data])) return redirect(url_for('main.game_dashboard', game_name=game.name)) if form_create.submit_create.data and form_create.validate_on_submit(): player = Player(name=form_create.name.data) player.set_auth_hash() game.game_players.append(GamePlayer(player=player, 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 Player for {game_name}', form_add=form_add, form_create=form_create, game=game) @bp.route('/game//removeplayer/') @login_required def remove_player(game_name, player_name): game = Game.query.filter_by(name=game_name).first_or_404() if not game.owned_by(current_user): abort(403) player = Player.query.filter(and_(Player.name == player_name, Player.games.contains(game))).first_or_404() game.players.remove(player) db.session.commit() return redirect(url_for('main.game_dashboard', game_name=game.name)) @bp.route('/game//player/', methods=['GET', 'POST']) @login_required def game_player(game_name, player_name): game = Game.query.filter_by(name=game_name).first_or_404() if not game.owned_by(current_user): abort(403) player = Player.query.filter(and_(Player.name == player_name, Player.games.contains(game))).first_or_404() gameplayer = [gameplayer for gameplayer in player.player_games if gameplayer.game == game][0] form = PlayerUpdateForm(role=gameplayer.role.name) if form.validate_on_submit(): gameplayer.role = Role[form.role.data] db.session.commit() return redirect(url_for('main.game_dashboard', game_name=game.name)) return render_template('player.html', title=f'{player.name} in {game_name}', game=game, player=player, form=form, json=json, location_encoder=LocationEncoder) @bp.route('/player//qrcode.png') @login_required def player_qrcode(auth_hash): player = Player.query.filter_by(auth_hash=auth_hash).first_or_404() if not current_user.owns_game_played_by(player): abort(403) img = generate_qr_code(url_for('main.player', auth_hash=auth_hash, _external=True)) return serve_pil_image(img) @bp.route('/player/') @login_required def player(auth_hash): player = Player.query.filter_by(auth_hash=auth_hash).first_or_404() return render_template('player.html',title=f'Player: {player.name}', player=player) @bp.route('/game//add_objective', methods=['GET', 'POST']) @login_required def add_objective(game_name): game = Game.query.filter_by(name=game_name).first_or_404() if not game.owned_by(current_user): abort(403) form = ObjectiveForm() objective = Objective(name='', latitude=52.0932, longitude=5.12405) if form.validate_on_submit(): objective = Objective(name=form.objective_name.data, longitude=form.longitude.data, latitude=form.latitude.data) objective.set_hash() game.objectives.append(objective) db.session.commit() flash(f"Objective has been added!") return redirect(url_for('main.game_dashboard', game_name=game.name)) return render_template('objective.html', title=f'Add Objective for {game_name}', form=form, objective=objective, owner=True) @bp.route('/objective//delete', methods=['GET']) @login_required def delete_objective(objective_hash): objective = Objective.query.filter_by(hash=objective_hash).first_or_404() if not objective.owned_by(current_user): abort(403) else: db.session.delete(objective) db.session.commit() return redirect(url_for('main.game_dashboard', game_name=objective.game.name)) def generate_qr_code(url): qr = qrcode.QRCode( version=None, error_correction=qrcode.constants.ERROR_CORRECT_M, box_size=30, border=4, ) qr.add_data(url) qr.make(fit=True) return qr.make_image(fill_color='black', back_color='white') # Source: https://stackoverflow.com/questions/7877282/how-to-send-image-generated-by-pil-to-browser def serve_pil_image(pil_img): img_io = BytesIO() pil_img.save(img_io, 'PNG', quality=70) img_io.seek(0) return send_file(img_io, mimetype='image/png') @bp.route('/objective//qrcode.png') @login_required def objective_qrcode(objective_hash): objective = Objective.query.filter_by(hash=objective_hash).first_or_404() if not objective.owned_by(current_user): abort(403) img = generate_qr_code(url_for('main.objective', objective_hash=objective.hash, _external=True)) return serve_pil_image(img) @bp.route('/objective/', methods=['GET', 'POST']) @login_required def objective(objective_hash): objective = Objective.query.filter_by(hash=objective_hash).first_or_404() owner = objective.owned_by(current_user) qrcode = generate_qr_code(objective) if owner else None form = ObjectiveForm() if form.submit.data and form.validate() and owner: objective.name = form.objective_name.data objective.longitude = form.longitude.data objective.latitude = form.latitude.data db.session.commit() return redirect(url_for('main.game_dashboard', game_name=objective.game.name)) return render_template('objective.html', title='Objective view', objective=objective, owner=owner, form=form, qrcode=qrcode)