diff --git a/apps/admin/backend/src/__snapshots__/app.exports.test.ts.snap b/apps/admin/backend/src/__snapshots__/app.exports.test.ts.snap
deleted file mode 100644
index b4709f024e..0000000000
--- a/apps/admin/backend/src/__snapshots__/app.exports.test.ts.snap
+++ /dev/null
@@ -1,96 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`batch export 1`] = `
-"Batch ID,Batch Name,Tabulator,Number of Ballots,"Governor - Ballots Cast","Governor - Undervotes","Governor - Overvotes","Governor - Josiah Bartlett","Governor - Hannah Dustin","Governor - John Spencer","Governor - Write In","United States Senator - Ballots Cast","United States Senator - Undervotes","United States Senator - Overvotes","United States Senator - John Langdon","United States Senator - William Preston","United States Senator - Write In","Representative in Congress - Ballots Cast","Representative in Congress - Undervotes","Representative in Congress - Overvotes","Representative in Congress - Jeremiah Smith","Representative in Congress - Nicholas Gilman","Representative in Congress - Richard Coote","Representative in Congress - Write In","Executive Councilor - Ballots Cast","Executive Councilor - Undervotes","Executive Councilor - Overvotes","Executive Councilor - Anne Waldron","Executive Councilor - Daniel Webster","Executive Councilor - Write In","State Senator - Ballots Cast","State Senator - Undervotes","State Senator - Overvotes","State Senator - James Poole","State Senator - Matthew Thornton","State Senator - Write In","State Representatives Hillsborough District 34 - Ballots Cast","State Representatives Hillsborough District 34 - Undervotes","State Representatives Hillsborough District 34 - Overvotes","State Representatives Hillsborough District 34 - Obadiah Carrigan","State Representatives Hillsborough District 34 - Mary Baker Eddy","State Representatives Hillsborough District 34 - Samuel Bell","State Representatives Hillsborough District 34 - Samuel Livermore","State Representatives Hillsborough District 34 - Elijah Miller","State Representatives Hillsborough District 34 - Isaac Hill","State Representatives Hillsborough District 34 - Abigail Bartlett","State Representatives Hillsborough District 34 - Jacob Freese","State Representatives Hillsborough District 34 - Write In","State Representative Hillsborough District 37 - Ballots Cast","State Representative Hillsborough District 37 - Undervotes","State Representative Hillsborough District 37 - Overvotes","State Representative Hillsborough District 37 - Abeil Foster","State Representative Hillsborough District 37 - Charles H. Hersey","State Representative Hillsborough District 37 - William Lovejoy","State Representative Hillsborough District 37 - Write In","Sheriff - Ballots Cast","Sheriff - Undervotes","Sheriff - Overvotes","Sheriff - Edward Randolph","Sheriff - Write In","County Attorney - Ballots Cast","County Attorney - Undervotes","County Attorney - Overvotes","County Attorney - Ezra Bartlett","County Attorney - Mary Woolson","County Attorney - Write In","County Treasurer - Ballots Cast","County Treasurer - Undervotes","County Treasurer - Overvotes","County Treasurer - John Smith","County Treasurer - Jane Jones","County Treasurer - Write In","Register of Deeds - Ballots Cast","Register of Deeds - Undervotes","Register of Deeds - Overvotes","Register of Deeds - John Mann","Register of Deeds - Ellen A. Stileman","Register of Deeds - Write In","Register of Probate - Ballots Cast","Register of Probate - Undervotes","Register of Probate - Overvotes","Register of Probate - Nathaniel Parker","Register of Probate - Claire Cutts","Register of Probate - Write In","County Commissioner - Ballots Cast","County Commissioner - Undervotes","County Commissioner - Overvotes","County Commissioner - Ichabod Goodwin","County Commissioner - Valbe Cady","County Commissioner - Write In","Constitutional Amendment Question 1 - Ballots Cast","Constitutional Amendment Question 1 - Undervotes","Constitutional Amendment Question 1 - Overvotes","Constitutional Amendment Question 1 - Yes","Constitutional Amendment Question 1 - No"
-9822c71014,9822c71014,VX-00-000,184,184,2,4,2,2,172,2,184,2,2,2,176,2,184,2,4,2,2,172,2,184,2,2,2,176,2,184,2,2,2,176,2,184,12,30,60,58,56,56,56,56,56,56,56,184,2,4,2,2,172,2,184,2,0,180,2,184,2,2,2,176,2,184,2,2,2,176,2,184,2,2,2,176,2,184,2,2,2,176,2,184,2,2,2,176,2,184,178,2,2,2
-"
-`;
-
-exports[`sems export 1`] = `
-{
- "aquarium-council-fish": {
- "metadata": {
- "ballots": 28,
- "overvotes": 8,
- "undervotes": 6,
- },
- "tallies": {
- "manta-ray": 10,
- "pufferfish": 8,
- "rockfish": 8,
- "triggerfish": 8,
- "write-in": 8,
- },
- },
- "best-animal-fish": {
- "metadata": {
- "ballots": 28,
- "overvotes": 2,
- "undervotes": 2,
- },
- "tallies": {
- "salmon": 22,
- "seahorse": 2,
- },
- },
- "best-animal-mammal": {
- "metadata": {
- "ballots": 28,
- "overvotes": 4,
- "undervotes": 2,
- },
- "tallies": {
- "fox": 18,
- "horse": 2,
- "otter": 2,
- },
- },
- "fishing": {
- "metadata": {
- "ballots": 56,
- "overvotes": 4,
- "undervotes": 44,
- },
- "tallies": {
- "no": 4,
- "yes": 4,
- },
- },
- "new-zoo-either": {
- "metadata": {
- "ballots": 56,
- "overvotes": 4,
- "undervotes": 44,
- },
- "tallies": {
- "no": 4,
- "yes": 4,
- },
- },
- "new-zoo-pick": {
- "metadata": {
- "ballots": 56,
- "overvotes": 4,
- "undervotes": 44,
- },
- "tallies": {
- "no": 4,
- "yes": 4,
- },
- },
- "zoo-council-mammal": {
- "metadata": {
- "ballots": 28,
- "overvotes": 6,
- "undervotes": 12,
- },
- "tallies": {
- "elephant": 12,
- "kangaroo": 12,
- "lion": 14,
- "write-in": 12,
- "zebra": 16,
- },
- },
-}
-`;
diff --git a/apps/admin/backend/src/__snapshots__/app.sems.test.ts.snap b/apps/admin/backend/src/__snapshots__/app.sems.test.ts.snap
new file mode 100644
index 0000000000..5505015833
--- /dev/null
+++ b/apps/admin/backend/src/__snapshots__/app.sems.test.ts.snap
@@ -0,0 +1,90 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`sems export 1`] = `
+{
+ "aquarium-council-fish": {
+ "metadata": {
+ "ballots": 28,
+ "overvotes": 8,
+ "undervotes": 6,
+ },
+ "tallies": {
+ "manta-ray": 10,
+ "pufferfish": 8,
+ "rockfish": 8,
+ "triggerfish": 8,
+ "write-in": 8,
+ },
+ },
+ "best-animal-fish": {
+ "metadata": {
+ "ballots": 28,
+ "overvotes": 2,
+ "undervotes": 2,
+ },
+ "tallies": {
+ "salmon": 22,
+ "seahorse": 2,
+ },
+ },
+ "best-animal-mammal": {
+ "metadata": {
+ "ballots": 28,
+ "overvotes": 4,
+ "undervotes": 2,
+ },
+ "tallies": {
+ "fox": 18,
+ "horse": 2,
+ "otter": 2,
+ },
+ },
+ "fishing": {
+ "metadata": {
+ "ballots": 56,
+ "overvotes": 4,
+ "undervotes": 44,
+ },
+ "tallies": {
+ "no": 4,
+ "yes": 4,
+ },
+ },
+ "new-zoo-either": {
+ "metadata": {
+ "ballots": 56,
+ "overvotes": 4,
+ "undervotes": 44,
+ },
+ "tallies": {
+ "no": 4,
+ "yes": 4,
+ },
+ },
+ "new-zoo-pick": {
+ "metadata": {
+ "ballots": 56,
+ "overvotes": 4,
+ "undervotes": 44,
+ },
+ "tallies": {
+ "no": 4,
+ "yes": 4,
+ },
+ },
+ "zoo-council-mammal": {
+ "metadata": {
+ "ballots": 28,
+ "overvotes": 6,
+ "undervotes": 12,
+ },
+ "tallies": {
+ "elephant": 12,
+ "kangaroo": 12,
+ "lion": 14,
+ "write-in": 12,
+ "zebra": 16,
+ },
+ },
+}
+`;
diff --git a/apps/admin/backend/src/app.exports.test.ts b/apps/admin/backend/src/app.sems.test.ts
similarity index 50%
rename from apps/admin/backend/src/app.exports.test.ts
rename to apps/admin/backend/src/app.sems.test.ts
index e30daa7526..b3200e0a5e 100644
--- a/apps/admin/backend/src/app.exports.test.ts
+++ b/apps/admin/backend/src/app.sems.test.ts
@@ -1,14 +1,8 @@
-import {
- electionGridLayoutNewHampshireAmherstFixtures,
- electionTwoPartyPrimaryFixtures,
-} from '@votingworks/fixtures';
+import { electionTwoPartyPrimaryFixtures } from '@votingworks/fixtures';
import {
BooleanEnvironmentVariableName,
getFeatureFlagMock,
} from '@votingworks/utils';
-import { tmpNameSync } from 'tmp';
-import { readFileSync } from 'fs';
-import { LogEventId } from '@votingworks/logging';
import {
buildTestEnvironment,
configureMachine,
@@ -41,50 +35,6 @@ afterEach(() => {
featureFlagMock.resetFeatureFlags();
});
-test('batch export', async () => {
- const { electionDefinition, castVoteRecordExport } =
- electionGridLayoutNewHampshireAmherstFixtures;
-
- const { apiClient, auth, logger } = buildTestEnvironment();
- await configureMachine(apiClient, auth, electionDefinition);
- mockElectionManagerAuth(auth, electionDefinition.electionHash);
-
- const loadFileResult = await apiClient.addCastVoteRecordFile({
- path: castVoteRecordExport.asDirectoryPath(),
- });
- loadFileResult.assertOk('load file failed');
-
- const path = tmpNameSync();
- const exportResult = await apiClient.exportBatchResults({ path });
- expect(exportResult.isOk()).toEqual(true);
- expect(readFileSync(path, 'utf-8').toString()).toMatchSnapshot();
- expect(logger.log).toHaveBeenLastCalledWith(
- LogEventId.FileSaved,
- 'election_manager',
- {
- disposition: 'success',
- filename: path,
- message: `Saved batch results to ${path} on the USB drive.`,
- }
- );
-
- // mock a failure
- const offLimitsPath = '/root/hidden';
- const failedExportResult = await apiClient.exportBatchResults({
- path: offLimitsPath,
- });
- expect(failedExportResult.isErr()).toEqual(true);
- expect(logger.log).toHaveBeenLastCalledWith(
- LogEventId.FileSaved,
- 'election_manager',
- {
- disposition: 'failure',
- filename: offLimitsPath,
- message: `Failed to save batch results to ${offLimitsPath} on the USB drive.`,
- }
- );
-});
-
test('sems export', async () => {
const { electionDefinition, castVoteRecordExport } =
electionTwoPartyPrimaryFixtures;
diff --git a/apps/admin/backend/src/app.ts b/apps/admin/backend/src/app.ts
index b4eec9200a..8ccbcb0a88 100644
--- a/apps/admin/backend/src/app.ts
+++ b/apps/admin/backend/src/app.ts
@@ -72,7 +72,6 @@ import {
import { handleEnteredWriteInCandidateData } from './util/manual_results';
import { addFileToZipStream } from './util/zip';
import { exportFile } from './util/export_file';
-import { generateBatchResultsFile } from './exports/batch_results';
import { tabulateElectionResults } from './tabulation/full_results';
import { getSemsExportableTallies } from './exports/sems_tallies';
import { generateTallyReportCsv } from './exports/csv_tally_report';
@@ -691,43 +690,6 @@ function buildApi({
return store.getScannerBatches(loadCurrentElectionIdOrThrow(workspace));
},
- async exportBatchResults(input: {
- path: string;
- }): Promise {
- debug('exporting batch results CSV file');
- const electionId = loadCurrentElectionIdOrThrow(workspace);
- const {
- electionDefinition: { election },
- } = assertDefined(store.getElection(electionId));
-
- const exportFileResult = await exportFile({
- path: input.path,
- data: generateBatchResultsFile({
- election,
- batchGroupedResults: await tabulateElectionResults({
- electionId,
- store,
- groupBy: { groupByBatch: true },
- }),
- allBatchMetadata: store.getScannerBatches(electionId),
- }),
- });
-
- await logger.log(
- LogEventId.FileSaved,
- assertDefined(await getUserRole()),
- {
- disposition: exportFileResult.isOk() ? 'success' : 'failure',
- message: `${
- exportFileResult.isOk() ? 'Saved' : 'Failed to save'
- } batch results to ${input.path} on the USB drive.`,
- filename: input.path,
- }
- );
-
- return exportFileResult;
- },
-
async getSemsExportableTallies(): Promise {
const electionId = loadCurrentElectionIdOrThrow(workspace);
diff --git a/apps/admin/backend/src/exports/batch_results.test.ts b/apps/admin/backend/src/exports/batch_results.test.ts
deleted file mode 100644
index d50e369dec..0000000000
--- a/apps/admin/backend/src/exports/batch_results.test.ts
+++ /dev/null
@@ -1,104 +0,0 @@
-import { electionTwoPartyPrimaryDefinition } from '@votingworks/fixtures';
-import { Tabulation } from '@votingworks/types';
-import { buildElectionResultsFixture } from '@votingworks/utils';
-import { Buffer } from 'buffer';
-import { generateBatchResultsFile } from './batch_results';
-import { ScannerBatch } from '../types';
-
-test('generateBatchResultsFile', async () => {
- const { election } = electionTwoPartyPrimaryDefinition;
- const batchGroupedResults: Tabulation.ElectionResultsGroupMap = {
- 'root&batchId=batch-1': buildElectionResultsFixture({
- election,
- cardCounts: {
- hmpb: [30, 29],
- bmd: 5,
- },
- contestResultsSummaries: {
- 'zoo-council-mammal': {
- type: 'candidate',
- ballots: 35,
- undervotes: 2,
- overvotes: 3,
- officialOptionTallies: {
- lion: 25,
- [Tabulation.GENERIC_WRITE_IN_ID]: 5,
- },
- },
- fishing: {
- type: 'yesno',
- ballots: 34,
- undervotes: 1,
- overvotes: 3,
- yesTally: 25,
- noTally: 5,
- },
- },
- includeGenericWriteIn: true,
- }),
- 'root&batchId=batch-2': buildElectionResultsFixture({
- election,
- cardCounts: {
- hmpb: [29, 29],
- bmd: 1,
- },
- contestResultsSummaries: {
- 'zoo-council-mammal': {
- type: 'candidate',
- ballots: 30,
- undervotes: 1,
- overvotes: 3,
- officialOptionTallies: {
- lion: 20,
- [Tabulation.GENERIC_WRITE_IN_ID]: 6,
- },
- },
- fishing: {
- type: 'yesno',
- ballots: 30,
- undervotes: 1,
- overvotes: 5,
- yesTally: 20,
- noTally: 4,
- },
- },
- includeGenericWriteIn: true,
- }),
- };
-
- const allBatchMetadata: ScannerBatch[] = [
- {
- electionId: 'id',
- batchId: 'batch-1',
- label: 'Batch 1',
- scannerId: 'scanner-1',
- },
- {
- electionId: 'id',
- batchId: 'batch-2',
- label: 'Batch 2',
- scannerId: 'scanner-1',
- },
- ];
-
- const csvExportStream = generateBatchResultsFile({
- election,
- batchGroupedResults,
- allBatchMetadata,
- });
-
- const chunks = [];
-
- for await (const chunk of csvExportStream) {
- chunks.push(Buffer.from(chunk));
- }
-
- const csvString = Buffer.concat(chunks).toString('utf-8');
-
- expect(csvString).toMatchInlineSnapshot(`
- "Batch ID,Batch Name,Tabulator,Number of Ballots,"Mammal Party Best Animal - Ballots Cast","Mammal Party Best Animal - Undervotes","Mammal Party Best Animal - Overvotes","Mammal Party Best Animal - Horse","Mammal Party Best Animal - Otter","Mammal Party Best Animal - Fox","Fish Party Best Animal - Ballots Cast","Fish Party Best Animal - Undervotes","Fish Party Best Animal - Overvotes","Fish Party Best Animal - Seahorse","Fish Party Best Animal - Salmon","Mammal Party Zoo Council - Ballots Cast","Mammal Party Zoo Council - Undervotes","Mammal Party Zoo Council - Overvotes","Mammal Party Zoo Council - Zebra","Mammal Party Zoo Council - Lion","Mammal Party Zoo Council - Kangaroo","Mammal Party Zoo Council - Elephant","Mammal Party Zoo Council - Write In","Fish Party Zoo Council - Ballots Cast","Fish Party Zoo Council - Undervotes","Fish Party Zoo Council - Overvotes","Fish Party Zoo Council - Manta Ray","Fish Party Zoo Council - Pufferfish","Fish Party Zoo Council - Rockfish","Fish Party Zoo Council - Triggerfish","Fish Party Zoo Council - Write In","Ballot Measure 1 - Ballots Cast","Ballot Measure 1 - Undervotes","Ballot Measure 1 - Overvotes","Ballot Measure 1 - Yes","Ballot Measure 1 - No","Ballot Measure 1 - Ballots Cast","Ballot Measure 1 - Undervotes","Ballot Measure 1 - Overvotes","Ballot Measure 1 - Yes","Ballot Measure 1 - No","Ballot Measure 3 - Ballots Cast","Ballot Measure 3 - Undervotes","Ballot Measure 3 - Overvotes","Ballot Measure 3 - Yes","Ballot Measure 3 - No"
- batch-1,Batch 1,scanner-1,35,0,0,0,0,0,0,0,0,0,0,0,35,2,3,0,25,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,34,1,3,25,5
- batch-2,Batch 2,scanner-1,30,0,0,0,0,0,0,0,0,0,0,0,30,1,3,0,20,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,1,5,20,4
- "
- `);
-});
diff --git a/apps/admin/backend/src/exports/batch_results.ts b/apps/admin/backend/src/exports/batch_results.ts
deleted file mode 100644
index 19a3aa8c83..0000000000
--- a/apps/admin/backend/src/exports/batch_results.ts
+++ /dev/null
@@ -1,131 +0,0 @@
-import { Election, Tabulation } from '@votingworks/types';
-import { assert } from '@votingworks/basics';
-import { Readable } from 'stream';
-import { getBallotCount, groupMapToGroupList } from '@votingworks/utils';
-import { stringify } from 'csv-stringify/sync';
-import { ScannerBatch } from '../types';
-
-function generateHeaderRow(election: Election): string {
- const contestSelectionHeaders: string[] = [];
- for (const contest of election.contests) {
- let contestTitle = contest.title;
- if (contest.type === 'candidate' && contest.partyId) {
- const party = election.parties.find((p) => p.id === contest.partyId);
- if (party) {
- contestTitle = `${party.fullName} ${contestTitle}`;
- }
- }
- contestTitle = contestTitle.replace(/[^a-z0-9 _-]+/gi, ' ').trim();
- contestSelectionHeaders.push(`${contestTitle} - Ballots Cast`);
- contestSelectionHeaders.push(`${contestTitle} - Undervotes`);
- contestSelectionHeaders.push(`${contestTitle} - Overvotes`);
- if (contest.type === 'candidate') {
- for (const candidate of contest.candidates) {
- contestSelectionHeaders.push(`${contestTitle} - ${candidate.name}`);
- }
- if (contest.allowWriteIns) {
- contestSelectionHeaders.push(`${contestTitle} - Write In`);
- }
- } else if (contest.type === 'yesno') {
- contestSelectionHeaders.push(`${contestTitle} - Yes`);
- contestSelectionHeaders.push(`${contestTitle} - No`);
- }
- }
- const headers = [
- 'Batch ID',
- 'Batch Name',
- 'Tabulator',
- 'Number of Ballots',
- ...contestSelectionHeaders,
- ];
-
- // use quotes for all headers that contain a dash i.e. the contest selection headers
- return stringify([headers], { quoted_match: /-/ });
-}
-
-function generateResultsRow(
- batchMetadata: ScannerBatch,
- batchResults: Tabulation.ElectionResults,
- election: Election
-): string {
- const contestVoteTotals: string[] = [];
- for (const contest of election.contests) {
- const contestResults = batchResults.contestResults[contest.id];
- assert(contestResults);
- contestVoteTotals.push(contestResults.ballots.toString());
- contestVoteTotals.push(contestResults.undervotes.toString());
- contestVoteTotals.push(contestResults.overvotes.toString());
- if (contest.type === 'candidate') {
- assert(contestResults.contestType === 'candidate');
- for (const candidate of contest.candidates) {
- contestVoteTotals.push(
- /* c8 ignore next - trivial fallback case */
- contestResults.tallies[candidate.id]?.tally.toString() ?? '0'
- );
- }
- if (contest.allowWriteIns) {
- contestVoteTotals.push(
- /* c8 ignore start - trivial fallback case */
- contestResults.tallies[
- Tabulation.GENERIC_WRITE_IN_ID
- ]?.tally.toString() ?? '0'
- /* c8 ignore end */
- );
- }
- } else if (contest.type === 'yesno') {
- assert(contestResults.contestType === 'yesno');
- contestVoteTotals.push(contestResults.yesTally.toString());
- contestVoteTotals.push(contestResults.noTally.toString());
- }
- }
- const row = [
- batchMetadata.batchId,
- batchMetadata.label,
- batchMetadata.scannerId,
- getBallotCount(batchResults.cardCounts),
- ...contestVoteTotals,
- ];
- return stringify([row]);
-}
-
-/**
- * Generates a CSV file of election results broken down by scanning batch. Returns the
- * file as a readable stream.
- *
- * CSV File format:
- * One row for every batch, in addition to a headers row.
- * Columns for every possible contest selection in every contest.
- * | Batch ID | Batch Name | Tabulator | Number Of Ballots | Contest 1 - Ballots Cast | Contest 1 - Undervotes | Contest 1 - Overvotes | Contest 1 - Selection Option 1 | ... | Contest N - Selection Option M |
- */
-export function generateBatchResultsFile({
- election,
- batchGroupedResults,
- allBatchMetadata,
-}: {
- election: Election;
- batchGroupedResults: Tabulation.ElectionResultsGroupMap;
- allBatchMetadata: ScannerBatch[];
-}): NodeJS.ReadableStream {
- const electionResultsList = groupMapToGroupList(batchGroupedResults);
- const batchMetadataLookup: Record = {};
- for (const batchMetadata of allBatchMetadata) {
- batchMetadataLookup[batchMetadata.batchId] = batchMetadata;
- }
-
- function* generateBatchResultsFileRows() {
- yield generateHeaderRow(election);
-
- for (const batchResults of electionResultsList) {
- // expect batch results to be grouped by batchId
- assert(batchResults.batchId !== undefined);
-
- // every batch with results should have a batch in the database
- const batchMetadata = batchMetadataLookup[batchResults.batchId];
- assert(batchMetadata);
-
- yield generateResultsRow(batchMetadata, batchResults, election);
- }
- }
-
- return Readable.from(generateBatchResultsFileRows());
-}
diff --git a/apps/admin/frontend/src/api.ts b/apps/admin/frontend/src/api.ts
index 1b831a8cea..d9f2d3028e 100644
--- a/apps/admin/frontend/src/api.ts
+++ b/apps/admin/frontend/src/api.ts
@@ -652,13 +652,6 @@ export const adjudicateWriteIn = {
},
} as const;
-export const exportBatchResults = {
- useMutation() {
- const apiClient = useApiClient();
- return useMutation(apiClient.exportBatchResults);
- },
-} as const;
-
export const exportTallyReportCsv = {
useMutation() {
const apiClient = useApiClient();
diff --git a/apps/admin/frontend/src/components/export_batch_tally_results_button.tsx b/apps/admin/frontend/src/components/export_batch_tally_results_button.tsx
deleted file mode 100644
index 93613b14bf..0000000000
--- a/apps/admin/frontend/src/components/export_batch_tally_results_button.tsx
+++ /dev/null
@@ -1,47 +0,0 @@
-import React, { useContext, useState } from 'react';
-import { Button } from '@votingworks/ui';
-import { generateBatchResultsDefaultFilename } from '@votingworks/utils';
-import { assert } from '@votingworks/basics';
-import { AppContext } from '../contexts/app_context';
-import { exportBatchResults, getCastVoteRecordFileMode } from '../api';
-import { SaveBackendFileModal } from './save_backend_file_modal';
-
-export function ExportBatchTallyResultsButton(): JSX.Element {
- const [isSaveModalOpen, setIsSaveModalOpen] = useState(false);
- const { electionDefinition } = useContext(AppContext);
- assert(electionDefinition);
- const { election } = electionDefinition;
-
- const exportBatchResultsMutation = exportBatchResults.useMutation();
-
- const castVoteRecordFileModeQuery = getCastVoteRecordFileMode.useQuery();
- const isTestMode = castVoteRecordFileModeQuery.data === 'test';
- const defaultFilename = generateBatchResultsDefaultFilename(
- isTestMode,
- election
- );
-
- return (
-
-
- {isSaveModalOpen && (
- setIsSaveModalOpen(false)}
- fileTypeTitle="Batch Results"
- fileType="batch results"
- defaultRelativePath={defaultFilename}
- />
- )}
-
- );
-}
diff --git a/apps/admin/frontend/src/screens/reporting/reports_screen.test.tsx b/apps/admin/frontend/src/screens/reporting/reports_screen.test.tsx
index 91716abf3a..47178b7e81 100644
--- a/apps/admin/frontend/src/screens/reporting/reports_screen.test.tsx
+++ b/apps/admin/frontend/src/screens/reporting/reports_screen.test.tsx
@@ -93,34 +93,6 @@ test('exporting SEMS results', async () => {
expect(fetchMock.called('/convert/reset')).toEqual(true);
});
-test('exporting batch results', async () => {
- apiMock.expectGetCastVoteRecordFileMode('test');
- apiMock.expectGetCardCounts({}, [getMockCardCounts(100)]);
-
- renderInAppContext(, {
- electionDefinition,
- apiMock,
- usbDriveStatus: mockUsbDriveStatus('mounted'),
- });
-
- await waitFor(() => {
- expect(screen.getButton('Save Batch Results CSV')).toBeEnabled();
- });
- userEvent.click(screen.getButton('Save Batch Results CSV'));
- await screen.findByRole('alertdialog');
-
- await screen.findByText('Save Batch Results');
- await screen.findByText(
- 'votingworks-test-batch-results_sample-county_example-primary-election_2020-11-03_22-22-00.csv'
- );
-
- apiMock.expectExportBatchResults(
- 'test-mount-point/votingworks-test-batch-results_sample-county_example-primary-election_2020-11-03_22-22-00.csv'
- );
- userEvent.click(screen.getByText('Save'));
- await screen.findByText(/Batch Results Saved/);
-});
-
describe('ballot count summary text', () => {
test('unlocked mode', async () => {
apiMock.expectGetCastVoteRecordFileMode('unlocked');
diff --git a/apps/admin/frontend/src/screens/reporting/reports_screen.tsx b/apps/admin/frontend/src/screens/reporting/reports_screen.tsx
index 47386d045d..bee4aa3787 100644
--- a/apps/admin/frontend/src/screens/reporting/reports_screen.tsx
+++ b/apps/admin/frontend/src/screens/reporting/reports_screen.tsx
@@ -33,7 +33,6 @@ import {
getCastVoteRecordFileMode,
getSemsExportableTallies,
} from '../../api';
-import { ExportBatchTallyResultsButton } from '../../components/export_batch_tally_results_button';
import { MarkResultsOfficialButton } from '../../components/mark_official_button';
export function ReportsScreen(): JSX.Element {
@@ -182,7 +181,6 @@ export function ReportsScreen(): JSX.Element {
- {' '}
{converterName !== '' && (