def _process_request(self, request, event_key):
        matches = []
        needs_time = []
        for match in JSONMatchesParser.parse(request.body, self.event.year):
            match = Match(
                id=Match.renderKeyName(
                    self.event.key.id(),
                    match.get("comp_level", None),
                    match.get("set_number", 0),
                    match.get("match_number", 0)),
                event=self.event.key,
                year=self.event.year,
                set_number=match.get("set_number", 0),
                match_number=match.get("match_number", 0),
                comp_level=match.get("comp_level", None),
                team_key_names=match.get("team_key_names", None),
                alliances_json=match.get("alliances_json", None),
                score_breakdown_json=match.get("score_breakdown_json", None),
                time_string=match.get("time_string", None),
                time=match.get("time", None),
            )

            if (not match.time or match.time == "") and match.time_string:
                # We can calculate the real time from the time string
                needs_time.append(match)
            matches.append(match)

        if needs_time:
            try:
                logging.debug("Calculating time!")
                MatchHelper.add_match_times(self.event, needs_time)
            except Exception, e:
                logging.error("Failed to calculate match times")
예제 #2
0
    def send_upcoming_matches(cls, live_events):
        from helpers.match_helper import MatchHelper  # PJL: Hacky :P
        # Causes circular import, otherwise
        # https://github.com/the-blue-alliance/the-blue-alliance/pull/1098#discussion_r25128966

        now = datetime.datetime.utcnow()
        for event in live_events:
            matches = event.matches
            if not matches:
                continue
            last_matches = MatchHelper.recentMatches(matches, num=1)
            next_matches = MatchHelper.upcomingMatches(matches, num=2)

            # First, compare the difference between scheduled times of next/last match
            # Send an upcoming notification if it's <10 minutes, to account for events ahead of schedule
            if last_matches != []:
                last_match = last_matches[0]
                for i, next_match in enumerate(next_matches):
                    if not next_match.push_sent and last_match.time and next_match.time:
                        diff = next_match.time - last_match.time
                        if diff < datetime.timedelta(minutes=10 * (i + 1)):
                            cls.send_upcoming_match_notification(next_match, event)

            for match in next_matches:
                if match and not match.push_sent:
                    # Only continue sending for the next match if a push hasn't already been sent for it
                    if match.time is None or match.time + datetime.timedelta(minutes=-7) <= now:
                        # Only send notifications for matches no more than 7 minutes (average-ish match cycle time) before it's scheduled to start
                        # Unless, the match has no time info. Then #yolo and send it
                        cls.send_upcoming_match_notification(match, event)
    def post(self):
        self._require_admin()
        event_key = self.request.get('event_key')
        matches_csv = self.request.get('matches_csv')
        matches = OffseasonMatchesParser.parse(matches_csv)

        event = Event.get_by_id(event_key)
        matches = [Match(
            id=Match.renderKeyName(
                event.key.id(),
                match.get("comp_level", None),
                match.get("set_number", 0),
                match.get("match_number", 0)),
            event=event.key,
            game=Match.FRC_GAMES_BY_YEAR.get(event.year, "frc_unknown"),
            set_number=match.get("set_number", 0),
            match_number=match.get("match_number", 0),
            comp_level=match.get("comp_level", None),
            team_key_names=match.get("team_key_names", None),
            alliances_json=match.get("alliances_json", None)
            )
            for match in matches]

        new_matches = MatchManipulator.createOrUpdate(matches)
        try:
            last_matches = MatchHelper.recentMatches(new_matches, 1)
            upcoming_matches = MatchHelper.upcomingMatches(new_matches, 8)
        except:
            logging.warning("Computing last/upcoming matches for Firebase failed!")
        try:
            FirebasePusher.updateEvent(event, last_matches, upcoming_matches)
        except:
            logging.warning("Enqueuing Firebase push failed!")

        self.redirect('/admin/event/{}'.format(event_key))
    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_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 _process_request(self, request, event_key):
        event = Event.get_by_id(event_key)
        year = int(event_key[:4])

        matches = []
        for match in JSONMatchesParser.parse(request.body, year):
            match = Match(
                id=Match.renderKeyName(
                    event.key.id(),
                    match.get("comp_level", None),
                    match.get("set_number", 0),
                    match.get("match_number", 0)),
                event=event.key,
                year=event.year,
                set_number=match.get("set_number", 0),
                match_number=match.get("match_number", 0),
                comp_level=match.get("comp_level", None),
                team_key_names=match.get("team_key_names", None),
                alliances_json=match.get("alliances_json", None),
                score_breakdown_json=match.get("score_breakdown_json", None),
                time_string=match.get("time_string", None),
                time=match.get("time", None),
            )

            if (not match.time or match.time == "") and match.time_string:
                # We can calculate the real time from the time string
                logging.debug("Calculating time!")
                MatchHelper.add_match_times(event, [match])

            matches.append(match)

        MatchManipulator.createOrUpdate(matches)

        self.response.out.write(json.dumps({'Success': "Matches successfully updated"}))
    def get(self):
        self._require_registration()

        current_events = filter(lambda e: e.now, EventHelper.getEventsWithinADay())
        popular_teams_events = TeamHelper.getPopularTeamsEvents(current_events)

        popular_team_keys = set()
        for team, _ in popular_teams_events:
            popular_team_keys.add(team.key.id())

        for event in current_events:
            event.prep_details()
            event.prep_matches()

        finished_matches = []
        current_matches = []
        upcoming_matches = []
        ranks = {}
        alliances = {}
        for event in current_events:
            if not event.details:
                continue
            finished_matches += MatchHelper.recentMatches(event.matches, num=1)
            for i, match in enumerate(MatchHelper.upcomingMatches(event.matches, num=3)):
                if not match.time:
                    continue

                if not event.details.predictions or match.key.id() not in event.details.predictions['match_predictions']['qual' if match.comp_level == 'qm' else 'playoff']:
                    match.prediction = defaultdict(lambda: defaultdict(float))
                    match.bluezone_score = 0
                else:
                    match.prediction = event.details.predictions['match_predictions']['qual' if match.comp_level == 'qm' else 'playoff'][match.key.id()]
                    match.bluezone_score = self.get_qual_bluezone_score(match.prediction) if match.comp_level == 'qm' else self.get_elim_bluezone_score(match.prediction)
                if i == 0:
                    current_matches.append(match)
                else:
                    upcoming_matches.append(match)
            if event.details.rankings2:
                for rank in event.details.rankings2:
                    ranks[rank['team_key']] = rank['rank']
            if event.alliance_selections:
                for i, alliance in enumerate(event.alliance_selections):
                    for pick in alliance['picks']:
                        alliances[pick] = i + 1

        finished_matches = sorted(finished_matches, key=lambda m: m.actual_time if m.actual_time else m.time)
        current_matches = sorted(current_matches, key=lambda m: m.predicted_time if m.predicted_time else m.time)
        upcoming_matches = sorted(upcoming_matches, key=lambda m: m.predicted_time if m.predicted_time else m.time)

        self.template_values.update({
            'finished_matches': finished_matches,
            'current_matches': current_matches,
            'upcoming_matches': upcoming_matches,
            'ranks': ranks,
            'alliances': alliances,
            'popular_team_keys': popular_team_keys,
        })

        self.response.out.write(jinja2_engine.render('match_suggestion.html', self.template_values))
예제 #8
0
 def test_event_winner(self):
     MatchHelper.add_surrogates(self.event)
     for match in self.event.matches:
         if match.comp_level != 'qm' or match.match_number != 18:
             for alliance_color in ['red', 'blue']:
                 self.assertEqual(match.alliances[alliance_color]['surrogates'], [])
         else:
             self.assertEqual(match.alliances['red']['surrogates'], ['frc5496'])
             self.assertEqual(match.alliances['blue']['surrogates'], ['frc1323'])
    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)
예제 #10
0
    def getMatches(self, event):
        matches_url = self.YEAR_MATCH_RESULTS_URL_PATTERN.get(
            event.year, self.DEFAULT_MATCH_RESULTS_URL_PATTERN) % (
                event.year, self.EVENT_SHORT_EXCEPTIONS.get(event.event_short,
                                                            event.event_short))

        match_dicts, _ = self.parse(matches_url, self.YEAR_MATCH_PARSER.get(event.year, self.DEFAULT_MATCH_PARSER))
        if not match_dicts:  # Matches have not been played, but qual match schedule may be out
            # If this is run when there are already matches in the DB, it will overwrite scores!
            # Check to make sure event has no existing matches
            if len(Match.query(Match.event == event.key).fetch(1, keys_only=True)) == 0:
                logging.warning("No matches found for {}. Trying to parse qual match schedule.".format(event.key.id()))

                qual_match_sched_url = self.MATCH_SCHEDULE_QUAL_URL_PATTERN % (
                    event.year, self.EVENT_SHORT_EXCEPTIONS.get(event.event_short,
                                                                event.event_short))
                match_dicts, _ = self.parse(qual_match_sched_url, self.MATCH_SCHEDULE_PARSER)

        for match_dict in match_dicts:
            alliances = json.loads(match_dict['alliances_json'])
            if (alliances['red']['score'] == -1 or alliances['blue']['score'] == -1 or
                match_dict['comp_level'] in Match.ELIM_LEVELS):
                break
        else:  # Only qual matches have been played and they have all been played
            # If this is run when there are already elim matches in the DB, it will overwrite scores!
            # Check to make sure event has no existing elim matches
            if len(Match.query(Match.event == event.key, Match.comp_level.IN(Match.ELIM_LEVELS)).fetch(1, keys_only=True)) == 0:
                logging.warning("No elim matches found for {}. Trying to parse elim match schedule.".format(event.key.id()))

                elim_match_sched_url = self.MATCH_SCHEDULE_ELIMS_URL_PATTERN % (
                    event.year, self.EVENT_SHORT_EXCEPTIONS.get(event.event_short,
                                                                event.event_short))
                elim_match_dicts, _ = self.parse(elim_match_sched_url, self.MATCH_SCHEDULE_PARSER)
                match_dicts += elim_match_dicts

        matches = [Match(
            id=Match.renderKeyName(
                event.key.id(),
                match_dict.get("comp_level", None),
                match_dict.get("set_number", 0),
                match_dict.get("match_number", 0)),
            event=event.key,
            game=Match.FRC_GAMES_BY_YEAR.get(event.year, "frc_unknown"),
            set_number=match_dict.get("set_number", 0),
            match_number=match_dict.get("match_number", 0),
            comp_level=match_dict.get("comp_level", None),
            team_key_names=match_dict.get("team_key_names", None),
            time_string=match_dict.get("time_string", None),
            alliances_json=match_dict.get("alliances_json", None)
            )
            for match_dict in match_dicts]

        MatchHelper.add_match_times(event, matches)
        return matches
예제 #11
0
    def test_match_times(self):
        with open('test_data/usfirst_html/usfirst_event_matches_2013cama.html', 'r') as f:  # using matches from a random event as data
            match_dicts, _ = UsfirstMatchesParser.parse(f.read())

        matches = self.matchDictToMatches(match_dicts)
        MatchHelper.add_match_times(self.event, matches)

        self.assertEqual(len(matches), 92)

        PST_OFFSET = -5
        self.assertEqual(matches[0].time, datetime.datetime(2014, 2, 28, 9, 0) - datetime.timedelta(hours=PST_OFFSET))
        self.assertEqual(matches[75].time, datetime.datetime(2014, 3, 1, 11, 50) - datetime.timedelta(hours=PST_OFFSET))
예제 #12
0
    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.now:
            matches_recent = MatchHelper.recentMatches(event.matches)
        else:
            matches_recent = 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,
            "awards": awards,
            "teams_a": teams_a,
            "teams_b": teams_b,
            "num_teams": num_teams,
            "oprs": oprs,
            "bracket_table": bracket_table,
        }
            
        path = os.path.join(os.path.dirname(__file__), '../templates/event_details.html')
        return template.render(path, template_values)
    def test_2017scmb_sequence(self):
        event = Event(
            id='2017scmb',
            event_short='scmb',
            year=2017,
            event_type_enum=0,
            timezone_id='America/New_York'
        )
        event.put()

        event_code = 'scmb'

        file_prefix = 'frc-api-response/v2.0/2017/schedule/{}/playoff/hybrid/'.format(event_code)
        context = ndb.get_context()
        result = context.urlfetch('https://www.googleapis.com/storage/v1/b/bucket/o?bucket=tbatv-prod-hrd.appspot.com&prefix={}'.format(file_prefix)).get_result()

        for item in json.loads(result.content)['items']:
            filename = item['name']
            time_str = filename.replace(file_prefix, '').replace('.json', '').strip()
            file_time = datetime.datetime.strptime(time_str, "%Y-%m-%d %H:%M:%S.%f")
            query_time = file_time + datetime.timedelta(seconds=30)
            MatchManipulator.createOrUpdate(DatafeedFMSAPI('v2.0', sim_time=query_time).getMatches('2017{}'.format(event_code)), run_post_update_hook=False)
        MatchHelper.deleteInvalidMatches(event.matches, event)

        qf_matches = Match.query(Match.event == ndb.Key(Event, '2017scmb'), Match.comp_level == 'qf').fetch()
        self.assertEqual(len(qf_matches), 11)

        sf_matches = Match.query(Match.event == ndb.Key(Event, '2017scmb'), Match.comp_level == 'sf').fetch()
        self.assertEqual(len(sf_matches), 4)

        f_matches = Match.query(Match.event == ndb.Key(Event, '2017scmb'), Match.comp_level == 'f').fetch()
        self.assertEqual(len(f_matches), 3)

        self.assertEqual(Match.get_by_id('2017scmb_qf4m1').alliances['red']['score'], 305)
        self.assertEqual(Match.get_by_id('2017scmb_qf4m1').alliances['blue']['score'], 305)
        self.assertEqual(Match.get_by_id('2017scmb_qf4m1').score_breakdown['red']['totalPoints'], 305)
        self.assertEqual(Match.get_by_id('2017scmb_qf4m1').score_breakdown['blue']['totalPoints'], 305)

        self.assertEqual(Match.get_by_id('2017scmb_qf4m2').alliances['red']['score'], 213)
        self.assertEqual(Match.get_by_id('2017scmb_qf4m2').alliances['blue']['score'], 305)
        self.assertEqual(Match.get_by_id('2017scmb_qf4m2').score_breakdown['red']['totalPoints'], 213)
        self.assertEqual(Match.get_by_id('2017scmb_qf4m2').score_breakdown['blue']['totalPoints'], 305)

        self.assertEqual(Match.get_by_id('2017scmb_qf4m3').alliances['red']['score'], 312)
        self.assertEqual(Match.get_by_id('2017scmb_qf4m3').alliances['blue']['score'], 255)
        self.assertEqual(Match.get_by_id('2017scmb_qf4m3').score_breakdown['red']['totalPoints'], 312)
        self.assertEqual(Match.get_by_id('2017scmb_qf4m3').score_breakdown['blue']['totalPoints'], 255)

        self.assertEqual(Match.get_by_id('2017scmb_qf4m4').alliances['red']['score'], 310)
        self.assertEqual(Match.get_by_id('2017scmb_qf4m4').alliances['blue']['score'], 306)
        self.assertEqual(Match.get_by_id('2017scmb_qf4m4').score_breakdown['red']['totalPoints'], 310)
        self.assertEqual(Match.get_by_id('2017scmb_qf4m4').score_breakdown['blue']['totalPoints'], 306)
예제 #14
0
    def test_match_times_dst(self):
        with open('test_data/usfirst_html/usfirst_event_matches_2012ct.html', 'r') as f:  # using matches from a random event as data
            match_dicts, _ = UsfirstMatchesParser.parse(f.read())

        matches = self.matchDictToMatches(match_dicts)
        MatchHelper.add_match_times(self.event_dst, matches)

        self.assertEqual(len(matches), 125)

        PST_OFFSET = -8
        PDT_OFFSET = -7
        self.assertEqual(matches[0].time, datetime.datetime(2014, 3, 8, 9, 0) - datetime.timedelta(hours=PST_OFFSET))
        self.assertEqual(matches[-1].time, datetime.datetime(2014, 3, 9, 16, 5) - datetime.timedelta(hours=PDT_OFFSET))
    def get(self, event_key):
        event = Event.get_by_id(event_key)
        if not event:
            self.abort(404)

        matches = event.matches
        if not matches:
            return

        timezone = pytz.timezone(event.timezone_id)
        played_matches = MatchHelper.recentMatches(matches, num=0)
        unplayed_matches = MatchHelper.upcomingMatches(matches, num=10)
        MatchTimePredictionHelper.predict_future_matches(played_matches, unplayed_matches, timezone, event.within_a_day)
    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 _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()

        matches_future = TeamEventMatchesQuery('frc{}'.format(team_number), event.key.id()).fetch_async()
        matches = MatchHelper.play_order_sort_matches(matches_future.get_result())

        # No match schedule yet
        if not matches:
            return "{}[{}] Team {} has no scheduled matches yet.".format(user_str, event_code_upper, team_number)

        next_match = None
        for match in matches:
            if not match.has_been_played:
                next_match = match
                break

        if next_match is None:
            return "{}[{}] Team {} has no more scheduled matches.".format(user_str, event_code_upper, team_number)

        return "{}[{}] Team {} will be playing in match {}.".format(user_str, event_code_upper, team_number, match.short_name)
    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 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_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)
예제 #22
0
 def previous_match(self):
     from helpers.match_helper import MatchHelper
     recent_matches = MatchHelper.recentMatches(self.matches, 1)[0]
     if recent_matches:
         return recent_matches[0]
     else:
         return None
예제 #23
0
    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))
예제 #24
0
 def test_cleanup(self):
     matches = setupMatches('test_data/cleanup_matches.csv')
     cleaned_matches = MatchHelper.deleteInvalidMatches(matches)
     indices = [9, 12, 26]
     for index in sorted(indices, reverse=True): #need to delete in reverse order so indices don't get messed up
         del matches[index]
     self.assertEqual(matches, cleaned_matches)
    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)
예제 #26
0
 def next_match(self):
     from helpers.match_helper import MatchHelper
     upcoming_matches = MatchHelper.upcomingMatches(self.matches, 1)
     if upcoming_matches:
         return upcoming_matches[0]
     else:
         return None
예제 #27
0
 def __init__(self, event):
     from helpers.match_helper import MatchHelper  # recursive import issues
     self.event = event
     self._event_feed = event.key_name
     self._district_feed = event.event_district_abbrev
     upcoming = MatchHelper.upcomingMatches(event.matches, 1)
     self.next_match = upcoming[0] if upcoming[0] else None
예제 #28
0
    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
예제 #29
0
    def _render(self, event_key):
        event = Event.get_by_id(event_key)
        if not event:
            self.abort(404)
            return
        medias_future = media_query.EventTeamsPreferredMediasQuery(event_key).fetch_async()
        next_match = MatchHelper.upcomingMatches(event.matches, num=1)
        next_match = next_match[0] if next_match else None
        team_and_medias = []
        if next_match:
            # Organize medias by team
            teams = ndb.get_multi([ndb.Key(Team, team_key) for team_key in next_match.alliances['red']['teams'] + next_match.alliances['blue']['teams']])
            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)

            stations = ['Red 1', 'Red 2', 'Red 3', 'Blue 1', 'Blue 2', 'Blue 3']
            for i, team in enumerate(teams):
                team_and_medias.append((team, stations[i], team_medias.get(team.key, [])))

        self.template_values.update({
            'event': event,
            'next_match': next_match,
            'teams_and_media': team_and_medias,
        })
        return jinja2_engine.render('nextmatch.html', self.template_values)
    def _render(self, team_number):
        team = Team.get_by_id("frc" + team_number)

        if not team:
            return self.redirect("/error/404")

        event_team_keys_future = EventTeam.query(EventTeam.team == team.key).fetch_async(1000, keys_only=True)
        award_keys_future = Award.query(Award.team == team.key).fetch_async(1000, keys_only=True)

        event_teams_futures = ndb.get_multi_async(event_team_keys_future.get_result())
        awards_futures = ndb.get_multi_async(award_keys_future.get_result())

        event_keys = [event_team_future.get_result().event for event_team_future in event_teams_futures]
        events_futures = ndb.get_multi_async(event_keys)

        awards_by_event = {}
        for award_future in awards_futures:
            award = award_future.get_result()
            if award.event.id() not in awards_by_event:
                awards_by_event[award.event.id()] = [award]
            else:
                awards_by_event[award.event.id()].append(award)

        event_awards = []
        current_event = None
        matches_upcoming = None
        short_cache = False
        for event_future in events_futures:
            event = event_future.get_result()
            if event.now:
                current_event = event

                team_matches_future = Match.query(Match.event == event.key, Match.team_key_names == team.key_name)\
                  .fetch_async(500, keys_only=True)
                matches = ndb.get_multi(team_matches_future.get_result())
                matches_upcoming = MatchHelper.upcomingMatches(matches)

            if event.within_a_day:
                short_cache = True

            if event.key_name in awards_by_event:
                sorted_awards = AwardHelper.organizeAwards(awards_by_event[event.key_name])['list']
            else:
                sorted_awards = []
            event_awards.append((event, sorted_awards))
        event_awards = sorted(event_awards, key=lambda (e, _): e.start_date if e.start_date else datetime.datetime(e.year, 12, 31))

        years = sorted(set([et.get_result().year for et in event_teams_futures if et.get_result().year != None]))

        template_values = {'team': team,
                           'event_awards': event_awards,
                           'years': years,
                           'current_event': current_event,
                           'matches_upcoming': matches_upcoming}

        if short_cache:
            self._cache_expiration = self.SHORT_CACHE_EXPIRATION

        path = os.path.join(os.path.dirname(__file__), '../templates/team_history.html')
        return template.render(path, template_values)
예제 #31
0
    def render_team_details(cls, handler, team, year, is_canonical):
        media_key_futures = Media.query(Media.references == team.key,
                                        Media.year == year).fetch_async(
                                            500, keys_only=True)
        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

        media_futures = ndb.get_multi_async(media_key_futures.get_result())

        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_future.get_result() for media_future in media_futures])

        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
        })

        if short_cache:
            handler._cache_expiration = handler.SHORT_CACHE_EXPIRATION

        path = os.path.join(os.path.dirname(__file__),
                            '../templates/team_details.html')
        return template.render(path, handler.template_values)
    def render_team_history(cls, handler, team, 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()
        award_futures = award_query.TeamAwardsQuery(
            team.key.id()).fetch_async()
        event_futures = event_query.TeamEventsQuery(
            team.key.id()).fetch_async()
        participation_future = team_query.TeamParticipationQuery(
            team.key.id()).fetch_async()
        social_media_future = media_query.TeamSocialMediaQuery(
            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,
            },
        }

        awards_by_event = {}
        for award in award_futures.get_result():
            if award.event.id() not in awards_by_event:
                awards_by_event[award.event.id()] = [award]
            else:
                awards_by_event[award.event.id()].append(award)

        event_awards = []
        current_event = None
        matches_upcoming = None
        short_cache = False
        years = set()
        for event in event_futures.get_result():
            years.add(event.year)
            if event.now:
                current_event = event
                matches = match_query.TeamEventMatchesQuery(
                    team.key.id(), event.key.id()).fetch()
                matches_upcoming = MatchHelper.upcomingMatches(matches)

            if event.within_a_day:
                short_cache = True

            if event.key_name in awards_by_event:
                sorted_awards = AwardHelper.organizeAwards(
                    awards_by_event[event.key_name])
            else:
                sorted_awards = []
            event_awards.append((event, sorted_awards))
        event_awards = sorted(
            event_awards,
            key=lambda (e, _): e.start_date
            if e.start_date else datetime.datetime(e.year, 12, 31))

        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

        social_medias = sorted(social_media_future.get_result(),
                               key=MediaHelper.social_media_sorter)

        handler.template_values.update({
            "is_canonical": is_canonical,
            "team": team,
            "event_awards": event_awards,
            "years": sorted(years),
            "social_medias": social_medias,
            "current_event": current_event,
            "matches_upcoming": matches_upcoming,
            "last_competed": last_competed,
            "current_year": current_year,
            "max_year": tba_config.MAX_YEAR,
            "hof": hall_of_fame,
        })

        if short_cache:
            handler._cache_expiration = handler.SHORT_CACHE_EXPIRATION

        return jinja2_engine.render("team_history.html",
                                    handler.template_values)
예제 #33
0
    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']
                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']

                # 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
예제 #34
0
    def parse(self, matches_json, year):
        """
        Parse JSON that contains a list of matches for a given year where each match is a dict of:
        comp_level: String in the set {"qm", "ef", "qf", "sf", "f"}
        set_number: Integer identifying the elim set number. Ignored for qual matches. ex: the 4 in qf4m2
        match_number: Integer identifying the match number within a set. ex: the 2 in qf4m2
        alliances: Dict of {'red': {'teams': ['frcXXX'...], 'score': S}, 'blue': {...}}. Where scores (S) are integers. Null scores indicate that a match has not yet been played.
        score_breakdown: Dict of {'red': {K1: V1, K2: V2, ...}, 'blue': {...}}. Where Kn are keys and Vn are values for those keys.
        time_string: String in the format "(H)H:MM AM/PM" for when the match will be played in the event's local timezone. ex: "9:15 AM"
        time: UTC time of the match as a string in ISO 8601 format (YYYY-MM-DDTHH:MM:SS).
        """
        try:
            matches = json.loads(matches_json)
        except:
            raise ParserInputException("Invalid JSON. Please check input.")

        parsed_matches = []
        for match in matches:
            if type(match) is not dict:
                raise ParserInputException("Matches must be dicts.")

            comp_level = match.get('comp_level', None)
            set_number = match.get('set_number', None)
            match_number = match.get('match_number', None)
            alliances = match.get('alliances', None)
            score_breakdown = match.get('score_breakdown', None)
            time_string = match.get('time_string', None)
            time_utc = match.get('time_utc', None)

            if comp_level is None:
                raise ParserInputException("Match must have a 'comp_level'")
            if comp_level not in Match.COMP_LEVELS:
                raise ParserInputException(
                    "'comp_level' must be one of: {}".format(
                        Match.COMP_LEVELS))

            if comp_level == 'qm':
                set_number = 1
            elif set_number is None or type(set_number) is not int:
                raise ParserInputException(
                    "Match must have an integer 'set_number'")

            if match_number is None or type(match_number) is not int:
                raise ParserInputException(
                    "Match must have an integer 'match_number'")

            if type(alliances) is not dict:
                raise ParserInputException("'alliances' must be a dict")
            else:
                for color, details in alliances.items():
                    if color not in {'red', 'blue'}:
                        raise ParserInputException(
                            "Alliance color '{}' not recognized".format(color))
                    if 'teams' not in details:
                        raise ParserInputException(
                            "alliances[color] must have key 'teams'")
                    if 'score' not in details:
                        raise ParserInputException(
                            "alliances[color] must have key 'score'")
                    for team_key in details['teams']:
                        if not re.match(r'frc\d+', str(team_key)):
                            raise ParserInputException(
                                "Bad team: '{}'. Must follow format 'frcXXX'.".
                                format(team_key))
                    if details['score'] is not None and type(
                            details['score']) is not int:
                        raise ParserInputException(
                            "alliances[color]['score'] must be an integer or null"
                        )

            if score_breakdown is not None:
                if type(score_breakdown) is not dict:
                    raise ParserInputException(
                        "'score_breakdown' must be a dict")
                else:
                    for color, breakdown in score_breakdown.items():
                        if color not in {'red', 'blue'}:
                            raise ParserInputException(
                                "Alliance color '{}' not recognized".format(
                                    color))
                        for k in breakdown.keys():
                            is_valid = MatchHelper.is_valid_score_breakdown_key(
                                k, year)
                            if is_valid != True:
                                raise ParserInputException(
                                    "Valid score breakdowns for {} are: {}".
                                    format(year, is_valid))

            datetime_utc = None
            if time_utc is not None:
                try:
                    import iso8601
                    datetime_utc = iso8601.parse_date(time_utc)
                    # remove timezone info because DatetimeProperty can't handle timezones
                    datetime_utc = datetime_utc.replace(tzinfo=None)
                except Exception:
                    raise ParserInputException(
                        "Could not parse 'time_utc'. Check that it is in ISO 8601 format."
                    )

            # validation passed. build new dicts to sanitize
            parsed_alliances = {
                'red': {
                    'teams': alliances['red']['teams'],
                    'score': alliances['red']['score'],
                },
                'blue': {
                    'teams': alliances['blue']['teams'],
                    'score': alliances['blue']['score'],
                },
            }
            parsed_match = {
                'comp_level':
                comp_level,
                'set_number':
                set_number,
                'match_number':
                match_number,
                'alliances_json':
                json.dumps(parsed_alliances),
                'score_breakdown_json':
                json.dumps(score_breakdown)
                if score_breakdown is not None else None,
                'time_string':
                time_string,
                'time':
                datetime_utc,
                'team_key_names':
                parsed_alliances['red']['teams'] +
                parsed_alliances['blue']['teams'],
            }

            parsed_matches.append(parsed_match)
        return parsed_matches
예제 #35
0
    def _render(self, event_key):
        event = Event.get_by_id(event_key)

        if not event or event.year < 2016 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)
예제 #36
0
    def __init__(self, has_event_details=True, batch_advance=False):
        self._step = 0
        self._substep = 0
        # whether to update rankings and alliance selections
        self._has_event_details = has_event_details
        # whether to update next playoff level all at once, or as winners are determined
        self._batch_advance = batch_advance

        # Load and save complete data
        load_fixture('test_data/fixtures/2016nytr_event_team_status.json',
                     kind={
                         'EventDetails': EventDetails,
                         'Event': Event,
                         'Match': Match
                     },
                     post_processor=self._event_key_adder)
        event = Event.get_by_id('2016nytr')

        # Add 3rd matches that never got played
        unplayed_matches = [
            Match(
                id='2016nytr_qf1m3',
                year=2016,
                event=event.key,
                comp_level='qf',
                set_number=1,
                match_number=3,
                alliances_json=json.dumps({
                    'red': {
                        'teams': ['frc3990', 'frc359', 'frc4508'],
                        'score': -1,
                    },
                    'blue': {
                        'teams': ['frc3044', 'frc4930', 'frc4481'],
                        'score': -1,
                    }
                }),
                time=datetime.datetime(2016, 3, 19, 18, 34),
            ),
            Match(
                id='2016nytr_qf3m3',
                year=2016,
                event=event.key,
                comp_level='qf',
                set_number=3,
                match_number=3,
                alliances_json=json.dumps({
                    'red': {
                        'teams': ['frc20', 'frc5254', 'frc229'],
                        'score': -1,
                    },
                    'blue': {
                        'teams': ['frc3003', 'frc358', 'frc527'],
                        'score': -1,
                    }
                }),
                time=datetime.datetime(2016, 3, 19, 18, 48),
            ),
            Match(
                id='2016nytr_sf1m3',
                year=2016,
                event=event.key,
                comp_level='sf',
                set_number=1,
                match_number=3,
                alliances_json=json.dumps({
                    'red': {
                        'teams': ['frc3990', 'frc359', 'frc4508'],
                        'score': -1,
                    },
                    'blue': {
                        'teams': ['frc5240', 'frc3419', 'frc663'],
                        'score': -1,
                    }
                }),
                time=datetime.datetime(2016, 3, 19, 19, 42),
            )
        ]

        self._event_details = event.details
        self._alliance_selections_without_backup = copy.deepcopy(
            event.details.alliance_selections)
        self._alliance_selections_without_backup[1]['backup'] = None
        self._played_matches = MatchHelper.organizeMatches(event.matches)
        self._all_matches = MatchHelper.organizeMatches(event.matches +
                                                        unplayed_matches)

        # Delete data
        event.details.key.delete()
        ndb.delete_multi([match.key for match in event.matches])
        ndb.get_context().clear_cache()

        # Used to keep track of non-batch advancement
        self._advancement_alliances = defaultdict(dict)
    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 = PlayoffAdvancementHelper.generateBracket(
                matches, event, event.alliance_selections)
            if 'f' in bracket:
                winning_alliance = '{}_alliance'.format(
                    bracket['f']['f1']['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']['f1'][winning_alliance]
                            if team.isdigit()
                        ],
                        recipient_json_list=[
                            json.dumps({
                                'team_number': team,
                                'awardee': None
                            }) for team in bracket['f']['f1'][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']['f1'][losing_alliance]
                            if team.isdigit()
                        ],
                        recipient_json_list=[
                            json.dumps({
                                'team_number': team,
                                'awardee': None
                            }) for team in bracket['f']['f1'][losing_alliance]
                        ],
                    ))
                AwardManipulator.createOrUpdate(awards)

        self.response.out.write(
            "Finished post-event tasks for {}. Created awards: {}".format(
                event_key, awards))
예제 #38
0
    def calc_rank_based_match_points(cls, event, district_points, match_futures, POINTS_MULTIPLIER):
        """
        Calculates match district points based on team ranking
        This algorithm was introduced for the 2015 season and also used for 2016
        See: http://www.firstinspires.org/node/7616 and also
        http://www.firstinspires.org/robotics/frc/blog/Admin-Manual-Section-7-and-the-FIRST-STRONGHOLD-Logo
        """
        from helpers.match_helper import MatchHelper  # circular import issue

        # qual match points are calculated by rank
        if event.rankings and len(event.rankings) > 1:
            rankings = event.rankings[1:]  # skip title row
            num_teams = len(rankings)
            alpha = 1.07
            for row in rankings:
                rank = int(row[0])
                team = 'frc{}'.format(row[1])
                qual_points = int(np.ceil(cls.inverf(float(num_teams - 2 * rank + 2) / (alpha * num_teams)) * (
                10.0 / cls.inverf(1.0 / alpha)) + 12))
                district_points['points'][team]['qual_points'] = qual_points * POINTS_MULTIPLIER
        else:
            logging.warning("Event {} has no rankings for qual_points calculations!".format(event.key.id()))

        matches = MatchHelper.organizeMatches([mf.get_result() for mf in match_futures])

        # qual match calculations. only used for tiebreaking
        for match in matches['qm']:
            for color in ['red', 'blue']:
                for team in match.alliances[color]['teams']:
                    score = match.alliances[color]['score']
                    district_points['tiebreakers'][team]['highest_qual_scores'] = heapq.nlargest(3, district_points[
                        'tiebreakers'][team]['highest_qual_scores'] + [score])

        # elim match point calculations
        # count number of matches played per team per comp level
        num_played = defaultdict(lambda: defaultdict(int))
        for level in ['qf', 'sf']:
            for match in matches[level]:
                if not match.has_been_played:
                    continue
                for color in ['red', 'blue']:
                    for team in match.alliances[color]['teams']:
                        num_played[level][team] += 1

        # qf and sf points
        advancement = MatchHelper.generatePlayoffAdvancement2015(matches)
        for last_level, level in [('qf', 'sf'), ('sf', 'f')]:
            for (teams, _, _) in advancement[last_level]:
                teams = ['frc{}'.format(t) for t in teams]
                done = False
                for match in matches[level]:
                    for color in ['red', 'blue']:
                        if set(teams).intersection(set(match.alliances[color]['teams'])) != set():
                            for team in teams:
                                points = DistrictPointValues.QF_WIN.get(event.year, DistrictPointValues.QF_WIN_DEFAULT) if last_level == 'qf' else DistrictPointValues.SF_WIN.get(event.year, DistrictPointValues.SF_WIN_DEFAULT)
                                district_points['points'][team]['elim_points'] += int(
                                    np.ceil(points * num_played[last_level][team])) * POINTS_MULTIPLIER
                            done = True
                            break
                        if done:
                            break
                    if done:
                        break

        # final points
        num_wins = {'red': 0, 'blue': 0}
        team_matches_played = {'red': [], 'blue': []}
        for match in matches['f']:
            if not match.has_been_played or match.winning_alliance == '':
                continue

            num_wins[match.winning_alliance] += 1
            for team in match.alliances[match.winning_alliance]['teams']:
                team_matches_played[match.winning_alliance].append(team)

            if num_wins[match.winning_alliance] >= 2:
                points = DistrictPointValues.F_WIN.get(event.year, DistrictPointValues.F_WIN_DEFAULT)
                for team in team_matches_played[match.winning_alliance]:
                    district_points['points'][team]['elim_points'] += points * POINTS_MULTIPLIER
예제 #39
0
    def step(self):
        event = Event.get_by_id('2016nytr')

        if self._step == 0:  # Qual match schedule added
            for match in copy.deepcopy(self._all_matches['qm']):
                for alliance in ['red', 'blue']:
                    match.alliances[alliance]['score'] = -1
                match.alliances_json = json.dumps(match.alliances)
                match.score_breakdown_json = None
                match.actual_time = None
                MatchManipulator.createOrUpdate(match)

            self._step += 1
        elif self._step == 1:  # After each qual match
            MatchManipulator.createOrUpdate(
                self._played_matches['qm'][self._substep])
            if self._substep < len(self._played_matches['qm']) - 1:
                self._substep += 1
            else:
                self._step += 1
                self._substep = 0
            EventDetailsManipulator.createOrUpdate(EventDetails(id='2016nytr'))
        elif self._step == 2:  # After alliance selections
            EventDetailsManipulator.createOrUpdate(
                EventDetails(id='2016nytr',
                             alliance_selections=self.
                             _alliance_selections_without_backup))
            self._step += 1
        elif self._step == 3:  # QF schedule added
            for match in copy.deepcopy(self._all_matches['qf']):
                for alliance in ['red', 'blue']:
                    match.alliances[alliance]['score'] = -1
                match.alliances_json = json.dumps(match.alliances)
                match.score_breakdown_json = None
                match.actual_time = None
                MatchManipulator.createOrUpdate(match)
            self._step += 1
        elif self._step == 4:  # After each QF match
            new_match = MatchHelper.play_order_sort_matches(
                self._played_matches['qf'])[self._substep]
            MatchManipulator.createOrUpdate(new_match)

            if not self._batch_advance:
                win_counts = {
                    'red': 0,
                    'blue': 0,
                }
                for i in xrange(new_match.match_number):
                    win_counts[Match.get_by_id(
                        Match.renderKeyName(new_match.event.id(),
                                            new_match.comp_level,
                                            new_match.set_number,
                                            i + 1)).winning_alliance] += 1
                for alliance, wins in win_counts.items():
                    if wins == 2:
                        s = new_match.set_number
                        if s in {1, 2}:
                            self._advancement_alliances[
                                'sf1']['red' if s ==
                                       1 else 'blue'] = new_match.alliances[
                                           alliance]['teams']
                        elif s in {3, 4}:
                            self._advancement_alliances[
                                'sf2']['red' if s ==
                                       3 else 'blue'] = new_match.alliances[
                                           alliance]['teams']
                        else:
                            raise Exception("Invalid set number: {}".format(s))

                        for match_set, alliances in self._advancement_alliances.items(
                        ):
                            if match_set.startswith('sf'):
                                for i in xrange(3):
                                    for match in copy.deepcopy(
                                            self._all_matches['sf']):
                                        key = '2016nytr_{}m{}'.format(
                                            match_set, i + 1)
                                        if match.key.id() == key:
                                            for color in ['red', 'blue']:
                                                match.alliances[color][
                                                    'score'] = -1
                                                match.alliances[color][
                                                    'teams'] = alliances.get(
                                                        color, [])
                                            match.alliances_json = json.dumps(
                                                match.alliances)
                                            match.score_breakdown_json = None
                                            match.actual_time = None
                                            MatchManipulator.createOrUpdate(
                                                match)

            if self._substep < len(self._played_matches['qf']) - 1:
                self._substep += 1
            else:
                self._step += 1 if self._batch_advance else 2
                self._substep = 0
        elif self._step == 5:  # SF schedule added
            if self._batch_advance:
                for match in copy.deepcopy(self._all_matches['sf']):
                    for alliance in ['red', 'blue']:
                        match.alliances[alliance]['score'] = -1
                    match.alliances_json = json.dumps(match.alliances)
                    match.score_breakdown_json = None
                    match.actual_time = None
                    MatchManipulator.createOrUpdate(match)
                self._step += 1
        elif self._step == 6:  # After each SF match
            new_match = MatchHelper.play_order_sort_matches(
                self._played_matches['sf'])[self._substep]
            MatchManipulator.createOrUpdate(new_match)

            if not self._batch_advance:
                win_counts = {
                    'red': 0,
                    'blue': 0,
                }
                for i in xrange(new_match.match_number):
                    win_counts[Match.get_by_id(
                        Match.renderKeyName(new_match.event.id(),
                                            new_match.comp_level,
                                            new_match.set_number,
                                            i + 1)).winning_alliance] += 1
                for alliance, wins in win_counts.items():
                    if wins == 2:
                        self._advancement_alliances['f1'][
                            'red' if new_match.set_number == 1 else
                            'blue'] = new_match.alliances[alliance]['teams']

                        for match_set, alliances in self._advancement_alliances.items(
                        ):
                            if match_set.startswith('f'):
                                for i in xrange(3):
                                    for match in copy.deepcopy(
                                            self._all_matches['f']):
                                        key = '2016nytr_{}m{}'.format(
                                            match_set, i + 1)
                                        if match.key.id() == key:
                                            for color in ['red', 'blue']:
                                                match.alliances[color][
                                                    'score'] = -1
                                                match.alliances[color][
                                                    'teams'] = alliances.get(
                                                        color, [])
                                            match.alliances_json = json.dumps(
                                                match.alliances)
                                            match.score_breakdown_json = None
                                            match.actual_time = None
                                            MatchManipulator.createOrUpdate(
                                                match)

            # Backup robot introduced
            if self._substep == 3:
                EventDetailsManipulator.createOrUpdate(
                    EventDetails(id='2016nytr',
                                 alliance_selections=self._event_details.
                                 alliance_selections))
            if self._substep < len(self._played_matches['sf']) - 1:
                self._substep += 1
            else:
                self._step += 1 if self._batch_advance else 2
                self._substep = 0
        elif self._step == 7:  # F schedule added
            if self._batch_advance:
                for match in copy.deepcopy(self._all_matches['f']):
                    for alliance in ['red', 'blue']:
                        match.alliances[alliance]['score'] = -1
                    match.alliances_json = json.dumps(match.alliances)
                    match.score_breakdown_json = None
                    match.actual_time = None
                    MatchManipulator.createOrUpdate(match)
                self._step += 1
        elif self._step == 8:  # After each F match
            MatchManipulator.createOrUpdate(
                MatchHelper.play_order_sort_matches(
                    self._played_matches['f'])[self._substep])
            if self._substep < len(self._played_matches['f']) - 1:
                self._substep += 1
            else:
                self._step += 1
                self._substep = 0

        ndb.get_context().clear_cache()
        # Re fetch event matches
        event = Event.get_by_id('2016nytr')
        MatchHelper.deleteInvalidMatches(event.matches)
        ndb.get_context().clear_cache()
        self._update_rankings()
예제 #40
0
    def parse(self, response):
        matches = response['Schedule']

        event_key = '{}{}'.format(self.year, self.event_short)
        event = Event.get_by_id(event_key)
        if event.timezone_id:
            event_tz = pytz.timezone(event.timezone_id)
        else:
            logging.warning(
                "Event {} has no timezone! Match times may be wrong.".format(
                    event_key))
            event_tz = None

        parsed_matches = []
        is_octofinals = len(
            matches) > 0 and 'Octofinal' in matches[0]['description']
        for match in matches:
            if 'tournamentLevel' in match:  # 2016+
                level = match['tournamentLevel']
            else:  # 2015
                level = match['level']
            comp_level = get_comp_level(self.year, level, match['matchNumber'],
                                        is_octofinals)
            set_number, match_number = get_set_match_number(
                self.year, comp_level, match['matchNumber'], is_octofinals)

            red_teams = []
            blue_teams = []
            team_key_names = []
            null_team = False
            sorted_teams = sorted(
                match['Teams'], key=lambda team: team['station']
            )  # Sort by station to ensure correct ordering. Kind of hacky.
            for team in sorted_teams:
                if team['teamNumber'] is None:
                    null_team = True
                team_key = 'frc{}'.format(team['teamNumber'])
                team_key_names.append(team_key)
                if 'Red' in team['station']:
                    red_teams.append(team_key)
                elif 'Blue' in team['station']:
                    blue_teams.append(team_key)
            if null_team and match['scoreRedFinal'] is None and match[
                    'scoreBlueFinal'] is None:
                continue

            alliances = {
                'red': {
                    'teams': red_teams,
                    'score': match['scoreRedFinal']
                },
                'blue': {
                    'teams': blue_teams,
                    'score': match['scoreBlueFinal']
                }
            }

            if not match[
                    'startTime']:  # no startTime means it's an unneeded rubber match
                continue

            time = datetime.datetime.strptime(match['startTime'].split('.')[0],
                                              TIME_PATTERN)
            actual_time_raw = match[
                'actualStartTime'] if 'actualStartTime' in match else None
            actual_time = None
            if event_tz is not None:
                time = time - event_tz.utcoffset(time)

            if actual_time_raw is not None:
                actual_time = datetime.datetime.strptime(
                    actual_time_raw.split('.')[0], TIME_PATTERN)
                if event_tz is not None:
                    actual_time = actual_time - event_tz.utcoffset(actual_time)

            parsed_matches.append(
                Match(
                    id=Match.renderKeyName(event_key, comp_level, set_number,
                                           match_number),
                    event=event.key,
                    year=event.year,
                    set_number=set_number,
                    match_number=match_number,
                    comp_level=comp_level,
                    team_key_names=team_key_names,
                    time=time,
                    actual_time=actual_time,
                    alliances_json=json.dumps(alliances),
                ))

        if self.year == 2015:
            # Fix null teams in elims (due to FMS API failure, some info not complete)
            # Should only happen for sf and f matches
            organized_matches = MatchHelper.organizeMatches(parsed_matches)
            for level in ['sf', 'f']:
                playoff_advancement = MatchHelper.generatePlayoffAdvancement2015(
                    organized_matches)
                if playoff_advancement[LAST_LEVEL[level]] != []:
                    for match in organized_matches[level]:
                        if 'frcNone' in match.team_key_names:
                            if level == 'sf':
                                red_seed, blue_seed = QF_SF_MAP[
                                    match.match_number]
                            else:
                                red_seed = 0
                                blue_seed = 1
                            red_teams = [
                                'frc{}'.format(t) for t in playoff_advancement[
                                    LAST_LEVEL[level]][red_seed][0]
                            ]
                            blue_teams = [
                                'frc{}'.format(t) for t in playoff_advancement[
                                    LAST_LEVEL[level]][blue_seed][0]
                            ]

                            alliances = match.alliances
                            alliances['red']['teams'] = red_teams
                            alliances['blue']['teams'] = blue_teams
                            match.alliances_json = json.dumps(alliances)
                            match.team_key_names = red_teams + blue_teams

            fixed_matches = []
            for key, matches in organized_matches.items():
                if key != 'num':
                    for match in matches:
                        if 'frcNone' not in match.team_key_names:
                            fixed_matches.append(match)
            parsed_matches = fixed_matches

        return parsed_matches
    def test_2017ncwin(self):
        event = Event(id='2017ncwin',
                      event_short='ncwin',
                      year=2017,
                      event_type_enum=0,
                      timezone_id='America/New_York')
        event.put()

        MatchManipulator.createOrUpdate(
            DatafeedFMSAPI('v2.0',
                           sim_time=datetime.datetime(
                               2017, 3, 05, 21, 2)).getMatches('2017ncwin'))
        MatchHelper.deleteInvalidMatches(event.matches, event)
        sf_matches = Match.query(Match.event == ndb.Key(Event, '2017ncwin'),
                                 Match.comp_level == 'sf').fetch()
        self.assertEqual(len(sf_matches), 6)

        self.assertEqual(
            Match.get_by_id('2017ncwin_sf2m1').alliances['red']['score'], 265)
        self.assertEqual(
            Match.get_by_id('2017ncwin_sf2m1').alliances['blue']['score'], 150)
        self.assertEqual(
            Match.get_by_id('2017ncwin_sf2m1').score_breakdown['red']
            ['totalPoints'], 265)
        self.assertEqual(
            Match.get_by_id('2017ncwin_sf2m1').score_breakdown['blue']
            ['totalPoints'], 150)

        ndb.get_context().clear_cache(
        )  # Prevent data from leaking between tests

        MatchManipulator.createOrUpdate(
            DatafeedFMSAPI('v2.0',
                           sim_time=datetime.datetime(
                               2017, 3, 05, 21, 30)).getMatches('2017ncwin'))
        MatchHelper.deleteInvalidMatches(event.matches, event)
        sf_matches = Match.query(Match.event == ndb.Key(Event, '2017ncwin'),
                                 Match.comp_level == 'sf').fetch()
        self.assertEqual(len(sf_matches), 6)

        self.assertEqual(
            Match.get_by_id('2017ncwin_sf2m1').alliances['red']['score'], 265)
        self.assertEqual(
            Match.get_by_id('2017ncwin_sf2m1').alliances['blue']['score'], 150)
        self.assertEqual(
            Match.get_by_id('2017ncwin_sf2m1').score_breakdown['red']
            ['totalPoints'], 265)
        self.assertEqual(
            Match.get_by_id('2017ncwin_sf2m1').score_breakdown['blue']
            ['totalPoints'], 150)
        self.assertEqual(
            Match.get_by_id('2017ncwin_sf2m2').alliances['red']['score'], 205)
        self.assertEqual(
            Match.get_by_id('2017ncwin_sf2m2').alliances['blue']['score'], 205)
        self.assertEqual(
            Match.get_by_id('2017ncwin_sf2m2').score_breakdown['red']
            ['totalPoints'], 205)
        self.assertEqual(
            Match.get_by_id('2017ncwin_sf2m2').score_breakdown['blue']
            ['totalPoints'], 205)

        ndb.get_context().clear_cache(
        )  # Prevent data from leaking between tests

        MatchManipulator.createOrUpdate(
            DatafeedFMSAPI('v2.0',
                           sim_time=datetime.datetime(
                               2017, 3, 05, 21, 35)).getMatches('2017ncwin'))
        MatchHelper.deleteInvalidMatches(event.matches, event)
        sf_matches = Match.query(Match.event == ndb.Key(Event, '2017ncwin'),
                                 Match.comp_level == 'sf').fetch()
        self.assertEqual(len(sf_matches), 6)

        self.assertEqual(
            Match.get_by_id('2017ncwin_sf2m1').alliances['red']['score'], 265)
        self.assertEqual(
            Match.get_by_id('2017ncwin_sf2m1').alliances['blue']['score'], 150)
        self.assertEqual(
            Match.get_by_id('2017ncwin_sf2m1').score_breakdown['red']
            ['totalPoints'], 265)
        self.assertEqual(
            Match.get_by_id('2017ncwin_sf2m1').score_breakdown['blue']
            ['totalPoints'], 150)
        self.assertEqual(
            Match.get_by_id('2017ncwin_sf2m2').alliances['red']['score'], 205)
        self.assertEqual(
            Match.get_by_id('2017ncwin_sf2m2').alliances['blue']['score'], 205)
        self.assertEqual(
            Match.get_by_id('2017ncwin_sf2m2').score_breakdown['red']
            ['totalPoints'], 205)
        self.assertEqual(
            Match.get_by_id('2017ncwin_sf2m2').score_breakdown['blue']
            ['totalPoints'], 205)
        self.assertEqual(
            Match.get_by_id('2017ncwin_sf2m3').alliances['red']['score'], 145)
        self.assertEqual(
            Match.get_by_id('2017ncwin_sf2m3').alliances['blue']['score'], 265)
        self.assertEqual(
            Match.get_by_id('2017ncwin_sf2m3').score_breakdown['red']
            ['totalPoints'], 145)
        self.assertEqual(
            Match.get_by_id('2017ncwin_sf2m3').score_breakdown['blue']
            ['totalPoints'], 265)

        ndb.get_context().clear_cache(
        )  # Prevent data from leaking between tests

        MatchManipulator.createOrUpdate(
            DatafeedFMSAPI('v2.0',
                           sim_time=datetime.datetime(
                               2017, 3, 05, 21, 51)).getMatches('2017ncwin'))
        MatchHelper.deleteInvalidMatches(event.matches, event)
        sf_matches = Match.query(Match.event == ndb.Key(Event, '2017ncwin'),
                                 Match.comp_level == 'sf').fetch()
        self.assertEqual(len(sf_matches), 7)

        self.assertEqual(
            Match.get_by_id('2017ncwin_sf2m1').alliances['red']['score'], 265)
        self.assertEqual(
            Match.get_by_id('2017ncwin_sf2m1').alliances['blue']['score'], 150)
        self.assertEqual(
            Match.get_by_id('2017ncwin_sf2m1').score_breakdown['red']
            ['totalPoints'], 265)
        self.assertEqual(
            Match.get_by_id('2017ncwin_sf2m1').score_breakdown['blue']
            ['totalPoints'], 150)
        self.assertEqual(
            Match.get_by_id('2017ncwin_sf2m2').alliances['red']['score'], 205)
        self.assertEqual(
            Match.get_by_id('2017ncwin_sf2m2').alliances['blue']['score'], 205)
        self.assertEqual(
            Match.get_by_id('2017ncwin_sf2m2').score_breakdown['red']
            ['totalPoints'], 205)
        self.assertEqual(
            Match.get_by_id('2017ncwin_sf2m2').score_breakdown['blue']
            ['totalPoints'], 205)
        self.assertEqual(
            Match.get_by_id('2017ncwin_sf2m3').alliances['red']['score'], 145)
        self.assertEqual(
            Match.get_by_id('2017ncwin_sf2m3').alliances['blue']['score'], 265)
        self.assertEqual(
            Match.get_by_id('2017ncwin_sf2m3').score_breakdown['red']
            ['totalPoints'], 145)
        self.assertEqual(
            Match.get_by_id('2017ncwin_sf2m3').score_breakdown['blue']
            ['totalPoints'], 265)
        self.assertEqual(
            Match.get_by_id('2017ncwin_sf2m4').alliances['red']['score'], 180)
        self.assertEqual(
            Match.get_by_id('2017ncwin_sf2m4').alliances['blue']['score'], 305)
        self.assertEqual(
            Match.get_by_id('2017ncwin_sf2m4').score_breakdown['red']
            ['totalPoints'], 180)
        self.assertEqual(
            Match.get_by_id('2017ncwin_sf2m4').score_breakdown['blue']
            ['totalPoints'], 305)
예제 #42
0
    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)
예제 #43
0
    def render_team_history(cls, handler, team, is_canonical):
        award_futures = award_query.TeamAwardsQuery(
            team.key.id()).fetch_async()
        event_futures = event_query.TeamEventsQuery(
            team.key.id()).fetch_async()
        participation_future = team_query.TeamParticipationQuery(
            team.key.id()).fetch_async()

        awards_by_event = {}
        for award in award_futures.get_result():
            if award.event.id() not in awards_by_event:
                awards_by_event[award.event.id()] = [award]
            else:
                awards_by_event[award.event.id()].append(award)

        event_awards = []
        current_event = None
        matches_upcoming = None
        short_cache = False
        years = set()
        for event in event_futures.get_result():
            years.add(event.year)
            if event.now:
                current_event = event
                matches = match_query.TeamEventMatchesQuery(
                    team.key.id(), event.key.id()).fetch()
                matches_upcoming = MatchHelper.upcomingMatches(matches)

            if event.within_a_day:
                short_cache = True

            if event.key_name in awards_by_event:
                sorted_awards = AwardHelper.organizeAwards(
                    awards_by_event[event.key_name])
            else:
                sorted_awards = []
            event_awards.append((event, sorted_awards))
        event_awards = sorted(
            event_awards,
            key=lambda (e, _): e.start_date
            if e.start_date else datetime.datetime(e.year, 12, 31))

        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,
            'event_awards': event_awards,
            'years': sorted(years),
            'current_event': current_event,
            'matches_upcoming': matches_upcoming,
            'last_competed': last_competed,
            'current_year': current_year
        })

        if short_cache:
            handler._cache_expiration = handler.SHORT_CACHE_EXPIRATION

        return jinja2_engine.render('team_history.html',
                                    handler.template_values)
예제 #44
0
    def get(self):
        self._require_registration()

        current_events = filter(lambda e: e.now,
                                EventHelper.getEventsWithinADay())
        popular_teams_events = TeamHelper.getPopularTeamsEvents(current_events)

        popular_team_keys = set()
        for team, _ in popular_teams_events:
            popular_team_keys.add(team.key.id())

        for event in current_events:
            event.prep_details()
            event.prep_matches()

        finished_matches = []
        current_matches = []
        upcoming_matches = []
        ranks = {}
        alliances = {}
        for event in current_events:
            if not event.details:
                continue
            finished_matches += MatchHelper.recentMatches(event.matches, num=1)
            for i, match in enumerate(
                    MatchHelper.upcomingMatches(event.matches, num=3)):
                if not match.time:
                    continue

                if not event.details.predictions or match.key.id(
                ) not in event.details.predictions['match_predictions'][
                        'qual' if match.comp_level == 'qm' else 'playoff']:
                    match.prediction = defaultdict(lambda: defaultdict(float))
                    match.bluezone_score = 0
                else:
                    match.prediction = event.details.predictions[
                        'match_predictions']['qual' if match.comp_level ==
                                             'qm' else 'playoff'][
                                                 match.key.id()]
                    match.bluezone_score = self.get_qual_bluezone_score(
                        match.prediction
                    ) if match.comp_level == 'qm' else self.get_elim_bluezone_score(
                        match.prediction)
                if i == 0:
                    current_matches.append(match)
                else:
                    upcoming_matches.append(match)
            if event.details.rankings2:
                for rank in event.details.rankings2:
                    ranks[rank['team_key']] = rank['rank']
            if event.alliance_selections:
                for i, alliance in enumerate(event.alliance_selections):
                    for pick in alliance['picks']:
                        alliances[pick] = i + 1

        finished_matches = sorted(finished_matches,
                                  key=lambda m: m.actual_time
                                  if m.actual_time else m.time)
        current_matches = sorted(current_matches,
                                 key=lambda m: m.predicted_time
                                 if m.predicted_time else m.time)
        upcoming_matches = sorted(upcoming_matches,
                                  key=lambda m: m.predicted_time
                                  if m.predicted_time else m.time)

        self.template_values.update({
            'finished_matches': finished_matches,
            'current_matches': current_matches,
            'upcoming_matches': upcoming_matches,
            'ranks': ranks,
            'alliances': alliances,
            'popular_team_keys': popular_team_keys,
        })

        self.response.out.write(
            jinja2_engine.render('match_suggestion.html',
                                 self.template_values))
    def _render(self, event_key):
        event = Event.get_by_id(event_key)

        if not event:
            self.abort(404)

        event.prepAwardsMatchesTeams()

        awards = AwardHelper.organizeAwards(event.awards)
        cleaned_matches = MatchHelper.deleteInvalidMatches(event.matches)
        matches = MatchHelper.organizeMatches(cleaned_matches)
        teams = TeamHelper.sortTeams(event.teams)

        num_teams = len(teams)
        middle_value = num_teams / 2
        if num_teams % 2 != 0:
            middle_value += 1
        teams_a, teams_b = teams[:middle_value], teams[middle_value:]

        oprs = [i for i in event.matchstats['oprs'].items()
                ] if (event.matchstats is not None
                      and 'oprs' in event.matchstats) else []
        oprs = sorted(oprs, key=lambda t: t[1], reverse=True)  # sort by OPR
        oprs = oprs[:15]  # get the top 15 OPRs

        if event.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)
예제 #46
0
    def parse(self, response):
        import pytz

        matches = response['Schedule']

        event_key = '{}{}'.format(self.year, self.event_short)
        event = Event.get_by_id(event_key)
        if event.timezone_id:
            event_tz = pytz.timezone(event.timezone_id)
        else:
            logging.warning(
                "Event {} has no timezone! Match times may be wrong.".format(
                    event_key))
            event_tz = None

        parsed_matches = []
        remapped_matches = {}  # If a key changes due to a tiebreaker
        for match in matches:
            if 'tournamentLevel' in match:  # 2016+
                level = match['tournamentLevel']
            else:  # 2015
                level = match['level']
            comp_level = PlayoffType.get_comp_level(event.playoff_type, level,
                                                    match['matchNumber'])
            set_number, match_number = PlayoffType.get_set_match_number(
                event.playoff_type, comp_level, match['matchNumber'])

            red_teams = []
            blue_teams = []
            red_surrogates = []
            blue_surrogates = []
            red_dqs = []
            blue_dqs = []
            team_key_names = []
            null_team = False
            sorted_teams = sorted(
                match.get('teams', match.get('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)
                    if team['dq']:
                        red_dqs.append(team_key)
                elif 'Blue' in team['station']:
                    blue_teams.append(team_key)
                    if team['surrogate']:
                        blue_surrogates.append(team_key)
                    if team['dq']:
                        blue_dqs.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,
                    'dqs': red_dqs,
                    'score': match['scoreRedFinal']
                },
                'blue': {
                    'teams': blue_teams,
                    'surrogates': blue_surrogates,
                    'dqs': blue_dqs,
                    '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 _render(self, event_key):
        event = Event.get_by_id(event_key)

        if not event:
            self.abort(404)

        event.prepAwardsMatchesTeams()
        medias_future = media_query.EventTeamsPreferredMediasQuery(event_key).fetch_async()

        awards = AwardHelper.organizeAwards(event.awards)
        cleaned_matches = MatchHelper.deleteInvalidMatches(event.matches)
        matches = MatchHelper.organizeMatches(cleaned_matches)
        teams = TeamHelper.sortTeams(event.teams)

        # Organize medias by team
        image_medias = MediaHelper.get_images([media for media in medias_future.get_result()])
        team_medias = defaultdict(list)
        for image_media in image_medias:
            for reference in image_media.references:
                team_medias[reference].append(image_media)
        team_and_medias = []
        for team in teams:
            team_and_medias.append((team, team_medias.get(team.key, [])))

        num_teams = len(team_and_medias)
        middle_value = num_teams / 2
        if num_teams % 2 != 0:
            middle_value += 1
        teams_a, teams_b = team_and_medias[:middle_value], team_and_medias[middle_value:]

        oprs = [i for i in event.matchstats['oprs'].items()] if (event.matchstats is not None and 'oprs' in event.matchstats) else []
        oprs = sorted(oprs, key=lambda t: t[1], reverse=True)  # sort by OPR
        oprs = oprs[:15]  # get the top 15 OPRs

        if event.now:
            matches_recent = MatchHelper.recentMatches(cleaned_matches)
            matches_upcoming = MatchHelper.upcomingMatches(cleaned_matches)
        else:
            matches_recent = None
            matches_upcoming = None

        bracket_table = MatchHelper.generateBracket(matches, event.alliance_selections)
        is_2015_playoff = EventHelper.is_2015_playoff(event_key)
        if is_2015_playoff:
            playoff_advancement = MatchHelper.generatePlayoffAdvancement2015(matches, event.alliance_selections)
            for comp_level in ['qf', 'sf']:
                if comp_level in bracket_table:
                    del bracket_table[comp_level]
        else:
            playoff_advancement = None

        district_points_sorted = None
        if event.district_points:
            district_points_sorted = sorted(event.district_points['points'].items(), key=lambda (team, points): -points['total'])

        event_insights = EventInsightsHelper.calculate_event_insights(cleaned_matches, event.year)
        event_insights_template = None
        if event_insights:
            event_insights_template = 'event_partials/event_insights_{}.html'.format(event.year)

        # rankings processing for ranking score per match
        full_rankings = event.rankings
        rankings_enhanced = event.rankings_enhanced
        if rankings_enhanced is not None:
            rp_index = RankingIndexes.CUMULATIVE_RANKING_SCORE[event.year]
            matches_index = RankingIndexes.MATCHES_PLAYED[event.year]
            ranking_criterion_name = full_rankings[0][rp_index]
            full_rankings[0].append(ranking_criterion_name + "/Match*")

            for row in full_rankings[1:]:
                team = row[1]
                if rankings_enhanced["ranking_score_per_match"] is not None:
                    rp_per_match = rankings_enhanced['ranking_score_per_match'][team]
                    row.append(rp_per_match)
                if rankings_enhanced["match_offset"] is not None:
                    match_offset = rankings_enhanced["match_offset"][team]
                    if match_offset != 0:
                        row[matches_index] = "{} ({})".format(row[matches_index], match_offset)

        self.template_values.update({
            "event": event,
            "district_name": DistrictType.type_names.get(event.event_district_enum, None),
            "district_abbrev": DistrictType.type_abbrevs.get(event.event_district_enum, None),
            "matches": matches,
            "matches_recent": matches_recent,
            "matches_upcoming": matches_upcoming,
            "awards": awards,
            "teams_a": teams_a,
            "teams_b": teams_b,
            "num_teams": num_teams,
            "oprs": oprs,
            "bracket_table": bracket_table,
            "playoff_advancement": playoff_advancement,
            "district_points_sorted": district_points_sorted,
            "is_2015_playoff": is_2015_playoff,
            "event_insights_qual": event_insights['qual'] if event_insights else None,
            "event_insights_playoff": event_insights['playoff'] if event_insights else None,
            "event_insights_template": event_insights_template,
        })

        if event.within_a_day:
            self._cache_expiration = self.SHORT_CACHE_EXPIRATION

        return jinja2_engine.render('event_details.html', self.template_values)
    def 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

        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

            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()])
        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)

        district_name = None
        district_abbrev = None
        team_districts = team_districts_future.get_result()
        for district in team_districts:
            if district.year == year:
                district_abbrev = district.abbreviation
                district_name = district.display_name

        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
        })

        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()

        awards = AwardHelper.organizeAwards(event.awards)
        cleaned_matches = MatchHelper.deleteInvalidMatches(event.matches)
        matches = MatchHelper.organizeMatches(cleaned_matches)
        teams = TeamHelper.sortTeams(event.teams)

        num_teams = len(teams)
        middle_value = num_teams / 2
        if num_teams % 2 != 0:
            middle_value += 1
        teams_a, teams_b = teams[:middle_value], teams[middle_value:]

        oprs = [i for i in event.matchstats['oprs'].items()
                ] if (event.matchstats is not None
                      and 'oprs' in event.matchstats) else []
        oprs = sorted(oprs, key=lambda t: t[1], reverse=True)  # sort by OPR
        oprs = oprs[:15]  # get the top 15 OPRs

        if event.now:
            matches_recent = MatchHelper.recentMatches(cleaned_matches)
            matches_upcoming = MatchHelper.upcomingMatches(cleaned_matches)
        else:
            matches_recent = None
            matches_upcoming = None

        bracket_table = MatchHelper.generateBracket(matches,
                                                    event.alliance_selections)
        is_2015_playoff = EventHelper.is_2015_playoff(event_key)
        if is_2015_playoff:
            playoff_advancement = MatchHelper.generatePlayoffAdvancement2015(
                matches, event.alliance_selections)
            for comp_level in ['qf', 'sf']:
                if comp_level in bracket_table:
                    del bracket_table[comp_level]
        else:
            playoff_advancement = None

        district_points_sorted = None
        if event.district_points:
            district_points_sorted = sorted(
                event.district_points['points'].items(),
                key=lambda (team, points): -points['total'])

        event_insights = EventInsightsHelper.calculate_event_insights(
            cleaned_matches, event.year)

        self.template_values.update({
            "event": event,
            "matches": matches,
            "matches_recent": matches_recent,
            "matches_upcoming": matches_upcoming,
            "awards": awards,
            "teams_a": teams_a,
            "teams_b": teams_b,
            "num_teams": num_teams,
            "oprs": oprs,
            "bracket_table": bracket_table,
            "playoff_advancement": playoff_advancement,
            "district_points_sorted": district_points_sorted,
            "is_2015_playoff": is_2015_playoff,
            "event_insights": event_insights
        })

        if event.within_a_day:
            self._cache_expiration = self.SHORT_CACHE_EXPIRATION

        return jinja2_engine.render('event_details.html', self.template_values)
    def _render(self, event_key):
        event = Event.get_by_id(event_key)

        if not event or event.year != 2016:
            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)
        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 not matches['qm'] and match_predictions:
            fake_matches = True
            for i in xrange(len(match_predictions.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),
                ))

        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,
            "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)
예제 #51
0
    def calculate_event_points(cls, event):
        match_key_futures = Match.query(Match.event == event.key).fetch_async(None, keys_only=True)
        award_key_futures = Award.query(Award.event == event.key).fetch_async(None, keys_only=True)

        match_futures = ndb.get_multi_async(match_key_futures.get_result())
        award_futures = ndb.get_multi_async(award_key_futures.get_result())

        POINTS_MULTIPLIER = 3 if event.event_type_enum == EventType.DISTRICT_CMP else 1

        district_points = {
            'points': defaultdict(lambda: {
                'qual_points': 0,
                'elim_points': 0,
                'alliance_points': 0,
                'award_points': 0,
                'total': 0,
            }),
            'tiebreakers': defaultdict(lambda: {  # for tiebreaker stats that can't be calculated with 'points'
                'qual_wins': 0,
                'highest_qual_scores': [],
            }),
        }

        # match points
        if event.year == 2015:
            from helpers.match_helper import MatchHelper  # circular import issue

            # qual match points are calculated by rank
            if event.rankings and len(event.rankings) > 1:
                rankings = event.rankings[1:]  # skip title row
                num_teams = len(rankings)
                alpha = 1.07
                for row in rankings:
                    rank = int(row[0])
                    team = 'frc{}'.format(row[1])
                    qual_points = int(np.ceil(cls.inverf(float(num_teams - 2 * rank + 2) / (alpha * num_teams)) * (10.0 / cls.inverf(1.0 / alpha)) + 12))
                    district_points['points'][team]['qual_points'] = qual_points * POINTS_MULTIPLIER
            else:
                logging.warning("Event {} has no rankings for qual_points calculations!".format(event.key.id()))

            matches = MatchHelper.organizeMatches([mf.get_result() for mf in match_futures])

            # qual match calculations. only used for tiebreaking
            for match in matches['qm']:
                for color in ['red', 'blue']:
                    for team in match.alliances[color]['teams']:
                        score = match.alliances[color]['score']
                        district_points['tiebreakers'][team]['highest_qual_scores'] = heapq.nlargest(3, district_points['tiebreakers'][team]['highest_qual_scores'] + [score])

            # elim match point calculations
            # count number of matches played per team per comp level
            num_played = defaultdict(lambda: defaultdict(int))
            for level in ['qf', 'sf']:
                for match in matches[level]:
                    if not match.has_been_played:
                        continue
                    for color in ['red', 'blue']:
                        for team in match.alliances[color]['teams']:
                            num_played[level][team] += 1

            # qf and sf points
            advancement = MatchHelper.generatePlayoffAdvancement2015(matches)
            for last_level, level in [('qf', 'sf'), ('sf', 'f')]:
                for (teams, _, _) in advancement[last_level]:
                    teams = ['frc{}'.format(t) for t in teams]
                    done = False
                    for match in matches[level]:
                        for color in ['red', 'blue']:
                            if set(teams).intersection(set(match.alliances[color]['teams'])) != set():
                                for team in teams:
                                    points = 5.0 if last_level == 'qf' else 3.3
                                    district_points['points'][team]['elim_points'] += int(np.ceil(points * num_played[last_level][team])) * POINTS_MULTIPLIER
                                done = True
                                break
                            if done:
                                break
                        if done:
                            break

            # final points
            num_wins = {'red': 0, 'blue': 0}
            team_matches_played = {'red': [], 'blue': []}
            for match in matches['f']:
                if not match.has_been_played or match.winning_alliance == '':
                    continue

                num_wins[match.winning_alliance] += 1
                for team in match.alliances[match.winning_alliance]['teams']:
                    team_matches_played[match.winning_alliance].append(team)

                if num_wins[match.winning_alliance] >= 2:
                    for team in team_matches_played[match.winning_alliance]:
                        district_points['points'][team]['elim_points'] += 5 * POINTS_MULTIPLIER

        else:
            elim_num_wins = defaultdict(lambda: defaultdict(int))
            elim_alliances = defaultdict(lambda: defaultdict(list))
            for match_future in match_futures:
                match = match_future.get_result()
                if not match.has_been_played:
                    continue

                if match.comp_level == 'qm':
                    if match.winning_alliance == '':
                        for team in match.team_key_names:
                            district_points['points'][team]['qual_points'] += 1 * POINTS_MULTIPLIER
                    else:
                        for team in match.alliances[match.winning_alliance]['teams']:
                            district_points['points'][team]['qual_points'] += 2 * POINTS_MULTIPLIER
                            district_points['tiebreakers'][team]['qual_wins'] += 1

                    for color in ['red', 'blue']:
                        for team in match.alliances[color]['teams']:
                            score = match.alliances[color]['score']
                            district_points['tiebreakers'][team]['highest_qual_scores'] = heapq.nlargest(3, district_points['tiebreakers'][team]['highest_qual_scores'] + [score])
                else:
                    if match.winning_alliance == '':
                        continue

                    match_set_key = '{}_{}{}'.format(match.event.id(), match.comp_level, match.set_number)
                    elim_num_wins[match_set_key][match.winning_alliance] += 1
                    elim_alliances[match_set_key][match.winning_alliance] += match.alliances[match.winning_alliance]['teams']

                    if elim_num_wins[match_set_key][match.winning_alliance] >= 2:
                        for team in elim_alliances[match_set_key][match.winning_alliance]:
                            district_points['points'][team]['elim_points'] += 5* POINTS_MULTIPLIER

        # alliance points
        if event.alliance_selections:
            selection_points = EventHelper.alliance_selections_to_points(event.alliance_selections)
            for team, points in selection_points.items():
                district_points['points'][team]['alliance_points'] += points * POINTS_MULTIPLIER
        else:
            logging.warning("Event {} has no alliance selection district_points!".format(event.key.id()))

        # award points
        for award_future in award_futures:
            award = award_future.get_result()
            if award.award_type_enum not in AwardType.NON_JUDGED_NON_TEAM_AWARDS:
                if award.award_type_enum == AwardType.CHAIRMANS:
                    point_value = 10
                elif award.award_type_enum in {AwardType.ENGINEERING_INSPIRATION, AwardType.ROOKIE_ALL_STAR}:
                    point_value = 8
                else:
                    point_value = 5
                for team in award.team_list:
                    district_points['points'][team.id()]['award_points'] += point_value * POINTS_MULTIPLIER

        for team, point_breakdown in district_points['points'].items():
            for p in point_breakdown.values():
                district_points['points'][team]['total'] += p

        return district_points
    def test_event_smulator_batch_advance(self):
        es = EventSimulator(batch_advance=True)

        # Before anything has happened
        event = Event.get_by_id('2016nytr')
        self.assertNotEqual(event, None)
        self.assertEqual(event.details, None)
        self.assertEqual(event.matches, [])

        # Qual match schedule added
        es.step()
        event = Event.get_by_id('2016nytr')
        self.assertNotEqual(event, None)
        self.assertNotEqual(event.details, None)
        for rank in event.details.rankings2:
            self.assertEqual(rank['sort_orders'][0], 0)

        self.assertEqual(len(event.matches), 72)
        for match in event.matches:
            self.assertEqual(match.comp_level, 'qm')
            self.assertFalse(match.has_been_played)
            self.assertEqual(match.actual_time, None)

        # After each qual match
        for i in xrange(72):
            es.step()
            event = Event.get_by_id('2016nytr')
            self.assertNotEqual(event, None)
            self.assertEqual(event.details.alliance_selections, None)
            self.assertEqual(len(event.matches), 72)

            matches = MatchHelper.play_order_sort_matches(event.matches)
            for j, match in enumerate(matches):
                if j <= i:
                    self.assertTrue(match.has_been_played)
                    self.assertNotEqual(match.actual_time, None)
                else:
                    self.assertFalse(match.has_been_played)

        # Check some final rankings
        self.assertEqual(event.details.rankings2[0]['sort_orders'][0], 22)
        self.assertEqual(event.details.rankings2[-1]['sort_orders'][0], 4)

        # After alliance selections
        es.step()
        event = Event.get_by_id('2016nytr')
        self.assertNotEqual(event, None)
        self.assertEqual(event.details.alliance_selections,
                         self._alliance_selections)
        self.assertEqual(len(event.matches), 72)

        # QF schedule added
        es.step()
        event = Event.get_by_id('2016nytr')
        self.assertNotEqual(event, None)
        self.assertEqual(event.details.alliance_selections,
                         self._alliance_selections)
        self.assertEqual(len(event.matches), 84)
        for match in event.matches:
            if match.comp_level == 'qm':
                self.assertTrue(match.has_been_played)
                self.assertNotEqual(match.actual_time, None)
            else:
                self.assertEqual(match.comp_level, 'qf')
                self.assertFalse(match.has_been_played)
                self.assertEqual(match.actual_time, None)

        # After each QF match
        for i in xrange(72, 82):
            es.step()
            event = Event.get_by_id('2016nytr')
            self.assertNotEqual(event, None)
            self.assertEqual(event.details.alliance_selections,
                             self._alliance_selections)
            if i <= 75:
                self.assertEqual(len(event.matches), 84)
            elif i <= 77:
                self.assertEqual(len(event.matches), 83)
            else:
                self.assertEqual(len(event.matches), 82)

            matches = MatchHelper.play_order_sort_matches(event.matches)
            for j, match in enumerate(matches):
                if match.key.id() in {'2016nytr_qf1m3', '2016nytr_qf3m3'}:
                    # Unneeded tiebreak matches
                    self.assertFalse(match.has_been_played)
                elif j <= i:
                    self.assertTrue(match.has_been_played)
                    self.assertNotEqual(match.actual_time, None)
                else:
                    self.assertFalse(match.has_been_played)

        # SF schedule added
        es.step()
        event = Event.get_by_id('2016nytr')
        self.assertNotEqual(event, None)
        self.assertEqual(event.details.alliance_selections,
                         self._alliance_selections)
        self.assertEqual(len(event.matches), 88)
        for match in event.matches:
            if match.comp_level in {'qm', 'qf'}:
                self.assertTrue(match.has_been_played)
                self.assertNotEqual(match.actual_time, None)
            else:
                self.assertEqual(match.comp_level, 'sf')
                self.assertFalse(match.has_been_played)
                self.assertEqual(match.actual_time, None)

        # After each SF match
        for i in xrange(82, 87):
            es.step()
            event = Event.get_by_id('2016nytr')
            self.assertNotEqual(event, None)

            if i < 85:
                self.assertEqual(event.details.alliance_selections,
                                 self._alliance_selections)
            else:
                self.assertEqual(event.details.alliance_selections,
                                 self._alliance_selections_with_backup)

            if i <= 83:
                self.assertEqual(len(event.matches), 88)
            else:
                self.assertEqual(len(event.matches), 87)

            matches = MatchHelper.play_order_sort_matches(event.matches)
            for j, match in enumerate(matches):
                if match.key.id() == '2016nytr_sf1m3':
                    # Unneeded tiebreak matches
                    self.assertFalse(match.has_been_played)
                elif j <= i:
                    self.assertTrue(match.has_been_played)
                    self.assertNotEqual(match.actual_time, None)
                else:
                    self.assertFalse(match.has_been_played)

        # F schedule added
        es.step()
        event = Event.get_by_id('2016nytr')
        self.assertNotEqual(event, None)
        self.assertEqual(event.details.alliance_selections,
                         self._alliance_selections_with_backup)
        self.assertEqual(len(event.matches), 90)
        for match in event.matches:
            if match.comp_level in {'qm', 'qf', 'sf'}:
                self.assertTrue(match.has_been_played)
                self.assertNotEqual(match.actual_time, None)
            else:
                self.assertEqual(match.comp_level, 'f')
                self.assertFalse(match.has_been_played)
                self.assertEqual(match.actual_time, None)

        # After each F match
        for i in xrange(87, 90):
            es.step()
            event = Event.get_by_id('2016nytr')
            self.assertNotEqual(event, None)
            self.assertEqual(event.details.alliance_selections,
                             self._alliance_selections_with_backup)
            self.assertEqual(len(event.matches), 90)

            matches = MatchHelper.play_order_sort_matches(event.matches)
            for j, match in enumerate(matches):
                if j <= i:
                    self.assertTrue(match.has_been_played)
                    self.assertNotEqual(match.actual_time, None)
                else:
                    self.assertFalse(match.has_been_played)
예제 #53
0
    def getMatches(self, event):
        matches_url = self.YEAR_MATCH_RESULTS_URL_PATTERN.get(
            event.year, self.DEFAULT_MATCH_RESULTS_URL_PATTERN) % (
                event.year,
                self.EVENT_SHORT_EXCEPTIONS.get(event.event_short,
                                                event.event_short))

        match_dicts, _ = self.parse(
            matches_url,
            self.YEAR_MATCH_PARSER.get(event.year, self.DEFAULT_MATCH_PARSER))
        if not match_dicts:  # Matches have not been played, but qual match schedule may be out
            # If this is run when there are already matches in the DB, it will overwrite scores!
            # Check to make sure event has no existing matches
            if len(
                    Match.query(Match.event == event.key).fetch(
                        1, keys_only=True)) == 0:
                logging.warning(
                    "No matches found for {}. Trying to parse qual match schedule."
                    .format(event.key.id()))

                qual_match_sched_url = self.MATCH_SCHEDULE_QUAL_URL_PATTERN % (
                    event.year,
                    self.EVENT_SHORT_EXCEPTIONS.get(event.event_short,
                                                    event.event_short))
                match_dicts, _ = self.parse(qual_match_sched_url,
                                            self.MATCH_SCHEDULE_PARSER)

        for match_dict in match_dicts:
            alliances = json.loads(match_dict['alliances_json'])
            if (alliances['red']['score'] == -1
                    or alliances['blue']['score'] == -1
                    or match_dict['comp_level'] in Match.ELIM_LEVELS):
                break
        else:  # Only qual matches have been played and they have all been played
            # If this is run when there are already elim matches in the DB, it will overwrite scores!
            # Check to make sure event has no existing elim matches
            if len(
                    Match.query(Match.event == event.key,
                                Match.comp_level.IN(Match.ELIM_LEVELS)).fetch(
                                    1, keys_only=True)) == 0:
                logging.warning(
                    "No elim matches found for {}. Trying to parse elim match schedule."
                    .format(event.key.id()))

                elim_match_sched_url = self.MATCH_SCHEDULE_ELIMS_URL_PATTERN % (
                    event.year,
                    self.EVENT_SHORT_EXCEPTIONS.get(event.event_short,
                                                    event.event_short))
                elim_match_dicts, _ = self.parse(elim_match_sched_url,
                                                 self.MATCH_SCHEDULE_PARSER)
                match_dicts += elim_match_dicts

        matches = [
            Match(id=Match.renderKeyName(event.key.id(),
                                         match_dict.get("comp_level", None),
                                         match_dict.get("set_number", 0),
                                         match_dict.get("match_number", 0)),
                  event=event.key,
                  game=Match.FRC_GAMES_BY_YEAR.get(event.year, "frc_unknown"),
                  set_number=match_dict.get("set_number", 0),
                  match_number=match_dict.get("match_number", 0),
                  comp_level=match_dict.get("comp_level", None),
                  team_key_names=match_dict.get("team_key_names", None),
                  time_string=match_dict.get("time_string", None),
                  alliances_json=match_dict.get("alliances_json", None))
            for match_dict in match_dicts
        ]

        MatchHelper.add_match_times(event, matches)
        return 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))
예제 #55
0
    def get(self):
        self._require_registration()

        user = self.user_bundle.account.key
        favorites = Favorite.query(ancestor=user).fetch()
        subscriptions = Subscription.query(ancestor=user).fetch()

        team_keys = set()
        team_fav = {}
        team_subs = {}
        event_keys = set()
        event_fav = {}
        event_subs = {}
        events = []
        match_keys = set()
        match_event_keys = set()
        match_fav = {}
        match_subs = {}
        for item in favorites + subscriptions:
            if item.model_type == ModelType.TEAM:
                team_keys.add(ndb.Key(Team, item.model_key))
                if type(item) == Favorite:
                    team_fav[item.model_key] = item
                elif type(item) == Subscription:
                    team_subs[item.model_key] = item
            elif item.model_type == ModelType.MATCH:
                match_keys.add(ndb.Key(Match, item.model_key))
                match_event_keys.add(ndb.Key(Event, item.model_key.split('_')[0]))
                if type(item) == Favorite:
                    match_fav[item.model_key] = item
                elif type(item) == Subscription:
                    match_subs[item.model_key] = item
            elif item.model_type == ModelType.EVENT:
                if item.model_key.endswith('*'):  # All year events wildcard
                    event_year = int(item.model_key[:-1])
                    events.append(Event(  # add fake event for rendering
                        id=item.model_key,
                        short_name='ALL EVENTS',
                        event_short=item.model_key,
                        year=event_year,
                        start_date=datetime.datetime(event_year, 1, 1),
                        end_date=datetime.datetime(event_year, 1, 1)
                    ))
                else:
                    event_keys.add(ndb.Key(Event, item.model_key))
                if type(item) == Favorite:
                    event_fav[item.model_key] = item
                elif type(item) == Subscription:
                    event_subs[item.model_key] = item

        team_futures = ndb.get_multi_async(team_keys)
        event_futures = ndb.get_multi_async(event_keys)
        match_futures = ndb.get_multi_async(match_keys)
        match_event_futures = ndb.get_multi_async(match_event_keys)

        teams = sorted([team_future.get_result() for team_future in team_futures], key=lambda x: x.team_number)
        team_fav_subs = []
        for team in teams:
            fav = team_fav.get(team.key.id(), None)
            subs = team_subs.get(team.key.id(), None)
            team_fav_subs.append((team, fav, subs))

        events += [event_future.get_result() for event_future in event_futures]
        EventHelper.sort_events(events)

        event_fav_subs = []
        for event in events:
            fav = event_fav.get(event.key.id(), None)
            subs = event_subs.get(event.key.id(), None)
            event_fav_subs.append((event, fav, subs))

        matches = [match_future.get_result() for match_future in match_futures]
        match_events = [match_event_future.get_result() for match_event_future in match_event_futures]
        MatchHelper.natural_sort_matches(matches)

        match_fav_subs_by_event = {}
        for event in match_events:
            match_fav_subs_by_event[event.key.id()] = (event, [])

        for match in matches:
            event_key = match.key.id().split('_')[0]
            fav = match_fav.get(match.key.id(), None)
            subs = match_subs.get(match.key.id(), None)
            match_fav_subs_by_event[event_key][1].append((match, fav, subs))

        event_match_fav_subs = sorted(match_fav_subs_by_event.values(), key=lambda x: EventHelper.distantFutureIfNoStartDate(x[0]))
        event_match_fav_subs = sorted(event_match_fav_subs, key=lambda x: EventHelper.distantFutureIfNoEndDate(x[0]))

        self.template_values['team_fav_subs'] = team_fav_subs
        self.template_values['event_fav_subs'] = event_fav_subs
        self.template_values['event_match_fav_subs'] = event_match_fav_subs
        self.template_values['status'] = self.request.get('status')
        self.template_values['year'] = datetime.datetime.now().year

        self.response.out.write(jinja2_engine.render('mytba.html', self.template_values))
예제 #56
0
    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()
        for district in team_districts:
            if district.year == year:
                district_abbrev = district.abbreviation
                district_name = district.display_name

        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 test_2017flwp_sequence(self):
        event = Event(id='2017flwp',
                      event_short='flwp',
                      year=2017,
                      event_type_enum=0,
                      timezone_id='America/New_York')
        event.put()

        event_code = 'flwp'

        file_prefix = 'frc-api-response/v2.0/2017/schedule/{}/playoff/hybrid/'.format(
            event_code)
        context = ndb.get_context()
        result = context.urlfetch(
            'https://www.googleapis.com/storage/v1/b/bucket/o?bucket=tbatv-prod-hrd.appspot.com&prefix={}'
            .format(file_prefix)).get_result()

        for item in json.loads(result.content)['items']:
            filename = item['name']
            time_str = filename.replace(file_prefix, '').replace('.json',
                                                                 '').strip()
            file_time = datetime.datetime.strptime(time_str,
                                                   "%Y-%m-%d %H:%M:%S.%f")
            query_time = file_time + datetime.timedelta(seconds=30)
            MatchManipulator.createOrUpdate(DatafeedFMSAPI(
                'v2.0',
                sim_time=query_time).getMatches('2017{}'.format(event_code)),
                                            run_post_update_hook=False)
        MatchHelper.deleteInvalidMatches(event.matches, event)

        sf_matches = Match.query(Match.event == ndb.Key(Event, '2017flwp'),
                                 Match.comp_level == 'sf').fetch()
        self.assertEqual(len(sf_matches), 7)

        self.assertEqual(
            Match.get_by_id('2017flwp_sf1m1').alliances['red']['score'], 305)
        self.assertEqual(
            Match.get_by_id('2017flwp_sf1m1').alliances['blue']['score'], 255)
        self.assertEqual(
            Match.get_by_id('2017flwp_sf1m1').score_breakdown['red']
            ['totalPoints'], 305)
        self.assertEqual(
            Match.get_by_id('2017flwp_sf1m1').score_breakdown['blue']
            ['totalPoints'], 255)

        self.assertEqual(
            Match.get_by_id('2017flwp_sf1m2').alliances['red']['score'], 165)
        self.assertEqual(
            Match.get_by_id('2017flwp_sf1m2').alliances['blue']['score'], 258)
        self.assertEqual(
            Match.get_by_id('2017flwp_sf1m2').score_breakdown['red']
            ['totalPoints'], 165)
        self.assertEqual(
            Match.get_by_id('2017flwp_sf1m2').score_breakdown['blue']
            ['totalPoints'], 258)

        self.assertEqual(
            Match.get_by_id('2017flwp_sf1m3').alliances['red']['score'], 255)
        self.assertEqual(
            Match.get_by_id('2017flwp_sf1m3').alliances['blue']['score'], 255)
        self.assertEqual(
            Match.get_by_id('2017flwp_sf1m3').score_breakdown['red']
            ['totalPoints'], 255)
        self.assertEqual(
            Match.get_by_id('2017flwp_sf1m3').score_breakdown['blue']
            ['totalPoints'], 255)

        self.assertEqual(
            Match.get_by_id('2017flwp_sf1m4').alliances['red']['score'], 255)
        self.assertEqual(
            Match.get_by_id('2017flwp_sf1m4').alliances['blue']['score'], 255)
        self.assertEqual(
            Match.get_by_id('2017flwp_sf1m4').score_breakdown['red']
            ['totalPoints'], 255)
        self.assertEqual(
            Match.get_by_id('2017flwp_sf1m4').score_breakdown['blue']
            ['totalPoints'], 255)

        self.assertEqual(
            Match.get_by_id('2017flwp_sf1m5').alliances['red']['score'], 165)
        self.assertEqual(
            Match.get_by_id('2017flwp_sf1m5').alliances['blue']['score'], 263)
        self.assertEqual(
            Match.get_by_id('2017flwp_sf1m5').score_breakdown['red']
            ['totalPoints'], 165)
        self.assertEqual(
            Match.get_by_id('2017flwp_sf1m5').score_breakdown['blue']
            ['totalPoints'], 263)
예제 #58
0
    def render_team_history(cls, handler, team, is_canonical):
        event_team_keys_future = EventTeam.query(
            EventTeam.team == team.key).fetch_async(1000, keys_only=True)
        award_keys_future = Award.query(
            Award.team_list == team.key).fetch_async(1000, keys_only=True)

        event_teams_futures = ndb.get_multi_async(
            event_team_keys_future.get_result())
        awards_futures = ndb.get_multi_async(award_keys_future.get_result())

        event_keys = [
            event_team_future.get_result().event
            for event_team_future in event_teams_futures
        ]
        events_futures = ndb.get_multi_async(event_keys)

        awards_by_event = {}
        for award_future in awards_futures:
            award = award_future.get_result()
            if award.event.id() not in awards_by_event:
                awards_by_event[award.event.id()] = [award]
            else:
                awards_by_event[award.event.id()].append(award)

        event_awards = []
        current_event = None
        matches_upcoming = None
        short_cache = False
        for event_future in events_futures:
            event = event_future.get_result()
            if event.now:
                current_event = event

                team_matches_future = Match.query(Match.event == event.key, Match.team_key_names == team.key_name)\
                  .fetch_async(500, keys_only=True)
                matches = ndb.get_multi(team_matches_future.get_result())
                matches_upcoming = MatchHelper.upcomingMatches(matches)

            if event.within_a_day:
                short_cache = True

            if event.key_name in awards_by_event:
                sorted_awards = AwardHelper.organizeAwards(
                    awards_by_event[event.key_name])
            else:
                sorted_awards = []
            event_awards.append((event, sorted_awards))
        event_awards = sorted(
            event_awards,
            key=lambda (e, _): e.start_date
            if e.start_date else datetime.datetime(e.year, 12, 31))

        years = sorted(
            set([
                et.get_result().year for et in event_teams_futures
                if et.get_result().year is not None
            ]))

        handler.template_values.update({
            'is_canonical': is_canonical,
            'team': team,
            'event_awards': event_awards,
            'years': years,
            'current_event': current_event,
            'matches_upcoming': matches_upcoming
        })

        if short_cache:
            handler._cache_expiration = handler.SHORT_CACHE_EXPIRATION

        path = os.path.join(os.path.dirname(__file__),
                            '../templates/team_history.html')
        return template.render(path, handler.template_values)
    def test_2017scmb(self):
        event = Event(id='2017scmb',
                      event_short='scmb',
                      year=2017,
                      event_type_enum=0,
                      timezone_id='America/New_York')
        event.put()

        MatchManipulator.createOrUpdate(
            DatafeedFMSAPI('v2.0',
                           sim_time=datetime.datetime(
                               2017, 3, 04, 19, 17)).getMatches('2017scmb'))
        MatchHelper.deleteInvalidMatches(event.matches, event)
        qf_matches = Match.query(Match.event == ndb.Key(Event, '2017scmb'),
                                 Match.comp_level == 'qf').fetch()
        self.assertEqual(len(qf_matches), 12)

        self.assertEqual(
            Match.get_by_id('2017scmb_qf4m1').alliances['red']['score'], 305)
        self.assertEqual(
            Match.get_by_id('2017scmb_qf4m1').alliances['blue']['score'], 305)
        self.assertEqual(
            Match.get_by_id('2017scmb_qf4m1').score_breakdown['red']
            ['totalPoints'], 305)
        self.assertEqual(
            Match.get_by_id('2017scmb_qf4m1').score_breakdown['blue']
            ['totalPoints'], 305)

        ndb.get_context().clear_cache(
        )  # Prevent data from leaking between tests

        MatchManipulator.createOrUpdate(
            DatafeedFMSAPI('v2.0',
                           sim_time=datetime.datetime(
                               2017, 3, 04, 19, 50)).getMatches('2017scmb'))
        MatchHelper.deleteInvalidMatches(event.matches, event)
        qf_matches = Match.query(Match.event == ndb.Key(Event, '2017scmb'),
                                 Match.comp_level == 'qf').fetch()
        self.assertEqual(len(qf_matches), 12)

        self.assertEqual(
            Match.get_by_id('2017scmb_qf4m1').alliances['red']['score'], 305)
        self.assertEqual(
            Match.get_by_id('2017scmb_qf4m1').alliances['blue']['score'], 305)
        self.assertEqual(
            Match.get_by_id('2017scmb_qf4m1').score_breakdown['red']
            ['totalPoints'], 305)
        self.assertEqual(
            Match.get_by_id('2017scmb_qf4m1').score_breakdown['blue']
            ['totalPoints'], 305)
        self.assertEqual(
            Match.get_by_id('2017scmb_qf4m2').alliances['red']['score'], 213)
        self.assertEqual(
            Match.get_by_id('2017scmb_qf4m2').alliances['blue']['score'], 305)
        self.assertEqual(
            Match.get_by_id('2017scmb_qf4m2').score_breakdown['red']
            ['totalPoints'], 213)
        self.assertEqual(
            Match.get_by_id('2017scmb_qf4m2').score_breakdown['blue']
            ['totalPoints'], 305)

        ndb.get_context().clear_cache(
        )  # Prevent data from leaking between tests

        MatchManipulator.createOrUpdate(
            DatafeedFMSAPI('v2.0',
                           sim_time=datetime.datetime(
                               2017, 3, 04, 20, 12)).getMatches('2017scmb'))
        MatchHelper.deleteInvalidMatches(event.matches, event)
        qf_matches = Match.query(Match.event == ndb.Key(Event, '2017scmb'),
                                 Match.comp_level == 'qf').fetch()
        self.assertEqual(len(qf_matches), 12)

        self.assertEqual(
            Match.get_by_id('2017scmb_qf4m1').alliances['red']['score'], 305)
        self.assertEqual(
            Match.get_by_id('2017scmb_qf4m1').alliances['blue']['score'], 305)
        self.assertEqual(
            Match.get_by_id('2017scmb_qf4m1').score_breakdown['red']
            ['totalPoints'], 305)
        self.assertEqual(
            Match.get_by_id('2017scmb_qf4m1').score_breakdown['blue']
            ['totalPoints'], 305)
        self.assertEqual(
            Match.get_by_id('2017scmb_qf4m2').alliances['red']['score'], 213)
        self.assertEqual(
            Match.get_by_id('2017scmb_qf4m2').alliances['blue']['score'], 305)
        self.assertEqual(
            Match.get_by_id('2017scmb_qf4m2').score_breakdown['red']
            ['totalPoints'], 213)
        self.assertEqual(
            Match.get_by_id('2017scmb_qf4m2').score_breakdown['blue']
            ['totalPoints'], 305)
        self.assertEqual(
            Match.get_by_id('2017scmb_qf4m3').alliances['red']['score'], 312)
        self.assertEqual(
            Match.get_by_id('2017scmb_qf4m3').alliances['blue']['score'], 255)
        self.assertEqual(
            Match.get_by_id('2017scmb_qf4m3').score_breakdown['red']
            ['totalPoints'], 312)
        self.assertEqual(
            Match.get_by_id('2017scmb_qf4m3').score_breakdown['blue']
            ['totalPoints'], 255)

        ndb.get_context().clear_cache(
        )  # Prevent data from leaking between tests

        MatchManipulator.createOrUpdate(
            DatafeedFMSAPI('v2.0',
                           sim_time=datetime.datetime(
                               2017, 3, 04, 20, 48)).getMatches('2017scmb'))
        MatchHelper.deleteInvalidMatches(event.matches, event)
        qf_matches = Match.query(Match.event == ndb.Key(Event, '2017scmb'),
                                 Match.comp_level == 'qf').fetch()
        self.assertEqual(len(qf_matches), 13)

        self.assertEqual(
            Match.get_by_id('2017scmb_qf4m1').alliances['red']['score'], 305)
        self.assertEqual(
            Match.get_by_id('2017scmb_qf4m1').alliances['blue']['score'], 305)
        self.assertEqual(
            Match.get_by_id('2017scmb_qf4m1').score_breakdown['red']
            ['totalPoints'], 305)
        self.assertEqual(
            Match.get_by_id('2017scmb_qf4m1').score_breakdown['blue']
            ['totalPoints'], 305)
        self.assertEqual(
            Match.get_by_id('2017scmb_qf4m2').alliances['red']['score'], 213)
        self.assertEqual(
            Match.get_by_id('2017scmb_qf4m2').alliances['blue']['score'], 305)
        self.assertEqual(
            Match.get_by_id('2017scmb_qf4m2').score_breakdown['red']
            ['totalPoints'], 213)
        self.assertEqual(
            Match.get_by_id('2017scmb_qf4m2').score_breakdown['blue']
            ['totalPoints'], 305)
        self.assertEqual(
            Match.get_by_id('2017scmb_qf4m3').alliances['red']['score'], 312)
        self.assertEqual(
            Match.get_by_id('2017scmb_qf4m3').alliances['blue']['score'], 255)
        self.assertEqual(
            Match.get_by_id('2017scmb_qf4m3').score_breakdown['red']
            ['totalPoints'], 312)
        self.assertEqual(
            Match.get_by_id('2017scmb_qf4m3').score_breakdown['blue']
            ['totalPoints'], 255)
        self.assertEqual(
            Match.get_by_id('2017scmb_qf4m4').alliances['red']['score'], 310)
        self.assertEqual(
            Match.get_by_id('2017scmb_qf4m4').alliances['blue']['score'], 306)
        self.assertEqual(
            Match.get_by_id('2017scmb_qf4m4').score_breakdown['red']
            ['totalPoints'], 310)
        self.assertEqual(
            Match.get_by_id('2017scmb_qf4m4').score_breakdown['blue']
            ['totalPoints'], 306)
    def parse(self, response):
        """
        This currently only works for the 2015 game, where elims matches are all part of one set.
        """
        matches = response['Schedule']

        event_key = '{}{}'.format(self.year, self.event_short)
        event = Event.get_by_id(event_key)
        if event.timezone_id:
            event_tz = pytz.timezone(event.timezone_id)
        else:
            logging.warning(
                "Event {} has no timezone! Match times may be wrong.".format(
                    event_key))
            event_tz = None

        set_number = 1
        parsed_matches = []
        for match in matches:
            comp_level = get_comp_level(match['level'], match['matchNumber'])
            match_number = get_match_number(comp_level, match['matchNumber'])

            red_teams = []
            blue_teams = []
            team_key_names = []
            null_team = False
            for team in match['Teams']:
                if team['teamNumber'] is None:
                    null_team = True
                team_key = 'frc{}'.format(team['teamNumber'])
                team_key_names.append(team_key)
                if 'Red' in team['station']:
                    red_teams.append(team_key)
                elif 'Blue' in team['station']:
                    blue_teams.append(team_key)
            if null_team and match['scoreRedFinal'] is None and match[
                    'scoreBlueFinal'] is None:
                continue

            alliances = {
                'red': {
                    'teams': red_teams,
                    'score': match['scoreRedFinal']
                },
                'blue': {
                    'teams': blue_teams,
                    'score': match['scoreBlueFinal']
                }
            }

            score_breakdown = {
                'red': {
                    'auto_points': match['scoreRedAuto'],
                    'foul_points': match['scoreRedFoul']
                },
                'blue': {
                    'auto_points': match['scoreBlueAuto'],
                    'foul_points': match['scoreBlueFoul']
                }
            }

            time = datetime.datetime.strptime(match['startTime'], 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, 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),
                      score_breakdown_json=json.dumps(score_breakdown)))

        # Fix null teams in elims (due to FMS API failure, some info not complete)
        # Should only happen for sf and f matches
        organized_matches = MatchHelper.organizeMatches(parsed_matches)
        for level in ['sf', 'f']:
            playoff_advancement = MatchHelper.generatePlayoffAdvancement2015(
                organized_matches)
            if playoff_advancement[LAST_LEVEL[level]] != []:
                for match in organized_matches[level]:
                    if 'frcNone' in match.team_key_names:
                        if level == 'sf':
                            red_seed, blue_seed = QF_SF_MAP[match.match_number]
                        else:
                            red_seed = 0
                            blue_seed = 1
                        red_teams = [
                            'frc{}'.format(t) for t in playoff_advancement[
                                LAST_LEVEL[level]][red_seed][0]
                        ]
                        blue_teams = [
                            'frc{}'.format(t) for t in playoff_advancement[
                                LAST_LEVEL[level]][blue_seed][0]
                        ]

                        alliances = match.alliances
                        alliances['red']['teams'] = red_teams
                        alliances['blue']['teams'] = blue_teams
                        match.alliances_json = json.dumps(alliances)
                        match.team_key_names = red_teams + blue_teams

        fixed_matches = []
        for key, matches in organized_matches.items():
            if key != 'num':
                for match in matches:
                    if 'frcNone' not in match.team_key_names:
                        fixed_matches.append(match)

        return fixed_matches