Implement transposed votes report
This commit is contained in:
parent
61e4eefca3
commit
59c1da794e
@ -171,6 +171,7 @@ Multiple constraints are supported using the method described by Hill ([*Voting
|
|||||||
### Report style
|
### Report style
|
||||||
|
|
||||||
* *Votes only*: The result sheet displays the number of votes held by each candidate at each stage of the count.
|
* *Votes only*: The result sheet displays the number of votes held by each candidate at each stage of the count.
|
||||||
|
* *Votes (transposed)*: Same as *Votes only*, but transfers are displayed to the left of, rather than above, progress totals.
|
||||||
* *Ballots and votes*: The result sheet displays the number of votes *and ballot papers* held by each candidate at each stage of the count.
|
* *Ballots and votes*: The result sheet displays the number of votes *and ballot papers* held by each candidate at each stage of the count.
|
||||||
|
|
||||||
This functionality is not available on the command line.
|
This functionality is not available on the command line.
|
||||||
|
@ -190,13 +190,10 @@
|
|||||||
Report style:
|
Report style:
|
||||||
<select id="selReport">
|
<select id="selReport">
|
||||||
<option value="votes" selected>Votes only</option>
|
<option value="votes" selected>Votes only</option>
|
||||||
|
<option value="votes_transposed">Votes (transposed)</option>
|
||||||
<option value="ballots_votes">Ballots and votes</option>
|
<option value="ballots_votes">Ballots and votes</option>
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
<label style="display:none;">
|
|
||||||
<input type="checkbox" id="chkReportTranspose" disabled>
|
|
||||||
Transpose transfers/<wbr>totals
|
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
<label class="col-12">
|
<label class="col-12">
|
||||||
Display up to
|
Display up to
|
||||||
|
@ -184,7 +184,6 @@ async function clickCount() {
|
|||||||
'decimals': document.getElementById('txtDP').value,
|
'decimals': document.getElementById('txtDP').value,
|
||||||
'normaliseBallots': document.getElementById('chkNormaliseBallots').checked,
|
'normaliseBallots': document.getElementById('chkNormaliseBallots').checked,
|
||||||
'reportStyle': document.getElementById('selReport').value,
|
'reportStyle': document.getElementById('selReport').value,
|
||||||
'reportTranspose': document.getElementById('chkReportTranspose').checked,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
279
src/stv/wasm.rs
279
src/stv/wasm.rs
@ -324,19 +324,35 @@ fn should_show_vre(opts: &stv::STVOptions) -> bool {
|
|||||||
/// Generate the first column of the HTML results table
|
/// Generate the first column of the HTML results table
|
||||||
fn init_results_table<N: Number>(election: &Election<N>, opts: &stv::STVOptions, report_style: &str) -> String {
|
fn init_results_table<N: Number>(election: &Election<N>, opts: &stv::STVOptions, report_style: &str) -> String {
|
||||||
let mut result = String::from(r#"<tr class="stage-no"><td rowspan="3"></td></tr><tr class="stage-kind"></tr><tr class="stage-comment"></tr>"#);
|
let mut result = String::from(r#"<tr class="stage-no"><td rowspan="3"></td></tr><tr class="stage-kind"></tr><tr class="stage-comment"></tr>"#);
|
||||||
match report_style {
|
|
||||||
"ballots_votes" => {
|
if report_style == "ballots_votes" {
|
||||||
result.push_str(&r#"<tr class="hint-papers-votes"><td></td></tr>"#);
|
result.push_str(&r#"<tr class="hint-papers-votes"><td></td></tr>"#);
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for candidate in election.candidates.iter() {
|
for candidate in election.candidates.iter() {
|
||||||
result.push_str(&format!(r#"<tr class="candidate transfers"><td rowspan="2">{}</td></tr><tr class="candidate votes"></tr>"#, candidate.name));
|
if report_style == "votes_transposed" {
|
||||||
|
result.push_str(&format!(r#"<tr class="candidate transfers"><td>{}</td></tr>"#, candidate.name));
|
||||||
|
} else {
|
||||||
|
result.push_str(&format!(r#"<tr class="candidate transfers"><td rowspan="2">{}</td></tr><tr class="candidate votes"></tr>"#, candidate.name));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
result.push_str(r#"<tr class="info transfers"><td rowspan="2">Exhausted</td></tr><tr class="info votes"></tr><tr class="info transfers"><td rowspan="2">Loss by fraction</td></tr><tr class="info votes"></tr><tr class="info transfers"><td>Total</td></tr><tr class="info transfers"><td>Quota</td></tr>"#);
|
|
||||||
|
if report_style == "votes_transposed" {
|
||||||
|
result.push_str(r#"<tr class="info transfers"><td>Exhausted</td></tr>"#);
|
||||||
|
} else {
|
||||||
|
result.push_str(r#"<tr class="info transfers"><td rowspan="2">Exhausted</td></tr><tr class="info votes"></tr>"#);
|
||||||
|
}
|
||||||
|
|
||||||
|
if report_style == "votes_transposed" {
|
||||||
|
result.push_str(r#"<tr class="info transfers"><td>Loss by fraction</td></tr><tr class="info transfers"><td>Total</td></tr><tr class="info transfers"><td>Quota</td></tr>"#);
|
||||||
|
} else {
|
||||||
|
result.push_str(r#"<tr class="info transfers"><td rowspan="2">Loss by fraction</td></tr><tr class="info votes"></tr><tr class="info transfers"><td>Total</td></tr><tr class="info transfers"><td>Quota</td></tr>"#);
|
||||||
|
}
|
||||||
|
|
||||||
if should_show_vre(opts) {
|
if should_show_vre(opts) {
|
||||||
result.push_str(r#"<tr class="info transfers"><td>Vote required for election</td></tr>"#);
|
result.push_str(r#"<tr class="info transfers"><td>Vote required for election</td></tr>"#);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -345,25 +361,48 @@ fn update_results_table<N: Number>(stage_num: usize, state: &CountState<N>, opts
|
|||||||
let result = Array::new();
|
let result = Array::new();
|
||||||
|
|
||||||
// Insert borders to left of new exclusions in Wright STV
|
// Insert borders to left of new exclusions in Wright STV
|
||||||
let mut tdclasses1 = "";
|
let classes_o; // Outer version
|
||||||
let mut tdclasses2 = "";
|
let classes_i; // Inner version
|
||||||
if opts.exclusion == stv::ExclusionMethod::Wright && state.kind == Some("Exclusion of") {
|
if opts.exclusion == stv::ExclusionMethod::Wright && state.kind == Some("Exclusion of") {
|
||||||
tdclasses1 = r#" class="blw""#;
|
classes_o = r#" class="blw""#;
|
||||||
tdclasses2 = r#"blw "#;
|
classes_i = r#"blw "#;
|
||||||
|
} else {
|
||||||
|
classes_o = "";
|
||||||
|
classes_i = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hide transfers column for first preferences if transposed
|
||||||
|
let hide_xfers_trsp;
|
||||||
|
if state.title == "First preferences" || (opts.exclusion == stv::ExclusionMethod::Wright && state.kind == Some("Exclusion of")) {
|
||||||
|
hide_xfers_trsp = true;
|
||||||
|
} else {
|
||||||
|
hide_xfers_trsp = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Header rows
|
// Header rows
|
||||||
match report_style {
|
match report_style {
|
||||||
"votes" => {
|
"votes" => {
|
||||||
result.push(&format!(r##"<td{0}><a href="#stage{1}">{1}</a></td>"##, tdclasses1, stage_num).into());
|
result.push(&format!(r##"<td{0}><a href="#stage{1}">{1}</a></td>"##, classes_o, stage_num).into());
|
||||||
result.push(&format!(r#"<td{}>{}</td>"#, tdclasses1, state.kind.unwrap_or("")).into());
|
result.push(&format!(r#"<td{}>{}</td>"#, classes_o, state.kind.unwrap_or("")).into());
|
||||||
result.push(&format!(r#"<td{}>{}</td>"#, tdclasses1, state.title).into());
|
result.push(&format!(r#"<td{}>{}</td>"#, classes_o, state.title).into());
|
||||||
|
}
|
||||||
|
"votes_transposed" => {
|
||||||
|
if hide_xfers_trsp {
|
||||||
|
result.push(&format!(r##"<td{0}><a href="#stage{1}">{1}</a></td>"##, classes_o, stage_num).into());
|
||||||
|
result.push(&format!(r#"<td{}>{}</td>"#, classes_o, state.kind.unwrap_or("")).into());
|
||||||
|
result.push(&format!(r#"<td{}>{}</td>"#, classes_o, state.title).into());
|
||||||
|
} else {
|
||||||
|
result.push(&format!(r##"<td{0} colspan="2"><a href="#stage{1}">{1}</a></td>"##, classes_o, stage_num).into());
|
||||||
|
result.push(&format!(r#"<td{} colspan="2">{}</td>"#, classes_o, state.kind.unwrap_or("")).into());
|
||||||
|
result.push(&format!(r#"<td{} colspan="2">{}</td>"#, classes_o, state.title).into());
|
||||||
|
//result.push(&format!(r#"<td{}>X'fers</td><td>Total</td>"#, tdclasses1).into());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"ballots_votes" => {
|
"ballots_votes" => {
|
||||||
result.push(&format!(r##"<td{0} colspan="2"><a href="#stage{1}">{1}</a></td>"##, tdclasses1, stage_num).into());
|
result.push(&format!(r##"<td{0} colspan="2"><a href="#stage{1}">{1}</a></td>"##, classes_o, stage_num).into());
|
||||||
result.push(&format!(r#"<td{} colspan="2">{}</td>"#, tdclasses1, state.kind.unwrap_or("")).into());
|
result.push(&format!(r#"<td{} colspan="2">{}</td>"#, classes_o, state.kind.unwrap_or("")).into());
|
||||||
result.push(&format!(r#"<td{} colspan="2">{}</td>"#, tdclasses1, state.title).into());
|
result.push(&format!(r#"<td{} colspan="2">{}</td>"#, classes_o, state.title).into());
|
||||||
result.push(&format!(r#"<td{}>Ballots</td><td>Votes</td>"#, tdclasses1).into());
|
result.push(&format!(r#"<td{}>Ballots</td><td>Votes</td>"#, classes_o).into());
|
||||||
}
|
}
|
||||||
_ => unreachable!("Invalid report_style")
|
_ => unreachable!("Invalid report_style")
|
||||||
}
|
}
|
||||||
@ -371,31 +410,80 @@ fn update_results_table<N: Number>(stage_num: usize, state: &CountState<N>, opts
|
|||||||
for candidate in state.election.candidates.iter() {
|
for candidate in state.election.candidates.iter() {
|
||||||
let count_card = &state.candidates[candidate];
|
let count_card = &state.candidates[candidate];
|
||||||
|
|
||||||
|
// TODO: REFACTOR THIS!!
|
||||||
|
|
||||||
match report_style {
|
match report_style {
|
||||||
"votes" => {
|
"votes" => {
|
||||||
match count_card.state {
|
match count_card.state {
|
||||||
CandidateState::Hopeful | CandidateState::Guarded => {
|
CandidateState::Hopeful | CandidateState::Guarded => {
|
||||||
result.push(&format!(r#"<td class="{}count">{}</td>"#, tdclasses2, pp(&count_card.transfers, opts.pp_decimals)).into());
|
result.push(&format!(r#"<td class="{}count">{}</td>"#, classes_i, pp(&count_card.transfers, opts.pp_decimals)).into());
|
||||||
result.push(&format!(r#"<td class="{}count">{}</td>"#, tdclasses2, pp(&count_card.votes, opts.pp_decimals)).into());
|
result.push(&format!(r#"<td class="{}count">{}</td>"#, classes_i, pp(&count_card.votes, opts.pp_decimals)).into());
|
||||||
}
|
}
|
||||||
CandidateState::Elected => {
|
CandidateState::Elected => {
|
||||||
result.push(&format!(r#"<td class="{}count elected">{}</td>"#, tdclasses2, pp(&count_card.transfers, opts.pp_decimals)).into());
|
result.push(&format!(r#"<td class="{}count elected">{}</td>"#, classes_i, pp(&count_card.transfers, opts.pp_decimals)).into());
|
||||||
result.push(&format!(r#"<td class="{}count elected">{}</td>"#, tdclasses2, pp(&count_card.votes, opts.pp_decimals)).into());
|
result.push(&format!(r#"<td class="{}count elected">{}</td>"#, classes_i, pp(&count_card.votes, opts.pp_decimals)).into());
|
||||||
}
|
}
|
||||||
CandidateState::Doomed => {
|
CandidateState::Doomed => {
|
||||||
result.push(&format!(r#"<td class="{}count excluded">{}</td>"#, tdclasses2, pp(&count_card.transfers, opts.pp_decimals)).into());
|
result.push(&format!(r#"<td class="{}count excluded">{}</td>"#, classes_i, pp(&count_card.transfers, opts.pp_decimals)).into());
|
||||||
result.push(&format!(r#"<td class="{}count excluded">{}</td>"#, tdclasses2, pp(&count_card.votes, opts.pp_decimals)).into());
|
result.push(&format!(r#"<td class="{}count excluded">{}</td>"#, classes_i, pp(&count_card.votes, opts.pp_decimals)).into());
|
||||||
}
|
}
|
||||||
CandidateState::Withdrawn => {
|
CandidateState::Withdrawn => {
|
||||||
result.push(&format!(r#"<td class="{}count excluded"></td>"#, tdclasses2).into());
|
result.push(&format!(r#"<td class="{}count excluded"></td>"#, classes_i).into());
|
||||||
result.push(&format!(r#"<td class="{}count excluded">WD</td>"#, tdclasses2).into());
|
result.push(&format!(r#"<td class="{}count excluded">WD</td>"#, classes_i).into());
|
||||||
}
|
}
|
||||||
CandidateState::Excluded => {
|
CandidateState::Excluded => {
|
||||||
result.push(&format!(r#"<td class="{}count excluded">{}</td>"#, tdclasses2, pp(&count_card.transfers, opts.pp_decimals)).into());
|
result.push(&format!(r#"<td class="{}count excluded">{}</td>"#, classes_i, pp(&count_card.transfers, opts.pp_decimals)).into());
|
||||||
if count_card.votes.is_zero() && count_card.parcels.iter().all(|p| p.votes.is_empty()) {
|
if count_card.votes.is_zero() && count_card.parcels.iter().all(|p| p.votes.is_empty()) {
|
||||||
result.push(&format!(r#"<td class="{}count excluded">Ex</td>"#, tdclasses2).into());
|
result.push(&format!(r#"<td class="{}count excluded">Ex</td>"#, classes_i).into());
|
||||||
} else {
|
} else {
|
||||||
result.push(&format!(r#"<td class="{}count excluded">{}</td>"#, tdclasses2, pp(&count_card.votes, opts.pp_decimals)).into());
|
result.push(&format!(r#"<td class="{}count excluded">{}</td>"#, classes_i, pp(&count_card.votes, opts.pp_decimals)).into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"votes_transposed" => {
|
||||||
|
match count_card.state {
|
||||||
|
CandidateState::Hopeful | CandidateState::Guarded => {
|
||||||
|
if hide_xfers_trsp {
|
||||||
|
result.push(&format!(r#"<td class="{}count">{}</td>"#, classes_i, pp(&count_card.votes, opts.pp_decimals)).into());
|
||||||
|
} else {
|
||||||
|
result.push(&format!(r#"<td class="{}count">{}</td><td class="count">{}</td>"#, classes_i, pps(&count_card.transfers, opts.pp_decimals), pp(&count_card.votes, opts.pp_decimals)).into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CandidateState::Elected => {
|
||||||
|
if hide_xfers_trsp {
|
||||||
|
result.push(&format!(r#"<td class="{}count elected">{}</td>"#, classes_i, pp(&count_card.votes, opts.pp_decimals)).into());
|
||||||
|
} else {
|
||||||
|
result.push(&format!(r#"<td class="{}count elected">{}</td><td class="count elected">{}</td>"#, classes_i, pps(&count_card.transfers, opts.pp_decimals), pp(&count_card.votes, opts.pp_decimals)).into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CandidateState::Doomed => {
|
||||||
|
if hide_xfers_trsp {
|
||||||
|
result.push(&format!(r#"<td class="{}count excluded">{}</td>"#, classes_i, pp(&count_card.votes, opts.pp_decimals)).into());
|
||||||
|
} else {
|
||||||
|
result.push(&format!(r#"<td class="{}count excluded">{}</td><td class="count excluded">{}</td>"#, classes_i, pps(&count_card.transfers, opts.pp_decimals), pp(&count_card.votes, opts.pp_decimals)).into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CandidateState::Withdrawn => {
|
||||||
|
if hide_xfers_trsp {
|
||||||
|
result.push(&format!(r#"<td class="{}count excluded">WD</td>"#, classes_i).into());
|
||||||
|
} else {
|
||||||
|
result.push(&format!(r#"<td class="{}count excluded"></td><td class="count excluded">WD</td>"#, classes_i).into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CandidateState::Excluded => {
|
||||||
|
if count_card.votes.is_zero() && count_card.parcels.iter().all(|p| p.votes.is_empty()) {
|
||||||
|
if hide_xfers_trsp {
|
||||||
|
result.push(&format!(r#"<td class="{}count excluded">Ex</td>"#, classes_i).into());
|
||||||
|
} else {
|
||||||
|
result.push(&format!(r#"<td class="{}count excluded">{}</td><td class="count excluded">Ex</td>"#, classes_i, pps(&count_card.transfers, opts.pp_decimals)).into());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if hide_xfers_trsp {
|
||||||
|
result.push(&format!(r#"<td class="{}count excluded">{}</td>"#, classes_i, pp(&count_card.votes, opts.pp_decimals)).into());
|
||||||
|
} else {
|
||||||
|
result.push(&format!(r#"<td class="{}count excluded">{}</td><td class="count excluded">{}</td>"#, classes_i, pps(&count_card.transfers, opts.pp_decimals), pp(&count_card.votes, opts.pp_decimals)).into());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -403,27 +491,27 @@ fn update_results_table<N: Number>(stage_num: usize, state: &CountState<N>, opts
|
|||||||
"ballots_votes" => {
|
"ballots_votes" => {
|
||||||
match count_card.state {
|
match count_card.state {
|
||||||
CandidateState::Hopeful | CandidateState::Guarded => {
|
CandidateState::Hopeful | CandidateState::Guarded => {
|
||||||
result.push(&format!(r#"<td class="{}count">{}</td><td class="count">{}</td>"#, tdclasses2, pp(&count_card.ballot_transfers, 0), pp(&count_card.transfers, opts.pp_decimals)).into());
|
result.push(&format!(r#"<td class="{}count">{}</td><td class="count">{}</td>"#, classes_i, pp(&count_card.ballot_transfers, 0), pp(&count_card.transfers, opts.pp_decimals)).into());
|
||||||
result.push(&format!(r#"<td class="{}count">{}</td><td class="count">{}</td>"#, tdclasses2, pp(&count_card.num_ballots(), 0), pp(&count_card.votes, opts.pp_decimals)).into());
|
result.push(&format!(r#"<td class="{}count">{}</td><td class="count">{}</td>"#, classes_i, pp(&count_card.num_ballots(), 0), pp(&count_card.votes, opts.pp_decimals)).into());
|
||||||
}
|
}
|
||||||
CandidateState::Elected => {
|
CandidateState::Elected => {
|
||||||
result.push(&format!(r#"<td class="{}count elected">{}</td><td class="count elected">{}</td>"#, tdclasses2, pp(&count_card.ballot_transfers, 0), pp(&count_card.transfers, opts.pp_decimals)).into());
|
result.push(&format!(r#"<td class="{}count elected">{}</td><td class="count elected">{}</td>"#, classes_i, pp(&count_card.ballot_transfers, 0), pp(&count_card.transfers, opts.pp_decimals)).into());
|
||||||
result.push(&format!(r#"<td class="{}count elected">{}</td><td class="count elected">{}</td>"#, tdclasses2, pp(&count_card.num_ballots(), 0), pp(&count_card.votes, opts.pp_decimals)).into());
|
result.push(&format!(r#"<td class="{}count elected">{}</td><td class="count elected">{}</td>"#, classes_i, pp(&count_card.num_ballots(), 0), pp(&count_card.votes, opts.pp_decimals)).into());
|
||||||
}
|
}
|
||||||
CandidateState::Doomed => {
|
CandidateState::Doomed => {
|
||||||
result.push(&format!(r#"<td class="{}count excluded">{}</td><td class="count excluded">{}</td>"#, tdclasses2, pp(&count_card.ballot_transfers, 0), pp(&count_card.transfers, opts.pp_decimals)).into());
|
result.push(&format!(r#"<td class="{}count excluded">{}</td><td class="count excluded">{}</td>"#, classes_i, pp(&count_card.ballot_transfers, 0), pp(&count_card.transfers, opts.pp_decimals)).into());
|
||||||
result.push(&format!(r#"<td class="{}count excluded">{}</td><td class="count excluded">{}</td>"#, tdclasses2, pp(&count_card.num_ballots(), 0), pp(&count_card.votes, opts.pp_decimals)).into());
|
result.push(&format!(r#"<td class="{}count excluded">{}</td><td class="count excluded">{}</td>"#, classes_i, pp(&count_card.num_ballots(), 0), pp(&count_card.votes, opts.pp_decimals)).into());
|
||||||
}
|
}
|
||||||
CandidateState::Withdrawn => {
|
CandidateState::Withdrawn => {
|
||||||
result.push(&format!(r#"<td class="{}count excluded"></td><td class="count excluded"></td>"#, tdclasses2).into());
|
result.push(&format!(r#"<td class="{}count excluded"></td><td class="count excluded"></td>"#, classes_i).into());
|
||||||
result.push(&format!(r#"<td class="{}count excluded"></td><td class="count excluded">WD</td>"#, tdclasses2).into());
|
result.push(&format!(r#"<td class="{}count excluded"></td><td class="count excluded">WD</td>"#, classes_i).into());
|
||||||
}
|
}
|
||||||
CandidateState::Excluded => {
|
CandidateState::Excluded => {
|
||||||
result.push(&format!(r#"<td class="{}count excluded">{}</td><td class="count excluded">{}</td>"#, tdclasses2, pp(&count_card.ballot_transfers, 0), pp(&count_card.transfers, opts.pp_decimals)).into());
|
result.push(&format!(r#"<td class="{}count excluded">{}</td><td class="count excluded">{}</td>"#, classes_i, pp(&count_card.ballot_transfers, 0), pp(&count_card.transfers, opts.pp_decimals)).into());
|
||||||
if count_card.votes.is_zero() && count_card.parcels.iter().all(|p| p.votes.is_empty()) {
|
if count_card.votes.is_zero() && count_card.parcels.iter().all(|p| p.votes.is_empty()) {
|
||||||
result.push(&format!(r#"<td class="{}count excluded"></td><td class="count excluded">Ex</td>"#, tdclasses2).into());
|
result.push(&format!(r#"<td class="{}count excluded"></td><td class="count excluded">Ex</td>"#, classes_i).into());
|
||||||
} else {
|
} else {
|
||||||
result.push(&format!(r#"<td class="{}count excluded">{}</td><td class="count excluded">{}</td>"#, tdclasses2, pp(&count_card.num_ballots(), 0), pp(&count_card.votes, opts.pp_decimals)).into());
|
result.push(&format!(r#"<td class="{}count excluded">{}</td><td class="count excluded">{}</td>"#, classes_i, pp(&count_card.num_ballots(), 0), pp(&count_card.votes, opts.pp_decimals)).into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -434,16 +522,25 @@ fn update_results_table<N: Number>(stage_num: usize, state: &CountState<N>, opts
|
|||||||
|
|
||||||
match report_style {
|
match report_style {
|
||||||
"votes" => {
|
"votes" => {
|
||||||
result.push(&format!(r#"<td class="{}count">{}</td>"#, tdclasses2, pp(&state.exhausted.transfers, opts.pp_decimals)).into());
|
result.push(&format!(r#"<td class="{}count">{}</td>"#, classes_i, pp(&state.exhausted.transfers, opts.pp_decimals)).into());
|
||||||
result.push(&format!(r#"<td class="{}count">{}</td>"#, tdclasses2, pp(&state.exhausted.votes, opts.pp_decimals)).into());
|
result.push(&format!(r#"<td class="{}count">{}</td>"#, classes_i, pp(&state.exhausted.votes, opts.pp_decimals)).into());
|
||||||
result.push(&format!(r#"<td class="{}count">{}</td>"#, tdclasses2, pp(&state.loss_fraction.transfers, opts.pp_decimals)).into());
|
result.push(&format!(r#"<td class="{}count">{}</td>"#, classes_i, pp(&state.loss_fraction.transfers, opts.pp_decimals)).into());
|
||||||
result.push(&format!(r#"<td class="{}count">{}</td>"#, tdclasses2, pp(&state.loss_fraction.votes, opts.pp_decimals)).into());
|
result.push(&format!(r#"<td class="{}count">{}</td>"#, classes_i, pp(&state.loss_fraction.votes, opts.pp_decimals)).into());
|
||||||
|
}
|
||||||
|
"votes_transposed" => {
|
||||||
|
if hide_xfers_trsp {
|
||||||
|
result.push(&format!(r#"<td class="{}count">{}</td>"#, classes_i, pp(&state.exhausted.votes, opts.pp_decimals)).into());
|
||||||
|
result.push(&format!(r#"<td class="{}count">{}</td>"#, classes_i, pp(&state.loss_fraction.votes, opts.pp_decimals)).into());
|
||||||
|
} else {
|
||||||
|
result.push(&format!(r#"<td class="{}count">{}</td><td class="count">{}</td>"#, classes_i, pps(&state.exhausted.transfers, opts.pp_decimals), pp(&state.exhausted.votes, opts.pp_decimals)).into());
|
||||||
|
result.push(&format!(r#"<td class="{}count">{}</td><td class="count">{}</td>"#, classes_i, pps(&state.loss_fraction.transfers, opts.pp_decimals), pp(&state.loss_fraction.votes, opts.pp_decimals)).into());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"ballots_votes" => {
|
"ballots_votes" => {
|
||||||
result.push(&format!(r#"<td class="{}count">{}</td><td class="count">{}</td>"#, tdclasses2, pp(&state.exhausted.ballot_transfers, 0), pp(&state.exhausted.transfers, opts.pp_decimals)).into());
|
result.push(&format!(r#"<td class="{}count">{}</td><td class="count">{}</td>"#, classes_i, pp(&state.exhausted.ballot_transfers, 0), pp(&state.exhausted.transfers, opts.pp_decimals)).into());
|
||||||
result.push(&format!(r#"<td class="{}count">{}</td><td class="count">{}</td>"#, tdclasses2, pp(&state.exhausted.num_ballots(), 0), pp(&state.exhausted.votes, opts.pp_decimals)).into());
|
result.push(&format!(r#"<td class="{}count">{}</td><td class="count">{}</td>"#, classes_i, pp(&state.exhausted.num_ballots(), 0), pp(&state.exhausted.votes, opts.pp_decimals)).into());
|
||||||
result.push(&format!(r#"<td class="{}count"></td><td class="count">{}</td>"#, tdclasses2, pp(&state.loss_fraction.transfers, opts.pp_decimals)).into());
|
result.push(&format!(r#"<td class="{}count"></td><td class="count">{}</td>"#, classes_i, pp(&state.loss_fraction.transfers, opts.pp_decimals)).into());
|
||||||
result.push(&format!(r#"<td class="{}count"></td><td class="count">{}</td>"#, tdclasses2, pp(&state.loss_fraction.votes, opts.pp_decimals)).into());
|
result.push(&format!(r#"<td class="{}count"></td><td class="count">{}</td>"#, classes_i, pp(&state.loss_fraction.votes, opts.pp_decimals)).into());
|
||||||
}
|
}
|
||||||
_ => unreachable!("Invalid report_style")
|
_ => unreachable!("Invalid report_style")
|
||||||
}
|
}
|
||||||
@ -455,48 +552,42 @@ fn update_results_table<N: Number>(stage_num: usize, state: &CountState<N>, opts
|
|||||||
|
|
||||||
match report_style {
|
match report_style {
|
||||||
"votes" => {
|
"votes" => {
|
||||||
result.push(&format!(r#"<td class="{}count">{}</td>"#, tdclasses2, pp(&total_vote, opts.pp_decimals)).into());
|
result.push(&format!(r#"<td class="{}count">{}</td>"#, classes_i, pp(&total_vote, opts.pp_decimals)).into());
|
||||||
|
}
|
||||||
|
"votes_transposed" => {
|
||||||
|
if hide_xfers_trsp {
|
||||||
|
result.push(&format!(r#"<td class="{}count">{}</td>"#, classes_i, pp(&total_vote, opts.pp_decimals)).into());
|
||||||
|
} else {
|
||||||
|
result.push(&format!(r#"<td class="{}count"></td><td class="count">{}</td>"#, classes_i, pp(&total_vote, opts.pp_decimals)).into());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"ballots_votes" => {
|
"ballots_votes" => {
|
||||||
// Calculate total ballots
|
// Calculate total ballots
|
||||||
let mut total_ballots = state.candidates.values().fold(N::zero(), |acc, cc| { acc + cc.num_ballots() });
|
let mut total_ballots = state.candidates.values().fold(N::zero(), |acc, cc| { acc + cc.num_ballots() });
|
||||||
total_ballots += state.exhausted.num_ballots();
|
total_ballots += state.exhausted.num_ballots();
|
||||||
|
result.push(&format!(r#"<td class="{}count">{}</td><td class="count">{}</td>"#, classes_i, pp(&total_ballots, 0), pp(&total_vote, opts.pp_decimals)).into());
|
||||||
result.push(&format!(r#"<td class="{}count">{}</td><td class="count">{}</td>"#, tdclasses2, pp(&total_ballots, 0), pp(&total_vote, opts.pp_decimals)).into());
|
|
||||||
}
|
}
|
||||||
_ => unreachable!("Invalid report_style")
|
_ => unreachable!("Invalid report_style")
|
||||||
}
|
}
|
||||||
|
|
||||||
match report_style {
|
if report_style == "votes" || (report_style == "votes_transposed" && hide_xfers_trsp) {
|
||||||
"votes" => {
|
result.push(&format!(r#"<td class="{}count">{}</td>"#, classes_i, pp(state.quota.as_ref().unwrap(), opts.pp_decimals)).into());
|
||||||
result.push(&format!(r#"<td class="{}count">{}</td>"#, tdclasses2, pp(state.quota.as_ref().unwrap(), opts.pp_decimals)).into());
|
} else {
|
||||||
}
|
result.push(&format!(r#"<td class="{}count"></td><td class="count">{}</td>"#, classes_i, pp(state.quota.as_ref().unwrap(), opts.pp_decimals)).into());
|
||||||
"ballots_votes" => {
|
|
||||||
result.push(&format!(r#"<td class="{}count"></td><td class="count">{}</td>"#, tdclasses2, pp(state.quota.as_ref().unwrap(), opts.pp_decimals)).into());
|
|
||||||
}
|
|
||||||
_ => unreachable!("Invalid report_style")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if should_show_vre(opts) {
|
if should_show_vre(opts) {
|
||||||
if let Some(vre) = &state.vote_required_election {
|
if let Some(vre) = &state.vote_required_election {
|
||||||
match report_style {
|
if report_style == "votes" || (report_style == "votes_transposed" && hide_xfers_trsp) {
|
||||||
"votes" => {
|
result.push(&format!(r#"<td class="{}count">{}</td>"#, classes_i, pp(vre, opts.pp_decimals)).into());
|
||||||
result.push(&format!(r#"<td class="{}count">{}</td>"#, tdclasses2, pp(vre, opts.pp_decimals)).into());
|
} else {
|
||||||
}
|
result.push(&format!(r#"<td class="{}count"></td><td class="count">{}</td>"#, classes_i, pp(vre, opts.pp_decimals)).into());
|
||||||
"ballots_votes" => {
|
|
||||||
result.push(&format!(r#"<td class="{}count"></td><td class="count">{}</td>"#, tdclasses2, pp(vre, opts.pp_decimals)).into());
|
|
||||||
}
|
|
||||||
_ => unreachable!("Invalid report_style")
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
match report_style {
|
if report_style == "votes" || (report_style == "votes_transposed" && hide_xfers_trsp) {
|
||||||
"votes" => {
|
result.push(&format!(r#"<td class="{}count"></td>"#, classes_i).into());
|
||||||
result.push(&format!(r#"<td class="{}count"></td>"#, tdclasses2).into());
|
} else {
|
||||||
}
|
result.push(&format!(r#"<td class="{}count"></td><td class="count"></td>"#, classes_i).into());
|
||||||
"ballots_votes" => {
|
|
||||||
result.push(&format!(r#"<td class="{}count"></td><td class="count"></td>"#, tdclasses2).into());
|
|
||||||
}
|
|
||||||
_ => unreachable!("Invalid report_style")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -515,7 +606,7 @@ fn finalise_results_table<N: Number>(state: &CountState<N>, report_style: &str)
|
|||||||
|
|
||||||
// Header rows
|
// Header rows
|
||||||
match report_style {
|
match report_style {
|
||||||
"votes" => {
|
"votes" | "votes_transposed" => {
|
||||||
result.push(&r#"<td rowspan="3"></td>"#.into());
|
result.push(&r#"<td rowspan="3"></td>"#.into());
|
||||||
result.push(&"".into());
|
result.push(&"".into());
|
||||||
result.push(&"".into());
|
result.push(&"".into());
|
||||||
@ -529,19 +620,24 @@ fn finalise_results_table<N: Number>(state: &CountState<N>, report_style: &str)
|
|||||||
_ => unreachable!("Invalid report_style")
|
_ => unreachable!("Invalid report_style")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let rowspan = if report_style == "votes_transposed" { "" } else { r#" rowspan="2""# };
|
||||||
|
|
||||||
// Candidate states
|
// Candidate states
|
||||||
for candidate in state.election.candidates.iter() {
|
for candidate in state.election.candidates.iter() {
|
||||||
let count_card = &state.candidates[candidate];
|
let count_card = &state.candidates[candidate];
|
||||||
if count_card.state == stv::CandidateState::Elected {
|
if count_card.state == stv::CandidateState::Elected {
|
||||||
result.push(&format!(r#"<td rowspan="2" class="bb elected">ELECTED {}</td>"#, count_card.order_elected).into());
|
result.push(&format!(r#"<td{} class="bb elected">ELECTED {}</td>"#, rowspan, count_card.order_elected).into());
|
||||||
} else if count_card.state == stv::CandidateState::Excluded {
|
} else if count_card.state == stv::CandidateState::Excluded {
|
||||||
result.push(&format!(r#"<td rowspan="2" class="bb excluded">Excluded {}</td>"#, -count_card.order_elected).into());
|
result.push(&format!(r#"<td{} class="bb excluded">Excluded {}</td>"#, rowspan, -count_card.order_elected).into());
|
||||||
} else if count_card.state == stv::CandidateState::Withdrawn {
|
} else if count_card.state == stv::CandidateState::Withdrawn {
|
||||||
result.push(&r#"<td rowspan="2" class="bb excluded">Withdrawn</td>"#.into());
|
result.push(&format!(r#"<td{} class="bb excluded">Withdrawn</td>"#, rowspan).into());
|
||||||
} else {
|
} else {
|
||||||
result.push(&r#"<td rowspan="2" class="bb"></td>"#.into());
|
result.push(&format!(r#"<td{} class="bb"></td>"#, rowspan).into());
|
||||||
|
}
|
||||||
|
|
||||||
|
if report_style != "votes_transposed" {
|
||||||
|
result.push(&"".into());
|
||||||
}
|
}
|
||||||
result.push(&"".into());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@ -589,3 +685,24 @@ fn pp<N: Number>(n: &N, dps: usize) -> String {
|
|||||||
|
|
||||||
return raw;
|
return raw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Signed version of [pp]
|
||||||
|
fn pps<N: Number>(n: &N, dps: usize) -> String {
|
||||||
|
if n.is_zero() {
|
||||||
|
return "".to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut raw = format!("{:.dps$}", n, dps=dps);
|
||||||
|
if raw.contains('.') {
|
||||||
|
raw = raw.replacen(".", ".<sup>", 1);
|
||||||
|
raw.push_str("</sup>");
|
||||||
|
}
|
||||||
|
|
||||||
|
if raw.starts_with('-') {
|
||||||
|
raw = raw.replacen("-", "−", 1);
|
||||||
|
} else {
|
||||||
|
raw.insert(0, '+');
|
||||||
|
}
|
||||||
|
|
||||||
|
return raw;
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user