nodes: 0, module; 1, function_definition; 2, function_name:games_by_time; 3, parameters; 4, block; 5, identifier:self; 6, identifier:start_game; 7, identifier:end_game; 8, expression_statement; 9, expression_statement; 10, expression_statement; 11, function_definition; 12, return_statement; 13, comment:"""Given a range of games, return the games sorted by time.
  
          Returns [(time, game_number), ...]
  
          The time will be a `datetime.datetime` and the game
          number is the integer used as the basis of the row ID.
  
          Note that when a cluster of self-play nodes are writing
          concurrently, the game numbers may be out of order.
          """; 14, assignment; 15, assignment; 16, function_name:parse; 17, parameters; 18, block; 19, call; 20, identifier:move_count; 21, string; 22, identifier:rows; 23, call; 24, identifier:r; 25, expression_statement; 26, expression_statement; 27, return_statement; 28, identifier:sorted; 29, argument_list; 30, string_content:move_count; 31, attribute; 32, argument_list; 33, assignment; 34, assignment; 35, tuple; 36, list_comprehension; 37, keyword_argument; 38, attribute; 39, identifier:read_rows; 40, call; 41, call; 42, keyword_argument; 43, identifier:rk; 44, call; 45, identifier:game; 46, subscript; 47, attribute; 48, identifier:game; 49, call; 50, for_in_clause; 51, identifier:key; 52, call; 53, identifier:self; 54, identifier:bt_table; 55, attribute; 56, argument_list; 57, attribute; 58, argument_list; 59, identifier:filter_; 60, call; 61, identifier:str; 62, argument_list; 63, call; 64, integer:0; 65, subscript; 66, identifier:timestamp; 67, identifier:parse; 68, argument_list; 69, identifier:r; 70, identifier:rows; 71, attribute; 72, argument_list; 73, identifier:ROWCOUNT_PREFIX; 74, identifier:format; 75, identifier:start_game; 76, identifier:ROWCOUNT_PREFIX; 77, identifier:format; 78, identifier:end_game; 79, attribute; 80, argument_list; 81, attribute; 82, string; 83, attribute; 84, argument_list; 85, subscript; 86, integer:0; 87, identifier:r; 88, identifier:operator; 89, identifier:itemgetter; 90, integer:0; 91, identifier:bigtable_row_filters; 92, identifier:ColumnRangeFilter; 93, identifier:METADATA; 94, identifier:move_count; 95, identifier:move_count; 96, identifier:r; 97, identifier:row_key; 98, string_content:utf-8; 99, call; 100, identifier:groups; 101, subscript; 102, identifier:move_count; 103, attribute; 104, argument_list; 105, attribute; 106, identifier:METADATA; 107, identifier:_game_from_counter; 108, identifier:match; 109, identifier:rk; 110, identifier:r; 111, identifier:cells
code: def games_by_time(self, start_game, end_game):
          """Given a range of games, return the games sorted by time.
  
          Returns [(time, game_number), ...]
  
          The time will be a `datetime.datetime` and the game
          number is the integer used as the basis of the row ID.
  
          Note that when a cluster of self-play nodes are writing
          concurrently, the game numbers may be out of order.
          """
          move_count = b'move_count'
          rows = self.bt_table.read_rows(
              ROWCOUNT_PREFIX.format(start_game),
              ROWCOUNT_PREFIX.format(end_game),
              filter_=bigtable_row_filters.ColumnRangeFilter(
                  METADATA, move_count, move_count))
  
          def parse(r):
              rk = str(r.row_key, 'utf-8')
              game = _game_from_counter.match(rk).groups()[0]
              return (r.cells[METADATA][move_count][0].timestamp, game)
          return sorted([parse(r) for r in rows], key=operator.itemgetter(0))