Example #1
0
    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 )
Example #2
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" )
Example #3
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")
Example #4
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
            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
Example #5
0
    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))
Example #6
0
    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 ) )
Example #7
0
    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")
Example #8
0
    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
Example #9
0
    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
Example #10
0
    def update_runinfo_put(self, params):
        user = params['user']
        game = params['game']
        category = params['category']
        seconds = params['seconds']
        time = params['time']
        date = params['date']
        video = params['video']
        version = params['version']

        # Update runinfo in memcache
        runinfo = self.get_runinfo(user.username,
                                   game,
                                   category,
                                   no_refresh=True)
        if runinfo is None:
            return
        if runinfo == self.OVER_QUOTA_ERROR:
            self.update_cache_runinfo(user.username, game, category, None)
            return

        runinfo['num_runs'] += 1
        runinfo['avg_seconds'] += ((1.0 / runinfo['num_runs']) *
                                   (seconds - runinfo['avg_seconds']))
        runinfo['avg_time'] = util.seconds_to_timestr(runinfo['avg_seconds'])
        if (runinfo['pb_seconds'] is None or runinfo['pb_seconds'] > seconds):
            # We need to update pb as well
            runinfo['pb_seconds'] = seconds
            runinfo['pb_time'] = time
            runinfo['pb_date'] = date
            runinfo['video'] = video
            runinfo['version'] = version
        self.update_cache_runinfo(user.username, game, category, runinfo)
Example #11
0
    def update_runinfo_put( self, params ):
        user = params[ 'user' ]
        game = params[ 'game' ]
        category = params[ 'category' ]
        seconds = params[ 'seconds' ]
        time = params[ 'time' ]
        date = params[ 'date' ]
        video = params[ 'video' ]
        version = params[ 'version' ]

        # Update runinfo in memcache
        runinfo = self.get_runinfo( user.username, game, category, 
                                    no_refresh=True )
        if runinfo is None:
            return

        runinfo['num_runs'] += 1
        runinfo['avg_seconds'] += ( ( 1.0 / runinfo['num_runs'] ) 
                                    * ( seconds - runinfo['avg_seconds'] ) )
        runinfo['avg_time'] = util.seconds_to_timestr( 
            runinfo['avg_seconds'] )
        if( runinfo['pb_seconds'] is None 
            or runinfo['pb_seconds'] > seconds ):
            # We need to update pb as well
            runinfo['pb_seconds'] = seconds
            runinfo['pb_time'] = time
            runinfo['pb_date'] = date
            runinfo['video'] = video
            runinfo['version'] = version
        self.update_cache_runinfo( user.username, game, category, runinfo )
Example #12
0
    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 )
Example #13
0
    def get( self ):
        user = self.get_user( )
        if not user:
            self.redirect( "/" )
            return

        params = dict( user=user )

        # Are we editing an existing run?
        run_id = self.request.get( 'edit' )
        if run_id:
            # Grab the run to edit
            run = self.get_run_by_id( run_id )
            if not run or ( not user.is_mod and user.username != run.username ):
                self.error( 404 )
                self.render( "404.html", user=user )
                return
            params[ 'game' ] = run.game
            params[ 'category' ] = run.category
            params[ 'time' ] = util.seconds_to_timestr( run.seconds )
            if run.date is not None:
                params[ 'datestr' ] = run.date.strftime( "%m/%d/%Y" );
            params[ 'run_id' ] = run_id
            if run.video is not None:
                params[ 'video' ] = run.video
            if run.version is not None:
                params[ 'version' ] = run.version
            if run.notes is not None:
                params[ 'notes' ] = run.notes
        else:
            # Start with the game, category and version from this user's 
            # last run
            run = self.get_last_run( user.username )
            params['set_date_to_today'] = True;
            if run is not None:
                params['game'] = run.game
                params['category'] = run.category
                if run.version is not None:
                    params['version'] = run.version

        # Grab all of the games and categories for autocompleting
        params['categories'] = self.get_categories( )
                    
        self.render( "submit.html", **params )
Example #14
0
    def get(self):
        user = self.get_user()
        if not user:
            self.redirect("/")
            return

        params = dict(user=user)

        # Are we editing an existing run?
        run_id = self.request.get('edit')
        if run_id:
            # Grab the run to edit
            run = self.get_run_by_id(run_id)
            if not run or (not user.is_mod and user.username != run.username):
                self.error(404)
                self.render("404.html", user=user)
                return
            params['game'] = run.game
            params['category'] = run.category
            params['time'] = util.seconds_to_timestr(run.seconds)
            if run.date is not None:
                params['datestr'] = run.date.strftime("%m/%d/%Y")
            params['run_id'] = run_id
            if run.video is not None:
                params['video'] = run.video
            if run.version is not None:
                params['version'] = run.version
            if run.notes is not None:
                params['notes'] = run.notes
        else:
            # Start with the game, category and version from this user's
            # last run
            run = self.get_last_run(user.username)
            params['set_date_to_today'] = True
            if run is not None:
                params['game'] = run.game
                params['category'] = run.category
                if run.version is not None:
                    params['version'] = run.version

        # Grab all of the games and categories for autocompleting
        params['categories'] = self.get_categories()

        self.render("submit.html", **params)
Example #15
0
    def get( self, game_code ):
        user = self.get_user( )
        if user == self.OVER_QUOTA_ERROR:
            self.error( 403 )
            self.render( "403.html" )
            return
        return_url = self.request.get( 'from' )
        if not return_url:
            return_url = "/"

        # Get the category
        category_code = self.request.get( 'c' )
        if user is None or not category_code:
            self.error( 404 )
            self.render( "404.html", user=user )
            return

        # Have to take the code of the category code because of percent
        # encoded plusses
        category_code = util.get_code( category_code )
        
        # Check to make sure that the user has run this game
        game_model = self.get_game_model( game_code )
        if game_model == self.OVER_QUOTA_ERROR:
            self.error( 403 )
            self.render( "403.html", user=user )
            return
        user_has_run = self.get_user_has_run( user.username, game_model.game )
        if user_has_run == self.OVER_QUOTA_ERROR:
            self.error( 403 )
            self.render( "403.html", user=user )
            return            
        if not user_has_run and not user.is_mod:
            self.error( 404 )
            self.render( "404.html", user=user )
            return

        # Find the corresponding gameinfo for this category
        gameinfolist = json.loads( game_model.info )
        gameinfo = None
        for g in gameinfolist:
            if util.get_code( g['category'] ) == category_code:
                gameinfo = g
                break
        if gameinfo is None:
            self.error( 404 )
            self.render( "404.html", user=user )
            return

        params = dict( user=user, game=game_model.game, game_code=game_code, 
                       category=gameinfo['category'], return_url=return_url )
        params['username'] = gameinfo.get( 'bk_runner' )
        if params['username'] is None:
            params['username'] = ''
            params['time'] = ''
            params['datestr'] = ''
            params['video'] = ''
            params['updating'] = False
        else:
            params['time'] = util.seconds_to_timestr( 
                gameinfo.get( 'bk_seconds' ) )
            params['datestr'] = gameinfo.get( 'bk_datestr' )
            params['video'] = gameinfo.get( 'bk_video' )
            params['updating'] = True

        if return_url[ 0 : len( '/runner/' ) ] == '/runner/':
            params['from_runnerpage'] = True
        else:
            params['from_runnerpage'] = False
            
        self.render( "updatebkt.html", **params )
Example #16
0
    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 )
Example #17
0
    def get_response(self, body):
        body_type = body.get('type')

        # Currently 5 types: verifylogin, gamelist, categories, gamecategories
        # and submitrun
        if body_type is None:
            return self.get_fail_response("No type given.")

        elif body_type == 'verifylogin':
            (valid, response) = self.verify_login(body)
            return response

        elif body_type == 'gamelist':
            # Note that this is a different type of gamelist than the one
            # generated in games.py
            categories = self.get_categories()
            d = dict()
            for game in categories.keys():
                d[util.get_code(game)] = game
            return self.get_success_response(data=d)

        elif body_type == 'categories':
            categories = self.get_categories()
            d = dict()
            for game, categorylist in categories.iteritems():
                game_code = util.get_code(game)
                for category in categorylist:
                    category_code = util.get_code(category)
                    d[game_code + ':' + category_code] = (game + ' - ' +
                                                          category)
            return self.get_success_response(data=d)

        elif body_type == 'gamecategories':
            game_code = body.get('game')
            game_model = self.get_game_model(game_code)
            if game_code is None:
                return self.get_fail_response('No game specified')
            elif game_model is None:
                return self.get_fail_response('Unknown game [' + game_code +
                                              '].')
            else:
                d = dict()
                gameinfolist = json.loads(game_model.info)
                for gameinfo in gameinfolist:
                    category = gameinfo['category']
                    d[util.get_code(category)] = category
                return self.get_success_response(data=d)

        elif body_type == 'submitrun':
            # First, verify login credentials
            (valid, response) = self.verify_login(body)
            if not valid:
                return response

            # Grab the params from the body
            username = body.get('username')
            game_code = body.get('game')
            category_code = body.get('category')
            version = body.get('version')
            time = body.get('runtime')
            video = body.get('video')
            notes = body.get('comment')
            splits = body.get('splits')

            # Make sure the game and category exist (can't handle new games
            # and categories just yet)
            if game_code is None:
                return self.get_fail_response('No game given')
            game_model = self.get_game_model(game_code)
            if game_model is None:
                return self.get_fail_response('Unknown game [' + game_code +
                                              '].')
            if category_code is None:
                return self.get_fail_response('No category specified')
            gameinfolist = json.loads(game_model.info)
            category = None
            for gameinfo in gameinfolist:
                if category_code == util.get_code(gameinfo['category']):
                    category = gameinfo['category']
                    break
            if category is None:
                return self.get_fail_response('Unknown category [' +
                                              category_code + '] for game [' +
                                              game_code + '].')

            # Parse the time into seconds and ensure it is valid
            if time is None:
                return self.get_fail_response('No runtime given')
            (seconds, time_error) = util.timestr_to_seconds(time)
            if seconds is None:
                return self.get_fail_response('Bad runtime [' + time +
                                              '] given: ' + time_error)
            # Ensure standard format
            time = util.seconds_to_timestr(seconds)

            # Make sure that the notes are not too long
            if notes is not None and len(notes) > 140:
                return self.get_fail_response('Comment is too long; must be ' +
                                              'at most 140 characters.')

            # Figure out current date in user's local timezone
            user = self.get_runner(util.get_code(username))
            if user.timezone:
                tz = pytz.timezone(user.timezone)
                local_today = datetime.now(pytz.utc).astimezone(tz)
            else:
                # UTC by default
                local_today = datetime.now(pytz.utc)
            date = local_today.date()

            # Load up the needed parameters and put a new run
            params = dict(user=user,
                          game=game_model.game,
                          game_code=game_code,
                          game_model=game_model,
                          category=category,
                          category_found=True,
                          seconds=seconds,
                          time=time,
                          video=video,
                          version=version,
                          notes=notes,
                          valid=True,
                          date=date,
                          datestr=date.strftime("%m/%d/%Y"),
                          is_bkt=False)
            if self.put_new_run(params):
                return self.get_success_response()
            else:
                if params.get('video_error'):
                    return self.get_fail_response('Bad video link [' + video +
                                                  ']: ' +
                                                  params['video_error'])
                else:
                    return self.get_fail_response('Sorry, an unknown error ' +
                                                  'occurred.')

        return self.get_fail_response("Unknown type [" + body_type + "].")
Example #18
0
    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
Example #19
0
    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)
Example #20
0
    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 + "]." )
Example #21
0
    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" )
Example #22
0
    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" )
Example #23
0
    def post( self ):
        user = self.get_user( )
        if not user:
            self.redirect( "/" )
            return

        game = self.request.get( 'game' )
        category = self.request.get( 'category' )
        time = self.request.get( 'time' )
        datestr = self.request.get( 'date' )
        video = self.request.get( 'video' )
        version = self.request.get( 'version' )
        notes = self.request.get( 'notes' )
        is_bkt = self.request.get( 'bkt', default_value="no" )
        if is_bkt == "yes":
            is_bkt = True
        else:
            is_bkt = False
        run_id = self.request.get( 'edit' )

        params = dict( user = user, game = game, category = category, 
                       time = time, datestr = datestr, video = video, 
                       version = version, notes = notes, run_id = run_id, 
                       is_bkt = is_bkt )

        valid = True

        # Make sure the game doesn't already exist under a similar name
        game_code = util.get_code( game )
        game_model = self.get_game_model( game_code )
        if not game_code:
            params['game_error'] = "Game cannot be blank"
            valid = False
        elif game_model is not None and game != game_model.game:
            params['game_error'] = ( "Game already exists under [" 
                                     + game_model.game + "] (case sensitive)."
                                     + " Hit submit again to confirm." )
            params['game'] = game_model.game
            valid = False
        elif not valid_game_or_category( game ):
            params['game_error'] = ( "Game name must not use any 'funny'"
                                     + " characters and can be up to 100 "
                                     + "characters long" )
            valid = False
        params[ 'game_code' ] = game_code
        params[ 'game_model' ] = game_model

        # Make sure the category doesn't already exist under a similar name
        category_code = util.get_code( category )
        category_found = False
        if not category_code:
            params['category_error'] = "Category cannot be blank"
            valid = False
        elif game_model is not None:
            infolist = json.loads( game_model.info )
            for info in infolist:
                if category_code == util.get_code( info['category'] ):
                    category_found = True
                    if category != info['category']:
                        params['category_error'] = ( "Category already exists "
                                                     + "under [" 
                                                     + info['category'] + "] "
                                                     + "(case sensitive). "
                                                     + "Hit submit again to "
                                                     + "confirm." )
                        params['category'] = info['category']
                        valid = False
                    break
        if not category_found and not valid_game_or_category( category ):
            params['category_error'] = ( "Category must not use any 'funny'"
                                         + " characters and can be up to 100 "
                                         + "characters long" )
            valid = False
        params[ 'category_found' ] = category_found

        # Parse the time into seconds, ensure it is valid
        ( seconds, time_error ) = util.timestr_to_seconds( time )
        if seconds is None:
            params['time_error'] = "Invalid time: " + time_error
            params['seconds'] = -1
            valid = False
        else:
            time = util.seconds_to_timestr( seconds ) # Enforce standard form
            params[ 'time' ] = time
            params[ 'seconds' ] = seconds

        # Parse the date, ensure it is valid
        ( params['date'], params['date_error'] ) = util.datestr_to_date( 
            datestr )
        if params['date_error']:
            params['date_error'] = "Invalid date: " + params['date_error']
            valid = False
                
        # Check that if this is a best known time, that it beats the old
        # best known time
        if is_bkt and game_model is not None:
            gameinfolist = json.loads( game_model.info )
            for gameinfo in gameinfolist:
                if gameinfo['category'] == params['category']:
                    if( gameinfo.get( 'bk_seconds' ) is not None
                        and gameinfo['bk_seconds'] <= seconds ):
                        s = ( "This time does not beat current best known "
                              + "time of " + util.seconds_to_timestr( 
                                  gameinfo.get( 'bk_seconds' ) ) 
                              + " by " + gameinfo['bk_runner'] 
                              + " (if best known time is incorrect, you can "
                              + "update best known time after submission)" )
                        params['bkt_error'] = s
                        params['is_bkt'] = False
                        valid = False
                    break

        # Make sure that the notes are not too long
        if len( notes ) > 140:
            params['notes_error'] = "Notes must be at most 140 characters"
            valid = False

        params['valid'] = valid
        
        if run_id:
            success = self.put_existing_run( params )
        else:
            success = self.put_new_run( params )
        if success:
            self.redirect( "/runner/" + util.get_code( user.username )
                           + "?q=view-all" )
        else:
            # Grab all of the games for autocompleting
            params['categories'] = self.get_categories( )            
            params['user'] = user
            self.render( "submit.html", **params )
Example #24
0
    def update_pblist_put(self, params):
        user = params["user"]
        game = params["game"]
        category = params["category"]
        seconds = params["seconds"]
        time = params["time"]
        video = params["video"]
        game_code = params["game_code"]
        date = params["date"]
        version = params["version"]

        # Update pblist in memcache
        cached_pblists = self.get_cached_pblists(user.username)
        if cached_pblists is None:
            return

        for page_num, res in cached_pblists.iteritems():
            pblist = res["pblist"]
            for pb in pblist:
                if pb["game"] == game:
                    pb["num_runs"] += 1
                    for i, info in enumerate(pb["infolist"]):
                        if info["category"] == category:
                            info["num_runs"] += 1
                            info["avg_seconds"] += (1.0 / info["num_runs"]) * (seconds - info["avg_seconds"])
                            info["avg_time"] = util.seconds_to_timestr(info["avg_seconds"], dec_places=0)
                            if info["pb_seconds"] is None or info["pb_seconds"] > seconds:
                                # Update pb
                                info["pb_seconds"] = seconds
                                info["pb_time"] = time
                                info["pb_date"] = date
                                info["video"] = video
                                info["version"] = version

                            pb["infolist"].sort(key=itemgetter("category"))
                            pb["infolist"].sort(key=itemgetter("num_runs"), reverse=True)
                            self.update_cache_pblist(user.username, cached_pblists)
                            return
                    if res["show_all"]:
                        # Found the game, but not the category and we are
                        # showing all runs.  Add the run.
                        info = dict(
                            username=user.username,
                            username_code=util.get_code(user.username),
                            category=category,
                            category_code=util.get_code(category),
                            pb_seconds=seconds,
                            pb_time=time,
                            pb_date=date,
                            num_runs=1,
                            avg_seconds=seconds,
                            avg_time=time,
                            video=video,
                            version=version,
                        )
                        pb["infolist"].append(info)
                        pb["infolist"].sort(key=itemgetter("category"))
                        pb["infolist"].sort(key=itemgetter("num_runs"), reverse=True)
                        self.update_cache_pblist(user.username, cached_pblists)
                        return
            if res["show_all"]:
                # Could not find the game and we are showing all runs. Add
                # the game/run.
                info = dict(
                    username=user.username,
                    username_code=util.get_code(user.username),
                    category=category,
                    category_code=util.get_code(category),
                    pb_seconds=seconds,
                    pb_time=time,
                    pb_date=date,
                    num_runs=1,
                    avg_seconds=seconds,
                    avg_time=time,
                    video=video,
                    version=version,
                )
                pb = dict(game=game, game_code=util.get_code(game), num_runs=1, infolist=[info])
                pblist.append(pb)
                self.update_cache_pblist(user.username, cached_pblists)
                return

        # Couldn't find this game, category combination, so we must nullify
        # memcache.  We can't just add the run since we may not have all of
        # the pblist pages in memcache, so we don't know if it is the only
        # run for this game, category or not.
        self.update_cache_pblist(user.username, None)
Example #25
0
    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)
Example #26
0
    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)
Example #27
0
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"
Example #28
0
    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
Example #29
0
    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")
Example #30
0
    def post( self, game_code ):
        user = self.get_user( )
        if user == self.OVER_QUOTA_ERROR:
            self.error( 403 )
            self.render( "403.html" )
            return
        return_url = self.request.get( 'from' )
        if not return_url:
            return_url = "/"

        # Get the category
        category_code = self.request.get( 'c' )
        if user is None or category_code is None:
            self.error( 404 )
            self.render( "404.html", user=user )
            return
        
        # Have to take the code of the category code because of percent
        # encoded plusses
        category_code = util.get_code( category_code )
        
        # Check to make sure that the user has run this game
        game_model = self.get_game_model( game_code )
        if game_model == self.OVER_QUOTA_ERROR:
            self.error( 403 )
            self.render( "403.html", user=user )
            return
        user_has_run = self.get_user_has_run( user.username, game_model.game )
        if user_has_run == self.OVER_QUOTA_ERROR:
            self.error( 403 )
            self.render( "403.html", user=user )
            return
        if not user_has_run and not user.is_mod:
            self.error( 404 )
            self.render( "404.html", user=user )
            return

        # Find the corresponding gameinfo for this category
        gameinfolist = json.loads( game_model.info )
        gameinfo = None
        for g in gameinfolist:
            if util.get_code( g['category'] ) == category_code:
                gameinfo = g
                break
        if gameinfo is None:
            self.error( 404 )
            self.render( "404.html", user=user )
            return

        # Get the inputs
        username = self.request.get( 'username' )
        time = self.request.get( 'time' )
        datestr = self.request.get( 'date' )
        video = self.request.get( 'video' )

        params = dict( user=user, game=game_model.game, game_code=game_code,
                       category=gameinfo['category'], username=username,
                       time=time, datestr=datestr, video=video, 
                       return_url=return_url )

        # Are we updating?
        if gameinfo.get( 'bk_runner' ) is None:
            params['updating'] = False
        else:
            params['updating'] = True

        valid = True

        # Check for where we came from
        if return_url[ 0 : len( '/runner/' ) ] == '/runner/':
            params['from_runnerpage'] = True
        else:
            params['from_runnerpage'] = False

        if not username and not time and not datestr and not video:
            gameinfo['bk_runner'] = None
            gameinfo['bk_seconds'] = None
            gameinfo['bk_datestr'] = None
            gameinfo['bk_video'] = None
            date = None
        else:
            # Make sure we got a username
            if not username:
                params['username_error'] = "You must enter a runner"
                valid = False

            # Parse the time into seconds, ensure it is valid
            ( seconds, time_error ) = util.timestr_to_seconds( time )
            if not seconds:
                params['time_error'] = "Invalid time: " + time_error
                valid = False

            # Parse the date, ensure it is valid
            ( date, date_error ) = util.datestr_to_date( datestr )
            if date_error:
                params['date_error'] = "Invalid date: " + date_error
                valid = False

            if not valid:
                self.render( "updatebkt.html", **params )
                return

            time = util.seconds_to_timestr( seconds ) # Standard format
            params['time'] = time

            # Store the best known time
            gameinfo['bk_runner'] = username
            gameinfo['bk_seconds'] = seconds
            gameinfo['bk_datestr'] = datestr
            gameinfo['bk_video'] = video

        gameinfo['bk_updater'] = user.username
        game_model.info = json.dumps( gameinfolist )
        game_model.put( )

        # Update game_model in memcache
        self.update_cache_game_model( game_code, game_model )

        # Update gamepage in memcache
        cached_gamepages = self.get_cached_gamepages( game_model.game,
                                                      category_code )
        if cached_gamepages is not None:
            for page_num, gamepage in cached_gamepages.iteritems( ):
                d = gamepage['d']
                d['bk_runner'] = gameinfo['bk_runner']
                d['bk_time'] = util.seconds_to_timestr(
                    gameinfo['bk_seconds'] )
                d['bk_date'] = date
                d['bk_video'] = gameinfo['bk_video']
            self.update_cache_gamepage( game_model.game, category_code,
                                        cached_gamepages )

        # All dun
        self.redirect( return_url )
Example #31
0
    def update_gamepage_put( self, params ):
        user = params[ 'user' ]
        game = params[ 'game' ]
        category = params[ 'category' ]
        seconds = params[ 'seconds' ]
        time = params[ 'time' ]
        date = params[ 'date' ]
        video = params[ 'video' ]
        is_bkt = params[ 'is_bkt' ]

        # Update gamepage in memcache
        gamepage = self.get_gamepage( game, no_refresh=True )
        if gamepage is None:
            return

        for d in gamepage:
            if d[ 'category' ] == category:
                if is_bkt:
                    # Update best known time for this category
                    d['bk_runner'] = user.username
                    d['bk_time'] = util.seconds_to_timestr( seconds )
                    d['bk_date'] = date
                    d['bk_video'] = video
                for i, runinfo in enumerate( d['infolist'] ):
                    if runinfo['username'] == user.username:
                        # User has run this category before
                        d['infolist'][i] = self.get_runinfo( user.username, 
                                                             game, category )
                        d['infolist'].sort( key=lambda x: util.get_valid_date(
                                x['pb_date'] ) )
                        d['infolist'].sort( key=itemgetter('pb_seconds') )
                        self.update_cache_gamepage( game, gamepage )
                        return
                
                # Category found, but user has not prev. run this category
                runinfo = self.get_runinfo( user.username, game, category )
                d['infolist'].append( runinfo )
                d['infolist'].sort( key=lambda x: util.get_valid_date(
                        x['pb_date'] ) )                
                d['infolist'].sort( key=itemgetter('pb_seconds') )
                gamepage.sort( key=lambda x: len(x['infolist']), reverse=True )
                self.update_cache_gamepage( game, gamepage )
                return
        
        # This is a new category for this game
        runinfo = self.get_runinfo( user.username, game, category )
        d = dict( category=category, 
                  category_code=util.get_code( category ),
                  infolist=[runinfo] )
        # Check for best known time. Since we update games.Games before 
        # updating gamepage, this will catch the case for when is_bkt is true.
        game_model = self.get_game_model( util.get_code( game ) )
        if game_model is None:
            logging.error( "Failed to update gamepage for " + game )
            self.update_cache_gamepage( game, None )
            return
        gameinfolist = json.loads( game_model.info )
        for gameinfo in gameinfolist:
            if gameinfo['category'] == category:
                d['bk_runner'] = gameinfo.get( 'bk_runner' )
                d['bk_time'] = util.seconds_to_timestr( 
                        gameinfo.get( 'bk_seconds' ) )
                d['bk_date'] = util.datestr_to_date( 
                    gameinfo.get( 'bk_datestr' ) )[ 0 ]
                d['bk_video'] = gameinfo.get( 'bk_video' )
                break
        gamepage.append( d )
        self.update_cache_gamepage( game, gamepage )
Example #32
0
    def get( self, username_code ):
        try:
            user = self.get_user( )
            if user == self.OVER_QUOTA_ERROR:
                user = None
            q = self.request.get( 'q', default_value=None )
            t = self.request.get( 't', default_value=None )
            show_all = False
            page_num = 1
            if t == 'show-all':
                show_all = True
            else:
                # Grab the page num
                page_num = self.request.get( 'page', default_value=1 )
                try:
                    page_num = int( page_num )
                except ValueError:
                    page_num = 1

            # Make sure the runner exists
            runner = self.get_runner( username_code )
            if runner is None:
                self.error( 404 )
                self.render( "404.html", user=user )
                return
            if runner == self.OVER_QUOTA_ERROR:
                self.error( 403 )
                if self.format == 'html':
                    self.render( "403.html", user=user )
                return
            username = runner.username
            gravatar = util.get_gravatar_url( runner.gravatar, size=120 )

            if q == 'view-all':
                # List all runs for this runner
                res = self.get_runlist_for_runner( username, page_num )
                if res == self.OVER_QUOTA_ERROR:
                    self.error( 403 )
                    self.render( "403.html", user=user )
                elif self.format == 'html':
                    self.render( "listruns.html", user=user, runner=runner,
                                 username_code=username_code,
                                 runlist=res['runlist'],
                                 page_num=res['page_num'],
                                 has_next=res['has_next'],
                                 has_prev=(res['page_num'] > 1),
                                 gravatar=gravatar )
                elif self.format == 'json':
                    self.render_json( res['runlist'] )
            else:
                # By default, list pbs for this runner
                res = self.get_pblist( username, page_num, show_all )
                if res == self.OVER_QUOTA_ERROR:
                    self.error( 403 )
                    self.render( "403.html", user=user )
                    return
                # We are also going to list the best known times for each game.
                # Let's gather those times here and add them to the pblist
                # info.
                pblist = res['pblist']
                for pb in pblist:
                    game_model = self.get_game_model( pb['game_code'] )
                    if game_model is None:
                        logging.error( "No game_model for game " + pb['game'] )
                        continue
                    if game_model == self.OVER_QUOTA_ERROR:
                        self.error( 403 )
                        self.render( "403.html", user=user )
                        return
                    gameinfolist = json.loads( game_model.info )
                    for runinfo in pb['infolist']:
                        # Find the matching gameinfo
                        for gameinfo in gameinfolist:
                            if gameinfo['category'] == runinfo['category']:
                                runinfo['bk_runner'] = gameinfo.get(
                                    'bk_runner' )
                                runinfo['bk_time'] = util.seconds_to_timestr(
                                    gameinfo.get( 'bk_seconds' ) )
                                runinfo['bk_video'] = gameinfo.get(
                                    'bk_video' )
                                break

                if runner.visible_columns:
                    visible_columns = json.loads( runner.visible_columns )
                else:
                    visible_columns = util.get_default_visible_columns( )

                if self.format == 'html':
                    self.render( "runnerpage.html", user=user, runner=runner,
                                 username_code=username_code, pblist=pblist,
                                 gravatar=gravatar, page_num=res['page_num'],
                                 has_next=res['has_next'],
                                 has_prev=( res['page_num'] > 1 ),
                                 visible_columns=visible_columns,
                                 show_all=res['show_all'] )
                elif self.format == 'json':
                    self.render_json( pblist )

        except DeadlineExceededError, msg:
            logging.error( msg )
            self.error( 403 )
            self.render( "deadline_exceeded.html", user=user )
Example #33
0
    def get(self, username_code):
        user = self.get_user()
        q = self.request.get('q', default_value=None)

        # Make sure the runner exists
        runner = self.get_runner(username_code)
        if runner is None:
            self.error(404)
            self.render("404.html", user=user)
            return
        username = runner.username
        gravatar = util.get_gravatar_url(runner.gravatar, size=120)

        if q == 'view-all':
            # List all runs for this runner
            runlist = self.get_runlist_for_runner(username)
            if self.format == 'html':
                self.render("listruns.html",
                            user=user,
                            runner=runner,
                            username_code=username_code,
                            runlist=runlist,
                            gravatar=gravatar)
            elif self.format == 'json':
                self.render_json(runlist)
        else:
            # By default, list pbs for this runner
            pblist = self.get_pblist(username)
            # We are also going to list the best known times for each game.
            # Let's gather those times here and add them to the pblist info.
            for pb in pblist:
                game_model = self.get_game_model(pb['game_code'])
                if game_model is None:
                    logging.error("No game_model for game " + pb['game'])
                    continue
                gameinfolist = json.loads(game_model.info)
                for runinfo in pb['infolist']:
                    # Find the matching gameinfo
                    for gameinfo in gameinfolist:
                        if gameinfo['category'] == runinfo['category']:
                            runinfo['bk_runner'] = gameinfo.get('bk_runner')
                            runinfo['bk_time'] = util.seconds_to_timestr(
                                gameinfo.get('bk_seconds'))
                            runinfo['bk_video'] = gameinfo.get('bk_video')
                            break

            if runner.visible_columns:
                visible_columns = json.loads(runner.visible_columns)
            else:
                visible_columns = util.get_default_visible_columns()

            if self.format == 'html':
                self.render("runnerpage.html",
                            user=user,
                            runner=runner,
                            username_code=username_code,
                            pblist=pblist,
                            gravatar=gravatar,
                            visible_columns=visible_columns)
            elif self.format == 'json':
                self.render_json(pblist)
Example #34
0
    def get( self, game_code ):
        try:
            user = self.get_user( )
            if not user:
                self.redirect( "/" )
                return
            elif user == self.OVER_QUOTA_ERROR:
                self.error( 403 )
                self.render( "403.html" )
                return

            params = dict( user=user )

            game_model = self.get_game_model( game_code )
            if game_model is None:
                self.error( 404 )
                self.render( "404.html", user=user )
                return
            if game_model == self.OVER_QUOTA_ERROR:
                self.error( 403 )
                self.render( "403.html", user=user )
                return        

            # Are we editing an existing run?
            run_id = self.request.get( 'edit' )
            if run_id:
                # Grab the run to edit
                run = self.get_run_by_id( run_id )
                if run == self.OVER_QUOTA_ERROR:
                    self.error( 403 )
                    self.render( "403.html", user=user )
                    return
                if not run or ( not user.is_mod 
                                and user.username != run.username ):
                    self.error( 404 )
                    self.render( "404.html", user=user )
                    return
                params[ 'game' ] = run.game
                params[ 'game_code' ] = game_code
                params[ 'category' ] = run.category
                params[ 'time' ] = util.seconds_to_timestr( run.seconds )
                if run.date is not None:
                    params[ 'datestr' ] = run.date.strftime( "%m/%d/%Y" );
                params[ 'run_id' ] = run_id
                if run.video is not None:
                    params[ 'video' ] = run.video
                if run.version is not None:
                    params[ 'version' ] = run.version
                if run.notes is not None:
                    params[ 'notes' ] = run.notes
            else:
                params['game'] = game_model.game
                params['game_code'] = game_code
                params['set_date_to_today'] = True;            
                    
            # Grab all of the categories for autocompleting
            params['categories'] = game_model.categories( )

            self.render( "submit.html", **params )

        except DeadlineExceededError, msg:
            logging.error( msg )
            self.error( 403 )
            self.render( "deadline_exceeded.html", user=user )
Example #35
0
    def get_response( self, body ):
        body_type = body.get( 'type' )

        # Currently 5 types: verifylogin, gamelist, categories, gamecategories
        # and submitrun
        if body_type is None:
            return self.get_fail_response( "No type given." )
        
        elif body_type == 'verifylogin':
            ( valid, response ) = self.verify_login( body )
            return response

        elif body_type == 'gamelist':
            # Note that this is a different type of gamelist than the one
            # generated in games.py
            categories = self.get_categories( )
            d = dict( )
            for game in categories.keys( ):
                d[ util.get_code( game ) ] = game
            return self.get_success_response( data=d )

        elif body_type == 'categories':
            categories = self.get_categories( )
            d = dict( )
            for game, categorylist in categories.iteritems( ):
                game_code = util.get_code( game )
                for category in categorylist:
                    category_code = util.get_code( category )
                    d[ game_code + ':' + category_code ] = ( game + ' - ' 
                                                             + category )
            return self.get_success_response( data=d )

        elif body_type == 'gamecategories':
            game_code = body.get( 'game' )
            game_model = self.get_game_model( game_code )
            if game_code is None:
                return self.get_fail_response( 'No game specified' )
            elif game_model is None:
                return self.get_fail_response( 'Unknown game [' 
                                               + game_code + '].' )
            else:
                d = dict( )
                gameinfolist = json.loads( game_model.info )
                for gameinfo in gameinfolist:
                    category = gameinfo['category']
                    d[ util.get_code( category ) ] = category
                return self.get_success_response( data=d )

        elif body_type == 'submitrun':
            # First, verify login credentials
            ( valid, response ) = self.verify_login( body )
            if not valid:
                return response

            # Grab the params from the body
            username = body.get( 'username' )
            game_code = body.get( 'game' )
            category_code = body.get( 'category' )
            version = body.get( 'version' )
            time = body.get( 'runtime' )
            video = body.get( 'video' )
            notes = body.get( 'comment' )
            splits = body.get( 'splits' )

            # Make sure the game and category exist (can't handle new games
            # and categories just yet)
            if game_code is None:
                return self.get_fail_response( 'No game given' )
            game_model = self.get_game_model( game_code )
            if game_model is None:
                return self.get_fail_response( 'Unknown game [' 
                                               + game_code + '].' )
            if category_code is None:
                return self.get_fail_response( 'No category specified' )
            gameinfolist = json.loads( game_model.info )
            category = None
            for gameinfo in gameinfolist:
                if category_code == util.get_code( gameinfo['category'] ):
                    category = gameinfo['category']
                    break
            if category is None:
                return self.get_fail_response( 'Unknown category [' 
                                               + category_code 
                                               + '] for game [' 
                                               + game_code + '].' )

            # Parse the time into seconds and ensure it is valid
            if time is None:
                return self.get_fail_response( 'No runtime given' )
            ( seconds, time_error ) = util.timestr_to_seconds( time )
            if seconds is None:
                return self.get_fail_response( 'Bad runtime [' + time 
                                               + '] given: ' + time_error )
            # Ensure standard format
            time = util.seconds_to_timestr( seconds )

            # Make sure that the notes are not too long
            if notes is not None and len( notes ) > 140:
                return self.get_fail_response( 'Comment is too long; must be '
                                               + 'at most 140 characters.' )

            # Figure out current date in user's local timezone
            user = self.get_runner( util.get_code( username ) )
            if user.timezone:
                tz = pytz.timezone( user.timezone )
                local_today = datetime.now( pytz.utc ).astimezone( tz )
            else:
                # UTC by default
                local_today = datetime.now( pytz.utc )
            date = local_today.date( )

            # Load up the needed parameters and put a new run
            params = dict( user=user,
                           game=game_model.game,
                           game_code=game_code,
                           game_model=game_model,
                           category=category,
                           category_found=True,
                           seconds=seconds,
                           time=time,
                           video=video,
                           version=version,
                           notes=notes,
                           valid=True,
                           date=date,
                           datestr=date.strftime( "%m/%d/%Y" ),
                           is_bkt=False ) 
            if self.put_new_run( params ):
                return self.get_success_response( )
            else:
                if params.get( 'video_error' ):
                    return self.get_fail_response( 'Bad video link [' 
                                                   + video + ']: ' 
                                                   + params[ 'video_error' ] )
                else:
                    return self.get_fail_response( 'Sorry, an unknown error '
                                                   + 'occurred.' )
                    
        return self.get_fail_response( "Unknown type [" + body_type + "]." )
Example #36
0
    def post( self, game_code ):
        user = self.get_user( )
        if not user:
            self.redirect( "/" )
            return
        elif user == self.OVER_QUOTA_ERROR:
            self.error( 403 )
            self.render( "403.html" )
            return

        category = self.request.get( 'category' )
        time = self.request.get( 'time' )
        datestr = self.request.get( 'date' )
        video = self.request.get( 'video' )
        version = self.request.get( 'version' )
        notes = self.request.get( 'notes' )
        is_bkt = self.request.get( 'bkt', default_value="no" )
        if is_bkt == "yes":
            is_bkt = True
        else:
            is_bkt = False
        run_id = self.request.get( 'edit' )

        # Have to take the code of the game code because of percent
        # encoded plusses
        game_code = util.get_code( game_code )

        params = dict( user = user, game_code = game_code,
                       category = category, 
                       time = time, datestr = datestr, video = video, 
                       version = version, notes = notes, run_id = run_id, 
                       is_bkt = is_bkt )

        valid = True

        # Make sure the game already exists
        game_model = self.get_game_model( game_code )
        game = ''
        if not game_code:
            params['game_error'] = "Game cannot be blank"
            valid = False
        elif game_model is None:
            params['game_error'] = ( "That's weird, we could not find any "
                                     + "records for that game" )
            valid = False
        elif game_model == self.OVER_QUOTA_ERROR:
            params['game_error'] = ( "PB Tracker is currently over its quota"
                                     + " limit for the day. Please try again "
                                     + "tomorrow." )
            valid = False
        else:
            game = game_model.game
        params[ 'game' ] = game
        params[ 'game_model' ] = game_model

        # Make sure the category doesn't already exist under a similar name
        category_code = util.get_code( category )
        category_found = False
        if not category_code:
            params['category_error'] = "Category cannot be blank"
            valid = False
        elif game_model is not None:
            infolist = json.loads( game_model.info )
            for info in infolist:
                if category_code == util.get_code( info['category'] ):
                    category_found = True
                    if category != info['category']:
                        params['category_error'] = ( "Category already exists "
                                                     + "under [" 
                                                     + info['category'] + "] "
                                                     + "(case sensitive). "
                                                     + "Hit submit again to "
                                                     + "confirm." )
                        params['category'] = info['category']
                        valid = False
                    break
        if not category_found and not games.valid_game_or_category( category ):
            params['category_error'] = ( "Category must not use any 'funny'"
                                         + " characters and can be up to 100 "
                                         + "characters long" )
            valid = False
        params[ 'category_found' ] = category_found

        # Parse the time into seconds, ensure it is valid
        ( seconds, time_error ) = util.timestr_to_seconds( time )
        if seconds is None:
            params['time_error'] = "Invalid time: " + time_error
            params['seconds'] = -1
            valid = False
        else:
            time = util.seconds_to_timestr( seconds ) # Enforce standard form
            params[ 'time' ] = time
            params[ 'seconds' ] = seconds

        # Parse the date, ensure it is valid
        ( params['date'], params['date_error'] ) = util.datestr_to_date( 
            datestr )
        if params['date_error']:
            params['date_error'] = "Invalid date: " + params['date_error']
            valid = False
                
        # Check that if this is a best known time, then it beats the old
        # best known time.
        if is_bkt and game_model is not None:
            gameinfolist = json.loads( game_model.info )
            for gameinfo in gameinfolist:
                if gameinfo['category'] == params['category']:
                    if( gameinfo.get( 'bk_seconds' ) is not None
                        and gameinfo['bk_seconds'] <= seconds ):
                        s = ( "This time does not beat current best known "
                              + "time of " + util.seconds_to_timestr( 
                                  gameinfo.get( 'bk_seconds' ) ) 
                              + " by " + gameinfo['bk_runner'] 
                              + " (if best known time is incorrect, you can "
                              + "update best known time after submission)" )
                        params['bkt_error'] = s
                        params['is_bkt'] = False
                        valid = False
                    break
                
        # Check that if this is not the best known time, then it doesn't beat
        # the old best known time
        if not is_bkt and game_model is not None:
            gameinfolist = json.loads( game_model.info )
            for gameinfo in gameinfolist:
                if gameinfo['category'] == params['category']:
                    if( gameinfo.get( 'bk_seconds' ) is not None
                        and seconds < gameinfo['bk_seconds'] ):
                        s = ( "This time beats the current best known time of "
                              + util.seconds_to_timestr( 
                                gameinfo.get( 'bk_seconds' ) )
                              + " by " + gameinfo['bk_runner']
                              + " (if best known time is incorrect, you can "
                              + "update best known time after submission)" )
                        params['bkt_error'] = s
                        params['is_bkt'] = True
                        valid = False
                    break

        # Make sure that the notes are not too long
        if len( notes ) > 140:
            params['notes_error'] = "Notes must be at most 140 characters"
            valid = False

        params['valid'] = valid
        
        if run_id:
            success = self.put_existing_run( params )
        else:
            success = self.put_new_run( params )
        if success:
            self.redirect( "/runner/" + util.get_code( user.username )
                           + "?q=view-all" )
        elif game_model is not None:
            try:
                # Grab all of the categories for autocompleting
                params['categories'] = game_model.categories( )
                params['user'] = user
                self.render( "submit.html", **params )
            except DeadlineExceededError, msg:
                logging.error( msg )
                self.error( 403 )
                self.render( "deadline_exceeded.html", user=user )
Example #37
0
    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 ) )
Example #38
0
    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 + "].")
Example #39
0
    def get(self, game_code):
        try:
            user = self.get_user()
            if not user:
                self.redirect("/")
                return
            elif user == self.OVER_QUOTA_ERROR:
                self.error(403)
                self.render("403.html")
                return

            params = dict(user=user)

            game_model = self.get_game_model(game_code)
            if game_model is None:
                self.error(404)
                self.render("404.html", user=user)
                return
            if game_model == self.OVER_QUOTA_ERROR:
                self.error(403)
                self.render("403.html", user=user)
                return

            # Are we editing an existing run?
            run_id = self.request.get('edit')
            if run_id:
                # Grab the run to edit
                run = self.get_run_by_id(run_id)
                if run == self.OVER_QUOTA_ERROR:
                    self.error(403)
                    self.render("403.html", user=user)
                    return
                if not run or (not user.is_mod
                               and user.username != run.username):
                    self.error(404)
                    self.render("404.html", user=user)
                    return
                params['game'] = run.game
                params['game_code'] = game_code
                params['category'] = run.category
                params['time'] = util.seconds_to_timestr(run.seconds)
                if run.date is not None:
                    params['datestr'] = run.date.strftime("%m/%d/%Y")
                params['run_id'] = run_id
                if run.video is not None:
                    params['video'] = run.video
                if run.version is not None:
                    params['version'] = run.version
                if run.notes is not None:
                    params['notes'] = run.notes
            else:
                params['game'] = game_model.game
                params['game_code'] = game_code
                params['set_date_to_today'] = True

            # Grab all of the categories for autocompleting
            params['categories'] = game_model.categories()

            self.render("submit.html", **params)

        except DeadlineExceededError, msg:
            logging.error(msg)
            self.error(403)
            self.render("deadline_exceeded.html", user=user)
    def change_categories(self, params):
        res = ""

        # Grab the old game model
        old_game_code = util.get_code(params["old_game"])
        if old_game_code == params["new_game_code"]:
            old_game_model = params["new_game_model"]
        else:
            old_game_model = self.get_game_model(old_game_code)
        if old_game_model is None:
            return "Did not find game [" + params["old_game"] + "]"

        if params["new_game_model"] is None:
            # New game does not exist, so create it
            params["new_game_model"] = games.Games(
                game=params["new_game"],
                info=json.dumps([]),
                num_pbs=0,
                parent=games.key(),
                key_name=params["new_game_code"],
            )
            res += "Created new game model for game [" + params["new_game"] + "]<br>"

        if not params["new_category_found"]:
            # Add the new category to the new game model
            gameinfolist = json.loads(params["new_game_model"].info)
            d = dict(
                category=params["new_category"],
                bk_runner=None,
                bk_seconds=None,
                bk_video=None,
                bk_datestr=None,
                bk_updater=None,
            )
            gameinfolist.append(d)
            params["new_game_model"].info = json.dumps(gameinfolist)
            res += "Added new category [" + params["new_category"] + "]<br>"

        # Grab the gameinfo for the old game
        oldgameinfolist = json.loads(old_game_model.info)
        oldgameinfo = None
        for g in oldgameinfolist:
            if util.get_code(params["old_category"]) == util.get_code(g["category"]):
                oldgameinfo = g
                break
        if oldgameinfo is None:
            return "Did not find old category [" + params["old_category"] + "]"

        # Grab the gameinfo for the new game
        newgameinfolist = json.loads(params["new_game_model"].info)
        newgameinfo = None
        for g in newgameinfolist:
            if util.get_code(params["new_category"]) == util.get_code(g["category"]):
                newgameinfo = g
                break
        if newgameinfo is None:
            return "Did not find new category [" + params["new_category"] + "]"

        # Update best known time if necessary
        if oldgameinfo.get("bk_seconds") is not None and (
            newgameinfo.get("bk_seconds") is None or oldgameinfo.get("bk_seconds") < newgameinfo.get("bk_seconds")
        ):
            newgameinfo["bk_seconds"] = oldgameinfo.get("bk_seconds")
            newgameinfo["bk_runner"] = oldgameinfo.get("bk_runner")
            newgameinfo["bk_datestr"] = oldgameinfo.get("bk_datestr")
            newgameinfo["bk_video"] = oldgameinfo.get("bk_video")
            newgameinfo["bk_updater"] = oldgameinfo.get("bk_updater")
            params["new_game_model"].info = json.dumps(newgameinfolist)
            res += "Updated bkt<br>"

        # Update num_pbs for old game, new game
        res += "Previous num_pbs for old game, category = " + str(old_game_model.num_pbs) + "<br>"
        res += "Previous num_pbs for new game, category = " + str(params["new_game_model"].num_pbs) + "<br>"
        q = db.Query(runs.Runs, projection=["username"], distinct=True)
        q.ancestor(runs.key())
        q.filter("game =", params["old_game"])
        q.filter("category =", params["old_category"])
        for run in q.run(limit=1000):
            old_game_model.num_pbs -= 1
            q2 = db.Query(runs.Runs)
            q2.ancestor(runs.key())
            q2.filter("game =", params["new_game"])
            q2.filter("category =", params["new_category"])
            q2.filter("username ="******"new_game_model"].num_pbs += 1
            else:
                # Need to decrement runner's num_pbs
                runner = self.get_runner(util.get_code(run.username))
                runner.num_pbs -= 1
                runner.put()
                res += (
                    "Updated "
                    + run.username
                    + " num_pbs from "
                    + str(runner.num_pbs + 1)
                    + " to "
                    + str(runner.num_pbs)
                    + "<br>"
                )

        res += "Updated num_pbs for old game, category = " + str(old_game_model.num_pbs) + "<br>"
        res += "Updated num_pbs for new game, category = " + str(params["new_game_model"].num_pbs) + "<br>"

        # Update old, new game models in database
        old_game_model.put()
        if old_game_code != params["new_game_code"]:
            params["new_game_model"].put()

        # Change the runs
        res += "<br>Changed runs:<br>"
        q = db.Query(runs.Runs)
        q.ancestor(runs.key())
        q.filter("game =", params["old_game"])
        q.filter("category =", params["old_category"])
        for run in q.run(limit=10000):
            # Update the run
            run.game = params["new_game"]
            run.category = params["new_category"]
            run.put()
            res += "Runner=" + run.username + " time=" + util.seconds_to_timestr(run.seconds) + "<br>"

        if not memcache.flush_all():
            res += "Failed to flush memcache<br>"

        # All dun
        return res