loader working
This commit is contained in:
parent
878c3e577e
commit
29ffe1190f
|
|
@ -6,99 +6,36 @@ version = 4
|
||||||
name = "ballot-counter"
|
name = "ballot-counter"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"csv",
|
"itertools",
|
||||||
|
"quick-csv",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "csv"
|
name = "either"
|
||||||
version = "1.3.1"
|
version = "1.15.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "acdc4883a9c96732e4733212c01447ebd805833b7275a73ca3ee080fd77afdaf"
|
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"csv-core",
|
"either",
|
||||||
"itoa",
|
|
||||||
"ryu",
|
|
||||||
"serde",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "csv-core"
|
name = "quick-csv"
|
||||||
version = "0.1.12"
|
version = "0.1.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7d02f3b0da4c6504f86e9cd789d8dbafab48c2321be74e9987593de5a894d93d"
|
checksum = "edf4b7701db7d2e4c9c010f21eebd8676a50f79223a0cf858162d24bff47338c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"rustc-serialize",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "rustc-serialize"
|
||||||
version = "1.0.15"
|
version = "0.3.25"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
checksum = "fe834bc780604f4674073badbad26d7219cadfb4a2275802db12cbae17498401"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "memchr"
|
|
||||||
version = "2.7.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "proc-macro2"
|
|
||||||
version = "1.0.95"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
|
|
||||||
dependencies = [
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "quote"
|
|
||||||
version = "1.0.40"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ryu"
|
|
||||||
version = "1.0.20"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde"
|
|
||||||
version = "1.0.219"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
|
||||||
dependencies = [
|
|
||||||
"serde_derive",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde_derive"
|
|
||||||
version = "1.0.219"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "syn"
|
|
||||||
version = "2.0.101"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-ident"
|
|
||||||
version = "1.0.18"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
|
||||||
|
|
|
||||||
|
|
@ -4,4 +4,5 @@ version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
csv = "1.3.1"
|
itertools = "0.14.0"
|
||||||
|
quick-csv = "0.1.6"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
|
use crate::header::Header;
|
||||||
|
|
||||||
|
#[derive(Copy,Clone,Debug,PartialEq)]
|
||||||
|
pub enum BallotType {
|
||||||
|
Party,
|
||||||
|
Candidate,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Ballot {
|
||||||
|
pub state: String,
|
||||||
|
pub division: String,
|
||||||
|
pub collection_point: String,
|
||||||
|
pub collection_point_id: usize,
|
||||||
|
pub batch_id: usize,
|
||||||
|
pub paper_id: usize,
|
||||||
|
pub votes: Vec<usize>,
|
||||||
|
pub ty: BallotType,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ballot {
|
||||||
|
pub fn parse_rows<T: std::io::BufRead>(csv: quick_csv::Csv<T>, header: &Header) -> Result<Vec<Ballot>, Box<dyn std::error::Error>> {
|
||||||
|
let mut ballots = Vec::<Self>::new();
|
||||||
|
|
||||||
|
let col_filter = |(index, place): (usize, &str)| match place.parse::<usize>() {
|
||||||
|
Ok(v) => Some((index, v - 1)),
|
||||||
|
Err(_) => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
for result in csv {
|
||||||
|
let row = result?;
|
||||||
|
let mut cols = row.columns()?;
|
||||||
|
|
||||||
|
let (state, division, collection_point, collection_point_id, batch_id, paper_id) = cols.next_tuple().ok_or("Missing columns")?;
|
||||||
|
let cols = cols.collect_vec();
|
||||||
|
let mut votes: Vec<Option<usize>>;
|
||||||
|
let ty: BallotType;
|
||||||
|
|
||||||
|
// below the line votes
|
||||||
|
if cols.len() > header.parties.len() {
|
||||||
|
ty = BallotType::Candidate;
|
||||||
|
votes = vec![None; header.candidates.len()];
|
||||||
|
for (col_id, place) in cols.iter().copied().skip(header.parties.len()).take(header.candidates.len()).enumerate().filter_map(col_filter) {
|
||||||
|
votes[place] = Some(col_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// above the line votes
|
||||||
|
else {
|
||||||
|
ty = BallotType::Party;
|
||||||
|
votes = vec![None; header.parties.len()];
|
||||||
|
for (col_id, place) in cols.iter().copied().take(header.parties.len()).enumerate().filter_map(col_filter) {
|
||||||
|
votes[place] = Some(col_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ballots.push(Ballot {
|
||||||
|
state: state.to_owned(),
|
||||||
|
division: division.to_owned(),
|
||||||
|
collection_point: collection_point.to_owned(),
|
||||||
|
collection_point_id: collection_point_id.parse()?,
|
||||||
|
batch_id: batch_id.parse()?,
|
||||||
|
paper_id: paper_id.parse()?,
|
||||||
|
votes: votes.iter().copied().filter_map(|v| v).collect(),
|
||||||
|
ty,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(ballots)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Candidate {
|
||||||
|
pub name: String,
|
||||||
|
pub party_id: Option<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
|
use crate::candidate::Candidate;
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Header {
|
||||||
|
pub parties: Vec<String>,
|
||||||
|
pub candidates: Vec<Candidate>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Header {
|
||||||
|
pub fn parse(row: quick_csv::Row) -> Result<Header, Box<dyn std::error::Error>> {
|
||||||
|
let mut header = Header {
|
||||||
|
parties: Vec::new(),
|
||||||
|
candidates: Vec::new(),
|
||||||
|
};
|
||||||
|
let mut parties_lookup = HashMap::<&str, usize>::new();
|
||||||
|
|
||||||
|
for col in row.columns()?.skip(6) {
|
||||||
|
let [party, name] = col.split(':').next_array().ok_or("Missing ':'")?;
|
||||||
|
|
||||||
|
if party == "UG" { // independents
|
||||||
|
header.candidates.push(Candidate {
|
||||||
|
name: name.to_owned(),
|
||||||
|
party_id: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if let Some(party_id) = parties_lookup.get(party).copied() {
|
||||||
|
header.candidates.push(Candidate {
|
||||||
|
name: name.to_owned(),
|
||||||
|
party_id: Some(party_id),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
parties_lookup.insert(party, header.parties.len());
|
||||||
|
header.parties.push(name.to_owned());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(header)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
40
src/main.rs
40
src/main.rs
|
|
@ -1,3 +1,41 @@
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
use ballot::{Ballot, BallotType};
|
||||||
|
use header::Header;
|
||||||
|
|
||||||
|
pub mod candidate;
|
||||||
|
mod ballot;
|
||||||
|
mod header;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("Hello, world!");
|
let csv_path = {
|
||||||
|
let args: Vec<String> = env::args().collect();
|
||||||
|
if args.len() != 2 {
|
||||||
|
eprintln!("Usage: {} <filename.csv>", args[0]);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
args[1].clone()
|
||||||
|
};
|
||||||
|
let mut csv = quick_csv::Csv::from_file(csv_path).unwrap().flexible(true);
|
||||||
|
let header = Header::parse(csv.next().unwrap().unwrap()).unwrap();
|
||||||
|
let ballots = Ballot::parse_rows(csv, &header).unwrap();
|
||||||
|
|
||||||
|
println!("Parties: {}", header.parties.len());
|
||||||
|
println!("Candidates: {}", header.candidates.len());
|
||||||
|
|
||||||
|
let total = ballots.len() as u32;
|
||||||
|
let mut total_atl = 0u32;
|
||||||
|
let mut total_btl = 0u32;
|
||||||
|
|
||||||
|
for ballot in ballots.iter() {
|
||||||
|
*match ballot.ty {
|
||||||
|
BallotType::Party => &mut total_atl,
|
||||||
|
BallotType::Candidate => &mut total_btl,
|
||||||
|
} += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Above the line: {total_atl} {}%", f64::from(total_atl) / f64::from(total) * 100.0);
|
||||||
|
println!("Below the line: {total_btl} {}%", f64::from(total_btl) / f64::from(total) * 100.0);
|
||||||
|
println!("Total: {total}");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue