From 1d4036c19ed9a9f84e13526c2b10eb6ad67be34d Mon Sep 17 00:00:00 2001 From: RunasSudo Date: Sun, 27 Jun 2021 22:09:34 +1000 Subject: [PATCH] Implement constraints in web UI --- html/index.html | 4 ++-- html/index.js | 23 ++++++++++++++++++----- html/worker.js | 9 +++++++-- src/stv/wasm.rs | 45 +++++++++++++++++++++++++++++++-------------- 4 files changed, 58 insertions(+), 23 deletions(-) diff --git a/html/index.html b/html/index.html index 44c2475..8b484e3 100644 --- a/html/index.html +++ b/html/index.html @@ -139,12 +139,12 @@ - +
diff --git a/html/index.js b/html/index.js index 2013800..33129fd 100644 --- a/html/index.js +++ b/html/index.js @@ -90,11 +90,22 @@ async function clickCount() { } // Read BLT file - let filePath = document.getElementById('bltFile').value; - filePath = filePath.substring(Math.max(filePath.lastIndexOf('\\'), filePath.lastIndexOf('/')) + 1); + let bltPath = document.getElementById('bltFile').value; + bltPath = bltPath.substring(Math.max(bltPath.lastIndexOf('\\'), bltPath.lastIndexOf('/')) + 1); let bltFile = document.getElementById('bltFile').files[0]; - let electionData = await bltFile.text(); + let bltData = await bltFile.text(); + + // Read CON file (if applicable) + let conPath = null; + let conData = null; + if (document.getElementById('conFile').files.length > 0) { + conPath = document.getElementById('conFile').value; + conPath = conPath.substring(Math.max(conPath.lastIndexOf('\\'), conPath.lastIndexOf('/')) + 1); + + let conFile = document.getElementById('conFile').files[0]; + conData = await conFile.text(); + } // Init STV options let optsStr = [ @@ -127,9 +138,11 @@ async function clickCount() { // Dispatch to worker worker.postMessage({ 'type': 'countElection', - 'electionData': electionData, + 'bltData': bltData, + 'conData': conData, 'optsStr': optsStr, - 'filePath': filePath, + 'bltPath': bltPath, + 'conPath': conPath, 'numbers': document.getElementById('selNumbers').value, 'decimals': document.getElementById('txtDP').value, 'normaliseBallots': document.getElementById('chkNormaliseBallots').checked, diff --git a/html/worker.js b/html/worker.js index c5dde8f..9123168 100644 --- a/html/worker.js +++ b/html/worker.js @@ -27,12 +27,17 @@ onmessage = function(evt) { } // Init election - election = wasm['election_from_blt_' + numbers](evt.data.electionData); + election = wasm['election_from_blt_' + numbers](evt.data.bltData); if (evt.data.normaliseBallots) { wasm['election_normalise_ballots_' + numbers](election); } + // Init constraints if applicable + if (evt.data.conData) { + wasm['election_load_constraints_' + numbers](election, evt.data.conData); + } + // Init STV options opts = wasm.STVOptions.new.apply(null, evt.data.optsStr); @@ -40,7 +45,7 @@ onmessage = function(evt) { opts.validate(); // Describe count - postMessage({'type': 'describeCount', 'content': wasm['describe_count_' + numbers](evt.data.filePath, election, opts)}); + postMessage({'type': 'describeCount', 'content': wasm['describe_count_' + numbers](evt.data.bltPath, election, opts)}); // Init results table postMessage({'type': 'initResultsTable', 'content': wasm['init_results_table_' + numbers](election, opts)}); diff --git a/src/stv/wasm.rs b/src/stv/wasm.rs index d34fc03..a7ef5d4 100644 --- a/src/stv/wasm.rs +++ b/src/stv/wasm.rs @@ -17,6 +17,7 @@ #![allow(rustdoc::private_intra_doc_links)] +use crate::constraints::Constraints; use crate::election::{CandidateState, CountState, Election}; use crate::numbers::{Fixed, GuardedFixed, NativeFloat64, Number, Rational}; use crate::stv; @@ -66,6 +67,13 @@ macro_rules! impl_type { election.0.normalise_ballots(); } + /// Call [Constraints::from_con] and set [Election::constraints] + #[wasm_bindgen] + #[allow(non_snake_case)] + pub fn [](election: &mut [], text: String) { + election.0.constraints = Some(Constraints::from_con(text.lines().map(|s| s.to_string()).into_iter())); + } + /// Wrapper for [stv::count_init] #[wasm_bindgen] #[allow(non_snake_case)] @@ -303,22 +311,31 @@ fn update_results_table(stage_num: usize, state: &CountState, opts result.push(&format!(r#"{}"#, tdclasses1, 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#"{}"#, tdclasses2, pp(&count_card.transfers, opts.pp_decimals)).into()); - result.push(&format!(r#"{}"#, tdclasses2, pp(&count_card.votes, opts.pp_decimals)).into()); - } else if count_card.state == stv::CandidateState::Excluded { - result.push(&format!(r#"{}"#, tdclasses2, pp(&count_card.transfers, opts.pp_decimals)).into()); - if count_card.votes.is_zero() { - result.push(&format!(r#"Ex"#, tdclasses2).into()); - } else { + match count_card.state { + CandidateState::Hopeful | CandidateState::Guarded => { + result.push(&format!(r#"{}"#, tdclasses2, pp(&count_card.transfers, opts.pp_decimals)).into()); + result.push(&format!(r#"{}"#, tdclasses2, pp(&count_card.votes, opts.pp_decimals)).into()); + } + CandidateState::Elected => { + result.push(&format!(r#"{}"#, tdclasses2, pp(&count_card.transfers, opts.pp_decimals)).into()); + result.push(&format!(r#"{}"#, tdclasses2, pp(&count_card.votes, opts.pp_decimals)).into()); + } + CandidateState::Doomed => { + result.push(&format!(r#"{}"#, tdclasses2, pp(&count_card.transfers, opts.pp_decimals)).into()); result.push(&format!(r#"{}"#, tdclasses2, pp(&count_card.votes, opts.pp_decimals)).into()); } - } else if count_card.state == stv::CandidateState::Withdrawn { - result.push(&format!(r#""#, tdclasses2).into()); - result.push(&format!(r#"WD"#, tdclasses2).into()); - } else { - result.push(&format!(r#"{}"#, tdclasses2, pp(&count_card.transfers, opts.pp_decimals)).into()); - result.push(&format!(r#"{}"#, tdclasses2, pp(&count_card.votes, opts.pp_decimals)).into()); + CandidateState::Withdrawn => { + result.push(&format!(r#""#, tdclasses2).into()); + result.push(&format!(r#"WD"#, tdclasses2).into()); + } + CandidateState::Excluded => { + result.push(&format!(r#"{}"#, tdclasses2, pp(&count_card.transfers, opts.pp_decimals)).into()); + if count_card.votes.is_zero() { + result.push(&format!(r#"Ex"#, tdclasses2).into()); + } else { + result.push(&format!(r#"{}"#, tdclasses2, pp(&count_card.votes, opts.pp_decimals)).into()); + } + } } } result.push(&format!(r#"{}"#, tdclasses2, pp(&state.exhausted.transfers, opts.pp_decimals)).into());