Browse Source

fix issues that arise from updating to database model 1.4

testing
Burathar 4 years ago
parent
commit
182eebbc35
  1. 7
      app/auth/forms.py
  2. 16
      app/auth/routes.py
  3. 11
      app/main/forms.py
  4. 58
      app/main/routes.py
  5. 2
      app/models/__init__.py
  6. 14
      app/models/game.py
  7. 7
      app/models/game_player.py
  8. 4
      app/models/location.py
  9. 4
      app/models/user.py
  10. 4
      app/templates/add_player.html
  11. 7
      app/templates/auth/user.html
  12. 4
      app/templates/game_bunny_dashboard.html
  13. 2
      app/templates/game_hunter_dashboard.html
  14. 18
      app/templates/game_owner_dashboard.html
  15. 14
      app/templates/game_player.html
  16. 2
      app/templates/index.html
  17. 74
      app/tests/test_models.py
  18. 121
      database_dump.txt
  19. 50
      migrations/versions/1d844798c277_reset_migrations.py
  20. 32
      the_hunt.py

7
app/auth/forms.py

@ -1,8 +1,7 @@ @@ -1,8 +1,7 @@
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField, BooleanField
from wtforms.validators import DataRequired, EqualTo, ValidationError, Length
from pytz import timezone
from app.models import Player
from app.models import User
class LoginForm(FlaskForm):
@ -19,6 +18,6 @@ class RegistrationForm(FlaskForm): @@ -19,6 +18,6 @@ class RegistrationForm(FlaskForm):
submit = SubmitField('Register')
def validate_username(self, username):
player = Player.query.filter_by(name=username.data).first()
if player is not None:
user = User.query.filter_by(name=username.data).first()
if user is not None:
raise ValidationError('Please use a different username.')

16
app/auth/routes.py

@ -2,7 +2,7 @@ from flask import render_template, flash, redirect, url_for @@ -2,7 +2,7 @@ from flask import render_template, flash, redirect, url_for
from flask_login import login_user, logout_user, current_user, login_required
from app import db
from app.auth import bp
from app.models import Player
from app.models import User
from app.auth.forms import LoginForm, RegistrationForm
@bp.route('/login', methods=['GET', 'POST'])
@ -11,11 +11,11 @@ def login(): @@ -11,11 +11,11 @@ def login():
return redirect(url_for('main.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):
user = User.query.filter_by(name=form.username.data).first()
if user is None or not user.check_password(form.password.data):
flash('Invalid username or password')
return redirect(url_for('auth.login'))
login_user(player, remember=form.remember_me.data)
login_user(user, remember=form.remember_me.data)
return redirect(url_for('main.index'))
return render_template('auth/login.html', title='Sign In', form=form)
@ -31,10 +31,10 @@ def register(): @@ -31,10 +31,10 @@ def register():
return redirect(url_for('main.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)
user = User(name=form.username.data)
user.set_password(form.password.data)
user.set_auth_hash()
db.session.add(user)
db.session.commit()
flash('Congratulations, you are now a registered user!')
return redirect(url_for('auth.login'))

11
app/main/forms.py

@ -34,7 +34,8 @@ class ObjectiveForm(FlaskForm): @@ -34,7 +34,8 @@ class ObjectiveForm(FlaskForm):
submit = SubmitField('Save')
def validate_objective_name(self, objective_name):
if objective_name.data == '': return
if objective_name.data == '':
return
objective = Objective.query.filter_by(name=objective_name.data).first()
if objective is not None:
raise ValidationError('Please use a different name.')
@ -44,11 +45,11 @@ class PlayerUpdateForm(FlaskForm): @@ -44,11 +45,11 @@ class PlayerUpdateForm(FlaskForm):
submit = SubmitField('Update')
class PlayerAddForm(FlaskForm):
name = StringField('Player Name', validators=[DataRequired(), Length(min=0, max=64)])
name = StringField('Username', validators=[DataRequired(), Length(min=0, max=64)])
role = SelectField('Player Role', choices=[('none', 'none'), ('owner', 'owner'), ('hunter', 'hunter'), ('bunny', 'bunny')], validators=[DataRequired()])
submit_add = SubmitField('Create')
class PlayerCreateForm(FlaskForm):
name = StringField('Player Name', validators=[DataRequired(), Length(min=0, max=64)])
class UserCreateForm(FlaskForm):
name = StringField('Username', validators=[DataRequired(), Length(min=0, max=64)])
role = SelectField('Player Role', choices=[('none', 'none'), ('owner', 'owner'), ('hunter', 'hunter'), ('bunny', 'bunny')], validators=[DataRequired()])
submit_create = SubmitField('Create')
submit_create = SubmitField('Create')

58
app/main/routes.py

@ -6,8 +6,8 @@ from sqlalchemy import and_ @@ -6,8 +6,8 @@ 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
from app.models import User, Game, Role, GamePlayer, Objective, ObjectiveMinimalEncoder, LocationEncoder
from app.main.forms import CreateGameForm, ObjectiveForm, PlayerAddForm, UserCreateForm, PlayerUpdateForm
@bp.route('/')
@bp.route('/index')
@ -24,7 +24,7 @@ def create_game(): @@ -24,7 +24,7 @@ def create_game():
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']))
game.game_players.append(GamePlayer(game_player=current_user, role=Role['owner']))
db.session.add(game)
db.session.commit()
flash(f"'{game.name}' had been created!")
@ -51,6 +51,7 @@ def game_dashboard(game_name): @@ -51,6 +51,7 @@ def game_dashboard(game_name):
if role is None:
abort(403)
@bp.route('/game/<game_name>/adduser', methods=['GET', 'POST'])
@bp.route('/game/<game_name>/addplayer', methods=['GET', 'POST'])
@login_required
def add_player(game_name):
@ -58,62 +59,63 @@ def add_player(game_name): @@ -58,62 +59,63 @@ def add_player(game_name):
if not game.owned_by(current_user):
abort(403)
form_add = PlayerAddForm()
form_create = PlayerCreateForm()
form_create = UserCreateForm()
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]))
user = User.query.filter_by(form_add.name.data).first_or_404()
game.game_players.append(GamePlayer(user=user, 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]))
user = User(name=form_create.name.data)
user.set_auth_hash()
game.game_players.append(GamePlayer(user=user, 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)
return render_template('add_player.html', title=f'Add User for {game_name}', form_add=form_add, form_create=form_create, game=game)
@bp.route('/game/<game_name>/removeplayer/<player_name>')
@bp.route('/game/<game_name>/removeuser/<username>')
@bp.route('/game/<game_name>/removeplayer/<username>')
@login_required
def remove_player(game_name, player_name):
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)
player = Player.query.filter(and_(Player.name == player_name, Player.games.contains(game))).first_or_404()
game.players.remove(player)
user = User.query.filter(and_(User.name == username, User.games.contains(game))).first_or_404()
game.players.remove(user)
db.session.commit()
return redirect(url_for('main.game_dashboard', game_name=game.name))
@bp.route('/game/<game_name>/player/<player_name>', methods=['GET', 'POST'])
@bp.route('/game/<game_name>/player/<username>', methods=['GET', 'POST'])
@login_required
def game_player(game_name, player_name):
def game_player(game_name, username):
game = Game.query.filter_by(name=game_name).first_or_404()
if not game.owned_by(current_user):
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]
user = User.query.filter(and_(User.name == username, User.games.contains(game))).first_or_404()
gameplayer = [gameplayer for gameplayer in user.user_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)
return render_template('game_player.html', title=f'{user.name} in {game_name}', game=game, user=user, form=form, json=json, location_encoder=LocationEncoder)
@bp.route('/player/<auth_hash>/qrcode.png')
@bp.route('/user/<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 current_user.owns_game_played_by(player):
def user_qrcode(auth_hash):
user = User.query.filter_by(auth_hash=auth_hash).first_or_404()
if not current_user.owns_game_played_by(user):
abort(403)
img = generate_qr_code(url_for('main.player', auth_hash=auth_hash, _external=True))
return serve_pil_image(img)
@bp.route('/player/<auth_hash>')
@bp.route('/user/<auth_hash>')
@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)
def user(auth_hash):
user = User.query.filter_by(auth_hash=auth_hash).first_or_404()
return render_template('auth/user.html', title=f'User: {user.name}', user=user)
@bp.route('/game/<game_name>/add_objective', methods=['GET', 'POST'])
@login_required

2
app/models/__init__.py

@ -1,8 +1,8 @@ @@ -1,8 +1,8 @@
from .game import Game
from .game_player import GamePlayer
from .game_state import GameState
from .location import Location, LocationEncoder
from .notification import Notification
from .notification_player import NotificationPlayer
from .objective import Objective, ObjectiveMinimalEncoder
from .player_caught_player import PlayerCaughtPlayer

14
app/models/game.py

@ -15,8 +15,10 @@ class Game(db.Model): @@ -15,8 +15,10 @@ class Game(db.Model):
'GamePlayer',
back_populates='game',
cascade="save-update, merge, delete, delete-orphan")
players = association_proxy('game_players', 'user',
creator=lambda user: GamePlayer(user=user))
users = association_proxy('game_players', 'user',
creator=lambda user: GamePlayer(user=user)) # to enable game.players.append(player)
creator=lambda user: GamePlayer(user=user))
objectives = db.relationship(
'Objective',
lazy='select',
@ -27,11 +29,11 @@ class Game(db.Model): @@ -27,11 +29,11 @@ class Game(db.Model):
backref=db.backref('game', lazy='joined'))
def last_player_locations(self):
return [player.last_location(self) for player in self.users if player.locations]
return [user.last_location(self) for user in self.players if user.locations]
def bunnies(self):
# pylint: disable=not-an-iterable
return [gameplayer.player for gameplayer in self.game_players if gameplayer.role == Role.bunny]
return [gameplayer.user for gameplayer in self.game_players if gameplayer.role == Role.bunny]
def last_locations(self, players):
locations = []
@ -39,7 +41,7 @@ class Game(db.Model): @@ -39,7 +41,7 @@ class Game(db.Model):
locations.append(player.last_location(self))
return locations
def owned_by(self, player):
def owned_by(self, user):
# pylint: disable=not-an-iterable
'''given player is an owner of game'''
return player in [gameplayer.player for gameplayer in self.game_players if gameplayer.role == Role.owner]
'''given user is an owner of this game'''
return user in [gameplayer.user for gameplayer in self.game_players if gameplayer.role == Role.owner]

7
app/models/game_player.py

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.schema import UniqueConstraint
from app import db
from .role import Role
@ -9,11 +10,13 @@ from .player_caught_player import PlayerCaughtPlayer @@ -9,11 +10,13 @@ from .player_caught_player import PlayerCaughtPlayer
class GamePlayer(db.Model):
__tablename__ = 'game_player'
id = db.Column(db.Integer, primary_key=True)
game_id = db.Column(db.Integer, db.ForeignKey('game.id'), primary_key=True, nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), primary_key=True, nullable=False)
game_id = db.Column(db.Integer, db.ForeignKey('game.id'), nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
role = db.Column(db.Enum(Role), server_default=Role(0).name, nullable=False)
game = db.relationship('Game', back_populates='game_players')
user = db.relationship('User', back_populates='user_games')
__table_args__ = (UniqueConstraint('game_id', 'user_id', name='_game_user_uc'),
)
player_notifications = db.relationship(
'NotificationPlayer',

4
app/models/location.py

@ -6,7 +6,7 @@ from app import db @@ -6,7 +6,7 @@ from app import db
class Location(db.Model):
__tablename__ = 'location'
id = db.Column(db.Integer, primary_key=True)
player_id = db.Column(db.Integer, db.ForeignKey('player.id'), nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
longitude = db.Column(db.Numeric(precision=15, scale=10, asdecimal=False, decimal_return_scale=None), nullable=False) # maybe check asdecimal and decimal_return_scale later?
latitude = db.Column(db.Numeric(precision=15, scale=10, asdecimal=False, decimal_return_scale=None), nullable=False)
timestamp = db.Column(db.DateTime, server_default=func.now(), nullable=False)
@ -17,7 +17,7 @@ class Location(db.Model): @@ -17,7 +17,7 @@ class Location(db.Model):
class LocationEncoder(JSONEncoder):
def default(self, location):
return {
'player_name' : location.player.name,
'username' : location.user.name,
'longitude' : location.longitude,
'latitude' : location.latitude,
'timestamp_utc' : str(location.timestamp)

4
app/models/user.py

@ -10,7 +10,7 @@ from app.models import GamePlayer, Role @@ -10,7 +10,7 @@ from app.models import GamePlayer, Role
class User(UserMixin, db.Model):
""" !Always call set_auth_hash() after creating new instance! """
__tablename__ = 'player'
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True, nullable=False)
auth_hash = db.Column(db.String(32), unique=True, nullable=True)
@ -36,6 +36,8 @@ class User(UserMixin, db.Model): @@ -36,6 +36,8 @@ class User(UserMixin, db.Model):
self.auth_hash = token_hex(16)
def check_password(self, password):
if not password or not self.password_hash:
return False
return check_password_hash(self.password_hash, password)
def locations_during_game(self, game):

4
app/templates/add_player.html

@ -3,7 +3,7 @@ @@ -3,7 +3,7 @@
{% block app_content %}
<h1>Add Player To Game</h1>
<h2>Add Existing Player</h2>
<h2>Add Existing User</h2>
<div class="row">
<div class="col-md-4">
<form action="" method="post" class="form" role="form">
@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
</form>
</div>
</div>
<h2>Create new Player</h2>
<h2>Create new User</h2>
<div class="row">
<div class="col-md-4">
<form action="" method="post" class="form" role="form">

7
app/templates/auth/user.html

@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
{% extends "base.html" %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block app_content %}
<h1>User</h1>
This page is is progress, it should enable you to claim a player account using the authhash, as long as the player hasnt logged in yet.
{% endblock %}

4
app/templates/game_bunny_dashboard.html

@ -28,7 +28,7 @@ @@ -28,7 +28,7 @@
{% for player in game.players %}
<tr>
<td><a href="{{ url_for('main.game_player', game_name = game.name, player_name = player.name) }}">{{ player.name }}</a></td>
{% for gameplayer in player.player_games if gameplayer.game == game %}
{% for gameplayer in player.user_games if gameplayer.game == game %}
<td>{{ gameplayer.role.name }}</td>
{% endfor %}
<td>{{ player.found_objectives | selectattr('game', '==', game)|list|length}}</td>
@ -129,7 +129,7 @@ @@ -129,7 +129,7 @@
], {icon: greenIcon}).addTo(map);
var timestamp_utc = moment.utc(players[i]['timestamp_utc']).toDate()
var timestamp_local = moment(timestamp_utc).local().format('YYYY-MM-DD HH:mm');
playerMarker.bindTooltip(`<b>${players[i]['player_name']}</b><br>
playerMarker.bindTooltip(`<b>${players[i]['username']}</b><br>
${timestamp_local}`).openPopup();
}

2
app/templates/game_hunter_dashboard.html

@ -72,7 +72,7 @@ @@ -72,7 +72,7 @@
], {icon: greenIcon}).addTo(map);
var timestamp_utc = moment.utc(players[i]['timestamp_utc']).toDate()
var timestamp_local = moment(timestamp_utc).local().format('YYYY-MM-DD HH:mm');
playerMarker.bindTooltip(`<b>${players[i]['player_name']}</b><br>
playerMarker.bindTooltip(`<b>${players[i]['username']}</b><br>
${timestamp_local}`).openPopup();
}

18
app/templates/game_owner_dashboard.html

@ -25,20 +25,20 @@ @@ -25,20 +25,20 @@
</tr>
</thead>
<tbody>
{% for player in game.players %}
{% for user in game.users %}
<tr>
<td><a href="{{ url_for('main.game_player', game_name = game.name, player_name = player.name) }}">{{ player.name }}</a></td>
{% for gameplayer in player.player_games if gameplayer.game == game %}
<td><a href="{{ url_for('main.game_player', game_name = game.name, username = user.name) }}">{{ user.name }}</a></td>
{% for gameplayer in user.user_games if gameplayer.game == game %}
<td>{{ gameplayer.role.name }}</td>
{% endfor %}
<td>{{ player.found_objectives | selectattr('game', '==', game)|list|length}}</td>
<td>{{ player.caught_players | selectattr('game', '==', game)|list|length}}</td>
<td>{{ player.caught_by_players | selectattr('game', '==', game)|list|length}}</td>
<td>{% with location = player.last_location(game) %}
<td>{{ user.found_objectives | selectattr('game', '==', game)|list|length}}</td>
<td>{{ user.caught_players | selectattr('game', '==', game)|list|length}}</td>
<td>{{ user.caught_by_players | selectattr('game', '==', game)|list|length}}</td>
<td>{% with location = user.last_location(game) %}
{% if location %}{{ moment(location.timestamp).fromNow()}}: {% endif %}
{{ location }}
{% endwith %}</td>
<td><a href="{{ url_for('main.remove_player', game_name=game.name, player_name=player.name) }}">
<td><a href="{{ url_for('main.remove_player', game_name=game.name, username=user.name) }}">
<button class="btn btn-danger">Delete</button></a>
</td>
</tr>
@ -129,7 +129,7 @@ @@ -129,7 +129,7 @@
], {icon: greenIcon}).addTo(map);
var timestamp_utc = moment.utc(players[i]['timestamp_utc']).toDate()
var timestamp_local = moment(timestamp_utc).local().format('YYYY-MM-DD HH:mm');
playerMarker.bindTooltip(`<b>${players[i]['player_name']}</b><br>
playerMarker.bindTooltip(`<b>${players[i]['username']}</b><br>
${timestamp_local}`).openPopup();
}

14
app/templates/player.html → app/templates/game_player.html

@ -8,22 +8,22 @@ @@ -8,22 +8,22 @@
{% endblock %}
{% block app_content %}
<h1>Player: {{ player.name }}</h1>
<h1>Player: {{ user.name }}</h1>
<hr>
<div class="row">
<div class="col-md-4 col-sm-6 col-xs-8">
<div class="row">
<form action="" method="post" class="form" role="form">
{{ form.hidden_tag() }}
{% for gameplayer in player.player_games if gameplayer.game == game %}
{% for gameplayer in user.user_games if gameplayer.game == game %}
{{ wtf.form_field(form.role, class='form-control') }}
{% endfor %}
{{ wtf.form_field(form.submit, class='btn btn-primary', value='Update') }}
</form>
</div>
{% if player.auth_hash %}
{% if user.auth_hash %}
<div class="row">
<img src="{{ url_for('main.player_qrcode', auth_hash=player.auth_hash) }}" alt="qr_code_failed", width="100%">
<img src="{{ url_for('main.user_qrcode', auth_hash=user.auth_hash) }}" alt="qr_code_failed", width="100%">
</div>
{% endif %}
</div>
@ -37,7 +37,7 @@ @@ -37,7 +37,7 @@
{{ moment.include_moment() }}
<script type="text/javascript", crossorigin="anonymous">
// Leaflet Map
'{% set last_location = player.last_location(game) %}'
'{% set last_location = user.last_location(game) %}'
var map = L.map( 'map', {
center: ['{{ last_location.latitude or 52.2 }}', '{{ last_location.longitude or 5.3 }}'],
minZoom: 6,
@ -60,7 +60,7 @@ @@ -60,7 +60,7 @@
shadowSize: [41, 41]
});
var locations = JSON.parse('{{ json.dumps(player.locations, cls=location_encoder)|safe }}')
var locations = JSON.parse('{{ json.dumps(user.locations_during_game(game)), cls=location_encoder)|safe }}')
for (var i = 0; i < locations.length; i++){
var playerMarker = L.marker([
locations[i]['latitude'],
@ -68,7 +68,7 @@ @@ -68,7 +68,7 @@
], {icon: greenIcon}).addTo(map);
var timestamp_utc = moment.utc(locations[i]['timestamp_utc']).toDate()
var timestamp_local = moment(timestamp_utc).local().format('YYYY-MM-DD HH:mm');
playerMarker.bindTooltip(`<b>${locations[i]['player_name']}</b><br>
playerMarker.bindTooltip(`<b>${locations[i]['username']}</b><br>
${timestamp_local}`).openPopup();
}

2
app/templates/index.html

@ -24,7 +24,7 @@ @@ -24,7 +24,7 @@
<td>{{ game.start_time }}</td>
<td>{{ game.end_time }}</td>
<td>
{% for gameplayer in current_user.player_games if gameplayer.game == game %}
{% for gameplayer in current_user.user_games if gameplayer.game == game %}
{{ gameplayer.role.name }}
{% endfor %}
</td>

74
app/tests/test_models.py

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
import unittest
from app import create_app, db
from app.models import Player, Game, Role, GamePlayer, Objective, ObjectiveMinimalEncoder, LocationEncoder
from app.models import User, Game, Role, GamePlayer, Objective, ObjectiveMinimalEncoder, LocationEncoder
from config import Config
class TestConfig(Config):
@ -24,49 +24,49 @@ class ModelsCase(unittest.TestCase): @@ -24,49 +24,49 @@ class ModelsCase(unittest.TestCase):
def test_is_game_owner(self):
g1 = Game(name='TestGame')
p1 = Player(name='Henk')
p2 = Player(name='Alfred')
u1 = User(name='Henk')
u2 = User(name='Alfred')
g1.game_players.append(GamePlayer(player=p1, role=Role.owner))
g1.game_players.append(GamePlayer(player=p2, role=Role.bunny))
g1.game_players.append(GamePlayer(player=u1, role=Role.owner))
g1.game_players.append(GamePlayer(player=u2, role=Role.bunny))
db.session.add(g1)
db.session.commit()
self.assertTrue(g1.is_game_owner(p1))
self.assertFalse(g1.is_game_owner(p2))
self.assertTrue(g1.is_game_owner(u1))
self.assertFalse(g1.is_game_owner(u2))
def test_is_player_game_owner(self):
g1 = Game(name='TestGame')
g2 = Game(name='AnotherGame')
p1 = Player(name='Henk')
p2 = Player(name='Alfred')
p3 = Player(name='Sasha')
u1 = User(name='Henk')
u2 = User(name='Alfred')
u3 = User(name='Sasha')
g1.game_players.append(GamePlayer(player=p1, role=Role.owner))
g1.game_players.append(GamePlayer(player=p2, role=Role.bunny))
g1.game_players.append(GamePlayer(player=u1, role=Role.owner))
g1.game_players.append(GamePlayer(player=u2, role=Role.bunny))
g2.game_players.append(GamePlayer(player=p1, role=Role.hunter))
g2.game_players.append(GamePlayer(player=p3, role=Role.bunny))
g2.game_players.append(GamePlayer(player=u1, role=Role.hunter))
g2.game_players.append(GamePlayer(player=u3, role=Role.bunny))
db.session.add(g1)
db.session.add(g2)
db.session.commit()
self.assertTrue(p1.owns_game_played_by(player=p2), "owner owns subject_player's game")
self.assertFalse(p1.owns_game_played_by(player=p3), "owner doesn't own subject_player's game")
self.assertTrue(p1.owns_game_played_by(player=p1), "owner owns it own's game")
self.assertTrue(u1.owns_game_played_by(player=u2), "owner owns subject_player's game")
self.assertFalse(u1.owns_game_played_by(player=u3), "owner doesn't own subject_player's game")
self.assertTrue(u1.owns_game_played_by(player=u1), "owner owns it own's game")
def test_is_objective_owner(self):
g1 = Game(name='TestGame')
g2 = Game(name='AnotherGame')
p1 = Player(name='Henk')
u1 = User(name='Henk')
g1.game_players.append(GamePlayer(player=p1, role=Role.owner))
g2.game_players.append(GamePlayer(player=p1, role=Role.bunny))
g1.game_players.append(GamePlayer(player=u1, role=Role.owner))
g2.game_players.append(GamePlayer(player=u1, role=Role.bunny))
o1 = Objective(name='o1')
o1.set_hash()
@ -80,32 +80,32 @@ class ModelsCase(unittest.TestCase): @@ -80,32 +80,32 @@ class ModelsCase(unittest.TestCase):
db.session.add(g2)
db.session.commit()
self.assertTrue(o1.owned_by(p1))
self.assertFalse(o2.owned_by(p1))
self.assertTrue(o1.owned_by(u1))
self.assertFalse(o2.owned_by(u1))
def test_role_in_game(self):
g1 = Game(name='TestGame')
p1 = Player(name='Henk')
p2 = Player(name='Alfred')
p3 = Player(name='Sasha')
p4 = Player(name='Demian')
p5 = Player(name='Karl')
u1 = User(name='Henk')
u2 = User(name='Alfred')
u3 = User(name='Sasha')
u4 = User(name='Demian')
u5 = User(name='Karl')
g1.game_players.append(GamePlayer(player=p1, role=Role.owner))
g1.game_players.append(GamePlayer(player=p2, role=Role.bunny))
g1.game_players.append(GamePlayer(player=p3, role=Role.hunter))
g1.game_players.append(GamePlayer(player=p4, role=Role.none))
g1.game_players.append(GamePlayer(player=u1, role=Role.owner))
g1.game_players.append(GamePlayer(player=u2, role=Role.bunny))
g1.game_players.append(GamePlayer(player=u3, role=Role.hunter))
g1.game_players.append(GamePlayer(player=u4, role=Role.none))
db.session.add(g1)
db.session.add(p5)
db.session.add(u5)
db.session.commit()
self.assertEqual(p1.role_in_game(g1), Role.owner)
self.assertEqual(p2.role_in_game(g1), Role.bunny)
self.assertEqual(p3.role_in_game(g1), Role.hunter)
self.assertEqual(p4.role_in_game(g1), Role.none)
self.assertEqual(p5.role_in_game(g1), None)
self.assertEqual(u1.role_in_game(g1), Role.owner)
self.assertEqual(u2.role_in_game(g1), Role.bunny)
self.assertEqual(u3.role_in_game(g1), Role.hunter)
self.assertEqual(u4.role_in_game(g1), Role.none)
self.assertEqual(u5.role_in_game(g1), None)
with self.assertRaises(AttributeError):
g1.get_role_for_game(None)

121
database_dump.txt

@ -0,0 +1,121 @@ @@ -0,0 +1,121 @@
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE alembic_version (
version_num VARCHAR(32) NOT NULL,
CONSTRAINT alembic_version_pkc PRIMARY KEY (version_num)
);
INSERT INTO alembic_version VALUES('3ef4c34115fd');
CREATE TABLE game (
id INTEGER NOT NULL,
name VARCHAR(64) NOT NULL,
state VARCHAR(11) DEFAULT 'initiated' NOT NULL,
start_time DATETIME,
end_time DATETIME,
PRIMARY KEY (id),
CONSTRAINT gamestate CHECK (state IN ('initiated', 'published', 'started', 'interrupted', 'finished'))
);
INSERT INTO game VALUES(1,'Marijns Game','initiated','2020-07-09 12:56:00.000000',NULL);
INSERT INTO game VALUES(2,'Spelletje','initiated',NULL,NULL);
CREATE TABLE player (
id INTEGER NOT NULL,
name VARCHAR(64) NOT NULL,
auth_hash VARCHAR(32),
password_hash VARCHAR(128),
PRIMARY KEY (id),
UNIQUE (auth_hash),
UNIQUE (name)
);
INSERT INTO player VALUES(1,'Marijn','a9df90398c66181bcb9e826e48c97d8a','pbkdf2:sha256:150000$WLe3pHiQ$ff75a3c2e8e0345be88f7f59b57f1cc27f710ef9012995b752f71d8914282cda');
INSERT INTO player VALUES(2,'Rogier','6a18131cea1be566c6a33e91349f7d91','pbkdf2:sha256:150000$YbcS3JRB$60c71304b2d148f82cd954f57d68ceb15b66fedc23bbe740cd5d97f0a60f7ce4');
INSERT INTO player VALUES(3,'Henk','df6a22a4c50abba430b82ab9e07d533e','pbkdf2:sha256:150000$PNIf5rdP$81cba639a9d12c946230411d2ba0d5bc22be4509578d7b0c9409047d907c26fc');
INSERT INTO player VALUES(4,'testplayer','79404d0d9dc869fb421c051790e74b38',NULL);
INSERT INTO player VALUES(5,'test','9a9f90e959261adb50fee40b4bf0ec35',NULL);
INSERT INTO player VALUES(6,'Tessa','f74c9e3d76b22368722d76a5b2445c23',NULL);
INSERT INTO player VALUES(7,'Jemoeder','fc14d4b87f28da7f88c496c33b2d9fe8',NULL);
INSERT INTO player VALUES(8,'jevader','a43c1704562a456bc1f098fab200805a',NULL);
INSERT INTO player VALUES(9,'jekind','0e6cb60258ed1fd71a95fbd13d6751e1',NULL);
CREATE TABLE game_player (
game_id INTEGER NOT NULL,
player_id INTEGER NOT NULL,
role VARCHAR(6) DEFAULT 'none' NOT NULL,
PRIMARY KEY (game_id, player_id),
FOREIGN KEY(game_id) REFERENCES game (id),
FOREIGN KEY(player_id) REFERENCES player (id),
CONSTRAINT role CHECK (role IN ('none', 'owner', 'hunter', 'bunny'))
);
INSERT INTO game_player VALUES(1,1,'owner');
INSERT INTO game_player VALUES(1,2,'hunter');
INSERT INTO game_player VALUES(1,3,'bunny');
INSERT INTO game_player VALUES(2,1,'hunter');
INSERT INTO game_player VALUES(2,3,'bunny');
INSERT INTO game_player VALUES(2,2,'owner');
CREATE TABLE location (
id INTEGER NOT NULL,
player_id INTEGER NOT NULL,
longitude NUMERIC(15, 10) NOT NULL,
latitude NUMERIC(15, 10) NOT NULL,
timestamp DATETIME DEFAULT (CURRENT_TIMESTAMP) NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY(player_id) REFERENCES player (id)
);
INSERT INTO location VALUES(1,2,5.1690699999999996094,52.224429999999998131,'2020-07-10 10:36:26');
INSERT INTO location VALUES(2,2,6.0314899999999997959,52.180669999999999219,'2020-07-10 10:37:19');
INSERT INTO location VALUES(3,2,5.4217500000000002913,51.882429999999999381,'2020-07-11 10:15:12');
INSERT INTO location VALUES(4,3,4.4604499999999998038,51.905310000000000059,'2020-07-17 20:50:00');
INSERT INTO location VALUES(5,3,4.4604499999999998038,52.236209999999999808,'2020-07-17 20:50:00');
CREATE TABLE notification (
id INTEGER NOT NULL,
game_id INTEGER NOT NULL,
message TEXT NOT NULL,
type VARCHAR(64) NOT NULL,
timestamp DATETIME DEFAULT (CURRENT_TIMESTAMP) NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY(game_id) REFERENCES game (id)
);
CREATE TABLE objective (
id INTEGER NOT NULL,
name VARCHAR(64),
game_id INTEGER NOT NULL,
hash VARCHAR(32) NOT NULL,
longitude NUMERIC(15, 10),
latitude NUMERIC(15, 10),
PRIMARY KEY (id),
FOREIGN KEY(game_id) REFERENCES game (id),
UNIQUE (hash)
);
INSERT INTO objective VALUES(1,'Je Moeder',1,'6d6ea175f38a912624b0d842230ec5a0',5.2999999999999998223,52.200000000000002843);
INSERT INTO objective VALUES(5,'Amsterdam',1,'2cb430ba9120fd92ad2694ef59e8232a',4.8120099999999998985,52.37225000000000108);
INSERT INTO objective VALUES(6,'Blok beton',1,'2a94b2b7ca0f01d6d6a025644ce47e46',5.2349899999999998101,52.549639999999996574);
INSERT INTO objective VALUES(7,'testdit',2,'d813048690c6b804bfce0268ccd60995',5.1240500000000004376,52.093200000000003057);
CREATE TABLE player_caught_player (
id INTEGER DEFAULT '-1' NOT NULL,
catching_player_id INTEGER NOT NULL,
caught_player_id INTEGER NOT NULL,
photo_reference VARCHAR(128) NOT NULL,
timestamp DATETIME DEFAULT (CURRENT_TIMESTAMP) NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY(catching_player_id) REFERENCES player (id),
FOREIGN KEY(caught_player_id) REFERENCES player (id),
UNIQUE (photo_reference)
);
CREATE TABLE notification_player (
notification_id INTEGER NOT NULL,
player_id INTEGER NOT NULL,
been_shown BOOLEAN DEFAULT 'True' NOT NULL,
PRIMARY KEY (notification_id, player_id),
FOREIGN KEY(notification_id) REFERENCES notification (id),
FOREIGN KEY(player_id) REFERENCES player (id),
CHECK (been_shown IN (0, 1))
);
CREATE TABLE player_found_objective (
objective_id INTEGER DEFAULT '-1' NOT NULL,
player_id INTEGER DEFAULT '-1' NOT NULL,
timestamp DATETIME DEFAULT (CURRENT_TIMESTAMP) NOT NULL,
PRIMARY KEY (objective_id, player_id),
FOREIGN KEY(objective_id) REFERENCES objective (id),
FOREIGN KEY(player_id) REFERENCES player (id)
);
INSERT INTO player_found_objective VALUES(1,2,'2020-07-10 09:49:10');
INSERT INTO player_found_objective VALUES(1,3,'2020-07-10 09:49:10');
CREATE UNIQUE INDEX ix_game_name ON game (name);
COMMIT;

50
migrations/versions/35d99c2732cb_reset_migrations.py → migrations/versions/1d844798c277_reset_migrations.py

@ -1,8 +1,8 @@ @@ -1,8 +1,8 @@
"""reset migrations
Revision ID: 35d99c2732cb
Revision ID: 1d844798c277
Revises:
Create Date: 2020-07-18 18:02:51.537046
Create Date: 2020-07-18 23:47:44.369860
"""
from alembic import op
@ -10,7 +10,7 @@ import sqlalchemy as sa @@ -10,7 +10,7 @@ import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '35d99c2732cb'
revision = '1d844798c277'
down_revision = None
branch_labels = None
depends_on = None
@ -27,7 +27,7 @@ def upgrade(): @@ -27,7 +27,7 @@ def upgrade():
sa.PrimaryKeyConstraint('id')
)
op.create_index(op.f('ix_game_name'), 'game', ['name'], unique=True)
op.create_table('player',
op.create_table('user',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(length=64), nullable=False),
sa.Column('auth_hash', sa.String(length=32), nullable=True),
@ -37,20 +37,22 @@ def upgrade(): @@ -37,20 +37,22 @@ def upgrade():
sa.UniqueConstraint('name')
)
op.create_table('game_player',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('game_id', sa.Integer(), nullable=False),
sa.Column('player_id', sa.Integer(), nullable=False),
sa.Column('user_id', sa.Integer(), nullable=False),
sa.Column('role', sa.Enum('none', 'owner', 'hunter', 'bunny', name='role'), server_default='none', nullable=False),
sa.ForeignKeyConstraint(['game_id'], ['game.id'], ),
sa.ForeignKeyConstraint(['player_id'], ['player.id'], ),
sa.PrimaryKeyConstraint('game_id', 'player_id')
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('game_id', 'user_id', name='_game_user_uc')
)
op.create_table('location',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('player_id', sa.Integer(), nullable=False),
sa.Column('user_id', sa.Integer(), nullable=False),
sa.Column('longitude', sa.Numeric(precision=15, scale=10, asdecimal=False), nullable=False),
sa.Column('latitude', sa.Numeric(precision=15, scale=10, asdecimal=False), nullable=False),
sa.Column('timestamp', sa.DateTime(), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
sa.ForeignKeyConstraint(['player_id'], ['player.id'], ),
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_table('notification',
@ -73,32 +75,32 @@ def upgrade(): @@ -73,32 +75,32 @@ def upgrade():
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('hash')
)
op.create_table('notification_player',
sa.Column('notification_id', sa.Integer(), nullable=False),
sa.Column('game_player_id', sa.Integer(), nullable=False),
sa.Column('been_shown', sa.Boolean(), server_default='True', nullable=False),
sa.ForeignKeyConstraint(['game_player_id'], ['game_player.id'], ),
sa.ForeignKeyConstraint(['notification_id'], ['notification.id'], ),
sa.PrimaryKeyConstraint('notification_id', 'game_player_id')
)
op.create_table('player_caught_player',
sa.Column('id', sa.Integer(), server_default='-1', autoincrement=True, nullable=False),
sa.Column('catching_player_id', sa.Integer(), nullable=False),
sa.Column('caught_player_id', sa.Integer(), nullable=False),
sa.Column('photo_reference', sa.String(length=128), nullable=False),
sa.Column('timestamp', sa.DateTime(), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
sa.ForeignKeyConstraint(['catching_player_id'], ['player.id'], ),
sa.ForeignKeyConstraint(['caught_player_id'], ['player.id'], ),
sa.ForeignKeyConstraint(['catching_player_id'], ['game_player.id'], ),
sa.ForeignKeyConstraint(['caught_player_id'], ['game_player.id'], ),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('photo_reference')
)
op.create_table('notification_player',
sa.Column('notification_id', sa.Integer(), nullable=False),
sa.Column('player_id', sa.Integer(), nullable=False),
sa.Column('been_shown', sa.Boolean(), server_default='True', nullable=False),
sa.ForeignKeyConstraint(['notification_id'], ['notification.id'], ),
sa.ForeignKeyConstraint(['player_id'], ['player.id'], ),
sa.PrimaryKeyConstraint('notification_id', 'player_id')
)
op.create_table('player_found_objective',
sa.Column('objective_id', sa.Integer(), server_default='-1', nullable=False),
sa.Column('player_id', sa.Integer(), server_default='-1', nullable=False),
sa.Column('game_player_id', sa.Integer(), server_default='-1', nullable=False),
sa.Column('timestamp', sa.DateTime(), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
sa.ForeignKeyConstraint(['game_player_id'], ['game_player.id'], ),
sa.ForeignKeyConstraint(['objective_id'], ['objective.id'], ),
sa.ForeignKeyConstraint(['player_id'], ['player.id'], ),
sa.PrimaryKeyConstraint('objective_id', 'player_id')
sa.PrimaryKeyConstraint('objective_id', 'game_player_id')
)
# ### end Alembic commands ###
@ -106,13 +108,13 @@ def upgrade(): @@ -106,13 +108,13 @@ def upgrade():
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('player_found_objective')
op.drop_table('notification_player')
op.drop_table('player_caught_player')
op.drop_table('notification_player')
op.drop_table('objective')
op.drop_table('notification')
op.drop_table('location')
op.drop_table('game_player')
op.drop_table('player')
op.drop_table('user')
op.drop_index(op.f('ix_game_name'), table_name='game')
op.drop_table('game')
# ### end Alembic commands ###

32
the_hunt.py

@ -1,12 +1,12 @@ @@ -1,12 +1,12 @@
from app import create_app, db
from app.models import Game, Player, Objective, Location, Notification, GamePlayer, \
from app.models import Game, User, Objective, Location, Notification, GamePlayer, \
PlayerFoundObjective, NotificationPlayer, PlayerCaughtPlayer, Role, GameState
app = create_app()
@app.shell_context_processor
def make_shell_context():
return {'db': db, 'Game' : Game, 'Player' : Player, 'Objective' : Objective,
return {'db': db, 'Game' : Game, 'User' : User, 'Objective' : Objective,
'Location' : Location, 'Notification' : Notification, 'GamePlayer' : GamePlayer,
'PlayerFoundObjective' : PlayerFoundObjective, 'NotificationPlayer' : NotificationPlayer,
'PlayerCaughtPlayer' : PlayerCaughtPlayer, 'Role' : Role, 'GameState' : GameState,
@ -16,21 +16,23 @@ def create_objects(): @@ -16,21 +16,23 @@ def create_objects():
g1 = Game(name='TestGame')
g2 = Game(name='MyGame')
p1 = Player(name='Marijn')
p2 = Player(name='Rogier')
p3 = Player(name='Henk')
p4 = Player(name='Emma')
p5 = Player(name='Demi')
u1 = User(name='Marijn')
u1.set_password('123')
u2 = User(name='Rogier')
u2.set_password('123')
u3 = User(name='Henk')
u4 = User(name='Emma')
u5 = User(name='Demi')
g1.game_players.append(GamePlayer(player=p1, role=Role.owner))
g1.game_players.append(GamePlayer(player=p2, role=Role.hunter))
g1.game_players.append(GamePlayer(player=p3, role=Role.hunter))
g1.game_players.append(GamePlayer(player=p4, role=Role.bunny))
g1.game_players.append(GamePlayer(player=p5, role=Role.bunny))
g1.game_players.append(GamePlayer(user=u1, role=Role.owner))
g1.game_players.append(GamePlayer(user=u2, role=Role.hunter))
g1.game_players.append(GamePlayer(user=u3, role=Role.hunter))
g1.game_players.append(GamePlayer(user=u4, role=Role.bunny))
g1.game_players.append(GamePlayer(user=u5, role=Role.bunny))
g2.game_players.append(GamePlayer(player=p1, role=Role.bunny))
g2.game_players.append(GamePlayer(player=p2, role=Role.owner))
g2.game_players.append(GamePlayer(player=p3, role=Role.hunter))
g2.game_players.append(GamePlayer(user=u1, role=Role.bunny))
g2.game_players.append(GamePlayer(user=u2, role=Role.owner))
g2.game_players.append(GamePlayer(user=u3, role=Role.hunter))
db.session.add(g1)
db.session.add(g2)

Loading…
Cancel
Save