Skip to content

Commit

Permalink
Code review: add batch test, clean up results writer, error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
artoonie committed Sep 17, 2024
1 parent 290400d commit 93c92e7
Show file tree
Hide file tree
Showing 4 changed files with 248 additions and 13 deletions.
29 changes: 17 additions & 12 deletions src/main/java/network/brightspots/rcv/ResultsWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -316,8 +316,22 @@ private void generateSummarySpreadsheet(
}
csvPrinter.println();

boolean isSlice = !isNullOrBlank(sliceId);
addActionRows(csvPrinter, isSlice);
final boolean isSlice = !isNullOrBlank(sliceId);
csvPrinter.print(isSlice ? "Eliminated*" : "Eliminated");
printActionSummary(csvPrinter, roundToEliminatedCandidates);

csvPrinter.print(isSlice ? "Elected*" : "Elected");
printActionSummary(csvPrinter, roundToWinningCandidates);

// Check that all candidates are included in the candidate order
Set<String> expectedCandidates = roundTallies.get(1).getCandidates();
Set<String> providedCandidates = new HashSet<>(candidateOrder);
if (!expectedCandidates.equals(providedCandidates)) {
throw new IllegalArgumentException(
"Candidate order must include all candidates in the contest. "
+ "\nExpected: " + expectedCandidates
+ "\nProvided: " + providedCandidates);
}

// For each candidate: for each round: output total votes
for (String candidate : candidateOrder) {
Expand Down Expand Up @@ -438,7 +452,7 @@ private void generateSummarySpreadsheet(
// whether the value in the final round is positive.
// Note that this concept only makes sense when we're reporting the overall tabulation, so we
// omit it when generating results at the individual by-slice level.
if (sliceId == null && roundToResidualSurplus.get(numRounds).signum() == 1) {
if (!isSlice && roundToResidualSurplus.get(numRounds).signum() == 1) {
csvPrinter.print("Residual surplus");
for (int round = 1; round <= numRounds; round++) {
csvPrinter.print(roundToResidualSurplus.get(round));
Expand Down Expand Up @@ -468,15 +482,6 @@ private void generateSummarySpreadsheet(
Logger.info("Summary spreadsheet generated successfully.");
}

// "action" rows describe which candidates were eliminated or elected
private void addActionRows(CSVPrinter csvPrinter, boolean withAsterisk) throws IOException {
csvPrinter.print(withAsterisk ? "Eliminated*" : "Eliminated");
printActionSummary(csvPrinter, roundToEliminatedCandidates);

csvPrinter.print(withAsterisk ? "Elected*" : "Elected");
printActionSummary(csvPrinter, roundToWinningCandidates);
}

private void addContestSummaryRows(CSVPrinter csvPrinter, RoundTally round1Tally)
throws IOException {
BigDecimal numNoRankings =
Expand Down
2 changes: 1 addition & 1 deletion src/test/java/network/brightspots/rcv/TabulatorTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -672,7 +672,7 @@ void precinctExample() {
@Test
@DisplayName("tabulate by batch")
void batchExample() {
runTabulationTest("batch_example");
runTabulationTest("batch_example", 2);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
Contest Information
Generated By,RCTab 1.3.999
CSV Format Version,1
Type of Election,Single-Winner
Contest,Batch example
Jurisdiction,"Minneapolis, MN"
Office,Mayor
Date,2017-11-07
Winner(s),Betsy Hodges
Batch,MINNEAPOLIS W-1 P-02

Contest Summary
Number to be Elected,1
Number of Candidates,19
Total Number of Ballots,1
Number of Undervotes (No Rankings),0

Rounds,Round 1 Votes,% of vote,transfer,Round 2 Votes,% of vote,transfer,Round 3 Votes,% of vote,transfer,Round 4 Votes,% of vote,transfer,Round 5 Votes,% of vote,transfer
Eliminated*,Undeclared Write-ins,,,Aswar Rahman; David Rosenfeld; L.A. Nik; Christopher Zimmerman; Ronald Lischeid; Ian Simpson; Charlie Gers; Captain Jack Sparrow; Theron Preston Washington; David John Wilson; Gregg A. Iverson; Troy Benjegerdes; Al Flowers; Nekima Levy-Pounds,,,Tom Hoch,,,Raymond Dehn,,,,,
Elected*,,,,,,,,,,,,,Betsy Hodges,,
Jacob Frey,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0
Betsy Hodges,0,0.0%,0,0,0.0%,0,0,0.0%,1,1,100.0%,0,1,100.0%,0
Tom Hoch,0,0.0%,0,0,0.0%,1,1,100.0%,-1,0,0.0%,0,0,0.0%,0
Raymond Dehn,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0
Nekima Levy-Pounds,1,100.0%,0,1,100.0%,-1,0,0.0%,0,0,0.0%,0,0,0.0%,0
David Rosenfeld,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0
Charlie Gers,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0
Captain Jack Sparrow,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0
Al Flowers,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0
Aswar Rahman,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0
L.A. Nik,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0
Christopher Zimmerman,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0
Ronald Lischeid,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0
Ian Simpson,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0
Theron Preston Washington,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0
David John Wilson,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0
Gregg A. Iverson,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0
Troy Benjegerdes,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0
Undeclared Write-ins,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0,0,0.0%,0
Active Ballots,1,,,1,,,1,,,1,,,1,,
Inactive Ballots by Overvotes,0,,0,0,,0,0,,0,0,,0,0,,0
Inactive Ballots by Skipped Rankings,0,,0,0,,0,0,,0,0,,0,0,,0
Inactive Ballots by Exhausted Choices,0,,0,0,,0,0,,0,0,,0,0,,0
Inactive Ballots by Repeated Rankings,0,,0,0,,0,0,,0,0,,0,0,,0
Inactive Ballots Total,0,,0,0,,0,0,,0,0,,0,0,,0

*Elect/Eliminate decisions are from the full contest. All other results on this report are at the batch level.
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
{
"config" : {
"batch" : "MINNEAPOLIS W-1 P-04",
"contest" : "Batch example",
"date" : "2017-11-07",
"generatedBy" : "RCTab 1.3.999",
"jurisdiction" : "Minneapolis, MN",
"office" : "Mayor"
},
"jsonFormatVersion" : "1",
"results" : [ {
"inactiveBallots" : {
"exhaustedChoices" : "0",
"overvotes" : "0",
"repeatedRankings" : "0",
"skippedRankings" : "0"
},
"round" : 1,
"tally" : {
"Al Flowers" : "0",
"Aswar Rahman" : "0",
"Betsy Hodges" : "1",
"Captain Jack Sparrow" : "0",
"Charlie Gers" : "0",
"Christopher Zimmerman" : "0",
"David John Wilson" : "0",
"David Rosenfeld" : "0",
"Gregg A. Iverson" : "0",
"Ian Simpson" : "0",
"Jacob Frey" : "0",
"L.A. Nik" : "0",
"Nekima Levy-Pounds" : "1",
"Raymond Dehn" : "0",
"Ronald Lischeid" : "0",
"Theron Preston Washington" : "0",
"Tom Hoch" : "1",
"Troy Benjegerdes" : "0",
"Undeclared Write-ins" : "0"
},
"tallyResults" : [ {
"eliminated" : "Undeclared Write-ins",
"transfers" : { }
} ],
"threshold" : "50"
}, {
"inactiveBallots" : {
"exhaustedChoices" : "0",
"overvotes" : "0",
"repeatedRankings" : "0",
"skippedRankings" : "0"
},
"round" : 2,
"tally" : {
"Al Flowers" : "0",
"Aswar Rahman" : "0",
"Betsy Hodges" : "1",
"Captain Jack Sparrow" : "0",
"Charlie Gers" : "0",
"Christopher Zimmerman" : "0",
"David John Wilson" : "0",
"David Rosenfeld" : "0",
"Gregg A. Iverson" : "0",
"Ian Simpson" : "0",
"Jacob Frey" : "0",
"L.A. Nik" : "0",
"Nekima Levy-Pounds" : "1",
"Raymond Dehn" : "0",
"Ronald Lischeid" : "0",
"Theron Preston Washington" : "0",
"Tom Hoch" : "1",
"Troy Benjegerdes" : "0"
},
"tallyResults" : [ {
"eliminated" : "Aswar Rahman",
"transfers" : { }
}, {
"eliminated" : "David Rosenfeld",
"transfers" : { }
}, {
"eliminated" : "L.A. Nik",
"transfers" : { }
}, {
"eliminated" : "Christopher Zimmerman",
"transfers" : { }
}, {
"eliminated" : "Ronald Lischeid",
"transfers" : { }
}, {
"eliminated" : "Ian Simpson",
"transfers" : { }
}, {
"eliminated" : "Charlie Gers",
"transfers" : { }
}, {
"eliminated" : "Captain Jack Sparrow",
"transfers" : { }
}, {
"eliminated" : "Theron Preston Washington",
"transfers" : { }
}, {
"eliminated" : "David John Wilson",
"transfers" : { }
}, {
"eliminated" : "Gregg A. Iverson",
"transfers" : { }
}, {
"eliminated" : "Troy Benjegerdes",
"transfers" : { }
}, {
"eliminated" : "Al Flowers",
"transfers" : { }
}, {
"eliminated" : "Nekima Levy-Pounds",
"transfers" : {
"Betsy Hodges" : "1"
}
} ],
"threshold" : "50"
}, {
"inactiveBallots" : {
"exhaustedChoices" : "0",
"overvotes" : "0",
"repeatedRankings" : "0",
"skippedRankings" : "0"
},
"round" : 3,
"tally" : {
"Betsy Hodges" : "2",
"Jacob Frey" : "0",
"Raymond Dehn" : "0",
"Tom Hoch" : "1"
},
"tallyResults" : [ {
"eliminated" : "Tom Hoch",
"transfers" : {
"Jacob Frey" : "1"
}
} ],
"threshold" : "49"
}, {
"inactiveBallots" : {
"exhaustedChoices" : "0",
"overvotes" : "0",
"repeatedRankings" : "0",
"skippedRankings" : "0"
},
"round" : 4,
"tally" : {
"Betsy Hodges" : "2",
"Jacob Frey" : "1",
"Raymond Dehn" : "0"
},
"tallyResults" : [ {
"eliminated" : "Raymond Dehn",
"transfers" : { }
} ],
"threshold" : "46"
}, {
"inactiveBallots" : {
"exhaustedChoices" : "0",
"overvotes" : "0",
"repeatedRankings" : "0",
"skippedRankings" : "0"
},
"round" : 5,
"tally" : {
"Betsy Hodges" : "2",
"Jacob Frey" : "1"
},
"tallyResults" : [ {
"elected" : "Betsy Hodges",
"transfers" : { }
} ],
"threshold" : "42"
} ],
"summary" : {
"finalThreshold" : "42",
"numCandidates" : 19,
"numWinners" : 1,
"totalNumBallots" : "3",
"undervotes" : 0
}
}

0 comments on commit 93c92e7

Please sign in to comment.