189 lines
5.6 KiB
Rust
189 lines
5.6 KiB
Rust
mod codec;
|
|
|
|
/// simple typed helper enums
|
|
mod simple_enums;
|
|
|
|
mod transaction;
|
|
|
|
fn main() {
|
|
let mut args: Vec<_> = std::env::args().skip(1).collect();
|
|
|
|
if args.is_empty() {
|
|
return;
|
|
}
|
|
|
|
use {
|
|
chrono::Datelike,
|
|
itertools::Itertools,
|
|
prettytable::{format, row, Table},
|
|
rayon::prelude::*,
|
|
std::{
|
|
collections::{BTreeMap, HashSet},
|
|
io::Write,
|
|
},
|
|
};
|
|
|
|
let mut highlight = HashSet::new();
|
|
let hl_only = args[0] == "--highlight-only";
|
|
|
|
if args[0] == "--highlight" || hl_only {
|
|
args.remove(0);
|
|
let eo_selst = args.iter().take_while(|i| *i != ";").count();
|
|
highlight = args
|
|
.drain(..std::cmp::min(eo_selst + 1, args.len()))
|
|
.collect();
|
|
highlight.remove(";");
|
|
}
|
|
|
|
let i_skipped = std::sync::atomic::AtomicUsize::new(0);
|
|
|
|
let mut dat: Vec<_> = args
|
|
.into_par_iter()
|
|
.flat_map(|i| {
|
|
let mut rdr = csv::ReaderBuilder::new()
|
|
.delimiter(b';')
|
|
.flexible(true)
|
|
.has_headers(false)
|
|
.from_reader(codec::Latin12Utf8Reader::new(
|
|
std::fs::File::open(i).expect("unable to open file"),
|
|
));
|
|
|
|
let mut recsit = rdr.records().skip(8);
|
|
|
|
assert_eq!(
|
|
recsit.next().unwrap().unwrap(),
|
|
vec![
|
|
"Buchungstag",
|
|
"Valuta",
|
|
"Auftraggeber/Zahlungsempfänger",
|
|
"Empfänger/Zahlungspflichtiger",
|
|
"Konto-Nr.",
|
|
"IBAN",
|
|
"BLZ",
|
|
"BIC",
|
|
"Vorgang/Verwendungszweck",
|
|
"Kundenreferenz",
|
|
"Währung",
|
|
"Umsatz",
|
|
" "
|
|
]
|
|
);
|
|
|
|
let mut idat = Vec::new();
|
|
let mut iisk = 0usize;
|
|
for result in recsit {
|
|
let record = result.expect("got invalid line");
|
|
let record_bak = record.clone();
|
|
let pres: Result<transaction::TransactionLine, _> =
|
|
std::convert::TryInto::try_into(record);
|
|
|
|
match pres {
|
|
Ok(tl) => {
|
|
if tl.direction == simple_enums::TransactionDirection::Haben
|
|
&& tl.waehrung == simple_enums::Waehrung::Eur
|
|
&& !tl.p_other.is_empty()
|
|
&& !tl.p_other.contains(" ZINS BIS ")
|
|
{
|
|
idat.push(tl)
|
|
} else {
|
|
iisk += 1;
|
|
}
|
|
}
|
|
Err(transaction::ParseError::Finalizer) => break,
|
|
Err(x) => panic!("got error '{}' @ {:?}", x, record_bak),
|
|
}
|
|
}
|
|
i_skipped.fetch_add(iisk, std::sync::atomic::Ordering::SeqCst);
|
|
idat
|
|
})
|
|
.collect();
|
|
|
|
dat.par_sort();
|
|
|
|
let oldlen = dat.len();
|
|
let mut newlen = 0usize;
|
|
let mut stdout = term::stdout().unwrap();
|
|
let mut accu = BTreeMap::<
|
|
(i32, bool, String),
|
|
(usize, transaction::TransactionValue, String, usize),
|
|
>::new();
|
|
|
|
for i in dat.into_iter().dedup() {
|
|
newlen += 1;
|
|
let mut ent = accu
|
|
.entry((i.d_buchungs.year(), i.d_buchungs.month() > 6, i.p_other))
|
|
.or_default();
|
|
ent.0 += 1;
|
|
ent.1 += i.umsatz;
|
|
}
|
|
|
|
writeln!(
|
|
&mut stdout,
|
|
"skipped {} duplicates and {} misc entries",
|
|
oldlen - newlen,
|
|
i_skipped.into_inner(),
|
|
)
|
|
.unwrap();
|
|
|
|
if accu.is_empty() {
|
|
return;
|
|
}
|
|
|
|
let mut pdsp = 0;
|
|
for i in accu.values_mut() {
|
|
i.2 = i.1.to_string();
|
|
i.3 = i.2.find('.').map(|x| i.2.len() - x).unwrap_or(0);
|
|
pdsp = std::cmp::max(pdsp, i.3);
|
|
}
|
|
|
|
let mut table = Table::new();
|
|
table.set_format(
|
|
format::FormatBuilder::new()
|
|
.column_separator('│')
|
|
.borders('│')
|
|
.separator(
|
|
format::LinePosition::Top,
|
|
format::LineSeparator::new('─', '┬', '┌', '┐'),
|
|
)
|
|
.separator(
|
|
format::LinePosition::Title,
|
|
format::LineSeparator::new('─', '┼', '├', '┤'),
|
|
)
|
|
.separator(
|
|
format::LinePosition::Bottom,
|
|
format::LineSeparator::new('─', '┴', '└', '┘'),
|
|
)
|
|
.padding(1, 1)
|
|
.build(),
|
|
);
|
|
|
|
table.set_titles(row!["Jahr", "Einzahler", "Zahlungen", "Summe"]);
|
|
|
|
let mut prev_yuhj = Option::<String>::None;
|
|
for (k, v) in accu.iter_mut() {
|
|
let is_hl = highlight.contains(&*k.2);
|
|
if !hl_only || is_hl {
|
|
let mut yearuhj = format!("{} {}I", k.0, if k.1 { "I" } else { "" });
|
|
let is_ny = prev_yuhj.as_ref().map(|i| i != &yearuhj);
|
|
v.2 += &" ".repeat(pdsp - v.3);
|
|
if is_ny.unwrap_or(false) {
|
|
table.add_row(row!["", "", "", ""]);
|
|
}
|
|
if is_ny.unwrap_or(true) {
|
|
prev_yuhj = Some(yearuhj.clone());
|
|
} else {
|
|
yearuhj.clear();
|
|
}
|
|
table.add_row(if !hl_only && is_hl {
|
|
row![yearuhj, FYBdb-> k.2, r-> v.0.to_string(), Fgbr-> v.2]
|
|
} else {
|
|
row![yearuhj, k.2, r-> v.0.to_string(), r-> v.2]
|
|
});
|
|
}
|
|
}
|
|
|
|
table
|
|
.print_term(&mut *stdout)
|
|
.expect("unable to print table");
|
|
}
|