1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
extern crate chrono; extern crate clap; extern crate two_timer; use crate::configure::Configuration; use crate::log::{Event, Filter, LogController}; use crate::util::fatal; use crate::vacation::VacationController; use chrono::{Duration, Local, NaiveDate, NaiveDateTime}; use clap::{App, AppSettings, Arg, ArgMatches, SubCommand}; use two_timer::parse; fn after_help() -> &'static str { "If you are expected to log a certain number of hours a day this command allows you \ to discover how many addional hours you will have to work to meet this expectation. Without any additional arguments the assumed period is the current day. Perhaps more useful \ is the pay period, but to use 'pay period' (abbreviated 'pp') as your time expression, \ you must have configured a pay period for the job log. See the configure subcommand. > job when when: today you were done at 4:16:52 PM All prefixes of 'when' are aliases of the subcommand. " } pub fn cli(mast: App<'static, 'static>, display_order: usize) -> App<'static, 'static> { mast.subcommand( SubCommand::with_name("when") .aliases(&["w", "wh", "whe"]) .about("Says when you will have worked all the hours expected within the given period") .after_help(after_help()) .setting(AppSettings::TrailingVarArg) .arg( Arg::with_name("period") .help("time expression") .long_help( "All the <period> arguments are concatenated to produce a time expression.", ) .value_name("period") .default_value("today") .multiple(true), ) .display_order(display_order), ) } pub fn run(directory: Option<&str>, matches: &ArgMatches) { let conf = Configuration::read(None, directory); let phrase = matches .values_of("period") .unwrap() .collect::<Vec<&str>>() .join(" "); println!("when: {}", phrase); match parse(&phrase, conf.two_timer_config()) { Ok((start, end, _)) => { let now = Local::now().naive_local(); if now <= start { fatal( format!( "the current moment, {}, must be after the first moment sought: {}.", now, start ), &conf, ) } else if start >= end { fatal( format!( "the current moment, {}, must be before the last moment sought: {}.", now, end ), &conf, ) } else { let mut reader = LogController::new(None, &conf).expect("could not read log"); let events = reader.events_in_range(&start, &now); let events = Event::gather_by_day(events, &end); let filter = Filter::dummy(); let events = VacationController::read(None, conf.directory()) .add_vacation_times(&start, &end, events, &conf, None, &filter); let mut hours_required = 0.0; let mut seconds_worked = 0.0; let mut last_workday: Option<NaiveDate> = None; for e in events { let date = e.start.date(); if conf.is_workday(&date) { if last_workday.is_none() || last_workday.unwrap() != date { hours_required += conf.day_length; last_workday = Some(date); } } seconds_worked += e.duration(&now); } let seconds_required = hours_required * (60.0 * 60.0); let delta = seconds_required - seconds_worked; let delta_hours = delta / (60.0 * 60.0); let completion_time = now + Duration::seconds(delta as i64); if completion_time > now { println!( "you will be finished at {}, {:.2} hours from now", tell_time(&now, &completion_time), delta_hours ); } else { println!("you were done at {}", tell_time(&now, &completion_time)); } } } Err(e) => fatal(e.msg(), &conf), } } fn tell_time(now: &NaiveDateTime, then: &NaiveDateTime) -> String { if now.date() == then.date() { format!("{}", then.format("%l:%M:%S %p")) } else { format!("{}", then.format("%l:%M:%S %p on %A, %e %B %Y")) } }