Skip to content

Commit

Permalink
Merge pull request #137 from chamil321/fix-test-driver
Browse files Browse the repository at this point in the history
Fix test driver to work with parliamentary date set
  • Loading branch information
MaryamZi authored Jul 24, 2020
2 parents 2b4f336 + 73b760a commit 069fc92
Show file tree
Hide file tree
Showing 5 changed files with 225 additions and 41 deletions.
12 changes: 6 additions & 6 deletions distributor/src/distributor/results.bal
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ service receiveResults on resultsListener {
imageData: ()
};
log:printInfo("Result data received for " + electionCode + "/" + resultType + "/" + resultCode);

// store the result in the DB against the resultCode and assign it a sequence #
CumulativeResult? resCumResult = check saveResult(result);

Expand Down Expand Up @@ -204,15 +204,15 @@ function cleanupParliamentaryJson(map<json> jin) {
j["seat_count"] = (j.seat_count == "") ? 0 : <int>'int:fromString(<string>j.seat_count);
}
if j.national_list_seat_count is string {
j["national_list_seat_count"] = (j.national_list_seat_count == "") ? 0 :
j["national_list_seat_count"] = (j.national_list_seat_count == "") ? 0 :
<int>'int:fromString(<string>j.national_list_seat_count);
}
}
}

if !(jin.hasKey("summary")) {
return;
}
}

cleanUpSummaryJson(<map<json>>jin.summary);
}
Expand Down Expand Up @@ -268,13 +268,13 @@ function sendParliamentaryIncrementalResult(CumulativeResult resCumResult, strin
string resultCode, Result result) returns error? {
if resCumResult is ParliamentaryCumulativeVotesResult {
return sendParliamentaryIncrementalVotesResult(resCumResult, electionCode, "R_VI", resultCode, result);
}
}

return sendParliamentaryIncrementalSeatsResult(resCumResult, electionCode, "R_SI", resultCode, result);
}

function sendParliamentaryIncrementalVotesResult(CumulativeResult resCumResult, string electionCode, string resultType,
string resultCode, Result result) returns error? {
function sendParliamentaryIncrementalVotesResult(CumulativeResult resCumResult, string electionCode, string resultType,
string resultCode, Result result) returns error? {
log:printInfo("Publishing cumulative result with " + electionCode + "/" + resultType + "/" + resultCode);

ParliamentaryCumulativeVotesResult parliamentaryCumResult = <ParliamentaryCumulativeVotesResult> resCumResult;
Expand Down
103 changes: 70 additions & 33 deletions testdriver/src/testdriver/controller.bal
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ listener http:Listener hl = new(9999);
boolean alreadyRunning = false;
int runCount = 0; // used to generate a unique election code

// TODO : Fix this properly - Negation issue
//function(http:Caller caller, http:Request req, string electionCode) returns error? startResults =
// electionType == ELECTION_TYPE_PARLIAMENTARY ? startParliamentaryResults : startPresidentialResults;
function(http:Caller caller, http:Request req, string electionCode) returns error? startResults =
startParliamentaryResults;

@http:ServiceConfig {
basePath: "/"
}
Expand All @@ -24,7 +30,8 @@ service TestController on hl {
body += "<div class='container-fluid'>";
body += "<h1>Select a test to run:</h1>";
body += "<ul>";
foreach var [code, [name, _, _, _, _]] in tests.entries() {
//TODO make it generic
foreach var [code, [name, _, _, _, _]] in parliamentaryTests.entries() {
body += "<li><a href='/start/" + code + "'>" + name + "</a></li>";
}
body += "</ul>";
Expand All @@ -43,38 +50,68 @@ service TestController on hl {
path: "/start/{electionCode}"
}
resource function start(http:Caller caller, http:Request req, string electionCode) returns error? {
if alreadyRunning {
return caller->ok("Test already running; try again later.");
}
string ec = <@untainted>electionCode;
if !tests.hasKey(ec) {
http:Response hr = new;
hr.statusCode = 404;
hr.setTextPayload("No such election to run tests with: " + ec);
check caller->respond(hr);
}
check caller->ok("Test data publishing starting.");

// yes i know race condition possible .. need to use lock to do this better (after it becomes non experimental)
alreadyRunning = true;

[ string, map<map<json>>[], map<json>[], map<map<json>>[], map<json>[] ]
[electionName, results, resultsByPD, results2, resultsByPD2] = tests.get(ec);


log:printInfo("Publishing new result set for " + ec + " starting at " + time:currentTime().toString());

http:Client rc = new (resultsURL);
_ = check rc->get("/result/reset"); // reset the results store
var e = sendResults("PRESIDENTIAL-FIRST", ec, rc, results, resultsByPD);
check startResults(caller, req, electionCode);
}
}

function startParliamentaryResults(http:Caller caller, http:Request req, string electionCode) returns error? {
if alreadyRunning {
return caller->ok("Test already running; try again later.");
}
string ec = <@untainted>electionCode;
if ec != "FAKE" {
check caller->notFound("No such election to run tests with: " + ec);
}
check caller->ok("Test data publishing starting.");

// yes i know race condition possible .. need to use lock to do this better (after it becomes non experimental)
alreadyRunning = true;

[ string, map<map<json>>[], map<json>[], map<map<json>>[], map<json>[] ]
[electionName, _ , parliamentaryFake, _ , _ ] = parliamentaryTests.get(ec);


log:printInfo("Publishing new result set for " + electionName + " starting at " + time:currentTime().toString());

http:Client rc = new (resultsURL);
_ = check rc->get("/result/reset"); // reset the results store

var e = sendParliamentaryResults(electionName, rc, parliamentaryFake);
if e is error {
log:printError("Error publishing results: " + e.toString());
}
alreadyRunning = false;
}

function startPresidentialResults(http:Caller caller, http:Request req, string electionCode) returns error? {
if alreadyRunning {
return caller->ok("Test already running; try again later.");
}
string ec = <@untainted>electionCode;
if !tests.hasKey(ec) {
check caller->notFound("No such election to run tests with: " + ec);
}
check caller->ok("Test data publishing starting.");

// yes i know race condition possible .. need to use lock to do this better (after it becomes non experimental)
alreadyRunning = true;

[ string, map<map<json>>[], map<json>[], map<map<json>>[], map<json>[] ]
[electionName, results, resultsByPD, results2, resultsByPD2] = tests.get(ec);


log:printInfo("Publishing new result set for " + ec + " starting at " + time:currentTime().toString());

http:Client rc = new (resultsURL);
_ = check rc->get("/result/reset"); // reset the results store
var e = sendResults("PRESIDENTIAL-FIRST", ec, rc, results, resultsByPD);
if e is error {
log:printError("Error publishing results: " + e.toString());
} else if results2.length() != 0 {
e = sendResults("PRESIDENTIAL-PREFS", ec, rc, results2, resultsByPD2);
if e is error {
log:printError("Error publishing results: " + e.toString());
} else if results2.length() != 0 {
e = sendResults("PRESIDENTIAL-PREFS", ec, rc, results2, resultsByPD2);
if e is error {
log:printError("Error publishing preference results: " + e.toString());
}
log:printError("Error publishing preference results: " + e.toString());
}
alreadyRunning = false;
}
}
alreadyRunning = false;
}
3 changes: 3 additions & 0 deletions testdriver/src/testdriver/gen_fake_parliamentary.bal
Original file line number Diff line number Diff line change
Expand Up @@ -413,4 +413,7 @@ function genFakeParliamentary() {
]
}
];

// load date to parliamentaryFake[]
parliamentaryFake.push(...data);
}
40 changes: 38 additions & 2 deletions testdriver/src/testdriver/main.bal
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import ballerina/lang.'int;

const ELECTION_TYPE_PRESIDENTIAL = "PRESIDENTIAL";
const ELECTION_TYPE_PARLIAMENTARY = "PARLIAMENTARY";

public type ElectionType ELECTION_TYPE_PRESIDENTIAL|ELECTION_TYPE_PARLIAMENTARY;

ElectionType electionType = ELECTION_TYPE_PARLIAMENTARY;

// data loaded from file
json[] data2015 = [];

Expand All @@ -17,17 +24,46 @@ map<json>[] resultsByPDFake = [];
map<map<json>>[] resultsFake2 = [];
map<json>[] resultsByPDFake2 = [];

map<json>[] parliamentaryFake = [];

// electionCode, first round results array, first round results by PD, second round results array, second round result by PD
map<[ string, map<map<json>>[], map<json>[], map<map<json>>[], map<json>[] ]> tests = {
map<[string, map<map<json>>[], map<json>[], map<map<json>>[], map<json>[]]> tests = {
"2015PB": [ "2015 Playback", results2015, resultsByPD2015, [], [] ],
"2019Empty": [ "2019 EMPTY TEST", results2019, resultsByPD2019, [], [] ],
"FAKE": [ "MOCK ELECTION", resultsFake, resultsByPDFake, resultsFake2, resultsByPDFake2 ]
};

map<[string, map<map<json>>[], map<json>[], map<map<json>>[], map<json>[]]> parliamentaryTests = {
"FAKE": [ "FAKE_ELECTION", [], parliamentaryFake, [], [] ]
};

function(string url, int delay) returns @untainted error? loadData =
electionType == ELECTION_TYPE_PARLIAMENTARY ? loadParliamentarylData : loadPresidentialData;

int sleeptime = 0;
string resultsURL = "";

public function main(string url = "http://localhost:8181", int delay = 10000) returns @untainted error? {
public function main(string url = "http://localhost:8181",
int delay = 10000,
ElectionType mode = ELECTION_TYPE_PARLIAMENTARY
) returns @untainted error? {
// Set the election type
electionType = <@untainted>mode;

check loadData(url, delay);
}

function loadParliamentarylData(string url, int delay) returns @untainted error? {

resultsURL = <@untainted> url;
sleeptime = <@untainted> delay;

// generate fake parliamentary data
genFakeParliamentary();
}

function loadPresidentialData(string url, int delay) returns @untainted error? {

resultsURL = <@untainted> url;
sleeptime = <@untainted> delay;

Expand Down
108 changes: 108 additions & 0 deletions testdriver/src/testdriver/sendparliamentaryresults.bal
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import ballerina/encoding;
import ballerina/http;
import ballerina/io;
import ballerina/runtime;
import ballerina/time;

const R_V = "R_V";
const R_S = "R_S";
const R_VS = "R_VS";
const R_VSN = "R_VSN";
const R_SC = "R_SC";
const R_NC = "R_NC";
const R_SCNC = "R_SCNC";

const FINAL = "FINAL";

function sendParliamentaryResults(string electionCode, http:Client rc, map<json>[] results) returns error? {

foreach map<json> result in results {

string resultType = result.'type.toString();
match resultType {
R_V => {
check updateByParty(<json[]>result.by_party);
check updateSummary(result);
check feedResult(rc, electionCode, resultType, result.pd_code.toString(), result);
}
R_S => {
check updateByParty(<json[]>result.by_party);
check feedResult(rc, electionCode, resultType, result.ed_code.toString(), result);
}
R_VS => {
check updateByParty(<json[]>result.by_party);
check updateSummary(result);
check feedResult(rc, electionCode, resultType, FINAL, result);
}
R_VSN => {
check updateByParty(<json[]>result.by_party);
check updateSummary(result);
check feedResult(rc, electionCode, resultType, FINAL, result);
}
R_SC => {
check feedResult(rc, electionCode, resultType, result.ed_code.toString(), result);
}
R_NC => {
check feedResult(rc, electionCode, resultType, FINAL, result);
}
R_SCNC => {
check feedResult(rc, electionCode, resultType, FINAL, result);
}
}
// delay a bit
runtime:sleep(sleeptime);
}
}

function updateByParty(json[] byPartyJson) returns error? {
// add missing info in test data:
foreach int i in 0 ..< byPartyJson.length() {
map<json> party = <map<json>>byPartyJson[i];
// change percentage to string
json val = party["vote_percentage"];

if val is float {
party["vote_percentage"] = io:sprintf ("%.2f", val);
} else {
// already a string so let it go
}
}
}

function updateSummary(map<json> result) returns error? {
// set the percentages in the summary
map<json> summary = <map<json>>result.summary;
summary["percent_valid"] = (<int>result.summary.polled == 0) ? "0.00" : io:sprintf("%.2f", <int>result.summary.valid*100.0/<int>result.summary.polled);
summary["percent_rejected"] = (<int>result.summary.polled == 0) ? "0.00" : io:sprintf("%.2f", <int>result.summary.rejected*100.0/<int>result.summary.polled);
summary["percent_polled"] = (<int>result.summary.electors == 0) ? "0.00" : io:sprintf("%.2f", <int>result.summary.polled*100.0/<int>result.summary.electors);
}


function feedResult (http:Client hc, string electionCode, string resType, string resCode, map<json> result) returns
error? {
// reset time stamp of the result to now
result["timestamp"] = check time:format(time:currentTime(), "yyyy-MM-dd'T'HH:mm:ss.SSSZ");
http:Response hr;

// sent alert
string params = "/?level=" + <string>result.level;
if result.ed_name is string {
params += "&ed_name=" + check encoding:encodeUriComponent(<string>result.ed_name, "UTF-8");
if result.pd_name is string {
params += "&pd_name=" + check encoding:encodeUriComponent(<string>result.pd_name, "UTF-8");
}
}
hr = check hc->post ("/result/notification/" + electionCode + "/" + resType + "/" + resCode + params, <json>{});
if hr.statusCode != http:STATUS_ACCEPTED {
io:println("Error while posting result notification to: /result/notification/" + electionCode + "/" + resType + "/" + resCode + params);
io:println("\tstatus=", hr.statusCode, ", contentType=", hr.getContentType(), " payload=", hr.getTextPayload());
return error ("Unable to post notification for " + resCode);
}

hr = check hc->post ("/result/data/" + electionCode + "/" + resType + "/" + resCode, result);
if hr.statusCode != http:STATUS_ACCEPTED {
io:println("Error while posting result to: /result/data/" + electionCode + "/" + resType + "/" + resCode);
io:println("\tstatus=", hr.statusCode, ", contentType=", hr.getContentType(), " payload=", hr.getTextPayload());
return error ("Unable to post result for " + resCode);
}
}

0 comments on commit 069fc92

Please sign in to comment.