def get_runlist_for_runner( self, username, page_num ): key = self.get_runlist_for_runner_memkey( username ) cached_runlists = memcache.get( key ) if cached_runlists is None: cached_runlists = dict( ) res = cached_runlists.get( page_num ) if res is None: # Not in memcache, so construct the runlist and store in memcache. res = dict( page_num=page_num, has_next=True ) runlist = [ ] try: q = runs.Runs.all( ) q.ancestor( runs.key() ) q.filter( 'username ='******'-date' ) q.order( '-datetime_created' ) c = memcache.get( self.get_runlist_for_runner_cursor_memkey( username, page_num ) ) if c: try: q.with_cursor( start_cursor=c ) except BadRequestError: res['page_num'] = 1 else: res['page_num'] = 1 for run in q.run( limit = self.RUNLIST_PAGE_LIMIT ): 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 ) ) c = q.cursor( ) cursor_key = self.get_runlist_for_runner_cursor_memkey( username, res['page_num'] + 1 ) if memcache.set( cursor_key, c ): logging.debug( "Set " + cursor_key + " in memcache" ) else: logging.warning( "Failed to set new " + cursor_key + " in memcache" ) if len( runlist ) < self.RUNLIST_PAGE_LIMIT: res['has_next'] = False except apiproxy_errors.OverQuotaError, msg: logging.error( msg ) return self.OVER_QUOTA_ERROR res['runlist'] = runlist cached_runlists[ res['page_num'] ] = res if memcache.set( key, cached_runlists ): logging.debug( "Set " + key + " in memcache" ) else: logging.warning( "Failed to set " + key + " in memcache" )
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 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(self): # Make sure it's me user = self.get_user() if not user: self.error(404) self.render("404.html", user=user) return elif user == self.OVER_QUOTA_ERROR: self.error(403) self.render("403.html") return elif user.username != "rggibson": self.error(404) self.render("404.html", user=user) return # Convert seconds to float count = 0 try: q = db.Query(runs.Runs) q.ancestor(runs.key()) for run in q.run(limit=1000000): count += 1 if not isinstance(run.seconds, float): run.seconds = float(run.seconds) run.put() except apiproxy_errors.OverQuotaError, message: logging.error(message) self.write("Over quota error caught after " + str(count) + "runs:<br>" + message) return
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 num_runs( self, username, game, category, limit ): q = db.Query( runs.Runs, keys_only=True ) q.ancestor( runs.key() ) q.filter( 'username ='******'game =', game ) q.filter( 'category =', category ) return q.count( limit=limit )
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 cleanup_games(self): # Grab all of the categories, indexed by game categories = self.get_categories() if categories == self.OVER_QUOTA_ERROR: return categories_modified = False games_to_delete = [] for game, categorylist in categories.iteritems(): # Grab the game model game_code = util.get_code(game) game_model = self.get_game_model(game_code) if game_model == self.OVER_QUOTA_ERROR: return gameinfolist = json.loads(game_model.info) game_model_modified = False glist = [(i, gameinfo) for i, gameinfo in enumerate(gameinfolist)] for i, gameinfo in reversed(glist): # Leave it if the category is marked as a base category if (gameinfo.get('is_base_category') and game != "Luigi's Mansion" and game != "Super Mario Bros.: The Lost Levels" and game != "The Legend of Zelda: A Link to the Past" and game != "Mega Man 9"): continue # Check if there is a run for this game and category try: q = db.Query(runs.Runs, keys_only=True) q.ancestor(runs.key()) q.filter('game =', game) q.filter('category =', gameinfo['category']) num_runs = q.count(limit=1) if num_runs == 0: # Remove this category del gameinfolist[i] logging.info("Removed " + gameinfo['category'] + " from " + game) game_model_modified = True # Remove this category in memcache too for j, category in enumerate(categorylist): if category == gameinfo['category']: del categorylist[j] categories_modified = True break else: logging.error("ERROR: Could not find in " + "categories") except apiproxy_errors.OverQuotaError, msg: logging.error(msg) # Remove the game if no more categories exist if len(gameinfolist) == 0: game_model.delete() games_to_delete.append(game) logging.info(game + " deleted") self.update_cache_game_model(game_code, None) # Update database and memcache if necessary elif game_model_modified: game_model.info = json.dumps(gameinfolist) game_model.put() self.update_cache_game_model(game_code, game_model)
def cleanup_games(self): # Grab all of the categories, indexed by game categories = self.get_categories() categories_modified = False games_to_delete = [] for game, categorylist in categories.iteritems(): # Grab the game model game_code = util.get_code(game) game_model = self.get_game_model(game_code) gameinfolist = json.loads(game_model.info) game_model_modified = False glist = [(i, gameinfo) for i, gameinfo in enumerate(gameinfolist)] for i, gameinfo in reversed(glist): # Leave it if the category is marked as a base category if ( gameinfo.get("is_base_category") and game != "Luigi's Mansion" and game != "Super Mario Bros.: The Lost Levels" and game != "The Legend of Zelda: A Link to the Past" ): continue # Check if there is a run for this game and category q = db.Query(runs.Runs, keys_only=True) q.ancestor(runs.key()) q.filter("game =", game) q.filter("category =", gameinfo["category"]) num_runs = q.count(limit=1) if num_runs == 0: # Remove this category del gameinfolist[i] logging.info("Removed " + gameinfo["category"] + " from " + game) game_model_modified = True # Remove this category in memcache too for j, category in enumerate(categorylist): if category == gameinfo["category"]: del categorylist[j] categories_modified = True break else: logging.error("ERROR: Could not find in categories") # Remove the game if no more categories exist if len(gameinfolist) == 0: game_model.delete() games_to_delete.append(game) logging.info(game + " deleted") self.update_cache_game_model(game_code, None) # Update database and memcache if necessary elif game_model_modified: game_model.info = json.dumps(gameinfolist) game_model.put() self.update_cache_game_model(game_code, game_model) # Finally, update categories in memcache if necessary if categories_modified: for game in games_to_delete: del categories[game] self.update_cache_categories(categories)
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( self ): # Grab all of the categories, indexed by game categories = self.get_categories( ) categories_modified = False games_to_delete = [ ] for game, categorylist in categories.iteritems( ): # Grab the game model game_code = util.get_code( game ) game_model = self.get_game_model( game_code ) gameinfolist = json.loads( game_model.info ) game_model_modified = False glist = [ ( i, gameinfo ) for i, gameinfo in enumerate( gameinfolist ) ] for i, gameinfo in reversed( glist ): # Leave it if the category is marked as a base category if gameinfo.get( 'is_base_category' ): continue # Check if there is a run for this game and category q = db.Query( runs.Runs, keys_only=True ) q.ancestor( runs.key() ) q.filter( 'game =', game ) q.filter( 'category =', gameinfo['category'] ) num_runs = q.count( limit=1 ) if num_runs == 0: # Remove this category del gameinfolist[ i ] logging.debug( "Removed " + gameinfo['category'] + " from " + game ) game_model_modified = True # Remove this category in memcache too for j, category in enumerate( categorylist ): if category == gameinfo['category']: del categorylist[ j ] categories_modified = True break else: logging.debug( "ERROR: Could not find in categories" ) # Remove the game if no more categories exist if len( gameinfolist ) == 0: game_model.delete( ) games_to_delete.append( game ) logging.debug( game + " deleted" ) self.update_cache_game_model( game_code, None ) # Update database and memcache if necessary elif game_model_modified: game_model.info = json.dumps( gameinfolist ) game_model.put( ) self.update_cache_game_model( game_code, game_model ) # Finally, update categories in memcache if necessary if categories_modified: for game in games_to_delete: del categories[ game ] self.update_cache_categories( categories )
def update_games_delete(self, game_model, category, delta_num_pbs): # Check if any runs exist now for this category num_category_runs = 1 try: q = db.Query(runs.Runs, keys_only=True) q.ancestor(runs.key()) q.filter("game =", game_model.game) q.filter("category =", category) num_category_runs = q.count(limit=1) except apiproxy_errors.OverQuotaError, msg: logging.error(msg)
def num_runs( self, username, game, category, limit ): try: q = db.Query( runs.Runs, keys_only=True ) q.ancestor( runs.key() ) q.filter( 'username ='******'game =', game ) q.filter( 'category =', category ) return q.count( limit=limit ) except apiproxy_errors.OverQuotaError, msg: logging.error( msg ) return 0
def num_runs(self, username, game, category, limit): try: q = db.Query(runs.Runs, keys_only=True) q.ancestor(runs.key()) q.filter('username ='******'game =', game) q.filter('category =', category) return q.count(limit=limit) except apiproxy_errors.OverQuotaError, msg: logging.error(msg) return 0
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_pblist(self, username, no_refresh=False): key = self.get_pblist_memkey(username) pblist = memcache.get(key) if pblist is None and not no_refresh: # 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. pblist = [] try: # Use a projection query to get all of the unique game, category # pairs q = db.Query(runs.Runs, projection=('game', 'category'), distinct=True) q.ancestor(runs.key()) q.filter('username ='******'game') q.order('category') cur_game = None for run in q.run(limit=1000): 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 # Add runinfo to pblist info = self.get_runinfo(username, run.game, run.category) if info == self.OVER_QUOTA_ERROR: return self.OVER_QUOTA_ERROR pb['infolist'].append(info) pb['num_runs'] += info['num_runs'] 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) # Sort the games by number of runs pblist.sort(key=itemgetter('num_runs'), reverse=True) if memcache.set(key, pblist): 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_run_by_id(self, run_id): key = self.get_run_by_id_memkey(run_id) run = memcache.get(key) if run is None: # Not in memcache, so get the run from database and store in # memcache. run = runs.Runs.get_by_id(long(run_id), parent=runs.key()) if memcache.set(key, run): logging.debug("Set run in memcache for run_id" + str(run_id)) else: logging.warning("Failed to set new run for run_id" + str(run_id) + " in memcache") else: logging.debug("Got run with run_id " + str(run_id) + " from " + "memcache") return run
def get_pblist( self, username, no_refresh=False ): key = self.get_pblist_memkey( username ) pblist = memcache.get( key ) if pblist is None and not no_refresh: # 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. pblist = [ ] try: # Use a projection query to get all of the unique game, category # pairs q = db.Query( runs.Runs, projection=('game', 'category'), distinct=True ) q.ancestor( runs.key() ) q.filter( 'username ='******'game' ) q.order( 'category' ) cur_game = None for run in q.run( limit = 1000 ): 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 # Add runinfo to pblist info = self.get_runinfo( username, run.game, run.category ) if info == self.OVER_QUOTA_ERROR: return self.OVER_QUOTA_ERROR pb['infolist'].append( info ) pb['num_runs'] += info['num_runs'] 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 ) # Sort the games by number of runs pblist.sort( key=itemgetter('num_runs'), reverse=True ) if memcache.set( key, pblist ): 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_run_by_id(self, run_id): key = self.get_run_by_id_memkey(run_id) run = memcache.get(key) if run is None: # Not in memcache, so get the run from database and store in # memcache. try: run = runs.Runs.get_by_id(long(run_id), parent=runs.key()) except apiproxy_errors.OverQuotaError, msg: logging.error(msg) return self.OVER_QUOTA_ERROR if memcache.set(key, run): logging.debug("Set run in memcache for run_id" + str(run_id)) else: logging.warning("Failed to set new run for run_id" + str(run_id) + " in memcache")
def get_last_run(self, username, no_refresh=False): key = self.get_last_run_memkey(username) run = memcache.get(key) if run is None and not no_refresh: # Not in memcache, so check datastore q = db.Query(runs.Runs) q.ancestor(runs.key()) q.filter('username ='******'-datetime_created') run = q.get() if memcache.set(key, run): logging.debug("Set " + key + " in memcache") else: logging.warning("Failed to set " + key + " in memcache") elif run is not None: logging.debug("Got " + key + " from memcache") return run
def get_run_by_id( self, run_id ): key = self.get_run_by_id_memkey( run_id ) run = memcache.get( key ) if run is None: # Not in memcache, so get the run from database and store in # memcache. try: run = runs.Runs.get_by_id( long( run_id ), parent=runs.key() ) except apiproxy_errors.OverQuotaError, msg: logging.error( msg ) return self.OVER_QUOTA_ERROR if memcache.set( key, run ): logging.debug( "Set run in memcache for run_id" + str( run_id ) ) else: logging.warning( "Failed to set new run for run_id" + str( run_id ) + " in memcache" )
def get_last_run(self, username, no_refresh=False): key = self.get_last_run_memkey(username) run = memcache.get(key) if run is None and not no_refresh: # Not in memcache, so check datastore q = db.Query(runs.Runs) q.ancestor(runs.key()) q.filter("username ="******"-datetime_created") run = q.get() if memcache.set(key, run): logging.debug("Set " + key + " in memcache") else: logging.warning("Failed to set " + key + " in memcache") elif run is not None: logging.debug("Got " + key + " from memcache") return run
def get_last_run( self, username, no_refresh=False ): key = self.get_last_run_memkey( username ) run = memcache.get( key ) if run is None and not no_refresh: # Not in memcache, so check datastore try: q = db.Query( runs.Runs ) q.ancestor( runs.key() ) q.filter( 'username ='******'-datetime_created' ) run = q.get( ) except apiproxy_errors.OverQuotaError, msg: logging.error( msg ) return self.OVER_QUOTA_ERROR if memcache.set( key, run ): logging.debug( "Set " + key + " in memcache" ) else: logging.warning( "Failed to set " + key + " in memcache" )
def get_pblist(self, username, no_refresh=False): key = self.get_pblist_memkey(username) pblist = memcache.get(key) if pblist is None and not no_refresh: # 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. pblist = [] # Use a projection query to get all of the unique game, category # pairs q = db.Query(runs.Runs, projection=("game", "category"), distinct=True) q.ancestor(runs.key()) q.filter("username ="******"game") q.order("category") cur_game = None for run in q.run(limit=1000): 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 # Add runinfo to pblist info = self.get_runinfo(username, run.game, run.category) pb["infolist"].append(info) pb["num_runs"] += info["num_runs"] # Sort the categories for a game by num_runs for pb in pblist: pb["infolist"].sort(key=itemgetter("num_runs"), reverse=True) # Sort the games by number of runs pblist.sort(key=itemgetter("num_runs"), reverse=True) if memcache.set(key, pblist): logging.debug("Set " + key + " in memcache") else: logging.warning("Failed to set " + key + " in memcache") elif pblist is not None: logging.debug("Got " + key + " from memcache") return pblist
def get_last_run(self, username, no_refresh=False): key = self.get_last_run_memkey(username) run = memcache.get(key) if run is None and not no_refresh: # Not in memcache, so check datastore try: q = db.Query(runs.Runs) q.ancestor(runs.key()) q.filter('username ='******'-datetime_created') run = q.get() except apiproxy_errors.OverQuotaError, msg: logging.error(msg) return self.OVER_QUOTA_ERROR if memcache.set(key, run): logging.debug("Set " + key + " in memcache") else: logging.warning("Failed to set " + key + " in memcache")
def get_user_has_run(self, username, game, no_refresh=False): key = self.get_user_has_run_memkey(username, game) user_has_run = memcache.get(key) if user_has_run is None and not no_refresh: # Not in memcache, so check datastore q = db.Query(runs.Runs, keys_only=True) q.ancestor(runs.key()) q.filter("username ="******"game =", game) num = q.count(limit=1) if num > 0: user_has_run = True else: user_has_run = False if memcache.set(key, user_has_run): logging.debug("Set " + key + " in memcache") else: logging.warning("Failed to set " + key + " in memcache") elif user_has_run is not None: logging.debug("Got " + key + " from memcache") return user_has_run
def get_user_has_run(self, username, game, no_refresh=False): key = self.get_user_has_run_memkey(username, game) user_has_run = memcache.get(key) if user_has_run is None and not no_refresh: # Not in memcache, so check datastore q = db.Query(runs.Runs, keys_only=True) q.ancestor(runs.key()) q.filter('username ='******'game =', game) num = q.count(limit=1) if num > 0: user_has_run = True else: user_has_run = False if memcache.set(key, user_has_run): logging.debug("Set " + key + " in memcache") else: logging.warning("Failed to set " + key + " in memcache") elif user_has_run is not None: logging.debug("Got " + key + " from memcache") return user_has_run
def get_user_has_run(self, username, game, no_refresh=False): key = self.get_user_has_run_memkey(username, game) user_has_run = memcache.get(key) if user_has_run is None and not no_refresh: # Not in memcache, so check datastore try: q = db.Query(runs.Runs, keys_only=True) q.ancestor(runs.key()) q.filter('username ='******'game =', game) num = q.count(limit=1) if num > 0: user_has_run = True else: user_has_run = False except apiproxy_errors.OverQuotaError, msg: logging.error(msg) return self.OVER_QUOTA_ERROR if memcache.set(key, user_has_run): logging.debug("Set " + key + " in memcache") else: logging.warning("Failed to set " + key + " in memcache")
def get_user_has_run( self, username, game, no_refresh=False ): key = self.get_user_has_run_memkey( username, game ) user_has_run = memcache.get( key ) if user_has_run is None and not no_refresh: # Not in memcache, so check datastore try: q = db.Query( runs.Runs, keys_only=True ) q.ancestor( runs.key() ) q.filter( 'username ='******'game =', game ) num = q.count( limit=1 ) if num > 0: user_has_run = True else: user_has_run = False except apiproxy_errors.OverQuotaError, msg: logging.error( msg ) return self.OVER_QUOTA_ERROR if memcache.set( key, user_has_run ): logging.debug( "Set " + key + " in memcache" ) else: logging.warning( "Failed to set " + key + " in memcache" )
def get( self ): # Make sure it's me user = self.get_user( ) if not user or user.username != "rggibson": self.error( 404 ) self.render( "404.html", user=user ) return # Convert seconds to float q = db.Query( runs.Runs ) q.ancestor( runs.key() ) count = 0 for run in q.run( limit=1000000 ): count += 1 if not isinstance( run.seconds, float ): try: run.seconds = float( run.seconds ) run.put( ) except apiproxy_errors.OverQuotaError, message: logging.error( message ) self.write( "Over quota error caught after " + str( count ) + "runs:<br>" + message ) return
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" )
# Check if any runs exist now for this category num_category_runs = 1 try: q = db.Query(runs.Runs, keys_only=True) q.ancestor(runs.key()) q.filter("game =", game_model.game) q.filter("category =", category) num_category_runs = q.count(limit=1) except apiproxy_errors.OverQuotaError, msg: logging.error(msg) if num_category_runs <= 0: # Check if any runs exist now for this game at all num_runs = 1 try: q = db.Query(runs.Runs, keys_only=True) q.ancestor(runs.key()) q.filter("game =", game_model.game) num_runs = q.count(limit=1) except apiproxy_errors.OverQuotaError, msg: logging.error(msg) if num_runs <= 0: # No runs exist. Delete this game from the db game = game_model.game game_model.delete() logging.info(game + " deleted") self.update_cache_game_model(util.get_code(game), None) # From gamelist in memcache too cached_gamelists = self.get_cached_gamelists() if cached_gamelists is not None: done = False for page_num, res in cached_gamelists.iteritems():
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"
def put_new_run( self, params ): user = params.get( 'user' ) game = params.get( 'game' ) category = params.get( 'category' ) seconds = params.get( 'seconds' ) time = params.get( 'time' ) video = params.get( 'video' ) version = params.get( 'version' ) notes = params.get( 'notes' ) valid = params.get( 'valid' ) # Add a new run to the database try: new_run = runs.Runs( username = user.username, game = game, category = category, seconds = seconds, date = params[ 'date' ], version = version, notes = notes, parent = runs.key() ) try: if video: new_run.video = video except db.BadValueError: params[ 'video_error' ] = "Invalid video URL" valid = False except db.BadValueError: valid = False if not valid: return False new_run.put( ) params[ 'run_id' ] = str( new_run.key().id() ) params[ 'datetime_created' ] = new_run.datetime_created logging.debug( "Put new run for runner " + user.username + ", game = " + game + ", category = " + category + ", time = " + time ) # Check whether this is the first run for this username, game, # category combination. This will determine whether we need to update # the gamelist and runnerlist, as well as update the num_pbs # for the game and runner. delta_num_pbs = 0 num_runs = self.num_runs( user.username, game, category, 2 ) if num_runs == 1: delta_num_pbs = 1 # Update games.Games, runners.Runners self.update_runner( user, delta_num_pbs ) self.update_games_put( params, delta_num_pbs ) # Update memcache self.update_cache_run_by_id( new_run.key().id(), new_run ) # Must update runinfo before updating pblist, gamepage since these # both rely on runinfo being up to date self.update_runinfo_put( params ) self.update_pblist_put( params ) self.update_gamepage_put( params ) self.update_runlist_for_runner_put( params ) self.update_cache_user_has_run( user.username, game, True ) self.update_cache_last_run( user.username, new_run ) if num_runs <= 0: logging.error( "Unexpected count [" + str(count) + "] for number of runs for " + username + ", " + game + ", " + category ) self.update_cache_gamelist( None ) self.update_cache_runnerlist( None ) if delta_num_pbs == 1: self.update_gamelist_put( params ) self.update_runnerlist_put( params ) return True
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 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 put_new_run(self, params): user = params.get('user') game = params.get('game') category = params.get('category') seconds = params.get('seconds') time = params.get('time') video = params.get('video') version = params.get('version') notes = params.get('notes') valid = params.get('valid') # Add a new run to the database try: new_run = runs.Runs(username=user.username, game=game, category=category, seconds=seconds, date=params['date'], version=version, notes=notes, parent=runs.key()) try: if video: new_run.video = video except db.BadValueError: params['video_error'] = "Invalid video URL" valid = False except db.BadValueError: valid = False if not valid: return False new_run.put() params['run_id'] = str(new_run.key().id()) params['datetime_created'] = new_run.datetime_created logging.debug("Put new run for runner " + user.username + ", game = " + game + ", category = " + category + ", time = " + time) # Check whether this is the first run for this username, game, # category combination. This will determine whether we need to update # the gamelist and runnerlist, as well as update the num_pbs # for the game and runner. delta_num_pbs = 0 num_runs = self.num_runs(user.username, game, category, 2) if num_runs == 1: delta_num_pbs = 1 # Update games.Games, runners.Runners self.update_runner(user, delta_num_pbs) self.update_games_put(params, delta_num_pbs) # Update memcache self.update_cache_run_by_id(new_run.key().id(), new_run) # Must update runinfo before updating pblist, gamepage since these # both rely on runinfo being up to date self.update_runinfo_put(params) self.update_pblist_put(params) self.update_gamepage_put(params) self.update_runlist_for_runner_put(params) self.update_cache_user_has_run(user.username, game, True) self.update_cache_last_run(user.username, new_run) if num_runs <= 0: logging.error("Unexpected count [" + str(count) + "] for number of runs for " + username + ", " + game + ", " + category) self.update_cache_gamelist(None, get_num_pbs=True) self.update_cache_gamelist(None, get_num_pbs=False) self.update_cache_runnerlist(None) if delta_num_pbs == 1: self.update_gamelist_put(params) self.update_runnerlist_put(params) return True
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( self ): QUERY_LIMIT = 1000 cursor_key = 'fixerupper-cursor' # Make sure it's me user = self.get_user( ) if not user: self.error( 404 ) self.render( "404.html", user=user ) return elif user == self.OVER_QUOTA_ERROR: self.error( 403 ) self.render( "403.html" ) return elif user.username != "rggibson": self.error( 404 ) self.render( "404.html", user=user ) return c = self.request.get( 'c', default_value=None ) try: # Add missing games and categories back in + update num_pbs q = db.Query( runs.Runs, projection=[ 'game', 'category', 'username' ], distinct=True ) q.ancestor( runs.key( ) ) q.order( 'game' ) if c is None: c = memcache.get( cursor_key ) if c: try: q.with_cursor( start_cursor=c ) logging.info( "Fixer upper using cursor " + c ) except BadRequestErro: logging.error( "FixerUpper failed to use cursor" ) pass game_model = None categories = None infolist = None old_num_pbs = None do_update = None cursor_to_save = c prev_cursor = c num_runs = 0 for run in q.run( limit=QUERY_LIMIT ): if game_model is None or game_model.game != run.game: # New game if game_model is not None: # Save previous game model game_model.info = json.dumps( infolist ) if do_update or game_model.num_pbs != old_num_pbs: game_model.put( ) self.update_cache_game_model( game_code, game_model ) cursor_to_save = prev_cursor game_code = util.get_code( run.game ) game_model = self.get_game_model( game_code ) if game_model is None: # Make a new game model game_model = games.Games( game=run.game, info=json.dumps( [ ] ), num_pbs=0, parent=games.key(), key_name=game_code ) logging.info( "Fixerupper put new game " + run.game + " in datastore." ) categories = game_model.categories( ) infolist = json.loads( game_model.info ) old_num_pbs = game_model.num_pbs do_update = False game_model.num_pbs = 0 game_model.num_pbs += 1 if run.category not in categories: # Add category infolist.append( dict( category=run.category, bk_runner=None, bk_seconds=None, bk_datestr=None, bk_video=None, bk_updater=None ) ) logging.info( "Fixerupper added category " + run.category + " to " + run.game ) categories.append( run.category ) do_update = True prev_cursor = q.cursor( ) num_runs += 1 if game_model is not None and num_runs < QUERY_LIMIT: # Save last game model game_model.info = json.dumps( infolist ) game_model.put( ) self.update_cache_game_model( game_code, game_model ) cursor_to_save = prev_cursor if cursor_to_save == memcache.get( cursor_key ): logging.error( "No games updated by FixerUpper." ) if game_model is not None: logging.error( "Last game was " + game_model.game ) self.write( "FixerUpper failed to update any games<br>" ) return if memcache.set( cursor_key, cursor_to_save ): s = "FixerUpper finished and saved cursor " + cursor_to_save if game_model is not None: s += "<br>Last game was " + game_model.game self.write( s ) else: self.write( "FixerUpper finished but failed to save cursor " + cursor_to_save ) except apiproxy_errors.OverQuotaError, msg: logging.error( msg ) self.write( "FixerUpper failed with over quota error<br>" ) return
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"
def post(self, run_id): user = self.get_user() if not user: self.redirect("/") return # Get the run run = runs.Runs.get_by_id(long(run_id), parent=runs.key()) if (not run or (not user.is_mod and run.username != user.username)): self.error(404) self.render("404.html", user=user) return # Grab the owner of the run if run.username == user.username: runner = user else: runner = self.get_runner(util.get_code(run.username)) # Delete the run run.delete() # Update memcache old_run = dict(game=run.game, category=run.category, seconds=run.seconds) self.update_cache_run_by_id(run_id, None) # Update games, runner delta_num_pbs = 0 num_runs = self.num_runs(runner.username, run.game, run.category, 1) if num_runs == 0: delta_num_pbs = -1 self.update_runner(runner, delta_num_pbs) self.update_games_delete( self.get_game_model(util.get_code(old_run['game'])), delta_num_pbs) # Must update runinfo before pblist and gamepage because pblist and # gamepage rely on accurate runinfo self.update_runinfo_delete(runner, old_run) self.update_pblist_delete(runner, old_run) self.update_gamepage_delete(runner, old_run) self.update_user_has_run_delete(runner, old_run) if num_runs <= 0: self.update_gamelist_delete(old_run) self.update_runnerlist_delete(runner) # Update runlist for runner in memcache runlist = self.get_runlist_for_runner(runner.username, no_refresh=True) if runlist: for i, run in enumerate(runlist): if run['run_id'] == run_id: del runlist[i] self.update_cache_runlist_for_runner( runner.username, runlist) break # Update last run last_run = self.get_last_run(runner.username, no_refresh=True) if last_run is not None and last_run.key().id() == long(run_id): self.update_cache_last_run(runner.username, None) # Done with deletion self.redirect("/runner/" + util.get_code(runner.username) + "?q=view-all")
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" )
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 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, 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 = runs.Runs.get_by_id( long( run_id ), parent=runs.key() ) if( not run or ( not user.is_mod and run.username != user.username ) ): self.error( 404 ) self.render( "404.html", user=user ) return # Grab the owner of the run if run.username == user.username: runner = user else: runner = self.get_runner( util.get_code( run.username ) ) if runner == self.OVER_QUOTA_ERROR: self.error( 403 ) self.render( "403.html", user=user ) return # Delete the run run.delete( ) # Update memcache old_run = dict( game = run.game, category = run.category, seconds = run.seconds ) self.update_cache_run_by_id( run_id, None ) # Update games, runner delta_num_pbs = 0 num_runs = self.num_runs( runner.username, run.game, run.category, 1 ) if num_runs == 0: delta_num_pbs = -1 self.update_runner( runner, delta_num_pbs ) game_model = self.get_game_model( util.get_code( old_run['game'] ) ) if game_model == self.OVER_QUOTA_ERROR: self.error( 403 ) self.render( "403.html", user=user ) return self.update_games_delete( game_model, run.category, delta_num_pbs ) self.update_pblist_delete( runner, old_run ) self.update_gamepage_delete( runner, old_run ) self.update_user_has_run_delete( runner, old_run ) if num_runs <= 0: self.update_gamelist_delete( old_run ) self.update_runnerlist_delete( runner ) # Update runlist for runner in memcache cached_runlists = self.get_cached_runlists_for_runner( runner.username ) if cached_runlists is not None: found_run = False for page_num, res in cached_runlists.iteritems( ): if found_run: break for i, run in enumerate( res['runlist'] ): if run[ 'run_id' ] == run_id: del cached_runlists[ page_num ]['runlist'][ i ] self.update_cache_runlist_for_runner( runner.username, cached_runlists ) found_run = True break # Update last run last_run = self.get_last_run( runner.username, no_refresh=True ) if last_run == self.OVER_QUOTA_ERROR: self.update_cache_last_run( runner.username, None ) elif last_run is not None and last_run.key( ).id( ) == long( run_id ): self.update_cache_last_run( runner.username, None ) # Done with deletion self.redirect( "/runner/" + util.get_code( runner.username ) + "?q=view-all" )
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