import calendar
+import collections
import datetime
import logging
import re
log = logging.getLogger(__name__)
+class Submission(object):
+ """Parses an incoming POST request for stats submissions."""
+
+ def __init__(self, body, headers):
+ # a copy of the HTTP headers
+ self.headers = headers
+
+ # a copy of the HTTP POST body
+ self.body = body
+
+ # game metadata
+ self.meta = {}
+
+ # raw player events
+ self.players = []
+
+ # raw team events
+ self.teams = []
+
+ # distinct weapons that we have seen fired
+ self.weapons = set()
+
+ # the parsing deque (we use this to allow peeking)
+ self.q = collections.deque(self.body.split("\n"))
+
+ def next_item(self):
+ """Returns the next key:value pair off the queue."""
+ try:
+ items = self.q.popleft().strip().split(' ', 1)
+ if len(items) == 1:
+ return None, None
+ else:
+ return items
+ except:
+ return None, None
+
+ def check_for_new_weapon_fired(self, sub_key):
+ """Checks if a given player key (subkey, actually) is a new weapon fired in the match."""
+ if sub_key.endswith("cnt-fired"):
+ weapon = sub_key.split("-")[1]
+ if weapon not in self.weapons:
+ self.weapons.add(weapon)
+
+ def parse_player(self, key, pid):
+ """Construct a player events listing from the submission."""
+
+ # all of the keys related to player records
+ player_keys = ['i', 'n', 't', 'e']
+
+ player = {key: pid}
+
+ # Consume all following 'i' 'n' 't' 'e' records
+ while len(self.q) > 0:
+ (key, value) = self.next_item()
+ if key is None and value is None:
+ continue
+ elif key == 'e':
+ (sub_key, sub_value) = value.split(' ', 1)
+ player[sub_key] = sub_value
+
+ # keep track of the distinct weapons fired during the match
+ self.check_for_new_weapon_fired(sub_key)
+ elif key == 'n':
+ player[key] = unicode(value, 'utf-8')
+ elif key in player_keys:
+ player[key] = value
+ else:
+ # something we didn't expect - put it back on the deque
+ self.q.appendleft("{} {}".format(key, value))
+ break
+
+ self.players.append(player)
+
+ def parse_team(self, key, tid):
+ """Construct a team events listing from the submission."""
+ team = {key: tid}
+
+ # Consume all following 'e' records
+ while len(self.q) > 0 and self.q[0].startswith('e'):
+ (_, value) = self.next_item()
+ (sub_key, sub_value) = value.split(' ', 1)
+ team[sub_key] = sub_value
+
+ self.teams.append(team)
+
+ def parse(self):
+ """Parses the request body into instance variables."""
+ while len(self.q) > 0:
+ (key, value) = self.next_item()
+ if key is None and value is None:
+ continue
+ elif key == 'S':
+ self.meta[key] = unicode(value, 'utf-8')
+ elif key == 'P':
+ self.parse_player(key, value)
+ elif key == 'Q':
+ self.parse_team(key, value)
+ else:
+ self.meta[key] = value
+
+ return self
+
+
def parse_stats_submission(body):
"""
Parses the POST request body for a stats submission