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_ @@ -12,7 +12,7 @@ from sqlalchemy import and_
from app import db
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,\
LocationEncoder, PlayerCaughtPlayer, Review, Location
from app.main.forms import CreateGameForm, ObjectiveForm, PlayerAddForm, UserCreateForm, \
@ -41,7 +41,6 @@ def create_game(): @@ -41,7 +41,6 @@ def create_game():
game = Game(name=form.game_name.data,
start_time=form.start_time.data,
end_time=form.end_time.data)
game.update_state()
game.players.append(GamePlayer(user=current_user, role=Role['owner']))
db.session.add(game)
db.session.commit()
@ -54,9 +53,7 @@ def create_game(): @@ -54,9 +53,7 @@ def create_game():
@bp.route('/game/<game_name>/change_settings', methods=['GET', 'POST'])
@login_required
def change_game_settings(game_name):
game = Game.query.filter_by(name=game_name).first_or_404()
if not game.owned_by(current_user):
abort(403)
game = get_game_if_owner(game_name)
form = CreateGameForm()
form.old_name = game.name
@ -80,20 +77,17 @@ def change_game_settings(game_name): @@ -80,20 +77,17 @@ def change_game_settings(game_name):
game.name = form.game_name.data
game.start_time = form.start_time.data
game.end_time = form.end_time.data
game.update_state()
db.session.commit()
flash(f"'{game.name}' had been updated!")
return redirect(url_for('main.game_dashboard', game_name=game.name))
else:
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')
@login_required
def delete_game(game_name):
game = Game.query.filter_by(name=game_name).first_or_404()
if not game.owned_by(current_user):
abort(403)
game = get_game_if_owner(game_name)
db.session.delete(game)
for user in game.users:
if not user.last_login:
@ -102,6 +96,45 @@ def delete_game(game_name): @@ -102,6 +96,45 @@ def delete_game(game_name):
flash(f"Game '{game.name}' has been deleted!")
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')
@login_required
def game_dashboard(game_name):
@ -109,7 +142,7 @@ def game_dashboard(game_name): @@ -109,7 +142,7 @@ def game_dashboard(game_name):
role = current_user.role_in_game(game)
if role == Role.owner:
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)
if role == Role.bunny:
return render_template('game_bunny_dashboard.html', title='Game Dashboard', game=game,
@ -205,9 +238,7 @@ def get_caught_bunny_photo_directory(game): @@ -205,9 +238,7 @@ def get_caught_bunny_photo_directory(game):
@bp.route('/game/<game_name>/review')
@login_required
def review_caught_bunny_photos(game_name):
game = Game.query.filter_by(name=game_name).first_or_404()
if not game.owned_by(current_user):
abort(403)
game = get_game_if_owner(game_name)
pcp_id = request.args.get('pcp_id', default=-1, type=int)
action = request.args.get('action', default='none', type=str).lower()
if pcp_id != -1:
@ -221,9 +252,7 @@ def review_caught_bunny_photos(game_name): @@ -221,9 +252,7 @@ def review_caught_bunny_photos(game_name):
@bp.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 game.owned_by(current_user):
abort(403)
game = get_game_if_owner(game_name)
form_add = PlayerAddForm()
form_add.role.choices = [(role.value, role.name) for role in Role]
@ -250,9 +279,7 @@ def add_player(game_name): @@ -250,9 +279,7 @@ def add_player(game_name):
@bp.route('/game/<game_name>/removeplayer/<username>')
@login_required
def remove_player(game_name, username):
game = Game.query.filter_by(name=game_name).first_or_404()
if not game.owned_by(current_user):
abort(403)
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:
@ -264,9 +291,7 @@ def remove_player(game_name, username): @@ -264,9 +291,7 @@ def remove_player(game_name, username):
@bp.route('/game/<game_name>/player/<username>', methods=['GET', 'POST'])
@login_required
def game_player(game_name, username):
game = Game.query.filter_by(name=game_name).first_or_404()
if not game.owned_by(current_user):
abort(403)
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)
@ -287,9 +312,7 @@ def game_player(game_name, username): @@ -287,9 +312,7 @@ def game_player(game_name, username):
@bp.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 game.owned_by(current_user):
abort(403)
game = get_game_if_owner(game_name)
form = ObjectiveForm()
objective = Objective(name='', latitude=52.0932, longitude=5.12405)
if form.validate_on_submit():

49
app/models/game.py

@ -10,7 +10,9 @@ class Game(db.Model): @@ -10,7 +10,9 @@ class Game(db.Model):
__tablename__ = 'game'
id = db.Column(db.Integer, primary_key=True)
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)
end_time = db.Column(db.DateTime)
players = db.relationship(
@ -68,40 +70,25 @@ class Game(db.Model): @@ -68,40 +70,25 @@ class Game(db.Model):
[player.player_caught_players for player in self.players]
for pcp in pcps if pcp.review == Review.none]
def update_state(self):
'''
Updates state according to game.start_time and game.end_time.
Returns True if state is changed.
'''
#pylint: disable=no-member
old_state = self.state
def is_active(self):
return self.get_state == GameState.active
def get_state(self):
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 self.state == GameState.hidden:
return False
self.state = GameState.published
if self.hidden:
return GameState.hidden
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 self.state == GameState.interrupted:
return False
self.state = GameState.active
if self.paused:
return GameState.interrupted
return GameState.active
if now > end: # After Game
if self.state == GameState.hidden:
return False
self.state = 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
if self.hidden:
return GameState.hidden
return GameState.finished

37
app/static/assets/utils.js

@ -0,0 +1,37 @@ @@ -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 @@ @@ -4,6 +4,7 @@
{% 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 %}
@ -35,11 +36,7 @@ @@ -35,11 +36,7 @@
{{ wtf.form_field(form.end_time_disabled, class='form-control') }}
</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') }}
{% endif %}
</form>
<hr>
</div>
@ -54,66 +51,19 @@ @@ -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">
$(function () {
// Datetime pickers
$('#timezone')[0].value = moment.tz.guess();
$('#datetimepicker_start').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_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()
});
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);
});
$("#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>
{% endblock %}

97
app/templates/edit_game.html

@ -0,0 +1,97 @@ @@ -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 @@ @@ -9,7 +9,6 @@
{% block app_content %}
<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) }}">
<button class="btn btn-primary">Change Game Settings</button>
</a>
@ -119,12 +118,5 @@ @@ -119,12 +118,5 @@
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>
{% endblock %}

10
app/utils.py

@ -1,6 +1,8 @@ @@ -1,6 +1,8 @@
from io import BytesIO
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):
qr = qrcode.QRCode(
@ -29,3 +31,9 @@ def flash_errors(form): @@ -29,3 +31,9 @@ def flash_errors(form):
getattr(form, field).label.text,
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