class Role(SurrogatePK, Model): """A role for a user.""" __tablename__ = 'roles' name = Column(db.String(80), unique=True, nullable=False) user_id = reference_col('users', nullable=True) user = relationship('User', backref='roles') def __init__(self, name, **kwargs): """Create instance.""" db.Model.__init__(self, name=name, **kwargs) def __repr__(self): """Represent instance as a unique string.""" return '<Role({name})>'.format(name=self.name)
class Player(SurrogatePK, Model): """A player.""" __tablename__ = 'players' first_name = Column(db.String(30)) last_name = Column(db.String(30)) aga_id = Column(db.Integer, index=True, unique=True) aga_rank = Column(db.Integer) white_player_games = relationship('WhitePlayerGame', backref='player') white_games = association_proxy('white_player_games', 'game') black_player_games = relationship('BlackPlayerGame', backref='player') black_games = association_proxy('black_player_games', 'game') def __init__(self, first_name, last_name, aga_id, aga_rank): """Initialize player.""" self.first_name = first_name self.last_name = last_name self.aga_id = aga_id self.aga_rank = aga_rank def __repr__(self): """Represent instance as a unique string.""" return ('<Player({first_name}, {last_name}, {aga_id})>'.format( first_name=self.first_name, last_name=self.last_name, aga_id=self.aga_id)) @property def games(self): """All games that player has played.""" return self.black_games + self.white_games @property def full_name(self): """Full player name.""" return '{0} {1}'.format(self.first_name, self.last_name) @classmethod def get_by_aga_id(cls, aga_id): """Get player by AGA ID.""" return cls.query.filter_by(aga_id=aga_id)[0] @classmethod def get_players(cls): """Get all players.""" return cls.query.all() def latest_season(self): """Get latest season player has played in.""" return sorted([game.season for game in self.games])[-1] def season_stats(self, season=None): """Get player statistics for a season.""" if season is None: season = self.latest_season() wins, losses = 0, 0 for game in ([game for game in self.games if game.season == season]): if ((game.winner == Color.white and game.white == self) or (game.winner == Color.black and game.black == self)): wins += 1 else: losses += 1 return {'wins': wins, 'losses': losses} def latest_episode(self): """Get latest episode player has played in.""" return sorted([game.episode for game in self.games])[-1] def episode_stats(self, episode=None, season=None): """Get player statistics for an episode.""" if episode is None: episode = self.latest_episode() if season is None: season = self.latest_season() wins, losses = 0, 0 for game in ([ game for game in self.games if game.season == season and game.episode == episode ]): if ((game.winner == Color.white and game.white == self) or (game.winner == Color.black and game.black == self)): wins += 1 else: losses += 1 return {'wins': wins, 'losses': losses} def league_stats(self): """Get player statistics for the whole league.""" wins, losses = 0, 0 for game in self.games: if ((game.winner == Color.white and game.white == self) or (game.winner == Color.black and game.black == self)): wins += 1 else: losses += 1 return {'wins': wins, 'losses': losses}
class Game(SurrogatePK, Model): """A game record.""" __tablename__ = 'games' white_player_game = relationship('WhitePlayerGame', backref='game', cascade='all, delete-orphan', uselist=False) white = association_proxy('white_player_game', 'player', creator=lambda pl: WhitePlayerGame(player=pl)) black_player_game = relationship('BlackPlayerGame', backref='game', cascade='all, delete-orphan', uselist=False) black = association_proxy('black_player_game', 'player', creator=lambda pl: BlackPlayerGame(player=pl)) winner = Column(db.Enum(Color)) handicap = Column(db.SmallInteger) komi = Column(db.SmallInteger) season = Column(db.Integer) episode = Column(db.Integer) created_at = Column(db.DateTime, nullable=False, default=dt.datetime.utcnow) played_at = Column(db.DateTime, nullable=False, default=dt.datetime.utcnow) last_modified_at = Column(db.DateTime, nullable=False, default=dt.datetime.utcnow) db.Index('ix_games_season_episode', 'season', 'episode') def __init__(self, white, black, winner, handicap, komi, season, episode, created_at=None, played_at=None, last_modified_at=None): """Initialize game.""" self.white = white self.black = black self.winner = winner self.handicap = handicap self.komi = komi self.season = season self.episode = episode self.created_at = created_at self.played_at = played_at self.last_modified_at = last_modified_at def __repr__(self): """Represent instance as a unique string.""" return ('<Game({white!r}, {black!r}, {winner}, {handicap}, {komi})>'. format(white=self.white, black=self.black, winner=self.winner, handicap=self.handicap, komi=self.komi)) def to_dict(self): """Return game as dictionary.""" return { 'game_id': self.id, 'white_id': self.white.id, 'black_id': self.black.id, 'winner': self.winner.name, 'handicap': self.handicap, 'komi': self.komi, 'season': self.season, 'episode': self.episode, 'created_at': str(self.created_at), 'played_at': str(self.played_at), 'last_modified_at': str(self.last_modified_at) } def update(self, **kwargs): """Override update method to reset last_modified_at.""" self.last_modified_at = dt.datetime.utcnow() super().update(**kwargs) @classmethod def get_by_season_ep(cls, season, episode): """Get games by season and episode.""" return cls.query.filter_by(season=season, episode=episode) @classmethod def get_max_season_ep(cls): """Get maximum season and episode.""" max_season, max_episode = session.query(func.max(cls.season), func.max(cls.episode)).one() max_season = 0 if max_season is None else max_season max_episode = 0 if max_episode is None else max_episode return (max_season, max_episode) @property def players(self): """Get players in game as set.""" return frozenset((self.white, self.black)) @classmethod def latest_episode(cls): """Get latest episode.""" games = cls.query.all() if len(games) > 0: return sorted([game.episode for game in games])[-1] else: return 1 @classmethod def latest_season(cls): """Get latest season.""" games = cls.query.all() if len(games) > 0: return sorted([game.season for game in games])[-1] else: return 1 @classmethod def episode_stats(cls, episode=None, season=None, num_players=5): """Get statistics for an episode.""" if episode is None: episode = cls.latest_episode() if season is None: season = cls.latest_season() wins, games_played = {}, {} games = [ game for game in cls.query.all() if game.season == season and game.episode == episode ] for game in games: if game.winner is Color.white: wins[game.white.id] = wins.get(game.white.id, 0) + 1 else: wins[game.black.id] = wins.get(game.black.id, 0) + 1 games_played[game.white.id] = games_played.get(game.white.id, 0) + 1 games_played[game.black.id] = games_played.get(game.black.id, 0) + 1 wins_list = enumerate( sorted([(Player.get_by_id(player_id), player_wins) for player_id, player_wins in wins.items()], key=lambda stat: stat[1], reverse=True)[0:num_players]) games_played_list = enumerate( sorted( [(Player.get_by_id(player_id), player_games_played) for player_id, player_games_played in games_played.items()], key=lambda stat: stat[1], reverse=True)[0:num_players]) return {'wins': wins_list, 'games_played': games_played_list}
class Game(SurrogatePK, Model): """A game record.""" __tablename__ = 'games' white_player_game = relationship('WhitePlayerGame', backref='game', cascade='all, delete-orphan', uselist=False) white = association_proxy('white_player_game', 'player', creator=lambda pl: WhitePlayerGame(player=pl)) black_player_game = relationship('BlackPlayerGame', backref='game', cascade='all, delete-orphan', uselist=False) black = association_proxy('black_player_game', 'player', creator=lambda pl: BlackPlayerGame(player=pl)) winner = Column(db.Enum(Color)) handicap = Column(db.SmallInteger) komi = Column(db.SmallInteger) season = Column(db.Integer) episode = Column(db.Integer) created_at = Column(db.DateTime, nullable=False, default=dt.datetime.utcnow) played_at = Column(db.DateTime, nullable=False, default=dt.datetime.utcnow) last_modified_at = Column(db.DateTime, nullable=False, default=dt.datetime.utcnow) db.Index('ix_games_season_episode', 'season', 'episode') def __init__(self, white, black, winner, handicap, komi, season, episode, created_at=None, played_at=None, last_modified_at=None): """Initialize game.""" self.white = white self.black = black self.winner = winner self.handicap = handicap self.komi = komi self.season = season self.episode = episode self.created_at = created_at self.played_at = played_at self.last_modified_at = last_modified_at def __repr__(self): """Represent instance as a unique string.""" return ('<Game({white!r}, {black!r}, {winner}, {handicap}, {komi})>'. format(white=self.white, black=self.black, winner=self.winner, handicap=self.handicap, komi=self.komi)) def to_dict(self): """Return game as dictionary.""" return { 'game_id': self.id, 'white_id': self.white.id, 'black_id': self.black.id, 'winner': self.winner.name, 'handicap': self.handicap, 'komi': self.komi, 'season': self.season, 'episode': self.episode, 'created_at': str(self.created_at), 'played_at': str(self.played_at), 'last_modified_at': str(self.last_modified_at) } def update(self, **kwargs): """Override update method to reset last_modified_at.""" self.last_modified_at = dt.datetime.utcnow() super().update(**kwargs) @classmethod def get_by_season_ep(cls, season, episode): """Get games by season and episode.""" return cls.query.filter_by(season=season, episode=episode) @classmethod def get_by_season(cls, season): """Get games by season.""" return cls.query.filter_by(season=season) @classmethod def get_max_season_ep(cls): """Get maximum season and episode.""" max_season, max_episode = session.query(func.max(cls.season), func.max(cls.episode)).one() max_season = 0 if max_season is None else max_season max_episode = 0 if max_episode is None else max_episode return (max_season, max_episode) @property def players(self): """Get players in game as set.""" return frozenset((self.white, self.black)) @classmethod def latest_season_episode(cls): """Get latest episode and season.""" games = cls.query.all() if len(games) > 0: return sorted([(game.season, game.episode) for game in games])[-1] else: return (0, 0) @classmethod def episode_stats(cls, episode=None, season=None, num_players=5): """Get statistics for an episode.""" latest_season_episode = cls.latest_season_episode() if episode is None: episode = latest_season_episode[1] if season is None: season = latest_season_episode[0] players = Player.query.all() wins = {p.id: 0 for p in players} games_played = {p.id: 0 for p in players} stones_given = {p.id: 0 for p in players} dans_slain = {p.id: 0 for p in players} kyus_killed = {p.id: 0 for p in players} games = [ game for game in cls.query.all() if game.season == season and game.episode == episode ] for game in games: if game.winner is Color.white: wins[game.white.id] = wins.get(game.white.id, 0) + 1 else: wins[game.black.id] = wins.get(game.black.id, 0) + 1 games_played[game.white.id] = games_played.get(game.white.id, 0) + 1 games_played[game.black.id] = games_played.get(game.black.id, 0) + 1 stones_given[game.white.id] = \ stones_given[game.white.id] + (game.handicap) black_player = Player.get_by_id(game.black.id) white_player = Player.get_by_id(game.white.id) if (white_player.aga_rank > 0 and black_player.aga_rank < 0 and game.winner is Color.black): dans_slain[game.black.id] = \ dans_slain.get(game.black.id, 0) + 1 elif (black_player.aga_rank > 0 and white_player.aga_rank < 0 and game.winner is Color.white): dans_slain[game.white.id] = \ dans_slain.get(game.white.id, 0) + 1 if (white_player.aga_rank > 0 and black_player.aga_rank < 0 and game.winner is Color.white): kyus_killed[game.white.id] = \ kyus_killed.get(game.white.id, 0) + 1 elif (black_player.aga_rank > 0 and white_player.aga_rank < 0 and game.winner is Color.black): kyus_killed[game.black.id] = \ kyus_killed.get(game.black.id, 0) + 1 win_ratios = { p.id: wins[p.id] / games_played[p.id] for p in players if games_played[p.id] > 0 } wins_list = enumerate( sorted([(Player.get_by_id(player_id), player_wins) for player_id, player_wins in wins.items()], key=lambda stat: stat[1], reverse=True)[0:num_players]) games_played_list = enumerate( sorted( [(Player.get_by_id(player_id), player_games_played) for player_id, player_games_played in games_played.items()], key=lambda stat: stat[1], reverse=True)[0:num_players]) win_ratios_list = enumerate( sorted([(Player.get_by_id(player_id), player_win_ratio) for player_id, player_win_ratio in win_ratios.items()], key=lambda stat: stat[1], reverse=True)[0:num_players]) stones_given_list = enumerate( sorted( [(Player.get_by_id(player_id), player_stones_given) for player_id, player_stones_given in stones_given.items()], key=lambda stat: stat[1], reverse=True)[0:num_players]) dans_slain_list = enumerate( sorted([(Player.get_by_id(player_id), player_dans_slain) for player_id, player_dans_slain in dans_slain.items()], key=lambda stat: stat[1], reverse=True)[0:num_players]) kyus_killed_list = enumerate( sorted([(Player.get_by_id(player_id), player_kyus_killed) for player_id, player_kyus_killed in kyus_killed.items()], key=lambda stat: stat[1], reverse=True)[0:num_players]) return { 'wins': wins_list, 'games_played': games_played_list, 'win_ratios': win_ratios_list, 'stones_given': stones_given_list, 'dans_slain': dans_slain_list, 'kyus_killed': kyus_killed_list } @classmethod def season_stats(cls, season=None, num_players=5): """Get statistics for a season.""" latest_season_episode = cls.latest_season_episode() if season is None: season = latest_season_episode[0] players = Player.query.all() wins_minus_losses = {p.id: 0 for p in players} wins = {p.id: 0 for p in players} games_played = {p.id: 0 for p in players} games_played_one_ep = {p.id: 0 for p in players} dans_slain = {p.id: 0 for p in players} kyus_killed = {p.id: 0 for p in players} games_against_weaker = {p.id: 0 for p in players} losses = {p.id: 0 for p in players} games_per_ep = { p.id: {ep: 0 for ep in range(1, latest_season_episode[1] + 1)} for p in players } wins_per_ep = { p.id: {ep: 0 for ep in range(1, latest_season_episode[1] + 1)} for p in players } losses_per_ep = { p.id: {ep: 0 for ep in range(1, latest_season_episode[1] + 1)} for p in players } games = [game for game in cls.query.all() if game.season == season] for game in games: if game.winner is Color.white: wins[game.white.id] = wins.get(game.white.id, 0) + 1 wins_per_ep[game.white.id][game.episode] = \ wins_per_ep[game.white.id][game.episode] + 1 losses[game.black.id] = losses.get(game.black.id, 0) + 1 losses_per_ep[game.black.id][game.episode] = \ losses_per_ep[game.black.id][game.episode] + 1 else: wins[game.black.id] = wins.get(game.black.id, 0) + 1 wins_per_ep[game.black.id][game.episode] = \ wins_per_ep[game.black.id][game.episode] + 1 losses[game.white.id] = losses.get(game.white.id, 0) + 1 losses_per_ep[game.white.id][game.episode] = \ losses_per_ep[game.white.id][game.episode] + 1 games_played[game.white.id] = games_played.get(game.white.id, 0) + 1 games_played[game.black.id] = games_played.get(game.black.id, 0) + 1 games_per_ep[game.white.id][game.episode] = \ games_per_ep[game.white.id][game.episode] + 1 games_per_ep[game.black.id][game.episode] = \ games_per_ep[game.black.id][game.episode] + 1 black_player = Player.get_by_id(game.black.id) white_player = Player.get_by_id(game.white.id) if (white_player.aga_rank > 0 and black_player.aga_rank < 0 and game.winner is Color.black): dans_slain[game.black.id] = \ dans_slain.get(game.black.id, 0) + 1 elif (black_player.aga_rank > 0 and white_player.aga_rank < 0 and game.winner is Color.white): dans_slain[game.white.id] = \ dans_slain.get(game.white.id, 0) + 1 if (white_player.aga_rank > 0 and black_player.aga_rank < 0 and game.winner is Color.white): kyus_killed[game.white.id] = \ kyus_killed.get(game.white.id, 0) + 1 elif (black_player.aga_rank > 0 and white_player.aga_rank < 0 and game.winner is Color.black): kyus_killed[game.black.id] = \ kyus_killed.get(game.black.id, 0) + 1 if (white_player.aga_rank > black_player.aga_rank): games_against_weaker[game.white.id] = \ games_against_weaker[game.white.id] + 1 if (black_player.aga_rank > white_player.aga_rank): games_against_weaker[game.black.id] = \ games_against_weaker[game.black.id] + 1 games_played_one_ep = { p.id: max([g for (d, g) in games_per_ep[p.id].items()]) for p in players } wins_minus_losses = { p.id: 2 * wins[p.id] - games_played[p.id] for p in players if games_played[p.id] > 0 } wins_list = enumerate( sorted([(Player.get_by_id(player_id), player_wins) for player_id, player_wins in wins.items()], key=lambda stat: stat[1], reverse=True)[0:num_players]) games_played_list = enumerate( sorted( [(Player.get_by_id(player_id), player_games_played) for player_id, player_games_played in games_played.items()], key=lambda stat: stat[1], reverse=True)[0:num_players]) games_played_one_ep_list = enumerate( sorted([(Player.get_by_id(player_id), player_games_played_one_ep) for player_id, player_games_played_one_ep in games_played_one_ep.items()], key=lambda stat: stat[1], reverse=True)[0:num_players]) wins_minus_losses_list = enumerate( sorted([(Player.get_by_id(player_id), player_wins_minus_losses) for player_id, player_wins_minus_losses in wins_minus_losses.items()], key=lambda stat: stat[1], reverse=True)[0:num_players]) games_against_weaker_list = enumerate( sorted([(Player.get_by_id(player_id), player_games_against_weaker) for player_id, player_games_against_weaker in games_against_weaker.items()], key=lambda stat: stat[1], reverse=True)[0:num_players]) dans_slain_list = enumerate( sorted([(Player.get_by_id(player_id), player_dans_slain) for player_id, player_dans_slain in dans_slain.items()], key=lambda stat: stat[1], reverse=True)[0:num_players]) kyus_killed_list = enumerate( sorted([(Player.get_by_id(player_id), player_kyus_killed) for player_id, player_kyus_killed in kyus_killed.items()], key=lambda stat: stat[1], reverse=True)[0:num_players]) losses_list = enumerate( sorted([(Player.get_by_id(player_id), player_losses) for player_id, player_losses in losses.items()], key=lambda stat: stat[1], reverse=True)[0:num_players]) steady_freddy = [ p for p in players if min([g for (d, g) in games_per_ep[p.id].items()]) > 0 ] fifteen_min_fame = [ p for p in players if len([ g for (d, g) in games_per_ep[p.id].items() if (wins_per_ep[p.id][d] >= 3 and losses_per_ep[p.id][d] == 0) ]) > 0 ] rock_bottom = [ p for p in players if len([ g for (d, g) in games_per_ep[p.id].items() if (losses_per_ep[p.id][d] >= 3 and wins_per_ep[p.id][d] == 0) ]) > 0 ] return { 'wins': wins_list, 'games_played': games_played_list, 'games_played_one_ep': games_played_one_ep_list, 'wins_minus_losses': wins_minus_losses_list, 'games_against_weaker': games_against_weaker_list, 'dans_slain': dans_slain_list, 'kyus_killed': kyus_killed_list, 'losses': losses_list, 'steady_freddy': steady_freddy, 'fifteen_min_fame': fifteen_min_fame, 'rock_bottom': rock_bottom }