diff --git a/.circleci/config.yml b/.circleci/config.yml index dabe829d36..ddfc4b52c6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -102,52 +102,6 @@ jobs: - store_test_results: path: apps/cacvote-mark/frontend/reports/ - # @votingworks/cacvote-scan-frontend - test-apps-cacvote-scan-frontend: - executor: nodejs - resource_class: xlarge - steps: - - checkout-and-install - - run: - name: Build - command: | - pnpm --dir apps/cacvote-scan/frontend build - - run: - name: Lint - command: | - pnpm --dir apps/cacvote-scan/frontend lint - - run: - name: Test - command: | - pnpm --dir apps/cacvote-scan/frontend test - environment: - JEST_JUNIT_OUTPUT_DIR: ./reports/ - - store_test_results: - path: apps/cacvote-scan/frontend/reports/ - - # @votingworks/cacvote-track-frontend - test-apps-cacvote-track-frontend: - executor: nodejs - resource_class: xlarge - steps: - - checkout-and-install - - run: - name: Build - command: | - pnpm --dir apps/cacvote-track/frontend build - - run: - name: Lint - command: | - pnpm --dir apps/cacvote-track/frontend lint - - run: - name: Test - command: | - pnpm --dir apps/cacvote-track/frontend test - environment: - JEST_JUNIT_OUTPUT_DIR: ./reports/ - - store_test_results: - path: apps/cacvote-track/frontend/reports/ - # @votingworks/exercises test-docs-exercises: executor: nodejs @@ -240,29 +194,6 @@ jobs: - store_test_results: path: libs/ballot-encoder/reports/ - # ballot-encoder-rs - test-libs-ballot-encoder-rs: - executor: nodejs - resource_class: xlarge - steps: - - checkout-and-install - - run: - name: Build - command: | - pnpm --dir libs/ballot-encoder-rs build - - run: - name: Lint - command: | - pnpm --dir libs/ballot-encoder-rs lint - - run: - name: Test - command: | - pnpm --dir libs/ballot-encoder-rs test - environment: - JEST_JUNIT_OUTPUT_DIR: ./reports/ - - store_test_results: - path: libs/ballot-encoder-rs/reports/ - # @votingworks/basics test-libs-basics: executor: nodejs @@ -309,29 +240,6 @@ jobs: - store_test_results: path: libs/cdf-schema-builder/reports/ - # central-scanner - test-libs-central-scanner: - executor: nodejs - resource_class: xlarge - steps: - - checkout-and-install - - run: - name: Build - command: | - pnpm --dir libs/central-scanner build - - run: - name: Lint - command: | - pnpm --dir libs/central-scanner lint - - run: - name: Test - command: | - pnpm --dir libs/central-scanner test - environment: - JEST_JUNIT_OUTPUT_DIR: ./reports/ - - store_test_results: - path: libs/central-scanner/reports/ - # @votingworks/db test-libs-db: executor: nodejs @@ -783,20 +691,6 @@ jobs: command: | cargo test -p auth-rs - test-crate-ballot-encoder-rs: - executor: nodejs - resource_class: xlarge - steps: - - checkout-and-install - - run: - name: Build - command: | - cargo build -p ballot-encoder-rs - - run: - name: Test - command: | - cargo test -p ballot-encoder-rs - test-crate-cacvote-jx-terminal-backend: executor: rust-db resource_class: xlarge @@ -831,40 +725,6 @@ jobs: command: | cargo test -p cacvote-jx-terminal-frontend - test-crate-cacvote-scan-backend: - executor: rust-db - resource_class: xlarge - steps: - - checkout-and-install - - run: - name: Setup Database - command: | - cd 'apps/cacvote-scan/backend' - cargo install sqlx-cli - cargo sqlx migrate run --source db/migrations - - run: - name: Build - command: | - cargo build -p cacvote-scan-backend - - run: - name: Test - command: | - cargo test -p cacvote-scan-backend - - test-crate-cacvote-scan-frontend: - executor: nodejs - resource_class: xlarge - steps: - - checkout-and-install - - run: - name: Build - command: | - cargo build -p cacvote-scan-frontend - - run: - name: Test - command: | - cargo test -p cacvote-scan-frontend - test-crate-cacvote-server: executor: rust-db resource_class: xlarge @@ -885,20 +745,6 @@ jobs: command: | cargo test -p cacvote-server - test-crate-central-scanner: - executor: nodejs - resource_class: xlarge - steps: - - checkout-and-install - - run: - name: Build - command: | - cargo build -p central-scanner - - run: - name: Test - command: | - cargo test -p central-scanner - test-crate-controllerd: executor: nodejs resource_class: xlarge @@ -975,16 +821,12 @@ workflows: - test-apps-cacvote-jx-terminal-frontend - test-apps-cacvote-mark-backend - test-apps-cacvote-mark-frontend - - test-apps-cacvote-scan-frontend - - test-apps-cacvote-track-frontend - test-docs-exercises - test-libs-auth - test-libs-backend - test-libs-ballot-encoder - - test-libs-ballot-encoder-rs - test-libs-basics - test-libs-cdf-schema-builder - - test-libs-central-scanner - test-libs-db - test-libs-dev-dock-backend - test-libs-dev-dock-frontend @@ -1005,13 +847,9 @@ workflows: - test-libs-usb-drive - test-libs-utils - test-crate-auth-rs - - test-crate-ballot-encoder-rs - test-crate-cacvote-jx-terminal-backend - test-crate-cacvote-jx-terminal-frontend - - test-crate-cacvote-scan-backend - - test-crate-cacvote-scan-frontend - test-crate-cacvote-server - - test-crate-central-scanner - test-crate-controllerd - test-crate-types-rs - test-crate-ui-rs diff --git a/.sqlx/query-023b1a60e2eab01a0211cfba0bf3529a0799492733c43aca2bd28198240202b7.json b/.sqlx/query-023b1a60e2eab01a0211cfba0bf3529a0799492733c43aca2bd28198240202b7.json deleted file mode 100644 index adea08f14a..0000000000 --- a/.sqlx/query-023b1a60e2eab01a0211cfba0bf3529a0799492733c43aca2bd28198240202b7.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n INSERT INTO elections (\n id,\n server_id,\n client_id,\n machine_id,\n jurisdiction_id,\n election_hash,\n definition\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7)\n ON CONFLICT (machine_id, client_id)\n DO UPDATE SET\n server_id = $2,\n jurisdiction_id = $5,\n election_hash = $6,\n definition = $7\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid", - "Uuid", - "Uuid", - "Varchar", - "Uuid", - "Varchar", - "Bytea" - ] - }, - "nullable": [] - }, - "hash": "023b1a60e2eab01a0211cfba0bf3529a0799492733c43aca2bd28198240202b7" -} diff --git a/.sqlx/query-088f2a385709494b30d8b9c4a792edb13102141d3ee1ce7ca52023f80ffce385.json b/.sqlx/query-088f2a385709494b30d8b9c4a792edb13102141d3ee1ce7ca52023f80ffce385.json deleted file mode 100644 index df9fc45762..0000000000 --- a/.sqlx/query-088f2a385709494b30d8b9c4a792edb13102141d3ee1ce7ca52023f80ffce385.json +++ /dev/null @@ -1,70 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT\n id as \"id: ServerId\",\n client_id as \"client_id: ClientId\",\n machine_id,\n common_access_card_id,\n common_access_card_certificate,\n registration_id as \"registration_id: ServerId\",\n cast_vote_record,\n cast_vote_record_signature,\n created_at\n FROM printed_ballots\n WHERE created_at > $1\n ORDER BY created_at DESC\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id: ServerId", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "client_id: ClientId", - "type_info": "Uuid" - }, - { - "ordinal": 2, - "name": "machine_id", - "type_info": "Varchar" - }, - { - "ordinal": 3, - "name": "common_access_card_id", - "type_info": "Varchar" - }, - { - "ordinal": 4, - "name": "common_access_card_certificate", - "type_info": "Bytea" - }, - { - "ordinal": 5, - "name": "registration_id: ServerId", - "type_info": "Uuid" - }, - { - "ordinal": 6, - "name": "cast_vote_record", - "type_info": "Bytea" - }, - { - "ordinal": 7, - "name": "cast_vote_record_signature", - "type_info": "Bytea" - }, - { - "ordinal": 8, - "name": "created_at", - "type_info": "Timestamptz" - } - ], - "parameters": { - "Left": [ - "Timestamptz" - ] - }, - "nullable": [ - false, - false, - false, - false, - false, - false, - false, - false, - false - ] - }, - "hash": "088f2a385709494b30d8b9c4a792edb13102141d3ee1ce7ca52023f80ffce385" -} diff --git a/.sqlx/query-0908a3548f4c094b729f06d9645745c79ed939919cf78f46b45f47f92b068430.json b/.sqlx/query-0908a3548f4c094b729f06d9645745c79ed939919cf78f46b45f47f92b068430.json deleted file mode 100644 index 33f1ad5b06..0000000000 --- a/.sqlx/query-0908a3548f4c094b729f06d9645745c79ed939919cf78f46b45f47f92b068430.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT created_at\n FROM registrations\n WHERE id = $1\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "created_at", - "type_info": "Timestamptz" - } - ], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [ - false - ] - }, - "hash": "0908a3548f4c094b729f06d9645745c79ed939919cf78f46b45f47f92b068430" -} diff --git a/.sqlx/query-0b7bebf4c98255aca7e2f12fc45603b3cc5e76516c25a262a471d0309f247ac6.json b/.sqlx/query-0b7bebf4c98255aca7e2f12fc45603b3cc5e76516c25a262a471d0309f247ac6.json deleted file mode 100644 index 2d0552da41..0000000000 --- a/.sqlx/query-0b7bebf4c98255aca7e2f12fc45603b3cc5e76516c25a262a471d0309f247ac6.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n INSERT INTO registrations (\n id,\n client_id,\n machine_id,\n jurisdiction_id,\n election_id,\n registration_request_id,\n common_access_card_id,\n precinct_id,\n ballot_style_id\n )\n VALUES (\n $1, $2, $3, $4,\n (SELECT id FROM elections WHERE client_id = $5),\n (SELECT id FROM registration_requests WHERE client_id = $6),\n $7, $8, $9\n )\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid", - "Uuid", - "Varchar", - "Uuid", - "Uuid", - "Uuid", - "Varchar", - "Varchar", - "Varchar" - ] - }, - "nullable": [] - }, - "hash": "0b7bebf4c98255aca7e2f12fc45603b3cc5e76516c25a262a471d0309f247ac6" -} diff --git a/.sqlx/query-0db11db12dc749b20f699f8dab01f5bb664b4a8441efc280fe6e2dd73ea6dcb5.json b/.sqlx/query-0db11db12dc749b20f699f8dab01f5bb664b4a8441efc280fe6e2dd73ea6dcb5.json deleted file mode 100644 index 06f04f9ab8..0000000000 --- a/.sqlx/query-0db11db12dc749b20f699f8dab01f5bb664b4a8441efc280fe6e2dd73ea6dcb5.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT\n id as \"id: ClientId\",\n COALESCE(ARRAY_LENGTH(scanned_ballot_ids, 1), 0) as \"ballot_count!: _\",\n CAST(\n (\n SELECT COUNT(DISTINCT election_id)\n FROM scanned_ballots\n WHERE id IN (SELECT UNNEST(scanned_ballot_ids))\n ) AS int4\n ) AS \"election_count!: _\",\n CAST(\n (\n SELECT COUNT(*)\n FROM scanned_ballots\n WHERE id IN (SELECT UNNEST(scanned_ballot_ids))\n AND server_id IS NOT NULL\n ) AS int4\n ) AS \"synced_count!: _\",\n started_at,\n ended_at\n FROM batches\n ORDER BY started_at DESC\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id: ClientId", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "ballot_count!: _", - "type_info": "Int4" - }, - { - "ordinal": 2, - "name": "election_count!: _", - "type_info": "Int4" - }, - { - "ordinal": 3, - "name": "synced_count!: _", - "type_info": "Int4" - }, - { - "ordinal": 4, - "name": "started_at", - "type_info": "Timestamptz" - }, - { - "ordinal": 5, - "name": "ended_at", - "type_info": "Timestamptz" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - false, - null, - null, - null, - false, - true - ] - }, - "hash": "0db11db12dc749b20f699f8dab01f5bb664b4a8441efc280fe6e2dd73ea6dcb5" -} diff --git a/.sqlx/query-1166fa780d90967db6362468c31a58ee5a2f9b869c5122be4bfa98100fa8da9e.json b/.sqlx/query-1166fa780d90967db6362468c31a58ee5a2f9b869c5122be4bfa98100fa8da9e.json deleted file mode 100644 index 5101f979eb..0000000000 --- a/.sqlx/query-1166fa780d90967db6362468c31a58ee5a2f9b869c5122be4bfa98100fa8da9e.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT created_at\n FROM registration_requests\n WHERE id = $1\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "created_at", - "type_info": "Timestamptz" - } - ], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [ - false - ] - }, - "hash": "1166fa780d90967db6362468c31a58ee5a2f9b869c5122be4bfa98100fa8da9e" -} diff --git a/.sqlx/query-168c524a19a63caf753903e070ab91b807b83a877f36ce1148e9dc84055567bb.json b/.sqlx/query-168c524a19a63caf753903e070ab91b807b83a877f36ce1148e9dc84055567bb.json deleted file mode 100644 index bd770011fc..0000000000 --- a/.sqlx/query-168c524a19a63caf753903e070ab91b807b83a877f36ce1148e9dc84055567bb.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT\n id as \"id: ClientId\",\n server_id as \"server_id: ServerId\",\n client_id as \"client_id: ClientId\",\n machine_id,\n definition as \"definition: String\",\n election_hash,\n created_at\n FROM elections\n WHERE created_at > $1\n ORDER BY created_at DESC\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id: ClientId", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "server_id: ServerId", - "type_info": "Uuid" - }, - { - "ordinal": 2, - "name": "client_id: ClientId", - "type_info": "Uuid" - }, - { - "ordinal": 3, - "name": "machine_id", - "type_info": "Varchar" - }, - { - "ordinal": 4, - "name": "definition: String", - "type_info": "Bytea" - }, - { - "ordinal": 5, - "name": "election_hash", - "type_info": "Varchar" - }, - { - "ordinal": 6, - "name": "created_at", - "type_info": "Timestamptz" - } - ], - "parameters": { - "Left": [ - "Timestamptz" - ] - }, - "nullable": [ - false, - false, - false, - false, - false, - false, - false - ] - }, - "hash": "168c524a19a63caf753903e070ab91b807b83a877f36ce1148e9dc84055567bb" -} diff --git a/.sqlx/query-1ad99291100c5835132b78d0299b95f6055b6ad49f95c746bc268d4be50515da.json b/.sqlx/query-1ad99291100c5835132b78d0299b95f6055b6ad49f95c746bc268d4be50515da.json deleted file mode 100644 index b37fb28ac5..0000000000 --- a/.sqlx/query-1ad99291100c5835132b78d0299b95f6055b6ad49f95c746bc268d4be50515da.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT\n created_at\n FROM printed_ballots\n WHERE id = $1\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "created_at", - "type_info": "Timestamptz" - } - ], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [ - false - ] - }, - "hash": "1ad99291100c5835132b78d0299b95f6055b6ad49f95c746bc268d4be50515da" -} diff --git a/.sqlx/query-1e4cd90eb6b0ee7010004c2ea399552e44a2e72ac4082672453fe50d2ec9a959.json b/.sqlx/query-1e4cd90eb6b0ee7010004c2ea399552e44a2e72ac4082672453fe50d2ec9a959.json deleted file mode 100644 index 87260ba21d..0000000000 --- a/.sqlx/query-1e4cd90eb6b0ee7010004c2ea399552e44a2e72ac4082672453fe50d2ec9a959.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT\n id as \"id: ClientId\",\n server_id as \"server_id: ServerId\",\n client_id as \"client_id: ClientId\",\n machine_id,\n definition as \"definition: String\",\n election_hash,\n created_at\n FROM elections\n ORDER BY created_at DESC\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id: ClientId", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "server_id: ServerId", - "type_info": "Uuid" - }, - { - "ordinal": 2, - "name": "client_id: ClientId", - "type_info": "Uuid" - }, - { - "ordinal": 3, - "name": "machine_id", - "type_info": "Varchar" - }, - { - "ordinal": 4, - "name": "definition: String", - "type_info": "Bytea" - }, - { - "ordinal": 5, - "name": "election_hash", - "type_info": "Varchar" - }, - { - "ordinal": 6, - "name": "created_at", - "type_info": "Timestamptz" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - false, - false, - false, - false, - false, - false, - false - ] - }, - "hash": "1e4cd90eb6b0ee7010004c2ea399552e44a2e72ac4082672453fe50d2ec9a959" -} diff --git a/.sqlx/query-1eddb2e8ea639c33dc35f7ac38b6550b279e0225f7212c1b2ed1c6627751798c.json b/.sqlx/query-1eddb2e8ea639c33dc35f7ac38b6550b279e0225f7212c1b2ed1c6627751798c.json deleted file mode 100644 index 13995d6395..0000000000 --- a/.sqlx/query-1eddb2e8ea639c33dc35f7ac38b6550b279e0225f7212c1b2ed1c6627751798c.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT\n client_id as \"client_id: ClientId\",\n machine_id,\n jurisdiction_id as \"jurisdiction_id: ServerId\",\n definition as \"definition: String\"\n FROM elections\n WHERE server_id IS NULL\n ORDER BY created_at ASC\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "client_id: ClientId", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "machine_id", - "type_info": "Varchar" - }, - { - "ordinal": 2, - "name": "jurisdiction_id: ServerId", - "type_info": "Uuid" - }, - { - "ordinal": 3, - "name": "definition: String", - "type_info": "Bytea" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - false, - false, - false, - false - ] - }, - "hash": "1eddb2e8ea639c33dc35f7ac38b6550b279e0225f7212c1b2ed1c6627751798c" -} diff --git a/.sqlx/query-26df6336d19d602d9fd7c56216e5bb1ad6e493fd26501d0efefbfdac3ed56404.json b/.sqlx/query-26df6336d19d602d9fd7c56216e5bb1ad6e493fd26501d0efefbfdac3ed56404.json new file mode 100644 index 0000000000..a11a6a49d3 --- /dev/null +++ b/.sqlx/query-26df6336d19d602d9fd7c56216e5bb1ad6e493fd26501d0efefbfdac3ed56404.json @@ -0,0 +1,21 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO elections (\n id,\n server_id,\n client_id,\n machine_id,\n jurisdiction_id,\n election_hash,\n definition,\n return_address\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8)\n ON CONFLICT (machine_id, client_id)\n DO UPDATE SET\n server_id = $2,\n jurisdiction_id = $5,\n election_hash = $6,\n definition = $7,\n return_address = $8\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Uuid", + "Uuid", + "Uuid", + "Varchar", + "Uuid", + "Varchar", + "Bytea", + "Text" + ] + }, + "nullable": [] + }, + "hash": "26df6336d19d602d9fd7c56216e5bb1ad6e493fd26501d0efefbfdac3ed56404" +} diff --git a/.sqlx/query-27002098140387da834f0454cd3d85228043228000d35680b0a2594f075980f5.json b/.sqlx/query-27002098140387da834f0454cd3d85228043228000d35680b0a2594f075980f5.json deleted file mode 100644 index f575028e66..0000000000 --- a/.sqlx/query-27002098140387da834f0454cd3d85228043228000d35680b0a2594f075980f5.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT\n id as \"id: ServerId\",\n client_id as \"client_id: ClientId\",\n machine_id,\n jurisdiction_id as \"jurisdiction_id: ServerId\",\n common_access_card_id,\n registration_request_id as \"registration_request_id: ServerId\",\n election_id as \"election_id: ServerId\",\n precinct_id,\n ballot_style_id,\n created_at\n FROM registrations\n ORDER BY created_at DESC\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id: ServerId", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "client_id: ClientId", - "type_info": "Uuid" - }, - { - "ordinal": 2, - "name": "machine_id", - "type_info": "Varchar" - }, - { - "ordinal": 3, - "name": "jurisdiction_id: ServerId", - "type_info": "Uuid" - }, - { - "ordinal": 4, - "name": "common_access_card_id", - "type_info": "Varchar" - }, - { - "ordinal": 5, - "name": "registration_request_id: ServerId", - "type_info": "Uuid" - }, - { - "ordinal": 6, - "name": "election_id: ServerId", - "type_info": "Uuid" - }, - { - "ordinal": 7, - "name": "precinct_id", - "type_info": "Varchar" - }, - { - "ordinal": 8, - "name": "ballot_style_id", - "type_info": "Varchar" - }, - { - "ordinal": 9, - "name": "created_at", - "type_info": "Timestamptz" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - false, - false, - false, - false, - false, - false, - false, - false, - false, - false - ] - }, - "hash": "27002098140387da834f0454cd3d85228043228000d35680b0a2594f075980f5" -} diff --git a/.sqlx/query-2b3e7b73876dac34e6c42e96eac3a14a507f6927219b909fc023c35a3417aba6.json b/.sqlx/query-2b3e7b73876dac34e6c42e96eac3a14a507f6927219b909fc023c35a3417aba6.json deleted file mode 100644 index d7f252d0ee..0000000000 --- a/.sqlx/query-2b3e7b73876dac34e6c42e96eac3a14a507f6927219b909fc023c35a3417aba6.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n INSERT INTO scanned_ballots (\n id,\n server_id,\n client_id,\n machine_id,\n election_id,\n cast_vote_record,\n created_at\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7)\n ON CONFLICT (client_id, machine_id)\n DO UPDATE SET\n server_id = $2,\n election_id = $5,\n cast_vote_record = $6,\n created_at = $7\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid", - "Uuid", - "Uuid", - "Varchar", - "Uuid", - "Bytea", - "Timestamptz" - ] - }, - "nullable": [] - }, - "hash": "2b3e7b73876dac34e6c42e96eac3a14a507f6927219b909fc023c35a3417aba6" -} diff --git a/.sqlx/query-264a83c6be1b7bfb3a27c44806068e47f7379222aeeeaaf462ae4d8d573b0309.json b/.sqlx/query-2d1bd85d0a88b678a2ae41f89f6c65322ba646919a0e5ead6cc85a17efe2e3a6.json similarity index 66% rename from .sqlx/query-264a83c6be1b7bfb3a27c44806068e47f7379222aeeeaaf462ae4d8d573b0309.json rename to .sqlx/query-2d1bd85d0a88b678a2ae41f89f6c65322ba646919a0e5ead6cc85a17efe2e3a6.json index 1e6dc1ef02..0e8ba1c228 100644 --- a/.sqlx/query-264a83c6be1b7bfb3a27c44806068e47f7379222aeeeaaf462ae4d8d573b0309.json +++ b/.sqlx/query-2d1bd85d0a88b678a2ae41f89f6c65322ba646919a0e5ead6cc85a17efe2e3a6.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n INSERT INTO elections (\n id,\n jurisdiction_id,\n client_id,\n machine_id,\n election_hash,\n definition\n )\n VALUES ($1, $2, $3, $4, $5, $6)\n ", + "query": "\n INSERT INTO elections (\n id,\n jurisdiction_id,\n client_id,\n machine_id,\n election_hash,\n definition,\n return_address\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7)\n ", "describe": { "columns": [], "parameters": { @@ -10,10 +10,11 @@ "Uuid", "Varchar", "Varchar", - "Bytea" + "Bytea", + "Text" ] }, "nullable": [] }, - "hash": "264a83c6be1b7bfb3a27c44806068e47f7379222aeeeaaf462ae4d8d573b0309" + "hash": "2d1bd85d0a88b678a2ae41f89f6c65322ba646919a0e5ead6cc85a17efe2e3a6" } diff --git a/.sqlx/query-34c92aeb34d294acde318d1e7ec792d15c8955043f8f2c72ba8a3aa6b5e57176.json b/.sqlx/query-34c92aeb34d294acde318d1e7ec792d15c8955043f8f2c72ba8a3aa6b5e57176.json deleted file mode 100644 index 61a3f32de2..0000000000 --- a/.sqlx/query-34c92aeb34d294acde318d1e7ec792d15c8955043f8f2c72ba8a3aa6b5e57176.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n INSERT INTO registrations (\n id,\n server_id,\n client_id,\n machine_id,\n jurisdiction_id,\n common_access_card_id,\n registration_request_id,\n election_id,\n precinct_id,\n ballot_style_id,\n created_at\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)\n ON CONFLICT (machine_id, client_id)\n DO UPDATE SET\n server_id = $2,\n jurisdiction_id = $5,\n common_access_card_id = $6,\n registration_request_id = $7,\n election_id = $8,\n precinct_id = $9,\n ballot_style_id = $10,\n created_at = $11\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid", - "Uuid", - "Uuid", - "Varchar", - "Uuid", - "Varchar", - "Uuid", - "Uuid", - "Varchar", - "Varchar", - "Timestamptz" - ] - }, - "nullable": [] - }, - "hash": "34c92aeb34d294acde318d1e7ec792d15c8955043f8f2c72ba8a3aa6b5e57176" -} diff --git a/.sqlx/query-389d4aa3005b51684781c3ce06a432308c5dd00dc14898452f7c0c0146e23595.json b/.sqlx/query-389d4aa3005b51684781c3ce06a432308c5dd00dc14898452f7c0c0146e23595.json deleted file mode 100644 index 89e6cc297e..0000000000 --- a/.sqlx/query-389d4aa3005b51684781c3ce06a432308c5dd00dc14898452f7c0c0146e23595.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT\n client_id as \"client_id: ClientId\",\n machine_id,\n (SELECT client_id FROM elections WHERE id = election_id) as \"election_id!: ClientId\",\n cast_vote_record,\n created_at\n FROM scanned_ballots\n WHERE server_id IS NULL\n ORDER BY created_at DESC\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "client_id: ClientId", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "machine_id", - "type_info": "Varchar" - }, - { - "ordinal": 2, - "name": "election_id!: ClientId", - "type_info": "Uuid" - }, - { - "ordinal": 3, - "name": "cast_vote_record", - "type_info": "Bytea" - }, - { - "ordinal": 4, - "name": "created_at", - "type_info": "Timestamptz" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - false, - false, - null, - false, - false - ] - }, - "hash": "389d4aa3005b51684781c3ce06a432308c5dd00dc14898452f7c0c0146e23595" -} diff --git a/.sqlx/query-4c2327c0aca2e22eed0ef8fefa9eb5155d233dcccd0cbb36c0b17f1117d18f47.json b/.sqlx/query-4c2327c0aca2e22eed0ef8fefa9eb5155d233dcccd0cbb36c0b17f1117d18f47.json deleted file mode 100644 index b2b9d72500..0000000000 --- a/.sqlx/query-4c2327c0aca2e22eed0ef8fefa9eb5155d233dcccd0cbb36c0b17f1117d18f47.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT\n client_id as \"client_id: ClientId\",\n machine_id,\n jurisdiction_id as \"jurisdiction_id: ServerId\",\n registration_request_id as \"registration_request_id: ClientId\",\n election_id as \"election_id: ClientId\",\n common_access_card_id,\n precinct_id,\n ballot_style_id\n FROM registrations\n WHERE server_id IS NULL\n ORDER BY created_at ASC\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "client_id: ClientId", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "machine_id", - "type_info": "Varchar" - }, - { - "ordinal": 2, - "name": "jurisdiction_id: ServerId", - "type_info": "Uuid" - }, - { - "ordinal": 3, - "name": "registration_request_id: ClientId", - "type_info": "Uuid" - }, - { - "ordinal": 4, - "name": "election_id: ClientId", - "type_info": "Uuid" - }, - { - "ordinal": 5, - "name": "common_access_card_id", - "type_info": "Varchar" - }, - { - "ordinal": 6, - "name": "precinct_id", - "type_info": "Varchar" - }, - { - "ordinal": 7, - "name": "ballot_style_id", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - false, - false, - false, - false, - false, - false, - false, - false - ] - }, - "hash": "4c2327c0aca2e22eed0ef8fefa9eb5155d233dcccd0cbb36c0b17f1117d18f47" -} diff --git a/.sqlx/query-4da37d9febc69ce401de6664a86a8959d85dfd6b59b32d41ca825f69f0de6d91.json b/.sqlx/query-4da37d9febc69ce401de6664a86a8959d85dfd6b59b32d41ca825f69f0de6d91.json deleted file mode 100644 index 1792efecd9..0000000000 --- a/.sqlx/query-4da37d9febc69ce401de6664a86a8959d85dfd6b59b32d41ca825f69f0de6d91.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n INSERT INTO admins (machine_id, common_access_card_id)\n VALUES ($1, $2)\n ON CONFLICT (common_access_card_id) DO NOTHING\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Varchar", - "Varchar" - ] - }, - "nullable": [] - }, - "hash": "4da37d9febc69ce401de6664a86a8959d85dfd6b59b32d41ca825f69f0de6d91" -} diff --git a/.sqlx/query-4e34a5a787fb778569a23355b62e910a2d9506e04f4af477ce0c5eb5e2e2f873.json b/.sqlx/query-4e34a5a787fb778569a23355b62e910a2d9506e04f4af477ce0c5eb5e2e2f873.json deleted file mode 100644 index 07b34dce82..0000000000 --- a/.sqlx/query-4e34a5a787fb778569a23355b62e910a2d9506e04f4af477ce0c5eb5e2e2f873.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT\n id as \"id: ServerId\",\n client_id as \"client_id: ClientId\",\n machine_id,\n jurisdiction_id as \"jurisdiction_id: ServerId\",\n common_access_card_id,\n given_name,\n family_name,\n created_at\n FROM registration_requests\n WHERE created_at > $1\n ORDER BY created_at DESC\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id: ServerId", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "client_id: ClientId", - "type_info": "Uuid" - }, - { - "ordinal": 2, - "name": "machine_id", - "type_info": "Varchar" - }, - { - "ordinal": 3, - "name": "jurisdiction_id: ServerId", - "type_info": "Uuid" - }, - { - "ordinal": 4, - "name": "common_access_card_id", - "type_info": "Varchar" - }, - { - "ordinal": 5, - "name": "given_name", - "type_info": "Varchar" - }, - { - "ordinal": 6, - "name": "family_name", - "type_info": "Varchar" - }, - { - "ordinal": 7, - "name": "created_at", - "type_info": "Timestamptz" - } - ], - "parameters": { - "Left": [ - "Timestamptz" - ] - }, - "nullable": [ - false, - false, - false, - false, - false, - false, - false, - false - ] - }, - "hash": "4e34a5a787fb778569a23355b62e910a2d9506e04f4af477ce0c5eb5e2e2f873" -} diff --git a/.sqlx/query-50da92ccb116b20ad4371ad8daa870507ce4e2c07d789f3090d264cef0bbb800.json b/.sqlx/query-50da92ccb116b20ad4371ad8daa870507ce4e2c07d789f3090d264cef0bbb800.json deleted file mode 100644 index 9cc7485385..0000000000 --- a/.sqlx/query-50da92ccb116b20ad4371ad8daa870507ce4e2c07d789f3090d264cef0bbb800.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT\n id,\n code,\n name,\n created_at\n FROM jurisdictions\n ORDER BY created_at ASC\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "code", - "type_info": "Varchar" - }, - { - "ordinal": 2, - "name": "name", - "type_info": "Varchar" - }, - { - "ordinal": 3, - "name": "created_at", - "type_info": "Timestamptz" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - false, - false, - false, - false - ] - }, - "hash": "50da92ccb116b20ad4371ad8daa870507ce4e2c07d789f3090d264cef0bbb800" -} diff --git a/.sqlx/query-51ede0fd1b6f6514d3aece77258ddf77ac0307e0802d38d01c32685063147088.json b/.sqlx/query-51ede0fd1b6f6514d3aece77258ddf77ac0307e0802d38d01c32685063147088.json deleted file mode 100644 index 831a43cd66..0000000000 --- a/.sqlx/query-51ede0fd1b6f6514d3aece77258ddf77ac0307e0802d38d01c32685063147088.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT\n id as \"id: _\",\n client_id as \"client_id: _\",\n machine_id,\n jurisdiction_id,\n definition,\n election_hash,\n created_at\n FROM elections\n ORDER BY created_at DESC\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id: _", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "client_id: _", - "type_info": "Uuid" - }, - { - "ordinal": 2, - "name": "machine_id", - "type_info": "Varchar" - }, - { - "ordinal": 3, - "name": "jurisdiction_id", - "type_info": "Uuid" - }, - { - "ordinal": 4, - "name": "definition", - "type_info": "Bytea" - }, - { - "ordinal": 5, - "name": "election_hash", - "type_info": "Varchar" - }, - { - "ordinal": 6, - "name": "created_at", - "type_info": "Timestamptz" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - false, - false, - false, - false, - false, - false, - false - ] - }, - "hash": "51ede0fd1b6f6514d3aece77258ddf77ac0307e0802d38d01c32685063147088" -} diff --git a/.sqlx/query-58571dbf3b269d22bce544f749178f43c6495ccfbc52509a8b6f28b3e286543c.json b/.sqlx/query-58571dbf3b269d22bce544f749178f43c6495ccfbc52509a8b6f28b3e286543c.json deleted file mode 100644 index a06d9b4b83..0000000000 --- a/.sqlx/query-58571dbf3b269d22bce544f749178f43c6495ccfbc52509a8b6f28b3e286543c.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n INSERT INTO scanned_ballots (\n id,\n server_id,\n client_id,\n machine_id,\n election_id,\n cast_vote_record,\n created_at\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7)\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid", - "Uuid", - "Uuid", - "Varchar", - "Uuid", - "Bytea", - "Timestamptz" - ] - }, - "nullable": [] - }, - "hash": "58571dbf3b269d22bce544f749178f43c6495ccfbc52509a8b6f28b3e286543c" -} diff --git a/.sqlx/query-609eeaa3902021ec364a7c0af101ebe6419fe43e7c56a82128ee55ed29bc0cf6.json b/.sqlx/query-609eeaa3902021ec364a7c0af101ebe6419fe43e7c56a82128ee55ed29bc0cf6.json deleted file mode 100644 index 7310eb4719..0000000000 --- a/.sqlx/query-609eeaa3902021ec364a7c0af101ebe6419fe43e7c56a82128ee55ed29bc0cf6.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT\n client_id AS \"client_id: ClientId\",\n common_access_card_id\n FROM registration_requests\n WHERE (SELECT COUNT(*) FROM registrations WHERE registration_requests.id = registrations.registration_request_id) = 0\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "client_id: ClientId", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "common_access_card_id", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - false, - false - ] - }, - "hash": "609eeaa3902021ec364a7c0af101ebe6419fe43e7c56a82128ee55ed29bc0cf6" -} diff --git a/.sqlx/query-62600d6a375f2e212459a01a7044b3bb09a21880d33e1f89f4205ca10b623404.json b/.sqlx/query-62600d6a375f2e212459a01a7044b3bb09a21880d33e1f89f4205ca10b623404.json deleted file mode 100644 index e1083c82f6..0000000000 --- a/.sqlx/query-62600d6a375f2e212459a01a7044b3bb09a21880d33e1f89f4205ca10b623404.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n INSERT INTO jurisdictions (id, code, name)\n VALUES ($1, $2, $3)\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid", - "Varchar", - "Varchar" - ] - }, - "nullable": [] - }, - "hash": "62600d6a375f2e212459a01a7044b3bb09a21880d33e1f89f4205ca10b623404" -} diff --git a/.sqlx/query-75e56f02315dc24a3ef097406c5672839095204be64b2799424a84c4309fb986.json b/.sqlx/query-75e56f02315dc24a3ef097406c5672839095204be64b2799424a84c4309fb986.json deleted file mode 100644 index 7df871a32c..0000000000 --- a/.sqlx/query-75e56f02315dc24a3ef097406c5672839095204be64b2799424a84c4309fb986.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n INSERT INTO registration_requests (\n id,\n client_id,\n machine_id,\n jurisdiction_id,\n common_access_card_id,\n given_name,\n family_name\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7)\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid", - "Uuid", - "Varchar", - "Uuid", - "Varchar", - "Varchar", - "Varchar" - ] - }, - "nullable": [] - }, - "hash": "75e56f02315dc24a3ef097406c5672839095204be64b2799424a84c4309fb986" -} diff --git a/.sqlx/query-78b992e6d2f51d7eb96191b53172fc9bcd53e06cc70d84284625187c94d011ff.json b/.sqlx/query-78b992e6d2f51d7eb96191b53172fc9bcd53e06cc70d84284625187c94d011ff.json deleted file mode 100644 index f86d87d021..0000000000 --- a/.sqlx/query-78b992e6d2f51d7eb96191b53172fc9bcd53e06cc70d84284625187c94d011ff.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT\n id as \"id: ServerId\",\n client_id as \"client_id: ClientId\",\n machine_id,\n election_id as \"election_id: ServerId\",\n cast_vote_record as \"cast_vote_record: _\",\n created_at\n FROM scanned_ballots\n ORDER BY created_at DESC\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id: ServerId", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "client_id: ClientId", - "type_info": "Uuid" - }, - { - "ordinal": 2, - "name": "machine_id", - "type_info": "Varchar" - }, - { - "ordinal": 3, - "name": "election_id: ServerId", - "type_info": "Uuid" - }, - { - "ordinal": 4, - "name": "cast_vote_record: _", - "type_info": "Bytea" - }, - { - "ordinal": 5, - "name": "created_at", - "type_info": "Timestamptz" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - false, - false, - false, - false, - false, - false - ] - }, - "hash": "78b992e6d2f51d7eb96191b53172fc9bcd53e06cc70d84284625187c94d011ff" -} diff --git a/.sqlx/query-7c68d97a911d03c24b2fc0b41c5a1e20068fd7e1f686a1f7e5c15539de6d9580.json b/.sqlx/query-7c68d97a911d03c24b2fc0b41c5a1e20068fd7e1f686a1f7e5c15539de6d9580.json deleted file mode 100644 index 8dfa526eef..0000000000 --- a/.sqlx/query-7c68d97a911d03c24b2fc0b41c5a1e20068fd7e1f686a1f7e5c15539de6d9580.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT\n scanned_ballots.id as \"id: ClientId\",\n scanned_ballots.server_id as \"server_id: ServerId\",\n scanned_ballots.election_id as \"election_id: ClientId\",\n scanned_ballots.cast_vote_record,\n scanned_ballots.created_at\n FROM scanned_ballots\n INNER JOIN elections ON elections.id = scanned_ballots.election_id\n WHERE elections.jurisdiction_id = $1\n ORDER BY created_at DESC\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id: ClientId", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "server_id: ServerId", - "type_info": "Uuid" - }, - { - "ordinal": 2, - "name": "election_id: ClientId", - "type_info": "Uuid" - }, - { - "ordinal": 3, - "name": "cast_vote_record", - "type_info": "Bytea" - }, - { - "ordinal": 4, - "name": "created_at", - "type_info": "Timestamptz" - } - ], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [ - false, - false, - false, - false, - false - ] - }, - "hash": "7c68d97a911d03c24b2fc0b41c5a1e20068fd7e1f686a1f7e5c15539de6d9580" -} diff --git a/.sqlx/query-869dcee67d4ee197b272b86b2ca9711116126764a1c8f600d00ceed977a20766.json b/.sqlx/query-869dcee67d4ee197b272b86b2ca9711116126764a1c8f600d00ceed977a20766.json deleted file mode 100644 index 2704524e1d..0000000000 --- a/.sqlx/query-869dcee67d4ee197b272b86b2ca9711116126764a1c8f600d00ceed977a20766.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n INSERT INTO printed_ballots (\n id,\n server_id,\n client_id,\n machine_id,\n common_access_card_id,\n common_access_card_certificate,\n registration_id,\n cast_vote_record,\n cast_vote_record_signature,\n created_at\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)\n ON CONFLICT (client_id, machine_id)\n DO UPDATE SET\n server_id = $2,\n cast_vote_record = $8,\n cast_vote_record_signature = $9,\n created_at = $10\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid", - "Uuid", - "Uuid", - "Varchar", - "Varchar", - "Bytea", - "Uuid", - "Bytea", - "Bytea", - "Timestamptz" - ] - }, - "nullable": [] - }, - "hash": "869dcee67d4ee197b272b86b2ca9711116126764a1c8f600d00ceed977a20766" -} diff --git a/.sqlx/query-c19fea777676a831ee36a352cd814cba5470b4f364c123d6ea65ae1682b0eefe.json b/.sqlx/query-c19fea777676a831ee36a352cd814cba5470b4f364c123d6ea65ae1682b0eefe.json deleted file mode 100644 index 60755a3d9d..0000000000 --- a/.sqlx/query-c19fea777676a831ee36a352cd814cba5470b4f364c123d6ea65ae1682b0eefe.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT server_id as \"server_id!: ServerId\"\n FROM scanned_ballots\n WHERE server_id IS NOT NULL\n ORDER BY created_at DESC\n LIMIT 1\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "server_id!: ServerId", - "type_info": "Uuid" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - false - ] - }, - "hash": "c19fea777676a831ee36a352cd814cba5470b4f364c123d6ea65ae1682b0eefe" -} diff --git a/.sqlx/query-c2c64ff498de789ab1ec0f5f4b2cd4d2f8b6682d6c0fbe321ccb9346fcc7301d.json b/.sqlx/query-c2c64ff498de789ab1ec0f5f4b2cd4d2f8b6682d6c0fbe321ccb9346fcc7301d.json deleted file mode 100644 index 29878c9cd4..0000000000 --- a/.sqlx/query-c2c64ff498de789ab1ec0f5f4b2cd4d2f8b6682d6c0fbe321ccb9346fcc7301d.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT\n id as \"id: _\",\n client_id as \"client_id: _\",\n machine_id,\n jurisdiction_id,\n definition,\n election_hash,\n created_at\n FROM elections\n WHERE created_at > $1\n ORDER BY created_at DESC\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id: _", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "client_id: _", - "type_info": "Uuid" - }, - { - "ordinal": 2, - "name": "machine_id", - "type_info": "Varchar" - }, - { - "ordinal": 3, - "name": "jurisdiction_id", - "type_info": "Uuid" - }, - { - "ordinal": 4, - "name": "definition", - "type_info": "Bytea" - }, - { - "ordinal": 5, - "name": "election_hash", - "type_info": "Varchar" - }, - { - "ordinal": 6, - "name": "created_at", - "type_info": "Timestamptz" - } - ], - "parameters": { - "Left": [ - "Timestamptz" - ] - }, - "nullable": [ - false, - false, - false, - false, - false, - false, - false - ] - }, - "hash": "c2c64ff498de789ab1ec0f5f4b2cd4d2f8b6682d6c0fbe321ccb9346fcc7301d" -} diff --git a/.sqlx/query-c72d6ec45e4ebc48188aee36c4e964fd5858a2fa2649c656d5025e4de6c866ae.json b/.sqlx/query-c72d6ec45e4ebc48188aee36c4e964fd5858a2fa2649c656d5025e4de6c866ae.json deleted file mode 100644 index 461fb2856c..0000000000 --- a/.sqlx/query-c72d6ec45e4ebc48188aee36c4e964fd5858a2fa2649c656d5025e4de6c866ae.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n INSERT INTO scanned_ballots (\n id,\n client_id,\n machine_id,\n election_id,\n cast_vote_record\n )\n VALUES (\n $1, $2, $3,\n (SELECT id FROM elections WHERE client_id = $4),\n $5\n )\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid", - "Uuid", - "Varchar", - "Uuid", - "Bytea" - ] - }, - "nullable": [] - }, - "hash": "c72d6ec45e4ebc48188aee36c4e964fd5858a2fa2649c656d5025e4de6c866ae" -} diff --git a/.sqlx/query-ca8f21b8ec1238ad97fe75692ce5383d238c4a5a53768269230063419162668d.json b/.sqlx/query-ca8f21b8ec1238ad97fe75692ce5383d238c4a5a53768269230063419162668d.json deleted file mode 100644 index 5321d06be4..0000000000 --- a/.sqlx/query-ca8f21b8ec1238ad97fe75692ce5383d238c4a5a53768269230063419162668d.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n UPDATE batches\n SET scanned_ballot_ids = ARRAY_APPEND(scanned_ballot_ids, $1)\n WHERE id = $2\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid", - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "ca8f21b8ec1238ad97fe75692ce5383d238c4a5a53768269230063419162668d" -} diff --git a/.sqlx/query-ccfb34dc0b1e0835b59eb39973b9d76d9edd5438a10b74ad58b941126ab9efe2.json b/.sqlx/query-ccfb34dc0b1e0835b59eb39973b9d76d9edd5438a10b74ad58b941126ab9efe2.json deleted file mode 100644 index ef75d917e2..0000000000 --- a/.sqlx/query-ccfb34dc0b1e0835b59eb39973b9d76d9edd5438a10b74ad58b941126ab9efe2.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n DELETE FROM printed_ballots\n WHERE now() - created_at > interval '1 second' * $1\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Float8" - ] - }, - "nullable": [] - }, - "hash": "ccfb34dc0b1e0835b59eb39973b9d76d9edd5438a10b74ad58b941126ab9efe2" -} diff --git a/.sqlx/query-d3a0c49c0d6d773cfc00384a42ce995827ad4eb6d4b435b27364301b2f2efc1e.json b/.sqlx/query-d3a0c49c0d6d773cfc00384a42ce995827ad4eb6d4b435b27364301b2f2efc1e.json deleted file mode 100644 index 5ebe310fca..0000000000 --- a/.sqlx/query-d3a0c49c0d6d773cfc00384a42ce995827ad4eb6d4b435b27364301b2f2efc1e.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT\n id as \"id: ServerId\",\n client_id as \"client_id: ClientId\",\n machine_id,\n jurisdiction_id as \"jurisdiction_id: ServerId\",\n common_access_card_id,\n given_name,\n family_name,\n created_at\n FROM registration_requests\n ORDER BY created_at DESC\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id: ServerId", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "client_id: ClientId", - "type_info": "Uuid" - }, - { - "ordinal": 2, - "name": "machine_id", - "type_info": "Varchar" - }, - { - "ordinal": 3, - "name": "jurisdiction_id: ServerId", - "type_info": "Uuid" - }, - { - "ordinal": 4, - "name": "common_access_card_id", - "type_info": "Varchar" - }, - { - "ordinal": 5, - "name": "given_name", - "type_info": "Varchar" - }, - { - "ordinal": 6, - "name": "family_name", - "type_info": "Varchar" - }, - { - "ordinal": 7, - "name": "created_at", - "type_info": "Timestamptz" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - false, - false, - false, - false, - false, - false, - false, - false - ] - }, - "hash": "d3a0c49c0d6d773cfc00384a42ce995827ad4eb6d4b435b27364301b2f2efc1e" -} diff --git a/.sqlx/query-d4bd52b3e2c3961766a2120fded05ffb1055e49c4ea8ff9b25f41afc1d1c41e0.json b/.sqlx/query-d4bd52b3e2c3961766a2120fded05ffb1055e49c4ea8ff9b25f41afc1d1c41e0.json deleted file mode 100644 index c4aa0e0754..0000000000 --- a/.sqlx/query-d4bd52b3e2c3961766a2120fded05ffb1055e49c4ea8ff9b25f41afc1d1c41e0.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT\n client_id as \"client_id: ClientId\",\n machine_id,\n election_id as \"election_id: ClientId\",\n cast_vote_record,\n created_at\n FROM scanned_ballots\n WHERE server_id IS NULL\n ORDER BY created_at DESC\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "client_id: ClientId", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "machine_id", - "type_info": "Varchar" - }, - { - "ordinal": 2, - "name": "election_id: ClientId", - "type_info": "Uuid" - }, - { - "ordinal": 3, - "name": "cast_vote_record", - "type_info": "Bytea" - }, - { - "ordinal": 4, - "name": "created_at", - "type_info": "Timestamptz" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - false, - false, - false, - false, - false - ] - }, - "hash": "d4bd52b3e2c3961766a2120fded05ffb1055e49c4ea8ff9b25f41afc1d1c41e0" -} diff --git a/.sqlx/query-d66db8b2deaf5c944481b903ec5a892ad2820668b3fdb8b54ce44d322d61f544.json b/.sqlx/query-d66db8b2deaf5c944481b903ec5a892ad2820668b3fdb8b54ce44d322d61f544.json deleted file mode 100644 index c9933cd554..0000000000 --- a/.sqlx/query-d66db8b2deaf5c944481b903ec5a892ad2820668b3fdb8b54ce44d322d61f544.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT\n created_at\n FROM scanned_ballots\n WHERE id = $1\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "created_at", - "type_info": "Timestamptz" - } - ], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [ - false - ] - }, - "hash": "d66db8b2deaf5c944481b903ec5a892ad2820668b3fdb8b54ce44d322d61f544" -} diff --git a/.sqlx/query-d95222a85acdaea5920302c8324b026a88493f8a891332420a6a25e4e2a98e99.json b/.sqlx/query-d95222a85acdaea5920302c8324b026a88493f8a891332420a6a25e4e2a98e99.json deleted file mode 100644 index f8b496c33e..0000000000 --- a/.sqlx/query-d95222a85acdaea5920302c8324b026a88493f8a891332420a6a25e4e2a98e99.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT id as \"id: ClientId\"\n FROM scanned_ballots\n WHERE server_id = $1\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id: ClientId", - "type_info": "Uuid" - } - ], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [ - false - ] - }, - "hash": "d95222a85acdaea5920302c8324b026a88493f8a891332420a6a25e4e2a98e99" -} diff --git a/.sqlx/query-d9d643718503989d188c6c78e4fe4129fbba214f4cfb4001eb19edfc4ab7c2ce.json b/.sqlx/query-d9d643718503989d188c6c78e4fe4129fbba214f4cfb4001eb19edfc4ab7c2ce.json deleted file mode 100644 index 33ac5f8078..0000000000 --- a/.sqlx/query-d9d643718503989d188c6c78e4fe4129fbba214f4cfb4001eb19edfc4ab7c2ce.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT\n machine_id,\n common_access_card_id,\n created_at\n FROM admins\n ORDER BY created_at ASC\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "machine_id", - "type_info": "Varchar" - }, - { - "ordinal": 1, - "name": "common_access_card_id", - "type_info": "Varchar" - }, - { - "ordinal": 2, - "name": "created_at", - "type_info": "Timestamptz" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - false, - false, - false - ] - }, - "hash": "d9d643718503989d188c6c78e4fe4129fbba214f4cfb4001eb19edfc4ab7c2ce" -} diff --git a/.sqlx/query-dadcb3746f28790d688946f16c418e064e93b1d252bbd993e03bf80f9a051b92.json b/.sqlx/query-dadcb3746f28790d688946f16c418e064e93b1d252bbd993e03bf80f9a051b92.json deleted file mode 100644 index dcd64b970e..0000000000 --- a/.sqlx/query-dadcb3746f28790d688946f16c418e064e93b1d252bbd993e03bf80f9a051b92.json +++ /dev/null @@ -1,76 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT\n id as \"id: ServerId\",\n client_id as \"client_id: ClientId\",\n machine_id,\n jurisdiction_id as \"jurisdiction_id: ServerId\",\n common_access_card_id,\n registration_request_id as \"registration_request_id: ServerId\",\n election_id as \"election_id: ServerId\",\n precinct_id,\n ballot_style_id,\n created_at\n FROM registrations\n WHERE created_at > $1\n ORDER BY created_at DESC\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id: ServerId", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "client_id: ClientId", - "type_info": "Uuid" - }, - { - "ordinal": 2, - "name": "machine_id", - "type_info": "Varchar" - }, - { - "ordinal": 3, - "name": "jurisdiction_id: ServerId", - "type_info": "Uuid" - }, - { - "ordinal": 4, - "name": "common_access_card_id", - "type_info": "Varchar" - }, - { - "ordinal": 5, - "name": "registration_request_id: ServerId", - "type_info": "Uuid" - }, - { - "ordinal": 6, - "name": "election_id: ServerId", - "type_info": "Uuid" - }, - { - "ordinal": 7, - "name": "precinct_id", - "type_info": "Varchar" - }, - { - "ordinal": 8, - "name": "ballot_style_id", - "type_info": "Varchar" - }, - { - "ordinal": 9, - "name": "created_at", - "type_info": "Timestamptz" - } - ], - "parameters": { - "Left": [ - "Timestamptz" - ] - }, - "nullable": [ - false, - false, - false, - false, - false, - false, - false, - false, - false, - false - ] - }, - "hash": "dadcb3746f28790d688946f16c418e064e93b1d252bbd993e03bf80f9a051b92" -} diff --git a/.sqlx/query-dc16fad7a566eaf2ca2e567c723231f7ed617b75c3dcd617270f37318012d00d.json b/.sqlx/query-dc16fad7a566eaf2ca2e567c723231f7ed617b75c3dcd617270f37318012d00d.json deleted file mode 100644 index 2c7f446012..0000000000 --- a/.sqlx/query-dc16fad7a566eaf2ca2e567c723231f7ed617b75c3dcd617270f37318012d00d.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n UPDATE batches\n SET ended_at = NOW()\n WHERE id = $1\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "dc16fad7a566eaf2ca2e567c723231f7ed617b75c3dcd617270f37318012d00d" -} diff --git a/.sqlx/query-e2603c8a2dda98dca20f1236bd9cd50f2c1ea4343ff236733172c1cf66916ec2.json b/.sqlx/query-e2603c8a2dda98dca20f1236bd9cd50f2c1ea4343ff236733172c1cf66916ec2.json deleted file mode 100644 index e89f34a34b..0000000000 --- a/.sqlx/query-e2603c8a2dda98dca20f1236bd9cd50f2c1ea4343ff236733172c1cf66916ec2.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n INSERT INTO printed_ballots (\n id,\n client_id,\n machine_id,\n common_access_card_id,\n common_access_card_certificate,\n registration_id,\n cast_vote_record,\n cast_vote_record_signature\n )\n VALUES (\n $1, $2, $3, $4, $5,\n (SELECT id FROM registrations WHERE client_id = $6),\n $7, $8\n )\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid", - "Uuid", - "Varchar", - "Varchar", - "Bytea", - "Uuid", - "Bytea", - "Bytea" - ] - }, - "nullable": [] - }, - "hash": "e2603c8a2dda98dca20f1236bd9cd50f2c1ea4343ff236733172c1cf66916ec2" -} diff --git a/.sqlx/query-53434d596cf7968794d37b469643351272f897ca683c98730a88a1c95ff17a62.json b/.sqlx/query-f112c9b052c9f083ff9dee6092d0e9bb9407e9d7a9e656bc52c0535bf0d8aac7.json similarity index 65% rename from .sqlx/query-53434d596cf7968794d37b469643351272f897ca683c98730a88a1c95ff17a62.json rename to .sqlx/query-f112c9b052c9f083ff9dee6092d0e9bb9407e9d7a9e656bc52c0535bf0d8aac7.json index ae7ee8ed87..173ad3c622 100644 --- a/.sqlx/query-53434d596cf7968794d37b469643351272f897ca683c98730a88a1c95ff17a62.json +++ b/.sqlx/query-f112c9b052c9f083ff9dee6092d0e9bb9407e9d7a9e656bc52c0535bf0d8aac7.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT\n jurisdiction_id as \"jurisdiction_id: ServerId\",\n client_id as \"client_id: ClientId\",\n machine_id,\n definition\n FROM elections\n WHERE server_id IS NULL\n ORDER BY created_at ASC\n ", + "query": "\n SELECT\n jurisdiction_id as \"jurisdiction_id: ServerId\",\n client_id as \"client_id: ClientId\",\n machine_id,\n definition,\n return_address\n FROM elections\n WHERE server_id IS NULL\n ORDER BY created_at ASC\n ", "describe": { "columns": [ { @@ -22,6 +22,11 @@ "ordinal": 3, "name": "definition", "type_info": "Bytea" + }, + { + "ordinal": 4, + "name": "return_address", + "type_info": "Text" } ], "parameters": { @@ -31,8 +36,9 @@ false, false, false, + false, false ] }, - "hash": "53434d596cf7968794d37b469643351272f897ca683c98730a88a1c95ff17a62" + "hash": "f112c9b052c9f083ff9dee6092d0e9bb9407e9d7a9e656bc52c0535bf0d8aac7" } diff --git a/.sqlx/query-f20f9b3ac334686762f3bddcac077b57a4d983779f8fbe4f5b67535e5179c554.json b/.sqlx/query-f20f9b3ac334686762f3bddcac077b57a4d983779f8fbe4f5b67535e5179c554.json deleted file mode 100644 index 5d91aec1a1..0000000000 --- a/.sqlx/query-f20f9b3ac334686762f3bddcac077b57a4d983779f8fbe4f5b67535e5179c554.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT\n client_id as \"client_id: ClientId\",\n machine_id,\n common_access_card_id,\n common_access_card_certificate,\n registration_id as \"registration_id: ClientId\",\n cast_vote_record,\n cast_vote_record_signature\n FROM printed_ballots\n WHERE server_id IS NULL\n ORDER BY created_at ASC\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "client_id: ClientId", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "machine_id", - "type_info": "Varchar" - }, - { - "ordinal": 2, - "name": "common_access_card_id", - "type_info": "Varchar" - }, - { - "ordinal": 3, - "name": "common_access_card_certificate", - "type_info": "Bytea" - }, - { - "ordinal": 4, - "name": "registration_id: ClientId", - "type_info": "Uuid" - }, - { - "ordinal": 5, - "name": "cast_vote_record", - "type_info": "Bytea" - }, - { - "ordinal": 6, - "name": "cast_vote_record_signature", - "type_info": "Bytea" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - false, - false, - false, - false, - false, - false, - false - ] - }, - "hash": "f20f9b3ac334686762f3bddcac077b57a4d983779f8fbe4f5b67535e5179c554" -} diff --git a/.sqlx/query-f305266bf89a3cab405a359d5f0ab357bbe118fa193c4ccddb9ac0cd9cafe202.json b/.sqlx/query-f305266bf89a3cab405a359d5f0ab357bbe118fa193c4ccddb9ac0cd9cafe202.json deleted file mode 100644 index c97a4aaecd..0000000000 --- a/.sqlx/query-f305266bf89a3cab405a359d5f0ab357bbe118fa193c4ccddb9ac0cd9cafe202.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT\n id as \"id: ServerId\",\n client_id as \"client_id: ClientId\",\n machine_id,\n common_access_card_id,\n common_access_card_certificate,\n registration_id as \"registration_id: ServerId\",\n cast_vote_record,\n cast_vote_record_signature,\n created_at\n FROM printed_ballots\n ORDER BY created_at DESC\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id: ServerId", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "client_id: ClientId", - "type_info": "Uuid" - }, - { - "ordinal": 2, - "name": "machine_id", - "type_info": "Varchar" - }, - { - "ordinal": 3, - "name": "common_access_card_id", - "type_info": "Varchar" - }, - { - "ordinal": 4, - "name": "common_access_card_certificate", - "type_info": "Bytea" - }, - { - "ordinal": 5, - "name": "registration_id: ServerId", - "type_info": "Uuid" - }, - { - "ordinal": 6, - "name": "cast_vote_record", - "type_info": "Bytea" - }, - { - "ordinal": 7, - "name": "cast_vote_record_signature", - "type_info": "Bytea" - }, - { - "ordinal": 8, - "name": "created_at", - "type_info": "Timestamptz" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - false, - false, - false, - false, - false, - false, - false, - false, - false - ] - }, - "hash": "f305266bf89a3cab405a359d5f0ab357bbe118fa193c4ccddb9ac0cd9cafe202" -} diff --git a/.sqlx/query-f472f6c1413aef8176b26064840908fac5d28f2049658fec150196890eda9fef.json b/.sqlx/query-f472f6c1413aef8176b26064840908fac5d28f2049658fec150196890eda9fef.json deleted file mode 100644 index 1d129dedb6..0000000000 --- a/.sqlx/query-f472f6c1413aef8176b26064840908fac5d28f2049658fec150196890eda9fef.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT created_at\n FROM elections\n WHERE id = $1\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "created_at", - "type_info": "Timestamptz" - } - ], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [ - false - ] - }, - "hash": "f472f6c1413aef8176b26064840908fac5d28f2049658fec150196890eda9fef" -} diff --git a/.sqlx/query-fd5bbe94ae688cbba67694013649ea3160d5e13c6c23bbbfdc7f3f32668b225f.json b/.sqlx/query-fd5bbe94ae688cbba67694013649ea3160d5e13c6c23bbbfdc7f3f32668b225f.json deleted file mode 100644 index dc221706ab..0000000000 --- a/.sqlx/query-fd5bbe94ae688cbba67694013649ea3160d5e13c6c23bbbfdc7f3f32668b225f.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT\n id as \"id: ServerId\",\n client_id as \"client_id: ClientId\",\n machine_id,\n election_id as \"election_id: ServerId\",\n cast_vote_record as \"cast_vote_record: _\",\n created_at\n FROM scanned_ballots\n WHERE created_at > $1\n ORDER BY created_at DESC\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id: ServerId", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "client_id: ClientId", - "type_info": "Uuid" - }, - { - "ordinal": 2, - "name": "machine_id", - "type_info": "Varchar" - }, - { - "ordinal": 3, - "name": "election_id: ServerId", - "type_info": "Uuid" - }, - { - "ordinal": 4, - "name": "cast_vote_record: _", - "type_info": "Bytea" - }, - { - "ordinal": 5, - "name": "created_at", - "type_info": "Timestamptz" - } - ], - "parameters": { - "Left": [ - "Timestamptz" - ] - }, - "nullable": [ - false, - false, - false, - false, - false, - false - ] - }, - "hash": "fd5bbe94ae688cbba67694013649ea3160d5e13c6c23bbbfdc7f3f32668b225f" -} diff --git a/.sqlx/query-fe73064275b12c3a341c87f926967f7e9e45b608e0dbb0d25af47a8bdc34dcae.json b/.sqlx/query-fe73064275b12c3a341c87f926967f7e9e45b608e0dbb0d25af47a8bdc34dcae.json deleted file mode 100644 index c24988fa3e..0000000000 --- a/.sqlx/query-fe73064275b12c3a341c87f926967f7e9e45b608e0dbb0d25af47a8bdc34dcae.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n INSERT INTO batches (id, scanned_ballot_ids)\n VALUES ($1, $2)\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid", - "UuidArray" - ] - }, - "nullable": [] - }, - "hash": "fe73064275b12c3a341c87f926967f7e9e45b608e0dbb0d25af47a8bdc34dcae" -} diff --git a/Cargo.lock b/Cargo.lock index 93ed7f8f83..50a5ed1fe3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -314,18 +314,6 @@ dependencies = [ "rustc-demangle", ] -[[package]] -name = "ballot-encoder-rs" -version = "0.1.0" -dependencies = [ - "bitstream-io", - "color-eyre", - "hex", - "nanoid", - "pretty_assertions", - "types-rs", -] - [[package]] name = "base64" version = "0.21.5" @@ -387,12 +375,6 @@ dependencies = [ "serde", ] -[[package]] -name = "bitstream-io" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e445576659fd04a57b44cbd00aa37aaa815ebefa0aa3cb677a6b5e63d883074f" - [[package]] name = "block-buffer" version = "0.10.4" @@ -424,12 +406,6 @@ version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" -[[package]] -name = "bytemuck" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" - [[package]] name = "byteorder" version = "1.5.0" @@ -511,55 +487,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "cacvote-scan-backend" -version = "0.1.0" -dependencies = [ - "async-stream", - "axum", - "ballot-encoder-rs", - "base64", - "central-scanner", - "clap", - "color-eyre", - "dotenvy", - "env_logger", - "futures-core", - "image", - "pretty_assertions", - "rayon", - "reqwest", - "rqrr", - "serde", - "serde_json", - "sqlx", - "time", - "tokio", - "tower-http", - "tracing", - "tracing-subscriber", - "types-rs", - "uuid", -] - -[[package]] -name = "cacvote-scan-frontend" -version = "0.1.0" -dependencies = [ - "console_error_panic_hook", - "dioxus", - "dioxus-logger", - "dioxus-web", - "log", - "reqwest", - "serde", - "serde_json", - "types-rs", - "ui-rs", - "wasm-bindgen", - "web-sys", -] - [[package]] name = "cacvote-server" version = "0.1.0" @@ -594,17 +521,6 @@ dependencies = [ "libc", ] -[[package]] -name = "central-scanner" -version = "0.1.0" -dependencies = [ - "color-eyre", - "env_logger", - "log", - "serde", - "tokio", -] - [[package]] name = "cfg-if" version = "0.1.10" @@ -708,12 +624,6 @@ dependencies = [ "tracing-error", ] -[[package]] -name = "color_quant" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" - [[package]] name = "colorchoice" version = "1.0.0" @@ -812,28 +722,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "338089f42c427b86394a5ee60ff321da23a5c89c9d89514c829687b26359fcff" -[[package]] -name = "crossbeam-deque" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fca89a0e215bab21874660c67903c5f143333cab1da83d041c7ded6053774751" -dependencies = [ - "cfg-if 1.0.0", - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e3681d554572a651dda4186cd47240627c3d0114d45a95f6ad27f2f22e7548d" -dependencies = [ - "autocfg", - "cfg-if 1.0.0", - "crossbeam-utils", -] - [[package]] name = "crossbeam-queue" version = "0.3.10" @@ -1196,19 +1084,6 @@ dependencies = [ "syn 2.0.46", ] -[[package]] -name = "env_logger" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece" -dependencies = [ - "humantime", - "is-terminal", - "log", - "regex", - "termcolor", -] - [[package]] name = "equivalent" version = "1.0.1" @@ -1430,34 +1305,6 @@ dependencies = [ "slab", ] -[[package]] -name = "g2gen" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc2c7625b2fc250dd90b63f7887a6bb0f7ec1d714c8278415bea2669ef20820e" -dependencies = [ - "g2poly", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "g2p" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc36d9bdc3d2da057775a9f4fa7d7b09edab3e0eda7a92cc353358fa63b8519e" -dependencies = [ - "g2gen", - "g2poly", -] - -[[package]] -name = "g2poly" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af6a86e750338603ea2c14b1c0bfe58cd61f87ca67a0021d9334996024608e12" - [[package]] name = "gcc" version = "0.3.55" @@ -1687,15 +1534,6 @@ dependencies = [ "ahash 0.7.7", ] -[[package]] -name = "hashbrown" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" -dependencies = [ - "ahash 0.8.7", -] - [[package]] name = "hashbrown" version = "0.14.3" @@ -1809,12 +1647,6 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - [[package]] name = "hyper" version = "0.14.28" @@ -1891,20 +1723,6 @@ dependencies = [ "unicode-normalization", ] -[[package]] -name = "image" -version = "0.24.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f3dfdbdd72063086ff443e297b61695500514b1e41095b6fb9a5ab48a70a711" -dependencies = [ - "bytemuck", - "byteorder", - "color_quant", - "jpeg-decoder", - "num-rational", - "num-traits", -] - [[package]] name = "indenter" version = "0.3.3" @@ -1978,17 +1796,6 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" -[[package]] -name = "is-terminal" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455" -dependencies = [ - "hermit-abi", - "rustix", - "windows-sys 0.52.0", -] - [[package]] name = "itertools" version = "0.12.0" @@ -2004,12 +1811,6 @@ version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" -[[package]] -name = "jpeg-decoder" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e" - [[package]] name = "js-sys" version = "0.3.66" @@ -2129,15 +1930,6 @@ dependencies = [ "hashbrown 0.12.3", ] -[[package]] -name = "lru" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e7d46de488603ffdd5f30afbc64fbba2378214a2c3a2fb83abf3d33126df17" -dependencies = [ - "hashbrown 0.13.2", -] - [[package]] name = "mach2" version = "0.4.2" @@ -2341,17 +2133,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-rational" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - [[package]] name = "num-traits" version = "0.2.17" @@ -2746,26 +2527,6 @@ dependencies = [ "rand_core", ] -[[package]] -name = "rayon" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] - [[package]] name = "redox_syscall" version = "0.4.1" @@ -2871,17 +2632,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "rqrr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a8b87d1f9f69bb1a6c77e20fd303f9617b2b68dcff87cd9bcbfff2ced4b8a0b" -dependencies = [ - "g2p", - "image", - "lru 0.9.0", -] - [[package]] name = "rsa" version = "0.9.6" @@ -3214,7 +2964,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5cd16550f1dd7866c7580dbf80c892dc1bef106737eeb850d42c62ec61896059" dependencies = [ - "lru 0.8.1", + "lru", "once_cell", "rustc-hash", ] @@ -3579,15 +3329,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "termcolor" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" -dependencies = [ - "winapi-util", -] - [[package]] name = "thiserror" version = "1.0.56" @@ -4102,9 +3843,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen-macro", @@ -4112,9 +3853,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", @@ -4139,9 +3880,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4149,9 +3890,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", @@ -4162,9 +3903,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "wasm-logger" @@ -4215,15 +3956,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -[[package]] -name = "winapi-util" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" -dependencies = [ - "winapi", -] - [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index 0a95232c63..f61ff3a949 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,11 +5,7 @@ members = [ "apps/mark-scan/pat-device-input", "apps/cacvote-jx-terminal/backend", "apps/cacvote-jx-terminal/frontend", - "apps/cacvote-scan/backend", - "apps/cacvote-scan/frontend", "libs/auth-rs", - "libs/ballot-encoder-rs", - "libs/central-scanner", "libs/logging", "libs/types-rs", "libs/ui-rs", @@ -20,12 +16,10 @@ members = [ async-stream = "0.3.5" auth-rs = { path = "libs/auth-rs" } axum = { version = "0.6.20" } -ballot-encoder-rs = { path = "libs/ballot-encoder-rs" } base64 = "0.21.4" base64-serde = "0.7.0" bitstream-io = "1.7.0" bitter = "0.6.1" -central-scanner = { path = "libs/central-scanner" } clap = { version = "4.3.23", features = ["cargo", "derive", "env"] } color-eyre = "0.6.2" crc16 = "0.4.0" @@ -58,7 +52,6 @@ proptest = "1.2.0" rayon = "1.7.0" regex = "1.9.1" reqwest = { version = "0.11.18", features = ["json"] } -rqrr = "0.6.0" rusttype = "0.9.3" serde = { version = "1.0.175", features = ["derive"] } serde_json = "1.0.103" @@ -79,8 +72,6 @@ types-rs = { path = "libs/types-rs" } ui-rs = { path = "libs/ui-rs" } uinput = "0.1.3" uuid = { version = "1.4.0", features = ["serde", "v4", "js"] } -zbar-rust = "0.0.21" - [workspace.dependencies.sqlx] version = "0.7.1" diff --git a/README.md b/README.md index 592dfc3a66..64f8d10436 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,6 @@ ## Structure - `apps/cacvote-mark`: the CACVote Mark voter-facing application -- `apps/cacvote-scan`: the CACVote Scan application for ballot scanning - `apps/cacvote-jx`: the CACVote Jurisdiction application for election management - `services/cacvote-server`: the CACVote server providing sync services for the diff --git a/apps/cacvote-jx-terminal/backend/db/migrations/20240305164418_remove_scanned_ballots.sql b/apps/cacvote-jx-terminal/backend/db/migrations/20240305164418_remove_scanned_ballots.sql new file mode 100644 index 0000000000..2a3af94c37 --- /dev/null +++ b/apps/cacvote-jx-terminal/backend/db/migrations/20240305164418_remove_scanned_ballots.sql @@ -0,0 +1 @@ +DROP TABLE scanned_ballots; diff --git a/apps/cacvote-jx-terminal/backend/src/db.rs b/apps/cacvote-jx-terminal/backend/src/db.rs index c1dfac64d9..2575e93370 100644 --- a/apps/cacvote-jx-terminal/backend/src/db.rs +++ b/apps/cacvote-jx-terminal/backend/src/db.rs @@ -18,7 +18,6 @@ use tracing::Level; use types_rs::cacvote::client::output::Jurisdiction; use types_rs::cacvote::jx; use types_rs::cacvote::{client, ClientId, ServerId}; -use types_rs::cdf::cvr::Cvr; use types_rs::election::{BallotStyleId, ElectionDefinition, ElectionHash, PrecinctId}; use uuid::Uuid; @@ -87,20 +86,6 @@ pub(crate) struct Registration { pub(crate) created_at: sqlx::types::time::OffsetDateTime, } -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub(crate) struct ScannedBallot { - pub(crate) id: ClientId, - pub(crate) server_id: Option, - pub(crate) client_id: ClientId, - pub(crate) machine_id: String, - pub(crate) election_id: ClientId, - #[serde(with = "Base64Standard")] - pub(crate) cast_vote_record: Vec, - #[serde(with = "time::serde::iso8601")] - pub(crate) created_at: sqlx::types::time::OffsetDateTime, -} - pub(crate) async fn get_app_data( executor: &mut sqlx::PgConnection, jurisdiction_code: String, @@ -116,7 +101,6 @@ pub(crate) async fn get_app_data( let registration_requests = get_registration_requests(executor, jurisdiction_id).await?; let registrations = get_registrations(executor, jurisdiction_id).await?; let printed_ballots = get_printed_ballots(executor, jurisdiction_id).await?; - let scanned_ballots = get_scanned_ballots(executor, jurisdiction_id).await?; Ok(jx::LoggedInAppData { registrations: registrations @@ -181,7 +165,6 @@ pub(crate) async fn get_app_data( }) .collect(), printed_ballots, - scanned_ballots, }) } @@ -569,46 +552,6 @@ pub(crate) async fn get_printed_ballots( .collect::, _>>() } -pub(crate) async fn get_scanned_ballots( - executor: &mut sqlx::PgConnection, - jurisdiction_id: ServerId, -) -> color_eyre::Result> { - let records = sqlx::query!( - r#" - SELECT - scanned_ballots.id as "id: ClientId", - scanned_ballots.server_id as "server_id: ServerId", - scanned_ballots.election_id as "election_id: ClientId", - scanned_ballots.cast_vote_record, - scanned_ballots.created_at - FROM scanned_ballots - INNER JOIN elections ON elections.id = scanned_ballots.election_id - WHERE elections.jurisdiction_id = $1 - ORDER BY created_at DESC - "#, - jurisdiction_id.as_uuid(), - ) - .fetch_all(&mut *executor) - .await?; - - records - .into_iter() - .map(|record| { - let cast_vote_record: Cvr = serde_json::from_slice(&record.cast_vote_record)?; - - Ok(jx::ScannedBallot { - id: record.id, - server_id: record.server_id, - election_id: record.election_id, - precinct_id: PrecinctId::from(cast_vote_record.ballot_style_unit_id.unwrap()), - ballot_style_id: BallotStyleId::from(cast_vote_record.ballot_style_id.unwrap()), - cast_vote_record: record.cast_vote_record, - created_at: record.created_at, - }) - }) - .collect::, _>>() -} - pub(crate) async fn create_registration( executor: &mut sqlx::PgConnection, config: &Config, @@ -939,67 +882,6 @@ pub(crate) async fn add_or_update_printed_ballot_from_cacvote_server( Ok(id) } -pub(crate) async fn add_or_update_scanned_ballot_from_cacvote_server( - executor: &mut sqlx::PgConnection, - scanned_ballot: client::output::ScannedBallot, -) -> color_eyre::Result { - let election_id = sqlx::query!( - r#" - SELECT id as "id: ClientId" - FROM elections - WHERE server_id = $1 - "#, - scanned_ballot.election_id.as_uuid(), - ) - .fetch_one(&mut *executor) - .await? - .id; - - sqlx::query!( - r#" - INSERT INTO scanned_ballots ( - id, - server_id, - client_id, - machine_id, - election_id, - cast_vote_record, - created_at - ) - VALUES ($1, $2, $3, $4, $5, $6, $7) - ON CONFLICT (client_id, machine_id) - DO UPDATE SET - server_id = $2, - election_id = $5, - cast_vote_record = $6, - created_at = $7 - "#, - ClientId::new().as_uuid(), - scanned_ballot.server_id.as_uuid(), - scanned_ballot.client_id.as_uuid(), - scanned_ballot.machine_id, - election_id.as_uuid(), - scanned_ballot.cast_vote_record, - scanned_ballot.created_at - ) - .execute(&mut *executor) - .await?; - - let id = sqlx::query!( - r#" - SELECT id as "id: ClientId" - FROM scanned_ballots - WHERE server_id = $1 - "#, - scanned_ballot.server_id.as_uuid(), - ) - .fetch_one(&mut *executor) - .await? - .id; - - Ok(id) -} - pub(crate) async fn add_or_update_registration_request_from_cacvote_server( executor: &mut sqlx::PgConnection, registration_request: client::output::RegistrationRequest, @@ -1197,94 +1079,6 @@ pub(crate) async fn get_printed_ballots_to_sync_to_cacvote_server( .collect() } -pub(crate) async fn get_scanned_ballots_to_sync_to_cacvote_server( - executor: &mut sqlx::PgConnection, -) -> color_eyre::Result> { - let records = sqlx::query!( - r#" - SELECT - client_id as "client_id: ClientId", - machine_id, - election_id as "election_id: ClientId", - cast_vote_record, - created_at - FROM scanned_ballots - WHERE server_id IS NULL - ORDER BY created_at DESC - "# - ) - .fetch_all(&mut *executor) - .await?; - - Ok(records - .into_iter() - .map(|r| client::input::ScannedBallot { - client_id: r.client_id, - machine_id: r.machine_id, - election_id: r.election_id, - cast_vote_record: r.cast_vote_record, - }) - .collect::>()) -} - -#[allow(dead_code)] -pub(crate) async fn add_scanned_ballot( - executor: &mut sqlx::PgConnection, - scanned_ballot: ScannedBallot, -) -> Result<(), sqlx::Error> { - let ScannedBallot { - id, - server_id, - client_id, - machine_id, - election_id, - cast_vote_record, - created_at, - } = scanned_ballot; - sqlx::query!( - r#" - INSERT INTO scanned_ballots ( - id, - server_id, - client_id, - machine_id, - election_id, - cast_vote_record, - created_at - ) - VALUES ($1, $2, $3, $4, $5, $6, $7) - "#, - id.as_uuid(), - server_id.map(|id| id.as_uuid()), - client_id.as_uuid(), - machine_id, - election_id.as_uuid(), - cast_vote_record, - created_at - ) - .execute(executor) - .await?; - - Ok(()) -} - -pub(crate) async fn get_last_synced_scanned_ballot_id( - executor: &mut sqlx::PgConnection, -) -> color_eyre::Result> { - Ok(sqlx::query!( - r#" - SELECT server_id as "server_id!: ServerId" - FROM scanned_ballots - WHERE server_id IS NOT NULL - ORDER BY created_at DESC - LIMIT 1 - "# - ) - .fetch_optional(&mut *executor) - .await? - .map(|r| r.server_id)) -} - #[cfg(test)] mod test { use super::*; @@ -1375,20 +1169,6 @@ mod test { } } - fn build_cacvote_server_scanned_ballot( - election: &client::output::Election, - cast_vote_record: Cvr, - ) -> client::output::ScannedBallot { - client::output::ScannedBallot { - server_id: election.server_id, - client_id: election.client_id, - machine_id: election.machine_id.clone(), - cast_vote_record: serde_json::to_vec(&cast_vote_record).unwrap(), - election_id: election.server_id, - created_at: OffsetDateTime::now_utc(), - } - } - fn build_config() -> Config { Config { cacvote_url: reqwest::Url::parse("http://localhost:8000").unwrap(), @@ -1475,7 +1255,6 @@ mod test { &election_definition, ); let printed_ballot = build_cacvote_server_printed_ballot(®istration, Cvr::default()); - let scanned_ballot = build_cacvote_server_scanned_ballot(&election, Cvr::default()); add_or_update_jurisdiction_from_cacvote_server(&mut db, jurisdiction) .await @@ -1497,10 +1276,6 @@ mod test { .await .unwrap(); - add_or_update_scanned_ballot_from_cacvote_server(&mut db, scanned_ballot) - .await - .unwrap(); - Ok(()) } } diff --git a/apps/cacvote-jx-terminal/backend/src/sync.rs b/apps/cacvote-jx-terminal/backend/src/sync.rs index 169e94f0be..40e6651932 100644 --- a/apps/cacvote-jx-terminal/backend/src/sync.rs +++ b/apps/cacvote-jx-terminal/backend/src/sync.rs @@ -60,11 +60,6 @@ pub(crate) async fn sync( .map_err(|e| { color_eyre::Report::msg(format!("failed to get last synced registration ID: {e}")) })?, - last_synced_scanned_ballot_id: db::get_last_synced_scanned_ballot_id(executor) - .await - .map_err(|e| { - color_eyre::Report::msg(format!("failed to get last synced scanned ballot ID: {e}")) - })?, last_synced_printed_ballot_id: db::get_last_synced_printed_ballot_id(executor) .await .map_err(|e| { @@ -98,13 +93,6 @@ pub(crate) async fn sync( "failed to get printed ballots to sync to CACVote Server: {e}" )) })?, - scanned_ballots: db::get_scanned_ballots_to_sync_to_cacvote_server(executor) - .await - .map_err(|e| { - color_eyre::Report::msg(format!( - "failed to get scanned ballots to sync to CACVote Server: {e}" - )) - })?, }; let sync_endpoint = config @@ -119,7 +107,6 @@ pub(crate) async fn sync( registration_requests, registrations, printed_ballots, - scanned_ballots, .. } = sync_output.clone(); @@ -170,15 +157,6 @@ pub(crate) async fn sync( } } - for scanned_ballot in scanned_ballots { - let result = - db::add_or_update_scanned_ballot_from_cacvote_server(executor, scanned_ballot).await; - - if let Err(e) = result { - tracing::error!("Failed to insert or update scanned ballot: {e}"); - } - } - Ok(()) } diff --git a/apps/cacvote-jx-terminal/frontend/src/pages/ballots_page.rs b/apps/cacvote-jx-terminal/frontend/src/pages/ballots_page.rs index 683bd04874..8f166ac8a5 100644 --- a/apps/cacvote-jx-terminal/frontend/src/pages/ballots_page.rs +++ b/apps/cacvote-jx-terminal/frontend/src/pages/ballots_page.rs @@ -14,7 +14,6 @@ pub fn BallotsPage(cx: Scope) -> Element { let elections = app_data.elections.clone(); let printed_ballots = app_data.printed_ballots.clone(); - let scanned_ballots = app_data.scanned_ballots.clone(); render!( h1 { class: "text-2xl font-bold mb-4", "Printed Ballots" } @@ -27,17 +26,6 @@ pub fn BallotsPage(cx: Scope) -> Element { printed_ballots: printed_ballots, }) } - - h1 { class: "text-2xl font-bold mt-4 mb-4", "Scanned Ballots" } - if scanned_ballots.is_empty() { - rsx!("No scanned ballots") - } else { - to_owned![elections, scanned_ballots]; - rsx!(ScannedBallotsTable { - elections: elections, - scanned_ballots: scanned_ballots, - }) - } ) } @@ -198,45 +186,3 @@ fn PrintedBallotsTable(cx: Scope) -> Element { } ) } - -#[derive(Debug, PartialEq, Props)] -struct RegistrationsTableProps { - elections: Vec, - scanned_ballots: Vec, -} - -fn ScannedBallotsTable(cx: Scope) -> Element { - render!( - table { class: "table-auto w-full", - thead { - tr { - th { class: "px-4 py-2 text-left", "Election" } - th { class: "px-4 py-2 text-left", "Created At" } - } - } - tbody { - for scanned_ballot in cx.props.scanned_ballots.iter() { - tr { - { - let election = cx - .props - .elections - .iter() - .find(|election| *election.id() == scanned_ballot.election_id) - .unwrap(); - rsx!(ElectionConfigurationCell { - election_title: election.title.clone(), - election_hash: election.election_hash.clone(), - precinct_id: scanned_ballot.precinct_id.clone(), - ballot_style_id: scanned_ballot.ballot_style_id.clone(), - }) - } - DateOrDateTimeCell { - date_or_datetime: scanned_ballot.created_at() - } - } - } - } - } - ) -} diff --git a/apps/cacvote-mark/backend/dev-workspace/cacvote-mark.db b/apps/cacvote-mark/backend/dev-workspace/cacvote-mark.db deleted file mode 100644 index 12628bc2c6..0000000000 Binary files a/apps/cacvote-mark/backend/dev-workspace/cacvote-mark.db and /dev/null differ diff --git a/apps/cacvote-mark/backend/dev-workspace/cacvote-mark.db.digest b/apps/cacvote-mark/backend/dev-workspace/cacvote-mark.db.digest deleted file mode 100644 index 555b408b22..0000000000 --- a/apps/cacvote-mark/backend/dev-workspace/cacvote-mark.db.digest +++ /dev/null @@ -1 +0,0 @@ -fe26a9faf02ab6abbb4924613d850073f0c2f008dda2405d79dae119f4efb300 \ No newline at end of file diff --git a/apps/cacvote-mark/backend/schema.sql b/apps/cacvote-mark/backend/schema.sql index df05a974d6..ac47e804d3 100644 --- a/apps/cacvote-mark/backend/schema.sql +++ b/apps/cacvote-mark/backend/schema.sql @@ -106,18 +106,3 @@ create table printed_ballots ( unique (client_id, machine_id) ); - -create table scanned_ballots ( - id uuid primary key, - -- generated on the server, present only if the record has been synced - server_id uuid unique, - -- generated on a client machine - client_id uuid not null, - -- ID of the machine this record was originally created on - machine_id varchar(255) not null, - election_id uuid not null references elections(id) on update cascade on delete cascade, - cast_vote_record bytea not null, - created_at timestamptz not null default current_timestamp, - - unique (client_id, machine_id) -); diff --git a/apps/cacvote-mark/backend/src/cacvote_server_client.ts b/apps/cacvote-mark/backend/src/cacvote_server_client.ts index 9f1feee13d..45f5483906 100644 --- a/apps/cacvote-mark/backend/src/cacvote_server_client.ts +++ b/apps/cacvote-mark/backend/src/cacvote_server_client.ts @@ -37,16 +37,6 @@ function describeSyncInputOrOutput( ); } - if (data.scannedBallots?.length) { - parts.push( - format.countPhrase({ - value: data.scannedBallots.length, - one: '1 scanned ballot', - many: `${data.scannedBallots.length} scanned ballots`, - }) - ); - } - if (data.jurisdictions?.length) { parts.push( format.countPhrase({ @@ -136,13 +126,11 @@ export class RaveServerClientImpl { lastSyncedRegistrationId: this.store.getLastSyncedRegistrationId(), lastSyncedElectionId: this.store.getLastSyncedElectionId(), lastSyncedPrintedBallotId: this.store.getLastSyncedPrintedBallotId(), - lastSyncedScannedBallotId: this.store.getLastSyncedScannedBallotId(), registrationRequests: this.store.getRegistrationRequestsToSync(), jurisdictions: this.store.getJurisdictionsToSync(), elections: this.store.getElectionsToSync(), registrations: this.store.getRegistrationsToSync(), printedBallots: this.store.getPrintedBallotsToSync(), - scannedBallots: this.store.getScannedBallotsToSync(), }; debug('CACVote sync input: %O', input); return input; @@ -279,31 +267,6 @@ export class RaveServerClientImpl { debug('created or replaced printed ballot %s', ballotId); } - - for (const scannedBallot of output.scannedBallots) { - const localElection = this.store.getElection({ - serverId: scannedBallot.electionId, - }); - - assert( - localElection, - `could not find local election with server id ${scannedBallot.electionId}` - ); - - const ballotId = this.store.createScannedBallot({ - id: - scannedBallot.machineId === VX_MACHINE_ID - ? scannedBallot.clientId - : ClientId(), - serverId: scannedBallot.serverId, - clientId: scannedBallot.clientId, - machineId: scannedBallot.machineId, - electionId: localElection.id, - castVoteRecord: scannedBallot.castVoteRecord, - }); - - debug('created or replaced scanned ballot %s', ballotId); - } } private updateServerSyncAttempt({ diff --git a/apps/cacvote-mark/backend/src/store.ts b/apps/cacvote-mark/backend/src/store.ts index 2e7383d3d7..ff7b30582c 100644 --- a/apps/cacvote-mark/backend/src/store.ts +++ b/apps/cacvote-mark/backend/src/store.ts @@ -32,8 +32,6 @@ import { ServerId, ServerSyncAttempt, ServerSyncAttemptRow, - ScannedBallotRow, - deserializeScannedBallot, Jurisdiction, JurisdictionRow, deserializeJurisdiction, @@ -43,7 +41,6 @@ import { ElectionInput, JurisdictionInput, PrintedBallotInput, - ScannedBallotInput, } from './types/sync'; const SchemaPath = join(__dirname, '../schema.sql'); @@ -613,55 +610,6 @@ export class Store { return id; } - createScannedBallot({ - id, - serverId, - clientId, - machineId, - electionId, - castVoteRecord, - }: { - id: ClientId; - serverId: ServerId; - clientId: ClientId; - machineId: string; - electionId: ClientId; - castVoteRecord: Buffer; - }): ClientId { - assert( - (serverId === undefined) === (clientId === undefined), - 'ballot serverId must be defined if and only if clientId is defined' - ); - assert( - (machineId === VX_MACHINE_ID) === (clientId === id || !clientId), - 'ballot machineId must be VX_MACHINE_ID if and only if ID equals clientId' - ); - - // TODO: validate votes against election definition - this.client.run( - ` - insert or replace into scanned_ballots ( - id, - server_id, - client_id, - machine_id, - election_id, - cast_vote_record - ) values ( - ?, ?, ?, ?, ?, ? - ) - `, - id, - serverId ?? null, - clientId ?? id, - machineId, - electionId, - castVoteRecord - ); - - return id; - } - /** * Records a cast ballot for a voter registration. */ @@ -964,20 +912,6 @@ export class Store { return result ? result.serverId : undefined; } - getLastSyncedScannedBallotId(): Optional { - const result = this.client.one( - ` - select - server_id as serverId - from scanned_ballots - where server_id is not null - order by created_at desc - ` - ) as Optional<{ serverId: ServerId }>; - - return result ? result.serverId : undefined; - } - getRegistrationRequestsToSync(): RegistrationRequest[] { const result = this.client.all( ` @@ -1104,32 +1038,4 @@ export class Store { }; }); } - - getScannedBallotsToSync(): ScannedBallotInput[] { - const result = this.client.all( - ` - select - id, - server_id as serverId, - client_id as clientId, - machine_id as machineId, - (select client_id from elections where id = election_id) as electionId, - cast_vote_record as castVoteRecord, - created_at as createdAt - from scanned_ballots - where server_id is null - ` - ) as ScannedBallotRow[]; - - return result.map((row) => { - const record = deserializeScannedBallot(row); - return { - ...record, - castVoteRecord: unsafeParse( - Base64StringSchema, - record.castVoteRecord.toString('base64') - ), - }; - }); - } } diff --git a/apps/cacvote-mark/backend/src/types/db.ts b/apps/cacvote-mark/backend/src/types/db.ts index 56166fa4ef..81bbdf94f5 100644 --- a/apps/cacvote-mark/backend/src/types/db.ts +++ b/apps/cacvote-mark/backend/src/types/db.ts @@ -315,65 +315,6 @@ export interface PrintedBallot { createdAt: DateTime; } -export interface ScannedBallotRow { - id: string; - serverId: string | null; - clientId: string; - machineId: string; - electionId: string; - castVoteRecord: Buffer; - createdAt: string; -} - -export function deserializeScannedBallot(row: ScannedBallotRow): ScannedBallot { - return { - id: row.id as ClientId, - serverId: (row.serverId ?? undefined) as Optional, - clientId: row.clientId as ClientId, - machineId: row.machineId, - electionId: row.electionId as ClientId, - castVoteRecord: row.castVoteRecord, - createdAt: DateTime.fromSQL(row.createdAt), - }; -} - -export interface ScannedBallot { - /** - * Database ID for a ballot record. - */ - id: ClientId; - - /** - * Server-side ID for this registration record, if sync'ed. - */ - serverId?: ServerId; - - /** - * Client-side ID for this registration record. - */ - clientId: ClientId; - - /** - * Machine ID for the machine that created this registration record. - */ - machineId: Id; - - /** - * Database ID for the associated election record. - */ - electionId: ClientId; - - /** - * Votes cast by the voter. - */ - castVoteRecord: Buffer; - - /** - * Date and time when the voter cast their votes. - */ - createdAt: DateTime; -} - export interface PrintedBallotRow { id: string; serverId: string | null; diff --git a/apps/cacvote-mark/backend/src/types/sync.ts b/apps/cacvote-mark/backend/src/types/sync.ts index 2f012a77d8..e626f0d255 100644 --- a/apps/cacvote-mark/backend/src/types/sync.ts +++ b/apps/cacvote-mark/backend/src/types/sync.ts @@ -109,13 +109,6 @@ export interface PrintedBallotInput { castVoteRecord: Base64String; } -export interface ScannedBallotInput { - clientId: ClientId; - machineId: Id; - electionId: ClientId; - castVoteRecord: Base64String; -} - export interface PrintedBallotOutput { serverId: ServerId; clientId: ClientId; @@ -139,23 +132,6 @@ export const PrintedBallotOutputSchema: z.ZodSchema = castVoteRecordSignature: Base64StringSchema, }); -export interface ScannedBallotOutput { - serverId: ServerId; - clientId: ClientId; - machineId: Id; - electionId: ServerId; - castVoteRecord: Buffer; -} - -export const ScannedBallotOutputSchema: z.ZodSchema = - z.object({ - serverId: ServerIdSchema, - clientId: ClientIdSchema, - machineId: z.string(), - electionId: ServerIdSchema, - castVoteRecord: Base64Buffer, - }); - export interface AdminInput { machineId: string; commonAccessCardId: string; @@ -213,13 +189,11 @@ export interface RaveServerSyncInput { lastSyncedRegistrationId?: ServerId; lastSyncedElectionId?: ServerId; lastSyncedPrintedBallotId?: ServerId; - lastSyncedScannedBallotId?: ServerId; jurisdictions?: JurisdictionInput[]; registrationRequests?: RegistrationRequestInput[]; elections?: ElectionInput[]; registrations?: RegistrationInput[]; printedBallots?: PrintedBallotInput[]; - scannedBallots?: ScannedBallotInput[]; } export interface RaveServerSyncOutput { @@ -229,7 +203,6 @@ export interface RaveServerSyncOutput { registrationRequests: RegistrationRequestOutput[]; registrations: RegistrationOutput[]; printedBallots: PrintedBallotOutput[]; - scannedBallots: ScannedBallotOutput[]; } export const RaveMarkSyncOutputSchema: z.ZodSchema = @@ -240,5 +213,4 @@ export const RaveMarkSyncOutputSchema: z.ZodSchema = registrationRequests: z.array(RegistrationRequestOutputSchema), registrations: z.array(RegistrationOutputSchema), printedBallots: z.array(PrintedBallotOutputSchema), - scannedBallots: z.array(ScannedBallotOutputSchema), }); diff --git a/apps/cacvote-scan/Makefile b/apps/cacvote-scan/Makefile deleted file mode 100644 index 56864c56b2..0000000000 --- a/apps/cacvote-scan/Makefile +++ /dev/null @@ -1,40 +0,0 @@ -APP := cacvote-scan - -ALL: build - -clean: clean-frontend clean-backend - -clean-frontend: - @echo "🧹 Cleaning frontend…" - @cd frontend && dx clean - -clean-backend: - @echo "🧹 Cleaning backend…" - @cd backend && cargo clean - -build: build-frontend build-backend - -build-frontend: - @echo "🛠️ Building frontend…" - @cd frontend && dx build --release - -build-backend: - @echo "🛠️ Building backend…" - @cd backend && cargo build --release - -dist: build - @echo "📦 Packaging application…" - @rm -rf dist && mkdir dist - @cp -r frontend/dist dist/public - @cp ../../target/release/$(APP)-backend dist/$(APP) - @echo "- \e[34;4mdist/public\e[0m: frontend assets" - @echo "- \e[34;4mdist/$(APP)\e[0m: application binary" - -run: dist - @echo "🚀 Running application in production mode…" - @cd dist && ./$(APP) - -reset-db: - @echo "🗑️ Resetting database…" - @cd backend && cargo sqlx database reset --source db/migrations - @echo "✅ Database reset" \ No newline at end of file diff --git a/apps/cacvote-scan/backend/.env b/apps/cacvote-scan/backend/.env deleted file mode 100644 index f5048735e1..0000000000 --- a/apps/cacvote-scan/backend/.env +++ /dev/null @@ -1,4 +0,0 @@ -DATABASE_URL=postgres:cacvote_scan -PORT=4001 -CACVOTE_URL=http://localhost:8000/ -VX_MACHINE_ID=cacvote-scan-dev diff --git a/apps/cacvote-scan/backend/Cargo.toml b/apps/cacvote-scan/backend/Cargo.toml deleted file mode 100644 index 205c21976d..0000000000 --- a/apps/cacvote-scan/backend/Cargo.toml +++ /dev/null @@ -1,35 +0,0 @@ -[package] -name = "cacvote-scan-backend" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -async-stream = { workspace = true } -axum = { workspace = true } -ballot-encoder-rs = { workspace = true } -base64 = { workspace = true } -central-scanner = { workspace = true } -clap = { workspace = true } -color-eyre = { workspace = true } -dotenvy = { workspace = true } -env_logger = { workspace = true } -futures-core = { workspace = true } -image = { workspace = true } -rayon = { workspace = true } -reqwest = { workspace = true } -rqrr = { workspace = true } -serde = { workspace = true } -serde_json = { workspace = true } -sqlx = { workspace = true } -time = { workspace = true } -tokio = { workspace = true, features = ["rt-multi-thread"] } -tower-http = { workspace = true, features = ["trace"] } -tracing = { workspace = true } -tracing-subscriber = { workspace = true, features = ["env-filter"] } -types-rs = { workspace = true, features = ["backend"] } -uuid = { workspace = true } - -[dev-dependencies] -pretty_assertions = { workspace = true } diff --git a/apps/cacvote-scan/backend/build.rs b/apps/cacvote-scan/backend/build.rs deleted file mode 100644 index 7609593841..0000000000 --- a/apps/cacvote-scan/backend/build.rs +++ /dev/null @@ -1,5 +0,0 @@ -// generated by `sqlx migrate build-script` -fn main() { - // trigger recompilation when a new migration is added - println!("cargo:rerun-if-changed=migrations"); -} \ No newline at end of file diff --git a/apps/cacvote-scan/backend/db/migrations/20230705182146_init.sql b/apps/cacvote-scan/backend/db/migrations/20230705182146_init.sql deleted file mode 100644 index 38b84df047..0000000000 --- a/apps/cacvote-scan/backend/db/migrations/20230705182146_init.sql +++ /dev/null @@ -1,90 +0,0 @@ -create table elections ( - id uuid primary key, - -- generated on the server - server_id uuid not null, - -- generated on a client machine - client_id uuid not null, - -- ID of the machine this record was originally created on - machine_id varchar(255) not null, - election_hash varchar(255) not null, - definition bytea not null, - created_at timestamptz not null default current_timestamp, - - unique (client_id, machine_id) -); - -create table registration_requests ( - id uuid primary key, - -- generated on the server - server_id uuid not null, - -- generated on a client machine - client_id uuid not null, - -- ID of the machine this record was originally created on - machine_id varchar(255) not null, - -- CAC ID of this record's voter - common_access_card_id varchar(36) not null, - given_name varchar(255) not null, - family_name varchar(255) not null, - address_line_1 varchar(255) not null, - address_line_2 varchar(255), - city varchar(255) not null, - state varchar(16) not null, - postal_code varchar(255) not null, - state_id varchar(255) not null, - created_at timestamptz not null default current_timestamp, - - unique (client_id, machine_id) -); - -create table registrations ( - id uuid primary key, - -- generated on the server - server_id uuid not null, - -- generated on a client machine - client_id uuid not null, - -- ID of the machine this record was originally created on - machine_id varchar(255) not null, - -- CAC ID of this record's voter - common_access_card_id varchar(36) not null, - registration_request_id uuid not null references registration_requests(id) on update cascade on delete cascade, - election_id uuid not null references elections(id) on update cascade on delete cascade, - precinct_id varchar(255) not null, - ballot_style_id varchar(255) not null, - created_at timestamptz not null default current_timestamp, - - unique (client_id, machine_id) -); - -create table printed_ballots ( - id uuid primary key, - -- generated on the server - server_id uuid not null, - -- generated on a client machine - client_id uuid not null, - -- ID of the machine this record was originally created on - machine_id varchar(255) not null, - -- CAC ID of this record's voter - common_access_card_id varchar(36) not null, - common_access_card_certificate bytea not null, - registration_id uuid not null references registrations(id) on update cascade on delete cascade, - cast_vote_record bytea not null, - cast_vote_record_signature bytea not null, - created_at timestamptz not null default current_timestamp, - - unique (client_id, machine_id) -); - -create table scanned_ballots ( - id uuid primary key, - -- generated on the server, present only if the record has been synced - server_id uuid unique, - -- generated on a client machine - client_id uuid not null, - -- ID of the machine this record was originally created on - machine_id varchar(255) not null, - election_id uuid not null references elections(id) on update cascade on delete cascade, - cast_vote_record bytea not null, - created_at timestamptz not null default current_timestamp, - - unique (client_id, machine_id) -); diff --git a/apps/cacvote-scan/backend/db/migrations/20230911215714_add_batches.down.sql b/apps/cacvote-scan/backend/db/migrations/20230911215714_add_batches.down.sql deleted file mode 100644 index 072126d4f2..0000000000 --- a/apps/cacvote-scan/backend/db/migrations/20230911215714_add_batches.down.sql +++ /dev/null @@ -1 +0,0 @@ -drop table if exists batches; diff --git a/apps/cacvote-scan/backend/db/migrations/20230911215714_add_batches.sql b/apps/cacvote-scan/backend/db/migrations/20230911215714_add_batches.sql deleted file mode 100644 index 1f18df87a2..0000000000 --- a/apps/cacvote-scan/backend/db/migrations/20230911215714_add_batches.sql +++ /dev/null @@ -1,7 +0,0 @@ -create table batches ( - id uuid primary key, - scanned_ballot_ids uuid[] not null, - error_message text, - started_at timestamptz not null default current_timestamp, - ended_at timestamptz -); diff --git a/apps/cacvote-scan/backend/db/migrations/20231023204942_add_jurisdictions.sql b/apps/cacvote-scan/backend/db/migrations/20231023204942_add_jurisdictions.sql deleted file mode 100644 index 1a40a27689..0000000000 --- a/apps/cacvote-scan/backend/db/migrations/20231023204942_add_jurisdictions.sql +++ /dev/null @@ -1,23 +0,0 @@ -create table jurisdictions ( - id uuid primary key, - name varchar(255) not null, - created_at timestamptz not null default current_timestamp -); - -alter table elections -add column jurisdiction_id uuid not null -references jurisdictions(id) -on update cascade -on delete cascade; - -alter table registration_requests -add column jurisdiction_id uuid not null -references jurisdictions(id) -on update cascade -on delete cascade; - -alter table registrations -add column jurisdiction_id uuid not null -references jurisdictions(id) -on update cascade -on delete cascade; diff --git a/apps/cacvote-scan/backend/db/migrations/20231109210853_add_jurisdiction_code.down.sql b/apps/cacvote-scan/backend/db/migrations/20231109210853_add_jurisdiction_code.down.sql deleted file mode 100644 index 30eee4c8ca..0000000000 --- a/apps/cacvote-scan/backend/db/migrations/20231109210853_add_jurisdiction_code.down.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE jurisdictions DROP CONSTRAINT IF EXISTS jurisdictions_code_unique; -ALTER TABLE jurisdictions DROP COLUMN IF EXISTS code; diff --git a/apps/cacvote-scan/backend/db/migrations/20231109210853_add_jurisdiction_code.sql b/apps/cacvote-scan/backend/db/migrations/20231109210853_add_jurisdiction_code.sql deleted file mode 100644 index 3c37d8f99b..0000000000 --- a/apps/cacvote-scan/backend/db/migrations/20231109210853_add_jurisdiction_code.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE jurisdictions ADD COLUMN code VARCHAR(255) NOT NULL; -ALTER TABLE jurisdictions ADD CONSTRAINT jurisdictions_code_unique UNIQUE (code); diff --git a/apps/cacvote-scan/backend/db/migrations/20231115222328_remove_registration_address.down.sql b/apps/cacvote-scan/backend/db/migrations/20231115222328_remove_registration_address.down.sql deleted file mode 100644 index fe508997e0..0000000000 --- a/apps/cacvote-scan/backend/db/migrations/20231115222328_remove_registration_address.down.sql +++ /dev/null @@ -1,6 +0,0 @@ -ALTER TABLE registration_requests ADD COLUMN address_line_1 varchar(255) not null; -ALTER TABLE registration_requests ADD COLUMN address_line_2 varchar(255); -ALTER TABLE registration_requests ADD COLUMN city varchar(255) not null; -ALTER TABLE registration_requests ADD COLUMN state varchar(16) not null; -ALTER TABLE registration_requests ADD COLUMN postal_code varchar(255) not null; -ALTER TABLE registration_requests ADD COLUMN state_id varchar(255) not null; \ No newline at end of file diff --git a/apps/cacvote-scan/backend/db/migrations/20231115222328_remove_registration_address.sql b/apps/cacvote-scan/backend/db/migrations/20231115222328_remove_registration_address.sql deleted file mode 100644 index eb837f1017..0000000000 --- a/apps/cacvote-scan/backend/db/migrations/20231115222328_remove_registration_address.sql +++ /dev/null @@ -1,6 +0,0 @@ -ALTER TABLE registration_requests DROP COLUMN address_line_1; -ALTER TABLE registration_requests DROP COLUMN address_line_2; -ALTER TABLE registration_requests DROP COLUMN city; -ALTER TABLE registration_requests DROP COLUMN state; -ALTER TABLE registration_requests DROP COLUMN postal_code; -ALTER TABLE registration_requests DROP COLUMN state_id; \ No newline at end of file diff --git a/apps/cacvote-scan/backend/db/migrations/20240226180624_add-return-address-to-elections.down.sql b/apps/cacvote-scan/backend/db/migrations/20240226180624_add-return-address-to-elections.down.sql deleted file mode 100644 index 79cc00ac83..0000000000 --- a/apps/cacvote-scan/backend/db/migrations/20240226180624_add-return-address-to-elections.down.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE elections DROP COLUMN return_address; \ No newline at end of file diff --git a/apps/cacvote-scan/backend/db/migrations/20240226180624_add-return-address-to-elections.sql b/apps/cacvote-scan/backend/db/migrations/20240226180624_add-return-address-to-elections.sql deleted file mode 100644 index 8490d3de87..0000000000 --- a/apps/cacvote-scan/backend/db/migrations/20240226180624_add-return-address-to-elections.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE elections ADD COLUMN return_address TEXT NOT NULL; diff --git a/apps/cacvote-scan/backend/script/build b/apps/cacvote-scan/backend/script/build deleted file mode 100755 index 31f6fdd9cb..0000000000 --- a/apps/cacvote-scan/backend/script/build +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" -cd "${DIR}/.." - -SQLX_OFFLINE=true cargo build \ No newline at end of file diff --git a/apps/cacvote-scan/backend/script/ci b/apps/cacvote-scan/backend/script/ci deleted file mode 100755 index 49d36b1895..0000000000 --- a/apps/cacvote-scan/backend/script/ci +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" -cd "${DIR}/.." - -if [ $(id -u) -eq 0 ] && [ "${CI:-}" == true ]; then - service postgresql start - sudo -u postgres createuser --superuser $(whoami) || true - sudo -u postgres createdb $(cat .env | grep DATABASE_URL | cut -d '=' -f 2 - | cut -d ':' -f 2 -) || true -fi - -SQLX_OFFLINE=true cargo test \ No newline at end of file diff --git a/apps/cacvote-scan/backend/script/lint b/apps/cacvote-scan/backend/script/lint deleted file mode 100755 index ee15136feb..0000000000 --- a/apps/cacvote-scan/backend/script/lint +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" -cd "${DIR}/.." - -SQLX_OFFLINE=true cargo clippy -- -D warnings \ No newline at end of file diff --git a/apps/cacvote-scan/backend/src/app.rs b/apps/cacvote-scan/backend/src/app.rs deleted file mode 100644 index 7855879ed5..0000000000 --- a/apps/cacvote-scan/backend/src/app.rs +++ /dev/null @@ -1,194 +0,0 @@ -//! Application definition, including all HTTP route handlers. -//! -//! Route handlers are bundled via [`setup`] into an [`axum::Router`], which can then be run -//! using [`run`] at the configured port (see [`config`][`super::config`]). - -use std::convert::Infallible; -use std::net::{IpAddr, Ipv4Addr, SocketAddr}; -use std::path::{Path, PathBuf}; -use std::sync::mpsc::channel; -use std::time::Duration; - -use async_stream::try_stream; -use axum::{ - extract::{DefaultBodyLimit, State}, - http::StatusCode, - response::{ - sse::{Event, KeepAlive, Sse}, - IntoResponse, - }, - routing::{get, post}, - Json, Router, -}; -use central_scanner::scan; -use futures_core::Stream; -use serde::Serialize; -use serde_json::json; -use sqlx::PgPool; -use time::OffsetDateTime; -use tokio::time::sleep; -use tower_http::services::{ServeDir, ServeFile}; -use tower_http::trace::TraceLayer; -use tracing::Level; -use types_rs::cacvote::ClientId; -use types_rs::election::PartialElectionHash; -use types_rs::scan::ScannedBallotStats; - -use crate::config::{Config, MAX_REQUEST_SIZE}; -use crate::db::{self, ScannedBallot}; -use crate::sheets::decode_page_from_image; - -type AppState = (Config, PgPool); - -/// Prepares the application with all the routes. Run the application with -/// `app::run(…)` once you have it. -pub(crate) async fn setup(pool: PgPool, config: Config) -> color_eyre::Result { - let _entered = tracing::span!(Level::DEBUG, "Setting up application").entered(); - - let dist_path = Path::new("../frontend/dist"); - let _ = std::fs::create_dir_all(dist_path); - - Ok(Router::new() - .fallback_service( - ServeDir::new(dist_path) - .append_index_html_on_directories(true) - .fallback(ServeFile::new(dist_path.join("index.html"))), - ) - .route("/api/status", get(get_status)) - .route("/api/status-stream", get(get_status_stream)) - .route("/api/scan", post(do_scan)) - .layer(DefaultBodyLimit::max(MAX_REQUEST_SIZE)) - .layer(TraceLayer::new_for_http()) - .with_state((config, pool))) -} - -/// Runs an application built by `app::setup(…)`. -pub(crate) async fn run(app: Router, config: &Config) -> color_eyre::Result<()> { - let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), config.port); - tracing::info!("Server listening at http://{addr}/"); - axum::Server::bind(&addr) - .serve(app.into_make_service()) - .await?; - Ok(()) -} - -#[derive(Debug, Serialize)] -pub(crate) struct ScannedCard { - cvr_data: Vec, - election_hash: PartialElectionHash, -} - -pub(crate) async fn get_status() -> impl IntoResponse { - StatusCode::OK -} - -pub(crate) async fn get_status_stream( - State((_, pool)): State, -) -> Sse>> { - let mut last_scanned_ballot_stats = ScannedBallotStats::default(); - - Sse::new(try_stream! { - loop { - let mut connection = pool.acquire().await.unwrap(); - match db::get_scanned_ballot_stats(&mut connection).await { - Ok(scanned_ballot_stats) => { - if scanned_ballot_stats != last_scanned_ballot_stats { - last_scanned_ballot_stats = scanned_ballot_stats.clone(); - yield Event::default().json_data(&json!({ - "status": "ok", - "stats": scanned_ballot_stats, - })).unwrap(); - } - } - Err(e) => { - yield Event::default().json_data(&json!({ - "status": "error", - "error": format!("{}", e), - })).unwrap(); - } - } - - sleep(Duration::from_millis(100)).await; - } - }) - .keep_alive(KeepAlive::default()) -} - -pub(crate) async fn do_scan(State((config, pool)): State) -> Json> { - let mut connection = pool.acquire().await.unwrap(); - let (tx, rx) = channel(); - - let batch_id = db::start_batch(&mut connection).await.unwrap(); - - let handle = std::thread::spawn(move || { - let session = scan(PathBuf::from("/tmp")).unwrap(); - for (side_a_path, side_b_path) in session { - tx.send((side_a_path, side_b_path)).expect("send() failed"); - } - }); - - let elections = db::get_elections(&mut connection, None).await.unwrap(); - - let mut cards = vec![]; - for (side_a_path, side_b_path) in rx { - let (side_a_result, side_b_result) = rayon::join( - move || decode_page_from_image(image::open(side_a_path).unwrap().to_luma8()), - move || decode_page_from_image(image::open(side_b_path).unwrap().to_luma8()), - ); - - match (side_a_result, side_b_result) { - (Err(side_a_err), Err(side_b_err)) => { - tracing::error!("Both sides failed: {side_a_err:?}, {side_b_err:?}"); - break; - } - (Ok(cvr_data), _) | (_, Ok(cvr_data)) => { - let (_, election_hash) = ballot_encoder_rs::decode_header(&cvr_data).unwrap(); - cards.push(ScannedCard { - election_hash, - cvr_data, - }) - } - } - } - - for card in cards.iter() { - if let Some(election) = elections.iter().find(|election| { - card.election_hash - .matches_election_hash(&election.election_hash) - }) { - let decoded_cvr = - ballot_encoder_rs::decode(&election.definition.election, card.cvr_data.as_slice()) - .unwrap(); - let cast_vote_record = serde_json::to_string(&decoded_cvr.cvr).unwrap(); - - let scanned_ballot_id = ClientId::new(); - let scanned_ballot = ScannedBallot { - id: scanned_ballot_id, - server_id: None, - client_id: scanned_ballot_id, - machine_id: config.machine_id.clone(), - election_id: election.id, - cast_vote_record: cast_vote_record.into_bytes(), - created_at: OffsetDateTime::now_utc(), - }; - - match db::add_scanned_ballot(&mut connection, scanned_ballot, batch_id).await { - Ok(_) => {} - Err(e) => { - tracing::error!("Failed to insert scanned ballot: {e}"); - } - } - } else { - tracing::error!( - "No election found for card with hash {}", - card.election_hash - ); - } - } - - handle.join().unwrap(); - - db::end_batch(&mut connection, batch_id).await.unwrap(); - - Json(cards) -} diff --git a/apps/cacvote-scan/backend/src/config.rs b/apps/cacvote-scan/backend/src/config.rs deleted file mode 100644 index 8b61b32621..0000000000 --- a/apps/cacvote-scan/backend/src/config.rs +++ /dev/null @@ -1,38 +0,0 @@ -//! Application configuration. - -use std::time::Duration; - -use clap::Parser; - -const TEN_MB: usize = 10 * 1024 * 1024; - -pub(crate) const MAX_REQUEST_SIZE: usize = TEN_MB; -pub(crate) const SYNC_INTERVAL: Duration = Duration::from_secs(5); - -#[derive(Debug, Clone, Parser)] -#[command(author, version, about)] -pub(crate) struct Config { - /// URL of the CACVote server, e.g. `https://cacvote.example.com/`. - #[arg(long, env = "CACVOTE_URL")] - pub(crate) cacvote_url: reqwest::Url, - - /// URL of the PostgreSQL database, e.g. `postgres://user:pass@host:port/dbname`. - #[arg(long, env = "DATABASE_URL")] - pub(crate) database_url: String, - - /// ID of this machine, e.g. `machine-1`. - #[arg(long, env = "VX_MACHINE_ID")] - pub(crate) machine_id: String, - - /// Port to listen on. - #[arg(long, env = "PORT")] - pub(crate) port: u16, - - /// Directory to serve static files from. - #[arg(long, env = "PUBLIC_DIR")] - pub(crate) public_dir: Option, - - /// Log level. - #[arg(long, env = "LOG_LEVEL", default_value = "info")] - pub(crate) log_level: tracing::Level, -} diff --git a/apps/cacvote-scan/backend/src/db.rs b/apps/cacvote-scan/backend/src/db.rs deleted file mode 100644 index 32eb85862d..0000000000 --- a/apps/cacvote-scan/backend/src/db.rs +++ /dev/null @@ -1,1055 +0,0 @@ -//! Database access for the application. -//! -//! All direct use of [SQLx][`sqlx`] queries should be in this module. When -//! modifying this file, be sure to run `cargo sqlx prepare --workspace` in the -//! workspace root to regenerate the query metadata for offline builds. -//! -//! To enable `cargo sqlx prepare --workspace`, install it via `cargo install -//! --locked sqlx-cli`. - -use std::str::FromStr; -use std::time::Duration; - -use serde::{Deserialize, Serialize}; -use sqlx::postgres::PgPoolOptions; -use sqlx::PgPool; -use tracing::Level; -use types_rs::cacvote::{client, ClientId, ServerId}; -use types_rs::election::{ElectionDefinition, ElectionHash}; -use types_rs::scan::{BatchStats, ScannedBallotStats}; - -use crate::config::Config; - -/// Sets up the database pool and runs any pending migrations, returning the -/// pool to be used by the app. -pub(crate) async fn setup(config: &Config) -> color_eyre::Result { - let _entered = tracing::span!(Level::DEBUG, "Setting up database").entered(); - let pool = PgPoolOptions::new() - .max_connections(5) - .acquire_timeout(Duration::from_secs(3)) - .connect(&config.database_url) - .await?; - sqlx::migrate!("db/migrations").run(&pool).await?; - Ok(pool) -} - -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub(crate) struct Election { - pub(crate) id: ClientId, - pub(crate) server_id: ServerId, - pub(crate) client_id: ClientId, - pub(crate) machine_id: String, - pub(crate) definition: ElectionDefinition, - pub(crate) election_hash: ElectionHash, - pub(crate) created_at: sqlx::types::time::OffsetDateTime, -} - -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub(crate) struct ScannedBallot { - pub(crate) id: ClientId, - pub(crate) server_id: Option, - pub(crate) client_id: ClientId, - pub(crate) machine_id: String, - pub(crate) election_id: ClientId, - pub(crate) cast_vote_record: Vec, - pub(crate) created_at: sqlx::types::time::OffsetDateTime, -} - -pub(crate) async fn get_last_synced_election_id( - executor: &mut sqlx::PgConnection, -) -> color_eyre::Result> { - Ok(sqlx::query!( - r#" - SELECT server_id as "server_id!: ServerId" - FROM elections - WHERE server_id IS NOT NULL - ORDER BY created_at DESC - LIMIT 1 - "# - ) - .fetch_optional(&mut *executor) - .await? - .map(|r| r.server_id)) -} - -pub(crate) async fn get_last_synced_registration_request_id( - executor: &mut sqlx::PgConnection, -) -> color_eyre::Result> { - Ok(sqlx::query!( - r#" - SELECT server_id as "server_id!: ServerId" - FROM registration_requests - WHERE server_id IS NOT NULL - ORDER BY created_at DESC - LIMIT 1 - "# - ) - .fetch_optional(&mut *executor) - .await? - .map(|r| r.server_id)) -} - -pub(crate) async fn get_last_synced_registration_id( - executor: &mut sqlx::PgConnection, -) -> color_eyre::Result> { - Ok(sqlx::query!( - r#" - SELECT server_id as "server_id!: ServerId" - FROM registrations - WHERE server_id IS NOT NULL - ORDER BY created_at DESC - LIMIT 1 - "# - ) - .fetch_optional(&mut *executor) - .await? - .map(|r| r.server_id)) -} - -pub(crate) async fn get_last_synced_printed_ballot_id( - executor: &mut sqlx::PgConnection, -) -> color_eyre::Result> { - Ok(sqlx::query!( - r#" - SELECT server_id as "server_id!: ServerId" - FROM printed_ballots - WHERE server_id IS NOT NULL - ORDER BY created_at DESC - LIMIT 1 - "# - ) - .fetch_optional(&mut *executor) - .await? - .map(|r| r.server_id)) -} - -#[allow(dead_code)] -pub(crate) async fn get_elections( - executor: &mut sqlx::PgConnection, - since_election_id: Option, -) -> Result, color_eyre::eyre::Error> { - let since_election = match since_election_id { - Some(id) => sqlx::query!( - r#" - SELECT created_at - FROM elections - WHERE id = $1 - "#, - id.as_uuid(), - ) - .fetch_optional(&mut *executor) - .await - .ok(), - None => None, - } - .flatten(); - - struct ElectionRecord { - id: ClientId, - server_id: ServerId, - client_id: ClientId, - machine_id: String, - definition: String, - election_hash: String, - created_at: sqlx::types::time::OffsetDateTime, - } - - let records = match since_election { - Some(election) => { - sqlx::query_as!( - ElectionRecord, - r#" - SELECT - id as "id: ClientId", - server_id as "server_id: ServerId", - client_id as "client_id: ClientId", - machine_id, - definition as "definition: String", - election_hash, - created_at - FROM elections - WHERE created_at > $1 - ORDER BY created_at DESC - "#, - election.created_at - ) - .fetch_all(&mut *executor) - .await? - } - None => { - sqlx::query_as!( - ElectionRecord, - r#" - SELECT - id as "id: ClientId", - server_id as "server_id: ServerId", - client_id as "client_id: ClientId", - machine_id, - definition as "definition: String", - election_hash, - created_at - FROM elections - ORDER BY created_at DESC - "#, - ) - .fetch_all(&mut *executor) - .await? - } - }; - - records - .into_iter() - .map(|record| { - Ok::(Election { - id: record.id, - server_id: record.server_id, - client_id: record.client_id, - machine_id: record.machine_id, - definition: record.definition.parse()?, - election_hash: ElectionHash::from_str(&record.election_hash)?, - created_at: record.created_at, - }) - }) - .collect::, _>>() -} - -pub(crate) async fn add_or_update_jurisdiction_from_cacvote_server( - executor: &mut sqlx::PgConnection, - record: client::output::Jurisdiction, -) -> color_eyre::Result { - sqlx::query!( - r#" - INSERT INTO jurisdictions ( - id, - code, - name, - created_at - ) - VALUES ($1, $2, $3, $4) - ON CONFLICT (id) - DO UPDATE SET - code = $2, - name = $3, - created_at = $4 - "#, - record.id.as_uuid(), - record.code, - record.name, - record.created_at - ) - .execute(executor) - .await?; - - Ok(record.id) -} - -pub(crate) async fn add_election_from_cacvote_server( - executor: &mut sqlx::PgConnection, - record: client::output::Election, -) -> color_eyre::Result { - sqlx::query!( - r#" - INSERT INTO elections ( - id, - server_id, - client_id, - machine_id, - jurisdiction_id, - election_hash, - definition, - return_address - ) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8) - ON CONFLICT (machine_id, client_id) - DO UPDATE SET - server_id = $2, - jurisdiction_id = $5, - election_hash = $6, - definition = $7, - return_address = $8 - "#, - ClientId::new().as_uuid(), - record.server_id.as_uuid(), - record.client_id.as_uuid(), - record.machine_id, - record.jurisdiction_id.as_uuid(), - record.definition.election_hash.as_str(), - record.definition.election_data, - record.return_address, - ) - .execute(&mut *executor) - .await?; - - let id = sqlx::query!( - r#" - SELECT id as "id: ClientId" - FROM elections - WHERE machine_id = $1 AND client_id = $2 - "#, - record.machine_id, - record.client_id.as_uuid(), - ) - .fetch_one(&mut *executor) - .await? - .id; - - Ok(id) -} - -pub(crate) async fn add_or_update_registration_from_cacvote_server( - executor: &mut sqlx::PgConnection, - registration: client::output::Registration, -) -> color_eyre::Result { - let registration_request_id = sqlx::query!( - r#" - SELECT id as "id: ClientId" - FROM registration_requests - WHERE server_id = $1 - "#, - registration.registration_request_id.as_uuid(), - ) - .fetch_one(&mut *executor) - .await? - .id; - - let election_id = sqlx::query!( - r#" - SELECT id as "id: ClientId" - FROM elections - WHERE server_id = $1 - "#, - registration.election_id.as_uuid(), - ) - .fetch_one(&mut *executor) - .await? - .id; - - sqlx::query!( - r#" - INSERT INTO registrations ( - id, - server_id, - client_id, - machine_id, - jurisdiction_id, - common_access_card_id, - registration_request_id, - election_id, - precinct_id, - ballot_style_id, - created_at - ) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) - ON CONFLICT (machine_id, client_id) - DO UPDATE SET - server_id = $2, - jurisdiction_id = $5, - common_access_card_id = $6, - registration_request_id = $7, - election_id = $8, - precinct_id = $9, - ballot_style_id = $10, - created_at = $11 - "#, - ClientId::new().as_uuid(), - registration.server_id.as_uuid(), - registration.client_id.as_uuid(), - registration.machine_id, - registration.jurisdiction_id.as_uuid(), - registration.common_access_card_id, - registration_request_id.as_uuid(), - election_id.as_uuid(), - registration.precinct_id, - registration.ballot_style_id, - registration.created_at - ) - .execute(&mut *executor) - .await?; - - let id = sqlx::query!( - r#" - SELECT id as "id: ClientId" - FROM registrations - WHERE machine_id = $1 AND client_id = $2 - "#, - registration.machine_id, - registration.client_id.as_uuid(), - ) - .fetch_one(&mut *executor) - .await? - .id; - - Ok(id) -} - -pub(crate) async fn add_or_update_printed_ballot_from_cacvote_server( - executor: &mut sqlx::PgConnection, - printed_ballot: client::output::PrintedBallot, -) -> color_eyre::Result { - let registration_client_id = sqlx::query!( - r#" - SELECT id as "id: ClientId" - FROM registrations - WHERE server_id = $1 - "#, - printed_ballot.registration_id.as_uuid(), - ) - .fetch_one(&mut *executor) - .await? - .id; - - sqlx::query!( - r#" - INSERT INTO printed_ballots ( - id, - server_id, - client_id, - machine_id, - common_access_card_id, - common_access_card_certificate, - registration_id, - cast_vote_record, - cast_vote_record_signature, - created_at - ) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) - ON CONFLICT (client_id, machine_id) - DO UPDATE SET - server_id = $2, - cast_vote_record = $8, - cast_vote_record_signature = $9, - created_at = $10 - "#, - ClientId::new().as_uuid(), - printed_ballot.server_id.as_uuid(), - printed_ballot.client_id.as_uuid(), - printed_ballot.machine_id, - printed_ballot.common_access_card_id, - printed_ballot.common_access_card_certificate, - registration_client_id.as_uuid(), - printed_ballot.cast_vote_record, - printed_ballot.cast_vote_record_signature, - printed_ballot.created_at - ) - .execute(&mut *executor) - .await?; - - let id = sqlx::query!( - r#" - SELECT id as "id: ClientId" - FROM printed_ballots - WHERE machine_id = $1 AND client_id = $2 - "#, - printed_ballot.machine_id, - printed_ballot.client_id.as_uuid(), - ) - .fetch_one(&mut *executor) - .await? - .id; - - Ok(id) -} - -pub(crate) async fn add_or_update_scanned_ballot_from_cacvote_server( - executor: &mut sqlx::PgConnection, - scanned_ballot: client::output::ScannedBallot, -) -> color_eyre::Result { - let election_id = sqlx::query!( - r#" - SELECT id as "id: ClientId" - FROM elections - WHERE server_id = $1 - "#, - scanned_ballot.election_id.as_uuid(), - ) - .fetch_one(&mut *executor) - .await? - .id; - - sqlx::query!( - r#" - INSERT INTO scanned_ballots ( - id, - server_id, - client_id, - machine_id, - election_id, - cast_vote_record, - created_at - ) - VALUES ($1, $2, $3, $4, $5, $6, $7) - ON CONFLICT (client_id, machine_id) - DO UPDATE SET - server_id = $2, - election_id = $5, - cast_vote_record = $6, - created_at = $7 - "#, - ClientId::new().as_uuid(), - scanned_ballot.server_id.as_uuid(), - scanned_ballot.client_id.as_uuid(), - scanned_ballot.machine_id, - election_id.as_uuid(), - scanned_ballot.cast_vote_record, - scanned_ballot.created_at - ) - .execute(&mut *executor) - .await?; - - let id = sqlx::query!( - r#" - SELECT id as "id: ClientId" - FROM scanned_ballots - WHERE server_id = $1 - "#, - scanned_ballot.server_id.as_uuid(), - ) - .fetch_one(&mut *executor) - .await? - .id; - - Ok(id) -} - -pub(crate) async fn add_or_update_registration_request_from_cacvote_server( - executor: &mut sqlx::PgConnection, - registration_request: client::output::RegistrationRequest, -) -> color_eyre::Result { - sqlx::query!( - r#" - INSERT INTO registration_requests ( - id, - server_id, - client_id, - machine_id, - jurisdiction_id, - common_access_card_id, - given_name, - family_name, - created_at - ) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) - ON CONFLICT (client_id, machine_id) - DO UPDATE SET - server_id = $2, - jurisdiction_id = $5, - common_access_card_id = $6, - given_name = $7, - family_name = $8, - created_at = $9 - "#, - ClientId::new().as_uuid(), - registration_request.server_id.as_uuid(), - registration_request.client_id.as_uuid(), - registration_request.machine_id, - registration_request.jurisdiction_id.as_uuid(), - registration_request.common_access_card_id, - registration_request.given_name, - registration_request.family_name, - registration_request.created_at - ) - .execute(&mut *executor) - .await?; - - let id = sqlx::query!( - r#" - SELECT id as "id: ClientId" - FROM registration_requests - WHERE server_id = $1 - "#, - registration_request.server_id.as_uuid(), - ) - .fetch_one(executor) - .await? - .id; - - Ok(id) -} - -pub(crate) async fn get_registration_requests_to_sync_to_cacvote_server( - executor: &mut sqlx::PgConnection, -) -> color_eyre::Result> { - let records = sqlx::query!( - r#" - SELECT - client_id as "client_id: ClientId", - machine_id, - jurisdiction_id as "jurisdiction_id: ServerId", - common_access_card_id, - given_name, - family_name - FROM registration_requests - WHERE server_id IS NULL - ORDER BY created_at ASC - "# - ) - .fetch_all(&mut *executor) - .await?; - - Ok(records - .into_iter() - .map(|r| client::input::RegistrationRequest { - client_id: r.client_id, - machine_id: r.machine_id, - jurisdiction_id: r.jurisdiction_id, - common_access_card_id: r.common_access_card_id, - given_name: r.given_name, - family_name: r.family_name, - }) - .collect()) -} - -pub(crate) async fn get_elections_to_sync_to_cacvote_server( - executor: &mut sqlx::PgConnection, -) -> color_eyre::Result> { - let records = sqlx::query!( - r#" - SELECT - client_id as "client_id: ClientId", - machine_id, - jurisdiction_id as "jurisdiction_id: ServerId", - definition as "definition: String", - return_address - FROM elections - WHERE server_id IS NULL - ORDER BY created_at ASC - "# - ) - .fetch_all(&mut *executor) - .await?; - - records - .into_iter() - .map(|e| { - Ok(client::input::Election { - client_id: e.client_id, - machine_id: e.machine_id, - jurisdiction_id: e.jurisdiction_id, - definition: e.definition.parse()?, - return_address: e.return_address, - }) - }) - .collect() -} - -pub(crate) async fn get_registrations_to_sync_to_cacvote_server( - executor: &mut sqlx::PgConnection, -) -> color_eyre::Result> { - let records = sqlx::query!( - r#" - SELECT - client_id as "client_id: ClientId", - machine_id, - jurisdiction_id as "jurisdiction_id: ServerId", - registration_request_id as "registration_request_id: ClientId", - election_id as "election_id: ClientId", - common_access_card_id, - precinct_id, - ballot_style_id - FROM registrations - WHERE server_id IS NULL - ORDER BY created_at ASC - "# - ) - .fetch_all(&mut *executor) - .await?; - - Ok(records - .into_iter() - .map(|r| client::input::Registration { - client_id: r.client_id, - machine_id: r.machine_id, - jurisdiction_id: r.jurisdiction_id, - election_id: r.election_id, - registration_request_id: r.registration_request_id, - common_access_card_id: r.common_access_card_id, - precinct_id: r.precinct_id, - ballot_style_id: r.ballot_style_id, - }) - .collect()) -} - -pub(crate) async fn get_printed_ballots_to_sync_to_cacvote_server( - executor: &mut sqlx::PgConnection, -) -> color_eyre::Result> { - let records = sqlx::query!( - r#" - SELECT - client_id as "client_id: ClientId", - machine_id, - common_access_card_id, - common_access_card_certificate, - registration_id as "registration_id: ClientId", - cast_vote_record, - cast_vote_record_signature - FROM printed_ballots - WHERE server_id IS NULL - ORDER BY created_at ASC - "# - ) - .fetch_all(&mut *executor) - .await?; - - records - .into_iter() - .map(|r| { - Ok(client::input::PrintedBallot { - client_id: r.client_id, - machine_id: r.machine_id, - common_access_card_id: r.common_access_card_id, - common_access_card_certificate: r.common_access_card_certificate, - registration_id: r.registration_id, - cast_vote_record: r.cast_vote_record, - cast_vote_record_signature: r.cast_vote_record_signature, - }) - }) - .collect() -} - -pub(crate) async fn get_scanned_ballots_to_sync_to_cacvote_server( - executor: &mut sqlx::PgConnection, -) -> color_eyre::Result> { - let records = sqlx::query!( - r#" - SELECT - client_id as "client_id: ClientId", - machine_id, - (SELECT client_id FROM elections WHERE id = election_id) as "election_id!: ClientId", - cast_vote_record, - created_at - FROM scanned_ballots - WHERE server_id IS NULL - ORDER BY created_at DESC - "# - ) - .fetch_all(&mut *executor) - .await?; - - Ok(records - .into_iter() - .map(|r| client::input::ScannedBallot { - client_id: r.client_id, - machine_id: r.machine_id, - election_id: r.election_id, - cast_vote_record: r.cast_vote_record, - }) - .collect::>()) -} - -pub(crate) async fn start_batch( - executor: &mut sqlx::PgConnection, -) -> Result { - let batch_id = ClientId::new(); - - sqlx::query!( - r#" - INSERT INTO batches (id, scanned_ballot_ids) - VALUES ($1, $2) - "#, - batch_id.as_uuid(), - &vec![], - ) - .execute(executor) - .await?; - - Ok(batch_id) -} - -pub(crate) async fn end_batch( - executor: &mut sqlx::PgConnection, - batch_id: ClientId, -) -> Result<(), sqlx::Error> { - sqlx::query!( - r#" - UPDATE batches - SET ended_at = NOW() - WHERE id = $1 - "#, - batch_id.as_uuid(), - ) - .execute(executor) - .await?; - - Ok(()) -} - -pub(crate) async fn add_scanned_ballot( - executor: &mut sqlx::PgConnection, - scanned_ballot: ScannedBallot, - batch_id: ClientId, -) -> Result<(), sqlx::Error> { - let ScannedBallot { - id, - server_id, - client_id, - machine_id, - election_id, - cast_vote_record, - created_at, - } = scanned_ballot; - sqlx::query!( - r#" - INSERT INTO scanned_ballots ( - id, - server_id, - client_id, - machine_id, - election_id, - cast_vote_record, - created_at - ) - VALUES ($1, $2, $3, $4, $5, $6, $7) - "#, - id.as_uuid(), - server_id.map(|id| id.as_uuid()), - client_id.as_uuid(), - machine_id, - election_id.as_uuid(), - cast_vote_record, - created_at - ) - .execute(&mut *executor) - .await?; - - sqlx::query!( - r#" - UPDATE batches - SET scanned_ballot_ids = ARRAY_APPEND(scanned_ballot_ids, $1) - WHERE id = $2 - "#, - id.as_uuid(), - batch_id.as_uuid(), - ) - .execute(executor) - .await?; - - Ok(()) -} - -pub(crate) async fn get_last_synced_scanned_ballot_id( - executor: &mut sqlx::PgConnection, -) -> color_eyre::Result> { - Ok(sqlx::query!( - r#" - SELECT server_id as "server_id!: ServerId" - FROM scanned_ballots - WHERE server_id IS NOT NULL - ORDER BY created_at DESC - LIMIT 1 - "# - ) - .fetch_optional(&mut *executor) - .await? - .map(|r| r.server_id)) -} - -pub(crate) async fn get_scanned_ballot_stats( - executor: &mut sqlx::PgConnection, -) -> color_eyre::Result { - let batches = sqlx::query_as!( - BatchStats, - r#" - SELECT - id as "id: ClientId", - COALESCE(ARRAY_LENGTH(scanned_ballot_ids, 1), 0) as "ballot_count!: _", - CAST( - ( - SELECT COUNT(DISTINCT election_id) - FROM scanned_ballots - WHERE id IN (SELECT UNNEST(scanned_ballot_ids)) - ) AS int4 - ) AS "election_count!: _", - CAST( - ( - SELECT COUNT(*) - FROM scanned_ballots - WHERE id IN (SELECT UNNEST(scanned_ballot_ids)) - AND server_id IS NOT NULL - ) AS int4 - ) AS "synced_count!: _", - started_at, - ended_at - FROM batches - ORDER BY started_at DESC - "# - ) - .fetch_all(executor) - .await?; - - Ok(ScannedBallotStats { batches }) -} - -#[cfg(test)] -mod test { - use super::*; - use pretty_assertions::assert_eq; - use time::OffsetDateTime; - use types_rs::cdf::cvr::Cvr; - - fn load_famous_names_election() -> ElectionDefinition { - let election_json = include_str!("../tests/fixtures/electionFamousNames2021.json"); - election_json.parse().unwrap() - } - - fn build_cacvote_server_jurisdiction() -> client::output::Jurisdiction { - client::output::Jurisdiction { - id: ServerId::new(), - code: "st.test-jurisdiction".to_owned(), - name: "Test Jurisdiction".to_owned(), - created_at: OffsetDateTime::now_utc(), - } - } - - fn build_cacvote_server_registration_request( - jurisdiction_id: ServerId, - ) -> client::output::RegistrationRequest { - client::output::RegistrationRequest { - server_id: ServerId::new(), - client_id: ClientId::new(), - machine_id: "mark-terminal-001".to_owned(), - jurisdiction_id, - common_access_card_id: "0000000000".to_owned(), - given_name: "John".to_owned(), - family_name: "Doe".to_owned(), - created_at: OffsetDateTime::now_utc(), - } - } - - fn build_cacvote_server_election( - jurisdiction_id: ServerId, - election_definition: ElectionDefinition, - ) -> client::output::Election { - client::output::Election { - server_id: ServerId::new(), - client_id: ClientId::new(), - jurisdiction_id, - return_address: "123 Main St, Anytown, USA".to_owned(), - machine_id: "mark-terminal-001".to_owned(), - election_hash: election_definition.election_hash.clone(), - definition: election_definition, - created_at: OffsetDateTime::now_utc(), - } - } - - fn build_cacvote_server_registration( - registration_request: &client::output::RegistrationRequest, - election: &client::output::Election, - election_definition: &ElectionDefinition, - ) -> client::output::Registration { - let ballot_style = &election_definition.election.ballot_styles[0]; - - client::output::Registration { - server_id: registration_request.server_id, - client_id: registration_request.client_id, - machine_id: registration_request.machine_id.clone(), - jurisdiction_id: election.jurisdiction_id, - election_id: election.server_id, - registration_request_id: registration_request.server_id, - common_access_card_id: registration_request.common_access_card_id.clone(), - precinct_id: ballot_style.precincts[0].to_string(), - ballot_style_id: ballot_style.id.to_string(), - created_at: OffsetDateTime::now_utc(), - } - } - - fn build_cacvote_server_printed_ballot( - registration: &client::output::Registration, - cast_vote_record: Cvr, - ) -> client::output::PrintedBallot { - client::output::PrintedBallot { - server_id: registration.server_id, - client_id: registration.client_id, - machine_id: registration.machine_id.clone(), - common_access_card_id: registration.common_access_card_id.clone(), - common_access_card_certificate: vec![], - registration_id: registration.registration_request_id, - cast_vote_record: serde_json::to_vec(&cast_vote_record).unwrap(), - cast_vote_record_signature: vec![], - created_at: OffsetDateTime::now_utc(), - } - } - - fn build_cacvote_server_scanned_ballot( - election: &client::output::Election, - cast_vote_record: Cvr, - ) -> client::output::ScannedBallot { - client::output::ScannedBallot { - server_id: election.server_id, - client_id: election.client_id, - machine_id: election.machine_id.clone(), - cast_vote_record: serde_json::to_vec(&cast_vote_record).unwrap(), - election_id: election.server_id, - created_at: OffsetDateTime::now_utc(), - } - } - - #[sqlx::test(migrations = "db/migrations")] - async fn test_add_election_from_cacvote_server(pool: sqlx::PgPool) -> sqlx::Result<()> { - let mut db = pool.acquire().await?; - - let election_definition = load_famous_names_election(); - let jurisdiction = build_cacvote_server_jurisdiction(); - let record = build_cacvote_server_election(jurisdiction.id, election_definition.clone()); - - add_or_update_jurisdiction_from_cacvote_server(&mut db, jurisdiction) - .await - .unwrap(); - let client_id = add_election_from_cacvote_server(&mut db, record.clone()) - .await - .unwrap(); - - // insert again, should be idempotent - let client_id2 = add_election_from_cacvote_server(&mut db, record.clone()) - .await - .unwrap(); - - assert_eq!(client_id, client_id2); - - Ok(()) - } - - #[sqlx::test(migrations = "db/migrations")] - async fn test_add_everything_to_database(pool: sqlx::PgPool) -> sqlx::Result<()> { - let mut db = pool.acquire().await?; - - let election_definition = load_famous_names_election(); - let jurisdiction = build_cacvote_server_jurisdiction(); - let registration_request = build_cacvote_server_registration_request(jurisdiction.id); - let election = build_cacvote_server_election(jurisdiction.id, election_definition.clone()); - let registration = build_cacvote_server_registration( - ®istration_request, - &election, - &election_definition, - ); - let printed_ballot = build_cacvote_server_printed_ballot(®istration, Cvr::default()); - let scanned_ballot = build_cacvote_server_scanned_ballot(&election, Cvr::default()); - - add_or_update_jurisdiction_from_cacvote_server(&mut db, jurisdiction) - .await - .unwrap(); - - add_election_from_cacvote_server(&mut db, election) - .await - .unwrap(); - - add_or_update_registration_request_from_cacvote_server(&mut db, registration_request) - .await - .unwrap(); - - add_or_update_registration_from_cacvote_server(&mut db, registration) - .await - .unwrap(); - - add_or_update_printed_ballot_from_cacvote_server(&mut db, printed_ballot) - .await - .unwrap(); - - add_or_update_scanned_ballot_from_cacvote_server(&mut db, scanned_ballot) - .await - .unwrap(); - - Ok(()) - } -} diff --git a/apps/cacvote-scan/backend/src/log.rs b/apps/cacvote-scan/backend/src/log.rs deleted file mode 100644 index 9428429552..0000000000 --- a/apps/cacvote-scan/backend/src/log.rs +++ /dev/null @@ -1,35 +0,0 @@ -//! Logging for CACVote Scan. -//! -//! CACVote Scan uses the `tracing` library for logging. After calling [`setup`], -//! you'll be able to call [`tracing::info!`], [`tracing::span!`], and others to -//! print log messages to `stdout` in a flexible and configurable way. -//! -//! You may use the `RUST_LOG` environment variable to configure logging at -//! runtime (see [`EnvFilter`][`tracing_subscriber::EnvFilter`]). - -use tracing_subscriber::{prelude::*, util::SubscriberInitExt}; - -use crate::config::Config; - -/// Sets up logging for the application. Call this early in the process -/// lifecycle to ensure logs are not silently ignored. -pub(crate) fn setup(config: &Config) -> color_eyre::Result<()> { - color_eyre::install()?; - let stdout_log = tracing_subscriber::fmt::layer().pretty(); - tracing_subscriber::registry() - .with( - tracing_subscriber::EnvFilter::builder() - .with_default_directive( - format!( - "{}={}", - env!("CARGO_PKG_NAME").replace('-', "_"), - config.log_level - ) - .parse()?, - ) - .from_env_lossy(), - ) - .with(stdout_log) - .init(); - Ok(()) -} diff --git a/apps/cacvote-scan/backend/src/main.rs b/apps/cacvote-scan/backend/src/main.rs deleted file mode 100644 index 7e69b28a0c..0000000000 --- a/apps/cacvote-scan/backend/src/main.rs +++ /dev/null @@ -1,62 +0,0 @@ -//! CACVote Scan scans ballots and synchronizes them to the CACVote Server. - -#![warn( - clippy::all, - clippy::todo, - clippy::empty_enum, - clippy::enum_glob_use, - clippy::mem_forget, - clippy::unused_self, - clippy::filter_map_next, - clippy::needless_continue, - clippy::needless_borrow, - clippy::match_wildcard_for_single_variants, - clippy::if_let_mutex, - clippy::mismatched_target_os, - clippy::await_holding_lock, - clippy::match_on_vec_items, - clippy::imprecise_flops, - clippy::suboptimal_flops, - clippy::lossy_float_literal, - clippy::rest_pat_in_fully_bound_structs, - clippy::fn_params_excessive_bools, - clippy::exit, - clippy::inefficient_to_string, - clippy::linkedlist, - clippy::macro_use_imports, - clippy::option_option, - clippy::verbose_file_reads, - clippy::unnested_or_patterns, - clippy::str_to_string, - rust_2018_idioms, - future_incompatible, - nonstandard_style, - missing_debug_implementations, - missing_docs -)] -#![deny(unreachable_pub)] -#![allow(elided_lifetimes_in_paths, clippy::type_complexity)] -#![forbid(unsafe_code)] -#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg))] -#![cfg_attr(test, allow(clippy::float_cmp))] -#![cfg_attr(not(test), warn(clippy::print_stdout, clippy::dbg_macro))] - -use clap::Parser; - -mod app; -mod config; -mod db; -mod log; -mod sheets; -mod sync; - -#[tokio::main] -async fn main() -> color_eyre::Result<()> { - let _ = dotenvy::from_filename(".env.local"); - dotenvy::dotenv()?; - let config = config::Config::parse(); - log::setup(&config)?; - let pool = db::setup(&config).await?; - sync::sync_periodically(&pool, config.clone()).await; - app::run(app::setup(pool, config.clone()).await?, &config).await -} diff --git a/apps/cacvote-scan/backend/src/sheets.rs b/apps/cacvote-scan/backend/src/sheets.rs deleted file mode 100644 index 7c3a47e11d..0000000000 --- a/apps/cacvote-scan/backend/src/sheets.rs +++ /dev/null @@ -1,23 +0,0 @@ -//! Utilities for extracting data from scanned ballot sheets. - -use base64::{engine::general_purpose::STANDARD, Engine}; -use color_eyre::eyre::eyre; -use rqrr::PreparedImage; - -pub(crate) fn decode_page_from_image(image: image::GrayImage) -> color_eyre::Result> { - let mut prepared_image = PreparedImage::prepare(image); - - prepared_image - .detect_grids() - .iter() - .flat_map(|g| g.decode()) - .next() - .map_or_else( - || Err(eyre!("No QR code found")), - |(_, content)| { - STANDARD - .decode(content.as_str()) - .map_err(|_| eyre!("Unable to decode QR code: {}", content)) - }, - ) -} diff --git a/apps/cacvote-scan/backend/src/sync.rs b/apps/cacvote-scan/backend/src/sync.rs deleted file mode 100644 index 69cc2e8122..0000000000 --- a/apps/cacvote-scan/backend/src/sync.rs +++ /dev/null @@ -1,231 +0,0 @@ -//! CACVote Server synchronization utilities. - -use sqlx::PgPool; -use tokio::time::sleep; -use tracing::Level; -use types_rs::cacvote::{RaveServerSyncInput, RaveServerSyncOutput}; - -use crate::{ - config::{Config, SYNC_INTERVAL}, - db, -}; - -/// Spawns an async loop that synchronizes with the CACVote Server on a fixed -/// schedule. -pub(crate) async fn sync_periodically(pool: &PgPool, config: Config) { - tracing::debug!( - "Starting sync loop, syncing every {} seconds", - SYNC_INTERVAL.as_secs() - ); - let mut connection = pool - .acquire() - .await - .expect("failed to acquire database connection"); - - tokio::spawn(async move { - loop { - match sync(&mut connection, &config).await { - Ok(_) => { - tracing::info!("Successfully synced with CACVote Server"); - } - Err(e) => { - tracing::error!("Failed to sync with CACVote Server: {e}"); - } - } - - sleep(SYNC_INTERVAL).await; - } - }); -} - -pub(crate) async fn sync( - executor: &mut sqlx::PgConnection, - config: &Config, -) -> color_eyre::eyre::Result<()> { - let span = tracing::span!(Level::DEBUG, "Syncing with CACVote Server"); - let _enter = span.enter(); - - check_status(config.cacvote_url.join("/api/status")?).await?; - - let sync_input = RaveServerSyncInput { - last_synced_election_id: db::get_last_synced_election_id(executor) - .await - .map_err(|e| { - color_eyre::Report::msg(format!("failed to get last synced election ID: {}", e)) - })?, - last_synced_registration_request_id: db::get_last_synced_registration_request_id(executor) - .await - .map_err(|e| { - color_eyre::Report::msg(format!( - "failed to get last synced registration request ID: {}", - e - )) - })?, - last_synced_registration_id: db::get_last_synced_registration_id(executor) - .await - .map_err(|e| { - color_eyre::Report::msg(format!("failed to get last synced registration ID: {}", e)) - })?, - last_synced_scanned_ballot_id: db::get_last_synced_scanned_ballot_id(executor) - .await - .map_err(|e| { - color_eyre::Report::msg(format!( - "failed to get last synced scanned ballot ID: {}", - e - )) - })?, - last_synced_printed_ballot_id: db::get_last_synced_printed_ballot_id(executor) - .await - .map_err(|e| { - color_eyre::Report::msg(format!( - "failed to get last synced printed ballot ID: {}", - e - )) - })?, - elections: db::get_elections_to_sync_to_cacvote_server(executor) - .await - .map_err(|e| { - color_eyre::Report::msg(format!( - "failed to get elections to sync to CACVote Server: {}", - e - )) - })?, - registration_requests: db::get_registration_requests_to_sync_to_cacvote_server(executor) - .await - .map_err(|e| { - color_eyre::Report::msg(format!( - "failed to get registration requests to sync to CACVote Server: {}", - e - )) - })?, - registrations: db::get_registrations_to_sync_to_cacvote_server(executor) - .await - .map_err(|e| { - color_eyre::Report::msg(format!( - "failed to get registrations to sync to CACVote Server: {}", - e - )) - })?, - printed_ballots: db::get_printed_ballots_to_sync_to_cacvote_server(executor) - .await - .map_err(|e| { - color_eyre::Report::msg(format!( - "failed to get printed ballots to sync to CACVote Server: {}", - e - )) - })?, - scanned_ballots: db::get_scanned_ballots_to_sync_to_cacvote_server(executor) - .await - .map_err(|e| { - color_eyre::Report::msg(format!( - "failed to get scanned ballots to sync to CACVote Server: {}", - e - )) - })?, - }; - - let sync_endpoint = config - .cacvote_url - .join("/api/sync") - .expect("failed to construct sync URL"); - let sync_output = request(sync_endpoint, &sync_input).await?; - - let RaveServerSyncOutput { - jurisdictions, - elections, - registration_requests, - registrations, - printed_ballots, - scanned_ballots, - .. - } = sync_output.clone(); - - for jurisdiction in jurisdictions { - let result = - db::add_or_update_jurisdiction_from_cacvote_server(executor, jurisdiction).await; - - if let Err(e) = result { - tracing::error!("Failed to insert or update jurisdiction: {}", e); - } - } - - for election in elections { - let result = db::add_election_from_cacvote_server(executor, election).await; - - if let Err(e) = result { - tracing::error!("Failed to insert election: {}", e); - } - } - - for registration_request in registration_requests { - let result = db::add_or_update_registration_request_from_cacvote_server( - executor, - registration_request, - ) - .await; - - if let Err(e) = result { - tracing::error!("Failed to insert or update registration request: {}", e); - } - } - - for registration in registrations { - let result = - db::add_or_update_registration_from_cacvote_server(executor, registration).await; - - if let Err(e) = result { - tracing::error!("Failed to insert or update registration: {}", e); - } - } - - for printed_ballot in printed_ballots { - let result = - db::add_or_update_printed_ballot_from_cacvote_server(executor, printed_ballot).await; - - if let Err(e) = result { - tracing::error!("Failed to insert or update printed ballot: {}", e); - } - } - - for scanned_ballot in scanned_ballots { - let result = - db::add_or_update_scanned_ballot_from_cacvote_server(executor, scanned_ballot).await; - - if let Err(e) = result { - tracing::error!("Failed to insert or update scanned ballot: {}", e); - } - } - - Ok(()) -} - -pub(crate) async fn check_status(endpoint: reqwest::Url) -> color_eyre::eyre::Result<()> { - let client = reqwest::Client::new(); - client - .get(endpoint.clone()) - .send() - .await? - .error_for_status() - .map_err(|e| { - color_eyre::eyre::eyre!( - "CACVote Server responded with an error (status URL={}): {}", - endpoint, - e - ) - }) - .map(|_| ()) -} - -pub(crate) async fn request( - endpoint: reqwest::Url, - sync_input: &RaveServerSyncInput, -) -> color_eyre::eyre::Result { - let client = reqwest::Client::new(); - Ok(client - .post(endpoint) - .json(sync_input) - .send() - .await? - .json::() - .await?) -} diff --git a/apps/cacvote-scan/backend/tests/fixtures/electionFamousNames2021.json b/apps/cacvote-scan/backend/tests/fixtures/electionFamousNames2021.json deleted file mode 100755 index 3e7a46c1fe..0000000000 --- a/apps/cacvote-scan/backend/tests/fixtures/electionFamousNames2021.json +++ /dev/null @@ -1,324 +0,0 @@ -{ - "title": "Lincoln Municipal General Election", - "state": "State of Hamilton", - "county": { - "id": "franklin", - "name": "Franklin County" - }, - "date": "2021-06-06T00:00:00-10:00", - "parties": [ - { - "id": "0", - "name": "Democrat", - "fullName": "Democratic Party", - "abbrev": "D" - }, - { - "id": "1", - "name": "Republican", - "fullName": "Republican Party", - "abbrev": "R" - }, - { - "id": "2", - "name": "Liberty", - "fullName": "Liberty Party", - "abbrev": "Li" - }, - { - "id": "3", - "name": "Green", - "fullName": "Green Party", - "abbrev": "G" - } - ], - "contests": [ - { - "id": "mayor", - "districtId": "district-1", - "type": "candidate", - "title": "Mayor", - "seats": 1, - "allowWriteIns": true, - "candidates": [ - { - "id": "sherlock-holmes", - "name": "Sherlock Holmes", - "partyIds": ["0"] - }, - { - "id": "thomas-edison", - "name": "Thomas Edison", - "partyIds": ["1"] - } - ] - }, - { - "id": "controller", - "districtId": "district-1", - "type": "candidate", - "title": "Controller", - "seats": 1, - "allowWriteIns": true, - "candidates": [ - { - "id": "winston-churchill", - "name": "Winston Churchill", - "partyIds": ["0"] - }, - { - "id": "oprah-winfrey", - "name": "Oprah Winfrey", - "partyIds": ["1"] - }, - { - "id": "louis-armstrong", - "name": "Louis Armstrong", - "partyIds": ["3"] - } - ] - }, - { - "id": "attorney", - "districtId": "district-1", - "type": "candidate", - "title": "Attorney", - "seats": 1, - "allowWriteIns": true, - "candidates": [ - { - "id": "john-snow", - "name": "John Snow", - "partyIds": ["1"] - }, - { - "id": "mark-twain", - "name": "Mark Twain", - "partyIds": ["3"] - } - ] - }, - { - "id": "public-works-director", - "districtId": "district-1", - "type": "candidate", - "title": "Public Works Director", - "seats": 1, - "allowWriteIns": true, - "candidates": [ - { - "id": "benjamin-franklin", - "name": "Benjamin Franklin", - "partyIds": ["0"] - }, - { - "id": "robert-downey-jr", - "name": "Robert Downey Jr.", - "partyIds": ["1"] - }, - { - "id": "bill-nye", - "name": "Bill Nye", - "partyIds": ["3"] - } - ] - }, - { - "id": "chief-of-police", - "districtId": "district-1", - "type": "candidate", - "title": "Chief of Police", - "seats": 1, - "allowWriteIns": true, - "candidates": [ - { - "id": "natalie-portman", - "name": "Natalie Portman", - "partyIds": ["0"] - }, - { - "id": "frank-sinatra", - "name": "Frank Sinatra", - "partyIds": ["1"] - }, - { - "id": "andy-warhol", - "name": "Andy Warhol", - "partyIds": ["3"] - }, - { - "id": "alfred-hitchcock", - "name": "Alfred Hitchcock", - "partyIds": ["3"] - } - ] - }, - { - "id": "parks-and-recreation-director", - "districtId": "district-1", - "type": "candidate", - "title": "Parks and Recreation Director", - "seats": 1, - "allowWriteIns": true, - "candidates": [ - { - "id": "charles-darwin", - "name": "Charles Darwin", - "partyIds": ["0"] - }, - { - "id": "stephen-hawking", - "name": "Stephen Hawking", - "partyIds": ["1"] - }, - { - "id": "johan-sebastian-bach", - "name": "Johann Sebastian Bach", - "partyIds": ["0"] - }, - { - "id": "alexander-graham-bell", - "name": "Alexander Graham Bell", - "partyIds": ["1"] - } - ] - }, - { - "id": "board-of-alderman", - "districtId": "district-1", - "type": "candidate", - "title": "Board of Alderman", - "seats": 4, - "allowWriteIns": true, - "candidates": [ - { - "id": "helen-keller", - "name": "Helen Keller", - "partyIds": ["0"] - }, - { - "id": "steve-jobs", - "name": "Steve Jobs", - "partyIds": ["1"] - }, - { - "id": "nikola-tesla", - "name": "Nikola Tesla", - "partyIds": ["0"] - }, - { - "id": "vincent-van-gogh", - "name": "Vincent Van Gogh", - "partyIds": ["1"] - }, - { - "id": "pablo-picasso", - "name": "Pablo Picasso", - "partyIds": ["1"] - }, - { - "id": "wolfgang-amadeus-mozart", - "name": "Wolfgang Amadeus Mozart", - "partyIds": ["2"] - } - ] - }, - { - "id": "city-council", - "districtId": "district-1", - "type": "candidate", - "title": "City Council", - "seats": 4, - "allowWriteIns": true, - "candidates": [ - { - "id": "marie-curie", - "name": "Marie Curie", - "partyIds": ["0"] - }, - { - "id": "indiana-jones", - "name": "Indiana Jones", - "partyIds": ["1"] - }, - { - "id": "mona-lisa", - "name": "Mona Lisa", - "partyIds": ["3"] - }, - { - "id": "jackie-chan", - "name": "Jackie Chan", - "partyIds": ["3"] - }, - { - "id": "tim-allen", - "name": "Tim Allen", - "partyIds": ["2"] - }, - { - "id": "mark-antony", - "name": "Mark Antony", - "partyIds": ["0"] - }, - { - "id": "harriet-tubman", - "name": "Harriet Tubman", - "partyIds": ["1"] - }, - { - "id": "martin-luther-king", - "name": "Dr. Martin Luther King Jr.", - "partyIds": ["0"] - }, - { - "id": "marilyn-monroe", - "name": "Marilyn Monroe", - "partyIds": ["1"] - } - ] - } - ], - "districts": [ - { - "id": "district-1", - "name": "City of Lincoln" - } - ], - "precincts": [ - { - "id": "23", - "name": "North Lincoln" - }, - { - "id": "22", - "name": "South Lincoln" - }, - { - "id": "21", - "name": "East Lincoln" - }, - { - "id": "20", - "name": "West Lincoln" - } - ], - "ballotStyles": [ - { - "id": "1", - "precincts": ["20", "21", "22", "23"], - "districts": ["district-1"] - } - ], - "sealUrl": "/seals/state-of-hamilton-official-seal.svg", - "adjudicationReasons": [ - "UninterpretableBallot", - "Overvote", - "Undervote", - "BlankBallot" - ], - "markThresholds": { - "definite": 0.12, - "marginal": 0.12 - } -} diff --git a/apps/cacvote-scan/frontend/.gitignore b/apps/cacvote-scan/frontend/.gitignore deleted file mode 100644 index ace9762350..0000000000 --- a/apps/cacvote-scan/frontend/.gitignore +++ /dev/null @@ -1,16 +0,0 @@ -# Generated by Cargo -# will have compiled files and executables -/target/ -/dist/ - -# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries -# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html -Cargo.lock - -# These are backup files generated by rustfmt -**/*.rs.bk - - -# Added by cargo - -/target diff --git a/apps/cacvote-scan/frontend/Cargo.toml b/apps/cacvote-scan/frontend/Cargo.toml deleted file mode 100644 index 37316065c7..0000000000 --- a/apps/cacvote-scan/frontend/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "cacvote-scan-frontend" -version = "0.1.0" -authors = ["VotingWorks Eng "] -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -dioxus = "0.4" -dioxus-web = "0.4" - -log = "0.4.19" -dioxus-logger = "0.4.1" -console_error_panic_hook = "0.1.7" -reqwest = { workspace = true } -serde = { workspace = true } -serde_json = { workspace = true } -types-rs = { workspace = true } -ui-rs = { workspace = true } -wasm-bindgen = "0.2.87" -web-sys = { version = "0.3.64", features = ["EventSource"] } diff --git a/apps/cacvote-scan/frontend/Dioxus.toml b/apps/cacvote-scan/frontend/Dioxus.toml deleted file mode 100644 index 6c339caf41..0000000000 --- a/apps/cacvote-scan/frontend/Dioxus.toml +++ /dev/null @@ -1,45 +0,0 @@ -[application] - -# App (Project) Name -name = "cacvote-scan-frontend" - -# Dioxus App Default Platform -# desktop, web, mobile, ssr -default_platform = "web" - -# `build` & `serve` dist path -out_dir = "dist" - -# resource (public) file folder -asset_dir = "public" - -[web.app] - -# HTML title tag content -title = "CACVote Scan" - -[web.watcher] - -# when watcher trigger, regenerate the `index.html` -reload_html = true - -# which files or dirs will be watcher monitoring -watch_path = ["src", "public"] - -# include `assets` in web platform -[web.resource] - -# CSS style file -style = ["/styles.css"] - -# Javascript code file -script = [] - -[web.resource.dev] - -# Javascript code file -# serve: [dev-server] only -script = [] - -[[web.proxy]] -backend = "http://127.0.0.1:4001/api" diff --git a/apps/cacvote-scan/frontend/LICENSE b/apps/cacvote-scan/frontend/LICENSE deleted file mode 100644 index bcdd828e9c..0000000000 --- a/apps/cacvote-scan/frontend/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2022 Dioxus - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/apps/cacvote-scan/frontend/README.md b/apps/cacvote-scan/frontend/README.md deleted file mode 100644 index fa6e47524a..0000000000 --- a/apps/cacvote-scan/frontend/README.md +++ /dev/null @@ -1,40 +0,0 @@ -# dioxus-template - -> a template for starting a dioxus project to be used with [dioxus-cli](https://github.com/DioxusLabs/cli) - -## Usage - -#### use `dioxus-cli` init the template: - -``` -dioxus init hello-dioxus -``` - -or you can choose the template, for this tempalte: - -``` -dioxus init hello-dioxus --template=gh:dioxuslabs/dioxus-template -``` - -#### Start a `dev-server` for the project: - -``` -cd ./hello-dioxus -dioxus serve -``` - -or package this project: - -``` -dioxus build --release -``` - -## Project Structure - -``` -.project -- public # save the assets you want include in your project. -- src # put your code -- - utils # save some public function -- - components # save some custom components -``` diff --git a/apps/cacvote-scan/frontend/input.css b/apps/cacvote-scan/frontend/input.css deleted file mode 100644 index bd6213e1df..0000000000 --- a/apps/cacvote-scan/frontend/input.css +++ /dev/null @@ -1,3 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; \ No newline at end of file diff --git a/apps/cacvote-scan/frontend/package.json b/apps/cacvote-scan/frontend/package.json deleted file mode 100644 index 268ee7829c..0000000000 --- a/apps/cacvote-scan/frontend/package.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "@votingworks/cacvote-scan-frontend", - "version": "1.0.0", - "description": "CACVote Scan frontend", - "keywords": [], - "license": "GPL-3.0", - "author": "VotingWorks Eng ", - "scripts": { - "build": "script/build", - "lint": "cargo fmt -- --check && cargo clippy -- -D warnings", - "start": "concurrently 'npm:start:*' 'npm:build:css:watch'", - "start:frontend": "dx serve --port 3000", - "start:backend": "pnpm --dir ../backend start", - "build:css:watch": "tailwindcss -i input.css -o ./public/styles.css --watch", - "test": "cargo test" - }, - "dependencies": { - "tailwindcss": "^3.3.3" - }, - "devDependencies": { - "concurrently": "7.6.0" - }, - "packageManager": "pnpm@8.1.0" -} diff --git a/apps/cacvote-scan/frontend/public/styles.css b/apps/cacvote-scan/frontend/public/styles.css deleted file mode 100644 index aa790faa9f..0000000000 --- a/apps/cacvote-scan/frontend/public/styles.css +++ /dev/null @@ -1,725 +0,0 @@ -/* -! tailwindcss v3.3.3 | MIT License | https://tailwindcss.com -*/ - -/* -1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) -2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116) -*/ - -*, -::before, -::after { - box-sizing: border-box; - /* 1 */ - border-width: 0; - /* 2 */ - border-style: solid; - /* 2 */ - border-color: #e5e7eb; - /* 2 */ -} - -::before, -::after { - --tw-content: ''; -} - -/* -1. Use a consistent sensible line-height in all browsers. -2. Prevent adjustments of font size after orientation changes in iOS. -3. Use a more readable tab size. -4. Use the user's configured `sans` font-family by default. -5. Use the user's configured `sans` font-feature-settings by default. -6. Use the user's configured `sans` font-variation-settings by default. -*/ - -html { - line-height: 1.5; - /* 1 */ - -webkit-text-size-adjust: 100%; - /* 2 */ - -moz-tab-size: 4; - /* 3 */ - -o-tab-size: 4; - tab-size: 4; - /* 3 */ - font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; - /* 4 */ - font-feature-settings: normal; - /* 5 */ - font-variation-settings: normal; - /* 6 */ -} - -/* -1. Remove the margin in all browsers. -2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. -*/ - -body { - margin: 0; - /* 1 */ - line-height: inherit; - /* 2 */ -} - -/* -1. Add the correct height in Firefox. -2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) -3. Ensure horizontal rules are visible by default. -*/ - -hr { - height: 0; - /* 1 */ - color: inherit; - /* 2 */ - border-top-width: 1px; - /* 3 */ -} - -/* -Add the correct text decoration in Chrome, Edge, and Safari. -*/ - -abbr:where([title]) { - -webkit-text-decoration: underline dotted; - text-decoration: underline dotted; -} - -/* -Remove the default font size and weight for headings. -*/ - -h1, -h2, -h3, -h4, -h5, -h6 { - font-size: inherit; - font-weight: inherit; -} - -/* -Reset links to optimize for opt-in styling instead of opt-out. -*/ - -a { - color: inherit; - text-decoration: inherit; -} - -/* -Add the correct font weight in Edge and Safari. -*/ - -b, -strong { - font-weight: bolder; -} - -/* -1. Use the user's configured `mono` font family by default. -2. Correct the odd `em` font sizing in all browsers. -*/ - -code, -kbd, -samp, -pre { - font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; - /* 1 */ - font-size: 1em; - /* 2 */ -} - -/* -Add the correct font size in all browsers. -*/ - -small { - font-size: 80%; -} - -/* -Prevent `sub` and `sup` elements from affecting the line height in all browsers. -*/ - -sub, -sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; -} - -sub { - bottom: -0.25em; -} - -sup { - top: -0.5em; -} - -/* -1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) -2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) -3. Remove gaps between table borders by default. -*/ - -table { - text-indent: 0; - /* 1 */ - border-color: inherit; - /* 2 */ - border-collapse: collapse; - /* 3 */ -} - -/* -1. Change the font styles in all browsers. -2. Remove the margin in Firefox and Safari. -3. Remove default padding in all browsers. -*/ - -button, -input, -optgroup, -select, -textarea { - font-family: inherit; - /* 1 */ - font-feature-settings: inherit; - /* 1 */ - font-variation-settings: inherit; - /* 1 */ - font-size: 100%; - /* 1 */ - font-weight: inherit; - /* 1 */ - line-height: inherit; - /* 1 */ - color: inherit; - /* 1 */ - margin: 0; - /* 2 */ - padding: 0; - /* 3 */ -} - -/* -Remove the inheritance of text transform in Edge and Firefox. -*/ - -button, -select { - text-transform: none; -} - -/* -1. Correct the inability to style clickable types in iOS and Safari. -2. Remove default button styles. -*/ - -button, -[type='button'], -[type='reset'], -[type='submit'] { - -webkit-appearance: button; - /* 1 */ - background-color: transparent; - /* 2 */ - background-image: none; - /* 2 */ -} - -/* -Use the modern Firefox focus style for all focusable elements. -*/ - -:-moz-focusring { - outline: auto; -} - -/* -Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) -*/ - -:-moz-ui-invalid { - box-shadow: none; -} - -/* -Add the correct vertical alignment in Chrome and Firefox. -*/ - -progress { - vertical-align: baseline; -} - -/* -Correct the cursor style of increment and decrement buttons in Safari. -*/ - -::-webkit-inner-spin-button, -::-webkit-outer-spin-button { - height: auto; -} - -/* -1. Correct the odd appearance in Chrome and Safari. -2. Correct the outline style in Safari. -*/ - -[type='search'] { - -webkit-appearance: textfield; - /* 1 */ - outline-offset: -2px; - /* 2 */ -} - -/* -Remove the inner padding in Chrome and Safari on macOS. -*/ - -::-webkit-search-decoration { - -webkit-appearance: none; -} - -/* -1. Correct the inability to style clickable types in iOS and Safari. -2. Change font properties to `inherit` in Safari. -*/ - -::-webkit-file-upload-button { - -webkit-appearance: button; - /* 1 */ - font: inherit; - /* 2 */ -} - -/* -Add the correct display in Chrome and Safari. -*/ - -summary { - display: list-item; -} - -/* -Removes the default spacing and border for appropriate elements. -*/ - -blockquote, -dl, -dd, -h1, -h2, -h3, -h4, -h5, -h6, -hr, -figure, -p, -pre { - margin: 0; -} - -fieldset { - margin: 0; - padding: 0; -} - -legend { - padding: 0; -} - -ol, -ul, -menu { - list-style: none; - margin: 0; - padding: 0; -} - -/* -Reset default styling for dialogs. -*/ - -dialog { - padding: 0; -} - -/* -Prevent resizing textareas horizontally by default. -*/ - -textarea { - resize: vertical; -} - -/* -1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) -2. Set the default placeholder color to the user's configured gray 400 color. -*/ - -input::-moz-placeholder, textarea::-moz-placeholder { - opacity: 1; - /* 1 */ - color: #9ca3af; - /* 2 */ -} - -input::placeholder, -textarea::placeholder { - opacity: 1; - /* 1 */ - color: #9ca3af; - /* 2 */ -} - -/* -Set the default cursor for buttons. -*/ - -button, -[role="button"] { - cursor: pointer; -} - -/* -Make sure disabled buttons don't get the pointer cursor. -*/ - -:disabled { - cursor: default; -} - -/* -1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) -2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) - This can trigger a poorly considered lint error in some tools but is included by design. -*/ - -img, -svg, -video, -canvas, -audio, -iframe, -embed, -object { - display: block; - /* 1 */ - vertical-align: middle; - /* 2 */ -} - -/* -Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) -*/ - -img, -video { - max-width: 100%; - height: auto; -} - -/* Make elements with the HTML hidden attribute stay hidden by default */ - -[hidden] { - display: none; -} - -*, ::before, ::after { - --tw-border-spacing-x: 0; - --tw-border-spacing-y: 0; - --tw-translate-x: 0; - --tw-translate-y: 0; - --tw-rotate: 0; - --tw-skew-x: 0; - --tw-skew-y: 0; - --tw-scale-x: 1; - --tw-scale-y: 1; - --tw-pan-x: ; - --tw-pan-y: ; - --tw-pinch-zoom: ; - --tw-scroll-snap-strictness: proximity; - --tw-gradient-from-position: ; - --tw-gradient-via-position: ; - --tw-gradient-to-position: ; - --tw-ordinal: ; - --tw-slashed-zero: ; - --tw-numeric-figure: ; - --tw-numeric-spacing: ; - --tw-numeric-fraction: ; - --tw-ring-inset: ; - --tw-ring-offset-width: 0px; - --tw-ring-offset-color: #fff; - --tw-ring-color: rgb(59 130 246 / 0.5); - --tw-ring-offset-shadow: 0 0 #0000; - --tw-ring-shadow: 0 0 #0000; - --tw-shadow: 0 0 #0000; - --tw-shadow-colored: 0 0 #0000; - --tw-blur: ; - --tw-brightness: ; - --tw-contrast: ; - --tw-grayscale: ; - --tw-hue-rotate: ; - --tw-invert: ; - --tw-saturate: ; - --tw-sepia: ; - --tw-drop-shadow: ; - --tw-backdrop-blur: ; - --tw-backdrop-brightness: ; - --tw-backdrop-contrast: ; - --tw-backdrop-grayscale: ; - --tw-backdrop-hue-rotate: ; - --tw-backdrop-invert: ; - --tw-backdrop-opacity: ; - --tw-backdrop-saturate: ; - --tw-backdrop-sepia: ; -} - -::backdrop { - --tw-border-spacing-x: 0; - --tw-border-spacing-y: 0; - --tw-translate-x: 0; - --tw-translate-y: 0; - --tw-rotate: 0; - --tw-skew-x: 0; - --tw-skew-y: 0; - --tw-scale-x: 1; - --tw-scale-y: 1; - --tw-pan-x: ; - --tw-pan-y: ; - --tw-pinch-zoom: ; - --tw-scroll-snap-strictness: proximity; - --tw-gradient-from-position: ; - --tw-gradient-via-position: ; - --tw-gradient-to-position: ; - --tw-ordinal: ; - --tw-slashed-zero: ; - --tw-numeric-figure: ; - --tw-numeric-spacing: ; - --tw-numeric-fraction: ; - --tw-ring-inset: ; - --tw-ring-offset-width: 0px; - --tw-ring-offset-color: #fff; - --tw-ring-color: rgb(59 130 246 / 0.5); - --tw-ring-offset-shadow: 0 0 #0000; - --tw-ring-shadow: 0 0 #0000; - --tw-shadow: 0 0 #0000; - --tw-shadow-colored: 0 0 #0000; - --tw-blur: ; - --tw-brightness: ; - --tw-contrast: ; - --tw-grayscale: ; - --tw-hue-rotate: ; - --tw-invert: ; - --tw-saturate: ; - --tw-sepia: ; - --tw-drop-shadow: ; - --tw-backdrop-blur: ; - --tw-backdrop-brightness: ; - --tw-backdrop-contrast: ; - --tw-backdrop-grayscale: ; - --tw-backdrop-hue-rotate: ; - --tw-backdrop-invert: ; - --tw-backdrop-opacity: ; - --tw-backdrop-saturate: ; - --tw-backdrop-sepia: ; -} - -.m-2 { - margin: 0.5rem; -} - -.mb-2 { - margin-bottom: 0.5rem; -} - -.flex { - display: flex; -} - -.table { - display: table; -} - -.hidden { - display: none; -} - -.h-screen { - height: 100vh; -} - -.w-screen { - width: 100vw; -} - -.flex-grow { - flex-grow: 1; -} - -.flex-row { - flex-direction: row; -} - -.flex-col { - flex-direction: column; -} - -.items-center { - align-items: center; -} - -.whitespace-nowrap { - white-space: nowrap; -} - -.rounded-lg { - border-radius: 0.5rem; -} - -.rounded-md { - border-radius: 0.375rem; -} - -.border { - border-width: 1px; -} - -.bg-gray-200 { - --tw-bg-opacity: 1; - background-color: rgb(229 231 235 / var(--tw-bg-opacity)); -} - -.bg-purple-500 { - --tw-bg-opacity: 1; - background-color: rgb(168 85 247 / var(--tw-bg-opacity)); -} - -.p-1 { - padding: 0.25rem; -} - -.p-2 { - padding: 0.5rem; -} - -.p-3 { - padding: 0.75rem; -} - -.px-2 { - padding-left: 0.5rem; - padding-right: 0.5rem; -} - -.px-3 { - padding-left: 0.75rem; - padding-right: 0.75rem; -} - -.px-4 { - padding-left: 1rem; - padding-right: 1rem; -} - -.py-2 { - padding-top: 0.5rem; - padding-bottom: 0.5rem; -} - -.pl-2 { - padding-left: 0.5rem; -} - -.text-left { - text-align: left; -} - -.text-center { - text-align: center; -} - -.text-3xl { - font-size: 1.875rem; - line-height: 2.25rem; -} - -.text-base { - font-size: 1rem; - line-height: 1.5rem; -} - -.text-lg { - font-size: 1.125rem; - line-height: 1.75rem; -} - -.text-sm { - font-size: 0.875rem; - line-height: 1.25rem; -} - -.text-xl { - font-size: 1.25rem; - line-height: 1.75rem; -} - -.font-bold { - font-weight: 700; -} - -.text-gray-400 { - --tw-text-opacity: 1; - color: rgb(156 163 175 / var(--tw-text-opacity)); -} - -.text-gray-700 { - --tw-text-opacity: 1; - color: rgb(55 65 81 / var(--tw-text-opacity)); -} - -.text-white { - --tw-text-opacity: 1; - color: rgb(255 255 255 / var(--tw-text-opacity)); -} - -.active\:bg-purple-700:active { - --tw-bg-opacity: 1; - background-color: rgb(126 34 206 / var(--tw-bg-opacity)); -} - -.disabled\:bg-purple-300:disabled { - --tw-bg-opacity: 1; - background-color: rgb(216 180 254 / var(--tw-bg-opacity)); -} - -@media (prefers-color-scheme: dark) { - .dark\:bg-gray-700 { - --tw-bg-opacity: 1; - background-color: rgb(55 65 81 / var(--tw-bg-opacity)); - } - - .dark\:bg-slate-800 { - --tw-bg-opacity: 1; - background-color: rgb(30 41 59 / var(--tw-bg-opacity)); - } - - .dark\:text-gray-200 { - --tw-text-opacity: 1; - color: rgb(229 231 235 / var(--tw-text-opacity)); - } - - .dark\:text-gray-300 { - --tw-text-opacity: 1; - color: rgb(209 213 219 / var(--tw-text-opacity)); - } -} \ No newline at end of file diff --git a/apps/cacvote-scan/frontend/script/build b/apps/cacvote-scan/frontend/script/build deleted file mode 100755 index 8e5208df7c..0000000000 --- a/apps/cacvote-scan/frontend/script/build +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -if ! command dx --version &> /dev/null; then - cargo install dioxus-cli -fi - -rustup target add wasm32-unknown-unknown -dx build \ No newline at end of file diff --git a/apps/cacvote-scan/frontend/src/main.rs b/apps/cacvote-scan/frontend/src/main.rs deleted file mode 100644 index bc4ee19326..0000000000 --- a/apps/cacvote-scan/frontend/src/main.rs +++ /dev/null @@ -1,184 +0,0 @@ -use dioxus::prelude::*; -use log::LevelFilter; -use serde::Deserialize; -use types_rs::scan::ScannedBallotStats; -use ui_rs::{Button, DateOrDateTimeCell, TableCell}; -use wasm_bindgen::prelude::*; -use web_sys::MessageEvent; - -fn main() { - // Init debug - dioxus_logger::init(LevelFilter::Info).expect("failed to init logger"); - console_error_panic_hook::set_once(); - - log::info!("starting app"); - dioxus_web::launch(App); -} - -fn get_root_url() -> reqwest::Url { - let loc = web_sys::window().unwrap().location(); - reqwest::Url::parse(loc.origin().unwrap().as_str()).unwrap() -} - -fn get_url(path: &str) -> reqwest::Url { - get_root_url().join(path).unwrap() -} - -fn use_scanned_ballot_stats(cx: Scope) -> &UseState> { - let ballot_stats = use_state(cx, || None); - - use_coroutine(cx, { - to_owned![ballot_stats]; - |_rx: UnboundedReceiver| async move { - #[derive(Deserialize)] - struct StatusStreamEvent { - stats: ScannedBallotStats, - } - let eventsource = web_sys::EventSource::new("/api/status-stream").unwrap(); - - let callback = Closure::wrap(Box::new(move |event: MessageEvent| { - if let Some(data) = event.data().as_string() { - match serde_json::from_str::(data.as_str()) { - Ok(event) => { - ballot_stats.set(Some(event.stats)); - } - Err(err) => { - log::error!("error deserializing status event: {:?}", err); - } - } - } - }) as Box); - - eventsource.set_onmessage(Some(callback.as_ref().unchecked_ref())); - callback.forget(); - } - }); - - ballot_stats -} - -#[allow(non_snake_case)] -fn App(cx: Scope) -> Element { - let is_scanning = use_state(cx, || false); - let ballot_stats = use_scanned_ballot_stats(cx); - - let scan_ballots = move |_| { - log::info!("scan ballots"); - is_scanning.set(true); - - cx.spawn({ - to_owned![is_scanning]; - async move { - let client = reqwest::Client::new(); - let result = client.post(get_url("/api/scan")).send().await; - is_scanning.set(false); - - match result { - Ok(response) => { - log::info!("response: {:?}", response); - - if !response.status().is_success() { - log::error!("error"); - return; - } - - log::info!("success"); - } - Err(err) => { - log::error!("error: {:?}", err); - } - } - } - }); - }; - - let is_scanning = *is_scanning.get(); - let ballot_stats = ballot_stats.get(); - - render! { - div { - class: "h-screen w-screen dark:bg-slate-800", - div { - class: "flex-col", - div { - class: "flex flex-row items-center mb-2 pl-2 bg-gray-200 dark:bg-gray-700", - div { - class: "text-3xl flex-grow font-bold text-gray-700 dark:text-gray-200", - "CACVote Scan" - } - div { - class: "m-2", - Button { - onclick: scan_ballots, - disabled: is_scanning, - if is_scanning { - "Scanning…" - } else { - "Scan Ballots" - }, - } - } - } - div { - class: "text-xl text-center text-gray-400 dark:text-gray-300 px-3", - if let Some(ballot_stats) = ballot_stats { - rsx! { - h3 { - class: "text-left", - "Batches" - } - table { - class: "text-sm", - thead { - tr { - th { "ID" } - th { "Status" } - th { "Started At" } - th { "Ended At" } - } - } - tbody { - for batch in ballot_stats.batches.clone().into_iter() { - rsx! { - tr { - TableCell { batch.id.to_string() } - TableCell { - match (batch.ballot_count, batch.election_count, batch.synced_count) { - (0, _, _) => "No ballots".to_owned(), - (1, _, 0) => "1 ballot (pending sync)".to_owned(), - (1, _, 1) => "1 ballot (synced)".to_owned(), - (b, 1, s) if b == s => format!("{b} ballots, 1 election (synced)"), - (b, 1, s) => format!("{b} ballots, 1 election ({s} synced)"), - (b, e, s) if b == s => format!("{b} ballots, {e} elections (synced)"), - (b, e, s) => format!("{b} ballots, {e} elections ({s} synced)"), - } - } - DateOrDateTimeCell { - date_or_datetime: batch.started_at, - } - match batch.ended_at { - Some(ended_at) => { - rsx! { - DateOrDateTimeCell { date_or_datetime: ended_at } - } - } - None => { - rsx! { - TableCell { - "Scanning…" - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } -} diff --git a/apps/cacvote-scan/frontend/tailwind.config.js b/apps/cacvote-scan/frontend/tailwind.config.js deleted file mode 100644 index e584227552..0000000000 --- a/apps/cacvote-scan/frontend/tailwind.config.js +++ /dev/null @@ -1,13 +0,0 @@ -/** @type {import('tailwindcss').Config} */ -module.exports = { - mode: 'all', - content: [ - './src/**/*.{rs,html,css}', - './dist/**/*.html', - '../../../libs/ui-rs/src/**/*.{rs,html,css}', - ], - theme: { - extend: {}, - }, - plugins: [], -}; diff --git a/apps/cacvote-track/frontend/.eslintignore b/apps/cacvote-track/frontend/.eslintignore deleted file mode 100644 index bfca69f134..0000000000 --- a/apps/cacvote-track/frontend/.eslintignore +++ /dev/null @@ -1,10 +0,0 @@ -/build -/codemods -/coverage -/prodserver/**/*.js -/src/**/*.js -/script/**/*.js -/scripts/**/*.js -/config/**/*.js -/*.config.js -/*.config.ts diff --git a/apps/cacvote-track/frontend/.eslintrc.json b/apps/cacvote-track/frontend/.eslintrc.json deleted file mode 100644 index 7d46d4c17a..0000000000 --- a/apps/cacvote-track/frontend/.eslintrc.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": ["plugin:vx/react"], - "rules": { - "vx/gts-jsdoc": "off", - "vx/gts-direct-module-export-access-only": "off", - "vx/gts-identifiers": ["error", { "allowedNames": ["jsQR"] }] - } -} diff --git a/apps/cacvote-track/frontend/README.md b/apps/cacvote-track/frontend/README.md deleted file mode 100644 index 8732638e73..0000000000 --- a/apps/cacvote-track/frontend/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# `cacvote-track` - -This app is hosted at https://cacvote-track.voting.works/ and is used in the -ballot tracking flow. At the moment it is only used for demo/testing purposes. diff --git a/apps/cacvote-track/frontend/index.html b/apps/cacvote-track/frontend/index.html deleted file mode 100644 index 9836c6daae..0000000000 --- a/apps/cacvote-track/frontend/index.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - VotingWorks RaveTrack - - - -
- - - diff --git a/apps/cacvote-track/frontend/jest.config.js b/apps/cacvote-track/frontend/jest.config.js deleted file mode 100644 index df400abf8e..0000000000 --- a/apps/cacvote-track/frontend/jest.config.js +++ /dev/null @@ -1,21 +0,0 @@ -const shared = require('../../../jest.config.shared'); - -/** - * @type {import('@jest/types').Config.InitialOptions} - */ -module.exports = { - ...shared, - resetMocks: true, - testEnvironment: 'jsdom', - transformIgnorePatterns: [ - '[/\\\\]node_modules[/\\\\].+\\.(js|jsx|mjs|cjs|ts|tsx)$', - ], - coverageThreshold: { - global: { - statements: 0, - branches: 0, - lines: 0, - functions: 0, - }, - }, -}; diff --git a/apps/cacvote-track/frontend/package.json b/apps/cacvote-track/frontend/package.json deleted file mode 100644 index feb512d5e2..0000000000 --- a/apps/cacvote-track/frontend/package.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "name": "@votingworks/cacvote-track-frontend", - "version": "1.0.0", - "private": true, - "license": "GPL-3.0", - "author": "VotingWorks Eng ", - "files": [ - "build", - "Makefile", - "prodserver" - ], - "scripts": { - "build": "pnpm type-check && vite build", - "clean": "rm -rf build tsconfig.tsbuildinfo tsconfig.build.tsbuildinfo", - "format": "prettier '**/*.+(css|graphql|json|less|md|mdx|sass|scss|yaml|yml)' --write", - "lint": "pnpm type-check && eslint .", - "lint:fix": "pnpm type-check && eslint . --fix", - "pre-commit": "lint-staged", - "start": "TZ=UTC pnpm -w run-dev cacvote-track", - "start:core": "pnpm -w run-dev vm-cacvote-track --core-only", - "start:prod": "concurrently --names frontend 'pnpm --dir prodserver start'", - "test": "is-ci test:ci test:watch", - "test:ci": "TZ=UTC CI=true jest --maxWorkers=7", - "test:coverage": "TZ=UTC jest --coverage --watchAll=false", - "test:update": "TZ=UTC jest -u --watchAll=false", - "test:watch": "TZ=UTC jest --watch", - "type-check": "tsc --build" - }, - "lint-staged": { - "*.+(js|jsx|ts|tsx)": [ - "eslint --quiet --fix" - ], - "*.+(css|graphql|json|less|md|mdx|sass|scss|yaml|yml)": [ - "prettier --write" - ], - "package.json": [ - "sort-package-json" - ] - }, - "dependencies": { - "@votingworks/basics": "workspace:*", - "@votingworks/types": "workspace:*", - "@votingworks/ui": "workspace:*", - "buffer": "^6.0.3", - "jsqr": "^1.4.0", - "path": "^0.12.7", - "react": "18.2.0", - "react-dom": "18.2.0" - }, - "devDependencies": { - "@jest/types": "^29.6.1", - "@testing-library/jest-dom": "^5.17.0", - "@testing-library/react": "^14.0.0", - "@testing-library/user-event": "^13.5.0", - "@types/jest": "^29.5.3", - "@types/react": "18.2.18", - "@types/react-dom": "^18.2.7", - "@types/testing-library__jest-dom": "^5.14.9", - "@vitejs/plugin-basic-ssl": "^1.0.1", - "@vitejs/plugin-react": "^1.3.2", - "@votingworks/monorepo-utils": "workspace:*", - "concurrently": "7.6.0", - "eslint": "8.51.0", - "eslint-config-airbnb": "^19.0.4", - "eslint-config-prettier": "^9.0.0", - "eslint-config-react-app": "^7.0.1", - "eslint-import-resolver-node": "^0.3.9", - "eslint-plugin-import": "^2.26.0", - "eslint-plugin-jest": "^27.2.3", - "eslint-plugin-jsx-a11y": "^6.6.1", - "eslint-plugin-prettier": "^5.0.0", - "eslint-plugin-react": "^7.31.8", - "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-testing-library": "^5.6.4", - "eslint-plugin-vx": "workspace:*", - "is-ci-cli": "2.2.0", - "jest": "^29.6.2", - "jest-environment-jsdom": "^29.6.2", - "jest-junit": "^16.0.0", - "jest-watch-typeahead": "^2.2.2", - "lint-staged": "11.0.0", - "ts-jest": "29.1.1", - "vite": "4.5.0" - }, - "packageManager": "pnpm@8.1.0", - "vx": { - "isBundled": true - } -} diff --git a/apps/cacvote-track/frontend/public/favicon.ico b/apps/cacvote-track/frontend/public/favicon.ico deleted file mode 120000 index cb3ccfe6b7..0000000000 --- a/apps/cacvote-track/frontend/public/favicon.ico +++ /dev/null @@ -1 +0,0 @@ -../../../../libs/ui/.storybook-static/favicon.ico \ No newline at end of file diff --git a/apps/cacvote-track/frontend/public/votingworks-wordmark-black.svg b/apps/cacvote-track/frontend/public/votingworks-wordmark-black.svg deleted file mode 120000 index d0df39a5e3..0000000000 --- a/apps/cacvote-track/frontend/public/votingworks-wordmark-black.svg +++ /dev/null @@ -1 +0,0 @@ -../../../../libs/ui/src/images/votingworks-wordmark-black.svg \ No newline at end of file diff --git a/apps/cacvote-track/frontend/src/app.tsx b/apps/cacvote-track/frontend/src/app.tsx deleted file mode 100644 index d72d5a9c37..0000000000 --- a/apps/cacvote-track/frontend/src/app.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { ColorMode, ScreenType, SizeMode } from '@votingworks/types'; -import { AppBase, ErrorBoundary, H3, Main, P, Screen } from '@votingworks/ui'; -import { AppRoot } from './app_root'; - -const DEFAULT_COLOR_MODE: ColorMode = 'contrastMedium'; -const DEFAULT_SCREEN_TYPE: ScreenType = 'lenovoThinkpad15'; -const DEFAULT_SIZE_MODE: SizeMode = 'touchSmall'; - -export function App(): JSX.Element { - return ( - - -
-

-

Something went wrong

-

-
- - } - > - -
- VotingWorks logo - -
-
-
-
- ); -} diff --git a/apps/cacvote-track/frontend/src/app_root.tsx b/apps/cacvote-track/frontend/src/app_root.tsx deleted file mode 100644 index f5df74f952..0000000000 --- a/apps/cacvote-track/frontend/src/app_root.tsx +++ /dev/null @@ -1,120 +0,0 @@ -import { throwIllegalValue } from '@votingworks/basics'; -import { Button, H4, P } from '@votingworks/ui'; -import jsQR from 'jsqr'; -import React, { useEffect, useRef, useState } from 'react'; -import { useRequestAnimationFrame } from './hooks/use_request_animation_frame'; -import { useVideoDevice } from './hooks/use_video_device'; - -const EXPECTED_QR_CODE_DATA = "'Twas brillig"; - -type Stage = 'init' | 'scanning' | 'scan-success' | 'prompt-for-phone' | 'done'; - -const VIDEO_DEVICE_CONSTRAINTS: MediaStreamConstraints = { - video: { facingMode: 'environment' }, -}; - -export function AppRoot(): JSX.Element { - const canvasRef = useRef(null); - const [stage, setStage] = useState('init'); - const videoDevice = useVideoDevice({ - enabled: stage === 'scanning', - constraints: VIDEO_DEVICE_CONSTRAINTS, - }); - - useRequestAnimationFrame( - () => { - const canvas = canvasRef.current; - - if (videoDevice && canvas) { - const imageData = videoDevice.getCurrentFrame(canvas); - const code = jsQR(imageData.data, imageData.width, imageData.height, { - inversionAttempts: 'dontInvert', - }); - - if (code?.data === EXPECTED_QR_CODE_DATA) { - setStage('scan-success'); - } - } - }, - typeof videoDevice !== 'undefined' - ); - - useEffect(() => { - if (stage === 'scan-success') { - const timeout = setTimeout(() => setStage('prompt-for-phone'), 1500); - return () => clearTimeout(timeout); - } - }, [stage]); - - switch (stage) { - case 'init': - return ( - -

Track Your Ballot

-

- Scan your mailing label to see when your ballot is received and - counted. -

- -
- ); - - case 'scanning': - return ( - -

Scan QR Code

-

- -

-

- Aim your camera at the QR code on your mailing label. -

-

- -

-
- ); - - case 'scan-success': - return

✅ Ballot Tracker Scanned

; - - case 'prompt-for-phone': - return ( - -

Get Text Updates

-

- Enter your phone number to receive text updates when your ballot is - received and counted: -

-

- -

-

- -

-
- ); - - case 'done': - return ( - -

Thank You

-

- You will receive a text message when your ballot is received and - counted. -

-

You may now close this window.

-
- ); - - default: - throwIllegalValue(stage); - } -} diff --git a/apps/cacvote-track/frontend/src/hooks/use_request_animation_frame.test.ts b/apps/cacvote-track/frontend/src/hooks/use_request_animation_frame.test.ts deleted file mode 100644 index 2f8f043e3a..0000000000 --- a/apps/cacvote-track/frontend/src/hooks/use_request_animation_frame.test.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { renderHook } from '@testing-library/react'; -import { sleep } from '@votingworks/basics'; -import { useRequestAnimationFrame } from './use_request_animation_frame'; - -test('does not call the callback if enabled=false', async () => { - const callback = jest.fn(); - renderHook(() => useRequestAnimationFrame(callback, false)); - await sleep(100); - expect(callback).not.toHaveBeenCalled(); -}); - -test('calls the callback if enabled=true', async () => { - const callback = jest.fn(); - renderHook(() => useRequestAnimationFrame(callback, true)); - await sleep(100); - expect(callback).toHaveBeenCalled(); -}); - -test('stops calling the callback if enabled=false', async () => { - const callback = jest.fn(); - const { rerender } = renderHook( - ({ enabled }) => useRequestAnimationFrame(callback, enabled), - { initialProps: { enabled: true } } - ); - await sleep(100); - rerender({ enabled: false }); - callback.mockClear(); - await sleep(100); - expect(callback).not.toHaveBeenCalled(); -}); diff --git a/apps/cacvote-track/frontend/src/hooks/use_request_animation_frame.ts b/apps/cacvote-track/frontend/src/hooks/use_request_animation_frame.ts deleted file mode 100644 index 634ce70e82..0000000000 --- a/apps/cacvote-track/frontend/src/hooks/use_request_animation_frame.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { useEffect } from 'react'; - -export function useRequestAnimationFrame( - callback: () => void, - enabled = true -): void { - useEffect(() => { - if (!enabled) { - return; - } - - let animationFrameId: number | undefined; - - function tick() { - callback(); - animationFrameId = requestAnimationFrame(tick); - } - - animationFrameId = requestAnimationFrame(tick); - - return () => { - if (animationFrameId !== undefined) { - cancelAnimationFrame(animationFrameId); - } - }; - }, [callback, enabled]); -} diff --git a/apps/cacvote-track/frontend/src/hooks/use_video_device.ts b/apps/cacvote-track/frontend/src/hooks/use_video_device.ts deleted file mode 100644 index 705cfb3d05..0000000000 --- a/apps/cacvote-track/frontend/src/hooks/use_video_device.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { useCallback, useEffect, useMemo, useState } from 'react'; - -export interface UseVideoDeviceProps { - enabled?: boolean; - constraints: MediaStreamConstraints; -} - -export interface VideoDevice { - camera: MediaStream; - width: number; - height: number; - getCurrentFrame(canvas: HTMLCanvasElement): ImageData; -} - -export function useVideoDevice({ - enabled, - constraints, -}: UseVideoDeviceProps): VideoDevice | undefined { - const video = useMemo(() => document.createElement('video'), []); - const [camera, setCamera] = useState(); - const [width, setWidth] = useState(); - const [height, setHeight] = useState(); - - const updateSize = useCallback(() => { - setWidth(video.videoWidth); - setHeight(video.videoHeight); - }, [video]); - - const setup = useCallback(async () => { - const stream = await navigator.mediaDevices.getUserMedia(constraints); - video.srcObject = stream; - // tell iOS Safari we don't want fullscreen - video.setAttribute('playsinline', 'true'); - await video.play(); - video.addEventListener('loadedmetadata', updateSize); - setCamera(stream); - }, [constraints, updateSize, video]); - - const teardown = useCallback(() => { - const stream = video.srcObject as MediaStream | undefined; - for (const track of stream?.getTracks() ?? []) { - track.stop(); - } - video.srcObject = null; - video.removeEventListener('loadedmetadata', updateSize); - setCamera(undefined); - setWidth(undefined); - setHeight(undefined); - }, [updateSize, video]); - - useEffect(() => { - if (enabled) { - void setup(); - } else { - teardown(); - } - - return teardown; - }, [enabled, setup, teardown]); - - return useMemo( - () => - !enabled || - typeof camera === 'undefined' || - typeof width === 'undefined' || - typeof height === 'undefined' || - video.readyState !== video.HAVE_ENOUGH_DATA - ? undefined - : { - camera, - width, - height, - getCurrentFrame(canvas: HTMLCanvasElement) { - /* eslint-disable no-param-reassign */ - canvas.width = width; - canvas.height = height; - /* eslint-enable no-param-reassign */ - const context = canvas.getContext('2d'); - if (context) { - context.drawImage(video, 0, 0, width, height); - return context.getImageData(0, 0, width, height); - } - throw new Error('Could not get canvas context'); - }, - }, - [enabled, camera, width, height, video] - ); -} diff --git a/apps/cacvote-track/frontend/src/index.tsx b/apps/cacvote-track/frontend/src/index.tsx deleted file mode 100644 index 5250b114f1..0000000000 --- a/apps/cacvote-track/frontend/src/index.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import './polyfills'; -import { createRoot } from 'react-dom/client'; -import { App } from './app'; - -createRoot(document.getElementById('root') as HTMLElement).render(); diff --git a/apps/cacvote-track/frontend/src/polyfills.ts b/apps/cacvote-track/frontend/src/polyfills.ts deleted file mode 100644 index 7d89d86d3b..0000000000 --- a/apps/cacvote-track/frontend/src/polyfills.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Provides polyfills needed for this application and its dependencies. - */ - -/* istanbul ignore file */ -import { Buffer } from 'buffer'; - -globalThis.global = globalThis; -globalThis.Buffer = Buffer; diff --git a/apps/cacvote-track/frontend/src/stubs/fs.ts b/apps/cacvote-track/frontend/src/stubs/fs.ts deleted file mode 100644 index e43dbcf9f1..0000000000 --- a/apps/cacvote-track/frontend/src/stubs/fs.ts +++ /dev/null @@ -1,299 +0,0 @@ -/** Stub for 'fs' module, generated by script/build-stubs. DO NOT EDIT */ -/* eslint-disable */ -/* istanbul ignore file */ - -function __stubFnDoNotCall(): never { - throw new Error('this function is a stub and should never be called'); -} -/** Stub for appendFile */ -export const appendFile = __stubFnDoNotCall; -/** Stub for appendFileSync */ -export const appendFileSync = __stubFnDoNotCall; -/** Stub for access */ -export const access = __stubFnDoNotCall; -/** Stub for accessSync */ -export const accessSync = __stubFnDoNotCall; -/** Stub for chown */ -export const chown = __stubFnDoNotCall; -/** Stub for chownSync */ -export const chownSync = __stubFnDoNotCall; -/** Stub for chmod */ -export const chmod = __stubFnDoNotCall; -/** Stub for chmodSync */ -export const chmodSync = __stubFnDoNotCall; -/** Stub for close */ -export const close = __stubFnDoNotCall; -/** Stub for closeSync */ -export const closeSync = __stubFnDoNotCall; -/** Stub for copyFile */ -export const copyFile = __stubFnDoNotCall; -/** Stub for copyFileSync */ -export const copyFileSync = __stubFnDoNotCall; -/** Stub for cp */ -export const cp = __stubFnDoNotCall; -/** Stub for cpSync */ -export const cpSync = __stubFnDoNotCall; -/** Stub for createReadStream */ -export const createReadStream = __stubFnDoNotCall; -/** Stub for createWriteStream */ -export const createWriteStream = __stubFnDoNotCall; -/** Stub for exists */ -export const exists = __stubFnDoNotCall; -/** Stub for existsSync */ -export const existsSync = __stubFnDoNotCall; -/** Stub for fchown */ -export const fchown = __stubFnDoNotCall; -/** Stub for fchownSync */ -export const fchownSync = __stubFnDoNotCall; -/** Stub for fchmod */ -export const fchmod = __stubFnDoNotCall; -/** Stub for fchmodSync */ -export const fchmodSync = __stubFnDoNotCall; -/** Stub for fdatasync */ -export const fdatasync = __stubFnDoNotCall; -/** Stub for fdatasyncSync */ -export const fdatasyncSync = __stubFnDoNotCall; -/** Stub for fstat */ -export const fstat = __stubFnDoNotCall; -/** Stub for fstatSync */ -export const fstatSync = __stubFnDoNotCall; -/** Stub for fsync */ -export const fsync = __stubFnDoNotCall; -/** Stub for fsyncSync */ -export const fsyncSync = __stubFnDoNotCall; -/** Stub for ftruncate */ -export const ftruncate = __stubFnDoNotCall; -/** Stub for ftruncateSync */ -export const ftruncateSync = __stubFnDoNotCall; -/** Stub for futimes */ -export const futimes = __stubFnDoNotCall; -/** Stub for futimesSync */ -export const futimesSync = __stubFnDoNotCall; -/** Stub for lchown */ -export const lchown = __stubFnDoNotCall; -/** Stub for lchownSync */ -export const lchownSync = __stubFnDoNotCall; -/** Stub for lchmod */ -export const lchmod = undefined; -/** Stub for lchmodSync */ -export const lchmodSync = undefined; -/** Stub for link */ -export const link = __stubFnDoNotCall; -/** Stub for linkSync */ -export const linkSync = __stubFnDoNotCall; -/** Stub for lstat */ -export const lstat = __stubFnDoNotCall; -/** Stub for lstatSync */ -export const lstatSync = __stubFnDoNotCall; -/** Stub for lutimes */ -export const lutimes = __stubFnDoNotCall; -/** Stub for lutimesSync */ -export const lutimesSync = __stubFnDoNotCall; -/** Stub for mkdir */ -export const mkdir = __stubFnDoNotCall; -/** Stub for mkdirSync */ -export const mkdirSync = __stubFnDoNotCall; -/** Stub for mkdtemp */ -export const mkdtemp = __stubFnDoNotCall; -/** Stub for mkdtempSync */ -export const mkdtempSync = __stubFnDoNotCall; -/** Stub for open */ -export const open = __stubFnDoNotCall; -/** Stub for openSync */ -export const openSync = __stubFnDoNotCall; -/** Stub for opendir */ -export const opendir = __stubFnDoNotCall; -/** Stub for opendirSync */ -export const opendirSync = __stubFnDoNotCall; -/** Stub for readdir */ -export const readdir = __stubFnDoNotCall; -/** Stub for readdirSync */ -export const readdirSync = __stubFnDoNotCall; -/** Stub for read */ -export const read = __stubFnDoNotCall; -/** Stub for readSync */ -export const readSync = __stubFnDoNotCall; -/** Stub for readv */ -export const readv = __stubFnDoNotCall; -/** Stub for readvSync */ -export const readvSync = __stubFnDoNotCall; -/** Stub for readFile */ -export const readFile = __stubFnDoNotCall; -/** Stub for readFileSync */ -export const readFileSync = __stubFnDoNotCall; -/** Stub for readlink */ -export const readlink = __stubFnDoNotCall; -/** Stub for readlinkSync */ -export const readlinkSync = __stubFnDoNotCall; -/** Stub for realpath */ -export const realpath = __stubFnDoNotCall; -/** Stub for realpathSync */ -export const realpathSync = __stubFnDoNotCall; -/** Stub for rename */ -export const rename = __stubFnDoNotCall; -/** Stub for renameSync */ -export const renameSync = __stubFnDoNotCall; -/** Stub for rm */ -export const rm = __stubFnDoNotCall; -/** Stub for rmSync */ -export const rmSync = __stubFnDoNotCall; -/** Stub for rmdir */ -export const rmdir = __stubFnDoNotCall; -/** Stub for rmdirSync */ -export const rmdirSync = __stubFnDoNotCall; -/** Stub for stat */ -export const stat = __stubFnDoNotCall; -/** Stub for statSync */ -export const statSync = __stubFnDoNotCall; -/** Stub for symlink */ -export const symlink = __stubFnDoNotCall; -/** Stub for symlinkSync */ -export const symlinkSync = __stubFnDoNotCall; -/** Stub for truncate */ -export const truncate = __stubFnDoNotCall; -/** Stub for truncateSync */ -export const truncateSync = __stubFnDoNotCall; -/** Stub for unwatchFile */ -export const unwatchFile = __stubFnDoNotCall; -/** Stub for unlink */ -export const unlink = __stubFnDoNotCall; -/** Stub for unlinkSync */ -export const unlinkSync = __stubFnDoNotCall; -/** Stub for utimes */ -export const utimes = __stubFnDoNotCall; -/** Stub for utimesSync */ -export const utimesSync = __stubFnDoNotCall; -/** Stub for watch */ -export const watch = __stubFnDoNotCall; -/** Stub for watchFile */ -export const watchFile = __stubFnDoNotCall; -/** Stub for writeFile */ -export const writeFile = __stubFnDoNotCall; -/** Stub for writeFileSync */ -export const writeFileSync = __stubFnDoNotCall; -/** Stub for write */ -export const write = __stubFnDoNotCall; -/** Stub for writeSync */ -export const writeSync = __stubFnDoNotCall; -/** Stub for writev */ -export const writev = __stubFnDoNotCall; -/** Stub for writevSync */ -export const writevSync = __stubFnDoNotCall; -/** Stub for Dir */ -export const Dir = __stubFnDoNotCall; -/** Stub for Dirent */ -export const Dirent = __stubFnDoNotCall; -/** Stub for Stats */ -export const Stats = __stubFnDoNotCall; -/** Stub for ReadStream */ -export const ReadStream = __stubFnDoNotCall; -/** Stub for WriteStream */ -export const WriteStream = __stubFnDoNotCall; -/** Stub for FileReadStream */ -export const FileReadStream = __stubFnDoNotCall; -/** Stub for FileWriteStream */ -export const FileWriteStream = __stubFnDoNotCall; -/** Stub for _toUnixTimestamp */ -export const _toUnixTimestamp = __stubFnDoNotCall; -/** Stub for F_OK */ -export const F_OK = 0; -/** Stub for R_OK */ -export const R_OK = 4; -/** Stub for W_OK */ -export const W_OK = 2; -/** Stub for X_OK */ -export const X_OK = 1; -/** Stub for constants */ -export const constants = { - UV_FS_SYMLINK_DIR: 1, - UV_FS_SYMLINK_JUNCTION: 2, - O_RDONLY: 0, - O_WRONLY: 1, - O_RDWR: 2, - UV_DIRENT_UNKNOWN: 0, - UV_DIRENT_FILE: 1, - UV_DIRENT_DIR: 2, - UV_DIRENT_LINK: 3, - UV_DIRENT_FIFO: 4, - UV_DIRENT_SOCKET: 5, - UV_DIRENT_CHAR: 6, - UV_DIRENT_BLOCK: 7, - S_IFMT: 61440, - S_IFREG: 32768, - S_IFDIR: 16384, - S_IFCHR: 8192, - S_IFBLK: 24576, - S_IFIFO: 4096, - S_IFLNK: 40960, - S_IFSOCK: 49152, - O_CREAT: 64, - O_EXCL: 128, - UV_FS_O_FILEMAP: 0, - O_NOCTTY: 256, - O_TRUNC: 512, - O_APPEND: 1024, - O_DIRECTORY: 16384, - O_NOATIME: 262144, - O_NOFOLLOW: 32768, - O_SYNC: 1052672, - O_DSYNC: 4096, - O_DIRECT: 65536, - O_NONBLOCK: 2048, - S_IRWXU: 448, - S_IRUSR: 256, - S_IWUSR: 128, - S_IXUSR: 64, - S_IRWXG: 56, - S_IRGRP: 32, - S_IWGRP: 16, - S_IXGRP: 8, - S_IRWXO: 7, - S_IROTH: 4, - S_IWOTH: 2, - S_IXOTH: 1, - F_OK: 0, - R_OK: 4, - W_OK: 2, - X_OK: 1, - UV_FS_COPYFILE_EXCL: 1, - COPYFILE_EXCL: 1, - UV_FS_COPYFILE_FICLONE: 2, - COPYFILE_FICLONE: 2, - UV_FS_COPYFILE_FICLONE_FORCE: 4, - COPYFILE_FICLONE_FORCE: 4, -}; -/** Stub for promises */ -export const promises = { - access: __stubFnDoNotCall, - copyFile: __stubFnDoNotCall, - cp: __stubFnDoNotCall, - open: __stubFnDoNotCall, - opendir: __stubFnDoNotCall, - rename: __stubFnDoNotCall, - truncate: __stubFnDoNotCall, - rm: __stubFnDoNotCall, - rmdir: __stubFnDoNotCall, - mkdir: __stubFnDoNotCall, - readdir: __stubFnDoNotCall, - readlink: __stubFnDoNotCall, - symlink: __stubFnDoNotCall, - lstat: __stubFnDoNotCall, - stat: __stubFnDoNotCall, - link: __stubFnDoNotCall, - unlink: __stubFnDoNotCall, - chmod: __stubFnDoNotCall, - lchmod: __stubFnDoNotCall, - lchown: __stubFnDoNotCall, - chown: __stubFnDoNotCall, - utimes: __stubFnDoNotCall, - lutimes: __stubFnDoNotCall, - realpath: __stubFnDoNotCall, - mkdtemp: __stubFnDoNotCall, - writeFile: __stubFnDoNotCall, - appendFile: __stubFnDoNotCall, - readFile: __stubFnDoNotCall, - watch: __stubFnDoNotCall, - get constants() { - return constants; - }, -}; diff --git a/apps/cacvote-track/frontend/tsconfig.json b/apps/cacvote-track/frontend/tsconfig.json deleted file mode 100644 index ebf3a6ed30..0000000000 --- a/apps/cacvote-track/frontend/tsconfig.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "extends": "../../../tsconfig.shared.json", - "include": ["src", "test"], - "exclude": ["src/setupProxy.js"], - "compilerOptions": { - "allowJs": true, - "allowSyntheticDefaultImports": true, - "esModuleInterop": true, - "jsx": "react-jsx", - "lib": ["dom", "dom.iterable", "esnext"], - "module": "esnext", - "moduleResolution": "node", - "noEmit": true, - "resolveJsonModule": true, - "skipLibCheck": true, - "noFallthroughCasesInSwitch": true, - "noUncheckedIndexedAccess": false - }, - "references": [ - { "path": "../../../libs/eslint-plugin-vx/tsconfig.build.json" }, - { "path": "../../../libs/monorepo-utils/tsconfig.build.json" }, - { "path": "../../../libs/types/tsconfig.build.json" }, - { "path": "../../../libs/ui/tsconfig.build.json" } - ] -} diff --git a/apps/cacvote-track/frontend/vite.config.ts b/apps/cacvote-track/frontend/vite.config.ts deleted file mode 100644 index de8cb27f88..0000000000 --- a/apps/cacvote-track/frontend/vite.config.ts +++ /dev/null @@ -1,92 +0,0 @@ -import basicSsl from '@vitejs/plugin-basic-ssl'; -import react from '@vitejs/plugin-react'; -import { join } from 'path'; -import { Alias, defineConfig, loadEnv } from 'vite'; -import { getWorkspacePackageInfo } from '@votingworks/monorepo-utils'; - -export default defineConfig((env) => { - const workspacePackages = getWorkspacePackageInfo(join(__dirname, '../..')); - - const envPrefix = 'REACT_APP_'; - const rootDotenvValues = loadEnv( - env.mode, - join(__dirname, '../../..'), - envPrefix - ); - const coreDotenvValues = loadEnv(env.mode, __dirname, envPrefix); - const processEnvDefines = [ - ...Object.entries(rootDotenvValues), - ...Object.entries(coreDotenvValues), - ['IS_INTEGRATION_TEST', process.env.IS_INTEGRATION_TEST || 'false'], - ].reduce>( - (acc, [key, value]) => ({ - ...acc, - [`process.env.${key}`]: JSON.stringify(value), - }), - {} - ); - - const basePort = Number(process.env.BASE_PORT) || 3000; - const port = Number(process.env.PORT) || basePort; - - return { - server: { - port, - }, - - build: { - // Write build files to `build` directory. - outDir: 'build', - - // Do not minify build files. We don't need the space savings and this is - // a minor transparency improvement. - minify: false, - }, - - // Replace some code in Node modules, `#define`-style, to avoid referencing - // Node-only globals like `process`. - define: { - 'process.env.NODE_DEBUG': 'undefined', - 'process.platform': JSON.stringify('browser'), - 'process.version': JSON.stringify(process.version), - - // TODO: Replace these with the appropriate `import.meta.env` values. - ...processEnvDefines, - }, - - resolve: { - alias: [ - // Replace NodeJS built-in modules with polyfills. - // - // The trailing slash is important, otherwise it will be resolved as a - // built-in NodeJS module. - { find: 'buffer', replacement: require.resolve('buffer/') }, - { find: 'fs', replacement: join(__dirname, './src/stubs/fs.ts') }, - { find: 'path', replacement: require.resolve('path/') }, - - // Create aliases for all workspace packages, i.e. - // - // { - // '@votingworks/types': '…/libs/types/src/index.ts', - // '@votingworks/utils': '…/libs/utils/src/index.ts', - // … - // } - // - // This allows re-mapping imports for workspace packages to their - // TypeScript source code rather than the built JavaScript. - ...Array.from(workspacePackages.values()).reduce( - (aliases, { path, name, source }) => - !source - ? aliases - : [...aliases, { find: name, replacement: join(path, source) }], - [] - ), - ], - }, - - plugins: [react(), basicSsl()], - - // Pass some environment variables to the client in `import.meta.env`. - envPrefix, - }; -}); diff --git a/libs/ballot-encoder-rs/Cargo.toml b/libs/ballot-encoder-rs/Cargo.toml deleted file mode 100644 index 1e2cfb0f1e..0000000000 --- a/libs/ballot-encoder-rs/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "ballot-encoder-rs" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -bitstream-io = { workspace = true } -color-eyre = { workspace = true } -hex = { workspace = true } -nanoid = { workspace = true } -types-rs = { workspace = true } - -[dev-dependencies] -pretty_assertions = { workspace = true } diff --git a/libs/ballot-encoder-rs/package.json b/libs/ballot-encoder-rs/package.json deleted file mode 100644 index 69b6988f49..0000000000 --- a/libs/ballot-encoder-rs/package.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "ballot-encoder-rs", - "version": "1.0.0", - "description": "", - "main": "index.js", - "scripts": { - "build": "cargo build", - "lint": "cargo clippy -- -D warnings", - "start": "cargo watch -x run", - "test": "cargo test" - }, - "keywords": [], - "author": "VotingWorks Eng ", - "license": "GPL-3.0", - "packageManager": "pnpm@8.1.0" -} diff --git a/libs/ballot-encoder-rs/src/consts.rs b/libs/ballot-encoder-rs/src/consts.rs deleted file mode 100644 index 89d9c6b5b8..0000000000 --- a/libs/ballot-encoder-rs/src/consts.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub(crate) const ENCODING_VERSION: u8 = 2; -pub(crate) const ELECTION_HASH_HEX_LENGTH: usize = 20; -pub(crate) const ELECTION_HASH_BYTE_LENGTH: usize = ELECTION_HASH_HEX_LENGTH / 2; -pub(crate) const MAXIMUM_WRITE_IN_NAME_LENGTH: usize = 40; -pub(crate) const BITS_PER_WRITE_IN_CHAR: u32 = 5; -pub(crate) const WRITE_IN_CHARS: &str = r#"ABCDEFGHIJKLMNOPQRSTUVWXYZ '"-.,"#; diff --git a/libs/ballot-encoder-rs/src/decode.rs b/libs/ballot-encoder-rs/src/decode.rs deleted file mode 100644 index 7b1dfd44c9..0000000000 --- a/libs/ballot-encoder-rs/src/decode.rs +++ /dev/null @@ -1,385 +0,0 @@ -use crate::{ - consts::{ - BITS_PER_WRITE_IN_CHAR, ELECTION_HASH_BYTE_LENGTH, ENCODING_VERSION, - MAXIMUM_WRITE_IN_NAME_LENGTH, WRITE_IN_CHARS, - }, - types::{BallotConfig, EncodableCvr, TestMode}, - util::sizeof, -}; -use bitstream_io::{BigEndian, BitRead, BitReader, Endianness}; -use std::io::Read; -use types_rs::{ - cdf::cvr::{ - CVRContest, CVRContestSelection, CVRSnapshot, CVRWriteIn, Cvr, IndicationStatus, - SelectionPosition, VxBallotType, - }, - election::{BallotStyleId, Contest, Election, PartialElectionHash, PrecinctId}, -}; - -/// Decodes a CVR from data encoded in a BMD ballot card. -pub fn decode(election: &Election, data: &[u8]) -> std::io::Result { - let mut reader = bitstream_io::BitReader::endian(data, BigEndian); - let decoded = decode_from(election, &mut reader)?; - - while !reader.byte_aligned() { - if reader.read_bit()? { - return Err(std::io::Error::new( - std::io::ErrorKind::InvalidInput, - "unexpected non-zero padding bit", - )); - } - } - - let mut remainder = vec![]; - let read_length = reader.into_reader().read_to_end(&mut remainder)?; - - if read_length > 0 { - return Err(std::io::Error::new( - std::io::ErrorKind::InvalidInput, - "unexpected trailing data", - )); - } - - Ok(decoded) -} - -/// Decodes the encoded header from data encoded in a BMD ballot card. This -/// should be used to validate the version & election hash before decoding the -/// rest of the CVR. -pub fn decode_header(data: &[u8]) -> std::io::Result<(u8, PartialElectionHash)> { - let mut reader = bitstream_io::BitReader::endian(data, BigEndian); - let version = decode_prelude_from(&mut reader)?; - let election_hash = decode_partial_election_hash_from(&mut reader)?; - - Ok((version, election_hash)) -} - -/// Decodes a CVR by reading data encoded in a BMD ballot card. -pub fn decode_from( - election: &Election, - reader: &mut BitReader<&[u8], E>, -) -> std::io::Result { - let version = decode_prelude_from(reader)?; - - if version != ENCODING_VERSION { - return Err(std::io::Error::new( - std::io::ErrorKind::InvalidInput, - format!("unsupported version: {}", version), - )); - } - - let partial_election_hash = decode_partial_election_hash_from(reader)?; - let (precinct_count, ballot_style_count, contest_count) = decode_check_data_from(reader)?; - - if precinct_count as usize != election.precincts.len() { - return Err(std::io::Error::new( - std::io::ErrorKind::InvalidInput, - format!( - "precinct count mismatch: expected {}, but got {}", - election.precincts.len(), - precinct_count - ), - )); - } - - if ballot_style_count as usize != election.ballot_styles.len() { - return Err(std::io::Error::new( - std::io::ErrorKind::InvalidInput, - format!( - "ballot style count mismatch: expected {}, but got {}", - election.ballot_styles.len(), - ballot_style_count - ), - )); - } - - if contest_count as usize != election.contests.len() { - return Err(std::io::Error::new( - std::io::ErrorKind::InvalidInput, - format!( - "contest count mismatch: expected {}, but got {}", - election.contests.len(), - contest_count - ), - )); - } - - let ballot_config = decode_ballot_config_from(election, reader)?; - let ballot_style = election - .ballot_styles - .get(ballot_config.ballot_style_index as usize) - .ok_or_else(|| { - std::io::Error::new( - std::io::ErrorKind::InvalidInput, - format!( - "cannot find ballot_style with index={}", - ballot_config.ballot_style_index - ), - ) - })?; - let precinct = election - .precincts - .get(ballot_config.precinct_index as usize) - .ok_or_else(|| { - std::io::Error::new( - std::io::ErrorKind::InvalidInput, - format!( - "cannot find precinct with index={}", - ballot_config.precinct_index - ), - ) - })?; - let contests = election - .get_contests(ballot_style.id.clone()) - .ok_or_else(|| { - std::io::Error::new( - std::io::ErrorKind::InvalidInput, - format!( - "cannot find contests for ballot_style_id={}", - ballot_style.id - ), - ) - })?; - let ballot_votes = decode_ballot_votes_from(contests, &ballot_style.id, &precinct.id, reader)?; - - Ok(EncodableCvr::new( - partial_election_hash, - ballot_votes, - ballot_config.ballot_mode, - )) -} - -pub fn decode_prelude_from(reader: &mut BitReader<&[u8], E>) -> std::io::Result { - let mut tag = [0u8; 2]; - reader.read_bytes(&mut tag)?; - if &tag != b"VX" { - return Err(std::io::Error::new( - std::io::ErrorKind::InvalidInput, - format!("invalid tag: {:?}", tag), - )); - } - - let version = reader.read(8)?; - - Ok(version) -} - -pub fn decode_partial_election_hash_from( - reader: &mut BitReader<&[u8], E>, -) -> std::io::Result { - let mut election_hash_bytes = [0u8; ELECTION_HASH_BYTE_LENGTH]; - reader.read_bytes(&mut election_hash_bytes)?; - - election_hash_bytes.try_into().map_err(|err| { - std::io::Error::new( - std::io::ErrorKind::InvalidInput, - format!("invalid election hash: {}", err), - ) - }) -} - -pub fn decode_check_data_from( - reader: &mut BitReader<&[u8], E>, -) -> std::io::Result<(u8, u8, u8)> { - let precinct_count: u8 = reader.read(8)?; - let ballot_style_count: u8 = reader.read(8)?; - let contest_count: u8 = reader.read(8)?; - - Ok((precinct_count, ballot_style_count, contest_count)) -} - -pub fn decode_ballot_config_from( - election: &Election, - reader: &mut BitReader<&[u8], E>, -) -> std::io::Result { - let precinct_index = reader.read::(sizeof(election.precincts.len() - 1))?; - let ballot_style_index = reader.read::(sizeof(election.ballot_styles.len() - 1))?; - let ballot_mode = if reader.read_bit()? { - TestMode::Test - } else { - TestMode::Live - }; - let ballot_type: VxBallotType = reader - .read::(sizeof(VxBallotType::max() as usize))? - .into(); - let ballot_id_present = reader.read_bit()?; - let ballot_id = if ballot_id_present { - let ballot_id_length = reader.read::(8)?; - let mut ballot_id = vec![0u8; ballot_id_length as usize]; - reader.read_bytes(&mut ballot_id)?; - Some(String::from_utf8(ballot_id).map_err(|_| { - std::io::Error::new( - std::io::ErrorKind::InvalidInput, - "ballot_id is not a valid UTF-8 string", - ) - })?) - } else { - None - }; - - Ok(BallotConfig { - precinct_index, - ballot_style_index, - ballot_type, - ballot_mode, - ballot_id, - }) -} - -pub fn decode_ballot_votes_from( - contests: Vec<&Contest>, - ballot_style_id: &BallotStyleId, - precinct_id: &PrecinctId, - reader: &mut BitReader<&[u8], E>, -) -> std::io::Result { - let mut contests_with_votes = vec![]; - - for contest in contests.iter() { - if reader.read_bit()? { - contests_with_votes.push(contest); - } - } - - let mut cvr_contests = vec![]; - - for contest in contests.iter() { - let contest_has_votes = contests_with_votes.contains(&contest); - - let cvr_contest_selections = match contest { - Contest::YesNo(_) => { - vec![ - CVRContestSelection { - contest_selection_id: Some("yes".to_string()), - option_position: Some(0), - selection_position: vec![SelectionPosition { - has_indication: if contest_has_votes && reader.read_bit()? { - IndicationStatus::Yes - } else { - IndicationStatus::No - }, - ..Default::default() - }], - ..Default::default() - }, - CVRContestSelection { - contest_selection_id: Some("no".to_string()), - option_position: Some(1), - selection_position: vec![SelectionPosition { - has_indication: if contest_has_votes && reader.read_bit()? { - IndicationStatus::Yes - } else { - IndicationStatus::No - }, - ..Default::default() - }], - ..Default::default() - }, - ] - } - Contest::Candidate(candidate_contest) => { - let mut cvr_contest_selections = vec![]; - let mut selected_candidate_count = 0usize; - - for (index, candidate) in candidate_contest.candidates.iter().enumerate() { - let has_vote = contest_has_votes && reader.read_bit()?; - if has_vote { - selected_candidate_count += 1; - } - cvr_contest_selections.push(CVRContestSelection { - contest_selection_id: Some(candidate.id.to_string()), - option_position: Some(index as i64), - selection_position: vec![SelectionPosition { - has_indication: if has_vote { - IndicationStatus::Yes - } else { - IndicationStatus::No - }, - ..Default::default() - }], - ..Default::default() - }); - } - - if contest_has_votes && candidate_contest.allow_write_ins { - let maximum_possible_write_ins = (candidate_contest.seats as usize - - selected_candidate_count) - .clamp(0, candidate_contest.candidates.len()); - - if maximum_possible_write_ins > 0 { - let write_in_count = - reader.read::(sizeof(maximum_possible_write_ins))?; - - for _ in 0..write_in_count { - let write_in_name = decode_write_in_name_from(reader)?; - cvr_contest_selections.push(CVRContestSelection { - contest_selection_id: None, - option_position: None, - selection_position: vec![SelectionPosition { - has_indication: IndicationStatus::Yes, - cvr_write_in: Some(CVRWriteIn { - text: Some(write_in_name), - ..Default::default() - }), - ..Default::default() - }], - ..Default::default() - }); - } - } - } - - cvr_contest_selections - } - }; - cvr_contests.push(CVRContest { - contest_id: contest.id().to_string(), - cvr_contest_selection: Some(cvr_contest_selections), - ..Default::default() - }); - } - - let cvr_snapshot = CVRSnapshot { - cvr_contest: Some(cvr_contests), - ..Default::default() - }; - - let cvr = Cvr { - ballot_style_id: Some(ballot_style_id.to_string()), - ballot_style_unit_id: Some(precinct_id.to_string()), - current_snapshot_id: cvr_snapshot.id.clone(), - cvr_snapshot: vec![cvr_snapshot], - ..Default::default() - }; - - Ok(cvr) -} - -pub fn decode_write_in_name_from( - reader: &mut BitReader<&[u8], E>, -) -> std::io::Result { - let write_in_name_length = reader.read::(sizeof(MAXIMUM_WRITE_IN_NAME_LENGTH))?; - - if write_in_name_length > MAXIMUM_WRITE_IN_NAME_LENGTH as u32 { - return Err(std::io::Error::new( - std::io::ErrorKind::InvalidInput, - format!("write-in name length is too long: {}", write_in_name_length), - )); - } - - let mut write_in_name = String::with_capacity(write_in_name_length as usize); - - for _ in 0..write_in_name_length { - let index = reader.read::(BITS_PER_WRITE_IN_CHAR)?; - let char = WRITE_IN_CHARS - .get(index as usize..=index as usize) - .ok_or_else(|| { - std::io::Error::new( - std::io::ErrorKind::InvalidInput, - format!("invalid write-in name char index: {}", index), - ) - })?; - write_in_name.push_str(char); - } - - Ok(write_in_name) -} diff --git a/libs/ballot-encoder-rs/src/encode.rs b/libs/ballot-encoder-rs/src/encode.rs deleted file mode 100644 index e4f7d8c5c0..0000000000 --- a/libs/ballot-encoder-rs/src/encode.rs +++ /dev/null @@ -1,385 +0,0 @@ -use crate::{ - consts::{ - BITS_PER_WRITE_IN_CHAR, ENCODING_VERSION, MAXIMUM_WRITE_IN_NAME_LENGTH, WRITE_IN_CHARS, - }, - types::{EncodableCvr, TestMode}, - util::sizeof, -}; -use bitstream_io::{BigEndian, BitWrite, BitWriter, Endianness}; -use types_rs::{ - cdf::cvr::{CVRContestSelection, Cvr, IndicationStatus, VxBallotType}, - election::{ - BallotStyleId, CandidateContest, Contest, Election, PartialElectionHash, PrecinctId, - YesNoContest, - }, -}; - -pub fn encode(election: &Election, encodable_cvr: &EncodableCvr) -> std::io::Result> { - let mut data = Vec::new(); - let mut writer = BitWriter::endian(&mut data, BigEndian); - - encode_into(election, encodable_cvr, &mut writer)?; - writer.byte_align()?; - - Ok(data) -} - -pub fn encode_into( - election: &Election, - encodable_cvr: &EncodableCvr, - writer: &mut BitWriter<&mut Vec, E>, -) -> std::io::Result<()> { - encode_prelude_into(writer)?; - - // TODO: validate votes - - let cvr = &encodable_cvr.cvr; - let test_mode = &encodable_cvr.test_mode; - let partial_election_hash = encodable_cvr.partial_election_hash.clone(); - - let precinct_id = PrecinctId::from(cvr.ballot_style_unit_id.clone().ok_or_else(|| { - std::io::Error::new( - std::io::ErrorKind::InvalidInput, - "ballot_style_unit_id is missing from CVR", - ) - })?); - let ballot_style_id = BallotStyleId::from(cvr.ballot_style_id.clone().ok_or_else(|| { - std::io::Error::new( - std::io::ErrorKind::InvalidInput, - "ballot_style_id is missing from CVR", - ) - })?); - let contests = election - .get_contests(ballot_style_id.clone()) - .ok_or_else(|| { - std::io::Error::new( - std::io::ErrorKind::InvalidInput, - format!("cannot find contests for ballot_style_id={ballot_style_id}"), - ) - })?; - - encode_election_hash_into(&partial_election_hash, writer)?; - encode_ballot_config_into( - election, - ballot_style_id, - precinct_id, - *test_mode, - cvr.vx_ballot_type.clone(), - cvr.unique_id.as_deref(), - writer, - )?; - encode_ballot_votes_into(contests, cvr, writer)?; - - Ok(()) -} - -pub fn encode_prelude_into( - writer: &mut BitWriter<&mut Vec, E>, -) -> std::io::Result<()> { - // write prelude - writer.write_bytes(b"VX")?; // tag - writer.write(8, ENCODING_VERSION)?; // version - - Ok(()) -} - -pub fn encode_election_hash_into( - partial_election_hash: &PartialElectionHash, - writer: &mut BitWriter<&mut Vec, E>, -) -> std::io::Result<()> { - writer.write_bytes(&partial_election_hash.to_bytes())?; - - Ok(()) -} - -pub fn encode_ballot_config_into( - election: &Election, - ballot_style_id: BallotStyleId, - precinct_id: PrecinctId, - test_mode: TestMode, - ballot_type: VxBallotType, - ballot_id: Option<&str>, - writer: &mut BitWriter<&mut Vec, E>, -) -> std::io::Result<()> { - let precinct_id = precinct_id.to_string(); - let ballot_style_id = ballot_style_id.to_string(); - let precinct_index = election - .precincts - .iter() - .position(|precinct| precinct.id.to_string() == precinct_id) - .ok_or_else(|| { - std::io::Error::new( - std::io::ErrorKind::InvalidInput, - format!("cannot find precinct with id={}", precinct_id), - ) - })?; - let ballot_style_index = election - .ballot_styles - .iter() - .position(|ballot_style| ballot_style.id.to_string() == ballot_style_id) - .ok_or_else(|| { - std::io::Error::new( - std::io::ErrorKind::InvalidInput, - format!("cannot find ballot_style with id={}", ballot_style_id), - ) - })?; - - writer.write(8, election.precincts.len() as u8)?; - writer.write(8, election.ballot_styles.len() as u8)?; - writer.write(8, election.contests.len() as u8)?; - writer.write(sizeof(election.precincts.len() - 1), precinct_index as u32)?; - writer.write( - sizeof(election.ballot_styles.len() - 1), - ballot_style_index as u32, - )?; - writer.write_bit(test_mode == TestMode::Test)?; - writer.write(sizeof(VxBallotType::max() as usize), u32::from(ballot_type))?; - if let Some(ballot_id) = ballot_id { - writer.write_bit(true)?; - writer.write(8, ballot_id.len() as u8)?; - writer.write_bytes(ballot_id.as_bytes())?; - } else { - writer.write_bit(false)?; - } - - Ok(()) -} - -pub fn encode_ballot_votes_into( - contests: Vec<&types_rs::election::Contest>, - cvr: &Cvr, - writer: &mut BitWriter<&mut Vec, E>, -) -> std::io::Result<()> { - let snapshot = cvr - .cvr_snapshot - .iter() - .find(|snapshot| snapshot.id == cvr.current_snapshot_id) - .ok_or_else(|| { - std::io::Error::new( - std::io::ErrorKind::InvalidInput, - format!( - "cannot find CVR snapshot with id={}", - cvr.current_snapshot_id - ), - ) - })?; - - let all_cvr_contests = snapshot.cvr_contest.as_ref().ok_or_else(|| { - std::io::Error::new( - std::io::ErrorKind::InvalidInput, - "cvr_contest is missing from CVR snapshot", - ) - })?; - - let all_cvr_contests_paired_with_contest_definition = contests - .iter() - .map(|contest| { - let contest_id = contest.id(); - let cvr_contest = all_cvr_contests - .iter() - .find(|cvr_contest| contest_id.to_string() == cvr_contest.contest_id) - .ok_or_else(|| { - std::io::Error::new( - std::io::ErrorKind::InvalidInput, - format!("cannot find contest with id={}", contest_id), - ) - })?; - Ok((cvr_contest, contest)) - }) - .collect::>>()?; - - let cvr_contests_with_votes = all_cvr_contests - .iter() - .filter(|cvr_contest| { - cvr_contest - .cvr_contest_selection - .as_ref() - .map(|cvr_contest_selection| { - cvr_contest_selection - .iter() - .flat_map(|selection| &selection.selection_position) - .any(|selection_position| { - selection_position.has_indication == IndicationStatus::Yes - }) - }) - .unwrap_or(false) - }) - .collect::>(); - - // write roll call - for (cvr_contest, _) in all_cvr_contests_paired_with_contest_definition.iter() { - let has_votes = cvr_contests_with_votes.contains(cvr_contest); - writer.write_bit(has_votes)?; - } - - for (cvr_contest, contest) in all_cvr_contests_paired_with_contest_definition.iter() { - if !cvr_contests_with_votes.contains(cvr_contest) { - continue; - } - - let selections = cvr_contest.cvr_contest_selection.as_ref().ok_or_else(|| { - std::io::Error::new( - std::io::ErrorKind::InvalidInput, - "cvr_contest_selection is missing from CVR contest", - ) - })?; - - match contest { - Contest::YesNo(yesno_contest) => { - encode_yesno_selections(yesno_contest, selections, writer)?; - } - Contest::Candidate(candidate_contest) => { - encode_candidate_selections(candidate_contest, selections, writer)?; - } - } - } - - Ok(()) -} - -fn encode_candidate_selections( - candidate_contest: &CandidateContest, - selections: &[CVRContestSelection], - writer: &mut BitWriter<&mut Vec, E>, -) -> std::io::Result<()> { - // candidate choices get one bit per candidate - let mut non_write_in_count = 0; - for candidate in candidate_contest.candidates.iter() { - let selection = selections - .iter() - .find(|selection| { - selection - .contest_selection_id - .as_ref() - .map(|contest_selection_id| contest_selection_id == &candidate.id.to_string()) - .unwrap_or(false) - }) - .ok_or_else(|| { - std::io::Error::new( - std::io::ErrorKind::InvalidInput, - format!( - "cannot find selection for candidate with id={}", - candidate.id - ), - ) - })?; - let is_selected = selection - .selection_position - .iter() - .any(|selection_position| selection_position.has_indication == IndicationStatus::Yes); - writer.write_bit(is_selected)?; - - if is_selected { - non_write_in_count += 1; - } - } - - if candidate_contest.allow_write_ins { - // write write-in data - let write_in_count = selections - .iter() - .filter(|selection| { - selection - .selection_position - .iter() - .any(|selection_position| { - selection_position.has_indication == IndicationStatus::Yes - && selection_position.cvr_write_in.is_some() - }) - }) - .count(); - let maximum_write_ins = (candidate_contest.seats - non_write_in_count).max(0); - - if maximum_write_ins > 0 { - writer.write(sizeof(maximum_write_ins as usize), write_in_count as u32)?; - - for selection in selections.iter() { - if selection - .selection_position - .iter() - .any(|selection_position| { - selection_position.has_indication == IndicationStatus::Yes - && selection_position.cvr_write_in.is_some() - }) - { - let write_in_name = selection - .selection_position - .iter() - .find_map(|selection_position| { - selection_position - .cvr_write_in - .as_ref() - .map(|cvr_write_in| cvr_write_in.text.as_ref()) - }) - .flatten() - .ok_or_else(|| { - std::io::Error::new( - std::io::ErrorKind::InvalidInput, - "write-in name is missing from selection", - ) - })?; - encode_write_in_name(write_in_name, writer)?; - } - } - } - } - - Ok(()) -} - -pub fn encode_write_in_name( - write_in_name: &str, - writer: &mut BitWriter<&mut Vec, E>, -) -> std::io::Result<()> { - if write_in_name.len() > MAXIMUM_WRITE_IN_NAME_LENGTH { - return Err(std::io::Error::new( - std::io::ErrorKind::InvalidInput, - format!("write-in name is too long: {}", write_in_name.len()), - )); - } - - writer.write( - sizeof(MAXIMUM_WRITE_IN_NAME_LENGTH), - write_in_name.len() as u8, - )?; - - for char in write_in_name.chars() { - let index = WRITE_IN_CHARS.find(char).ok_or_else(|| { - std::io::Error::new( - std::io::ErrorKind::InvalidInput, - format!("invalid write-in name char: {char}"), - ) - })?; - writer.write(BITS_PER_WRITE_IN_CHAR, index as u32)?; - } - - Ok(()) -} - -pub fn encode_yesno_selections( - contest: &YesNoContest, - selections: &[CVRContestSelection], - writer: &mut BitWriter<&mut Vec, E>, -) -> std::io::Result<()> { - if selections.len() != 1 { - return Err(std::io::Error::new( - std::io::ErrorKind::InvalidInput, - format!( - "expected 1 selection for contest with id={}, but got {}", - contest.id, - selections.len() - ), - )); - } - - let selection = &selections[0]; - let is_yes_vote = selection - .contest_selection_id - .as_ref() - .map(|contest_selection_id| contest_selection_id == "yes") - .unwrap_or(false); - - writer.write_bit(is_yes_vote)?; - - Ok(()) -} diff --git a/libs/ballot-encoder-rs/src/lib.rs b/libs/ballot-encoder-rs/src/lib.rs deleted file mode 100644 index 008da7a549..0000000000 --- a/libs/ballot-encoder-rs/src/lib.rs +++ /dev/null @@ -1,9 +0,0 @@ -mod consts; -mod decode; -mod encode; -mod types; -mod util; - -pub use decode::{decode, decode_header}; -pub use encode::encode; -pub use types::*; diff --git a/libs/ballot-encoder-rs/src/types.rs b/libs/ballot-encoder-rs/src/types.rs deleted file mode 100644 index fd55ce49ef..0000000000 --- a/libs/ballot-encoder-rs/src/types.rs +++ /dev/null @@ -1,39 +0,0 @@ -use std::str::FromStr; - -use types_rs::{ - cdf::cvr::{Cvr, VxBallotType}, - election::PartialElectionHash, -}; - -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum TestMode { - Test, - Live, -} - -#[derive(Debug, Clone, PartialEq)] -pub struct EncodableCvr { - pub partial_election_hash: PartialElectionHash, - pub cvr: Cvr, - pub test_mode: TestMode, -} - -#[derive(Debug)] -pub struct BallotConfig { - pub precinct_index: u32, - pub ballot_style_index: u32, - pub ballot_type: VxBallotType, - pub ballot_mode: TestMode, - pub ballot_id: Option, -} - -impl EncodableCvr { - pub fn new(partial_election_hash: PartialElectionHash, cvr: Cvr, test_mode: TestMode) -> Self { - Self { - partial_election_hash: PartialElectionHash::from_str(partial_election_hash.as_str()) - .unwrap_or(partial_election_hash), - cvr, - test_mode, - } - } -} diff --git a/libs/ballot-encoder-rs/src/util.rs b/libs/ballot-encoder-rs/src/util.rs deleted file mode 100644 index 3d1de20b16..0000000000 --- a/libs/ballot-encoder-rs/src/util.rs +++ /dev/null @@ -1,9 +0,0 @@ -pub(crate) fn sizeof(number: usize) -> u32 { - let mut size = 0; - let mut n = number; - while n > 0 { - size += 1; - n >>= 1; - } - size.max(1) -} diff --git a/libs/ballot-encoder-rs/tests/encoding.rs b/libs/ballot-encoder-rs/tests/encoding.rs deleted file mode 100644 index 6213751597..0000000000 --- a/libs/ballot-encoder-rs/tests/encoding.rs +++ /dev/null @@ -1,219 +0,0 @@ -use ballot_encoder_rs::{decode, encode, EncodableCvr, TestMode}; -use bitstream_io::{BigEndian, BitWrite, BitWriter}; -use fixtures::encoded::{ - FAMOUS_NAMES_EAST_LINCOLN_STYLE_1_VOTES_IN_ALL_CONTESTS, - FAMOUS_NAMES_EAST_LINCOLN_STYLE_1_WRITE_INS, -}; -use types_rs::{ - cdf::cvr::{IndicationStatus, VxBallotType}, - election::{BallotStyleId, PrecinctId}, -}; - -use crate::{ - fixtures::{ - encoded::FAMOUS_NAMES_EAST_LINCOLN_STYLE_1_NO_VOTES, read_famous_names_election_definition, - }, - util::{assert_bytes_equal, assert_cvrs_equivalent, build_cvr, sizeof}, -}; - -mod fixtures; -mod util; - -#[test] -fn test_compare_to_manually_encoded_empty_votes() { - let election_definition = read_famous_names_election_definition(); - let election = &election_definition.election; - let election_hash = &election_definition.election_hash; - let ballot_style_id = election.ballot_styles[0].id.clone(); - let contests = election.get_contests(ballot_style_id).unwrap(); - let mut data = vec![]; - let mut writer = BitWriter::endian(&mut data, BigEndian); - // prelude + version number - writer.write_bytes(b"VX").unwrap(); - writer.write(8, 2).unwrap(); - // election hash - let partial_election_hash = election_hash.to_partial(); - dbg!( - partial_election_hash.to_bytes(), - partial_election_hash.to_bytes().len() - ); - writer - .write_bytes(&partial_election_hash.to_bytes()) - .unwrap(); - // check data - writer.write(8, election.precincts.len() as u8).unwrap(); - writer.write(8, election.ballot_styles.len() as u8).unwrap(); - writer.write(8, election.contests.len() as u8).unwrap(); - // precinct index - writer - .write(sizeof(election.precincts.len() - 1), 2) - .unwrap(); - // ballot style index - writer - .write(sizeof(election.ballot_styles.len() - 1), 0) - .unwrap(); - // test ballot? - writer.write_bit(true).unwrap(); - // ballot type - writer - .write( - sizeof(VxBallotType::max() as usize), - u32::from(VxBallotType::Precinct), - ) - .unwrap(); - // ballot id? - writer.write_bit(false).unwrap(); - // vote roll call only, no vote data - for _ in contests { - writer.write_bit(false).unwrap(); - } - writer.byte_align().unwrap(); - - let cvr_with_no_votes = build_cvr( - &election_definition.election, - BallotStyleId::from("1".to_string()), - PrecinctId::from("21".to_string()), - |_, _| (None, IndicationStatus::No), - ); - let encodable_cvr = EncodableCvr::new( - election_definition.election_hash.to_partial(), - cvr_with_no_votes, - TestMode::Test, - ); - assert_bytes_equal( - data.as_slice(), - FAMOUS_NAMES_EAST_LINCOLN_STYLE_1_NO_VOTES.as_slice(), - ); - assert_eq!( - encode(&election_definition.election, &encodable_cvr).unwrap(), - data - ); - let decoded_cvr = decode(&election_definition.election, data.as_slice()).unwrap(); - - assert_cvrs_equivalent(&encodable_cvr, &decoded_cvr); - - let round_trip_encoded_cvr = encode(&election_definition.election, &decoded_cvr).unwrap(); - assert_bytes_equal( - round_trip_encoded_cvr.as_slice(), - FAMOUS_NAMES_EAST_LINCOLN_STYLE_1_NO_VOTES.as_slice(), - ); -} - -#[test] -fn test_empty_votes_from_machine_encoded() { - let election_definition = read_famous_names_election_definition(); - let cvr_with_no_votes = build_cvr( - &election_definition.election, - BallotStyleId::from("1".to_string()), - PrecinctId::from("21".to_string()), - |_, _| (None, IndicationStatus::No), - ); - let encodable_cvr = EncodableCvr::new( - election_definition.election_hash.to_partial(), - cvr_with_no_votes, - TestMode::Test, - ); - let encoded_cvr = encode(&election_definition.election, &encodable_cvr).unwrap(); - assert_bytes_equal( - encoded_cvr.as_slice(), - FAMOUS_NAMES_EAST_LINCOLN_STYLE_1_NO_VOTES.as_slice(), - ); - - let decoded_cvr = decode(&election_definition.election, encoded_cvr.as_slice()).unwrap(); - assert_cvrs_equivalent(&encodable_cvr, &decoded_cvr); -} - -#[test] -fn test_votes_from_machine_encoded() { - let election_definition = read_famous_names_election_definition(); - let cvr_with_votes = build_cvr( - &election_definition.election, - BallotStyleId::from("1".to_string()), - PrecinctId::from("21".to_string()), - |_, option_id| match option_id { - "thomas-edison" - | "winston-churchill" - | "mark-twain" - | "bill-nye" - | "alfred-hitchcock" - | "johan-sebastian-bach" - | "nikola-tesla" - | "jackie-chan" - | "tim-allen" - | "harriet-tubman" - | "marilyn-monroe" => (None, IndicationStatus::Yes), - _ => (None, IndicationStatus::No), - }, - ); - let encodable_cvr = EncodableCvr::new( - election_definition.election_hash.to_partial(), - cvr_with_votes, - TestMode::Test, - ); - assert_bytes_equal( - encode(&election_definition.election, &encodable_cvr) - .unwrap() - .as_slice(), - FAMOUS_NAMES_EAST_LINCOLN_STYLE_1_VOTES_IN_ALL_CONTESTS.as_slice(), - ); - assert_cvrs_equivalent( - &decode( - &election_definition.election, - &FAMOUS_NAMES_EAST_LINCOLN_STYLE_1_VOTES_IN_ALL_CONTESTS, - ) - .unwrap(), - &encodable_cvr, - ); -} - -#[test] -fn test_votes_from_machine_encoded_with_write_ins() { - let election_definition = read_famous_names_election_definition(); - let cvr_with_votes = build_cvr( - &election_definition.election, - BallotStyleId::from("1".to_string()), - PrecinctId::from("22".to_string()), - |contest_id, option_id| match (contest_id.to_string().as_str(), option_id) { - (_, "thomas-edison") - | (_, "oprah-winfrey") - | (_, "john-snow") - | (_, "bill-nye") - | (_, "vincent-van-gogh") - | (_, "wolfgang-amadeus-mozart") - | (_, "tim-allen") - | (_, "harriet-tubman") => (None, IndicationStatus::Yes), - ("chief-of-police", "write-in-0") => { - (Some("MERLIN".to_string()), IndicationStatus::Yes) - } - ("parks-and-recreation-director", "write-in-0") => ( - Some(r#"QWERTYUIOPASDFGHJKL'"ZXCVBNM,.- "'.,-POI"#.to_string()), - IndicationStatus::Yes, - ), - ("board-of-alderman", "write-in-0") => { - (Some("JOHN".to_string()), IndicationStatus::Yes) - } - ("board-of-alderman", "write-in-1") => { - (Some("ALICE".to_string()), IndicationStatus::Yes) - } - _ => (None, IndicationStatus::No), - }, - ); - let encodable_cvr = EncodableCvr::new( - election_definition.election_hash.to_partial(), - cvr_with_votes, - TestMode::Test, - ); - let decoded = decode( - &election_definition.election, - &FAMOUS_NAMES_EAST_LINCOLN_STYLE_1_WRITE_INS, - ) - .unwrap(); - - assert_cvrs_equivalent(&decoded, &encodable_cvr); - assert_bytes_equal( - encode(&election_definition.election, &encodable_cvr) - .unwrap() - .as_slice(), - FAMOUS_NAMES_EAST_LINCOLN_STYLE_1_WRITE_INS.as_slice(), - ); -} diff --git a/libs/ballot-encoder-rs/tests/fixtures/electionFamousNames2021.json b/libs/ballot-encoder-rs/tests/fixtures/electionFamousNames2021.json deleted file mode 100755 index 3e7a46c1fe..0000000000 --- a/libs/ballot-encoder-rs/tests/fixtures/electionFamousNames2021.json +++ /dev/null @@ -1,324 +0,0 @@ -{ - "title": "Lincoln Municipal General Election", - "state": "State of Hamilton", - "county": { - "id": "franklin", - "name": "Franklin County" - }, - "date": "2021-06-06T00:00:00-10:00", - "parties": [ - { - "id": "0", - "name": "Democrat", - "fullName": "Democratic Party", - "abbrev": "D" - }, - { - "id": "1", - "name": "Republican", - "fullName": "Republican Party", - "abbrev": "R" - }, - { - "id": "2", - "name": "Liberty", - "fullName": "Liberty Party", - "abbrev": "Li" - }, - { - "id": "3", - "name": "Green", - "fullName": "Green Party", - "abbrev": "G" - } - ], - "contests": [ - { - "id": "mayor", - "districtId": "district-1", - "type": "candidate", - "title": "Mayor", - "seats": 1, - "allowWriteIns": true, - "candidates": [ - { - "id": "sherlock-holmes", - "name": "Sherlock Holmes", - "partyIds": ["0"] - }, - { - "id": "thomas-edison", - "name": "Thomas Edison", - "partyIds": ["1"] - } - ] - }, - { - "id": "controller", - "districtId": "district-1", - "type": "candidate", - "title": "Controller", - "seats": 1, - "allowWriteIns": true, - "candidates": [ - { - "id": "winston-churchill", - "name": "Winston Churchill", - "partyIds": ["0"] - }, - { - "id": "oprah-winfrey", - "name": "Oprah Winfrey", - "partyIds": ["1"] - }, - { - "id": "louis-armstrong", - "name": "Louis Armstrong", - "partyIds": ["3"] - } - ] - }, - { - "id": "attorney", - "districtId": "district-1", - "type": "candidate", - "title": "Attorney", - "seats": 1, - "allowWriteIns": true, - "candidates": [ - { - "id": "john-snow", - "name": "John Snow", - "partyIds": ["1"] - }, - { - "id": "mark-twain", - "name": "Mark Twain", - "partyIds": ["3"] - } - ] - }, - { - "id": "public-works-director", - "districtId": "district-1", - "type": "candidate", - "title": "Public Works Director", - "seats": 1, - "allowWriteIns": true, - "candidates": [ - { - "id": "benjamin-franklin", - "name": "Benjamin Franklin", - "partyIds": ["0"] - }, - { - "id": "robert-downey-jr", - "name": "Robert Downey Jr.", - "partyIds": ["1"] - }, - { - "id": "bill-nye", - "name": "Bill Nye", - "partyIds": ["3"] - } - ] - }, - { - "id": "chief-of-police", - "districtId": "district-1", - "type": "candidate", - "title": "Chief of Police", - "seats": 1, - "allowWriteIns": true, - "candidates": [ - { - "id": "natalie-portman", - "name": "Natalie Portman", - "partyIds": ["0"] - }, - { - "id": "frank-sinatra", - "name": "Frank Sinatra", - "partyIds": ["1"] - }, - { - "id": "andy-warhol", - "name": "Andy Warhol", - "partyIds": ["3"] - }, - { - "id": "alfred-hitchcock", - "name": "Alfred Hitchcock", - "partyIds": ["3"] - } - ] - }, - { - "id": "parks-and-recreation-director", - "districtId": "district-1", - "type": "candidate", - "title": "Parks and Recreation Director", - "seats": 1, - "allowWriteIns": true, - "candidates": [ - { - "id": "charles-darwin", - "name": "Charles Darwin", - "partyIds": ["0"] - }, - { - "id": "stephen-hawking", - "name": "Stephen Hawking", - "partyIds": ["1"] - }, - { - "id": "johan-sebastian-bach", - "name": "Johann Sebastian Bach", - "partyIds": ["0"] - }, - { - "id": "alexander-graham-bell", - "name": "Alexander Graham Bell", - "partyIds": ["1"] - } - ] - }, - { - "id": "board-of-alderman", - "districtId": "district-1", - "type": "candidate", - "title": "Board of Alderman", - "seats": 4, - "allowWriteIns": true, - "candidates": [ - { - "id": "helen-keller", - "name": "Helen Keller", - "partyIds": ["0"] - }, - { - "id": "steve-jobs", - "name": "Steve Jobs", - "partyIds": ["1"] - }, - { - "id": "nikola-tesla", - "name": "Nikola Tesla", - "partyIds": ["0"] - }, - { - "id": "vincent-van-gogh", - "name": "Vincent Van Gogh", - "partyIds": ["1"] - }, - { - "id": "pablo-picasso", - "name": "Pablo Picasso", - "partyIds": ["1"] - }, - { - "id": "wolfgang-amadeus-mozart", - "name": "Wolfgang Amadeus Mozart", - "partyIds": ["2"] - } - ] - }, - { - "id": "city-council", - "districtId": "district-1", - "type": "candidate", - "title": "City Council", - "seats": 4, - "allowWriteIns": true, - "candidates": [ - { - "id": "marie-curie", - "name": "Marie Curie", - "partyIds": ["0"] - }, - { - "id": "indiana-jones", - "name": "Indiana Jones", - "partyIds": ["1"] - }, - { - "id": "mona-lisa", - "name": "Mona Lisa", - "partyIds": ["3"] - }, - { - "id": "jackie-chan", - "name": "Jackie Chan", - "partyIds": ["3"] - }, - { - "id": "tim-allen", - "name": "Tim Allen", - "partyIds": ["2"] - }, - { - "id": "mark-antony", - "name": "Mark Antony", - "partyIds": ["0"] - }, - { - "id": "harriet-tubman", - "name": "Harriet Tubman", - "partyIds": ["1"] - }, - { - "id": "martin-luther-king", - "name": "Dr. Martin Luther King Jr.", - "partyIds": ["0"] - }, - { - "id": "marilyn-monroe", - "name": "Marilyn Monroe", - "partyIds": ["1"] - } - ] - } - ], - "districts": [ - { - "id": "district-1", - "name": "City of Lincoln" - } - ], - "precincts": [ - { - "id": "23", - "name": "North Lincoln" - }, - { - "id": "22", - "name": "South Lincoln" - }, - { - "id": "21", - "name": "East Lincoln" - }, - { - "id": "20", - "name": "West Lincoln" - } - ], - "ballotStyles": [ - { - "id": "1", - "precincts": ["20", "21", "22", "23"], - "districts": ["district-1"] - } - ], - "sealUrl": "/seals/state-of-hamilton-official-seal.svg", - "adjudicationReasons": [ - "UninterpretableBallot", - "Overvote", - "Undervote", - "BlankBallot" - ], - "markThresholds": { - "definite": 0.12, - "marginal": 0.12 - } -} diff --git a/libs/ballot-encoder-rs/tests/fixtures/encoded.rs b/libs/ballot-encoder-rs/tests/fixtures/encoded.rs deleted file mode 100644 index 3b133a73df..0000000000 --- a/libs/ballot-encoder-rs/tests/fixtures/encoded.rs +++ /dev/null @@ -1,320 +0,0 @@ -#[allow(clippy::unusual_byte_groupings)] -pub const FAMOUS_NAMES_EAST_LINCOLN_STYLE_1_NO_VOTES: [u8; 19] = [ - // prelude - b'V', - b'X', - 0x02, // version - // election hash - 0xb4, - 0xe0, - 0x78, - 0x14, - 0xb4, - 0x69, - 0x11, - 0x21, - 0x1e, - 0xc7, - // check data - 0x04, // precinct count - 0x01, // ballot style count - 0x08, // contest count - // ballot config - 0b10_0_1_0000, - //││ │ │ ││││ - //││ │ │ └┴┴┴── ballot type (precinct=0) - //││ │ └── test ballot (true) - //││ └── ballot style index (0) - //└┴── precinct index (2) - 0b0_0000000, - //│ │││││││ - //│ └┴┴┴┴┴┴── vote roll call #0-6 (all false) - //└── ballot id present? (false) - 0b0_0000000, - //│ │││││││ - //│ └┴┴┴┴┴┴── padding bits - //└── vote roll call #7 (false) -]; - -#[allow(clippy::unusual_byte_groupings)] -pub const FAMOUS_NAMES_EAST_LINCOLN_STYLE_1_VOTES_IN_ALL_CONTESTS: [u8; 23] = [ - // prelude - b'V', - b'X', - 0x02, // version - // election hash - 0xb4, - 0xe0, - 0x78, - 0x14, - 0xb4, - 0x69, - 0x11, - 0x21, - 0x1e, - 0xc7, - // check data - 0x04, // precinct count - 0x01, // ballot style count - 0x08, // contest count - // ballot config - 0b10_0_1_0000, - //││ │ │ ││││ - //││ │ │ └┴┴┴── ballot type (precinct=0) - //││ │ └── test ballot (true) - //││ └── ballot style index (0) - //└┴── precinct index (2) - 0b0_1111111, - //│ │││││││ - //│ └┴┴┴┴┴┴── vote roll call #0-6 (all true) - //└ ballot id present? (false) - 0b1_01_100_01, - //│ ││ │││ ││ - //│ ││ │││ └┴── "attorney" votes (john-snow=no, mark-twain=yes) - //│ ││ └┴┴── "controller" votes (winston-churchill=yes, oprah-winfrey=no, louis-armstrong=no) - //│ └┴── "mayor" votes (sherlock-holmes=no, thomas-edison=yes) - //└── vote roll call #7 (true) - 0b001_0001_0, - //│││ ││││ │ - //│││ ││││ └── "parks-and-recreation-director" vote #0 (charles-darwin=no) - //│││ └┴┴┴── "chief-of-police" votes (natalie-portman=no, frank-sinatra=no, andy-warhol=no, alfred-hitchcock=yes) - //└┴┴── "public-works-director" votes (benjamin-franklin=no, robert-downey-jr=no, bill-nye=yes) - 0b010_00100, - //│││ │││││ - //│││ └┴┴┴┴── "board-of-alderman" votes #0-4 (helen-keller=no, steve-jobs=no, nikola-tesla=yes, vincent-van-gogh=no, pablo-picasso=no) - //└┴┴── "parks-and-recreation-director" votes #1-3 (stephen-hawking=no, johan-sebastian-bach=yes, alexander-graham-bell=no) - 0b0_00_00011, - //│ ││ │││││ - //│ ││ └┴┴┴┴── "city-council" votes #0-4 (marie-curie=no, indiana-jones=no, mona-lisa=no, jackie-chan=yes, tim-allen=yes) - //│ └┴── "board-of-alderman" write-in count (0) - //└── "board-of-alderman" vote #5 (wolfgang-amadeus-mozart=no) - 0b0101_0000, - //││││ ││││ - //││││ └┴┴┴── padding bits - //└┴┴┴── "city-council" votes #5-8 (mark-antony=no, harriet-tubman=yes, martin-luther-king=no, marilyn-monroe=yes) -]; - -#[allow(clippy::unusual_byte_groupings)] -pub const FAMOUS_NAMES_EAST_LINCOLN_STYLE_1_WRITE_INS: [u8; 61] = [ - // prelude - b'V', - b'X', - 0x02, // version - // election hash - 0xb4, - 0xe0, - 0x78, - 0x14, - 0xb4, - 0x69, - 0x11, - 0x21, - 0x1e, - 0xc7, - // check data - 0x04, // precinct count - 0x01, // ballot style count - 0x08, // contest count - // ballot config - 0b01_0_1_0000, - //││ │ │ ││││ - //││ │ │ └┴┴┴── ballot type (precinct=0) - //││ │ └── test ballot (true) - //││ └── ballot style index (0) - //└┴── precinct index (1) - 0b0_1111111, - //│ │││││││ - //│ └┴┴┴┴┴┴── vote roll call #0-6 (all true) - //└ ballot id present? (false) - 0b1_01_010_10, - //│ ││ │││ ││ - //│ ││ │││ └┴── "attorney" votes (john-snow=yes, mark-twain=no) - //│ ││ └┴┴── "controller" votes (winston-churchill=no, oprah-winfrey=yes, louis-armstrong=no) - //│ └┴── "mayor" votes (sherlock-holmes=no, thomas-edison=yes) - //└── vote roll call #7 (true) - 0b001_0000_1, - //│││ ││││ │ - //│││ ││││ └── "chief-of-police" write-in count (1) - //│││ └┴┴┴── "chief-of-police" votes (natalie-portman=no, frank-sinatra=no, andy-warhol=no, alfred-hitchcock=no) - //└┴┴── "public-works-director" votes (benjamin-franklin=no, robert-downey-jr=no, bill-nye=yes) - 0b000110_01, - //││││││ ││ - //││││││ └┴── "chief-of-police" write-in #0 char #0 (M) bits #0-1 - //└┴┴┴┴┴── "chief-of-police" write-in #0 name length (6) - 0b100_00100, - //│││ │││││ - //│││ └┴┴┴┴── "chief-of-police" write-in #0 char #1 (E) bits #0-4 - //└┴┴── "chief-of-police" write-in #0 char #0 (M) bits #2-4 - 0b10001_010, - //│││││ │││ - //│││││ └┴┴── "chief-of-police" write-in #0 char #3 (L) bits #0-2 - //└┴┴┴┴── "chief-of-police" write-in #0 char #2 (R) bits #0-4 - 0b11_01000_0, - //││ │││││ │ - //││ │││││ └── "chief-of-police" write-in #0 char #5 (N) bit #0 - //││ └┴┴┴┴── "chief-of-police" write-in #0 char #4 (I) bits #0-4 - //└┴── "chief-of-police" write-in #0 char #3 (L) bits #3-4 - 0b1101_0000, - //││││ ││││ - //││││ └┴┴┴── "parks-and-recreation-director" votes (charles-darwin=no, stephen-hawking=no, johan-sebastian-bach=no, alexander-graham-bell=no) - //└┴┴┴── "chief-of-police" write-in #0 char #5 (N) bits #1-4 - 0b1_101000_1, - //│ ││││││ │ - //│ ││││││ └── "parks-and-recreation-director" write-in #0 char #0 (Q) bit #0 - //│ ││││││ - //│ └┴┴┴┴┴── "parks-and-recreation-director" write-in #0 length (40) - //└── "parks-and-recreation-director" write-in count (1) - 0b0000_1011, - //││││ ││││ - //││││ └┴┴┴── "parks-and-recreation-director" write-in #0 char #1 (W) bits #0-3 - //└┴┴┴── "parks-and-recreation-director" write-in #0 char #0 (Q) bits #1-4 - 0b0_00100_10, - //│ │││││ ││ - //│ │││││ └┴── "parks-and-recreation-director" write-in #0 char #3 (R) bits #0-1 - //│ └┴┴┴┴── "parks-and-recreation-director" write-in #0 char #2 (E) bits #0-4 - //└── "parks-and-recreation-director" write-in #0 char #1 (W) bit #4 - 0b001_10011, - //│││ │││││ - //│││ └┴┴┴┴── "parks-and-recreation-director" write-in #0 char #4 (T) bits #0-4 - //└┴┴── "parks-and-recreation-director" write-in #0 char #3 (R) bits #2-4 - 0b11000_101, - //│││││ │││ - //│││││ └┴┴── "parks-and-recreation-director" write-in #0 char #6 (U) bits #0-2 - //└┴┴┴┴── "parks-and-recreation-director" write-in #0 char #5 (Y) bits #0-4 - 0b00_01000_0, - //││ │││││ │ - //││ │││││ └── "parks-and-recreation-director" write-in #0 char #8 (O) bit #0 - //││ └┴┴┴┴── "parks-and-recreation-director" write-in #0 char #7 (I) bits #0-4 - //└┴── "parks-and-recreation-director" write-in #0 char #6 (U) bits #2-4 - 0b1110_0111, - //││││ ││││ - //││││ └┴┴┴── "parks-and-recreation-director" write-in #0 char #9 (P) bits #0-3 - //└┴┴┴── "parks-and-recreation-director" write-in #0 char #8 (O) bits #1-4 - 0b1_00000_10, - //│ │││││ ││ - //│ │││││ └┴── "parks-and-recreation-director" write-in #0 char #11 (S) bits #0-1 - //│ └┴┴┴┴── "parks-and-recreation-director" write-in #0 char #10 (A) bits #0-4 - //└── "parks-and-recreation-director" write-in #0 char #9 (P) bit #4 - 0b010_00011, - //│││ │││││ - //│││ └┴┴┴┴── "parks-and-recreation-director" write-in #0 char #12 (D) bits #0-4 - //└┴┴── "parks-and-recreation-director" write-in #0 char #11 (S) bits #2-4 - 0b00101_001, - //│││││ │││ - //│││││ └┴┴── "parks-and-recreation-director" write-in #0 char #14 (G) bits #0-2 - //└┴┴┴┴── "parks-and-recreation-director" write-in #0 char #13 (F) bits #0-4 - 0b10_00111_0, - //││ │││││ │ - //││ │││││ └── "parks-and-recreation-director" write-in #0 char #16 (J) bit #0 - //││ └┴┴┴┴── "parks-and-recreation-director" write-in #0 char #15 (H) bits #0-4 - //└┴── "parks-and-recreation-director" write-in #0 char #14 (G) bits #2-4 - 0b1001_0101, - //││││ ││││ - //││││ └┴┴┴── "parks-and-recreation-director" write-in #0 char #17 (K) bits #0-3 - //└┴┴┴── "parks-and-recreation-director" write-in #0 char #16 (J) bits #1-4 - 0b0_01011_11, - //│ │││││ ││ - //│ │││││ └┴── "parks-and-recreation-director" write-in #0 char #19 (') bits #0-1 - //│ └┴┴┴┴── "parks-and-recreation-director" write-in #0 char #18 (L) bits #0-4 - //└── "parks-and-recreation-director" write-in #0 char #17 (K) bit #4 - 0b011_11100, - //│││ │││││ - //│││ └┴┴┴┴── "parks-and-recreation-director" write-in #0 char #20 (") bits #0-4 - //└┴┴── "parks-and-recreation-director" write-in #0 char #19 (') bits #2-4 - 0b11001_101, - //│││││ │││ - //│││││ └┴┴── "parks-and-recreation-director" write-in #0 char #22 (X) bits #0-2 - //└┴┴┴┴── "parks-and-recreation-director" write-in #0 char #21 (Z) bits #0-4 - 0b11_00010_1, - //││ │││││ │ - //││ │││││ └── "parks-and-recreation-director" write-in #0 char #24 (V) bit #0 - //││ └┴┴┴┴── "parks-and-recreation-director" write-in #0 char #23 (C) bits #0-4 - //└┴── "parks-and-recreation-director" write-in #0 char #22 (X) bits #2-4 - 0b0101_0000, - //││││ ││││ - //││││ └┴┴┴── "parks-and-recreation-director" write-in #0 char #25 (B) bits #0-3 - //└┴┴┴── "parks-and-recreation-director" write-in #0 char #24 (V) bits #1-4 - 0b1_01101_01, - //│ │││││ ││ - //│ │││││ └┴── "parks-and-recreation-director" write-in #0 char #27 (M) bits #0-1 - //│ └┴┴┴┴── "parks-and-recreation-director" write-in #0 char #26 (N) bits #0-4 - //└── "parks-and-recreation-director" write-in #0 char #25 (B) bit #4 - 0b100_11111, - //│││ │││││ - //│││ └┴┴┴┴── "parks-and-recreation-director" write-in #0 char #28 (,) bits #0-4 - //└┴┴── "parks-and-recreation-director" write-in #0 char #27 (M) bits #2-4 - 0b11110_111, - //│││││ │││ - //│││││ └┴┴── "parks-and-recreation-director" write-in #0 char #30 (-) bits #0-2 - //└┴┴┴┴── "parks-and-recreation-director" write-in #0 char #29 (.) bits #0-4 - 0b01_11010_1, - //││ │││││ │ - //││ │││││ └── "parks-and-recreation-director" write-in #0 char #32 (") bit #0 - //││ └┴┴┴┴── "parks-and-recreation-director" write-in #0 char #31 ( ) bits #0-4 - //└┴── "parks-and-recreation-director" write-in #0 char #30 (-) bits #2-4 - 0b1100_1101, - //││││ ││││ - //││││ └┴┴┴── "parks-and-recreation-director" write-in #0 char #33 (') bits #0-3 - //└┴┴┴── "parks-and-recreation-director" write-in #0 char #32 (") bits #1-4 - 0b1_11110_11, - //│ │││││ ││ - //│ │││││ └┴── "parks-and-recreation-director" write-in #0 char #35 (,) bits #0-1 - //│ └┴┴┴┴── "parks-and-recreation-director" write-in #0 char #34 (.) bits #0-4 - //└── "parks-and-recreation-director" write-in #0 char #33 (') bit #4 - 0b111_11101, - //│││ │││││ - //│││ └┴┴┴┴── "parks-and-recreation-director" write-in #0 char #36 (-) bits #0-4 - //└┴┴── "parks-and-recreation-director" write-in #0 char #35 (,) bits #2-4 - 0b01111_011, - //│││││ │││ - //│││││ └┴┴── "parks-and-recreation-director" write-in #0 char #38 (O) bits #0-2 - //└┴┴┴┴── "parks-and-recreation-director" write-in #0 char #37 (P) bits #0-4 - 0b10_01000_0, - //││ │││││ │ - //││ │││││ └── "board-of-alderman" vote #0 (helen-keller=no) - //││ └┴┴┴┴── "parks-and-recreation-director" write-in #0 char #39 (I) bits #0-4 - //└┴── "parks-and-recreation-director" write-in #0 char #38 (O) bits #2-4 - 0b00101_10_0, - //│││││ ││ │ - //│││││ ││ └── "board-of-alderman" write-in #0 length (4) bit #0 - //│││││ └┴── "board-of-alderman" write-in count (2) - //└┴┴┴┴── "board-of-alderman" votes #1-3 (steve-jobs=no, nikola-tesla=no, vincent-van-gogh=yes, pablo-picasso=no, wolfgang-amadeus-mozart=no) - 0b00100_010, - //│││││ │││ - //│││││ └┴┴── "board-of-alderman" write-in #0 char #0 (J) bits #0-2 - //└┴┴┴┴── "board-of-alderman" write-in #0 length (4) bits #1-5 - 0b01_01110_0, - //││ │││││ │ - //││ │││││ └── "board-of-alderman" write-in #0 char #2 (H) bit #0 - //││ └┴┴┴┴── "board-of-alderman" write-in #0 char #1 (O) bits #0-4 - //└┴── "board-of-alderman" write-in #0 char #0 (J) bits #3-4 - 0b0111_0110, - //││││ ││││ - //││││ └┴┴┴── "board-of-alderman" write-in #0 char #3 (N) bits #0-3 - //└┴┴┴── "board-of-alderman" write-in #0 char #2 (H) bits #1-4 - 0b1_000101_0, - //│ ││││││ │ - //│ ││││││ └── "board-of-alderman" write-in #1 char #0 (A) bit #0 - //│ └┴┴┴┴┴── "board-of-alderman" write-in #1 length (5) - //└── "board-of-alderman" write-in #0 char #3 (N) bit #4 - 0b0000_0101, - //││││ ││││ - //││││ └┴┴┴── "board-of-alderman" write-in #1 char #1 (L) bits #0-3 - //└┴┴┴── "board-of-alderman" write-in #1 char #0 (A) bits #1-4 - 0b1_01000_00, - //│ │││││ ││ - //│ │││││ └┴── "board-of-alderman" write-in #1 char #3 (C) bits #0-1 - //│ └┴┴┴┴── "board-of-alderman" write-in #1 char #2 (I) bits #0-4 - //└── "board-of-alderman" write-in #1 char #1 (L) bit #4 - 0b010_00100, - //│││ │││││ - //│││ └┴┴┴┴── "board-of-alderman" write-in #1 char #4 (E) bits #0-4 - //└┴┴── "board-of-alderman" write-in #1 char #3 (C) bits #2-4 - 0b00001010, - //││││││││ - //└┴┴┴┴┴┴┴── "city-council" votes #0-7 (marie-curie=no, indiana-jones=no, mona-lisa=no, jackie-chan=no, tim-allen=yes, mark-antony=no, harriet-tubman=yes, martin-luther-king=no, marilyn-monroe=no) - 0b00_000000, - //││ ││││││ - //││ └┴┴┴┴┴── padding bits - //└┴── "city-council" write-in count (0) -]; diff --git a/libs/ballot-encoder-rs/tests/fixtures/mod.rs b/libs/ballot-encoder-rs/tests/fixtures/mod.rs deleted file mode 100644 index a4d9628664..0000000000 --- a/libs/ballot-encoder-rs/tests/fixtures/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -use types_rs::election::ElectionDefinition; - -pub mod encoded; - -pub fn read_famous_names_election_definition() -> ElectionDefinition { - include_str!("./electionFamousNames2021.json") - .parse::() - .unwrap() -} diff --git a/libs/ballot-encoder-rs/tests/util/mod.rs b/libs/ballot-encoder-rs/tests/util/mod.rs deleted file mode 100644 index db35842fbf..0000000000 --- a/libs/ballot-encoder-rs/tests/util/mod.rs +++ /dev/null @@ -1,152 +0,0 @@ -use ballot_encoder_rs::EncodableCvr; -use nanoid::nanoid; -use pretty_assertions::assert_eq; -use types_rs::cdf::cvr::{ - CVRContest, CVRContestSelection, CVRSnapshot, CVRWriteIn, Cvr, IndicationStatus, - SelectionPosition, -}; -use types_rs::election::{BallotStyleId, Contest, ContestId, Election, PrecinctId}; - -pub fn build_cvr( - election: &Election, - ballot_style_id: BallotStyleId, - precinct_id: PrecinctId, - get_indication_status: impl Fn(&ContestId, &str) -> (Option, IndicationStatus), -) -> Cvr { - let id = nanoid!(); - let contests = election - .get_contests(ballot_style_id.clone()) - .unwrap() - .iter() - .map(|contest| { - let contest_id = contest.id().to_string(); - let contest_selection_ids = contest - .option_ids() - .iter() - .map(|option_id| option_id.to_string()) - .collect::>(); - build_cvr_contest( - ContestId::from(contest_id), - contest_selection_ids.iter().map(|s| s.as_str()).collect(), - match contest { - Contest::YesNo(_) => 0, - Contest::Candidate(candidate_contest) => { - if candidate_contest.allow_write_ins { - candidate_contest.seats as usize - } else { - 0 - } - } - }, - &get_indication_status, - ) - }) - .collect(); - Cvr { - ballot_style_id: Some(ballot_style_id.to_string()), - ballot_style_unit_id: Some(precinct_id.to_string()), - current_snapshot_id: id.clone(), - cvr_snapshot: vec![CVRSnapshot { - id, - cvr_contest: Some(contests), - ..Default::default() - }], - ..Default::default() - } -} - -pub fn assert_cvrs_equivalent(left: &EncodableCvr, right: &EncodableCvr) { - let mut left: EncodableCvr = (*left).clone(); - - // check consistency - assert_eq!(left.cvr.current_snapshot_id, left.cvr.cvr_snapshot[0].id); - assert_eq!(right.cvr.current_snapshot_id, right.cvr.cvr_snapshot[0].id); - - // update the snapshot IDs to match - left.cvr.current_snapshot_id = right.cvr.current_snapshot_id.clone(); - left.cvr.cvr_snapshot[0].id = right.cvr.cvr_snapshot[0].id.clone(); - - assert_eq!(left, *right); -} - -pub fn assert_bytes_equal(left: &[u8], right: &[u8]) { - assert_eq!(pretty_bytes(left), pretty_bytes(right)); -} - -fn pretty_bytes(bytes: &[u8]) -> String { - let mut binary_string = String::new(); - for byte in bytes.iter() { - let char = char::from(*byte); - binary_string.push_str(&format!( - "{:08b} {:02x?} {}\n", - byte, - byte, - if char.is_alphanumeric() { char } else { ' ' }, - )); - } - binary_string -} - -fn build_cvr_contest( - contest_id: ContestId, - contest_selection_ids: Vec<&str>, - possible_write_in_count: usize, - get_indication_status: impl Fn(&ContestId, &str) -> (Option, IndicationStatus), -) -> CVRContest { - let write_in_contest_selections = (0..possible_write_in_count).flat_map(|index| { - let (write_in_name, has_indication) = - get_indication_status(&contest_id, &format!("write-in-{}", index)); - - write_in_name.map(|write_in_name| CVRContestSelection { - contest_selection_id: None, - option_position: None, - selection_position: vec![SelectionPosition { - has_indication, - cvr_write_in: Some(CVRWriteIn { - text: Some(write_in_name), - ..Default::default() - }), - ..Default::default() - }], - ..Default::default() - }) - }); - CVRContest { - contest_id: contest_id.to_string(), - cvr_contest_selection: Some( - contest_selection_ids - .iter() - .enumerate() - .map(|(option_position, contest_selection_id)| { - let (write_in_name, has_indication) = - get_indication_status(&contest_id, contest_selection_id); - CVRContestSelection { - contest_selection_id: Some(contest_selection_id.to_string()), - option_position: Some(option_position as i64), - selection_position: vec![SelectionPosition { - has_indication, - cvr_write_in: write_in_name.map(|text| CVRWriteIn { - text: Some(text), - ..Default::default() - }), - ..Default::default() - }], - ..Default::default() - } - }) - .chain(write_in_contest_selections) - .collect(), - ), - ..Default::default() - } -} - -pub fn sizeof(number: usize) -> u32 { - let mut size = 0; - let mut n = number; - while n > 0 { - size += 1; - n >>= 1; - } - size.max(1) -} diff --git a/libs/central-scanner/Cargo.toml b/libs/central-scanner/Cargo.toml deleted file mode 100644 index 35bec95d9a..0000000000 --- a/libs/central-scanner/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "central-scanner" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -color-eyre = { workspace = true } -env_logger = { workspace = true } -log = { workspace = true } -serde = { workspace = true } -tokio = { workspace = true } diff --git a/libs/central-scanner/package.json b/libs/central-scanner/package.json deleted file mode 100644 index 74aa50bba8..0000000000 --- a/libs/central-scanner/package.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "central-scanner", - "version": "1.0.0", - "description": "", - "main": "index.js", - "scripts": { - "build": "cargo build", - "lint": "cargo clippy -- -D warnings", - "start": "cargo watch -x run", - "test": "cargo test" - }, - "keywords": [], - "author": "VotingWorks Eng ", - "license": "GPL-3.0", - "packageManager": "pnpm@8.1.0" -} diff --git a/libs/central-scanner/src/fujitsu.rs b/libs/central-scanner/src/fujitsu.rs deleted file mode 100644 index 931abeb158..0000000000 --- a/libs/central-scanner/src/fujitsu.rs +++ /dev/null @@ -1,280 +0,0 @@ -use std::fmt::{self, Debug, Formatter}; -use std::io::{self, BufRead, BufReader, BufWriter, Write}; -use std::process::{Child, ChildStdin, ChildStdout, Stdio}; -use std::sync::mpsc::{self, Receiver, SyncSender}; -use std::{path::PathBuf, process::Command}; - -use color_eyre::eyre::eyre; -use log::{debug, error, info}; - -/// Start a new scan session. -/// -/// # Example -/// -/// ```rust -/// use std::path::PathBuf; -/// use central_scanner::scan; -/// -/// fn do_scan() -> Result<(), Box> { -/// let session = scan(PathBuf::from("/tmp"))?; -/// for (side_a_path, side_b_path) in session { -/// println!("side_a_path: {:?}", side_a_path); -/// println!("side_b_path: {:?}", side_b_path); -/// } -/// Ok(()) -/// } -/// ``` -pub fn scan(directory: PathBuf) -> io::Result { - ScanSession::start(directory) -} - -/// Internal actions that control the `scanimage` process. -#[derive(Debug)] -pub enum Action { - /// Request a new card be scanned. - Scan, - - /// Stop the scan session. - Stop, -} - -/// Represents a scanned card, i.e. a sheet of paper. -pub type Card = (PathBuf, PathBuf); - -/// A scan session manages the `scanimage` process and handles requests to scan -/// cards. -pub struct ScanSession { - /// The directory where the scanned images are stored. Must be writable by - /// the current user. - directory: PathBuf, - - /// The `scanimage` process. - scanimage: std::process::Child, - - /// The channel used to send scan requests to the `scanimage` process. - action_tx: SyncSender, - - /// The channel used to receive scanned cards from the `scanimage` process. - card_rx: Receiver<(String, String)>, -} - -impl Debug for ScanSession { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.debug_struct("ScanSession") - .field("directory", &self.directory) - .finish() - } -} - -/// Start the `scanimage` process in batch mode with all the expected options. -fn start_scanimage(batch_template: &str) -> io::Result { - info!("starting scanimage with batch template: {}", batch_template); - Command::new("scanimage") - .arg("-d") - .arg("fujitsu") - .arg("--resolution") - .arg("200") - .arg("--format=jpeg") - .arg("--source=ADF Duplex") - .arg("--dropoutcolor") - .arg("Red") - .arg(format!("--batch={batch_template}")) - .arg("--batch-print") - .arg("--batch-prompt") - .arg("--mode") - .arg("gray") - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .spawn() -} - -/// Loops over `lines` until it finds the line containing ``. This line -/// indicates that `scanimage` is ready to scan. -fn block_until_scanimage_ready(lines: &mut I) -> io::Result<()> -where - I: Iterator>, -{ - debug!("[scanimage] blocking until ready to scan"); - for line in lines { - let line = line?; - debug!("[scanimage] stderr: {}", line); - if line.contains("") { - debug!("[scanimage] ready"); - break; - } - } - Ok(()) -} - -/// Sends the lines from `lines` to the log as debug messages. -fn pipe_stderr_to_log(lines: I) -where - I: Iterator> + Send + 'static, -{ - std::thread::spawn(move || { - for line in lines.flatten() { - debug!("[scanimage] stderr: {}", line); - } - - debug!("[scanimage] stderr closed"); - }); -} - -/// Establish a duplex channel between the `scanimage` process and the calling -/// thread. The calling thread will send `Action` values to the `scanimage` -/// process and receive scanned cards. -fn establish_batch_scanimage_duplex_channel( - stdin: ChildStdin, - stdout: ChildStdout, -) -> (SyncSender, Receiver<(String, String)>) { - let (card_tx, card_rx) = mpsc::sync_channel::<(String, String)>(1); - let (action_tx, action_rx) = mpsc::sync_channel::(1); - - std::thread::spawn(move || -> color_eyre::Result<()> { - let mut stdin_writer = BufWriter::new(stdin); - let mut first_line: Option = None; - let stdout_reader = BufReader::new(stdout); - - let mut wait_for_action = move || -> color_eyre::Result<()> { - debug!("waiting for action"); - match action_rx.recv() { - Ok(Action::Scan) => { - info!("scanning card"); - debug!("stdin: ('\\n\\n')"); - stdin_writer - .write_all(b"\n\n") - .expect("unable to write to stdin"); - stdin_writer.flush().expect("unable to flush stdin"); - Ok(()) - } - Ok(Action::Stop) => { - info!("stop received, exiting stdout process loop"); - Err(eyre!("stop received")) - } - Err(err) => { - error!("error receiving action: {}", err); - Err(eyre!("error receiving action")) - } - } - }; - - // don't start reading stdout until we've received the first action. - // we don't want to start scanning cards until we've received the first - // request to scan - wait_for_action()?; - - for line in stdout_reader.lines().map_while(Result::ok) { - debug!("[scanimage] stdout: {}", line); - if let Some(first_line) = first_line.take() { - card_tx - .send((first_line, line)) - .expect("unable to send card"); - // wait for the next scan action before reading the next card, - // just in case the caller wants to stop the scan session early - wait_for_action()?; - } else { - first_line = Some(line); - } - } - - if let Some(first_line) = first_line { - error!( - "[scanimage] unexpected end of stdout, dropping card: {}", - first_line - ); - } - - debug!("[scanimage] stdout closed"); - info!("scanimage session complete"); - Ok(()) - }); - - (action_tx, card_rx) -} - -impl ScanSession { - /// Start a new scan session. - fn start(directory: PathBuf) -> io::Result { - let batch_template = directory.join("scan-%04d.jpeg"); - let batch_template = batch_template - .to_str() - .expect("unable to convert path to string"); - let mut scanimage = start_scanimage(batch_template)?; - - let stdin = scanimage.stdin.take().ok_or_else(|| { - io::Error::new(io::ErrorKind::BrokenPipe, "scanimage stdin not available") - })?; - let stdout = scanimage.stdout.take().ok_or_else(|| { - io::Error::new(io::ErrorKind::BrokenPipe, "scanimage stdout not available") - })?; - let stderr = scanimage.stderr.take().ok_or_else(|| { - io::Error::new(io::ErrorKind::BrokenPipe, "scanimage stderr not available") - })?; - - let stderr_reader = BufReader::new(stderr); - let mut lines = stderr_reader.lines(); - - block_until_scanimage_ready(lines.by_ref())?; - pipe_stderr_to_log(lines); - let (action_tx, card_rx) = establish_batch_scanimage_duplex_channel(stdin, stdout); - - Ok(ScanSession { - directory, - scanimage, - action_tx, - card_rx, - }) - } - - /// Stop the scan session early, i.e. maybe before all pending cards have - /// been scanned. This method does not need to be called if the scan session - /// is iterated to completion. - pub fn stop(&mut self) -> io::Result<()> { - debug!("ScanSession::stop() called, sending Action::Stop"); - let _ = self.action_tx.send(Action::Stop); - self.scanimage.kill()?; - self.scanimage.wait()?; - Ok(()) - } -} - -impl Iterator for ScanSession { - type Item = Card; - - /// Scan the next card. Blocks until the card has been scanned. If `None` is - /// returned, the scan session has been stopped and `scanimage` has exited. - /// If `Some` is returned, the card has been scanned and the tuple contains - /// the paths to the front and back of the card. The paths will be files - /// inside the directory specified when the scan session was started. - fn next(&mut self) -> Option { - debug!("Iterator::next() called, sending Action::Scan"); - - if let Err(err) = self.action_tx.send(Action::Scan) { - debug!("action_tx.send() returned Err: {}", err); - return None; - } - - if let Ok((side_a_path, side_b_path)) = self.card_rx.recv() { - debug!( - "card_rx.recv() returned Ok: {:?}", - (&side_a_path, &side_b_path) - ); - let side_a_path = PathBuf::from(side_a_path); - let side_b_path = PathBuf::from(side_b_path); - assert!(side_a_path.starts_with(&self.directory)); - assert!(side_b_path.starts_with(&self.directory)); - Some((side_a_path, side_b_path)) - } else { - debug!("card_rx.recv() returned Err, scan session stopped"); - None - } - } -} - -impl Drop for ScanSession { - fn drop(&mut self) { - // make a best effort to stop the scan session - let _ = self.stop(); - } -} diff --git a/libs/central-scanner/src/lib.rs b/libs/central-scanner/src/lib.rs deleted file mode 100644 index ee6d10bf88..0000000000 --- a/libs/central-scanner/src/lib.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod fujitsu; - -pub use fujitsu::scan; diff --git a/libs/types-rs/src/cacvote/client/input.rs b/libs/types-rs/src/cacvote/client/input.rs index 3fbfd7c05b..25a912516e 100644 --- a/libs/types-rs/src/cacvote/client/input.rs +++ b/libs/types-rs/src/cacvote/client/input.rs @@ -81,13 +81,3 @@ pub struct PrintedBallot { #[serde(with = "Base64Standard")] pub cast_vote_record_signature: Vec, } - -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct ScannedBallot { - pub client_id: ClientId, - pub machine_id: String, - pub election_id: ClientId, - #[serde(with = "Base64Standard")] - pub cast_vote_record: Vec, -} diff --git a/libs/types-rs/src/cacvote/client/output.rs b/libs/types-rs/src/cacvote/client/output.rs index 8d2e2b78c7..674a2f7c9b 100644 --- a/libs/types-rs/src/cacvote/client/output.rs +++ b/libs/types-rs/src/cacvote/client/output.rs @@ -87,19 +87,6 @@ pub struct PrintedBallot { pub created_at: time::OffsetDateTime, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -#[serde(rename_all = "camelCase")] -pub struct ScannedBallot { - pub server_id: ServerId, - pub client_id: ClientId, - pub machine_id: String, - pub election_id: ServerId, - #[serde(with = "Base64Standard")] - pub cast_vote_record: Vec, - #[serde(with = "time::serde::iso8601")] - pub created_at: time::OffsetDateTime, -} - #[cfg(test)] mod tests { use super::*; diff --git a/libs/types-rs/src/cacvote/jx/mod.rs b/libs/types-rs/src/cacvote/jx/mod.rs index 67165e05d6..db9ca42a02 100644 --- a/libs/types-rs/src/cacvote/jx/mod.rs +++ b/libs/types-rs/src/cacvote/jx/mod.rs @@ -269,26 +269,6 @@ impl PrintedBallot { } } -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -pub struct ScannedBallot { - pub id: ClientId, - pub server_id: ServerId, - pub election_id: ClientId, - pub precinct_id: PrecinctId, - pub ballot_style_id: BallotStyleId, - #[serde(with = "Base64Standard")] - pub cast_vote_record: Vec, - #[serde(with = "time::serde::iso8601")] - pub created_at: time::OffsetDateTime, -} - -impl ScannedBallot { - #[must_use] - pub fn created_at(&self) -> &time::OffsetDateTime { - &self.created_at - } -} - #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)] pub enum SmartcardStatus { #[default] @@ -323,7 +303,6 @@ pub struct LoggedInAppData { pub registration_requests: Vec, pub registrations: Vec, pub printed_ballots: Vec, - pub scanned_ballots: Vec, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] diff --git a/libs/types-rs/src/cacvote/mod.rs b/libs/types-rs/src/cacvote/mod.rs index 49ee0f115d..4d28bc681e 100644 --- a/libs/types-rs/src/cacvote/mod.rs +++ b/libs/types-rs/src/cacvote/mod.rs @@ -92,8 +92,6 @@ pub struct RaveServerSyncInput { #[serde(default)] pub last_synced_printed_ballot_id: Option, #[serde(default)] - pub last_synced_scanned_ballot_id: Option, - #[serde(default)] pub registration_requests: Vec, #[serde(default)] pub elections: Vec, @@ -101,8 +99,6 @@ pub struct RaveServerSyncInput { pub registrations: Vec, #[serde(default)] pub printed_ballots: Vec, - #[serde(default)] - pub scanned_ballots: Vec, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -114,5 +110,4 @@ pub struct RaveServerSyncOutput { pub registration_requests: Vec, pub registrations: Vec, pub printed_ballots: Vec, - pub scanned_ballots: Vec, } diff --git a/libs/types-rs/src/lib.rs b/libs/types-rs/src/lib.rs index c7d54148c8..a57a2777ef 100644 --- a/libs/types-rs/src/lib.rs +++ b/libs/types-rs/src/lib.rs @@ -4,6 +4,5 @@ pub mod cacvote; pub mod cdf; pub mod election; pub mod geometry; -pub mod scan; pub mod util; pub mod votes; diff --git a/libs/types-rs/src/scan.rs b/libs/types-rs/src/scan.rs deleted file mode 100644 index 6125f62f3f..0000000000 --- a/libs/types-rs/src/scan.rs +++ /dev/null @@ -1,22 +0,0 @@ -use serde::{Deserialize, Serialize}; - -use crate::cacvote::ClientId; - -#[derive(Debug, Serialize, Deserialize, PartialEq, Default, Clone)] -#[serde(rename_all = "camelCase")] -pub struct ScannedBallotStats { - pub batches: Vec, -} - -#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] -#[serde(rename_all = "camelCase")] -pub struct BatchStats { - pub id: ClientId, - pub ballot_count: i32, - pub election_count: i32, - pub synced_count: i32, - #[serde(with = "time::serde::iso8601")] - pub started_at: time::OffsetDateTime, - #[serde(with = "time::serde::iso8601::option")] - pub ended_at: Option, -} diff --git a/mprocs.yaml b/mprocs.yaml index e3a388bfc7..0f58a12339 100644 --- a/mprocs.yaml +++ b/mprocs.yaml @@ -3,16 +3,6 @@ procs: cwd: 'services/cacvote-server' shell: 'cargo watch -x run' - cacvote-scan-backend: - cwd: 'apps/cacvote-scan/backend' - shell: 'cargo watch -x run' - cacvote-scan-frontend: - cwd: 'apps/cacvote-scan/frontend' - shell: 'dx serve --port 4000' - cacvote-scan-frontend-css: - cwd: 'apps/cacvote-scan/frontend' - shell: 'pnpm tailwindcss -i input.css -o ./public/styles.css --watch' - cacvote-jx-backend: cwd: 'apps/cacvote-jx-terminal/backend' shell: 'cargo watch -x run' diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1553b5e134..6fa1fd8502 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -442,143 +442,6 @@ importers: specifier: 1.0.6 version: 1.0.6 - apps/cacvote-scan/frontend: - dependencies: - tailwindcss: - specifier: ^3.3.3 - version: 3.3.3 - devDependencies: - concurrently: - specifier: 7.6.0 - version: 7.6.0 - - apps/cacvote-track/frontend: - dependencies: - '@votingworks/basics': - specifier: workspace:* - version: link:../../../libs/basics - '@votingworks/types': - specifier: workspace:* - version: link:../../../libs/types - '@votingworks/ui': - specifier: workspace:* - version: link:../../../libs/ui - buffer: - specifier: ^6.0.3 - version: 6.0.3 - jsqr: - specifier: ^1.4.0 - version: 1.4.0 - path: - specifier: ^0.12.7 - version: 0.12.7 - react: - specifier: 18.2.0 - version: 18.2.0 - react-dom: - specifier: 18.2.0 - version: 18.2.0(react@18.2.0) - devDependencies: - '@jest/types': - specifier: ^29.6.1 - version: 29.6.1 - '@testing-library/jest-dom': - specifier: ^5.17.0 - version: 5.17.0 - '@testing-library/react': - specifier: ^14.0.0 - version: 14.0.0(react-dom@18.2.0)(react@18.2.0) - '@testing-library/user-event': - specifier: ^13.5.0 - version: 13.5.0(@testing-library/dom@9.3.1) - '@types/jest': - specifier: ^29.5.3 - version: 29.5.3 - '@types/react': - specifier: 18.2.18 - version: 18.2.18 - '@types/react-dom': - specifier: ^18.2.7 - version: 18.2.7 - '@types/testing-library__jest-dom': - specifier: ^5.14.9 - version: 5.14.9 - '@vitejs/plugin-basic-ssl': - specifier: ^1.0.1 - version: 1.0.1(vite@4.5.0) - '@vitejs/plugin-react': - specifier: ^1.3.2 - version: 1.3.2 - '@votingworks/monorepo-utils': - specifier: workspace:* - version: link:../../../libs/monorepo-utils - concurrently: - specifier: 7.6.0 - version: 7.6.0 - eslint: - specifier: 8.51.0 - version: 8.51.0 - eslint-config-airbnb: - specifier: ^19.0.4 - version: 19.0.4(eslint-plugin-import@2.26.0)(eslint-plugin-jsx-a11y@6.6.1)(eslint-plugin-react-hooks@4.6.0)(eslint-plugin-react@7.31.8)(eslint@8.51.0) - eslint-config-prettier: - specifier: ^9.0.0 - version: 9.0.0(eslint@8.51.0) - eslint-config-react-app: - specifier: ^7.0.1 - version: 7.0.1(@babel/plugin-syntax-flow@7.18.6)(@babel/plugin-transform-react-jsx@7.22.15)(eslint@8.51.0)(jest@29.6.2)(typescript@5.2.2) - eslint-import-resolver-node: - specifier: ^0.3.9 - version: 0.3.9 - eslint-plugin-import: - specifier: ^2.26.0 - version: 2.26.0(@typescript-eslint/parser@6.7.0)(eslint-import-resolver-typescript@3.6.0)(eslint@8.51.0) - eslint-plugin-jest: - specifier: ^27.2.3 - version: 27.2.3(@typescript-eslint/eslint-plugin@6.7.0)(eslint@8.51.0)(jest@29.6.2)(typescript@5.2.2) - eslint-plugin-jsx-a11y: - specifier: ^6.6.1 - version: 6.6.1(eslint@8.51.0) - eslint-plugin-prettier: - specifier: ^5.0.0 - version: 5.0.0(@types/eslint@8.4.1)(eslint-config-prettier@9.0.0)(eslint@8.51.0)(prettier@3.0.3) - eslint-plugin-react: - specifier: ^7.31.8 - version: 7.31.8(eslint@8.51.0) - eslint-plugin-react-hooks: - specifier: ^4.6.0 - version: 4.6.0(eslint@8.51.0) - eslint-plugin-testing-library: - specifier: ^5.6.4 - version: 5.6.4(eslint@8.51.0)(typescript@5.2.2) - eslint-plugin-vx: - specifier: workspace:* - version: link:../../../libs/eslint-plugin-vx - is-ci-cli: - specifier: 2.2.0 - version: 2.2.0 - jest: - specifier: ^29.6.2 - version: 29.6.2(@types/node@16.18.23) - jest-environment-jsdom: - specifier: ^29.6.2 - version: 29.6.2 - jest-junit: - specifier: ^16.0.0 - version: 16.0.0 - jest-watch-typeahead: - specifier: ^2.2.2 - version: 2.2.2(jest@29.6.2) - lint-staged: - specifier: 11.0.0 - version: 11.0.0 - ts-jest: - specifier: 29.1.1 - version: 29.1.1(@babel/core@7.22.9)(@jest/types@29.6.1)(esbuild@0.18.17)(jest@29.6.2)(typescript@5.2.2) - vite: - specifier: 4.5.0 - version: 4.5.0(@types/node@16.18.23) - docs/exercises: dependencies: '@types/jest': @@ -984,8 +847,6 @@ importers: specifier: 29.1.1 version: 29.1.1(@babel/core@7.22.9)(@jest/types@29.6.1)(esbuild@0.17.11)(jest@29.6.2)(typescript@5.2.2) - libs/ballot-encoder-rs: {} - libs/basics: dependencies: deep-eql: @@ -1084,8 +945,6 @@ importers: specifier: 29.1.1 version: 29.1.1(@babel/core@7.22.9)(@jest/types@29.6.1)(esbuild@0.17.11)(jest@29.6.2)(typescript@5.2.2) - libs/central-scanner: {} - libs/db: dependencies: '@votingworks/basics': @@ -9122,15 +8981,6 @@ packages: '@typescript-eslint/types': 6.7.0 eslint-visitor-keys: 3.4.3 - /@vitejs/plugin-basic-ssl@1.0.1(vite@4.5.0): - resolution: {integrity: sha512-pcub+YbFtFhaGRTo1832FQHQSHvMrlb43974e2eS8EKleR3p1cDdkJFPci1UhwkEf1J9Bz+wKBSzqpKp7nNj2A==} - engines: {node: '>=14.6.0'} - peerDependencies: - vite: ^3.0.0 || ^4.0.0 - dependencies: - vite: 4.5.0(@types/node@16.18.23) - dev: true - /@vitejs/plugin-react@1.3.2: resolution: {integrity: sha512-aurBNmMo0kz1O4qRoY+FM4epSA39y3ShWGuqfLRA/3z0oEJAdtoSfgA3aO98/PCCHAqMaduLxIxErWrVKIFzXA==} engines: {node: '>=12.0.0'} @@ -15192,10 +15042,6 @@ packages: optionalDependencies: graceful-fs: 4.2.11 - /jsqr@1.4.0: - resolution: {integrity: sha512-dxLob7q65Xg2DvstYkRpkYtmKm2sPJ9oFhrhmudT1dZvNFFTlroai3AWSpLey/w5vMcLBXRgOJsbXpdN9HzU/A==} - dev: false - /jsx-ast-utils@3.3.3: resolution: {integrity: sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==} engines: {node: '>=4.0'} diff --git a/script/cacvote-setup b/script/cacvote-setup index 3a5bf44229..1f502cedfa 100755 --- a/script/cacvote-setup +++ b/script/cacvote-setup @@ -228,7 +228,6 @@ setup-postgresql() { setup-app-db services/cacvote-server postgres:cacvote setup-app-db apps/cacvote-jx-terminal/backend postgres:cacvote_jx - setup-app-db apps/cacvote-scan/backend postgres:cacvote_scan } setup-app-db() { diff --git a/script/reset-db b/script/reset-db index 1c555770c7..61593fb505 100755 --- a/script/reset-db +++ b/script/reset-db @@ -8,10 +8,6 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" cd apps/cacvote-mark/backend rm -rf dev-workspace ) -( - cd apps/cacvote-scan/backend - cargo sqlx database reset -y --source=db/migrations -) ( cd apps/cacvote-jx-terminal/backend cargo sqlx database reset -y --source=db/migrations diff --git a/services/cacvote-server/db/migrations/20240305164418_remove_scanned_ballots.sql b/services/cacvote-server/db/migrations/20240305164418_remove_scanned_ballots.sql new file mode 100644 index 0000000000..2a3af94c37 --- /dev/null +++ b/services/cacvote-server/db/migrations/20240305164418_remove_scanned_ballots.sql @@ -0,0 +1 @@ +DROP TABLE scanned_ballots; diff --git a/services/cacvote-server/src/app.rs b/services/cacvote-server/src/app.rs index 75f88a7b71..4f8c4958ca 100644 --- a/services/cacvote-server/src/app.rs +++ b/services/cacvote-server/src/app.rs @@ -78,12 +78,10 @@ pub(crate) async fn do_sync( last_synced_registration_id, last_synced_election_id, last_synced_printed_ballot_id, - last_synced_scanned_ballot_id, registration_requests, elections, registrations, printed_ballots, - scanned_ballots, } = input; for client_request in registration_requests.into_iter() { @@ -121,14 +119,6 @@ pub(crate) async fn do_sync( } } - for scanned_ballot in scanned_ballots.into_iter() { - let result = db::add_scanned_ballot_from_client(&mut txn, scanned_ballot).await; - - if let Err(e) = result { - tracing::error!("Failed to insert scanned ballot: {e}"); - } - } - let get_jurisdictions_result = db::get_jurisdictions(&mut txn).await; let jurisdictions = match get_jurisdictions_result { Err(e) => { @@ -185,15 +175,6 @@ pub(crate) async fn do_sync( Ok(ballots) => ballots, }; - let scanned_ballots = - match db::get_scanned_ballots(&mut txn, last_synced_scanned_ballot_id).await { - Err(e) => { - tracing::error!("Failed to get scanned ballots: {e}"); - return Err(Json(json!({ "error": e.to_string() }))); - } - Ok(ballots) => ballots, - }; - let output = RaveServerSyncOutput { jurisdictions: jurisdictions.into_iter().map(Into::into).collect(), admins: admins.into_iter().map(Into::into).collect(), @@ -201,7 +182,6 @@ pub(crate) async fn do_sync( registration_requests: registration_requests.into_iter().map(Into::into).collect(), registrations: registrations.into_iter().map(Into::into).collect(), printed_ballots: printed_ballots.into_iter().map(Into::into).collect(), - scanned_ballots: scanned_ballots.into_iter().map(Into::into).collect(), }; if let Err(err) = txn.commit().await { diff --git a/services/cacvote-server/src/db.rs b/services/cacvote-server/src/db.rs index d33076a497..3fedcfbf91 100644 --- a/services/cacvote-server/src/db.rs +++ b/services/cacvote-server/src/db.rs @@ -252,39 +252,6 @@ impl From for client::output::PrintedBallot { } } -#[derive(Debug, Serialize, Deserialize)] -pub(crate) struct ScannedBallot { - pub(crate) id: ServerId, - pub(crate) client_id: ClientId, - pub(crate) machine_id: String, - pub(crate) election_id: ServerId, - #[serde(with = "Base64Standard")] - pub(crate) cast_vote_record: Vec, - pub(crate) created_at: sqlx::types::time::OffsetDateTime, -} - -impl From for client::output::ScannedBallot { - fn from(ballot: ScannedBallot) -> Self { - let ScannedBallot { - id, - client_id, - machine_id, - election_id, - cast_vote_record, - created_at, - } = ballot; - - Self { - server_id: id, - client_id, - machine_id, - election_id, - cast_vote_record, - created_at, - } - } -} - pub(crate) async fn add_jurisdiction( executor: &mut sqlx::PgConnection, jurisdiction: client::input::Jurisdiction, @@ -840,100 +807,6 @@ pub(crate) async fn get_printed_ballots( .collect::>>() } -pub(crate) async fn add_scanned_ballot_from_client( - executor: &mut sqlx::PgConnection, - ballot: client::input::ScannedBallot, -) -> color_eyre::Result { - let ballot_id = ServerId::new(); - - sqlx::query!( - r#" - INSERT INTO scanned_ballots ( - id, - client_id, - machine_id, - election_id, - cast_vote_record - ) - VALUES ( - $1, $2, $3, - (SELECT id FROM elections WHERE client_id = $4), - $5 - ) - "#, - ballot_id.as_uuid(), - ballot.client_id.as_uuid(), - ballot.machine_id, - ballot.election_id.as_uuid(), - ballot.cast_vote_record - ) - .execute(&mut *executor) - .await?; - - Ok(ballot_id) -} - -pub(crate) async fn get_scanned_ballots( - executor: &mut sqlx::PgConnection, - since_ballot_id: Option, -) -> color_eyre::Result> { - let since_ballot = match since_ballot_id { - Some(id) => sqlx::query!( - r#" - SELECT - created_at - FROM scanned_ballots - WHERE id = $1 - "#, - id.as_uuid(), - ) - .fetch_optional(&mut *executor) - .await - .ok() - .flatten(), - None => None, - }; - - match since_ballot { - Some(ballot) => sqlx::query_as!( - ScannedBallot, - r#" - SELECT - id as "id: ServerId", - client_id as "client_id: ClientId", - machine_id, - election_id as "election_id: ServerId", - cast_vote_record as "cast_vote_record: _", - created_at - FROM scanned_ballots - WHERE created_at > $1 - ORDER BY created_at DESC - "#, - ballot.created_at - ) - .fetch_all(&mut *executor) - .await - .map_err(Into::into), - None => sqlx::query_as!( - ScannedBallot, - r#" - SELECT - id as "id: ServerId", - client_id as "client_id: ClientId", - machine_id, - election_id as "election_id: ServerId", - cast_vote_record as "cast_vote_record: _", - created_at - FROM scanned_ballots - ORDER BY created_at DESC - "#, - ) - .fetch_all(&mut *executor) - .await - .map_err(Into::into), - } -} - #[cfg(test)] mod test { use super::*; diff --git a/vxsuite.code-workspace b/vxsuite.code-workspace index dd97e112b5..e4b1454343 100644 --- a/vxsuite.code-workspace +++ b/vxsuite.code-workspace @@ -1,9 +1,5 @@ { "folders": [ - { - "path": "apps/cacvote-jx-terminal/backend", - "name": "apps/cacvote-jx-terminal/backend" - }, { "path": "apps/cacvote-jx-terminal/frontend", "name": "apps/cacvote-jx-terminal/frontend" @@ -16,18 +12,6 @@ "path": "apps/cacvote-mark/frontend", "name": "apps/cacvote-mark/frontend" }, - { - "path": "apps/cacvote-scan/backend", - "name": "apps/cacvote-scan/backend" - }, - { - "path": "apps/cacvote-scan/frontend", - "name": "apps/cacvote-scan/frontend" - }, - { - "path": "apps/cacvote-track/frontend", - "name": "apps/cacvote-track/frontend" - }, { "path": "docs", "name": "docs" @@ -40,10 +24,6 @@ "path": "libs/auth", "name": "libs/auth" }, - { - "path": "libs/auth-rs", - "name": "libs/auth-rs" - }, { "path": "libs/backend", "name": "libs/backend" @@ -52,10 +32,6 @@ "path": "libs/ballot-encoder", "name": "libs/ballot-encoder" }, - { - "path": "libs/ballot-encoder-rs", - "name": "libs/ballot-encoder-rs" - }, { "path": "libs/basics", "name": "libs/basics" @@ -64,10 +40,6 @@ "path": "libs/cdf-schema-builder", "name": "libs/cdf-schema-builder" }, - { - "path": "libs/central-scanner", - "name": "libs/central-scanner" - }, { "path": "libs/db", "name": "libs/db" @@ -132,18 +104,10 @@ "path": "libs/types", "name": "libs/types" }, - { - "path": "libs/types-rs", - "name": "libs/types-rs" - }, { "path": "libs/ui", "name": "libs/ui" }, - { - "path": "libs/ui-rs", - "name": "libs/ui-rs" - }, { "path": "libs/usb-drive", "name": "libs/usb-drive" @@ -159,10 +123,6 @@ { "path": "script", "name": "script" - }, - { - "path": "services/cacvote-server", - "name": "services/cacvote-server" } ], "settings": {