* Add test reproducing error with duplicate section names * Stop removing duplicate contests from report * Test to ensure duplicates aren't removed in selections * fix linting issues
This commit is contained in:
Родитель
3cbbcccede
Коммит
c79b021fb3
|
@ -7,13 +7,26 @@ from electionguard_gui.models.election_dto import ElectionDto
|
|||
|
||||
def get_plaintext_ballot_report(
|
||||
election: ElectionDto, plaintext_ballot: PlaintextTally
|
||||
) -> dict[str, Any]:
|
||||
) -> list:
|
||||
manifest = election.get_manifest()
|
||||
selection_names = manifest.get_selection_names("en")
|
||||
contest_names = manifest.get_contest_names()
|
||||
selection_write_ins = _get_candidate_write_ins(manifest)
|
||||
parties = _get_selection_parties(manifest)
|
||||
tally_report = {}
|
||||
tally_report = _get_tally_report(
|
||||
plaintext_ballot, selection_names, contest_names, selection_write_ins, parties
|
||||
)
|
||||
return tally_report
|
||||
|
||||
|
||||
def _get_tally_report(
|
||||
plaintext_ballot: PlaintextTally,
|
||||
selection_names: dict[str, str],
|
||||
contest_names: dict[str, str],
|
||||
selection_write_ins: dict[str, bool],
|
||||
parties: dict[str, str],
|
||||
) -> list:
|
||||
tally_report = []
|
||||
contests = plaintext_ballot.contests.values()
|
||||
for tally_contest in contests:
|
||||
selections = list(tally_contest.selections.values())
|
||||
|
@ -21,7 +34,12 @@ def get_plaintext_ballot_report(
|
|||
selections, selection_names, selection_write_ins, parties
|
||||
)
|
||||
contest_name = contest_names.get(tally_contest.object_id, "n/a")
|
||||
tally_report[contest_name] = contest_details
|
||||
tally_report.append(
|
||||
{
|
||||
"name": contest_name,
|
||||
"details": contest_details,
|
||||
}
|
||||
)
|
||||
return tally_report
|
||||
|
||||
|
||||
|
|
|
@ -3,8 +3,8 @@ export default {
|
|||
ballot: Object,
|
||||
},
|
||||
template: /*html*/ `
|
||||
<div v-for="(contestContents, contestName) in ballot" class="mb-5">
|
||||
<h2>{{contestName}}</h2>
|
||||
<div v-for="contest in ballot" class="mb-5">
|
||||
<h2>{{contest.name}}</h2>
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -15,7 +15,7 @@ export default {
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="contestInfo in contestContents.selections">
|
||||
<tr v-for="contestInfo in contest.details.selections">
|
||||
<td>{{contestInfo.name}}</td>
|
||||
<td>{{contestInfo.party}}</td>
|
||||
<td class="text-end">{{contestInfo.tally}}</td>
|
||||
|
@ -24,13 +24,13 @@ export default {
|
|||
<tr class="table-secondary">
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td class="text-end"><strong>{{contestContents.nonWriteInTotal}}</strong></td>
|
||||
<td class="text-end"><strong>{{contest.details.nonWriteInTotal}}</strong></td>
|
||||
<td class="text-end"><strong>100.00%</strong></td>
|
||||
</tr>
|
||||
<tr v-if="contestContents.writeInTotal !== null">
|
||||
<tr v-if="contest.details.writeInTotal !== null">
|
||||
<td></td>
|
||||
<td class="text-end">Write-Ins</td>
|
||||
<td class="text-end">{{contestContents.writeInTotal}}</td>
|
||||
<td class="text-end">{{contest.details.writeInTotal}}</td>
|
||||
<td class="text-end"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
|
|
@ -1,12 +1,121 @@
|
|||
from unittest.mock import MagicMock, patch
|
||||
from electionguard.tally import PlaintextTallySelection
|
||||
from electionguard_gui.services.plaintext_ballot_service import _get_contest_details
|
||||
from electionguard.tally import PlaintextTally, PlaintextTallySelection
|
||||
from electionguard_gui.services.plaintext_ballot_service import (
|
||||
_get_contest_details,
|
||||
_get_tally_report,
|
||||
)
|
||||
from tests.base_test_case import BaseTestCase
|
||||
|
||||
|
||||
class TestPlaintextBallotService(BaseTestCase):
|
||||
"""Test the ElectionDto class"""
|
||||
|
||||
def test_get_tally_report_with_no_contests(self) -> None:
|
||||
# ARRANGE
|
||||
plaintext_ballot = PlaintextTally("tally", {})
|
||||
selection_names: dict[str, str] = {}
|
||||
selection_write_ins: dict[str, bool] = {}
|
||||
parties: dict[str, str] = {}
|
||||
contest_names: dict[str, str] = {}
|
||||
|
||||
# ACT
|
||||
result = _get_tally_report(
|
||||
plaintext_ballot,
|
||||
selection_names,
|
||||
contest_names,
|
||||
selection_write_ins,
|
||||
parties,
|
||||
)
|
||||
|
||||
# ASSERT
|
||||
self.assertEqual(0, len(result))
|
||||
|
||||
@patch("electionguard.tally.PlaintextTallySelection")
|
||||
def test_given_one_contest_with_valid_name_when_get_tally_report_then_name_returned(
|
||||
self, plaintext_tally_selection: MagicMock
|
||||
) -> None:
|
||||
# ARRANGE
|
||||
plaintext_tally_selection.object_id = "c-1"
|
||||
plaintext_ballot = PlaintextTally("tally", {"c-1": plaintext_tally_selection})
|
||||
selection_names: dict[str, str] = {}
|
||||
selection_write_ins: dict[str, bool] = {}
|
||||
parties: dict[str, str] = {}
|
||||
contest_names: dict[str, str] = {"c-1": "Contest 1"}
|
||||
|
||||
# ACT
|
||||
result = _get_tally_report(
|
||||
plaintext_ballot,
|
||||
selection_names,
|
||||
contest_names,
|
||||
selection_write_ins,
|
||||
parties,
|
||||
)
|
||||
|
||||
# ASSERT
|
||||
self.assertEqual(1, len(result))
|
||||
self.assertEqual("Contest 1", result[0]["name"])
|
||||
|
||||
@patch("electionguard.tally.PlaintextTallySelection")
|
||||
def test_given_one_contest_with_invalid_name_when_get_tally_report_then_name_is_na(
|
||||
self, plaintext_tally_selection: MagicMock
|
||||
) -> None:
|
||||
# ARRANGE
|
||||
plaintext_tally_selection.object_id = "c-1"
|
||||
plaintext_ballot = PlaintextTally("tally", {"c-1": plaintext_tally_selection})
|
||||
selection_names: dict[str, str] = {}
|
||||
selection_write_ins: dict[str, bool] = {}
|
||||
parties: dict[str, str] = {}
|
||||
contest_names: dict[str, str] = {}
|
||||
|
||||
# ACT
|
||||
result = _get_tally_report(
|
||||
plaintext_ballot,
|
||||
selection_names,
|
||||
contest_names,
|
||||
selection_write_ins,
|
||||
parties,
|
||||
)
|
||||
|
||||
# ASSERT
|
||||
self.assertEqual(1, len(result))
|
||||
self.assertEqual("n/a", list(result)[0]["name"])
|
||||
|
||||
@patch("electionguard.tally.PlaintextTallySelection")
|
||||
@patch("electionguard.tally.PlaintextTallySelection")
|
||||
def test_given_two_contests_with_duplicate_names_when_get_tally_report_then_both_names_returned(
|
||||
self,
|
||||
plaintext_tally_selection1: MagicMock,
|
||||
plaintext_tally_selection2: MagicMock,
|
||||
) -> None:
|
||||
# ARRANGE
|
||||
plaintext_tally_selection1.object_id = "c-1"
|
||||
plaintext_tally_selection2.object_id = "c-2"
|
||||
plaintext_ballot = PlaintextTally(
|
||||
"tally",
|
||||
{
|
||||
"c-1": plaintext_tally_selection1,
|
||||
"c-2": plaintext_tally_selection2,
|
||||
},
|
||||
)
|
||||
selection_names: dict[str, str] = {}
|
||||
selection_write_ins: dict[str, bool] = {}
|
||||
parties: dict[str, str] = {}
|
||||
contest_names: dict[str, str] = {"c-1": "My Contest", "c-2": "My Contest"}
|
||||
|
||||
# ACT
|
||||
result = _get_tally_report(
|
||||
plaintext_ballot,
|
||||
selection_names,
|
||||
contest_names,
|
||||
selection_write_ins,
|
||||
parties,
|
||||
)
|
||||
|
||||
# ASSERT
|
||||
self.assertEqual(2, len(result))
|
||||
self.assertEqual("My Contest", list(result)[0]["name"])
|
||||
self.assertEqual("My Contest", list(result)[1]["name"])
|
||||
|
||||
def test_zero_sections(self) -> None:
|
||||
# ARRANGE
|
||||
selections: list[PlaintextTallySelection] = []
|
||||
|
@ -55,6 +164,57 @@ class TestPlaintextBallotService(BaseTestCase):
|
|||
self.assertEqual("National Union Party", selection["party"])
|
||||
self.assertEqual(1, selection["percent"])
|
||||
|
||||
@patch("electionguard.tally.PlaintextTallySelection")
|
||||
@patch("electionguard.tally.PlaintextTallySelection")
|
||||
def test_duplicate_section_names(
|
||||
self,
|
||||
plaintext_tally_selection1: MagicMock,
|
||||
plaintext_tally_selection2: MagicMock,
|
||||
) -> None:
|
||||
# ARRANGE
|
||||
plaintext_tally_selection1.object_id = "S1"
|
||||
plaintext_tally_selection1.tally = 1
|
||||
plaintext_tally_selection2.object_id = "S2"
|
||||
plaintext_tally_selection2.tally = 9
|
||||
selections: list[PlaintextTallySelection] = [
|
||||
plaintext_tally_selection1,
|
||||
plaintext_tally_selection2,
|
||||
]
|
||||
selection_names: dict[str, str] = {
|
||||
"S1": "Abraham Lincoln",
|
||||
"S2": "Abraham Lincoln",
|
||||
}
|
||||
selection_write_ins: dict[str, bool] = {
|
||||
"S1": False,
|
||||
"S2": False,
|
||||
}
|
||||
parties: dict[str, str] = {
|
||||
"S1": "National Union Party",
|
||||
"S2": "National Union Party",
|
||||
}
|
||||
|
||||
# ACT
|
||||
result = _get_contest_details(
|
||||
selections, selection_names, selection_write_ins, parties
|
||||
)
|
||||
|
||||
# ASSERT
|
||||
self.assertEqual(10, result["nonWriteInTotal"])
|
||||
self.assertEqual(None, result["writeInTotal"])
|
||||
self.assertEqual(2, len(result["selections"]))
|
||||
|
||||
selection = result["selections"][0]
|
||||
self.assertEqual("Abraham Lincoln", selection["name"])
|
||||
self.assertEqual(1, selection["tally"])
|
||||
self.assertEqual("National Union Party", selection["party"])
|
||||
self.assertEqual(0.1, selection["percent"])
|
||||
|
||||
selection = result["selections"][1]
|
||||
self.assertEqual("Abraham Lincoln", selection["name"])
|
||||
self.assertEqual(9, selection["tally"])
|
||||
self.assertEqual("National Union Party", selection["party"])
|
||||
self.assertEqual(0.9, selection["percent"])
|
||||
|
||||
@patch("electionguard.tally.PlaintextTallySelection")
|
||||
def test_one_write_in(self, plaintext_tally_selection: MagicMock) -> None:
|
||||
# ARRANGE
|
||||
|
|
Загрузка…
Ссылка в новой задаче