Example #1
0
 def _MatchWinner(self, match):
   winner = 'TIE' if match.get('isComplete') else None
   if util_lib.Access(match, 'top.winner'):
     winner = match['top']['teamID']
   elif util_lib.Access(match, 'bottom.winner'):
     winner = match['bottom']['teamID']
   return winner
Example #2
0
  def _LoadStage(self, stage_id):
    """Loads a single stage (bracket)."""
    with self._lock:
      stage = self._proxy.FetchJson(
          '/'.join([self._BASE_URL, 'stages', stage_id]), force_lookup=True)
      bracket = esports_pb2.Bracket(
          bracket_id=self._MakeBracketId(stage_id),
          name=stage['name'],
          league_id=self.league_id,
          is_playoffs='playoff' in stage['name'].lower())
      self._brackets[bracket.bracket_id] = bracket

      matches = self._proxy.FetchJson(
          '/'.join([self._BASE_URL, 'stages', stage_id, 'matches']),
          force_lookup=True)
      # Battlefy doesn't provide actual match start times. We assume that
      # matches are only provided for the current week. And then replace with
      # completed time if it exists.
      default_match_time = util_lib.ArrowTime(
          weekday=5, hour=12, tz='America/Los_Angeles')
      for match in matches:
        match_time = default_match_time
        if 'completedAt' in match:
          match_time = arrow.get(match['completedAt'])
        m = bracket.schedule.add(
            match_id=match['_id'],
            bracket_id=bracket.bracket_id,
            red=util_lib.Access(match, 'top.teamID', 'BYE'),
            blue=util_lib.Access(match, 'bottom.teamID', 'BYE'),
            timestamp=match_time.timestamp,
            winner=self._MatchWinner(match))
        self._matches[m.match_id] = m
        stats = None
        if self.stats_enabled and m.winner:
          stats = self._proxy.FetchJson(
              '/'.join([self._BASE_URL, 'matches', m.match_id]),
              params={'extend[stats]': 'true'},
              force_lookup=True)
        for stat_idx, game_id in enumerate(match.get('appliedRiotGameIDs', [])):
          game = m.games.add(
              game_id=game_id, realm=self._realm, hash=match['lolHookUrl'])
          game_stats = util_lib.Access(stats, '0.stats.%d.stats' % stat_idx)
          if game_stats:
            self._ParseGameStats(game, game_stats)

    self._UpdateStandings(bracket)
Example #3
0
  def _LoadSchedule(self, bracket):
    """Loads schedule for given bracket.

    This is a lie. It loads the schedule for the bracket's tournament and
    pretends like all matches belong to this bracket since Rito no longer
    provides indication which bracket a match belongs to.

    Args:
      bracket: The bracket for which the schedule should be loaded.

    Returns:
      List of matches that were updated i.e., completed since the last time that
      _LoadSchedule was called.
    """
    updated_matches = []
    schedule_data = self._FetchEsportsData(
        'getSchedule', {'leagueId': bracket.league_id})
    schedule_data = util_lib.Access(schedule_data, 'schedule.events')
    if not schedule_data:
      logging.warning('Failed to load schedule for %s', bracket.name)
      return []
    for event in schedule_data:
      winner = 'TIE' if event['state'] == 'completed' else None
      if util_lib.Access(event, 'match.teams.0.result.outcome') == 'win':
        winner = self._FindTeamId(event['match']['teams'][0]['code'])
      elif util_lib.Access(event, 'match.teams.1.result.outcome') == 'win':
        winner = self._FindTeamId(event['match']['teams'][1]['code'])
      match_id = event['match']['id']
      if match_id in self._matches:
        match = self._matches[match_id]
        if winner and not match.winner:
          match.winner = winner
          updated_matches.append(match)
      else:
        match = bracket.schedule.add(
            match_id=event['match']['id'],
            bracket_id=bracket.bracket_id,
            red=self._FindTeamId(event['match']['teams'][0]['code']),
            blue=self._FindTeamId(event['match']['teams'][1]['code']),
            timestamp=arrow.get(event['startTime']).timestamp,
            winner=winner)
        self._matches[match.match_id] = match
    return updated_matches
Example #4
0
  def _LoadStandings(self, tournament_id):
    """(Re)loads standings for a given tournament_id.

    Note: If no bracket exists for the given `stage` of the tournament, this
    will create one, otherwise it will simply clear the existing standings for
    the bracket and update in place.

    Args:
      tournament_id: ID of tournament.
    """
    standings_data = self._FetchEsportsData(
        'getStandings', {'tournamentId': tournament_id})
    if not standings_data or 'standings' not in standings_data:
      logging.error('Failed to get standings.')
      return
    for stage in standings_data['standings'][0]['stages']:
      for section in stage['sections']:
        # The section name is more of a slug and not a pretty name. Lolesports
        # uses JavaScript and a locale file to convert to human name, but we do
        # the best we can given that it's non-trivial (and even more brittle) to
        # try to parse the JS to figure out exactly what to display.
        section_name = section['name'].replace('_', ' ').title()
        full_name = '%s: %s' % (stage['name'], section_name)
        bracket_id = self._MakeBracketId(full_name)
        if bracket_id in self._brackets:
          b = self._brackets[bracket_id]
          del b.standings[:]
        else:
          b = esports_pb2.Bracket(
              bracket_id=bracket_id,
              name=full_name,
              league_id=self._league_id,
              is_playoffs=stage['slug'] == 'playoffs')
          self._brackets[b.bracket_id] = b

        rankings = util_lib.Access(section, 'rankings')
        if not rankings:
          continue
        for group in rankings:
          for team in group['teams']:
            if team['id'] not in self._teams:
              self._LoadTeam(team['slug'])
            if team['id'] not in self._teams:
              # New for 2020, we have TBD teams which don't exist.
              continue
            b.standings.add(
                rank=group['ordinal'],
                wins=team['record']['wins'],
                losses=team['record']['losses'],
                team=self._teams[team['id']])
Example #5
0
 def _LoadData(self):
   """Helper method to load data."""
   updated_matches = []
   tournament_data = self._FetchEsportsData('getTournamentsForLeague',
                                            {'leagueId': self._league_id})
   if not tournament_data or 'leagues' not in tournament_data:
     logging.error('Could not get tournaments for league: %s', self._region)
     return []
   for tournament in self._FindActiveTournaments(
       # We requested a single league so we try to take the first.
       util_lib.Access(tournament_data, 'leagues.0.tournaments', [])):
     self._LoadStandings(tournament['id'])
     updated_matches.extend(
         self._LoadSchedule(list(self._brackets.values())[0]))
   return updated_matches
Example #6
0
    def GetAQI(self, location: str):
        """Get air quality index for the location.

    Args:
      location: Location in human readable form.

    Returns:
      AQI response from airnow.
    """
        location = self._LocationToGPS(location)
        if not location:
            return None
        zip_code = util_lib.Access(location, 'address_components.zip')
        if not zip_code:
            return None

        return self._CallAQI(zip_code)
Example #7
0
 def _LoadTeam(self, slug):
   """Loads information about a team from Rito."""
   team_data = self._FetchEsportsData('getTeams', {'id': slug})
   team_data = util_lib.Access(team_data, 'teams.0')
   if not team_data:
     logging.warning('Failed to load team: %s', slug)
     return
   team = esports_pb2.Team(
       team_id=team_data['id'],
       name=team_data['name'],
       abbreviation=team_data['code'])
   observed_positions = set()
   for player_data in team_data['players']:
     position = player_data['role'].title()
     team.players.add(
         summoner_name=player_data['summonerName'],
         position=position,
         is_substitute=position in observed_positions,
         team_id=team.team_id)
     observed_positions.add(position)
   self._teams[team.team_id] = team
Example #8
0
 def _UpdateSchedule(self, schedule, bracket):
   """Update existing matches if they are now wonnered."""
   updated_matches = []
   match_count = 0
   with self._lock:
     for week in schedule:
       for match in week['matches']:
         match_count += 1
         match_id = '%s-%s-%s' % (self.league_id, bracket.bracket_id,
                                  match_count)
         old_match = self._matches.get(match_id)
         if not old_match or old_match.winner:
           continue
         for team in [match['team1'], match['team2']]:
           team_id = util_lib.Access(team, 'ref.id')
           if not team_id or not team['outcome']:
             continue
           if team['outcome'] == 'VICTORY':
             old_match.winner = team_id
           elif team['outcome'] == 'TIE':
             old_match.winner = 'TIE'
         if old_match.winner:
           updated_matches.append(old_match)
   return updated_matches
Example #9
0
  def GetHeadlines(self, query, max_results=5):
    endpoint_url = self._params.base_url + 'search/v2/articlesearch.json'
    r = self._proxy.FetchJson(
        endpoint_url,
        params={
            'api-key': self._params.api_key,
            'q': query,
            'sort': 'relevance',
            'fl': 'headline,web_url,source,pub_date',
            'begin_date': arrow.now().shift(years=-1).strftime('%Y%m%d')
        })

    articles = []
    docs = util_lib.Access(r, 'response.docs')
    for doc in docs:
      if 'source' not in doc:
        continue
      articles.append({
          'title': doc['headline'].get('main'),
          'url': doc['web_url'],
          'source': doc['source'],
          'pub_date': doc['pub_date']
      })
    return articles[:max_results]
Example #10
0
  def _ParseSchedule(self, schedule, bracket):
    """Parse schedule into bracket."""
    match_count = 0
    standings = {}
    with self._lock:
      for week in schedule:
        for match in week['matches']:
          match_count += 1
          m = bracket.schedule.add(
              match_id='%s-%s-%s' %
              (self.league_id, bracket.bracket_id, match_count),
              bracket_id=bracket.bracket_id,
              blue=util_lib.Access(match, 'team1.ref.id', 'TBD'),
              red=util_lib.Access(match, 'team2.ref.id', 'TBD'),
              timestamp=match['timestampSec'])
          self._matches[m.match_id] = m
          for game in match['games']:
            game_proto = m.games.add(
                game_id=str(util_lib.Access(game, 'ref.gameId')),
                realm=self._realm,
                hash=util_lib.Access(game, 'ref.tournamentCode'))
            if self.stats_enabled and util_lib.Access(game, 'winner'):
              response = self._FetchJson(
                  'game', [game_proto.game_id, game_proto.hash],
                  use_year=False,
                  use_storage=True)
              if response:
                json_format.ParseDict(
                    response, game_proto.stats, ignore_unknown_fields=True)

          for team in [match['team1'], match['team2']]:
            team_id = util_lib.Access(team, 'ref.id')
            if not team_id:
              continue
            if team_id not in self._teams:
              self._teams[team_id] = esports_pb2.Team(
                  team_id=team_id,
                  abbreviation=team_id,
                  name=team['ref']['displayName'],
                  league_id=self.league_id)
            if team_id not in standings:
              standings[team_id] = esports_pb2.TeamStanding(
                  team=self._teams[team_id])
            if not team['outcome']:
              continue
            if team['outcome'] == 'VICTORY':
              m.winner = team_id
              standings[team_id].wins += 1
              standings[team_id].points += 3
            elif team['outcome'] == 'TIE':
              m.winner = 'TIE'
              standings[team_id].ties += 1
              standings[team_id].points += 1
            else:
              standings[team_id].losses += 1
      standings = sorted(
          standings.values(), key=lambda x: x.points, reverse=True)
      rank = 1
      cur_points = -1
      for i, team in enumerate(standings):
        if team.points != cur_points:
          rank = i + 1
          cur_points = team.points
        team.rank = rank
      bracket.standings.extend(standings)
Example #11
0
    def _ScrapePickBanData(self, tournament_id, region, match):
        """For each game in match, fetches and tallies pick/ban data from Riot."""
        game_id_mappings = self.FetchEsportsData(
            'matchDetails', (tournament_id, match['id']),
            use_storage=True).get('gameIdMappings')
        if not game_id_mappings:
            # We won't be able to find the gameHash, so just log
            logging.info('Not retrieving game stats for match %s', match['id'])
            return

        # Skip games that weren't actually played
        for guid, game in (m for m in match['games'].items()
                           if 'gameId' in m[1]):
            self.num_games[region] += 1
            game_hash = [
                i['gameHash'] for i in game_id_mappings if i['id'] == guid
            ]
            if len(game_hash) != 1:
                logging.warning(
                    'Couldn\'t find exactly one hash for match/game %s/%s:',
                    match['id'], guid)
                logging.warning('\tActual: %s', game_hash)
                continue

            game_hash = game_hash[0]
            game_stats = self._proxy.FetchJson(
                'https://acs.leagueoflegends.com/v1/stats/game/%s/%s?gameHash=%s'
                % (game['gameRealm'], game['gameId'], game_hash),
                use_storage=True)
            winning_team = None

            participant_to_player = {
                p['participantId']: util_lib.Access(p, 'player.summonerName')
                for p in game_stats.get('participantIdentities', [])
            }

            # Collect ban data
            for team in game_stats.get('teams', []):
                if team.get('win') == 'Win':
                    winning_team = team.get('teamId')
                for ban in team.get('bans', []):
                    self.champ_to_stats[ban['championId']][region]['bans'] += 1

            # Collect pick and W/L data
            for player in game_stats.get('participants', []):
                champ_id = player['championId']
                champ_name = self._game.GetChampNameFromId(champ_id)

                # We need to use separate player_name and player_key here because Rito
                # doesn't like to keep things like capitalization consistent with player
                # names so they aren't useful as keys, but we still want to display the
                # "canonical" player name back to the user eventually, so we save it as
                # a value in player_to_stats instead.
                player_name = participant_to_player[player['participantId']]
                player_key = util_lib.CanonicalizeName(player_name)
                self.player_to_stats[player_key]['name'] = player_name
                if player.get('teamId') == winning_team:
                    self.champ_to_stats[champ_id][region]['wins'] += 1
                    self.player_to_stats[player_key][champ_name]['wins'] += 1
                    self.player_to_stats[player_key]['num_games']['wins'] += 1
                self.champ_to_stats[champ_id][region]['picks'] += 1
                self.player_to_stats[player_key][champ_name]['picks'] += 1
                self.player_to_stats[player_key]['num_games']['picks'] += 1
Example #12
0
    def GetRoster(self, team, region, include_subs):
        """Gets roster for specified team."""
        roster = []
        query = util_lib.CanonicalizeName(team)
        slug_to_role = {
            'toplane': 'Top',
            'jungle': 'Jungle',
            'midlane': 'Mid',
            'adcarry': 'ADC',
            'support': 'Support'
        }
        role_ordering = {
            'toplane': 0,
            'jungle': 1,
            'midlane': 2,
            'adcarry': 3,
            'support': 4
        }

        query_params = {}
        if region:
            query_params['region'] = region
        if include_subs:
            query_params['full'] = True
        roster_data = self._proxy.FetchJson(
            ESPORTS_API_BASE_URL + 'roster/%s' % query, query_params)
        if 'error' in roster_data:
            if roster_data.get('ready'):
                logging.warning('Roster service returned an error: %s',
                                roster_data['error'])
                return roster
            return ['Roster data is still loading, try again soon.']
        elif not roster_data:
            logging.error('Could not retrieve roster.')
            return roster

        roster.append('%s %sRoster:' %
                      (util_lib.Access(roster_data, 'team.0.name', query),
                       'Full ' if include_subs else ''))

        # A map of actual player names to what their name should be displayed as.
        # You know, for memes.
        player_name_substitutions = {
            'Revolta': 'Travolta',
            'MikeYeung': 'Mike "Mike Yeung" Yeung'
        }
        players = []
        sorted_roster_data = sorted(roster_data.items(),
                                    key=lambda x: role_ordering.get(x[0], -1))
        for position, player_list in sorted_roster_data:
            if position not in slug_to_role:
                continue
            # Make sure we add the starter before any possible subs
            for player in sorted(player_list, key=lambda x: not x['starter']):
                real_name = player['name']
                display_name = player_name_substitutions.get(
                    real_name, real_name)
                player_pair = [display_name, slug_to_role[position]]
                if not player['starter']:
                    player_pair[1] += ' (Sub)'
                players.append(player_pair)

        roster.extend([' - '.join(p) for p in players])
        return roster