def test_parse_2017_events_with_cmp_hacks(self):
        hack_sitevar = Sitevar(id='cmp_registration_hacks')
        hack_sitevar.contents = {
            "event_name_override": [
                {"event": "2017cmpmo", "name": "FIRST Championship Event", "short_name": "Championship"},
                {"event": "2017cmptx", "name": "FIRST Championship Event", "short_name": "Championship"}],
            "set_start_to_last_day": ["2017cmptx", "2017cmpmo"],
            "divisions_to_skip": ["2017arc", "2017cars", "2017cur", "2017dal", "2017dar"],
        }
        hack_sitevar.put()

        with open('test_data/fms_api/2017_event_list.json', 'r') as f:
            events, districts = FMSAPIEventListParser(2017).parse(json.loads(f.read()))
            self.assertEqual(len(events), 159)
            self.assertEqual(len(districts), 10)

            non_einstein_types = EventType.CMP_EVENT_TYPES
            non_einstein_types.remove(EventType.CMP_FINALS)
            for key in hack_sitevar.contents['divisions_to_skip']:
                self.assertFalse(filter(lambda e: e.key_name == key, events))

            einstein_stl = next(e for e in events if e.key_name == '2017cmpmo')
            self.assertIsNotNone(einstein_stl)
            self.assertEqual(einstein_stl.name, "FIRST Championship Event (St. Louis)")
            self.assertEqual(einstein_stl.short_name, "Championship (St. Louis)")
            self.assertEquals(einstein_stl.start_date, datetime.datetime(year=2017, month=4, day=29, hour=0, minute=0, second=0))
            self.assertEquals(einstein_stl.end_date, datetime.datetime(year=2017, month=4, day=29, hour=23, minute=59, second=59))

            einstein_hou = next(e for e in events if e.key_name == '2017cmptx')
            self.assertIsNotNone(einstein_hou)
            self.assertEqual(einstein_hou.name, "FIRST Championship Event (Houston)")
            self.assertEqual(einstein_hou.short_name, "Championship (Houston)")
            self.assertEquals(einstein_hou.start_date, datetime.datetime(year=2017, month=4, day=22, hour=0, minute=0, second=0))
            self.assertEquals(einstein_hou.end_date, datetime.datetime(year=2017, month=4, day=22, hour=23, minute=59, second=59))
Example #2
0
    def send_upcoming_matches(cls, live_events):
        from helpers.match_helper import MatchHelper  # PJL: Hacky :P
        # Causes circular import, otherwise
        # https://github.com/the-blue-alliance/the-blue-alliance/pull/1098#discussion_r25128966

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

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

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

            # Determine if event is down
            if cls.is_event_down(last_matches[0] if last_matches else None,
                                 next_matches[0] if next_matches else None):
                down_events.append(event.key_name)

        # Update the status sitevar
        status_sitevar = Sitevar.get_by_id('apistatus.down_events')
        if status_sitevar is None:
            status_sitevar = Sitevar(id="apistatus.down_events",
                                     description="A list of down event keys",
                                     values_json="[]")
        old_status = status_sitevar.contents

        status_sitevar.contents = down_events
        status_sitevar.put()

        # Clear API Response cache
        ApiStatusController.clear_cache_if_needed(old_status, down_events)
Example #3
0
    def get(self, event_key):
        import pytz

        event = Event.get_by_id(event_key)
        if not event:
            self.abort(404)

        matches = event.matches
        if not matches or not event.timezone_id:
            return

        timezone = pytz.timezone(event.timezone_id)
        played_matches = MatchHelper.recentMatches(matches, num=0)
        unplayed_matches = MatchHelper.upcomingMatches(matches,
                                                       num=len(matches))
        MatchTimePredictionHelper.predict_future_matches(
            event_key, played_matches, unplayed_matches, timezone,
            event.within_a_day)

        # Detect whether the event is down
        # An event NOT down if ANY unplayed match's predicted time is within its scheduled time by a threshold and
        # the last played match (if it exists) wasn't too long ago.
        event_down = len(unplayed_matches) > 0
        for unplayed_match in unplayed_matches:
            if ((unplayed_match.predicted_time and unplayed_match.time
                 and unplayed_match.predicted_time <
                 unplayed_match.time + datetime.timedelta(minutes=30)) or
                (played_matches == [] or played_matches[-1].actual_time is None
                 or played_matches[-1].actual_time >
                 datetime.datetime.now() - datetime.timedelta(minutes=30))):
                event_down = False
                break

        status_sitevar = Sitevar.get_by_id('apistatus.down_events')
        if status_sitevar is None:
            status_sitevar = Sitevar(id="apistatus.down_events",
                                     description="A list of down event keys",
                                     values_json="[]")
        old_status = set(status_sitevar.contents)
        new_status = old_status.copy()
        if event_down:
            new_status.add(event_key)
        elif event_key in new_status:
            new_status.remove(event_key)

        status_sitevar.contents = list(new_status)
        status_sitevar.put()

        # Clear API Response cache
        ApiStatusController.clear_cache_if_needed(old_status, new_status)
    def send_upcoming_matches(cls, live_events):
        from helpers.match_helper import MatchHelper  # PJL: Hacky :P
        # Causes circular import, otherwise
        # https://github.com/the-blue-alliance/the-blue-alliance/pull/1098#discussion_r25128966

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

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

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

            # Determine if event is down
            if cls.is_event_down(last_matches[0] if last_matches else None, next_matches[0] if next_matches else None):
                down_events.append(event.key_name)

        # Update the status sitevar
        status_sitevar = Sitevar.get_by_id('apistatus.down_events')
        if status_sitevar is None:
            status_sitevar = Sitevar(id="apistatus.down_events", description="A list of down event keys", values_json="[]")
        old_status = status_sitevar.contents

        status_sitevar.contents = down_events
        status_sitevar.put()

        # Clear API Response cache
        ApiStatusController.clear_cache_if_needed(old_status, down_events)
    def get(self, event_key):
        import pytz

        event = Event.get_by_id(event_key)
        if not event:
            self.abort(404)

        matches = event.matches
        if not matches or not event.timezone_id:
            return

        timezone = pytz.timezone(event.timezone_id)
        played_matches = MatchHelper.recentMatches(matches, num=0)
        unplayed_matches = MatchHelper.upcomingMatches(matches, num=len(matches))
        MatchTimePredictionHelper.predict_future_matches(event_key, played_matches, unplayed_matches, timezone, event.within_a_day)

        # Detect whether the event is down
        # An event NOT down if ANY unplayed match's predicted time is within its scheduled time by a threshold and
        # the last played match (if it exists) wasn't too long ago.
        event_down = len(unplayed_matches) > 0
        for unplayed_match in unplayed_matches:
            if ((unplayed_match.predicted_time and unplayed_match.time and
                unplayed_match.predicted_time < unplayed_match.time + datetime.timedelta(minutes=30)) or
                (played_matches == [] or played_matches[-1].actual_time is None or played_matches[-1].actual_time > datetime.datetime.now() - datetime.timedelta(minutes=30))):
                event_down = False
                break

        status_sitevar = Sitevar.get_by_id('apistatus.down_events')
        if status_sitevar is None:
            status_sitevar = Sitevar(id="apistatus.down_events", description="A list of down event keys", values_json="[]")
        old_status = set(status_sitevar.contents)
        new_status = old_status.copy()
        if event_down:
            new_status.add(event_key)
        elif event_key in new_status:
            new_status.remove(event_key)

        status_sitevar.contents = list(new_status)
        status_sitevar.put()

        # Clear API Response cache
        ApiStatusController.clear_cache_if_needed(old_status, new_status)
    def test_parse_2017_events_with_cmp_hacks(self):
        hack_sitevar = Sitevar(id='cmp_registration_hacks')
        hack_sitevar.contents = {
            'should_store_divisions': False,
            'einstein_name': 'FIRST Championship Event',
            'einstein_short_name': 'Championship',
            'should_change_einstein_dates': False
        }
        hack_sitevar.put()

        with open('test_data/fms_api/2017_event_list.json', 'r') as f:
            events, districts = FMSAPIEventListParser(2017).parse(
                json.loads(f.read()))
            self.assertEqual(len(events), 147)
            self.assertEqual(len(districts), 10)

            non_einstein_types = EventType.CMP_EVENT_TYPES
            non_einstein_types.remove(EventType.CMP_FINALS)
            self.assertFalse(
                any(event.event_type_enum in non_einstein_types
                    for event in events))

            einstein_stl = next(e for e in events if e.key_name == '2017cmpmo')
            self.assertIsNotNone(einstein_stl)
            self.assertEqual(einstein_stl.name,
                             "FIRST Championship Event (St. Louis)")
            self.assertEqual(einstein_stl.short_name,
                             "Championship (St. Louis)")
            self.assertEquals(
                einstein_stl.start_date,
                datetime.datetime(year=2017,
                                  month=4,
                                  day=26,
                                  hour=0,
                                  minute=0,
                                  second=0))
            self.assertEquals(
                einstein_stl.end_date,
                datetime.datetime(year=2017,
                                  month=4,
                                  day=29,
                                  hour=23,
                                  minute=59,
                                  second=59))

            einstein_hou = next(e for e in events if e.key_name == '2017cmptx')
            self.assertIsNotNone(einstein_hou)
            self.assertEqual(einstein_hou.name,
                             "FIRST Championship Event (Houston)")
            self.assertEqual(einstein_hou.short_name, "Championship (Houston)")
            self.assertEquals(
                einstein_hou.start_date,
                datetime.datetime(year=2017,
                                  month=4,
                                  day=19,
                                  hour=0,
                                  minute=0,
                                  second=0))
            self.assertEquals(
                einstein_hou.end_date,
                datetime.datetime(year=2017,
                                  month=4,
                                  day=22,
                                  hour=23,
                                  minute=59,
                                  second=59))