2020-04-13 23:56:25 +02:00
use super ::tracc ::ListView ;
use itertools ::Itertools ;
2020-01-25 22:15:41 +01:00
use serde ::{ Deserialize , Serialize } ;
use serde_json ::from_reader ;
2020-04-18 18:15:23 +02:00
use std ::default ;
2020-01-25 22:15:41 +01:00
use std ::fmt ;
use std ::fs ::File ;
use std ::io ::BufReader ;
2020-04-13 23:56:25 +02:00
use time ::OffsetDateTime ;
2020-01-25 22:15:41 +01:00
pub struct TimeSheet {
pub times : Vec < TimePoint > ,
pub selected : usize ,
pub register : Option < TimePoint > ,
2020-04-13 23:56:25 +02:00
pub editing_time : bool ,
2020-01-25 22:15:41 +01:00
}
2020-04-13 23:56:25 +02:00
#[ derive(Serialize, Deserialize, Clone, Debug) ]
2020-01-25 22:15:41 +01:00
pub struct TimePoint {
text : String ,
2020-04-13 23:56:25 +02:00
time : OffsetDateTime ,
2020-01-25 22:15:41 +01:00
}
impl TimePoint {
pub fn new ( text : & str ) -> Self {
Self {
text : String ::from ( text ) ,
2020-04-13 23:56:25 +02:00
time : OffsetDateTime ::now_local ( ) ,
2020-01-25 22:15:41 +01:00
}
}
}
impl fmt ::Display for TimePoint {
fn fmt ( & self , f : & mut fmt ::Formatter ) -> fmt ::Result {
2020-04-13 23:56:25 +02:00
write! (
f ,
" [{}] {} " ,
self . time
. to_offset ( time ::UtcOffset ::current_local_offset ( ) )
. format ( " %H:%M " ) ,
self . text
)
2020-01-25 22:15:41 +01:00
}
}
2020-04-18 18:15:23 +02:00
impl default ::Default for TimePoint {
fn default ( ) -> Self {
TimePoint ::new ( " " )
}
}
2020-04-13 23:56:25 +02:00
fn read_times ( path : & str ) -> Option < Vec < TimePoint > > {
File ::open ( path )
. ok ( )
. map ( | f | BufReader ::new ( f ) )
. and_then ( | r | from_reader ( r ) . ok ( ) )
}
2020-01-25 22:15:41 +01:00
impl TimeSheet {
2020-04-13 23:56:25 +02:00
pub fn open_or_create ( path : & str ) -> Self {
2020-01-25 22:15:41 +01:00
Self {
2020-04-13 23:56:25 +02:00
times : read_times ( path ) . unwrap_or ( vec! [ TimePoint ::new ( " Did something " ) ] ) ,
2020-01-25 22:15:41 +01:00
selected : 0 ,
register : None ,
2020-04-13 23:56:25 +02:00
editing_time : false ,
2020-01-25 22:15:41 +01:00
}
}
pub fn printable ( & self ) -> Vec < String > {
self . times . iter ( ) . map ( TimePoint ::to_string ) . collect ( )
}
2020-04-13 23:56:25 +02:00
fn current ( & self ) -> & TimePoint {
& self . times [ self . selected ]
2020-01-25 22:15:41 +01:00
}
2020-04-13 23:56:25 +02:00
pub fn time_by_tasks ( & self ) -> String {
let mut time_by_task = std ::collections ::HashMap ::new ( ) ;
let durations = self
. times
//.iter()
//.tuple_windows()
. windows ( 2 )
. map ( | ts | {
let prev = & ts [ 0 ] ;
let next = & ts [ 1 ] ;
let diff = next . time - prev . time ;
( prev . text . clone ( ) , diff )
} ) ;
//.fold(
//std::collections::HashMap::new(),
//|mut map, (text, duration)| {
// *map.entry(text).or_insert(time::Duration::zero()) += duration;
// map
//},
//);
for ( text , duration ) in durations {
* time_by_task . entry ( text ) . or_insert ( time ::Duration ::zero ( ) ) + = duration ;
2020-01-25 22:15:41 +01:00
}
2020-04-13 23:56:25 +02:00
let mut times : Vec < _ > = time_by_task
. into_iter ( )
. map ( | ( text , duration ) | format! ( " {} : {} " , text , format_duration ( & duration ) ) )
. collect ( ) ;
times . sort ( ) ;
times . join ( " ; " )
2020-01-25 22:15:41 +01:00
}
2020-04-13 23:56:25 +02:00
pub fn sum_as_str ( & self ) -> String {
let total = self
. times
. windows ( 2 )
. fold ( time ::Duration ::zero ( ) , | total , ts | {
let last = ts [ 0 ] . time ;
let next = ts [ 1 ] . time ;
total + ( next - last )
} ) ;
format_duration ( & total )
2020-01-25 22:15:41 +01:00
}
2020-04-13 23:56:25 +02:00
}
fn format_duration ( d : & time ::Duration ) -> String {
format! ( " {} : {:02} " , d . whole_hours ( ) , d . whole_minutes ( ) . max ( 1 ) % 60 )
}
2020-01-25 22:15:41 +01:00
2020-04-13 23:56:25 +02:00
impl ListView < TimePoint > for TimeSheet {
fn selection_pointer ( & mut self ) -> & mut usize {
& mut self . selected
2020-01-25 22:15:41 +01:00
}
2020-04-13 23:56:25 +02:00
fn list ( & mut self ) -> & mut Vec < TimePoint > {
& mut self . times
2020-01-25 22:15:41 +01:00
}
2020-04-13 23:56:25 +02:00
fn register ( & mut self ) -> & mut Option < TimePoint > {
& mut self . register
}
fn normal_mode ( & mut self ) {
2020-01-25 22:15:41 +01:00
if self . current ( ) . text . is_empty ( ) {
self . remove_current ( ) ;
self . selected = self . selected . saturating_sub ( 1 ) ;
}
2020-04-13 23:56:25 +02:00
self . times . sort_by_key ( | t | t . time ) ;
2020-01-25 22:15:41 +01:00
}
2020-04-13 23:56:25 +02:00
fn toggle_current ( & mut self ) {
self . editing_time = ! self . editing_time ;
2020-01-25 22:15:41 +01:00
}
2020-04-13 23:56:25 +02:00
fn append_to_current ( & mut self , chr : char ) {
self . times [ self . selected ] . text . push ( chr ) ;
2020-01-25 22:15:41 +01:00
}
2020-04-13 23:56:25 +02:00
fn backspace ( & mut self ) {
self . times [ self . selected ] . text . pop ( ) ;
}
2020-01-25 22:15:41 +01:00
}