Browse Source

split create_game and edit_game. replace game state with hidden and paused in database (issue #34)

testing
Burathar 4 years ago
parent
commit
3f9b3f0650
  1. 75
      app/main/routes.py
  2. 49
      app/models/game.py
  3. 37
      app/static/assets/utils.js
  4. 64
      app/templates/create_game.html
  5. 97
      app/templates/edit_game.html
  6. 8
      app/templates/game_owner_dashboard.html
  7. 10
      app/utils.py

75
app/main/routes.py

@ -12,7 +12,7 @@ from sqlalchemy import and_
from app import db from app import db
from app.main import bp from app.main import bp
from app.utils import generate_qr_code, serve_pil_image, flash_errors from app.utils import generate_qr_code, serve_pil_image, flash_errors, get_game_if_owner
from app.models import User, Game, Role, GamePlayer, GameState, Objective, ObjectiveMinimalEncoder,\ from app.models import User, Game, Role, GamePlayer, GameState, Objective, ObjectiveMinimalEncoder,\
LocationEncoder, PlayerCaughtPlayer, Review, Location LocationEncoder, PlayerCaughtPlayer, Review, Location
from app.main.forms import CreateGameForm, ObjectiveForm, PlayerAddForm, UserCreateForm, \ from app.main.forms import CreateGameForm, ObjectiveForm, PlayerAddForm, UserCreateForm, \
@ -41,7 +41,6 @@ def create_game():
game = Game(name=form.game_name.data, game = Game(name=form.game_name.data,
start_time=form.start_time.data, start_time=form.start_time.data,
end_time=form.end_time.data) end_time=form.end_time.data)
game.update_state()
game.players.append(GamePlayer(user=current_user, role=Role['owner'])) game.players.append(GamePlayer(user=current_user, role=Role['owner']))
db.session.add(game) db.session.add(game)
db.session.commit() db.session.commit()
@ -54,9 +53,7 @@ def create_game():
@bp.route('/game/<game_name>/change_settings', methods=['GET', 'POST']) @bp.route('/game/<game_name>/change_settings', methods=['GET', 'POST'])
@login_required @login_required
def change_game_settings(game_name): def change_game_settings(game_name):
game = Game.query.filter_by(name=game_name).first_or_404() game = get_game_if_owner(game_name)
if not game.owned_by(current_user):
abort(403)
form = CreateGameForm() form = CreateGameForm()
form.old_name = game.name form.old_name = game.name
@ -80,20 +77,17 @@ def change_game_settings(game_name):
game.name = form.game_name.data game.name = form.game_name.data
game.start_time = form.start_time.data game.start_time = form.start_time.data
game.end_time = form.end_time.data game.end_time = form.end_time.data
game.update_state()
db.session.commit() db.session.commit()
flash(f"'{game.name}' had been updated!") flash(f"'{game.name}' had been updated!")
return redirect(url_for('main.game_dashboard', game_name=game.name)) return redirect(url_for('main.game_dashboard', game_name=game.name))
else: else:
flash_errors(form) flash_errors(form)
return render_template('create_game.html', title='Chage Game Settings', form=form) return render_template('edit_game.html', title='Chage Game Settings', form=form, game=game)
@bp.route('/game/<game_name>/delete') @bp.route('/game/<game_name>/delete')
@login_required @login_required
def delete_game(game_name): def delete_game(game_name):
game = Game.query.filter_by(name=game_name).first_or_404() game = get_game_if_owner(game_name)
if not game.owned_by(current_user):
abort(403)
db.session.delete(game) db.session.delete(game)
for user in game.users: for user in game.users:
if not user.last_login: if not user.last_login:
@ -102,6 +96,45 @@ def delete_game(game_name):
flash(f"Game '{game.name}' has been deleted!") flash(f"Game '{game.name}' has been deleted!")
return redirect(url_for('main.index')) return redirect(url_for('main.index'))
@bp.route('/game/<game_name>/unhide')
@bp.route('/game/<game_name>/publish')
@login_required
def publish_game(game_name):
game = get_game_if_owner(game_name)
game.hidden = False
db.session.commit()
flash(f"Game '{game.name}' has been published!")
return redirect(url_for('main.index'))
@bp.route('/game/<game_name>/hide')
@login_required
def hide_game(game_name):
game = get_game_if_owner(game_name)
game.hidden = True
db.session.commit()
flash(f"Game '{game.name}' has been hidden!")
return redirect(url_for('main.index'))
@bp.route('/game/<game_name>/pause')
@login_required
def pause_game(game_name):
game = get_game_if_owner(game_name)
game.paused = True
db.session.commit()
flash(f"Game '{game.name}' has been paused!")
return redirect(url_for('main.index'))
@bp.route('/game/<game_name>/unpause')
@bp.route('/game/<game_name>/resume')
@login_required
def resume_game(game_name):
game = get_game_if_owner(game_name)
game.paused = False
db.session.commit()
flash(f"Game '{game.name}' has been paused!")
return redirect(url_for('main.index'))
@bp.route('/game/<game_name>/dashboard') @bp.route('/game/<game_name>/dashboard')
@login_required @login_required
def game_dashboard(game_name): def game_dashboard(game_name):
@ -109,7 +142,7 @@ def game_dashboard(game_name):
role = current_user.role_in_game(game) role = current_user.role_in_game(game)
if role == Role.owner: if role == Role.owner:
return render_template('game_owner_dashboard.html', title='Game Dashboard', game=game, return render_template('game_owner_dashboard.html', title='Game Dashboard', game=game,
json=json, objective_encoder=ObjectiveMinimalEncoder, json=json, objective_encoder=ObjectiveMinimalEncoder,
location_encoder=LocationEncoder) location_encoder=LocationEncoder)
if role == Role.bunny: if role == Role.bunny:
return render_template('game_bunny_dashboard.html', title='Game Dashboard', game=game, return render_template('game_bunny_dashboard.html', title='Game Dashboard', game=game,
@ -205,9 +238,7 @@ def get_caught_bunny_photo_directory(game):
@bp.route('/game/<game_name>/review') @bp.route('/game/<game_name>/review')
@login_required @login_required
def review_caught_bunny_photos(game_name): def review_caught_bunny_photos(game_name):
game = Game.query.filter_by(name=game_name).first_or_404() game = get_game_if_owner(game_name)
if not game.owned_by(current_user):
abort(403)
pcp_id = request.args.get('pcp_id', default=-1, type=int) pcp_id = request.args.get('pcp_id', default=-1, type=int)
action = request.args.get('action', default='none', type=str).lower() action = request.args.get('action', default='none', type=str).lower()
if pcp_id != -1: if pcp_id != -1:
@ -221,9 +252,7 @@ def review_caught_bunny_photos(game_name):
@bp.route('/game/<game_name>/addplayer', methods=['GET', 'POST']) @bp.route('/game/<game_name>/addplayer', methods=['GET', 'POST'])
@login_required @login_required
def add_player(game_name): def add_player(game_name):
game = Game.query.filter_by(name=game_name).first_or_404() game = get_game_if_owner(game_name)
if not game.owned_by(current_user):
abort(403)
form_add = PlayerAddForm() form_add = PlayerAddForm()
form_add.role.choices = [(role.value, role.name) for role in Role] form_add.role.choices = [(role.value, role.name) for role in Role]
@ -250,9 +279,7 @@ def add_player(game_name):
@bp.route('/game/<game_name>/removeplayer/<username>') @bp.route('/game/<game_name>/removeplayer/<username>')
@login_required @login_required
def remove_player(game_name, username): def remove_player(game_name, username):
game = Game.query.filter_by(name=game_name).first_or_404() game = get_game_if_owner(game_name)
if not game.owned_by(current_user):
abort(403)
user = User.query.filter(and_(User.name == username, User.games.contains(game))).first_or_404() user = User.query.filter(and_(User.name == username, User.games.contains(game))).first_or_404()
game.users.remove(user) game.users.remove(user)
if not user.last_login: if not user.last_login:
@ -264,9 +291,7 @@ def remove_player(game_name, username):
@bp.route('/game/<game_name>/player/<username>', methods=['GET', 'POST']) @bp.route('/game/<game_name>/player/<username>', methods=['GET', 'POST'])
@login_required @login_required
def game_player(game_name, username): def game_player(game_name, username):
game = Game.query.filter_by(name=game_name).first_or_404() game = get_game_if_owner(game_name)
if not game.owned_by(current_user):
abort(403)
user = User.query.filter((User.name == username) & (User.games.contains(game))).first_or_404() user = User.query.filter((User.name == username) & (User.games.contains(game))).first_or_404()
player = user.player_in(game) player = user.player_in(game)
@ -287,9 +312,7 @@ def game_player(game_name, username):
@bp.route('/game/<game_name>/add_objective', methods=['GET', 'POST']) @bp.route('/game/<game_name>/add_objective', methods=['GET', 'POST'])
@login_required @login_required
def add_objective(game_name): def add_objective(game_name):
game = Game.query.filter_by(name=game_name).first_or_404() game = get_game_if_owner(game_name)
if not game.owned_by(current_user):
abort(403)
form = ObjectiveForm() form = ObjectiveForm()
objective = Objective(name='', latitude=52.0932, longitude=5.12405) objective = Objective(name='', latitude=52.0932, longitude=5.12405)
if form.validate_on_submit(): if form.validate_on_submit():

49
app/models/game.py

@ -10,7 +10,9 @@ class Game(db.Model):
__tablename__ = 'game' __tablename__ = 'game'
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), index=True, unique=True, nullable=False) name = db.Column(db.String(64), index=True, unique=True, nullable=False)
state = db.Column(db.Enum(GameState), server_default=GameState(1).name, nullable=False) #state = db.Column(db.Enum(GameState), server_default=GameState(1).name, nullable=False)
hidden = db.Column(db.Boolean, server_default='True', nullable=False)
paused = db.Column(db.Boolean, server_default='False', nullable=False)
start_time = db.Column(db.DateTime) start_time = db.Column(db.DateTime)
end_time = db.Column(db.DateTime) end_time = db.Column(db.DateTime)
players = db.relationship( players = db.relationship(
@ -68,40 +70,25 @@ class Game(db.Model):
[player.player_caught_players for player in self.players] [player.player_caught_players for player in self.players]
for pcp in pcps if pcp.review == Review.none] for pcp in pcps if pcp.review == Review.none]
def update_state(self): def is_active(self):
''' return self.get_state == GameState.active
Updates state according to game.start_time and game.end_time.
Returns True if state is changed. def get_state(self):
'''
#pylint: disable=no-member
old_state = self.state
now = datetime.utcnow() now = datetime.utcnow()
start = self.start_time.replace(tzinfo=None) or datetime.min start = (self.start_time or datetime.min).replace(tzinfo=None)
if now < start: # Before Game if now < start: # Before Game
if self.state == GameState.hidden: if self.hidden:
return False return GameState.hidden
self.state = GameState.published return GameState.published
end = self.end_time.replace(tzinfo=None) or datetime.max end = (self.end_time or datetime.max).replace(tzinfo=None)
if start < now < end: # During Game if start < now < end: # During Game
if self.state == GameState.interrupted: if self.paused:
return False return GameState.interrupted
self.state = GameState.active return GameState.active
if now > end: # After Game if now > end: # After Game
if self.state == GameState.hidden: if self.hidden:
return False return GameState.hidden
self.state = GameState.finished return GameState.finished
if self.state != old_state:
return True
return False
def is_active(self):
self.update_state()
return self.state == GameState.active
def get_state(self):
self.update_state()
return self.state

37
app/static/assets/utils.js

@ -0,0 +1,37 @@
function createDateTimePicker(datePicker, checkbox, date){
$(datePicker).datetimepicker({
//useCurrent: false, //Important! See issue #1075
locale: 'en-gb',
format: 'DD-MM-YYYY HH:mm',
keepInvalid: true,
sideBySide: true,
defaultDate: null,
timeZone: moment.tz.guess()
});
$(checkbox).change(function() {
updateDateTimePicker(datePicker, checkbox)
});
if (!$(checkbox)[0].checked){
if ($(datePicker)[0].value == ''){
$(datePicker)[0].value = date.format('DD-MM-YYYY HH:mm');
} else if (!$('.alert')[0]) { //Don't convert datetime again after error
$(datePicker)[0].value = moment.utc($(datePicker)[0].value, 'DD-MM-YYYY HH:mm').local().format('DD-MM-YYYY HH:mm');
}
} else {
$(datePicker).data("DateTimePicker").disable();
};
}
function updateDateTimePicker(picker, checkbox){
if ($(checkbox).prop("checked")) {
$(picker).data("DateTimePicker").disable();
}
else {
$(picker).data("DateTimePicker").enable();
if ($(picker)[0].value == ''){
$(picker)[0].value = moment().format('DD-MM-YYYY HH:mm');
}
}
}

64
app/templates/create_game.html

@ -4,6 +4,7 @@
{% block head %} {% block head %}
{{ super() }} {{ super() }}
<link rel="stylesheet" href="{{ url_for('static', filename='assets/bootstrap/bootstrap-datetimepicker.min.css') }}"> <link rel="stylesheet" href="{{ url_for('static', filename='assets/bootstrap/bootstrap-datetimepicker.min.css') }}">
<script src="{{ url_for('static', filename='assets/utils.js') }}"></script>
{% endblock %} {% endblock %}
{% block app_content %} {% block app_content %}
@ -35,11 +36,7 @@
{{ wtf.form_field(form.end_time_disabled, class='form-control') }} {{ wtf.form_field(form.end_time_disabled, class='form-control') }}
</div> </div>
</div> </div>
{% if form.old_name %}
{{ wtf.form_field(form.submit, class='btn btn-primary', value="Update") }}
{% else %}
{{ wtf.form_field(form.submit, class='btn btn-primary') }} {{ wtf.form_field(form.submit, class='btn btn-primary') }}
{% endif %}
</form> </form>
<hr> <hr>
</div> </div>
@ -54,66 +51,19 @@
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/4.17.47/js/bootstrap-datetimepicker.min.js"></script> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/4.17.47/js/bootstrap-datetimepicker.min.js"></script>
<script type="text/javascript"> <script type="text/javascript">
$(function () { $(function () {
// Datetime pickers
$('#timezone')[0].value = moment.tz.guess(); $('#timezone')[0].value = moment.tz.guess();
$('#datetimepicker_start').datetimepicker({ var date = moment()
//useCurrent: false, //Important! See issue #1075 createDateTimePicker('#datetimepicker_start', "#start_time_disabled", date)
locale: 'en-gb', date.add(1, 'hour');
format: 'DD-MM-YYYY HH:mm', createDateTimePicker('#datetimepicker_end', "#end_time_disabled", date)
keepInvalid: true,
sideBySide: true,
defaultDate: null,
timeZone: moment.tz.guess()
});
$('#datetimepicker_end').datetimepicker({
//useCurrent: false, //Important! See issue #1075
locale: 'en-gb',
format: 'DD-MM-YYYY HH:mm',
keepInvalid: true,
sideBySide: true,
defaultDate: null,
timeZone: moment.tz.guess()
});
$("#datetimepicker_start").on("dp.change", function (e) { $("#datetimepicker_start").on("dp.change", function (e) {
$('#datetimepicker_end').data("DateTimePicker").minDate(e.date); $('#datetimepicker_end').data("DateTimePicker").minDate(e.date);
}); });
$("#datetimepicker_end").on("dp.change", function (e) { $("#datetimepicker_end").on("dp.change", function (e) {
$('#datetimepicker_start').data("DateTimePicker").maxDate(e.date); $('#datetimepicker_start').data("DateTimePicker").maxDate(e.date);
}); });
$("#start_time_disabled").change(function() {
updateDateTimePicker('#datetimepicker_start', '#start_time_disabled')
});
$("#end_time_disabled").change(function() {
updateDateTimePicker('#datetimepicker_end', '#end_time_disabled')
});
function updateDateTimePicker(picker, checkbox){
if ($(checkbox).prop("checked")) {
$(picker).data("DateTimePicker").disable();
}
else {
$(picker).data("DateTimePicker").enable();
if ($(picker)[0].value == ''){
$(picker)[0].value = moment().format('DD-MM-YYYY HH:mm');
}
}
}
// On page load
var date = moment()
'{% if not form.start_time_disabled.data and not form.start_time.data %}'
$('#datetimepicker_start')[0].value = date.format('DD-MM-YYYY HH:mm');
date.add(1, 'hour');
'{% endif %}'
'{% if not form.end_time_disabled.data and not form.end_time.data %}'
$('#datetimepicker_end')[0].value = date.format('DD-MM-YYYY HH:mm');
'{% endif %}'
updateDateTimePicker('#datetimepicker_start', '#start_time_disabled');
updateDateTimePicker('#datetimepicker_end', '#end_time_disabled');
'{% if form.start_time.data %}'
$('#datetimepicker_start')[0].value = moment.utc('{{ form.start_time.data }}').local().format('DD-MM-YYYY HH:mm');
'{% endif %}'
'{% if form.end_time.data %}'
$('#datetimepicker_end')[0].value = moment.utc('{{ form.end_time.data }}').local().format('DD-MM-YYYY HH:mm');
'{% endif %}'
}); });
</script> </script>
{% endblock %} {% endblock %}

97
app/templates/edit_game.html

@ -0,0 +1,97 @@
{% extends 'base.html' %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block head %}
{{ super() }}
<link rel="stylesheet" href="{{ url_for('static', filename='assets/bootstrap/bootstrap-datetimepicker.min.css') }}">
<script src="{{ url_for('static', filename='assets/utils.js') }}"></script>
{% endblock %}
{% block app_content %}
<h1>{{ title }}</h1>
<div class="col-md-4 col-sm-6 col-xs-8">
<hr>
<form action="" method="post" class="form" role="form">
{{ form.hidden_tag() }}
{{ form.timezone }}
{{ wtf.form_field(form.game_name, class='form-control') }}
{{ form.start_time.label }}
<div class="form-group row">
<div class="col-sm-7">
{{ form.start_time }}
</div>
<div class="col-sm-5 align-self-center">
{{ wtf.form_field(form.start_time_disabled, class='form-control') }}
</div>
</div>
{{ form.end_time.label }}
<div class="form-group row">
<div class="col-sm-7">
{{ form.end_time }}
</div>
<div class="col-sm-5 align-self-center">
{{ wtf.form_field(form.end_time_disabled, class='form-control') }}
</div>
</div>
{{ wtf.form_field(form.submit, class='btn btn-primary', value="Update") }}
</form>
<hr>
{% if game.hidden %}
<a href="{{ url_for('main.publish_game', game_name=game.name) }}">
<button class="btn btn-success">Publish Game</button>
</a>
{% else %}
<a href="{{ url_for('main.hide_game', game_name=game.name) }}">
<button class="btn btn-success">Hide Game</button>
</a>
{% endif %}
{% if game.paused %}
<a href="{{ url_for('main.resume_game', game_name=game.name) }}">
<button class="btn btn-primary">Resume Game</button>
</a>
{% else %}
<a href="{{ url_for('main.pause_game', game_name=game.name) }}">
<button class="btn btn-primary">Pause Game</button>
</a>
{% endif %}
<button class="btn btn-danger" onclick="deleteGame()">Delete Game</button>
</div>
{% endblock %}
{% block scripts %}
{{ super() }}
<!-- TODO: Scripts downloaden naar repo? -->
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment-with-locales.min.js"></script>
<script type="text/javascript" src="https://momentjs.com/downloads/moment-timezone-with-data.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/4.17.47/js/bootstrap-datetimepicker.min.js"></script>
<script type="text/javascript">
// Datetime pickers
$(function () {
$('#timezone')[0].value = moment.tz.guess();
var date = moment()
createDateTimePicker('#datetimepicker_start', "#start_time_disabled", date)
date.add(1, 'hour');
createDateTimePicker('#datetimepicker_end', "#end_time_disabled", date)
$("#datetimepicker_start").on("dp.change", function (e) {
$('#datetimepicker_end').data("DateTimePicker").minDate(e.date);
});
$("#datetimepicker_end").on("dp.change", function (e) {
$('#datetimepicker_start').data("DateTimePicker").maxDate(e.date);
});
});
// Delete Game button
function deleteGame() {
if (confirm("Are you sure you want to delete this game?")) {
window.location.href = "{{ url_for('main.delete_game', game_name=game.name) }}"
}
}
</script>
{% endblock %}

8
app/templates/game_owner_dashboard.html

@ -9,7 +9,6 @@
{% block app_content %} {% block app_content %}
<h1>{{ game.name }} Dashboard</h1> <h1>{{ game.name }} Dashboard</h1>
<button class="btn btn-danger" onclick="deleteGame()">Delete Game</button>
<a href="{{ url_for('main.change_game_settings', game_name=game.name) }}"> <a href="{{ url_for('main.change_game_settings', game_name=game.name) }}">
<button class="btn btn-primary">Change Game Settings</button> <button class="btn btn-primary">Change Game Settings</button>
</a> </a>
@ -119,12 +118,5 @@
map.fitBounds(markers); map.fitBounds(markers);
} }
//Delete Game button
function deleteGame() {
if (confirm("Are you sure you want to delete this game?")) {
window.location.href = "{{ url_for('main.delete_game', game_name=game.name) }}"
}
}
</script> </script>
{% endblock %} {% endblock %}

10
app/utils.py

@ -1,6 +1,8 @@
from io import BytesIO from io import BytesIO
import qrcode import qrcode
from flask import send_file, flash from flask import send_file, flash, abort
from flask_login import current_user
from app.models import Game
def generate_qr_code(url): def generate_qr_code(url):
qr = qrcode.QRCode( qr = qrcode.QRCode(
@ -29,3 +31,9 @@ def flash_errors(form):
getattr(form, field).label.text, getattr(form, field).label.text,
error error
), 'error') ), 'error')
def get_game_if_owner(game_name):
game = Game.query.filter_by(name=game_name).first_or_404()
if not game.owned_by(current_user):
abort(403)
return game

Loading…
Cancel
Save