Add detailed commentary to JS report sheet
This commit is contained in:
parent
c3c2fed444
commit
235c17b33a
@ -218,6 +218,10 @@
|
||||
|
||||
<div id="resultLogs"></div>
|
||||
|
||||
<script>
|
||||
var pyRCV2version = 'GITVERSION';
|
||||
</script>
|
||||
|
||||
<script src="vendor/BigInt_BigRat-a5f89e2.min.js"></script>
|
||||
<script src="vendor/big-6.0.0.min.js"></script>
|
||||
<script src="vendor/sjcl-1.0.8.min.js"></script>
|
||||
|
@ -293,6 +293,22 @@ async function clickCount() {
|
||||
|
||||
// Result logs
|
||||
let elP = document.createElement('p');
|
||||
let filePath = document.getElementById('bltFile').value;
|
||||
filePath = filePath.substring(Math.max(filePath.lastIndexOf('\\'), filePath.lastIndexOf('/')) + 1);
|
||||
if (pyRCV2version === 'GITVERSION') {
|
||||
elP.innerText = 'Count computed by pyRCV2 (development version).';
|
||||
} else {
|
||||
elP.innerText = 'Count computed by pyRCV2 (revision ' + pyRCV2version + ').';
|
||||
}
|
||||
elP.innerText += ' Read ' + evt.data.total_ballots + ' ballots from ‘' + filePath + '’ for election ‘' + election.name + '’.';
|
||||
if (evt.data.options === '') {
|
||||
elP.innerText += ' Counting using default options.';
|
||||
} else {
|
||||
elP.innerHTML += ' Counting using options <span style="font-family:monospace;">' + evt.data.options + '</span>.';
|
||||
}
|
||||
divResultLogs.appendChild(elP);
|
||||
|
||||
elP = document.createElement('p');
|
||||
elP.innerText = 'Stage comments:';
|
||||
divResultLogs.appendChild(elP);
|
||||
olLogs = document.createElement('ol');
|
||||
@ -405,6 +421,7 @@ async function clickCount() {
|
||||
|
||||
if (evt.data.type === 'done') {
|
||||
let result = evt.data.result;
|
||||
let winners = [];
|
||||
|
||||
// Display results
|
||||
|
||||
@ -421,12 +438,26 @@ async function clickCount() {
|
||||
} else if (countCard.state === py.pyRCV2.model.CandidateState.ELECTED || countCard.state === py.pyRCV2.model.CandidateState.PROVISIONALLY_ELECTED || countCard.state === py.pyRCV2.model.CandidateState.DISTRIBUTING_SURPLUS) {
|
||||
elTd.classList.add('elected');
|
||||
elTd.innerHTML = 'ELECTED ' + countCard.order_elected;
|
||||
winners.append([candidate, countCard]);
|
||||
}
|
||||
elTd.style.borderTop = '1px solid black';
|
||||
elTr1.appendChild(elTd);
|
||||
}
|
||||
|
||||
elTd.style.borderBottom = '1px solid black';
|
||||
|
||||
let elP = document.createElement('p');
|
||||
elP.innerText = 'Count complete. The winning candidates are, in order of election:'
|
||||
divResultLogs.appendChild(elP);
|
||||
|
||||
winners.sort(function(x1, x2) { return x1[1].order_elected - x2[1].order_elected; });
|
||||
let elOl = document.createElement('ol');
|
||||
for (let [candidate, countCard] of winners) {
|
||||
let elLi = document.createElement('li');
|
||||
elLi.innerText = candidate;
|
||||
elOl.appendChild(elLi);
|
||||
}
|
||||
divResultLogs.appendChild(elOl);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -33,6 +33,12 @@ body {
|
||||
padding-bottom: 0.5em;
|
||||
}
|
||||
|
||||
@media print {
|
||||
.menudiv {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.menudiv .subheading {
|
||||
font-size: 0.8em;
|
||||
font-weight: bold;
|
||||
|
@ -37,9 +37,10 @@ onmessage = function(evt) {
|
||||
ppDPs = evt.data.data.ppDPs;
|
||||
|
||||
let election = py.pyRCV2.blt.readBLT(evt.data.data.data);
|
||||
postMessage({'type': 'init', 'election': {
|
||||
'candidates': election.candidates.map(c => c.py_name)
|
||||
}});
|
||||
let total_ballots = py.pyRCV2.numbers.Num(0);
|
||||
for (let b of election.ballots) {
|
||||
total_ballots.__iadd__(b.value);
|
||||
}
|
||||
|
||||
// Create counter
|
||||
if (evt.data.data.transfers === 'uig') {
|
||||
@ -63,6 +64,16 @@ onmessage = function(evt) {
|
||||
counter.options['ties'] = [tiesPrompt];
|
||||
}
|
||||
|
||||
postMessage({
|
||||
'type': 'init',
|
||||
'election': {
|
||||
'name': election.py_name,
|
||||
'candidates': election.candidates.map(c => c.py_name)
|
||||
},
|
||||
'total_ballots': total_ballots.pp(0),
|
||||
'options': counter.describe_options(),
|
||||
});
|
||||
|
||||
// Reset
|
||||
stage = 1;
|
||||
try {
|
||||
|
@ -36,7 +36,7 @@ def add_parser(subparsers):
|
||||
parser.add_argument('--defer-surpluses', action='store_true', help='defer surplus transfers if possible')
|
||||
parser.add_argument('--numbers', '-n', choices=['fixed', 'rational', 'native'], default='fixed', help='numbers mode (default: fixed)')
|
||||
parser.add_argument('--decimals', type=int, default=5, help='decimal places if --numbers fixed (default: 5)')
|
||||
parser.add_argument('--no-round-quota', action='store_true', help='do not round the quota')
|
||||
#parser.add_argument('--no-round-quota', action='store_true', help='do not round the quota')
|
||||
parser.add_argument('--round-quota', type=int, help='round quota to specified decimal places')
|
||||
parser.add_argument('--round-votes', type=int, help='round votes to specified decimal places')
|
||||
parser.add_argument('--round-tvs', type=int, help='round transfer values to specified decimal places')
|
||||
@ -45,7 +45,7 @@ def add_parser(subparsers):
|
||||
parser.add_argument('--method', '-m', choices=['wig', 'uig', 'eg', 'meek'], default='wig', help='method of transferring surpluses (default: wig - weighted inclusive Gregory)')
|
||||
parser.add_argument('--transferable-only', action='store_true', help='examine only transferable papers during surplus distributions')
|
||||
parser.add_argument('--exclusion', choices=['one_round', 'parcels_by_order', 'by_value', 'wright'], default='one_round', help='how to perform exclusions (default: one_round)')
|
||||
parser.add_argument('--ties', '-t', action='append', choices=['backwards', 'forwards', 'prompt', 'random'], default=None, help='how to resolve ties (default: backwards then random)')
|
||||
parser.add_argument('--ties', '-t', action='append', choices=['backwards', 'forwards', 'prompt', 'random'], default=None, help='how to resolve ties (default: prompt)')
|
||||
parser.add_argument('--random-seed', default=None, help='arbitrary string used to seed the RNG for random tie breaking')
|
||||
parser.add_argument('--hide-excluded', action='store_true', help='hide excluded candidates from results report')
|
||||
parser.add_argument('--sort-votes', action='store_true', help='sort candidates by votes in results report')
|
||||
@ -116,8 +116,8 @@ def main(args):
|
||||
else:
|
||||
counter = WIGSTVCounter(election, vars(args))
|
||||
|
||||
if args.no_round_quota:
|
||||
counter.options['round_quota'] = None
|
||||
#if args.no_round_quota:
|
||||
# counter.options['round_quota'] = None
|
||||
|
||||
if args.ties is None:
|
||||
args.ties = ['prompt']
|
||||
|
@ -17,8 +17,10 @@
|
||||
__pragma__ = lambda x: None
|
||||
|
||||
from pyRCV2.model import CandidateState, CountCard, CountCompleted, CountStepResult
|
||||
import pyRCV2.numbers
|
||||
from pyRCV2.numbers import Num
|
||||
from pyRCV2.safedict import SafeDict
|
||||
import pyRCV2.ties
|
||||
|
||||
class STVException(Exception):
|
||||
def __init__(self, message):
|
||||
@ -535,6 +537,57 @@ class BaseSTVCounter:
|
||||
self.elect_meeting_quota() # Repeat as the vote required for election may have changed
|
||||
return
|
||||
|
||||
def describe_options(self):
|
||||
result = []
|
||||
if self.options['quota'] != 'droop':
|
||||
result.append('--quota ' + self.options['quota'])
|
||||
if self.options['quota_criterion'] != 'geq':
|
||||
result.append('--quota-criterion ' + self.options['quota_criterion'])
|
||||
if self.options['quota_mode'] != 'static':
|
||||
result.append('--quota-mode ' + self.options['quota_mode'])
|
||||
if not self.options['bulk_elect']:
|
||||
result.append('--no-bulk-elect')
|
||||
if self.options['bulk_exclude']:
|
||||
result.append('--bulk-exclude')
|
||||
if self.options['defer_surpluses']:
|
||||
result.append('--defer-surpluses')
|
||||
if pyRCV2.numbers._numclass is pyRCV2.numbers.Rational:
|
||||
result.append('--numbers rational')
|
||||
elif pyRCV2.numbers._numclass is pyRCV2.numbers.Native:
|
||||
result.append('--numbers native')
|
||||
else:
|
||||
# Fixed
|
||||
if pyRCV2.numbers.get_dps() != 5:
|
||||
result.append('--decimals ' + str(pyRCV2.numbers.get_dps()))
|
||||
if self.options['round_quota'] is not None:
|
||||
result.append('--round-quota ' + str(self.options['round_quota']))
|
||||
if self.options['round_votes'] is not None:
|
||||
result.append('--round-votes ' + str(self.options['round_votes']))
|
||||
if self.options['round_tvs'] is not None:
|
||||
result.append('--round-tvs ' + str(self.options['round_tvs']))
|
||||
if self.options['round_weights'] is not None:
|
||||
result.append('--round-weights ' + str(self.options['round_weights']))
|
||||
if self.options['surplus_order'] != 'size':
|
||||
result.append('--surplus-order ' + self.options['surplus_order'])
|
||||
if self.options['papers'] == 'transferable':
|
||||
result.append('--transferable-only')
|
||||
if self.options['exclusion'] != 'one_round':
|
||||
result.append('--exclusion ' + self.options['exclusion'])
|
||||
if len(self.options['ties']) == 0 and isinstance(self.options['ties'][0], pyRCV2.ties.TiesPrompt):
|
||||
pass
|
||||
else:
|
||||
for t in self.options['ties']:
|
||||
if isinstance(t, pyRCV2.ties.TiesBackwards):
|
||||
result.append('--ties backwards')
|
||||
elif isinstance(t, pyRCV2.ties.TiesForwards):
|
||||
result.append('--ties forwards')
|
||||
elif isinstance(t, pyRCV2.ties.TiesRandom):
|
||||
result.append('--ties random')
|
||||
result.append('--random-seed ' + t.random.seed)
|
||||
elif isinstance(t, pyRCV2.ties.TiesPrompt):
|
||||
result.append('--ties prompt')
|
||||
return ' '.join(result)
|
||||
|
||||
# -----------------
|
||||
# UTILITY FUNCTIONS
|
||||
# -----------------
|
||||
|
@ -53,6 +53,10 @@ class WIGSTVCounter(BaseSTVCounter):
|
||||
Basic weighted inclusive Gregory STV counter
|
||||
"""
|
||||
|
||||
def describe_options(self):
|
||||
# WIG is the default
|
||||
return BaseSTVCounter.describe_options(self)
|
||||
|
||||
def do_surplus(self, candidate_surplus, count_card, surplus):
|
||||
next_preferences, total_ballots, total_votes, next_exhausted, exhausted_ballots, exhausted_votes = self.next_preferences(count_card.parcels)
|
||||
|
||||
@ -218,6 +222,10 @@ class UIGSTVCounter(WIGSTVCounter):
|
||||
Basic unweighted inclusive Gregory STV counter
|
||||
"""
|
||||
|
||||
def describe_options(self):
|
||||
"""Overrides BaseSTVCounter.describe_options"""
|
||||
return '--method uig ' + BaseSTVCounter.describe_options(self)
|
||||
|
||||
def __init__(self, *args):
|
||||
WIGSTVCounter.__init__(self, *args)
|
||||
|
||||
@ -301,6 +309,10 @@ class EGSTVCounter(UIGSTVCounter):
|
||||
Exclusive Gregory (last bundle) STV implementation
|
||||
"""
|
||||
|
||||
def describe_options(self):
|
||||
"""Overrides BaseSTVCounter.describe_options"""
|
||||
return '--method eg ' + BaseSTVCounter.describe_options(self)
|
||||
|
||||
def do_surplus(self, candidate_surplus, count_card, surplus):
|
||||
"""Overrides UIGSTVCounter.do_surplus"""
|
||||
|
||||
|
@ -38,12 +38,15 @@ class MeekCountCard(CountCard):
|
||||
return result
|
||||
|
||||
class MeekSTVCounter(BaseSTVCounter):
|
||||
def describe_options(self):
|
||||
"""Overrides BaseSTVCounter.describe_options"""
|
||||
return '--method meek ' + BaseSTVCounter.describe_options(self)
|
||||
|
||||
def __init__(self, *args):
|
||||
BaseSTVCounter.__init__(self, *args)
|
||||
self.candidates = SafeDict([(c, MeekCountCard()) for c in self.election.candidates])
|
||||
|
||||
self._quota_tolerance_ub = Num('1.0001')
|
||||
self._quota_tolerance_lb = Num('0.9999')
|
||||
self._quota_tolerance = Num('1.0001')
|
||||
|
||||
def reset(self):
|
||||
if self.options['quota_mode'] != 'progressive':
|
||||
@ -149,7 +152,7 @@ class MeekSTVCounter(BaseSTVCounter):
|
||||
|
||||
# Do surpluses need to be distributed?
|
||||
__pragma__('opov')
|
||||
has_surplus = [(c, cc) for c, cc in self.candidates.items() if cc.state == CandidateState.ELECTED and cc.votes / self.quota > self._quota_tolerance_ub]
|
||||
has_surplus = [(c, cc) for c, cc in self.candidates.items() if cc.state == CandidateState.ELECTED and cc.votes / self.quota > self._quota_tolerance]
|
||||
__pragma__('noopov')
|
||||
|
||||
if len(has_surplus) > 0:
|
||||
@ -171,7 +174,7 @@ class MeekSTVCounter(BaseSTVCounter):
|
||||
self.compute_quota()
|
||||
|
||||
__pragma__('opov')
|
||||
has_surplus = [(c, cc) for c, cc in self.candidates.items() if cc.state == CandidateState.ELECTED and cc.votes / self.quota > self._quota_tolerance_ub]
|
||||
has_surplus = [(c, cc) for c, cc in self.candidates.items() if cc.state == CandidateState.ELECTED and cc.votes / self.quota > self._quota_tolerance]
|
||||
__pragma__('noopov')
|
||||
|
||||
if num_iterations == 1:
|
||||
|
Reference in New Issue
Block a user