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 tblResult = document.getElementById('result');
|
||||
var divLogs2 = document.getElementById('resultLogs2');
|
||||
var olStageComments;
|
||||
|
||||
function updateResultTable(result) {
|
||||
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() {
|
||||
if (document.getElementById('bltFile').files.length === 0) {
|
||||
return;
|
||||
@ -53,6 +61,10 @@ async function clickCount() {
|
||||
// Init results table
|
||||
tblResult.innerHTML = wasm.init_results_table_Rational(election);
|
||||
|
||||
divLogs2.innerHTML = '<p>Stage comments:</p>';
|
||||
olStageComments = document.createElement('ol');
|
||||
divLogs2.append(olStageComments);
|
||||
|
||||
// Init STV options
|
||||
let opts = wasm.STVOptions.new(
|
||||
document.getElementById('chkRoundTVs').checked ? parseInt(document.getElementById('txtRoundTVs').value) : null,
|
||||
@ -72,6 +84,7 @@ async function clickCount() {
|
||||
let state = wasm.CountStateRational.new(election);
|
||||
wasm.count_init_Rational(state, opts);
|
||||
updateResultTable(wasm.update_results_table_Rational(1, state, opts));
|
||||
updateStageComments(wasm.update_stage_comments_Rational(state));
|
||||
|
||||
for (let stageNum = 2;; stageNum++) {
|
||||
let isDone = wasm.count_one_stage_Rational(state, opts);
|
||||
@ -79,5 +92,10 @@ async function clickCount() {
|
||||
break;
|
||||
}
|
||||
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 {
|
||||
text-align: center;
|
||||
}
|
||||
tr.stage-kind td:not(:first-child) {
|
||||
tr.stage-kind td {
|
||||
font-size: 0.75em;
|
||||
min-width: 5rem;
|
||||
color: #1b2839;
|
||||
@ -106,10 +106,10 @@ tr.info td {
|
||||
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;
|
||||
}
|
||||
tr.info:last-child td {
|
||||
tr.info:last-child td, .bb {
|
||||
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);
|
||||
}
|
||||
|
||||
/*#[wasm_bindgen]
|
||||
#[wasm_bindgen]
|
||||
#[allow(non_snake_case)]
|
||||
pub fn [<make_and_print_result_$type>](stage_num: usize, state: &[<CountState$type>]) {
|
||||
let result = StageResult {
|
||||
kind: state.0.kind,
|
||||
title: &state.0.title,
|
||||
logs: state.0.logger.render(),
|
||||
state: CountStateOrRef::from(&state.0),
|
||||
};
|
||||
print_stage(stage_num, &result);
|
||||
}*/
|
||||
pub fn [<update_stage_comments_$type>](state: &[<CountState$type>]) -> String {
|
||||
return update_stage_comments(&state.0);
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
#[allow(non_snake_case)]
|
||||
pub fn [<finalise_results_table_$type>](state: &[<CountState$type>]) -> Array {
|
||||
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
|
||||
// Required as we cannot specify &'static in wasm-bindgen: issue #1187
|
||||
@ -128,7 +134,7 @@ impl_type!(NativeFloat64);
|
||||
// Reporting
|
||||
|
||||
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() {
|
||||
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() {
|
||||
let count_card = state.candidates.get(candidate).unwrap();
|
||||
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="elected">{:.dps$}</td>"#, count_card.votes, 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="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 {
|
||||
result.push(&format!(r#"<td class="count excluded">{}</td>"#, pp(&count_card.votes, opts.pp_decimals)).into());
|
||||
}
|
||||
} else {
|
||||
result.push(&format!(r#"<td>{:.dps$}</td>"#, count_card.transfers, dps=opts.pp_decimals).into());
|
||||
result.push(&format!(r#"<td>{:.dps$}</td>"#, count_card.votes, dps=opts.pp_decimals).into());
|
||||
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>{:.dps$}</td>"#, state.exhausted.votes, dps=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>{:.dps$}</td>"#, state.loss_fraction.votes, 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 class="count">{}</td>"#, pp(&state.exhausted.votes, 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 class="count">{}</td>"#, pp(&state.loss_fraction.votes, opts.pp_decimals)).into());
|
||||
|
||||
// Calculate total votes
|
||||
let mut total_vote = state.candidates.values().fold(N::zero(), |acc, cc| { acc + &cc.votes });
|
||||
total_vote += &state.exhausted.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;
|
||||
}
|
||||
|
||||
/*fn print_candidates<'a, N: 'a + Number, I: Iterator<Item=(&'a Candidate, &'a CountCard<'a, N>)>>(candidates: I) {
|
||||
for (candidate, count_card) in candidates {
|
||||
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 update_stage_comments<N: Number>(state: &CountState<N>) -> String {
|
||||
return state.logger.render().join(" ");
|
||||
}
|
||||
|
||||
fn print_stage<N: Number>(stage_num: usize, result: &StageResult<N>) {
|
||||
// Print stage details
|
||||
match result.kind {
|
||||
None => { cprintln!("{}. {}", stage_num, result.title); }
|
||||
Some(kind) => { cprintln!("{}. {} {}", stage_num, kind, result.title); }
|
||||
};
|
||||
cprintln!("{}", result.logs.join(" "));
|
||||
fn finalise_results_table<N: Number>(state: &CountState<N>) -> Array {
|
||||
let result = Array::new();
|
||||
|
||||
let state = result.state.as_ref();
|
||||
// Header rows
|
||||
result.push(&r#"<td rowspan="3"></td>"#.into());
|
||||
result.push(&"".into());
|
||||
result.push(&"".into());
|
||||
|
||||
// Print candidates
|
||||
let candidates = state.election.candidates.iter()
|
||||
.map(|c| (c, state.candidates.get(c).unwrap()));
|
||||
print_candidates(candidates);
|
||||
// Candidate states
|
||||
for candidate in state.election.candidates.iter() {
|
||||
let count_card = state.candidates.get(candidate).unwrap();
|
||||
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
|
||||
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);
|
||||
return result;
|
||||
}
|
||||
|
||||
let mut total_vote = state.candidates.values().fold(N::zero(), |acc, cc| { acc + &cc.votes });
|
||||
total_vote += &state.exhausted.votes;
|
||||
total_vote += &state.loss_fraction.votes;
|
||||
cprintln!("Total votes: {:.dps$}", total_vote, dps=2);
|
||||
fn final_result_summary<N: Number>(state: &CountState<N>) -> String {
|
||||
let mut result = String::from("<p>Count complete. The winning candidates are, in order of election:</p><ol>");
|
||||
|
||||
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
Block a user