From 75ed57437a8400c5214aeadd1358361cffd600d6 Mon Sep 17 00:00:00 2001 From: Armin Samii Date: Wed, 11 Mar 2026 14:37:52 -0400 Subject: [PATCH 1/5] Support ES&S CVRs with multiple contests --- .../brightspots/rcv/StreamingCvrReader.java | 8 + .../brightspots/rcv/TabulatorTests.java | 8 +- .../ess_multi_contest_config.json | 86 +++++++ .../ess_multi_contest_cvr.xlsx | Bin 0 -> 61956 bytes ...multi_contest_expected_detailed_report.csv | 39 +++ ...ulti_contest_expected_detailed_report.json | 222 ++++++++++++++++++ 6 files changed, 362 insertions(+), 1 deletion(-) create mode 100644 src/test/resources/network/brightspots/rcv/test_data/ess_multi_contest/ess_multi_contest_config.json create mode 100644 src/test/resources/network/brightspots/rcv/test_data/ess_multi_contest/ess_multi_contest_cvr.xlsx create mode 100644 src/test/resources/network/brightspots/rcv/test_data/ess_multi_contest/ess_multi_contest_expected_detailed_report.csv create mode 100644 src/test/resources/network/brightspots/rcv/test_data/ess_multi_contest/ess_multi_contest_expected_detailed_report.json diff --git a/src/main/java/network/brightspots/rcv/StreamingCvrReader.java b/src/main/java/network/brightspots/rcv/StreamingCvrReader.java index 96959e4fb..a3595f20f 100644 --- a/src/main/java/network/brightspots/rcv/StreamingCvrReader.java +++ b/src/main/java/network/brightspots/rcv/StreamingCvrReader.java @@ -183,6 +183,14 @@ private void endCvr() { String computedCastVoteRecordId = String.format("%s-%d", OutputWriter.sanitizeStringForOutput(excelFileName), cvrIndex); + boolean areAllCandidatesEmpty = currentRankings.stream().allMatch( + ranking -> isNullOrBlank(ranking.getValue())); + if (areAllCandidatesEmpty) { + Logger.auditable( + "Skipping cast vote record with no votes for any candidates: %s", computedCastVoteRecordId); + return; + } + // add precinct ID if needed if (precinctColumnIndex != null) { if (currentPrecinct == null) { diff --git a/src/test/java/network/brightspots/rcv/TabulatorTests.java b/src/test/java/network/brightspots/rcv/TabulatorTests.java index 5144cc304..ade726618 100644 --- a/src/test/java/network/brightspots/rcv/TabulatorTests.java +++ b/src/test/java/network/brightspots/rcv/TabulatorTests.java @@ -991,6 +991,12 @@ void tabulateByPrecinctWithoutPrecincts() { @DisplayName("halting error when CVRs have a ranking larger than the max-configured value") void maxRankingValidationFails() { runTabulationTest("max_ranking_enforcement", - TabulatorSession.CastVoteRecordGenericParseException.class.toString()); + TabulatorSession.CastVoteRecordGenericParseException.class.toString()); + } + + @Test + @DisplayName("ES&S correctly ignores empty CVRs in multi-contest CVR") + void essMultiContest() { + runTabulationTest("ess_multi_contest"); } } diff --git a/src/test/resources/network/brightspots/rcv/test_data/ess_multi_contest/ess_multi_contest_config.json b/src/test/resources/network/brightspots/rcv/test_data/ess_multi_contest/ess_multi_contest_config.json new file mode 100644 index 000000000..6bae58fa6 --- /dev/null +++ b/src/test/resources/network/brightspots/rcv/test_data/ess_multi_contest/ess_multi_contest_config.json @@ -0,0 +1,86 @@ +{ + "tabulatorVersion" : "TEST", + "outputSettings" : { + "contestName" : "ES&S Multi-Contest", + "outputDirectory" : "output", + "contestDate" : "", + "contestJurisdiction" : "", + "contestOffice" : "", + "tabulateByBatch" : false, + "tabulateByPrecinct" : false, + "generateCdfJson" : false + }, + "cvrFileSources" : [ { + "filePath" : "ess_multi_contest_cvr.xlsx", + "contestId" : "", + "firstVoteColumnIndex" : "9", + "firstVoteRowIndex" : "2", + "idColumnIndex" : "4", + "batchColumnIndex" : "5", + "precinctColumnIndex" : "7", + "overvoteDelimiter" : "", + "provider" : "ess", + "overvoteLabel" : "overvote", + "skippedRankLabel" : "undervote", + "undeclaredWriteInLabel" : "Write-in", + "treatBlankAsUndeclaredWriteIn" : false + } ], + "candidates" : [ { + "name" : "Mickey Mouse", + "excluded" : false, + "aliases" : [ ] + }, { + "name" : "George Washington", + "excluded" : false, + "aliases" : [ ] + }, { + "name" : "Luke Skywalker", + "excluded" : false, + "aliases" : [ ] + }, { + "name" : "Zelda", + "excluded" : false, + "aliases" : [ ] + }, { + "name" : "Abraham Lincoln", + "excluded" : false, + "aliases" : [ ] + }, { + "name" : "Michael Jackson", + "excluded" : false, + "aliases" : [ ] + }, { + "name" : "Indiana Jones", + "excluded" : false, + "aliases" : [ ] + }, { + "name" : "Bugs Bunny", + "excluded" : false, + "aliases" : [ ] + }, { + "name" : "John Oliver", + "excluded" : false, + "aliases" : [ ] + } ], + "rules" : { + "tiebreakMode" : "useCandidateOrder", + "overvoteRule" : "exhaustImmediately", + "winnerElectionMode" : "singleWinnerMajority", + "randomSeed" : "", + "numberOfWinners" : "1", + "multiSeatBottomsUpPercentageThreshold" : "", + "decimalPlacesForVoteArithmetic" : "4", + "maxSkippedRanksAllowed" : "1", + "maxRankingsAllowed" : "5", + "nonIntegerWinningThreshold" : false, + "doesFirstRoundDetermineThreshold" : false, + "hareQuota" : false, + "batchElimination" : false, + "cutoffElimination" : false, + "continueUntilTwoCandidatesRemain" : false, + "stopTabulationEarlyAfterRound" : "", + "exhaustOnDuplicateCandidate" : false, + "rulesDescription" : "", + "treatBlankAsUndeclaredWriteIn" : false + } +} \ No newline at end of file diff --git a/src/test/resources/network/brightspots/rcv/test_data/ess_multi_contest/ess_multi_contest_cvr.xlsx b/src/test/resources/network/brightspots/rcv/test_data/ess_multi_contest/ess_multi_contest_cvr.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..3f982c988e1eaaaca233b07f353097b12df124a9 GIT binary patch literal 61956 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�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 Date: Wed, 11 Mar 2026 14:58:29 -0400 Subject: [PATCH 2/5] Proof of concept: each ignored ballot style must be 100% ignored --- .../brightspots/rcv/StreamingCvrReader.java | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/main/java/network/brightspots/rcv/StreamingCvrReader.java b/src/main/java/network/brightspots/rcv/StreamingCvrReader.java index a3595f20f..9c5244137 100644 --- a/src/main/java/network/brightspots/rcv/StreamingCvrReader.java +++ b/src/main/java/network/brightspots/rcv/StreamingCvrReader.java @@ -22,8 +22,10 @@ import java.io.File; import java.io.IOException; import java.security.InvalidParameterException; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.regex.Pattern; import javafx.util.Pair; import javax.xml.parsers.ParserConfigurationException; @@ -60,6 +62,8 @@ final class StreamingCvrReader extends BaseCvrReader { private final Integer batchColumnIndex; // 0-based column index of currentPrecinct name (if present) private final Integer precinctColumnIndex; + // 0-based column index of Ballot Style (if present) + private final Integer ballotStyleColumnIndex; // optional delimiter for cells that contain multiple candidates private final String overvoteDelimiter; private final String overvoteLabel; @@ -84,6 +88,8 @@ final class StreamingCvrReader extends BaseCvrReader { private int lastRankSeen; // flag indicating data issues during parsing private boolean encounteredDataErrors = false; + // Does this ballot style have any empty rankings? + private Map ballotStyleHasEmptyRankings = new HashMap<>(); StreamingCvrReader(ContestConfig config, RawContestConfig.CvrSource source) { super(config, source); @@ -104,6 +110,7 @@ final class StreamingCvrReader extends BaseCvrReader { !isNullOrBlank(source.getPrecinctColumnIndex()) ? Integer.parseInt(source.getPrecinctColumnIndex()) - 1 : null; + this.ballotStyleColumnIndex = /** AS A TEST **/ 8; this.overvoteDelimiter = source.getOvervoteDelimiter(); this.overvoteLabel = source.getOvervoteLabel(); this.skippedRankLabel = source.getSkippedRankLabel(); @@ -183,9 +190,25 @@ private void endCvr() { String computedCastVoteRecordId = String.format("%s-%d", OutputWriter.sanitizeStringForOutput(excelFileName), cvrIndex); - boolean areAllCandidatesEmpty = currentRankings.stream().allMatch( + boolean areAllCurrentRankingsEmpty = currentRankings.stream().allMatch( ranking -> isNullOrBlank(ranking.getValue())); - if (areAllCandidatesEmpty) { + if (ballotStyleColumnIndex != null) { + String ballotStyle = currentCvrData.size() > ballotStyleColumnIndex + ? currentCvrData.get(ballotStyleColumnIndex) + : null; + + if (ballotStyleHasEmptyRankings.containsKey(ballotStyle)) { + Boolean hasPreviousEmptyRankings = ballotStyleHasEmptyRankings.get(ballotStyle); + if (hasPreviousEmptyRankings != areAllCurrentRankingsEmpty) { + Logger.severe("Ballot style %s has some cast vote records with votes and some without. " + + "Cast vote record file: %s", ballotStyle, excelFileName); + encounteredDataErrors = true; + } else { + ballotStyleHasEmptyRankings.put(ballotStyle, areAllCurrentRankingsEmpty); + } + } + } + if (areAllCurrentRankingsEmpty) { Logger.auditable( "Skipping cast vote record with no votes for any candidates: %s", computedCastVoteRecordId); return; From 9eeb20a13850c95009b453bef698c6b7eaab9fb6 Mon Sep 17 00:00:00 2001 From: Armin Samii Date: Tue, 17 Mar 2026 15:29:58 -0400 Subject: [PATCH 3/5] Fix implementation and two erroneous tests --- .../network/brightspots/rcv/StreamingCvrReader.java | 12 +++++++----- ...mines_threshold_test_expected_detailed_report.csv | 4 ++-- ...ines_threshold_test_expected_detailed_report.json | 4 ++-- ...hold_tiebreaker_test_expected_detailed_report.csv | 4 ++-- ...old_tiebreaker_test_expected_detailed_report.json | 4 ++-- 5 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/main/java/network/brightspots/rcv/StreamingCvrReader.java b/src/main/java/network/brightspots/rcv/StreamingCvrReader.java index a3595f20f..cb538c4d0 100644 --- a/src/main/java/network/brightspots/rcv/StreamingCvrReader.java +++ b/src/main/java/network/brightspots/rcv/StreamingCvrReader.java @@ -82,6 +82,8 @@ final class StreamingCvrReader extends BaseCvrReader { private List cvrList; // last rankings cell observed for CVR in progress private int lastRankSeen; + // has this CVR had any non-blank candidate cells? + private boolean hasSeenAnyNonBlankCandidateCells; // flag indicating data issues during parsing private boolean encounteredDataErrors = false; @@ -172,6 +174,7 @@ private void beginCvr() { currentBatch = null; currentPrecinct = null; lastRankSeen = 0; + hasSeenAnyNonBlankCandidateCells = false; } // complete construction of new CVR object @@ -183,11 +186,9 @@ private void endCvr() { String computedCastVoteRecordId = String.format("%s-%d", OutputWriter.sanitizeStringForOutput(excelFileName), cvrIndex); - boolean areAllCandidatesEmpty = currentRankings.stream().allMatch( - ranking -> isNullOrBlank(ranking.getValue())); - if (areAllCandidatesEmpty) { - Logger.auditable( - "Skipping cast vote record with no votes for any candidates: %s", computedCastVoteRecordId); + if (!hasSeenAnyNonBlankCandidateCells) { + Logger.info( + "Skipping CVR with no votes for any candidates: %s", computedCastVoteRecordId); return; } @@ -267,6 +268,7 @@ private void cvrCell(int col, String cellData) { for (String candidate : candidates) { candidate = candidate.trim(); + hasSeenAnyNonBlankCandidateCells |= !candidate.isBlank(); if (candidates.length > 1 && (candidate.isBlank() || candidate.equals(skippedRankLabel))) { Logger.severe( "If a cell contains multiple candidates split by the overvote delimiter, " diff --git a/src/test/resources/network/brightspots/rcv/test_data/first_round_determines_threshold_test/first_round_determines_threshold_test_expected_detailed_report.csv b/src/test/resources/network/brightspots/rcv/test_data/first_round_determines_threshold_test/first_round_determines_threshold_test_expected_detailed_report.csv index 9e88ac95c..6a9af43e1 100644 --- a/src/test/resources/network/brightspots/rcv/test_data/first_round_determines_threshold_test/first_round_determines_threshold_test_expected_detailed_report.csv +++ b/src/test/resources/network/brightspots/rcv/test_data/first_round_determines_threshold_test/first_round_determines_threshold_test_expected_detailed_report.csv @@ -12,8 +12,8 @@ Final Threshold,9 Contest Summary Number to be Elected,1 Number of Candidates,3 -Total Number of Ballots,19 -Number of Undervotes (No Rankings),3 +Total Number of Ballots,16 +Number of Undervotes (No Rankings),0 Rounds,Round 1 Votes,% of vote,transfer,Round 2 Votes,% of vote,transfer Eliminated,"Haadoow, Hamza A.*",,,,, diff --git a/src/test/resources/network/brightspots/rcv/test_data/first_round_determines_threshold_test/first_round_determines_threshold_test_expected_detailed_report.json b/src/test/resources/network/brightspots/rcv/test_data/first_round_determines_threshold_test/first_round_determines_threshold_test_expected_detailed_report.json index f3400513b..1db43655d 100644 --- a/src/test/resources/network/brightspots/rcv/test_data/first_round_determines_threshold_test/first_round_determines_threshold_test_expected_detailed_report.json +++ b/src/test/resources/network/brightspots/rcv/test_data/first_round_determines_threshold_test/first_round_determines_threshold_test_expected_detailed_report.json @@ -49,7 +49,7 @@ "finalThreshold" : "9", "numCandidates" : 4, "numWinners" : 1, - "totalNumBallots" : "19", - "undervotes" : 3 + "totalNumBallots" : "16", + "undervotes" : 0 } } \ No newline at end of file diff --git a/src/test/resources/network/brightspots/rcv/test_data/first_round_determines_threshold_tiebreaker_test/first_round_determines_threshold_tiebreaker_test_expected_detailed_report.csv b/src/test/resources/network/brightspots/rcv/test_data/first_round_determines_threshold_tiebreaker_test/first_round_determines_threshold_tiebreaker_test_expected_detailed_report.csv index 06177766c..97070a4f4 100644 --- a/src/test/resources/network/brightspots/rcv/test_data/first_round_determines_threshold_tiebreaker_test/first_round_determines_threshold_tiebreaker_test_expected_detailed_report.csv +++ b/src/test/resources/network/brightspots/rcv/test_data/first_round_determines_threshold_tiebreaker_test/first_round_determines_threshold_tiebreaker_test_expected_detailed_report.csv @@ -12,8 +12,8 @@ Final Threshold,9 Contest Summary Number to be Elected,1 Number of Candidates,3 -Total Number of Ballots,19 -Number of Undervotes (No Rankings),2 +Total Number of Ballots,17 +Number of Undervotes (No Rankings),0 Rounds,Round 1 Votes,% of vote,transfer,Round 2 Votes,% of vote,transfer Eliminated,"Haadoow, Hamza A.",,,,, diff --git a/src/test/resources/network/brightspots/rcv/test_data/first_round_determines_threshold_tiebreaker_test/first_round_determines_threshold_tiebreaker_test_expected_detailed_report.json b/src/test/resources/network/brightspots/rcv/test_data/first_round_determines_threshold_tiebreaker_test/first_round_determines_threshold_tiebreaker_test_expected_detailed_report.json index a6236fb37..b0be11052 100644 --- a/src/test/resources/network/brightspots/rcv/test_data/first_round_determines_threshold_tiebreaker_test/first_round_determines_threshold_tiebreaker_test_expected_detailed_report.json +++ b/src/test/resources/network/brightspots/rcv/test_data/first_round_determines_threshold_tiebreaker_test/first_round_determines_threshold_tiebreaker_test_expected_detailed_report.json @@ -49,7 +49,7 @@ "finalThreshold" : "9", "numCandidates" : 4, "numWinners" : 1, - "totalNumBallots" : "19", - "undervotes" : 2 + "totalNumBallots" : "17", + "undervotes" : 0 } } \ No newline at end of file From ea1b2c688c880606f16f49d5c0c24f03d323d54a Mon Sep 17 00:00:00 2001 From: Armin Samii Date: Tue, 17 Mar 2026 15:49:34 -0400 Subject: [PATCH 4/5] info -> auditable --- src/main/java/network/brightspots/rcv/StreamingCvrReader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/network/brightspots/rcv/StreamingCvrReader.java b/src/main/java/network/brightspots/rcv/StreamingCvrReader.java index cb538c4d0..7a35d16e3 100644 --- a/src/main/java/network/brightspots/rcv/StreamingCvrReader.java +++ b/src/main/java/network/brightspots/rcv/StreamingCvrReader.java @@ -187,7 +187,7 @@ private void endCvr() { String.format("%s-%d", OutputWriter.sanitizeStringForOutput(excelFileName), cvrIndex); if (!hasSeenAnyNonBlankCandidateCells) { - Logger.info( + Logger.auditable( "Skipping CVR with no votes for any candidates: %s", computedCastVoteRecordId); return; } From b4e6620b79fc51942cf4e95cb673936ad81ccd34 Mon Sep 17 00:00:00 2001 From: Armin Samii Date: Tue, 17 Mar 2026 15:51:58 -0400 Subject: [PATCH 5/5] fix checkstyle --- .../java/network/brightspots/rcv/StreamingCvrReader.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/network/brightspots/rcv/StreamingCvrReader.java b/src/main/java/network/brightspots/rcv/StreamingCvrReader.java index 7d8d7149e..dc43463b1 100644 --- a/src/main/java/network/brightspots/rcv/StreamingCvrReader.java +++ b/src/main/java/network/brightspots/rcv/StreamingCvrReader.java @@ -112,7 +112,7 @@ final class StreamingCvrReader extends BaseCvrReader { !isNullOrBlank(source.getPrecinctColumnIndex()) ? Integer.parseInt(source.getPrecinctColumnIndex()) - 1 : null; - this.ballotStyleColumnIndex = /** AS A TEST **/ null; + this.ballotStyleColumnIndex = null; // to be implemented this.overvoteDelimiter = source.getOvervoteDelimiter(); this.overvoteLabel = source.getOvervoteLabel(); this.skippedRankLabel = source.getSkippedRankLabel(); @@ -199,7 +199,8 @@ private void endCvr() { : null; if (ballotStyleHasEmptyRankings.containsKey(ballotStyle)) { - Boolean hasPreviouslySeenNonBlankCandidateCells = ballotStyleHasEmptyRankings.get(ballotStyle); + Boolean hasPreviouslySeenNonBlankCandidateCells = + ballotStyleHasEmptyRankings.get(ballotStyle); if (hasPreviouslySeenNonBlankCandidateCells != hasSeenAnyNonBlankCandidateCells) { Logger.severe("Ballot style %s has some cast vote records with votes and some without. " + "Cast vote record file: %s", ballotStyle, excelFileName);