Пример #1
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 ) )
Пример #2
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,
                                      category_code = util.get_code( 
                            run.category ),
                                      time = util.
                                      seconds_to_timestr( run.seconds ),
                                      date = run.date,
                                      datetime_created = run.datetime_created,
                                      video = run.video,
                                      version = run.version,
                                      notes = run.notes ) )

            if memcache.set( key, runlist ):
                logging.debug( "Set " + key + " in memcache" )
            else:
                logging.warning( "Failed to set " + key + " in memcache" )
        elif runlist is not None:
            logging.debug( "Got " + key + " from memcache" )
        return runlist
Пример #3
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
Пример #4
0
    def get_runlist_for_runner( self, username, page_num ):
        key = self.get_runlist_for_runner_memkey( username )
        cached_runlists = memcache.get( key )
        if cached_runlists is None:
            cached_runlists = dict( )
        res = cached_runlists.get( page_num )
        if res is None:
            # Not in memcache, so construct the runlist and store in memcache.
            res = dict( page_num=page_num,
                        has_next=True )
            runlist = [ ]
            try:
                q = runs.Runs.all( )
                q.ancestor( runs.key() )
                q.filter( 'username ='******'-date' )
                q.order( '-datetime_created' )
                c = memcache.get( self.get_runlist_for_runner_cursor_memkey(
                    username, page_num ) )
                if c:
                    try:
                        q.with_cursor( start_cursor=c )
                    except BadRequestError:
                        res['page_num'] = 1
                else:
                    res['page_num'] = 1
                for run in q.run( limit = self.RUNLIST_PAGE_LIMIT ):
                    runlist.append( dict(
                        run_id = str( run.key().id() ),
                        game = run.game,
                        game_code = util.get_code( run.game ),
                        category = run.category,
                        category_code = util.get_code( run.category ),
                        time = util.
                        seconds_to_timestr( run.seconds ),
                        date = run.date,
                        datetime_created = run.datetime_created,
                        video = run.video,
                        version = run.version,
                        notes = run.notes ) )
                c = q.cursor( )
                cursor_key = self.get_runlist_for_runner_cursor_memkey(
                    username, res['page_num'] + 1 )
                if memcache.set( cursor_key, c ):
                    logging.debug( "Set " + cursor_key + " in memcache" )
                else:
                    logging.warning( "Failed to set new " + cursor_key
                                     + " in memcache" )
                if len( runlist ) < self.RUNLIST_PAGE_LIMIT:
                    res['has_next'] = False
            except apiproxy_errors.OverQuotaError, msg:
                logging.error( msg )
                return self.OVER_QUOTA_ERROR

            res['runlist'] = runlist
            cached_runlists[ res['page_num'] ] = res
            if memcache.set( key, cached_runlists ):
                logging.debug( "Set " + key + " in memcache" )
            else:
                logging.warning( "Failed to set " + key + " in memcache" )
Пример #5
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 )
Пример #6
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" )
Пример #7
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
Пример #8
0
    def get_runnerlist( self, no_refresh=False ):
        key = self.get_runnerlist_memkey( )
        runnerlist = memcache.get( key )
        if runnerlist is None and not no_refresh:
            # Build the runnerlist, which is a list of dictionaries where each
            # dict gives the username and number of pbs for that user.
            # The list is sorted by numbers of pbs for the user.
            runnerlist = [ ]
            try:
                q = db.Query( runners.Runners, 
                              projection=('username', 'gravatar', 'num_pbs') )
                q.ancestor( runners.key() )
                q.order( '-num_pbs' )
                q.order( 'username' )
                for runner in q.run( limit=100000 ):
                    runnerlist.append( 
                        dict( username = runner.username, 
                              username_code = util.get_code( runner.username ),
                              num_pbs = runner.num_pbs,
                              gravatar_url = util.get_gravatar_url( 
                                  runner.gravatar ) ) )
            except apiproxy_errors.OverQuotaError, msg:
                logging.error( msg )
                return self.OVER_QUOTA_ERROR

            if memcache.set( key, runnerlist ):
                logging.debug( "Set runnerlist in memcache" )
            else:
                logging.warning( "Failed to set new runnerlist in memcache" )
Пример #9
0
    def get_gamelist( self, no_refresh=False, get_num_pbs=True ):
        key = self.get_gamelist_memkey( get_num_pbs )
        gamelist = memcache.get( key )
        if gamelist is None and not no_refresh:
            # Build the gamelist, which is a list of dictionaries where each
            # dict gives the game, game_code and number of pbs for that game.
            # The list is sorted by numbers of pbs for the game
            gamelist = [ ]
            projection = [ 'game' ]
            if get_num_pbs:
                projection.append( 'num_pbs' )
            try:
                q = db.Query( games.Games, projection=projection )
                q.ancestor( games.key() )
                if get_num_pbs:
                    q.order( '-num_pbs' )
                q.order( 'game' )
                for game_model in q.run( limit=10000 ):
                    if get_num_pbs and game_model.num_pbs <= 0:
                        break
                    if get_num_pbs:
                        d = dict( game = game_model.game,
                                  game_code = util.get_code( game_model.game ),
                                  num_pbs = game_model.num_pbs )
                        gamelist.append( d )
                    else:
                        gamelist.append( str( game_model.game ) )
            except apiproxy_errors.OverQuotaError, msg:
                logging.error( msg )
                return self.OVER_QUOTA_ERROR

            if memcache.set( key, gamelist ):
                logging.debug( "Set " + key + " in memcache" )
            else:
                logging.warning( "Failed to set new " + key + " in memcache" )
Пример #10
0
    def post( self, username_code ):
        user = self.get_user( )

        # Make sure this is the correct user
        if user is None:
            self.error( 404 )
            self.render( "404.html", user=user )
            return
        elif user == self.OVER_QUOTA_ERROR:
            self.error( 403 )
            self.render( "403.html" )
            return
        elif util.get_code( user.username ) != username_code:
            self.error( 404 )
            self.render( "404.html", user=user )
            return

        # Get the visible columns
        visible_columns = util.get_default_visible_columns( )
        for key in visible_columns:
            checked = self.request.get( key + '_visible', default_value="no" )
            if checked == "yes":
                visible_columns[ key ] = True
            else:
                visible_columns[ key ] = False
        
        # Store
        user.visible_columns = json.dumps( visible_columns )
        user.put( )

        # Update memcache
        self.update_cache_runner( username_code, user )

        # That's all
        self.redirect( "/runner/" + username_code )
Пример #11
0
    def update_runlist_for_runner_put( self, params ):
        user = params[ 'user' ]
        game = params[ 'game' ]
        game_code = params[ 'game_code' ]
        category = params[ 'category' ]
        time = params[ 'time' ]
        video = params[ 'video' ]
        version = params[ 'version' ]
        notes = params[ 'notes' ]
        date = params[ 'date' ]
        datetime_created = params[ 'datetime_created' ]
        run_id = params[ 'run_id' ]

        # Update runlist for runner in memcache
        runlist = self.get_runlist_for_runner( user.username, 
                                               no_refresh=True )
        if runlist is not None:
            runlist.insert( 0, dict( run_id = run_id,
                                     game = game, 
                                     game_code = game_code,
                                     category = category, 
                                     category_code = util.get_code( category ),
                                     time = time,
                                     date = date, 
                                     datetime_created = datetime_created,
                                     video = video,
                                     version = version,
                                     notes = notes ) )
            runlist.sort( key=lambda x: util.get_valid_date( x['date'] ),
                          reverse=True )
            self.update_cache_runlist_for_runner( user.username, runlist )
Пример #12
0
 def get_gamelist(self, no_refresh=False):
     key = self.get_gamelist_memkey()
     gamelist = memcache.get(key)
     if gamelist is None and not no_refresh:
         # Build the gamelist, which is a list of dictionaries where each
         # dict gives the game, game_code and number of pbs for that game.
         # The list is sorted by numbers of pbs for the game
         gamelist = []
         q = db.Query(games.Games, projection=('game', 'num_pbs'))
         q.ancestor(games.key())
         q.order('-num_pbs')
         q.order('game')
         for game_model in q.run(limit=10000):
             if game_model.num_pbs <= 0:
                 break
             gamelist.append(
                 dict(game=game_model.game,
                      game_code=util.get_code(game_model.game),
                      num_pbs=game_model.num_pbs))
         if memcache.set(key, gamelist):
             logging.debug("Set gamelist in memcache")
         else:
             logging.warning("Failed to set new gamelist in memcache")
     elif gamelist is not None:
         logging.debug("Got gamelist from memcache")
     return gamelist
Пример #13
0
 def get_gamelist(self, no_refresh=False):
     key = self.get_gamelist_memkey()
     gamelist = memcache.get(key)
     if gamelist is None and not no_refresh:
         # Build the gamelist, which is a list of dictionaries where each
         # dict gives the game, game_code and number of pbs for that game.
         # The list is sorted by numbers of pbs for the game
         gamelist = []
         q = db.Query(games.Games, projection=("game", "num_pbs"))
         q.ancestor(games.key())
         q.order("-num_pbs")
         q.order("game")
         for game_model in q.run(limit=10000):
             if game_model.num_pbs <= 0:
                 break
             gamelist.append(
                 dict(game=game_model.game, game_code=util.get_code(game_model.game), num_pbs=game_model.num_pbs)
             )
         if memcache.set(key, gamelist):
             logging.debug("Set gamelist in memcache")
         else:
             logging.warning("Failed to set new gamelist in memcache")
     elif gamelist is not None:
         logging.debug("Got gamelist from memcache")
     return gamelist
Пример #14
0
    def post(self, username_code):
        user = self.get_user()

        # Make sure this is the correct user
        if user is None:
            self.error(404)
            self.render("404.html", user=user)
            return
        elif user == self.OVER_QUOTA_ERROR:
            self.error(403)
            self.render("403.html")
            return
        elif util.get_code(user.username) != username_code:
            self.error(404)
            self.render("404.html", user=user)
            return

        # Get the visible columns
        visible_columns = util.get_default_visible_columns()
        for key in visible_columns:
            checked = self.request.get(key + '_visible', default_value="no")
            if checked == "yes":
                visible_columns[key] = True
            else:
                visible_columns[key] = False

        # Store
        user.visible_columns = json.dumps(visible_columns)
        user.put()

        # Update memcache
        self.update_cache_runner(username_code, user)

        # That's all
        self.redirect("/runner/" + username_code)
Пример #15
0
    def get(self, username_code):
        user = self.get_user()

        # Make sure this is the correct user
        if user is None:
            self.error(404)
            self.render("404.html", user=user)
            return
        elif user == self.OVER_QUOTA_ERROR:
            self.error(403)
            self.render("403.html")
            return
        elif util.get_code(user.username) != username_code:
            self.error(404)
            self.render("404.html", user=user)
            return

        # Get the stored visible columns, or get the default ones
        if user.visible_columns:
            visible_columns = json.loads(user.visible_columns)
        else:
            visible_columns = util.get_default_visible_columns()

        self.render("edit_table.html",
                    user=user,
                    username_code=username_code,
                    visible_columns=visible_columns)
Пример #16
0
 def get_runnerlist(self, no_refresh=False):
     key = self.get_runnerlist_memkey()
     runnerlist = memcache.get(key)
     if runnerlist is None and not no_refresh:
         # Build the runnerlist, which is a list of dictionaries where each
         # dict gives the username and number of pbs for that user.
         # The list is sorted by numbers of pbs for the user.
         runnerlist = []
         q = db.Query(runners.Runners,
                      projection=('username', 'gravatar', 'num_pbs'))
         q.ancestor(runners.key())
         q.order('-num_pbs')
         q.order('username')
         for runner in q.run(limit=100000):
             runnerlist.append(
                 dict(username=runner.username,
                      username_code=util.get_code(runner.username),
                      num_pbs=runner.num_pbs,
                      gravatar_url=util.get_gravatar_url(runner.gravatar)))
         if memcache.set(key, runnerlist):
             logging.debug("Set runnerlist in memcache")
         else:
             logging.warning("Failed to set new runnerlist in memcache")
     elif runnerlist is not None:
         logging.debug("Got runnerlist from memcache")
     return runnerlist
Пример #17
0
 def get_runnerlist(self, no_refresh=False):
     key = self.get_runnerlist_memkey()
     runnerlist = memcache.get(key)
     if runnerlist is None and not no_refresh:
         # Build the runnerlist, which is a list of dictionaries where each
         # dict gives the username and number of pbs for that user.
         # The list is sorted by numbers of pbs for the user.
         runnerlist = []
         q = db.Query(runners.Runners, projection=("username", "gravatar", "num_pbs"))
         q.ancestor(runners.key())
         q.order("-num_pbs")
         q.order("username")
         for runner in q.run(limit=100000):
             runnerlist.append(
                 dict(
                     username=runner.username,
                     username_code=util.get_code(runner.username),
                     num_pbs=runner.num_pbs,
                     gravatar_url=util.get_gravatar_url(runner.gravatar),
                 )
             )
         if memcache.set(key, runnerlist):
             logging.debug("Set runnerlist in memcache")
         else:
             logging.warning("Failed to set new runnerlist in memcache")
     elif runnerlist is not None:
         logging.debug("Got runnerlist from memcache")
     return runnerlist
Пример #18
0
    def get( self, username_code ):
        user = self.get_user( )

        # Make sure this is the correct user
        if user is None:
            self.error( 404 )
            self.render( "404.html", user=user )
            return
        elif user == self.OVER_QUOTA_ERROR:
            self.error( 403 )
            self.render( "403.html" )
            return
        elif util.get_code( user.username ) != username_code:
            self.error( 404 )
            self.render( "404.html", user=user )
            return

        # Get the stored visible columns, or get the default ones 
        if user.visible_columns:
            visible_columns = json.loads( user.visible_columns )
        else:
            visible_columns = util.get_default_visible_columns( )

        self.render( "edit_table.html", user=user, username_code=username_code,
                     visible_columns=visible_columns )
Пример #19
0
    def get( self, game_code ):
        user = self.get_user( )

        # Make sure this game exists
        game_model = self.get_game_model( game_code )
        if not game_model:
            self.error( 404 )
            self.render( "404.html", user=user )
            return

        # Find out if this user has run this game
        if user is not None:
            user_has_run = self.get_user_has_run( user.username, 
                                                  game_model.game )
        else:
            user_has_run = False
            
        gamepage = self.get_gamepage( game_model.game )
        
        # Add gravatar images to the gamepage
        for d in gamepage:
            for run in d['infolist']:
                runner = self.get_runner( util.get_code( run['username'] ) )
                if runner is not None:
                    run['gravatar_url'] = util.get_gravatar_url( 
                        runner.gravatar, size=20 )

        if self.format == 'html':
            self.render( "gamepage.html", user=user, game=game_model.game, 
                         game_code=game_code, gamepage=gamepage,
                         user_has_run=user_has_run )
        elif self.format == 'json':
            self.render_json( gamepage )
Пример #20
0
    def cleanup_games(self):
        # Grab all of the categories, indexed by game
        categories = self.get_categories()
        categories_modified = False

        games_to_delete = []
        for game, categorylist in categories.iteritems():
            # Grab the game model
            game_code = util.get_code(game)
            game_model = self.get_game_model(game_code)
            gameinfolist = json.loads(game_model.info)
            game_model_modified = False
            glist = [(i, gameinfo) for i, gameinfo in enumerate(gameinfolist)]
            for i, gameinfo in reversed(glist):
                # Leave it if the category is marked as a base category
                if (
                    gameinfo.get("is_base_category")
                    and game != "Luigi's Mansion"
                    and game != "Super Mario Bros.: The Lost Levels"
                    and game != "The Legend of Zelda: A Link to the Past"
                ):
                    continue
                # Check if there is a run for this game and category
                q = db.Query(runs.Runs, keys_only=True)
                q.ancestor(runs.key())
                q.filter("game =", game)
                q.filter("category =", gameinfo["category"])
                num_runs = q.count(limit=1)
                if num_runs == 0:
                    # Remove this category
                    del gameinfolist[i]
                    logging.info("Removed " + gameinfo["category"] + " from " + game)
                    game_model_modified = True
                    # Remove this category in memcache too
                    for j, category in enumerate(categorylist):
                        if category == gameinfo["category"]:
                            del categorylist[j]
                            categories_modified = True
                            break
                    else:
                        logging.error("ERROR: Could not find in categories")
            # Remove the game if no more categories exist
            if len(gameinfolist) == 0:
                game_model.delete()
                games_to_delete.append(game)
                logging.info(game + " deleted")
                self.update_cache_game_model(game_code, None)
            # Update database and memcache if necessary
            elif game_model_modified:
                game_model.info = json.dumps(gameinfolist)
                game_model.put()
                self.update_cache_game_model(game_code, game_model)

        # Finally, update categories in memcache if necessary
        if categories_modified:
            for game in games_to_delete:
                del categories[game]
            self.update_cache_categories(categories)
Пример #21
0
    def cleanup_games( self ):
        # Grab all of the categories, indexed by game
        categories = self.get_categories( )
        categories_modified = False

        games_to_delete = [ ]
        for game, categorylist in categories.iteritems( ):
            # Grab the game model
            game_code = util.get_code( game )
            game_model = self.get_game_model( game_code )
            gameinfolist = json.loads( game_model.info )
            game_model_modified = False
            glist = [ ( i, gameinfo ) 
                      for i, gameinfo in enumerate( gameinfolist ) ]
            for i, gameinfo in reversed( glist ):
                # Leave it if the category is marked as a base category
                if( gameinfo.get( 'is_base_category' )
                    and game != "Luigi's Mansion"
                    and game != "Super Mario Bros.: The Lost Levels"
                    and game != "The Legend of Zelda: A Link to the Past" ):
                    continue
                # Check if there is a run for this game and category
                q = db.Query( runs.Runs, keys_only=True )
                q.ancestor( runs.key() )
                q.filter( 'game =', game )
                q.filter( 'category =', gameinfo['category'] )
                num_runs = q.count( limit=1 )
                if num_runs == 0:
                    # Remove this category
                    del gameinfolist[ i ]
                    logging.info( "Removed " + gameinfo['category'] 
                                   + " from " + game )
                    game_model_modified = True
                    # Remove this category in memcache too
                    for j, category in enumerate( categorylist ):
                        if category == gameinfo['category']:
                            del categorylist[ j ]
                            categories_modified = True
                            break
                    else:
                        logging.error( "ERROR: Could not find in categories" )
            # Remove the game if no more categories exist
            if len( gameinfolist ) == 0:
                game_model.delete( )
                games_to_delete.append( game )
                logging.info( game + " deleted" )
                self.update_cache_game_model( game_code, None )
            # Update database and memcache if necessary
            elif game_model_modified:
                game_model.info = json.dumps( gameinfolist )
                game_model.put( )
                self.update_cache_game_model( game_code, game_model )
        
        # Finally, update categories in memcache if necessary
        if categories_modified:
            for game in games_to_delete:
                del categories[ game ]
            self.update_cache_categories( categories )
Пример #22
0
    def get_gamelist( self, page_num ):
        key = self.get_gamelist_memkey( )
        data = memcache.get( key )
        if data is None:
            data = dict( )
        res = data.get( page_num )
        if res is None:
            # Build the gamelist, which is a list of dictionaries where each
            # dict gives the game, game_code and number of pbs for that game.
            # The list is sorted by numbers of pbs for the game
            res = dict( page_num=page_num )
            gamelist = [ ]
            projection = [ 'game', 'num_pbs' ]
            try:
                q = db.Query( games.Games, projection=projection )
                q.ancestor( games.key() )
                q.order( '-num_pbs' )
                q.order( 'game' )
                c = memcache.get( self.get_gamelist_cursor_memkey( page_num ) )
                if c:
                    try:
                        q.with_cursor( start_cursor=c )
                    except BadRequestError:
                        res['page_num'] = 1
                else:
                    # Send the user back to the first page
                    res['page_num'] = 1
                for game_model in q.run( limit=self.PAGE_LIMIT ):
                    if game_model.num_pbs <= 0:
                        break
                    d = dict( game = game_model.game,
                              game_code = util.get_code( game_model.game ),
                              num_pbs = game_model.num_pbs )
                    gamelist.append( d )
                c = q.cursor( )
                cursor_key = self.get_gamelist_cursor_memkey(
                    res['page_num'] + 1 )
                if memcache.set( cursor_key, c ):
                    logging.debug( "Set " + cursor_key + " in memcache" )
                else:
                    logging.warning( "Failed to set new " + cursor_key
                                     + " in memcache" )
                if len( gamelist ) >= self.PAGE_LIMIT:
                    res['has_next'] = True
                else:
                    res['has_next'] = False
                res['gamelist'] = gamelist
            except apiproxy_errors.OverQuotaError, msg:
                logging.error( msg )
                return self.OVER_QUOTA_ERROR

            data[ res['page_num'] ] = res
            if memcache.set( key, data ):
                logging.debug( "Set " + key + " in memcache" )
            else:
                logging.warning( "Failed to set new " + key + " in memcache" )
Пример #23
0
    def get(self, run_id):
        user = self.get_user()
        if not user:
            self.redirect("/")
            return

        # Get the run
        run = self.get_run_by_id(run_id)
        if (not run or (not user.is_mod and run.username != user.username)):
            self.error(404)
            self.render("404.html", user=user)
            return

        self.render("deleterun.html",
                    user=user,
                    run=run,
                    game_code=util.get_code(run.game),
                    username_code=util.get_code(user.username),
                    time=util.seconds_to_timestr(run.seconds))
Пример #24
0
 def is_valid_login( self, username, password ):
     username_code = util.get_code( username )
     
     # Find the user in the database
     try:
         user = runners.Runners.get_by_key_name( username_code, 
                                                 parent=runners.key() )
     except apiproxy_errors.OverQuotaError, msg:
         logging.error( msg )
         return False, dict( user_error="Over quota error" )
Пример #25
0
    def is_valid_login(self, username, password):
        username_code = util.get_code(username)

        # Find the user in the database
        try:
            user = runners.Runners.get_by_key_name(username_code,
                                                   parent=runners.key())
        except apiproxy_errors.OverQuotaError, msg:
            logging.error(msg)
            return False, dict(user_error="Over quota error")
Пример #26
0
    def get( self ):
        # Grab all of the categories, indexed by game
        categories = self.get_categories( )
        categories_modified = False

        games_to_delete = [ ]
        for game, categorylist in categories.iteritems( ):
            # Grab the game model
            game_code = util.get_code( game )
            game_model = self.get_game_model( game_code )
            gameinfolist = json.loads( game_model.info )
            game_model_modified = False
            glist = [ ( i, gameinfo ) 
                      for i, gameinfo in enumerate( gameinfolist ) ]
            for i, gameinfo in reversed( glist ):
                # Leave it if the category is marked as a base category
                if gameinfo.get( 'is_base_category' ):
                    continue
                # Check if there is a run for this game and category
                q = db.Query( runs.Runs, keys_only=True )
                q.ancestor( runs.key() )
                q.filter( 'game =', game )
                q.filter( 'category =', gameinfo['category'] )
                num_runs = q.count( limit=1 )
                if num_runs == 0:
                    # Remove this category
                    del gameinfolist[ i ]
                    logging.debug( "Removed " + gameinfo['category'] 
                                   + " from " + game )
                    game_model_modified = True
                    # Remove this category in memcache too
                    for j, category in enumerate( categorylist ):
                        if category == gameinfo['category']:
                            del categorylist[ j ]
                            categories_modified = True
                            break
                    else:
                        logging.debug( "ERROR: Could not find in categories" )
            # Remove the game if no more categories exist
            if len( gameinfolist ) == 0:
                game_model.delete( )
                games_to_delete.append( game )
                logging.debug( game + " deleted" )
                self.update_cache_game_model( game_code, None )
            # Update database and memcache if necessary
            elif game_model_modified:
                game_model.info = json.dumps( gameinfolist )
                game_model.put( )
                self.update_cache_game_model( game_code, game_model )
        
        # Finally, update categories in memcache if necessary
        if categories_modified:
            for game in games_to_delete:
                del categories[ game ]
            self.update_cache_categories( categories )
Пример #27
0
    def get_runnerlist( self, page_num ):
        key = self.get_runnerlist_memkey( )
        data = memcache.get( key )
        if data is None:
            data = dict( )
        res = data.get( page_num )
        if res is None:
            # Build the runnerlist, which is a list of dictionaries where each
            # dict gives the username and number of pbs for that user.
            # The list is sorted by numbers of pbs for the user.
            res = dict( page_num=page_num,
                        runnerlist=[ ],
                        has_next=True )
            try:
                q = db.Query( runners.Runners, 
                              projection=['username', 'gravatar', 'num_pbs'] )
                q.ancestor( runners.key() )
                q.order( '-num_pbs' )
                q.order( 'username' )
                c = memcache.get( self.get_runnerlist_cursor_memkey(
                    page_num ) )
                if c:
                    try:
                        q.with_cursor( start_cursor=c )
                    except BadRequestError:
                        res['page_num'] = 1
                else:
                    res['page_num'] = 1
                for runner in q.run( limit=self.PAGE_LIMIT ):
                    res['runnerlist'].append( 
                        dict( username = runner.username,
                              username_code = util.get_code( runner.username ),
                              num_pbs = runner.num_pbs,
                              gravatar_url = util.get_gravatar_url( 
                                  runner.gravatar ) ) )
                c = q.cursor( )
                cursor_key = self.get_runnerlist_cursor_memkey(
                    res['page_num'] + 1 )
                if memcache.set( cursor_key, c ):
                    logging.debug( "Set " + cursor_key + " in memcache" )
                else:
                    logging.warning( "Failed to set new " + cursor_key
                                     + " in memcache" )
                if len( res['runnerlist'] ) < self.PAGE_LIMIT:
                    res['has_next'] = False
            except apiproxy_errors.OverQuotaError, msg:
                logging.error( msg )
                return self.OVER_QUOTA_ERROR

            data[ res['page_num'] ] = res
            if memcache.set( key, data ):
                logging.debug( "Set " + key + " in memcache" )
            else:
                logging.warning( "Failed to set new " + key + " in memcache" )
Пример #28
0
    def get(self, game_code):
        user = self.get_user()

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

        # Make sure this game exists
        game_model = self.get_game_model(game_code)
        if not game_model:
            self.error(404)
            self.render("404.html", user=user)
            return

        # Find out if this user has run this game
        if user is not None:
            user_has_run = self.get_user_has_run(user.username,
                                                 game_model.game)
        else:
            user_has_run = False

        gamepage = self.get_gamepage(game_model.game)

        # Add gravatar images to the gamepage
        for d in gamepage:
            for run in d['infolist']:
                runner = self.get_runner(util.get_code(run['username']))
                if runner is not None:
                    run['gravatar_url'] = util.get_gravatar_url(
                        runner.gravatar, size=20)

        if self.format == 'html':
            self.render("gamepage.html",
                        user=user,
                        game=game_model.game,
                        game_code=game_code,
                        gamepage=gamepage,
                        user_has_run=user_has_run)
        elif self.format == 'json':
            self.render_json(gamepage)
Пример #29
0
    def is_valid_login(self, username, password):
        username_code = util.get_code(username)

        # Find the user in the database
        user = runners.Runners.get_by_key_name(username_code, parent=runners.key())
        if not user:
            return False, dict(user_error="Username not found")

        # Check for valid password
        if util.valid_pw(username_code, password, user.password):
            return True, dict()
        else:
            return False, dict(pass_error="Invalid password")
Пример #30
0
    def get_pblist(self, username, no_refresh=False):
        key = self.get_pblist_memkey(username)
        pblist = memcache.get(key)
        if pblist is None and not no_refresh:
            # Not in memcache, so construct the pblist and store in memcache.
            # pblist is a list of dictionaries with 3 indices, 'game',
            # 'game_code' and 'infolist'.  The infolist is another list of
            # dictionaries containing all the info for each pb of the game.
            pblist = []
            try:
                # Use a projection query to get all of the unique game, category
                # pairs
                q = db.Query(runs.Runs,
                             projection=('game', 'category'),
                             distinct=True)
                q.ancestor(runs.key())
                q.filter('username ='******'game')
                q.order('category')
                cur_game = None
                for run in q.run(limit=1000):
                    if run.game != cur_game:
                        # New game
                        pb = dict(game=run.game,
                                  game_code=util.get_code(run.game),
                                  num_runs=0,
                                  infolist=[])
                        pblist.append(pb)
                        cur_game = run.game

                    # Add runinfo to pblist
                    info = self.get_runinfo(username, run.game, run.category)
                    if info == self.OVER_QUOTA_ERROR:
                        return self.OVER_QUOTA_ERROR
                    pb['infolist'].append(info)
                    pb['num_runs'] += info['num_runs']
            except apiproxy_errors.OverQuotaError, msg:
                logging.error(msg)
                return self.OVER_QUOTA_ERROR

            # Sort the categories for a game by num_runs
            for pb in pblist:
                pb['infolist'].sort(key=itemgetter('num_runs'), reverse=True)

            # Sort the games by number of runs
            pblist.sort(key=itemgetter('num_runs'), reverse=True)

            if memcache.set(key, pblist):
                logging.debug("Set " + key + " in memcache")
            else:
                logging.warning("Failed to set " + key + " in memcache")
Пример #31
0
    def is_valid_login(self, username, password):
        username_code = util.get_code(username)

        # Find the user in the database
        user = runners.Runners.get_by_key_name(username_code,
                                               parent=runners.key())
        if not user:
            return False, dict(user_error="Username not found")

        # Check for valid password
        if util.valid_pw(username_code, password, user.password):
            return True, dict()
        else:
            return False, dict(pass_error="Invalid password")
Пример #32
0
    def get_pblist( self, username, no_refresh=False ):
        key = self.get_pblist_memkey( username )
        pblist = memcache.get( key )
        if pblist is None and not no_refresh:
            # Not in memcache, so construct the pblist and store in memcache.
            # pblist is a list of dictionaries with 3 indices, 'game', 
            # 'game_code' and 'infolist'.  The infolist is another list of 
            # dictionaries containing all the info for each pb of the game.
            pblist = [ ]
            try:
                # Use a projection query to get all of the unique game, category
                # pairs
                q = db.Query( runs.Runs, projection=('game', 'category'), 
                              distinct=True )
                q.ancestor( runs.key() )
                q.filter( 'username ='******'game' )
                q.order( 'category' )
                cur_game = None
                for run in q.run( limit = 1000 ):
                    if run.game != cur_game:
                        # New game
                        pb = dict( game = run.game, 
                                   game_code = util.get_code( run.game ),
                                   num_runs = 0,
                                   infolist = [ ] )
                        pblist.append( pb )
                        cur_game = run.game                

                    # Add runinfo to pblist
                    info = self.get_runinfo( username, run.game, run.category )
                    if info == self.OVER_QUOTA_ERROR:
                        return self.OVER_QUOTA_ERROR
                    pb['infolist'].append( info )
                    pb['num_runs'] += info['num_runs']
            except apiproxy_errors.OverQuotaError, msg:
                logging.error( msg )
                return self.OVER_QUOTA_ERROR

            # Sort the categories for a game by num_runs
            for pb in pblist:
                pb['infolist'].sort( key=itemgetter('num_runs'), reverse=True )

            # Sort the games by number of runs
            pblist.sort( key=itemgetter('num_runs'), reverse=True )

            if memcache.set( key, pblist ):
                logging.debug( "Set " + key + " in memcache" )
            else:
                logging.warning( "Failed to set " + key + " in memcache" )
Пример #33
0
    def post( self ):
        username = self.request.get( 'username' )
        password = self.request.get( 'password' )
        return_url = self.request.get( 'from' )
        if not return_url:
            return_url = "/"

        ( valid, errors ) = self.is_valid_login( username, password )
        if not valid:
            self.render( "login.html", username=username,
                         return_url=return_url, **errors )
        else:
            # Success!
            self.login( util.get_code( username ) )
            self.redirect( return_url )
Пример #34
0
    def put_new_game( self, game ):
        # Add a new game to the database
        try:
            game_model = games.Games( game = game,
                                      info = json.dumps( [ ] ),
                                      parent = games.key( ),
                                      key_name = util.get_code( game ) )
        except db.BadValueError:
            return False
        
        game_model.put( )
        logging.warning( "Put new game " + game + " in database." )

        # Update memcache
        self.update_gamelist_snp_put( game )

        return True
Пример #35
0
    def post(self):
        username = self.request.get('username')
        password = self.request.get('password')
        return_url = self.request.get('from')
        if not return_url:
            return_url = "/"

        (valid, errors) = self.is_valid_login(username, password)
        if not valid:
            self.render("login.html",
                        username=username,
                        return_url=return_url,
                        **errors)
        else:
            # Success!
            self.login(util.get_code(username))
            self.redirect(return_url)
Пример #36
0
    def put_new_game(self, game):
        # Add a new game to the database
        try:
            game_model = games.Games(game=game,
                                     info=json.dumps([]),
                                     parent=games.key(),
                                     key_name=util.get_code(game))
        except db.BadValueError:
            return False

        game_model.put()
        logging.warning("Put new game " + game + " in database.")

        # Update memcache
        self.update_gamelist_snp_put(game)

        return True
Пример #37
0
    def get_pblist(self, username, no_refresh=False):
        key = self.get_pblist_memkey(username)
        pblist = memcache.get(key)
        if pblist is None and not no_refresh:
            # Not in memcache, so construct the pblist and store in memcache.
            # pblist is a list of dictionaries with 3 indices, 'game',
            # 'game_code' and 'infolist'.  The infolist is another list of
            # dictionaries containing all the info for each pb of the game.
            pblist = []
            # Use a projection query to get all of the unique game, category
            # pairs
            q = db.Query(runs.Runs, projection=("game", "category"), distinct=True)
            q.ancestor(runs.key())
            q.filter("username ="******"game")
            q.order("category")
            cur_game = None
            for run in q.run(limit=1000):
                if run.game != cur_game:
                    # New game
                    pb = dict(game=run.game, game_code=util.get_code(run.game), num_runs=0, infolist=[])
                    pblist.append(pb)
                    cur_game = run.game

                # Add runinfo to pblist
                info = self.get_runinfo(username, run.game, run.category)
                pb["infolist"].append(info)
                pb["num_runs"] += info["num_runs"]

            # Sort the categories for a game by num_runs
            for pb in pblist:
                pb["infolist"].sort(key=itemgetter("num_runs"), reverse=True)

            # Sort the games by number of runs
            pblist.sort(key=itemgetter("num_runs"), reverse=True)

            if memcache.set(key, pblist):
                logging.debug("Set " + key + " in memcache")
            else:
                logging.warning("Failed to set " + key + " in memcache")
        elif pblist is not None:
            logging.debug("Got " + key + " from memcache")
        return pblist
Пример #38
0
    def verify_mod_login(self, body):
        # First, verify login credentials
        (valid, response) = self.verify_login(body)
        if not valid:
            return valid, response

        # Make sure the user is a mod
        username = body.get('username')
        user = self.get_runner(util.get_code(username))
        if user == self.OVER_QUOTA_ERROR:
            return False, self.get_fail_response("PB Tracker is currently " +
                                                 "experiencing an over " +
                                                 "quota limit down time " +
                                                 "period.")
        if not user.is_mod:
            body_type = body.get('type')
            return False, self.get_fail_response("You must be a mod to use [" +
                                                 body_type + "].")

        return True, self.get_success_response()
Пример #39
0
    def verify_mod_login( self, body ):
        # First, verify login credentials
        ( valid, response ) = self.verify_login( body )
        if not valid:
            return valid, response

        # Make sure the user is a mod
        username = body.get( 'username' )
        user = self.get_runner( util.get_code( username ) )
        if user == self.OVER_QUOTA_ERROR:
            return False, self.get_fail_response( "PB Tracker is currently "
                                                  + "experiencing an over "
                                                  + "quota limit down time "
                                                  + "period." )
        if not user.is_mod:
            body_type = body.get( 'type' )
            return False, self.get_fail_response( "You must be a mod to use ["
                                                  + body_type + "]." )

        return True, self.get_success_response( )
Пример #40
0
 def login(self):
     # 获取登陆二维码
     self.get_qrlogin_pic()
     print('请扫描二维码登陆')
     image_open = Image.open(os.getcwd() + '/login_qr.png')
     image_open.show()
     while (True):
         check = self.check()
         values = check.cookies.values()
         if len(values) > 0:
             # 字节转化为字符串
             s = str(check.content)
             split = s.split(',')
             # 跳转返回的url(获取响应头的cookies)
             self.session.get(split[2].replace('\'', ''))
             break
         time.sleep(3)
     # 登陆认证获取cookie
     authorize = self.authorize()
     code = util.get_code(authorize.url)
     self.get_login_cookie(code)
     self.spider_session.save_cookies_to_local(self.spider_session.get_user_id())
Пример #41
0
    def update_runlist_for_runner_put(self, params):
        user = params["user"]
        game = params["game"]
        game_code = params["game_code"]
        category = params["category"]
        time = params["time"]
        video = params["video"]
        version = params["version"]
        notes = params["notes"]
        date = params["date"]
        datetime_created = params["datetime_created"]
        run_id = params["run_id"]

        # Update runlist for runner in memcache
        cached_runlists = self.get_cached_runlists_for_runner(user.username)
        if cached_runlists is not None:
            res = cached_runlists.get(1)
            if res is not None:
                res["runlist"].insert(
                    0,
                    dict(
                        run_id=run_id,
                        game=game,
                        game_code=game_code,
                        category=category,
                        category_code=util.get_code(category),
                        time=time,
                        date=date,
                        datetime_created=datetime_created,
                        video=video,
                        version=version,
                        notes=notes,
                    ),
                )
                res["runlist"].sort(key=lambda x: util.get_valid_date(x["date"]), reverse=True)
                self.update_cache_runlist_for_runner(user.username, cached_runlists)
Пример #42
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 )
Пример #43
0
 def loadFromCSV(self, path):
     code = get_code(path)
     df = pd.read_csv(path)
     self.loadFromDataFrame(code, df)
Пример #44
0
    def post(self, run_id):
        user = self.get_user()
        if not user:
            self.redirect("/")
            return

        # Get the run
        run = runs.Runs.get_by_id(long(run_id), parent=runs.key())
        if (not run or (not user.is_mod and run.username != user.username)):
            self.error(404)
            self.render("404.html", user=user)
            return

        # Grab the owner of the run
        if run.username == user.username:
            runner = user
        else:
            runner = self.get_runner(util.get_code(run.username))

        # Delete the run
        run.delete()

        # Update memcache
        old_run = dict(game=run.game,
                       category=run.category,
                       seconds=run.seconds)
        self.update_cache_run_by_id(run_id, None)

        # Update games, runner
        delta_num_pbs = 0
        num_runs = self.num_runs(runner.username, run.game, run.category, 1)
        if num_runs == 0:
            delta_num_pbs = -1
        self.update_runner(runner, delta_num_pbs)
        self.update_games_delete(
            self.get_game_model(util.get_code(old_run['game'])), delta_num_pbs)

        # Must update runinfo before pblist and gamepage because pblist and
        # gamepage rely on accurate runinfo
        self.update_runinfo_delete(runner, old_run)
        self.update_pblist_delete(runner, old_run)
        self.update_gamepage_delete(runner, old_run)
        self.update_user_has_run_delete(runner, old_run)
        if num_runs <= 0:
            self.update_gamelist_delete(old_run)
            self.update_runnerlist_delete(runner)

        # Update runlist for runner in memcache
        runlist = self.get_runlist_for_runner(runner.username, no_refresh=True)
        if runlist:
            for i, run in enumerate(runlist):
                if run['run_id'] == run_id:
                    del runlist[i]
                    self.update_cache_runlist_for_runner(
                        runner.username, runlist)
                    break

        # Update last run
        last_run = self.get_last_run(runner.username, no_refresh=True)
        if last_run is not None and last_run.key().id() == long(run_id):
            self.update_cache_last_run(runner.username, None)

        # Done with deletion
        self.redirect("/runner/" + util.get_code(runner.username) +
                      "?q=view-all")
Пример #45
0
    def post(self):
        user = self.get_user()
        if not user:
            self.redirect("/")
            return
        elif user == self.OVER_QUOTA_ERROR:
            self.error(403)
            self.render("403.html")
            return

        game = self.request.get('game')

        params = dict(user=user, game=game)

        valid = True

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

        if not valid:
            # Grab all of the games for autocompleting
            params['games'] = self.get_gamelist(get_num_pbs=False)
            if params['games'] == self.OVER_QUOTA_ERROR:
                self.error(403)
                self.render("403.html", user=user)
            else:
                self.render("presubmit.html", **params)
            return

        if game_model is None:
            # Add the game to the database
            valid = self.put_new_game(game)
            if not valid:
                params['game_error'] = ("Failed to process game with name [" +
                                        game + "] (unknown error)")

        if valid:
            self.redirect("/submit/" + game_code + '/')
        else:
            # Grab all of the games for autocompleting
            params['games'] = self.get_gamelist(get_num_pbs=False)
            if params['games'] == self.OVER_QUOTA_ERROR:
                self.error(403)
                self.render("403.html", user=user)
            else:
                self.render("presubmit.html", **params)
            return
Пример #46
0
                                                  old_run['game'],
                                                  old_run['category'], None)
                        return
                except apiproxy_errors.OverQuotaError, msg:
                    logging.error(msg)
                    self.update_cache_runinfo(user.username, old_run['game'],
                                              old_run['category'], None)
                    return
            self.update_cache_runinfo(user.username, old_run['game'],
                                      old_run['category'], runinfo)
        else:
            # No other runs for game, category combo
            self.update_cache_runinfo(
                user.username, old_run['game'], old_run['category'],
                dict(username=user.username,
                     username_code=util.get_code(user.username),
                     category=old_run['category'],
                     category_code=util.get_code(old_run['category']),
                     pb_seconds=None,
                     pb_time=None,
                     pb_date=None,
                     num_runs=0,
                     avg_seconds=0,
                     avg_time='0:00',
                     video=None,
                     version=None))

    def update_pblist_put(self, params):
        user = params['user']
        game = params['game']
        category = params['category']
Пример #47
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
Пример #48
0
 def update_runner(self, runner, delta_num_pbs):
     if delta_num_pbs != 0:
         runner.num_pbs += delta_num_pbs
         runner.put()
         self.update_cache_runner(util.get_code(runner.username), runner)
Пример #49
0
    def post(self):
        user = self.get_user()
        if user == self.OVER_QUOTA_ERROR:
            self.error(403)
            self.render("403.html")
            return
        username = self.request.get('username')
        password = self.request.get('password')
        verify = self.request.get('verify')
        twitter = self.request.get('twitter')
        if twitter[0:1] == '@':
            twitter = twitter[1:]
        youtube = self.request.get('youtube')
        youtube = youtube.split('/')[-1]
        twitch = self.request.get('twitch')
        twitch = twitch.split('/')[-1]
        hitbox = self.request.get('hitbox')
        hitbox = hitbox.split('/')[-1]
        timezone = self.request.get('timezone')
        gravatar = self.request.get('gravatar')
        if user is not None:
            username_code = util.get_code(user.username)
        else:
            username_code = util.get_code(username)
        return_url = self.request.get('from')
        if not return_url:
            return_url = "/"
        elif user is not None and user.is_mod:
            # Mod is editing a user's profile, possibly his or her own
            username_code = return_url.split('/')[-1]
            user = self.get_runner(username_code)
            if user == self.OVER_QUOTA_ERROR:
                self.error(403)
                self.render("403.html")
                return

        params = dict(user=user,
                      username=username,
                      password=password,
                      verify=verify,
                      twitter=twitter,
                      youtube=youtube,
                      twitch=twitch,
                      hitbox=hitbox,
                      gravatar=gravatar,
                      timezone=timezone,
                      return_url=return_url)

        valid = True

        if user is None and not valid_username(username):
            params['user_error'] = ("Username must be between " +
                                    "1 and 20 alphanumeric, dash, or " +
                                    "underscore characters.")
            valid = False
        elif user is None:
            # Check if username already exists
            runner = self.get_runner(username_code)
            if runner == self.OVER_QUOTA_ERROR:
                self.error(403)
                self.render("403.html", user=user)
                return
            if runner is not None:
                params['user_error'] = "That user already exists."
                valid = False

        if not valid_password(password):
            if user is None or len(password) > 0:
                params['pass_error'] = ("Password must be between " +
                                        "3 and 20 characters.")
                valid = False

        if password != verify:
            params['ver_error'] = "Passwords do not match."
            valid = False

        if gravatar != "" and not valid_email(gravatar):
            if (user is None or not user.gravatar
                    or gravatar != '<private email>'):
                params['gravatar_error'] = "That's not a valid email."
                valid = False
        if user is not None and gravatar == '<private email>':
            params['gravatar_url'] = util.get_gravatar_url(user.gravatar, 30)

        if timezone != '' and timezone not in pytz.common_timezones:
            params['timezone_error'] = "Invalid timezone."
            valid = False

        if not valid:
            self.render("signup.html",
                        timezones=pytz.common_timezones,
                        **params)
            return

        if not user:
            # Add a new runner to the database
            runner = runners.Runners(username=username,
                                     password=util.make_pw_hash(
                                         username_code, password),
                                     twitter=twitter,
                                     youtube=youtube,
                                     twitch=twitch,
                                     hitbox=hitbox,
                                     timezone=timezone,
                                     num_pbs=0,
                                     parent=runners.key(),
                                     key_name=username_code)
            if gravatar:
                runner.gravatar = hashlib.md5(gravatar.lower()).hexdigest()

            runner.put()

            # Update runner in memcache
            self.update_cache_runner(username_code, runner)

            # Update runnerlist in memcache.  Note that this puts the runner
            # at the end of the list, rather than in alphabetical order among
            # those runners with 0 pbs.  The runner will be sorted properly
            # if the memcache gets flushed, which is good enough
            runnerlist = self.get_runnerlist(no_refresh=True)
            if runnerlist == self.OVER_QUOTA_ERROR:
                self.update_cache_runnerlist(None)
            elif runnerlist is not None:
                runnerlist.append(
                    dict(username=username,
                         username_code=username_code,
                         num_pbs=0,
                         gravatar_url=util.get_gravatar_url(runner.gravatar)))
                self.update_cache_runnerlist(runnerlist)

            # Update runs for runner in memcache
            self.update_cache_runlist_for_runner(username, [])

            self.login(username_code)

        else:
            # Editing the current user
            if len(password) > 0:
                user.password = util.make_pw_hash(username_code, password)
            user.twitter = twitter
            user.youtube = youtube
            user.twitch = twitch
            user.hitbox = hitbox
            user.timezone = timezone
            if gravatar and gravatar != '<private email>':
                user.gravatar = hashlib.md5(gravatar.lower()).hexdigest()
            elif not gravatar:
                user.gravatar = None

            user.put()

            # Update user in memcache
            self.update_cache_runner(username_code, user)

            # Update runnerlist in memcache if gravatar updated
            if gravatar != '<private email>':
                runnerlist = self.get_runnerlist(no_refresh=True)
                if runnerlist == self.OVER_QUOTA_ERROR:
                    self.update_cache_runnerlist(None)
                elif runnerlist is not None:
                    for runnerdict in runnerlist:
                        if runnerdict['username'] == user.username:
                            runnerdict['gravatar_url'] = util.get_gravatar_url(
                                user.gravatar)
                            break
                    self.update_cache_runnerlist(runnerlist)

        self.redirect(return_url)
Пример #50
0
    def put_existing_run(self, params):
        user = params['user']
        game = params['game']
        game_code = params['game_code']
        category = params['category']
        seconds = params['seconds']
        time = params['time']
        video = params['video']
        version = params['version']
        notes = params['notes']
        valid = params['valid']
        run_id = params['run_id']

        # Grab the old run, which we will update to be the new run
        new_run = self.get_run_by_id(run_id)
        if new_run == self.OVER_QUOTA_ERROR:
            return False
        if (new_run is None
                or (not user.is_mod and new_run.username != user.username)):
            return False

        # Get the owner of this run
        if new_run.username != user.username:
            runner = self.get_runner(util.get_code(new_run.username))
            if runner == self.OVER_QUOTA_ERROR:
                return False
            params['user'] = runner
        else:
            runner = user

        # Store the contents of the old run
        old_run = dict(game=new_run.game,
                       category=new_run.category,
                       seconds=new_run.seconds)
        old_game_model = self.get_game_model(util.get_code(old_run['game']))
        if old_game_model == self.OVER_QUOTA_ERROR:
            return False

        # Update the run
        try:
            new_run.game = game
            new_run.category = category
            new_run.seconds = seconds
            new_run.date = params['date']
            new_run.version = version
            new_run.notes = notes
        except db.BadValueError:
            valid = False
        if video:
            try:
                new_run.video = video
            except db.BadValueError:
                params['video_error'] = "Invalid video URL"
                valid = False
        elif new_run.video:
            new_run.video = None

        if not valid:
            return False

        new_run.put()
        logging.debug("Put updated run for runner " + runner.username +
                      ", game = " + game + ", category = " + category +
                      ", time= " + time + ", run_id = " + run_id)

        # Figure out the change in num_pbs for the old and new game, as well
        # as the runner
        delta_num_pbs_old = 0
        delta_num_pbs_new = 0
        if game != old_run['game'] or category != old_run['category']:
            num_runs = self.num_runs(runner.username, old_run['game'],
                                     old_run['category'], 1)
            if num_runs == 0:
                delta_num_pbs_old = -1
            num_runs = self.num_runs(runner.username, game, category, 2)
            if num_runs == 1:
                delta_num_pbs_new = 1

        # Update games.Games and runners.Runners
        self.update_runner(runner, delta_num_pbs_old + delta_num_pbs_new)
        if game == old_run['game']:
            self.update_games_delete(params['game_model'], delta_num_pbs_old)
        else:
            self.update_games_delete(old_game_model, delta_num_pbs_old)
        self.update_games_put(params, delta_num_pbs_new)

        # Update memcache with the removal of the old run and addition of the
        # new run.
        self.update_cache_run_by_id(run_id, new_run)
        # Must update runinfo before pblist and gamepage as in put_new_run()
        self.update_runinfo_delete(runner, old_run)
        self.update_runinfo_put(params)
        self.update_pblist_delete(runner, old_run)
        self.update_pblist_put(params)
        self.update_gamepage_delete(runner, old_run)
        self.update_gamepage_put(params)
        self.update_user_has_run_delete(runner, old_run)
        self.update_cache_user_has_run(runner.username, game, True)

        # Update gamelist and runnerlist in memcache
        if delta_num_pbs_old == -1:
            self.update_gamelist_delete(old_run)
            self.update_runnerlist_delete(runner)
        if delta_num_pbs_new == 1:
            self.update_gamelist_put(params)
            self.update_runnerlist_put(params)

        # Replace the old run in the runlist for runner in memcache
        runlist = self.get_runlist_for_runner(runner.username, no_refresh=True)
        if runlist == self.OVER_QUOTA_ERROR:
            self.update_cache_runlist_for_runner(runner.username, None)
        elif runlist is not None:
            for run in runlist:
                if run['run_id'] == run_id:
                    run['game'] = game
                    run['game_code'] = game_code
                    run['category'] = category
                    run['category_code'] = util.get_code(category)
                    run['time'] = time
                    run['date'] = new_run.date
                    run['video'] = video
                    run['version'] = version
                    run['notes'] = notes
                    runlist.sort(key=lambda x: util.get_valid_date(x['date']),
                                 reverse=True)
                    self.update_cache_runlist_for_runner(
                        runner.username, runlist)
                    break

        # Check to see if we need to replace the last run for this user
        last_run = self.get_last_run(runner.username, no_refresh=True)
        if last_run == self.OVER_QUOTA_ERROR:
            self.update_cache_last_run(runner.username, None)
        elif (last_run is not None
              and new_run.key().id() == last_run.key().id()):
            self.update_cache_last_run(runner.username, new_run)

        return True
Пример #51
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")
Пример #52
0
    def get(self, game_code):
        try:
            user = self.get_user()
            if user == self.OVER_QUOTA_ERROR:
                user = None

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

            # Make sure this game exists
            game_model = self.get_game_model(game_code)
            if not game_model:
                self.error(404)
                self.render("404.html", user=user)
                return
            if game_model == self.OVER_QUOTA_ERROR:
                self.error(403)
                self.render("403.html", user=user)
                return

            # Find out if this user has run this game
            if user is not None:
                user_has_run = self.get_user_has_run(user.username,
                                                     game_model.game)
                if user_has_run == self.OVER_QUOTA_ERROR:
                    user_has_run = False
            else:
                user_has_run = False

            gamepage = self.get_gamepage(game_model.game)
            if gamepage == self.OVER_QUOTA_ERROR:
                self.error(403)
                self.render("403.html", user=user)
                return

            # Add gravatar images to the gamepage
            for d in gamepage:
                for run in d['infolist']:
                    runner = self.get_runner(util.get_code(run['username']))
                    if runner == self.OVER_QUOTA_ERROR:
                        self.error(403)
                        if self.format == 'html':
                            self.render("403.html", user=user)
                        return
                    if runner is not None:
                        run['gravatar_url'] = util.get_gravatar_url(
                            runner.gravatar, size=20)

            if self.format == 'html':
                self.render("gamepage.html",
                            user=user,
                            game=game_model.game,
                            game_code=game_code,
                            gamepage=gamepage,
                            user_has_run=user_has_run)
            elif self.format == 'json':
                self.render_json(gamepage)

        except DeadlineExceededError, msg:
            logging.error(msg)
            self.error(403)
            self.render("deadline_exceeded.html", user=user)
Пример #53
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 + "].")
Пример #54
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)
Пример #55
0
    def put_existing_run( self, params ):
        user = params[ 'user' ]
        game = params[ 'game' ]
        game_code = params[ 'game_code' ]
        category = params[ 'category' ]
        seconds = params[ 'seconds' ]
        time = params[ 'time' ]
        video = params[ 'video' ]
        version = params[ 'version' ]
        notes = params[ 'notes' ]
        valid = params[ 'valid' ]
        run_id = params[ 'run_id' ]

        # Grab the old run, which we will update to be the new run
        new_run = self.get_run_by_id( run_id )
        if ( new_run is None 
             or ( not user.is_mod and new_run.username != user.username ) ):
            return False

        # Get the owner of this run
        if new_run.username != user.username:
            runner = self.get_runner( util.get_code( new_run.username ) )
            params['user'] = runner
        else:
            runner = user

        # Store the contents of the old run
        old_run = dict( game = new_run.game,
                        category = new_run.category,
                        seconds = new_run.seconds )

        # Update the run
        try:
            new_run.game = game
            new_run.category = category
            new_run.seconds = seconds
            new_run.date = params['date']
            new_run.version = version
            new_run.notes = notes
        except db.BadValueError:
            valid = False
        if video:
            try:
                new_run.video = video
            except db.BadValueError:
                params['video_error'] = "Invalid video URL"
                valid = False
        elif new_run.video:
            new_run.video = None
            
        if not valid:
            return False
            
        new_run.put( )
        logging.debug( "Put updated run for runner " + runner.username
                       + ", game = " + game + ", category = " + category
                       + ", time= " + time + ", run_id = " + run_id )

        # Figure out the change in num_pbs for the old and new game, as well
        # as the runner
        delta_num_pbs_old = 0
        delta_num_pbs_new = 0
        if game != old_run['game'] or category != old_run['category']:
            num_runs = self.num_runs( runner.username, old_run[ 'game' ], 
                                      old_run[ 'category' ], 1 )
            if num_runs == 0:
                delta_num_pbs_old = -1
            num_runs = self.num_runs( runner.username, game, category, 2 )
            if num_runs == 1:
                delta_num_pbs_new = 1
            
        # Update games.Games and runners.Runners
        self.update_runner( runner, delta_num_pbs_old + delta_num_pbs_new )
        if game == old_run['game']:
            self.update_games_delete( params['game_model'], delta_num_pbs_old )
        else:
            self.update_games_delete( self.get_game_model( util.get_code( 
                        old_run['game'] ) ), delta_num_pbs_old )
        self.update_games_put( params, delta_num_pbs_new )

        # Update memcache with the removal of the old run and addition of the
        # new run.
        self.update_cache_run_by_id( run_id, new_run )
        # Must update runinfo before pblist and gamepage as in put_new_run()
        self.update_runinfo_delete( runner, old_run )
        self.update_runinfo_put( params )
        self.update_pblist_delete( runner, old_run )
        self.update_pblist_put( params )
        self.update_gamepage_delete( runner, old_run )
        self.update_gamepage_put( params )
        self.update_user_has_run_delete( runner, old_run )
        self.update_cache_user_has_run( runner.username, game, True )

        # Update gamelist and runnerlist in memcache
        if delta_num_pbs_old == -1:
            self.update_gamelist_delete( old_run )
            self.update_runnerlist_delete( runner )
        if delta_num_pbs_new == 1:
            self.update_gamelist_put( params )
            self.update_runnerlist_put( params )

        # Replace the old run in the runlist for runner in memcache
        runlist = self.get_runlist_for_runner( runner.username, 
                                               no_refresh=True )
        if runlist:
            for run in runlist:
                if run[ 'run_id' ] == run_id:
                    run[ 'game' ] = game
                    run[ 'game_code' ] = game_code
                    run[ 'category' ] = category
                    run[ 'time' ] = time
                    run[ 'date' ] = new_run.date
                    run[ 'video' ] = video
                    run[ 'version' ] = version
                    run[ 'notes' ] = notes
                    runlist.sort( key=lambda x: util.get_valid_date( 
                        x['date'] ), reverse=True )
                    self.update_cache_runlist_for_runner( runner.username, 
                                                          runlist )
                    break

        # Check to see if we need to replace the last run for this user
        last_run = self.get_last_run( runner.username, no_refresh=True )
        if( last_run is not None 
            and new_run.key().id() == last_run.key().id() ):
            self.update_cache_last_run( runner.username, new_run )

        return True
Пример #56
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)
Пример #57
0
    def post( self ):
        user = self.get_user( )
        if not user:
            self.redirect( "/" )
            return
        elif user == self.OVER_QUOTA_ERROR:
            self.error( 403 )
            self.render( "403.html" )
            return

        game = self.request.get( 'game' )

        params = dict( user = user, game = game )

        valid = True

        # Make sure the game doesn't already exist under a similar name
        game_code = util.get_code( game )
        game_model = self.get_game_model( game_code )
        if game_model == self.OVER_QUOTA_ERROR:
            self.error( 403 )
            self.render( "403.html", user=user )
            return        
        if not game_code:
            params['game_error'] = "Game cannot be blank"
            valid = False
        elif game_model is not None and game != game_model.game:
            params['game_error'] = ( "Game already exists under [" 
                                     + game_model.game + "] (case sensitive)."
                                     + " Hit submit again to confirm." )
            params['game'] = game_model.game
            valid = False
        elif not games.valid_game_or_category( game ):
            params['game_error'] = ( "Game name must not use any 'funny'"
                                     + " characters and can be up to 100 "
                                     + "characters long" )
            valid = False
            
        if not valid:
            # Grab all of the games for autocompleting
            params['games'] = self.get_static_gamelist( )   
            if params['games'] == self.OVER_QUOTA_ERROR:
                self.error( 403 )
                self.render( "403.html", user=user )
            else:
                self.render( "presubmit.html", **params )
            return

        if game_model is None:
            # Add the game to the database
            valid = self.put_new_game( game )
            if not valid:
                params['game_error'] = ( "Failed to process game with name ["
                                         + game + "] (unknown error)" )

        if valid:
            self.redirect( "/submit/" + game_code + '/' )
        else:
            # Grab all of the games for autocompleting
            params['games'] = self.get_static_gamelist( )
            if params['games'] == self.OVER_QUOTA_ERROR:
                self.error( 403 )
                self.render( "403.html", user=user )
            else:
                self.render( "presubmit.html", **params )
            return
Пример #58
0
 def update_games_delete(self, game_model, delta_num_pbs):
     if delta_num_pbs != 0:
         game_model.num_pbs += delta_num_pbs
         game_model.put()
         self.update_cache_game_model(util.get_code(game_model.game),
                                      game_model)
Пример #59
0
    def post( self, run_id ):
        user = self.get_user( )
        if not user:
            self.redirect( "/" )
            return
        elif user == self.OVER_QUOTA_ERROR:
            self.error( 403 )
            self.render( "403.html" )
            return

        # Get the run
        run = runs.Runs.get_by_id( long( run_id ), parent=runs.key() )
        if( not run or 
            ( not user.is_mod and run.username != user.username ) ):
            self.error( 404 ) 
            self.render( "404.html", user=user )
            return

        # Grab the owner of the run
        if run.username == user.username:
            runner = user
        else:
            runner = self.get_runner( util.get_code( run.username ) )
            if runner == self.OVER_QUOTA_ERROR:
                self.error( 403 )
                self.render( "403.html", user=user )
                return

        # Delete the run
        run.delete( )

        # Update memcache
        old_run = dict( game = run.game, category = run.category,
                        seconds = run.seconds )
        self.update_cache_run_by_id( run_id, None )

        # Update games, runner
        delta_num_pbs = 0
        num_runs = self.num_runs( runner.username, run.game, run.category, 1 )
        if num_runs == 0:
            delta_num_pbs = -1
        self.update_runner( runner, delta_num_pbs )
        game_model = self.get_game_model( util.get_code( old_run['game'] ) )
        if game_model == self.OVER_QUOTA_ERROR:
            self.error( 403 )
            self.render( "403.html", user=user )
            return
        self.update_games_delete( game_model, run.category, delta_num_pbs )

        self.update_pblist_delete( runner, old_run )
        self.update_gamepage_delete( runner, old_run )
        self.update_user_has_run_delete( runner, old_run )
        if num_runs <= 0:
            self.update_gamelist_delete( old_run )
            self.update_runnerlist_delete( runner )

        # Update runlist for runner in memcache
        cached_runlists = self.get_cached_runlists_for_runner(
            runner.username )
        if cached_runlists is not None:
            found_run = False
            for page_num, res in cached_runlists.iteritems( ):
                if found_run:
                    break
                for i, run in enumerate( res['runlist'] ):
                    if run[ 'run_id' ] == run_id:
                        del cached_runlists[ page_num ]['runlist'][ i ]
                        self.update_cache_runlist_for_runner( runner.username,
                                                              cached_runlists )
                        found_run = True
                        break

        # Update last run
        last_run = self.get_last_run( runner.username, no_refresh=True )
        if last_run == self.OVER_QUOTA_ERROR:
            self.update_cache_last_run( runner.username, None )
        elif last_run is not None and last_run.key( ).id( ) == long( run_id ):
            self.update_cache_last_run( runner.username, None )

        # Done with deletion
        self.redirect( "/runner/" + util.get_code( runner.username )
                       + "?q=view-all" )
Пример #60
0
    def post( self ):
        # Get the user
        user = self.get_user( )
        if not user:
            self.redirect( "/" )
            return

        # Make sure user is a mod
        if not user.is_mod:
            self.error( 404 )
            self.render( "404.html", user=user )
            return

        old_game = self.request.get( 'old-game' )
        old_category = self.request.get( 'old-category' )
        new_game = self.request.get( 'new-game' )
        new_category = self.request.get( 'new-category' )

        params = dict( user=user, old_game=old_game, old_category=old_category,
                       new_game=new_game, new_category=new_category )

        valid = True

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

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

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

        changes = self.change_categories( params )
    
        logging.info( changes )

        # Render changes
        self.write( changes )