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, login from app.models import GamePlayer, 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)) 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() @login.user_loader def load_user(id): return User.query.get(int(id))