from flask import render_template, flash, redirect, url_for, request, abort, send_file from flask_login import login_user, logout_user, current_user, login_required from sqlalchemy import and_ from app import app, db from app.models import Player, Game, Role, GamePlayer, Objective, ObjectiveMinimalEncoder, LocationEncoder from app.forms import LoginForm, RegistrationForm, CreateGameForm, ObjectiveForm, PlayerAddForm, PlayerCreateForm, PlayerUpdateForm import json import qrcode from io import BytesIO @app.route('/') @app.route('/index') @login_required def index(): return render_template("index.html", title='Home') @app.route('/login', methods=['GET', 'POST']) def login(): if current_user.is_authenticated: return redirect(url_for('index')) form = LoginForm() if form.validate_on_submit(): player = Player.query.filter_by(name=form.username.data).first() if player is None or not player.check_password(form.password.data): flash('Invalid username or password') return redirect(url_for('login')) login_user(player, remember=form.remember_me.data) return redirect(url_for('index')) return render_template('login.html', title='Sign In', form=form) @app.route('/logout') @login_required def logout(): logout_user() return redirect(url_for('index')) @app.route('/register', methods=['GET', 'POST']) def register(): if current_user.is_authenticated: return redirect(url_for('index')) form = RegistrationForm() if form.validate_on_submit(): player = Player(name=form.username.data) player.set_password(form.password.data) player.set_auth_hash() db.session.add(player) db.session.commit() flash('Congratulations, you are now a registered user!') return redirect(url_for('login')) return render_template('register.html', title='Register', form=form) @app.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'])) #check if this works, otherwise use 'owner' db.session.add(game) db.session.commit() flash(f"'{game.name}' had been created!") return redirect(url_for('game_dashboard', game_name=game.name)) return render_template('create_game.html', title='Create Game', form=form) @app.route('/game//dashboard') @login_required def game_dashboard(game_name): #game = Game.query.filter(Game.game_players.any(and_(GamePlayer.player.has(Player.name == current_user.name), GamePlayer.role == 'owner'))).first_or_404() game = Game.query.filter_by(name = game_name).first_or_404() if not is_game_owner(game): abort(403) return render_template('game_dashboard.html', title = 'Game Dashboard', game=game, json=json, objective_encoder=ObjectiveMinimalEncoder, location_encoder=LocationEncoder) @app.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 is_game_owner(game): 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('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('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) @app.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 is_game_owner(game): 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('game_dashboard', game_name=game.name)) @app.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 is_game_owner(game): 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('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) @app.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 is_player_game_owner(player): abort(403) img = generate_qr_code(url_for('objective', objective_hash=objective.hash, _external=True)) return serve_pil_image(img) def is_game_owner(game): return current_user in [gameplayer.player for gameplayer in game.game_players if gameplayer.role == Role.owner] def is_player_game_owner(player): return current_user in [gameplayer.player for gameplayer in [game for game in player.games ].game_players if game_player.role == Role.owner] def is_objective_owner(objective): return current_user in [gameplayer.player for gameplayer in objective.game.game_players if gameplayer.role == Role.owner] @app.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 is_game_owner(game): 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('game_dashboard', game_name=game.name)) return render_template('objective.html', title=f'Add Objective for {game_name}', form=form, objective=objective, owner=True) @app.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 is_objective_owner(objective): abort(403) if is_objective_owner(objective): db.session.delete(objective) db.session.commit() return redirect(url_for('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') @app.route('/objective//qrcode.png') @login_required def objective_qrcode(objective_hash): objective = Objective.query.filter_by(hash = objective_hash).first_or_404() if not is_objective_owner(objective): abort(403) img = generate_qr_code(url_for('objective', objective_hash=objective.hash, _external=True)) return serve_pil_image(img) @app.route('/objective/', methods=['GET', 'POST']) @login_required def objective(objective_hash): objective = Objective.query.filter_by(hash = objective_hash).first_or_404() owner = is_objective_owner(objective) 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('game_dashboard', game_name=objective.game.name)) return render_template('objective.html', title='Objective view', objective=objective, owner=owner, form=form, qrcode=qrcode)