Skip to content

Commit

Permalink
First commit
Browse files Browse the repository at this point in the history
  • Loading branch information
dankski committed May 15, 2020
1 parent 8c52a70 commit 983d9a9
Show file tree
Hide file tree
Showing 10 changed files with 319 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/target
**/*.rs.bk
**/.DS_Store
81 changes: 81 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[package]
name = "work"
version = "0.1.0"
authors = ["Giacomo Amoroso <[email protected]>"]
edition = "2018"

[dependencies]
chrono = "0.4.6"
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod task;
40 changes: 40 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
extern crate work;

use std::env;
use std::fs::File;
use std::io::prelude::*;

use work::task::activity::*;
use work::task::work::Day;


fn filename (args: &[String]) -> Option<&str> {
if args.len() == 2 {
return Some(&args[1]);
}
return None;
}

fn help () -> String {
String::from("Missing filename! Please invoke: $ ./work [filename]")
}

fn main() {
let args: Vec<String> = env::args().collect();
let fname = filename(&args);

if fname.is_none() {
println!("{}", help());
return;
}

let mut file = File::open(fname.unwrap()).expect("file not found!");
let mut content = String::new();
file.read_to_string(&mut content).expect("something went wrong reading the file.");

let _activities: Vec<Activity> = content.lines().map(|l| work::task::activity::parse_activity_line(&l)).collect();
let mut day = Day::new();

day.load(_activities);
println!("{}", day.sum_report());
}
77 changes: 77 additions & 0 deletions src/task/activity.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
use chrono::NaiveTime;
use crate::task::utils::time_hm;

pub struct Activity {
description: String,
start: NaiveTime,
end: NaiveTime
}

impl Activity {

pub fn with (descrition: &String, start: &NaiveTime, end: &NaiveTime) -> Activity {
Activity{description: descrition.clone(), start: start.clone(), end: end.clone()}
}

pub fn duration (&self) -> f64 {
let span = self.end.signed_duration_since(self.start());
let h = (span.num_minutes() / 60) as f64;
let m = ((span.num_minutes() % 60) as f64) * (1.0/60.0);
h + m
}

pub fn description (&self) -> String {
self.description.clone()
}

pub fn start (&self) -> NaiveTime {
self.start.clone()
}

pub fn end (&self) -> NaiveTime {
self.end.clone()
}

pub fn clone (&self) -> Activity {
Activity {description: self.description().clone(), start: self.start(), end: self.end()}
}
}

pub fn parse_activity_line (line: &str) -> Activity {
let tokens: Vec<&str> = line.splitn(3, ' ').collect();
Activity::with(
&String::from(tokens[2]),
&time_hm(&String::from(tokens[0])),
&time_hm(&String::from(tokens[1]))
)
}

pub fn str_duration (activity: &Activity) -> String {
let span = activity.end().signed_duration_since(activity.start());
let h = (span.num_minutes() / 60) as f64;
let m = ((span.num_minutes() % 60) as f64) * (1.0/60.0);
format!("{:.2}", (h + m).abs())
}


#[cfg(test)]
mod tests {
use super::*;
#[test]
fn should_calculate_actitivity_duration () {
let mut activity = Activity::with(&"test".to_string(), &NaiveTime::from_hms(17,0,0), &NaiveTime::from_hms(18,0,0));
assert_eq!(str_duration(&activity), "1.00");

activity = Activity::with(&"test".to_string(), &NaiveTime::from_hms(23,0,0), &NaiveTime::from_hms(0,0,0));
assert_eq!(str_duration(&activity), "23.00");
}

#[test]
fn should_calculate_activity_duration_with_minutes () {
let mut activity = Activity::with(&"test".to_string(), &NaiveTime::from_hms(17,0,0), &NaiveTime::from_hms(18,45,0));
assert_eq!(str_duration(&activity), "1.75");

activity = Activity::with(&"test".to_string(), &NaiveTime::from_hms(17,30,0), &NaiveTime::from_hms(18,00,0));
assert_eq!(str_duration(&activity), "0.50");
}
}
3 changes: 3 additions & 0 deletions src/task/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub mod activity;
pub mod utils;
pub mod work;
32 changes: 32 additions & 0 deletions src/task/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
extern crate chrono;

use chrono::NaiveTime;

pub fn time_hm (time: &String) -> NaiveTime {
match NaiveTime::parse_from_str(time, "%H:%M") {
Ok(NaiveTime) => NaiveTime,
Err(error) => {
panic!("There was a problem parsing the time: {:?}", error)
},
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn should_parse_naive_time () {
let time = String::from("15:30");
assert_eq!(time_hm(&time), NaiveTime::from_hms(15, 30, 0));
}

#[test]
fn should_return_time_span () {
let start = time_hm(&String::from("15:30"));
let end = time_hm(&String::from("16:01"));
let span = end.signed_duration_since(start);

assert_eq!(span.num_minutes(), 31);
}
}
70 changes: 70 additions & 0 deletions src/task/work.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
use crate::task::activity::str_duration;
use crate::task::activity::Activity;
use std::collections::HashMap;

pub struct Day {
activities: Vec<Activity>,
}

impl Day {
pub fn new() -> Day {
Day {
activities: Vec::new(),
}
}

pub fn load(&mut self, activities: Vec<Activity>) {
activities
.iter()
.for_each(|a| self.activities.push(a.clone()));
}

pub fn simple_report(&self) -> String {
let mut sp = String::new();
sp.push_str("--------------------------------------------------------------\n");
sp.push_str("Simple Report\n");
sp.push_str("--------------------------------------------------------------\n");
self
.activities
.iter()
.for_each(|a| sp.push_str(&format!("{}\t{}\n", str_duration(&a), a.description())));
sp.push_str("--------------------------------------------------------------\n\n");
sp.clone()
}

pub fn sum_report(&self) -> String {
let mut sp = String::new();
sp.push_str(&self.simple_report());
sp.push_str("Summarized\n");
sp.push_str("--------------------------------------------------------------\n");
sp.push_str(&self.aggregate_activities());
sp.push_str("--------------------------------------------------------------\n");
sp.push_str(&self.total_hours());
sp.push_str("--------------------------------------------------------------\n");
sp.clone()
}

fn aggregate_activities(&self) -> String {
let mut hm: HashMap<String, f64> = HashMap::new();
for a in self.activities.iter() {
if hm.contains_key(&a.description()) {
let v = hm.get(&a.description()).unwrap_or(&0.0);
let sum = v + a.duration();
hm.insert(a.description(), sum);
} else {
hm.insert(a.description(), a.duration());
}
}

let mut agg = String::new();
hm.iter()
.for_each(|(k, v)| agg.push_str(format!("{:.2}\t{}\n", v, k).as_str()));
agg.clone()
}

fn total_hours(&self) -> String {
let s: f64 = self.activities.iter().map(|a| a.duration()).sum();
println!("{}",s);
format!("Total:\t{:.2}\n", s)
}
}
4 changes: 4 additions & 0 deletions today.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
08:00 08:15 Worktime for
08:15 12:00 ALIAM-999
13:00 15:00 Worktime for
15:00 17:30 Programming on stuff

0 comments on commit 983d9a9

Please sign in to comment.