Пример #1
0
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
Пример #2
0
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
Пример #3
0
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
Пример #4
0
    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]
Пример #5
0
    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]
Пример #6
0
    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)
Пример #7
0
    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)
Пример #8
0
    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
Пример #9
0
 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
Пример #10
0
    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
Пример #11
0
 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
Пример #12
0
    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
Пример #13
0
    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
Пример #14
0
    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
Пример #15
0
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
Пример #16
0
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"))
Пример #17
0
    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
Пример #18
0
    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
Пример #19
0
    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
Пример #20
0
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 = []
Пример #21
0
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']
Пример #22
0
    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
Пример #23
0
    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
Пример #24
0
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
Пример #25
0
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
Пример #26
0
    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()