Make proper HTML interface with presets
This commit is contained in:
parent
dcc3dcc5e7
commit
643e23b6ec
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,5 +1,5 @@
|
|||||||
/.python-version
|
/.python-version
|
||||||
/__target__
|
/__target__
|
||||||
/test
|
/test
|
||||||
/bundle.js
|
/html/bundle.js
|
||||||
__pycache__
|
__pycache__
|
||||||
|
8
build.sh
8
build.sh
@ -1,4 +1,6 @@
|
|||||||
#!/bin/env bash
|
#!/bin/env bash
|
||||||
|
OUTFILE=html/bundle.js
|
||||||
|
|
||||||
# Transcrypt
|
# Transcrypt
|
||||||
transcrypt $@ --nomin pyRCV2.transcrypt || exit 1
|
transcrypt $@ --nomin pyRCV2.transcrypt || exit 1
|
||||||
|
|
||||||
@ -10,7 +12,7 @@ perl -0777 -i -pe 's#export function sum \(iterable\) {\n let result = 0;#exp
|
|||||||
perl -0777 -i -pe 's#key \(a\) > key \(b\) \?#__gt__\(key \(a\), key \(b\)\) \?#g' __target__/org.transcrypt.__runtime__.js || exit 1
|
perl -0777 -i -pe 's#key \(a\) > key \(b\) \?#__gt__\(key \(a\), key \(b\)\) \?#g' __target__/org.transcrypt.__runtime__.js || exit 1
|
||||||
|
|
||||||
# Roll up
|
# Roll up
|
||||||
rollup __target__/pyRCV2.transcrypt.js -o bundle.js --name py -f iife --no-treeshake || exit 1
|
rollup __target__/pyRCV2.transcrypt.js -o $OUTFILE --name py -f iife --no-treeshake || exit 1
|
||||||
|
|
||||||
# Patch
|
# Patch incorrectly generated iterator functions
|
||||||
perl -0777 -i -pe "s#'__index0__'#__index0__#g" bundle.js || exit 1
|
perl -0777 -i -pe "s#'__index0__'#__index0__#g" $OUTFILE || exit 1
|
||||||
|
122
html/index.html
Normal file
122
html/index.html
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>pyRCV2</title>
|
||||||
|
<style type="text/css">
|
||||||
|
html, body {
|
||||||
|
font-family: 'Liberation Sans', FreeSans, Helvetica, Arial, sans-serif;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
td {
|
||||||
|
padding: 0px 8px;
|
||||||
|
}
|
||||||
|
td.count {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
tr:first-child td {
|
||||||
|
vertical-align: bottom;
|
||||||
|
}
|
||||||
|
td.excluded {
|
||||||
|
background-color: #fecfcfff;
|
||||||
|
}
|
||||||
|
td.elected {
|
||||||
|
background-color: #d1fca7ff;
|
||||||
|
}
|
||||||
|
tr.info td {
|
||||||
|
background-color: #edededff;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<input type="file" id="bltFile">
|
||||||
|
<button onclick="clickCount()">Count</button>
|
||||||
|
<label>
|
||||||
|
Preset:
|
||||||
|
<select id="selPreset" onchange="changePreset()">
|
||||||
|
<option value="scottish" selected>Scottish STV</option>
|
||||||
|
<option value="stvc">pyRCV STV-C</option>
|
||||||
|
<option value="prsa77">PRSA 1977</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<button id="btnAdvancedOptions" onclick="clickAdvancedOptions()">Show advanced options</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="divAdvancedOptions" style="display: none;">
|
||||||
|
<label>
|
||||||
|
Quota:
|
||||||
|
<select id="selQuotaCriterion">
|
||||||
|
<option value="geq" selected>>=</option>
|
||||||
|
<option value="gt">></option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<select id="selQuota">
|
||||||
|
<option value="droop" selected>Droop</option>
|
||||||
|
<option value="droop_exact">Droop (exact)</option>
|
||||||
|
<option value="hare">Hare</option>
|
||||||
|
<option value="hare_exact">Hare (exact)</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" id="chkProgQuota">
|
||||||
|
Progressive quota
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" id="chkBulkExclusion">
|
||||||
|
Bulk exclusion (NYI)
|
||||||
|
</label>
|
||||||
|
<br>
|
||||||
|
<label>
|
||||||
|
Numbers:
|
||||||
|
<select id="selNumbers">
|
||||||
|
<option value="native">Native float</option>
|
||||||
|
<option value="int">Native integer</option>
|
||||||
|
<option value="rational">Rational</option>
|
||||||
|
<option value="fixed" selected>Fixed</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
Decimal places (if Numbers = Fixed):
|
||||||
|
<input type="number" id="txtDP" value="5" style="width: 3em;">
|
||||||
|
</label>
|
||||||
|
<br>
|
||||||
|
<label>
|
||||||
|
Surplus order:
|
||||||
|
<select id="selSurplus">
|
||||||
|
<option value="size" selected>By size</option>
|
||||||
|
<option value="order">By order</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
Transfer value (NYI):
|
||||||
|
<select id="selTransfers">
|
||||||
|
<option value="wig" selected>Weighted inclusive Gregory</option>
|
||||||
|
<option value="uig">Unweighted inclusive Gregory</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<br>
|
||||||
|
<label>
|
||||||
|
Ties (NYI):
|
||||||
|
<select id="selTies">
|
||||||
|
<option value="backwards_random" selected>Backwards then random</option>
|
||||||
|
<option value="random">Random</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
Random seed:
|
||||||
|
<input type="text" id="txtSeed" value="Not yet implemented">
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table id="result"></table>
|
||||||
|
|
||||||
|
<script src="http://peterolson.github.com/BigRational.js/BigInt_BigRat.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/big.js@6.0.0/big.min.js"></script>
|
||||||
|
<script src="bundle.js"></script>
|
||||||
|
<script src="index.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
260
html/index.js
Normal file
260
html/index.js
Normal file
@ -0,0 +1,260 @@
|
|||||||
|
/*
|
||||||
|
pyRCV2: Preferential vote counting
|
||||||
|
Copyright © 2020 Lee Yingtong Li (RunasSudo)
|
||||||
|
|
||||||
|
This program is 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.
|
||||||
|
|
||||||
|
This program is 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 this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function clickAdvancedOptions() {
|
||||||
|
if (document.getElementById('divAdvancedOptions').style.display === 'none') {
|
||||||
|
document.getElementById('divAdvancedOptions').style.display = 'block';
|
||||||
|
document.getElementById('btnAdvancedOptions').innerHTML = 'Hide advanced options';
|
||||||
|
} else {
|
||||||
|
document.getElementById('divAdvancedOptions').style.display = 'none';
|
||||||
|
document.getElementById('btnAdvancedOptions').innerHTML = 'Show advanced options';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function changePreset() {
|
||||||
|
if (document.getElementById('selPreset').value === 'scottish') {
|
||||||
|
document.getElementById('selQuotaCriterion').value = 'geq';
|
||||||
|
document.getElementById('selQuota').value = 'droop';
|
||||||
|
document.getElementById('chkProgQuota').checked = false;
|
||||||
|
document.getElementById('chkBulkExclusion').checked = false;
|
||||||
|
document.getElementById('selNumbers').value = 'fixed';
|
||||||
|
document.getElementById('txtDP').value = '5';
|
||||||
|
document.getElementById('selSurplus').value = 'size';
|
||||||
|
document.getElementById('selTransfers').value = 'wig';
|
||||||
|
document.getElementById('selTies').value = 'backwards_random';
|
||||||
|
} else if (document.getElementById('selPreset').value === 'stvc') {
|
||||||
|
document.getElementById('selQuotaCriterion').value = 'gt';
|
||||||
|
document.getElementById('selQuota').value = 'droop_exact';
|
||||||
|
document.getElementById('chkProgQuota').checked = true;
|
||||||
|
document.getElementById('chkBulkExclusion').checked = true;
|
||||||
|
document.getElementById('selNumbers').value = 'rational';
|
||||||
|
document.getElementById('selSurplus').value = 'size';
|
||||||
|
document.getElementById('selTransfers').value = 'wig';
|
||||||
|
document.getElementById('selTies').value = 'backwards_random';
|
||||||
|
} else if (document.getElementById('selPreset').value === 'prsa77') {
|
||||||
|
document.getElementById('selQuotaCriterion').value = 'geq';
|
||||||
|
document.getElementById('selQuota').value = 'droop';
|
||||||
|
document.getElementById('chkProgQuota').checked = false;
|
||||||
|
document.getElementById('chkBulkExclusion').checked = false;
|
||||||
|
document.getElementById('selNumbers').value = 'int';
|
||||||
|
document.getElementById('selSurplus').value = 'order';
|
||||||
|
document.getElementById('selTransfers').value = 'uig';
|
||||||
|
document.getElementById('selTies').value = 'backwards_random';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function clickCount() {
|
||||||
|
// Read BLT file
|
||||||
|
let bltFile = document.getElementById('bltFile').files[0];
|
||||||
|
let text = await bltFile.text();
|
||||||
|
|
||||||
|
// Initialise table rows
|
||||||
|
let tblResults = document.getElementById('result');
|
||||||
|
tblResults.innerHTML = '';
|
||||||
|
let candMap = {}; // candidate name -> rows
|
||||||
|
|
||||||
|
// Step election
|
||||||
|
let worker = new Worker('worker.js');
|
||||||
|
let election, elComment, elExhausted1, elExhausted2, elLTF1, elLTF2, elTotal, elQuota;
|
||||||
|
|
||||||
|
worker.onmessage = function(evt) {
|
||||||
|
if (evt.data.type === 'init') {
|
||||||
|
election = evt.data.election;
|
||||||
|
|
||||||
|
// Comment row
|
||||||
|
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');
|
||||||
|
elExhausted1.classList.add('info');
|
||||||
|
elExhausted2 = document.createElement('tr');
|
||||||
|
elExhausted2.classList.add('info');
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
// Loss to fraction row
|
||||||
|
elLTF1 = document.createElement('tr');
|
||||||
|
elLTF1.classList.add('info');
|
||||||
|
elLTF2 = document.createElement('tr');
|
||||||
|
elLTF2.classList.add('info');
|
||||||
|
|
||||||
|
elTd = document.createElement('td');
|
||||||
|
elTd.setAttribute('rowspan', '2');
|
||||||
|
elTd.style.borderTop = '1px solid black';
|
||||||
|
elTd.innerText = 'Loss to fraction';
|
||||||
|
elLTF1.appendChild(elTd);
|
||||||
|
|
||||||
|
if (document.getElementById('selNumbers').value === 'int') {
|
||||||
|
tblResults.appendChild(elLTF1);
|
||||||
|
tblResults.appendChild(elLTF2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Total row
|
||||||
|
elTotal = document.createElement('tr');
|
||||||
|
elTotal.classList.add('info');
|
||||||
|
elTd = document.createElement('td');
|
||||||
|
elTd.style.borderTop = '1px solid black';
|
||||||
|
elTd.innerText = 'Total';
|
||||||
|
elTotal.appendChild(elTd);
|
||||||
|
tblResults.appendChild(elTotal);
|
||||||
|
|
||||||
|
// Quota row
|
||||||
|
elQuota = document.createElement('tr');
|
||||||
|
elQuota.classList.add('info');
|
||||||
|
elTd = document.createElement('td');
|
||||||
|
elTd.style.borderTop = '1px solid black';
|
||||||
|
elTd.style.borderBottom = '1px solid black';
|
||||||
|
elTd.innerText = 'Quota';
|
||||||
|
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.classList.add('count');
|
||||||
|
if (countCard.state === py.pyRCV2.model.CandidateState.WITHDRAWN || countCard.state === py.pyRCV2.model.CandidateState.EXCLUDED) {
|
||||||
|
elTd.classList.add('excluded');
|
||||||
|
} else if (countCard.state === py.pyRCV2.model.CandidateState.ELECTED || countCard.state === py.pyRCV2.model.CandidateState.PROVISIONALLY_ELECTED) {
|
||||||
|
elTd.classList.add('elected');
|
||||||
|
}
|
||||||
|
elTd.style.borderTop = '1px solid black';
|
||||||
|
if (countCard.transfers != '0.00') {
|
||||||
|
elTd.innerText = countCard.transfers;
|
||||||
|
}
|
||||||
|
elTr1.appendChild(elTd);
|
||||||
|
|
||||||
|
elTd = document.createElement('td');
|
||||||
|
elTd.classList.add('count');
|
||||||
|
if (countCard.state === py.pyRCV2.model.CandidateState.WITHDRAWN) {
|
||||||
|
elTd.classList.add('excluded');
|
||||||
|
elTd.innerText = 'WD';
|
||||||
|
} else if (countCard.state === py.pyRCV2.model.CandidateState.ELECTED || countCard.state === py.pyRCV2.model.CandidateState.PROVISIONALLY_ELECTED) {
|
||||||
|
elTd.classList.add('elected');
|
||||||
|
elTd.innerText = countCard.votes;
|
||||||
|
|
||||||
|
elTr1.querySelector('td:first-child').classList.add('elected');
|
||||||
|
} else if (countCard.state === py.pyRCV2.model.CandidateState.EXCLUDED) {
|
||||||
|
elTd.classList.add('excluded');
|
||||||
|
elTd.innerText = 'EX';
|
||||||
|
} else {
|
||||||
|
elTd.innerText = countCard.votes;
|
||||||
|
}
|
||||||
|
elTr2.appendChild(elTd);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display exhausted votes
|
||||||
|
elTd = document.createElement('td');
|
||||||
|
elTd.classList.add('count');
|
||||||
|
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.classList.add('count');
|
||||||
|
elTd.innerText = result.exhausted.votes;
|
||||||
|
elExhausted2.appendChild(elTd);
|
||||||
|
|
||||||
|
// Display loss to fraction
|
||||||
|
elTd = document.createElement('td');
|
||||||
|
elTd.classList.add('count');
|
||||||
|
elTd.style.borderTop = '1px solid black';
|
||||||
|
if (result.loss_fraction.transfers != '0.00') {
|
||||||
|
elTd.innerText = result.loss_fraction.transfers;
|
||||||
|
}
|
||||||
|
elLTF1.appendChild(elTd);
|
||||||
|
|
||||||
|
elTd = document.createElement('td');
|
||||||
|
elTd.classList.add('count');
|
||||||
|
elTd.innerText = result.loss_fraction.votes;
|
||||||
|
elLTF2.appendChild(elTd);
|
||||||
|
|
||||||
|
// Display total
|
||||||
|
elTd = document.createElement('td');
|
||||||
|
elTd.classList.add('count');
|
||||||
|
elTd.style.borderTop = '1px solid black';
|
||||||
|
elTd.innerText = result.total;
|
||||||
|
elTotal.appendChild(elTd);
|
||||||
|
|
||||||
|
// Display quota
|
||||||
|
elTd = document.createElement('td');
|
||||||
|
elTd.classList.add('count');
|
||||||
|
elTd.style.borderTop = '1px solid black';
|
||||||
|
elTd.style.borderBottom = '1px solid black';
|
||||||
|
elTd.innerText = result.quota;
|
||||||
|
elQuota.appendChild(elTd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
worker.onerror = function(evt) {
|
||||||
|
throw evt;
|
||||||
|
}
|
||||||
|
|
||||||
|
worker.postMessage({
|
||||||
|
'numbers': document.getElementById('selNumbers').value,
|
||||||
|
'fixedDPs': parseInt(document.getElementById('txtDP').value),
|
||||||
|
'options': {
|
||||||
|
'quota_criterion': document.getElementById('selQuotaCriterion').value,
|
||||||
|
'quota': document.getElementById('selQuota').value,
|
||||||
|
'prog_quota': document.getElementById('chkProgQuota').checked,
|
||||||
|
'bulk_exclude': document.getElementById('chkBulkExclusion').checked,
|
||||||
|
'surplus_order': document.getElementById('selSurplus').value,
|
||||||
|
'transfer_value': document.getElementById('selTransfers').value,
|
||||||
|
'ties': document.getElementById('selTies').value
|
||||||
|
},
|
||||||
|
'data': text
|
||||||
|
});
|
||||||
|
}
|
@ -1,10 +1,25 @@
|
|||||||
|
/*
|
||||||
|
pyRCV2: Preferential vote counting
|
||||||
|
Copyright © 2020 Lee Yingtong Li (RunasSudo)
|
||||||
|
|
||||||
|
This program is 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.
|
||||||
|
|
||||||
|
This program is 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 this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
importScripts('http://peterolson.github.com/BigRational.js/BigInt_BigRat.min.js', 'https://cdn.jsdelivr.net/npm/big.js@6.0.0/big.min.js', 'bundle.js');
|
importScripts('http://peterolson.github.com/BigRational.js/BigInt_BigRat.min.js', 'https://cdn.jsdelivr.net/npm/big.js@6.0.0/big.min.js', 'bundle.js');
|
||||||
|
|
||||||
function sleep(ms) {
|
onmessage = function(evt) {
|
||||||
return new Promise(resolve => setTimeout(resolve, ms));
|
// Set settings
|
||||||
}
|
|
||||||
|
|
||||||
onmessage = async function(evt) {
|
|
||||||
if (evt.data.numbers === 'native') {
|
if (evt.data.numbers === 'native') {
|
||||||
py.pyRCV2.numbers.set_numclass(py.pyRCV2.numbers.Native);
|
py.pyRCV2.numbers.set_numclass(py.pyRCV2.numbers.Native);
|
||||||
}
|
}
|
||||||
@ -16,6 +31,7 @@ onmessage = async function(evt) {
|
|||||||
}
|
}
|
||||||
if (evt.data.numbers === 'fixed') {
|
if (evt.data.numbers === 'fixed') {
|
||||||
py.pyRCV2.numbers.set_numclass(py.pyRCV2.numbers.Fixed);
|
py.pyRCV2.numbers.set_numclass(py.pyRCV2.numbers.Fixed);
|
||||||
|
py.pyRCV2.numbers.set_dps(evt.data.fixedDPs);
|
||||||
}
|
}
|
||||||
|
|
||||||
let election = py.pyRCV2.blt.readBLT(evt.data.data);
|
let election = py.pyRCV2.blt.readBLT(evt.data.data);
|
||||||
@ -24,7 +40,7 @@ onmessage = async function(evt) {
|
|||||||
}});
|
}});
|
||||||
|
|
||||||
// Create counter
|
// Create counter
|
||||||
let counter = py.pyRCV2.method.base_stv.BaseSTVCounter(election);
|
let counter = py.pyRCV2.method.base_stv.BaseSTVCounter(election, evt.data.options);
|
||||||
|
|
||||||
// Reset
|
// Reset
|
||||||
let result = counter.reset();
|
let result = counter.reset();
|
||||||
@ -50,7 +66,6 @@ onmessage = async function(evt) {
|
|||||||
|
|
||||||
// Step election
|
// Step election
|
||||||
while (true) {
|
while (true) {
|
||||||
//await sleep(1000);
|
|
||||||
result = counter.step();
|
result = counter.step();
|
||||||
if (py.isinstance(result, py.pyRCV2.model.CountCompleted)) {
|
if (py.isinstance(result, py.pyRCV2.model.CountCompleted)) {
|
||||||
postMessage({'type': 'done'});
|
postMessage({'type': 'done'});
|
237
test.html
237
test.html
@ -1,237 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>pyRCV2 test</title>
|
|
||||||
<style type="text/css">
|
|
||||||
html, body {
|
|
||||||
font-family: 'Liberation Sans', FreeSans, Helvetica, Arial, sans-serif;
|
|
||||||
}
|
|
||||||
table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
}
|
|
||||||
td {
|
|
||||||
padding: 0px 8px;
|
|
||||||
}
|
|
||||||
td.count {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
tr:first-child td {
|
|
||||||
vertical-align: bottom;
|
|
||||||
}
|
|
||||||
td.excluded {
|
|
||||||
background-color: #fecfcfff;
|
|
||||||
}
|
|
||||||
td.elected {
|
|
||||||
background-color: #d1fca7ff;
|
|
||||||
}
|
|
||||||
tr.info td {
|
|
||||||
background-color: #edededff;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<input type="file" id="bltFile">
|
|
||||||
<button onclick="clickBtn();">OK</button>
|
|
||||||
<select id="numbers"><option value="native" selected>Native Float</option><option value="int">Native Integer</option><option value="rational">Rational</option><option value="fixed">Fixed (6 d.p.)</option></select>
|
|
||||||
|
|
||||||
<table id="result"></table>
|
|
||||||
|
|
||||||
<script src="http://peterolson.github.com/BigRational.js/BigInt_BigRat.min.js"></script>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/big.js@6.0.0/big.min.js"></script>
|
|
||||||
<script src="bundle.js"></script>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
async function clickBtn() {
|
|
||||||
// Read BLT file
|
|
||||||
let bltFile = document.getElementById('bltFile').files[0];
|
|
||||||
let text = await bltFile.text();
|
|
||||||
|
|
||||||
// Initialise table rows
|
|
||||||
let tblResults = document.getElementById('result');
|
|
||||||
tblResults.innerHTML = '';
|
|
||||||
let candMap = {}; // candidate name -> rows
|
|
||||||
|
|
||||||
// Step election
|
|
||||||
let worker = new Worker('worker.js');
|
|
||||||
let election, elComment, elExhausted1, elExhausted2, elLTF1, elLTF2, elTotal, elQuota;
|
|
||||||
|
|
||||||
worker.onmessage = function(evt) {
|
|
||||||
if (evt.data.type === 'init') {
|
|
||||||
election = evt.data.election;
|
|
||||||
|
|
||||||
// Comment row
|
|
||||||
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');
|
|
||||||
elExhausted1.classList.add('info');
|
|
||||||
elExhausted2 = document.createElement('tr');
|
|
||||||
elExhausted2.classList.add('info');
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
// Loss to fraction row
|
|
||||||
elLTF1 = document.createElement('tr');
|
|
||||||
elLTF1.classList.add('info');
|
|
||||||
elLTF2 = document.createElement('tr');
|
|
||||||
elLTF2.classList.add('info');
|
|
||||||
|
|
||||||
elTd = document.createElement('td');
|
|
||||||
elTd.setAttribute('rowspan', '2');
|
|
||||||
elTd.style.borderTop = '1px solid black';
|
|
||||||
elTd.innerText = 'Loss to fraction';
|
|
||||||
elLTF1.appendChild(elTd);
|
|
||||||
|
|
||||||
if (document.getElementById('numbers').value === 'int') {
|
|
||||||
tblResults.appendChild(elLTF1);
|
|
||||||
tblResults.appendChild(elLTF2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Total row
|
|
||||||
elTotal = document.createElement('tr');
|
|
||||||
elTotal.classList.add('info');
|
|
||||||
elTd = document.createElement('td');
|
|
||||||
elTd.style.borderTop = '1px solid black';
|
|
||||||
elTd.innerText = 'Total';
|
|
||||||
elTotal.appendChild(elTd);
|
|
||||||
tblResults.appendChild(elTotal);
|
|
||||||
|
|
||||||
// Quota row
|
|
||||||
elQuota = document.createElement('tr');
|
|
||||||
elQuota.classList.add('info');
|
|
||||||
elTd = document.createElement('td');
|
|
||||||
elTd.style.borderTop = '1px solid black';
|
|
||||||
elTd.style.borderBottom = '1px solid black';
|
|
||||||
elTd.innerText = 'Quota';
|
|
||||||
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.classList.add('count');
|
|
||||||
if (countCard.state === py.pyRCV2.model.CandidateState.WITHDRAWN || countCard.state === py.pyRCV2.model.CandidateState.EXCLUDED) {
|
|
||||||
elTd.classList.add('excluded');
|
|
||||||
} else if (countCard.state === py.pyRCV2.model.CandidateState.ELECTED || countCard.state === py.pyRCV2.model.CandidateState.PROVISIONALLY_ELECTED) {
|
|
||||||
elTd.classList.add('elected');
|
|
||||||
}
|
|
||||||
elTd.style.borderTop = '1px solid black';
|
|
||||||
if (countCard.transfers != '0.00') {
|
|
||||||
elTd.innerText = countCard.transfers;
|
|
||||||
}
|
|
||||||
elTr1.appendChild(elTd);
|
|
||||||
|
|
||||||
elTd = document.createElement('td');
|
|
||||||
elTd.classList.add('count');
|
|
||||||
if (countCard.state === py.pyRCV2.model.CandidateState.WITHDRAWN) {
|
|
||||||
elTd.classList.add('excluded');
|
|
||||||
elTd.innerText = 'WD';
|
|
||||||
} else if (countCard.state === py.pyRCV2.model.CandidateState.ELECTED || countCard.state === py.pyRCV2.model.CandidateState.PROVISIONALLY_ELECTED) {
|
|
||||||
elTd.classList.add('elected');
|
|
||||||
elTd.innerText = countCard.votes;
|
|
||||||
|
|
||||||
elTr1.querySelector('td:first-child').classList.add('elected');
|
|
||||||
} else if (countCard.state === py.pyRCV2.model.CandidateState.EXCLUDED) {
|
|
||||||
elTd.classList.add('excluded');
|
|
||||||
elTd.innerText = 'EX';
|
|
||||||
} else {
|
|
||||||
elTd.innerText = countCard.votes;
|
|
||||||
}
|
|
||||||
elTr2.appendChild(elTd);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display exhausted votes
|
|
||||||
elTd = document.createElement('td');
|
|
||||||
elTd.classList.add('count');
|
|
||||||
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.classList.add('count');
|
|
||||||
elTd.innerText = result.exhausted.votes;
|
|
||||||
elExhausted2.appendChild(elTd);
|
|
||||||
|
|
||||||
// Display loss to fraction
|
|
||||||
elTd = document.createElement('td');
|
|
||||||
elTd.classList.add('count');
|
|
||||||
elTd.style.borderTop = '1px solid black';
|
|
||||||
if (result.loss_fraction.transfers != '0.00') {
|
|
||||||
elTd.innerText = result.loss_fraction.transfers;
|
|
||||||
}
|
|
||||||
elLTF1.appendChild(elTd);
|
|
||||||
|
|
||||||
elTd = document.createElement('td');
|
|
||||||
elTd.classList.add('count');
|
|
||||||
elTd.innerText = result.loss_fraction.votes;
|
|
||||||
elLTF2.appendChild(elTd);
|
|
||||||
|
|
||||||
// Display total
|
|
||||||
elTd = document.createElement('td');
|
|
||||||
elTd.classList.add('count');
|
|
||||||
elTd.style.borderTop = '1px solid black';
|
|
||||||
elTd.innerText = result.total;
|
|
||||||
elTotal.appendChild(elTd);
|
|
||||||
|
|
||||||
// Display quota
|
|
||||||
elTd = document.createElement('td');
|
|
||||||
elTd.classList.add('count');
|
|
||||||
elTd.style.borderTop = '1px solid black';
|
|
||||||
elTd.style.borderBottom = '1px solid black';
|
|
||||||
elTd.innerText = result.quota;
|
|
||||||
elQuota.appendChild(elTd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
worker.onerror = function(evt) {
|
|
||||||
throw evt;
|
|
||||||
}
|
|
||||||
|
|
||||||
worker.postMessage({
|
|
||||||
'numbers': document.getElementById('numbers').value,
|
|
||||||
'data': text
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
Reference in New Issue
Block a user