Display order of election in count report
This commit is contained in:
parent
7afd8d7bfd
commit
7ee56f97f8
@ -361,6 +361,32 @@ async function clickCount() {
|
|||||||
elVRE.appendChild(elTd);
|
elVRE.appendChild(elTd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (evt.data.type === 'done') {
|
||||||
|
let result = evt.data.result;
|
||||||
|
|
||||||
|
// Display results
|
||||||
|
|
||||||
|
for (let [candidate, countCard] of result.candidates) {
|
||||||
|
[elTr1, elTr2] = candMap[candidate];
|
||||||
|
elTd = document.createElement('td');
|
||||||
|
elTd.setAttribute('rowspan', '2');
|
||||||
|
if (countCard.state === py.pyRCV2.model.CandidateState.WITHDRAWN) {
|
||||||
|
elTd.classList.add('excluded');
|
||||||
|
elTd.innerHTML = 'Withdrawn';
|
||||||
|
} else if (countCard.state === py.pyRCV2.model.CandidateState.EXCLUDED || countCard.state === py.pyRCV2.model.CandidateState.EXCLUDING) {
|
||||||
|
elTd.classList.add('excluded');
|
||||||
|
elTd.innerHTML = 'Excluded ' + (-countCard.order_elected);
|
||||||
|
} 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;
|
||||||
|
}
|
||||||
|
elTd.style.borderTop = '1px solid black';
|
||||||
|
elTr1.appendChild(elTd);
|
||||||
|
}
|
||||||
|
|
||||||
|
elTd.style.borderBottom = '1px solid black';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
worker.onerror = function(evt) {
|
worker.onerror = function(evt) {
|
||||||
|
@ -65,26 +65,7 @@ onmessage = function(evt) {
|
|||||||
stage = 1;
|
stage = 1;
|
||||||
result = counter.reset();
|
result = counter.reset();
|
||||||
|
|
||||||
postMessage({'type': 'result', 'result': {
|
postMessage({'type': 'result', 'result': resultToJS(result)});
|
||||||
'stage': stage,
|
|
||||||
'comment': result.comment,
|
|
||||||
'candidates': result.candidates.py_items().map(([c, cc]) => [c.py_name, {
|
|
||||||
'transfers': cc.transfers.pp(ppDP),
|
|
||||||
'votes': cc.votes.pp(ppDP),
|
|
||||||
'state': cc.state
|
|
||||||
}]),
|
|
||||||
'exhausted': {
|
|
||||||
'transfers': result.exhausted.transfers.pp(ppDP),
|
|
||||||
'votes': result.exhausted.votes.pp(ppDP)
|
|
||||||
},
|
|
||||||
'loss_fraction': {
|
|
||||||
'transfers': result.loss_fraction.transfers.pp(ppDP),
|
|
||||||
'votes': result.loss_fraction.votes.pp(ppDP)
|
|
||||||
},
|
|
||||||
'total': result.total.pp(ppDP),
|
|
||||||
'quota': result.quota.pp(ppDP),
|
|
||||||
'vote_required_election': result.vote_required_election === null ? null : result.vote_required_election.pp(ppDP),
|
|
||||||
}});
|
|
||||||
|
|
||||||
stepElection();
|
stepElection();
|
||||||
} else if (evt.data.type === 'require_input') {
|
} else if (evt.data.type === 'require_input') {
|
||||||
@ -115,16 +96,23 @@ function stepElection() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (py.isinstance(result, py.pyRCV2.model.CountCompleted)) {
|
if (py.isinstance(result, py.pyRCV2.model.CountCompleted)) {
|
||||||
postMessage({'type': 'done'});
|
postMessage({'type': 'done', 'result': resultToJS(result)});
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
postMessage({'type': 'result', 'result': {
|
postMessage({'type': 'result', 'result': resultToJS(result)});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resultToJS(result) {
|
||||||
|
return {
|
||||||
'stage': stage,
|
'stage': stage,
|
||||||
'comment': result.comment,
|
'comment': result.comment,
|
||||||
'candidates': result.candidates.py_items().map(([c, cc]) => [c.py_name, {
|
'candidates': result.candidates.py_items().map(([c, cc]) => [c.py_name, {
|
||||||
'transfers': cc.transfers.pp(ppDP),
|
'transfers': cc.transfers.pp(ppDP),
|
||||||
'votes': cc.votes.pp(ppDP),
|
'votes': cc.votes.pp(ppDP),
|
||||||
'state': cc.state
|
'state': cc.state,
|
||||||
|
'order_elected': cc.order_elected,
|
||||||
}]),
|
}]),
|
||||||
'exhausted': {
|
'exhausted': {
|
||||||
'transfers': result.exhausted.transfers.pp(ppDP),
|
'transfers': result.exhausted.transfers.pp(ppDP),
|
||||||
@ -137,7 +125,5 @@ function stepElection() {
|
|||||||
'total': result.total.pp(ppDP),
|
'total': result.total.pp(ppDP),
|
||||||
'quota': result.quota.pp(ppDP),
|
'quota': result.quota.pp(ppDP),
|
||||||
'vote_required_election': result.vote_required_election === null ? null : result.vote_required_election.pp(ppDP),
|
'vote_required_election': result.vote_required_election === null ? null : result.vote_required_election.pp(ppDP),
|
||||||
}});
|
};
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -60,9 +60,9 @@ def print_step(args, stage, result):
|
|||||||
for candidate, count_card in results:
|
for candidate, count_card in results:
|
||||||
state = None
|
state = None
|
||||||
if count_card.state == pyRCV2.model.CandidateState.ELECTED or count_card.state == pyRCV2.model.CandidateState.PROVISIONALLY_ELECTED or count_card.state == pyRCV2.model.CandidateState.DISTRIBUTING_SURPLUS:
|
if count_card.state == pyRCV2.model.CandidateState.ELECTED or count_card.state == pyRCV2.model.CandidateState.PROVISIONALLY_ELECTED or count_card.state == pyRCV2.model.CandidateState.DISTRIBUTING_SURPLUS:
|
||||||
state = 'ELECTED'
|
state = 'ELECTED {}'.format(count_card.order_elected)
|
||||||
elif count_card.state == pyRCV2.model.CandidateState.EXCLUDED or count_card.state == pyRCV2.model.CandidateState.EXCLUDING:
|
elif count_card.state == pyRCV2.model.CandidateState.EXCLUDED or count_card.state == pyRCV2.model.CandidateState.EXCLUDING:
|
||||||
state = 'Excluded'
|
state = 'Excluded {}'.format(-count_card.order_elected)
|
||||||
elif count_card.state == pyRCV2.model.CandidateState.WITHDRAWN:
|
elif count_card.state == pyRCV2.model.CandidateState.WITHDRAWN:
|
||||||
state = 'Withdrawn'
|
state = 'Withdrawn'
|
||||||
|
|
||||||
|
@ -89,6 +89,7 @@ class BaseSTVCounter:
|
|||||||
self.total_orig = sum((b.value for b in self.election.ballots), Num('0'))
|
self.total_orig = sum((b.value for b in self.election.ballots), Num('0'))
|
||||||
|
|
||||||
self.num_elected = 0
|
self.num_elected = 0
|
||||||
|
self.num_excluded = 0
|
||||||
|
|
||||||
# Withdraw candidates
|
# Withdraw candidates
|
||||||
for candidate in self.election.withdrawn:
|
for candidate in self.election.withdrawn:
|
||||||
@ -196,17 +197,28 @@ class BaseSTVCounter:
|
|||||||
|
|
||||||
# Have sufficient candidates been elected?
|
# Have sufficient candidates been elected?
|
||||||
if self.num_elected >= self.election.seats:
|
if self.num_elected >= self.election.seats:
|
||||||
return CountCompleted()
|
__pragma__('opov')
|
||||||
|
return CountCompleted(
|
||||||
|
'Count complete',
|
||||||
|
self.candidates,
|
||||||
|
self.exhausted,
|
||||||
|
self.loss_fraction,
|
||||||
|
self.total + self.exhausted.votes + self.loss_fraction.votes,
|
||||||
|
self.quota,
|
||||||
|
self.vote_required_election,
|
||||||
|
)
|
||||||
|
__pragma__('noopov')
|
||||||
|
|
||||||
# Are there just enough candidates to fill all the seats?
|
# Are there just enough candidates to fill all the seats?
|
||||||
if self.options['bulk_elect']:
|
if self.options['bulk_elect']:
|
||||||
if self.num_elected + sum(1 for c, cc in self.candidates.items() if cc.state == CandidateState.HOPEFUL or cc.state == CandidateState.EXCLUDING) <= self.election.seats:
|
# Include EXCLUDING to avoid interrupting an exclusion
|
||||||
|
if len(self.election.candidates) - self.num_excluded + sum(1 for c, cc in self.candidates.items() if cc.state == CandidateState.EXCLUDING) <= self.election.seats:
|
||||||
# Declare elected all remaining candidates
|
# Declare elected all remaining candidates
|
||||||
for candidate, count_card in self.candidates.items():
|
for candidate, count_card in self.candidates.items():
|
||||||
if count_card.state == CandidateState.HOPEFUL:
|
if count_card.state == CandidateState.HOPEFUL:
|
||||||
count_card.state = CandidateState.PROVISIONALLY_ELECTED
|
count_card.state = CandidateState.PROVISIONALLY_ELECTED
|
||||||
count_card.order_elected = self.num_elected
|
|
||||||
self.num_elected += 1
|
self.num_elected += 1
|
||||||
|
count_card.order_elected = self.num_elected
|
||||||
|
|
||||||
__pragma__('opov')
|
__pragma__('opov')
|
||||||
result = CountStepResult(
|
result = CountStepResult(
|
||||||
@ -338,15 +350,15 @@ class BaseSTVCounter:
|
|||||||
|
|
||||||
# If we did not perform bulk election in before_surpluses: Are there just enough candidates to fill all the seats?
|
# If we did not perform bulk election in before_surpluses: Are there just enough candidates to fill all the seats?
|
||||||
if not self.options['bulk_elect']:
|
if not self.options['bulk_elect']:
|
||||||
if self.num_elected + sum(1 for c, cc in self.candidates.items() if cc.state == CandidateState.HOPEFUL) <= self.election.seats:
|
if len(self.election.candidates) - self.num_excluded <= self.election.seats:
|
||||||
# Declare elected one remaining candidate at a time
|
# Declare elected one remaining candidate at a time
|
||||||
hopefuls = [(c, cc) for c, cc in self.candidates.items() if cc.state == CandidateState.HOPEFUL]
|
hopefuls = [(c, cc) for c, cc in self.candidates.items() if cc.state == CandidateState.HOPEFUL]
|
||||||
hopefuls.sort(key=lambda x: x[1].votes, reverse=True)
|
hopefuls.sort(key=lambda x: x[1].votes, reverse=True)
|
||||||
candidate_elected, count_card = self.choose_highest(hopefuls)
|
candidate_elected, count_card = self.choose_highest(hopefuls)
|
||||||
|
|
||||||
count_card.state = CandidateState.PROVISIONALLY_ELECTED
|
count_card.state = CandidateState.PROVISIONALLY_ELECTED
|
||||||
count_card.order_elected = self.num_elected
|
|
||||||
self.num_elected += 1
|
self.num_elected += 1
|
||||||
|
count_card.order_elected = self.num_elected
|
||||||
|
|
||||||
__pragma__('opov')
|
__pragma__('opov')
|
||||||
result = CountStepResult(
|
result = CountStepResult(
|
||||||
@ -370,7 +382,10 @@ class BaseSTVCounter:
|
|||||||
|
|
||||||
candidates_excluded = self.candidates_to_exclude()
|
candidates_excluded = self.candidates_to_exclude()
|
||||||
for candidate, count_card in candidates_excluded:
|
for candidate, count_card in candidates_excluded:
|
||||||
|
if count_card.state != CandidateState.EXCLUDING:
|
||||||
count_card.state = CandidateState.EXCLUDING
|
count_card.state = CandidateState.EXCLUDING
|
||||||
|
self.num_excluded += 1
|
||||||
|
count_card.order_elected = -self.num_excluded
|
||||||
|
|
||||||
# Handle Wright STV
|
# Handle Wright STV
|
||||||
if self.options['exclusion'] == 'wright':
|
if self.options['exclusion'] == 'wright':
|
||||||
@ -433,7 +448,7 @@ class BaseSTVCounter:
|
|||||||
Returns List[Tuple[Candidate, CountCard]]
|
Returns List[Tuple[Candidate, CountCard]]
|
||||||
"""
|
"""
|
||||||
|
|
||||||
remaining_candidates = self.num_elected + sum(1 for c, cc in self.candidates.items() if cc.state == CandidateState.HOPEFUL)
|
remaining_candidates = len(self.election.candidates) - self.num_excluded
|
||||||
__pragma__('opov')
|
__pragma__('opov')
|
||||||
total_surpluses = sum((cc.votes - self.quota for c, cc in self.candidates.items() if cc.votes > self.quota), Num(0))
|
total_surpluses = sum((cc.votes - self.quota for c, cc in self.candidates.items() if cc.votes > self.quota), Num(0))
|
||||||
__pragma__('noopov')
|
__pragma__('noopov')
|
||||||
@ -574,8 +589,8 @@ class BaseSTVCounter:
|
|||||||
candidate, count_card = x[0], x[1]
|
candidate, count_card = x[0], x[1]
|
||||||
|
|
||||||
count_card.state = CandidateState.PROVISIONALLY_ELECTED
|
count_card.state = CandidateState.PROVISIONALLY_ELECTED
|
||||||
count_card.order_elected = self.num_elected
|
|
||||||
self.num_elected += 1
|
self.num_elected += 1
|
||||||
|
count_card.order_elected = self.num_elected
|
||||||
|
|
||||||
meets_quota.remove(x)
|
meets_quota.remove(x)
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ class CountCard:
|
|||||||
self.orig_votes = Num('0')
|
self.orig_votes = Num('0')
|
||||||
self.transfers = Num('0')
|
self.transfers = Num('0')
|
||||||
self.state = CandidateState.HOPEFUL
|
self.state = CandidateState.HOPEFUL
|
||||||
self.order_elected = None
|
self.order_elected = None # Negative for order of exclusion
|
||||||
|
|
||||||
# self.parcels = List[Parcel]
|
# self.parcels = List[Parcel]
|
||||||
# Parcel = List[Tuple[Ballot, Num]]
|
# Parcel = List[Tuple[Ballot, Num]]
|
||||||
@ -94,9 +94,6 @@ class CountCard:
|
|||||||
result.order_elected = self.order_elected
|
result.order_elected = self.order_elected
|
||||||
return result
|
return result
|
||||||
|
|
||||||
class CountCompleted:
|
|
||||||
pass
|
|
||||||
|
|
||||||
class CountStepResult:
|
class CountStepResult:
|
||||||
def __init__(self, comment, candidates, exhausted, loss_fraction, total, quota, vote_required_election):
|
def __init__(self, comment, candidates, exhausted, loss_fraction, total, quota, vote_required_election):
|
||||||
self.comment = comment
|
self.comment = comment
|
||||||
@ -119,3 +116,6 @@ class CountStepResult:
|
|||||||
__pragma__('noopov')
|
__pragma__('noopov')
|
||||||
|
|
||||||
return CountStepResult(self.comment, candidates, self.exhausted.clone(), self.loss_fraction.clone(), self.total, self.quota)
|
return CountStepResult(self.comment, candidates, self.exhausted.clone(), self.loss_fraction.clone(), self.total, self.quota)
|
||||||
|
|
||||||
|
class CountCompleted(CountStepResult):
|
||||||
|
pass
|
||||||
|
Reference in New Issue
Block a user