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 get(self): suggestions = Suggestion.query().filter( Suggestion.review_state == Suggestion.REVIEW_PENDING).filter( Suggestion.target_model == "event") suggestions_by_event_key = {} for suggestion in suggestions: if 'webcast_dict' in suggestion.contents: suggestion.webcast_template = 'webcast/{}.html'.format(suggestion.contents['webcast_dict']['type']) suggestions_by_event_key.setdefault(suggestion.target_key, []).append(suggestion) suggestion_sets = [] for event_key, suggestions in suggestions_by_event_key.items(): suggestion_sets.append({ "event": Event.get_by_id(event_key), "suggestions": suggestions }) self.template_values.update({ "event_key": self.request.get("event_key"), "success": self.request.get("success"), "suggestion_sets": suggestion_sets }) path = os.path.join(os.path.dirname(__file__), '../../templates/suggest_event_webcast_review_list.html') self.response.out.write(template.render(path, self.template_values))
def get(self, event_key): df = DatafeedUsfirst() event = Event.get_by_id(event_key) new_matches = MatchManipulator.createOrUpdate(df.getMatches(event)) if new_matches: for match in new_matches: if hasattr(match, 'dirty') and match.dirty: # Enqueue push notification try: FirebasePusher.updated_event(event.key_name) except: logging.warning("Enqueuing Firebase push failed!") # Enqueue task to calculate matchstats taskqueue.add( url='/tasks/math/do/event_matchstats/' + event.key_name, method='GET') break template_values = { 'matches': new_matches, } path = os.path.join(os.path.dirname(__file__), '../templates/datafeeds/usfirst_matches_get.html') self.response.out.write(template.render(path, template_values))
def post(self, match_key): self._require_admin() alliances_json = self.request.get("alliances_json") alliances = json.loads(alliances_json) youtube_videos = json.loads(self.request.get("youtube_videos")) team_key_names = list() for alliance in alliances: team_key_names.extend(alliances[alliance].get('teams', None)) match = Match( id=match_key, event=Event.get_by_id(self.request.get("event_key_name")).key, set_number=int(self.request.get("set_number")), match_number=int(self.request.get("match_number")), comp_level=self.request.get("comp_level"), team_key_names=team_key_names, alliances_json=alliances_json, # no_auto_update = str(self.request.get("no_auto_update")).lower() == "true", #TODO ) match = MatchManipulator.createOrUpdate(match) match.youtube_videos = youtube_videos match.dirty = True # hacky MatchManipulator.createOrUpdate(match) self.redirect("/admin/match/" + match.key_name)
def _render(self, event_key): event = Event.get_by_id(event_key) if not event: self.abort(404) return medias_future = media_query.EventTeamsPreferredMediasQuery(event_key).fetch_async() next_match = MatchHelper.upcomingMatches(event.matches, num=1) next_match = next_match[0] if next_match else None team_and_medias = [] if next_match: # Organize medias by team teams = ndb.get_multi([ndb.Key(Team, team_key) for team_key in next_match.alliances['red']['teams'] + next_match.alliances['blue']['teams']]) image_medias = MediaHelper.get_images([media for media in medias_future.get_result()]) team_medias = defaultdict(list) for image_media in image_medias: for reference in image_media.references: team_medias[reference].append(image_media) stations = ['Red 1', 'Red 2', 'Red 3', 'Blue 1', 'Blue 2', 'Blue 3'] for i, team in enumerate(teams): team_and_medias.append((team, stations[i], team_medias.get(team.key, []))) self.template_values.update({ 'event': event, 'next_match': next_match, 'teams_and_media': team_and_medias, }) return jinja2_engine.render('nextmatch.html', self.template_values)
def get(self, event_key): event = Event.get_by_id(event_key) matchstats_dict = MatchstatsHelper.calculate_matchstats(event.matches, event.year) if event.year == 2016: organized_matches = MatchHelper.organizeMatches(event.matches) match_predictions, match_prediction_stats = PredictionHelper.get_match_predictions(organized_matches['qm']) ranking_predictions, ranking_prediction_stats = PredictionHelper.get_ranking_predictions(organized_matches['qm'], match_predictions) matchstats_dict['match_predictions'] = match_predictions matchstats_dict['match_prediction_stats'] = match_prediction_stats matchstats_dict['ranking_predictions'] = ranking_predictions matchstats_dict['ranking_prediction_stats'] = ranking_prediction_stats if any([v != {} for v in matchstats_dict.values()]): event.matchstats_json = json.dumps(matchstats_dict) EventManipulator.createOrUpdate(event) else: logging.warn("Matchstat calculation for {} failed!".format(event_key)) template_values = { 'matchstats_dict': matchstats_dict, } path = os.path.join(os.path.dirname(__file__), '../templates/math/event_matchstats_do.html') self.response.out.write(template.render(path, template_values))
def post(self, event_key): self._require_login() self._require_registration() current_user_id = self.user_bundle.account.key.id() event = Event.get_by_id(event_key) subscribed_matches = set(self.request.get_all('subscribed_matches')) for match in event.matches: if not match.has_been_played: match_key = match.key.id() if match.key.id() in subscribed_matches: sub = Subscription( parent=ndb.Key(Account, current_user_id), user_id=current_user_id, model_type=ModelType.MATCH, model_key=match_key, notification_types=[NotificationType.UPCOMING_MATCH] ) MyTBAHelper.add_subscription(sub) else: MyTBAHelper.remove_subscription(current_user_id, match_key, ModelType.MATCH) self.redirect('/account/mytba?status=match_updated#my-matches'.format(event_key))
def parse(self, response): matches = response['MatchScores'] event_key = '{}{}'.format(self.year, self.event_short) event = Event.get_by_id(event_key) match_details_by_key = {} for match in matches: comp_level = PlayoffType.get_comp_level(event.playoff_type, match['matchLevel'], match['matchNumber']) set_number, match_number = PlayoffType.get_set_match_number(event.playoff_type, comp_level, match['matchNumber']) breakdown = { 'red': {}, 'blue': {}, } if 'coopertition' in match: breakdown['coopertition'] = match['coopertition'] if 'coopertitionPoints' in match: breakdown['coopertition_points'] = match['coopertitionPoints'] for alliance in match['Alliances']: color = alliance['alliance'].lower() for key, value in alliance.items(): if key != 'alliance': breakdown[color][key] = value match_details_by_key[Match.renderKeyName( '{}{}'.format(self.year, self.event_short), comp_level, set_number, match_number)] = breakdown return match_details_by_key
def post(self, event_key_id): self._require_admin() event = Event.get_by_id(event_key_id) if not event: self.abort(404) place_id = self.request.get('place_id') if not place_id: self.abort(400) # Construct a mostly empty input struct that'll get filled in location_input = { 'place_id': place_id, 'geometry': { 'location': { 'lat': '', 'lng': '', }, }, 'name': '', 'types': [], } location_info = LocationHelper.construct_location_info_async(location_input).get_result() event.normalized_location = LocationHelper.build_normalized_location(location_info) EventManipulator.createOrUpdate(event) self.redirect('/admin/event/{}'.format(event_key_id))
def get(self, event_key): df = DatafeedTba() event = Event.get_by_id(event_key) match_filetypes = df.getVideos(event) if match_filetypes: matches_to_put = [] for match in event.matches: if match.tba_videos != match_filetypes.get(match.key_name, []): match.tba_videos = match_filetypes.get(match.key_name, []) match.dirty = True matches_to_put.append(match) MatchManipulator.createOrUpdate(matches_to_put) tbavideos = match_filetypes.items() else: logging.info("No tbavideos found for event " + event.key_name) tbavideos = [] template_values = { 'tbavideos': tbavideos, } if 'X-Appengine-Taskname' not in self.request.headers: # Only write out if not in taskqueue path = os.path.join(os.path.dirname(__file__), '../templates/datafeeds/tba_videos_get.html') self.response.out.write(template.render(path, template_values))
def post(self): self._require_admin() event_key = self.request.get('event_key') matches_csv = self.request.get('matches_csv') matches = OffseasonMatchesParser.parse(matches_csv) event = Event.get_by_id(event_key) matches = [Match( id=Match.renderKeyName( event.key.id(), match.get("comp_level", None), match.get("set_number", 0), match.get("match_number", 0)), event=event.key, game=Match.FRC_GAMES_BY_YEAR.get(event.year, "frc_unknown"), set_number=match.get("set_number", 0), match_number=match.get("match_number", 0), comp_level=match.get("comp_level", None), team_key_names=match.get("team_key_names", None), alliances_json=match.get("alliances_json", None) ) for match in matches] try: FirebasePusher.updated_event(event.key_name) except: logging.warning("Enqueuing Firebase push failed!") self.redirect('/admin/event/{}'.format(event_key))
def _render(self, event_key): self.response.headers.add_header('content-type', 'application/json', charset='utf-8') event = Event.get_by_id(event_key) if not event: return json.dumps(None) return json.dumps(event.remap_teams)
def get(self, event_key=None): self._require_login() self._require_registration() if event_key is None: events = EventHelper.getEventsWithinADay() EventHelper.sort_events(events) self.template_values['events'] = events self.response.out.write(jinja2_engine.render('mytba_add_hot_matches_base.html', self.template_values)) return event = Event.get_by_id(event_key) if not event: self.abort(404) subscriptions_future = Subscription.query( Subscription.model_type==ModelType.MATCH, Subscription.notification_types==NotificationType.UPCOMING_MATCH, ancestor=self.user_bundle.account.key).fetch_async(projection=[Subscription.model_key]) matches = [] if event.matchstats and 'match_predictions' in event.matchstats: match_predictions = event.matchstats['match_predictions'] max_hotness = 0 min_hotness = float('inf') for match in event.matches: if not match.has_been_played and match.key.id() in match_predictions: prediction = match_predictions[match.key.id()] red_score = prediction['red']['score'] blue_score = prediction['blue']['score'] if red_score > blue_score: winner_score = red_score loser_score = blue_score else: winner_score = blue_score loser_score = red_score hotness = winner_score + 2.0*loser_score # Favor close high scoring matches max_hotness = max(max_hotness, hotness) min_hotness = min(min_hotness, hotness) match.hotness = hotness matches.append(match) existing_subscriptions = set() for sub in subscriptions_future.get_result(): existing_subscriptions.add(sub.model_key) hot_matches = [] for match in matches: match.hotness = 100 * (match.hotness - min_hotness) / (max_hotness - min_hotness) match.already_subscribed = match.key.id() in existing_subscriptions hot_matches.append(match) hot_matches = sorted(hot_matches, key=lambda match: -match.hotness) matches_dict = {'qm': hot_matches[:25]} self.template_values['event'] = event self.template_values['matches'] = matches_dict self.response.out.write(jinja2_engine.render('mytba_add_hot_matches.html', self.template_values))
def get(self, alias): special_webcasts_future = Sitevar.get_by_id_async('gameday.special_webcasts') special_webcasts = special_webcasts_future.get_result() aliases = special_webcasts.contents.get("aliases", {}) if special_webcasts else {} if alias in aliases: self.redirect("/gameday{}".format(aliases[alias])) return # Allow an alias to be an event key if not ValidationHelper.event_id_validator(alias): event = Event.get_by_id(alias) if event and event.webcast and event.within_a_day: params = self.get_param_string_for_event(event) self.redirect("/gameday{}".format(params)) return # Allow an alias to be a team number team_key = "frc{}".format(alias) if not ValidationHelper.team_id_validator(team_key): now = datetime.datetime.now() team_events_future = TeamYearEventsQuery(team_key, now.year).fetch_async() team_events = team_events_future.get_result() for event in team_events: if event and event.webcast and event.within_a_day: params = self.get_param_string_for_event(event) self.redirect("/gameday{}".format(params)) return self.redirect("/gameday") return
def _process_request(self, request, event_key): alliance_selections = JSONAllianceSelectionsParser.parse(request.body) event = Event.get_by_id(event_key) event.alliance_selections_json = json.dumps(alliance_selections) event.dirty = True # TODO: hacky EventManipulator.createOrUpdate(event)
def post(self, event_key_id): self._require_admin() event = Event.get_by_id(event_key_id) if not event: self.redirect("/admin/event/" + event.key_name) return event_details = event.details if not event_details or not event_details.alliance_selections: # No alliance data to modify self.redirect("/admin/event/" + event.key_name) return team_in = "frc{}".format(self.request.get("backup_in")) team_out = "frc{}".format(self.request.get("backup_out")) # Make sure both teams are attending the event et_in = EventTeam.get_by_id("{}_{}".format(event.key_name, team_in)) et_out = EventTeam.get_by_id("{}_{}".format(event.key_name, team_out)) if not et_in and et_out: # Bad teams supplied self.redirect("/admin/event/" + event.key_name) return for alliance in event_details.alliance_selections: if team_out in alliance.get('picks', []): alliance['backup'] = {} alliance['backup']['in'] = team_in alliance['backup']['out'] = team_out EventDetailsManipulator.createOrUpdate(event_details) break self.redirect("/admin/event/" + event.key_name) return
def _process_request(self, request, event_key): event = Event.get_by_id(event_key) year = int(event_key[:4]) matches = [] for match in JSONMatchesParser.parse(request.body, year): match = Match( id=Match.renderKeyName( event.key.id(), match.get("comp_level", None), match.get("set_number", 0), match.get("match_number", 0)), event=event.key, year=event.year, set_number=match.get("set_number", 0), match_number=match.get("match_number", 0), comp_level=match.get("comp_level", None), team_key_names=match.get("team_key_names", None), alliances_json=match.get("alliances_json", None), score_breakdown_json=match.get("score_breakdown_json", None), time_string=match.get("time_string", None), time=match.get("time", None), ) if (not match.time or match.time == "") and match.time_string: # We can calculate the real time from the time string logging.debug("Calculating time!") MatchHelper.add_match_times(event, [match]) matches.append(match) MatchManipulator.createOrUpdate(matches) self.response.out.write(json.dumps({'Success': "Matches successfully updated"}))
def getEventAlliances(self, event_key): year = int(event_key[:4]) event_short = event_key[4:] event = Event.get_by_id(event_key) alliances = self._parse(self.FMS_API_EVENT_ALLIANCES_URL_PATTERN % (year, self._get_event_short(event_short, event)), FMSAPIEventAlliancesParser()) return alliances
def getMatches(self, event_key): year = int(event_key[:4]) event_short = event_key[4:] event = Event.get_by_id(event_key) hs_parser = FMSAPIHybridScheduleParser(year, event_short) detail_parser = FMSAPIMatchDetailsParser(year, event_short) qual_matches_future = self._parse_async(self.FMS_API_HYBRID_SCHEDULE_QUAL_URL_PATTERN % (year, self._get_event_short(event_short, event)), hs_parser) playoff_matches_future = self._parse_async(self.FMS_API_HYBRID_SCHEDULE_PLAYOFF_URL_PATTERN % (year, self._get_event_short(event_short, event)), hs_parser) qual_details_future = self._parse_async(self.FMS_API_MATCH_DETAILS_QUAL_URL_PATTERN % (year, self._get_event_short(event_short, event)), detail_parser) playoff_details_future = self._parse_async(self.FMS_API_MATCH_DETAILS_PLAYOFF_URL_PATTERN % (year, self._get_event_short(event_short, event)), detail_parser) matches_by_key = {} qual_matches = qual_matches_future.get_result() if qual_matches is not None: for match in qual_matches[0]: matches_by_key[match.key.id()] = match playoff_matches = playoff_matches_future.get_result() if playoff_matches is not None: for match in playoff_matches[0]: matches_by_key[match.key.id()] = match qual_details = qual_details_future.get_result() qual_details_items = qual_details.items() if qual_details is not None else [] playoff_details = playoff_details_future.get_result() playoff_details_items = playoff_details.items() if playoff_details is not None else [] for match_key, match_details in qual_details_items + playoff_details_items: match_key = playoff_matches[1].get(match_key, match_key) if match_key in matches_by_key: matches_by_key[match_key].score_breakdown_json = json.dumps(match_details) return filter( lambda m: not FMSAPIHybridScheduleParser.is_blank_match(m), matches_by_key.values())
def post(self): event_key = self.request.get('event_key') awards_json = self.request.get('awards_json') awards = json.loads(awards_json) event = Event.get_by_id(event_key) def _getTeamKey(award): team = Team.get_by_id('frc' + str(award.get('team_number', None))) if team is not None: return team.key else: return None awards = [Award( id = Award.renderKeyName(event.key_name, award.get('name')), name = award.get('name', None), team = _getTeamKey(award), awardee = award.get('awardee', None), year = event.year, official_name = award.get('official_name', None), event = event.key) for award in awards] AwardManipulator.createOrUpdate(awards) self.redirect('/admin/event/{}'.format(event_key))
def get(self, event_key): event = Event.get_by_id(event_key) if not event: self.abort(404) if not event.remap_teams: return event.prepAwardsMatchesTeams() # Remap matches EventHelper.remapteams_matches(event.matches, event.remap_teams) MatchManipulator.createOrUpdate(event.matches) # Remap alliance selections if event.alliance_selections: EventHelper.remapteams_alliances(event.alliance_selections, event.remap_teams) # Remap rankings if event.rankings: EventHelper.remapteams_rankings(event.rankings, event.remap_teams) if event.details and event.details.rankings2: EventHelper.remapteams_rankings2(event.details.rankings2, event.remap_teams) EventDetailsManipulator.createOrUpdate(event.details) # Remap awards EventHelper.remapteams_awards(event.awards, event.remap_teams) AwardManipulator.createOrUpdate(event.awards, auto_union=False)
def create_target_model(self, suggestion): event_id = self.request.get("event_short", None) event_key = str(self.request.get("year")) + str.lower(str(self.request.get("event_short"))) if not event_id: # Need to supply a key :( return 'missing_key', None if not Event.validate_key_name(event_key): # Bad event key generated return 'bad_key', None start_date = None if self.request.get("start_date"): start_date = datetime.strptime(self.request.get("start_date"), "%Y-%m-%d") end_date = None if self.request.get("end_date"): end_date = datetime.strptime(self.request.get("end_date"), "%Y-%m-%d") existing_event = Event.get_by_id(event_key) if existing_event: return 'duplicate_key', None first_code = self.request.get("first_code", '') event = Event( id=event_key, end_date=end_date, event_short=self.request.get("event_short"), event_type_enum=EventType.OFFSEASON, district_key=None, venue=self.request.get("venue"), venue_address=self.request.get("venue_address"), city=self.request.get("city"), state_prov=self.request.get("state"), country=self.request.get("country"), name=self.request.get("name"), short_name=self.request.get("short_name"), start_date=start_date, website=self.request.get("website"), year=int(self.request.get("year")), first_code=first_code, official=(not first_code == ''), ) EventManipulator.createOrUpdate(event) author = suggestion.author.get() OutgoingNotificationHelper.send_suggestion_result_email( to=author.email, subject="[TBA] Offseason Event Suggestion: {}".format(event.name), email_body="""Dear {}, Thank you for suggesting an offseason event to The Blue Alliance. Your suggestion has been approved and you can find the event at https://thebluealliance.com/event/{} If you are the event's organizer and would like to upload teams attending, match videos, or real-time match results to TBA before or during the event, you can do so using the TBA EventWizard - request auth keys here: https://www.thebluealliance.com/request/apiwrite Thanks for helping make TBA better, The Blue Alliance Admins """.format(author.nickname, event_key) ) return 'success', event_key
def _render(self, event_key, webcast_number): self.response.headers.add_header('content-type', 'application/json', charset='utf-8') output = {} if not webcast_number.isdigit(): return json.dumps(output) webcast_number = int(webcast_number) - 1 event = Event.get_by_id(event_key) if event and event.webcast: webcast = event.webcast[webcast_number] if 'type' in webcast and 'channel' in webcast: output['player'] = self._renderPlayer(webcast) else: special_webcasts_future = Sitevar.get_by_id_async('gameday.special_webcasts') special_webcasts = special_webcasts_future.get_result() if special_webcasts: special_webcasts = special_webcasts.contents else: special_webcasts = {} if event_key in special_webcasts: webcast = special_webcasts[event_key] if 'type' in webcast and 'channel' in webcast: output['player'] = self._renderPlayer(webcast) return json.dumps(output)
def _process_request(self, request, event_key): rankings = JSONRankingsParser.parse(request.body) event = Event.get_by_id(event_key) event.rankings_json = json.dumps(rankings) event.dirty = True # TODO: hacky EventManipulator.createOrUpdate(event)
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 get(self, event_key): self._require_admin() event = Event.get_by_id(event_key) if not event: self.abort(404) event.prepAwardsMatchesTeams() reg_sitevar = Sitevar.get_by_id("cmp_registration_hacks") api_keys = ApiAuthAccess.query(ApiAuthAccess.event_list == ndb.Key(Event, event_key)).fetch() event_medias = Media.query(Media.references == event.key).fetch(500) self.template_values.update({ "event": event, "medias": event_medias, "cache_key": event_controller.EventDetail('2016nyny').cache_key.format(event.key_name), "flushed": self.request.get("flushed"), "playoff_types": PlayoffType.type_names, "write_auths": api_keys, "event_sync_disable": reg_sitevar and event_key in reg_sitevar.contents.get('divisions_to_skip', []), "set_start_day_to_last": reg_sitevar and event_key in reg_sitevar.contents.get('set_start_to_last_day', []), "skip_eventteams": reg_sitevar and event_key in reg_sitevar.contents.get('skip_eventteams', []), "event_name_override": next(iter(filter(lambda e: e.get("event") == event_key, reg_sitevar.contents.get("event_name_override", []))), {}).get("name", "") }) path = os.path.join(os.path.dirname(__file__), '../../templates/admin/event_details.html') self.response.out.write(template.render(path, self.template_values))
def getEventInfo(self, event_key): """ Return an Event dict with basic information """ memcache_key = "api_event_info_%s" % event_key event_dict = memcache.get(memcache_key) if event_dict is None: event = Event.get_by_id(event_key) if event is not None: event_dict = dict() event_dict["key"] = event.key_name event_dict["year"] = event.year event_dict["event_code"] = event.event_short event_dict["name"] = event.name event_dict["short_name"] = event.short_name event_dict["location"] = event.location event_dict["official"] = event.official event_dict["facebook_eid"] = event.facebook_eid if event.start_date: event_dict["start_date"] = event.start_date.isoformat() else: event_dict["start_date"] = None if event.end_date: event_dict["end_date"] = event.end_date.isoformat() else: event_dict["end_date"] = None event.prepTeams() event_dict["teams"] = [team.key_name for team in event.teams] #TODO: Reduce caching time before 2013 season. 2592000 is one month -gregmarra if tba_config.CONFIG["memcache"]: memcache.set(memcache_key, event_dict, 2592000) return event_dict
def get(self, event_key): datafeed = DatafeedUsfirst() event = Event.get_by_id(event_key) new_awards = AwardManipulator.createOrUpdate(datafeed.getEventAwards(event)) if type(new_awards) != list: new_awards = [new_awards] # create EventTeams team_ids = set() for award in new_awards: for team in award.team_list: team_ids.add(team.id()) 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]) template_values = { 'awards': new_awards, } path = os.path.join(os.path.dirname(__file__), '../templates/datafeeds/usfirst_awards_get.html') self.response.out.write(template.render(path, template_values))
def post(self): self._require_admin() event_key = self.request.get('event_key') matches_csv = self.request.get('matches_csv') matches, _ = OffseasonMatchesParser.parse(matches_csv) event = Event.get_by_id(event_key) matches = [Match( id=Match.renderKeyName( event.key.id(), match.get("comp_level", None), match.get("set_number", 0), match.get("match_number", 0)), event=event.key, year=event.year, set_number=match.get("set_number", 0), match_number=match.get("match_number", 0), comp_level=match.get("comp_level", None), team_key_names=match.get("team_key_names", None), alliances_json=match.get("alliances_json", None) ) for match in matches] MatchManipulator.createOrUpdate(matches) self.redirect('/admin/event/{}'.format(event_key))
def post(self): self.verify_permissions() suggestion_id = int(self.request.get("suggestion_id")) verdict = self.request.get("verdict") message = self.request.get("user_message") admin_email_body = None email_body = None user = None event_key = None status = '' if verdict == "accept": status = 'accept' auth_id, user, event_key, email_body = self._process_accepted(suggestion_id, message) admin_email_body = """{} ({}) has accepted the request with the following message: {} View the key: https://www.thebluealliance.com/admin/api_auth/edit/{} """.format(self.user_bundle.account.display_name, self.user_bundle.account.email, message, auth_id) elif verdict == "reject": suggestion = Suggestion.get_by_id(suggestion_id) event_key = suggestion.contents['event_key'] user = suggestion.author.get() event = Event.get_by_id(event_key) suggestion.review_state = Suggestion.REVIEW_REJECTED suggestion.reviewer = self.user_bundle.account.key suggestion.reviewed_at = datetime.now() suggestion.put() status = 'reject' email_body = """Hi {}, We have reviewer your request for auth tokens for {} {} and have regretfully declined with the following message: {} If you have any questions, please don't hesitate to reach out to us at [email protected] Thanks, TBA Admins """.format(user.display_name, event.year, event.name, message) admin_email_body = """{} ({}) has rejected this request with the following reason: {} """.format(self.user_bundle.account.display_name, self.user_bundle.account.email, message) # Notify the user their keys are available if email_body: mail.send_mail(sender="The Blue Alliance Contact <*****@*****.**>", to=user.email, subject="The Blue Alliance Auth Tokens for {}".format(event_key), body=email_body) if admin_email_body: # Subject should match the one in suggest_apiwrite_controller subject = "Trusted API Key Request for {}".format(event_key) SuggestionNotifier.send_admin_alert_email(subject, admin_email_body) self.redirect("/suggest/apiwrite/review?success={}".format(status))
def get(self, event_key): event = Event.get_by_id(event_key) if event.event_district_enum == DistrictType.NO_DISTRICT: self.response.out.write( "Can't calculate district points for a non-district event!") return district_points = DistrictHelper.calculate_event_points(event) event.district_points_json = json.dumps(district_points) EventManipulator.createOrUpdate(event) self.response.out.write(event.district_points)
def get(self, event_key): event = Event.get_by_id(event_key) if not event: self.abort(404) return taskqueue.add( url='/tasks/math/do/playoff_advancement_update/{}'.format( event.key_name), method='GET') self.response.out.write("Enqueued time prediction for {}".format( event.key_name))
def get(self, event_key_id): self._require_admin() event = Event.get_by_id(event_key_id) if not event: self.abort(404) event.normalized_location = None LocationHelper.update_event_location(event) event = EventManipulator.createOrUpdate(event) self.response.out.write("New location: {}".format( event.normalized_location))
def post(self, event_key_id): self._require_admin() event = Event.get_by_id(event_key_id) alliance_selections_csv = self.request.get('alliance_selections_csv') alliance_selections = CSVAllianceSelectionsParser.parse( alliance_selections_csv) event_details = EventDetails(id=event_key_id, alliance_selections=alliance_selections) EventDetailsManipulator.createOrUpdate(event_details) self.redirect("/admin/event/" + event.key_name)
def post(self, event_key_id): self._require_admin() webcast = dict() webcast["type"] = self.request.get("webcast_type") webcast["channel"] = self.request.get("webcast_channel") if self.request.get("webcast_file"): webcast["file"] = self.request.get("webcast_file") event = Event.get_by_id(event_key_id) EventWebcastAdder.add_webcast(event, webcast) self.redirect("/admin/event/" + event.key_name)
def _process_request(self, request, event_key): rankings = JSONRankingsParser.parse(request.body) event = Event.get_by_id(event_key) event_details = EventDetails(id=event_key, rankings=rankings) if event_details.year == 2017: # TODO: Temporary fix. Should directly parse request into rankings2 event_details.rankings2 = RankingsHelper.convert_rankings( event_details) EventDetailsManipulator.createOrUpdate(event_details) self.response.out.write( json.dumps({'Success': "Rankings successfully updated"}))
def get(self, event_key): self._require_admin() event = Event.get_by_id(event_key) if not event: self.abort(404) return existing_event_team_keys = set( EventTeam.query(EventTeam.event == event.key).fetch( 1000, keys_only=True)) EventTeamManipulator.delete_keys(existing_event_team_keys) self.response.out.write("Deleted {} EventTeams from {}".format( len(existing_event_team_keys), event_key))
def getEventDetails(self, event_key): year = int(event_key[:4]) event_short = event_key[4:] event = Event.get_by_id(event_key) api_event_short = self._get_event_short(event_short, event) result = self._parse( self.FMS_API_EVENT_DETAILS_URL_PATTERN % (year, api_event_short), FMSAPIEventListParser(year, short=event_short)) if result: return result else: return [], []
def post(self): if self.request.get("verdict") == "accept": webcast = dict() webcast["type"] = self.request.get("webcast_type") webcast["channel"] = self.request.get("webcast_channel") if self.request.get("webcast_file"): webcast["file"] = self.request.get("webcast_file") event = Event.get_by_id(self.request.get("event_key")) suggestion_key = self.request.get("suggestion_key") suggestion = Suggestion.get_by_id(int(suggestion_key) if suggestion_key.isdigit() else suggestion_key) EventWebcastAdder.add_webcast(event, webcast) MemcacheWebcastFlusher.flush() suggestion.review_state = Suggestion.REVIEW_ACCEPTED suggestion.reviewer = self.user_bundle.account.key suggestion.reviewed_at = datetime.datetime.now() suggestion.put() self.redirect("/suggest/event/webcast/review?success=accept&event_key=%s" % event.key.id()) return elif self.request.get("verdict") == "reject": suggestion_key = self.request.get("suggestion_key") suggestion = Suggestion.get_by_id(int(suggestion_key) if suggestion_key.isdigit() else suggestion_key) suggestion.review_state = Suggestion.REVIEW_REJECTED suggestion.reviewer = self.user_bundle.account.key suggestion.reviewed_at = datetime.datetime.now() suggestion.put() self.redirect("/suggest/event/webcast/review?success=reject") return elif self.request.get("verdict") == "reject_all": suggestion_keys = self.request.get("suggestion_keys").split(",") suggestions = [Suggestion.get_by_id(int(suggestion_key) if suggestion_key.isdigit() else suggestion_key) for suggestion_key in suggestion_keys] for suggestion in suggestions: event_key = suggestion.target_key suggestion.review_state = Suggestion.REVIEW_REJECTED suggestion.reviewer = self.user_bundle.account.key suggestion.reviewed_at = datetime.datetime.now() suggestion.put() self.redirect("/suggest/event/webcast/review?success=reject_all&event_key=%s" % event_key) return self.redirect("/suggest/event/webcast/review")
def get(self, event_key): self._require_login('/account/register') self._require_registration('/account/register') # Handle wildcard for all events in a year event = None is_wildcard = False if event_key.endswith('*'): try: year = int(event_key[:-1]) except: year = None if year and year >= 1992 and year <= tba_config.MAX_YEAR: event = Event( # fake event for rendering name="ALL {} EVENTS".format(year), year=year, ) is_wildcard = True else: event = Event.get_by_id(event_key) if not event: self.abort(404) user = self.user_bundle.account.key favorite = Favorite.query(Favorite.model_key == event_key, Favorite.model_type == ModelType.EVENT, ancestor=user).get() subscription = Subscription.query( Favorite.model_key == event_key, Favorite.model_type == ModelType.EVENT, ancestor=user).get() if not favorite and not subscription: # New entry; default to being a favorite is_favorite = True else: is_favorite = favorite is not None enabled_notifications = [ (en, NotificationType.render_names[en]) for en in NotificationType.enabled_event_notifications ] self.template_values['event'] = event self.template_values['is_wildcard'] = is_wildcard self.template_values['is_favorite'] = is_favorite self.template_values['subscription'] = subscription self.template_values['enabled_notifications'] = enabled_notifications self.response.out.write( jinja2_engine.render('mytba_event.html', self.template_values))
def postUpdateHook(cls, event_details_list, updated_attr_list, is_new_list): """ To run after models have been updated """ for (event_details, updated_attrs) in zip(event_details_list, updated_attr_list): event = Event.get_by_id(event_details.key.id()) if event.within_a_day and "alliance_selections" in updated_attrs: try: NotificationHelper.send_alliance_update(event) except Exception: logging.error( "Error sending alliance update notification for {}". format(event.key_name)) logging.error(traceback.format_exc()) try: TBANSHelper.alliance_selection(event) except Exception: logging.error( "Error sending alliance update notification for {}". format(event.key_name)) logging.error(traceback.format_exc()) # Enqueue task to calculate district points try: taskqueue.add( url='/tasks/math/do/district_points_calc/{}'.format( event.key.id()), method='GET') except Exception: logging.error( "Error enqueuing district_points_calc for {}".format( event.key.id())) logging.error(traceback.format_exc()) # Enqueue task to calculate event team status try: taskqueue.add(url='/tasks/math/do/event_team_status/{}'.format( event.key.id()), method='GET') except Exception: logging.error( "Error enqueuing event_team_status for {}".format( event.key.id())) logging.error(traceback.format_exc()) try: FirebasePusher.update_event_details(event_details) except Exception: logging.warning("Firebase update_event_details failed!")
def create_target_model(self, suggestion): webcast = dict() webcast["type"] = self.request.get("webcast_type") webcast["channel"] = self.request.get("webcast_channel") if self.request.get("webcast_file"): webcast["file"] = self.request.get("webcast_file") if self.request.get('webcast_date'): webcast['date'] = self.request.get('webcast_date') event = Event.get_by_id(self.request.get("event_key")) # Defer because of transactions deferred.defer(EventWebcastAdder.add_webcast, event, webcast) deferred.defer(MemcacheWebcastFlusher.flush) return True
def get(self): self._require_registration() if not self.request.get("event_key"): self.redirect("/", abort=True) event = Event.get_by_id(self.request.get("event_key")) self.template_values.update({ "status": self.request.get("status"), "event": event, }) self.response.out.write(jinja2_engine.render('suggestions/suggest_event_webcast.html', self.template_values))
def _update_rankings(self): """ Generates and saves fake rankings """ event = Event.get_by_id('2016nytr') team_wins = defaultdict(int) team_losses = defaultdict(int) team_ties = defaultdict(int) teams = set() for match in event.matches: if match.comp_level == 'qm': for alliance in ['red', 'blue']: for team in match.alliances[alliance]['teams']: teams.add(team) if match.has_been_played: if alliance == match.winning_alliance: team_wins[team] += 1 elif match.winning_alliance == '': team_ties[team] += 1 else: team_losses[team] += 1 rankings = [] for team in sorted(teams): wins = team_wins[team] losses = team_losses[team] ties = team_ties[team] rankings.append({ 'team_key': team, 'record': { 'wins': wins, 'losses': losses, 'ties': ties, }, 'matches_played': wins + losses + ties, 'dq': 0, 'sort_orders': [2 * wins + ties, 0, 0, 0, 0], 'qual_average': None, }) rankings = sorted(rankings, key=lambda r: -r['sort_orders'][0]) for i, ranking in enumerate(rankings): ranking['rank'] = i + 1 EventDetailsManipulator.createOrUpdate( EventDetails( id='2016nytr', rankings2=rankings, ))
def getMatches(self, event_key): year = int(event_key[:4]) event_short = event_key[4:] event = Event.get_by_id(event_key) hs_parser = FMSAPIHybridScheduleParser(year, event_short) detail_parser = FMSAPIMatchDetailsParser(year, event_short) qual_matches_future = self._parse_async( self.FMS_API_HYBRID_SCHEDULE_QUAL_URL_PATTERN % (year, self._get_event_short(event_short, event)), hs_parser) playoff_matches_future = self._parse_async( self.FMS_API_HYBRID_SCHEDULE_PLAYOFF_URL_PATTERN % (year, self._get_event_short(event_short, event)), hs_parser) qual_details_future = self._parse_async( self.FMS_API_MATCH_DETAILS_QUAL_URL_PATTERN % (year, self._get_event_short(event_short, event)), detail_parser) playoff_details_future = self._parse_async( self.FMS_API_MATCH_DETAILS_PLAYOFF_URL_PATTERN % (year, self._get_event_short(event_short, event)), detail_parser) # Organize matches by key matches_by_key = {} qual_matches = qual_matches_future.get_result() if qual_matches is not None: for match in qual_matches[0]: matches_by_key[match.key.id()] = match playoff_matches = playoff_matches_future.get_result() remapped_playoff_matches = {} if playoff_matches is not None: for match in playoff_matches[0]: matches_by_key[match.key.id()] = match remapped_playoff_matches = playoff_matches[1] # Add details to matches based on key qual_details = qual_details_future.get_result() qual_details_items = qual_details.items( ) if qual_details is not None else [] playoff_details = playoff_details_future.get_result() playoff_details_items = playoff_details.items( ) if playoff_details is not None else [] for match_key, match_details in qual_details_items + playoff_details_items: # Deal with remapped playoff matches, defaulting to the original match key match_key = remapped_playoff_matches.get(match_key, match_key) if match_key in matches_by_key: matches_by_key[match_key].score_breakdown_json = json.dumps( match_details) return filter( lambda m: not FMSAPIHybridScheduleParser.is_blank_match(m), matches_by_key.values())
def postUpdateHook(cls, event_details_list, updated_attr_list, is_new_list): """ To run after models have been updated """ for (event_details, updated_attrs) in zip(event_details_list, updated_attr_list): event = Event.get_by_id(event_details.key.id()) try: if event.within_a_day and "alliance_selections" in updated_attrs: # Send updated alliances notification logging.info("Sending alliance notifications for {}".format(event.key_name)) NotificationHelper.send_alliance_update(event) except Exception: logging.error("Error sending alliance update notification for {}".format(event.key_name)) logging.error(traceback.format_exc())
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 get(self, event_key): self._require_admin() event = Event.get_by_id(event_key) self.template_values.update({ "event": event, 'alliance_selections': json.dumps(event.alliance_selections), 'rankings': json.dumps(event.rankings), "playoff_types": PlayoffType.type_names, }) path = os.path.join(os.path.dirname(__file__), '../../templates/admin/event_edit.html') self.response.out.write(template.render(path, self.template_values))
def get(self, event_key): df = DatafeedOffseason() event = Event.get_by_id(event_key) url = self.request.get('url') new_matches = MatchManipulator.createOrUpdate(df.getMatches(event, url)) template_values = { 'matches': new_matches, } path = os.path.join(os.path.dirname(__file__), '../templates/datafeeds/offseason_matches_get.html') self.response.out.write(template.render(path, template_values))
def post(self): self._require_admin() event_key = self.request.get("event_key").strip() event = Event.get_by_id(event_key) if not event: self.redirect("/admin/event/" + event.key_name) return if event.playoff_type != PlayoffType.ROUND_ROBIN_6_TEAM: logging.warning("Can't set advancement for non-round robin events") self.redirect("/admin/event/" + event.key_name) return advancement_csv = self.request.get("advancement_csv") comp_level = self.request.get("comp_level") matches_per_team = int(self.request.get("num_matches")) if comp_level not in Match.ELIM_LEVELS: logging.warning("Bad comp level: {}".format(comp_level)) self.redirect("/admin/event/" + event.key_name) return parsed_advancement = CSVAdvancementParser.parse( advancement_csv, matches_per_team) advancement = PlayoffAdvancementHelper.generatePlayoffAdvancementFromCSV( event, parsed_advancement, comp_level) cleaned_matches = MatchHelper.deleteInvalidMatches( event.matches, event) matches = MatchHelper.organizeMatches(cleaned_matches) bracket_table = PlayoffAdvancementHelper.generateBracket( matches, event, event.alliance_selections) comp_levels = bracket_table.keys() for comp_level in comp_levels: if comp_level != 'f': del bracket_table[comp_level] existing_details = EventDetails.get_by_id(event.key_name) new_advancement = existing_details.playoff_advancement if existing_details and existing_details.playoff_advancement else {} new_advancement.update(advancement) event_details = EventDetails( id=event.key_name, playoff_advancement={ 'advancement': new_advancement, 'bracket': bracket_table, }, ) EventDetailsManipulator.createOrUpdate(event_details) self.redirect("/admin/event/" + event.key_name) return
def post(self, event_key_id): self._require_admin() event = Event.get_by_id(event_key_id) teams_csv = self.request.get('teams_csv') team_numbers = CSVTeamsParser.parse(teams_csv) event_teams = [] for team_number in team_numbers: event_teams.append(ndb.Key(EventTeam, '{}_frc{}'.format(event.key.id(), team_number))) EventTeamManipulator.delete_keys(event_teams) self.redirect("/admin/event/" + event.key_name)
def get(self, event_key): self._require_admin() event = Event.get_by_id(event_key) if not event: self.abort(404) event.prepAwardsMatchesTeams() self.template_values.update({ "event": event }) path = os.path.join(os.path.dirname(__file__), '../../templates/admin/event_details.html') self.response.out.write(template.render(path, self.template_values))
def get(self, event_key): event = Event.get_by_id(event_key) if not event: self.abort(404) matches = event.matches if not matches: return timezone = pytz.timezone(event.timezone_id) played_matches = MatchHelper.recentMatches(matches, num=0) unplayed_matches = MatchHelper.upcomingMatches(matches, num=10) MatchTimePredictionHelper.predict_future_matches( played_matches, unplayed_matches, timezone, event.within_a_day)
def get(self): try: q = self.request.get("q") logging.info("search query: %s" % q) if q.isdigit(): team_id = "frc%s" % q team = Team.get_by_id(team_id) if team: self.redirect(team.details_url) return None elif q[:4].isdigit(): # Check for event key event = Event.get_by_id(q) if event: self.redirect(event.details_url) return None else: # Check for event short year = datetime.datetime.now().year # default to current year event = Event.get_by_id('{}{}'.format(year, q)) if event: self.redirect(event.details_url) return None except Exception, e: logging.warning("warning: %s" % e)
def get(self, type): self._require_registration('/account/') user_id = self.user_bundle.account.key.id() logging.info("Sending for {}".format(type)) try: type = int(type) except ValueError: # Not passed a valid int, just stop here logging.info("Invalid number passed") self.redirect('/apidocs/webhooks') return event = Event.get_by_id('2014necmp') match = Match.get_by_id('2014necmp_f1m1') if type == NotificationType.UPCOMING_MATCH: notification = UpcomingMatchNotification(match, event) elif type == NotificationType.MATCH_SCORE: notification = MatchScoreNotification(match) elif type == NotificationType.LEVEL_STARTING: notification = CompLevelStartingNotification(match, event) elif type == NotificationType.ALLIANCE_SELECTION: notification = AllianceSelectionNotification(event) elif type == NotificationType.AWARDS: notification = AwardsUpdatedNotification(event) elif type == NotificationType.MEDIA_POSTED: # Not implemented yet pass elif type == NotificationType.DISTRICT_POINTS_UPDATED: notification = DistrictPointsUpdatedNotification('2014ne') elif type == NotificationType.SCHEDULE_UPDATED: notification = ScheduleUpdatedNotification(event, match) elif type == NotificationType.FINAL_RESULTS: # Not implemented yet pass else: # Not passed a valid int, return self.redirect('/apidocs/webhooks') return keys = PushHelper.get_client_ids_for_users([user_id]) logging.info("Keys: {}".format(keys)) if notification: # This page should not push notifications to the firebase queue # Nor should its notifications be tracked in analytics notification.send(keys, push_firebase=False, track_call=False) self.redirect('/apidocs/webhooks')
def getEventRankings(self, event_key): year = int(event_key[:4]) event_short = event_key[4:] event = Event.get_by_id(event_key) result = self._parse( self.FMS_API_EVENT_RANKINGS_URL_PATTERN % (year, self._get_event_short(event_short, event)), [ FMSAPIEventRankingsParser(year), FMSAPIEventRankings2Parser(year) ]) if result: return result else: return None, None
def post(self, event_key_id): self._require_admin() event = Event.get_by_id(event_key_id) alliance_selections_csv = self.request.get('alliance_selections_csv') alliance_selections = CSVAllianceSelectionsParser.parse(alliance_selections_csv) if alliance_selections and event.alliance_selections != alliance_selections: event.alliance_selections_json = json.dumps(alliance_selections) event._alliance_selections = None event.dirty = True EventManipulator.createOrUpdate(event) self.redirect("/admin/event/" + event.key_name)
def post(self, event_key): self._require_admin() event = Event.get_by_id(event_key) if not event: self.redirect("/admin/event/" + event.key_name) return details = EventDetails.get_by_id(event.key_name) if details: details.playoff_advancement = {} EventDetailsManipulator.createOrUpdate(details) self.redirect("/admin/event/" + event.key_name) return
def _render(self, event_key): event = Event.get_by_id(event_key) if not event: self.abort(404) matches = MatchHelper.organizeMatches(event.matches) self.template_values.update({ "event": event, "matches": matches, "datetime": datetime.datetime.now() }) self.response.headers['content-type'] = 'application/xml; charset=UTF-8' return jinja2_engine.render('event_rss.xml', self.template_values)
def get(self, event_key): event = Event.get_by_id(event_key) matchstats_dict = MatchstatsHelper.calculate_matchstats( event.matches, event.year) if any([v != {} for v in matchstats_dict.values()]): pass else: logging.warn( "Matchstat calculation for {} failed!".format(event_key)) matchstats_dict = None predictions_dict = None if event.year in { 2016, 2017, 2018, 2019, 2020 } and event.event_type_enum in EventType.SEASON_EVENT_TYPES or event.enable_predictions: sorted_matches = MatchHelper.play_order_sort_matches(event.matches) match_predictions, match_prediction_stats, stat_mean_vars = PredictionHelper.get_match_predictions( sorted_matches) ranking_predictions, ranking_prediction_stats = PredictionHelper.get_ranking_predictions( sorted_matches, match_predictions) predictions_dict = { 'match_predictions': match_predictions, 'match_prediction_stats': match_prediction_stats, 'stat_mean_vars': stat_mean_vars, 'ranking_predictions': ranking_predictions, 'ranking_prediction_stats': ranking_prediction_stats } event_insights = EventInsightsHelper.calculate_event_insights( event.matches, event.year) event_details = EventDetails( id=event_key, matchstats=matchstats_dict, predictions=predictions_dict, insights=event_insights, ) EventDetailsManipulator.createOrUpdate(event_details) template_values = { 'matchstats_dict': matchstats_dict, } if 'X-Appengine-Taskname' not in self.request.headers: # Only write out if not in taskqueue path = os.path.join(os.path.dirname(__file__), '../templates/math/event_matchstats_do.html') self.response.out.write(template.render(path, template_values))