-
-
Notifications
You must be signed in to change notification settings - Fork 860
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor DebateResult and associated classes #1180
Conversation
- Removing obsolete comment from tables.py - Converted name of current iron parameter from camelCase
acb1635
to
387359e
Compare
63f8970
to
cf3d72c
Compare
cf3d72c
to
5cb43ed
Compare
4378c02
to
f7f04c1
Compare
@@ -461,6 +461,21 @@ class BallotsPerDebateElimination(ChoicePreference): | |||
default = 'per-adj' | |||
|
|||
|
|||
@tournament_preferences_registry.register | |||
class BallotMustConfirmWinner(ChoicePreference): | |||
help_text = _("Whether adjudicator(s) must select the winning team in their ballot, and how it should be treated. Note: Not supported in BP.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't really know how to word this preference...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, good question. I think what you've got for the help text above is good. I tend to think of the three below options as constraints on scores rather than on the winner, though. Like:
- Do not require separate winner selection; infer from higher speaker score (no ties allowed)
- High-point wins only: Require winner selection to verify that winning team scored higher
- Tied-point wins allowed: Require winner selection to verify that winning team tied or scored higher
- Low-point wins allowed: Selected winning teams wins, regardless of speaker scores
d7f0b9e
to
6194086
Compare
This commit adds a new table to the Results application to hold team scores by individual adjudicators (TeamScoreByAdj) as a counterpart to SpeakerScoreByAdj and TeamScore. Its use is primarily for by-adj elimination ballots. The uses are not yet implemented. It has these fields: - Ballot submission - Debate-Adjudicator - Debate-Team - Win? - Margin - Score The points field was not included as only two-team formats can use this table and the win column can serve the same role. The margin field was added as it could signal greater dissent from an adjudicator than another. A database migration has also been added to both create this model and to populate it with existing SpeakerScoreByAdj records. All of the current data in this table will be redundant with existing data. Some methods in other results admin classes have been removed as duplicating inherited methods.
This commit reduces the number of various DebateResult classes by making them agnostic to the number of teams. With that, the base class branches to "By Adjudicator" and "Consensus," with a mixin included for their subclasses to support scoring. For this, scoring is assigning speaker scores, which are then combined to create team scores. Teams may not be scored without speakers. This greatly simplifies the inheritance structure of DebateResult. The scoresheet selection for each type of DebateResult is in the DebateResult class. To support BP in this structure, the `winners()` attribute of scoresheets is now a list of winning sides. There is an assertion to prevent an incorrect number of winners though. Also, a specific scoresheet for BP Elimination has been created. No new options to access DebateResults without scores has been created in this patch. Tests are also updated to take the new structure, but no new tests have been added as of this commit. Finding the proper DebateResult class has been spun-off from the factory function.
As a consequence of the previous commit, uses of DebateResult in certain places needed to be updated. This commit updates its use for ballot notifications (using isinstance) and the dropping of "advancing" in favour of the generalization "winner(s)" in results. This commit does not update ballot forms, which will be dealt with separately.
This change makes the current forms usable with the new format of DebateResult, which will be refactored in another commit. It also marks the password label for translation and does some cleanup.
This commit adds fields for adjudicators to precise the winning team on their ballots through a drop-down after their scores, using side names. Checks have also been added to results to check for empty lists when finding the winner.
This commit creates a preference to activate the winning team selection drop-down in ballots, further enabling tied-points and low-point wins with their associated scoresheet classes. Validation has been added to the forms if the declared winner does not correspond to the form in high-point wins.
This commit refactors the result forms to be orthogonal to the Debate- Result classes. In that, mixins are used to distinguish forms using (or not) scores. The DebateResult class has also been added as a parameter for the forms with a correspondence dict between forms and results in views.py. A new preference allows for speaker scores to be omitted from ballots depending on the round stage.
6194086
to
194143f
Compare
This commit replaces all the logic to determine the result of a debate, such as the margin/splits, etc. with completing a DebateResult object and saving that. This commit refers to methods created as part of TabbycatDebate#1180.
60a743c
to
88705fc
Compare
# Conflicts: # tabbycat/results/result.py # tabbycat/tournaments/models.py
This commit creates API endpoints that can be used to get and create debate results. Result endpoints are under each debate (pairing), and can be filtered to just the confirmed ballots. When public, only the confirmed ballot is shown. The base for the results is in a new class ResultInfo, which takes a DebateResult object and transforms the methods to retrieve info into object attributes, that can then be read by DRF serializer fields. Serializer subclasses were created to mirror the new DebateInfo structure, but the save() methods bypass ResultInfo to set and save DebateResult directly.
This commit adds the schema definition for the Results endpoints to the OpenAPI schema.
# Conflicts: # tabbycat/api/serializers.py
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hello, this happens when I set it up on a standard Australs setup:
Traceback (most recent call last):
File "/home/czlee/tabbycat/venv/lib/python3.8/site-packages/django/core/handlers/exception.py", line 47, in inner
response = get_response(request)
[...]
File "/home/czlee/tabbycat/venv/lib/python3.8/site-packages/django/forms/forms.py", line 402, in _clean_form
cleaned_data = self.clean()
File "/home/czlee/tabbycat/tabbycat/results/forms.py", line 514, in clean
self.clean_scoresheet(cleaned_data)
File "/home/czlee/tabbycat/tabbycat/results/forms.py", line 831, in clean_scoresheet
high_point_declared = max(side_totals, key=lambda key: side_totals[key]) == cleaned_data[self._fieldname_declared_winner()]
TypeError: _fieldname_declared_winner() missing 1 required positional argument: 'adj'
@@ -461,6 +461,21 @@ class BallotsPerDebateElimination(ChoicePreference): | |||
default = 'per-adj' | |||
|
|||
|
|||
@tournament_preferences_registry.register | |||
class BallotMustConfirmWinner(ChoicePreference): | |||
help_text = _("Whether adjudicator(s) must select the winning team in their ballot, and how it should be treated. Note: Not supported in BP.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, good question. I think what you've got for the help text above is good. I tend to think of the three below options as constraints on scores rather than on the winner, though. Like:
- Do not require separate winner selection; infer from higher speaker score (no ties allowed)
- High-point wins only: Require winner selection to verify that winning team scored higher
- Tied-point wins allowed: Require winner selection to verify that winning team tied or scored higher
- Low-point wins allowed: Selected winning teams wins, regardless of speaker scores
Can I also pull @philipbelesky in to offer guidance on how this would fit in with the old/new results entry interfaces? This obviously requires changes to the result entry form; should we be (evenutally) modifying both forms or just one of them? |
I think just the old results entry form. I've come to the conclusion that if/when the new entry interface is finished it needs to post to an API endpoint rather than its current (flawed) approach of trying to wrap Vue around Django's forms. That change is pretty much a rewrite so I don't think the current state of the 'new' form needs to be kept up to date - if anything it could be removed. |
- Renamed "adj" message parameter to "adjudicator" - Added team sides to declared winners dropdown - Don't set declared winners with high-point wins - Specify adj in voting validation declared winner
# Conflicts: # tabbycat/results/views.py
Fixed |
Forms look better now, thanks! Will go over code/internals a bit later. Don't put this in this PR (doesn't even need to be this release), but I wonder if we can optionally highlight low-point/tied-point wins in the results table. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few minor things I noticed but looks good, will merge and if you want to touch anything up feel free to do so in a new commit pushed straight to develop.
logger.exception("Tried to set score, but scoresheet doesn't do scores. " | ||
"self.takes_scores is %s", self.takes_scores) | ||
def get_winner(self, adjudicator): | ||
return next(iter(self.scoresheets[adjudicator].winners()), None) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there an assumption here that there is at most one winner whenever this function is called? If so, it's probably better to include an assert
statement that verifies that .winners()
has at most one item, and then return that item using .pop()
, rather than using next(iter(...))
. That way if the condition fails, it fails fast, and also it'll be more explicit to read.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, there may be either 0 or 1 winners, and if there are no winners, None
should be returned. .pop()
couldn't be used as it would mutate the set.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I said at most one winner 😉 as in, there can't be more than one, though there might be less than one. But good point re pop()
—kinda dissatisfying that next(iter(...))
is the cleanest way otherwise, but it is what it is! I still think you want to assert
assumptions to get it to fail fast though, or at least get the logger to throw an error at Sentry (if you think crashing due to the state is worse than ploughing ahead silently).
Just noting a regression in #922 (see reopening comment there). |
This restores the line added in a29981d to make set_score() fail silently if the adjudicator is not on the panel. It was removed by the merger of #1180, specifically in a0eb1f7, which had been done long before a29981d but not been merged until it was merged with #1180 (i.e., it was a merge conflict whose resolution appears to have been overlooked). Fixes #922.
This branch is the work in refactoring
DebateResult
,Scoresheet
, andResultForm
for technical debt and allowing for new types of results. See commit messages for more details.Supersedes #1004
Goal:
Fixes #527
Fixes #643
Fixes #1003