Perform counting asynchronously in web worker
This commit is contained in:
parent
fa18641462
commit
25d16eb8b8
197
test.html
197
test.html
@ -28,120 +28,121 @@
|
|||||||
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
|
||||||
|
|
||||||
// Comment row
|
|
||||||
let elComment = document.createElement('tr');
|
|
||||||
let elTd = document.createElement('td');
|
|
||||||
elComment.appendChild(elTd);
|
|
||||||
tblResults.appendChild(elComment);
|
|
||||||
|
|
||||||
// Candidates
|
|
||||||
for (let candidate of election.candidates) {
|
|
||||||
let elTr1 = document.createElement('tr');
|
|
||||||
let elTr2 = document.createElement('tr');
|
|
||||||
|
|
||||||
elTd = document.createElement('td');
|
|
||||||
elTd.setAttribute('rowspan', '2');
|
|
||||||
elTd.style.borderTop = '1px solid black';
|
|
||||||
elTd.innerText = candidate.py_name;
|
|
||||||
elTr1.appendChild(elTd);
|
|
||||||
|
|
||||||
tblResults.appendChild(elTr1);
|
|
||||||
tblResults.appendChild(elTr2);
|
|
||||||
|
|
||||||
candMap.set(candidate, [elTr1, elTr2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exhausted votes row
|
|
||||||
let elExhausted1 = document.createElement('tr');
|
|
||||||
let elExhausted2 = document.createElement('tr');
|
|
||||||
|
|
||||||
elTd = document.createElement('td');
|
|
||||||
elTd.setAttribute('rowspan', '2');
|
|
||||||
elTd.style.borderTop = '1px solid black';
|
|
||||||
elTd.innerText = 'Exhausted';
|
|
||||||
elExhausted1.appendChild(elTd);
|
|
||||||
|
|
||||||
tblResults.appendChild(elExhausted1);
|
|
||||||
tblResults.appendChild(elExhausted2);
|
|
||||||
|
|
||||||
// Quota row
|
|
||||||
let elQuota = document.createElement('tr');
|
|
||||||
elTd = document.createElement('td');
|
|
||||||
elTd.style.borderTop = '1px solid black';
|
|
||||||
elTd.innerText = 'Quota';
|
|
||||||
elQuota.appendChild(elTd);
|
|
||||||
tblResults.appendChild(elQuota);
|
|
||||||
|
|
||||||
// Step election
|
// Step election
|
||||||
result = counter.reset();
|
let worker = new Worker('worker.js');
|
||||||
|
let election, elComment, elExhausted1, elExhausted2, elQuota;
|
||||||
|
|
||||||
do {
|
worker.onmessage = function(evt) {
|
||||||
// Display results
|
if (evt.data.type == 'init') {
|
||||||
elTd = document.createElement('td');
|
election = evt.data.election;
|
||||||
elTd.innerText = result.comment;
|
|
||||||
elComment.appendChild(elTd);
|
|
||||||
|
|
||||||
for (let [candidate, countCard] of result.candidates.impl) {
|
// Comment row
|
||||||
[elTr1, elTr2] = candMap.get(candidate);
|
elComment = document.createElement('tr');
|
||||||
|
let elTd = document.createElement('td');
|
||||||
|
elComment.appendChild(elTd);
|
||||||
|
tblResults.appendChild(elComment);
|
||||||
|
|
||||||
|
// Candidates
|
||||||
|
for (let candidate of election.candidates) {
|
||||||
|
let elTr1 = document.createElement('tr');
|
||||||
|
let elTr2 = document.createElement('tr');
|
||||||
|
|
||||||
|
elTd = document.createElement('td');
|
||||||
|
elTd.setAttribute('rowspan', '2');
|
||||||
|
elTd.style.borderTop = '1px solid black';
|
||||||
|
elTd.innerText = candidate;
|
||||||
|
elTr1.appendChild(elTd);
|
||||||
|
|
||||||
|
tblResults.appendChild(elTr1);
|
||||||
|
tblResults.appendChild(elTr2);
|
||||||
|
|
||||||
|
candMap[candidate] = [elTr1, elTr2];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exhausted votes row
|
||||||
|
elExhausted1 = document.createElement('tr');
|
||||||
|
elExhausted2 = document.createElement('tr');
|
||||||
|
|
||||||
|
elTd = document.createElement('td');
|
||||||
|
elTd.setAttribute('rowspan', '2');
|
||||||
|
elTd.style.borderTop = '1px solid black';
|
||||||
|
elTd.innerText = 'Exhausted';
|
||||||
|
elExhausted1.appendChild(elTd);
|
||||||
|
|
||||||
|
tblResults.appendChild(elExhausted1);
|
||||||
|
tblResults.appendChild(elExhausted2);
|
||||||
|
|
||||||
|
// Quota row
|
||||||
|
elQuota = document.createElement('tr');
|
||||||
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') {
|
elTd.innerText = 'Quota';
|
||||||
elTd.innerText = countCard.transfers.pp(2);
|
elQuota.appendChild(elTd);
|
||||||
|
tblResults.appendChild(elQuota);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (evt.data.type == 'result') {
|
||||||
|
let result = evt.data.result;
|
||||||
|
|
||||||
|
// Display results
|
||||||
|
elTd = document.createElement('td');
|
||||||
|
elTd.innerText = result.comment;
|
||||||
|
elComment.appendChild(elTd);
|
||||||
|
|
||||||
|
for (let [candidate, countCard] of result.candidates) {
|
||||||
|
[elTr1, elTr2] = candMap[candidate];
|
||||||
|
|
||||||
|
elTd = document.createElement('td');
|
||||||
|
elTd.style.borderTop = '1px solid black';
|
||||||
|
if (countCard.transfers != '0.00') {
|
||||||
|
elTd.innerText = countCard.transfers;
|
||||||
|
}
|
||||||
|
elTr1.appendChild(elTd);
|
||||||
|
|
||||||
|
elTd = document.createElement('td');
|
||||||
|
if (countCard.state == py.pyRCV2.model.CandidateState.WITHDRAWN) {
|
||||||
|
elTd.innerText = 'WD';
|
||||||
|
} else if (countCard.state == py.pyRCV2.model.CandidateState.ELECTED || countCard.state == py.pyRCV2.model.CandidateState.PROVISIONALLY_ELECTED) {
|
||||||
|
elTd.innerText = countCard.votes;
|
||||||
|
elTd.style.fontWeight = 'bold';
|
||||||
|
} else if (countCard.state == py.pyRCV2.model.CandidateState.EXCLUDED) {
|
||||||
|
elTd.innerText = 'EX';
|
||||||
|
} else {
|
||||||
|
elTd.innerText = countCard.votes;
|
||||||
|
}
|
||||||
|
elTr2.appendChild(elTd);
|
||||||
}
|
}
|
||||||
elTr1.appendChild(elTd);
|
|
||||||
|
// Display exhausted votes
|
||||||
|
elTd = document.createElement('td');
|
||||||
|
elTd.style.borderTop = '1px solid black';
|
||||||
|
if (result.exhausted.transfers != '0.00') {
|
||||||
|
elTd.innerText = result.exhausted.transfers;
|
||||||
|
}
|
||||||
|
elExhausted1.appendChild(elTd);
|
||||||
|
|
||||||
elTd = document.createElement('td');
|
elTd = document.createElement('td');
|
||||||
if (countCard.state == py.pyRCV2.model.CandidateState.WITHDRAWN) {
|
elTd.innerText = result.exhausted.votes;
|
||||||
elTd.innerText = 'WD';
|
elExhausted2.appendChild(elTd);
|
||||||
} else if (countCard.state == py.pyRCV2.model.CandidateState.ELECTED || countCard.state == py.pyRCV2.model.CandidateState.PROVISIONALLY_ELECTED) {
|
|
||||||
elTd.innerText = countCard.votes.pp(2);
|
// Display quota
|
||||||
elTd.style.fontWeight = 'bold';
|
elTd = document.createElement('td');
|
||||||
} else if (countCard.state == py.pyRCV2.model.CandidateState.EXCLUDED) {
|
elTd.style.borderTop = '1px solid black';
|
||||||
elTd.innerText = 'EX';
|
elTd.innerText = result.quota;
|
||||||
} else {
|
elQuota.appendChild(elTd);
|
||||||
elTd.innerText = countCard.votes.pp(2);
|
|
||||||
}
|
|
||||||
elTr2.appendChild(elTd);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Display exhausted votes
|
worker.onerror = function(evt) {
|
||||||
elTd = document.createElement('td');
|
throw evt;
|
||||||
elTd.style.borderTop = '1px solid black';
|
}
|
||||||
if (result.exhausted.transfers.pp(2) != '0.00') {
|
|
||||||
elTd.innerText = result.exhausted.transfers.pp(2);
|
|
||||||
}
|
|
||||||
elExhausted1.appendChild(elTd);
|
|
||||||
|
|
||||||
elTd = document.createElement('td');
|
worker.postMessage(text);
|
||||||
elTd.innerText = result.exhausted.votes.pp(2);
|
|
||||||
elExhausted2.appendChild(elTd);
|
|
||||||
|
|
||||||
// Display quota
|
|
||||||
elTd = document.createElement('td');
|
|
||||||
elTd.style.borderTop = '1px solid black';
|
|
||||||
elTd.innerText = result.quota.pp(2);
|
|
||||||
elQuota.appendChild(elTd);
|
|
||||||
|
|
||||||
// Step election
|
|
||||||
result = counter.step();
|
|
||||||
if (py.isinstance(result, py.pyRCV2.model.CountCompleted)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} while (true);
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
51
worker.js
Normal file
51
worker.js
Normal 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)
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user