Basic tabular output in HTML

This commit is contained in:
RunasSudo 2021-06-02 22:46:36 +10:00
parent 97a776ff79
commit 37622eb78d
No known key found for this signature in database
GPG Key ID: 7234E476BF21C61A
5 changed files with 88 additions and 21 deletions

10
Cargo.lock generated
View File

@ -202,6 +202,15 @@ version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
[[package]]
name = "js-sys"
version = "0.3.51"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83bdfbace3a0e81a4253f73b49e960b053e396a11012cbd49b9b74d6a2b67062"
dependencies = [
"wasm-bindgen",
]
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.4.0" version = "1.4.0"
@ -290,6 +299,7 @@ dependencies = [
"csv", "csv",
"flate2", "flate2",
"git-version", "git-version",
"js-sys",
"num-bigint", "num-bigint",
"num-rational", "num-rational",
"num-traits", "num-traits",

View File

@ -15,6 +15,7 @@ wasm-bindgen = "0.2.74"
# Only for WebAssembly - include here for syntax highlighting # Only for WebAssembly - include here for syntax highlighting
#[target.'cfg(target_arch = "wasm32")'.dependencies] #[target.'cfg(target_arch = "wasm32")'.dependencies]
console_error_panic_hook = "0.1.6" console_error_panic_hook = "0.1.6"
js-sys = "0.3.51"
num-bigint = "0.4.0" num-bigint = "0.4.0"
num-rational = "0.4.0" num-rational = "0.4.0"
paste = "1.0.5" paste = "1.0.5"

View File

@ -25,13 +25,16 @@ function clickAdvancedOptions() {
} }
} }
console.log = function(v) {
document.getElementById('resultLogs1').append(v);
document.getElementById('resultLogs1').append("\n");
};
var wasm = wasm_bindgen; var wasm = wasm_bindgen;
var tblResult = document.getElementById('result');
function updateResultTable(result) {
for (let i = 0; i < result.length; i++) {
tblResult.rows[i].insertAdjacentHTML('beforeend', result[i]);
}
}
async function clickCount() { async function clickCount() {
if (document.getElementById('bltFile').files.length === 0) { if (document.getElementById('bltFile').files.length === 0) {
return; return;
@ -46,10 +49,12 @@ async function clickCount() {
// Init election // Init election
let election = wasm.election_from_blt_Rational(electionData); let election = wasm.election_from_blt_Rational(electionData);
let state = wasm.CountStateRational.new(election);
// Init results table
tblResult.innerHTML = wasm.init_results_table_Rational(election);
// Init STV options // Init STV options
let stv_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,
document.getElementById('chkRoundWeights').checked ? parseInt(document.getElementById('txtRoundWeights').value) : null, document.getElementById('chkRoundWeights').checked ? parseInt(document.getElementById('txtRoundWeights').value) : null,
document.getElementById('chkRoundVotes').checked ? parseInt(document.getElementById('txtRoundVotes').value) : null, document.getElementById('chkRoundVotes').checked ? parseInt(document.getElementById('txtRoundVotes').value) : null,
@ -64,14 +69,15 @@ async function clickCount() {
); );
// Step election // Step election
wasm.count_init_Rational(state, stv_opts); let state = wasm.CountStateRational.new(election);
wasm.make_and_print_result_Rational(1, state); wasm.count_init_Rational(state, opts);
updateResultTable(wasm.update_results_table_Rational(1, state, opts));
for (let stage_num = 2;; stage_num++) { for (let stageNum = 2;; stageNum++) {
let is_done = wasm.count_one_stage_Rational(state, stv_opts); let isDone = wasm.count_one_stage_Rational(state, opts);
if (is_done) { if (isDone) {
break; break;
} }
wasm.make_and_print_result_Rational(stage_num, state); updateResultTable(wasm.update_results_table_Rational(stageNum, state, opts));
} }
} }

View File

@ -83,9 +83,6 @@ 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-no td:not(:first-child) {
border-top: 1px solid #76858c;
}
tr.stage-kind td:not(:first-child) { tr.stage-kind td:not(:first-child) {
font-size: 0.75em; font-size: 0.75em;
min-width: 5rem; min-width: 5rem;
@ -109,10 +106,10 @@ tr.info td {
color-adjust: exact; color-adjust: exact;
-webkit-print-color-adjust: exact; -webkit-print-color-adjust: exact;
} }
td.bt { tr.stage-no td:not(:first-child), tr.transfers td {
border-top: 1px solid #76858c; border-top: 1px solid #76858c;
} }
td.bb { tr.info:last-child td {
border-bottom: 1px solid #76858c; border-bottom: 1px solid #76858c;
} }

View File

@ -21,6 +21,7 @@ use crate::stv;
extern crate console_error_panic_hook; extern crate console_error_panic_hook;
use js_sys::Array;
use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen::prelude::wasm_bindgen;
// Logging // Logging
@ -70,6 +71,18 @@ macro_rules! impl_type {
#[wasm_bindgen] #[wasm_bindgen]
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub fn [<init_results_table_$type>](election: &[<Election$type>]) -> String {
return init_results_table(&election.0);
}
#[wasm_bindgen]
#[allow(non_snake_case)]
pub fn [<update_results_table_$type>](stage_num: usize, state: &[<CountState$type>], opts: &stv::STVOptions) -> Array {
return update_results_table(stage_num, &state.0, opts);
}
/*#[wasm_bindgen]
#[allow(non_snake_case)]
pub fn [<make_and_print_result_$type>](stage_num: usize, state: &[<CountState$type>]) { pub fn [<make_and_print_result_$type>](stage_num: usize, state: &[<CountState$type>]) {
let result = StageResult { let result = StageResult {
kind: state.0.kind, kind: state.0.kind,
@ -78,7 +91,7 @@ macro_rules! impl_type {
state: CountStateOrRef::from(&state.0), state: CountStateOrRef::from(&state.0),
}; };
print_stage(stage_num, &result); print_stage(stage_num, &result);
} }*/
// 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
@ -114,7 +127,47 @@ impl_type!(NativeFloat64);
// Reporting // Reporting
fn print_candidates<'a, N: 'a + Number, I: Iterator<Item=(&'a Candidate, &'a CountCard<'a, N>)>>(candidates: I) { 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>"#);
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(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>"#);
return result;
}
fn update_results_table<N: Number>(stage_num: usize, state: &CountState<N>, opts: &stv::STVOptions) -> Array {
let result = Array::new();
result.push(&format!(r#"<td>{}</td>"#, stage_num).into());
result.push(&format!(r#"<td>{}</td>"#, state.kind.unwrap_or("")).into());
result.push(&format!(r#"<td>{}</td>"#, state.title).into());
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());
} 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>{:.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());
// 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>{:.dps$}</td>"#, state.quota, dps=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 { for (candidate, count_card) in candidates {
if count_card.state == CandidateState::ELECTED { if count_card.state == CandidateState::ELECTED {
cprintln!("- {}: {:.dps$} ({:.dps$}) - ELECTED {}", candidate.name, count_card.votes, count_card.transfers, count_card.order_elected, dps=2); cprintln!("- {}: {:.dps$} ({:.dps$}) - ELECTED {}", candidate.name, count_card.votes, count_card.transfers, count_card.order_elected, dps=2);
@ -153,4 +206,4 @@ fn print_stage<N: Number>(stage_num: usize, result: &StageResult<N>) {
cprintln!("Quota: {:.dps$}", state.quota, dps=2); cprintln!("Quota: {:.dps$}", state.quota, dps=2);
cprintln!(""); cprintln!("");
} }*/