Further work on HTML output
This commit is contained in:
parent
37622eb78d
commit
3bb538e99e
|
@ -28,6 +28,8 @@ function clickAdvancedOptions() {
|
||||||
var wasm = wasm_bindgen;
|
var wasm = wasm_bindgen;
|
||||||
|
|
||||||
var tblResult = document.getElementById('result');
|
var tblResult = document.getElementById('result');
|
||||||
|
var divLogs2 = document.getElementById('resultLogs2');
|
||||||
|
var olStageComments;
|
||||||
|
|
||||||
function updateResultTable(result) {
|
function updateResultTable(result) {
|
||||||
for (let i = 0; i < result.length; i++) {
|
for (let i = 0; i < result.length; i++) {
|
||||||
|
@ -35,6 +37,12 @@ function updateResultTable(result) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateStageComments(comment) {
|
||||||
|
let elLi = document.createElement('li');
|
||||||
|
elLi.innerHTML = comment;
|
||||||
|
olStageComments.append(elLi);
|
||||||
|
}
|
||||||
|
|
||||||
async function clickCount() {
|
async function clickCount() {
|
||||||
if (document.getElementById('bltFile').files.length === 0) {
|
if (document.getElementById('bltFile').files.length === 0) {
|
||||||
return;
|
return;
|
||||||
|
@ -53,6 +61,10 @@ async function clickCount() {
|
||||||
// Init results table
|
// Init results table
|
||||||
tblResult.innerHTML = wasm.init_results_table_Rational(election);
|
tblResult.innerHTML = wasm.init_results_table_Rational(election);
|
||||||
|
|
||||||
|
divLogs2.innerHTML = '<p>Stage comments:</p>';
|
||||||
|
olStageComments = document.createElement('ol');
|
||||||
|
divLogs2.append(olStageComments);
|
||||||
|
|
||||||
// Init STV options
|
// Init STV options
|
||||||
let opts = wasm.STVOptions.new(
|
let opts = wasm.STVOptions.new(
|
||||||
document.getElementById('chkRoundTVs').checked ? parseInt(document.getElementById('txtRoundTVs').value) : null,
|
document.getElementById('chkRoundTVs').checked ? parseInt(document.getElementById('txtRoundTVs').value) : null,
|
||||||
|
@ -72,6 +84,7 @@ async function clickCount() {
|
||||||
let state = wasm.CountStateRational.new(election);
|
let state = wasm.CountStateRational.new(election);
|
||||||
wasm.count_init_Rational(state, opts);
|
wasm.count_init_Rational(state, opts);
|
||||||
updateResultTable(wasm.update_results_table_Rational(1, state, opts));
|
updateResultTable(wasm.update_results_table_Rational(1, state, opts));
|
||||||
|
updateStageComments(wasm.update_stage_comments_Rational(state));
|
||||||
|
|
||||||
for (let stageNum = 2;; stageNum++) {
|
for (let stageNum = 2;; stageNum++) {
|
||||||
let isDone = wasm.count_one_stage_Rational(state, opts);
|
let isDone = wasm.count_one_stage_Rational(state, opts);
|
||||||
|
@ -79,5 +92,10 @@ async function clickCount() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
updateResultTable(wasm.update_results_table_Rational(stageNum, state, opts));
|
updateResultTable(wasm.update_results_table_Rational(stageNum, state, opts));
|
||||||
|
updateStageComments(wasm.update_stage_comments_Rational(state));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateResultTable(wasm.finalise_results_table_Rational(state));
|
||||||
|
|
||||||
|
divLogs2.insertAdjacentHTML('beforeend', wasm.final_result_summary_Rational(state));
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,7 +83,7 @@ td.count sup {
|
||||||
tr.stage-no td, tr.stage-kind td, tr.stage-comment td {
|
tr.stage-no td, tr.stage-kind td, tr.stage-comment td {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
tr.stage-kind td:not(:first-child) {
|
tr.stage-kind td {
|
||||||
font-size: 0.75em;
|
font-size: 0.75em;
|
||||||
min-width: 5rem;
|
min-width: 5rem;
|
||||||
color: #1b2839;
|
color: #1b2839;
|
||||||
|
@ -106,10 +106,10 @@ tr.info td {
|
||||||
color-adjust: exact;
|
color-adjust: exact;
|
||||||
-webkit-print-color-adjust: exact;
|
-webkit-print-color-adjust: exact;
|
||||||
}
|
}
|
||||||
tr.stage-no td:not(:first-child), tr.transfers td {
|
tr.stage-no td:not(:empty), tr.transfers td {
|
||||||
border-top: 1px solid #76858c;
|
border-top: 1px solid #76858c;
|
||||||
}
|
}
|
||||||
tr.info:last-child td {
|
tr.info:last-child td, .bb {
|
||||||
border-bottom: 1px solid #76858c;
|
border-bottom: 1px solid #76858c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
143
src/stv/wasm.rs
143
src/stv/wasm.rs
|
@ -81,17 +81,23 @@ macro_rules! impl_type {
|
||||||
return update_results_table(stage_num, &state.0, opts);
|
return update_results_table(stage_num, &state.0, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
pub fn [<make_and_print_result_$type>](stage_num: usize, state: &[<CountState$type>]) {
|
pub fn [<update_stage_comments_$type>](state: &[<CountState$type>]) -> String {
|
||||||
let result = StageResult {
|
return update_stage_comments(&state.0);
|
||||||
kind: state.0.kind,
|
}
|
||||||
title: &state.0.title,
|
|
||||||
logs: state.0.logger.render(),
|
#[wasm_bindgen]
|
||||||
state: CountStateOrRef::from(&state.0),
|
#[allow(non_snake_case)]
|
||||||
};
|
pub fn [<finalise_results_table_$type>](state: &[<CountState$type>]) -> Array {
|
||||||
print_stage(stage_num, &result);
|
return finalise_results_table(&state.0);
|
||||||
}*/
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn [<final_result_summary_$type>](state: &[<CountState$type>]) -> String {
|
||||||
|
return final_result_summary(&state.0);
|
||||||
|
}
|
||||||
|
|
||||||
// Wrapper structs
|
// Wrapper structs
|
||||||
// Required as we cannot specify &'static in wasm-bindgen: issue #1187
|
// Required as we cannot specify &'static in wasm-bindgen: issue #1187
|
||||||
|
@ -128,7 +134,7 @@ impl_type!(NativeFloat64);
|
||||||
// Reporting
|
// Reporting
|
||||||
|
|
||||||
fn init_results_table<N: Number>(election: &Election<N>) -> String {
|
fn init_results_table<N: Number>(election: &Election<N>) -> String {
|
||||||
let mut result = String::from(r#"<tr class="stage-no"><td></td></tr><tr class="stage-kind"><td></td></tr><tr class="stage-comment"><td></td></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>"#);
|
||||||
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));
|
result.push_str(&format!(r#"<tr class="candidate transfers"><td rowspan="2">{}</td></tr><tr class="candidate votes"></tr>"#, candidate.name));
|
||||||
}
|
}
|
||||||
|
@ -144,66 +150,97 @@ 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.get(candidate).unwrap();
|
let count_card = state.candidates.get(candidate).unwrap();
|
||||||
if count_card.state == stv::CandidateState::ELECTED {
|
if count_card.state == stv::CandidateState::ELECTED {
|
||||||
result.push(&format!(r#"<td class="elected">{:.dps$}</td>"#, count_card.transfers, dps=opts.pp_decimals).into());
|
result.push(&format!(r#"<td class="count elected">{}</td>"#, pp(&count_card.transfers, opts.pp_decimals)).into());
|
||||||
result.push(&format!(r#"<td class="elected">{:.dps$}</td>"#, count_card.votes, dps=opts.pp_decimals).into());
|
result.push(&format!(r#"<td class="count elected">{}</td>"#, pp(&count_card.votes, opts.pp_decimals)).into());
|
||||||
|
} else if count_card.state == stv::CandidateState::EXCLUDED {
|
||||||
|
result.push(&format!(r#"<td class="count excluded">{}</td>"#, pp(&count_card.transfers, opts.pp_decimals)).into());
|
||||||
|
if count_card.votes.is_zero() {
|
||||||
|
result.push(&r#"<td class="count excluded">Ex</td>"#.into());
|
||||||
} else {
|
} else {
|
||||||
result.push(&format!(r#"<td>{:.dps$}</td>"#, count_card.transfers, dps=opts.pp_decimals).into());
|
result.push(&format!(r#"<td class="count excluded">{}</td>"#, pp(&count_card.votes, opts.pp_decimals)).into());
|
||||||
result.push(&format!(r#"<td>{:.dps$}</td>"#, count_card.votes, dps=opts.pp_decimals).into());
|
}
|
||||||
|
} else {
|
||||||
|
result.push(&format!(r#"<td class="count">{}</td>"#, pp(&count_card.transfers, opts.pp_decimals)).into());
|
||||||
|
result.push(&format!(r#"<td class="count">{}</td>"#, pp(&count_card.votes, opts.pp_decimals)).into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result.push(&format!(r#"<td>{:.dps$}</td>"#, state.exhausted.transfers, dps=opts.pp_decimals).into());
|
result.push(&format!(r#"<td class="count">{}</td>"#, pp(&state.exhausted.transfers, opts.pp_decimals)).into());
|
||||||
result.push(&format!(r#"<td>{:.dps$}</td>"#, state.exhausted.votes, dps=opts.pp_decimals).into());
|
result.push(&format!(r#"<td class="count">{}</td>"#, pp(&state.exhausted.votes, opts.pp_decimals)).into());
|
||||||
result.push(&format!(r#"<td>{:.dps$}</td>"#, state.loss_fraction.transfers, dps=opts.pp_decimals).into());
|
result.push(&format!(r#"<td class="count">{}</td>"#, pp(&state.loss_fraction.transfers, opts.pp_decimals)).into());
|
||||||
result.push(&format!(r#"<td>{:.dps$}</td>"#, state.loss_fraction.votes, dps=opts.pp_decimals).into());
|
result.push(&format!(r#"<td class="count">{}</td>"#, pp(&state.loss_fraction.votes, opts.pp_decimals)).into());
|
||||||
|
|
||||||
// Calculate total votes
|
// Calculate total votes
|
||||||
let mut total_vote = state.candidates.values().fold(N::zero(), |acc, cc| { acc + &cc.votes });
|
let mut total_vote = state.candidates.values().fold(N::zero(), |acc, cc| { acc + &cc.votes });
|
||||||
total_vote += &state.exhausted.votes;
|
total_vote += &state.exhausted.votes;
|
||||||
total_vote += &state.loss_fraction.votes;
|
total_vote += &state.loss_fraction.votes;
|
||||||
result.push(&format!(r#"<td>{:.dps$}</td>"#, total_vote, dps=opts.pp_decimals).into());
|
result.push(&format!(r#"<td class="count">{}</td>"#, pp(&total_vote, opts.pp_decimals)).into());
|
||||||
|
|
||||||
result.push(&format!(r#"<td>{:.dps$}</td>"#, state.quota, dps=opts.pp_decimals).into());
|
result.push(&format!(r#"<td class="count">{}</td>"#, pp(&state.quota, opts.pp_decimals)).into());
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*fn print_candidates<'a, N: 'a + Number, I: Iterator<Item=(&'a Candidate, &'a CountCard<'a, N>)>>(candidates: I) {
|
fn update_stage_comments<N: Number>(state: &CountState<N>) -> String {
|
||||||
for (candidate, count_card) in candidates {
|
return state.logger.render().join(" ");
|
||||||
if count_card.state == CandidateState::ELECTED {
|
|
||||||
cprintln!("- {}: {:.dps$} ({:.dps$}) - ELECTED {}", candidate.name, count_card.votes, count_card.transfers, count_card.order_elected, dps=2);
|
|
||||||
} else if count_card.state == CandidateState::EXCLUDED {
|
|
||||||
cprintln!("- {}: {:.dps$} ({:.dps$}) - Excluded {}", candidate.name, count_card.votes, count_card.transfers, -count_card.order_elected, dps=2);
|
|
||||||
} else {
|
|
||||||
cprintln!("- {}: {:.dps$} ({:.dps$})", candidate.name, count_card.votes, count_card.transfers, dps=2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_stage<N: Number>(stage_num: usize, result: &StageResult<N>) {
|
fn finalise_results_table<N: Number>(state: &CountState<N>) -> Array {
|
||||||
// Print stage details
|
let result = Array::new();
|
||||||
match result.kind {
|
|
||||||
None => { cprintln!("{}. {}", stage_num, result.title); }
|
|
||||||
Some(kind) => { cprintln!("{}. {} {}", stage_num, kind, result.title); }
|
|
||||||
};
|
|
||||||
cprintln!("{}", result.logs.join(" "));
|
|
||||||
|
|
||||||
let state = result.state.as_ref();
|
// Header rows
|
||||||
|
result.push(&r#"<td rowspan="3"></td>"#.into());
|
||||||
|
result.push(&"".into());
|
||||||
|
result.push(&"".into());
|
||||||
|
|
||||||
// Print candidates
|
// Candidate states
|
||||||
let candidates = state.election.candidates.iter()
|
for candidate in state.election.candidates.iter() {
|
||||||
.map(|c| (c, state.candidates.get(c).unwrap()));
|
let count_card = state.candidates.get(candidate).unwrap();
|
||||||
print_candidates(candidates);
|
if count_card.state == stv::CandidateState::ELECTED {
|
||||||
|
result.push(&format!(r#"<td rowspan="2" class="bb elected">ELECTED {}</td>"#, count_card.order_elected).into());
|
||||||
|
} 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());
|
||||||
|
} else {
|
||||||
|
result.push(&r#"<td rowspan="2" class="bb"></td>"#.into());
|
||||||
|
}
|
||||||
|
result.push(&"".into());
|
||||||
|
}
|
||||||
|
|
||||||
// Print summary rows
|
return result;
|
||||||
cprintln!("Exhausted: {:.dps$} ({:.dps$})", state.exhausted.votes, state.exhausted.transfers, dps=2);
|
}
|
||||||
cprintln!("Loss by fraction: {:.dps$} ({:.dps$})", state.loss_fraction.votes, state.loss_fraction.transfers, dps=2);
|
|
||||||
|
|
||||||
let mut total_vote = state.candidates.values().fold(N::zero(), |acc, cc| { acc + &cc.votes });
|
fn final_result_summary<N: Number>(state: &CountState<N>) -> String {
|
||||||
total_vote += &state.exhausted.votes;
|
let mut result = String::from("<p>Count complete. The winning candidates are, in order of election:</p><ol>");
|
||||||
total_vote += &state.loss_fraction.votes;
|
|
||||||
cprintln!("Total votes: {:.dps$}", total_vote, dps=2);
|
|
||||||
|
|
||||||
cprintln!("Quota: {:.dps$}", state.quota, dps=2);
|
let mut winners = Vec::new();
|
||||||
|
for (candidate, count_card) in state.candidates.iter() {
|
||||||
|
if count_card.state == CandidateState::ELECTED {
|
||||||
|
winners.push((candidate, count_card.order_elected));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
winners.sort_unstable_by(|a, b| a.1.partial_cmp(&b.1).unwrap());
|
||||||
|
|
||||||
cprintln!("");
|
for (winner, _) in winners.into_iter() {
|
||||||
}*/
|
result.push_str(&format!("<li>{}</li>", winner.name));
|
||||||
|
}
|
||||||
|
|
||||||
|
result.push_str("</ol>");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pp<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);
|
||||||
|
}
|
||||||
|
|
||||||
|
return raw;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue