Implement constraints in web UI

This commit is contained in:
RunasSudo 2021-06-27 22:09:34 +10:00
parent 38eef74e77
commit 1d4036c19e
No known key found for this signature in database
GPG Key ID: 7234E476BF21C61A
4 changed files with 58 additions and 23 deletions

View File

@ -139,12 +139,12 @@
<input type="text" id="txtSeed" value=""> <input type="text" id="txtSeed" value="">
</label> </label>
</div> </div>
<!--<div class="subheading"> <div class="subheading">
Constraints: Constraints:
</div> </div>
<div> <div>
<input type="file" id="conFile"> <input type="file" id="conFile">
</div>--> </div>
</div> </div>
<div class="col-6 cols-12" style="align-self: start;"> <div class="col-6 cols-12" style="align-self: start;">
<div class="col-12 subheading"> <div class="col-12 subheading">

View File

@ -90,11 +90,22 @@ async function clickCount() {
} }
// Read BLT file // Read BLT file
let filePath = document.getElementById('bltFile').value; let bltPath = document.getElementById('bltFile').value;
filePath = filePath.substring(Math.max(filePath.lastIndexOf('\\'), filePath.lastIndexOf('/')) + 1); bltPath = bltPath.substring(Math.max(bltPath.lastIndexOf('\\'), bltPath.lastIndexOf('/')) + 1);
let bltFile = document.getElementById('bltFile').files[0]; 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 // Init STV options
let optsStr = [ let optsStr = [
@ -127,9 +138,11 @@ async function clickCount() {
// Dispatch to worker // Dispatch to worker
worker.postMessage({ worker.postMessage({
'type': 'countElection', 'type': 'countElection',
'electionData': electionData, 'bltData': bltData,
'conData': conData,
'optsStr': optsStr, 'optsStr': optsStr,
'filePath': filePath, 'bltPath': bltPath,
'conPath': conPath,
'numbers': document.getElementById('selNumbers').value, 'numbers': document.getElementById('selNumbers').value,
'decimals': document.getElementById('txtDP').value, 'decimals': document.getElementById('txtDP').value,
'normaliseBallots': document.getElementById('chkNormaliseBallots').checked, 'normaliseBallots': document.getElementById('chkNormaliseBallots').checked,

View File

@ -27,12 +27,17 @@ onmessage = function(evt) {
} }
// Init election // Init election
election = wasm['election_from_blt_' + numbers](evt.data.electionData); election = wasm['election_from_blt_' + numbers](evt.data.bltData);
if (evt.data.normaliseBallots) { if (evt.data.normaliseBallots) {
wasm['election_normalise_ballots_' + numbers](election); 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 // Init STV options
opts = wasm.STVOptions.new.apply(null, evt.data.optsStr); opts = wasm.STVOptions.new.apply(null, evt.data.optsStr);
@ -40,7 +45,7 @@ onmessage = function(evt) {
opts.validate(); opts.validate();
// Describe count // 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 // Init results table
postMessage({'type': 'initResultsTable', 'content': wasm['init_results_table_' + numbers](election, opts)}); postMessage({'type': 'initResultsTable', 'content': wasm['init_results_table_' + numbers](election, opts)});

View File

@ -17,6 +17,7 @@
#![allow(rustdoc::private_intra_doc_links)] #![allow(rustdoc::private_intra_doc_links)]
use crate::constraints::Constraints;
use crate::election::{CandidateState, CountState, Election}; use crate::election::{CandidateState, CountState, Election};
use crate::numbers::{Fixed, GuardedFixed, NativeFloat64, Number, Rational}; use crate::numbers::{Fixed, GuardedFixed, NativeFloat64, Number, Rational};
use crate::stv; use crate::stv;
@ -66,6 +67,13 @@ macro_rules! impl_type {
election.0.normalise_ballots(); election.0.normalise_ballots();
} }
/// Call [Constraints::from_con] and set [Election::constraints]
#[wasm_bindgen]
#[allow(non_snake_case)]
pub fn [<election_load_constraints_$type>](election: &mut [<Election$type>], text: String) {
election.0.constraints = Some(Constraints::from_con(text.lines().map(|s| s.to_string()).into_iter()));
}
/// Wrapper for [stv::count_init] /// Wrapper for [stv::count_init]
#[wasm_bindgen] #[wasm_bindgen]
#[allow(non_snake_case)] #[allow(non_snake_case)]
@ -303,22 +311,31 @@ fn update_results_table<N: Number>(stage_num: usize, state: &CountState<N>, opts
result.push(&format!(r#"<td{}>{}</td>"#, tdclasses1, state.title).into()); result.push(&format!(r#"<td{}>{}</td>"#, tdclasses1, state.title).into());
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 { match count_card.state {
result.push(&format!(r#"<td class="{}count elected">{}</td>"#, tdclasses2, pp(&count_card.transfers, opts.pp_decimals)).into()); CandidateState::Hopeful | CandidateState::Guarded => {
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">{}</td>"#, tdclasses2, pp(&count_card.transfers, opts.pp_decimals)).into());
} else if count_card.state == stv::CandidateState::Excluded { result.push(&format!(r#"<td class="{}count">{}</td>"#, tdclasses2, pp(&count_card.votes, opts.pp_decimals)).into());
result.push(&format!(r#"<td class="{}count excluded">{}</td>"#, tdclasses2, pp(&count_card.transfers, opts.pp_decimals)).into()); }
if count_card.votes.is_zero() { CandidateState::Elected => {
result.push(&format!(r#"<td class="{}count excluded">Ex</td>"#, tdclasses2).into()); result.push(&format!(r#"<td class="{}count elected">{}</td>"#, tdclasses2, pp(&count_card.transfers, opts.pp_decimals)).into());
} else { result.push(&format!(r#"<td class="{}count elected">{}</td>"#, tdclasses2, pp(&count_card.votes, opts.pp_decimals)).into());
}
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>"#, tdclasses2, pp(&count_card.votes, opts.pp_decimals)).into()); result.push(&format!(r#"<td class="{}count excluded">{}</td>"#, tdclasses2, pp(&count_card.votes, opts.pp_decimals)).into());
} }
} else if count_card.state == stv::CandidateState::Withdrawn { CandidateState::Withdrawn => {
result.push(&format!(r#"<td class="{}count excluded"></td>"#, tdclasses2).into()); result.push(&format!(r#"<td class="{}count excluded"></td>"#, tdclasses2).into());
result.push(&format!(r#"<td class="{}count excluded">WD</td>"#, tdclasses2).into()); result.push(&format!(r#"<td class="{}count excluded">WD</td>"#, tdclasses2).into());
} else { }
result.push(&format!(r#"<td class="{}count">{}</td>"#, tdclasses2, pp(&count_card.transfers, opts.pp_decimals)).into()); CandidateState::Excluded => {
result.push(&format!(r#"<td class="{}count">{}</td>"#, tdclasses2, pp(&count_card.votes, opts.pp_decimals)).into()); result.push(&format!(r#"<td class="{}count excluded">{}</td>"#, tdclasses2, pp(&count_card.transfers, opts.pp_decimals)).into());
if count_card.votes.is_zero() {
result.push(&format!(r#"<td class="{}count excluded">Ex</td>"#, tdclasses2).into());
} 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">{}</td>"#, tdclasses2, pp(&state.exhausted.transfers, opts.pp_decimals)).into()); result.push(&format!(r#"<td class="{}count">{}</td>"#, tdclasses2, pp(&state.exhausted.transfers, opts.pp_decimals)).into());