Skip to content

Commit cf995e9

Browse files
authored
Support command line arguments in sample_generator and fix spoil rate (#162)
* Support command line arguments in sample_generator Run `pipenv run ./src/electionguardtest/sample_generator.py --help` for more information. This also fixes a bug with spoiling the generated ballots. Because it was using a modulo operator when spoiling, only ~1% of the ballots would ever be spoiled, regardless of the desired amount. * Run sample data generation in CI with 0% spoiled ballots
1 parent 48ed717 commit cf995e9

3 files changed

Lines changed: 45 additions & 8 deletions

File tree

.github/workflows/pull_request.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ jobs:
5050
- name: Full Test & Coverage
5151
run: make coverage
5252
- name: Generate Sample Data
53-
run: make generate-sample-data
53+
# TODO: Issue #152
54+
run: make generate-sample-data SAMPLE_BALLOT_SPOIL_RATE=0
5455

5556
mac_check:
5657
name: MacOS Check

Makefile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ WINDOWS_32BIT_GMPY2 ?= packages/gmpy2-2.0.8-cp38-cp38-win32.whl
55
WINDOWS_64BIT_GMPY2 ?= packages/gmpy2-2.0.8-cp38-cp38-win_amd64.whl
66
OS ?= $(shell python -c 'import platform; print(platform.system())')
77
IS_64_BIT ?= $(shell python -c 'from sys import maxsize; print(maxsize > 2**32)')
8+
SAMPLE_BALLOT_COUNT ?= 5
9+
SAMPLE_BALLOT_SPOIL_RATE ?= 50
810

911
all: environment install validate lint coverage
1012

@@ -136,7 +138,7 @@ dependency-graph-ci:
136138

137139
# Sample Data
138140
generate-sample-data:
139-
pipenv run python src/electionguardtest/sample_generator.py
141+
pipenv run python src/electionguardtest/sample_generator.py -n $(SAMPLE_BALLOT_COUNT) -s $(SAMPLE_BALLOT_SPOIL_RATE)
140142

141143
# Package
142144
package:

src/electionguardtest/sample_generator.py

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
from electionguard.utils import get_optional
2121

2222
DEFAULT_NUMBER_OF_BALLOTS = 5
23-
CAST_SPOIL_RATIO = 50
24-
THRESHOLD_ONLY = True
23+
DEFAULT_SPOIL_RATE = 50
24+
DEFAULT_USE_ALL_GUARDIANS = False
2525

2626

2727
class ElectionSampleDataGenerator:
@@ -44,7 +44,8 @@ def __init__(self) -> None:
4444
def generate(
4545
self,
4646
number_of_ballots: int = DEFAULT_NUMBER_OF_BALLOTS,
47-
cast_spoil_ratio: int = CAST_SPOIL_RATIO,
47+
spoil_rate: int = DEFAULT_SPOIL_RATE,
48+
use_all_guardians: bool = DEFAULT_USE_ALL_GUARDIANS,
4849
):
4950
"""
5051
Generate the sample data set
@@ -78,7 +79,7 @@ def generate(
7879
# Randomly cast/spoil the ballots
7980
accepted_ballots: List[CiphertextAcceptedBallot] = []
8081
for ballot in ciphertext_ballots:
81-
if randint(0, 100) % cast_spoil_ratio == 0:
82+
if randint(0, 100) < spoil_rate:
8283
accepted_ballots.append(ballot_box.spoil(ballot))
8384
else:
8485
accepted_ballots.append(ballot_box.cast(ballot))
@@ -94,7 +95,7 @@ def generate(
9495
)
9596

9697
for i, guardian in enumerate(private_data.guardians):
97-
if i < QUORUM or not THRESHOLD_ONLY:
98+
if use_all_guardians or i < QUORUM:
9899
decrypter.announce(guardian)
99100

100101
plaintext_tally = get_optional(decrypter.get_plaintext_tally())
@@ -118,4 +119,37 @@ def generate(
118119

119120

120121
if __name__ == "__main__":
121-
ElectionSampleDataGenerator().generate()
122+
import argparse
123+
124+
parser = argparse.ArgumentParser(
125+
description="Generate sample ballot data",
126+
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
127+
)
128+
parser.add_argument(
129+
"-n",
130+
"--number-of-ballots",
131+
metavar="<count>",
132+
default=DEFAULT_NUMBER_OF_BALLOTS,
133+
type=int,
134+
help="The number of ballots to generate.",
135+
)
136+
parser.add_argument(
137+
"-s",
138+
"--spoil-rate",
139+
metavar="<rate>",
140+
default=DEFAULT_SPOIL_RATE,
141+
type=int,
142+
help="The approximate percentage of total ballots to spoil instead of cast. Provide a number from 0-100.",
143+
)
144+
parser.add_argument(
145+
"-a",
146+
"--all-guardians",
147+
default=DEFAULT_USE_ALL_GUARDIANS,
148+
action="store_true",
149+
help="If specified, all guardians will be included. Otherwise, only the threshold number will be included.",
150+
)
151+
args = parser.parse_args()
152+
153+
ElectionSampleDataGenerator().generate(
154+
args.number_of_ballots, args.spoil_rate, args.all_guardians
155+
)

0 commit comments

Comments
 (0)