def test_parse_qual(self): self.event = Event( id="2016nyny", name="NYC Regional", event_type_enum=EventType.REGIONAL, short_name="NYC", event_short="nyny", year=2016, end_date=datetime(2016, 03, 27), official=True, start_date=datetime(2016, 03, 24), timezone_id="America/New_York" ) self.event.put() with open('test_data/fms_api/2016_nyny_hybrid_schedule_qual.json', 'r') as f: matches, _ = FMSAPIHybridScheduleParser(2016, 'nyny').parse(json.loads(f.read())) self.assertTrue(isinstance(matches, list)) self.assertEqual(len(matches), 88) # Assert we get enough of each match type clean_matches = MatchHelper.organizeMatches(matches) self.assertEqual(len(clean_matches["qm"]), 88) # Changed format in 2018 with open('test_data/fms_api/2016_nyny_hybrid_schedule_qual_2018update.json', 'r') as f: matches, _ = FMSAPIHybridScheduleParser(2016, 'nyny').parse(json.loads(f.read())) self.assertTrue(isinstance(matches, list)) self.assertEqual(len(matches), 88) # Assert we get enough of each match type clean_matches = MatchHelper.organizeMatches(matches) self.assertEqual(len(clean_matches["qm"]), 88)
def get(self, event_key): event = Event.get_by_id(event_key) matchstats_dict = MatchstatsHelper.calculate_matchstats( event.matches, event.year) if event.year == 2016: organized_matches = MatchHelper.organizeMatches(event.matches) match_predictions, match_prediction_stats = PredictionHelper.get_match_predictions( organized_matches['qm']) ranking_predictions, ranking_prediction_stats = PredictionHelper.get_ranking_predictions( organized_matches['qm'], match_predictions) matchstats_dict['match_predictions'] = match_predictions matchstats_dict['match_prediction_stats'] = match_prediction_stats matchstats_dict['ranking_predictions'] = ranking_predictions matchstats_dict[ 'ranking_prediction_stats'] = ranking_prediction_stats if any([v != {} for v in matchstats_dict.values()]): event.matchstats_json = json.dumps(matchstats_dict) EventManipulator.createOrUpdate(event) else: logging.warn( "Matchstat calculation for {} failed!".format(event_key)) template_values = { 'matchstats_dict': matchstats_dict, } path = os.path.join(os.path.dirname(__file__), '../templates/math/event_matchstats_do.html') self.response.out.write(template.render(path, template_values))
def test_parse_foc_b05(self): self.event = Event( id="2017nhfoc", name="FIRST Festival of Champions", event_type_enum=EventType.CMP_FINALS, short_name="FIRST Festival of Champions", event_short="nhfoc", first_code="foc", year=2017, end_date=datetime(2017, 07, 29), official=True, start_date=datetime(2017, 07, 29), timezone_id="America/New_York", playoff_type=PlayoffType.BO5_FINALS ) self.event.put() with open('test_data/fms_api/2017foc_staging_hybrid_schedule_playoff.json', 'r') as f: matches, _ = FMSAPIHybridScheduleParser(2017, 'nhfoc').parse(json.loads(f.read())) self.assertTrue(isinstance(matches, list)) self.assertEquals(len(matches), 5) # Assert we get enough of each match type clean_matches = MatchHelper.organizeMatches(matches) self.assertEqual(len(clean_matches["ef"]), 0) self.assertEqual(len(clean_matches["qf"]), 0) self.assertEqual(len(clean_matches["sf"]), 0) self.assertEqual(len(clean_matches["f"]), 5) for i, match in enumerate(clean_matches['f']): self.assertEqual(match.set_number, 1) self.assertEqual(match.match_number, i+1)
def generateTeamAtEventStatusAsync(cls, team_key, event): """ Generate Team@Event status items :return: a tuple future <long summary string, qual record, qual ranking, playoff status> """ team_number = team_key[3:] event.prep_details() # We need all the event's playoff matches here to properly account for backup teams matches = yield EventMatchesQuery(event.key.id()).fetch_async() qual_match_count = 0 playoff_match_count = 0 playoff_matches = [] for match in matches: if match.comp_level in Match.ELIM_LEVELS: playoff_match_count += 1 playoff_matches.append(match) else: qual_match_count += 1 matches = MatchHelper.organizeMatches(playoff_matches) team_status = cls.generate_team_at_event_status(team_key, event, matches) rank_status = team_status.get('rank', None) alliance_status = team_status.get('alliance', None) playoff_status = team_status.get('playoff', None) # Playoff Status status, short_playoff_status = cls._get_playoff_status_string(team_key, alliance_status, playoff_status) # Still in quals or team did not make it to elims if not rank_status or rank_status.get('played', 0) == 0: # No matches played yet status = "Team {} has not played any matches yet.".format(team_number) if not status else status record = '?' rank_str = '?' else: # Compute rank & num_teams # Gets record from ranking data to account for surrogate matches rank = rank_status.get('rank', '?') ranking_points = rank_status.get('first_sort', '?') record = rank_status.get('record', '?') num_teams = rank_status.get('total', '?') rank_str = "Rank {} with {} RP".format(rank, ranking_points) alliance_name = alliance_status.get('name', '?') if alliance_status else '?' # Compute final long status for nightbot, if one isn't already there matches_per_team = qual_match_count // rank_status.get('total', 1) if rank_status.get('played', 0) - matches_per_team > 0 and not status: if rank is not None: status = "Team {} is currently rank {}/{} with a record of {} and {} ranking points.".format(team_number, rank, num_teams, record, ranking_points) else: status = "Team {} currently has a record of {}.".format(team_number, record) elif not status: if alliance_status is None and playoff_match_count == 0: status = "Team {} ended qualification matches at rank {}/{} with a record of {}.".format(team_number, rank, num_teams, record) elif alliance_status is None and playoff_match_count > 0: status = "Team {} ended qualification matches at rank {}/{} with a record of {} and was not picked for playoff matches.".format(team_number, rank, num_teams, record) else: status = "Team {} will be competing in the playoff matches on {}.".format(team_number, alliance_name) raise ndb.Return(status, record, rank_str, short_playoff_status)
def test_parse_2champs_einstein(self): self.event = Event( id="2017cmptx", name="Einstein (Houston)", event_type_enum=EventType.CMP_FINALS, short_name="Einstein", event_short="cmptx", year=2017, end_date=datetime(2017, 03, 27), official=True, start_date=datetime(2017, 03, 24), timezone_id="America/New_York", playoff_type=PlayoffType.ROUND_ROBIN_6_TEAM ) self.event.put() with open('test_data/fms_api/2017cmptx_staging_playoff_schedule.json', 'r') as f: matches, _ = FMSAPIHybridScheduleParser(2017, 'cmptx').parse(json.loads(f.read())) self.assertTrue(isinstance(matches, list)) self.assertEquals(len(matches), 18) # Assert we get enough of each match type clean_matches = MatchHelper.organizeMatches(matches) self.assertEqual(len(clean_matches["ef"]), 0) self.assertEqual(len(clean_matches["qf"]), 0) self.assertEqual(len(clean_matches["sf"]), 15) self.assertEqual(len(clean_matches["f"]), 3)
def test_parse_2015_playoff(self): self.event = Event( id="2015nyny", name="NYC Regional", event_type_enum=EventType.REGIONAL, short_name="NYC", event_short="nyny", year=2015, end_date=datetime(2015, 03, 27), official=True, start_date=datetime(2015, 03, 24), timezone_id="America/New_York", playoff_type=PlayoffType.AVG_SCORE_8_TEAM ) self.event.put() with open('test_data/fms_api/2015nyny_hybrid_schedule_playoff.json', 'r') as f: matches, _ = FMSAPIHybridScheduleParser(2015, 'nyny').parse(json.loads(f.read())) self.assertTrue(isinstance(matches, list)) self.assertEqual(len(matches), 17) # Assert we get enough of each match type clean_matches = MatchHelper.organizeMatches(matches) self.assertEqual(len(clean_matches["ef"]), 0) self.assertEqual(len(clean_matches["qf"]), 8) self.assertEqual(len(clean_matches["sf"]), 6) self.assertEqual(len(clean_matches["f"]), 3)
def generate_team_at_event_status(cls, team_key, event, matches=None): """ Generate a dict containing team@event status information :param team_key: Key name of the team to focus on :param event: Event object :param matches: Organized matches (via MatchHelper.organizeMatches) from the event, optional """ event_details = event.details if not matches: matches = event.matches team_matches = [m for m in matches if team_key in m.team_key_names] next_match = MatchHelper.upcomingMatches(team_matches, num=1) last_match = MatchHelper.recentMatches(team_matches, num=1) matches = MatchHelper.organizeMatches(matches) return copy.deepcopy( { 'qual': cls._build_qual_info(team_key, event_details, matches, event.year), 'alliance': cls._build_alliance_info(team_key, event_details, matches), 'playoff': cls._build_playoff_info(team_key, event_details, matches, event.year, event.playoff_type), 'last_match_key': last_match[0].key_name if last_match else None, 'next_match_key': next_match[0].key_name if next_match else None, } ) # TODO: Results are getting mixed unless copied. 2017-02-03 -fangeugene
def _render(self, event_key): event_future = EventQuery(event_key).fetch_async(return_updated=True) matches_future = EventMatchesQuery(event_key).fetch_async(return_updated=True) event, event_updated = event_future.get_result() matches, matches_updated = matches_future.get_result() self._last_modified = max(event_updated, matches_updated) cleaned_matches = MatchHelper.deleteInvalidMatches(matches, event) matches = MatchHelper.organizeMatches(cleaned_matches) bracket_table, playoff_advancement, _, _ = PlayoffAdvancementHelper.generatePlayoffAdvancement(event, matches) output = [] for level in Match.ELIM_LEVELS: level_ranks = [] if playoff_advancement and playoff_advancement.get(level): if event.playoff_type == PlayoffType.AVG_SCORE_8_TEAM: level_ranks = PlayoffAdvancementHelper.transform2015AdvancementLevelForApi(event, playoff_advancement, level) else: level_ranks = PlayoffAdvancementHelper.transformRoundRobinAdvancementLevelForApi(event, playoff_advancement, level) elif bracket_table and bracket_table.get(level): level_ranks = PlayoffAdvancementHelper.transformBracketLevelForApi(event, bracket_table, level) output.extend(level_ranks) return json.dumps(output, ensure_ascii=True, indent=2, sort_keys=True)
def test_parse_2champs_einstein(self): self.event = Event( id="2017cmptx", name="Einstein (Houston)", event_type_enum=EventType.CMP_FINALS, short_name="Einstein", event_short="cmptx", year=2017, end_date=datetime(2017, 03, 27), official=True, start_date=datetime(2017, 03, 24), timezone_id="America/New_York", playoff_type=PlayoffType.ROUND_ROBIN_6_TEAM ) self.event.put() with open('test_data/fms_api/2017cmptx_staging_playoff_schedule.json', 'r') as f: matches, _ = FMSAPIHybridScheduleParser(2017, 'cmptx').parse(json.loads(f.read())) self.assertTrue(isinstance(matches, list)) self.assertEquals(len(matches), 18) # Assert we get enough of each match type clean_matches = MatchHelper.organizeMatches(matches) self.assertEqual(len(clean_matches["ef"]), 0) self.assertEqual(len(clean_matches["qf"]), 0) self.assertEqual(len(clean_matches["sf"]), 15) self.assertEqual(len(clean_matches["f"]), 3)
def test_parse_2017micmp(self): # 2017micmp is a 4 team bracket that starts playoff match numbering at 1 self.event = Event( id="2017micmp", name="Michigan District Champs", event_type_enum=EventType.DISTRICT_CMP, short_name="Michigan", event_short="micmp", year=2017, end_date=datetime(2017, 03, 27), official=True, start_date=datetime(2017, 03, 24), timezone_id="America/New_York", playoff_type=PlayoffType.BRACKET_4_TEAM ) self.event.put() with open('test_data/fms_api/2017micmp_playoff_schedule.json', 'r') as f: matches, _ = FMSAPIHybridScheduleParser(2017, 'micmp').parse(json.loads(f.read())) self.assertTrue(isinstance(matches, list)) self.assertEquals(len(matches), 6) # Assert we get enough of each match type clean_matches = MatchHelper.organizeMatches(matches) self.assertEqual(len(clean_matches["ef"]), 0) self.assertEqual(len(clean_matches["qf"]), 0) self.assertEqual(len(clean_matches["sf"]), 4) self.assertEqual(len(clean_matches["f"]), 2)
def get_event_winners(cls, event, matches): """ First alliance to win two finals matches is the winner """ matches_by_type = MatchHelper.organizeMatches(matches) if 'f' not in matches_by_type or not matches_by_type['f']: return set() finals_matches = matches_by_type['f'] red_wins = 0 blue_wins = 0 for match in finals_matches: if match.has_been_played: if match.winning_alliance == 'red': red_wins += 1 elif match.winning_alliance == 'blue': blue_wins += 1 winning_teams = set() if red_wins >= 2: winning_teams = set(finals_matches[0].alliances['red']['teams']) elif blue_wins >= 2: winning_teams = set(finals_matches[0].alliances['blue']['teams']) # Return the entire alliance alliance_selections = event.alliance_selections if alliance_selections: for alliance in alliance_selections: if len(winning_teams.intersection(set(alliance['picks']))) >= 2: complete_alliance = set(alliance['picks']) if alliance else set() if alliance and alliance.get('backup'): complete_alliance.add(alliance['backup']['in']) return complete_alliance # Fall back to the match winners return winning_teams
def test_parse_2017micmp(self): # 2017micmp is a 4 team bracket that starts playoff match numbering at 1 self.event = Event( id="2017micmp", name="Michigan District Champs", event_type_enum=EventType.DISTRICT_CMP, short_name="Michigan", event_short="micmp", year=2017, end_date=datetime(2017, 03, 27), official=True, start_date=datetime(2017, 03, 24), timezone_id="America/New_York", playoff_type=PlayoffType.BRACKET_4_TEAM ) self.event.put() with open('test_data/fms_api/2017micmp_playoff_schedule.json', 'r') as f: matches, _ = FMSAPIHybridScheduleParser(2017, 'micmp').parse(json.loads(f.read())) self.assertTrue(isinstance(matches, list)) self.assertEquals(len(matches), 6) # Assert we get enough of each match type clean_matches = MatchHelper.organizeMatches(matches) self.assertEqual(len(clean_matches["ef"]), 0) self.assertEqual(len(clean_matches["qf"]), 0) self.assertEqual(len(clean_matches["sf"]), 4) self.assertEqual(len(clean_matches["f"]), 2)
def test_parse_foc_b05(self): self.event = Event( id="2017nhfoc", name="FIRST Festival of Champions", event_type_enum=EventType.CMP_FINALS, short_name="FIRST Festival of Champions", event_short="nhfoc", first_code="foc", year=2017, end_date=datetime(2017, 07, 29), official=True, start_date=datetime(2017, 07, 29), timezone_id="America/New_York", playoff_type=PlayoffType.BO5_FINALS ) self.event.put() with open('test_data/fms_api/2017foc_staging_hybrid_schedule_playoff.json', 'r') as f: matches, _ = FMSAPIHybridScheduleParser(2017, 'nhfoc').parse(json.loads(f.read())) self.assertTrue(isinstance(matches, list)) self.assertEquals(len(matches), 5) # Assert we get enough of each match type clean_matches = MatchHelper.organizeMatches(matches) self.assertEqual(len(clean_matches["ef"]), 0) self.assertEqual(len(clean_matches["qf"]), 0) self.assertEqual(len(clean_matches["sf"]), 0) self.assertEqual(len(clean_matches["f"]), 5) for i, match in enumerate(clean_matches['f']): self.assertEqual(match.set_number, 1) self.assertEqual(match.match_number, i+1)
def get(self, event_key): event = Event.get_by_id(event_key) if not event: self.abort(404) event_future = EventQuery(event_key).fetch_async(return_updated=True) matches_future = EventMatchesQuery(event_key).fetch_async( return_updated=True) event, _ = event_future.get_result() matches, _ = matches_future.get_result() cleaned_matches = MatchHelper.deleteInvalidMatches(matches, event) matches = MatchHelper.organizeMatches(cleaned_matches) bracket_table, playoff_advancement, _, _ = PlayoffAdvancementHelper.generatePlayoffAdvancement( event, matches) event_details = EventDetails( id=event.key_name, playoff_advancement={ 'advancement': playoff_advancement, 'bracket': bracket_table, }, ) EventDetailsManipulator.createOrUpdate(event_details) self.response.out.write("New playoff advancement for {}\n{}".format( event.key_name, json.dumps(event_details.playoff_advancement, indent=2, sort_keys=True)))
def test_parse_playoff_with_octofinals(self): self.event = Event(id="2016micmp", name="Michigan District Champs", event_type_enum=EventType.DISTRICT_CMP, short_name="Michigan", event_short="micmp", year=2016, end_date=datetime(2016, 03, 27), official=True, start_date=datetime(2016, 03, 24), timezone_id="America/New_York") self.event.put() with open( 'test_data/fms_api/2016_micmp_staging_hybrid_schedule_playoff.json', 'r') as f: matches = FMSAPIHybridScheduleParser(2016, 'micmp').parse( json.loads(f.read())) self.assertTrue(isinstance(matches, list)) self.assertEquals(len(matches), 36) # Assert we get enough of each match type clean_matches = MatchHelper.organizeMatches(matches) self.assertEqual(len(clean_matches["ef"]), 20) self.assertEqual(len(clean_matches["qf"]), 10) self.assertEqual(len(clean_matches["sf"]), 4) self.assertEqual(len(clean_matches["f"]), 2)
def test_parse_playoff_with_octofinals(self): self.event = Event( id="2016micmp", name="Michigan District Champs", event_type_enum=EventType.DISTRICT_CMP, short_name="Michigan", event_short="micmp", year=2016, end_date=datetime(2016, 03, 27), official=True, start_date=datetime(2016, 03, 24), timezone_id="America/New_York" ) self.event.put() with open('test_data/fms_api/2016_micmp_staging_hybrid_schedule_playoff.json', 'r') as f: matches = FMSAPIHybridScheduleParser(2016, 'micmp').parse(json.loads(f.read())) self.assertTrue(isinstance(matches, list)) self.assertEquals(len(matches), 36) # Assert we get enough of each match type clean_matches = MatchHelper.organizeMatches(matches) self.assertEqual(len(clean_matches["ef"]), 20) self.assertEqual(len(clean_matches["qf"]), 10) self.assertEqual(len(clean_matches["sf"]), 4) self.assertEqual(len(clean_matches["f"]), 2)
def get(self, event_key): event = Event.get_by_id(event_key) matchstats_dict = MatchstatsHelper.calculate_matchstats(event.matches, event.year) if event.year == 2016: organized_matches = MatchHelper.organizeMatches(event.matches) match_predictions, match_prediction_stats = PredictionHelper.get_match_predictions(organized_matches['qm']) ranking_predictions, ranking_prediction_stats = PredictionHelper.get_ranking_predictions(organized_matches['qm'], match_predictions) matchstats_dict['match_predictions'] = match_predictions matchstats_dict['match_prediction_stats'] = match_prediction_stats matchstats_dict['ranking_predictions'] = ranking_predictions matchstats_dict['ranking_prediction_stats'] = ranking_prediction_stats if any([v != {} for v in matchstats_dict.values()]): event.matchstats_json = json.dumps(matchstats_dict) EventManipulator.createOrUpdate(event) else: logging.warn("Matchstat calculation for {} failed!".format(event_key)) template_values = { 'matchstats_dict': matchstats_dict, } path = os.path.join(os.path.dirname(__file__), '../templates/math/event_matchstats_do.html') self.response.out.write(template.render(path, template_values))
def get(self, event_key, comp_level, to_delete): self._require_admin() event = Event.get_by_id(event_key) if not event: self.abort(404) organized_matches = MatchHelper.organizeMatches(event.matches) if comp_level not in organized_matches: self.abort(400) return matches_to_delete = [] if to_delete == 'all': matches_to_delete = [m for m in organized_matches[comp_level]] elif to_delete == 'unplayed': matches_to_delete = [ m for m in organized_matches[comp_level] if not m.has_been_played ] delete_count = len(matches_to_delete) if matches_to_delete: MatchManipulator.delete(matches_to_delete) self.redirect("/admin/event/{}?deleted={}#matches".format( event_key, delete_count))
def test_parse_playoff(self): self.event = Event(id="2016nyny", name="NYC Regional", event_type_enum=EventType.REGIONAL, short_name="NYC", event_short="nyny", year=2016, end_date=datetime(2016, 03, 27), official=True, start_date=datetime(2016, 03, 24), timezone_id="America/New_York") self.event.put() with open('test_data/fms_api/2016_nyny_hybrid_schedule_playoff.json', 'r') as f: matches = FMSAPIHybridScheduleParser(2016, 'nyny').parse( json.loads(f.read())) self.assertTrue(isinstance(matches, list)) self.assertEqual(len(matches), 15) # Assert we get enough of each match type clean_matches = MatchHelper.organizeMatches(matches) self.assertEqual(len(clean_matches["ef"]), 0) self.assertEqual(len(clean_matches["qf"]), 9) self.assertEqual(len(clean_matches["sf"]), 4) self.assertEqual(len(clean_matches["f"]), 2)
def _render(self, event_key): event = Event.get_by_id(event_key) if not event: return self.redirect("/error/404") event.prepAwards() event.prepMatches() event.prepTeams() awards = AwardHelper.organizeAwards(event.awards) matches = MatchHelper.organizeMatches(event.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 = sorted(zip(event.oprs,event.opr_teams), reverse=True) # sort by OPR oprs = oprs[:14] # get the top 15 OPRs if event.within_a_day: matches_recent = MatchHelper.recentMatches(event.matches) matches_upcoming = MatchHelper.upcomingMatches(event.matches) else: matches_recent = None matches_upcoming = None bracket_table = {} qf_matches = matches['qf'] sf_matches = matches['sf'] f_matches = matches['f'] if qf_matches: bracket_table['qf'] = MatchHelper.generateBracket(qf_matches) if sf_matches: bracket_table['sf'] = MatchHelper.generateBracket(sf_matches) if f_matches: bracket_table['f'] = MatchHelper.generateBracket(f_matches) template_values = { "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, } 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, 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.within_a_day: matches_recent = MatchHelper.recentMatches(cleaned_matches) matches_upcoming = MatchHelper.upcomingMatches(cleaned_matches) else: matches_recent = None matches_upcoming = None bracket_table = {} qf_matches = matches['qf'] sf_matches = matches['sf'] f_matches = matches['f'] if qf_matches: bracket_table['qf'] = MatchHelper.generateBracket(qf_matches) if sf_matches: bracket_table['sf'] = MatchHelper.generateBracket(sf_matches) if f_matches: bracket_table['f'] = MatchHelper.generateBracket(f_matches) template_values = { "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, } 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, 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.within_a_day: 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) 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, "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 calc_rank_based_match_points(cls, event, district_points, matches, 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 rankings = event.details.rankings2 if rankings: num_teams = len(rankings) alpha = 1.07 for ranking in rankings: rank = ranking['rank'] team = ranking['team_key'] qual_points = int( math.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: msg = "Event {} has no rankings for qual_points calculations!".format( event.key.id()) if event.event_type_enum in EventType.SEASON_EVENT_TYPES: logging.warning(msg) else: logging.info(msg) matches = MatchHelper.organizeMatches(matches) # 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 if event.year == 2015: cls.calc_elim_match_points_2015(district_points, matches, POINTS_MULTIPLIER) else: elim_matches = matches.get('qf', []) + matches.get( 'sf', []) + matches.get('f', []) cls.calc_elim_match_points(district_points, elim_matches, POINTS_MULTIPLIER)
def generateTeamAtEventStatusAsync(cls, team_key, event): """ Generate Team@Event status items :return: a tuple future <long summary string, qual record, qual ranking, playoff status> """ team_number = team_key[3:] matches, event_details = yield TeamEventMatchesQuery( team_key, event.key.id()).fetch_async(), EventDetails.get_by_id_async( event.key.id()) matches = MatchHelper.organizeMatches(matches) # Compute alliances alliance_number = cls._get_alliance_number(team_key, event_details) # Playoff Status status, short_playoff_status = cls._get_playoff_status( team_key, matches, alliance_number) # Still in quals or team did not make it to elims # Compute qual W-L-T wins, losses, ties, unplayed_qual = cls._get_qual_wlt( team_key, matches) if wins == 0 and losses == 0 and ties == 0: # No matches played yet status = "Team {} has not played any matches yet.".format( team_number) if not status else status # Compute rank & num_teams # Gets record from ranking data to account for surrogate matches rank, ranking_points, record, num_teams = cls._get_rank( team_number, event_details) rank_str = "Rank {} with {} RP".format(rank, ranking_points) # Compute final long status for nightbot, if one isn't already there if unplayed_qual > 0 and not status: if rank is not None: status = "Team {} is currently rank {}/{} with a record of {} and {} ranking points.".format( team_number, rank, num_teams, record, ranking_points) else: status = "Team {} currently has a record of {}.".format( team_number, record) elif not status: if alliance_number is None: status = "Team {} ended qualification matches at rank {}/{} with a record of {}.".format( team_number, rank, num_teams, record) elif alliance_number == 0: status = "Team {} ended qualification matches at rank {}/{} with a record of {} and was not picked for playoff matches.".format( team_number, rank, num_teams, record) else: status = "Team {} will be competing in the playoff matches on alliance #{}.".format( team_number, alliance_number) raise ndb.Return(status, record, rank_str, short_playoff_status)
def _render(self, event_key): event = Event.get_by_id(event_key) if not event: self.abort(404) matches = MatchHelper.organizeMatches(event.matches) self.template_values.update({"event": event, "matches": matches, "datetime": datetime.datetime.now()}) path = os.path.join(os.path.dirname(__file__), "../templates/event_rss.xml") self.response.headers["content-type"] = "application/xml; charset=UTF-8" return template.render(path, self.template_values)
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 if event.year == 2015: cls.calc_elim_match_points_2015(district_points, matches, POINTS_MULTIPLIER) else: elim_matches = matches.get('qf', []) + matches.get( 'sf', []) + matches.get('f', []) cls.calc_elim_match_points(district_points, elim_matches, POINTS_MULTIPLIER)
def post(self): self._require_admin() event_key = self.request.get("event_key").strip() event = Event.get_by_id(event_key) if not event: self.redirect("/admin/event/" + event.key_name) return if event.playoff_type != PlayoffType.ROUND_ROBIN_6_TEAM: logging.warning("Can't set advancement for non-round robin events") self.redirect("/admin/event/" + event.key_name) return advancement_csv = self.request.get("advancement_csv") comp_level = self.request.get("comp_level") matches_per_team = int(self.request.get("num_matches")) if comp_level not in Match.ELIM_LEVELS: logging.warning("Bad comp level: {}".format(comp_level)) self.redirect("/admin/event/" + event.key_name) return parsed_advancement = CSVAdvancementParser.parse( advancement_csv, matches_per_team) advancement = PlayoffAdvancementHelper.generatePlayoffAdvancementFromCSV( event, parsed_advancement, comp_level) cleaned_matches = MatchHelper.deleteInvalidMatches( event.matches, event) matches = MatchHelper.organizeMatches(cleaned_matches) bracket_table = PlayoffAdvancementHelper.generateBracket( matches, event, event.alliance_selections) comp_levels = bracket_table.keys() for comp_level in comp_levels: if comp_level != 'f': del bracket_table[comp_level] existing_details = EventDetails.get_by_id(event.key_name) new_advancement = existing_details.playoff_advancement if existing_details and existing_details.playoff_advancement else {} new_advancement.update(advancement) event_details = EventDetails( id=event.key_name, playoff_advancement={ 'advancement': new_advancement, 'bracket': bracket_table, }, ) EventDetailsManipulator.createOrUpdate(event_details) self.redirect("/admin/event/" + event.key_name) return
def calc_rank_based_match_points(cls, event, district_points, matches, 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: msg = "Event {} has no rankings for qual_points calculations!".format(event.key.id()) if event.event_type_enum in EventType.SEASON_EVENT_TYPES: logging.warning(msg) else: logging.info(msg) matches = MatchHelper.organizeMatches(matches) # 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 if event.year == 2015: cls.calc_elim_match_points_2015(district_points, matches, POINTS_MULTIPLIER) else: elim_matches = matches.get("qf", []) + matches.get("sf", []) + matches.get("f", []) cls.calc_elim_match_points(district_points, elim_matches, POINTS_MULTIPLIER)
def _render(self, event_key): event = Event.get_by_id(event_key) if not event: self.abort(404) matches = MatchHelper.organizeMatches(event.matches) self.template_values.update({ "event": event, "matches": matches, "datetime": datetime.datetime.now() }) self.response.headers['content-type'] = 'application/xml; charset=UTF-8' return jinja2_engine.render('event_rss.xml', self.template_values)
def _render(self, event_key): event = Event.get_by_id(event_key) if not event: self.abort(404) matches = MatchHelper.organizeMatches(event.matches) self.template_values.update({ "event": event, "matches": matches, "datetime": datetime.datetime.now() }) self.response.headers['content-type'] = 'application/xml; charset=UTF-8' return jinja2_engine.render('event_rss.xml', self.template_values)
def _render(self, event_key): event = Event.get_by_id(event_key) if not event: self.abort(404) matches = MatchHelper.organizeMatches(event.matches) self.template_values.update({ "event": event, "matches": matches, "datetime": datetime.datetime.now() }) path = os.path.join(os.path.dirname(__file__), '../templates/event_rss.xml') self.response.headers['content-type'] = 'application/xml; charset=UTF-8' return template.render(path, self.template_values)
def _render(self, event_key): event = Event.get_by_id(event_key) if not event: return self.redirect("/error/404") matches = MatchHelper.organizeMatches(event.matches) template_values = { "event": event, "matches": matches, "datetime": datetime.datetime.now() } path = os.path.join(os.path.dirname(__file__), '../templates/event_rss.xml') self.response.headers.add_header('content-type', 'application/xml', charset='utf-8') return template.render(path, template_values)
def generate_team_at_event_status(cls, team_key, event, matches=None): """ Generate a dict containing team@event status information :param team_key: Key name of the team to focus on :param event: Event object :param matches: Organized matches (via MatchHelper.organizeMatches) from the event, optional """ event_details = event.details if not matches: matches = event.matches matches = MatchHelper.organizeMatches(matches) return copy.deepcopy({ 'qual': cls._build_qual_info(team_key, event_details, matches, event.year), 'alliance': cls._build_alliance_info(team_key, event_details, matches), 'playoff': cls._build_playoff_info(team_key, event_details, matches, event.year) }) # TODO: Results are getting mixed unless copied. 2017-02-03 -fangeugene
def get(self, event_key): # Fetch for later event_future = Event.get_by_id_async(event_key) matches_future = match_query.EventMatchesQuery(event_key).fetch_async() # Rebuild event teams taskqueue.add( url='/tasks/math/do/eventteam_update/' + event_key, method='GET') # Create Winner/Finalist awards for offseason events awards = [] event = event_future.get_result() if event.event_type_enum in {EventType.OFFSEASON, EventType.FOC}: matches = MatchHelper.organizeMatches(matches_future.get_result()) bracket = MatchHelper.generateBracket(matches, event, event.alliance_selections) if 'f' in bracket: winning_alliance = '{}_alliance'.format(bracket['f'][1]['winning_alliance']) if winning_alliance == 'red_alliance': losing_alliance = 'blue_alliance' else: losing_alliance = 'red_alliance' awards.append(Award( id=Award.render_key_name(event.key_name, AwardType.WINNER), name_str="Winner", award_type_enum=AwardType.WINNER, year=event.year, event=event.key, event_type_enum=event.event_type_enum, team_list=[ndb.Key(Team, 'frc{}'.format(team)) for team in bracket['f'][1][winning_alliance] if team.isdigit()], recipient_json_list=[json.dumps({'team_number': team, 'awardee': None}) for team in bracket['f'][1][winning_alliance]], )) awards.append(Award( id=Award.render_key_name(event.key_name, AwardType.FINALIST), name_str="Finalist", award_type_enum=AwardType.FINALIST, year=event.year, event=event.key, event_type_enum=event.event_type_enum, team_list=[ndb.Key(Team, 'frc{}'.format(team)) for team in bracket['f'][1][losing_alliance] if team.isdigit()], recipient_json_list=[json.dumps({'team_number': team, 'awardee': None}) for team in bracket['f'][1][losing_alliance]], )) AwardManipulator.createOrUpdate(awards) self.response.out.write("Finished post-event tasks for {}. Created awards: {}".format(event_key, awards))
def generate_team_at_event_status(cls, team_key, event, matches=None): """ Generate a dict containing team@event status information :param team_key: Key name of the team to focus on :param event: Event object :param matches: Organized matches (via MatchHelper.organizeMatches) from the event, optional """ event_details = event.details if not matches: matches = event.matches matches = [match for match in matches if match.comp_level in Match.ELIM_LEVELS] matches = MatchHelper.organizeMatches(matches) return { 'rank': cls._build_ranking_info(team_key, event_details), 'alliance': cls._build_alliance_info(team_key, event_details), 'playoff': cls._build_playoff_status(team_key, event_details, matches) }
def calc_rank_based_match_points(cls, event, district_points, matches, 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 rankings = event.details.rankings2 if rankings: num_teams = len(rankings) alpha = 1.07 for ranking in rankings: rank = ranking['rank'] team = ranking['team_key'] qual_points = int(math.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: msg = "Event {} has no rankings for qual_points calculations!".format(event.key.id()) if event.event_type_enum in EventType.SEASON_EVENT_TYPES: logging.warning(msg) else: logging.info(msg) matches = MatchHelper.organizeMatches(matches) # 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 if event.year == 2015: cls.calc_elim_match_points_2015(district_points, matches, POINTS_MULTIPLIER) else: elim_matches = matches.get('qf', []) + matches.get('sf', []) + matches.get('f', []) cls.calc_elim_match_points(district_points, elim_matches, POINTS_MULTIPLIER)
def generateTeamAtEventStatus(cls, team_key, event): """ Generate Team@Event status items :return: a tuple <long summary string, qual record, qual ranking, playoff status> """ team_number = team_key[3:] matches_future = TeamEventMatchesQuery(team_key, event.key.id()).fetch_async() matches = MatchHelper.organizeMatches(matches_future.get_result()) # Compute alliances alliance_number = cls._get_alliance_number(team_key, event) # Playoff Status status, short_playoff_status = cls._get_playoff_status(team_key, matches, alliance_number) # Still in quals or team did not make it to elims # Compute qual W-L-T wins, losses, ties, unplayed_qual = cls._get_qual_wlt(team_key, matches) if wins == 0 and losses == 0 and ties == 0: # No matches played yet status = "Team {} has not played any matches yet.".format(team_number) if not status else status # Compute rank & num_teams # Gets record from ranking data to account for surrogate matches rank, ranking_points, record, num_teams = cls._get_rank(team_number, event) rank_str = "Rank {} with {} RP".format(rank, ranking_points) # Compute final long status for nightbot, if one isn't already there if unplayed_qual > 0 and not status: if rank is not None: status = "Team {} is currently rank {}/{} with a record of {} and {} ranking points.".format(team_number, rank, num_teams, record, ranking_points) else: status = "Team {} currently has a record of {}.".format(team_number, record) elif not status: if alliance_number is None: status = "Team {} ended qualification matches at rank {}/{} with a record of {}.".format(team_number, rank, num_teams, record) elif alliance_number == 0: status = "Team {} ended qualification matches at rank {}/{} with a record of {} and was not picked for playoff matches.".format(team_number, rank, num_teams, record) else: status = "Team {} will be competing in the playoff matches on alliance #{}.".format(team_number, alliance_number) return status, record, rank_str, short_playoff_status
def get(self, event_key): memcache_key = "event_detail_%s" % event_key html = memcache.get(memcache_key) if html is None: event = Event.get_by_key_name(event_key) matches = MatchHelper.organizeMatches(event.match_set) teams = TeamHelper.sortTeams([a.team for a in event.teams]) template_values = { "event": event, "matches": matches, "teams": teams, } path = os.path.join(os.path.dirname(__file__), '../templates/events/details.html') html = template.render(path, template_values) memcache.set(memcache_key, html, 300) self.response.out.write(html)
def generate_team_at_event_status(cls, team_key, event, matches=None): """ Generate a dict containing team@event status information :param team_key: Key name of the team to focus on :param event: Event object :param matches: Organized matches (via MatchHelper.organizeMatches) from the event, optional """ event_details = event.details if not matches: matches = event.matches team_matches = [m for m in matches if team_key in m.team_key_names] next_match = MatchHelper.upcomingMatches(team_matches, num=1) last_match = MatchHelper.recentMatches(team_matches, num=1) matches = MatchHelper.organizeMatches(matches) return copy.deepcopy({ 'qual': cls._build_qual_info(team_key, event_details, matches, event.year), 'alliance': cls._build_alliance_info(team_key, event_details, matches), 'playoff': cls._build_playoff_info(team_key, event_details, matches, event.year, event.playoff_type), 'last_match_key': last_match[0].key_name if last_match else None, 'next_match_key': next_match[0].key_name if next_match else None, }) # TODO: Results are getting mixed unless copied. 2017-02-03 -fangeugene
def get(self, event_key): memcache_key = "event_rss_%s" % event_key html = memcache.get(memcache_key) if html is None: event = Event.get_by_key_name(event_key) matches = MatchHelper.organizeMatches(event.match_set) rss_items = [] # Loop through and generate RSS items for each match for match in matches['f'] + matches['sf'] + matches['qf'] + matches['ef'] + matches['qm']: match.unpack_json() new_item = PyRSS2Gen.RSSItem( title = str(match.verbose_name()), link = 'http://www.thebluealliance.com/match/' + match.get_key_name() + '', # List the red and blue alliance teams and their score # TODO: Make this generic in case there's ever not just red/blue -gregmarra 12 Mar 2011 # TODO: Make this output format something either very machine or very human readable. # Probably opt for human, since machines should be using the API. -gregmarra 12 Mar 2011 description = "Red Alliance: " + ' '.join(match.alliances["red"]["teams"]) + " " + "Score: " + str(match.alliances["red"]["score"]) + " " + "Blue Alliance: " + ' '.join(match.alliances["blue"]["teams"]) + " " + "Score: " + str(match.alliances["blue"]["score"]) ) rss_items.append(new_item) # Create final rss document rss = PyRSS2Gen.RSS2( title = event.name + "-- " + str(event.year), link = 'http://www.thebluealliance.com/event/' + str(event.get_key_name()) + '', description = "RSS feed for the " + event.name + " provided by The Blue Alliance." , lastBuildDate = datetime.datetime.now(), items = rss_items ) html = rss.to_xml() memcache.set(memcache_key, html, 300) self.response.out.write(html)
def generate_team_at_event_status(cls, team_key, event, matches=None): """ Generate a dict containing team@event status information :param team_key: Key name of the team to focus on :param event: Event object :param matches: Organized matches (via MatchHelper.organizeMatches) from the event, optional """ event_details = event.details if not matches: matches = event.matches matches = MatchHelper.organizeMatches(matches) return copy.deepcopy( { 'qual': cls._build_qual_info(team_key, event_details, matches, event.year), 'alliance': cls._build_alliance_info(team_key, event_details, matches), 'playoff': cls._build_playoff_info(team_key, event_details, matches, event.year) } ) # TODO: Results are getting mixed unless copied. 2017-02-03 -fangeugene
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 = [] 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 get_ranking_predictions(cls, matches, match_predictions, n=1000): matches = MatchHelper.organizeMatches(matches)['qm'] if not matches or not match_predictions: return None, None match_predictions = match_predictions.get('qual') if not match_predictions: return None, None # Calc surrogates match_counts = defaultdict(int) for match in matches: for alliance_color in ['red', 'blue']: for team in match.alliances[alliance_color]['teams']: match_counts[team] += 1 num_matches = min(match_counts.values()) surrogate_teams = set() for k, v in match_counts.items(): if v > num_matches: surrogate_teams.add(k) # Calculate ranking points and tiebreakers all_rankings = defaultdict(lambda: [0] * n) all_ranking_points = defaultdict(lambda: [0] * n) last_played_match = None for i in xrange(n): team_ranking_points = defaultdict(int) team_rank_tiebreaker = defaultdict(int) num_played = defaultdict(int) for match in matches: for alliance_color in ['red', 'blue']: for team in match.alliances[alliance_color]['teams']: num_played[team] += 1 sampled_rp1 = {} sampled_rp2 = {} sampled_tiebreaker = {} # Get actual results or sampled results, depending if match has been played if match.has_been_played: if not match.score_breakdown: # Can't do rankings without score breakdown return None, None last_played_match = match.key.id() sampled_winner = match.winning_alliance for alliance_color in ['red', 'blue']: if match.year == 2016: sampled_rp1[alliance_color] = match.score_breakdown[alliance_color]['teleopDefensesBreached'] sampled_rp2[alliance_color] = match.score_breakdown[alliance_color]['teleopTowerCaptured'] sampled_tiebreaker[alliance_color] = match.score_breakdown[alliance_color]['autoPoints'] elif match.year == 2017: sampled_rp1[alliance_color] = match.score_breakdown[alliance_color]['kPaRankingPointAchieved'] sampled_rp2[alliance_color] = match.score_breakdown[alliance_color]['rotorRankingPointAchieved'] sampled_tiebreaker[alliance_color] = match.score_breakdown[alliance_color]['totalPoints'] elif match.year == 2018: sampled_rp1[alliance_color] = match.score_breakdown[alliance_color]['autoQuestRankingPoint'] sampled_rp2[alliance_color] = match.score_breakdown[alliance_color]['faceTheBossRankingPoint'] sampled_tiebreaker[alliance_color] = match.score_breakdown[alliance_color]['totalPoints'] else: prediction = match_predictions[match.key.id()] if np.random.uniform(high=1) < prediction['prob']: sampled_winner = prediction['winning_alliance'] else: if prediction['winning_alliance'] == 'red': sampled_winner = 'blue' elif prediction['winning_alliance'] == 'blue': sampled_winner = 'red' for alliance_color in ['red', 'blue']: if match.year == 2016: sampled_rp1[alliance_color] = np.random.uniform(high=1) < prediction[alliance_color]['prob_breach'] sampled_rp2[alliance_color] = np.random.uniform(high=1) < prediction[alliance_color]['prob_capture'] sampled_tiebreaker[alliance_color] = prediction[alliance_color]['auto_points'] elif match.year == 2017: sampled_rp1[alliance_color] = np.random.uniform(high=1) < prediction[alliance_color]['prob_pressure'] sampled_rp2[alliance_color] = np.random.uniform(high=1) < prediction[alliance_color]['prob_gears'] sampled_tiebreaker[alliance_color] = prediction[alliance_color]['score'] elif match.year == 2018: sampled_rp1[alliance_color] = np.random.uniform(high=1) < prediction[alliance_color]['prob_auto_quest'] sampled_rp2[alliance_color] = np.random.uniform(high=1) < prediction[alliance_color]['prob_face_boss'] sampled_tiebreaker[alliance_color] = prediction[alliance_color]['score'] # Using match results, update RP and tiebreaker for alliance_color in ['red', 'blue']: for team in match.alliances[alliance_color]['teams']: if team in surrogate_teams and num_played[team] == 3: continue if sampled_rp1[alliance_color]: team_ranking_points[team] += 1 if sampled_rp2[alliance_color]: team_ranking_points[team] += 1 team_rank_tiebreaker[team] += sampled_tiebreaker[alliance_color] if sampled_winner == '': for alliance_color in ['red', 'blue']: for team in match.alliances[alliance_color]['teams']: if team in surrogate_teams and num_played[team] == 3: continue team_ranking_points[team] += 1 else: for team in match.alliances[sampled_winner]['teams']: if team in surrogate_teams and num_played[team] == 3: continue team_ranking_points[team] += 2 sampled_loser = 'red' if sampled_winner == 'blue' else 'blue' for team in match.alliances[sampled_loser]['teams']: team_ranking_points[team] += 0 # Compute ranks for this sample sample_rankings = sorted(team_ranking_points.items(), key=lambda x: -team_rank_tiebreaker[x[0]]) # Sort by tiebreaker. sample_rankings = sorted(sample_rankings, key=lambda x: -x[1]) # Sort by RP. Sort is stable. for rank, (team, ranking_points) in enumerate(sample_rankings): all_rankings[team][i] = rank + 1 all_ranking_points[team][i] = ranking_points rankings = {} for team, team_rankings in all_rankings.items(): avg_rank = np.mean(team_rankings) min_rank = min(team_rankings) median_rank = np.median(team_rankings) max_rank = max(team_rankings) avg_rp = np.mean(all_ranking_points[team]) min_rp = min(all_ranking_points[team]) max_rp = max(all_ranking_points[team]) rankings[team] = (avg_rank, min_rank, median_rank, max_rank, avg_rp, min_rp, max_rp) ranking_predictions = sorted(rankings.items(), key=lambda x: x[1][0]) # Sort by avg_rank ranking_stats = {'last_played_match': last_played_match} return ranking_predictions, ranking_stats
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 get(self, event_key): self._require_admin() event = Event.get_by_id(event_key) if not event: self.abort(404) event.prepAwardsMatchesTeams() reg_sitevar = Sitevar.get_by_id("cmp_registration_hacks") api_keys = ApiAuthAccess.query( ApiAuthAccess.event_list == ndb.Key(Event, event_key)).fetch() event_medias = Media.query(Media.references == event.key).fetch(500) playoff_template = PlayoffAdvancementHelper.getPlayoffTemplate(event) elim_bracket_html = jinja2_engine.render( "bracket_partials/bracket_table.html", { "bracket_table": event.playoff_bracket, "event": event }) advancement_html = jinja2_engine.render( "playoff_partials/{}.html".format(playoff_template), { "event": event, "playoff_advancement": event.playoff_advancement, "playoff_advancement_tiebreakers": PlayoffAdvancementHelper.ROUND_ROBIN_TIEBREAKERS.get( event.year), "bracket_table": event.playoff_bracket }) if playoff_template else "None" organized_matches = MatchHelper.organizeMatches(event.matches) match_stats = [] for comp_level in Match.COMP_LEVELS: level_matches = organized_matches[comp_level] if not level_matches: continue match_stats.append({ 'comp_level': comp_level, 'level_name': Match.COMP_LEVELS_VERBOSE_FULL[comp_level], 'total': len(level_matches), 'played': len(filter(lambda m: m.has_been_played, level_matches)), 'unplayed': len(filter(lambda m: not m.has_been_played, level_matches)), }) self.template_values.update({ "event": event, "medias": event_medias, "flushed": self.request.get("flushed"), "playoff_types": PlayoffType.type_names, "write_auths": api_keys, "event_sync_disable": reg_sitevar and event_key in reg_sitevar.contents.get('divisions_to_skip', []), "set_start_day_to_last": reg_sitevar and event_key in reg_sitevar.contents.get( 'set_start_to_last_day', []), "skip_eventteams": reg_sitevar and event_key in reg_sitevar.contents.get('skip_eventteams', []), "event_name_override": next( iter( filter(lambda e: e.get("event") == event_key, reg_sitevar.contents.get("event_name_override", []))), {}).get("name", ""), "elim_bracket_html": elim_bracket_html, "advancement_html": advancement_html, 'match_stats': match_stats, 'deleted_count': self.request.get('deleted'), }) path = os.path.join(os.path.dirname(__file__), '../../templates/admin/event_details.html') self.response.out.write(template.render(path, self.template_values))
def get_ranking_predictions(cls, matches, match_predictions, n=1000): matches = MatchHelper.organizeMatches(matches)['qm'] if not matches or not match_predictions: return None, None match_predictions = match_predictions.get('qual') if not match_predictions: return None, None # Calc surrogates match_counts = defaultdict(int) for match in matches: for alliance_color in ['red', 'blue']: for team in match.alliances[alliance_color]['teams']: match_counts[team] += 1 num_matches = min(match_counts.values()) surrogate_teams = set() for k, v in match_counts.items(): if v > num_matches: surrogate_teams.add(k) # Calculate ranking points and tiebreakers all_rankings = defaultdict(lambda: [0] * n) all_ranking_points = defaultdict(lambda: [0] * n) last_played_match = None for i in xrange(n): team_ranking_points = defaultdict(int) team_rank_tiebreaker = defaultdict(int) num_played = defaultdict(int) for match in matches: for alliance_color in ['red', 'blue']: for team in match.alliances[alliance_color]['teams']: num_played[team] += 1 sampled_rp1 = { 'red': False, 'blue': False, } sampled_rp2 = { 'red': False, 'blue': False, } sampled_tiebreaker = { 'red': 0, 'blue': 0, } # Get actual results or sampled results, depending if match has been played if match.has_been_played: if not match.score_breakdown: # Can't do rankings without score breakdown return None, None last_played_match = match.key.id() sampled_winner = match.winning_alliance for alliance_color in ['red', 'blue']: if match.year == 2016: sampled_rp1[ alliance_color] = match.score_breakdown[ alliance_color]['teleopDefensesBreached'] sampled_rp2[ alliance_color] = match.score_breakdown[ alliance_color]['teleopTowerCaptured'] sampled_tiebreaker[ alliance_color] = match.score_breakdown[ alliance_color]['autoPoints'] elif match.year == 2017: sampled_rp1[ alliance_color] = match.score_breakdown[ alliance_color]['kPaRankingPointAchieved'] sampled_rp2[alliance_color] = match.score_breakdown[ alliance_color]['rotorRankingPointAchieved'] sampled_tiebreaker[ alliance_color] = match.score_breakdown[ alliance_color]['totalPoints'] elif match.year == 2018: sampled_rp1[ alliance_color] = match.score_breakdown[ alliance_color]['autoQuestRankingPoint'] sampled_rp2[ alliance_color] = match.score_breakdown[ alliance_color]['faceTheBossRankingPoint'] sampled_tiebreaker[ alliance_color] = match.score_breakdown[ alliance_color]['totalPoints'] elif match.year == 2019: sampled_rp1[alliance_color] = match.score_breakdown[ alliance_color]['completeRocketRankingPoint'] sampled_rp2[ alliance_color] = match.score_breakdown[ alliance_color]['habDockingRankingPoint'] sampled_tiebreaker[ alliance_color] = match.score_breakdown[ alliance_color]['totalPoints'] elif match.year == 2020: sampled_rp1[alliance_color] = match.score_breakdown[ alliance_color]['shieldEnergizedRankingPoint'] sampled_rp2[ alliance_color] = match.score_breakdown[ alliance_color][ 'shieldOperationalRankingPoint'] sampled_tiebreaker[ alliance_color] = match.score_breakdown[ alliance_color]['totalPoints'] else: prediction = match_predictions[match.key.id()] if np.random.uniform(high=1) < prediction['prob']: sampled_winner = prediction['winning_alliance'] else: if prediction['winning_alliance'] == 'red': sampled_winner = 'blue' elif prediction['winning_alliance'] == 'blue': sampled_winner = 'red' for alliance_color in ['red', 'blue']: if match.year == 2016: sampled_rp1[alliance_color] = np.random.uniform( high=1 ) < prediction[alliance_color]['prob_breach'] sampled_rp2[alliance_color] = np.random.uniform( high=1 ) < prediction[alliance_color]['prob_capture'] sampled_tiebreaker[alliance_color] = prediction[ alliance_color]['auto_points'] elif match.year == 2017: sampled_rp1[alliance_color] = np.random.uniform( high=1 ) < prediction[alliance_color]['prob_pressure'] sampled_rp2[alliance_color] = np.random.uniform( high=1 ) < prediction[alliance_color]['prob_gears'] sampled_tiebreaker[alliance_color] = prediction[ alliance_color]['score'] elif match.year == 2018: sampled_rp1[alliance_color] = np.random.uniform( high=1 ) < prediction[alliance_color]['prob_auto_quest'] sampled_rp2[alliance_color] = np.random.uniform( high=1 ) < prediction[alliance_color]['prob_face_boss'] sampled_tiebreaker[alliance_color] = prediction[ alliance_color]['score'] elif match.year == 2019: sampled_rp1[alliance_color] = np.random.uniform( high=1) < prediction[alliance_color][ 'prob_complete_rocket'] sampled_rp2[alliance_color] = np.random.uniform( high=1 ) < prediction[alliance_color]['prob_hab_docking'] sampled_tiebreaker[alliance_color] = prediction[ alliance_color]['score'] elif match.year == 2020: sampled_rp1[alliance_color] = np.random.uniform( high=1) < prediction[alliance_color][ 'prob_shield_energized'] sampled_rp2[alliance_color] = np.random.uniform( high=1) < prediction[alliance_color][ 'prob_shield_operational'] sampled_tiebreaker[alliance_color] = prediction[ alliance_color]['score'] # Using match results, update RP and tiebreaker for alliance_color in ['red', 'blue']: for team in match.alliances[alliance_color]['teams']: if team in surrogate_teams and num_played[team] == 3: continue if sampled_rp1[alliance_color]: team_ranking_points[team] += 1 if sampled_rp2[alliance_color]: team_ranking_points[team] += 1 team_rank_tiebreaker[team] += sampled_tiebreaker[ alliance_color] if sampled_winner == '': for alliance_color in ['red', 'blue']: for team in match.alliances[alliance_color]['teams']: if team in surrogate_teams and num_played[ team] == 3: continue team_ranking_points[team] += 1 else: for team in match.alliances[sampled_winner]['teams']: if team in surrogate_teams and num_played[team] == 3: continue team_ranking_points[team] += 2 sampled_loser = 'red' if sampled_winner == 'blue' else 'blue' for team in match.alliances[sampled_loser]['teams']: team_ranking_points[team] += 0 # Compute ranks for this sample sample_rankings = sorted(team_ranking_points.items(), key=lambda x: -team_rank_tiebreaker[x[0]] ) # Sort by tiebreaker. sample_rankings = sorted( sample_rankings, key=lambda x: -x[1]) # Sort by RP. Sort is stable. for rank, (team, ranking_points) in enumerate(sample_rankings): all_rankings[team][i] = rank + 1 all_ranking_points[team][i] = ranking_points rankings = {} for team, team_rankings in all_rankings.items(): avg_rank = np.mean(team_rankings) min_rank = min(team_rankings) median_rank = np.median(team_rankings) max_rank = max(team_rankings) avg_rp = np.mean(all_ranking_points[team]) min_rp = min(all_ranking_points[team]) max_rp = max(all_ranking_points[team]) rankings[team] = (avg_rank, min_rank, median_rank, max_rank, avg_rp, min_rp, max_rp) ranking_predictions = sorted(rankings.items(), key=lambda x: x[1][0]) # Sort by avg_rank ranking_stats = {'last_played_match': last_played_match} return ranking_predictions, ranking_stats
def _render(self, team_number): self.response.headers['content-type'] = 'text/plain; charset="utf-8"' user = self.request.get('user') if user: user_str = '@{}, '.format(user) else: user_str = '' team_event_or_error = validate_team(user_str, team_number) if type(team_event_or_error) == str: return team_event_or_error _, event = team_event_or_error event_code_upper = event.event_short.upper() team_key = 'frc{}'.format(team_number) matches_future = TeamEventMatchesQuery(team_key, event.key.id()).fetch_async() matches = MatchHelper.organizeMatches(matches_future.get_result()) # Compute alliances alliance_number = None if event.alliance_selections: for i, alliance in enumerate(event.alliance_selections): if team_key in alliance['picks']: alliance_number = i + 1 break else: alliance_number = 0 # Team didn't make it to elims level_map = { 'qf': 'quarters', 'sf': 'semis', 'f': 'the finals', } for comp_level in ['f', 'sf', 'qf']: # playoffs level_str = level_map[comp_level] if matches[comp_level]: wins = 0 losses = 0 for match in matches[comp_level]: if match.has_been_played: if team_key in match.alliances[match.winning_alliance]['teams']: wins += 1 else: losses += 1 if wins == 2: if comp_level == 'f': return "{}[{}] Team {} won the event on alliance #{}.".format(user_str, event_code_upper, team_number, alliance_number) else: return "{}[{}] Team {} won {} on alliance #{}.".format(user_str, event_code_upper, team_number, level_str, alliance_number) elif losses == 2: return "{}[{}] Team {} got knocked out in {} on alliance #{}.".format(user_str, event_code_upper, team_number, level_str, alliance_number) else: return "{}[{}] Team {} is currently {}-{} in {} on alliance #{}.".format(user_str, event_code_upper, team_number, wins, losses, level_str, alliance_number) # Still in quals or team did not make it to elims # Compute qual W-L-T wins = 0 losses = 0 ties = 0 unplayed_qual = 0 for match in matches['qm']: if match.has_been_played: if match.winning_alliance == '': ties += 1 elif team_key in match.alliances[match.winning_alliance]['teams']: wins += 1 else: losses += 1 else: unplayed_qual += 1 if wins == 0 and losses == 0 and ties == 0: # No matches played yet return "{}[{}] Team {} has not played any matches yet.".format(user_str, event_code_upper, team_number) # Compute rank & num_teams rank = None ranking_points = None if event.rankings: num_teams = len(event.rankings) - 1 for i, row in enumerate(event.rankings): if row[1] == team_number: rank = i ranking_points = int(float(row[2])) break if unplayed_qual > 0: if rank is not None: return "{}[{}] Team {} is currently rank {}/{} with a record of {}-{}-{} and {} ranking points.".format(user_str, event_code_upper, team_number, rank, num_teams, wins, losses, ties, ranking_points) else: return "{}[{}] Team {} currently has a record of {}-{}-{}.".format(user_str, event_code_upper, team_number, wins, losses, ties) else: if alliance_number is None: return "{}[{}] Team {} ended qualification matches at rank {}/{} with a record of {}-{}-{}.".format(user_str, event_code_upper, team_number, rank, num_teams, wins, losses, ties) elif alliance_number == 0: return "{}[{}] Team {} ended qualification matches at rank {}/{} with a record of {}-{}-{} and was not picked for playoff matches.".format(user_str, event_code_upper, team_number, rank, num_teams, wins, losses, ties) else: return "{}[{}] Team {} will be competing in the playoff matches on alliance #{}.".format(user_str, event_code_upper, team_number, alliance_number)
def generateTeamAtEventStatusAsync(cls, team_key, event): """ Generate Team@Event status items :return: a tuple future <long summary string, qual record, qual ranking, playoff status> """ team_number = team_key[3:] event.prep_details() # We need all the event's playoff matches here to properly account for backup teams event.prep_matches() qual_match_count = 0 playoff_match_count = 0 playoff_matches = [] matches = event.matches for match in matches: if match.comp_level in Match.ELIM_LEVELS: playoff_match_count += 1 playoff_matches.append(match) else: qual_match_count += 1 matches = MatchHelper.organizeMatches(playoff_matches) team_status = cls.generate_team_at_event_status(team_key, event, matches) rank_status = team_status.get('rank', None) alliance_status = team_status.get('alliance', None) playoff_status = team_status.get('playoff', None) # Playoff Status status, short_playoff_status = cls._get_playoff_status_string(team_key, alliance_status, playoff_status) # Still in quals or team did not make it to elims if not rank_status or rank_status.get('played', 0) == 0: # No matches played yet status = "Team {} has not played any matches yet.".format(team_number) if not status else status record = '?' rank_str = '?' else: # Compute rank & num_teams # Gets record from ranking data to account for surrogate matches rank = rank_status.get('rank', '?') ranking_points = rank_status.get('first_sort', '?') record = rank_status.get('record', '?') num_teams = rank_status.get('total', '?') rank_str = "Rank {} with {} RP".format(rank, ranking_points) alliance_name = alliance_status.get('name', '?') if alliance_status else '?' # Compute final long status for nightbot, if one isn't already there matches_per_team = qual_match_count // rank_status.get('total', 1) if rank_status.get('played', 0) - matches_per_team > 0 and not status: if rank is not None: status = "Team {} is currently rank {}/{} with a record of {} and {} ranking points.".format(team_number, rank, num_teams, record, ranking_points) else: status = "Team {} currently has a record of {}.".format(team_number, record) elif not status: if alliance_status is None and playoff_match_count == 0: status = "Team {} ended qualification matches at rank {}/{} with a record of {}.".format(team_number, rank, num_teams, record) elif alliance_status is None and playoff_match_count > 0: status = "Team {} ended qualification matches at rank {}/{} with a record of {} and was not picked for playoff matches.".format(team_number, rank, num_teams, record) else: status = "Team {} will be competing in the playoff matches on {}.".format(team_number, alliance_name) raise ndb.Return(status, record, rank_str, short_playoff_status)
def render_team_details(cls, handler, team, year, is_canonical): media_future = media_query.TeamYearMediaQuery(team.key.id(), year).fetch_async() robot_future = Robot.get_by_id_async('{}_{}'.format( team.key.id(), year)) team_districts_future = team_query.TeamDistrictsQuery( team.key.id()).fetch_async() events_sorted, matches_by_event_key, awards_by_event_key, valid_years = TeamDetailsDataFetcher.fetch( team, year, return_valid_years=True) if not events_sorted: return None participation = [] year_wlt_list = [] year_match_avg_list = [] current_event = None matches_upcoming = None short_cache = False for event in events_sorted: event_matches = matches_by_event_key.get(event.key, []) event_awards = AwardHelper.organizeAwards( awards_by_event_key.get(event.key, [])) matches_organized = MatchHelper.organizeMatches(event_matches) if event.now: current_event = event matches_upcoming = MatchHelper.upcomingMatches(event_matches) if event.within_a_day: short_cache = True if year == 2015: display_wlt = None match_avg = EventHelper.calculateTeamAvgScoreFromMatches( team.key_name, event_matches) year_match_avg_list.append(match_avg) qual_avg, elim_avg, _, _ = match_avg else: qual_avg = None elim_avg = None wlt = EventHelper.calculateTeamWLTFromMatches( team.key_name, event_matches) year_wlt_list.append(wlt) if wlt["win"] + wlt["loss"] + wlt["tie"] == 0: display_wlt = None else: display_wlt = wlt team_rank = None if event.rankings: for element in event.rankings: if str(element[1]) == str(team.team_number): team_rank = element[0] break participation.append({ 'event': event, 'matches': matches_organized, 'wlt': display_wlt, 'qual_avg': qual_avg, 'elim_avg': elim_avg, 'rank': team_rank, 'awards': event_awards }) if year == 2015: year_wlt = None year_qual_scores = [] year_elim_scores = [] for _, _, event_qual_scores, event_elim_scores in year_match_avg_list: year_qual_scores += event_qual_scores year_elim_scores += event_elim_scores year_qual_avg = float(sum(year_qual_scores)) / len( year_qual_scores) if year_qual_scores != [] else None year_elim_avg = float(sum(year_elim_scores)) / len( year_elim_scores) if year_elim_scores != [] else None else: year_qual_avg = None year_elim_avg = None year_wlt = {"win": 0, "loss": 0, "tie": 0} for wlt in year_wlt_list: year_wlt["win"] += wlt["win"] year_wlt["loss"] += wlt["loss"] year_wlt["tie"] += wlt["tie"] if year_wlt["win"] + year_wlt["loss"] + year_wlt["tie"] == 0: year_wlt = None medias_by_slugname = MediaHelper.group_by_slugname( [media for media in media_future.get_result()]) image_medias = MediaHelper.get_images( [media for media in media_future.get_result()]) district_name = None district_abbrev = None team_districts = team_districts_future.get_result() if year in team_districts: district_key = team_districts[year] district_abbrev = district_key[4:] district_type = DistrictType.abbrevs[district_abbrev] district_name = DistrictType.type_names[district_type] handler.template_values.update({ "is_canonical": is_canonical, "team": team, "participation": participation, "year": year, "years": valid_years, "year_wlt": year_wlt, "year_qual_avg": year_qual_avg, "year_elim_avg": year_elim_avg, "current_event": current_event, "matches_upcoming": matches_upcoming, "medias_by_slugname": medias_by_slugname, "image_medias": image_medias, "robot": robot_future.get_result(), "district_name": district_name, "district_abbrev": district_abbrev, }) if short_cache: handler._cache_expiration = handler.SHORT_CACHE_EXPIRATION return jinja2_engine.render('team_details.html', handler.template_values)
def render_team_details(cls, handler, team, year, is_canonical): media_future = media_query.TeamYearMediaQuery(team.key.id(), year).fetch_async() social_media_future = media_query.TeamSocialMediaQuery(team.key.id()).fetch_async() robot_future = Robot.get_by_id_async('{}_{}'.format(team.key.id(), year)) team_districts_future = team_query.TeamDistrictsQuery(team.key.id()).fetch_async() participation_future = team_query.TeamParticipationQuery(team.key.id()).fetch_async() events_sorted, matches_by_event_key, awards_by_event_key, valid_years = TeamDetailsDataFetcher.fetch(team, year, return_valid_years=True) if not events_sorted: return None participation = [] season_wlt_list = [] offseason_wlt_list = [] year_match_avg_list = [] current_event = None matches_upcoming = None short_cache = False for event in events_sorted: event_matches = matches_by_event_key.get(event.key, []) event_awards = AwardHelper.organizeAwards(awards_by_event_key.get(event.key, [])) matches_organized = MatchHelper.organizeMatches(event_matches) if event.now: current_event = event matches_upcoming = MatchHelper.upcomingMatches(event_matches) if event.within_a_day: short_cache = True if year == 2015: display_wlt = None match_avg = EventHelper.calculateTeamAvgScoreFromMatches(team.key_name, event_matches) year_match_avg_list.append(match_avg) qual_avg, elim_avg, _, _ = match_avg else: qual_avg = None elim_avg = None wlt = EventHelper.calculateTeamWLTFromMatches(team.key_name, event_matches) if event.event_type_enum in EventType.SEASON_EVENT_TYPES: season_wlt_list.append(wlt) else: offseason_wlt_list.append(wlt) if wlt["win"] + wlt["loss"] + wlt["tie"] == 0: display_wlt = None else: display_wlt = wlt team_rank = None if event.rankings: for element in event.rankings: if str(element[1]) == str(team.team_number): team_rank = element[0] break participation.append({'event': event, 'matches': matches_organized, 'wlt': display_wlt, 'qual_avg': qual_avg, 'elim_avg': elim_avg, 'rank': team_rank, 'awards': event_awards}) season_wlt = None offseason_wlt = None if year == 2015: year_wlt = None year_qual_scores = [] year_elim_scores = [] for _, _, event_qual_scores, event_elim_scores in year_match_avg_list: year_qual_scores += event_qual_scores year_elim_scores += event_elim_scores year_qual_avg = float(sum(year_qual_scores)) / len(year_qual_scores) if year_qual_scores != [] else None year_elim_avg = float(sum(year_elim_scores)) / len(year_elim_scores) if year_elim_scores != [] else None else: year_qual_avg = None year_elim_avg = None season_wlt = {"win": 0, "loss": 0, "tie": 0} offseason_wlt = {"win": 0, "loss": 0, "tie": 0} for wlt in season_wlt_list: season_wlt["win"] += wlt["win"] season_wlt["loss"] += wlt["loss"] season_wlt["tie"] += wlt["tie"] if season_wlt["win"] + season_wlt["loss"] + season_wlt["tie"] == 0: season_wlt = None for wlt in offseason_wlt_list: offseason_wlt["win"] += wlt["win"] offseason_wlt["loss"] += wlt["loss"] offseason_wlt["tie"] += wlt["tie"] if offseason_wlt["win"] + offseason_wlt["loss"] + offseason_wlt["tie"] == 0: offseason_wlt = None medias_by_slugname = MediaHelper.group_by_slugname([media for media in media_future.get_result()]) image_medias = MediaHelper.get_images(media_future.get_result()) social_medias = sorted(social_media_future.get_result(), key=MediaHelper.social_media_sorter) preferred_image_medias = filter(lambda x: team.key in x.preferred_references, image_medias) district_name = None district_abbrev = None team_districts = team_districts_future.get_result() if year in team_districts: district_key = team_districts[year] district_abbrev = district_key[4:] district_type = DistrictType.abbrevs[district_abbrev] district_name = DistrictType.type_names[district_type] last_competed = None participation_years = participation_future.get_result() if len(participation_years) > 0: last_competed = max(participation_years) current_year = datetime.date.today().year handler.template_values.update({ "is_canonical": is_canonical, "team": team, "participation": participation, "year": year, "years": valid_years, "season_wlt": season_wlt, "offseason_wlt": offseason_wlt, "year_qual_avg": year_qual_avg, "year_elim_avg": year_elim_avg, "current_event": current_event, "matches_upcoming": matches_upcoming, "medias_by_slugname": medias_by_slugname, "social_medias": social_medias, "image_medias": image_medias, "preferred_image_medias": preferred_image_medias, "robot": robot_future.get_result(), "district_name": district_name, "district_abbrev": district_abbrev, "last_competed": last_competed, "current_year": current_year, }) if short_cache: handler._cache_expiration = handler.SHORT_CACHE_EXPIRATION return jinja2_engine.render('team_details.html', handler.template_values)
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_team_details(cls, handler, team, year, is_canonical): hof_award_future = award_query.TeamEventTypeAwardsQuery(team.key.id(), EventType.CMP_FINALS, AwardType.CHAIRMANS).fetch_async() hof_video_future = media_query.TeamTagMediasQuery(team.key.id(), MediaTag.CHAIRMANS_VIDEO).fetch_async() hof_presentation_future = media_query.TeamTagMediasQuery(team.key.id(), MediaTag.CHAIRMANS_PRESENTATION).fetch_async() hof_essay_future = media_query.TeamTagMediasQuery(team.key.id(), MediaTag.CHAIRMANS_ESSAY).fetch_async() media_future = media_query.TeamYearMediaQuery(team.key.id(), year).fetch_async() social_media_future = media_query.TeamSocialMediaQuery(team.key.id()).fetch_async() robot_future = Robot.get_by_id_async("{}_{}".format(team.key.id(), year)) team_districts_future = team_query.TeamDistrictsQuery(team.key.id()).fetch_async() participation_future = team_query.TeamParticipationQuery(team.key.id()).fetch_async() hof_awards = hof_award_future.get_result() hof_video = hof_video_future.get_result() hof_presentation = hof_presentation_future.get_result() hof_essay = hof_essay_future.get_result() hall_of_fame = { "is_hof": len(hof_awards) > 0, "years": [award.year for award in hof_awards], "media": { "video": hof_video[0].youtube_url_link if len(hof_video) > 0 else None, "presentation": hof_presentation[0].youtube_url_link if len(hof_presentation) > 0 else None, "essay": hof_essay[0].external_link if len(hof_essay) > 0 else None, }, } events_sorted, matches_by_event_key, awards_by_event_key, valid_years = TeamDetailsDataFetcher.fetch(team, year, return_valid_years=True) if not events_sorted: return None district_name = None district_abbrev = None team_district_points = None team_districts = team_districts_future.get_result() for district in team_districts: if district and district.year == year: district_abbrev = district.abbreviation district_name = district.display_name if district.rankings: team_district_points = next( iter(filter(lambda r: r['team_key'] == team.key_name, district.rankings)), None) break participation = [] season_wlt_list = [] offseason_wlt_list = [] year_match_avg_list = [] current_event = None matches_upcoming = None short_cache = False for event in events_sorted: event_matches = matches_by_event_key.get(event.key, []) event_awards = AwardHelper.organizeAwards(awards_by_event_key.get(event.key, [])) matches_organized = MatchHelper.organizeMatches(event_matches) if event.now: current_event = event matches_upcoming = MatchHelper.upcomingMatches(event_matches) if event.within_a_day: short_cache = True if year == 2015: display_wlt = None match_avg = EventHelper.calculateTeamAvgScoreFromMatches(team.key_name, event_matches) year_match_avg_list.append(match_avg) qual_avg, elim_avg, _, _ = match_avg else: qual_avg = None elim_avg = None wlt = EventHelper.calculateTeamWLTFromMatches(team.key_name, event_matches) if event.event_type_enum in EventType.SEASON_EVENT_TYPES: season_wlt_list.append(wlt) else: offseason_wlt_list.append(wlt) if wlt["win"] + wlt["loss"] + wlt["tie"] == 0: display_wlt = None else: display_wlt = wlt team_rank = None if event.details and event.details.rankings2: for ranking in event.details.rankings2: if ranking['team_key'] == team.key.id(): team_rank = ranking['rank'] break video_ids = [] playlist = "" for level in Match.COMP_LEVELS: matches = matches_organized[level] for match in matches: video_ids += [video.split("?")[0] for video in match.youtube_videos] if video_ids: playlist_title = u"{} (Team {})".format(event.name, team.team_number) playlist = u"https://www.youtube.com/watch_videos?video_ids={}&title={}" playlist = playlist.format(u",".join(video_ids), playlist_title) district_points = None if team_district_points: district_points = next( iter( filter(lambda e: e['event_key'] == event.key_name, team_district_points['event_points'])), None) participation.append({ "event": event, "matches": matches_organized, "wlt": display_wlt, "qual_avg": qual_avg, "elim_avg": elim_avg, "rank": team_rank, "awards": event_awards, "playlist": playlist, "district_points": district_points, }) season_wlt = None offseason_wlt = None if year == 2015: year_wlt = None year_qual_scores = [] year_elim_scores = [] for _, _, event_qual_scores, event_elim_scores in year_match_avg_list: year_qual_scores += event_qual_scores year_elim_scores += event_elim_scores year_qual_avg = float(sum(year_qual_scores)) / len(year_qual_scores) if year_qual_scores != [] else None year_elim_avg = float(sum(year_elim_scores)) / len(year_elim_scores) if year_elim_scores != [] else None else: year_qual_avg = None year_elim_avg = None season_wlt = {"win": 0, "loss": 0, "tie": 0} offseason_wlt = {"win": 0, "loss": 0, "tie": 0} for wlt in season_wlt_list: season_wlt["win"] += wlt["win"] season_wlt["loss"] += wlt["loss"] season_wlt["tie"] += wlt["tie"] if season_wlt["win"] + season_wlt["loss"] + season_wlt["tie"] == 0: season_wlt = None for wlt in offseason_wlt_list: offseason_wlt["win"] += wlt["win"] offseason_wlt["loss"] += wlt["loss"] offseason_wlt["tie"] += wlt["tie"] if offseason_wlt["win"] + offseason_wlt["loss"] + offseason_wlt["tie"] == 0: offseason_wlt = None medias_by_slugname = MediaHelper.group_by_slugname([media for media in media_future.get_result()]) avatar = MediaHelper.get_avatar(media_future.get_result()) image_medias = MediaHelper.get_images(media_future.get_result()) social_medias = sorted(social_media_future.get_result(), key=MediaHelper.social_media_sorter) preferred_image_medias = filter(lambda x: team.key in x.preferred_references, image_medias) last_competed = None participation_years = participation_future.get_result() if len(participation_years) > 0: last_competed = max(participation_years) current_year = datetime.date.today().year handler.template_values.update({ "is_canonical": is_canonical, "team": team, "participation": participation, "year": year, "years": valid_years, "season_wlt": season_wlt, "offseason_wlt": offseason_wlt, "year_qual_avg": year_qual_avg, "year_elim_avg": year_elim_avg, "current_event": current_event, "matches_upcoming": matches_upcoming, "medias_by_slugname": medias_by_slugname, "avatar": avatar, "social_medias": social_medias, "image_medias": image_medias, "preferred_image_medias": preferred_image_medias, "robot": robot_future.get_result(), "district_name": district_name, "district_abbrev": district_abbrev, "last_competed": last_competed, "current_year": current_year, "max_year": tba_config.MAX_YEAR, "hof": hall_of_fame, "team_district_points": team_district_points, }) if short_cache: handler._cache_expiration = handler.SHORT_CACHE_EXPIRATION return jinja2_engine.render("team_details.html", handler.template_values)
def _render(self, team_number): self.response.headers['content-type'] = 'text/plain; charset="utf-8"' team_event_or_error = validate_team(team_number) if type(team_event_or_error) == str: return team_event_or_error _, event = team_event_or_error event_code_upper = event.event_short.upper() team_key = 'frc{}'.format(team_number) matches_future = TeamEventMatchesQuery(team_key, event.key.id()).fetch_async() matches = MatchHelper.organizeMatches(matches_future.get_result()) # Compute alliances alliance_number = None if event.alliance_selections: for i, alliance in enumerate(event.alliance_selections): if team_key in alliance['picks']: alliance_number = i + 1 break else: alliance_number = 0 # Team didn't make it to elims level_map = { 'qf': 'quarters', 'sf': 'semis', 'f': 'the finals', } for comp_level in ['f', 'sf', 'qf']: # playoffs level_str = level_map[comp_level] if matches[comp_level]: wins = 0 losses = 0 for match in matches[comp_level]: if match.has_been_played: if team_key in match.alliances[match.winning_alliance]['teams']: wins += 1 else: losses += 1 if wins == 2: if comp_level == 'f': return "[{}]: Team {} won the event on alliance #{}.".format(event_code_upper, team_number, alliance_number) else: return "[{}]: Team {} won {} on alliance #{}.".format(event_code_upper, team_number, level_str, alliance_number) elif losses == 2: return "[{}]: Team {} got knocked out in {} on alliance #{}.".format(event_code_upper, team_number, level_str, alliance_number) else: return "[{}]: Team {} is currently {}-{} in {} on alliance #{}.".format(event_code_upper, team_number, wins, losses, level_str, alliance_number) # Still in quals or team did not make it to elims # Compute qual W-L-T wins = 0 losses = 0 ties = 0 unplayed_qual = 0 for match in matches['qm']: if match.has_been_played: if match.winning_alliance == '': ties += 1 elif team_key in match.alliances[match.winning_alliance]['teams']: wins += 1 else: losses += 1 else: unplayed_qual += 1 # Compute rank & num_teams rank = None if event.rankings: num_teams = len(event.rankings) - 1 for i, row in enumerate(event.rankings): if row[1] == team_number: rank = i if unplayed_qual > 0: if rank is not None: return "[{}]: Team {} is currently rank {}/{} with a record of {}-{}-{}.".format(event_code_upper, team_number, rank, num_teams, wins, losses, ties) else: return "[{}]: Team {} currently has a record of {}-{}-{} at [{}].".format(event_code_upper, team_number, wins, losses, ties) else: if alliance_number is None: return "[{}]: Team {} ended qualification matches at rank {}/{} with a record of {}-{}-{}.".format(event_code_upper, team_number, rank, num_teams, wins, losses, ties) elif alliance_number == 0: return "[{}]: Team {} ended qualification matches at rank {}/{} with a record of {}-{}-{} and was not picked for playoff matches.".format(event_code_upper, team_number, rank, num_teams, wins, losses, ties) else: return "[{}]: Team {} will be competing in the playoff matches on alliance #{}.".format(event_code_upper, team_number, alliance_number)
def _render(self, event_key): event = Event.get_by_id(event_key) if not event or event.year < 2016 or not event.details or not event.details.predictions: self.abort(404) event.get_matches_async() match_predictions = event.details.predictions.get('match_predictions', None) match_prediction_stats = event.details.predictions.get('match_prediction_stats', None) ranking_predictions = event.details.predictions.get('ranking_predictions', None) ranking_prediction_stats = event.details.predictions.get('ranking_prediction_stats', None) cleaned_matches = MatchHelper.deleteInvalidMatches(event.matches, event) matches = MatchHelper.organizeMatches(cleaned_matches) # If no matches but there are match predictions, create fake matches # For cases where FIRST doesn't allow posting of match schedule fake_matches = False if match_predictions and (not matches['qm'] and match_predictions['qual']): fake_matches = True for i in xrange(len(match_predictions['qual'].keys())): match_number = i + 1 alliances = { 'red': { 'score': -1, 'teams': ['frc?', 'frc?', 'frc?'] }, 'blue': { 'score': -1, 'teams': ['frc?', 'frc?', 'frc?'] } } matches['qm'].append(Match( id=Match.renderKeyName( event_key, 'qm', 1, match_number), event=event.key, year=event.year, set_number=1, match_number=match_number, comp_level='qm', alliances_json=json.dumps(alliances), )) # Add actual scores to predictions distribution_info = {} for comp_level in Match.COMP_LEVELS: level = 'qual' if comp_level == 'qm' else 'playoff' for match in matches[comp_level]: distribution_info[match.key.id()] = { 'level': level, 'red_actual_score': match.alliances['red']['score'], 'blue_actual_score': match.alliances['blue']['score'], 'red_mean': match_predictions[level][match.key.id()]['red']['score'], 'blue_mean': match_predictions[level][match.key.id()]['blue']['score'], 'red_var': match_predictions[level][match.key.id()]['red']['score_var'], 'blue_var': match_predictions[level][match.key.id()]['blue']['score_var'], } last_played_match_num = None if ranking_prediction_stats: last_played_match_key = ranking_prediction_stats.get('last_played_match', None) if last_played_match_key: last_played_match_num = last_played_match_key.split('_qm')[1] self.template_values.update({ "event": event, "matches": matches, "fake_matches": fake_matches, "match_predictions": match_predictions, "distribution_info_json": json.dumps(distribution_info), "match_prediction_stats": match_prediction_stats, "ranking_predictions": ranking_predictions, "ranking_prediction_stats": ranking_prediction_stats, "last_played_match_num": last_played_match_num, }) if event.within_a_day: self._cache_expiration = self.SHORT_CACHE_EXPIRATION return jinja2_engine.render('event_insights.html', self.template_values)