def calc_elim_match_points_2015(cls, district_points, matches, POINTS_MULTIPLIER): from helpers.match_helper import MatchHelper # circular import issue # count number of matches played per team per comp level num_played = defaultdict(lambda: defaultdict(int)) for level in ['qf', 'sf']: for match in matches[level]: if not match.has_been_played: continue for color in ['red', 'blue']: for team in match.alliances[color]['teams']: num_played[level][team] += 1 # qf and sf points advancement = MatchHelper.generatePlayoffAdvancement2015(matches) for last_level, level in [('qf', 'sf'), ('sf', 'f')]: for (teams, _, _) in advancement[last_level]: teams = ['frc{}'.format(t) for t in teams] done = False for match in matches[level]: for color in ['red', 'blue']: if set(teams).intersection( set(match.alliances[color]['teams'])) != set(): for team in teams: points = DistrictPointValues.QF_WIN.get( match.year, DistrictPointValues.QF_WIN_DEFAULT ) if last_level == 'qf' else DistrictPointValues.SF_WIN.get( match.year, DistrictPointValues.SF_WIN_DEFAULT) district_points['points'][team][ 'elim_points'] += int( math.ceil(points * num_played[last_level][team]) ) * POINTS_MULTIPLIER done = True break if done: break if done: break # final points num_wins = {'red': 0, 'blue': 0} team_matches_played = {'red': [], 'blue': []} for match in matches['f']: if not match.has_been_played or match.winning_alliance == '': continue num_wins[match.winning_alliance] += 1 for team in match.alliances[match.winning_alliance]['teams']: team_matches_played[match.winning_alliance].append(team) if num_wins[match.winning_alliance] >= 2: points = DistrictPointValues.F_WIN.get( match.year, DistrictPointValues.F_WIN_DEFAULT) for team in team_matches_played[match.winning_alliance]: district_points['points'][team][ 'elim_points'] += points * POINTS_MULTIPLIER
def calc_elim_match_points_2015(cls, district_points, matches, POINTS_MULTIPLIER): from helpers.match_helper import MatchHelper # circular import issue # count number of matches played per team per comp level num_played = defaultdict(lambda: defaultdict(int)) for level in ["qf", "sf"]: for match in matches[level]: if not match.has_been_played: continue for color in ["red", "blue"]: for team in match.alliances[color]["teams"]: num_played[level][team] += 1 # qf and sf points advancement = MatchHelper.generatePlayoffAdvancement2015(matches) for last_level, level in [("qf", "sf"), ("sf", "f")]: for (teams, _, _) in advancement[last_level]: teams = ["frc{}".format(t) for t in teams] done = False for match in matches[level]: for color in ["red", "blue"]: if set(teams).intersection(set(match.alliances[color]["teams"])) != set(): for team in teams: points = ( DistrictPointValues.QF_WIN.get(match.year, DistrictPointValues.QF_WIN_DEFAULT) if last_level == "qf" else DistrictPointValues.SF_WIN.get(match.year, DistrictPointValues.SF_WIN_DEFAULT) ) district_points["points"][team]["elim_points"] += ( int(np.ceil(points * num_played[last_level][team])) * POINTS_MULTIPLIER ) done = True break if done: break if done: break # final points num_wins = {"red": 0, "blue": 0} team_matches_played = {"red": [], "blue": []} for match in matches["f"]: if not match.has_been_played or match.winning_alliance == "": continue num_wins[match.winning_alliance] += 1 for team in match.alliances[match.winning_alliance]["teams"]: team_matches_played[match.winning_alliance].append(team) if num_wins[match.winning_alliance] >= 2: points = DistrictPointValues.F_WIN.get(match.year, DistrictPointValues.F_WIN_DEFAULT) for team in team_matches_played[match.winning_alliance]: district_points["points"][team]["elim_points"] += points * POINTS_MULTIPLIER
def calc_elim_match_points_2015(cls, district_points, matches, POINTS_MULTIPLIER): from helpers.match_helper import MatchHelper # circular import issue # count number of matches played per team per comp level num_played = defaultdict(lambda: defaultdict(int)) for level in ['qf', 'sf']: for match in matches[level]: if not match.has_been_played: continue for color in ['red', 'blue']: for team in match.alliances[color]['teams']: num_played[level][team] += 1 # qf and sf points advancement = MatchHelper.generatePlayoffAdvancement2015(matches) for last_level, level in [('qf', 'sf'), ('sf', 'f')]: for (teams, _, _) in advancement[last_level]: teams = ['frc{}'.format(t) for t in teams] done = False for match in matches[level]: for color in ['red', 'blue']: if set(teams).intersection(set(match.alliances[color]['teams'])) != set(): for team in teams: points = DistrictPointValues.QF_WIN.get(match.year, DistrictPointValues.QF_WIN_DEFAULT) if last_level == 'qf' else DistrictPointValues.SF_WIN.get(match.year, DistrictPointValues.SF_WIN_DEFAULT) district_points['points'][team]['elim_points'] += int( math.ceil(points * num_played[last_level][team])) * POINTS_MULTIPLIER done = True break if done: break if done: break # final points num_wins = {'red': 0, 'blue': 0} team_matches_played = {'red': [], 'blue': []} for match in matches['f']: if not match.has_been_played or match.winning_alliance == '': continue num_wins[match.winning_alliance] += 1 for team in match.alliances[match.winning_alliance]['teams']: team_matches_played[match.winning_alliance].append(team) if num_wins[match.winning_alliance] >= 2: points = DistrictPointValues.F_WIN.get(match.year, DistrictPointValues.F_WIN_DEFAULT) for team in team_matches_played[match.winning_alliance]: district_points['points'][team]['elim_points'] += points * POINTS_MULTIPLIER
def _render(self, event_key): event = EventQuery(event_key).fetch() if not event: self.abort(404) event.prepAwardsMatchesTeams() event.prep_details() medias_future = media_query.EventTeamsPreferredMediasQuery(event_key).fetch_async() district_future = DistrictQuery(event.district_key.id()).fetch_async() if event.district_key else None event_medias_future = media_query.EventMediasQuery(event_key).fetch_async() status_sitevar_future = Sitevar.get_by_id_async('apistatus.down_events') event_divisions_future = None event_codivisions_future = None parent_event_future = None if event.divisions: event_divisions_future = ndb.get_multi_async(event.divisions) elif event.parent_event: parent_event_future = event.parent_event.get_async() event_codivisions_future = EventDivisionsQuery(event.parent_event.id()).fetch_async() awards = AwardHelper.organizeAwards(event.awards) cleaned_matches = MatchHelper.deleteInvalidMatches(event.matches, event) matches = MatchHelper.organizeMatches(cleaned_matches) teams = TeamHelper.sortTeams(event.teams) # Organize medias by team image_medias = MediaHelper.get_images([media for media in medias_future.get_result()]) team_medias = defaultdict(list) for image_media in image_medias: for reference in image_media.references: team_medias[reference].append(image_media) team_and_medias = [] for team in teams: team_and_medias.append((team, team_medias.get(team.key, []))) num_teams = len(team_and_medias) middle_value = num_teams / 2 if num_teams % 2 != 0: middle_value += 1 teams_a, teams_b = team_and_medias[:middle_value], team_and_medias[middle_value:] oprs = [i for i in event.matchstats['oprs'].items()] if (event.matchstats is not None and 'oprs' in event.matchstats) else [] oprs = sorted(oprs, key=lambda t: t[1], reverse=True) # sort by OPR oprs = oprs[:15] # get the top 15 OPRs if event.now: matches_recent = MatchHelper.recentMatches(cleaned_matches) matches_upcoming = MatchHelper.upcomingMatches(cleaned_matches) else: matches_recent = None matches_upcoming = None bracket_table = MatchHelper.generateBracket(matches, event, event.alliance_selections) playoff_advancement = None playoff_template = None double_elim_matches = None if EventHelper.is_2015_playoff(event_key): playoff_advancement = MatchHelper.generatePlayoffAdvancement2015(matches, event.alliance_selections) playoff_template = 'playoff_table' for comp_level in ['qf', 'sf']: if comp_level in bracket_table: del bracket_table[comp_level] elif event.playoff_type == PlayoffType.ROUND_ROBIN_6_TEAM: playoff_advancement = MatchHelper.generatePlayoffAdvancementRoundRobin(matches, event.year, event.alliance_selections) playoff_template = 'playoff_round_robin_6_team' comp_levels = bracket_table.keys() for comp_level in comp_levels: if comp_level != 'f': del bracket_table[comp_level] elif event.playoff_type == PlayoffType.BO3_FINALS or event.playoff_type == PlayoffType.BO5_FINALS: comp_levels = bracket_table.keys() for comp_level in comp_levels: if comp_level != 'f': del bracket_table[comp_level] elif event.playoff_type == PlayoffType.DOUBLE_ELIM_8_TEAM: double_elim_matches = MatchHelper.organizeDoubleElimMatches(matches) district_points_sorted = None if event.district_key and event.district_points: district_points_sorted = sorted(event.district_points['points'].items(), key=lambda (team, points): -points['total']) event_insights = event.details.insights if event.details else None event_insights_template = None if event_insights: event_insights_template = 'event_partials/event_insights_{}.html'.format(event.year) district = district_future.get_result() if district_future else None event_divisions = None if event_divisions_future: event_divisions = [e.get_result() for e in event_divisions_future] elif event_codivisions_future: event_divisions = event_codivisions_future.get_result() medias_by_slugname = MediaHelper.group_by_slugname([media for media in event_medias_future.get_result()]) has_time_predictions = matches_upcoming and any(match.predicted_time for match in matches_upcoming) status_sitevar = status_sitevar_future.get_result() self.template_values.update({ "event": event, "event_down": status_sitevar and event_key in status_sitevar.contents, "district_name": district.display_name if district else None, "district_abbrev": district.abbreviation if district else None, "matches": matches, "matches_recent": matches_recent, "matches_upcoming": matches_upcoming, 'has_time_predictions': has_time_predictions, "awards": awards, "teams_a": teams_a, "teams_b": teams_b, "num_teams": num_teams, "oprs": oprs, "bracket_table": bracket_table, "playoff_advancement": playoff_advancement, "playoff_template": playoff_template, "district_points_sorted": district_points_sorted, "event_insights_qual": event_insights['qual'] if event_insights else None, "event_insights_playoff": event_insights['playoff'] if event_insights else None, "event_insights_template": event_insights_template, "medias_by_slugname": medias_by_slugname, "event_divisions": event_divisions, 'parent_event': parent_event_future.get_result() if parent_event_future else None, 'double_elim_matches': double_elim_matches, 'double_elim_playoff_types': PlayoffType.DOUBLE_ELIM_TYPES, }) if event.within_a_day: self._cache_expiration = self.SHORT_CACHE_EXPIRATION return jinja2_engine.render('event_details.html', self.template_values)
def parse(self, response): matches = response['Schedule'] event_key = '{}{}'.format(self.year, self.event_short) event = Event.get_by_id(event_key) if event.timezone_id: event_tz = pytz.timezone(event.timezone_id) else: logging.warning("Event {} has no timezone! Match times may be wrong.".format(event_key)) event_tz = None parsed_matches = [] is_octofinals = len(matches) > 0 and 'Octofinal' in matches[0]['description'] for match in matches: if 'tournamentLevel' in match: # 2016+ level = match['tournamentLevel'] else: # 2015 level = match['level'] comp_level = get_comp_level(self.year, level, match['matchNumber'], is_octofinals) set_number, match_number = get_set_match_number(self.year, comp_level, match['matchNumber'], is_octofinals) red_teams = [] blue_teams = [] team_key_names = [] null_team = False for team in match['Teams']: if team['teamNumber'] is None: null_team = True team_key = 'frc{}'.format(team['teamNumber']) team_key_names.append(team_key) if 'Red' in team['station']: red_teams.append(team_key) elif 'Blue' in team['station']: blue_teams.append(team_key) if null_team and match['scoreRedFinal'] is None and match['scoreBlueFinal'] is None: continue alliances = { 'red': { 'teams': red_teams, 'score': match['scoreRedFinal'] }, 'blue': { 'teams': blue_teams, 'score': match['scoreBlueFinal'] } } if not match['startTime']: # no startTime means it's an unneeded rubber match continue time = datetime.datetime.strptime(match['startTime'].split('.')[0], TIME_PATTERN) actual_time_raw = match['actualStartTime'] if 'actualStartTime' in match else None actual_time = None if event_tz is not None: time = time - event_tz.utcoffset(time) if actual_time_raw is not None: actual_time = datetime.datetime.strptime(actual_time_raw.split('.')[0], TIME_PATTERN) if event_tz is not None: actual_time = actual_time - event_tz.utcoffset(actual_time) parsed_matches.append(Match( id=Match.renderKeyName( event_key, comp_level, set_number, match_number), event=event.key, year=event.year, set_number=set_number, match_number=match_number, comp_level=comp_level, team_key_names=team_key_names, time=time, actual_time=actual_time, alliances_json=json.dumps(alliances), )) if self.year == 2015: # Fix null teams in elims (due to FMS API failure, some info not complete) # Should only happen for sf and f matches organized_matches = MatchHelper.organizeMatches(parsed_matches) for level in ['sf', 'f']: playoff_advancement = MatchHelper.generatePlayoffAdvancement2015(organized_matches) if playoff_advancement[LAST_LEVEL[level]] != []: for match in organized_matches[level]: if 'frcNone' in match.team_key_names: if level == 'sf': red_seed, blue_seed = QF_SF_MAP[match.match_number] else: red_seed = 0 blue_seed = 1 red_teams = ['frc{}'.format(t) for t in playoff_advancement[LAST_LEVEL[level]][red_seed][0]] blue_teams = ['frc{}'.format(t) for t in playoff_advancement[LAST_LEVEL[level]][blue_seed][0]] alliances = match.alliances alliances['red']['teams'] = red_teams alliances['blue']['teams'] = blue_teams match.alliances_json = json.dumps(alliances) match.team_key_names = red_teams + blue_teams fixed_matches = [] for key, matches in organized_matches.items(): if key != 'num': for match in matches: if 'frcNone' not in match.team_key_names: fixed_matches.append(match) parsed_matches = fixed_matches return parsed_matches
def parse(self, response): matches = response['Schedule'] event_key = '{}{}'.format(self.year, self.event_short) event = Event.get_by_id(event_key) if event.timezone_id: event_tz = pytz.timezone(event.timezone_id) else: logging.warning( "Event {} has no timezone! Match times may be wrong.".format( event_key)) event_tz = None parsed_matches = [] remapped_matches = {} # If a key changes due to a tiebreaker for match in matches: if 'tournamentLevel' in match: # 2016+ level = match['tournamentLevel'] else: # 2015 level = match['level'] comp_level = PlayoffType.get_comp_level(event.playoff_type, level, match['matchNumber']) set_number, match_number = PlayoffType.get_set_match_number( event.playoff_type, comp_level, match['matchNumber']) red_teams = [] blue_teams = [] red_surrogates = [] blue_surrogates = [] team_key_names = [] null_team = False sorted_teams = sorted( match['Teams'], key=lambda team: team['station'] ) # Sort by station to ensure correct ordering. Kind of hacky. for team in sorted_teams: if team['teamNumber'] is None: null_team = True team_key = 'frc{}'.format(team['teamNumber']) team_key_names.append(team_key) if 'Red' in team['station']: red_teams.append(team_key) if team['surrogate']: red_surrogates.append(team_key) elif 'Blue' in team['station']: blue_teams.append(team_key) if team['surrogate']: blue_surrogates.append(team_key) if null_team and match['scoreRedFinal'] is None and match[ 'scoreBlueFinal'] is None: continue alliances = { 'red': { 'teams': red_teams, 'surrogates': red_surrogates, 'score': match['scoreRedFinal'] }, 'blue': { 'teams': blue_teams, 'surrogates': blue_surrogates, 'score': match['scoreBlueFinal'] }, } if not match[ 'startTime']: # no startTime means it's an unneeded rubber match continue time = datetime.datetime.strptime(match['startTime'].split('.')[0], TIME_PATTERN) if event_tz is not None: time = time - event_tz.utcoffset(time) actual_time_raw = match[ 'actualStartTime'] if 'actualStartTime' in match else None actual_time = None if actual_time_raw is not None: actual_time = datetime.datetime.strptime( actual_time_raw.split('.')[0], TIME_PATTERN) if event_tz is not None: actual_time = actual_time - event_tz.utcoffset(actual_time) post_result_time_raw = match.get('postResultTime') post_result_time = None if post_result_time_raw is not None: post_result_time = datetime.datetime.strptime( post_result_time_raw.split('.')[0], TIME_PATTERN) if event_tz is not None: post_result_time = post_result_time - event_tz.utcoffset( post_result_time) key_name = Match.renderKeyName(event_key, comp_level, set_number, match_number) # Check for tiebreaker matches existing_match = Match.get_by_id(key_name) # Follow chain of existing matches while existing_match is not None and existing_match.tiebreak_match_key is not None: logging.info("Following Match {} to {}".format( existing_match.key.id(), existing_match.tiebreak_match_key.id())) existing_match = existing_match.tiebreak_match_key.get() # Check if last existing match needs to be tiebroken if existing_match and existing_match.comp_level != 'qm' and \ existing_match.has_been_played and \ existing_match.winning_alliance == '' and \ existing_match.actual_time != actual_time and \ not self.is_blank_match(existing_match): logging.warning("Match {} is tied!".format( existing_match.key.id())) # TODO: Only query within set if set_number ever gets indexed match_count = 0 for match_key in Match.query( Match.event == event.key, Match.comp_level == comp_level).fetch(keys_only=True): _, match_key = match_key.id().split('_') if match_key.startswith('{}{}'.format( comp_level, set_number)): match_count += 1 # Sanity check: Tiebreakers must be played after at least 3 matches, or 6 for finals if match_count < 3 or (match_count < 6 and comp_level == 'f'): logging.warning( "Match supposedly tied, but existing count is {}! Skipping match." .format(match_count)) continue match_number = match_count + 1 new_key_name = Match.renderKeyName(event_key, comp_level, set_number, match_number) remapped_matches[key_name] = new_key_name key_name = new_key_name # Point existing match to new tiebreaker match existing_match.tiebreak_match_key = ndb.Key(Match, key_name) parsed_matches.append(existing_match) logging.warning("Creating new match: {}".format(key_name)) elif existing_match: remapped_matches[key_name] = existing_match.key.id() key_name = existing_match.key.id() match_number = existing_match.match_number parsed_matches.append( Match( id=key_name, event=event.key, year=event.year, set_number=set_number, match_number=match_number, comp_level=comp_level, team_key_names=team_key_names, time=time, actual_time=actual_time, post_result_time=post_result_time, alliances_json=json.dumps(alliances), )) if self.year == 2015: # Fix null teams in elims (due to FMS API failure, some info not complete) # Should only happen for sf and f matches organized_matches = MatchHelper.organizeMatches(parsed_matches) for level in ['sf', 'f']: playoff_advancement = MatchHelper.generatePlayoffAdvancement2015( organized_matches) if playoff_advancement[LAST_LEVEL[level]] != []: for match in organized_matches[level]: if 'frcNone' in match.team_key_names: if level == 'sf': red_seed, blue_seed = QF_SF_MAP[ match.match_number] else: red_seed = 0 blue_seed = 1 red_teams = [ 'frc{}'.format(t) for t in playoff_advancement[ LAST_LEVEL[level]][red_seed][0] ] blue_teams = [ 'frc{}'.format(t) for t in playoff_advancement[ LAST_LEVEL[level]][blue_seed][0] ] alliances = match.alliances alliances['red']['teams'] = red_teams alliances['blue']['teams'] = blue_teams match.alliances_json = json.dumps(alliances) match.team_key_names = red_teams + blue_teams fixed_matches = [] for key, matches in organized_matches.items(): if key != 'num': for match in matches: if 'frcNone' not in match.team_key_names: fixed_matches.append(match) parsed_matches = fixed_matches return parsed_matches, remapped_matches
def parse(self, response): """ This currently only works for the 2015 game, where elims matches are all part of one set. """ matches = response['Schedule'] event_key = '{}{}'.format(self.year, self.event_short) event = Event.get_by_id(event_key) if event.timezone_id: event_tz = pytz.timezone(event.timezone_id) else: logging.warning( "Event {} has no timezone! Match times may be wrong.").format( event_key) event_tz = None set_number = 1 parsed_matches = [] for match in matches: comp_level = self._get_comp_level(match['level'], match['matchNumber']) match_number = self._get_match_number(comp_level, match['matchNumber']) red_teams = [] blue_teams = [] team_key_names = [] null_team = False for team in match['Teams']: if team['teamNumber'] is None: null_team = True team_key = 'frc{}'.format(team['teamNumber']) team_key_names.append(team_key) if 'Red' in team['station']: red_teams.append(team_key) elif 'Blue' in team['station']: blue_teams.append(team_key) if null_team and match['scoreRedFinal'] is None and match[ 'scoreBlueFinal'] is None: continue alliances = { 'red': { 'teams': red_teams, 'score': match['scoreRedFinal'] }, 'blue': { 'teams': blue_teams, 'score': match['scoreBlueFinal'] } } score_breakdown = { 'red': { 'auto': match['scoreRedAuto'], 'foul': match['scoreRedFoul'] }, 'blue': { 'auto': match['scoreBlueAuto'], 'foul': match['scoreBlueFoul'] } } time = datetime.datetime.strptime(match['startTime'], "%Y-%m-%dT%H:%M:%S") if event_tz is not None: time = time - event_tz.utcoffset(time) parsed_matches.append( Match( id=Match.renderKeyName(event_key, comp_level, set_number, match_number), event=event.key, game= "frc_unknown", # TODO: deprecate in favor of a 'year' property set_number=set_number, match_number=match_number, comp_level=comp_level, team_key_names=team_key_names, time=time, alliances_json=json.dumps(alliances), score_breakdown_json=json.dumps(score_breakdown))) # Fix null teams in elims (due to FMS API failure, some info not complete) # Should only happen for sf and f matches organized_matches = MatchHelper.organizeMatches(parsed_matches) for level in ['sf', 'f']: playoff_advancement = MatchHelper.generatePlayoffAdvancement2015( organized_matches) if playoff_advancement[LAST_LEVEL[level]] != []: for match in organized_matches[level]: if 'frcNone' in match.team_key_names: if level == 'sf': red_seed, blue_seed = QF_SF_MAP[match.match_number] else: red_seed = 0 blue_seed = 1 red_teams = [ 'frc{}'.format(t) for t in playoff_advancement[ LAST_LEVEL[level]][red_seed][0] ] blue_teams = [ 'frc{}'.format(t) for t in playoff_advancement[ LAST_LEVEL[level]][blue_seed][0] ] alliances = match.alliances alliances['red']['teams'] = red_teams alliances['blue']['teams'] = blue_teams match.alliances_json = json.dumps(alliances) match.team_key_names = red_teams + blue_teams fixed_matches = [] for key, matches in organized_matches.items(): if key != 'num': for match in matches: if 'frcNone' not in match.team_key_names: fixed_matches.append(match) return fixed_matches
def parse(self, response): matches = response["Schedule"] event_key = "{}{}".format(self.year, self.event_short) event = Event.get_by_id(event_key) if event.timezone_id: event_tz = pytz.timezone(event.timezone_id) else: logging.warning("Event {} has no timezone! Match times may be wrong.".format(event_key)) event_tz = None parsed_matches = [] is_octofinals = len(matches) > 0 and "Octofinal" in matches[0]["description"] for match in matches: if "tournamentLevel" in match: # 2016+ level = match["tournamentLevel"] else: # 2015 level = match["level"] comp_level = get_comp_level(self.year, level, match["matchNumber"], is_octofinals) set_number, match_number = get_set_match_number(self.year, comp_level, match["matchNumber"], is_octofinals) red_teams = [] blue_teams = [] team_key_names = [] null_team = False sorted_teams = sorted( match["Teams"], key=lambda team: team["station"] ) # Sort by station to ensure correct ordering. Kind of hacky. for team in sorted_teams: if team["teamNumber"] is None: null_team = True team_key = "frc{}".format(team["teamNumber"]) team_key_names.append(team_key) if "Red" in team["station"]: red_teams.append(team_key) elif "Blue" in team["station"]: blue_teams.append(team_key) if null_team and match["scoreRedFinal"] is None and match["scoreBlueFinal"] is None: continue alliances = { "red": {"teams": red_teams, "score": match["scoreRedFinal"]}, "blue": {"teams": blue_teams, "score": match["scoreBlueFinal"]}, } if not match["startTime"]: # no startTime means it's an unneeded rubber match continue time = datetime.datetime.strptime(match["startTime"].split(".")[0], TIME_PATTERN) actual_time_raw = match["actualStartTime"] if "actualStartTime" in match else None actual_time = None if event_tz is not None: time = time - event_tz.utcoffset(time) if actual_time_raw is not None: actual_time = datetime.datetime.strptime(actual_time_raw.split(".")[0], TIME_PATTERN) if event_tz is not None: actual_time = actual_time - event_tz.utcoffset(actual_time) parsed_matches.append( Match( id=Match.renderKeyName(event_key, comp_level, set_number, match_number), event=event.key, year=event.year, set_number=set_number, match_number=match_number, comp_level=comp_level, team_key_names=team_key_names, time=time, actual_time=actual_time, alliances_json=json.dumps(alliances), ) ) if self.year == 2015: # Fix null teams in elims (due to FMS API failure, some info not complete) # Should only happen for sf and f matches organized_matches = MatchHelper.organizeMatches(parsed_matches) for level in ["sf", "f"]: playoff_advancement = MatchHelper.generatePlayoffAdvancement2015(organized_matches) if playoff_advancement[LAST_LEVEL[level]] != []: for match in organized_matches[level]: if "frcNone" in match.team_key_names: if level == "sf": red_seed, blue_seed = QF_SF_MAP[match.match_number] else: red_seed = 0 blue_seed = 1 red_teams = ["frc{}".format(t) for t in playoff_advancement[LAST_LEVEL[level]][red_seed][0]] blue_teams = [ "frc{}".format(t) for t in playoff_advancement[LAST_LEVEL[level]][blue_seed][0] ] alliances = match.alliances alliances["red"]["teams"] = red_teams alliances["blue"]["teams"] = blue_teams match.alliances_json = json.dumps(alliances) match.team_key_names = red_teams + blue_teams fixed_matches = [] for key, matches in organized_matches.items(): if key != "num": for match in matches: if "frcNone" not in match.team_key_names: fixed_matches.append(match) parsed_matches = fixed_matches return parsed_matches
def parse(self, response): matches = response['Schedule'] event_key = '{}{}'.format(self.year, self.event_short) event = Event.get_by_id(event_key) if event.timezone_id: event_tz = pytz.timezone(event.timezone_id) else: logging.warning("Event {} has no timezone! Match times may be wrong.".format(event_key)) event_tz = None parsed_matches = [] remapped_matches = {} # If a key changes due to a tiebreaker is_octofinals = len(matches) > 0 and 'Octofinal' in matches[0]['description'] for match in matches: if 'tournamentLevel' in match: # 2016+ level = match['tournamentLevel'] else: # 2015 level = match['level'] comp_level = get_comp_level(self.year, level, match['matchNumber'], is_octofinals) set_number, match_number = get_set_match_number(self.year, comp_level, match['matchNumber'], is_octofinals) red_teams = [] blue_teams = [] red_surrogates = [] blue_surrogates = [] team_key_names = [] null_team = False sorted_teams = sorted(match['Teams'], key=lambda team: team['station']) # Sort by station to ensure correct ordering. Kind of hacky. for team in sorted_teams: if team['teamNumber'] is None: null_team = True team_key = 'frc{}'.format(team['teamNumber']) team_key_names.append(team_key) if 'Red' in team['station']: red_teams.append(team_key) if team['surrogate']: red_surrogates.append(team_key) elif 'Blue' in team['station']: blue_teams.append(team_key) if team['surrogate']: blue_surrogates.append(team_key) if null_team and match['scoreRedFinal'] is None and match['scoreBlueFinal'] is None: continue alliances = { 'red': { 'teams': red_teams, 'surrogates': red_surrogates, 'score': match['scoreRedFinal'] }, 'blue': { 'teams': blue_teams, 'surrogates': blue_surrogates, 'score': match['scoreBlueFinal'] }, } if not match['startTime']: # no startTime means it's an unneeded rubber match continue time = datetime.datetime.strptime(match['startTime'].split('.')[0], TIME_PATTERN) if event_tz is not None: time = time - event_tz.utcoffset(time) actual_time_raw = match['actualStartTime'] if 'actualStartTime' in match else None actual_time = None if actual_time_raw is not None: actual_time = datetime.datetime.strptime(actual_time_raw.split('.')[0], TIME_PATTERN) if event_tz is not None: actual_time = actual_time - event_tz.utcoffset(actual_time) post_result_time_raw = match.get('postResultTime') post_result_time = None if post_result_time_raw is not None: post_result_time = datetime.datetime.strptime(post_result_time_raw.split('.')[0], TIME_PATTERN) if event_tz is not None: post_result_time = post_result_time - event_tz.utcoffset(post_result_time) key_name = Match.renderKeyName( event_key, comp_level, set_number, match_number) # Check for tiebreaker matches existing_match = Match.get_by_id(key_name) # Follow chain of existing matches while existing_match is not None and existing_match.tiebreak_match_key is not None: logging.info("Following Match {} to {}".format(existing_match.key.id(), existing_match.tiebreak_match_key.id())) existing_match = existing_match.tiebreak_match_key.get() # Check if last existing match needs to be tiebroken if existing_match and existing_match.comp_level != 'qm' and \ existing_match.has_been_played and \ existing_match.winning_alliance == '' and \ existing_match.actual_time != actual_time and \ not self.is_blank_match(existing_match): logging.warning("Match {} is tied!".format(existing_match.key.id())) # TODO: Only query within set if set_number ever gets indexed match_count = 0 for match_key in Match.query(Match.event==event.key, Match.comp_level==comp_level).fetch(keys_only=True): _, match_key = match_key.id().split('_') if match_key.startswith('{}{}'.format(comp_level, set_number)): match_count += 1 # Sanity check: Tiebreakers must be played after at least 3 matches, or 6 for finals if match_count < 3 or (match_count < 6 and comp_level == 'f'): logging.warning("Match supposedly tied, but existing count is {}! Skipping match.".format(match_count)) continue match_number = match_count + 1 new_key_name = Match.renderKeyName( event_key, comp_level, set_number, match_number) remapped_matches[key_name] = new_key_name key_name = new_key_name # Point existing match to new tiebreaker match existing_match.tiebreak_match_key = ndb.Key(Match, key_name) parsed_matches.append(existing_match) logging.warning("Creating new match: {}".format(key_name)) elif existing_match: remapped_matches[key_name] = existing_match.key.id() key_name = existing_match.key.id() match_number = existing_match.match_number parsed_matches.append(Match( id=key_name, event=event.key, year=event.year, set_number=set_number, match_number=match_number, comp_level=comp_level, team_key_names=team_key_names, time=time, actual_time=actual_time, post_result_time=post_result_time, alliances_json=json.dumps(alliances), )) if self.year == 2015: # Fix null teams in elims (due to FMS API failure, some info not complete) # Should only happen for sf and f matches organized_matches = MatchHelper.organizeMatches(parsed_matches) for level in ['sf', 'f']: playoff_advancement = MatchHelper.generatePlayoffAdvancement2015(organized_matches) if playoff_advancement[LAST_LEVEL[level]] != []: for match in organized_matches[level]: if 'frcNone' in match.team_key_names: if level == 'sf': red_seed, blue_seed = QF_SF_MAP[match.match_number] else: red_seed = 0 blue_seed = 1 red_teams = ['frc{}'.format(t) for t in playoff_advancement[LAST_LEVEL[level]][red_seed][0]] blue_teams = ['frc{}'.format(t) for t in playoff_advancement[LAST_LEVEL[level]][blue_seed][0]] alliances = match.alliances alliances['red']['teams'] = red_teams alliances['blue']['teams'] = blue_teams match.alliances_json = json.dumps(alliances) match.team_key_names = red_teams + blue_teams fixed_matches = [] for key, matches in organized_matches.items(): if key != 'num': for match in matches: if 'frcNone' not in match.team_key_names: fixed_matches.append(match) parsed_matches = fixed_matches return parsed_matches, remapped_matches
def parse(self, response): matches = response['Schedule'] event_key = '{}{}'.format(self.year, self.event_short) event = Event.get_by_id(event_key) if event.timezone_id: event_tz = pytz.timezone(event.timezone_id) else: logging.warning("Event {} has no timezone! Match times may be wrong.".format(event_key)) event_tz = None parsed_matches = [] for match in matches: if 'tournamentLevel' in match: # 2016+ level = match['tournamentLevel'] else: # 2015 level = match['level'] comp_level = get_comp_level(self.year, level, match['matchNumber']) set_number, match_number = get_set_match_number(self.year, comp_level, match['matchNumber']) red_teams = [] blue_teams = [] team_key_names = [] null_team = False for team in match['Teams']: if team['teamNumber'] is None: null_team = True team_key = 'frc{}'.format(team['teamNumber']) team_key_names.append(team_key) if 'Red' in team['station']: red_teams.append(team_key) elif 'Blue' in team['station']: blue_teams.append(team_key) if null_team and match['scoreRedFinal'] is None and match['scoreBlueFinal'] is None: continue alliances = { 'red': { 'teams': red_teams, 'score': match['scoreRedFinal'] }, 'blue': { 'teams': blue_teams, 'score': match['scoreBlueFinal'] } } if not match['startTime']: # no startTime means it's an unneeded rubber match continue time = datetime.datetime.strptime(match['startTime'].split('.')[0], TIME_PATTERN) actual_time_raw = match['actualStartTime'] if 'actualStartTime' in match else None actual_time = None if event_tz is not None: time = time - event_tz.utcoffset(time) if actual_time_raw is not None: actual_time = datetime.datetime.strptime(actual_time_raw.split('.')[0], TIME_PATTERN) if event_tz is not None: actual_time = actual_time - event_tz.utcoffset(actual_time) parsed_matches.append(Match( id=Match.renderKeyName( event_key, comp_level, set_number, match_number), event=event.key, year=event.year, set_number=set_number, match_number=match_number, comp_level=comp_level, team_key_names=team_key_names, time=time, actual_time=actual_time, alliances_json=json.dumps(alliances), )) if self.year == 2015: # Fix null teams in elims (due to FMS API failure, some info not complete) # Should only happen for sf and f matches organized_matches = MatchHelper.organizeMatches(parsed_matches) for level in ['sf', 'f']: playoff_advancement = MatchHelper.generatePlayoffAdvancement2015(organized_matches) if playoff_advancement[LAST_LEVEL[level]] != []: for match in organized_matches[level]: if 'frcNone' in match.team_key_names: if level == 'sf': red_seed, blue_seed = QF_SF_MAP[match.match_number] else: red_seed = 0 blue_seed = 1 red_teams = ['frc{}'.format(t) for t in playoff_advancement[LAST_LEVEL[level]][red_seed][0]] blue_teams = ['frc{}'.format(t) for t in playoff_advancement[LAST_LEVEL[level]][blue_seed][0]] alliances = match.alliances alliances['red']['teams'] = red_teams alliances['blue']['teams'] = blue_teams match.alliances_json = json.dumps(alliances) match.team_key_names = red_teams + blue_teams fixed_matches = [] for key, matches in organized_matches.items(): if key != 'num': for match in matches: if 'frcNone' not in match.team_key_names: fixed_matches.append(match) parsed_matches = fixed_matches return parsed_matches
def calc_rank_based_match_points(cls, event, district_points, match_futures, POINTS_MULTIPLIER): """ Calculates match district points based on team ranking This algorithm was introduced for the 2015 season and also used for 2016 See: http://www.firstinspires.org/node/7616 and also http://www.firstinspires.org/robotics/frc/blog/Admin-Manual-Section-7-and-the-FIRST-STRONGHOLD-Logo """ from helpers.match_helper import MatchHelper # circular import issue # qual match points are calculated by rank if event.rankings and len(event.rankings) > 1: rankings = event.rankings[1:] # skip title row num_teams = len(rankings) alpha = 1.07 for row in rankings: rank = int(row[0]) team = 'frc{}'.format(row[1]) qual_points = int(np.ceil(cls.inverf(float(num_teams - 2 * rank + 2) / (alpha * num_teams)) * ( 10.0 / cls.inverf(1.0 / alpha)) + 12)) district_points['points'][team]['qual_points'] = qual_points * POINTS_MULTIPLIER else: logging.warning("Event {} has no rankings for qual_points calculations!".format(event.key.id())) matches = MatchHelper.organizeMatches([mf.get_result() for mf in match_futures]) # qual match calculations. only used for tiebreaking for match in matches['qm']: for color in ['red', 'blue']: for team in match.alliances[color]['teams']: score = match.alliances[color]['score'] district_points['tiebreakers'][team]['highest_qual_scores'] = heapq.nlargest(3, district_points[ 'tiebreakers'][team]['highest_qual_scores'] + [score]) # elim match point calculations # count number of matches played per team per comp level num_played = defaultdict(lambda: defaultdict(int)) for level in ['qf', 'sf']: for match in matches[level]: if not match.has_been_played: continue for color in ['red', 'blue']: for team in match.alliances[color]['teams']: num_played[level][team] += 1 # qf and sf points advancement = MatchHelper.generatePlayoffAdvancement2015(matches) for last_level, level in [('qf', 'sf'), ('sf', 'f')]: for (teams, _, _) in advancement[last_level]: teams = ['frc{}'.format(t) for t in teams] done = False for match in matches[level]: for color in ['red', 'blue']: if set(teams).intersection(set(match.alliances[color]['teams'])) != set(): for team in teams: points = DistrictPointValues.QF_WIN.get(event.year, DistrictPointValues.QF_WIN_DEFAULT) if last_level == 'qf' else DistrictPointValues.SF_WIN.get(event.year, DistrictPointValues.SF_WIN_DEFAULT) district_points['points'][team]['elim_points'] += int( np.ceil(points * num_played[last_level][team])) * POINTS_MULTIPLIER done = True break if done: break if done: break # final points num_wins = {'red': 0, 'blue': 0} team_matches_played = {'red': [], 'blue': []} for match in matches['f']: if not match.has_been_played or match.winning_alliance == '': continue num_wins[match.winning_alliance] += 1 for team in match.alliances[match.winning_alliance]['teams']: team_matches_played[match.winning_alliance].append(team) if num_wins[match.winning_alliance] >= 2: points = DistrictPointValues.F_WIN.get(event.year, DistrictPointValues.F_WIN_DEFAULT) for team in team_matches_played[match.winning_alliance]: district_points['points'][team]['elim_points'] += points * POINTS_MULTIPLIER
def parse(self, response): """ This currently only works for the 2015 game, where elims matches are all part of one set. """ matches = response["Schedule"] event_key = "{}{}".format(self.year, self.event_short) event = Event.get_by_id(event_key) if event.timezone_id: event_tz = pytz.timezone(event.timezone_id) else: logging.warning("Event {} has no timezone! Match times may be wrong.".format(event_key)) event_tz = None set_number = 1 parsed_matches = [] for match in matches: comp_level = get_comp_level(match["level"], match["matchNumber"]) match_number = get_match_number(comp_level, match["matchNumber"]) red_teams = [] blue_teams = [] team_key_names = [] null_team = False for team in match["Teams"]: if team["teamNumber"] is None: null_team = True team_key = "frc{}".format(team["teamNumber"]) team_key_names.append(team_key) if "Red" in team["station"]: red_teams.append(team_key) elif "Blue" in team["station"]: blue_teams.append(team_key) if null_team and match["scoreRedFinal"] is None and match["scoreBlueFinal"] is None: continue alliances = { "red": {"teams": red_teams, "score": match["scoreRedFinal"]}, "blue": {"teams": blue_teams, "score": match["scoreBlueFinal"]}, } score_breakdown = { "red": {"auto_points": match["scoreRedAuto"], "foul_points": match["scoreRedFoul"]}, "blue": {"auto_points": match["scoreBlueAuto"], "foul_points": match["scoreBlueFoul"]}, } time = datetime.datetime.strptime(match["startTime"], "%Y-%m-%dT%H:%M:%S") if event_tz is not None: time = time - event_tz.utcoffset(time) parsed_matches.append( Match( id=Match.renderKeyName(event_key, comp_level, set_number, match_number), event=event.key, year=event.year, set_number=set_number, match_number=match_number, comp_level=comp_level, team_key_names=team_key_names, time=time, alliances_json=json.dumps(alliances), score_breakdown_json=json.dumps(score_breakdown), ) ) # Fix null teams in elims (due to FMS API failure, some info not complete) # Should only happen for sf and f matches organized_matches = MatchHelper.organizeMatches(parsed_matches) for level in ["sf", "f"]: playoff_advancement = MatchHelper.generatePlayoffAdvancement2015(organized_matches) if playoff_advancement[LAST_LEVEL[level]] != []: for match in organized_matches[level]: if "frcNone" in match.team_key_names: if level == "sf": red_seed, blue_seed = QF_SF_MAP[match.match_number] else: red_seed = 0 blue_seed = 1 red_teams = ["frc{}".format(t) for t in playoff_advancement[LAST_LEVEL[level]][red_seed][0]] blue_teams = ["frc{}".format(t) for t in playoff_advancement[LAST_LEVEL[level]][blue_seed][0]] alliances = match.alliances alliances["red"]["teams"] = red_teams alliances["blue"]["teams"] = blue_teams match.alliances_json = json.dumps(alliances) match.team_key_names = red_teams + blue_teams fixed_matches = [] for key, matches in organized_matches.items(): if key != "num": for match in matches: if "frcNone" not in match.team_key_names: fixed_matches.append(match) return fixed_matches
def _render(self, event_key): event = Event.get_by_id(event_key) if not event: self.abort(404) event.prepAwardsMatchesTeams() medias_future = media_query.EventTeamsPreferredMediasQuery(event_key).fetch_async() awards = AwardHelper.organizeAwards(event.awards) cleaned_matches = MatchHelper.deleteInvalidMatches(event.matches) matches = MatchHelper.organizeMatches(cleaned_matches) teams = TeamHelper.sortTeams(event.teams) # Organize medias by team image_medias = MediaHelper.get_images([media for media in medias_future.get_result()]) team_medias = defaultdict(list) for image_media in image_medias: for reference in image_media.references: team_medias[reference].append(image_media) team_and_medias = [] for team in teams: team_and_medias.append((team, team_medias.get(team.key, []))) num_teams = len(team_and_medias) middle_value = num_teams / 2 if num_teams % 2 != 0: middle_value += 1 teams_a, teams_b = team_and_medias[:middle_value], team_and_medias[middle_value:] oprs = [i for i in event.matchstats['oprs'].items()] if (event.matchstats is not None and 'oprs' in event.matchstats) else [] oprs = sorted(oprs, key=lambda t: t[1], reverse=True) # sort by OPR oprs = oprs[:15] # get the top 15 OPRs if event.now: matches_recent = MatchHelper.recentMatches(cleaned_matches) matches_upcoming = MatchHelper.upcomingMatches(cleaned_matches) else: matches_recent = None matches_upcoming = None bracket_table = MatchHelper.generateBracket(matches, event.alliance_selections) is_2015_playoff = EventHelper.is_2015_playoff(event_key) if is_2015_playoff: playoff_advancement = MatchHelper.generatePlayoffAdvancement2015(matches, event.alliance_selections) for comp_level in ['qf', 'sf']: if comp_level in bracket_table: del bracket_table[comp_level] else: playoff_advancement = None district_points_sorted = None if event.district_points: district_points_sorted = sorted(event.district_points['points'].items(), key=lambda (team, points): -points['total']) event_insights = EventInsightsHelper.calculate_event_insights(cleaned_matches, event.year) event_insights_template = None if event_insights: event_insights_template = 'event_partials/event_insights_{}.html'.format(event.year) # rankings processing for ranking score per match full_rankings = event.rankings rankings_enhanced = event.rankings_enhanced if rankings_enhanced is not None: rp_index = RankingIndexes.CUMULATIVE_RANKING_SCORE[event.year] matches_index = RankingIndexes.MATCHES_PLAYED[event.year] ranking_criterion_name = full_rankings[0][rp_index] full_rankings[0].append(ranking_criterion_name + "/Match*") for row in full_rankings[1:]: team = row[1] if rankings_enhanced["ranking_score_per_match"] is not None: rp_per_match = rankings_enhanced['ranking_score_per_match'][team] row.append(rp_per_match) if rankings_enhanced["match_offset"] is not None: match_offset = rankings_enhanced["match_offset"][team] if match_offset != 0: row[matches_index] = "{} ({})".format(row[matches_index], match_offset) self.template_values.update({ "event": event, "district_name": DistrictType.type_names.get(event.event_district_enum, None), "district_abbrev": DistrictType.type_abbrevs.get(event.event_district_enum, None), "matches": matches, "matches_recent": matches_recent, "matches_upcoming": matches_upcoming, "awards": awards, "teams_a": teams_a, "teams_b": teams_b, "num_teams": num_teams, "oprs": oprs, "bracket_table": bracket_table, "playoff_advancement": playoff_advancement, "district_points_sorted": district_points_sorted, "is_2015_playoff": is_2015_playoff, "event_insights_qual": event_insights['qual'] if event_insights else None, "event_insights_playoff": event_insights['playoff'] if event_insights else None, "event_insights_template": event_insights_template, }) if event.within_a_day: self._cache_expiration = self.SHORT_CACHE_EXPIRATION return jinja2_engine.render('event_details.html', self.template_values)
def parse(self, response): """ This currently only works for the 2015 game, where elims matches are all part of one set. """ matches = response['Schedule'] event_key = '{}{}'.format(self.year, self.event_short) event = Event.get_by_id(event_key) if event.timezone_id: event_tz = pytz.timezone(event.timezone_id) else: logging.warning("Event {} has no timezone! Match times may be wrong.".format(event_key)) event_tz = None set_number = 1 parsed_matches = [] for match in matches: comp_level = self._get_comp_level(match['level'], match['matchNumber']) match_number = self._get_match_number(comp_level, match['matchNumber']) red_teams = [] blue_teams = [] team_key_names = [] null_team = False for team in match['Teams']: if team['teamNumber'] is None: null_team = True team_key = 'frc{}'.format(team['teamNumber']) team_key_names.append(team_key) if 'Red' in team['station']: red_teams.append(team_key) elif 'Blue' in team['station']: blue_teams.append(team_key) if null_team and match['scoreRedFinal'] is None and match['scoreBlueFinal'] is None: continue alliances = { 'red': { 'teams': red_teams, 'score': match['scoreRedFinal'] }, 'blue': { 'teams': blue_teams, 'score': match['scoreBlueFinal'] } } score_breakdown = { 'red': { 'auto': match['scoreRedAuto'], 'foul': match['scoreRedFoul'] }, 'blue': { 'auto': match['scoreBlueAuto'], 'foul': match['scoreBlueFoul'] } } time = datetime.datetime.strptime(match['startTime'], "%Y-%m-%dT%H:%M:%S") if event_tz is not None: time = time - event_tz.utcoffset(time) parsed_matches.append(Match( id=Match.renderKeyName( event_key, comp_level, set_number, match_number), event=event.key, year=event.year, set_number=set_number, match_number=match_number, comp_level=comp_level, team_key_names=team_key_names, time=time, alliances_json=json.dumps(alliances), score_breakdown_json=json.dumps(score_breakdown) )) # Fix null teams in elims (due to FMS API failure, some info not complete) # Should only happen for sf and f matches organized_matches = MatchHelper.organizeMatches(parsed_matches) for level in ['sf', 'f']: playoff_advancement = MatchHelper.generatePlayoffAdvancement2015(organized_matches) if playoff_advancement[LAST_LEVEL[level]] != []: for match in organized_matches[level]: if 'frcNone' in match.team_key_names: if level == 'sf': red_seed, blue_seed = QF_SF_MAP[match.match_number] else: red_seed = 0 blue_seed = 1 red_teams = ['frc{}'.format(t) for t in playoff_advancement[LAST_LEVEL[level]][red_seed][0]] blue_teams = ['frc{}'.format(t) for t in playoff_advancement[LAST_LEVEL[level]][blue_seed][0]] alliances = match.alliances alliances['red']['teams'] = red_teams alliances['blue']['teams'] = blue_teams match.alliances_json = json.dumps(alliances) match.team_key_names = red_teams + blue_teams fixed_matches = [] for key, matches in organized_matches.items(): if key != 'num': for match in matches: if 'frcNone' not in match.team_key_names: fixed_matches.append(match) return fixed_matches
def _render(self, event_key): event = Event.get_by_id(event_key) if not event: self.abort(404) event.prepAwardsMatchesTeams() medias_future = media_query.EventTeamsPreferredMediasQuery( event_key).fetch_async() awards = AwardHelper.organizeAwards(event.awards) cleaned_matches = MatchHelper.deleteInvalidMatches(event.matches) matches = MatchHelper.organizeMatches(cleaned_matches) teams = TeamHelper.sortTeams(event.teams) # Organize medias by team image_medias = MediaHelper.get_images( [media for media in medias_future.get_result()]) team_medias = defaultdict(list) for image_media in image_medias: for reference in image_media.references: team_medias[reference].append(image_media) team_and_medias = [] for team in teams: team_and_medias.append((team, team_medias.get(team.key, []))) num_teams = len(team_and_medias) middle_value = num_teams / 2 if num_teams % 2 != 0: middle_value += 1 teams_a, teams_b = team_and_medias[:middle_value], team_and_medias[ middle_value:] oprs = [i for i in event.matchstats['oprs'].items() ] if (event.matchstats is not None and 'oprs' in event.matchstats) else [] oprs = sorted(oprs, key=lambda t: t[1], reverse=True) # sort by OPR oprs = oprs[:15] # get the top 15 OPRs if event.now: matches_recent = MatchHelper.recentMatches(cleaned_matches) matches_upcoming = MatchHelper.upcomingMatches(cleaned_matches) else: matches_recent = None matches_upcoming = None bracket_table = MatchHelper.generateBracket(matches, event.alliance_selections) is_2015_playoff = EventHelper.is_2015_playoff(event_key) if is_2015_playoff: playoff_advancement = MatchHelper.generatePlayoffAdvancement2015( matches, event.alliance_selections) for comp_level in ['qf', 'sf']: if comp_level in bracket_table: del bracket_table[comp_level] else: playoff_advancement = None district_points_sorted = None if event.district_points: district_points_sorted = sorted( event.district_points['points'].items(), key=lambda (team, points): -points['total']) event_insights = EventInsightsHelper.calculate_event_insights( cleaned_matches, event.year) event_insights_template = None if event_insights: event_insights_template = 'event_partials/event_insights_{}.html'.format( event.year) self.template_values.update({ "event": event, "district_name": DistrictType.type_names.get(event.event_district_enum, None), "district_abbrev": DistrictType.type_abbrevs.get(event.event_district_enum, None), "matches": matches, "matches_recent": matches_recent, "matches_upcoming": matches_upcoming, "awards": awards, "teams_a": teams_a, "teams_b": teams_b, "num_teams": num_teams, "oprs": oprs, "bracket_table": bracket_table, "playoff_advancement": playoff_advancement, "district_points_sorted": district_points_sorted, "is_2015_playoff": is_2015_playoff, "event_insights_qual": event_insights['qual'] if event_insights else None, "event_insights_playoff": event_insights['playoff'] if event_insights else None, "event_insights_template": event_insights_template, }) if event.within_a_day: self._cache_expiration = self.SHORT_CACHE_EXPIRATION return jinja2_engine.render('event_details.html', self.template_values)
def _render(self, event_key): event = Event.get_by_id(event_key) if not event: self.abort(404) event.prepAwardsMatchesTeams() awards = AwardHelper.organizeAwards(event.awards) cleaned_matches = MatchHelper.deleteInvalidMatches(event.matches) matches = MatchHelper.organizeMatches(cleaned_matches) teams = TeamHelper.sortTeams(event.teams) num_teams = len(teams) middle_value = num_teams / 2 if num_teams % 2 != 0: middle_value += 1 teams_a, teams_b = teams[:middle_value], teams[middle_value:] oprs = [i for i in event.matchstats['oprs'].items()] if (event.matchstats is not None and 'oprs' in event.matchstats) else [] oprs = sorted(oprs, key=lambda t: t[1], reverse=True) # sort by OPR oprs = oprs[:15] # get the top 15 OPRs if event.now: matches_recent = MatchHelper.recentMatches(cleaned_matches) matches_upcoming = MatchHelper.upcomingMatches(cleaned_matches) else: matches_recent = None matches_upcoming = None bracket_table = MatchHelper.generateBracket(matches, event.alliance_selections) is_2015_playoff = EventHelper.is_2015_playoff(event_key) if is_2015_playoff: playoff_advancement = MatchHelper.generatePlayoffAdvancement2015(matches, event.alliance_selections) for comp_level in ['qf', 'sf']: if comp_level in bracket_table: del bracket_table[comp_level] else: playoff_advancement = None district_points_sorted = None if event.district_points: district_points_sorted = sorted(event.district_points['points'].items(), key=lambda (team, points): -points['total']) self.template_values.update({ "event": event, "matches": matches, "matches_recent": matches_recent, "matches_upcoming": matches_upcoming, "awards": awards, "teams_a": teams_a, "teams_b": teams_b, "num_teams": num_teams, "oprs": oprs, "bracket_table": bracket_table, "playoff_advancement": playoff_advancement, "district_points_sorted": district_points_sorted, "is_2015_playoff": is_2015_playoff, }) if event.within_a_day: self._cache_expiration = self.SHORT_CACHE_EXPIRATION path = os.path.join(os.path.dirname(__file__), '../templates/event_details.html') return template.render(path, self.template_values)
def _render(self, event_key): event = EventQuery(event_key).fetch() if not event: self.abort(404) event.prepAwardsMatchesTeams() event.prep_details() medias_future = media_query.EventTeamsPreferredMediasQuery(event_key).fetch_async() district_future = DistrictQuery(event.district_key.id()).fetch_async() if event.district_key else None event_medias_future = media_query.EventMediasQuery(event_key).fetch_async() awards = AwardHelper.organizeAwards(event.awards) cleaned_matches = MatchHelper.deleteInvalidMatches(event.matches) matches = MatchHelper.organizeMatches(cleaned_matches) teams = TeamHelper.sortTeams(event.teams) # Organize medias by team image_medias = MediaHelper.get_images([media for media in medias_future.get_result()]) team_medias = defaultdict(list) for image_media in image_medias: for reference in image_media.references: team_medias[reference].append(image_media) team_and_medias = [] for team in teams: team_and_medias.append((team, team_medias.get(team.key, []))) num_teams = len(team_and_medias) middle_value = num_teams / 2 if num_teams % 2 != 0: middle_value += 1 teams_a, teams_b = team_and_medias[:middle_value], team_and_medias[middle_value:] oprs = [i for i in event.matchstats['oprs'].items()] if (event.matchstats is not None and 'oprs' in event.matchstats) else [] oprs = sorted(oprs, key=lambda t: t[1], reverse=True) # sort by OPR oprs = oprs[:15] # get the top 15 OPRs if event.now: matches_recent = MatchHelper.recentMatches(cleaned_matches) matches_upcoming = MatchHelper.upcomingMatches(cleaned_matches) else: matches_recent = None matches_upcoming = None bracket_table = MatchHelper.generateBracket(matches, event.alliance_selections) is_2015_playoff = EventHelper.is_2015_playoff(event_key) if is_2015_playoff: playoff_advancement = MatchHelper.generatePlayoffAdvancement2015(matches, event.alliance_selections) for comp_level in ['qf', 'sf']: if comp_level in bracket_table: del bracket_table[comp_level] else: playoff_advancement = None district_points_sorted = None if event.district_points: district_points_sorted = sorted(event.district_points['points'].items(), key=lambda (team, points): -points['total']) event_insights = event.details.insights if event.details else None event_insights_template = None if event_insights: event_insights_template = 'event_partials/event_insights_{}.html'.format(event.year) district = district_future.get_result() if district_future else None medias_by_slugname = MediaHelper.group_by_slugname([media for media in event_medias_future.get_result()]) self.template_values.update({ "event": event, "district_name": district.display_name if district else None, "district_abbrev": district.abbreviation if district else None, "matches": matches, "matches_recent": matches_recent, "matches_upcoming": matches_upcoming, "awards": awards, "teams_a": teams_a, "teams_b": teams_b, "num_teams": num_teams, "oprs": oprs, "bracket_table": bracket_table, "playoff_advancement": playoff_advancement, "district_points_sorted": district_points_sorted, "is_2015_playoff": is_2015_playoff, "event_insights_qual": event_insights['qual'] if event_insights else None, "event_insights_playoff": event_insights['playoff'] if event_insights else None, "event_insights_template": event_insights_template, "medias_by_slugname": medias_by_slugname, }) if event.within_a_day: self._cache_expiration = self.SHORT_CACHE_EXPIRATION return jinja2_engine.render('event_details.html', self.template_values)
def _render(self, event_key): event = Event.get_by_id(event_key) if not event: self.abort(404) event.prepAwardsMatchesTeams() awards = AwardHelper.organizeAwards(event.awards) if event.within_a_day: cleaned_matches = event.matches else: cleaned_matches = MatchHelper.deleteInvalidMatches(event.matches) matches = MatchHelper.organizeMatches(cleaned_matches) teams = TeamHelper.sortTeams(event.teams) num_teams = len(teams) middle_value = num_teams / 2 if num_teams % 2 != 0: middle_value += 1 teams_a, teams_b = teams[:middle_value], teams[middle_value:] oprs = [i for i in event.matchstats['oprs'].items() ] if (event.matchstats is not None and 'oprs' in event.matchstats) else [] oprs = sorted(oprs, key=lambda t: t[1], reverse=True) # sort by OPR oprs = oprs[:15] # get the top 15 OPRs if event.now: matches_recent = MatchHelper.recentMatches(cleaned_matches) matches_upcoming = MatchHelper.upcomingMatches(cleaned_matches) else: matches_recent = None matches_upcoming = None bracket_table = MatchHelper.generateBracket(matches, event.alliance_selections) if event.year == 2015: playoff_advancement = MatchHelper.generatePlayoffAdvancement2015( matches, event.alliance_selections) for comp_level in ['qf', 'sf']: if comp_level in bracket_table: del bracket_table[comp_level] else: playoff_advancement = None district_points_sorted = None if event.district_points: district_points_sorted = sorted( event.district_points['points'].items(), key=lambda (team, points): -points['total']) self.template_values.update({ "event": event, "matches": matches, "matches_recent": matches_recent, "matches_upcoming": matches_upcoming, "awards": awards, "teams_a": teams_a, "teams_b": teams_b, "num_teams": num_teams, "oprs": oprs, "bracket_table": bracket_table, "playoff_advancement": playoff_advancement, "district_points_sorted": district_points_sorted, }) if event.within_a_day: self._cache_expiration = self.SHORT_CACHE_EXPIRATION path = os.path.join(os.path.dirname(__file__), '../templates/event_details.html') return template.render(path, self.template_values)
def calculate_event_points(cls, event): match_key_futures = Match.query(Match.event == event.key).fetch_async(None, keys_only=True) award_key_futures = Award.query(Award.event == event.key).fetch_async(None, keys_only=True) match_futures = ndb.get_multi_async(match_key_futures.get_result()) award_futures = ndb.get_multi_async(award_key_futures.get_result()) POINTS_MULTIPLIER = 3 if event.event_type_enum == EventType.DISTRICT_CMP else 1 district_points = { 'points': defaultdict(lambda: { 'qual_points': 0, 'elim_points': 0, 'alliance_points': 0, 'award_points': 0, 'total': 0, }), 'tiebreakers': defaultdict(lambda: { # for tiebreaker stats that can't be calculated with 'points' 'qual_wins': 0, 'highest_qual_scores': [], }), } # match points if event.year == 2015: from helpers.match_helper import MatchHelper # circular import issue # qual match points are calculated by rank if event.rankings and len(event.rankings) > 1: rankings = event.rankings[1:] # skip title row num_teams = len(rankings) alpha = 1.07 for row in rankings: rank = int(row[0]) team = 'frc{}'.format(row[1]) qual_points = int(np.ceil(cls.inverf(float(num_teams - 2 * rank + 2) / (alpha * num_teams)) * (10.0 / cls.inverf(1.0 / alpha)) + 12)) district_points['points'][team]['qual_points'] = qual_points * POINTS_MULTIPLIER else: logging.warning("Event {} has no rankings for qual_points calculations!".format(event.key.id())) matches = MatchHelper.organizeMatches([mf.get_result() for mf in match_futures]) # qual match calculations. only used for tiebreaking for match in matches['qm']: for color in ['red', 'blue']: for team in match.alliances[color]['teams']: score = match.alliances[color]['score'] district_points['tiebreakers'][team]['highest_qual_scores'] = heapq.nlargest(3, district_points['tiebreakers'][team]['highest_qual_scores'] + [score]) # elim match point calculations # count number of matches played per team per comp level num_played = defaultdict(lambda: defaultdict(int)) for level in ['qf', 'sf']: for match in matches[level]: if not match.has_been_played: continue for color in ['red', 'blue']: for team in match.alliances[color]['teams']: num_played[level][team] += 1 # qf and sf points advancement = MatchHelper.generatePlayoffAdvancement2015(matches) for last_level, level in [('qf', 'sf'), ('sf', 'f')]: for (teams, _, _) in advancement[last_level]: teams = ['frc{}'.format(t) for t in teams] done = False for match in matches[level]: for color in ['red', 'blue']: if set(teams).intersection(set(match.alliances[color]['teams'])) != set(): for team in teams: points = 5.0 if last_level == 'qf' else 3.3 district_points['points'][team]['elim_points'] += int(np.ceil(points * num_played[last_level][team])) * POINTS_MULTIPLIER done = True break if done: break if done: break # final points num_wins = {'red': 0, 'blue': 0} team_matches_played = {'red': [], 'blue': []} for match in matches['f']: if not match.has_been_played or match.winning_alliance == '': continue num_wins[match.winning_alliance] += 1 for team in match.alliances[match.winning_alliance]['teams']: team_matches_played[match.winning_alliance].append(team) if num_wins[match.winning_alliance] >= 2: for team in team_matches_played[match.winning_alliance]: district_points['points'][team]['elim_points'] += 5 * POINTS_MULTIPLIER else: elim_num_wins = defaultdict(lambda: defaultdict(int)) elim_alliances = defaultdict(lambda: defaultdict(list)) for match_future in match_futures: match = match_future.get_result() if not match.has_been_played: continue if match.comp_level == 'qm': if match.winning_alliance == '': for team in match.team_key_names: district_points['points'][team]['qual_points'] += 1 * POINTS_MULTIPLIER else: for team in match.alliances[match.winning_alliance]['teams']: district_points['points'][team]['qual_points'] += 2 * POINTS_MULTIPLIER district_points['tiebreakers'][team]['qual_wins'] += 1 for color in ['red', 'blue']: for team in match.alliances[color]['teams']: score = match.alliances[color]['score'] district_points['tiebreakers'][team]['highest_qual_scores'] = heapq.nlargest(3, district_points['tiebreakers'][team]['highest_qual_scores'] + [score]) else: if match.winning_alliance == '': continue match_set_key = '{}_{}{}'.format(match.event.id(), match.comp_level, match.set_number) elim_num_wins[match_set_key][match.winning_alliance] += 1 elim_alliances[match_set_key][match.winning_alliance] += match.alliances[match.winning_alliance]['teams'] if elim_num_wins[match_set_key][match.winning_alliance] >= 2: for team in elim_alliances[match_set_key][match.winning_alliance]: district_points['points'][team]['elim_points'] += 5* POINTS_MULTIPLIER # alliance points if event.alliance_selections: selection_points = EventHelper.alliance_selections_to_points(event.alliance_selections) for team, points in selection_points.items(): district_points['points'][team]['alliance_points'] += points * POINTS_MULTIPLIER else: logging.warning("Event {} has no alliance selection district_points!".format(event.key.id())) # award points for award_future in award_futures: award = award_future.get_result() if award.award_type_enum not in AwardType.NON_JUDGED_NON_TEAM_AWARDS: if award.award_type_enum == AwardType.CHAIRMANS: point_value = 10 elif award.award_type_enum in {AwardType.ENGINEERING_INSPIRATION, AwardType.ROOKIE_ALL_STAR}: point_value = 8 else: point_value = 5 for team in award.team_list: district_points['points'][team.id()]['award_points'] += point_value * POINTS_MULTIPLIER for team, point_breakdown in district_points['points'].items(): for p in point_breakdown.values(): district_points['points'][team]['total'] += p return district_points