Esempio n. 1
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 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 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)
    def test_2017flwp(self):
        event = Event(id='2017flwp',
                      event_short='flwp',
                      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, 21, 22)).getMatches('2017flwp'))
        MatchHelper.deleteInvalidMatches(event.matches)

        sf_matches = Match.query(Match.event == ndb.Key(Event, '2017flwp'),
                                 Match.comp_level == 'sf').fetch()
        self.assertEqual(len(sf_matches), 5)
        old_match = Match.get_by_id('2017flwp_sf1m3')
        self.assertNotEqual(old_match, None)
        self.assertEqual(old_match.alliances['red']['score'], 255)
        self.assertEqual(old_match.alliances['blue']['score'], 255)
        self.assertEqual(old_match.score_breakdown['red']['totalPoints'], 255)
        self.assertEqual(old_match.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, 21, 35)).getMatches('2017flwp'))
        MatchHelper.deleteInvalidMatches(event.matches)

        sf_matches = Match.query(Match.event == ndb.Key(Event, '2017flwp'),
                                 Match.comp_level == 'sf').fetch()
        self.assertEqual(len(sf_matches), 6)
        new_match = Match.get_by_id('2017flwp_sf1m3')
        self.assertNotEqual(new_match, None)

        self.assertEqual(old_match.alliances, new_match.alliances)
        self.assertEqual(old_match.score_breakdown, new_match.score_breakdown)

        tiebreaker_match = Match.get_by_id('2017flwp_sf1m4')
        self.assertNotEqual(tiebreaker_match, None)

        self.assertEqual(tiebreaker_match.alliances['red']['score'], 165)
        self.assertEqual(tiebreaker_match.alliances['blue']['score'], 263)
        self.assertEqual(
            tiebreaker_match.score_breakdown['red']['totalPoints'], 165)
        self.assertEqual(
            tiebreaker_match.score_breakdown['blue']['totalPoints'], 263)
Esempio n. 5
0
    def get(self, event_key):
        event = Event.get_by_id(event_key)
        team_ids = set()
        
        # Add teams from Matches
        for match in Match.query(Match.event == event.key).fetch(1000):
            for team in match.team_key_names:
                team_ids.add(team)
        
        teams = TeamManipulator.createOrUpdate([Team(
            id = team_id,
            team_number = int(team_id[3:]))
            for team_id in team_ids])

        if teams:
            event_teams = EventTeamManipulator.createOrUpdate([EventTeam(
                id = event_key + "_" + team.key.id(),
                event = event.key,
                team = team.key,
                year = event.year)
                for team in teams])
        else:
            event_teams = None
        
        template_values = {
            'event_teams': event_teams,
        }
        
        path = os.path.join(os.path.dirname(__file__), '../templates/math/eventteam_update_do.html')
        self.response.out.write(template.render(path, 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)
Esempio n. 7
0
    def post(self):
        self._require_registration()

        event_key = self.request.get("event_key")
        if not event_key:
            self.response.out.write("No event key found")
            return
        event_future = Event.get_by_id_async(event_key)
        event = event_future.get_result()
        if not event:
            self.response.out.write("Invalid event key {}".format(event_key))
            return

        match_futures = Match.query(Match.event == event.key).fetch_async(keys_only=True)
        valid_match_keys = [match.id() for match in match_futures.get_result()]

        num_videos = int(self.request.get("num_videos", 0))
        suggestions_added = 0
        for i in range(0, num_videos):
            yt_id = self.request.get("video_id_{}".format(i))
            match_partial = self.request.get("match_partial_{}".format(i))
            if not yt_id or not match_partial:
                continue

            match_key = "{}_{}".format(event_key, match_partial)
            if match_key not in valid_match_keys:
                continue

            status = SuggestionCreator.createMatchVideoYouTubeSuggestion(self.user_bundle.account.key, yt_id, match_key)
            if status == 'success':
                suggestions_added += 1

        self.redirect('/suggest/event/video?event_key={}&num_added={}'.format(event_key, suggestions_added))
Esempio n. 8
0
    def _render(self, team_key, event_key):
        match_keys_future = Match.query(Match.event == ndb.Key(Event, self.event_key), Match.team_key_names == self.team_key).fetch_async(None, keys_only=True)
        match_futures = ndb.get_multi_async(match_keys_future.get_result())

        matches = [ModelToDict.matchConverter(match_future.get_result()) for match_future in match_futures]

        return json.dumps(matches, ensure_ascii=True)
Esempio n. 9
0
    def get(self, year):
        year_event_keys = Event.query(Event.year == int(year)).fetch(1000, keys_only=True)

        final_match_keys = []
        for event_key in year_event_keys:
            final_match_keys.extend(Match.query(Match.event == event_key, Match.comp_level == 'f').fetch(100, keys_only=True))

        match_keys_to_repair = []
        for match_key in final_match_keys:
            key_name = match_key.id()
            if '_f0m' in key_name:
                match_keys_to_repair.append(match_key)

        deleted_keys = []
        matches_to_repair = ndb.get_multi(match_keys_to_repair)
        for match in matches_to_repair:
            deleted_keys.append(match.key)

            event = ndb.get_multi([match.event])[0]
            match.set_number = 1
            match.key = ndb.Key(Match, Match.renderKeyName(
                event.key.id(),
                match.comp_level,
                match.set_number,
                match.match_number))

        MatchManipulator.createOrUpdate(matches_to_repair)
        MatchManipulator.delete_keys(deleted_keys)

        template_values = {'deleted_keys': deleted_keys,
                           'new_matches': matches_to_repair}

        path = os.path.join(os.path.dirname(__file__), '../templates/math/final_matches_repair_do.html')
        self.response.out.write(template.render(path, template_values))
Esempio n. 10
0
 def _query_async(self):
     team_key = self._query_args[0]
     year = self._query_args[1]
     matches = yield Match.query(
         Match.team_key_names == team_key,
         Match.year == year).fetch_async()
     raise ndb.Return(matches)
Esempio n. 11
0
 def _query_async(self):
     team_key = self._query_args[0]
     event_key = self._query_args[1]
     matches = yield Match.query(Match.team_key_names == team_key,
                                 Match.event == ndb.Key(
                                     Event, event_key)).fetch_async()
     raise ndb.Return(matches)
Esempio n. 12
0
 def _query_async(self):
     team_key = self._query_args[0]
     event_key = self._query_args[1]
     matches = yield Match.query(
         Match.team_key_names == team_key,
         Match.event == ndb.Key(Event, event_key)).fetch_async()
     raise ndb.Return(matches)
 def get_matches_async(event_keys):
     if event_keys == []:
         raise ndb.Return([])
     match_keys = yield Match.query(
         Match.event.IN(event_keys), Match.team_key_names == team.key_name).fetch_async(500, keys_only=True)
     matches = yield ndb.get_multi_async(match_keys)
     raise ndb.Return(matches)
    def _render(self, team_key, event_key):
        match_keys_future = Match.query(Match.event == ndb.Key(Event, self.event_key), Match.team_key_names == self.team_key).fetch_async(None, keys_only=True)
        match_futures = ndb.get_multi_async(match_keys_future.get_result())

        matches = [ModelToDict.matchConverter(match_future.get_result()) for match_future in match_futures]

        return json.dumps(matches, ensure_ascii=True)
Esempio n. 15
0
    def addTeamDetails(cls, team_dict, year):
        """
        Consume a Team dict, and return it with a year's Events filtered and Matches added
        """

        # TODO Matches should live under Events - gregmarra 1 feb 2011
        # TODO Filter Events by year - gregmarra 1 feb 2011

        memcache_key = "api_team_details_%s_%s" % (team_dict["key"], year)
        matches_list = memcache.get(memcache_key)
        if matches_list is None:
            matches = list()
            team = Team.get_by_id(team_dict["key"])
            for e in [a.event.get() for a in EventTeam.query(EventTeam.team == team.key).fetch(1000) if a.year == year]:
                match_list = Match.query(Match.event == event.key, Match.team_key_names == team.key_name).fetch(500)
                matches.extend(match_list)
            matches_list = list()
            for match in matches:
                match_dict = dict()
                match_dict["key"] = match.key_name
                match_dict["event"] = match.event
                match_dict["comp_level"] = match.comp_level
                match_dict["set_number"] = match.set_number
                match_dict["match_number"] = match.match_number
                match_dict["team_keys"] = match.team_key_names
                match_dict["alliances"] = json.loads(match.alliances_json)
                matches_list.append(match_dict)

            # TODO: Reduce caching time before 2013 season. 2592000 is one month -gregmarra
            if tba_config.CONFIG["memcache"]:
                memcache.set(memcache_key, matches_list, 2592000)

        team_dict["matches"] = matches_list
        return team_dict
Esempio n. 16
0
    def get(self, year):
        year_event_keys = Event.query(Event.year == int(year)).fetch(1000, keys_only=True)

        final_match_keys = []
        for event_key in year_event_keys:
            final_match_keys.extend(Match.query(Match.event == event_key, Match.comp_level == 'f').fetch(100, keys_only=True))

        match_keys_to_repair = []
        for match_key in final_match_keys:
            key_name = match_key.id()
            if '_f0m' in key_name:
                match_keys_to_repair.append(match_key)

        deleted_keys = []
        matches_to_repair = ndb.get_multi(match_keys_to_repair)
        for match in matches_to_repair:
            deleted_keys.append(match.key)

            event = ndb.get_multi([match.event])[0]
            match.set_number = 1
            match.key = ndb.Key(Match, Match.renderKeyName(
                event.key.id(),
                match.comp_level,
                match.set_number,
                match.match_number))

        MatchManipulator.createOrUpdate(matches_to_repair)
        MatchManipulator.delete_keys(deleted_keys)

        template_values = {'deleted_keys': deleted_keys,
                           'new_matches': matches_to_repair}

        path = os.path.join(os.path.dirname(__file__), '../templates/math/final_matches_repair_do.html')
        self.response.out.write(template.render(path, template_values))
    def update(self, event_key):
        """
        Updates EventTeams for an event.
        Returns a tuple of (teams, event_teams, event_team_keys_to_delete)
        An EventTeam is valid iff the team:
        a) played a match at the event,
        b) the team received an award at the event,
        c) or the event has not yet occurred.
        """
        event = Event.get_by_id(event_key)

        # Add teams from Matches and Awards
        team_ids = set()
        match_key_futures = Match.query(
            Match.event == event.key).fetch_async(1000, keys_only=True)
        award_key_futures = Award.query(
            Award.event == event.key).fetch_async(1000, 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())

        for match_future in match_futures:
            match = match_future.get_result()
            for team in match.team_key_names:
                team_ids.add(team)
        for award_future in award_futures:
            award = award_future.get_result()
            for team_key in award.team_list:
                team_ids.add(team_key.id())

        # Create or update EventTeams
        teams = [Team(id=team_id,
                      team_number=int(team_id[3:]))
                      for team_id in team_ids]

        if teams:
            event_teams = [EventTeam(id=event_key + "_" + team.key.id(),
                                     event=event.key,
                                     team=team.key,
                                     year=event.year)
                                     for team in teams]
        else:
            event_teams = None

        # Delete EventTeams for teams who did not participate in the event
        # Only runs if event is over
        existing_event_teams_keys = EventTeam.query(
            EventTeam.event == event.key).fetch(1000, keys_only=True)
        existing_event_teams = ndb.get_multi(existing_event_teams_keys)
        existing_team_ids = set()
        for et in existing_event_teams:
            existing_team_ids.add(et.team.id())

        et_keys_to_delete = set()
        if event.end_date is not None and event.end_date < datetime.datetime.now():
            for team_id in existing_team_ids.difference(team_ids):
                et_key_name = "{}_{}".format(event.key_name, team_id)
                et_keys_to_delete.add(ndb.Key(EventTeam, et_key_name))
            ndb.delete_multi(et_keys_to_delete)

        return teams, event_teams, et_keys_to_delete
    def post(self):
        self._require_registration()

        event_key = self.request.get("event_key")
        if not event_key:
            self.response.out.write("No event key found")
            return
        event_future = Event.get_by_id_async(event_key)
        event = event_future.get_result()
        if not event:
            self.response.out.write("Invalid event key {}".format(event_key))
            return

        match_futures = Match.query(Match.event == event.key).fetch_async(keys_only=True)
        valid_match_keys = [match.id() for match in match_futures.get_result()]

        num_videos = int(self.request.get("num_videos", 0))
        suggestions_added = 0
        for i in range(0, num_videos):
            yt_id = self.request.get("video_id_{}".format(i))
            match_partial = self.request.get("match_partial_{}".format(i))
            if not yt_id or not match_partial:
                continue

            match_key = "{}_{}".format(event_key, match_partial)
            if match_key not in valid_match_keys:
                continue

            status = SuggestionCreator.createMatchVideoYouTubeSuggestion(self.user_bundle.account.key, yt_id, match_key)
            if status == 'success':
                suggestions_added += 1

        self.redirect('/suggest/event/video?event_key={}&num_added={}'.format(event_key, suggestions_added))
 def _query_async(self):
     event_key = self._query_args[0]
     match_keys = yield Match.query(
         Match.event == ndb.Key(Event, event_key)).fetch_async(
             keys_only=True)
     matches = yield ndb.get_multi_async(match_keys)
     raise ndb.Return(matches)
Esempio n. 20
0
    def addTeamDetails(self, team_dict, year):
        """
        Consume a Team dict, and return it with a year's Events filtered and Matches added
        """
        
        # TODO Matches should live under Events - gregmarra 1 feb 2011
        # TODO Filter Events by year - gregmarra 1 feb 2011
        
        memcache_key = "api_team_details_%s_%s" % (team_dict["key"], year)
        matches_list = memcache.get(memcache_key)
        if matches_list is None:
            matches = list()
            team = Team.get_by_id(team_dict["key"])
            for e in [a.event.get() for a in EventTeam.query(EventTeam.team == team.key).fetch(1000) if a.year == year]:
                match_list = Match.query(Match.event == event.key, Match.team_key_names == team.key_name).fetch(500)
                matches.extend(match_list)
            matches_list = list()
            for match in matches:
                match_dict = dict()
                match_dict["key"] = match.key_name
                match_dict["event"] = match.event
                match_dict["comp_level"] = match.comp_level
                match_dict["set_number"] = match.set_number
                match_dict["match_number"] = match.match_number
                match_dict["team_keys"] = match.team_key_names
                match_dict["alliances"] = json.loads(match.alliances_json)
                matches_list.append(match_dict)

            #TODO: Reduce caching time before 2013 season. 2592000 is one month -gregmarra
            if tba_config.CONFIG["memcache"]: memcache.set(memcache_key, matches_list, 2592000)

        team_dict["matches"] = matches_list
        return team_dict
 def _query_async(self):
     team_key = self._query_args[0]
     event_key = self._query_args[1]
     match_keys = yield Match.query(
         Match.team_key_names == team_key,
         Match.event == ndb.Key(Event, event_key)).fetch_async(keys_only=True)
     matches = yield ndb.get_multi_async(match_keys)
     raise ndb.Return(matches)
 def _query_async(self):
     team_key = self._query_args[0]
     year = self._query_args[1]
     match_keys = yield Match.query(
         Match.team_key_names == team_key,
         Match.year == year).fetch_async(keys_only=True)
     matches = yield ndb.get_multi_async(match_keys)
     raise ndb.Return(matches)
 def _query_async(self):
     team_key = self._query_args[0]
     year = self._query_args[1]
     match_keys = yield Match.query(
         Match.team_key_names == team_key,
         Match.year == year).fetch_async(keys_only=True)
     matches = yield ndb.get_multi_async(match_keys)
     raise ndb.Return(matches)
    def _process_request(self, request, event_key):
        if request.body != event_key:
            self._errors = json.dumps({"Error": "To delete all matches for this event, the body of the request must be the event key."})
            self.abort(400)

        keys_to_delete = Match.query(Match.event == ndb.Key(Event, event_key)).fetch(keys_only=True)
        MatchManipulator.delete_keys(keys_to_delete)

        self.response.out.write(json.dumps({'Success': "All matches for {} deleted".format(event_key)}))
Esempio n. 25
0
 def get_matches_async(event_keys):
     if event_keys == []:
         raise ndb.Return([])
     match_keys = yield Match.query(
         Match.event.IN(event_keys),
         Match.team_key_names == team.key_name).fetch_async(
             500, keys_only=True)
     matches = yield ndb.get_multi_async(match_keys)
     raise ndb.Return(matches)
Esempio n. 26
0
 def _query_async(self):
     team_key = self._query_args[0]
     event_key = self._query_args[1]
     match_keys = yield Match.query(
         Match.team_key_names == team_key,
         Match.event == ndb.Key(Event,
                                event_key)).fetch_async(keys_only=True)
     matches = yield ndb.get_multi_async(match_keys)
     raise ndb.Return(filter(None, matches))
Esempio n. 27
0
 def getTeamWLT(self, team_key, event):
     """
     Given a team_key, and an event, find the team's Win Loss Tie.
     """
     match_keys = Match.query(Match.event == event.key,
                              Match.team_key_names == team_key).fetch(
                                  500, keys_only=True)
     return self.calculateTeamWLTFromMatches(team_key,
                                             ndb.get_multi(match_keys))
    def _process_request(self, request, event_key):
        if request.body != event_key:
            self._errors = json.dumps({"Error": "To delete all matches for this event, the body of the request must be the event key."})
            self.abort(400)

        keys_to_delete = Match.query(Match.event == ndb.Key(Event, event_key)).fetch(keys_only=True)
        MatchManipulator.delete_keys(keys_to_delete)

        self.response.out.write(json.dumps({'Success': "All matches for {} deleted".format(event_key)}))
    def createMatchVideoSuggestion(self):
        user_bundle = UserBundle()
        match = Match.query().fetch(1)[0]  #probably a cleaner way to do this

        suggestion = Suggestion(author=user_bundle.account.key,
                                target_key=match.key_name,
                                target_model="match")
        suggestion.contents = {"youtube_videos": [self.YOUTUBE_ID]}
        suggestion.put()
 def get(self):
     match_count = Match.query().count()
     
     template_values = {
         "match_count": match_count
     }
     
     path = os.path.join(os.path.dirname(__file__), '../../templates/admin/match_dashboard.html')
     self.response.out.write(template.render(path, template_values))
 def get(self):
     self._require_admin()
     match_count = Match.query().count()
     
     self.template_values.update({
         "match_count": match_count
     })
     
     path = os.path.join(os.path.dirname(__file__), '../../templates/admin/match_dashboard.html')
     self.response.out.write(template.render(path, self.template_values))
    def createMatchVideoSuggestion(self):
        user_bundle = UserBundle()
        match = Match.query().fetch(1)[0] #probably a cleaner way to do this

        suggestion = Suggestion(
            author=user_bundle.account.key,
            target_key=match.key_name,
            target_model="match")
        suggestion.contents = {"youtube_videos": [self.YOUTUBE_ID]}
        suggestion.put()
    def test_2017flwp(self):
        event = Event(
            id='2017flwp',
            event_short='flwp',
            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, 21, 22)).getMatches('2017flwp'))
        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), 5)
        old_match = Match.get_by_id('2017flwp_sf1m3')
        self.assertNotEqual(old_match, None)
        self.assertEqual(old_match.alliances['red']['score'], 255)
        self.assertEqual(old_match.alliances['blue']['score'], 255)
        self.assertEqual(old_match.score_breakdown['red']['totalPoints'], 255)
        self.assertEqual(old_match.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, 21, 35)).getMatches('2017flwp'))
        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), 6)
        new_match = Match.get_by_id('2017flwp_sf1m3')
        self.assertNotEqual(new_match, None)

        self.assertEqual(old_match.alliances, new_match.alliances)
        self.assertEqual(old_match.score_breakdown, new_match.score_breakdown)

        tiebreaker_match = Match.get_by_id('2017flwp_sf1m4')
        self.assertNotEqual(tiebreaker_match, None)

        self.assertEqual(tiebreaker_match.alliances['red']['score'], 165)
        self.assertEqual(tiebreaker_match.alliances['blue']['score'], 263)
        self.assertEqual(tiebreaker_match.score_breakdown['red']['totalPoints'], 165)
        self.assertEqual(tiebreaker_match.score_breakdown['blue']['totalPoints'], 263)
Esempio n. 34
0
 def get_event_matches_async(event_team_key):
     event_team = yield event_team_key.get_async()
     years.add(event_team.year)  # years is a "global" variable (defined below). Doing this removes the complexity of having to propagate the years up through the tasklet call chain.
     if (event_team.year == year):
         event = yield event_team.event.get_async()
         if not event.start_date:
             event.start_date = datetime.datetime(year, 12, 31) #unknown goes last
         matches_keys = yield Match.query(
           Match.event == event.key, Match.team_key_names == team.key_name).fetch_async(500, keys_only=True)
         matches = yield ndb.get_multi_async(matches_keys)
         raise ndb.Return((event, matches))
     raise ndb.Return(None)
Esempio n. 35
0
 def get(self, event_key):
     opr = []
     teams = []
     oprs = []
     event = Event.get_by_id(event_key)
     if Match.query(Match.event == event.key).count() > 0:
         try:
             opr,teams = OprHelper.opr(event_key)
             oprs.append((opr,teams))
             event.oprs = opr
             event.opr_teams = teams
             event.put()
         except Exception, e:
             logging.error("OPR error on event %s. %s" % (event_key, e))
    def post(self, event_key_id):
        logging.warning("Deleting %s at the request of %s / %s" % (
            event_key_id,
            users.get_current_user().user_id(),
            users.get_current_user().email()))

        event = Event.get_by_id(event_key_id)
        
        matches = Match.query(Match.event == event.key).fetch(5000)
        MatchManipulator.delete(matches)

        event_teams = EventTeam.query(EventTeam.event == event.key).fetch(5000)
        EventTeamManipulator.delete(event_teams)

        EventManipulator.delete(event)

        self.redirect("/admin/events?deleted=%s" % event_key_id)
Esempio n. 37
0
    def post(self, event_key_id):
        self._require_admin()

        logging.warning("Deleting %s at the request of %s / %s" %
                        (event_key_id, self.user_bundle.user.user_id(),
                         self.user_bundle.user.email()))

        event = Event.get_by_id(event_key_id)

        matches = Match.query(Match.event == event.key).fetch(5000)
        MatchManipulator.delete(matches)

        event_teams = EventTeam.query(EventTeam.event == event.key).fetch(5000)
        EventTeamManipulator.delete(event_teams)

        EventManipulator.delete(event)

        self.redirect("/admin/events?deleted=%s" % event_key_id)
    def post(self):
        event = Event.get_by_id(self.request.get("event_key_name"))
        matches_to_delete = list()
        match_keys_to_delete = list()
        if event is not None:
            for match in Match.query(Match.event == event.key):
                if match.key.id() != match.key_name:
                    matches_to_delete.append(match)
                    match_keys_to_delete.append(match.key_name)
            
            ndb.delete_multi(matches_to_delete)
        
        template_values = {
            "match_keys_deleted": match_keys_to_delete,
            "tried_delete": True
        }

        path = os.path.join(os.path.dirname(__file__), '../../templates/admin/matches_cleanup.html')
        self.response.out.write(template.render(path, template_values))
Esempio n. 39
0
    def post(self):
        self._require_admin()
        event = Event.get_by_id(self.request.get("event_key_name"))
        matches_to_delete = list()
        match_keys_to_delete = list()
        if event is not None:
            for match in Match.query(Match.event == event.key):
                if match.key.id() != match.key_name:
                    matches_to_delete.append(match)
                    match_keys_to_delete.append(match.key_name)

            ndb.delete_multi(matches_to_delete)

        self.template_values.update({
            "match_keys_deleted": match_keys_to_delete,
            "tried_delete": True
        })

        path = os.path.join(os.path.dirname(__file__), '../../templates/admin/matches_cleanup.html')
        self.response.out.write(template.render(path, self.template_values))
Esempio n. 40
0
 def get_matches_async(self):
     from models.match import Match
     match_keys = yield Match.query(Match.event == self.key).fetch_async(
         500, keys_only=True)
     self._matches = yield ndb.get_multi_async(match_keys)
    def parse(self, response):
        matches = response['Schedule']

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

        parsed_matches = []
        remapped_matches = {}  # If a key changes due to a tiebreaker
        is_octofinals = len(matches) > 0 and 'Octofinal' in matches[0]['description']
        for match in matches:
            if 'tournamentLevel' in match:  # 2016+
                level = match['tournamentLevel']
            else:  # 2015
                level = match['level']
            comp_level = get_comp_level(self.year, level, match['matchNumber'], is_octofinals)
            set_number, match_number = get_set_match_number(self.year, comp_level, match['matchNumber'], is_octofinals)

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

            if null_team and match['scoreRedFinal'] is None and match['scoreBlueFinal'] is None:
                continue

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

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

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

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

            post_result_time_raw = match.get('postResultTime')
            post_result_time = None
            if post_result_time_raw is not None:
                post_result_time = datetime.datetime.strptime(post_result_time_raw.split('.')[0], TIME_PATTERN)
                if event_tz is not None:
                    post_result_time = post_result_time - event_tz.utcoffset(post_result_time)

            key_name = Match.renderKeyName(
                event_key,
                comp_level,
                set_number,
                match_number)

            # Check for tiebreaker matches
            existing_match = Match.get_by_id(key_name)
            # Follow chain of existing matches
            while existing_match is not None and existing_match.tiebreak_match_key is not None:
                logging.info("Following Match {} to {}".format(existing_match.key.id(), existing_match.tiebreak_match_key.id()))
                existing_match = existing_match.tiebreak_match_key.get()
            # Check if last existing match needs to be tiebroken
            if existing_match and existing_match.comp_level != 'qm' and \
                    existing_match.has_been_played and \
                    existing_match.winning_alliance == '' and \
                    existing_match.actual_time != actual_time and \
                    not self.is_blank_match(existing_match):
                logging.warning("Match {} is tied!".format(existing_match.key.id()))

                # TODO: Only query within set if set_number ever gets indexed
                match_count = 0
                for match_key in Match.query(Match.event==event.key, Match.comp_level==comp_level).fetch(keys_only=True):
                    _, match_key = match_key.id().split('_')
                    if match_key.startswith('{}{}'.format(comp_level, set_number)):
                        match_count += 1

                # Sanity check: Tiebreakers must be played after at least 3 matches, or 6 for finals
                if match_count < 3 or (match_count < 6 and comp_level == 'f'):
                    logging.warning("Match supposedly tied, but existing count is {}! Skipping match.".format(match_count))
                    continue

                match_number = match_count + 1
                new_key_name = Match.renderKeyName(
                    event_key,
                    comp_level,
                    set_number,
                    match_number)
                remapped_matches[key_name] = new_key_name
                key_name = new_key_name

                # Point existing match to new tiebreaker match
                existing_match.tiebreak_match_key = ndb.Key(Match, key_name)
                parsed_matches.append(existing_match)

                logging.warning("Creating new match: {}".format(key_name))
            elif existing_match:
                remapped_matches[key_name] = existing_match.key.id()
                key_name = existing_match.key.id()
                match_number = existing_match.match_number

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

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

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

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

        return parsed_matches, remapped_matches
Esempio n. 42
0
 def getTeamWLT(self, team_key, event):
     """
     Given a team_key, and an event, find the team's Win Loss Tie.
     """
     matches = Match.query(Match.event == event.key, Match.team_key_names == team_key).fetch(500)
     return self.calculateTeamWLTFromMatches(team_key, matches)
Esempio n. 43
0
    def test_matches_update(self):
        self.matches_auth.put()

        update_request_path = '/api/trusted/v1/event/2014casj/matches/update'
        delete_request_path = '/api/trusted/v1/event/2014casj/matches/delete'
        delete_all_request_path = '/api/trusted/v1/event/2014casj/matches/delete_all'

        # add one match
        matches = [{
            'comp_level': 'qm',
            'set_number': 1,
            'match_number': 1,
            'alliances': {
                'red': {
                    'teams': ['frc1', 'frc2', 'frc3'],
                    'score': 25
                },
                'blue': {
                    'teams': ['frc4', 'frc5', 'frc6'],
                    'score': 26
                },
            },
            'time_string': '9:00 AM',
            'time_utc': '2014-08-31T16:00:00',
        }]
        request_body = json.dumps(matches)
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', update_request_path,
                                      request_body)).hexdigest()
        response = self.testapp.post(update_request_path,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_1',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)

        self.assertEqual(response.status_code, 200)

        db_matches = Match.query(Match.event == self.event.key).fetch(None)
        self.assertEqual(len(db_matches), 1)
        self.assertTrue('2014casj_qm1' in [m.key.id() for m in db_matches])

        # add another match
        matches = [{
            'comp_level': 'f',
            'set_number': 1,
            'match_number': 1,
            'alliances': {
                'red': {
                    'teams': ['frc1', 'frc2', 'frc3'],
                    'score': 250
                },
                'blue': {
                    'teams': ['frc4', 'frc5', 'frc6'],
                    'score': 260
                },
            },
            'time_string': '10:00 AM',
            'time_utc': '2014-08-31T17:00:00',
        }]
        request_body = json.dumps(matches)
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', update_request_path,
                                      request_body)).hexdigest()
        response = self.testapp.post(update_request_path,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_1',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)
        self.assertEqual(response.status_code, 200)

        db_matches = Match.query(Match.event == self.event.key).fetch(None)
        self.assertEqual(len(db_matches), 2)
        self.assertTrue('2014casj_qm1' in [m.key.id() for m in db_matches])
        self.assertTrue('2014casj_f1m1' in [m.key.id() for m in db_matches])

        # add a match and delete a match
        matches = [{
            'comp_level': 'f',
            'set_number': 1,
            'match_number': 2,
            'alliances': {
                'red': {
                    'teams': ['frc1', 'frc2', 'frc3'],
                    'score': 250
                },
                'blue': {
                    'teams': ['frc4', 'frc5', 'frc6'],
                    'score': 260
                },
            },
            'score_breakdown': {
                'red': {
                    'auto': 20,
                    'assist': 40,
                    'truss+catch': 20,
                    'teleop_goal+foul': 20
                },
                'blue': {
                    'auto': 40,
                    'assist': 60,
                    'truss+catch': 10,
                    'teleop_goal+foul': 40
                },
            },
            'time_string': '11:00 AM',
            'time_utc': '2014-08-31T18:00:00',
        }]
        request_body = json.dumps(matches)
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', update_request_path,
                                      request_body)).hexdigest()
        response = self.testapp.post(update_request_path,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_1',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)
        self.assertEqual(response.status_code, 200)

        keys_to_delete = ['qm1']
        request_body = json.dumps(keys_to_delete)
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', delete_request_path,
                                      request_body)).hexdigest()
        response = self.testapp.post(delete_request_path,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_1',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.json['keys_deleted'], ['qm1'])

        db_matches = Match.query(Match.event == self.event.key).fetch(None)
        self.assertEqual(len(db_matches), 2)
        self.assertTrue('2014casj_f1m1' in [m.key.id() for m in db_matches])
        self.assertTrue('2014casj_f1m2' in [m.key.id() for m in db_matches])

        db_matches = Match.query(Match.event == self.event.key).fetch(None)
        self.assertEqual(len(db_matches), 2)
        self.assertTrue('2014casj_f1m1' in [m.key.id() for m in db_matches])
        self.assertTrue('2014casj_f1m2' in [m.key.id() for m in db_matches])

        # verify match data
        match = Match.get_by_id('2014casj_f1m2')
        self.assertEqual(match.time, datetime.datetime(2014, 8, 31, 18, 0))
        self.assertEqual(match.time_string, '11:00 AM')
        self.assertEqual(match.alliances['red']['score'], 250)
        self.assertEqual(match.score_breakdown['red']['truss+catch'], 20)

        # test delete all matches
        request_body = ''
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', delete_all_request_path,
                                      request_body)).hexdigest()
        response = self.testapp.post(delete_all_request_path,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_1',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)
        self.assertEqual(response.status_code, 400)

        request_body = '2014casj'
        sig = md5.new('{}{}{}'.format('321tEsTsEcReT', delete_all_request_path,
                                      request_body)).hexdigest()
        response = self.testapp.post(delete_all_request_path,
                                     request_body,
                                     headers={
                                         'X-TBA-Auth-Id': 'tEsT_id_1',
                                         'X-TBA-Auth-Sig': sig
                                     },
                                     expect_errors=True)
        self.assertEqual(response.status_code, 200)

        db_matches = Match.query(Match.event == self.event.key).fetch(None)
        self.assertEqual(len(db_matches), 0)
Esempio n. 44
0
 def get_matches_async(self):
     from models.match import Match
     match_keys = yield Match.query(Match.event == self.key).fetch_async(500, keys_only=True)
     self._matches = yield ndb.get_multi_async(match_keys)
    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 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
        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 update(self, event_key):
        """
        Updates EventTeams for an event.
        Returns a tuple of (teams, event_teams, event_team_keys_to_delete)
        An EventTeam is valid iff the team:
        a) played a match at the event,
        b) the team received an award at the event,
        c) the event has not yet occurred,
        d) or the event is not from the current year. (This is to make sure we don't delete old data we may no longer be able to scrape)
        """
        event = Event.get_by_id(event_key)
        cur_year = datetime.datetime.now().year

        # Add teams from Matches and Awards
        team_ids = set()
        match_key_futures = Match.query(
            Match.event == event.key).fetch_async(1000, keys_only=True)
        award_key_futures = Award.query(
            Award.event == event.key).fetch_async(1000, 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())

        for match_future in match_futures:
            match = match_future.get_result()
            for team in match.team_key_names:
                team_ids.add(team)
        for award_future in award_futures:
            award = award_future.get_result()
            for team_key in award.team_list:
                team_ids.add(team_key.id())

        # Create or update EventTeams
        teams = [Team(id=team_id,
                      team_number=int(team_id[3:]))
                      for team_id in team_ids]

        if teams:
            event_teams = [EventTeam(id=event_key + "_" + team.key.id(),
                                     event=event.key,
                                     team=team.key,
                                     year=event.year)
                                     for team in teams]
        else:
            event_teams = None

        # Delete EventTeams for teams who did not participate in the event
        # Only runs if event is over
        existing_event_teams_keys = EventTeam.query(
            EventTeam.event == event.key).fetch(1000, keys_only=True)
        existing_event_teams = ndb.get_multi(existing_event_teams_keys)
        existing_team_ids = set()
        for et in existing_event_teams:
            existing_team_ids.add(et.team.id())

        et_keys_to_delete = set()
        if event.year == cur_year and event.end_date is not None and event.end_date < datetime.datetime.now():
            for team_id in existing_team_ids.difference(team_ids):
                et_key_name = "{}_{}".format(event.key_name, team_id)
                et_keys_to_delete.add(ndb.Key(EventTeam, et_key_name))

        return teams, event_teams, et_keys_to_delete
Esempio n. 48
0
 def getTeamWLT(self, team_key, event):
     """
     Given a team_key, and an event, find the team's Win Loss Tie.
     """
     match_keys = Match.query(Match.event == event.key, Match.team_key_names == team_key).fetch(500, keys_only=True)
     return self.calculateTeamWLTFromMatches(team_key, ndb.get_multi(match_keys))
Esempio n. 49
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_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)

        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)
Esempio n. 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)
        district_team_key_futures = DistrictTeam.query(DistrictTeam.district == event.event_district_enum, DistrictTeam.year == event.year).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())
        district_team_futures = ndb.get_multi_async(district_team_key_futures.get_result())

        # Typically 3 for District CMP, 1 otherwise
        POINTS_MULTIPLIER = DistrictPointValues.DISTRICT_CMP_MULTIPLIER.get(event.year, DistrictPointValues.DISTRICT_CMP_MULIPLIER_DEFAULT) if event.event_type_enum == EventType.DISTRICT_CMP else DistrictPointValues.STANDARD_MULTIPLIER

        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': [],
            }),
        }
        single_district_points = district_points.copy()

        # match points
        if event.year >= 2015:
            # Switched to ranking-based points for 2015 and onward
            cls.calc_rank_based_match_points(event, district_points, match_futures, POINTS_MULTIPLIER)
        else:
            cls.calc_wlt_based_match_points(district_points, match_futures, POINTS_MULTIPLIER)

        # alliance points
        if event.alliance_selections:
            selection_points = EventHelper.alliance_selections_to_points(event.key_name, POINTS_MULTIPLIER, event.alliance_selections)
            for team, points in selection_points.items():
                district_points['points'][team]['alliance_points'] += points
        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()
            point_value = 0
            if event.year >= 2014:
                if award.award_type_enum not in AwardType.NON_JUDGED_NON_TEAM_AWARDS:
                    if award.award_type_enum == AwardType.CHAIRMANS:
                        point_value = DistrictPointValues.CHAIRMANS.get(event.year, DistrictPointValues.CHAIRMANS_DEFAULT)
                    elif award.award_type_enum in {AwardType.ENGINEERING_INSPIRATION, AwardType.ROOKIE_ALL_STAR}:
                        point_value = DistrictPointValues.EI_AND_RAS_DEFAULT
                    else:
                        point_value = DistrictPointValues.OTHER_AWARD_DEFAULT
            else:  # Legacy awards
                if award.award_type_enum in DistrictPointValues.LEGACY_5_PT_AWARDS.get(event.year, []):
                    point_value = 5
                elif award.award_type_enum in DistrictPointValues.LEGACY_2_PT_AWARDS.get(event.year, []):
                    point_value = 2

            # Add award points to all teams who won
            for team in award.team_list:
                district_points['points'][team.id()]['award_points'] += point_value * POINTS_MULTIPLIER

        # Filter out teams not in this district (only keep those with a DistrictTeam present for this district)
        for district_team_future in district_team_futures:
            district_team = district_team_future.get_result()
            team_key = district_team.team.id()
            if team_key in district_points['points']:
                single_district_points['points'][team_key] = district_points['points'][team_key]
            if team_key in district_points['tiebreakers']:
                single_district_points['tiebreakers'][team_key] = district_points['tiebreakers'][team_key]

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

        return single_district_points
    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)
        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)
        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)
        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)
        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)
    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)
        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)
        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)
        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)
        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)
Esempio n. 54
0
 def _query_async(self):
     team_key = self._query_args[0]
     year = self._query_args[1]
     matches = yield Match.query(Match.team_key_names == team_key,
                                 Match.year == year).fetch_async()
     raise ndb.Return(matches)
    def parse(self, response):
        matches = response['Schedule']

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

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

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

            if null_team and match['scoreRedFinal'] is None and match[
                    'scoreBlueFinal'] is None:
                continue

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

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

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

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

            post_result_time_raw = match.get('postResultTime')
            post_result_time = None
            if post_result_time_raw is not None:
                post_result_time = datetime.datetime.strptime(
                    post_result_time_raw.split('.')[0], TIME_PATTERN)
                if event_tz is not None:
                    post_result_time = post_result_time - event_tz.utcoffset(
                        post_result_time)

            key_name = Match.renderKeyName(event_key, comp_level, set_number,
                                           match_number)

            # Check for tiebreaker matches
            existing_match = Match.get_by_id(key_name)
            # Follow chain of existing matches
            while existing_match is not None and existing_match.tiebreak_match_key is not None:
                logging.info("Following Match {} to {}".format(
                    existing_match.key.id(),
                    existing_match.tiebreak_match_key.id()))
                existing_match = existing_match.tiebreak_match_key.get()
            # Check if last existing match needs to be tiebroken
            if existing_match and existing_match.comp_level != 'qm' and \
                    existing_match.has_been_played and \
                    existing_match.winning_alliance == '' and \
                    existing_match.actual_time != actual_time and \
                    not self.is_blank_match(existing_match):
                logging.warning("Match {} is tied!".format(
                    existing_match.key.id()))

                # TODO: Only query within set if set_number ever gets indexed
                match_count = 0
                for match_key in Match.query(
                        Match.event == event.key,
                        Match.comp_level == comp_level).fetch(keys_only=True):
                    _, match_key = match_key.id().split('_')
                    if match_key.startswith('{}{}'.format(
                            comp_level, set_number)):
                        match_count += 1

                # Sanity check: Tiebreakers must be played after at least 3 matches, or 6 for finals
                if match_count < 3 or (match_count < 6 and comp_level == 'f'):
                    logging.warning(
                        "Match supposedly tied, but existing count is {}! Skipping match."
                        .format(match_count))
                    continue

                match_number = match_count + 1
                new_key_name = Match.renderKeyName(event_key, comp_level,
                                                   set_number, match_number)
                remapped_matches[key_name] = new_key_name
                key_name = new_key_name

                # Point existing match to new tiebreaker match
                existing_match.tiebreak_match_key = ndb.Key(Match, key_name)
                parsed_matches.append(existing_match)

                logging.warning("Creating new match: {}".format(key_name))
            elif existing_match:
                remapped_matches[key_name] = existing_match.key.id()
                key_name = existing_match.key.id()
                match_number = existing_match.match_number

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

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

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

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

        return parsed_matches, remapped_matches
Esempio n. 56
0
 def _query_async(self):
     event_key = self._query_args[0]
     matches = yield Match.query(
         Match.event == ndb.Key(Event, event_key)).fetch_async()
     raise ndb.Return(matches)
Esempio n. 57
0
 def prepMatches(self):
     from models.match import Match
     if self._matches_future is None:
         self._matches_future = Match.query(Match.event == self.key).fetch_async(500)
Esempio n. 58
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_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)