def test_team_updated(self):
        affected_refs = {
            'key': {ndb.Key(Team, 'frc254'),
                    ndb.Key(Team, 'frc604')},
        }
        cache_keys = [
            q.cache_key
            for q in get_affected_queries.team_updated(affected_refs)
        ]

        self.assertEqual(len(cache_keys), 16)
        self.assertTrue(TeamQuery('frc254').cache_key in cache_keys)
        self.assertTrue(TeamQuery('frc604').cache_key in cache_keys)
        self.assertTrue(TeamListQuery(0).cache_key in cache_keys)
        self.assertTrue(TeamListQuery(1).cache_key in cache_keys)
        self.assertTrue(TeamListYearQuery(2015, 0).cache_key in cache_keys)
        self.assertTrue(TeamListYearQuery(2015, 1).cache_key in cache_keys)
        self.assertTrue(TeamListYearQuery(2010, 1).cache_key in cache_keys)
        self.assertTrue(DistrictTeamsQuery('2015fim').cache_key in cache_keys)
        self.assertTrue(DistrictTeamsQuery('2015mar').cache_key in cache_keys)
        self.assertTrue(DistrictTeamsQuery('2016ne').cache_key in cache_keys)
        self.assertTrue(EventTeamsQuery('2015casj').cache_key in cache_keys)
        self.assertTrue(EventTeamsQuery('2015cama').cache_key in cache_keys)
        self.assertTrue(EventTeamsQuery('2010cama').cache_key in cache_keys)
        self.assertTrue(
            EventEventTeamsQuery('2015casj').cache_key in cache_keys)
        self.assertTrue(
            EventEventTeamsQuery('2015cama').cache_key in cache_keys)
        self.assertTrue(
            EventEventTeamsQuery('2010cama').cache_key in cache_keys)
    def test_districtteam_updated(self):
        affected_refs = {
            'district_key': {ndb.Key(District, '2015fim'), ndb.Key(District, '2015mar')},
            'team': {ndb.Key(Team, 'frc254'), ndb.Key(Team, 'frc604')}
        }
        cache_keys = [q.cache_key for q in get_affected_queries.districtteam_updated(affected_refs)]

        self.assertEqual(len(cache_keys), 4)
        self.assertTrue(DistrictTeamsQuery('2015fim').cache_key in cache_keys)
        self.assertTrue(DistrictTeamsQuery('2015mar').cache_key in cache_keys)
        self.assertTrue(TeamDistrictsQuery('frc254').cache_key in cache_keys)
        self.assertTrue(TeamDistrictsQuery('frc604').cache_key in cache_keys)
예제 #3
0
def team_updated(affected_refs):
    team_keys = filter(None, affected_refs['key'])

    event_team_keys_future = EventTeam.query(
        EventTeam.team.IN([team_key for team_key in team_keys
                           ])).fetch_async(None, keys_only=True)
    district_team_keys_future = DistrictTeam.query(
        DistrictTeam.team.IN([team_key for team_key in team_keys
                              ])).fetch_async(None, keys_only=True)

    queries_and_keys = []
    for team_key in team_keys:
        queries_and_keys.append((TeamQuery(team_key.id())))
        page_num = _get_team_page_num(team_key.id())
        queries_and_keys.append((TeamListQuery(page_num)))

    for et_key in event_team_keys_future.get_result():
        year = int(et_key.id()[:4])
        event_key = et_key.id().split('_')[0]
        page_num = _get_team_page_num(et_key.id().split('_')[1])
        queries_and_keys.append((TeamListYearQuery(year, page_num)))
        queries_and_keys.append((EventTeamsQuery(event_key)))

    for dt_key in district_team_keys_future.get_result():
        district_key = dt_key.id().split('_')[0]
        queries_and_keys.append((DistrictTeamsQuery(district_key)))

    return queries_and_keys
예제 #4
0
    def _render(self, district_abbrev, year=None):
        self._set_district(district_abbrev)

        district_teams = DistrictTeamsQuery('{}{}'.format(self.year, self.district_abbrev)).fetch()

        district_teams_dict = [ModelToDict.teamConverter(team) for team in district_teams]

        return json.dumps(district_teams_dict, ensure_ascii=True)
예제 #5
0
 def _render(self, district_key, model_type=None):
     teams, self._last_modified = DistrictTeamsQuery(district_key).fetch(
         dict_version=3, return_updated=True)
     if model_type is not None:
         teams = filter_team_properties(teams, model_type)
     return json.dumps(teams,
                       ensure_ascii=True,
                       indent=True,
                       sort_keys=True)
예제 #6
0
    def _render(self, district_abbrev, year=None):
        self._set_district(district_abbrev, self.year)

        if self.year < 2009:
            return json.dumps([], ensure_ascii=True)

        events_future = DistrictEventsQuery(
            District.renderKeyName(self.year, district_abbrev)).fetch_async()
        district_teams_future = DistrictTeamsQuery("{}{}".format(
            year, district_abbrev)).fetch_async()

        events = events_future.get_result()
        if not events:
            return json.dumps([], ensure_ascii=True)
        EventHelper.sort_events(events)

        team_totals = DistrictHelper.calculate_rankings(
            events, district_teams_future.get_result(), self.year)

        rankings = []

        current_rank = 1
        for key, points in team_totals:
            point_detail = {}
            point_detail["rank"] = current_rank
            point_detail["team_key"] = key
            point_detail["event_points"] = {}
            for event in points["event_points"]:
                event_key = event[0].key_name
                point_detail["event_points"][event_key] = event[1]
                event_details = Event.get_by_id(event_key)
                point_detail["event_points"][event[0].key_name][
                    'district_cmp'] = True if event_details.event_type_enum == EventType.DISTRICT_CMP else False

            if "rookie_bonus" in points:
                point_detail["rookie_bonus"] = points["rookie_bonus"]
            else:
                point_detail["rookie_bonus"] = 0
            point_detail["point_total"] = points["point_total"]
            rankings.append(point_detail)
            current_rank += 1

        return json.dumps(rankings)
예제 #7
0
def districtteam_updated(affected_refs):
    district_keys = filter(None, affected_refs['district_key'])
    team_keys = filter(None, affected_refs['team'])

    queries_and_keys = []
    for district_key in district_keys:
        queries_and_keys.append(DistrictTeamsQuery(district_key.id()))

    for team_key in team_keys:
        queries_and_keys.append(TeamDistrictsQuery(team_key.id()))

    return queries_and_keys
    def _render(self, district_abbrev, year=None):
        self._set_district(district_abbrev, self.year)

        if self.year < 2009:
            return json.dumps([], ensure_ascii=True)

        events_future = DistrictEventsQuery(District.renderKeyName(self.year, district_abbrev)).fetch_async()
        district_teams_future = DistrictTeamsQuery("{}{}".format(year, district_abbrev)).fetch_async()

        events = events_future.get_result()
        if not events:
            return json.dumps([], ensure_ascii=True)
        EventHelper.sort_events(events)

        team_totals = DistrictHelper.calculate_rankings(events, district_teams_future.get_result(), self.year)

        rankings = []

        current_rank = 1
        for key, points in team_totals:
            point_detail = {}
            point_detail["rank"] = current_rank
            point_detail["team_key"] = key
            point_detail["event_points"] = {}
            for event in points["event_points"]:
                event_key = event[0].key_name
                point_detail["event_points"][event_key] = event[1]
                event_details = Event.get_by_id(event_key)
                point_detail["event_points"][event[0].key_name]['district_cmp'] = True if event_details.event_type_enum == EventType.DISTRICT_CMP else False

            if "rookie_bonus" in points:
                point_detail["rookie_bonus"] = points["rookie_bonus"]
            else:
                point_detail["rookie_bonus"] = 0
            point_detail["point_total"] = points["point_total"]
            rankings.append(point_detail)
            current_rank += 1

        return json.dumps(rankings)
예제 #9
0
    def get(self, district_key):
        district = District.get_by_id(district_key)
        if not district:
            self.response.out.write(
                "District {} does not exist!".format(district_key))
            return

        events_future = DistrictEventsQuery(district_key).fetch_async()
        teams_future = DistrictTeamsQuery(district_key).fetch_async()

        events = events_future.get_result()
        for event in events:
            event.prep_details()
        EventHelper.sort_events(events)
        team_totals = DistrictHelper.calculate_rankings(
            events, teams_future, district.year)

        rankings = []
        current_rank = 1
        for key, points in team_totals:
            point_detail = {}
            point_detail["rank"] = current_rank
            point_detail["team_key"] = key
            point_detail["event_points"] = []
            for event, event_points in points["event_points"]:
                event_points['event_key'] = event.key.id()
                event_points['district_cmp'] = (
                    event.event_type_enum == EventType.DISTRICT_CMP or
                    event.event_type_enum == EventType.DISTRICT_CMP_DIVISION)
                point_detail["event_points"].append(event_points)

            point_detail["rookie_bonus"] = points.get("rookie_bonus", 0)
            point_detail["point_total"] = points["point_total"]
            rankings.append(point_detail)
            current_rank += 1

        if rankings:
            district.rankings = rankings
            DistrictManipulator.createOrUpdate(district)

        if 'X-Appengine-Taskname' not in self.request.headers:  # Only write out if not in taskqueue
            self.response.out.write(
                "Finished calculating rankings for: {}".format(district_key))
예제 #10
0
    def _render(self, district_abbrev, year=None, explicit_year=False):
        district = DistrictQuery('{}{}'.format(year, district_abbrev)).fetch()
        if not district:
            self.abort(404)

        events_future = DistrictEventsQuery(district.key_name).fetch_async()

        # needed for district teams
        district_teams_future = DistrictTeamsQuery(district.key_name).fetch_async()

        # needed for valid_years
        history_future = DistrictHistoryQuery(district.abbreviation).fetch_async()

        # needed for valid_districts
        districts_in_year_future = DistrictsInYearQuery(district.year).fetch_async()

        # needed for active team statuses
        live_events = []
        if year == datetime.datetime.now().year:  # Only show active teams for current year
            live_events = EventHelper.getWeekEvents()
        live_eventteams_futures = []
        for event in live_events:
            live_eventteams_futures.append(EventTeamsQuery(event.key_name).fetch_async())

        events = events_future.get_result()
        EventHelper.sort_events(events)
        events_by_key = {}
        for event in events:
            events_by_key[event.key.id()] = event
        week_events = EventHelper.groupByWeek(events)

        valid_districts = set()
        districts_in_year = districts_in_year_future.get_result()
        for dist in districts_in_year:
            valid_districts.add((dist.display_name, dist.abbreviation))
        valid_districts = sorted(valid_districts, key=lambda (name, _): name)

        teams = TeamHelper.sortTeams(district_teams_future.get_result())
        team_keys = set([t.key.id() for t in teams])

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

        # Currently Competing Team Status
        event_team_keys = []
        for event, teams_future in zip(live_events, live_eventteams_futures):
            for team in teams_future.get_result():
                if team.key.id() in team_keys:
                    event_team_keys.append(ndb.Key(EventTeam, '{}_{}'.format(event.key.id(), team.key.id())))  # Should be in context cache

        ndb.get_multi(event_team_keys)  # Warms context cache
        live_events_with_teams = []
        for event, teams_future in zip(live_events, live_eventteams_futures):
            teams_and_statuses = []
            has_teams = False
            for team in teams_future.get_result():
                if team.key.id() in team_keys:
                    has_teams = True
                    event_team = EventTeam.get_by_id('{}_{}'.format(event.key.id(), team.key.id()))  # Should be in context cache
                    status_str = {
                        'alliance': EventTeamStatusHelper.generate_team_at_event_alliance_status_string(team.key.id(), event_team.status),
                        'playoff': EventTeamStatusHelper.generate_team_at_event_playoff_status_string(team.key.id(), event_team.status),
                    }
                    teams_and_statuses.append((
                        team,
                        event_team.status,
                        status_str
                    ))
            if has_teams:
                teams_and_statuses.sort(key=lambda x: x[0].team_number)
                live_events_with_teams.append((event, teams_and_statuses))
        live_events_with_teams.sort(key=lambda x: x[0].name)
        live_events_with_teams.sort(key=lambda x: EventHelper.distantFutureIfNoStartDate(x[0]))
        live_events_with_teams.sort(key=lambda x: EventHelper.distantFutureIfNoEndDate(x[0]))

        # Get valid years
        district_history = history_future.get_result()
        valid_years = map(lambda d: d.year, district_history)
        valid_years = sorted(valid_years)

        self.template_values.update({
            'explicit_year': explicit_year,
            'year': year,
            'valid_years': valid_years,
            'valid_districts': valid_districts,
            'district_name': district.display_name,
            'district_abbrev': district_abbrev,
            'week_events': week_events,
            'events_by_key': events_by_key,
            'rankings': district.rankings,
            'advancement': district.advancement,
            'teams_a': teams_a,
            'teams_b': teams_b,
            'live_events_with_teams': live_events_with_teams,
        })

        return jinja2_engine.render('district_details.html', self.template_values)
    def _render(self, district_abbrev, year=None, explicit_year=False):
        district_type = DistrictType.abbrevs[district_abbrev]

        event_keys = Event.query(Event.year == year, Event.event_district_enum == district_type).fetch(None, keys_only=True)
        if not event_keys:
            self.abort(404)

        # needed for district teams
        district_key = '{}{}'.format(year, district_abbrev)
        district_teams_future = DistrictTeamsQuery(district_key).fetch_async()

        # needed for valid_years
        all_cmp_event_keys_future = Event.query(Event.event_district_enum == district_type, Event.event_type_enum == EventType.DISTRICT_CMP).fetch_async(None, keys_only=True)

        # needed for valid_districts
        district_cmp_keys_future = Event.query(Event.year == year, Event.event_type_enum == EventType.DISTRICT_CMP).fetch_async(None, keys_only=True)  # to compute valid_districts

        event_futures = ndb.get_multi_async(event_keys)
        event_team_keys_future = EventTeam.query(EventTeam.event.IN(event_keys)).fetch_async(None, keys_only=True)
        team_futures = ndb.get_multi_async(set([ndb.Key(Team, et_key.id().split('_')[1]) for et_key in event_team_keys_future.get_result()]))

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

        district_cmp_futures = ndb.get_multi_async(district_cmp_keys_future.get_result())

        team_totals = DistrictHelper.calculate_rankings(events, team_futures, year)

        valid_districts = set()
        for district_cmp_future in district_cmp_futures:
            district_cmp = district_cmp_future.get_result()
            cmp_dis_type = district_cmp.event_district_enum
            if cmp_dis_type is None:
                logging.warning("District event {} has unknown district type!".format(district_cmp.key.id()))
            else:
                valid_districts.add((DistrictType.type_names[cmp_dis_type], DistrictType.type_abbrevs[cmp_dis_type]))
        valid_districts = sorted(valid_districts, key=lambda (name, _): name)

        teams = TeamHelper.sortTeams(district_teams_future.get_result())

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

        self.template_values.update({
            'explicit_year': explicit_year,
            'year': year,
            'valid_years': sorted(set([int(event_key.id()[:4]) for event_key in all_cmp_event_keys_future.get_result()])),
            'valid_districts': valid_districts,
            'district_name': DistrictType.type_names[district_type],
            'district_abbrev': district_abbrev,
            'events': events,
            'team_totals': team_totals,
            'teams_a': teams_a,
            'teams_b': teams_b,
        })

        path = os.path.join(os.path.dirname(__file__), '../templates/district_details.html')
        return template.render(path, self.template_values)
    def _render(self, district_abbrev, year=None, explicit_year=False):
        district_type = DistrictType.abbrevs[district_abbrev]

        event_keys = Event.query(Event.year == year, Event.event_district_enum == district_type).fetch(None, keys_only=True)
        if not event_keys:
            self.abort(404)

        # needed for district teams
        district_key = '{}{}'.format(year, district_abbrev)
        district_teams_future = DistrictTeamsQuery(district_key).fetch_async()

        # needed for valid_years
        all_cmp_event_keys_future = Event.query(Event.event_district_enum == district_type, Event.event_type_enum == EventType.DISTRICT_CMP).fetch_async(None, keys_only=True)

        # needed for valid_districts
        district_cmp_keys_future = Event.query(Event.year == year, Event.event_type_enum == EventType.DISTRICT_CMP).fetch_async(None, keys_only=True)  # to compute valid_districts

        # Needed for active team statuses
        live_events = EventHelper.getWeekEvents()
        live_eventteams_futures = []
        for event in live_events:
            live_eventteams_futures.append(EventTeamsQuery(event.key_name).fetch_async())

        event_futures = ndb.get_multi_async(event_keys)
        event_team_keys_future = EventTeam.query(EventTeam.event.IN(event_keys)).fetch_async(None, keys_only=True)
        team_futures = ndb.get_multi_async(set([ndb.Key(Team, et_key.id().split('_')[1]) for et_key in event_team_keys_future.get_result()]))

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

        district_cmp_futures = ndb.get_multi_async(district_cmp_keys_future.get_result())

        team_totals = DistrictHelper.calculate_rankings(events, team_futures, year)

        valid_districts = set()
        for district_cmp_future in district_cmp_futures:
            district_cmp = district_cmp_future.get_result()
            cmp_dis_type = district_cmp.event_district_enum
            if cmp_dis_type is None:
                logging.warning("District event {} has unknown district type!".format(district_cmp.key.id()))
            else:
                valid_districts.add((DistrictType.type_names[cmp_dis_type], DistrictType.type_abbrevs[cmp_dis_type]))
        valid_districts = sorted(valid_districts, key=lambda (name, _): name)

        teams = TeamHelper.sortTeams(district_teams_future.get_result())

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

        # Currently Competing Team Status
        live_events_with_teams = EventTeamStatusHelper.buildEventTeamStatus(live_events, live_eventteams_futures, teams)
        live_events_with_teams.sort(key=lambda x: x[0].name)

        self.template_values.update({
            'explicit_year': explicit_year,
            'year': year,
            'valid_years': sorted(set([int(event_key.id()[:4]) for event_key in all_cmp_event_keys_future.get_result()])),
            'valid_districts': valid_districts,
            'district_name': DistrictType.type_names[district_type],
            'district_abbrev': district_abbrev,
            'events': events,
            'team_totals': team_totals,
            'teams_a': teams_a,
            'teams_b': teams_b,
            'live_events_with_teams': live_events_with_teams,
        })

        path = os.path.join(os.path.dirname(__file__), '../templates/district_details.html')
        return template.render(path, self.template_values)
예제 #13
0
    def _render(self, district_abbrev, year=None, explicit_year=False):
        district = DistrictQuery('{}{}'.format(year, district_abbrev)).fetch()
        if not district:
            self.abort(404)

        events_future = DistrictEventsQuery(district.key_name).fetch_async()

        # needed for district teams
        district_teams_future = DistrictTeamsQuery(district.key_name).fetch_async()

        # needed for valid_years
        history_future = DistrictHistoryQuery(district.abbreviation).fetch_async()

        # needed for valid_districts
        districts_in_year_future = DistrictsInYearQuery(district.year).fetch_async()

        # Temp disabled on 2017-02-18 -fangeugene
        # # Needed for active team statuses
        # live_events = []
        # if year == datetime.datetime.now().year:  # Only show active teams for current year
        #     live_events = EventHelper.getWeekEvents()
        # live_eventteams_futures = []
        # for event in live_events:
        #     live_eventteams_futures.append(EventTeamsQuery(event.key_name).fetch_async())

        events = events_future.get_result()
        EventHelper.sort_events(events)
        events_by_key = {}
        for event in events:
            events_by_key[event.key.id()] = event
        week_events = EventHelper.groupByWeek(events)

        valid_districts = set()
        districts_in_year = districts_in_year_future.get_result()
        for dist in districts_in_year:
            valid_districts.add((dist.display_name, dist.abbreviation))
        valid_districts = sorted(valid_districts, key=lambda (name, _): name)

        teams = TeamHelper.sortTeams(district_teams_future.get_result())

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

        # Temp disabled on 2017-02-18 -fangeugene
        # # Currently Competing Team Status
        # live_events_with_teams = EventTeamStatusHelper.buildEventTeamStatus(live_events, live_eventteams_futures, teams)
        # live_events_with_teams.sort(key=lambda x: x[0].name)

        # Get valid years
        district_history = history_future.get_result()
        valid_years = map(lambda d: d.year, district_history)
        valid_years = sorted(valid_years)

        self.template_values.update({
            'explicit_year': explicit_year,
            'year': year,
            'valid_years': valid_years,
            'valid_districts': valid_districts,
            'district_name': district.display_name,
            'district_abbrev': district_abbrev,
            'week_events': week_events,
            'events_by_key': events_by_key,
            'rankings': district.rankings,
            'teams_a': teams_a,
            'teams_b': teams_b,
            # 'live_events_with_teams': live_events_with_teams,  # Temp disabled on 2017-02-18 -fangeugene
        })

        return jinja2_engine.render('district_details.html', self.template_values)
    def _render(self, district_abbrev, year=None, explicit_year=False):
        district = DistrictQuery('{}{}'.format(year, district_abbrev)).fetch()
        if not district:
            self.abort(404)

        events_future = DistrictEventsQuery(district.key_name).fetch_async()

        # needed for district teams
        district_teams_future = DistrictTeamsQuery(
            district.key_name).fetch_async()

        # needed for valid_years
        history_future = DistrictHistoryQuery(
            district.abbreviation).fetch_async()

        # needed for valid_districts
        districts_in_year_future = DistrictsInYearQuery(
            district.year).fetch_async()

        # needed for active team statuses
        live_events = []
        if year == datetime.datetime.now(
        ).year:  # Only show active teams for current year
            live_events = EventHelper.getWeekEvents()
        live_eventteams_futures = []
        for event in live_events:
            live_eventteams_futures.append(
                EventTeamsQuery(event.key_name).fetch_async())

        events = events_future.get_result()
        EventHelper.sort_events(events)
        events_by_key = {}
        for event in events:
            events_by_key[event.key.id()] = event
        week_events = EventHelper.groupByWeek(events)

        valid_districts = set()
        districts_in_year = districts_in_year_future.get_result()
        for dist in districts_in_year:
            valid_districts.add((dist.display_name, dist.abbreviation))
        valid_districts = sorted(valid_districts, key=lambda (name, _): name)

        teams = TeamHelper.sortTeams(district_teams_future.get_result())
        team_keys = set([t.key.id() for t in teams])

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

        # Currently Competing Team Status
        event_team_keys = []
        for event, teams_future in zip(live_events, live_eventteams_futures):
            for team in teams_future.get_result():
                if team.key.id() in team_keys:
                    event_team_keys.append(
                        ndb.Key(EventTeam, '{}_{}'.format(
                            event.key.id(),
                            team.key.id())))  # Should be in context cache

        ndb.get_multi(event_team_keys)  # Warms context cache
        live_events_with_teams = []
        for event, teams_future in zip(live_events, live_eventteams_futures):
            teams_and_statuses = []
            has_teams = False
            for team in teams_future.get_result():
                if team.key.id() in team_keys:
                    has_teams = True
                    event_team = EventTeam.get_by_id('{}_{}'.format(
                        event.key.id(),
                        team.key.id()))  # Should be in context cache
                    status_str = {
                        'alliance':
                        EventTeamStatusHelper.
                        generate_team_at_event_alliance_status_string(
                            team.key.id(), event_team.status),
                        'playoff':
                        EventTeamStatusHelper.
                        generate_team_at_event_playoff_status_string(
                            team.key.id(), event_team.status),
                    }
                    teams_and_statuses.append(
                        (team, event_team.status, status_str))
            if has_teams:
                teams_and_statuses.sort(key=lambda x: x[0].team_number)
                live_events_with_teams.append((event, teams_and_statuses))
        live_events_with_teams.sort(key=lambda x: x[0].name)
        live_events_with_teams.sort(
            key=lambda x: EventHelper.distantFutureIfNoStartDate(x[0]))
        live_events_with_teams.sort(
            key=lambda x: EventHelper.distantFutureIfNoEndDate(x[0]))

        # Get valid years
        district_history = history_future.get_result()
        valid_years = map(lambda d: d.year, district_history)
        valid_years = sorted(valid_years)

        self.template_values.update({
            'explicit_year':
            explicit_year,
            'year':
            year,
            'valid_years':
            valid_years,
            'valid_districts':
            valid_districts,
            'district_name':
            district.display_name,
            'district_abbrev':
            district_abbrev,
            'week_events':
            week_events,
            'events_by_key':
            events_by_key,
            'rankings':
            district.rankings,
            'advancement':
            district.advancement,
            'teams_a':
            teams_a,
            'teams_b':
            teams_b,
            'live_events_with_teams':
            live_events_with_teams,
        })

        return jinja2_engine.render('district_details.html',
                                    self.template_values)
    def _render(self, district_abbrev, year=None, explicit_year=False):
        district_type = DistrictType.abbrevs[district_abbrev]

        event_keys = Event.query(Event.year == year, Event.event_district_enum == district_type).fetch(None, keys_only=True)
        if not event_keys:
            self.abort(404)

        # needed for district teams
        district_key = '{}{}'.format(year, district_abbrev)
        district_teams_future = DistrictTeamsQuery(district_key).fetch_async()

        # needed for valid_years
        all_cmp_event_keys_future = Event.query(Event.event_district_enum == district_type, Event.event_type_enum == EventType.DISTRICT_CMP).fetch_async(None, keys_only=True)

        # needed for valid_districts
        district_cmp_keys_future = Event.query(Event.year == year, Event.event_type_enum == EventType.DISTRICT_CMP).fetch_async(None, keys_only=True)  # to compute valid_districts

        # Needed for active team statuses
        live_events = []
        if year == datetime.datetime.now().year:  # Only show active teams for current year
            live_events = EventHelper.getWeekEvents()
        live_eventteams_futures = []
        for event in live_events:
            live_eventteams_futures.append(EventTeamsQuery(event.key_name).fetch_async())

        event_futures = ndb.get_multi_async(event_keys)
        event_team_keys_future = EventTeam.query(EventTeam.event.IN(event_keys)).fetch_async(None, keys_only=True)
        team_futures = ndb.get_multi_async(set([ndb.Key(Team, et_key.id().split('_')[1]) for et_key in event_team_keys_future.get_result()]))

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

        district_cmp_futures = ndb.get_multi_async(district_cmp_keys_future.get_result())

        team_totals = DistrictHelper.calculate_rankings(events, team_futures, year)

        valid_districts = set()
        for district_cmp_future in district_cmp_futures:
            district_cmp = district_cmp_future.get_result()
            cmp_dis_type = district_cmp.event_district_enum
            if cmp_dis_type is None:
                logging.warning("District event {} has unknown district type!".format(district_cmp.key.id()))
            else:
                valid_districts.add((DistrictType.type_names[cmp_dis_type], DistrictType.type_abbrevs[cmp_dis_type]))
        valid_districts = sorted(valid_districts, key=lambda (name, _): name)

        teams = TeamHelper.sortTeams(district_teams_future.get_result())

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

        # Currently Competing Team Status
        live_events_with_teams = EventTeamStatusHelper.buildEventTeamStatus(live_events, live_eventteams_futures, teams)
        live_events_with_teams.sort(key=lambda x: x[0].name)

        self.template_values.update({
            'explicit_year': explicit_year,
            'year': year,
            'valid_years': sorted(set([int(event_key.id()[:4]) for event_key in all_cmp_event_keys_future.get_result()])),
            'valid_districts': valid_districts,
            'district_name': DistrictType.type_names[district_type],
            'district_abbrev': district_abbrev,
            'week_events': week_events,
            'team_totals': team_totals,
            'teams_a': teams_a,
            'teams_b': teams_b,
            'live_events_with_teams': live_events_with_teams,
        })

        return jinja2_engine.render('district_details.html', self.template_values)