def update_runinfo_delete( self, user, old_run ): # Update avg, num runs runinfo = self.get_runinfo( user.username, old_run['game'], old_run['category'], no_refresh=True ) if runinfo is None: return if runinfo == self.OVER_QUOTA_ERROR: self.update_cache_runinfo( user.username, game, category, None ) return if runinfo['num_runs'] <= 0: logging.error( "Failed to update runinfo due to nonpositive " + "num_runs " + str( runinfo['num_runs'] ) ) self.update_cache_runinfo( user.username, old_run['game'], old_run['category'], None ) return if( runinfo['num_runs'] > 1 ): runinfo['avg_seconds'] -= ( 1.0 * old_run['seconds'] / runinfo['num_runs'] ) runinfo['num_runs'] -= 1 runinfo['avg_seconds'] *= ( 1.0 * ( runinfo['num_runs'] + 1 ) / runinfo['num_runs'] ) runinfo['avg_time'] = util.seconds_to_timestr( runinfo['avg_seconds'] ) if( runinfo['pb_seconds'] == old_run['seconds'] ): # We need to replace the pb too try: q = db.Query( runs.Runs, projection=('seconds', 'date', 'video', 'version') ) q.ancestor( runs.key() ) q.filter( 'username ='******'game =', old_run['game'] ) q.filter( 'category =', old_run['category'] ) q.order( 'seconds' ) q.order( 'date' ) pb_run = q.get( ) if pb_run: runinfo['pb_seconds'] = pb_run.seconds runinfo['pb_time'] = util.seconds_to_timestr( pb_run.seconds ) runinfo['pb_date'] = pb_run.date runinfo['video'] = pb_run.video runinfo['version'] = pb_run.version else: logging.error( "Unable to update runinfo due to no " + "new pb found" ) self.update_cache_runinfo( user.username, old_run['game'], old_run['category'], None ) return except apiproxy_errors.OverQuotaError, msg: logging.error( msg ) self.update_cache_runinfo( user.username, old_run['game'], old_run['category'], None ) return self.update_cache_runinfo( user.username, old_run['game'], old_run['category'], runinfo )
def get_runinfo( self, username, game, category, no_refresh=False ): key = self.get_runinfo_memkey( username, game, category ) runinfo = memcache.get( key ) if runinfo is None and not no_refresh: # Not in memcache, so constrcut the runinfo dictionary pb_run = None avg_seconds = 0 num_runs = 0 try: q = db.Query( runs.Runs, projection=('seconds', 'date', 'video', 'version') ) q.ancestor( runs.key() ) q.filter('username ='******'game =', game) q.filter('category =', category) q.order('-date') # Cut off old runs for run in q.run( limit = 100000 ): num_runs += 1 avg_seconds += ( 1.0 / num_runs ) * ( run.seconds - avg_seconds ) if( pb_run is None or run.seconds <= pb_run.seconds ): pb_run = run runinfo = dict( username = username, username_code = util.get_code( username ), category = category, category_code = util.get_code( category ), pb_seconds = None, pb_time = None, pb_date = None, num_runs = num_runs, avg_seconds = avg_seconds, avg_time = util.seconds_to_timestr( avg_seconds, dec_places=0), video = None ) except apiproxy_errors.OverQuotaError, msg: logging.error( msg ) return self.OVER_QUOTA_ERROR # Set the pb time if pb_run: runinfo['pb_seconds'] = pb_run.seconds runinfo['pb_time'] = util.seconds_to_timestr( pb_run.seconds ) runinfo['pb_date'] = pb_run.date runinfo['video'] = pb_run.video runinfo['version'] = pb_run.version if memcache.set( key, runinfo ): logging.debug( "Set " + key + " in memcache" ) else: logging.warning( "Failed to set " + key + " in memcache" )
def get_runinfo(self, username, game, category, no_refresh=False): key = self.get_runinfo_memkey(username, game, category) runinfo = memcache.get(key) if runinfo is None and not no_refresh: # Not in memcache, so constrcut the runinfo dictionary pb_run = None avg_seconds = 0 num_runs = 0 try: q = db.Query(runs.Runs, projection=('seconds', 'date', 'video', 'version')) q.ancestor(runs.key()) q.filter('username ='******'game =', game) q.filter('category =', category) q.order('-date') # Cut off old runs for run in q.run(limit=100000): num_runs += 1 avg_seconds += (1.0 / num_runs) * (run.seconds - avg_seconds) if (pb_run is None or run.seconds <= pb_run.seconds): pb_run = run runinfo = dict(username=username, username_code=util.get_code(username), category=category, category_code=util.get_code(category), pb_seconds=None, pb_time=None, pb_date=None, num_runs=num_runs, avg_seconds=avg_seconds, avg_time=util.seconds_to_timestr(avg_seconds, dec_places=0), video=None) except apiproxy_errors.OverQuotaError, msg: logging.error(msg) return self.OVER_QUOTA_ERROR # Set the pb time if pb_run: runinfo['pb_seconds'] = pb_run.seconds runinfo['pb_time'] = util.seconds_to_timestr(pb_run.seconds) runinfo['pb_date'] = pb_run.date runinfo['video'] = pb_run.video runinfo['version'] = pb_run.version if memcache.set(key, runinfo): logging.debug("Set " + key + " in memcache") else: logging.warning("Failed to set " + key + " in memcache")
def get_runinfo(self, username, game, category, no_refresh=False): key = self.get_runinfo_memkey(username, game, category) runinfo = memcache.get(key) if runinfo is None and not no_refresh: # Not in memcache, so constrcut the runinfo dictionary q = db.Query(runs.Runs, projection=("seconds", "date", "video", "version")) q.ancestor(runs.key()) q.filter("username ="******"game =", game) q.filter("category =", category) q.order("-date") # Cut off old runs pb_run = None avg_seconds = 0 num_runs = 0 for run in q.run(limit=100000): num_runs += 1 avg_seconds += (1.0 / num_runs) * (run.seconds - avg_seconds) if pb_run is None or run.seconds <= pb_run.seconds: pb_run = run runinfo = dict( username=username, username_code=util.get_code(username), category=category, category_code=util.get_code(category), pb_seconds=None, pb_time=None, pb_date=None, num_runs=num_runs, avg_seconds=avg_seconds, avg_time=util.seconds_to_timestr(avg_seconds), video=None, ) # Set the pb time if pb_run: runinfo["pb_seconds"] = pb_run.seconds runinfo["pb_time"] = util.seconds_to_timestr(pb_run.seconds) runinfo["pb_date"] = pb_run.date runinfo["video"] = pb_run.video runinfo["version"] = pb_run.version if memcache.set(key, runinfo): logging.debug("Set " + key + " in memcache") else: logging.warning("Failed to set " + key + " in memcache") elif runinfo is not None: logging.debug("Got " + key + " from memcache") return runinfo
def get(self, run_id): user = self.get_user() if not user: self.redirect("/") return elif user == self.OVER_QUOTA_ERROR: self.error(403) self.render("403.html") return # Get the run run = self.get_run_by_id(run_id) if run == self.OVER_QUOTA_ERROR: self.error(403) self.render("403.html", user=user) return if (not run or (not user.is_mod and run.username != user.username)): self.error(404) self.render("404.html", user=user) return self.render("deleterun.html", user=user, run=run, game_code=util.get_code(run.game), username_code=util.get_code(user.username), time=util.seconds_to_timestr(run.seconds))
def get( self, run_id ): user = self.get_user( ) if not user: self.redirect( "/" ) return elif user == self.OVER_QUOTA_ERROR: self.error( 403 ) self.render( "403.html" ) return # Get the run run = self.get_run_by_id( run_id ) if run == self.OVER_QUOTA_ERROR: self.error( 403 ) self.render( "403.html", user=user ) return if( not run or ( not user.is_mod and run.username != user.username ) ): self.error( 404 ) self.render( "404.html", user=user ) return self.render( "deleterun.html", user=user, run=run, game_code=util.get_code( run.game ), username_code=util.get_code( user.username ), time=util.seconds_to_timestr( run.seconds ) )
def get_runlist_for_runner(self, username, no_refresh=False): key = self.get_runlist_for_runner_memkey(username) runlist = memcache.get(key) if runlist is None and not no_refresh: # Not in memcache, so construct the runlist and store in memcache. runlist = [] try: q = runs.Runs.all() q.ancestor(runs.key()) q.filter('username ='******'-date') q.order('-datetime_created') for run in q.run(limit=1000): runlist.append( dict(run_id=str(run.key().id()), game=run.game, game_code=util.get_code(run.game), category=run.category, category_code=util.get_code(run.category), time=util.seconds_to_timestr(run.seconds), date=run.date, datetime_created=run.datetime_created, video=run.video, version=run.version, notes=run.notes)) except apiproxy_errors.OverQuotaError, msg: logging.error(msg) return self.OVER_QUOTA_ERROR if memcache.set(key, runlist): logging.debug("Set " + key + " in memcache") else: logging.warning("Failed to set " + key + " in memcache")
def get_gamepage(self, game, no_refresh=False): key = self.get_gamepage_memkey(game) gamepage = memcache.get(key) if gamepage is None and not no_refresh: # Not in memcache, so construct the gamepage and store it in # memcache. # Gamepage is a list of dictionaries. These dictionaries have up # to 5 keys, 'category', 'bk_runner', 'bk_time', 'bk_video' and # 'infolist'. gamepage = [] # Grab the game model game_model = self.get_game_model(util.get_code(game)) if game_model is None: logging.error("Could not create " + key + " due to no " + "game model") return None gameinfolist = json.loads(game_model.info) # Use a projection query to get all of the unique # username, category pairs q = db.Query(runs.Runs, projection=("username", "category"), distinct=True) q.ancestor(runs.key()) q.filter("game =", game) q.order("category") cur_category = None for run in q.run(limit=1000): if run.category != cur_category: # New category d = dict(category=run.category, category_code=util.get_code(run.category), infolist=[]) gamepage.append(d) cur_category = run.category # Check for a best known time for this category for gameinfo in gameinfolist: if gameinfo["category"] == run.category: d["bk_runner"] = gameinfo.get("bk_runner") d["bk_time"] = util.seconds_to_timestr(gameinfo.get("bk_seconds")) d["bk_date"] = util.datestr_to_date(gameinfo.get("bk_datestr"))[0] d["bk_video"] = gameinfo.get("bk_video") break # Add the info to the gamepage info = self.get_runinfo(run.username, game, run.category) d["infolist"].append(info) # For each category, sort the runlist by seconds, breaking ties # by date for runlist in gamepage: runlist["infolist"].sort(key=lambda x: util.get_valid_date(x["pb_date"])) runlist["infolist"].sort(key=itemgetter("pb_seconds")) # Sort the categories by number of runners gamepage.sort(key=lambda x: len(x["infolist"]), reverse=True) if memcache.set(key, gamepage): logging.debug("Set " + key + " in memcache") else: logging.warning("Failed to set " + key + " in memcache") elif gamepage is not None: logging.debug("Got " + key + " from memcache") return gamepage
def get_runlist_for_runner(self, username, no_refresh=False): key = self.get_runlist_for_runner_memkey(username) runlist = memcache.get(key) if runlist is None and not no_refresh: # Not in memcache, so construct the runlist and store in memcache. runlist = [] q = runs.Runs.all() q.ancestor(runs.key()) q.filter("username ="******"-date") q.order("-datetime_created") for run in q.run(limit=1000): runlist.append( dict( run_id=str(run.key().id()), game=run.game, game_code=util.get_code(run.game), category=run.category, time=util.seconds_to_timestr(run.seconds), date=run.date, datetime_created=run.datetime_created, video=run.video, version=run.version, notes=run.notes, ) ) if memcache.set(key, runlist): logging.debug("Set " + key + " in memcache") else: logging.warning("Failed to set " + key + " in memcache") elif runlist is not None: logging.debug("Got " + key + " from memcache") return runlist
def update_runinfo_put(self, params): user = params['user'] game = params['game'] category = params['category'] seconds = params['seconds'] time = params['time'] date = params['date'] video = params['video'] version = params['version'] # Update runinfo in memcache runinfo = self.get_runinfo(user.username, game, category, no_refresh=True) if runinfo is None: return if runinfo == self.OVER_QUOTA_ERROR: self.update_cache_runinfo(user.username, game, category, None) return runinfo['num_runs'] += 1 runinfo['avg_seconds'] += ((1.0 / runinfo['num_runs']) * (seconds - runinfo['avg_seconds'])) runinfo['avg_time'] = util.seconds_to_timestr(runinfo['avg_seconds']) if (runinfo['pb_seconds'] is None or runinfo['pb_seconds'] > seconds): # We need to update pb as well runinfo['pb_seconds'] = seconds runinfo['pb_time'] = time runinfo['pb_date'] = date runinfo['video'] = video runinfo['version'] = version self.update_cache_runinfo(user.username, game, category, runinfo)
def update_runinfo_put( self, params ): user = params[ 'user' ] game = params[ 'game' ] category = params[ 'category' ] seconds = params[ 'seconds' ] time = params[ 'time' ] date = params[ 'date' ] video = params[ 'video' ] version = params[ 'version' ] # Update runinfo in memcache runinfo = self.get_runinfo( user.username, game, category, no_refresh=True ) if runinfo is None: return runinfo['num_runs'] += 1 runinfo['avg_seconds'] += ( ( 1.0 / runinfo['num_runs'] ) * ( seconds - runinfo['avg_seconds'] ) ) runinfo['avg_time'] = util.seconds_to_timestr( runinfo['avg_seconds'] ) if( runinfo['pb_seconds'] is None or runinfo['pb_seconds'] > seconds ): # We need to update pb as well runinfo['pb_seconds'] = seconds runinfo['pb_time'] = time runinfo['pb_date'] = date runinfo['video'] = video runinfo['version'] = version self.update_cache_runinfo( user.username, game, category, runinfo )
def get( self, game_code ): user = self.get_user( ) return_url = self.request.get( 'from' ) if not return_url: return_url = "/" # Get the category category_code = self.request.get( 'c' ) if user is None or not category_code: self.error( 404 ) self.render( "404.html", user=user ) return # Have to take the code of the category code because of percent # encoded plusses category_code = util.get_code( category_code ) # Check to make sure that the user has run this game game_model = self.get_game_model( game_code ) user_has_run = self.get_user_has_run( user.username, game_model.game ) if not user_has_run and not user.is_mod: self.error( 404 ) self.render( "404.html", user=user ) return # Find the corresponding gameinfo for this category gameinfolist = json.loads( game_model.info ) gameinfo = None for g in gameinfolist: if util.get_code( g['category'] ) == category_code: gameinfo = g break if gameinfo is None: self.error( 404 ) self.render( "404.html", user=user ) return params = dict( user=user, game=game_model.game, game_code=game_code, category=gameinfo['category'], return_url=return_url ) params['username'] = gameinfo.get( 'bk_runner' ) if params['username'] is None: params['username'] = '' params['time'] = '' params['datestr'] = '' params['video'] = '' params['updating'] = False else: params['time'] = util.seconds_to_timestr( gameinfo.get( 'bk_seconds' ) ) params['datestr'] = gameinfo.get( 'bk_datestr' ) params['video'] = gameinfo.get( 'bk_video' ) params['updating'] = True if return_url[ 0 : len( '/runner/' ) ] == '/runner/': params['from_runnerpage'] = True else: params['from_runnerpage'] = False self.render( "updatebkt.html", **params )
def get( self ): user = self.get_user( ) if not user: self.redirect( "/" ) return params = dict( user=user ) # Are we editing an existing run? run_id = self.request.get( 'edit' ) if run_id: # Grab the run to edit run = self.get_run_by_id( run_id ) if not run or ( not user.is_mod and user.username != run.username ): self.error( 404 ) self.render( "404.html", user=user ) return params[ 'game' ] = run.game params[ 'category' ] = run.category params[ 'time' ] = util.seconds_to_timestr( run.seconds ) if run.date is not None: params[ 'datestr' ] = run.date.strftime( "%m/%d/%Y" ); params[ 'run_id' ] = run_id if run.video is not None: params[ 'video' ] = run.video if run.version is not None: params[ 'version' ] = run.version if run.notes is not None: params[ 'notes' ] = run.notes else: # Start with the game, category and version from this user's # last run run = self.get_last_run( user.username ) params['set_date_to_today'] = True; if run is not None: params['game'] = run.game params['category'] = run.category if run.version is not None: params['version'] = run.version # Grab all of the games and categories for autocompleting params['categories'] = self.get_categories( ) self.render( "submit.html", **params )
def get(self): user = self.get_user() if not user: self.redirect("/") return params = dict(user=user) # Are we editing an existing run? run_id = self.request.get('edit') if run_id: # Grab the run to edit run = self.get_run_by_id(run_id) if not run or (not user.is_mod and user.username != run.username): self.error(404) self.render("404.html", user=user) return params['game'] = run.game params['category'] = run.category params['time'] = util.seconds_to_timestr(run.seconds) if run.date is not None: params['datestr'] = run.date.strftime("%m/%d/%Y") params['run_id'] = run_id if run.video is not None: params['video'] = run.video if run.version is not None: params['version'] = run.version if run.notes is not None: params['notes'] = run.notes else: # Start with the game, category and version from this user's # last run run = self.get_last_run(user.username) params['set_date_to_today'] = True if run is not None: params['game'] = run.game params['category'] = run.category if run.version is not None: params['version'] = run.version # Grab all of the games and categories for autocompleting params['categories'] = self.get_categories() self.render("submit.html", **params)
def get( self, game_code ): user = self.get_user( ) if user == self.OVER_QUOTA_ERROR: self.error( 403 ) self.render( "403.html" ) return return_url = self.request.get( 'from' ) if not return_url: return_url = "/" # Get the category category_code = self.request.get( 'c' ) if user is None or not category_code: self.error( 404 ) self.render( "404.html", user=user ) return # Have to take the code of the category code because of percent # encoded plusses category_code = util.get_code( category_code ) # Check to make sure that the user has run this game game_model = self.get_game_model( game_code ) if game_model == self.OVER_QUOTA_ERROR: self.error( 403 ) self.render( "403.html", user=user ) return user_has_run = self.get_user_has_run( user.username, game_model.game ) if user_has_run == self.OVER_QUOTA_ERROR: self.error( 403 ) self.render( "403.html", user=user ) return if not user_has_run and not user.is_mod: self.error( 404 ) self.render( "404.html", user=user ) return # Find the corresponding gameinfo for this category gameinfolist = json.loads( game_model.info ) gameinfo = None for g in gameinfolist: if util.get_code( g['category'] ) == category_code: gameinfo = g break if gameinfo is None: self.error( 404 ) self.render( "404.html", user=user ) return params = dict( user=user, game=game_model.game, game_code=game_code, category=gameinfo['category'], return_url=return_url ) params['username'] = gameinfo.get( 'bk_runner' ) if params['username'] is None: params['username'] = '' params['time'] = '' params['datestr'] = '' params['video'] = '' params['updating'] = False else: params['time'] = util.seconds_to_timestr( gameinfo.get( 'bk_seconds' ) ) params['datestr'] = gameinfo.get( 'bk_datestr' ) params['video'] = gameinfo.get( 'bk_video' ) params['updating'] = True if return_url[ 0 : len( '/runner/' ) ] == '/runner/': params['from_runnerpage'] = True else: params['from_runnerpage'] = False self.render( "updatebkt.html", **params )
def post( self, game_code ): user = self.get_user( ) if user == self.OVER_QUOTA_ERROR: self.error( 403 ) self.render( "403.html" ) return return_url = self.request.get( 'from' ) if not return_url: return_url = "/" # Get the category category_code = self.request.get( 'c' ) if user is None or category_code is None: self.error( 404 ) self.render( "404.html", user=user ) return # Have to take the code of the category code because of percent # encoded plusses category_code = util.get_code( category_code ) # Check to make sure that the user has run this game game_model = self.get_game_model( game_code ) if game_model == self.OVER_QUOTA_ERROR: self.error( 403 ) self.render( "403.html", user=user ) return user_has_run = self.get_user_has_run( user.username, game_model.game ) if user_has_run == self.OVER_QUOTA_ERROR: self.error( 403 ) self.render( "403.html", user=user ) return if not user_has_run and not user.is_mod: self.error( 404 ) self.render( "404.html", user=user ) return # Find the corresponding gameinfo for this category gameinfolist = json.loads( game_model.info ) gameinfo = None for g in gameinfolist: if util.get_code( g['category'] ) == category_code: gameinfo = g break if gameinfo is None: self.error( 404 ) self.render( "404.html", user=user ) return # Get the inputs username = self.request.get( 'username' ) time = self.request.get( 'time' ) datestr = self.request.get( 'date' ) video = self.request.get( 'video' ) params = dict( user=user, game=game_model.game, game_code=game_code, category=gameinfo['category'], username=username, time=time, datestr=datestr, video=video, return_url=return_url ) # Are we updating? if gameinfo.get( 'bk_runner' ) is None: params['updating'] = False else: params['updating'] = True valid = True # Check for where we came from if return_url[ 0 : len( '/runner/' ) ] == '/runner/': params['from_runnerpage'] = True else: params['from_runnerpage'] = False if not username and not time and not datestr and not video: gameinfo['bk_runner'] = None gameinfo['bk_seconds'] = None gameinfo['bk_datestr'] = None gameinfo['bk_video'] = None date = None else: # Make sure we got a username if not username: params['username_error'] = "You must enter a runner" valid = False # Parse the time into seconds, ensure it is valid ( seconds, time_error ) = util.timestr_to_seconds( time ) if not seconds: params['time_error'] = "Invalid time: " + time_error valid = False # Parse the date, ensure it is valid ( date, date_error ) = util.datestr_to_date( datestr ) if date_error: params['date_error'] = "Invalid date: " + date_error valid = False if not valid: self.render( "updatebkt.html", **params ) return time = util.seconds_to_timestr( seconds ) # Standard format params['time'] = time # Store the best known time gameinfo['bk_runner'] = username gameinfo['bk_seconds'] = seconds gameinfo['bk_datestr'] = datestr gameinfo['bk_video'] = video gameinfo['bk_updater'] = user.username game_model.info = json.dumps( gameinfolist ) game_model.put( ) # Update game_model in memcache self.update_cache_game_model( game_code, game_model ) # Update gamepage in memcache gamepage = self.get_gamepage( game_model.game, no_refresh=True ) if gamepage == self.OVER_QUOTA_ERROR: self.update_cache_gamepage( game, None ) elif gamepage is not None: for d in gamepage: if d['category'] == gameinfo['category']: d['bk_runner'] = gameinfo['bk_runner'] d['bk_time'] = util.seconds_to_timestr( gameinfo['bk_seconds'] ) d['bk_date'] = date d['bk_video'] = gameinfo['bk_video'] break self.update_cache_gamepage( game_model.game, gamepage ) # All dun self.redirect( return_url )
def get_response(self, body): body_type = body.get('type') # Currently 5 types: verifylogin, gamelist, categories, gamecategories # and submitrun if body_type is None: return self.get_fail_response("No type given.") elif body_type == 'verifylogin': (valid, response) = self.verify_login(body) return response elif body_type == 'gamelist': # Note that this is a different type of gamelist than the one # generated in games.py categories = self.get_categories() d = dict() for game in categories.keys(): d[util.get_code(game)] = game return self.get_success_response(data=d) elif body_type == 'categories': categories = self.get_categories() d = dict() for game, categorylist in categories.iteritems(): game_code = util.get_code(game) for category in categorylist: category_code = util.get_code(category) d[game_code + ':' + category_code] = (game + ' - ' + category) return self.get_success_response(data=d) elif body_type == 'gamecategories': game_code = body.get('game') game_model = self.get_game_model(game_code) if game_code is None: return self.get_fail_response('No game specified') elif game_model is None: return self.get_fail_response('Unknown game [' + game_code + '].') else: d = dict() gameinfolist = json.loads(game_model.info) for gameinfo in gameinfolist: category = gameinfo['category'] d[util.get_code(category)] = category return self.get_success_response(data=d) elif body_type == 'submitrun': # First, verify login credentials (valid, response) = self.verify_login(body) if not valid: return response # Grab the params from the body username = body.get('username') game_code = body.get('game') category_code = body.get('category') version = body.get('version') time = body.get('runtime') video = body.get('video') notes = body.get('comment') splits = body.get('splits') # Make sure the game and category exist (can't handle new games # and categories just yet) if game_code is None: return self.get_fail_response('No game given') game_model = self.get_game_model(game_code) if game_model is None: return self.get_fail_response('Unknown game [' + game_code + '].') if category_code is None: return self.get_fail_response('No category specified') gameinfolist = json.loads(game_model.info) category = None for gameinfo in gameinfolist: if category_code == util.get_code(gameinfo['category']): category = gameinfo['category'] break if category is None: return self.get_fail_response('Unknown category [' + category_code + '] for game [' + game_code + '].') # Parse the time into seconds and ensure it is valid if time is None: return self.get_fail_response('No runtime given') (seconds, time_error) = util.timestr_to_seconds(time) if seconds is None: return self.get_fail_response('Bad runtime [' + time + '] given: ' + time_error) # Ensure standard format time = util.seconds_to_timestr(seconds) # Make sure that the notes are not too long if notes is not None and len(notes) > 140: return self.get_fail_response('Comment is too long; must be ' + 'at most 140 characters.') # Figure out current date in user's local timezone user = self.get_runner(util.get_code(username)) if user.timezone: tz = pytz.timezone(user.timezone) local_today = datetime.now(pytz.utc).astimezone(tz) else: # UTC by default local_today = datetime.now(pytz.utc) date = local_today.date() # Load up the needed parameters and put a new run params = dict(user=user, game=game_model.game, game_code=game_code, game_model=game_model, category=category, category_found=True, seconds=seconds, time=time, video=video, version=version, notes=notes, valid=True, date=date, datestr=date.strftime("%m/%d/%Y"), is_bkt=False) if self.put_new_run(params): return self.get_success_response() else: if params.get('video_error'): return self.get_fail_response('Bad video link [' + video + ']: ' + params['video_error']) else: return self.get_fail_response('Sorry, an unknown error ' + 'occurred.') return self.get_fail_response("Unknown type [" + body_type + "].")
def change_categories( self, params ): res = '' # Grab the old game model old_game_code = util.get_code( params['old_game'] ) if old_game_code == params['new_game_code']: old_game_model = params['new_game_model'] else: old_game_model = self.get_game_model( old_game_code ) if old_game_model is None: return "Did not find game [" + params['old_game'] + "]" if params['new_game_model'] is None: # New game does not exist, so create it params['new_game_model'] = games.Games( game = params['new_game'], info = json.dumps( [ ] ), num_pbs = 0, parent = games.key( ), key_name = params['new_game_code'] ) res += ( 'Created new game model for game [' + params['new_game'] + ']<br>' ) if not params['new_category_found']: # Add the new category to the new game model gameinfolist = json.loads( params['new_game_model'].info ) d = dict( category=params['new_category'], bk_runner=None, bk_seconds=None, bk_video=None, bk_datestr=None, bk_updater=None ) gameinfolist.append( d ) params['new_game_model'].info = json.dumps( gameinfolist ) res += 'Added new category [' + params['new_category'] + ']<br>' # Grab the gameinfo for the old game oldgameinfolist = json.loads( old_game_model.info ) oldgameinfo = None for g in oldgameinfolist: if( util.get_code( params['old_category'] ) == util.get_code( g['category'] ) ): oldgameinfo = g break if oldgameinfo is None: return "Did not find old category [" + params['old_category'] + ']' # Grab the gameinfo for the new game newgameinfolist = json.loads( params['new_game_model'].info ) newgameinfo = None for g in newgameinfolist: if( util.get_code( params['new_category'] ) == util.get_code( g['category'] ) ): newgameinfo = g break if newgameinfo is None: return "Did not find new category [" + params['new_category'] + ']' # Update best known time if necessary if( oldgameinfo.get( 'bk_seconds' ) is not None and ( newgameinfo.get( 'bk_seconds' ) is None or oldgameinfo.get( 'bk_seconds' ) < newgameinfo.get( 'bk_seconds' ) ) ): newgameinfo['bk_seconds'] = oldgameinfo.get( 'bk_seconds' ) newgameinfo['bk_runner'] = oldgameinfo.get( 'bk_runner' ) newgameinfo['bk_datestr'] = oldgameinfo.get( 'bk_datestr' ) newgameinfo['bk_video'] = oldgameinfo.get( 'bk_video' ) newgameinfo['bk_updater'] = oldgameinfo.get( 'bk_updater' ) params['new_game_model'].info = json.dumps( newgameinfolist ) res += 'Updated bkt<br>' # Update num_pbs for old game, new game res += ( 'Previous num_pbs for old game, category = ' + str( old_game_model.num_pbs ) + '<br>' ) res += ( 'Previous num_pbs for new game, category = ' + str( params['new_game_model'].num_pbs ) + '<br>' ) q = db.Query( runs.Runs, projection=['username'], distinct=True ) q.ancestor( runs.key() ) q.filter( 'game =', params['old_game'] ) q.filter( 'category =', params['old_category'] ) for run in q.run( limit=1000 ): old_game_model.num_pbs -= 1 q2 = db.Query( runs.Runs ) q2.ancestor( runs.key() ) q2.filter( 'game =', params['new_game'] ) q2.filter( 'category =', params['new_category'] ) q2.filter( 'username ='******'new_game_model'].num_pbs += 1 else: # Need to decrement runner's num_pbs runner = self.get_runner( util.get_code( run.username ) ) runner.num_pbs -= 1 runner.put( ) res += ( "Updated " + run.username + " num_pbs from " + str( runner.num_pbs + 1 ) + " to " + str( runner.num_pbs ) + "<br>" ) res += ( 'Updated num_pbs for old game, category = ' + str( old_game_model.num_pbs ) + '<br>' ) res += ( 'Updated num_pbs for new game, category = ' + str( params['new_game_model'].num_pbs ) + '<br>' ) # Update old, new game models in database old_game_model.put( ) if old_game_code != params['new_game_code']: params['new_game_model'].put( ) # Change the runs res += "<br>Changed runs:<br>" q = db.Query( runs.Runs ) q.ancestor( runs.key() ) q.filter( 'game =', params['old_game'] ) q.filter( 'category =', params['old_category'] ) for run in q.run( limit = 10000 ): # Update the run run.game = params['new_game'] run.category = params['new_category'] run.put( ) res += ( 'Runner=' + run.username + ' time=' + util.seconds_to_timestr( run.seconds ) + '<br>' ) if not memcache.flush_all( ): res += "Failed to flush memcache<br>" # All dun return res
def post(self): user = self.get_user() if not user: self.redirect("/") return game = self.request.get('game') category = self.request.get('category') time = self.request.get('time') datestr = self.request.get('date') video = self.request.get('video') version = self.request.get('version') notes = self.request.get('notes') is_bkt = self.request.get('bkt', default_value="no") if is_bkt == "yes": is_bkt = True else: is_bkt = False run_id = self.request.get('edit') params = dict(user=user, game=game, category=category, time=time, datestr=datestr, video=video, version=version, notes=notes, run_id=run_id, is_bkt=is_bkt) valid = True # Make sure the game doesn't already exist under a similar name game_code = util.get_code(game) game_model = self.get_game_model(game_code) if not game_code: params['game_error'] = "Game cannot be blank" valid = False elif game_model is not None and game != game_model.game: params['game_error'] = ("Game already exists under [" + game_model.game + "] (case sensitive)." + " Hit submit again to confirm.") params['game'] = game_model.game valid = False elif not games.valid_game_or_category(game): params['game_error'] = ("Game name must not use any 'funny'" + " characters and can be up to 100 " + "characters long") valid = False params['game_code'] = game_code params['game_model'] = game_model # Make sure the category doesn't already exist under a similar name category_code = util.get_code(category) category_found = False if not category_code: params['category_error'] = "Category cannot be blank" valid = False elif game_model is not None: infolist = json.loads(game_model.info) for info in infolist: if category_code == util.get_code(info['category']): category_found = True if category != info['category']: params['category_error'] = ( "Category already exists " + "under [" + info['category'] + "] " + "(case sensitive). " + "Hit submit again to " + "confirm.") params['category'] = info['category'] valid = False break if not category_found and not games.valid_game_or_category(category): params['category_error'] = ("Category must not use any 'funny'" + " characters and can be up to 100 " + "characters long") valid = False params['category_found'] = category_found # Parse the time into seconds, ensure it is valid (seconds, time_error) = util.timestr_to_seconds(time) if seconds is None: params['time_error'] = "Invalid time: " + time_error params['seconds'] = -1 valid = False else: time = util.seconds_to_timestr(seconds) # Enforce standard form params['time'] = time params['seconds'] = seconds # Parse the date, ensure it is valid (params['date'], params['date_error']) = util.datestr_to_date(datestr) if params['date_error']: params['date_error'] = "Invalid date: " + params['date_error'] valid = False # Check that if this is a best known time, then it beats the old # best known time. if is_bkt and game_model is not None: gameinfolist = json.loads(game_model.info) for gameinfo in gameinfolist: if gameinfo['category'] == params['category']: if (gameinfo.get('bk_seconds') is not None and gameinfo['bk_seconds'] <= seconds): s = ("This time does not beat current best known " + "time of " + util.seconds_to_timestr( gameinfo.get('bk_seconds')) + " by " + gameinfo['bk_runner'] + " (if best known time is incorrect, you can " + "update best known time after submission)") params['bkt_error'] = s params['is_bkt'] = False valid = False break # Check that if this is not the best known time, then it doesn't beat # the old best known time if not is_bkt and game_model is not None: gameinfolist = json.loads(game_model.info) for gameinfo in gameinfolist: if gameinfo['category'] == params['category']: if (gameinfo.get('bk_seconds') is not None and seconds < gameinfo['bk_seconds']): s = ( "This time beats the current best known time of " + util.seconds_to_timestr(gameinfo.get('bk_seconds')) + " by " + gameinfo['bk_runner'] + " (if best known time is incorrect, you can " + "update best known time after submission)") params['bkt_error'] = s params['is_bkt'] = True valid = False break # Make sure that the notes are not too long if len(notes) > 140: params['notes_error'] = "Notes must be at most 140 characters" valid = False params['valid'] = valid if run_id: success = self.put_existing_run(params) else: success = self.put_new_run(params) if success: self.redirect("/runner/" + util.get_code(user.username) + "?q=view-all") else: # Grab all of the games for autocompleting params['categories'] = self.get_categories() params['user'] = user self.render("submit.html", **params)
def get_response( self, body ): body_type = body.get( 'type' ) # Currently 5 types: verifylogin, gamelist, categories, gamecategories # and submitrun if body_type is None: return self.get_fail_response( "No type given." ) elif body_type == 'verifylogin': ( valid, response ) = self.verify_login( body ) return response elif body_type == 'gamelist': return self.get_fail_response( "Type [" + body_type + "] currently" + " not supported dynamically, but " + "you can find a static JSON " + "response at " + "https://www.dropbox.com/s/" + "xnvsmx3mt0i4nbv/gamelist.json?dl" + "=0" ) elif body_type == 'categories': return self.get_fail_response( "Type [" + body_type + "] currently" + " not supported dynamically, but " + "you can find a static JSON " + "response at " + "https://www.dropbox.com/s/" + "irdj4xakh72g541/categories.json?" + "dl=0" ) elif body_type == 'modgamelist': # First, verify login credentials and that we are a mod ( valid, response ) = self.verify_mod_login( body ) if not valid: return response # Note that this is a different type of gamelist than the one # generated in games.py categories = self.get_categories( ) if categories == self.OVER_QUOTA_ERROR: return self.get_fail_response( "PB Tracker is currently " + "experiencing an over " + "quota downtime period." ) d = dict( ) for game in categories.keys( ): d[ util.get_code( game ) ] = game return self.get_success_response( data=d ) elif body_type == 'modcategories': # First, verify login credentials and that we are a mod ( valid, response ) = self.verify_mod_login( body ) if not valid: return response categories = self.get_categories( ) if categories == self.OVER_QUOTA_ERROR: return self.get_fail_response( "PB Tracker is currently " + "experiencing an over " + "quota downtime period." ) d = dict( ) for game, categorylist in categories.iteritems( ): game_code = util.get_code( game ) for category in categorylist: category_code = util.get_code( category ) d[ game_code + ':' + category_code ] = ( game + ' - ' + category ) return self.get_success_response( data=d ) elif body_type == 'gamecategories': return self.get_fail_response( "Type [" + body_type + "] currently" + " not supported, sorry." ) # game_code = body.get( 'game' ) # game_model = self.get_game_model( game_code ) # if game_code is None: # return self.get_fail_response( 'No game specified' ) # elif game_model is None: # return self.get_fail_response( 'Unknown game [' # + game_code + '].' ) # TODO: elif game_model == self.OVER_QUOTA_ERROR: # else: # d = dict( ) # gameinfolist = json.loads( game_model.info ) # for gameinfo in gameinfolist: # category = gameinfo['category'] # d[ util.get_code( category ) ] = category # return self.get_success_response( data=d ) elif body_type == 'submitrun': # First, verify login credentials ( valid, response ) = self.verify_login( body ) if not valid: return response # Grab the params from the body username = body.get( 'username' ) game_code = body.get( 'game' ) category_code = body.get( 'category' ) version = body.get( 'version' ) time = body.get( 'runtime' ) video = body.get( 'video' ) notes = body.get( 'comment' ) splits = body.get( 'splits' ) # Make sure the game and category exist (can't handle new games # and categories just yet) if game_code is None: return self.get_fail_response( 'No game given' ) game_model = self.get_game_model( game_code ) if game_model is None: return self.get_fail_response( 'Unknown game [' + game_code + '].' ) if game_model == self.OVER_QUOTA_ERROR: return self.get_fail_response( 'PB Tracker is currently over ' + "quota. Please try again " + "later." ) if category_code is None: return self.get_fail_response( 'No category specified' ) gameinfolist = json.loads( game_model.info ) category = None for gameinfo in gameinfolist: if category_code == util.get_code( gameinfo['category'] ): category = gameinfo['category'] break if category is None: return self.get_fail_response( 'Unknown category [' + category_code + '] for game [' + game_code + '].' ) # Parse the time into seconds and ensure it is valid if time is None: return self.get_fail_response( 'No runtime given' ) ( seconds, time_error ) = util.timestr_to_seconds( time ) if seconds is None: return self.get_fail_response( 'Bad runtime [' + time + '] given: ' + time_error ) # Ensure standard format time = util.seconds_to_timestr( seconds ) # Make sure that the notes are not too long if notes is not None and len( notes ) > 140: return self.get_fail_response( 'Comment is too long; must be ' + 'at most 140 characters.' ) # Figure out current date in user's local timezone user = self.get_runner( util.get_code( username ) ) if user == self.OVER_QUOTA_ERROR: return False, self.get_fail_response( "PB Tracker is currently " + "experiencing an over " + "quota limit down time " + "period." ) if user.timezone: tz = pytz.timezone( user.timezone ) local_today = datetime.now( pytz.utc ).astimezone( tz ) else: # UTC by default local_today = datetime.now( pytz.utc ) date = local_today.date( ) # Load up the needed parameters and put a new run params = dict( user=user, game=game_model.game, game_code=game_code, game_model=game_model, category=category, category_found=True, seconds=seconds, time=time, video=video, version=version, notes=notes, valid=True, date=date, datestr=date.strftime( "%m/%d/%Y" ), is_bkt=False ) if self.put_new_run( params ): return self.get_success_response( ) else: if params.get( 'video_error' ): return self.get_fail_response( 'Bad video link [' + video + ']: ' + params[ 'video_error' ] ) else: return self.get_fail_response( 'Sorry, an unknown error ' + 'occurred.' ) return self.get_fail_response( "Unknown type [" + body_type + "]." )
def get_gamepage( self, game, category, page_num ): if category is None: return dict( has_next=False, page_num=0, d=None ) category_code = util.get_code( category ) key = self.get_gamepage_memkey( game, category_code ) cached_gamepages = memcache.get( key ) if cached_gamepages is None: cached_gamepages = dict( ) gamepage = cached_gamepages.get( page_num ) if gamepage is None: # Not in memcache, so construct the gamepage and store it in # memcache. # gamepage['d'] has up to 7 keys: # 'category', 'category_code', 'bk_runner', 'bk_time', # 'bk_date', 'bk_video' and 'infolist'. gamepage = dict( page_num=page_num, has_next=True ) d = dict( category=category, category_code=category_code, infolist=[ ] ) # Grab the game model game_model = self.get_game_model( util.get_code( game ) ) if game_model is None: logging.error( "Could not create " + key + " due to no " + "game model" ) return dict( has_next=False, page_num=0, d=None ) if game_model == self.OVER_QUOTA_ERROR: return self.OVER_QUOTA_ERROR gameinfolist = json.loads( game_model.info ) # Check for a best known time for this category for gameinfo in gameinfolist: if gameinfo['category'] == category: d['bk_runner'] = gameinfo.get( 'bk_runner' ) d['bk_time'] = util.seconds_to_timestr( gameinfo.get( 'bk_seconds' ) ) d['bk_date'] = util.datestr_to_date( gameinfo.get( 'bk_datestr' ) )[ 0 ] d['bk_video'] = gameinfo.get( 'bk_video' ) break try: # Get 1 run per username q = db.Query( runs.Runs, projection=['username', 'seconds', 'date', 'video', 'version'] ) q.ancestor( runs.key() ) q.filter( 'game =', game ) q.filter( 'category =', category ) q.order( 'seconds' ) q.order( 'date' ) usernames_seen = set( ) cached_cursor = memcache.get( self.get_gamepage_cursor_memkey( game, category_code, page_num ) ) if cached_cursor: try: q.with_cursor( start_cursor=cached_cursor['c'] ) usernames_seen = cached_cursor['usernames_seen'] except BadRequestError: gamepage['page_num'] = page_num else: gamepage['page_num'] = 1 num_runs = 0 for run in q.run( limit = self.GAMEPAGE_PAGE_LIMIT ): num_runs += 1 if run.username in usernames_seen: continue # Add the info to the gamepage info = dict( username = run.username, username_code = util.get_code( run.username ), category = category, category_code = category_code, pb_seconds = run.seconds, pb_time = util.seconds_to_timestr( run.seconds ), pb_date = run.date, video = run.video, version = run.version ) d['infolist'].append( info ) usernames_seen.add( run.username ) if num_runs < self.GAMEPAGE_PAGE_LIMIT: gamepage['has_next'] = False else: c = q.cursor( ) cached_cursor = dict( c=c, usernames_seen=usernames_seen ) cursor_key = self.get_gamepage_cursor_memkey( game, category_code, gamepage['page_num'] + 1 ) if memcache.set( cursor_key, cached_cursor ): logging.debug( "Set " + cursor_key + " in memcache" ) else: logging.warning( "Failed to set new " + cursor_key + " in memcache" ) except apiproxy_errors.OverQuotaError, msg: logging.error( msg ) return self.OVER_QUOTA_ERROR gamepage['d'] = d cached_gamepages[ gamepage['page_num'] ] = gamepage if memcache.set( key, cached_gamepages ): logging.debug( "Set " + key + " in memcache" ) else: logging.warning( "Failed to set " + key + " in memcache" )
def get_pblist( self, username, page_num, show_all ): key = self.get_pblist_memkey( username ) cached_pblists = memcache.get( key ) if cached_pblists is None: cached_pblists = dict( ) res = cached_pblists.get( page_num ) if res is None or ( show_all and not res['show_all'] ): pblist = [ ] c = None if res is None: res = dict( page_num=page_num, has_next=False, show_all=show_all ) # Not in memcache, so construct the pblist and store in # memcache. # pblist is a list of dictionaries with 3 indices, 'game', # 'game_code' and 'infolist'. The infolist is another list of # dictionaries containing all the info for each pb of the game. c = memcache.get( self.get_pblist_cursor_memkey( username, page_num ) ) else: # Need to update this page to a show_all res['show_all'] = show_all res['has_next'] = False res['page_num'] = page_num if page_num == 1: # Can try to start from normal page 2 pblist = res['pblist'] c = memcache.get( self.get_pblist_cursor_memkey( username, page_num + 1 ) ) try: q = db.Query( runs.Runs, projection=['game', 'category', 'seconds', 'date', 'video', 'version'] ) q.ancestor( runs.key() ) q.filter( 'username ='******'game' ) q.order( 'category' ) q.order( 'seconds' ) if c: try: q.with_cursor( start_cursor=c ) except BadRequestError: res['page_num'] = 1 pblist = [ ] else: res['page_num'] = 1 pblist = [ ] cur_game = None pb = None if len( pblist ) > 0: cur_game = pblist[ -1 ]['game'] pb = pblist[ -1 ] cur_category = None info = None cursor_to_save = c last_cursor = None runs_queried = 0 limit = self.PB_PAGE_LIMIT if show_all: limit = self.PB_PAGE_SHOW_ALL_LIMIT for run in q.run( limit=limit ): if run.game != cur_game: # New game pb = dict( game = run.game, game_code = util.get_code( run.game ), num_runs = 0, infolist = [ ] ) pblist.append( pb ) cur_game = run.game cur_category = None if run.category != cur_category: # New category info = dict( username = username, username_code = util.get_code( username ), category = run.category, category_code = util.get_code( run.category ), pb_seconds = run.seconds, pb_time = util.seconds_to_timestr( run.seconds ), pb_date = run.date, num_runs = 1, avg_seconds = run.seconds, avg_time = util.seconds_to_timestr( run.seconds, dec_places=0 ), video = run.video, version = run.version ) pb['infolist'].append( info ) cur_category = run.category if last_cursor is not None: cursor_to_save = last_cursor else: # Repeat game, category info['num_runs'] += 1 info['avg_seconds'] += ( 1.0 / info['num_runs'] ) * ( run.seconds - info['avg_seconds'] ) info['avg_time'] = util.seconds_to_timestr( info['avg_seconds'], dec_places=0 ) pb['num_runs'] += 1 runs_queried += 1 last_cursor = q.cursor( ) if runs_queried < limit: # Made it to the end cursor_to_save = q.cursor( ) if res['page_num'] == 1: res['show_all'] = True else: res['has_next'] = True # Last category found is possibly incomplete, so remove del pblist[ -1 ]['infolist'][ -1 ] if len( pblist[ -1 ]['infolist'] ) <= 0: del pblist[ -1 ] if len( pblist ) <= 0: # Too many runs for this game, category pb = dict( game = cur_game, game_code = util.get_code( cur_game ), num_runs = 0, infolist = [ dict( username=username, username_code = util.get_code( username ), category=( 'TOO MANY RUNS FOR ' + 'CATEGORY: ' + cur_category + ' (max is ' + str( self.PB_PAGE_LIMIT - 1 ) + ', please delete some runs)' ), category_code=util.get_code( cur_category ), pb_seconds=0, pb_time=util.seconds_to_timestr( 0 ), pb_date=None, num_runs=0, avg_seconds=0, avg_time=util.seconds_to_timestr( 0 ), video=None ) ] ) pblist.append( pb ) if res['show_all']: # Over limit even with show all res['has_next'] = False pb = dict( game='TOO MANY RUNS TO SHOW ALL', game_code='???', num_runs=0, infolist=[ dict( username=username, username_code = util.get_code( username ), category=( 'MAX IS ' + str( self.PB_PAGE_SHOW_ALL_LIMIT ) + ', PLEASE DELETE ' + 'SOME RUNS' ), category_code='???', pb_seconds=0, pb_time=util.seconds_to_timestr( 0 ), pb_date=None, num_runs=0, avg_seconds=0, avg_time=util.seconds_to_timestr( 0 ), video=None ) ] ) res['pblist'] = [ pb ] return res cursor_key = self.get_pblist_cursor_memkey( username, res['page_num'] + 1 ) if memcache.set( cursor_key, cursor_to_save ): logging.debug( 'Set ' + cursor_key + " in memcache" ) else: logging.warning( 'Failed to set ' + cursor_key + ' in memcache' ) except apiproxy_errors.OverQuotaError, msg: logging.error( msg ) return self.OVER_QUOTA_ERROR # Sort the categories for a game by num_runs for pb in pblist: pb['infolist'].sort( key=itemgetter('num_runs'), reverse=True ) res['pblist'] = pblist cached_pblists[ res['page_num'] ] = res if memcache.set( key, cached_pblists ): logging.debug( "Set " + key + " in memcache" ) else: logging.warning( "Failed to set " + key + " in memcache" )
def post( self ): user = self.get_user( ) if not user: self.redirect( "/" ) return game = self.request.get( 'game' ) category = self.request.get( 'category' ) time = self.request.get( 'time' ) datestr = self.request.get( 'date' ) video = self.request.get( 'video' ) version = self.request.get( 'version' ) notes = self.request.get( 'notes' ) is_bkt = self.request.get( 'bkt', default_value="no" ) if is_bkt == "yes": is_bkt = True else: is_bkt = False run_id = self.request.get( 'edit' ) params = dict( user = user, game = game, category = category, time = time, datestr = datestr, video = video, version = version, notes = notes, run_id = run_id, is_bkt = is_bkt ) valid = True # Make sure the game doesn't already exist under a similar name game_code = util.get_code( game ) game_model = self.get_game_model( game_code ) if not game_code: params['game_error'] = "Game cannot be blank" valid = False elif game_model is not None and game != game_model.game: params['game_error'] = ( "Game already exists under [" + game_model.game + "] (case sensitive)." + " Hit submit again to confirm." ) params['game'] = game_model.game valid = False elif not valid_game_or_category( game ): params['game_error'] = ( "Game name must not use any 'funny'" + " characters and can be up to 100 " + "characters long" ) valid = False params[ 'game_code' ] = game_code params[ 'game_model' ] = game_model # Make sure the category doesn't already exist under a similar name category_code = util.get_code( category ) category_found = False if not category_code: params['category_error'] = "Category cannot be blank" valid = False elif game_model is not None: infolist = json.loads( game_model.info ) for info in infolist: if category_code == util.get_code( info['category'] ): category_found = True if category != info['category']: params['category_error'] = ( "Category already exists " + "under [" + info['category'] + "] " + "(case sensitive). " + "Hit submit again to " + "confirm." ) params['category'] = info['category'] valid = False break if not category_found and not valid_game_or_category( category ): params['category_error'] = ( "Category must not use any 'funny'" + " characters and can be up to 100 " + "characters long" ) valid = False params[ 'category_found' ] = category_found # Parse the time into seconds, ensure it is valid ( seconds, time_error ) = util.timestr_to_seconds( time ) if seconds is None: params['time_error'] = "Invalid time: " + time_error params['seconds'] = -1 valid = False else: time = util.seconds_to_timestr( seconds ) # Enforce standard form params[ 'time' ] = time params[ 'seconds' ] = seconds # Parse the date, ensure it is valid ( params['date'], params['date_error'] ) = util.datestr_to_date( datestr ) if params['date_error']: params['date_error'] = "Invalid date: " + params['date_error'] valid = False # Check that if this is a best known time, that it beats the old # best known time if is_bkt and game_model is not None: gameinfolist = json.loads( game_model.info ) for gameinfo in gameinfolist: if gameinfo['category'] == params['category']: if( gameinfo.get( 'bk_seconds' ) is not None and gameinfo['bk_seconds'] <= seconds ): s = ( "This time does not beat current best known " + "time of " + util.seconds_to_timestr( gameinfo.get( 'bk_seconds' ) ) + " by " + gameinfo['bk_runner'] + " (if best known time is incorrect, you can " + "update best known time after submission)" ) params['bkt_error'] = s params['is_bkt'] = False valid = False break # Make sure that the notes are not too long if len( notes ) > 140: params['notes_error'] = "Notes must be at most 140 characters" valid = False params['valid'] = valid if run_id: success = self.put_existing_run( params ) else: success = self.put_new_run( params ) if success: self.redirect( "/runner/" + util.get_code( user.username ) + "?q=view-all" ) else: # Grab all of the games for autocompleting params['categories'] = self.get_categories( ) params['user'] = user self.render( "submit.html", **params )
def update_pblist_put(self, params): user = params["user"] game = params["game"] category = params["category"] seconds = params["seconds"] time = params["time"] video = params["video"] game_code = params["game_code"] date = params["date"] version = params["version"] # Update pblist in memcache cached_pblists = self.get_cached_pblists(user.username) if cached_pblists is None: return for page_num, res in cached_pblists.iteritems(): pblist = res["pblist"] for pb in pblist: if pb["game"] == game: pb["num_runs"] += 1 for i, info in enumerate(pb["infolist"]): if info["category"] == category: info["num_runs"] += 1 info["avg_seconds"] += (1.0 / info["num_runs"]) * (seconds - info["avg_seconds"]) info["avg_time"] = util.seconds_to_timestr(info["avg_seconds"], dec_places=0) if info["pb_seconds"] is None or info["pb_seconds"] > seconds: # Update pb info["pb_seconds"] = seconds info["pb_time"] = time info["pb_date"] = date info["video"] = video info["version"] = version pb["infolist"].sort(key=itemgetter("category")) pb["infolist"].sort(key=itemgetter("num_runs"), reverse=True) self.update_cache_pblist(user.username, cached_pblists) return if res["show_all"]: # Found the game, but not the category and we are # showing all runs. Add the run. info = dict( username=user.username, username_code=util.get_code(user.username), category=category, category_code=util.get_code(category), pb_seconds=seconds, pb_time=time, pb_date=date, num_runs=1, avg_seconds=seconds, avg_time=time, video=video, version=version, ) pb["infolist"].append(info) pb["infolist"].sort(key=itemgetter("category")) pb["infolist"].sort(key=itemgetter("num_runs"), reverse=True) self.update_cache_pblist(user.username, cached_pblists) return if res["show_all"]: # Could not find the game and we are showing all runs. Add # the game/run. info = dict( username=user.username, username_code=util.get_code(user.username), category=category, category_code=util.get_code(category), pb_seconds=seconds, pb_time=time, pb_date=date, num_runs=1, avg_seconds=seconds, avg_time=time, video=video, version=version, ) pb = dict(game=game, game_code=util.get_code(game), num_runs=1, infolist=[info]) pblist.append(pb) self.update_cache_pblist(user.username, cached_pblists) return # Couldn't find this game, category combination, so we must nullify # memcache. We can't just add the run since we may not have all of # the pblist pages in memcache, so we don't know if it is the only # run for this game, category or not. self.update_cache_pblist(user.username, None)
def update_gamepage_put(self, params): user = params['user'] game = params['game'] category = params['category'] seconds = params['seconds'] time = params['time'] date = params['date'] video = params['video'] is_bkt = params['is_bkt'] # Update gamepage in memcache gamepage = self.get_gamepage(game, no_refresh=True) if gamepage is None: return if gamepage == self.OVER_QUOTA_ERROR: self.update_cache_gamepage(game, None) return for d in gamepage: if d['category'] == category: if is_bkt: # Update best known time for this category d['bk_runner'] = user.username d['bk_time'] = util.seconds_to_timestr(seconds) d['bk_date'] = date d['bk_video'] = video for i, runinfo in enumerate(d['infolist']): if runinfo['username'] == user.username: # User has run this category before d['infolist'][i] = self.get_runinfo( user.username, game, category) if d['infolist'][i] == self.OVER_QUOTA_ERROR: gamepage = None else: d['infolist'].sort(key=lambda x: util. get_valid_date(x['pb_date'])) d['infolist'].sort(key=itemgetter('pb_seconds')) self.update_cache_gamepage(game, gamepage) return # Category found, but user has not prev. run this category runinfo = self.get_runinfo(user.username, game, category) if runinfo == self.OVER_QUOTA_ERROR: gamepage = None else: d['infolist'].append(runinfo) d['infolist'].sort( key=lambda x: util.get_valid_date(x['pb_date'])) d['infolist'].sort(key=itemgetter('pb_seconds')) gamepage.sort(key=lambda x: len(x['infolist']), reverse=True) self.update_cache_gamepage(game, gamepage) return # This is a new category for this game runinfo = self.get_runinfo(user.username, game, category) if runinfo == self.OVER_QUOTA_ERROR: self.update_cache_gamepage(game, gamepage) return d = dict(category=category, category_code=util.get_code(category), infolist=[runinfo]) # Check for best known time. Since we update games.Games before # updating gamepage, this will catch the case for when is_bkt is true. game_model = self.get_game_model(util.get_code(game)) if game_model is None: logging.error("Failed to update gamepage for " + game) self.update_cache_gamepage(game, None) return if game_model == self.OVER_QUOTA_ERROR: self.update_cache_gamepage(game, None) return gameinfolist = json.loads(game_model.info) for gameinfo in gameinfolist: if gameinfo['category'] == category: d['bk_runner'] = gameinfo.get('bk_runner') d['bk_time'] = util.seconds_to_timestr( gameinfo.get('bk_seconds')) d['bk_date'] = util.datestr_to_date( gameinfo.get('bk_datestr'))[0] d['bk_video'] = gameinfo.get('bk_video') break gamepage.append(d) self.update_cache_gamepage(game, gamepage)
def update_runinfo_delete(self, user, old_run): # Update avg, num runs runinfo = self.get_runinfo(user.username, old_run['game'], old_run['category'], no_refresh=True) if runinfo is None: return if runinfo == self.OVER_QUOTA_ERROR: self.update_cache_runinfo(user.username, game, category, None) return if runinfo['num_runs'] <= 0: logging.error("Failed to update runinfo due to nonpositive " + "num_runs " + str(runinfo['num_runs'])) self.update_cache_runinfo(user.username, old_run['game'], old_run['category'], None) return if (runinfo['num_runs'] > 1): runinfo['avg_seconds'] -= (1.0 * old_run['seconds'] / runinfo['num_runs']) runinfo['num_runs'] -= 1 runinfo['avg_seconds'] *= (1.0 * (runinfo['num_runs'] + 1) / runinfo['num_runs']) runinfo['avg_time'] = util.seconds_to_timestr( runinfo['avg_seconds']) if (runinfo['pb_seconds'] == old_run['seconds']): # We need to replace the pb too try: q = db.Query(runs.Runs, projection=('seconds', 'date', 'video', 'version')) q.ancestor(runs.key()) q.filter('username ='******'game =', old_run['game']) q.filter('category =', old_run['category']) q.order('seconds') q.order('date') pb_run = q.get() if pb_run: runinfo['pb_seconds'] = pb_run.seconds runinfo['pb_time'] = util.seconds_to_timestr( pb_run.seconds) runinfo['pb_date'] = pb_run.date runinfo['video'] = pb_run.video runinfo['version'] = pb_run.version else: logging.error("Unable to update runinfo due to no " + "new pb found") self.update_cache_runinfo(user.username, old_run['game'], old_run['category'], None) return except apiproxy_errors.OverQuotaError, msg: logging.error(msg) self.update_cache_runinfo(user.username, old_run['game'], old_run['category'], None) return self.update_cache_runinfo(user.username, old_run['game'], old_run['category'], runinfo)
class ChangeCategories(runhandler.RunHandler): def get(self): # Get the user user = self.get_user() if not user: self.redirect("/") return elif user == self.OVER_QUOTA_ERROR: self.error(403) self.render("403.html") return # Make sure user is a mod if not user.is_mod: self.error(404) self.render("404.html", user=user) return params = dict(user=user, categories=self.get_categories()) if params['categories'] == self.OVER_QUOTA_ERROR: self.error(403) self.render("403.html", user=user) return self.render("change_categories.html", **params) def post(self): # Get the user user = self.get_user() if not user: self.redirect("/") return elif user == self.OVER_QUOTA_ERROR: self.error(403) self.render("403.html") return # Make sure user is a mod if not user.is_mod: self.error(404) self.render("404.html", user=user) return old_game = self.request.get('old-game') old_category = self.request.get('old-category') new_game = self.request.get('new-game') new_category = self.request.get('new-category') params = dict(user=user, old_game=old_game, old_category=old_category, new_game=new_game, new_category=new_category) valid = True # Make sure the new game doesn't already exist under a similar name new_game_code = util.get_code(new_game) new_game_model = self.get_game_model(new_game_code) if not new_game_code: params['new_game_error'] = "New game cannot be blank" valid = False if new_game_model == self.OVER_QUOTA_ERROR: self.error(403) self.render("403.html", user=user) return elif new_game_model is not None and new_game != new_game_model.game: params['new_game_error'] = ("New game already exists under [" + new_game_model.game + "] (case sensitive)." + " Hit submit again to confirm.") params['new_game'] = new_game_model.game valid = False elif not games.valid_game_or_category(new_game): params['new_game_error'] = ("Game name must not use any 'funny'" + " characters and can be up to 100 " + "characters long") valid = False params['new_game_code'] = new_game_code params['new_game_model'] = new_game_model # Make sure the category doesn't already exist under a similar name new_category_code = util.get_code(new_category) new_category_found = False if not new_category_code: params['new_category_error'] = "Category cannot be blank" valid = False elif new_game_model is not None: infolist = json.loads(new_game_model.info) for info in infolist: if new_category_code == util.get_code(info['category']): new_category_found = True if new_category != info['category']: params['new_category_error'] = ( "Category already exists " + "under [" + info['category'] + "] " + "(case sensitive). " + "Hit submit again to " + "confirm.") params['new_category'] = info['category'] valid = False break if (not new_category_found and not games.valid_game_or_category(new_category)): params['new_category_error'] = ( "Category must not use any 'funny'" + " characters and can be up to 100 " + "characters long") valid = False params['new_category_found'] = new_category_found if not valid: self.render("change_categories.html", **params) return changes = self.change_categories(params) logging.info(changes) # Render changes self.write(changes) @db.transactional(xg=True) def change_categories(self, params): res = '' # Grab the old game model old_game_code = util.get_code(params['old_game']) if old_game_code == params['new_game_code']: old_game_model = params['new_game_model'] else: old_game_model = self.get_game_model(old_game_code) if old_game_model == self.OVER_QUOTA_ERROR: self.error(403) self.render("403.html") return if old_game_model is None: return "Did not find game [" + params['old_game'] + "]" if params['new_game_model'] is None: # New game does not exist, so create it params['new_game_model'] = games.Games( game=params['new_game'], info=json.dumps([]), num_pbs=0, parent=games.key(), key_name=params['new_game_code']) res += ('Created new game model for game [' + params['new_game'] + ']<br>') if not params['new_category_found']: # Add the new category to the new game model gameinfolist = json.loads(params['new_game_model'].info) d = dict(category=params['new_category'], bk_runner=None, bk_seconds=None, bk_video=None, bk_datestr=None, bk_updater=None) gameinfolist.append(d) params['new_game_model'].info = json.dumps(gameinfolist) res += 'Added new category [' + params['new_category'] + ']<br>' # Grab the gameinfo for the old game oldgameinfolist = json.loads(old_game_model.info) oldgameinfo = None for g in oldgameinfolist: if (util.get_code(params['old_category']) == util.get_code( g['category'])): oldgameinfo = g break if oldgameinfo is None: return "Did not find old category [" + params['old_category'] + ']' # Grab the gameinfo for the new game newgameinfolist = json.loads(params['new_game_model'].info) newgameinfo = None for g in newgameinfolist: if (util.get_code(params['new_category']) == util.get_code( g['category'])): newgameinfo = g break if newgameinfo is None: return "Did not find new category [" + params['new_category'] + ']' # Update best known time if necessary if (oldgameinfo.get('bk_seconds') is not None and (newgameinfo.get('bk_seconds') is None or oldgameinfo.get('bk_seconds') < newgameinfo.get('bk_seconds'))): newgameinfo['bk_seconds'] = oldgameinfo.get('bk_seconds') newgameinfo['bk_runner'] = oldgameinfo.get('bk_runner') newgameinfo['bk_datestr'] = oldgameinfo.get('bk_datestr') newgameinfo['bk_video'] = oldgameinfo.get('bk_video') newgameinfo['bk_updater'] = oldgameinfo.get('bk_updater') params['new_game_model'].info = json.dumps(newgameinfolist) res += 'Updated bkt<br>' if not memcache.flush_all(): res += "Failed to flush memcache<br>" # Update num_pbs for old game, new game res += ('Previous num_pbs for old game, category = ' + str(old_game_model.num_pbs) + '<br>') res += ('Previous num_pbs for new game, category = ' + str(params['new_game_model'].num_pbs) + '<br>') try: q = db.Query(runs.Runs, projection=['username'], distinct=True) q.ancestor(runs.key()) q.filter('game =', params['old_game']) q.filter('category =', params['old_category']) for run in q.run(limit=1000): old_game_model.num_pbs -= 1 q2 = db.Query(runs.Runs) q2.ancestor(runs.key()) q2.filter('game =', params['new_game']) q2.filter('category =', params['new_category']) q2.filter('username ='******'new_game_model'].num_pbs += 1 else: # Need to decrement runner's num_pbs runner = self.get_runner(util.get_code(run.username)) if runner == self.OVER_QUOTA_ERROR: return 'Over quota error' runner.num_pbs -= 1 runner.put() res += ("Updated " + run.username + " num_pbs from " + str(runner.num_pbs + 1) + " to " + str(runner.num_pbs) + "<br>") except apiproxy_errors.OverQuotaError, msg: logging.error(msg) return "Ran out of quota" res += ('Updated num_pbs for old game, category = ' + str(old_game_model.num_pbs) + '<br>') res += ('Updated num_pbs for new game, category = ' + str(params['new_game_model'].num_pbs) + '<br>') # Update old, new game models in database old_game_model.put() if old_game_code != params['new_game_code']: params['new_game_model'].put() # Change the runs res += "<br>Changed runs:<br>" try: q = db.Query(runs.Runs) q.ancestor(runs.key()) q.filter('game =', params['old_game']) q.filter('category =', params['old_category']) for run in q.run(limit=10000): # Update the run run.game = params['new_game'] run.category = params['new_category'] run.put() res += ('Runner=' + run.username + ' time=' + util.seconds_to_timestr(run.seconds) + '<br>') except apiproxy_errors.OverQuotaError, msg: logging.error(msg) return "Ran out of quota when changing runs"
def update_pblist_delete(self, user, old_run): # Update pblist with the removal of the old run cached_pblists = self.get_cached_pblists(user.username) if cached_pblists is None: return for page_num, res in cached_pblists.iteritems(): pblist = res["pblist"] for i, pb in enumerate(pblist): if pb["game"] == old_run["game"]: pb["num_runs"] -= 1 for j, info in enumerate(pb["infolist"]): if info["category"] == old_run["category"]: if info["num_runs"] <= 1: # No other runs for game, category combo del pb["infolist"][j] if len(pb["infolist"]) <= 0: del cached_pblists[page_num]["pblist"][i] self.update_cache_pblist(user.username, cached_pblists) return else: new_avg = (info["avg_seconds"] * info["num_runs"]) - old_run["seconds"] info["num_runs"] -= 1 info["avg_seconds"] = 1.0 * new_avg / info["num_runs"] info["avg_time"] = util.seconds_to_timestr(info["avg_seconds"], dec_places=0) if info["pb_seconds"] >= old_run["seconds"]: # Update our PB for this game, category q = db.Query(runs.Runs, projection=["seconds", "date", "video", "version"]) q.ancestor(runs.key()) q.filter("username ="******"game =", old_run["game"]) q.filter("category =", old_run["category"]) q.order("seconds") for run in q.run(limit=1): info["pb_seconds"] = run.seconds info["pb_time"] = util.seconds_to_timestr(run.seconds) info["pb_date"] = run.date info["video"] = run.video info["version"] = run.version break else: logging.error( "Failed to update PB for " + user.username + ", " + old_run["game"] + ", " + old_run["category"] + " on pblist_delete" ) self.update_cache_pblist(user.username, None) return pb["infolist"][j] = info pb["infolist"].sort(key=itemgetter("category")) pb["infolist"].sort(key=itemgetter("num_runs"), reverse=True) self.update_cache_pblist(user.username, cached_pblists) return # Couldn't find this game, category in memcache, so nothing # to update return
def get_gamepage(self, game, no_refresh=False): key = self.get_gamepage_memkey(game) gamepage = memcache.get(key) if gamepage is None and not no_refresh: # Not in memcache, so construct the gamepage and store it in # memcache. # Gamepage is a list of dictionaries. These dictionaries have up # to 5 keys, 'category', 'bk_runner', 'bk_time', 'bk_video' and # 'infolist'. gamepage = [] # Grab the game model game_model = self.get_game_model(util.get_code(game)) if game_model is None: logging.error("Could not create " + key + " due to no " + "game model") return None if game_model == self.OVER_QUOTA_ERROR: return self.OVER_QUOTA_ERROR gameinfolist = json.loads(game_model.info) try: # Use a projection query to get all of the unique # username, category pairs q = db.Query(runs.Runs, projection=('username', 'category'), distinct=True) q.ancestor(runs.key()) q.filter('game =', game) q.order('category') cur_category = None for run in q.run(limit=1000): if run.category != cur_category: # New category d = dict(category=run.category, category_code=util.get_code(run.category), infolist=[]) gamepage.append(d) cur_category = run.category # Check for a best known time for this category for gameinfo in gameinfolist: if gameinfo['category'] == run.category: d['bk_runner'] = gameinfo.get('bk_runner') d['bk_time'] = util.seconds_to_timestr( gameinfo.get('bk_seconds')) d['bk_date'] = util.datestr_to_date( gameinfo.get('bk_datestr'))[0] d['bk_video'] = gameinfo.get('bk_video') break # Add the info to the gamepage info = self.get_runinfo(run.username, game, run.category) if info == self.OVER_QUOTA_ERROR: return self.OVER_QUOTA_ERROR d['infolist'].append(info) except apiproxy_errors.OverQuotaError, msg: logging.error(msg) return self.OVER_QUOTA_ERROR # For each category, sort the runlist by seconds, breaking ties # by date for runlist in gamepage: runlist['infolist'].sort( key=lambda x: util.get_valid_date(x['pb_date'])) runlist['infolist'].sort(key=itemgetter('pb_seconds')) # Sort the categories by number of runners gamepage.sort(key=lambda x: len(x['infolist']), reverse=True) if memcache.set(key, gamepage): logging.debug("Set " + key + " in memcache") else: logging.warning("Failed to set " + key + " in memcache")
def post( self, game_code ): user = self.get_user( ) if user == self.OVER_QUOTA_ERROR: self.error( 403 ) self.render( "403.html" ) return return_url = self.request.get( 'from' ) if not return_url: return_url = "/" # Get the category category_code = self.request.get( 'c' ) if user is None or category_code is None: self.error( 404 ) self.render( "404.html", user=user ) return # Have to take the code of the category code because of percent # encoded plusses category_code = util.get_code( category_code ) # Check to make sure that the user has run this game game_model = self.get_game_model( game_code ) if game_model == self.OVER_QUOTA_ERROR: self.error( 403 ) self.render( "403.html", user=user ) return user_has_run = self.get_user_has_run( user.username, game_model.game ) if user_has_run == self.OVER_QUOTA_ERROR: self.error( 403 ) self.render( "403.html", user=user ) return if not user_has_run and not user.is_mod: self.error( 404 ) self.render( "404.html", user=user ) return # Find the corresponding gameinfo for this category gameinfolist = json.loads( game_model.info ) gameinfo = None for g in gameinfolist: if util.get_code( g['category'] ) == category_code: gameinfo = g break if gameinfo is None: self.error( 404 ) self.render( "404.html", user=user ) return # Get the inputs username = self.request.get( 'username' ) time = self.request.get( 'time' ) datestr = self.request.get( 'date' ) video = self.request.get( 'video' ) params = dict( user=user, game=game_model.game, game_code=game_code, category=gameinfo['category'], username=username, time=time, datestr=datestr, video=video, return_url=return_url ) # Are we updating? if gameinfo.get( 'bk_runner' ) is None: params['updating'] = False else: params['updating'] = True valid = True # Check for where we came from if return_url[ 0 : len( '/runner/' ) ] == '/runner/': params['from_runnerpage'] = True else: params['from_runnerpage'] = False if not username and not time and not datestr and not video: gameinfo['bk_runner'] = None gameinfo['bk_seconds'] = None gameinfo['bk_datestr'] = None gameinfo['bk_video'] = None date = None else: # Make sure we got a username if not username: params['username_error'] = "You must enter a runner" valid = False # Parse the time into seconds, ensure it is valid ( seconds, time_error ) = util.timestr_to_seconds( time ) if not seconds: params['time_error'] = "Invalid time: " + time_error valid = False # Parse the date, ensure it is valid ( date, date_error ) = util.datestr_to_date( datestr ) if date_error: params['date_error'] = "Invalid date: " + date_error valid = False if not valid: self.render( "updatebkt.html", **params ) return time = util.seconds_to_timestr( seconds ) # Standard format params['time'] = time # Store the best known time gameinfo['bk_runner'] = username gameinfo['bk_seconds'] = seconds gameinfo['bk_datestr'] = datestr gameinfo['bk_video'] = video gameinfo['bk_updater'] = user.username game_model.info = json.dumps( gameinfolist ) game_model.put( ) # Update game_model in memcache self.update_cache_game_model( game_code, game_model ) # Update gamepage in memcache cached_gamepages = self.get_cached_gamepages( game_model.game, category_code ) if cached_gamepages is not None: for page_num, gamepage in cached_gamepages.iteritems( ): d = gamepage['d'] d['bk_runner'] = gameinfo['bk_runner'] d['bk_time'] = util.seconds_to_timestr( gameinfo['bk_seconds'] ) d['bk_date'] = date d['bk_video'] = gameinfo['bk_video'] self.update_cache_gamepage( game_model.game, category_code, cached_gamepages ) # All dun self.redirect( return_url )
def update_gamepage_put( self, params ): user = params[ 'user' ] game = params[ 'game' ] category = params[ 'category' ] seconds = params[ 'seconds' ] time = params[ 'time' ] date = params[ 'date' ] video = params[ 'video' ] is_bkt = params[ 'is_bkt' ] # Update gamepage in memcache gamepage = self.get_gamepage( game, no_refresh=True ) if gamepage is None: return for d in gamepage: if d[ 'category' ] == category: if is_bkt: # Update best known time for this category d['bk_runner'] = user.username d['bk_time'] = util.seconds_to_timestr( seconds ) d['bk_date'] = date d['bk_video'] = video for i, runinfo in enumerate( d['infolist'] ): if runinfo['username'] == user.username: # User has run this category before d['infolist'][i] = self.get_runinfo( user.username, game, category ) d['infolist'].sort( key=lambda x: util.get_valid_date( x['pb_date'] ) ) d['infolist'].sort( key=itemgetter('pb_seconds') ) self.update_cache_gamepage( game, gamepage ) return # Category found, but user has not prev. run this category runinfo = self.get_runinfo( user.username, game, category ) d['infolist'].append( runinfo ) d['infolist'].sort( key=lambda x: util.get_valid_date( x['pb_date'] ) ) d['infolist'].sort( key=itemgetter('pb_seconds') ) gamepage.sort( key=lambda x: len(x['infolist']), reverse=True ) self.update_cache_gamepage( game, gamepage ) return # This is a new category for this game runinfo = self.get_runinfo( user.username, game, category ) d = dict( category=category, category_code=util.get_code( category ), infolist=[runinfo] ) # Check for best known time. Since we update games.Games before # updating gamepage, this will catch the case for when is_bkt is true. game_model = self.get_game_model( util.get_code( game ) ) if game_model is None: logging.error( "Failed to update gamepage for " + game ) self.update_cache_gamepage( game, None ) return gameinfolist = json.loads( game_model.info ) for gameinfo in gameinfolist: if gameinfo['category'] == category: d['bk_runner'] = gameinfo.get( 'bk_runner' ) d['bk_time'] = util.seconds_to_timestr( gameinfo.get( 'bk_seconds' ) ) d['bk_date'] = util.datestr_to_date( gameinfo.get( 'bk_datestr' ) )[ 0 ] d['bk_video'] = gameinfo.get( 'bk_video' ) break gamepage.append( d ) self.update_cache_gamepage( game, gamepage )
def get( self, username_code ): try: user = self.get_user( ) if user == self.OVER_QUOTA_ERROR: user = None q = self.request.get( 'q', default_value=None ) t = self.request.get( 't', default_value=None ) show_all = False page_num = 1 if t == 'show-all': show_all = True else: # Grab the page num page_num = self.request.get( 'page', default_value=1 ) try: page_num = int( page_num ) except ValueError: page_num = 1 # Make sure the runner exists runner = self.get_runner( username_code ) if runner is None: self.error( 404 ) self.render( "404.html", user=user ) return if runner == self.OVER_QUOTA_ERROR: self.error( 403 ) if self.format == 'html': self.render( "403.html", user=user ) return username = runner.username gravatar = util.get_gravatar_url( runner.gravatar, size=120 ) if q == 'view-all': # List all runs for this runner res = self.get_runlist_for_runner( username, page_num ) if res == self.OVER_QUOTA_ERROR: self.error( 403 ) self.render( "403.html", user=user ) elif self.format == 'html': self.render( "listruns.html", user=user, runner=runner, username_code=username_code, runlist=res['runlist'], page_num=res['page_num'], has_next=res['has_next'], has_prev=(res['page_num'] > 1), gravatar=gravatar ) elif self.format == 'json': self.render_json( res['runlist'] ) else: # By default, list pbs for this runner res = self.get_pblist( username, page_num, show_all ) if res == self.OVER_QUOTA_ERROR: self.error( 403 ) self.render( "403.html", user=user ) return # We are also going to list the best known times for each game. # Let's gather those times here and add them to the pblist # info. pblist = res['pblist'] for pb in pblist: game_model = self.get_game_model( pb['game_code'] ) if game_model is None: logging.error( "No game_model for game " + pb['game'] ) continue if game_model == self.OVER_QUOTA_ERROR: self.error( 403 ) self.render( "403.html", user=user ) return gameinfolist = json.loads( game_model.info ) for runinfo in pb['infolist']: # Find the matching gameinfo for gameinfo in gameinfolist: if gameinfo['category'] == runinfo['category']: runinfo['bk_runner'] = gameinfo.get( 'bk_runner' ) runinfo['bk_time'] = util.seconds_to_timestr( gameinfo.get( 'bk_seconds' ) ) runinfo['bk_video'] = gameinfo.get( 'bk_video' ) break if runner.visible_columns: visible_columns = json.loads( runner.visible_columns ) else: visible_columns = util.get_default_visible_columns( ) if self.format == 'html': self.render( "runnerpage.html", user=user, runner=runner, username_code=username_code, pblist=pblist, gravatar=gravatar, page_num=res['page_num'], has_next=res['has_next'], has_prev=( res['page_num'] > 1 ), visible_columns=visible_columns, show_all=res['show_all'] ) elif self.format == 'json': self.render_json( pblist ) except DeadlineExceededError, msg: logging.error( msg ) self.error( 403 ) self.render( "deadline_exceeded.html", user=user )
def get(self, username_code): user = self.get_user() q = self.request.get('q', default_value=None) # Make sure the runner exists runner = self.get_runner(username_code) if runner is None: self.error(404) self.render("404.html", user=user) return username = runner.username gravatar = util.get_gravatar_url(runner.gravatar, size=120) if q == 'view-all': # List all runs for this runner runlist = self.get_runlist_for_runner(username) if self.format == 'html': self.render("listruns.html", user=user, runner=runner, username_code=username_code, runlist=runlist, gravatar=gravatar) elif self.format == 'json': self.render_json(runlist) else: # By default, list pbs for this runner pblist = self.get_pblist(username) # We are also going to list the best known times for each game. # Let's gather those times here and add them to the pblist info. for pb in pblist: game_model = self.get_game_model(pb['game_code']) if game_model is None: logging.error("No game_model for game " + pb['game']) continue gameinfolist = json.loads(game_model.info) for runinfo in pb['infolist']: # Find the matching gameinfo for gameinfo in gameinfolist: if gameinfo['category'] == runinfo['category']: runinfo['bk_runner'] = gameinfo.get('bk_runner') runinfo['bk_time'] = util.seconds_to_timestr( gameinfo.get('bk_seconds')) runinfo['bk_video'] = gameinfo.get('bk_video') break if runner.visible_columns: visible_columns = json.loads(runner.visible_columns) else: visible_columns = util.get_default_visible_columns() if self.format == 'html': self.render("runnerpage.html", user=user, runner=runner, username_code=username_code, pblist=pblist, gravatar=gravatar, visible_columns=visible_columns) elif self.format == 'json': self.render_json(pblist)
def get( self, game_code ): try: user = self.get_user( ) if not user: self.redirect( "/" ) return elif user == self.OVER_QUOTA_ERROR: self.error( 403 ) self.render( "403.html" ) return params = dict( user=user ) game_model = self.get_game_model( game_code ) if game_model is None: self.error( 404 ) self.render( "404.html", user=user ) return if game_model == self.OVER_QUOTA_ERROR: self.error( 403 ) self.render( "403.html", user=user ) return # Are we editing an existing run? run_id = self.request.get( 'edit' ) if run_id: # Grab the run to edit run = self.get_run_by_id( run_id ) if run == self.OVER_QUOTA_ERROR: self.error( 403 ) self.render( "403.html", user=user ) return if not run or ( not user.is_mod and user.username != run.username ): self.error( 404 ) self.render( "404.html", user=user ) return params[ 'game' ] = run.game params[ 'game_code' ] = game_code params[ 'category' ] = run.category params[ 'time' ] = util.seconds_to_timestr( run.seconds ) if run.date is not None: params[ 'datestr' ] = run.date.strftime( "%m/%d/%Y" ); params[ 'run_id' ] = run_id if run.video is not None: params[ 'video' ] = run.video if run.version is not None: params[ 'version' ] = run.version if run.notes is not None: params[ 'notes' ] = run.notes else: params['game'] = game_model.game params['game_code'] = game_code params['set_date_to_today'] = True; # Grab all of the categories for autocompleting params['categories'] = game_model.categories( ) self.render( "submit.html", **params ) except DeadlineExceededError, msg: logging.error( msg ) self.error( 403 ) self.render( "deadline_exceeded.html", user=user )
def get_response( self, body ): body_type = body.get( 'type' ) # Currently 5 types: verifylogin, gamelist, categories, gamecategories # and submitrun if body_type is None: return self.get_fail_response( "No type given." ) elif body_type == 'verifylogin': ( valid, response ) = self.verify_login( body ) return response elif body_type == 'gamelist': # Note that this is a different type of gamelist than the one # generated in games.py categories = self.get_categories( ) d = dict( ) for game in categories.keys( ): d[ util.get_code( game ) ] = game return self.get_success_response( data=d ) elif body_type == 'categories': categories = self.get_categories( ) d = dict( ) for game, categorylist in categories.iteritems( ): game_code = util.get_code( game ) for category in categorylist: category_code = util.get_code( category ) d[ game_code + ':' + category_code ] = ( game + ' - ' + category ) return self.get_success_response( data=d ) elif body_type == 'gamecategories': game_code = body.get( 'game' ) game_model = self.get_game_model( game_code ) if game_code is None: return self.get_fail_response( 'No game specified' ) elif game_model is None: return self.get_fail_response( 'Unknown game [' + game_code + '].' ) else: d = dict( ) gameinfolist = json.loads( game_model.info ) for gameinfo in gameinfolist: category = gameinfo['category'] d[ util.get_code( category ) ] = category return self.get_success_response( data=d ) elif body_type == 'submitrun': # First, verify login credentials ( valid, response ) = self.verify_login( body ) if not valid: return response # Grab the params from the body username = body.get( 'username' ) game_code = body.get( 'game' ) category_code = body.get( 'category' ) version = body.get( 'version' ) time = body.get( 'runtime' ) video = body.get( 'video' ) notes = body.get( 'comment' ) splits = body.get( 'splits' ) # Make sure the game and category exist (can't handle new games # and categories just yet) if game_code is None: return self.get_fail_response( 'No game given' ) game_model = self.get_game_model( game_code ) if game_model is None: return self.get_fail_response( 'Unknown game [' + game_code + '].' ) if category_code is None: return self.get_fail_response( 'No category specified' ) gameinfolist = json.loads( game_model.info ) category = None for gameinfo in gameinfolist: if category_code == util.get_code( gameinfo['category'] ): category = gameinfo['category'] break if category is None: return self.get_fail_response( 'Unknown category [' + category_code + '] for game [' + game_code + '].' ) # Parse the time into seconds and ensure it is valid if time is None: return self.get_fail_response( 'No runtime given' ) ( seconds, time_error ) = util.timestr_to_seconds( time ) if seconds is None: return self.get_fail_response( 'Bad runtime [' + time + '] given: ' + time_error ) # Ensure standard format time = util.seconds_to_timestr( seconds ) # Make sure that the notes are not too long if notes is not None and len( notes ) > 140: return self.get_fail_response( 'Comment is too long; must be ' + 'at most 140 characters.' ) # Figure out current date in user's local timezone user = self.get_runner( util.get_code( username ) ) if user.timezone: tz = pytz.timezone( user.timezone ) local_today = datetime.now( pytz.utc ).astimezone( tz ) else: # UTC by default local_today = datetime.now( pytz.utc ) date = local_today.date( ) # Load up the needed parameters and put a new run params = dict( user=user, game=game_model.game, game_code=game_code, game_model=game_model, category=category, category_found=True, seconds=seconds, time=time, video=video, version=version, notes=notes, valid=True, date=date, datestr=date.strftime( "%m/%d/%Y" ), is_bkt=False ) if self.put_new_run( params ): return self.get_success_response( ) else: if params.get( 'video_error' ): return self.get_fail_response( 'Bad video link [' + video + ']: ' + params[ 'video_error' ] ) else: return self.get_fail_response( 'Sorry, an unknown error ' + 'occurred.' ) return self.get_fail_response( "Unknown type [" + body_type + "]." )
def post( self, game_code ): user = self.get_user( ) if not user: self.redirect( "/" ) return elif user == self.OVER_QUOTA_ERROR: self.error( 403 ) self.render( "403.html" ) return category = self.request.get( 'category' ) time = self.request.get( 'time' ) datestr = self.request.get( 'date' ) video = self.request.get( 'video' ) version = self.request.get( 'version' ) notes = self.request.get( 'notes' ) is_bkt = self.request.get( 'bkt', default_value="no" ) if is_bkt == "yes": is_bkt = True else: is_bkt = False run_id = self.request.get( 'edit' ) # Have to take the code of the game code because of percent # encoded plusses game_code = util.get_code( game_code ) params = dict( user = user, game_code = game_code, category = category, time = time, datestr = datestr, video = video, version = version, notes = notes, run_id = run_id, is_bkt = is_bkt ) valid = True # Make sure the game already exists game_model = self.get_game_model( game_code ) game = '' if not game_code: params['game_error'] = "Game cannot be blank" valid = False elif game_model is None: params['game_error'] = ( "That's weird, we could not find any " + "records for that game" ) valid = False elif game_model == self.OVER_QUOTA_ERROR: params['game_error'] = ( "PB Tracker is currently over its quota" + " limit for the day. Please try again " + "tomorrow." ) valid = False else: game = game_model.game params[ 'game' ] = game params[ 'game_model' ] = game_model # Make sure the category doesn't already exist under a similar name category_code = util.get_code( category ) category_found = False if not category_code: params['category_error'] = "Category cannot be blank" valid = False elif game_model is not None: infolist = json.loads( game_model.info ) for info in infolist: if category_code == util.get_code( info['category'] ): category_found = True if category != info['category']: params['category_error'] = ( "Category already exists " + "under [" + info['category'] + "] " + "(case sensitive). " + "Hit submit again to " + "confirm." ) params['category'] = info['category'] valid = False break if not category_found and not games.valid_game_or_category( category ): params['category_error'] = ( "Category must not use any 'funny'" + " characters and can be up to 100 " + "characters long" ) valid = False params[ 'category_found' ] = category_found # Parse the time into seconds, ensure it is valid ( seconds, time_error ) = util.timestr_to_seconds( time ) if seconds is None: params['time_error'] = "Invalid time: " + time_error params['seconds'] = -1 valid = False else: time = util.seconds_to_timestr( seconds ) # Enforce standard form params[ 'time' ] = time params[ 'seconds' ] = seconds # Parse the date, ensure it is valid ( params['date'], params['date_error'] ) = util.datestr_to_date( datestr ) if params['date_error']: params['date_error'] = "Invalid date: " + params['date_error'] valid = False # Check that if this is a best known time, then it beats the old # best known time. if is_bkt and game_model is not None: gameinfolist = json.loads( game_model.info ) for gameinfo in gameinfolist: if gameinfo['category'] == params['category']: if( gameinfo.get( 'bk_seconds' ) is not None and gameinfo['bk_seconds'] <= seconds ): s = ( "This time does not beat current best known " + "time of " + util.seconds_to_timestr( gameinfo.get( 'bk_seconds' ) ) + " by " + gameinfo['bk_runner'] + " (if best known time is incorrect, you can " + "update best known time after submission)" ) params['bkt_error'] = s params['is_bkt'] = False valid = False break # Check that if this is not the best known time, then it doesn't beat # the old best known time if not is_bkt and game_model is not None: gameinfolist = json.loads( game_model.info ) for gameinfo in gameinfolist: if gameinfo['category'] == params['category']: if( gameinfo.get( 'bk_seconds' ) is not None and seconds < gameinfo['bk_seconds'] ): s = ( "This time beats the current best known time of " + util.seconds_to_timestr( gameinfo.get( 'bk_seconds' ) ) + " by " + gameinfo['bk_runner'] + " (if best known time is incorrect, you can " + "update best known time after submission)" ) params['bkt_error'] = s params['is_bkt'] = True valid = False break # Make sure that the notes are not too long if len( notes ) > 140: params['notes_error'] = "Notes must be at most 140 characters" valid = False params['valid'] = valid if run_id: success = self.put_existing_run( params ) else: success = self.put_new_run( params ) if success: self.redirect( "/runner/" + util.get_code( user.username ) + "?q=view-all" ) elif game_model is not None: try: # Grab all of the categories for autocompleting params['categories'] = game_model.categories( ) params['user'] = user self.render( "submit.html", **params ) except DeadlineExceededError, msg: logging.error( msg ) self.error( 403 ) self.render( "deadline_exceeded.html", user=user )
def update_runinfo_delete( self, user, old_run ): # Update avg, num runs runinfo = self.get_runinfo( user.username, old_run['game'], old_run['category'], no_refresh=True ) if runinfo is None: return if runinfo['num_runs'] <= 0: logging.error( "Failed to update runinfo due to nonpositive " + "num_runs " + str( runinfo['num_runs'] ) ) self.update_cache_runinfo( user.username, old_run['game'], old_run['category'], None ) return if( runinfo['num_runs'] > 1 ): runinfo['avg_seconds'] -= ( 1.0 * old_run['seconds'] / runinfo['num_runs'] ) runinfo['num_runs'] -= 1 runinfo['avg_seconds'] *= ( 1.0 * ( runinfo['num_runs'] + 1 ) / runinfo['num_runs'] ) runinfo['avg_time'] = util.seconds_to_timestr( runinfo['avg_seconds'] ) if( runinfo['pb_seconds'] == old_run['seconds'] ): # We need to replace the pb too q = db.Query( runs.Runs, projection=('seconds', 'date', 'video', 'version') ) q.ancestor( runs.key() ) q.filter( 'username ='******'game =', old_run['game'] ) q.filter( 'category =', old_run['category'] ) q.order( 'seconds' ) q.order( 'date' ) pb_run = q.get( ) if pb_run: runinfo['pb_seconds'] = pb_run.seconds runinfo['pb_time'] = util.seconds_to_timestr( pb_run.seconds ) runinfo['pb_date'] = pb_run.date runinfo['video'] = pb_run.video runinfo['version'] = pb_run.version else: logging.error( "Unable to update runinfo due to no new " + "pb found" ) self.update_cache_runinfo( user.username, old_run['game'], old_run['category'], None ) return self.update_cache_runinfo( user.username, old_run['game'], old_run['category'], runinfo ) else: # No other runs for game, category combo self.update_cache_runinfo( user.username, old_run['game'], old_run['category'], dict( username=user.username, username_code=util.get_code( user.username ), category=old_run['category'], category_code=util.get_code( old_run['category'] ), pb_seconds=None, pb_time=None, pb_date=None, num_runs=0, avg_seconds=0, avg_time='0:00', video=None, version=None ) )
def get_response(self, body): body_type = body.get('type') # Currently 5 types: verifylogin, gamelist, categories, gamecategories # and submitrun if body_type is None: return self.get_fail_response("No type given.") elif body_type == 'verifylogin': (valid, response) = self.verify_login(body) return response elif body_type == 'gamelist': return self.get_fail_response("Type [" + body_type + "] currently" + " not supported dynamically, but " + "you can find a static JSON " + "response at " + "https://www.dropbox.com/s/" + "xnvsmx3mt0i4nbv/gamelist.json?dl" + "=0") elif body_type == 'categories': return self.get_fail_response("Type [" + body_type + "] currently" + " not supported dynamically, but " + "you can find a static JSON " + "response at " + "https://www.dropbox.com/s/" + "irdj4xakh72g541/categories.json?" + "dl=0") elif body_type == 'modgamelist': # First, verify login credentials and that we are a mod (valid, response) = self.verify_mod_login(body) if not valid: return response # Note that this is a different type of gamelist than the one # generated in games.py categories = self.get_categories() if categories == self.OVER_QUOTA_ERROR: return self.get_fail_response("PB Tracker is currently " + "experiencing an over " + "quota downtime period.") d = dict() for game in categories.keys(): d[util.get_code(game)] = game return self.get_success_response(data=d) elif body_type == 'modcategories': # First, verify login credentials and that we are a mod (valid, response) = self.verify_mod_login(body) if not valid: return response categories = self.get_categories() if categories == self.OVER_QUOTA_ERROR: return self.get_fail_response("PB Tracker is currently " + "experiencing an over " + "quota downtime period.") d = dict() for game, categorylist in categories.iteritems(): game_code = util.get_code(game) for category in categorylist: category_code = util.get_code(category) d[game_code + ':' + category_code] = (game + ' - ' + category) return self.get_success_response(data=d) elif body_type == 'gamecategories': return self.get_fail_response("Type [" + body_type + "] currently" + " not supported, sorry.") # game_code = body.get( 'game' ) # game_model = self.get_game_model( game_code ) # if game_code is None: # return self.get_fail_response( 'No game specified' ) # elif game_model is None: # return self.get_fail_response( 'Unknown game [' # + game_code + '].' ) # TODO: elif game_model == self.OVER_QUOTA_ERROR: # else: # d = dict( ) # gameinfolist = json.loads( game_model.info ) # for gameinfo in gameinfolist: # category = gameinfo['category'] # d[ util.get_code( category ) ] = category # return self.get_success_response( data=d ) elif body_type == 'submitrun': # First, verify login credentials (valid, response) = self.verify_login(body) if not valid: return response # Grab the params from the body username = body.get('username') game_code = body.get('game') category_code = body.get('category') version = body.get('version') time = body.get('runtime') video = body.get('video') notes = body.get('comment') splits = body.get('splits') # Make sure the game and category exist (can't handle new games # and categories just yet) if game_code is None: return self.get_fail_response('No game given') game_model = self.get_game_model(game_code) if game_model is None: return self.get_fail_response('Unknown game [' + game_code + '].') if game_model == self.OVER_QUOTA_ERROR: return self.get_fail_response('PB Tracker is currently over ' + "quota. Please try again " + "later.") if category_code is None: return self.get_fail_response('No category specified') gameinfolist = json.loads(game_model.info) category = None for gameinfo in gameinfolist: if category_code == util.get_code(gameinfo['category']): category = gameinfo['category'] break if category is None: return self.get_fail_response('Unknown category [' + category_code + '] for game [' + game_code + '].') # Parse the time into seconds and ensure it is valid if time is None: return self.get_fail_response('No runtime given') (seconds, time_error) = util.timestr_to_seconds(time) if seconds is None: return self.get_fail_response('Bad runtime [' + time + '] given: ' + time_error) # Ensure standard format time = util.seconds_to_timestr(seconds) # Make sure that the notes are not too long if notes is not None and len(notes) > 140: return self.get_fail_response('Comment is too long; must be ' + 'at most 140 characters.') # Figure out current date in user's local timezone user = self.get_runner(util.get_code(username)) if user == self.OVER_QUOTA_ERROR: return False, self.get_fail_response( "PB Tracker is currently " + "experiencing an over " + "quota limit down time " + "period.") if user.timezone: tz = pytz.timezone(user.timezone) local_today = datetime.now(pytz.utc).astimezone(tz) else: # UTC by default local_today = datetime.now(pytz.utc) date = local_today.date() # Load up the needed parameters and put a new run params = dict(user=user, game=game_model.game, game_code=game_code, game_model=game_model, category=category, category_found=True, seconds=seconds, time=time, video=video, version=version, notes=notes, valid=True, date=date, datestr=date.strftime("%m/%d/%Y"), is_bkt=False) if self.put_new_run(params): return self.get_success_response() else: if params.get('video_error'): return self.get_fail_response('Bad video link [' + video + ']: ' + params['video_error']) else: return self.get_fail_response('Sorry, an unknown error ' + 'occurred.') return self.get_fail_response("Unknown type [" + body_type + "].")
def get(self, game_code): try: user = self.get_user() if not user: self.redirect("/") return elif user == self.OVER_QUOTA_ERROR: self.error(403) self.render("403.html") return params = dict(user=user) game_model = self.get_game_model(game_code) if game_model is None: self.error(404) self.render("404.html", user=user) return if game_model == self.OVER_QUOTA_ERROR: self.error(403) self.render("403.html", user=user) return # Are we editing an existing run? run_id = self.request.get('edit') if run_id: # Grab the run to edit run = self.get_run_by_id(run_id) if run == self.OVER_QUOTA_ERROR: self.error(403) self.render("403.html", user=user) return if not run or (not user.is_mod and user.username != run.username): self.error(404) self.render("404.html", user=user) return params['game'] = run.game params['game_code'] = game_code params['category'] = run.category params['time'] = util.seconds_to_timestr(run.seconds) if run.date is not None: params['datestr'] = run.date.strftime("%m/%d/%Y") params['run_id'] = run_id if run.video is not None: params['video'] = run.video if run.version is not None: params['version'] = run.version if run.notes is not None: params['notes'] = run.notes else: params['game'] = game_model.game params['game_code'] = game_code params['set_date_to_today'] = True # Grab all of the categories for autocompleting params['categories'] = game_model.categories() self.render("submit.html", **params) except DeadlineExceededError, msg: logging.error(msg) self.error(403) self.render("deadline_exceeded.html", user=user)
def change_categories(self, params): res = "" # Grab the old game model old_game_code = util.get_code(params["old_game"]) if old_game_code == params["new_game_code"]: old_game_model = params["new_game_model"] else: old_game_model = self.get_game_model(old_game_code) if old_game_model is None: return "Did not find game [" + params["old_game"] + "]" if params["new_game_model"] is None: # New game does not exist, so create it params["new_game_model"] = games.Games( game=params["new_game"], info=json.dumps([]), num_pbs=0, parent=games.key(), key_name=params["new_game_code"], ) res += "Created new game model for game [" + params["new_game"] + "]<br>" if not params["new_category_found"]: # Add the new category to the new game model gameinfolist = json.loads(params["new_game_model"].info) d = dict( category=params["new_category"], bk_runner=None, bk_seconds=None, bk_video=None, bk_datestr=None, bk_updater=None, ) gameinfolist.append(d) params["new_game_model"].info = json.dumps(gameinfolist) res += "Added new category [" + params["new_category"] + "]<br>" # Grab the gameinfo for the old game oldgameinfolist = json.loads(old_game_model.info) oldgameinfo = None for g in oldgameinfolist: if util.get_code(params["old_category"]) == util.get_code(g["category"]): oldgameinfo = g break if oldgameinfo is None: return "Did not find old category [" + params["old_category"] + "]" # Grab the gameinfo for the new game newgameinfolist = json.loads(params["new_game_model"].info) newgameinfo = None for g in newgameinfolist: if util.get_code(params["new_category"]) == util.get_code(g["category"]): newgameinfo = g break if newgameinfo is None: return "Did not find new category [" + params["new_category"] + "]" # Update best known time if necessary if oldgameinfo.get("bk_seconds") is not None and ( newgameinfo.get("bk_seconds") is None or oldgameinfo.get("bk_seconds") < newgameinfo.get("bk_seconds") ): newgameinfo["bk_seconds"] = oldgameinfo.get("bk_seconds") newgameinfo["bk_runner"] = oldgameinfo.get("bk_runner") newgameinfo["bk_datestr"] = oldgameinfo.get("bk_datestr") newgameinfo["bk_video"] = oldgameinfo.get("bk_video") newgameinfo["bk_updater"] = oldgameinfo.get("bk_updater") params["new_game_model"].info = json.dumps(newgameinfolist) res += "Updated bkt<br>" # Update num_pbs for old game, new game res += "Previous num_pbs for old game, category = " + str(old_game_model.num_pbs) + "<br>" res += "Previous num_pbs for new game, category = " + str(params["new_game_model"].num_pbs) + "<br>" q = db.Query(runs.Runs, projection=["username"], distinct=True) q.ancestor(runs.key()) q.filter("game =", params["old_game"]) q.filter("category =", params["old_category"]) for run in q.run(limit=1000): old_game_model.num_pbs -= 1 q2 = db.Query(runs.Runs) q2.ancestor(runs.key()) q2.filter("game =", params["new_game"]) q2.filter("category =", params["new_category"]) q2.filter("username ="******"new_game_model"].num_pbs += 1 else: # Need to decrement runner's num_pbs runner = self.get_runner(util.get_code(run.username)) runner.num_pbs -= 1 runner.put() res += ( "Updated " + run.username + " num_pbs from " + str(runner.num_pbs + 1) + " to " + str(runner.num_pbs) + "<br>" ) res += "Updated num_pbs for old game, category = " + str(old_game_model.num_pbs) + "<br>" res += "Updated num_pbs for new game, category = " + str(params["new_game_model"].num_pbs) + "<br>" # Update old, new game models in database old_game_model.put() if old_game_code != params["new_game_code"]: params["new_game_model"].put() # Change the runs res += "<br>Changed runs:<br>" q = db.Query(runs.Runs) q.ancestor(runs.key()) q.filter("game =", params["old_game"]) q.filter("category =", params["old_category"]) for run in q.run(limit=10000): # Update the run run.game = params["new_game"] run.category = params["new_category"] run.put() res += "Runner=" + run.username + " time=" + util.seconds_to_timestr(run.seconds) + "<br>" if not memcache.flush_all(): res += "Failed to flush memcache<br>" # All dun return res