Initial commit after refactor
This commit is contained in:
11
rust/timetracking/timelogging/Cargo.toml
Normal file
11
rust/timetracking/timelogging/Cargo.toml
Normal file
@@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "timelogging"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
regex = "1"
|
||||
chrono = "0.4"
|
||||
itertools = "0.8"
|
136
rust/timetracking/timelogging/src/lib.rs
Normal file
136
rust/timetracking/timelogging/src/lib.rs
Normal file
@@ -0,0 +1,136 @@
|
||||
|
||||
pub mod timetracking {
|
||||
use std::io::BufRead;
|
||||
use std::collections::HashMap;
|
||||
use regex::Regex;
|
||||
use chrono::NaiveDateTime;
|
||||
use itertools::Itertools;
|
||||
|
||||
pub fn nearest(hr: f64) -> f64 {
|
||||
let t = hr * 4.0;
|
||||
t.round() / 4.0
|
||||
}
|
||||
|
||||
pub fn process_input_file(reader: &mut Box<dyn BufRead>) -> (
|
||||
HashMap<String, Vec<i64>>,
|
||||
HashMap<String, Vec<i64>>,
|
||||
Vec<String>) {
|
||||
let mut start: HashMap<String, Vec<i64>> = HashMap::new();
|
||||
let mut finish: HashMap<String, Vec<i64>> = HashMap::new();
|
||||
let mut categories: Vec<String> = Vec::new();
|
||||
|
||||
// regex
|
||||
let empty = Regex::new(r"^\s*$").unwrap(); // regex for finding empty lines (and skipping them)
|
||||
let comment = Regex::new(r"^\s*#").unwrap(); // regex for finding empty lines (and skipping them)
|
||||
let delim = Regex::new(r":\s{2}").unwrap(); // regex for splitting timestamp entry
|
||||
let begin = Regex::new(r"Begin\s+(?P<action>.*)\s*$").unwrap();
|
||||
let end = Regex::new(r"End\s+(?P<action>.*)\s*$").unwrap();
|
||||
let category = Regex::new(r"\[(?P<category>.*)\]\s+(?P<activity>.*)\s*$").unwrap();
|
||||
|
||||
for line in reader.lines() {
|
||||
// need to get the string out of line (which is an option enum)
|
||||
let l: String = line.unwrap();
|
||||
|
||||
// if the line is empty or is a comment
|
||||
if empty.is_match(&l) || comment.is_match(&l) {
|
||||
continue
|
||||
};
|
||||
|
||||
// Parse the timestamp and entry
|
||||
let splits: Vec<_> = delim.split(&l.trim_end()).into_iter().collect();
|
||||
// splits should only ever have two elements (only one delimiter per log line)
|
||||
let datetime = splits[0];
|
||||
let entry = splits[1];
|
||||
let date_time = NaiveDateTime::parse_from_str(datetime,"%Y-%m-%d %H:%M:%S").unwrap();
|
||||
let timestamp = date_time.timestamp();
|
||||
|
||||
// Process a Begin
|
||||
if begin.is_match(&entry) {
|
||||
let caps = begin.captures(&entry).unwrap();
|
||||
let action = caps.name("action").unwrap().as_str();
|
||||
start.entry(action.to_string()).or_insert(Vec::new()).push(timestamp);
|
||||
let cats = category.captures(&entry).unwrap();
|
||||
let cat = cats.name("category").unwrap().as_str();
|
||||
categories.push(cat.to_string());
|
||||
|
||||
};
|
||||
|
||||
// Process an End
|
||||
if end.is_match(&entry) {
|
||||
let caps = end.captures(&entry).unwrap();
|
||||
let action = caps.name("action").unwrap().as_str();
|
||||
finish.entry(action.to_string()).or_insert(Vec::new()).push(timestamp);
|
||||
let cats = category.captures(&entry).unwrap();
|
||||
let cat = cats.name("category").unwrap().as_str();
|
||||
categories.push(cat.to_string());
|
||||
};
|
||||
|
||||
}; // end for
|
||||
// sort and dedup categories
|
||||
categories.sort_unstable();
|
||||
categories.dedup();
|
||||
|
||||
(start, finish, categories)
|
||||
}
|
||||
|
||||
pub fn generate_individual_timecards (
|
||||
start: &mut HashMap<String, Vec<i64>>,
|
||||
finish: &mut HashMap<String, Vec<i64>>
|
||||
) -> (
|
||||
HashMap<String, i64>,
|
||||
f64
|
||||
)
|
||||
{
|
||||
let mut ind: HashMap<String, i64> = HashMap::new();
|
||||
let mut gtoth: f64 = 0.0;
|
||||
for (act, tstamps) in start.iter_mut().sorted_by_key(|x| x.0) {
|
||||
let bc: i64 = tstamps.len().try_into().unwrap();
|
||||
let mut ec: i64 = 0;
|
||||
if finish.contains_key(&act.to_string()) {
|
||||
ec = finish.get(&act.to_string()).expect("Somehow, not a vector!").len().try_into().unwrap();
|
||||
};
|
||||
|
||||
// Debugging
|
||||
// println!("bc: {}, ec: {}", bc, ec);
|
||||
// This block ensures the lengths of start and finish for this action are equal
|
||||
if bc - ec > 1 {
|
||||
panic!("ERROR: Missing more than one End"); // need to fix log file
|
||||
} else if bc > ec { // bc should be exactly one greater
|
||||
tstamps.pop();
|
||||
} else if ec > bc {
|
||||
panic!("ERROR: Missing a Begin"); // need to fix log file
|
||||
};
|
||||
|
||||
// Debugging
|
||||
//for t in tstamps.into_iter() {
|
||||
// print!("{} ", t);
|
||||
//};
|
||||
//println!();
|
||||
//for t in finish.get_mut(&act.to_string()).expect("Somehow, not a vector!").into_iter() {
|
||||
// print!("{} ", t);
|
||||
//};
|
||||
//println!();
|
||||
|
||||
|
||||
let mut delta: i64 = 0;
|
||||
for i in 0..tstamps.len() {
|
||||
let beg = tstamps[i];
|
||||
let en = finish.get_mut(&act.to_string()).unwrap()[i];
|
||||
delta += en - beg;
|
||||
};
|
||||
|
||||
ind.insert(act.to_string(), delta);
|
||||
let d: f64 = nearest(delta as f64 / 3600.00);
|
||||
if d == 0.0 {
|
||||
gtoth += 0.08;
|
||||
} else {
|
||||
gtoth += d;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
(ind, gtoth)
|
||||
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user