def deferred_ratings(): """ This is the deferred ratings table calculation process """ # Disable the in-context cache to save memory # (it doesn't give any speed advantage for this processing) Context.disable_cache() t0 = time.time() try: _create_ratings() except DeadlineExceededError as ex: # Hit deadline: save the stuff we already have and # defer a new task to continue where we left off logging.error(u"Deadline exceeded in ratings, failing permamently") # Normal return prevents this task from being run again raise deferred.PermanentTaskFailure() except Exception as ex: logging.error( u"Exception in ratings, failing permanently: {0}".format(ex)) # Avoid having the task retried raise deferred.PermanentTaskFailure() t1 = time.time() logging.info(u"Ratings calculation finished in {0:.2f} seconds".format(t1 - t0)) StatsModel.log_cache_stats() StatsModel.clear_cache( ) # Do not maintain the cache in memory between runs
def deferred_ratings(): """ This is the deferred ratings table calculation process """ # Disable the in-context cache to save memory # (it doesn't give any speed advantage for this processing) Context.disable_cache() t0 = time.time() try: _create_ratings() except DeadlineExceededError as ex: # Hit deadline: save the stuff we already have and # defer a new task to continue where we left off logging.error(u"Deadline exceeded in ratings, failing permamently") # Normal return prevents this task from being run again raise deferred.PermanentTaskFailure() except Exception as ex: logging.error(u"Exception in ratings, failing permanently: {0}".format(ex)) # Avoid having the task retried raise deferred.PermanentTaskFailure() t1 = time.time() logging.info(u"Ratings calculation finished in {0:.2f} seconds".format(t1 - t0)) StatsModel.log_cache_stats() StatsModel.clear_cache() # Do not maintain the cache in memory between runs
def deferred_ratings(): """ This is the deferred ratings table calculation process """ with Client.get_context() as context: t0 = time.time() try: _create_ratings() except Exception as ex: logging.error("Exception in deferred_ratings: {0!r}".format(ex)) return t1 = time.time() StatsModel.log_cache_stats() # Do not maintain the cache in memory between runs StatsModel.clear_cache() logging.info( "Ratings calculation finished in {0:.2f} seconds".format(t1 - t0))
def _run_stats(from_time, to_time): """ Runs a process to update user statistics and Elo ratings """ logging.info("Generating stats from {0} to {1}".format(from_time, to_time)) if from_time is None or to_time is None: # Time range must be specified return False if from_time >= to_time: # Null time range return False # Clear previous cache contents, if any StatsModel.clear_cache() # Iterate over all finished games within the time span in temporal order # pylint: disable=singleton-comparison q = (GameModel.query( ndb.AND(GameModel.ts_last_move > from_time, GameModel.ts_last_move <= to_time)).order( GameModel.ts_last_move).filter(GameModel.over == True)) # The accumulated user statistics users = dict() def _init_stat(user_id, robot_level): """ Returns the newest StatsModel instance available for the given user """ return StatsModel.newest_before(from_time, user_id, robot_level) cnt = 0 ts_last_processed = None try: # Use i as a progress counter i = 0 for gm in iter_q(q, chunk_size=250): i += 1 lm = Alphabet.format_timestamp(gm.ts_last_move or gm.timestamp) p0 = None if gm.player0 is None else gm.player0.id() p1 = None if gm.player1 is None else gm.player1.id() robot_game = (p0 is None) or (p1 is None) if robot_game: rl = gm.robot_level else: rl = 0 s0 = gm.score0 s1 = gm.score1 if (s0 == 0) and (s1 == 0): # When a game ends by resigning immediately, # make sure that the weaker player # doesn't get Elo points for a draw; in fact, # ignore such a game altogether in the statistics continue if p0 is None: k0 = "robot-" + str(rl) else: k0 = p0 if p1 is None: k1 = "robot-" + str(rl) else: k1 = p1 if k0 in users: urec0 = users[k0] else: users[k0] = urec0 = _init_stat(p0, rl if p0 is None else 0) if k1 in users: urec1 = users[k1] else: users[k1] = urec1 = _init_stat(p1, rl if p1 is None else 0) # Number of games played urec0.games += 1 urec1.games += 1 if not robot_game: urec0.human_games += 1 urec1.human_games += 1 # Total scores urec0.score += s0 urec1.score += s1 urec0.score_against += s1 urec1.score_against += s0 if not robot_game: urec0.human_score += s0 urec1.human_score += s1 urec0.human_score_against += s1 urec1.human_score_against += s0 # Wins and losses if s0 > s1: urec0.wins += 1 urec1.losses += 1 elif s1 > s0: urec1.wins += 1 urec0.losses += 1 if not robot_game: if s0 > s1: urec0.human_wins += 1 urec1.human_losses += 1 elif s1 > s0: urec1.human_wins += 1 urec0.human_losses += 1 # Find out whether players are established or beginners est0 = urec0.games > ESTABLISHED_MARK est1 = urec1.games > ESTABLISHED_MARK # Save the Elo point state used in the calculation gm.elo0, gm.elo1 = urec0.elo, urec1.elo # Compute the Elo points of both players adj = _compute_elo((urec0.elo, urec1.elo), s0, s1, est0, est1) # When an established player is playing a beginning (provisional) player, # leave the Elo score of the established player unchanged # Adjust player 0 if est0 and not est1: adj = (0, adj[1]) gm.elo0_adj = adj[0] urec0.elo += adj[0] # Adjust player 1 if est1 and not est0: adj = (adj[0], 0) gm.elo1_adj = adj[1] urec1.elo += adj[1] # If not a robot game, compute the human-only Elo if not robot_game: gm.human_elo0, gm.human_elo1 = urec0.human_elo, urec1.human_elo adj = _compute_elo((urec0.human_elo, urec1.human_elo), s0, s1, est0, est1) # Adjust player 0 if est0 and not est1: adj = (0, adj[1]) gm.human_elo0_adj = adj[0] urec0.human_elo += adj[0] # Adjust player 1 if est1 and not est0: adj = (adj[0], 0) gm.human_elo1_adj = adj[1] urec1.human_elo += adj[1] # Save the game object with the new Elo adjustment statistics gm.put() # Save the last processed timestamp ts_last_processed = lm cnt += 1 # Report on our progress if i % 500 == 0: logging.info("Processed {0} games".format(i)) except Exception as ex: logging.error( "Exception in _run_stats() after {0} games and {1} users: {2!r}". format(cnt, len(users), ex)) return False # Completed without incident logging.info( "Normal completion of stats for {1} games and {0} users".format( len(users), cnt)) _write_stats(to_time, users) return True