Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactored ContestRequest.java as an abstract base class with GetAsse… #98

Merged
merged 1 commit into from
Jun 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ the raire assertion generation engine (https://github.com/DemocracyDevelopers/ra
import au.org.democracydevelopers.raire.RaireSolution;
import au.org.democracydevelopers.raire.RaireSolution.RaireResultOrError;
import au.org.democracydevelopers.raireservice.persistence.repository.ContestRepository;
import au.org.democracydevelopers.raireservice.request.ContestRequest;
import au.org.democracydevelopers.raireservice.request.GenerateAssertionsRequest;
import au.org.democracydevelopers.raireservice.request.GetAssertionsRequest;
import au.org.democracydevelopers.raireservice.request.RequestValidationException;
import au.org.democracydevelopers.raireservice.response.GenerateAssertionsResponse;
Expand Down Expand Up @@ -83,7 +83,7 @@ public class AssertionController {
* appropriate http error.
*/
@PostMapping(path = "/generate-assertions", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<GenerateAssertionsResponse> serve(@RequestBody ContestRequest request)
public ResponseEntity<GenerateAssertionsResponse> serve(@RequestBody GenerateAssertionsRequest request)
throws RequestValidationException, RaireServiceException
{
final String prefix = "[endpoint:generate-assertions]";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,19 @@ the raire assertion generation engine (https://github.com/DemocracyDevelopers/ra
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.beans.ConstructorProperties;

/**
* Request (expected to be json) identifying a contest by name and listing other data:
* Abstract class that serves as a parent class for particular requests to raire. This class
* identifies a contest by name and includes
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update comment to remove time limit mention.

* - the candidates (by name),
* - the total auditable ballots in the universe (used to calculate difficulty in raire),
* - the time limit allowed to raire.
* This is used directly for requesting assertion generation.
* The only significant method is a verification method for checking that the data items are
* present and have reasonable values.
* The get assertions request type inherits from this class and adds some other fields and
* validations.
* GenerateAssertionsRequest and GetAssertionsRequest inherit from this class and add some other
* fields and validations.
*/
public class ContestRequest {

private final static Logger logger = LoggerFactory.getLogger(ContestRequest.class);
public abstract class ContestRequest {

/**
* The name of the contest
Expand All @@ -54,29 +51,24 @@ public class ContestRequest {
public final int totalAuditableBallots;

/**
* The elapsed time allowed to raire to generate the assertions, in seconds.
* Ignored for GetAssertionsRequests.
* List of candidate names.
*/
public final double timeLimitSeconds;
public final List<String> candidates;

/**
* List of candidate names.
* Class-wide logger.
*/
public final List<String> candidates;
private final static Logger logger = LoggerFactory.getLogger(ContestRequest.class);

/**
* All args constructor.
* @param contestName the name of the contest
* @param totalAuditableBallots the total auditable ballots in the universe under audit.
* @param timeLimitSeconds the elapsed time allowed for RAIRE to generate assertions, in seconds.
* @param candidates the list of candidates by name
*/
@ConstructorProperties({"contestName", "totalAuditableBallots", "timeLimitSeconds","candidates"})
public ContestRequest(String contestName, int totalAuditableBallots, double timeLimitSeconds,
List<String> candidates) {
protected ContestRequest(String contestName, int totalAuditableBallots, List<String> candidates) {
this.contestName = contestName;
this.totalAuditableBallots = totalAuditableBallots;
this.timeLimitSeconds = timeLimitSeconds;
this.candidates = candidates;
}

Expand All @@ -90,9 +82,8 @@ public ContestRequest(String contestName, int totalAuditableBallots, double time
public void Validate(ContestRepository contestRepository) throws RequestValidationException {
final String prefix = "[Validate]";
logger.debug(String.format("%s Validating a Contest Request for contest %s " +
"with specified candidates %s, total number of auditable ballots %d, and time limit " +
"on assertion generation of %fs.", prefix, contestName, candidates, totalAuditableBallots,
timeLimitSeconds));
"with specified candidates %s, total number of auditable ballots %d.", prefix, contestName,
candidates, totalAuditableBallots));

if(contestName == null || contestName.isBlank()) {
final String msg = String.format("%s No contest name specified. " +
Expand Down Expand Up @@ -129,13 +120,5 @@ public void Validate(ContestRepository contestRepository) throws RequestValidati
logger.error(msg);
throw new RequestValidationException(msg);
}
if(timeLimitSeconds <= 0) {
final String msg = String.format("%s Non-positive time limit on assertion generation (%f). " +
"Throwing a RequestValidationException.", prefix, timeLimitSeconds);
logger.error(msg);
throw new RequestValidationException(msg);
}

logger.debug(String.format("%s Request for contest information valid.", prefix));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
Democracy Developers IRV extensions to colorado-rla.

@copyright 2024 Colorado Department of State

These IRV extensions are designed to connect to a running instance of the raire
service (https://github.com/DemocracyDevelopers/raire-service), in order to
generate assertions that can be audited using colorado-rla.

The colorado-rla IRV extensions are free software: you can redistribute it and/or modify it under the terms
of the GNU Affero General Public License as published by the Free Software Foundation, either
version 3 of the License, or (at your option) any later version.

The colorado-rla IRV extensions are distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License along with
raire-service. If not, see <https://www.gnu.org/licenses/>.
*/

package au.org.democracydevelopers.raireservice.request;

import au.org.democracydevelopers.raireservice.persistence.repository.ContestRepository;
import java.beans.ConstructorProperties;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Request (expected to be json) derived from ContestRequest, identifying a contest by name and
* listing other data:
* - the candidates (by name),
* - the total auditable ballots in the universe (used to calculate difficulty in raire),
* - the time limit allowed to raire.
* This is used for requesting assertion generation.
* The only significant method is a verification method for checking that the data items are
* present and have reasonable values.
*/
public class GenerateAssertionsRequest extends ContestRequest {

/**
* The elapsed time allowed to raire to generate the assertions, in seconds.
*/
public final double timeLimitSeconds;

/**
* Class-wide logger.
*/
private final static Logger logger = LoggerFactory.getLogger(ContestRequest.class);

/**
* All args constructor.
* @param contestName the name of the contest
* @param totalAuditableBallots the total auditable ballots in the universe under audit.
* @param timeLimitSeconds the elapsed time allowed for RAIRE to generate assertions, in seconds.
* @param candidates the list of candidates by name
*/
@ConstructorProperties({"contestName", "totalAuditableBallots", "timeLimitSeconds","candidates"})
public GenerateAssertionsRequest(String contestName, int totalAuditableBallots, double timeLimitSeconds,
List<String> candidates) {
super(contestName, totalAuditableBallots, candidates);
this.timeLimitSeconds = timeLimitSeconds;
}

/**
* Validates the GenerateAssertionsRequest,
* super::Validate() checks that the contest exists and is an IRV contest, that
* the total ballots has a sensible value, and that the contest has candidates.
* Note it does _not_ check whether the candidates are present in the CVRs.
* This function adds a test that the timeLimitSeconds has a sensible value.
* @param contestRepository the respository for getting Contest objects from the database.
* @throws RequestValidationException if the request is invalid.
*/
public void Validate(ContestRepository contestRepository) throws RequestValidationException {
final String prefix = "[Validate]";
logger.debug(String.format("%s Validating a GenerateAssertionsRequest for contest %s " +
"with time limit %f seconds.", prefix, contestName, timeLimitSeconds));

super.Validate(contestRepository);

if (timeLimitSeconds <= 0) {
final String msg = String.format("%s Non-positive time limit on assertion generation (%f). " +
"Throwing a RequestValidationException.", prefix, timeLimitSeconds);
logger.error(msg);
throw new RequestValidationException(msg);
}

logger.debug(String.format("%s Generate Assertions Request validated.", prefix));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,6 @@ public class GetAssertionsRequest extends ContestRequest {

private final static Logger logger = LoggerFactory.getLogger(GetAssertionsRequest.class);

/**
* Default time limit, in seconds. Currently ignored.
*/
private final static double DEFAULT_TIME_LIMIT = 5;

/**
* The winner, as stated by the request. This is written into response metadata
* _without_ being checked.
Expand All @@ -74,15 +69,18 @@ public class GetAssertionsRequest extends ContestRequest {
public GetAssertionsRequest(String contestName, int totalAuditableBallots, List<String> candidates,
String winner, BigDecimal riskLimit) {

super(contestName, totalAuditableBallots, DEFAULT_TIME_LIMIT, candidates);
super(contestName, totalAuditableBallots, candidates);
this.winner = winner;
this.riskLimit = riskLimit;
}

/**
* Validates the request to retrieve assertions for the contest, checking that the contest exists
* and is an IRV contest, that the risk limit has a sensible value, and that there are candidates.
* Validates the GetAssertionsRequest,
* super::Validate() checks that the contest exists and is an IRV contest, that
* the total ballots has a sensible value, and that the contest has candidates.
* Note it does _not_ check whether the candidates are present in the CVRs.
* This function adds tests that the risk limit has a sensible value, and that the winner
* exists and is one of the listed candidates.
* @param contestRepository the repository for getting Contest objects from the database.
* @throws RequestValidationException if the request is invalid.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ the raire assertion generation engine (https://github.com/DemocracyDevelopers/ra
import au.org.democracydevelopers.raireservice.persistence.repository.CVRContestInfoRepository;
import au.org.democracydevelopers.raireservice.persistence.repository.ContestRepository;
import au.org.democracydevelopers.raireservice.request.ContestRequest;
import au.org.democracydevelopers.raireservice.request.GenerateAssertionsRequest;
import au.org.democracydevelopers.raireservice.service.RaireServiceException.RaireErrorCode;
import jakarta.transaction.Transactional;
import java.util.HashMap;
Expand Down Expand Up @@ -80,7 +81,7 @@ public GenerateAssertionsService(CVRContestInfoRepository cvrContestInfoReposito
* it referred to candidates that were not in the expected list) or an error arose in database
* access.
*/
public RaireResultOrError generateAssertions(ContestRequest request)
public RaireResultOrError generateAssertions(GenerateAssertionsRequest request)
throws RaireServiceException {
final String prefix = "[generateAssertions]";
try{
Expand Down Expand Up @@ -149,7 +150,7 @@ public RaireResultOrError generateAssertions(ContestRequest request)
RaireProblem raireProblem = new RaireProblem(
metadata, consolidator.getVotes(), request.candidates.size(), null,
new BallotComparisonOneOnDilutedMargin(request.totalAuditableBallots),
TrimAlgorithm.MinimizeAssertions, null, (double) request.timeLimitSeconds
TrimAlgorithm.MinimizeAssertions, null, request.timeLimitSeconds
);

// Tell raire-java to generate assertions, returning a RaireSolutionOrError.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ the raire assertion generation engine (https://github.com/DemocracyDevelopers/ra
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import au.org.democracydevelopers.raireservice.request.ContestRequest;
import au.org.democracydevelopers.raireservice.request.GenerateAssertionsRequest;
import au.org.democracydevelopers.raireservice.testUtils;
import java.util.List;
import java.util.stream.Stream;
Expand Down Expand Up @@ -142,7 +142,7 @@ public void generateAssertionsFromNoVotesIsAnError() {
testUtils.log(logger, "generateAssertionsFromNoVotesIsAnError");
String url = baseURL + port + generateAssertionsEndpoint;

ContestRequest request = new ContestRequest("No CVR Mayoral", 100,
GenerateAssertionsRequest request = new GenerateAssertionsRequest("No CVR Mayoral", 100,
10, aliceAndBob);

ResponseEntity<String> response = restTemplate.postForEntity(url, request, String.class);
Expand All @@ -165,7 +165,7 @@ public void testExpectedErrors(String contestName, int totalBallots, double time
testUtils.log(logger, "testExpectedErrors");
String url = baseURL + port + generateAssertionsEndpoint;

ContestRequest request = new ContestRequest(contestName, totalBallots, timeLimit, candidateList);
GenerateAssertionsRequest request = new GenerateAssertionsRequest(contestName, totalBallots, timeLimit, candidateList);

ResponseEntity<String> response = restTemplate.postForEntity(url, request, String.class);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ the raire assertion generation engine (https://github.com/DemocracyDevelopers/ra
import au.org.democracydevelopers.raire.algorithm.RaireResult;
import au.org.democracydevelopers.raire.assertions.AssertionAndDifficulty;
import au.org.democracydevelopers.raire.assertions.NotEliminatedBefore;
import au.org.democracydevelopers.raireservice.request.ContestRequest;
import au.org.democracydevelopers.raireservice.request.GenerateAssertionsRequest;
import au.org.democracydevelopers.raireservice.request.GetAssertionsRequest;
import au.org.democracydevelopers.raireservice.response.GenerateAssertionsResponse;
import au.org.democracydevelopers.raireservice.service.RaireServiceException.RaireErrorCode;
Expand Down Expand Up @@ -140,7 +140,7 @@ public void testGuideToRairePart2Example1() {
String generateUrl = baseURL + port + generateAssertionsEndpoint;
String getUrl = baseURL + port + getAssertionsEndpoint;

ContestRequest request = new ContestRequest(guideToRaireExample1,
GenerateAssertionsRequest request = new GenerateAssertionsRequest(guideToRaireExample1,
27, DEFAULT_TIME_LIMIT, Arrays.stream(aliceBobChuanDiego).toList());


Expand Down Expand Up @@ -185,7 +185,7 @@ void testGuideToRairePart2Example2() {
testUtils.log(logger, "testGuideToRairePart2Example2");
String generateUrl = baseURL + port + generateAssertionsEndpoint;
String getUrl = baseURL + port + getAssertionsEndpoint;
ContestRequest request = new ContestRequest(guideToRaireExample2,
GenerateAssertionsRequest request = new GenerateAssertionsRequest(guideToRaireExample2,
41, 5, Arrays.stream(aliceChuanBob).toList());

// Request for the assertions to be generated.
Expand Down Expand Up @@ -237,7 +237,7 @@ public void simpleContestSingleCounty() {
String generateUrl = baseURL + port + generateAssertionsEndpoint;
String getUrl = baseURL + port + getAssertionsEndpoint;

ContestRequest request = new ContestRequest(simpleContest,
GenerateAssertionsRequest request = new GenerateAssertionsRequest(simpleContest,
5, 5, Arrays.stream(aliceChuanBob).toList());

// Request for the assertions to be generated.
Expand Down Expand Up @@ -281,7 +281,7 @@ public void simpleContestCrossCounty() {
String generateUrl = baseURL + port + generateAssertionsEndpoint;
String getUrl = baseURL + port + getAssertionsEndpoint;

ContestRequest request = new ContestRequest(crossCountySimpleContest,
GenerateAssertionsRequest request = new GenerateAssertionsRequest(crossCountySimpleContest,
5, 5, Arrays.stream(aliceChuanBob).toList());

// Request for the assertions to be generated.
Expand Down Expand Up @@ -334,7 +334,7 @@ public void simpleContestSingleCountyDoubleBallots() {

// Tell raire that the totalAuditableBallots is double the number in the database
// for this contest.
ContestRequest request = new ContestRequest(simpleContest,
GenerateAssertionsRequest request = new GenerateAssertionsRequest(simpleContest,
10, 5, Arrays.stream(aliceChuanBob).toList());

// Request for the assertions to be generated.
Expand Down Expand Up @@ -378,7 +378,7 @@ public void simpleContestSingleCountyInsufficientBallotsError() {
testUtils.log(logger, "simpleContestSingleCountyInsufficientBallotsError");
String generateUrl = baseURL + port + generateAssertionsEndpoint;

ContestRequest notEnoughBallotsRequest = new ContestRequest(simpleContest,
GenerateAssertionsRequest notEnoughBallotsRequest = new GenerateAssertionsRequest(simpleContest,
2, 5, Arrays.stream(aliceChuanBob).toList());

// Request for the assertions to be generated.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ the raire assertion generation engine (https://github.com/DemocracyDevelopers/ra

import au.org.democracydevelopers.raire.RaireSolution;
import au.org.democracydevelopers.raireservice.NSWValues.Expected;
import au.org.democracydevelopers.raireservice.request.ContestRequest;
import au.org.democracydevelopers.raireservice.request.GenerateAssertionsRequest;
import au.org.democracydevelopers.raireservice.request.GetAssertionsRequest;
import au.org.democracydevelopers.raireservice.response.GenerateAssertionsResponse;
import au.org.democracydevelopers.raireservice.testUtils;
Expand Down Expand Up @@ -89,7 +89,7 @@ public void checkAllNSWByAPI() {

for(Expected expected : expectedSolutionData) {
testUtils.log(logger, "checkAllNSWByAPI: contest "+expected.contestName());
ContestRequest generateRequest = new ContestRequest(
GenerateAssertionsRequest generateRequest = new GenerateAssertionsRequest(
expected.contestName(), expected.ballotCount(), DEFAULT_TIME_LIMIT, expected.choices());

// Request for the assertions to be generated.
Expand Down
Loading
Loading