import datetime\r
import logging\r
+import pyramid.httpexceptions\r
import re\r
import time\r
-from pyramid.config import get_current_registry\r
from pyramid.response import Response\r
+from sqlalchemy import Sequence\r
from sqlalchemy.orm.exc import MultipleResultsFound, NoResultFound\r
from xonstat.d0_blind_id import d0_blind_id_verify\r
from xonstat.models import *\r
\r
log = logging.getLogger(__name__)\r
\r
+def is_supported_gametype(gametype):\r
+ """Whether a gametype is supported or not"""\r
+ flg_supported = True\r
\r
-def is_verified_request(request):\r
- (idfp, status) = d0_blind_id_verify(\r
- sig=request.headers['X-D0-Blind-Id-Detached-Signature'],\r
- querystring='',\r
- postdata=request.body)\r
+ if gametype == 'cts' or gametype == 'ca' or gametype == 'lms':\r
+ flg_supported = False\r
\r
- log.debug('\nidfp: {0}\nstatus: {1}'.format(idfp, status))\r
+ return flg_supported\r
\r
- if idfp != None:\r
- return True\r
- else:\r
- return False\r
\r
+def verify_request(request):\r
+ try:\r
+ (idfp, status) = d0_blind_id_verify(\r
+ sig=request.headers['X-D0-Blind-Id-Detached-Signature'],\r
+ querystring='',\r
+ postdata=request.body)\r
+\r
+ log.debug('\nidfp: {0}\nstatus: {1}'.format(idfp, status))\r
+ except: \r
+ idfp = None\r
+ status = None\r
\r
-def has_minimum_real_players(player_events):\r
+ return (idfp, status)\r
+\r
+\r
+def has_minimum_real_players(settings, player_events):\r
"""\r
Determines if the collection of player events has enough "real" players\r
to store in the database. The minimum setting comes from the config file\r
"""\r
flg_has_min_real_players = True\r
\r
- settings = get_current_registry().settings\r
try: \r
minimum_required_players = int(\r
settings['xonstat.minimum_required_players'])\r
session.add(player)\r
\r
\r
-def get_or_create_server(session=None, name=None):\r
+def get_or_create_server(session=None, name=None, hashkey=None):\r
"""\r
Find a server by name or create one if not found. Parameters:\r
\r
session - SQLAlchemy database session factory\r
name - server name of the server to be found or created\r
+ hashkey - server hashkey\r
"""\r
try:\r
# find one by that name, if it exists\r
server = session.query(Server).filter_by(name=name).one()\r
- log.debug("Found server id {0}: {1}".format(\r
- server.server_id, server.name.encode('utf-8')))\r
+\r
+ # store new hashkey\r
+ if server.hashkey != hashkey:\r
+ server.hashkey = hashkey\r
+ session.add(server)\r
+\r
+ log.debug("Found existing server {0}".format(server.server_id))\r
+\r
+ except MultipleResultsFound, e:\r
+ # multiple found, so also filter by hashkey\r
+ server = session.query(Server).filter_by(name=name).\\r
+ filter_by(hashkey=hashkey).one()\r
+ log.debug("Found existing server {0}".format(server.server_id))\r
+\r
except NoResultFound, e:\r
- server = Server(name=name)\r
+ # not found, create one\r
+ server = Server(name=name, hashkey=hashkey)\r
session.add(server)\r
session.flush()\r
- log.debug("Created server id {0}: {1}".format(\r
- server.server_id, server.name.encode('utf-8')))\r
- except MultipleResultsFound, e:\r
- # multiple found, so use the first one but warn\r
- log.debug(e)\r
- servers = session.query(Server).filter_by(name=name).order_by(\r
- Server.server_id).all()\r
- server = servers[0]\r
- log.debug("Created server id {0}: {1} but found \\r
- multiple".format(\r
- server.server_id, server.name.encode('utf-8')))\r
+ log.debug("Created server {0} with hashkey {1}".format(\r
+ server.server_id, server.hashkey))\r
\r
return server\r
\r
+\r
def get_or_create_map(session=None, name=None):\r
"""\r
Find a map by name or create one if not found. Parameters:\r
map_id - map on which the game was played\r
winner - the team id of the team that won\r
"""\r
-\r
- game = Game(start_dt=start_dt, game_type_cd=game_type_cd,\r
+ seq = Sequence('games_game_id_seq')\r
+ game_id = session.execute(seq)\r
+ game = Game(game_id=game_id, start_dt=start_dt, game_type_cd=game_type_cd,\r
server_id=server_id, map_id=map_id, winner=winner)\r
session.add(game)\r
- session.flush()\r
log.debug("Created game id {0} on server {1}, map {2} at \\r
{3}".format(game.game_id, \r
server_id, map_id, start_dt))\r
# in here setup default values (e.g. if game type is CTF then\r
# set kills=0, score=0, captures=0, pickups=0, fckills=0, etc\r
# TODO: use game's create date here instead of now()\r
- pgstat = PlayerGameStat(create_dt=datetime.datetime.now())\r
+ seq = Sequence('player_game_stats_player_game_stat_id_seq')\r
+ pgstat_id = session.execute(seq)\r
+ pgstat = PlayerGameStat(player_game_stat_id=pgstat_id, \r
+ create_dt=datetime.datetime.now())\r
\r
# set player id from player record\r
pgstat.player_id = player.player_id\r
\r
# if the nick we end up with is different from the one in the\r
# player record, change the nick to reflect the new value\r
- if pgstat.nick != player.nick and player.player_id > 1:\r
+ if pgstat.nick != player.nick and player.player_id > 2:\r
register_new_nick(session, player, pgstat.nick)\r
\r
# if the player is ranked #1 and it is a team game, set the game's winner\r
session.add(game)\r
\r
session.add(pgstat)\r
- session.flush()\r
\r
return pgstat\r
\r
matched = re.search("acc-(.*?)-cnt-fired", key)\r
if matched:\r
weapon_cd = matched.group(1)\r
+ seq = Sequence('player_weapon_stats_player_weapon_stats_id_seq')\r
+ pwstat_id = session.execute(seq)\r
pwstat = PlayerWeaponStat()\r
+ pwstat.player_weapon_stats_id = pwstat_id\r
pwstat.player_id = player.player_id\r
pwstat.game_id = game.game_id\r
pwstat.player_game_stat_id = pgstat.player_game_stat_id\r
pwstat.frags = int(round(float(\r
player_events['acc-' + weapon_cd + '-frags'])))\r
\r
+ log.debug(pwstat)\r
session.add(pwstat)\r
+ log.debug(pwstat)\r
pwstats.append(pwstat)\r
\r
return pwstats\r
current_team = None\r
players = []\r
\r
+ log.debug("----- BEGIN REQUEST BODY -----")\r
log.debug(request.body)\r
+ log.debug("----- END REQUEST BODY -----")\r
\r
for line in request.body.split('\n'):\r
try:\r
Entry handler for POST stats submissions.\r
"""\r
try:\r
- if not is_verified_request(request):\r
- raise Exception("Request is not verified.")\r
-\r
session = DBSession()\r
\r
+ (idfp, status) = verify_request(request)\r
+ if not idfp:\r
+ raise pyramid.httpexceptions.HTTPUnauthorized\r
+ \r
(game_meta, players) = parse_body(request) \r
- \r
+ \r
if not has_required_metadata(game_meta):\r
- log.debug("Required game meta fields (T, G, M, or S) missing. "\\r
+ log.debug("Required game meta fields missing. "\\r
"Can't continue.")\r
- raise Exception("Required game meta fields (T, G, M, or S) missing.")\r
- \r
- if not has_minimum_real_players(players):\r
- raise Exception("The number of real players is below the minimum. "\\r
- "Stats will be ignored.")\r
-\r
- server = get_or_create_server(session=session, name=game_meta['S'])\r
+ raise pyramid.exceptions.HTTPUnprocessableEntity\r
+ \r
+ if not is_supported_gametype(game_meta['G']):\r
+ raise pyramid.httpexceptions.HTTPOk\r
+ \r
+ if not has_minimum_real_players(request.registry.settings, players):\r
+ log.debug("The number of real players is below the minimum. " + \r
+ "Stats will be ignored.")\r
+ raise pyramid.httpexceptions.HTTPOk\r
+ \r
+ server = get_or_create_server(session=session, hashkey=idfp, \r
+ name=game_meta['S'])\r
+ \r
gmap = get_or_create_map(session=session, name=game_meta['M'])\r
-\r
+ log.debug(gmap)\r
+ \r
game = create_game(session=session, \r
start_dt=datetime.datetime(\r
*time.gmtime(float(game_meta['T']))[:6]), \r
server_id=server.server_id, game_type_cd=game_meta['G'], \r
- map_id=gmap.map_id)\r
- \r
+ map_id=gmap.map_id)\r
+ log.debug(gmap)\r
+ \r
# find or create a record for each player\r
# and add stats for each if they were present at the end\r
# of the game\r
nick = player_events['n']\r
else:\r
nick = None\r
-\r
+ \r
if 'matches' in player_events and 'scoreboardvalid' \\r
- in player_events:\r
+ in player_events:\r
player = get_or_create_player(session=session, \r
hashkey=player_events['P'], nick=nick)\r
log.debug('Creating stats for %s' % player_events['P'])\r
create_player_stats(session=session, player=player, game=game, \r
player_events=player_events)\r
- \r
+ \r
session.commit()\r
log.debug('Success! Stats recorded.')\r
return Response('200 OK')\r