diff --git a/client/src/component/AuditReportForm.tsx b/client/src/component/AuditReportForm.tsx index 65c746aa..6e420fe5 100644 --- a/client/src/component/AuditReportForm.tsx +++ b/client/src/component/AuditReportForm.tsx @@ -24,8 +24,8 @@ const REPORT_TYPES: ReportType[] = [ {key:'contest_selection', label:'Contest selection'}, {key:'contests_by_county', label:'Contests by county'}, {key:'seed', label:'Seed'}, - {key:'tabulate', label:'Tabulate'}, - {key:'tabulate_county', label:'Tabulate county'}, + {key:'tabulate_plurality', label:'Tabulate'}, + {key:'tabulate_county_plurality', label:'Tabulate county'}, {key:'upload_status', label:'Upload status'}, {key:'ResultReport', label:'Result Report'}, {key:'ActivityReport', label:'Activity Report'}, diff --git a/server/eclipse-project/src/main/java/us/freeandfair/corla/query/ExportQueries.java b/server/eclipse-project/src/main/java/us/freeandfair/corla/query/ExportQueries.java index 93393318..d7772a84 100644 --- a/server/eclipse-project/src/main/java/us/freeandfair/corla/query/ExportQueries.java +++ b/server/eclipse-project/src/main/java/us/freeandfair/corla/query/ExportQueries.java @@ -182,7 +182,7 @@ public static List getSqlFolderFiles() { final String folder = "sql"; final String[] fileNames = {"batch_count_comparison.sql", "contest.sql", "contest_comparison.sql", "contest_selection.sql", "contests_by_county.sql", - "tabulate.sql", "tabulate_county.sql", "upload_status.sql", "seed.sql", + "tabulate_plurality.sql", "tabulate_county_plurality.sql", "upload_status.sql", "seed.sql", "summarize_IRV.sql", "ranked_ballot_interpretation.sql"}; for (final String f : fileNames) { paths.add(String.format("%s/%s", folder, f)); diff --git a/server/eclipse-project/src/main/java/us/freeandfair/corla/report/CountyReport.java b/server/eclipse-project/src/main/java/us/freeandfair/corla/report/CountyReport.java index bbb8c838..efc6ee44 100644 --- a/server/eclipse-project/src/main/java/us/freeandfair/corla/report/CountyReport.java +++ b/server/eclipse-project/src/main/java/us/freeandfair/corla/report/CountyReport.java @@ -543,6 +543,7 @@ public Workbook generateExcelWorkbook() { max_cell_number = Math.max(max_cell_number, cell_number); row_number = row_number - 1; // don't skip a line for the first contest + // Print the county contest results. These do not need IRV summaries. for (final CountyContestResult ccr : my_driving_contest_results) { StateReport.CellStatus status = StateReport.makeContestSummary(row_number, max_cell_number, ccr, summary_sheet, diff --git a/server/eclipse-project/src/main/java/us/freeandfair/corla/report/StateReport.java b/server/eclipse-project/src/main/java/us/freeandfair/corla/report/StateReport.java index 33595e9b..f891a6de 100644 --- a/server/eclipse-project/src/main/java/us/freeandfair/corla/report/StateReport.java +++ b/server/eclipse-project/src/main/java/us/freeandfair/corla/report/StateReport.java @@ -483,6 +483,7 @@ public Workbook generateExcelWorkbook() { // County-level results really don't make sense for IRV. Therefore print a // very brief summary and a reference to the assertions csv. + // At this stage, we don't want to print IRV choices. for (final CountyContestResult ccr : e.getValue().drivingContestResults()) { final CellStatus status = makeContestSummary(row_number, max_cell_number, ccr, summary_sheet, @@ -684,15 +685,15 @@ public Workbook generateExcelWorkbook() { /** * Make the cells for one contest's worth of summary data. For IRV, this just prints the - * contest name, choices, and the fact that it's IRV. For plurality, it outputs much more - * detailed data. - * @param row_number Number of the current row, which is incremented as we progress and - * included in Cell Status on return. - * @param max_cell_number The maximum cell number used, which is included in Cell Status on return. - * @param ccr The County Contest Result to be summarized. - * @param summary_sheet The sheet to be added to. - * @param bold_style All these styles are included (though they could be class-level finals) - * in case a caller from another class wants different styles. + * contest name, the fact that it's IRV, and a message about where to find more information. + * For plurality, it outputs much more detailed data. + * @param row_number Number of the current row, which is incremented as we progress and + * included in Cell Status on return. + * @param max_cell_number The maximum cell number used, which is included in Cell Status on return. + * @param ccr The County Contest Result to be summarized. + * @param summary_sheet The sheet to be added to. + * @param bold_style All these styles are included (though they could be class-level finals) + * in case a caller from another class wants different styles. * @param integer_style " * @param bold_right_style " * @param decimal_style " @@ -704,52 +705,50 @@ public Workbook generateExcelWorkbook() { protected static CellStatus makeContestSummary(int row_number, int max_cell_number, final CountyContestResult ccr, final Sheet summary_sheet, final CellStyle bold_style, final CellStyle integer_style, final CellStyle bold_right_style, final CellStyle decimal_style, final CellStyle standard_style, final CellStyle standard_right_style) { - row_number++; - Row row = summary_sheet.createRow(row_number++); - int cell_number = 0; + row_number++; + Row row = summary_sheet.createRow(row_number++); + int cell_number = 0; - Cell cell = row.createCell(cell_number++); - cell.setCellStyle(bold_style); + Cell cell = row.createCell(cell_number++); + cell.setCellStyle(bold_style); - final boolean isIRV = ccr.contest().description().equals(ContestType.IRV.toString()); + final boolean isIRV = ccr.contest().description().equals(ContestType.IRV.toString()); - if (isIRV) { - cell.setCellValue(ccr.contest().name() + " - IRV"); - } else { - cell.setCellValue(ccr.contest().name() + " - Vote For " + ccr.contest().votesAllowed()); - } + // Print the contest name, with a summary message about the contest type + final String contestname = ccr.contest().name() + (isIRV ? " - IRV" : " - Vote For " + ccr.contest().votesAllowed()); + cell.setCellValue(contestname); - // Keep the choices column, for both IRV and plurality. + if (isIRV) { + // If it's IRV, the summary data is in the assertions csv. Add a message, that's all. + cell = row.createCell(cell_number++); + cell.setCellStyle(standard_style); + cell.setCellValue("See assertions csv"); + } else { + // Plurality. + // Data headers - choices, winners, vote tallies, margins. cell = row.createCell(cell_number++); cell.setCellStyle(bold_style); cell.setCellValue("Choice"); - if (isIRV) { - // If it's IRV, the summary data is in the assertions csv. Add a message. - cell = row.createCell(cell_number++); - cell.setCellStyle(standard_style); - cell.setCellValue("See assertions csv"); - } else { - // Plurality. Print the other data - winners, vote tallies, margins. - cell = row.createCell(cell_number++); - cell.setCellStyle(bold_right_style); - cell.setCellValue("W/L"); + cell = row.createCell(cell_number++); + cell.setCellStyle(bold_right_style); + cell.setCellValue("W/L"); - cell = row.createCell(cell_number++); - cell.setCellStyle(bold_right_style); - cell.setCellValue("Votes"); + cell = row.createCell(cell_number++); + cell.setCellStyle(bold_right_style); + cell.setCellValue("Votes"); - cell = row.createCell(cell_number++); - cell.setCellStyle(bold_right_style); - cell.setCellValue("Margin"); + cell = row.createCell(cell_number++); + cell.setCellStyle(bold_right_style); + cell.setCellValue("Margin"); - cell = row.createCell(cell_number++); - cell.setCellStyle(bold_right_style); - cell.setCellValue("Diluted Margin %"); - } + cell = row.createCell(cell_number++); + cell.setCellStyle(bold_right_style); + cell.setCellValue("Diluted Margin %"); + // Now print the actual data. for (final String choice : ccr.rankedChoices()) { - // List all the choices, for both IRV and plurality. + // List all the choices. row = summary_sheet.createRow(row_number++); max_cell_number = Math.max(max_cell_number, cell_number); @@ -758,40 +757,39 @@ protected static CellStatus makeContestSummary(int row_number, int max_cell_numb cell.setCellStyle(standard_style); cell.setCellValue(choice); - if (!isIRV) { - // Print the other data - winners, vote tallies, margins - only for plurality, not IRV. - cell = row.createCell(cell_number++); - cell.setCellStyle(standard_right_style); - if ((ccr.winners().stream().anyMatch(w -> w.equalsIgnoreCase(choice)))) { - cell.setCellValue("W"); - } else { - cell.setCellValue("L"); - } + // Winners, vote tallies, margins. + cell = row.createCell(cell_number++); + cell.setCellStyle(standard_right_style); + if ((ccr.winners().stream().anyMatch(w -> w.equalsIgnoreCase(choice)))) { + cell.setCellValue("W"); + } else { + cell.setCellValue("L"); + } + + cell = row.createCell(cell_number++); + cell.setCellStyle(integer_style); + cell.setCellType(CellType.NUMERIC); + cell.setCellValue(ccr.voteTotals().get(choice)); + if (ccr.winners().contains(choice)) { cell = row.createCell(cell_number++); cell.setCellStyle(integer_style); cell.setCellType(CellType.NUMERIC); - cell.setCellValue(ccr.voteTotals().get(choice)); - - if (ccr.winners().contains(choice)) { - cell = row.createCell(cell_number++); - cell.setCellStyle(integer_style); - cell.setCellType(CellType.NUMERIC); - final OptionalInt margin = ccr.marginToNearestLoser(choice); - if (margin.isPresent()) { - cell.setCellValue(margin.getAsInt()); - } + final OptionalInt margin = ccr.marginToNearestLoser(choice); + if (margin.isPresent()) { + cell.setCellValue(margin.getAsInt()); + } - cell = row.createCell(cell_number++); - cell.setCellStyle(decimal_style); - cell.setCellType(CellType.NUMERIC); - final BigDecimal diluted_margin = ccr.countyDilutedMarginToNearestLoser(choice); - if (diluted_margin != null) { - cell.setCellValue(diluted_margin.doubleValue() * 100); - } + cell = row.createCell(cell_number++); + cell.setCellStyle(decimal_style); + cell.setCellType(CellType.NUMERIC); + final BigDecimal diluted_margin = ccr.countyDilutedMarginToNearestLoser(choice); + if (diluted_margin != null) { + cell.setCellValue(diluted_margin.doubleValue() * 100); } } } + } return new CellStatus(row_number, max_cell_number); } diff --git a/server/eclipse-project/src/main/resources/sql/summarize_IRV.sql b/server/eclipse-project/src/main/resources/sql/summarize_IRV.sql index 3852f431..0b75fdf2 100644 --- a/server/eclipse-project/src/main/resources/sql/summarize_IRV.sql +++ b/server/eclipse-project/src/main/resources/sql/summarize_IRV.sql @@ -1,7 +1,29 @@ -- Show the IRV summaries, sorted by contest name. -- This is usually a (unique) winner and an empty error, but may also be a -- blank winner and a non-empty error with an explanatory message. +-- This also adds a column to indicate whether the contest was targeted for audit. This requires +-- a join with the contest_to_audit table, which is slightly complicated by the fact that that table +-- stores _one of_ the contest ids for multi-county contests, so we have to check whether there is +-- _any_ contest of the same name with an ID in the contest_to_audit table. This simply selects the +-- first audit reason, in the very unlikely event that one contest is selected for both COUNTY_WIDE +-- and STATE_WIDE reasons. -SELECT gas.contest_name, gas.winner, gas.error, gas.message +SELECT gas.contest_name, COALESCE(targets.targeted_reason, 'Not targeted') as target_reason, gas.winner, gas.error, gas.message FROM generate_assertions_summary as gas -ORDER BY contest_name; + -- This sub-query attaches audit target reasons (aggregated), if any, to contest names, and + -- 'Not targeted' otherwise. + -- Inner join because we only want the ones in the generate_assertions_summary table. +LEFT JOIN ( + SELECT contest.name as name, contest_to_audit.reason as targeted_reason + FROM contest_to_audit + -- Again an inner join, because we only want the ones in the table of targeted contests. + -- This will give us the one row of the contest table that happens to have been identified in the + -- contest_to_audit table + INNER JOIN contest + ON contest_to_audit.contest_id = contest.id +) AS targets +ON gas.contest_name = targets.name; + + + + diff --git a/server/eclipse-project/src/main/resources/sql/tabulate_county.sql b/server/eclipse-project/src/main/resources/sql/tabulate_county_plurality.sql similarity index 100% rename from server/eclipse-project/src/main/resources/sql/tabulate_county.sql rename to server/eclipse-project/src/main/resources/sql/tabulate_county_plurality.sql diff --git a/server/eclipse-project/src/main/resources/sql/tabulate.sql b/server/eclipse-project/src/main/resources/sql/tabulate_plurality.sql similarity index 100% rename from server/eclipse-project/src/main/resources/sql/tabulate.sql rename to server/eclipse-project/src/main/resources/sql/tabulate_plurality.sql