Skip to content

Commit

Permalink
Merge pull request #139 from DemocracyDevelopers/73-generate-assertio…
Browse files Browse the repository at this point in the history
…ns-endpoint

73 generate assertions endpoint
  • Loading branch information
vteague authored Jul 2, 2024
2 parents f5d0f38 + f330a81 commit 19746b9
Show file tree
Hide file tree
Showing 19 changed files with 1,014 additions and 175 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
raire-service. If not, see <https://www.gnu.org/licenses/>.
*/

package au.org.democracydevelopers.corla.raire.requestToRaire;
package au.org.democracydevelopers.corla.communication.requestToRaire;

import java.util.List;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
raire-service. If not, see <https://www.gnu.org/licenses/>.
*/

package au.org.democracydevelopers.corla.raire.requestToRaire;
package au.org.democracydevelopers.corla.communication.requestToRaire;

import org.apache.log4j.LogManager;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
raire-service. If not, see <https://www.gnu.org/licenses/>.
*/

package au.org.democracydevelopers.corla.raire.requestToRaire;
package au.org.democracydevelopers.corla.communication.requestToRaire;

import org.apache.log4j.LogManager;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,27 @@
raire-service. If not, see <https://www.gnu.org/licenses/>.
*/

package au.org.democracydevelopers.corla.raire.responseFromRaire;
package au.org.democracydevelopers.corla.communication.responseFromRaire;

import java.util.Objects;

/**
* The success response when a ContestRequest is sent to raire's generate-assertions endpoint. This
* simply returns the winner, as calculated by raire, along with the name of the contest for which
* the initial request was made.
* This record is identical to the record of the same name in raire-service. Used for
* deserialization.
*
* @param contestName The name of the contest.
* @param winner The winner of the contest, as calculated by raire.
*/
public record GenerateAssertionsResponse(String contestName, String winner) {
public final class GenerateAssertionsResponse {
public String contestName;
public String winner;

/**
* @param contestName The name of the contest.
* @param winner The winner of the contest, as calculated by raire.
*/
public GenerateAssertionsResponse(String contestName, String winner) {
this.contestName = contestName;
this.winner = winner;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ the raire assertion generation engine (https://github.com/DemocracyDevelopers/ra
raire-service. If not, see <https://www.gnu.org/licenses/>.
*/

package au.org.democracydevelopers.corla.raire.responseFromRaire;
package au.org.democracydevelopers.corla.communication.responseFromRaire;

/**
* Errors sent back from the raire service via http headers. The fields exactly match the
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
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.corla.communication.responseToColoradoRla;

import java.beans.ConstructorProperties;

/**
* The response to be sent to colorado-rla after a GenerateAssertionsRequest.
* These are identified by contest name.
* The success response is simply a winner and an empty error string.
* Error responses have some non-empty error and usually a winner set to "UNKNOWN".
* Note that errors may sometimes have a real winner, though they usually don't. For example, if trimming assertions
* times out, there will be both a statement of that error and a real winner.
*/
public final class GenerateAssertionsResponseWithErrors {
public final String contestName;
public final String winner;
public final String raireError;

/**
* @param contestName The name of the contest.
* @param winner The winner of the contest, as calculated by raire.
* @param raireError The error message returned from raire. Empty if there was no error.
*/
@ConstructorProperties({"contestName", "winner", "raireError"})
public GenerateAssertionsResponseWithErrors(String contestName, String winner, String raireError) {
this.contestName = contestName;
this.winner = winner;
this.raireError = raireError;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/*
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.corla.endpoint;

import au.org.democracydevelopers.corla.model.ContestType;
import com.google.gson.Gson;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import us.freeandfair.corla.asm.ASMEvent;
import us.freeandfair.corla.controller.ContestCounter;
import us.freeandfair.corla.endpoint.AbstractDoSDashboardEndpoint;
import us.freeandfair.corla.model.*;
import java.util.List;

/**
* An abstract endpoint for communicating with raire. Includes all the information for collecting IRV contests
* and making a request to raire, including the location of the raire service.
* Used by GetAssertions and GenerateAssertions.
*/
public abstract class AbstractAllIrvEndpoint extends AbstractDoSDashboardEndpoint {

/**
* Class-wide logger
*/
private static final Logger LOGGER = LogManager.getLogger(AbstractAllIrvEndpoint.class);

/**
* GSON, for serialising requests.
*/
protected final Gson gson = new Gson();

/**
* Identify RAIRE service URL from config.
*/
protected static final String RAIRE_URL = "raire_url";

/**
* The httpClient used for making requests to the raire-service.
*/
protected final CloseableHttpClient httpClient = HttpClients.createDefault();


/**
* The event to return for this endpoint.
*/
protected final ThreadLocal<ASMEvent> my_event = new ThreadLocal<>();

/**
* @return State admin authorization is necessary for this endpoint.
*/
public AuthorizationType requiredAuthorization() { return AuthorizationType.STATE; }

/**
* {@inheritDoc}
*/
@Override
protected ASMEvent endpointEvent() {
return my_event.get();
}

/**
* {@inheritDoc}
*/
@Override
protected void reset() {
my_event.set(null);
}

/**
* Get all the ContestResults whose contests are consistently IRV.
* @return A list of all ContestResults for IRV contests.
* @throws RuntimeException if it encounters contests with a mix of IRV and any other contest type.
*/
public static List<ContestResult> getIRVContestResults() {
final String prefix = "[getIRVContestResults]";
final String msg = "Inconsistent contest types:";

// Find all the ContestResults with any that match IRV.
List<ContestResult> results = ContestCounter.countAllContests().stream()
.filter(cr -> cr.getContests().stream().map(Contest::description)
.anyMatch(d -> d.equalsIgnoreCase(ContestType.IRV.toString()))).toList();

// The above should be sufficient, but just in case, check that each contest we found _all_ matches IRV, and
// throw a RuntimeException if not.
for (final ContestResult cr : results) {
if (cr.getContests().stream().map(Contest::description)
.anyMatch(d -> !d.equalsIgnoreCase(ContestType.IRV.toString()))) {
LOGGER.error(String.format("%s %s %s", prefix, msg, cr.getContestName()));
throw new RuntimeException(msg + cr.getContestName());
}
}

return results;
}
}
Loading

0 comments on commit 19746b9

Please sign in to comment.