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