Burathar
5 years ago
13 changed files with 308 additions and 268 deletions
@ -1,268 +0,0 @@ |
|||||||
from enum import Enum |
|
||||||
from secrets import token_hex |
|
||||||
from datetime import datetime |
|
||||||
from json import JSONEncoder |
|
||||||
from flask_login import UserMixin |
|
||||||
from werkzeug.security import generate_password_hash, check_password_hash |
|
||||||
from sqlalchemy.ext.associationproxy import association_proxy |
|
||||||
from sqlalchemy.sql import func |
|
||||||
from flask_moment import Moment |
|
||||||
from app import db, login |
|
||||||
|
|
||||||
moment = Moment() |
|
||||||
|
|
||||||
class Role(Enum): |
|
||||||
none = 0 |
|
||||||
owner = 1 |
|
||||||
hunter = 2 |
|
||||||
bunny = 3 |
|
||||||
|
|
||||||
class GameState(Enum): |
|
||||||
initiated = 1 |
|
||||||
published = 2 |
|
||||||
started = 3 |
|
||||||
interrupted = 4 |
|
||||||
finished = 5 |
|
||||||
|
|
||||||
class GamePlayer(db.Model): |
|
||||||
__tablename__ = 'game_player' |
|
||||||
game_id = db.Column(db.Integer, db.ForeignKey('game.id'), primary_key=True, nullable=False) |
|
||||||
player_id = db.Column(db.Integer, db.ForeignKey('player.id'), primary_key=True, nullable=False) |
|
||||||
role = db.Column(db.Enum(Role), server_default=Role(0).name, nullable=False) |
|
||||||
game = db.relationship('Game', back_populates='game_players') |
|
||||||
player = db.relationship('Player', back_populates='player_games') |
|
||||||
|
|
||||||
class PlayerFoundObjective(db.Model): |
|
||||||
__tablename__ = 'player_found_objective' |
|
||||||
objective_id = db.Column(db.Integer, db.ForeignKey('objective.id'), primary_key=True, nullable=False, server_default='-1') |
|
||||||
player_id = db.Column(db.Integer, db.ForeignKey('player.id'), primary_key=True, nullable=False, server_default='-1') |
|
||||||
timestamp = db.Column(db.DateTime, server_default=func.now(), nullable=False) |
|
||||||
player = db.relationship('Player', back_populates='player_found_objectives') |
|
||||||
objective = db.relationship('Objective', back_populates='objective_found_by') |
|
||||||
|
|
||||||
class NotificationPlayer(db.Model): |
|
||||||
__tablename__ = 'notification_player' |
|
||||||
notification_id = db.Column(db.Integer, db.ForeignKey('notification.id'), primary_key=True, nullable=False) |
|
||||||
player_id= db.Column(db.Integer, db.ForeignKey('player.id'), primary_key=True, nullable=False) |
|
||||||
been_shown = db.Column(db.Boolean, server_default='True', nullable=False) |
|
||||||
notification = db.relationship('Notification', back_populates='notification_recipients') |
|
||||||
recipient = db.relationship('Player', back_populates='player_notifications') |
|
||||||
|
|
||||||
class PlayerCaughtPlayer(db.Model): |
|
||||||
__tablename__ = 'player_caught_player' |
|
||||||
id = db.Column(db.Integer, primary_key=True, autoincrement=True, server_default='-1') |
|
||||||
catching_player_id = db.Column(db.Integer, db.ForeignKey('player.id'), nullable=False) |
|
||||||
caught_player_id = db.Column(db.Integer, db.ForeignKey('player.id'), nullable=False) |
|
||||||
photo_reference = db.Column(db.String(128), unique=True, nullable=False) |
|
||||||
timestamp = db.Column(db.DateTime, server_default=func.now(), nullable=False) |
|
||||||
catching_player = db.relationship('Player', back_populates='player_caught_by_players', foreign_keys=[catching_player_id]) |
|
||||||
caught_player = db.relationship('Player', back_populates='player_caught_players', foreign_keys=[caught_player_id]) |
|
||||||
|
|
||||||
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) |
|
||||||
start_time = db.Column(db.DateTime) |
|
||||||
end_time = db.Column(db.DateTime) |
|
||||||
game_players = db.relationship( |
|
||||||
'GamePlayer', |
|
||||||
back_populates='game', |
|
||||||
cascade="save-update, merge, delete, delete-orphan") |
|
||||||
players = association_proxy('game_players', 'player', |
|
||||||
creator=lambda player: GamePlayer(player=player)) # to enable game.players.append(player) |
|
||||||
objectives = db.relationship( |
|
||||||
'Objective', |
|
||||||
lazy='select', |
|
||||||
backref=db.backref('game', lazy='joined')) |
|
||||||
notifications = db.relationship( |
|
||||||
'Notification', |
|
||||||
lazy='select', |
|
||||||
backref=db.backref('game', lazy='joined')) |
|
||||||
|
|
||||||
def last_player_locations(self): |
|
||||||
return [player.last_location(self) for player in self.players if player.locations] |
|
||||||
|
|
||||||
def bunnies(self): |
|
||||||
# pylint: disable=not-an-iterable |
|
||||||
return [gameplayer.player for gameplayer in self.game_players if gameplayer.role == Role.bunny] |
|
||||||
|
|
||||||
def last_locations(self, players): |
|
||||||
locations = [] |
|
||||||
for player in players: |
|
||||||
locations.append(player.last_location(self)) |
|
||||||
return locations |
|
||||||
|
|
||||||
def owned_by(self, player): |
|
||||||
# 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] |
|
||||||
|
|
||||||
class Player(UserMixin, db.Model): |
|
||||||
""" !Always call set_auth_hash() after creating new instance! """ |
|
||||||
__tablename__ = 'player' |
|
||||||
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) |
|
||||||
password_hash = db.Column(db.String(128)) |
|
||||||
|
|
||||||
player_games = db.relationship( |
|
||||||
'GamePlayer', |
|
||||||
back_populates='player', |
|
||||||
cascade='save-update, merge, delete, delete-orphan') |
|
||||||
games = association_proxy('player_games', 'game', |
|
||||||
creator=lambda game: GamePlayer(game=game)) |
|
||||||
|
|
||||||
player_found_objectives = db.relationship( |
|
||||||
'PlayerFoundObjective', |
|
||||||
back_populates='player', |
|
||||||
cascade='save-update, merge, delete, delete-orphan') |
|
||||||
found_objectives = association_proxy('player_found_objectives', 'objective', |
|
||||||
creator=lambda objective: PlayerFoundObjective(objective=objective)) |
|
||||||
|
|
||||||
player_notifications = db.relationship( |
|
||||||
'NotificationPlayer', |
|
||||||
back_populates='recipient', |
|
||||||
cascade='save-update, merge, delete, delete-orphan') |
|
||||||
notifications = association_proxy('player_notifications', 'notification', |
|
||||||
creator=lambda notification: NotificationPlayer(notification=notification)) |
|
||||||
|
|
||||||
player_caught_players = db.relationship( |
|
||||||
'PlayerCaughtPlayer', |
|
||||||
back_populates='catching_player', |
|
||||||
cascade='save-update, merge, delete, delete-orphan', |
|
||||||
foreign_keys=[PlayerCaughtPlayer.catching_player_id]) |
|
||||||
caught_players = association_proxy('player_caught_players', 'player', |
|
||||||
creator=lambda player: PlayerCaughtPlayer(caught_player=player)) |
|
||||||
|
|
||||||
player_caught_by_players = db.relationship( |
|
||||||
'PlayerCaughtPlayer', |
|
||||||
back_populates='caught_player', |
|
||||||
cascade='save-update, merge, delete, delete-orphan', |
|
||||||
foreign_keys=[PlayerCaughtPlayer.caught_player_id]) |
|
||||||
caught_by_players = association_proxy('player_caught_by_players', 'player', |
|
||||||
creator=lambda player: PlayerCaughtPlayer(catching_player=player)) |
|
||||||
|
|
||||||
locations = db.relationship( |
|
||||||
'Location', |
|
||||||
lazy='select', |
|
||||||
backref=db.backref('player', lazy='joined')) |
|
||||||
|
|
||||||
def set_password(self, password): |
|
||||||
self.password_hash = generate_password_hash(password) |
|
||||||
|
|
||||||
def set_auth_hash(self): |
|
||||||
self.auth_hash = token_hex(16) |
|
||||||
|
|
||||||
def check_password(self, password): |
|
||||||
return check_password_hash(self.password_hash, password) |
|
||||||
|
|
||||||
def locations_during_game(self, game): |
|
||||||
# pylint: disable=not-an-iterable |
|
||||||
if not self.locations: |
|
||||||
return None |
|
||||||
if game is None: |
|
||||||
return self.locations |
|
||||||
game_start = game.start_time or datetime.min |
|
||||||
game_end = game.end_time or datetime.max |
|
||||||
return (location for location in self.locations if location.timestamp > game_start and location.timestamp < game_end) |
|
||||||
|
|
||||||
def last_location(self, game=None): |
|
||||||
# pylint: disable=not-an-iterable |
|
||||||
if not self.locations: |
|
||||||
return None |
|
||||||
if game is None: |
|
||||||
return max(self.locations, key=lambda location: location.timestamp) |
|
||||||
return max(self.locations_during_game(game), key=lambda location: location.timestamp) |
|
||||||
|
|
||||||
def role_in_game(self, game): |
|
||||||
'''returns the role as Role enum of player in given game. Returns None if player does not participate in game''' |
|
||||||
# pylint: disable=not-an-iterable |
|
||||||
gameplayers = [gameplayer for gameplayer in self.player_games if gameplayer.game == game] |
|
||||||
if not gameplayers: |
|
||||||
return None |
|
||||||
return gameplayers[0].role |
|
||||||
|
|
||||||
def owns_game_played_by(self, player): |
|
||||||
'''self is an owner of a game the player participates in''' |
|
||||||
return self in [gameplayer.player for gameplayers in |
|
||||||
[game.game_players for game in player.games] |
|
||||||
for gameplayer in gameplayers if gameplayer.role == Role.owner] |
|
||||||
|
|
||||||
@staticmethod |
|
||||||
def delete_orphans(): |
|
||||||
Player.query.filter(~Player.player_games.any()).delete() |
|
||||||
db.session.commit() |
|
||||||
|
|
||||||
@login.user_loader |
|
||||||
def load_user(id): |
|
||||||
return Player.query.get(int(id)) |
|
||||||
|
|
||||||
class Objective(db.Model): |
|
||||||
""" !Always call set_hash after() creating new instance! """ |
|
||||||
__tablename__ = 'objective' |
|
||||||
id = db.Column(db.Integer, primary_key=True) |
|
||||||
name = db.Column(db.String(64)) |
|
||||||
game_id = db.Column(db.Integer, db.ForeignKey('game.id'), nullable=False) |
|
||||||
hash = db.Column(db.String(32), unique=True, nullable=False) |
|
||||||
longitude = db.Column(db.Numeric(precision=15, scale=10, asdecimal=False, decimal_return_scale=None)) # maybe check asdecimal and decimal_return_scale later? |
|
||||||
latitude = db.Column(db.Numeric(precision=15, scale=10, asdecimal=False, decimal_return_scale=None)) |
|
||||||
objective_found_by = db.relationship( |
|
||||||
'PlayerFoundObjective', |
|
||||||
back_populates='objective', |
|
||||||
cascade='save-update, merge, delete, delete-orphan') |
|
||||||
found_by = association_proxy('objective_found_by', 'player', |
|
||||||
creator=lambda player: PlayerFoundObjective(player=player)) |
|
||||||
|
|
||||||
def set_hash(self): |
|
||||||
self.hash = token_hex(16) |
|
||||||
|
|
||||||
def owned_by(self, player): |
|
||||||
'''given player is an owner of a game object is part of''' |
|
||||||
return player in [gameplayer.player for gameplayer in self.game.game_players if gameplayer.role == Role.owner] |
|
||||||
|
|
||||||
class ObjectiveMinimalEncoder(JSONEncoder): |
|
||||||
def default(self, objective): |
|
||||||
return { |
|
||||||
'name' : objective.name, |
|
||||||
'hash' : objective.hash, |
|
||||||
'longitude' : objective.longitude, |
|
||||||
'latitude' : objective.latitude |
|
||||||
} |
|
||||||
|
|
||||||
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) |
|
||||||
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) |
|
||||||
|
|
||||||
def __str__(self): |
|
||||||
return f'{self.longitude}, {self.latitude}' |
|
||||||
|
|
||||||
class LocationEncoder(JSONEncoder): |
|
||||||
def default(self, location): |
|
||||||
return { |
|
||||||
'player_name' : location.player.name, |
|
||||||
'longitude' : location.longitude, |
|
||||||
'latitude' : location.latitude, |
|
||||||
'timestamp_utc' : str(location.timestamp) |
|
||||||
} |
|
||||||
|
|
||||||
class Notification(db.Model): |
|
||||||
__tablename__ = 'notification' |
|
||||||
id = db.Column(db.Integer, primary_key=True) |
|
||||||
game_id = db.Column(db.Integer, db.ForeignKey('game.id'), nullable=False) |
|
||||||
message = db.Column(db.Text, nullable=False) |
|
||||||
type = db.Column(db.String(64), nullable=False, default='General') |
|
||||||
timestamp = db.Column(db.DateTime, server_default=func.now(), nullable=False) |
|
||||||
|
|
||||||
notification_recipients = db.relationship( |
|
||||||
'NotificationPlayer', |
|
||||||
back_populates='notification', |
|
||||||
cascade='save-update, merge, delete, delete-orphan') |
|
||||||
recipients = association_proxy('notification_recipients', 'player', |
|
||||||
creator=lambda player: NotificationPlayer(recipient=player)) |
|
||||||
|
|
||||||
#start_time = db.Column(db.DateTime, server_default=func.now()) |
|
@ -0,0 +1,16 @@ |
|||||||
|
from app import login |
||||||
|
|
||||||
|
from app.models.game import Game |
||||||
|
from app.models.game_player import GamePlayer |
||||||
|
from app.models.game_state import GameState |
||||||
|
from app.models.location import Location, LocationEncoder |
||||||
|
from app.models.notification_player import NotificationPlayer |
||||||
|
from app.models.objective import Objective, ObjectiveMinimalEncoder |
||||||
|
from app.models.player_caught_player import PlayerCaughtPlayer |
||||||
|
from app.models.player_found_objective import PlayerFoundObjective |
||||||
|
from app.models.role import Role |
||||||
|
from app.models.user import User |
||||||
|
|
||||||
|
@login.user_loader |
||||||
|
def load_user(id): |
||||||
|
return User.query.get(int(id)) |
@ -0,0 +1,43 @@ |
|||||||
|
from sqlalchemy.ext.associationproxy import association_proxy |
||||||
|
from app import db |
||||||
|
from app.models import GameState, GamePlayer, Role |
||||||
|
|
||||||
|
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) |
||||||
|
start_time = db.Column(db.DateTime) |
||||||
|
end_time = db.Column(db.DateTime) |
||||||
|
game_players = db.relationship( |
||||||
|
'GamePlayer', |
||||||
|
back_populates='game', |
||||||
|
cascade="save-update, merge, delete, delete-orphan") |
||||||
|
users = association_proxy('game_players', 'user', |
||||||
|
creator=lambda user: GamePlayer(user=user)) # to enable game.players.append(player) |
||||||
|
objectives = db.relationship( |
||||||
|
'Objective', |
||||||
|
lazy='select', |
||||||
|
backref=db.backref('game', lazy='joined')) |
||||||
|
notifications = db.relationship( |
||||||
|
'Notification', |
||||||
|
lazy='select', |
||||||
|
backref=db.backref('game', lazy='joined')) |
||||||
|
|
||||||
|
def last_player_locations(self): |
||||||
|
return [player.last_location(self) for player in self.users if player.locations] |
||||||
|
|
||||||
|
def bunnies(self): |
||||||
|
# pylint: disable=not-an-iterable |
||||||
|
return [gameplayer.player for gameplayer in self.game_players if gameplayer.role == Role.bunny] |
||||||
|
|
||||||
|
def last_locations(self, players): |
||||||
|
locations = [] |
||||||
|
for player in players: |
||||||
|
locations.append(player.last_location(self)) |
||||||
|
return locations |
||||||
|
|
||||||
|
def owned_by(self, player): |
||||||
|
# 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] |
@ -0,0 +1,10 @@ |
|||||||
|
from app import db |
||||||
|
from app.models import Role |
||||||
|
|
||||||
|
class GamePlayer(db.Model): |
||||||
|
__tablename__ = 'game_player' |
||||||
|
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) |
||||||
|
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') |
@ -0,0 +1,9 @@ |
|||||||
|
from enum import Enum |
||||||
|
|
||||||
|
class GameState(Enum): |
||||||
|
initiated = 1 |
||||||
|
published = 2 |
||||||
|
started = 3 |
||||||
|
interrupted = 4 |
||||||
|
finished = 5 |
||||||
|
|
@ -0,0 +1,24 @@ |
|||||||
|
from json import JSONEncoder |
||||||
|
from sqlalchemy.sql import func |
||||||
|
|
||||||
|
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) |
||||||
|
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) |
||||||
|
|
||||||
|
def __str__(self): |
||||||
|
return f'{self.longitude}, {self.latitude}' |
||||||
|
|
||||||
|
class LocationEncoder(JSONEncoder): |
||||||
|
def default(self, location): |
||||||
|
return { |
||||||
|
'player_name' : location.player.name, |
||||||
|
'longitude' : location.longitude, |
||||||
|
'latitude' : location.latitude, |
||||||
|
'timestamp_utc' : str(location.timestamp) |
||||||
|
} |
@ -0,0 +1,20 @@ |
|||||||
|
from sqlalchemy.ext.associationproxy import association_proxy |
||||||
|
from sqlalchemy.sql import func |
||||||
|
|
||||||
|
from app import db |
||||||
|
from app.models import NotificationPlayer |
||||||
|
|
||||||
|
class Notification(db.Model): |
||||||
|
__tablename__ = 'notification' |
||||||
|
id = db.Column(db.Integer, primary_key=True) |
||||||
|
game_id = db.Column(db.Integer, db.ForeignKey('game.id'), nullable=False) |
||||||
|
message = db.Column(db.Text, nullable=False) |
||||||
|
type = db.Column(db.String(64), nullable=False, default='General') |
||||||
|
timestamp = db.Column(db.DateTime, server_default=func.now(), nullable=False) |
||||||
|
|
||||||
|
notification_recipients = db.relationship( |
||||||
|
'NotificationPlayer', |
||||||
|
back_populates='notification', |
||||||
|
cascade='save-update, merge, delete, delete-orphan') |
||||||
|
recipients = association_proxy('notification_recipients', 'player', |
||||||
|
creator=lambda player: NotificationPlayer(recipient=player)) |
@ -0,0 +1,10 @@ |
|||||||
|
from app import db |
||||||
|
|
||||||
|
class NotificationPlayer(db.Model): |
||||||
|
__tablename__ = 'notification_player' |
||||||
|
notification_id = db.Column(db.Integer, db.ForeignKey('notification.id'), primary_key=True, nullable=False) |
||||||
|
player_id = db.Column(db.Integer, db.ForeignKey('player.id'), primary_key=True, nullable=False) |
||||||
|
been_shown = db.Column(db.Boolean, server_default='True', nullable=False) |
||||||
|
notification = db.relationship('Notification', back_populates='notification_recipients') |
||||||
|
recipient = db.relationship('Player', back_populates='player_notifications') |
||||||
|
|
@ -0,0 +1,38 @@ |
|||||||
|
from secrets import token_hex |
||||||
|
from json import JSONEncoder |
||||||
|
from sqlalchemy.ext.associationproxy import association_proxy |
||||||
|
|
||||||
|
from app import db |
||||||
|
from app.models import PlayerFoundObjective, Role |
||||||
|
|
||||||
|
class Objective(db.Model): |
||||||
|
""" !Always call set_hash after() creating new instance! """ |
||||||
|
__tablename__ = 'objective' |
||||||
|
id = db.Column(db.Integer, primary_key=True) |
||||||
|
name = db.Column(db.String(64)) |
||||||
|
game_id = db.Column(db.Integer, db.ForeignKey('game.id'), nullable=False) |
||||||
|
hash = db.Column(db.String(32), unique=True, nullable=False) |
||||||
|
longitude = db.Column(db.Numeric(precision=15, scale=10, asdecimal=False, decimal_return_scale=None)) # maybe check asdecimal and decimal_return_scale later? |
||||||
|
latitude = db.Column(db.Numeric(precision=15, scale=10, asdecimal=False, decimal_return_scale=None)) |
||||||
|
objective_found_by = db.relationship( |
||||||
|
'PlayerFoundObjective', |
||||||
|
back_populates='objective', |
||||||
|
cascade='save-update, merge, delete, delete-orphan') |
||||||
|
found_by = association_proxy('objective_found_by', 'player', |
||||||
|
creator=lambda player: PlayerFoundObjective(player=player)) |
||||||
|
|
||||||
|
def set_hash(self): |
||||||
|
self.hash = token_hex(16) |
||||||
|
|
||||||
|
def owned_by(self, player): |
||||||
|
'''given player is an owner of a game object is part of''' |
||||||
|
return player in [gameplayer.player for gameplayer in self.game.game_players if gameplayer.role == Role.owner] |
||||||
|
|
||||||
|
class ObjectiveMinimalEncoder(JSONEncoder): |
||||||
|
def default(self, objective): |
||||||
|
return { |
||||||
|
'name' : objective.name, |
||||||
|
'hash' : objective.hash, |
||||||
|
'longitude' : objective.longitude, |
||||||
|
'latitude' : objective.latitude |
||||||
|
} |
@ -0,0 +1,14 @@ |
|||||||
|
from sqlalchemy.sql import func |
||||||
|
|
||||||
|
from app import db |
||||||
|
|
||||||
|
class PlayerCaughtPlayer(db.Model): |
||||||
|
__tablename__ = 'player_caught_player' |
||||||
|
id = db.Column(db.Integer, primary_key=True, autoincrement=True, server_default='-1') |
||||||
|
catching_player_id = db.Column(db.Integer, db.ForeignKey('player.id'), nullable=False) |
||||||
|
caught_player_id = db.Column(db.Integer, db.ForeignKey('player.id'), nullable=False) |
||||||
|
photo_reference = db.Column(db.String(128), unique=True, nullable=False) |
||||||
|
timestamp = db.Column(db.DateTime, server_default=func.now(), nullable=False) |
||||||
|
catching_player = db.relationship('Player', back_populates='player_caught_by_players', foreign_keys=[catching_player_id]) |
||||||
|
caught_player = db.relationship('Player', back_populates='player_caught_players', foreign_keys=[caught_player_id]) |
||||||
|
|
@ -0,0 +1,11 @@ |
|||||||
|
from sqlalchemy.sql import func |
||||||
|
|
||||||
|
from app import db |
||||||
|
|
||||||
|
class PlayerFoundObjective(db.Model): |
||||||
|
__tablename__ = 'player_found_objective' |
||||||
|
objective_id = db.Column(db.Integer, db.ForeignKey('objective.id'), primary_key=True, nullable=False, server_default='-1') |
||||||
|
player_id = db.Column(db.Integer, db.ForeignKey('player.id'), primary_key=True, nullable=False, server_default='-1') |
||||||
|
timestamp = db.Column(db.DateTime, server_default=func.now(), nullable=False) |
||||||
|
player = db.relationship('Player', back_populates='player_found_objectives') |
||||||
|
objective = db.relationship('Objective', back_populates='objective_found_by') |
@ -0,0 +1,8 @@ |
|||||||
|
from enum import Enum |
||||||
|
|
||||||
|
class Role(Enum): |
||||||
|
none = 0 |
||||||
|
owner = 1 |
||||||
|
hunter = 2 |
||||||
|
bunny = 3 |
||||||
|
|
@ -0,0 +1,105 @@ |
|||||||
|
from secrets import token_hex |
||||||
|
from datetime import datetime |
||||||
|
|
||||||
|
from flask_login import UserMixin |
||||||
|
from sqlalchemy.ext.associationproxy import association_proxy |
||||||
|
from werkzeug.security import generate_password_hash, check_password_hash |
||||||
|
|
||||||
|
from app import db |
||||||
|
from app.models import GamePlayer, PlayerFoundObjective, PlayerCaughtPlayer, NotificationPlayer, Role |
||||||
|
|
||||||
|
class User(UserMixin, db.Model): |
||||||
|
""" !Always call set_auth_hash() after creating new instance! """ |
||||||
|
__tablename__ = 'player' |
||||||
|
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) |
||||||
|
password_hash = db.Column(db.String(128)) |
||||||
|
|
||||||
|
user_games = db.relationship( |
||||||
|
'GamePlayer', |
||||||
|
back_populates='user', |
||||||
|
cascade='save-update, merge, delete, delete-orphan') |
||||||
|
games = association_proxy('user_games', 'game', |
||||||
|
creator=lambda game: GamePlayer(game=game)) |
||||||
|
|
||||||
|
player_found_objectives = db.relationship( |
||||||
|
'PlayerFoundObjective', |
||||||
|
back_populates='player', |
||||||
|
cascade='save-update, merge, delete, delete-orphan') |
||||||
|
found_objectives = association_proxy('player_found_objectives', 'objective', |
||||||
|
creator=lambda objective: PlayerFoundObjective(objective=objective)) |
||||||
|
|
||||||
|
player_notifications = db.relationship( |
||||||
|
'NotificationPlayer', |
||||||
|
back_populates='recipient', |
||||||
|
cascade='save-update, merge, delete, delete-orphan') |
||||||
|
notifications = association_proxy('player_notifications', 'notification', |
||||||
|
creator=lambda notification: NotificationPlayer(notification=notification)) |
||||||
|
|
||||||
|
player_caught_players = db.relationship( |
||||||
|
'PlayerCaughtPlayer', |
||||||
|
back_populates='catching_player', |
||||||
|
cascade='save-update, merge, delete, delete-orphan', |
||||||
|
foreign_keys=[PlayerCaughtPlayer.catching_player_id]) |
||||||
|
caught_players = association_proxy('player_caught_players', 'player', |
||||||
|
creator=lambda player: PlayerCaughtPlayer(caught_player=player)) |
||||||
|
|
||||||
|
player_caught_by_players = db.relationship( |
||||||
|
'PlayerCaughtPlayer', |
||||||
|
back_populates='caught_player', |
||||||
|
cascade='save-update, merge, delete, delete-orphan', |
||||||
|
foreign_keys=[PlayerCaughtPlayer.caught_player_id]) |
||||||
|
caught_by_players = association_proxy('player_caught_by_players', 'player', |
||||||
|
creator=lambda player: PlayerCaughtPlayer(catching_player=player)) |
||||||
|
|
||||||
|
locations = db.relationship( |
||||||
|
'Location', |
||||||
|
lazy='select', |
||||||
|
backref=db.backref('player', lazy='joined')) |
||||||
|
|
||||||
|
def set_password(self, password): |
||||||
|
self.password_hash = generate_password_hash(password) |
||||||
|
|
||||||
|
def set_auth_hash(self): |
||||||
|
self.auth_hash = token_hex(16) |
||||||
|
|
||||||
|
def check_password(self, password): |
||||||
|
return check_password_hash(self.password_hash, password) |
||||||
|
|
||||||
|
def locations_during_game(self, game): |
||||||
|
# pylint: disable=not-an-iterable |
||||||
|
if not self.locations: |
||||||
|
return None |
||||||
|
if game is None: |
||||||
|
return self.locations |
||||||
|
game_start = game.start_time or datetime.min |
||||||
|
game_end = game.end_time or datetime.max |
||||||
|
return (location for location in self.locations if location.timestamp > game_start and location.timestamp < game_end) |
||||||
|
|
||||||
|
def last_location(self, game=None): |
||||||
|
# pylint: disable=not-an-iterable |
||||||
|
if not self.locations: |
||||||
|
return None |
||||||
|
if game is None: |
||||||
|
return max(self.locations, key=lambda location: location.timestamp) |
||||||
|
return max(self.locations_during_game(game), key=lambda location: location.timestamp) |
||||||
|
|
||||||
|
def role_in_game(self, game): |
||||||
|
'''returns the role as Role enum of player in given game. Returns None if player does not participate in game''' |
||||||
|
# pylint: disable=not-an-iterable |
||||||
|
gameplayers = [gameplayer for gameplayer in self.user_games if gameplayer.game == game] |
||||||
|
if not gameplayers: |
||||||
|
return None |
||||||
|
return gameplayers[0].role |
||||||
|
|
||||||
|
def owns_game_played_by(self, player): |
||||||
|
'''self is an owner of a game the player participates in''' |
||||||
|
return self in [gameplayer.player for gameplayers in |
||||||
|
[game.game_players for game in player.games] |
||||||
|
for gameplayer in gameplayers if gameplayer.role == Role.owner] |
||||||
|
|
||||||
|
@staticmethod |
||||||
|
def delete_orphans(): |
||||||
|
User.query.filter(~User.user_games.any()).delete() |
||||||
|
db.session.commit() |
Loading…
Reference in new issue