Browse Source

implement photoreview

testing
Burathar 4 years ago
parent
commit
2bc24a47c2
  1. 3
      app/main/forms.py
  2. 53
      app/main/routes.py
  3. 1
      app/models/__init__.py
  4. 9
      app/models/game.py
  5. 19
      app/models/game_player.py
  6. 4
      app/models/player_caught_player.py
  7. 14
      app/models/review.py
  8. 1
      app/templates/_game_player_info.html
  9. 37
      app/templates/_review_photo.html
  10. 12
      app/templates/game_hunter_dashboard.html
  11. 10
      app/templates/game_owner_dashboard.html
  12. 6
      app/templates/game_player.html
  13. 14
      app/templates/review_caught_bunny_photos.html
  14. 18
      the_hunt.py

3
app/main/forms.py

@ -34,10 +34,13 @@ class ObjectiveForm(FlaskForm):
latitude = FloatField('Latitude', validators=[DataRequired(), NumberRange(min=-90, max=90)]) latitude = FloatField('Latitude', validators=[DataRequired(), NumberRange(min=-90, max=90)])
longitude = FloatField('Longitude', validators=[DataRequired(), NumberRange(min=-180, max=180)]) longitude = FloatField('Longitude', validators=[DataRequired(), NumberRange(min=-180, max=180)])
submit = SubmitField('Save') submit = SubmitField('Save')
old_name = ''
def validate_objective_name(self, objective_name): def validate_objective_name(self, objective_name):
if objective_name.data == '': if objective_name.data == '':
return return
if objective_name.data == self.old_name:
return
objective = Objective.query.filter_by(name=objective_name.data).first() objective = Objective.query.filter_by(name=objective_name.data).first()
if objective is not None: if objective is not None:
raise ValidationError('Please use a different name.') raise ValidationError('Please use a different name.')

53
app/main/routes.py

@ -13,7 +13,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 from app.utils import generate_qr_code, serve_pil_image
from app.models import User, Game, Role, GamePlayer, Objective, ObjectiveMinimalEncoder, LocationEncoder, PlayerCaughtPlayer from app.models import User, Game, Role, GamePlayer, Objective, ObjectiveMinimalEncoder, LocationEncoder, PlayerCaughtPlayer, Review
from app.main.forms import CreateGameForm, ObjectiveForm, PlayerAddForm, UserCreateForm, PlayerUpdateForm, CatchBunnyForm from app.main.forms import CreateGameForm, ObjectiveForm, PlayerAddForm, UserCreateForm, PlayerUpdateForm, CatchBunnyForm
@bp.before_app_request @bp.before_app_request
@ -35,7 +35,8 @@ def index():
def create_game(): def create_game():
form = CreateGameForm() form = CreateGameForm()
if form.validate_on_submit(): 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(name=form.game_name.data, start_time=form.start_time.data,
end_time=form.end_time.data)
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()
@ -63,17 +64,18 @@ def game_dashboard(game_name):
game = Game.query.filter_by(name=game_name).first_or_404() game = Game.query.filter_by(name=game_name).first_or_404()
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', return render_template('game_owner_dashboard.html', title='Game Dashboard', game=game,
game=game, json=json, objective_encoder=ObjectiveMinimalEncoder, location_encoder=LocationEncoder) json=json, objective_encoder=ObjectiveMinimalEncoder,
location_encoder=LocationEncoder)
if role == Role.bunny: if role == Role.bunny:
return render_template('game_bunny_dashboard.html', title='Game Dashboard', return render_template('game_bunny_dashboard.html', title='Game Dashboard', game=game,
game=game, json=json, location_encoder=LocationEncoder) json=json, location_encoder=LocationEncoder)
if role == Role.hunter: if role == Role.hunter:
return render_template('game_hunter_dashboard.html', title='Game Dashboard', return render_template('game_hunter_dashboard.html', title='Game Dashboard', game=game,
game=game, json=json, location_encoder=LocationEncoder) json=json, location_encoder=LocationEncoder)
if role == Role.none: if role == Role.none:
return render_template('game_hunter_dashboard.html', title='Game Dashboard', return render_template('game_hunter_dashboard.html', title='Game Dashboard', game=game,
game=game, json=json, location_encoder=LocationEncoder) json=json, location_encoder=LocationEncoder)
if role is None: if role is None:
abort(403) abort(403)
@ -152,6 +154,20 @@ def get_caught_bunny_photo_directory(game):
secure_filename(game.name) / \ secure_filename(game.name) / \
current_app.config['PLAYER_CAUGHT_PLAYER_PHOTO_DIR_NAME'] current_app.config['PLAYER_CAUGHT_PLAYER_PHOTO_DIR_NAME']
@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)
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:
pcp = PlayerCaughtPlayer.query.filter_by(id=pcp_id).first_or_404()
review = Review.parse_string(action)
pcp.review = review
db.session.commit()
return render_template('review_caught_bunny_photos.html', game=game)
@bp.route('/game/<game_name>/adduser', methods=['GET', 'POST']) @bp.route('/game/<game_name>/adduser', methods=['GET', 'POST'])
@bp.route('/game/<game_name>/addplayer', methods=['GET', 'POST']) @bp.route('/game/<game_name>/addplayer', methods=['GET', 'POST'])
@ -176,7 +192,8 @@ def add_player(game_name):
db.session.commit() db.session.commit()
return redirect(url_for('main.game_dashboard', game_name=game.name)) return redirect(url_for('main.game_dashboard', game_name=game.name))
return render_template('add_player.html', title=f'Add User 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>/removeuser/<username>') @bp.route('/game/<game_name>/removeuser/<username>')
@bp.route('/game/<game_name>/removeplayer/<username>') @bp.route('/game/<game_name>/removeplayer/<username>')
@ -229,7 +246,8 @@ def add_objective(game_name):
db.session.commit() db.session.commit()
flash(f"Objective has been added!") flash(f"Objective has been added!")
return redirect(url_for('main.game_dashboard', game_name=game.name)) return redirect(url_for('main.game_dashboard', game_name=game.name))
return render_template('objective.html', title=f'Add Objective for {game_name}', form=form, objective=objective, owner=True) return render_template('objective.html', title=f'Add Objective for {game_name}',
form=form, objective=objective, owner=True)
@bp.route('/objective/<objective_hash>/delete', methods=['GET']) @bp.route('/objective/<objective_hash>/delete', methods=['GET'])
@login_required @login_required
@ -269,17 +287,18 @@ def objective(objective_hash):
else: else:
flash('You have already found this objective') flash('You have already found this objective')
return redirect(url_for('main.game_dashboard', game_name=objective.game.name)) return redirect(url_for('main.game_dashboard', game_name=objective.game.name))
elif not objective.owned_by(current_user): if not objective.owned_by(current_user):
flash("Only bunnies in an objective's game can find objectives!") flash("Only bunnies in an objective's game can find objectives!")
abort(403) abort(403)
owner = objective.owned_by(current_user) qrcode = generate_qr_code(objective)
qrcode = generate_qr_code(objective) if owner else None
form = ObjectiveForm() form = ObjectiveForm()
if form.submit.data and form.validate() and owner: form.old_name = objective.name
if form.submit.data and form.validate():
objective.name = form.objective_name.data objective.name = form.objective_name.data
objective.longitude = form.longitude.data objective.longitude = form.longitude.data
objective.latitude = form.latitude.data objective.latitude = form.latitude.data
db.session.commit() db.session.commit()
return redirect(url_for('main.game_dashboard', game_name=objective.game.name)) return redirect(url_for('main.game_dashboard', game_name=objective.game.name))
return render_template('objective.html', title='Objective view', objective=objective, owner=owner, form=form, qrcode=qrcode) return render_template('objective.html', title='Objective view',
objective=objective, owner=True, form=form, qrcode=qrcode)

1
app/models/__init__.py

@ -7,5 +7,6 @@ from .notification_player import NotificationPlayer
from .objective import Objective, ObjectiveMinimalEncoder from .objective import Objective, ObjectiveMinimalEncoder
from .player_caught_player import PlayerCaughtPlayer from .player_caught_player import PlayerCaughtPlayer
from .player_found_objective import PlayerFoundObjective from .player_found_objective import PlayerFoundObjective
from .review import Review
from .role import Role from .role import Role
from .user import User from .user import User

9
app/models/game.py

@ -3,6 +3,7 @@ from app import db
from .game_state import GameState from .game_state import GameState
from .game_player import GamePlayer from .game_player import GamePlayer
from .role import Role from .role import Role
from .review import Review
class Game(db.Model): class Game(db.Model):
__tablename__ = 'game' __tablename__ = 'game'
@ -47,6 +48,12 @@ class Game(db.Model):
return [gameplayer for gameplayer in self.players if gameplayer.role == Role.bunny] return [gameplayer for gameplayer in self.players if gameplayer.role == Role.bunny]
def owned_by(self, user): def owned_by(self, user):
# pylint: disable=not-an-iterable
'''given user is an owner of this game''' '''given user is an owner of this game'''
# pylint: disable=not-an-iterable
return user in [gameplayer.user for gameplayer in self.players if gameplayer.role == Role.owner] return user in [gameplayer.user for gameplayer in self.players if gameplayer.role == Role.owner]
def unreviewed_bunny_photos(self):
# pylint: disable=not-an-iterable
return [pcp for pcps in
[player.player_caught_players for player in self.players]
for pcp in pcps if pcp.review == Review.none]

19
app/models/game_player.py

@ -7,6 +7,7 @@ from app import db
from .role import Role from .role import Role
from .notification_player import NotificationPlayer from .notification_player import NotificationPlayer
from .player_found_objective import PlayerFoundObjective from .player_found_objective import PlayerFoundObjective
from .review import Review
class GamePlayer(db.Model): class GamePlayer(db.Model):
__tablename__ = 'game_player' __tablename__ = 'game_player'
@ -49,10 +50,20 @@ class GamePlayer(db.Model):
objectives = ['['] objectives = ['[']
for objective in self.game.objectives: for objective in self.game.objectives:
obj = { obj = {
'name' : objective.name, 'name' : objective.name,
'longitude' : objective.longitude, 'longitude' : objective.longitude,
'latitude' : objective.latitude, 'latitude' : objective.latitude,
'found' : objective in self.found_objectives} 'found' : objective in self.found_objectives}
objectives.append(json.dumps(obj)) objectives.append(json.dumps(obj))
objectives.append(',') objectives.append(',')
return ''.join(objectives)[:-1] + ']' return ''.join(objectives)[:-1] + ']'
def accepted_caught_players(self):
return [pcp.caught_player
for pcp in self.player_caught_players
if pcp.review == Review.accepted]
def accepted_caught_by_players(self):
return [pcp.catching_player
for pcp in self.player_caught_by_players
if pcp.review == Review.accepted]

4
app/models/player_caught_player.py

@ -2,6 +2,7 @@ from sqlalchemy.sql import func
from app import db from app import db
from .game_player import GamePlayer from .game_player import GamePlayer
from .review import Review
class PlayerCaughtPlayer(db.Model): class PlayerCaughtPlayer(db.Model):
__tablename__ = 'player_caught_player' __tablename__ = 'player_caught_player'
@ -9,6 +10,7 @@ class PlayerCaughtPlayer(db.Model):
catching_player_id = db.Column(db.Integer, db.ForeignKey('game_player.id'), nullable=False) catching_player_id = db.Column(db.Integer, db.ForeignKey('game_player.id'), nullable=False)
caught_player_id = db.Column(db.Integer, db.ForeignKey('game_player.id'), nullable=False) caught_player_id = db.Column(db.Integer, db.ForeignKey('game_player.id'), nullable=False)
timestamp = db.Column(db.DateTime, server_default=func.now(), nullable=False) timestamp = db.Column(db.DateTime, server_default=func.now(), nullable=False)
review = db.Column(db.Enum(Review), server_default=Review(0).name, nullable=False)
catching_player = db.relationship('GamePlayer', primaryjoin=(catching_player_id == GamePlayer.id), catching_player = db.relationship('GamePlayer', primaryjoin=(catching_player_id == GamePlayer.id),
backref=db.backref('player_caught_players', cascade='save-update, merge, delete, delete-orphan')) backref=db.backref('player_caught_players', cascade='save-update, merge, delete, delete-orphan'))
caught_player = db.relationship('GamePlayer', primaryjoin=(caught_player_id == GamePlayer.id), caught_player = db.relationship('GamePlayer', primaryjoin=(caught_player_id == GamePlayer.id),
@ -23,4 +25,4 @@ pc = PlayerCaughtPlayer(caught_player=p2, catching_player=p1)
db.session.add(pc) db.session.add(pc)
db.session.commit() db.session.commit()
''' '''

14
app/models/review.py

@ -0,0 +1,14 @@
from enum import Enum
class Review(Enum):
none = 0
denied = 1
accepted = 2
@classmethod
def parse_string(cls, string):
if string == 'accept' or string == 'accepted':
return cls.accepted
if string == 'deny' or string == 'denied':
return cls.denied
return cls.none

1
app/templates/_game_player_info.html

@ -17,6 +17,5 @@
<th>End Time</th> <th>End Time</th>
<td>{% if game.end_time %}{{ moment(game.end_time).format('DD-MM-YYYY, hh:mm')}}{% else %}-{% endif %}</td> <td>{% if game.end_time %}{{ moment(game.end_time).format('DD-MM-YYYY, hh:mm')}}{% else %}-{% endif %}</td>
</tr> </tr>
</table> </table>
</div> </div>

37
app/templates/_review_photo.html

@ -0,0 +1,37 @@
<div class="row">
<div class="col-md-4">
<div class="row">
<div class="table-responsive">
<table class="table">
<tr>
<th>Hunter</th>
<td>{{ pcp.catching_player.user.name }}</td>
</tr>
<tr>
<th>Bunny</th>
<td>{{ pcp.caught_player.user.name }}</td>
</tr>
<tr>
<th>Time</th>
<td>{{ pcp.timestamp.strftime('%Y-%m-%d %H:%M') }}</td>
</tr>
</table>
</div>
</div>
<div class="row">
<a href="{{ url_for('main.review_caught_bunny_photos', game_name=game.name, pcp_id=pcp.id, action='accept') }}">
<button class="btn btn-success">Accept</button>
</a>
<a href="{{ url_for('main.review_caught_bunny_photos', game_name=game.name, pcp_id=pcp.id, action='deny') }}">
<button class="btn btn-danger">Reject</button>
</a>
</div>
</div>
<div class="col-md-8">
<img src="{{ url_for('main.caught_bunny_photo', game_name=game.name,
timestamp=pcp.timestamp.strftime('%Y%m%d%H%M%S'),
bunny_name=pcp.caught_player.user.name,
hunter_name=pcp.catching_player.user.name) }}"
alt="could not load photo", width="100%">
</div>
</div>

12
app/templates/game_hunter_dashboard.html

@ -19,16 +19,24 @@
<thead> <thead>
<tr> <tr>
<th scope="col">Player Name</th> <th scope="col">Player Name</th>
<th scope="col">Times Caught</th> <th scope="col">Times Caught
<span style="font-size: smaller;">
(<span style="color:green;">Accepted</span>/<span style="color:red;">Denied</span>/<span style="color:gray;">Not reviewed</span>)
</span>
</th>
<th scope="col">Last location</th> <th scope="col">Last location</th>
<th scope="col"></th> <th scope="col"></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% set player = current_user.player_in(game) %}
{% for bunny in game.bunnies() %} {% for bunny in game.bunnies() %}
<tr> <tr>
<td>{{ bunny.user.name }}</td> <td>{{ bunny.user.name }}</td>
<td>{{ bunny.player_caught_by_players | selectattr('catching_player', '==', current_user.player_in(game)) |list|length}}</td> <td><span style="color:green;">{{ bunny.player_caught_by_players | selectattr('catching_player', '==', player) | selectattr('review.name', '==', 'accepted') |list|length}}</span> /
<span style="color:red;">{{ bunny.player_caught_by_players | selectattr('catching_player', '==', player) | selectattr('review.name', '==', 'denied') |list|length}}</span> /
<span style="color:gray;">{{ bunny.player_caught_by_players | selectattr('catching_player', '==', player) | selectattr('review.name', '==', 'none') |list|length}}</span>
</td>
<td>{% with location = bunny.last_location() %} <td>{% with location = bunny.last_location() %}
{% if location %}{{ moment(location.timestamp).fromNow()}}: {% endif %} {% if location %}{{ moment(location.timestamp).fromNow()}}: {% endif %}
{{ location }} {{ location }}

10
app/templates/game_owner_dashboard.html

@ -9,7 +9,11 @@
{% block app_content %} {% block app_content %}
<h1>{{ game.name }} Dashboard</h1> <h1>{{ game.name }} Dashboard</h1>
{% if game.unreviewed_bunny_photos() %}
<a href="{{ url_for('main.review_caught_bunny_photos', game_name=game.name) }}">
<button class="btn btn-primary">Review Bunny Photos</button>
</a>
{% endif %}
<h2>Players:</h2> <h2>Players:</h2>
<p><a href="{{ url_for('main.add_player', game_name = game.name) }}">Add player</a></p> <p><a href="{{ url_for('main.add_player', game_name = game.name) }}">Add player</a></p>
<div class="table-responsive"> <div class="table-responsive">
@ -31,8 +35,8 @@
<td><a href="{{ url_for('main.game_player', game_name = game.name, username = player.user.name) }}">{{ player.user.name }}</a></td> <td><a href="{{ url_for('main.game_player', game_name = game.name, username = player.user.name) }}">{{ player.user.name }}</a></td>
<td>{{ player.role.name }}</td> <td>{{ player.role.name }}</td>
<td>{{ player.found_objectives | list | length }}</td> <td>{{ player.found_objectives | list | length }}</td>
<td>{{ player.caught_players | list | length }}</td> <td>{{ player.accepted_caught_players() | list | length }}</td>
<td>{{ player.caught_by_players | list | length }}</td> <td>{{ player.accepted_caught_by_players() | list | length }}</td>
<td>{% with location = player.last_location() %} <td>{% with location = player.last_location() %}
{% if location %}{{ moment(location.timestamp).fromNow()}}: {% endif %} {% if location %}{{ moment(location.timestamp).fromNow()}}: {% endif %}
{{ location }} {{ location }}

6
app/templates/game_player.html

@ -16,13 +16,13 @@
{{ wtf.quick_form(form, button_map={'submit': 'primary'}) }} {{ wtf.quick_form(form, button_map={'submit': 'primary'}) }}
</div> </div>
{% if player.user.auth_hash and not player.user.last_login %} {% if player.user.auth_hash and not player.user.last_login and not player.user.password_hash %}
<div class="row"> <div class="row">
<a href="{{ url_for('auth.user_hash_login', auth_hash=player.user.auth_hash) }}"> <a href="{{ url_for('auth.user_hash_login', auth_hash=player.user.auth_hash) }}">
<img src="{{ url_for('auth.user_qrcode', auth_hash=player.user.auth_hash) }}" alt="qr_code_failed" width="80%" title="login code for {{ player.user.name }}"> <img src="{{ url_for('auth.user_qrcode', auth_hash=player.user.auth_hash) }}" alt="qr_code_failed" width="80%" title="login code for {{ player.user.name }}">
</a> </a>
</div> </div>
{% elif not player.user.last_login %} {% elif not player.user.last_login and not player.user.password_hash %}
<br> <br>
<div class="row"> <div class="row">
<a href="#" , id="generate_auth_hash"> <a href="#" , id="generate_auth_hash">
@ -40,6 +40,7 @@
<thead> <thead>
<tr> <tr>
<th scope="col">Player Name</th> <th scope="col">Player Name</th>
<th scope="col">Review</th>
<th scope="col">Time</th> <th scope="col">Time</th>
<th scope="col"></th> <th scope="col"></th>
</tr> </tr>
@ -50,6 +51,7 @@
<td><a <td><a
href="{{ url_for('main.game_player', game_name=player.game.name, username = pcp.caught_player.user.name) }}">{{ pcp.caught_player.user.name }}</a> href="{{ url_for('main.game_player', game_name=player.game.name, username = pcp.caught_player.user.name) }}">{{ pcp.caught_player.user.name }}</a>
</td> </td>
<td>{{ pcp.review.name.title() }}</td>
<td>{{ moment(pcp.timestamp).fromNow() }}</td> <td>{{ moment(pcp.timestamp).fromNow() }}</td>
<td><a href="{{ url_for('main.caught_bunny_photo', <td><a href="{{ url_for('main.caught_bunny_photo',
game_name=player.game.name, game_name=player.game.name,

14
app/templates/review_caught_bunny_photos.html

@ -0,0 +1,14 @@
{% extends "base.html" %}
{% block app_content %}
<div class="col-md-1"></div>
<div class="col-md-10">
<h1>Bunny Photo Review</h1>
<hr>
{% for pcp in game.unreviewed_bunny_photos() %}
{% include '_review_photo.html' %}
<hr>
{% endfor %}
</div>
<div class="col.md-1"></div>
{% endblock %}

18
the_hunt.py

@ -1,6 +1,6 @@
from app import create_app, db from app import create_app, db
from app.models import Game, User, Objective, Location, Notification, GamePlayer, \ from app.models import Game, User, Objective, Location, Notification, GamePlayer, \
PlayerFoundObjective, NotificationPlayer, PlayerCaughtPlayer, Role, GameState PlayerFoundObjective, NotificationPlayer, PlayerCaughtPlayer, Role, GameState, Review
app = create_app() app = create_app()
@ -10,7 +10,7 @@ def make_shell_context():
'Location' : Location, 'Notification' : Notification, 'GamePlayer' : GamePlayer, 'Location' : Location, 'Notification' : Notification, 'GamePlayer' : GamePlayer,
'PlayerFoundObjective' : PlayerFoundObjective, 'NotificationPlayer' : NotificationPlayer, 'PlayerFoundObjective' : PlayerFoundObjective, 'NotificationPlayer' : NotificationPlayer,
'PlayerCaughtPlayer' : PlayerCaughtPlayer, 'Role' : Role, 'GameState' : GameState, 'PlayerCaughtPlayer' : PlayerCaughtPlayer, 'Role' : Role, 'GameState' : GameState,
'create_objects' : create_objects} 'Review' : Review, 'create_objects' : create_objects}
def create_objects(): def create_objects():
g1 = Game(name='TestGame') g1 = Game(name='TestGame')
@ -24,16 +24,28 @@ def create_objects():
u4 = User(name='Emma') u4 = User(name='Emma')
u5 = User(name='Demi') u5 = User(name='Demi')
o1 = Objective(name='Florin', latitude=52.0932, longitude=5.12405)
o2 = Objective(name='Amsterdam', latitude=52.35547, longitude=5.12405)
o3 = Objective(name='Amersfoort', latitude=52.17056, longitude=5.12405)
o1.set_hash()
o2.set_hash()
o3.set_hash()
g1.players.append(GamePlayer(user=u1, role=Role.owner)) g1.players.append(GamePlayer(user=u1, role=Role.owner))
g1.players.append(GamePlayer(user=u2, role=Role.hunter)) g1.players.append(GamePlayer(user=u2, role=Role.hunter))
g1.players.append(GamePlayer(user=u3, role=Role.hunter)) g1.players.append(GamePlayer(user=u3, role=Role.hunter))
g1.players.append(GamePlayer(user=u4, role=Role.bunny)) g1.players.append(GamePlayer(user=u4, role=Role.bunny))
g1.players.append(GamePlayer(user=u5, role=Role.bunny)) g1.players.append(GamePlayer(user=u5, role=Role.bunny))
g1.objectives.append(o1)
g1.objectives.append(o2)
g1.objectives.append(o3)
g2.players.append(GamePlayer(user=u1, role=Role.bunny)) g2.players.append(GamePlayer(user=u1, role=Role.bunny))
g2.players.append(GamePlayer(user=u2, role=Role.owner)) g2.players.append(GamePlayer(user=u2, role=Role.owner))
g2.players.append(GamePlayer(user=u3, role=Role.hunter)) g2.players.append(GamePlayer(user=u3, role=Role.hunter))
db.session.add(g1) db.session.add(g1)
db.session.add(g2) db.session.add(g2)
db.session.commit() db.session.commit()

Loading…
Cancel
Save