Fix flush pass to print to and from department/account

This commit is contained in:
Piv
2023-03-10 17:30:23 +10:30
parent 8dc0881197
commit f5a777baa7
2 changed files with 46 additions and 40 deletions

View File

@@ -91,7 +91,16 @@ pub struct CsvCost {
pub department: String, pub department: String,
#[serde(serialize_with = "round_serialize")] #[serde(serialize_with = "round_serialize")]
pub value: f64, pub value: f64,
pub pass: Option<i32>, }
#[derive(Serialize)]
pub struct MovedMoneyAmount {
pub account: String,
pub department: String,
pub value: f64,
pub from_account: String,
pub from_department: String,
pub pass: i32,
} }
fn round_serialize<S>(x: &f64, s: S) -> Result<S::Ok, S::Error> fn round_serialize<S>(x: &f64, s: S) -> Result<S::Ok, S::Error>
@@ -271,16 +280,15 @@ where
} }
// Then run move_money // Then run move_money
let moved = move_money_2(lines, &rules, flush_pass); let moved = move_money_2(lines, &rules, flush_pass, output);
// Ouput the list moved moneys if !flush_pass {
for money in moved { // Ouput the final moneys
for (unit, value) in money.totals { for (unit, value) in moved {
output.serialize(CsvCost { output.serialize(CsvCost {
account: unit.account, account: unit.account,
department: unit.department, department: unit.department,
value, value,
pass: if flush_pass { Some(money.pass) } else { None },
})?; })?;
} }
} }
@@ -323,12 +331,6 @@ fn extract_range(from: String, to: String, options: &Vec<String>) -> HashSet<Str
// Advantage of this approach is it can be easily extended to run on the gpu. // Advantage of this approach is it can be easily extended to run on the gpu.
pub fn move_money_1() {} pub fn move_money_1() {}
pub struct MoveMoneyResult {
pass: i32,
// TODO: We want the from account and the to account
totals: HashMap<Unit, f64>,
}
// Approach 2: // Approach 2:
// Traditinoal/naive, total for each department is stored in an initial map (department -> total amount). // Traditinoal/naive, total for each department is stored in an initial map (department -> total amount).
// Another map is built up for each rule, and each rule is processed based on the amount in the current total // Another map is built up for each rule, and each rule is processed based on the amount in the current total
@@ -340,41 +342,39 @@ pub struct MoveMoneyResult {
// to every. It's also much more memory efficient than approach 1. // to every. It's also much more memory efficient than approach 1.
// TODO: Time both approaches to seee which is faster depending on the size of the input data/number of rules // TODO: Time both approaches to seee which is faster depending on the size of the input data/number of rules
// Verdict: This is already pretty fast, at least much faster than ppm for BigDataset // Verdict: This is already pretty fast, at least much faster than ppm for BigDataset
pub fn move_money_2( pub fn move_money_2<W>(
initial_totals: HashMap<Unit, f64>, initial_totals: HashMap<Unit, f64>,
rules: &Vec<MovementRule>, rules: &Vec<MovementRule>,
flush_pass: bool, flush_pass: bool,
) -> Vec<MoveMoneyResult> { output: &mut csv::Writer<W>,
) -> HashMap<Unit, f64>
where
W: std::io::Write,
{
// Note: It's potentially a bit more intensive to use cloned totals (rather than just update temp_total per rule), // Note: It's potentially a bit more intensive to use cloned totals (rather than just update temp_total per rule),
// but it's much simpler code and, and since we're only working line-by-line, it isn't really that much memory in practice // but it's much simpler code and, and since we're only working line-by-line, it isn't really that much memory in practice
// TODO: This is initial totals is problematic, as we may move into a cc that wasn't in the list of lines. In which case we'd need // TODO: This is initial totals is problematic, as we may move into a cc that wasn't in the list of lines. In which case we'd need
// to add a new line // to add a new line
let mut running_total = HashMap::from(initial_totals); let mut running_total = HashMap::from(initial_totals);
let mut temp_total = running_total.clone(); let mut temp_total = running_total.clone();
let mut move_money_result: Vec<MoveMoneyResult> = vec![];
let mut current_pass = 0; let mut current_pass = 0;
if flush_pass { if flush_pass {
move_money_result.push(MoveMoneyResult { for (unit, value) in running_total.iter() {
pass: current_pass, output
totals: running_total.clone(), .serialize(MovedMoneyAmount {
}) account: unit.account.clone(),
department: unit.department.clone(),
value: *value,
from_account: unit.account.clone(),
from_department: unit.department.clone(),
pass: current_pass,
})
.expect("Failed to write moved amount")
}
} }
for rule in rules { for rule in rules {
if rule.is_separator { if rule.is_separator {
if flush_pass { current_pass += 1;
current_pass += 1;
// Flush the totals at the end of this pass (more specifically the change)
move_money_result.push(MoveMoneyResult {
pass: current_pass,
totals: temp_total
.iter()
.map(|(unit, value)| {
(unit.clone(), value - running_total.get(unit).unwrap_or(&0.))
})
.filter(|(_, value)| *value != 0.)
.collect(),
});
}
running_total = temp_total.clone(); running_total = temp_total.clone();
} else { } else {
for (unit, amount) in &running_total { for (unit, amount) in &running_total {
@@ -399,6 +399,18 @@ pub fn move_money_2(
} else { } else {
unit.account.clone() unit.account.clone()
}; };
if flush_pass {
output
.serialize(MovedMoneyAmount {
department: department.clone(),
account: department.clone(),
value: added_amount,
from_department: unit.department.clone(),
from_account: unit.account.clone(),
pass: current_pass,
})
.expect("Failed to write moved amount");
}
*temp_total *temp_total
.entry(Unit { .entry(Unit {
department, department,
@@ -409,12 +421,7 @@ pub fn move_money_2(
} }
} }
} }
move_money_result.push(MoveMoneyResult { temp_total
pass: current_pass,
totals: temp_total,
});
move_money_result
} }
#[cfg(test)] #[cfg(test)]

View File

@@ -381,7 +381,6 @@ where
account: cost.account.clone(), account: cost.account.clone(),
department: department.department, department: department.department,
value: department.value, value: department.value,
pass: None,
})?; })?;
} }
} }