import pyramid.httpexceptions
from sqlalchemy import Sequence
-from sqlalchemy.orm.exc import MultipleResultsFound, NoResultFound
+from sqlalchemy.orm.exc import NoResultFound
from xonstat.elo import EloProcessor
from xonstat.models import DBSession, Server, Map, Game, PlayerGameStat, PlayerWeaponStat
from xonstat.models import PlayerRank, PlayerCaptime
# does any human have a fastest cap?
self.human_fastest = False
+ self.parse()
+
def next_item(self):
"""Returns the next key:value pair off the queue."""
try:
"""Construct a player events listing from the submission."""
# all of the keys related to player records
- player_keys = ['i', 'n', 't', 'e']
+ player_keys = ['i', 'n', 't', 'r', 'e']
player = {key: pid}
except:
minimum_required_players = 2
- return len(submission.human_players) >= minimum_required_players
+ return len(submission.humans) >= minimum_required_players
def do_precondition_checks(settings, submission):
pgstat.rank = int(events.get('rank', None))
pgstat.scoreboardpos = int(events.get('scoreboardpos', pgstat.rank))
- if pgstat.nick != player.nick \
- and player.player_id > 2 \
- and pgstat.nick != 'Anonymous Player':
- register_new_nick(session, player, pgstat.nick)
-
wins = False
# gametype-specific stuff is handled here. if passed to us, we store it
def update_player(session, player, events):
- pass
+ """
+ Updates a player record using the latest information.
+ :param session: SQLAlchemy session
+ :param player: Player model representing what is in the database right now (before updates)
+ :param events: Dict of player events from the submission
+ :return: player
+ """
+ nick = events.get('n', 'Anonymous Player')[:128]
+ if nick != player.nick and not nick.startswith("Anonymous Player"):
+ register_new_nick(session, player, nick)
+
+ return player
def create_player(session, events):
- pass
+ """
+ Creates a new player from the list of events.
+ :param session: SQLAlchemy session
+ :param events: Dict of player events from the submission
+ :return: Player
+ """
+ player = Player()
+ session.add(player)
+ session.flush()
+
+ nick = events.get('n', None)
+ if nick:
+ player.nick = nick[:128]
+ player.stripped_nick = strip_colors(qfont_decode(player.nick))
+ else:
+ player.nick = "Anonymous Player #{0}".format(player.player_id)
+ player.stripped_nick = player.nick
+
+ hk = Hashkey(player_id=player.player_id, hashkey=events.get('P', None))
+ session.add(hk)
+
+ return player
-def get_or_create_players(session, game, gmap, events_by_hashkey):
+def get_or_create_players(session, events_by_hashkey):
hashkeys = set(events_by_hashkey.keys())
players_by_hashkey = {}
.filter(Player.player_id == Hashkey.player_id)\
.filter(Hashkey.hashkey.in_(hashkeys))\
.all():
+ log.debug("Found existing player {} with hashkey {}"
+ .format(p.player_id, hk.hashkey))
+
player = update_player(session, p, events_by_hashkey[hk.hashkey])
players_by_hashkey[hk.hashkey] = player
hashkeys.remove(hk.hashkey)
# The remainder are the players we haven't seen before, so we need to create them.
for hashkey in hashkeys:
player = create_player(session, events_by_hashkey[hashkey])
+
+ log.debug("Created player {0} ({2}) with hashkey {1}"
+ .format(player.player_id, hashkey, player.nick.encode('utf-8')))
+
players_by_hashkey[hashkey] = player
return players_by_hashkey
"""
Entry handler for POST stats submissions.
"""
- try:
- # placeholder for the actual session
- session = None
+ # placeholder for the actual session
+ session = None
+ try:
log.debug("\n----- BEGIN REQUEST BODY -----\n" + request.body +
- "----- END REQUEST BODY -----\n\n")
+ "----- END REQUEST BODY -----\n\n")
(idfp, status) = verify_request(request)
- submission = Submission(request.body, request.headers)
+ try:
+ submission = Submission(request.body, request.headers)
+ except:
+ msg = "Invalid submission"
+ log.debug(msg)
+ raise pyramid.httpexceptions.HTTPUnprocessableEntity(
+ body=msg,
+ content_type="text/plain"
+ )
do_precondition_checks(request.registry.settings, submission)
- #----------------------------------------------------------------------
+ #######################################################################
# Actual setup (inserts/updates) below here
- #----------------------------------------------------------------------
+ #######################################################################
session = DBSession()
# All game types create Game, Server, Map, and Player records
)
events_by_hashkey = {elem["P"]: elem for elem in submission.humans + submission.bots}
- get_or_create_players(session, game, gmap, events_by_hashkey)
- # keep track of the players we've seen
- player_ids = []
+ players_by_hashkey = get_or_create_players(session, events_by_hashkey)
+
pgstats = []
- hashkeys = {}
- for events in submission.humans + submission.bots:
- player = get_or_create_player(session, events['P'], events.get('n', None))
+ elo_pgstats = []
+ player_ids = []
+ hashkeys_by_player_id = {}
+ for hashkey, player in players_by_hashkey.items():
+ events = events_by_hashkey[hashkey]
pgstat = create_game_stat(session, game, gmap, player, events)
pgstats.append(pgstat)
+ # player ranking opt-out
+ if 'r' in events and events['r'] != '0':
+ elo_pgstats.append(pgstat)
+
if player.player_id > 1:
create_anticheats(session, pgstat, game, player, events)
if player.player_id > 2:
player_ids.append(player.player_id)
- hashkeys[player.player_id] = events['P']
+ hashkeys_by_player_id[player.player_id] = hashkey
if should_do_weapon_stats(submission.game_type_cd) and player.player_id > 1:
create_weapon_stats(session, submission.version, game, player, pgstat, events)
- # store them on games for easy access
+ # player_ids for human players get stored directly on games for fast indexing
game.players = player_ids
for events in submission.teams:
create_team_stat(session, game, events)
if server.elo_ind and gametype_elo_eligible(submission.game_type_cd):
- ep = EloProcessor(session, game, pgstats)
+ ep = EloProcessor(session, game, elo_pgstats)
ep.save(session)
+ elos = ep.wip
+ else:
+ elos = {}
session.commit()
log.debug('Success! Stats recorded.')
"game": game,
"gmap": gmap,
"player_ids": player_ids,
- "hashkeys": hashkeys,
- "elos": ep.wip,
+ "hashkeys": hashkeys_by_player_id,
+ "elos": elos,
"ranks": ranks,
}