import json
import qrcode
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 io import BytesIO
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
@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/<game_name>/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/<game_name>/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/<game_name>/removeplayer/<player_name> ' )
@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/<game_name>/player/<player_name> ' , 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/<auth_hash>/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 ( ' player ' , auth_hash = auth_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/<game_name>/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/<objective_hash>/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/<objective_hash>/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/<objective_hash> ' , 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 )