+"""
+Model initialization and mapping.
+"""
+
from sqlalchemy import MetaData
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import scoped_session, sessionmaker, mapper
def initialize_db(engine=None):
+ """
+ Initialize the database using reflection.
+
+ :param engine: The SQLAlchemy engine instance to bind.
+ :return: None
+ """
DBSession.configure(bind=engine)
Base.metadata.bind = engine
+"""
+Models related to games.
+"""
+
from calendar import timegm
from xonstat.util import pretty_date, strip_colors, html_colors
class Game(object):
+ """
+ An individual game.
+ """
+
def __init__(self, game_id=None, start_dt=None, game_type_cd=None,
server_id=None, map_id=None, winner=None):
self.game_id = game_id
self.winner = winner
def __repr__(self):
- return "<Game(%s, %s, %s, %s)>" % (self.game_id, self.start_dt, self.game_type_cd, self.server_id)
+ return ("<Game({}, {}, {}, {})>"
+ .format(self.game_id, self.start_dt, self.game_type_cd, self.server_id))
def to_dict(self):
- return {'game_id':self.game_id, 'start':self.start_dt.strftime('%Y-%m-%dT%H:%M:%SZ'),
- 'game_type_cd':self.game_type_cd, 'server_id':self.server_id}
+ return {
+ 'game_id': self.game_id,
+ 'start': self.start_dt.strftime('%Y-%m-%dT%H:%M:%SZ'),
+ 'game_type_cd': self.game_type_cd,
+ 'server_id': self.server_id
+ }
def fuzzy_date(self):
return pretty_date(self.start_dt)
class PlayerGameStat(object):
+ """
+ The individual statistics a player has gained/lost during a game.
+ """
+
def __init__(self, player_game_stat_id=None, create_dt=None):
self.player_game_stat_id = player_game_stat_id
self.create_dt = create_dt
def __repr__(self):
- return "<PlayerGameStat(%s, %s, %s)>" % (self.player_id, self.game_id, self.create_dt)
+ return "<PlayerGameStat({}, {}, {})>".format(self.player_id, self.game_id, self.create_dt)
def to_dict(self):
- return {'player_id':self.player_id, 'game_id':self.game_id,
- 'create_dt':self.create_dt.strftime('%Y-%m-%dT%H:%M:%SZ'),
- 'alivetime':self.alivetime, 'rank':self.rank, 'score':self.score, 'team':self.team}
+ return {
+ 'player_id': self.player_id,
+ 'game_id': self.game_id,
+ 'create_dt': self.create_dt.strftime('%Y-%m-%dT%H:%M:%SZ'),
+ 'alivetime': self.alivetime,
+ 'rank': self.rank,
+ 'score': self.score,
+ 'team': self.team
+ }
def nick_stripped(self):
if self.nick is None:
class PlayerWeaponStat(object):
+ """
+ The metrics for a single weapon in a game for a player.
+ """
+
def __init__(self, player_id=None, game_id=None, weapon_cd=None):
self.player_id = player_id
self.game_id = game_id
self.frags = 0
def __repr__(self):
- return "<PlayerWeaponStat(%s, %s, %s)>" % (self.player_weapon_stats_id, self.player_id, self.game_id)
+ return ("<PlayerWeaponStat({}, {}, {})>"
+ .format(self.player_weapon_stats_id, self.player_id, self.game_id))
def to_dict(self):
return {
- 'weapon_cd':self.weapon_cd,
- 'player_weapon_stats_id':self.player_weapon_stats_id,
- 'player_id':self.player_id,
- 'game_id':self.game_id,
- 'fired':self.fired,
- 'max':self.max,
- 'hit':self.hit,
- 'actual':self.actual,
- 'frags':self.frags,
+ 'weapon_cd': self.weapon_cd,
+ 'player_weapon_stats_id': self.player_weapon_stats_id,
+ 'player_id': self.player_id,
+ 'game_id': self.game_id,
+ 'fired': self.fired,
+ 'max': self.max,
+ 'hit': self.hit,
+ 'actual': self.actual,
+ 'frags': self.frags,
}
class TeamGameStat(object):
+ """
+ Team level metrics.
+ """
+
def __init__(self, team_game_stat_id=None, create_dt=None):
self.team_game_stat_id = team_game_stat_id
self.create_dt = create_dt
def __repr__(self):
- return "<TeamGameStat(%s, %s, %s)>" % (self.team_game_stat_id, self.game_id, self.team)
+ return "<TeamGameStat({}, {}, {})>".format(self.team_game_stat_id, self.game_id, self.team)
def to_dict(self):
return {
- 'team_game_stat_id':self.team_game_stat_id,
- 'game_id':self.game_id,
- 'team':self.team,
- 'score':self.score,
- 'rounds':self.rounds,
- 'caps':self.caps,
- 'create_dt':self.create_dt.strftime('%Y-%m-%dT%H:%M:%SZ'),
+ 'team_game_stat_id': self.team_game_stat_id,
+ 'game_id': self.game_id,
+ 'team': self.team,
+ 'score': self.score,
+ 'rounds': self.rounds,
+ 'caps': self.caps,
+ 'create_dt': self.create_dt.strftime('%Y-%m-%dT%H:%M:%SZ'),
}
+ # TODO: move this function to util
def team_html_color(self):
if self.team == 5:
return "red"
class PlayerGameAnticheat(object):
- def __init__(self, player_id=None, game_id=None, key=None,
- value=None, create_dt=None):
- self.player_id = player_id
- self.game_id = game_id
- self.key = key
- self.value = value
- self.create_dt = create_dt
+ """
+ Anticheat metrics sent by the server to identify odd patterns.
+ """
+
+ def __init__(self, player_id=None, game_id=None, key=None, value=None, create_dt=None):
+ self.player_id = player_id
+ self.game_id = game_id
+ self.key = key
+ self.value = value
+ self.create_dt = create_dt
def __repr__(self):
- return "<PlayerGameAnticheat(%s, %d)>" % (self.key, self.value)
+ return "<PlayerGameAnticheat({}, {})>".format(self.key, self.value)
class GameType(object):
+ """
+ A particular type of game.
+ """
+
def __repr__(self):
- return "<GameType(%s, %s, %s)>" % (self.game_type_cd, self.descr, self.active_ind)
+ return "<GameType({}, {}, {})>".format(self.game_type_cd, self.descr, self.active_ind)
def to_dict(self):
- return {'game_type_cd':self.game_type_cd, 'name':self.descr, 'active':self.active_ind}
+ return {
+ 'game_type_cd': self.game_type_cd,
+ 'name': self.descr,
+ 'active': self.active_ind,
+ }
class Weapon(object):
+ """
+ A particular type of weapon.
+ """
+
def __repr__(self):
- return "<Weapon(%s, %s, %s)>" % (self.weapon_cd, self.descr, self.active_ind)
+ return "<Weapon({}, {}, {})>".format(self.weapon_cd, self.descr, self.active_ind)
def to_dict(self):
- return {'weapon_cd':self.weapon_cd, 'name':self.descr, 'active':self.active_ind}
\ No newline at end of file
+ return {
+ 'weapon_cd': self.weapon_cd,
+ 'name': self.descr,
+ 'active': self.active_ind,
+ }
\ No newline at end of file
+"""
+Models related to the main index page.
+"""
+
from xonstat.util import html_colors
class SummaryStat(object):
+ """
+ The high level summary line that is shown on the main page.
+ """
+
def __repr__(self):
- return "<SummaryStat(total_players=%s, total_games=%s, total_servers=%s)>" % (self.total_players, self.total_games, self.total_servers)
+ return ("<SummaryStat(total_players={}, total_games={}, total_servers={})>"
+ .format(self.total_players, self.total_games, self.total_servers))
class ActivePlayer(object):
- def __init__(self, sort_order=None, player_id=None, nick=None,
- alivetime=None):
+ """
+ A record in the "Most Active Players" list.
+ """
+
+ def __init__(self, sort_order=None, player_id=None, nick=None, alivetime=None):
self.sort_order = sort_order
self.player_id = player_id
self.nick = nick
return html_colors(self.nick)
def __repr__(self):
- return "<ActivePlayer(%s, %s)>" % (self.sort_order, self.player_id)
+ return "<ActivePlayer({}, {})>".format(self.sort_order, self.player_id)
class ActiveServer(object):
- def __init__(self, sort_order=None, server_id=None, server_name=None,
- games=None):
+ """
+ A record in the "Most Active Servers" list.
+ """
+
+ def __init__(self, sort_order=None, server_id=None, server_name=None, games=None):
self.sort_order = sort_order
self.server_id = server_id
self.server_name = server_name
self.games = games
def __repr__(self):
- return "<ActiveServer(%s, %s)>" % (self.sort_order, self.server_id)
+ return "<ActiveServer({}, {})>".format(self.sort_order, self.server_id)
class ActiveMap(object):
- def __init__(self, sort_order=None, map_id=None, map_name=None,
- games=None):
+ """
+ A record in the "Most Active Maps" list.
+ """
+
+ def __init__(self, sort_order=None, map_id=None, map_name=None, games=None):
self.sort_order = sort_order
self.map_id = map_id
self.map_name = map_name
self.games = games
def __repr__(self):
- return "<ActiveMap(%s, %s)>" % (self.sort_order, self.map_id)
\ No newline at end of file
+ return "<ActiveMap({}, {})>".format(self.sort_order, self.map_id)
+"""
+Models related to maps.
+"""
+
from calendar import timegm
from xonstat.util import pretty_date, strip_colors, html_colors
class Map(object):
+ """
+ A playable map. Roughly equivalent to a pk3 file, but distinguished by name instead.
+ """
+
def __init__(self, name=None):
self.name = name
def __repr__(self):
- return "<Map(%s, %s, %s)>" % (self.map_id, self.name, self.version)
+ return "<Map({}, {}, {})>".format(self.map_id, self.name, self.version)
def to_dict(self):
- return {'map_id':self.map_id, 'name':self.name, 'version':self.version,}
+ return {
+ 'map_id': self.map_id,
+ 'name': self.name,
+ 'version': self.version,
+ }
def fuzzy_date(self):
return pretty_date(self.create_dt)
return timegm(self.create_dt.timetuple())
+# TODO: investigate if this model is truly a model, or really just a query (i.e. namedtuple)
class MapCapTime(object):
- """Fastest flag capture times per map, assembled from a SQLAlchemy query"""
+ """
+ Fastest flag capture times per map.
+ """
+
def __init__(self, row):
- self.fastest_cap = row.fastest_cap
- self.create_dt = row.create_dt
- self.create_dt_epoch = timegm(row.create_dt.timetuple())
- self.create_dt_fuzzy = pretty_date(row.create_dt)
- self.player_id = row.player_id
- self.player_nick = row.player_nick
+ self.fastest_cap = row.fastest_cap
+ self.create_dt = row.create_dt
+ self.create_dt_epoch = timegm(row.create_dt.timetuple())
+ self.create_dt_fuzzy = pretty_date(row.create_dt)
+ self.player_id = row.player_id
+ self.player_nick = row.player_nick
self.player_nick_stripped = strip_colors(row.player_nick)
- self.player_nick_html = html_colors(row.player_nick)
- self.game_id = row.game_id
- self.server_id = row.server_id
- self.server_name = row.server_name
+ self.player_nick_html = html_colors(row.player_nick)
+ self.game_id = row.game_id
+ self.server_id = row.server_id
+ self.server_name = row.server_name
def to_dict(self):
return {
- "fastest_cap" : self.fastest_cap.total_seconds(),
- "create_dt_epoch" : self.create_dt_epoch,
- "create_dt_fuzzy" : self.create_dt_fuzzy,
- "player_id" : self.player_id,
- "player_nick" : self.player_nick,
- "player_nick_stripped" : self.player_nick_stripped,
- "game_id" : self.game_id,
- "server_id" : self.server_id,
- "server_name" : self.server_name,
- }
\ No newline at end of file
+ "fastest_cap": self.fastest_cap.total_seconds(),
+ "create_dt_epoch": self.create_dt_epoch,
+ "create_dt_fuzzy": self.create_dt_fuzzy,
+ "player_id": self.player_id,
+ "player_nick": self.player_nick,
+ "player_nick_stripped": self.player_nick_stripped,
+ "game_id": self.game_id,
+ "server_id": self.server_id,
+ "server_name": self.server_name,
+ }
+"""
+Models related to players.
+"""
+
from calendar import timegm
from xonstat.util import html_colors, strip_colors, pretty_date, qfont_decode
class Player(object):
+ """
+ A player, which can represent either a human or a bot.
+ """
+
def nick_html_colors(self, limit=None):
if self.nick is None:
return "Anonymous Player"
return pretty_date(self.create_dt)
def __repr__(self):
- return "<Player(%s, %s)>" % (self.player_id, self.nick.encode('utf-8'))
+ return "<Player({}, {})>".format(self.player_id, self.nick.encode('utf-8'))
def to_dict(self):
- return {'player_id':self.player_id, 'nick':self.nick,
- 'joined':self.create_dt.strftime('%Y-%m-%dT%H:%M:%SZ'),
- 'active_ind':self.active_ind, 'location':self.location,
- 'stripped_nick':qfont_decode(self.stripped_nick)}
+ return {
+ 'player_id': self.player_id,
+ 'nick': self.nick,
+ 'joined': self.create_dt.strftime('%Y-%m-%dT%H:%M:%SZ'),
+ 'active_ind': self.active_ind,
+ 'location': self.location,
+ 'stripped_nick': qfont_decode(self.stripped_nick),
+ }
def epoch(self):
return timegm(self.create_dt.timetuple())
+class Achievement(object):
+ """
+ A type of achievement. Referenced implicitly in PlayerAchievement.
+ """
+
+ def __repr__(self):
+ return "<Achievement({}, {}, {})>".format(self.achievement_cd, self.descr, self.limit)
+
+ def to_dict(self):
+ return {
+ 'achievement_cd': self.achievement_cd,
+ 'name': self.descr,
+ 'limit':self.limit,
+ }
+
+
class PlayerAchievement(object):
+ """
+ Achievements a player has earned.
+ """
+
def __repr__(self):
- return "<PlayerAchievement(%s, %s)>" % (self.player_id, self.achievement_cd)
+ return "<PlayerAchievement({}, {})>".format(self.player_id, self.achievement_cd)
def to_dict(self):
- return {'player_id':self.player_id, 'achievement_cd':self.achievement_cd}
+ return {
+ 'player_id': self.player_id,
+ 'achievement_cd': self.achievement_cd,
+ }
class Hashkey(object):
+ """
+ A player's identifying key from the d0_blind_id library.
+ """
+
def __init__(self, player_id=None, hashkey=None):
self.player_id = player_id
self.hashkey = hashkey
def __repr__(self):
- return "<Hashkey(%s, %s)>" % (self.player_id, self.hashkey)
+ return "<Hashkey({}, {})>".format(self.player_id, self.hashkey)
def to_dict(self):
- return {'player_id':self.player_id, 'hashkey':self.hashkey}
+ return {
+ 'player_id': self.player_id,
+ 'hashkey': self.hashkey
+ }
class PlayerNick(object):
+ """
+ A single nickname a player has used in a game.
+ """
+
def __repr__(self):
- return "<PlayerNick(%s, %s)>" % (self.player_id, qfont_decode(self.stripped_nick))
+ return "<PlayerNick({}, {})>".format(self.player_id, qfont_decode(self.stripped_nick))
def to_dict(self):
- return {'player_id':self.player_id, 'name':qfont_decode(self.stripped_nick)}
+ return {
+ 'player_id': self.player_id,
+ 'name': qfont_decode(self.stripped_nick),
+ }
class PlayerElo(object):
- def __init__(self, player_id=None, game_type_cd=None, elo=None):
+ """
+ A player's skill for a particular game type, as determined by a modified Elo algorithm.
+ """
+ def __init__(self, player_id=None, game_type_cd=None, elo=None):
self.player_id = player_id
self.game_type_cd = game_type_cd
self.elo = elo
self.games = 0
def __repr__(self):
- return "<PlayerElo(pid=%s, gametype=%s, elo=%s, games=%s)>" % (self.player_id, self.game_type_cd, self.elo, self.games)
+ return ("<PlayerElo(pid={}, gametype={}, elo={}, games={})>"
+ .format(self.player_id, self.game_type_cd, self.elo, self.games))
def to_dict(self):
- return {'player_id':self.player_id, 'game_type_cd':self.game_type_cd, 'elo':self.elo, 'games':self.games}
+ return {
+ 'player_id': self.player_id,
+ 'game_type_cd': self.game_type_cd,
+ 'elo': self.elo,
+ 'games': self.games,
+ }
class PlayerRank(object):
+ """
+ A player's rank for a given game type.
+ """
def nick_html_colors(self, limit=None):
if self.nick is None:
return html_colors(self.nick, limit)
def __repr__(self):
- return "<PlayerRank(pid=%s, gametype=%s, rank=%s)>" % (self.player_id, self.game_type_cd, self.rank)
+ return ("<PlayerRank(pid={}, gametype={}, rank={})>"
+ .format(self.player_id, self.game_type_cd, self.rank))
def to_dict(self):
- return {'player_id':self.player_id, 'game_type_cd':self.game_type_cd, 'rank':self.rank}
+ return {
+ 'player_id': self.player_id,
+ 'game_type_cd': self.game_type_cd,
+ 'rank': self.rank
+ }
class PlayerCaptime(object):
- def __init__(self, player_id=None, game_id=None, map_id=None,
- fastest_cap=None, mod=None):
+ """
+ A flag capture time for a player on a given map.
+ """
+
+ def __init__(self, player_id=None, game_id=None, map_id=None, fastest_cap=None, mod=None):
self.player_id = player_id
self.game_id = game_id
self.map_id = map_id
self.mod = mod
def __repr__(self):
- return "<PlayerCaptime(pid=%s, map_id=%s, mod=%s)>" % (self.player_id,
- self.map_id, self.mod)
+ return ("<PlayerCaptime(pid={}, map_id={}, mod={})>"
+ .format(self.player_id, self.map_id, self.mod))
def fuzzy_date(self):
return pretty_date(self.create_dt)
class PlayerGroups(object):
+ """
+ An authorization group a player belongs to. Used to control access.
+ """
+
def __init__(self, player_id=None, group_name=None):
self.player_id = player_id
self.group_name = group_name
def __repr__(self):
- return "<PlayerGroups(%s, %s)>" % (self.player_id, self.group_name)
+ return "<PlayerGroups({}, {})>".format(self.player_id, self.group_name)
+# TODO: determine if this is a real model (it is very similar to PlayerCaptime from above)
class PlayerCapTime(object):
- """Fastest flag capture times per player, assembled from a SQLAlchemy query"""
+ """
+ Fastest flag capture times per player.
+ """
+
def __init__(self, row):
self.fastest_cap = row.fastest_cap
self.create_dt = row.create_dt
"map_name": self.map_name,
"server_id": self.server_id,
"server_name": self.server_name,
- }
+ }
class PlayerMedal(object):
- def __repr__(self):
- return "<PlayerRank(pid=%s, place=%s, alt=%s)>" % (self.player_id,
- self.place, self.alt)
-
+ """
+ A medal a player has earned in a large tournament.
+ """
-class Achievement(object):
def __repr__(self):
- return "<Achievement(%s, %s, %s)>" % (self.achievement_cd, self.descr, self.limit)
-
- def to_dict(self):
- return {'achievement_cd':self.achievement_cd, 'name':self.descr, 'limit':self.limit}
\ No newline at end of file
+ return "<PlayerRank(pid={}, place={}, alt={})>".format(self.player_id, self.place, self.alt)
+"""
+Models related to servers.
+"""
+
from calendar import timegm
from datetime import datetime as dt
class Server(object):
+ """
+ A Xonotic server, identifiable by name and (when there's a conflict) hashkey.
+ """
+
def __init__(self, name=None, hashkey=None, ip_addr=None):
self.name = name
self.hashkey = hashkey
self.create_dt = dt.utcnow()
def __repr__(self):
- return "<Server(%s, %s)>" % (self.server_id, self.name.encode('utf-8'))
+ return "<Server({}, {})>".format(self.server_id, self.name.encode('utf-8'))
def to_dict(self):
- return {'server_id':self.server_id, 'name':self.name,
- 'ip_addr':self.ip_addr, 'location':self.location}
+ return {
+ 'server_id': self.server_id,
+ 'name': self.name,
+ 'ip_addr': self.ip_addr,
+ 'location': self.location,
+ }
def fuzzy_date(self):
return pretty_date(self.create_dt)