-
Notifications
You must be signed in to change notification settings - Fork 30
Fix bug in discrepancy calculation for ES&S overvotes #2162
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -526,15 +526,32 @@ def ballot_vote_deltas( | |
| if reported is None: | ||
| reported = {choice.id: "0" for choice in contest.choices} | ||
|
|
||
| deltas = {} | ||
| for choice in contest.choices: | ||
| reported_vote = ( | ||
| 0 if reported[choice.id] in ["o", "u"] else int(reported[choice.id]) | ||
| ) | ||
| audited_vote = ( | ||
| 0 if audited[choice.id] in ["o", "u"] else int(audited[choice.id]) | ||
| ) | ||
| deltas[choice.id] = reported_vote - audited_vote | ||
| # Special case for ES&S overvotes/undervotes. | ||
| has_overvote = "o" in reported.values() | ||
| has_undervote = "u" in reported.values() | ||
| audited_votes = sum(map(int, (audited.values()))) | ||
| # If the audited result correctly identified overvote/undervote, return no | ||
| # delta. Otherwise, return discrepancies as usual, but substituting in | ||
| # overvotes/undervotes. | ||
| if has_overvote: | ||
| if audited_votes > 1: | ||
| return None | ||
| else: | ||
| deltas = { | ||
| choice.id: 1 - int(audited[choice.id]) for choice in contest.choices | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a little weird, though it's closer to what the math does. The math treats an overvote as a vote for the winner and a vote for the loser, whereas this logic treats an overvote as a vote for every candidate. |
||
| } | ||
| elif has_undervote: | ||
| if audited_votes < 1: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm assuming undervote here refers to a completely undervoted, i.e., blank contest. And not a vote for N where someone didn't vote for 0 but just some number less than N |
||
| return None | ||
| else: | ||
| deltas = { | ||
| choice.id: 0 - int(audited[choice.id]) for choice in contest.choices | ||
| } | ||
| else: | ||
| deltas = { | ||
| choice.id: int(reported[choice.id]) - int(audited[choice.id]) | ||
| for choice in contest.choices | ||
| } | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| if all(delta == 0 for delta in deltas.values()): | ||
| return 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.
Hm I know this is just pulled from the audit math but do we need to account for the case that a contest allows for more than 1 vote?
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.
ES&S CVRs only suport vote-for-1 so far: https://github.com/votingworks/arlo/blob/jonah/fix-ess-cvr-error/server/api/cvrs.py#L851-L853
We might want to add an assertion here and in the audit math about that assumption in case it changes