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 = [ ] 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 ) ) 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_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( 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_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_runnerlist( self, no_refresh=False ): key = self.get_runnerlist_memkey( ) runnerlist = memcache.get( key ) if runnerlist is None and not no_refresh: # Build the runnerlist, which is a list of dictionaries where each # dict gives the username and number of pbs for that user. # The list is sorted by numbers of pbs for the user. runnerlist = [ ] try: q = db.Query( runners.Runners, projection=('username', 'gravatar', 'num_pbs') ) q.ancestor( runners.key() ) q.order( '-num_pbs' ) q.order( 'username' ) for runner in q.run( limit=100000 ): runnerlist.append( dict( username = runner.username, username_code = util.get_code( runner.username ), num_pbs = runner.num_pbs, gravatar_url = util.get_gravatar_url( runner.gravatar ) ) ) except apiproxy_errors.OverQuotaError, msg: logging.error( msg ) return self.OVER_QUOTA_ERROR if memcache.set( key, runnerlist ): logging.debug( "Set runnerlist in memcache" ) else: logging.warning( "Failed to set new runnerlist in memcache" )
def get_gamelist( self, no_refresh=False, get_num_pbs=True ): key = self.get_gamelist_memkey( get_num_pbs ) gamelist = memcache.get( key ) if gamelist is None and not no_refresh: # Build the gamelist, which is a list of dictionaries where each # dict gives the game, game_code and number of pbs for that game. # The list is sorted by numbers of pbs for the game gamelist = [ ] projection = [ 'game' ] if get_num_pbs: projection.append( 'num_pbs' ) try: q = db.Query( games.Games, projection=projection ) q.ancestor( games.key() ) if get_num_pbs: q.order( '-num_pbs' ) q.order( 'game' ) for game_model in q.run( limit=10000 ): if get_num_pbs and game_model.num_pbs <= 0: break if get_num_pbs: d = dict( game = game_model.game, game_code = util.get_code( game_model.game ), num_pbs = game_model.num_pbs ) gamelist.append( d ) else: gamelist.append( str( game_model.game ) ) except apiproxy_errors.OverQuotaError, msg: logging.error( msg ) return self.OVER_QUOTA_ERROR if memcache.set( key, gamelist ): logging.debug( "Set " + key + " in memcache" ) else: logging.warning( "Failed to set new " + key + " in memcache" )
def post( self, username_code ): user = self.get_user( ) # Make sure this is the correct user if user is None: 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 util.get_code( user.username ) != username_code: self.error( 404 ) self.render( "404.html", user=user ) return # Get the visible columns visible_columns = util.get_default_visible_columns( ) for key in visible_columns: checked = self.request.get( key + '_visible', default_value="no" ) if checked == "yes": visible_columns[ key ] = True else: visible_columns[ key ] = False # Store user.visible_columns = json.dumps( visible_columns ) user.put( ) # Update memcache self.update_cache_runner( username_code, user ) # That's all self.redirect( "/runner/" + username_code )
def update_runlist_for_runner_put( self, params ): user = params[ 'user' ] game = params[ 'game' ] game_code = params[ 'game_code' ] category = params[ 'category' ] time = params[ 'time' ] video = params[ 'video' ] version = params[ 'version' ] notes = params[ 'notes' ] date = params[ 'date' ] datetime_created = params[ 'datetime_created' ] run_id = params[ 'run_id' ] # Update runlist for runner in memcache runlist = self.get_runlist_for_runner( user.username, no_refresh=True ) if runlist is not None: runlist.insert( 0, dict( run_id = run_id, game = game, game_code = game_code, category = category, category_code = util.get_code( category ), time = time, date = date, datetime_created = datetime_created, video = video, version = version, notes = notes ) ) runlist.sort( key=lambda x: util.get_valid_date( x['date'] ), reverse=True ) self.update_cache_runlist_for_runner( user.username, runlist )
def get_gamelist(self, no_refresh=False): key = self.get_gamelist_memkey() gamelist = memcache.get(key) if gamelist is None and not no_refresh: # Build the gamelist, which is a list of dictionaries where each # dict gives the game, game_code and number of pbs for that game. # The list is sorted by numbers of pbs for the game gamelist = [] q = db.Query(games.Games, projection=('game', 'num_pbs')) q.ancestor(games.key()) q.order('-num_pbs') q.order('game') for game_model in q.run(limit=10000): if game_model.num_pbs <= 0: break gamelist.append( dict(game=game_model.game, game_code=util.get_code(game_model.game), num_pbs=game_model.num_pbs)) if memcache.set(key, gamelist): logging.debug("Set gamelist in memcache") else: logging.warning("Failed to set new gamelist in memcache") elif gamelist is not None: logging.debug("Got gamelist from memcache") return gamelist
def get_gamelist(self, no_refresh=False): key = self.get_gamelist_memkey() gamelist = memcache.get(key) if gamelist is None and not no_refresh: # Build the gamelist, which is a list of dictionaries where each # dict gives the game, game_code and number of pbs for that game. # The list is sorted by numbers of pbs for the game gamelist = [] q = db.Query(games.Games, projection=("game", "num_pbs")) q.ancestor(games.key()) q.order("-num_pbs") q.order("game") for game_model in q.run(limit=10000): if game_model.num_pbs <= 0: break gamelist.append( dict(game=game_model.game, game_code=util.get_code(game_model.game), num_pbs=game_model.num_pbs) ) if memcache.set(key, gamelist): logging.debug("Set gamelist in memcache") else: logging.warning("Failed to set new gamelist in memcache") elif gamelist is not None: logging.debug("Got gamelist from memcache") return gamelist
def post(self, username_code): user = self.get_user() # Make sure this is the correct user if user is None: 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 util.get_code(user.username) != username_code: self.error(404) self.render("404.html", user=user) return # Get the visible columns visible_columns = util.get_default_visible_columns() for key in visible_columns: checked = self.request.get(key + '_visible', default_value="no") if checked == "yes": visible_columns[key] = True else: visible_columns[key] = False # Store user.visible_columns = json.dumps(visible_columns) user.put() # Update memcache self.update_cache_runner(username_code, user) # That's all self.redirect("/runner/" + username_code)
def get(self, username_code): user = self.get_user() # Make sure this is the correct user if user is None: 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 util.get_code(user.username) != username_code: self.error(404) self.render("404.html", user=user) return # Get the stored visible columns, or get the default ones if user.visible_columns: visible_columns = json.loads(user.visible_columns) else: visible_columns = util.get_default_visible_columns() self.render("edit_table.html", user=user, username_code=username_code, visible_columns=visible_columns)
def get_runnerlist(self, no_refresh=False): key = self.get_runnerlist_memkey() runnerlist = memcache.get(key) if runnerlist is None and not no_refresh: # Build the runnerlist, which is a list of dictionaries where each # dict gives the username and number of pbs for that user. # The list is sorted by numbers of pbs for the user. runnerlist = [] q = db.Query(runners.Runners, projection=('username', 'gravatar', 'num_pbs')) q.ancestor(runners.key()) q.order('-num_pbs') q.order('username') for runner in q.run(limit=100000): runnerlist.append( dict(username=runner.username, username_code=util.get_code(runner.username), num_pbs=runner.num_pbs, gravatar_url=util.get_gravatar_url(runner.gravatar))) if memcache.set(key, runnerlist): logging.debug("Set runnerlist in memcache") else: logging.warning("Failed to set new runnerlist in memcache") elif runnerlist is not None: logging.debug("Got runnerlist from memcache") return runnerlist
def get_runnerlist(self, no_refresh=False): key = self.get_runnerlist_memkey() runnerlist = memcache.get(key) if runnerlist is None and not no_refresh: # Build the runnerlist, which is a list of dictionaries where each # dict gives the username and number of pbs for that user. # The list is sorted by numbers of pbs for the user. runnerlist = [] q = db.Query(runners.Runners, projection=("username", "gravatar", "num_pbs")) q.ancestor(runners.key()) q.order("-num_pbs") q.order("username") for runner in q.run(limit=100000): runnerlist.append( dict( username=runner.username, username_code=util.get_code(runner.username), num_pbs=runner.num_pbs, gravatar_url=util.get_gravatar_url(runner.gravatar), ) ) if memcache.set(key, runnerlist): logging.debug("Set runnerlist in memcache") else: logging.warning("Failed to set new runnerlist in memcache") elif runnerlist is not None: logging.debug("Got runnerlist from memcache") return runnerlist
def get( self, username_code ): user = self.get_user( ) # Make sure this is the correct user if user is None: 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 util.get_code( user.username ) != username_code: self.error( 404 ) self.render( "404.html", user=user ) return # Get the stored visible columns, or get the default ones if user.visible_columns: visible_columns = json.loads( user.visible_columns ) else: visible_columns = util.get_default_visible_columns( ) self.render( "edit_table.html", user=user, username_code=username_code, visible_columns=visible_columns )
def get( self, game_code ): user = self.get_user( ) # Make sure this game exists game_model = self.get_game_model( game_code ) if not game_model: self.error( 404 ) self.render( "404.html", user=user ) return # Find out if this user has run this game if user is not None: user_has_run = self.get_user_has_run( user.username, game_model.game ) else: user_has_run = False gamepage = self.get_gamepage( game_model.game ) # Add gravatar images to the gamepage for d in gamepage: for run in d['infolist']: runner = self.get_runner( util.get_code( run['username'] ) ) if runner is not None: run['gravatar_url'] = util.get_gravatar_url( runner.gravatar, size=20 ) if self.format == 'html': self.render( "gamepage.html", user=user, game=game_model.game, game_code=game_code, gamepage=gamepage, user_has_run=user_has_run ) elif self.format == 'json': self.render_json( gamepage )
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 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 get_gamelist( self, page_num ): key = self.get_gamelist_memkey( ) data = memcache.get( key ) if data is None: data = dict( ) res = data.get( page_num ) if res is None: # Build the gamelist, which is a list of dictionaries where each # dict gives the game, game_code and number of pbs for that game. # The list is sorted by numbers of pbs for the game res = dict( page_num=page_num ) gamelist = [ ] projection = [ 'game', 'num_pbs' ] try: q = db.Query( games.Games, projection=projection ) q.ancestor( games.key() ) q.order( '-num_pbs' ) q.order( 'game' ) c = memcache.get( self.get_gamelist_cursor_memkey( page_num ) ) if c: try: q.with_cursor( start_cursor=c ) except BadRequestError: res['page_num'] = 1 else: # Send the user back to the first page res['page_num'] = 1 for game_model in q.run( limit=self.PAGE_LIMIT ): if game_model.num_pbs <= 0: break d = dict( game = game_model.game, game_code = util.get_code( game_model.game ), num_pbs = game_model.num_pbs ) gamelist.append( d ) c = q.cursor( ) cursor_key = self.get_gamelist_cursor_memkey( 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( gamelist ) >= self.PAGE_LIMIT: res['has_next'] = True else: res['has_next'] = False res['gamelist'] = gamelist except apiproxy_errors.OverQuotaError, msg: logging.error( msg ) return self.OVER_QUOTA_ERROR data[ res['page_num'] ] = res if memcache.set( key, data ): logging.debug( "Set " + key + " in memcache" ) else: logging.warning( "Failed to set new " + key + " in memcache" )
def get(self, run_id): user = self.get_user() if not user: self.redirect("/") return # Get the run run = self.get_run_by_id(run_id) 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 is_valid_login( self, username, password ): username_code = util.get_code( username ) # Find the user in the database try: user = runners.Runners.get_by_key_name( username_code, parent=runners.key() ) except apiproxy_errors.OverQuotaError, msg: logging.error( msg ) return False, dict( user_error="Over quota error" )
def is_valid_login(self, username, password): username_code = util.get_code(username) # Find the user in the database try: user = runners.Runners.get_by_key_name(username_code, parent=runners.key()) except apiproxy_errors.OverQuotaError, msg: logging.error(msg) return False, dict(user_error="Over quota error")
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 get_runnerlist( self, page_num ): key = self.get_runnerlist_memkey( ) data = memcache.get( key ) if data is None: data = dict( ) res = data.get( page_num ) if res is None: # Build the runnerlist, which is a list of dictionaries where each # dict gives the username and number of pbs for that user. # The list is sorted by numbers of pbs for the user. res = dict( page_num=page_num, runnerlist=[ ], has_next=True ) try: q = db.Query( runners.Runners, projection=['username', 'gravatar', 'num_pbs'] ) q.ancestor( runners.key() ) q.order( '-num_pbs' ) q.order( 'username' ) c = memcache.get( self.get_runnerlist_cursor_memkey( page_num ) ) if c: try: q.with_cursor( start_cursor=c ) except BadRequestError: res['page_num'] = 1 else: res['page_num'] = 1 for runner in q.run( limit=self.PAGE_LIMIT ): res['runnerlist'].append( dict( username = runner.username, username_code = util.get_code( runner.username ), num_pbs = runner.num_pbs, gravatar_url = util.get_gravatar_url( runner.gravatar ) ) ) c = q.cursor( ) cursor_key = self.get_runnerlist_cursor_memkey( 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( res['runnerlist'] ) < self.PAGE_LIMIT: res['has_next'] = False except apiproxy_errors.OverQuotaError, msg: logging.error( msg ) return self.OVER_QUOTA_ERROR data[ res['page_num'] ] = res if memcache.set( key, data ): logging.debug( "Set " + key + " in memcache" ) else: logging.warning( "Failed to set new " + key + " in memcache" )
def get(self, game_code): user = self.get_user() # Have to take the code of the game code because of percent # encoded plusses game_code = util.get_code(game_code) # Make sure this game exists game_model = self.get_game_model(game_code) if not game_model: self.error(404) self.render("404.html", user=user) return # Find out if this user has run this game if user is not None: user_has_run = self.get_user_has_run(user.username, game_model.game) else: user_has_run = False gamepage = self.get_gamepage(game_model.game) # Add gravatar images to the gamepage for d in gamepage: for run in d['infolist']: runner = self.get_runner(util.get_code(run['username'])) if runner is not None: run['gravatar_url'] = util.get_gravatar_url( runner.gravatar, size=20) if self.format == 'html': self.render("gamepage.html", user=user, game=game_model.game, game_code=game_code, gamepage=gamepage, user_has_run=user_has_run) elif self.format == 'json': self.render_json(gamepage)
def is_valid_login(self, username, password): username_code = util.get_code(username) # Find the user in the database user = runners.Runners.get_by_key_name(username_code, parent=runners.key()) if not user: return False, dict(user_error="Username not found") # Check for valid password if util.valid_pw(username_code, password, user.password): return True, dict() else: return False, dict(pass_error="Invalid password")
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_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 post( self ): username = self.request.get( 'username' ) password = self.request.get( 'password' ) return_url = self.request.get( 'from' ) if not return_url: return_url = "/" ( valid, errors ) = self.is_valid_login( username, password ) if not valid: self.render( "login.html", username=username, return_url=return_url, **errors ) else: # Success! self.login( util.get_code( username ) ) self.redirect( return_url )
def put_new_game( self, game ): # Add a new game to the database try: game_model = games.Games( game = game, info = json.dumps( [ ] ), parent = games.key( ), key_name = util.get_code( game ) ) except db.BadValueError: return False game_model.put( ) logging.warning( "Put new game " + game + " in database." ) # Update memcache self.update_gamelist_snp_put( game ) return True
def post(self): username = self.request.get('username') password = self.request.get('password') return_url = self.request.get('from') if not return_url: return_url = "/" (valid, errors) = self.is_valid_login(username, password) if not valid: self.render("login.html", username=username, return_url=return_url, **errors) else: # Success! self.login(util.get_code(username)) self.redirect(return_url)
def put_new_game(self, game): # Add a new game to the database try: game_model = games.Games(game=game, info=json.dumps([]), parent=games.key(), key_name=util.get_code(game)) except db.BadValueError: return False game_model.put() logging.warning("Put new game " + game + " in database.") # Update memcache self.update_gamelist_snp_put(game) return True
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 verify_mod_login(self, body): # First, verify login credentials (valid, response) = self.verify_login(body) if not valid: return valid, response # Make sure the user is a mod username = body.get('username') 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 not user.is_mod: body_type = body.get('type') return False, self.get_fail_response("You must be a mod to use [" + body_type + "].") return True, self.get_success_response()
def verify_mod_login( self, body ): # First, verify login credentials ( valid, response ) = self.verify_login( body ) if not valid: return valid, response # Make sure the user is a mod username = body.get( 'username' ) 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 not user.is_mod: body_type = body.get( 'type' ) return False, self.get_fail_response( "You must be a mod to use [" + body_type + "]." ) return True, self.get_success_response( )
def login(self): # 获取登陆二维码 self.get_qrlogin_pic() print('请扫描二维码登陆') image_open = Image.open(os.getcwd() + '/login_qr.png') image_open.show() while (True): check = self.check() values = check.cookies.values() if len(values) > 0: # 字节转化为字符串 s = str(check.content) split = s.split(',') # 跳转返回的url(获取响应头的cookies) self.session.get(split[2].replace('\'', '')) break time.sleep(3) # 登陆认证获取cookie authorize = self.authorize() code = util.get_code(authorize.url) self.get_login_cookie(code) self.spider_session.save_cookies_to_local(self.spider_session.get_user_id())
def update_runlist_for_runner_put(self, params): user = params["user"] game = params["game"] game_code = params["game_code"] category = params["category"] time = params["time"] video = params["video"] version = params["version"] notes = params["notes"] date = params["date"] datetime_created = params["datetime_created"] run_id = params["run_id"] # Update runlist for runner in memcache cached_runlists = self.get_cached_runlists_for_runner(user.username) if cached_runlists is not None: res = cached_runlists.get(1) if res is not None: res["runlist"].insert( 0, dict( run_id=run_id, game=game, game_code=game_code, category=category, category_code=util.get_code(category), time=time, date=date, datetime_created=datetime_created, video=video, version=version, notes=notes, ), ) res["runlist"].sort(key=lambda x: util.get_valid_date(x["date"]), reverse=True) self.update_cache_runlist_for_runner(user.username, cached_runlists)
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 loadFromCSV(self, path): code = get_code(path) df = pd.read_csv(path) self.loadFromDataFrame(code, df)
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 post(self): user = self.get_user() if not user: self.redirect("/") return elif user == self.OVER_QUOTA_ERROR: self.error(403) self.render("403.html") return game = self.request.get('game') params = dict(user=user, game=game) 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 game_model == self.OVER_QUOTA_ERROR: self.error(403) self.render("403.html", user=user) return 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 if not valid: # Grab all of the games for autocompleting params['games'] = self.get_gamelist(get_num_pbs=False) if params['games'] == self.OVER_QUOTA_ERROR: self.error(403) self.render("403.html", user=user) else: self.render("presubmit.html", **params) return if game_model is None: # Add the game to the database valid = self.put_new_game(game) if not valid: params['game_error'] = ("Failed to process game with name [" + game + "] (unknown error)") if valid: self.redirect("/submit/" + game_code + '/') else: # Grab all of the games for autocompleting params['games'] = self.get_gamelist(get_num_pbs=False) if params['games'] == self.OVER_QUOTA_ERROR: self.error(403) self.render("403.html", user=user) else: self.render("presubmit.html", **params) return
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) 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_pblist_put(self, params): user = params['user'] game = params['game'] category = params['category']
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 update_runner(self, runner, delta_num_pbs): if delta_num_pbs != 0: runner.num_pbs += delta_num_pbs runner.put() self.update_cache_runner(util.get_code(runner.username), runner)
def post(self): user = self.get_user() if user == self.OVER_QUOTA_ERROR: self.error(403) self.render("403.html") return username = self.request.get('username') password = self.request.get('password') verify = self.request.get('verify') twitter = self.request.get('twitter') if twitter[0:1] == '@': twitter = twitter[1:] youtube = self.request.get('youtube') youtube = youtube.split('/')[-1] twitch = self.request.get('twitch') twitch = twitch.split('/')[-1] hitbox = self.request.get('hitbox') hitbox = hitbox.split('/')[-1] timezone = self.request.get('timezone') gravatar = self.request.get('gravatar') if user is not None: username_code = util.get_code(user.username) else: username_code = util.get_code(username) return_url = self.request.get('from') if not return_url: return_url = "/" elif user is not None and user.is_mod: # Mod is editing a user's profile, possibly his or her own username_code = return_url.split('/')[-1] user = self.get_runner(username_code) if user == self.OVER_QUOTA_ERROR: self.error(403) self.render("403.html") return params = dict(user=user, username=username, password=password, verify=verify, twitter=twitter, youtube=youtube, twitch=twitch, hitbox=hitbox, gravatar=gravatar, timezone=timezone, return_url=return_url) valid = True if user is None and not valid_username(username): params['user_error'] = ("Username must be between " + "1 and 20 alphanumeric, dash, or " + "underscore characters.") valid = False elif user is None: # Check if username already exists runner = self.get_runner(username_code) if runner == self.OVER_QUOTA_ERROR: self.error(403) self.render("403.html", user=user) return if runner is not None: params['user_error'] = "That user already exists." valid = False if not valid_password(password): if user is None or len(password) > 0: params['pass_error'] = ("Password must be between " + "3 and 20 characters.") valid = False if password != verify: params['ver_error'] = "Passwords do not match." valid = False if gravatar != "" and not valid_email(gravatar): if (user is None or not user.gravatar or gravatar != '<private email>'): params['gravatar_error'] = "That's not a valid email." valid = False if user is not None and gravatar == '<private email>': params['gravatar_url'] = util.get_gravatar_url(user.gravatar, 30) if timezone != '' and timezone not in pytz.common_timezones: params['timezone_error'] = "Invalid timezone." valid = False if not valid: self.render("signup.html", timezones=pytz.common_timezones, **params) return if not user: # Add a new runner to the database runner = runners.Runners(username=username, password=util.make_pw_hash( username_code, password), twitter=twitter, youtube=youtube, twitch=twitch, hitbox=hitbox, timezone=timezone, num_pbs=0, parent=runners.key(), key_name=username_code) if gravatar: runner.gravatar = hashlib.md5(gravatar.lower()).hexdigest() runner.put() # Update runner in memcache self.update_cache_runner(username_code, runner) # Update runnerlist in memcache. Note that this puts the runner # at the end of the list, rather than in alphabetical order among # those runners with 0 pbs. The runner will be sorted properly # if the memcache gets flushed, which is good enough runnerlist = self.get_runnerlist(no_refresh=True) if runnerlist == self.OVER_QUOTA_ERROR: self.update_cache_runnerlist(None) elif runnerlist is not None: runnerlist.append( dict(username=username, username_code=username_code, num_pbs=0, gravatar_url=util.get_gravatar_url(runner.gravatar))) self.update_cache_runnerlist(runnerlist) # Update runs for runner in memcache self.update_cache_runlist_for_runner(username, []) self.login(username_code) else: # Editing the current user if len(password) > 0: user.password = util.make_pw_hash(username_code, password) user.twitter = twitter user.youtube = youtube user.twitch = twitch user.hitbox = hitbox user.timezone = timezone if gravatar and gravatar != '<private email>': user.gravatar = hashlib.md5(gravatar.lower()).hexdigest() elif not gravatar: user.gravatar = None user.put() # Update user in memcache self.update_cache_runner(username_code, user) # Update runnerlist in memcache if gravatar updated if gravatar != '<private email>': runnerlist = self.get_runnerlist(no_refresh=True) if runnerlist == self.OVER_QUOTA_ERROR: self.update_cache_runnerlist(None) elif runnerlist is not None: for runnerdict in runnerlist: if runnerdict['username'] == user.username: runnerdict['gravatar_url'] = util.get_gravatar_url( user.gravatar) break self.update_cache_runnerlist(runnerlist) self.redirect(return_url)
def put_existing_run(self, params): user = params['user'] game = params['game'] game_code = params['game_code'] category = params['category'] seconds = params['seconds'] time = params['time'] video = params['video'] version = params['version'] notes = params['notes'] valid = params['valid'] run_id = params['run_id'] # Grab the old run, which we will update to be the new run new_run = self.get_run_by_id(run_id) if new_run == self.OVER_QUOTA_ERROR: return False if (new_run is None or (not user.is_mod and new_run.username != user.username)): return False # Get the owner of this run if new_run.username != user.username: runner = self.get_runner(util.get_code(new_run.username)) if runner == self.OVER_QUOTA_ERROR: return False params['user'] = runner else: runner = user # Store the contents of the old run old_run = dict(game=new_run.game, category=new_run.category, seconds=new_run.seconds) old_game_model = self.get_game_model(util.get_code(old_run['game'])) if old_game_model == self.OVER_QUOTA_ERROR: return False # Update the run try: new_run.game = game new_run.category = category new_run.seconds = seconds new_run.date = params['date'] new_run.version = version new_run.notes = notes except db.BadValueError: valid = False if video: try: new_run.video = video except db.BadValueError: params['video_error'] = "Invalid video URL" valid = False elif new_run.video: new_run.video = None if not valid: return False new_run.put() logging.debug("Put updated run for runner " + runner.username + ", game = " + game + ", category = " + category + ", time= " + time + ", run_id = " + run_id) # Figure out the change in num_pbs for the old and new game, as well # as the runner delta_num_pbs_old = 0 delta_num_pbs_new = 0 if game != old_run['game'] or category != old_run['category']: num_runs = self.num_runs(runner.username, old_run['game'], old_run['category'], 1) if num_runs == 0: delta_num_pbs_old = -1 num_runs = self.num_runs(runner.username, game, category, 2) if num_runs == 1: delta_num_pbs_new = 1 # Update games.Games and runners.Runners self.update_runner(runner, delta_num_pbs_old + delta_num_pbs_new) if game == old_run['game']: self.update_games_delete(params['game_model'], delta_num_pbs_old) else: self.update_games_delete(old_game_model, delta_num_pbs_old) self.update_games_put(params, delta_num_pbs_new) # Update memcache with the removal of the old run and addition of the # new run. self.update_cache_run_by_id(run_id, new_run) # Must update runinfo before pblist and gamepage as in put_new_run() self.update_runinfo_delete(runner, old_run) self.update_runinfo_put(params) self.update_pblist_delete(runner, old_run) self.update_pblist_put(params) self.update_gamepage_delete(runner, old_run) self.update_gamepage_put(params) self.update_user_has_run_delete(runner, old_run) self.update_cache_user_has_run(runner.username, game, True) # Update gamelist and runnerlist in memcache if delta_num_pbs_old == -1: self.update_gamelist_delete(old_run) self.update_runnerlist_delete(runner) if delta_num_pbs_new == 1: self.update_gamelist_put(params) self.update_runnerlist_put(params) # Replace the old run in the runlist for runner in memcache runlist = self.get_runlist_for_runner(runner.username, no_refresh=True) if runlist == self.OVER_QUOTA_ERROR: self.update_cache_runlist_for_runner(runner.username, None) elif runlist is not None: for run in runlist: if run['run_id'] == run_id: run['game'] = game run['game_code'] = game_code run['category'] = category run['category_code'] = util.get_code(category) run['time'] = time run['date'] = new_run.date run['video'] = video run['version'] = version run['notes'] = notes runlist.sort(key=lambda x: util.get_valid_date(x['date']), reverse=True) self.update_cache_runlist_for_runner( runner.username, runlist) break # Check to see if we need to replace the last run for this user 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 new_run.key().id() == last_run.key().id()): self.update_cache_last_run(runner.username, new_run) return True
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 get(self, game_code): try: user = self.get_user() if user == self.OVER_QUOTA_ERROR: user = None # Have to take the code of the game code because of percent # encoded plusses game_code = util.get_code(game_code) # Make sure this game exists game_model = self.get_game_model(game_code) if not game_model: 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 # Find out if this user has run this game if user is not None: user_has_run = self.get_user_has_run(user.username, game_model.game) if user_has_run == self.OVER_QUOTA_ERROR: user_has_run = False else: user_has_run = False gamepage = self.get_gamepage(game_model.game) if gamepage == self.OVER_QUOTA_ERROR: self.error(403) self.render("403.html", user=user) return # Add gravatar images to the gamepage for d in gamepage: for run in d['infolist']: runner = self.get_runner(util.get_code(run['username'])) if runner == self.OVER_QUOTA_ERROR: self.error(403) if self.format == 'html': self.render("403.html", user=user) return if runner is not None: run['gravatar_url'] = util.get_gravatar_url( runner.gravatar, size=20) if self.format == 'html': self.render("gamepage.html", user=user, game=game_model.game, game_code=game_code, gamepage=gamepage, user_has_run=user_has_run) elif self.format == 'json': self.render_json(gamepage) 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': 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 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 put_existing_run( self, params ): user = params[ 'user' ] game = params[ 'game' ] game_code = params[ 'game_code' ] category = params[ 'category' ] seconds = params[ 'seconds' ] time = params[ 'time' ] video = params[ 'video' ] version = params[ 'version' ] notes = params[ 'notes' ] valid = params[ 'valid' ] run_id = params[ 'run_id' ] # Grab the old run, which we will update to be the new run new_run = self.get_run_by_id( run_id ) if ( new_run is None or ( not user.is_mod and new_run.username != user.username ) ): return False # Get the owner of this run if new_run.username != user.username: runner = self.get_runner( util.get_code( new_run.username ) ) params['user'] = runner else: runner = user # Store the contents of the old run old_run = dict( game = new_run.game, category = new_run.category, seconds = new_run.seconds ) # Update the run try: new_run.game = game new_run.category = category new_run.seconds = seconds new_run.date = params['date'] new_run.version = version new_run.notes = notes except db.BadValueError: valid = False if video: try: new_run.video = video except db.BadValueError: params['video_error'] = "Invalid video URL" valid = False elif new_run.video: new_run.video = None if not valid: return False new_run.put( ) logging.debug( "Put updated run for runner " + runner.username + ", game = " + game + ", category = " + category + ", time= " + time + ", run_id = " + run_id ) # Figure out the change in num_pbs for the old and new game, as well # as the runner delta_num_pbs_old = 0 delta_num_pbs_new = 0 if game != old_run['game'] or category != old_run['category']: num_runs = self.num_runs( runner.username, old_run[ 'game' ], old_run[ 'category' ], 1 ) if num_runs == 0: delta_num_pbs_old = -1 num_runs = self.num_runs( runner.username, game, category, 2 ) if num_runs == 1: delta_num_pbs_new = 1 # Update games.Games and runners.Runners self.update_runner( runner, delta_num_pbs_old + delta_num_pbs_new ) if game == old_run['game']: self.update_games_delete( params['game_model'], delta_num_pbs_old ) else: self.update_games_delete( self.get_game_model( util.get_code( old_run['game'] ) ), delta_num_pbs_old ) self.update_games_put( params, delta_num_pbs_new ) # Update memcache with the removal of the old run and addition of the # new run. self.update_cache_run_by_id( run_id, new_run ) # Must update runinfo before pblist and gamepage as in put_new_run() self.update_runinfo_delete( runner, old_run ) self.update_runinfo_put( params ) self.update_pblist_delete( runner, old_run ) self.update_pblist_put( params ) self.update_gamepage_delete( runner, old_run ) self.update_gamepage_put( params ) self.update_user_has_run_delete( runner, old_run ) self.update_cache_user_has_run( runner.username, game, True ) # Update gamelist and runnerlist in memcache if delta_num_pbs_old == -1: self.update_gamelist_delete( old_run ) self.update_runnerlist_delete( runner ) if delta_num_pbs_new == 1: self.update_gamelist_put( params ) self.update_runnerlist_put( params ) # Replace the old run in the runlist for runner in memcache runlist = self.get_runlist_for_runner( runner.username, no_refresh=True ) if runlist: for run in runlist: if run[ 'run_id' ] == run_id: run[ 'game' ] = game run[ 'game_code' ] = game_code run[ 'category' ] = category run[ 'time' ] = time run[ 'date' ] = new_run.date run[ 'video' ] = video run[ 'version' ] = version run[ 'notes' ] = notes runlist.sort( key=lambda x: util.get_valid_date( x['date'] ), reverse=True ) self.update_cache_runlist_for_runner( runner.username, runlist ) break # Check to see if we need to replace the last run for this user last_run = self.get_last_run( runner.username, no_refresh=True ) if( last_run is not None and new_run.key().id() == last_run.key().id() ): self.update_cache_last_run( runner.username, new_run ) return True
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 post( self ): user = self.get_user( ) if not user: self.redirect( "/" ) return elif user == self.OVER_QUOTA_ERROR: self.error( 403 ) self.render( "403.html" ) return game = self.request.get( 'game' ) params = dict( user = user, game = game ) 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 game_model == self.OVER_QUOTA_ERROR: self.error( 403 ) self.render( "403.html", user=user ) return 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 if not valid: # Grab all of the games for autocompleting params['games'] = self.get_static_gamelist( ) if params['games'] == self.OVER_QUOTA_ERROR: self.error( 403 ) self.render( "403.html", user=user ) else: self.render( "presubmit.html", **params ) return if game_model is None: # Add the game to the database valid = self.put_new_game( game ) if not valid: params['game_error'] = ( "Failed to process game with name [" + game + "] (unknown error)" ) if valid: self.redirect( "/submit/" + game_code + '/' ) else: # Grab all of the games for autocompleting params['games'] = self.get_static_gamelist( ) if params['games'] == self.OVER_QUOTA_ERROR: self.error( 403 ) self.render( "403.html", user=user ) else: self.render( "presubmit.html", **params ) return
def update_games_delete(self, game_model, delta_num_pbs): if delta_num_pbs != 0: game_model.num_pbs += delta_num_pbs game_model.put() self.update_cache_game_model(util.get_code(game_model.game), game_model)
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 post( self ): # Get the user user = self.get_user( ) if not user: self.redirect( "/" ) 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 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 )