Perform counting asynchronously in web worker

This commit is contained in:
RunasSudo 2020-10-17 23:38:42 +11:00
parent fa18641462
commit 25d16eb8b8
Signed by: RunasSudo
GPG Key ID: 7234E476BF21C61A
2 changed files with 153 additions and 101 deletions

View File

@ -28,21 +28,21 @@
let bltFile = document.getElementById('bltFile').files[0]; let bltFile = document.getElementById('bltFile').files[0];
let text = await bltFile.text(); let text = await bltFile.text();
let election = py.pyRCV2.blt.readBLT(text);
// Create counter
let counter = py.pyRCV2.method.STVCCounter.STVCCounter(election);
// Reset
let result = counter.reset();
// Initialise table rows // Initialise table rows
let tblResults = document.getElementById('result'); let tblResults = document.getElementById('result');
tblResults.innerHTML = ''; tblResults.innerHTML = '';
let candMap = new Map(); // Map Candidate -> rows let candMap = {}; // candidate name -> rows
// Step election
let worker = new Worker('worker.js');
let election, elComment, elExhausted1, elExhausted2, elQuota;
worker.onmessage = function(evt) {
if (evt.data.type == 'init') {
election = evt.data.election;
// Comment row // Comment row
let elComment = document.createElement('tr'); elComment = document.createElement('tr');
let elTd = document.createElement('td'); let elTd = document.createElement('td');
elComment.appendChild(elTd); elComment.appendChild(elTd);
tblResults.appendChild(elComment); tblResults.appendChild(elComment);
@ -55,18 +55,18 @@
elTd = document.createElement('td'); elTd = document.createElement('td');
elTd.setAttribute('rowspan', '2'); elTd.setAttribute('rowspan', '2');
elTd.style.borderTop = '1px solid black'; elTd.style.borderTop = '1px solid black';
elTd.innerText = candidate.py_name; elTd.innerText = candidate;
elTr1.appendChild(elTd); elTr1.appendChild(elTd);
tblResults.appendChild(elTr1); tblResults.appendChild(elTr1);
tblResults.appendChild(elTr2); tblResults.appendChild(elTr2);
candMap.set(candidate, [elTr1, elTr2]); candMap[candidate] = [elTr1, elTr2];
} }
// Exhausted votes row // Exhausted votes row
let elExhausted1 = document.createElement('tr'); elExhausted1 = document.createElement('tr');
let elExhausted2 = document.createElement('tr'); elExhausted2 = document.createElement('tr');
elTd = document.createElement('td'); elTd = document.createElement('td');
elTd.setAttribute('rowspan', '2'); elTd.setAttribute('rowspan', '2');
@ -78,29 +78,29 @@
tblResults.appendChild(elExhausted2); tblResults.appendChild(elExhausted2);
// Quota row // Quota row
let elQuota = document.createElement('tr'); elQuota = document.createElement('tr');
elTd = document.createElement('td'); elTd = document.createElement('td');
elTd.style.borderTop = '1px solid black'; elTd.style.borderTop = '1px solid black';
elTd.innerText = 'Quota'; elTd.innerText = 'Quota';
elQuota.appendChild(elTd); elQuota.appendChild(elTd);
tblResults.appendChild(elQuota); tblResults.appendChild(elQuota);
}
// Step election if (evt.data.type == 'result') {
result = counter.reset(); let result = evt.data.result;
do {
// Display results // Display results
elTd = document.createElement('td'); elTd = document.createElement('td');
elTd.innerText = result.comment; elTd.innerText = result.comment;
elComment.appendChild(elTd); elComment.appendChild(elTd);
for (let [candidate, countCard] of result.candidates.impl) { for (let [candidate, countCard] of result.candidates) {
[elTr1, elTr2] = candMap.get(candidate); [elTr1, elTr2] = candMap[candidate];
elTd = document.createElement('td'); elTd = document.createElement('td');
elTd.style.borderTop = '1px solid black'; elTd.style.borderTop = '1px solid black';
if (countCard.transfers.pp(2) != '0.00') { if (countCard.transfers != '0.00') {
elTd.innerText = countCard.transfers.pp(2); elTd.innerText = countCard.transfers;
} }
elTr1.appendChild(elTd); elTr1.appendChild(elTd);
@ -108,12 +108,12 @@
if (countCard.state == py.pyRCV2.model.CandidateState.WITHDRAWN) { if (countCard.state == py.pyRCV2.model.CandidateState.WITHDRAWN) {
elTd.innerText = 'WD'; elTd.innerText = 'WD';
} else if (countCard.state == py.pyRCV2.model.CandidateState.ELECTED || countCard.state == py.pyRCV2.model.CandidateState.PROVISIONALLY_ELECTED) { } else if (countCard.state == py.pyRCV2.model.CandidateState.ELECTED || countCard.state == py.pyRCV2.model.CandidateState.PROVISIONALLY_ELECTED) {
elTd.innerText = countCard.votes.pp(2); elTd.innerText = countCard.votes;
elTd.style.fontWeight = 'bold'; elTd.style.fontWeight = 'bold';
} else if (countCard.state == py.pyRCV2.model.CandidateState.EXCLUDED) { } else if (countCard.state == py.pyRCV2.model.CandidateState.EXCLUDED) {
elTd.innerText = 'EX'; elTd.innerText = 'EX';
} else { } else {
elTd.innerText = countCard.votes.pp(2); elTd.innerText = countCard.votes;
} }
elTr2.appendChild(elTd); elTr2.appendChild(elTd);
} }
@ -121,27 +121,28 @@
// Display exhausted votes // Display exhausted votes
elTd = document.createElement('td'); elTd = document.createElement('td');
elTd.style.borderTop = '1px solid black'; elTd.style.borderTop = '1px solid black';
if (result.exhausted.transfers.pp(2) != '0.00') { if (result.exhausted.transfers != '0.00') {
elTd.innerText = result.exhausted.transfers.pp(2); elTd.innerText = result.exhausted.transfers;
} }
elExhausted1.appendChild(elTd); elExhausted1.appendChild(elTd);
elTd = document.createElement('td'); elTd = document.createElement('td');
elTd.innerText = result.exhausted.votes.pp(2); elTd.innerText = result.exhausted.votes;
elExhausted2.appendChild(elTd); elExhausted2.appendChild(elTd);
// Display quota // Display quota
elTd = document.createElement('td'); elTd = document.createElement('td');
elTd.style.borderTop = '1px solid black'; elTd.style.borderTop = '1px solid black';
elTd.innerText = result.quota.pp(2); elTd.innerText = result.quota;
elQuota.appendChild(elTd); elQuota.appendChild(elTd);
// Step election
result = counter.step();
if (py.isinstance(result, py.pyRCV2.model.CountCompleted)) {
break;
} }
} while (true); }
worker.onerror = function(evt) {
throw evt;
}
worker.postMessage(text);
} }
</script> </script>
</body> </body>

51
worker.js Normal file
View File

@ -0,0 +1,51 @@
importScripts('http://peterolson.github.com/BigRational.js/BigInt_BigRat.min.js', 'bundle.js');
onmessage = function(evt) {
let election = py.pyRCV2.blt.readBLT(evt.data);
postMessage({'type': 'init', 'election': {
'candidates': election.candidates.map(c => c.py_name)
}});
// Create counter
let counter = py.pyRCV2.method.STVCCounter.STVCCounter(election);
// Reset
let result = counter.reset();
postMessage({'type': 'result', 'result': {
'comment': result.comment,
'candidates': result.candidates.py_items().map(([c, cc]) => [c.py_name, {
'transfers': cc.transfers.pp(2),
'votes': cc.votes.pp(2),
'state': cc.state
}]),
'exhausted': {
'transfers': result.exhausted.transfers.pp(2),
'votes': result.exhausted.votes.pp(2)
},
'quota': result.quota.pp(2)
}});
// Step election
while (true) {
result = counter.step();
if (py.isinstance(result, py.pyRCV2.model.CountCompleted)) {
postMessage({'type': 'done'});
break;
} else {
postMessage({'type': 'result', 'result': {
'comment': result.comment,
'candidates': result.candidates.py_items().map(([c, cc]) => [c.py_name, {
'transfers': cc.transfers.pp(2),
'votes': cc.votes.pp(2),
'state': cc.state
}]),
'exhausted': {
'transfers': result.exhausted.transfers.pp(2),
'votes': result.exhausted.votes.pp(2)
},
'quota': result.quota.pp(2)
}});
}
}
}