def get_breaking_teams(category, prefetch=(), rankings=('rank',)): """Returns a list of StandingInfo objects, one for each team, with one additional attribute populated: for each StandingInfo `tsi`, `tsi.break_rank` is the rank of the team out of those that are in the break. `prefetch` is passed to `prefetch_related()` in the Team query. `rankings` is passed to `rankings` in the TeamStandingsGenerator. """ teams = category.breaking_teams.all().prefetch_related(*prefetch) metrics = category.tournament.pref('team_standings_precedence') generator = TeamStandingsGenerator(metrics, rankings) standings = generator.generate(teams) breakingteams_by_team_id = {bt.team_id: bt for bt in category.breakingteam_set.all()} for tsi in standings: bt = breakingteams_by_team_id[tsi.team.id] if bt.break_rank is None: if bt.remark: tsi.break_rank = "(" + bt.get_remark_display().lower() + ")" else: tsi.break_rank = "<no rank, no remark>" else: tsi.break_rank = bt.break_rank return standings
def get_breaking_teams(category, prefetch=(), rankings=('rank', )): """Returns a list of StandingInfo objects, one for each team, with one additional attribute populated: for each StandingInfo `tsi`, `tsi.break_rank` is the rank of the team out of those that are in the break. `prefetch` is passed to `prefetch_related()` in the Team query. `rankings` is passed to `rankings` in the TeamStandingsGenerator. """ teams = category.breaking_teams.all().prefetch_related(*prefetch) metrics = category.tournament.pref('team_standings_precedence') generator = TeamStandingsGenerator(metrics, rankings) standings = generator.generate(teams) breakingteams_by_team_id = { bt.team_id: bt for bt in category.breakingteam_set.all() } for tsi in standings: bt = breakingteams_by_team_id[tsi.team.id] if bt.break_rank is None: if bt.remark: tsi.break_rank = "(" + bt.get_remark_display().lower() + ")" else: tsi.break_rank = "<no rank, no remark>" else: tsi.break_rank = bt.break_rank return standings
def get_draw_with_standings(round): draw = round.get_draw() if round.prev is None: return None, draw teams = round.tournament.team_set.select_related('institution') metrics = round.tournament.pref('team_standings_precedence') generator = TeamStandingsGenerator(metrics, ('rank', 'subrank')) standings = generator.generate(teams, round=round.prev) for debate in draw: aff_standing = standings.get_standing(debate.aff_team) neg_standing = standings.get_standing(debate.neg_team) debate.metrics = [(a, n) for a, n in zip(aff_standing.itermetrics(), neg_standing.itermetrics())] if round.is_break_round: debate.aff_breakrank = BreakingTeam.objects.get( break_category=round.break_category, team=debate.aff_team.id).break_rank debate.neg_breakrank = BreakingTeam.objects.get( break_category=round.break_category, team=debate.neg_team.id).break_rank else: if "points" in standings.metric_keys: debate.aff_is_pullup = abs(aff_standing.metrics["points"] - debate.bracket) >= 1 debate.neg_is_pullup = abs(neg_standing.metrics["points"] - debate.bracket) >= 1 debate.aff_subrank = aff_standing.rankings["subrank"] debate.neg_subrank = neg_standing.rankings["subrank"] return standings, draw
def get_tables(self): if self.tournament.pref('teams_in_debate') != 'bp': logger.warning("Tried to access position balance report for a non-BP tournament") return [] if self.round.prev is None: logger.warning("Tried to access position balance report for first round") return [] if self.round.is_break_round: logger.warning("Tried to access position balance report for a break round") return [] draw = self.round.debate_set_with_prefetches(ordering=('room_rank',), institutions=True) teams = Team.objects.filter(debateteam__debate__round=self.round) side_histories_before = get_side_history(teams, self.tournament.sides, self.round.prev.seq) side_histories_now = get_side_history(teams, self.tournament.sides, self.round.seq) metrics = self.tournament.pref('team_standings_precedence') generator = TeamStandingsGenerator(metrics[0:1], ()) standings = generator.generate(teams, round=self.round.prev) summary_table = PositionBalanceReportSummaryTableBuilder(view=self, title=_("Teams with position imbalances"), empty_title=_("No teams with position imbalances! Hooray!") + " 😊") summary_table.build(draw, teams, side_histories_before, side_histories_now, standings) draw_table = PositionBalanceReportDrawTableBuilder(view=self, title=_("Annotated draw")) draw_table.build(draw, teams, side_histories_before, side_histories_now, standings) return [summary_table, draw_table]
def get_tables(self): if self.tournament.pref('teams_in_debate') != 'bp': logger.warning("Tried to access position balance report for a non-BP tournament") return [] if self.round.prev is None: logger.warning("Tried to access position balance report for first round") return [] if self.round.is_break_round: logger.warning("Tried to access position balance report for a break round") return [] draw = self.round.debate_set_with_prefetches(ordering=('room_rank',), institutions=True) teams = Team.objects.filter(debateteam__debate__round=self.round) side_histories_before = get_side_history(teams, self.tournament.sides, self.round.prev.seq) side_histories_now = get_side_history(teams, self.tournament.sides, self.round.seq) metrics = self.tournament.pref('team_standings_precedence') generator = TeamStandingsGenerator(metrics[0:1], ()) standings = generator.generate(teams, round=self.round.prev) summary_table = PositionBalanceReportSummaryTableBuilder(view=self, title=_("Teams with position imbalances"), empty_title=_("No teams with position imbalances! Hooray!") + " 😊") summary_table.build(draw, teams, side_histories_before, side_histories_now, standings) draw_table = PositionBalanceReportDrawTableBuilder(view=self, title=_("Annotated draw")) draw_table.build(draw, teams, side_histories_before, side_histories_now, standings) return [summary_table, draw_table]
def retrieve_standings(self): """Retrieves standings and places them in `self.standings`.""" metrics = self.category.tournament.pref('team_standings_precedence') self.check_required_metrics(metrics) generator = TeamStandingsGenerator(metrics, self.rankings) generated = generator.generate(self.team_queryset) self.standings = list(generated)
def retrieve_standings(self): """Retrieves standings and places them in `self.standings`.""" metrics = self.category.tournament.pref('team_standings_precedence') self.check_required_metrics(metrics) generator = TeamStandingsGenerator(metrics, self.rankings) generated = generator.generate(self.team_queryset) self.standings = list(generated)
def get_standard_table(self): r = self.round if r.is_break_round: sort_key = "room-rank" sort_order = 'asc' else: sort_key = "bracket" sort_order = 'desc' table = AdminDrawTableBuilder(view=self, sort_key=sort_key, sort_order=sort_order, empty_title=_("No debates in this round")) draw = self.get_draw() populate_history(draw) if r.is_break_round: table.add_room_rank_columns(draw) else: table.add_debate_bracket_columns(draw) table.add_debate_venue_columns(draw, for_admin=True) table.add_debate_team_columns(draw) # For draw details and draw draft pages if (r.draw_status == Round.STATUS_DRAFT or self.detailed) and r.prev: teams = Team.objects.filter(debateteam__debate__round=r) metrics = self.tournament.pref('team_standings_precedence') if self.tournament.pref('teams_in_debate') == 'two': pullup_metric = PowerPairedDrawGenerator.PULLUP_RESTRICTION_METRICS[self.tournament.pref('draw_pullup_restriction')] else: pullup_metric = None # subrank only makes sense if there's a second metric to rank on rankings = ('rank', 'subrank') if len(metrics) > 1 else ('rank',) generator = TeamStandingsGenerator(metrics, rankings, extra_metrics=(pullup_metric,) if pullup_metric and pullup_metric not in metrics else ()) standings = generator.generate(teams, round=r.prev) if not r.is_break_round: table.add_debate_ranking_columns(draw, standings) else: self._add_break_rank_columns(table, draw, r.break_category) table.add_debate_metric_columns(draw, standings) table.add_debate_side_history_columns(draw, r.prev) elif not (r.draw_status == Round.STATUS_DRAFT or self.detailed): table.add_debate_adjudicators_column(draw, show_splits=False, for_admin=True) table.add_draw_conflicts_columns(draw, self.venue_conflicts, self.adjudicator_conflicts) if not r.is_break_round: table.highlight_rows_by_column_value(column=0) # highlight first row of a new bracket return table
def get_bp_position_balance_table(self): draw = self.get_draw() teams = Team.objects.filter(debateteam__debate__round=self.round) side_histories_before = get_side_history(teams, self.tournament.sides, self.round.prev.seq) side_histories_now = get_side_history(teams, self.tournament.sides, self.round.seq) generator = TeamStandingsGenerator(('points',), ()) standings = generator.generate(teams, round=self.round.prev) draw_table = PositionBalanceReportDrawTableBuilder(view=self) draw_table.build(draw, teams, side_histories_before, side_histories_now, standings) self.highlighted_cells_exist = any(draw_table.get_imbalance_category(team) is not None for team in teams) return draw_table
def get_teams(self): metrics = self.round.tournament.pref('team_standings_precedence') generator = TeamStandingsGenerator(metrics, ('rank', 'subrank'), tiebreak="random") standings = generator.generate(super().get_teams(), round=self.round.prev) ranked = [] for standing in standings: team = standing.team team.points = next(standing.itermetrics()) ranked.append(team) return ranked
def get_bp_position_balance_table(self): draw = self.get_draw() teams = Team.objects.filter(debateteam__debate__round=self.round) side_histories_before = get_side_history(teams, self.tournament.sides, self.round.prev.seq) side_histories_now = get_side_history(teams, self.tournament.sides, self.round.seq) metrics = self.tournament.pref('team_standings_precedence') generator = TeamStandingsGenerator(metrics[0:1], ()) standings = generator.generate(teams, round=self.round.prev) draw_table = PositionBalanceReportDrawTableBuilder(view=self) draw_table.build(draw, teams, side_histories_before, side_histories_now, standings) self.highlighted_cells_exist = any(draw_table.get_imbalance_category(team) is not None for team in teams) return draw_table
def get_standard_table(self): r = self.get_round() if r.is_break_round: sort_key = _("Room rank") sort_order = 'asc' else: sort_key = _("Bracket") sort_order = 'desc' table = AdminDrawTableBuilder( view=self, sort_key=sort_key, sort_order=sort_order, empty_title=_("No Debates for this Round")) draw = self.get_draw() populate_history(draw) if r.is_break_round: table.add_room_rank_columns(draw) else: table.add_debate_bracket_columns(draw) table.add_debate_venue_columns(draw, for_admin=True) table.add_debate_team_columns(draw) # For draw details and draw draft pages if (r.draw_status == Round.STATUS_DRAFT or self.detailed) and r.prev: teams = Team.objects.filter(debateteam__debate__round=r) metrics = self.get_tournament().pref('team_standings_precedence') generator = TeamStandingsGenerator(metrics, ('rank', 'subrank')) standings = generator.generate(teams, round=r.prev) if not r.is_break_round: table.add_debate_ranking_columns(draw, standings) else: self._add_break_rank_columns(table, draw, r.break_category) table.add_debate_metric_columns(draw, standings) table.add_debate_side_history_columns(draw, r.prev) elif not (r.draw_status == Round.STATUS_DRAFT or self.detailed): table.add_debate_adjudicators_column(draw, show_splits=False) table.add_draw_conflicts_columns(draw, self.venue_conflicts, self.adjudicator_conflicts) if not r.is_break_round: table.highlight_rows_by_column_value( column=0) # highlight first row of a new bracket return table
def get_teams(self): metrics = self.round.tournament.pref('team_standings_precedence') generator = TeamStandingsGenerator(metrics, ('rank', 'subrank'), tiebreak="random") standings = generator.generate(super().get_teams(), round=self.round.prev) ranked = [] for standing in standings: team = standing.team team.points = next(standing.itermetrics()) ranked.append(team) return ranked
def get_standard_table(self): r = self.round if r.is_break_round: sort_key = "room-rank" sort_order = 'asc' else: sort_key = "bracket" sort_order = 'desc' table = AdminDrawTableBuilder(view=self, sort_key=sort_key, sort_order=sort_order, empty_title=_("No Debates for this Round")) draw = self.get_draw() populate_history(draw) if r.is_break_round: table.add_room_rank_columns(draw) else: table.add_debate_bracket_columns(draw) table.add_debate_venue_columns(draw, for_admin=True) table.add_debate_team_columns(draw) # For draw details and draw draft pages if (r.draw_status == Round.STATUS_DRAFT or self.detailed) and r.prev: teams = Team.objects.filter(debateteam__debate__round=r) metrics = self.tournament.pref('team_standings_precedence') # subrank only makes sense if there's a second metric to rank on rankings = ('rank', 'subrank') if len(metrics) > 1 else ('rank',) generator = TeamStandingsGenerator(metrics, rankings) standings = generator.generate(teams, round=r.prev) if not r.is_break_round: table.add_debate_ranking_columns(draw, standings) else: self._add_break_rank_columns(table, draw, r.break_category) table.add_debate_metric_columns(draw, standings) if self.tournament.pref('draw_pullup_restriction') == 'least_to_date': table.add_number_of_pullups_columns(draw, r.prev) table.add_debate_side_history_columns(draw, r.prev) elif not (r.draw_status == Round.STATUS_DRAFT or self.detailed): table.add_debate_adjudicators_column(draw, show_splits=False) table.add_draw_conflicts_columns(draw, self.venue_conflicts, self.adjudicator_conflicts) if not r.is_break_round: table.highlight_rows_by_column_value(column=0) # highlight first row of a new bracket return table
def get_breaking_teams(category, include_all=False, include_categories=False): """Returns a list of Teams, with additional attributes. For each Team t in the returned list: t.rank is the rank of the team, including ineligible teams. t.break_rank is the rank of the team out of those that are in the break. 'category' must be a BreakCategory instance. If 'include_all' is True: - Teams that would break but for the institution cap are included in the returned list, with t.break_rank set to the string "(capped)". - If category.is_general is True, then teams that would break but for being ineligible for this category are included in the returned list, but with t.break_rank set to the string "(ineligible)". Note that in no circumstances are teams that broke in a higher priority category included in the list of returned teams. If 'include_all' is False, the capped and ineligible teams are excluded, but t.rank is still the rank including those teams. If 'include_categories' is True, t.categories_for_display will be a comma- delimited list of category names that are not this category, and lower or equal priority to this category. """ teams = category.breaking_teams.all() if not include_all: teams = teams.filter(break_rank__isnull=False) metrics = category.tournament.pref('team_standings_precedence') generator = TeamStandingsGenerator(metrics, ('rank',)) standings = generator.generate(teams) for standing in standings: bt = standing.team.breakingteam_set.get(break_category=category) standing.rank = bt.rank if bt.break_rank is None: if bt.remark: standing.break_rank = "(" + bt.get_remark_display().lower() + ")" else: standing.break_rank = "<error>" else: standing.break_rank = bt.break_rank if include_categories: categories = standing.team.break_categories_nongeneral.exclude(id=category.id).exclude(priority__lt=category.priority) standing.categories_for_display = "(" + ", ".join(c.name for c in categories) + ")" if categories else "" else: standing.categories_for_display = "" return standings
class TeamStandingsPrecedence(MultiValueChoicePreference): help_text = _( "Metrics to use to rank teams (see documentation for further details)") verbose_name = _("Team standings precedence") section = standings name = 'team_standings_precedence' choices = TeamStandingsGenerator.get_metric_choices() nfields = 8 allow_empty = True default = ['wins', 'speaks_avg'] def validate(self, value): super().validate(value) # Check that non-repeatable metrics aren't listed twice classes = [ TeamStandingsGenerator.metric_annotator_classes[metric] for metric in value ] duplicates = [ c for c in classes if c.repeatable is False and classes.count(c) > 1 ] if duplicates: duplicates_str = ", ".join( list(set(force_text(c.name) for c in duplicates))) raise ValidationError( _("The following metrics can't be listed twice: " "%(duplicates)s") % {'duplicates': duplicates_str}) # Check that who-beat-whom isn't listed first if value[0] in ["wbw", "wbwd"]: raise ValidationError( _("Who-beat-whom can't be listed as the first metric"))
def get_table(self): r = self.get_round() tournament = self.get_tournament() table = TabbycatTableBuilder(view=self) if r.draw_status == r.STATUS_NONE: return table # Return Blank draw = r.debate_set_with_prefetches(ordering=('room_rank', ), institutions=True, venues=True) populate_history(draw) if r.is_break_round: table.add_room_rank_columns(draw) else: table.add_debate_bracket_columns(draw) table.add_debate_venue_columns(draw, for_admin=True) table.add_team_columns([d.aff_team for d in draw], key=aff_name(tournament).capitalize(), hide_institution=True) table.add_team_columns([d.neg_team for d in draw], key=neg_name(tournament).capitalize(), hide_institution=True) # For draw details and draw draft pages if (r.draw_status == r.STATUS_DRAFT or self.detailed) and r.prev: teams = Team.objects.filter(debateteam__debate__round=r) metrics = r.tournament.pref('team_standings_precedence') generator = TeamStandingsGenerator(metrics, ('rank', 'subrank')) standings = generator.generate(teams, round=r.prev) if not r.is_break_round: table.add_debate_ranking_columns(draw, standings) else: self._add_break_rank_columns(table, draw, r.break_category) table.add_debate_metric_columns(draw, standings) table.add_sides_count([d.aff_team for d in draw], r.prev, 'aff') table.add_sides_count([d.neg_team for d in draw], r.prev, 'neg') else: table.add_debate_adjudicators_column(draw, show_splits=False) table.add_draw_conflicts_columns(draw) if not r.is_break_round: table.highlight_rows_by_column_value( column=0) # highlight first row of a new bracket return table
def get_teams(self): """Get teams in ranked order.""" teams = super().get_teams() if self.round.tournament.pref('draw_pullup_restriction') == 'least_to_date': annotate_npullups(teams, self.round.prev) metrics = self.round.tournament.pref('team_standings_precedence') generator = TeamStandingsGenerator(metrics, ('rank', 'subrank'), tiebreak="random") standings = generator.generate(teams, round=self.round.prev) ranked = [] for standing in standings: team = standing.team team.points = next(standing.itermetrics()) ranked.append(team) return ranked
def get_table(self): r = self.get_round() table = TabbycatTableBuilder(view=self) if r.draw_status == r.STATUS_NONE: return table # Return Blank draw = r.debate_set_with_prefetches(ordering=('room_rank',), institutions=True) populate_history(draw) if r.is_break_round: table.add_room_rank_columns(draw) else: table.add_debate_bracket_columns(draw) table.add_debate_venue_columns(draw) table.add_team_columns([d.aff_team for d in draw], key="Aff", hide_institution=True) table.add_team_columns([d.neg_team for d in draw], key="Neg", hide_institution=True) # For draw details and draw draft pages if (r.draw_status == r.STATUS_DRAFT or self.detailed) and r.prev: teams = Team.objects.filter(debateteam__debate__round=r) metrics = r.tournament.pref('team_standings_precedence') generator = TeamStandingsGenerator(metrics, ('rank', 'subrank')) standings = generator.generate(teams, round=r.prev) if not r.is_break_round: table.add_debate_ranking_columns(draw, standings) else: table.add_column( {'tooltip': "Affirmative Team's Break Rank", 'text': "ABR"}, ["%s" % d.aff_team.break_rank_for_category(r.break_category) for d in draw]) table.add_column( {'tooltip': "Negative Team's Break Rank", 'text': "NBR"}, ["%s" % d.neg_team.break_rank_for_category(r.break_category) for d in draw]) table.add_debate_metric_columns(draw, standings) table.add_sides_count([d.aff_team for d in draw], r.prev, 'aff') table.add_sides_count([d.neg_team for d in draw], r.prev, 'neg') else: table.add_debate_adjudicators_column(draw, show_splits=False) table.add_draw_conflicts_columns(draw) if not r.is_break_round: table.highlight_rows_by_column_value(column=0) # highlight first row of a new bracket return table
class TeamStandingsExtraMetrics(MultiValueChoicePreference): help_text = _("Metrics to calculate, but not used to rank teams") verbose_name = _("Team standings extra metrics") section = standings name = 'team_standings_extra_metrics' choices = TeamStandingsGenerator.get_metric_choices(ranked_only=False) nfields = 5 allow_empty = True default = []
class TeamStandingsPrecedence(MultiValueChoicePreference): help_text = "Metrics to use to rank teams (see documentation for further details)" verbose_name = "Team standings precedence" section = standings name = "team_standings_precedence" choices = TeamStandingsGenerator.get_metric_choices() nfields = 8 allow_empty = True default = ['wins', 'speaks_avg']
def get_teams(self): """Get teams in ranked order.""" teams = super().get_teams() if self.round.tournament.pref( 'draw_pullup_restriction') == 'least_to_date': annotate_npullups(teams, self.round.prev) metrics = self.round.tournament.pref('team_standings_precedence') generator = TeamStandingsGenerator(metrics, ('rank', 'subrank'), tiebreak="random") standings = generator.generate(teams, round=self.round.prev) ranked = [] for standing in standings: team = standing.team team.points = next(standing.itermetrics()) ranked.append(team) return ranked
def get_teams(self): """Get teams in ranked order.""" teams = super().get_teams() metrics = self.round.tournament.pref('team_standings_precedence') pullup_metric = PowerPairedDrawGenerator.PULLUP_RESTRICTION_METRICS[self.round.tournament.pref('draw_pullup_restriction')] generator = TeamStandingsGenerator(metrics, ('rank', 'subrank'), tiebreak="random", extra_metrics=(pullup_metric,) if pullup_metric and pullup_metric not in metrics else ()) standings = generator.generate(teams, round=self.round.prev) ranked = [] for standing in standings: team = standing.team team.points = next(standing.itermetrics(), 0) or 0 if pullup_metric: setattr(team, pullup_metric, standing.metrics[pullup_metric]) ranked.append(team) return ranked
def get_draw_with_standings(round): draw = round.get_draw() if round.prev is None: return None, draw teams = Team.objects.teams_for_standings(round) metrics = round.tournament.pref('team_standings_precedence') generator = TeamStandingsGenerator(metrics, ('rank', 'subrank')) standings = generator.generate(teams, round=round.prev) for debate in draw: aff_standing = standings.get_team_standing(debate.aff_team) neg_standing = standings.get_team_standing(debate.neg_team) debate.aff_subrank = aff_standing.rankings["subrank"] debate.neg_subrank = neg_standing.rankings["subrank"] debate.metrics = [(a, n) for a, n in zip(aff_standing.itermetrics(), neg_standing.itermetrics())] if "points" in standings.metric_keys: debate.aff_is_pullup = abs(aff_standing.metrics["points"] - debate.bracket) >= 1 debate.neg_is_pullup = abs(neg_standing.metrics["points"] - debate.bracket) >= 1 return standings, draw
def _generate_breaking_teams(category, eligible_teams, teams_broken_higher_priority=set()): """Generates a list of breaking teams for the given category and returns a list of teams in the (actual) break, i.e. excluding teams that are ineligible, capped, broke in a different break, and so on.""" metrics = category.tournament.pref('team_standings_precedence') generator = TeamStandingsGenerator(metrics, ('rank',)) standings = generator.generate(eligible_teams) break_size = category.break_size institution_cap = category.institution_cap or None prev_rank_value = tuple([None] * len(standings.metric_keys)) prev_break_rank_value = tuple([None] * len(standings.metric_keys)) cur_rank = 0 breaking_teams = list() breaking_teams_to_create = list() # Variables for institutional caps and non-breaking teams cur_break_rank = 0 # actual break rank cur_break_seq = 0 # sequential count of breaking teams teams_from_institution = Counter() for i, standing in enumerate(standings, start=1): team = standing.team try: bt = BreakingTeam.objects.get(break_category=category, team=team) existing = True except BreakingTeam.DoesNotExist: bt = BreakingTeam(break_category=category, team=team) existing = False # Compute overall rank rank_value = tuple(standing.itermetrics()) if rank_value != prev_rank_value: # if we have enough teams, we're done if len(breaking_teams) >= break_size: break cur_rank = i prev_rank_value = rank_value bt.rank = cur_rank # If there is an existing remark, scrub the break rank and skip if existing and bt.remark: bt.break_rank = None # Check if ineligible elif not team.break_categories.filter(pk=category.pk).exists(): bt.remark = bt.REMARK_INELIGIBLE # Check if capped out by institution cap elif institution_cap is not None and teams_from_institution[team.institution] >= institution_cap: bt.remark = bt.REMARK_CAPPED # Check if already broken to a higher category elif team in teams_broken_higher_priority: bt.remark = bt.REMARK_DIFFERENT_BREAK # If neither, this team is in the break else: # Compute break rank cur_break_seq += 1 if rank_value != prev_break_rank_value: cur_break_rank = cur_break_seq prev_break_rank_value = rank_value bt.break_rank = cur_break_rank breaking_teams.append(team) # Take note of the institution teams_from_institution[team.institution] += 1 bt.full_clean() if existing: bt.save() else: breaking_teams_to_create.append(bt) BreakingTeam.objects.bulk_create(breaking_teams_to_create) BreakingTeam.objects.filter(break_category=category, break_rank__isnull=False).exclude( team_id__in=[t.id for t in breaking_teams]).delete() return breaking_teams
def draw(self, override_team_checkins=False): from draw.models import Debate, TeamPositionAllocation from draw import DrawGenerator from participants.models import Team if self.draw_status != self.STATUS_NONE: raise RuntimeError( "Tried to run draw on round that already has a draw") # Delete all existing debates for this round. self.debate_set.all().delete() # There is a bit of logic to go through to figure out what we need to # provide to the draw class. OPTIONS_TO_CONFIG_MAPPING = { "avoid_institution": "draw_rules__avoid_same_institution", "avoid_history": "draw_rules__avoid_team_history", "history_penalty": "draw_rules__team_history_penalty", "institution_penalty": "draw_rules__team_institution_penalty", "side_allocations": "draw_rules__draw_side_allocations", } if override_team_checkins is True: teams = self.tournament.team_set.all() else: teams = self.active_teams.all() from participants.models import Team orig_len = len(teams) teams = teams.exclude(type=Team.TYPE_BYE) if orig_len != len(teams): logger.info("Excluded bye teams: {} total, {} teams after cull".format(orig_len, len(teams))) # Set type-specific options if self.draw_type == self.DRAW_RANDOM: draw_type = "random" OPTIONS_TO_CONFIG_MAPPING.update({ "avoid_conflicts": "draw_rules__draw_avoid_conflicts", }) elif self.draw_type == self.DRAW_MANUAL: draw_type = "manual" elif self.draw_type == self.DRAW_POWERPAIRED: from participants.models import Team from standings.teams import TeamStandingsGenerator metrics = self.tournament.pref('team_standings_precedence') generator = TeamStandingsGenerator(metrics, ('rank', 'subrank'), tiebreak="random") standings = generator.generate(teams, round=self.prev) teams = [] for standing in standings: team = standing.team team.points = next(standing.itermetrics()) teams.append(team) draw_type = "power_paired" OPTIONS_TO_CONFIG_MAPPING.update({ "avoid_conflicts" : "draw_rules__draw_avoid_conflicts", "odd_bracket" : "draw_rules__draw_odd_bracket", "pairing_method" : "draw_rules__draw_pairing_method", }) elif self.draw_type == self.DRAW_ROUNDROBIN: draw_type = "round_robin" else: raise RuntimeError("Break rounds aren't supported yet.") # Annotate attributes as required by DrawGenerator. if self.prev: for team in teams: team.aff_count = team.get_aff_count(self.prev.seq) else: for team in teams: team.aff_count = 0 # Evaluate this query set first to avoid hitting the database inside a loop. tpas = dict() TPA_MAP = {TeamPositionAllocation.POSITION_AFFIRMATIVE: "aff", TeamPositionAllocation.POSITION_NEGATIVE: "neg"} for tpa in self.teampositionallocation_set.all(): tpas[tpa.team] = TPA_MAP[tpa.position] for team in teams: if team in tpas: team.allocated_side = tpas[team] del tpas options = dict() for key, value in OPTIONS_TO_CONFIG_MAPPING.items(): options[key] = self.tournament.preferences[value] if options["side_allocations"] == "manual-ballot": options["side_allocations"] = "balance" drawer = DrawGenerator(draw_type, teams, results=None, **options) draw = drawer.generate() self.make_debates(draw) self.draw_status = self.STATUS_DRAFT self.save()