class User(db.Model): id = db.Column(db.Integer, primary_key=True) steam_id = db.Column(db.String(40), unique=True) name = db.Column(db.String(40)) admin = db.Column(db.Boolean, default=False) servers = db.relationship('GameServer', backref='user', lazy='dynamic') teams = db.relationship('Team', backref='user', lazy='dynamic') matches = db.relationship('Match', backref='user', lazy='dynamic') @staticmethod def get_or_create(steam_id): rv = User.query.filter_by(steam_id=steam_id).first() if rv is None: rv = User() rv.steam_id = steam_id db.session.add(rv) app.logger.info('Creating user for {}'.format(steam_id)) rv.admin = ('ADMIN_IDS' in app.config) and (steam_id in app.config['ADMIN_IDS']) return rv def get_url(self): return url_for('user', userid=self.id) def get_steam_url(self): return 'http://steamcommunity.com/profiles/{}'.format(self.steam_id) def get_recent_matches(self, limit=10): return self.matches.filter_by(cancelled=False).limit(limit) def __repr__(self): return 'User(id={}, steam_id={}, name={}, admin={})'.format( self.id, self.steam_id, self.name, self.admin)
class MapStats(db.Model): id = db.Column(db.Integer, primary_key=True) match_id = db.Column(db.Integer, db.ForeignKey('match.id')) map_number = db.Column(db.Integer) map_name = db.Column(db.String(64)) start_time = db.Column(db.DateTime) end_time = db.Column(db.DateTime) winner = db.Column(db.Integer, db.ForeignKey('team.id')) team1_score = db.Column(db.Integer, default=0) team2_score = db.Column(db.Integer, default=0) player_stats = db.relationship('PlayerStats', backref='mapstats', lazy='dynamic') @staticmethod def get_or_create(match_id, map_number, map_name=''): match = Match.query.get(match_id) if match is None or map_number >= match.max_maps: return None rv = MapStats.query.filter_by(match_id=match_id, map_number=map_number).first() if rv is None: rv = MapStats() rv.match_id = match_id rv.map_number = map_number rv.map_name = map_name rv.start_time = datetime.datetime.utcnow() rv.team1_score = 0 rv.team2_score = 0 db.session.add(rv) return rv def __repr__(self): return 'MapStats(' + str(self.id) + ',' + str(self.map_name) + ')'
class Season(db.Model): id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('user.id')) name = db.Column(db.String(60), default='') start_date = db.Column(db.DateTime, default=datetime.datetime.utcnow()) end_date = db.Column(db.DateTime) matches = db.relationship('Match', backref='season', lazy='dynamic') @staticmethod def create(user, name, start_date, end_date): rv = Season() rv.user_id = user.id rv.name = name rv.start_date = start_date rv.end_date = end_date db.session.add(rv) return rv def get_season_name(self): return self.name def set_data(self, user, name, start_date, end_date): self.user_id = user.id self.name = name self.start_date = start_date self.end_date = end_date def can_edit(self, user): if not user: return False if self.user_id == user.id: return True return False def can_delete(self, user): if not self.can_edit(user): return False return self.get_recent_matches().count() == 0 def get_recent_matches(self, limit=10): season = Season.query.get_or_404(self.id) matches = season.matches recent_matches = matches.filter( (Match.season_id == self.id) & (Match.cancelled == False) & (Match.start_time != None) # noqa: E712 ).order_by(-Match.id).limit(5) if recent_matches is None: return [] else: return recent_matches def __repr__(self): return 'Season(id={}, user_id={}, name={}, start_date={}, end_date={})'.format( self.id, self.user_id, self.name, self.start_date, self.end_date)
class Match(db.Model): id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('user.id')) server_id = db.Column(db.Integer, db.ForeignKey('game_server.id'), index=True) team1_id = db.Column(db.Integer, db.ForeignKey('team.id')) team2_id = db.Column(db.Integer, db.ForeignKey('team.id')) season_id = db.Column(db.Integer, db.ForeignKey('season.id')) team1_string = db.Column(db.String(32), default='') team2_string = db.Column(db.String(32), default='') winner = db.Column(db.Integer, db.ForeignKey('team.id')) plugin_version = db.Column(db.String(32), default='unknown') forfeit = db.Column(db.Boolean, default=False) cancelled = db.Column(db.Boolean, default=False) start_time = db.Column(db.DateTime) end_time = db.Column(db.DateTime) max_maps = db.Column(db.Integer) title = db.Column(db.String(60), default='') skip_veto = db.Column(db.Boolean) api_key = db.Column(db.String(32)) veto_first = db.Column(db.String(5)) veto_mappool = db.Column(db.String(500)) map_stats = db.relationship('MapStats', backref='match', lazy='dynamic') side_type = db.Column(db.String(32)) team1_score = db.Column(db.Integer, default=0) team2_score = db.Column(db.Integer, default=0) team1_series_score = db.Column(db.Integer, default=0) team2_series_score = db.Column(db.Integer, default=0) spectator_auths = db.Column(db.PickleType) private_match = db.Column(db.Boolean) enforce_teams = db.Column(db.Boolean, default=True) @staticmethod def create(user, team1_id, team2_id, team1_string, team2_string, max_maps, skip_veto, title, veto_mappool, season_id, side_type, veto_first, server_id=None, team1_series_score=None, team2_series_score=None, spectator_auths=None, private_match=False, enforce_teams=True): rv = Match() rv.user_id = user.id rv.team1_id = team1_id rv.team2_id = team2_id rv.season_id = season_id rv.side_type = side_type rv.skip_veto = skip_veto rv.title = title rv.veto_mappool = ' '.join(veto_mappool) rv.server_id = server_id rv.max_maps = max_maps if veto_first == "CT": rv.veto_first = "team1" elif veto_first == "T": rv.veto_first = "team2" else: rv.veto_first = None rv.api_key = ''.join( random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(24)) rv.team1_series_score = team1_series_score rv.team2_series_score = team2_series_score rv.spectator_auths = spectator_auths rv.private_match = private_match rv.enforce_teams = enforce_teams db.session.add(rv) return rv def get_status_string(self, show_winner=True): if self.pending(): return 'Pending' elif self.live(): team1_score, team2_score = self.get_current_score() return 'Live, {}:{}'.format(team1_score, team2_score) elif self.finished(): t1score, t2score = self.get_current_score() min_score = min(t1score, t2score) max_score = max(t1score, t2score) score_string = '{}:{}'.format(max_score, min_score) if not show_winner: return 'Finished' elif self.winner == self.team1_id: return 'Won {} by {}'.format(score_string, self.get_team1().name) elif self.winner == self.team2_id: return 'Won {} by {}'.format(score_string, self.get_team2().name) else: return 'Tied {}'.format(score_string) else: return 'Cancelled' def is_private_match(self): return self.private_match def get_vs_string(self): team1 = self.get_team1() team2 = self.get_team2() scores = self.get_current_score() str = '{} vs {} ({}:{})'.format(team1.get_name_url_html(), team2.get_name_url_html(), scores[0], scores[1]) return Markup(str) def finalized(self): return self.cancelled or self.finished() def pending(self): return self.start_time is None and not self.cancelled def finished(self): return self.end_time is not None and not self.cancelled def live(self): return self.start_time is not None and self.end_time is None and not self.cancelled def get_server(self): return GameServer.query.filter_by(id=self.server_id).first() def get_start_time(self): return self.start_time if self.start_time is not None else '' def get_end_time(self): return self.end_time if self.end_time is not None else '' def get_season(self): if self.season_id: return Season.query.get(self.season_id) else: return None def get_season_id(self): return self.season_id def get_current_score(self): if self.max_maps == 1: mapstat = self.map_stats.first() if not mapstat: return (0, 0) else: return (mapstat.team1_score, mapstat.team2_score) else: return (self.team1_score, self.team2_score) def send_to_server(self): server = GameServer.query.get(self.server_id) if not server: return False url = url_for('match.match_config', matchid=self.id, _external=True, _scheme='http') # Remove http protocal since the get5 plugin can't parse args with the # : in them. url = url.replace("http://", "") url = url.replace("https://", "") loadmatch_response = server.send_rcon_command('get5_loadmatch_url ' + url) server.send_rcon_command('get5_web_api_key ' + self.api_key) # ***HACK FIX TO ENSURE CHECK_AUTHS WORKS AS INTENDED*** server.send_rcon_command('map de_dust2') if loadmatch_response: # There should be no response return False return True def get_team1(self): return Team.query.get(self.team1_id) def get_team2(self): return Team.query.get(self.team2_id) def get_user(self): return User.query.get(self.user_id) def get_winner(self): if self.team1_score > self.team2_score: return self.get_team1() elif self.team2_score > self.team1_score: return self.get_team2() else: return None def get_loser(self): if self.team1_score > self.team2_score: return self.get_team2() elif self.team2_score > self.team1_score: return self.get_team1() else: return None def build_match_dict(self): d = {} d['matchid'] = str(self.id) d['match_title'] = self.title d['side_type'] = self.side_type d['veto_first'] = self.veto_first d['skip_veto'] = self.skip_veto if self.max_maps == 2: d['bo2_series'] = True else: d['maps_to_win'] = self.max_maps / 2 + 1 def add_team_data(teamkey, teamid, matchtext): team = Team.query.get(teamid) if not team: return d[teamkey] = {} # Add entries if they have values. def add_if(key, value): if value: d[teamkey][key] = value add_if('name', team.name) add_if('name', team.name) add_if('tag', team.tag) add_if('flag', team.flag.upper()) add_if('logo', team.logo) add_if('matchtext', matchtext) # Add new series score. if teamkey == 'team1': add_if('series_score', self.team1_series_score) else: add_if('series_score', self.team2_series_score) # Attempt to send in KV Pairs of preferred names. # If none, or error, send in the regular list. try: d[teamkey]['players'] = OrderedDict() for uid, name in zip(team.auths, team.preferred_names): if uid: d[teamkey]['players'][uid] = name except: d[teamkey]['players'] = filter(lambda x: x != '', team.auths) add_team_data('team1', self.team1_id, self.team1_string) add_team_data('team2', self.team2_id, self.team2_string) d['cvars'] = {} d['cvars']['get5_web_api_url'] = url_for('home', _external=True, _scheme='http') d['cvars']['get5_check_auths'] = "1" if self.enforce_teams else "0" # Add in for spectators modification. d['min_spectators_to_ready'] = 0 # Perm spectators will go within config, then can add more from match # screen. d['spectators'] = {"players": app.config['SPECTATOR_IDS']} # If we don't have any perm spectators, create the new list. if not d['spectators']: d['spectators'] = {"players": []} # Append auths from match page if we have any. if self.spectator_auths: for spectator in self.spectator_auths: d['spectators']["players"].append(spectator) if not d['spectators']['players']: d['spectators'] = None if self.veto_mappool: d['maplist'] = [] for map in self.veto_mappool.split(): d['maplist'].append(map) return d def __repr__(self): return 'Match(id={})'.format(self.id)
class Match(db.Model): id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('user.id')) server_id = db.Column(db.Integer, db.ForeignKey('game_server.id'), index=True) team1_id = db.Column(db.Integer, db.ForeignKey('team.id')) team2_id = db.Column(db.Integer, db.ForeignKey('team.id')) team1_string = db.Column(db.String(32), default='') team2_string = db.Column(db.String(32), default='') winner = db.Column(db.Integer, db.ForeignKey('team.id')) plugin_version = db.Column(db.String(32), default='unknown') forfeit = db.Column(db.Boolean, default=False) cancelled = db.Column(db.Boolean, default=False) start_time = db.Column(db.DateTime) end_time = db.Column(db.DateTime) max_maps = db.Column(db.Integer) title = db.Column(db.String(60), default='') skip_veto = db.Column(db.Boolean) api_key = db.Column(db.String(32)) veto_mappool = db.Column(db.String(500)) map_stats = db.relationship('MapStats', backref='match', lazy='dynamic') team1_score = db.Column(db.Integer, default=0) team2_score = db.Column(db.Integer, default=0) @staticmethod def create(user, team1_id, team2_id, team1_string, team2_string, max_maps, skip_veto, title, veto_mappool, server_id=None): rv = Match() rv.user_id = user.id rv.team1_id = team1_id rv.team2_id = team2_id rv.skip_veto = skip_veto rv.title = title rv.veto_mappool = ' '.join(veto_mappool) rv.server_id = server_id rv.max_maps = max_maps rv.api_key = ''.join( random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(24)) db.session.add(rv) return rv def get_status_string(self, show_winner=True): if self.pending(): return 'Pending' elif self.live(): team1_score, team2_score = self.get_current_score() return 'Live, {}:{}'.format(team1_score, team2_score) elif self.finished(): t1score, t2score = self.get_current_score() min_score = min(t1score, t2score) max_score = max(t1score, t2score) score_string = '{}:{}'.format(max_score, min_score) if not show_winner: return 'Finished' elif self.winner == self.team1_id: return 'Won {} by {}'.format(score_string, self.get_team1().name) elif self.winner == self.team2_id: return 'Won {} by {}'.format(score_string, self.get_team2().name) else: return 'Tied {}'.format(score_string) else: return 'Cancelled' def get_vs_string(self): team1 = self.get_team1() team2 = self.get_team2() scores = self.get_current_score() str = '{} vs {} ({}:{})'.format(team1.get_name_url_html(), team2.get_name_url_html(), scores[0], scores[1]) return Markup(str) def finalized(self): return self.cancelled or self.finished() def pending(self): return self.start_time is None and not self.cancelled def finished(self): return self.end_time is not None and not self.cancelled def live(self): return self.start_time is not None and self.end_time is None and not self.cancelled def get_server(self): return GameServer.query.filter_by(id=self.server_id).first() def get_current_score(self): if self.max_maps == 1: mapstat = self.map_stats.first() if not mapstat: return (0, 0) else: return (mapstat.team1_score, mapstat.team2_score) else: return (self.team1_score, self.team2_score) def send_to_server(self): server = GameServer.query.get(self.server_id) if not server: return False url = url_for('match.match_config', matchid=self.id, _external=True, _scheme='http') # Remove http protocal since the get5 plugin can't parse args with the # : in them. url = url.replace("http://", "") url = url.replace("https://", "") loadmatch_response = server.send_rcon_command('get5_loadmatch_url ' + url) server.send_rcon_command('get5_web_api_key ' + self.api_key) if loadmatch_response: # There should be no response return False return True def get_team1(self): return Team.query.get(self.team1_id) def get_team2(self): return Team.query.get(self.team2_id) def get_user(self): return User.query.get(self.user_id) def get_winner(self): if self.team1_score > self.team2_score: return self.get_team1() elif self.team2_score > self.team1_score: return self.get_team2() else: return None def get_loser(self): if self.team1_score > self.team2_score: return self.get_team2() elif self.team2_score > self.team1_score: return self.get_team1() else: return None def build_match_dict(self): d = {} d['matchid'] = str(self.id) d['match_title'] = self.title d['skip_veto'] = self.skip_veto if self.max_maps == 2: d['bo2_series'] = True else: d['maps_to_win'] = self.max_maps / 2 + 1 def add_team_data(teamkey, teamid, matchtext): team = Team.query.get(teamid) if not team: return d[teamkey] = {} # Add entries if they have values. def add_if(key, value): if value: d[teamkey][key] = value add_if('name', team.name) add_if('name', team.name) add_if('tag', team.tag) add_if('flag', team.flag.upper()) add_if('logo', team.logo) add_if('matchtext', matchtext) d[teamkey]['players'] = filter(lambda x: x != '', team.auths) add_team_data('team1', self.team1_id, self.team1_string) add_team_data('team2', self.team2_id, self.team2_string) d['cvars'] = {} d['cvars']['get5_web_api_url'] = url_for('home', _external=True, _scheme='http') if self.veto_mappool: d['maplist'] = [] for map in self.veto_mappool.split(): d['maplist'].append(map) if 'SPECTATOR_IDS' in app.config: d['spectators'] = {"players": app.config['SPECTATOR_IDS']} if 'MIN_SPECTATORS_TO_READY' in app.config: d['min_spectators_to_ready'] = app.config[ 'MIN_SPECTATORS_TO_READY'] return d def __repr__(self): return 'Match(id={})'.format(self.id)
class Tournament(db.Model): id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('user.id')) winner = db.Column(db.Integer, db.ForeignKey('team.id')) cancelled = db.Column(db.Boolean, default=False) finished = db.Column(db.Boolean, default=False) start_time = db.Column(db.DateTime) end_time = db.Column(db.DateTime) name = db.Column(db.String(60)) url = db.Column(db.String(60)) challonge_id = db.Column(db.Integer) challonge_data = db.Column(db.PickleType) participants = db.relationship('Team', secondary=TournamentTeam, backref='tournaments', lazy='dynamic') matches = db.relationship('Match', backref='tournament', lazy='dynamic') serverpool = db.relationship('GameServer', secondary=TournamentGameServer, backref='tournaments', lazy='dynamic') veto_mappool = db.Column(db.String(160)) @staticmethod def create(user, name, url, veto_mappool, serverpool=None, challonge_id=None, challonge_data=None): rv = Tournament() rv.user_id = user.id rv.url = url rv.name = name rv.veto_mappool = ' '.join(veto_mappool) if serverpool: rv.serverpool.extend(serverpool) rv.challonge_id = challonge_id rv.challonge_data = challonge_data db.session.add(rv) return rv def finalized(self): return self.cancelled or self.finished() def pending(self): return self.start_time is None and not self.cancelled def finished(self): return self.end_time is not None and not self.cancelled def live(self): return self.start_time is not None and self.end_time is None and not self.cancelled def get_user(self): return User.query.get(self.user_id) def get_available_server(self): for server in self.serverpool.all(): if not server.in_use: return server return None def __repr__(self): return 'Tournament(id={})'.format(self.id)