From 0955a38ee2b9e41196912343ab692c5dc598471f Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sat, 17 Jun 2017 14:20:46 +0200 Subject: [PATCH 01/22] Initial import --- Cargo.toml | 1 + imag-timetrack/Cargo.toml | 38 ++++++++++ imag-timetrack/src/main.rs | 24 ++++++ imag-timetrack/src/ui.rs | 150 +++++++++++++++++++++++++++++++++++++ 4 files changed, 213 insertions(+) create mode 100644 imag-timetrack/Cargo.toml create mode 100644 imag-timetrack/src/main.rs create mode 100644 imag-timetrack/src/ui.rs diff --git a/Cargo.toml b/Cargo.toml index 410576cd..e6121515 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ members = [ "imag-ref", "imag-store", "imag-tag", + "imag-timetrack", "imag-todo", "imag-view", "libimagannotation", diff --git a/imag-timetrack/Cargo.toml b/imag-timetrack/Cargo.toml new file mode 100644 index 00000000..5f8956d0 --- /dev/null +++ b/imag-timetrack/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "imag-timetrack" +version = "0.3.0" +authors = ["Matthias Beyer "] + +description = "Part of the imag core distribution: imag-tag command" + +keywords = ["imag", "PIM", "personal", "information", "management"] +readme = "../README.md" +license = "LGPL-2.1" + +documentation = "https://matthiasbeyer.github.io/imag/imag_documentation/index.html" +repository = "https://github.com/matthiasbeyer/imag" +homepage = "http://imag-pim.org" + +[dependencies] +clap = "2.*" +log = "0.3" +version = "2.0.1" +semver = "0.2" +toml = "^0.4" +chrono = "^0.4" + +[dependencies.libimagstore] +path = "../libimagstore" + +[dependencies.libimagrt] +path = "../libimagrt" + +[dependencies.libimagerror] +path = "../libimagerror" + +[dependencies.libimagentrytimetrack] +path = "../libimagentrytimetrack" + +[dependencies.libimagutil] +path = "../libimagutil" + diff --git a/imag-timetrack/src/main.rs b/imag-timetrack/src/main.rs new file mode 100644 index 00000000..134ff3d6 --- /dev/null +++ b/imag-timetrack/src/main.rs @@ -0,0 +1,24 @@ +// +// imag - the personal information management suite for the commandline +// Copyright (C) 2015, 2016 Matthias Beyer and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; version +// 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// + +mod ui; + +fn main() { + println!("Hello, world!"); +} diff --git a/imag-timetrack/src/ui.rs b/imag-timetrack/src/ui.rs new file mode 100644 index 00000000..f394057d --- /dev/null +++ b/imag-timetrack/src/ui.rs @@ -0,0 +1,150 @@ +// +// imag - the personal information management suite for the commandline +// Copyright (C) 2015, 2016 Matthias Beyer and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; version +// 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// + +use clap::{Arg, App, SubCommand}; + +pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { + app.subcommand(SubCommand::with_name("start") + .about("Start time tracking") + .version("0.1") + .arg(Arg::with_name("start-time") + .index(1) + .required(false) + .help("Start-time when to start the timetracking")) + .arg(Arg::with_name("tags") + .index(2) + .required(true) + .multiple(true) + .help("Tags to start")) + ) + + .subcommand(SubCommand::with_name("stop") + .about("Stop time tracking") + .version("0.1") + .arg(Arg::with_name("end-time") + .index(1) + .required(false) + .help("End-time when to stop the timetracking")) + .arg(Arg::with_name("tags") + .index(2) + .required(true) + .multiple(true) + .help("Tags to stop")) + ) + + .subcommand(SubCommand::with_name("track") + .about("Track time in given range") + .version("0.1") + .arg(Arg::with_name("start-time") + .index(1) + .required(true) + .help("Start-time when to start the timetracking")) + .arg(Arg::with_name("end-time") + .index(2) + .required(true) + .help("End-time when to stop the timetracking")) + .arg(Arg::with_name("tags") + .index(3) + .required(true) + .multiple(true) + .help("Tags to stop")) + ) + + .subcommand(SubCommand::with_name("continue") + .about("Continue last stopped time tracking") + .version("0.1") + ) + + .subcommand(SubCommand::with_name("day") + .about("Print stats about day") + .version("0.1") + .arg(Arg::with_name("start") + .index(1) + .required(false) + .help("Limit to specific date and time, start time (default: today, 00:00:00)")) + .arg(Arg::with_name("end") + .index(2) + .required(false) + .help("Limit to specific date and time, end time (default: today, 23:59:59)")) + .arg(Arg::with_name("tags") + .long("tags") + .short("t") + .required(false) + .multiple(true) + .help("Limit to certain tags")) + ) + + .subcommand(SubCommand::with_name("week") + .about("Print stats about week") + .version("0.1") + .arg(Arg::with_name("start") + .index(1) + .required(false) + .help("Limit to specific date and time, start time (default: today, 00:00:00)")) + .arg(Arg::with_name("end") + .index(2) + .required(false) + .help("Limit to specific date and time, end time (default: today, 23:59:59)")) + .arg(Arg::with_name("tags") + .long("tags") + .short("t") + .required(false) + .multiple(true) + .help("Limit to certain tags")) + ) + + .subcommand(SubCommand::with_name("month") + .about("Print stats about month") + .version("0.1") + .arg(Arg::with_name("start") + .index(1) + .required(false) + .help("Limit to specific date and time, start time (default: today, 00:00:00)")) + .arg(Arg::with_name("end") + .index(2) + .required(false) + .help("Limit to specific date and time, end time (default: today, 23:59:59)")) + .arg(Arg::with_name("tags") + .long("tags") + .short("t") + .required(false) + .multiple(true) + .help("Limit to certain tags")) + ) + + .subcommand(SubCommand::with_name("year") + .about("Print stats about year") + .version("0.1") + .arg(Arg::with_name("start") + .index(1) + .required(false) + .help("Limit to specific date and time, start time (default: today, 00:00:00)")) + .arg(Arg::with_name("end") + .index(2) + .required(false) + .help("Limit to specific date and time, end time (default: today, 23:59:59)")) + .arg(Arg::with_name("tags") + .long("tags") + .short("t") + .required(false) + .multiple(true) + .help("Limit to certain tags")) + ) + +} From 8379c3b5e5402feb0fa7bac20376b16af3f31845 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Mon, 26 Jun 2017 17:47:12 +0200 Subject: [PATCH 02/22] Add modules for functionality --- imag-timetrack/src/cont.rs | 25 +++++++++++++++++++ imag-timetrack/src/day.rs | 25 +++++++++++++++++++ imag-timetrack/src/main.rs | 49 ++++++++++++++++++++++++++++++++++++- imag-timetrack/src/month.rs | 25 +++++++++++++++++++ imag-timetrack/src/start.rs | 23 +++++++++++++++++ imag-timetrack/src/stop.rs | 23 +++++++++++++++++ imag-timetrack/src/track.rs | 25 +++++++++++++++++++ imag-timetrack/src/week.rs | 25 +++++++++++++++++++ imag-timetrack/src/year.rs | 25 +++++++++++++++++++ 9 files changed, 244 insertions(+), 1 deletion(-) create mode 100644 imag-timetrack/src/cont.rs create mode 100644 imag-timetrack/src/day.rs create mode 100644 imag-timetrack/src/month.rs create mode 100644 imag-timetrack/src/start.rs create mode 100644 imag-timetrack/src/stop.rs create mode 100644 imag-timetrack/src/track.rs create mode 100644 imag-timetrack/src/week.rs create mode 100644 imag-timetrack/src/year.rs diff --git a/imag-timetrack/src/cont.rs b/imag-timetrack/src/cont.rs new file mode 100644 index 00000000..4700485c --- /dev/null +++ b/imag-timetrack/src/cont.rs @@ -0,0 +1,25 @@ +// +// imag - the personal information management suite for the commandline +// Copyright (C) 2015, 2016 Matthias Beyer and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; version +// 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// + +use libimagrt::runtime::Runtime; + +pub fn cont(rt: &Runtime) -> i32 { + unimplemented!() +} + diff --git a/imag-timetrack/src/day.rs b/imag-timetrack/src/day.rs new file mode 100644 index 00000000..0c3fdcd1 --- /dev/null +++ b/imag-timetrack/src/day.rs @@ -0,0 +1,25 @@ +// +// imag - the personal information management suite for the commandline +// Copyright (C) 2015, 2016 Matthias Beyer and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; version +// 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// + +use libimagrt::runtime::Runtime; + +pub fn day(rt: &Runtime) -> i32 { + unimplemented!() +} + diff --git a/imag-timetrack/src/main.rs b/imag-timetrack/src/main.rs index 134ff3d6..c396e19a 100644 --- a/imag-timetrack/src/main.rs +++ b/imag-timetrack/src/main.rs @@ -17,8 +17,55 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // +mod cont; +mod day; +mod month; +mod start; +mod stop; +mod track; mod ui; +mod week; +mod year; + +use cont::cont; +use day::day; +use month::month; +use start::start; +use stop::stop; +use track::track; +use ui::build_ui; +use week::week; +use year::year; + +use libimagrt::setup::generate_runtime_setup; fn main() { - println!("Hello, world!"); + let rt = generate_runtime_setup("imag-timetrack", + &version!()[..], + "Time tracking module", + build_ui); + + let command = rt.cli().subcommand_name(); + let retval = if let Some(command) = command { + debug!("Call: {}", command); + match command { + "continue" => cont(&rt), + "day" => day(&rt), + "month" => month(&rt), + "start" => start(&rt), + "stop" => stop(&rt), + "track" => track(&rt), + "week" => week(&rt), + "year" => year(&rt), + _ => { + error!("Unknown command"); + 1 + }, + } + } else { + error!("No command"); + 1 + }; + + ::std::process::exit(retval); } diff --git a/imag-timetrack/src/month.rs b/imag-timetrack/src/month.rs new file mode 100644 index 00000000..61d974d7 --- /dev/null +++ b/imag-timetrack/src/month.rs @@ -0,0 +1,25 @@ +// +// imag - the personal information management suite for the commandline +// Copyright (C) 2015, 2016 Matthias Beyer and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; version +// 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// + +use libimagrt::runtime::Runtime; + +pub fn month(rt: &Runtime) -> i32 { + unimplemented!() +} + diff --git a/imag-timetrack/src/start.rs b/imag-timetrack/src/start.rs new file mode 100644 index 00000000..6926eb38 --- /dev/null +++ b/imag-timetrack/src/start.rs @@ -0,0 +1,23 @@ +// +// imag - the personal information management suite for the commandline +// Copyright (C) 2015, 2016 Matthias Beyer and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; version +// 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// + +pub fn start(rt: &Runtime) -> i32 { + unimplemented!() +} + diff --git a/imag-timetrack/src/stop.rs b/imag-timetrack/src/stop.rs new file mode 100644 index 00000000..61ca02a8 --- /dev/null +++ b/imag-timetrack/src/stop.rs @@ -0,0 +1,23 @@ +// +// imag - the personal information management suite for the commandline +// Copyright (C) 2015, 2016 Matthias Beyer and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; version +// 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// + +pub fn stop(rt: &Runtime) -> i32 { + unimplemented!() +} + diff --git a/imag-timetrack/src/track.rs b/imag-timetrack/src/track.rs new file mode 100644 index 00000000..c2c36bc0 --- /dev/null +++ b/imag-timetrack/src/track.rs @@ -0,0 +1,25 @@ +// +// imag - the personal information management suite for the commandline +// Copyright (C) 2015, 2016 Matthias Beyer and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; version +// 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// + +use libimagrt::runtime::Runtime; + +pub fn track(rt: &Runtime) -> i32 { + unimplemented!() +} + diff --git a/imag-timetrack/src/week.rs b/imag-timetrack/src/week.rs new file mode 100644 index 00000000..f81b6db0 --- /dev/null +++ b/imag-timetrack/src/week.rs @@ -0,0 +1,25 @@ +// +// imag - the personal information management suite for the commandline +// Copyright (C) 2015, 2016 Matthias Beyer and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; version +// 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// + +use libimagrt::runtime::Runtime; + +pub fn week(rt: &Runtime) -> i32 { + unimplemented!() +} + diff --git a/imag-timetrack/src/year.rs b/imag-timetrack/src/year.rs new file mode 100644 index 00000000..7f7ddeb8 --- /dev/null +++ b/imag-timetrack/src/year.rs @@ -0,0 +1,25 @@ +// +// imag - the personal information management suite for the commandline +// Copyright (C) 2015, 2016 Matthias Beyer and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; version +// 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// + +use libimagrt::runtime::Runtime; + +pub fn year(rt: &Runtime) -> i32 { + unimplemented!() +} + From ba628cea6d7223817a78a98e02abbe17825c39a1 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sat, 8 Jul 2017 14:26:06 +0200 Subject: [PATCH 03/22] Add impl of start() --- imag-timetrack/src/start.rs | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/imag-timetrack/src/start.rs b/imag-timetrack/src/start.rs index 6926eb38..13b1f3e8 100644 --- a/imag-timetrack/src/start.rs +++ b/imag-timetrack/src/start.rs @@ -17,7 +17,38 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // +use std::str::FromStr; + +use libimagrt::runtime::Runtime; +use libimagerror::trace::trace_error; +use libimagentrytimetrack::tag::TimeTrackingTag; +use libimagentrytimetrack::timetrackingstore::TimeTrackStore; +use libimagerror::trace::MapErrTrace; + pub fn start(rt: &Runtime) -> i32 { - unimplemented!() + let (_, cmd) = rt.cli().subcommand(); + let cmd = cmd.unwrap(); // checked in main() + + let start = match cmd.value_of("start-time").map(::chrono::naive::NaiveDateTime::from_str) { + None => ::chrono::offset::Local::now().naive_local(), + Some(Ok(ndt)) => ndt, + Some(Err(e)) => { + trace_error(&e); + error!("Cannot continue, not having start time"); + return 1 + }, + }; + + cmd.values_of("tags") + .unwrap() // enforced by clap + .map(String::from) + .map(TimeTrackingTag::from) + .fold(0, |acc, ttt| { + rt.store() + .create_timetracking_at(&start, &ttt) + .map_err_trace() + .map(|_| acc) + .unwrap_or(1) + }) } From 10686461340144a0d0482bbe2a6cf1daf1f52d18 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sun, 9 Jul 2017 20:20:26 +0200 Subject: [PATCH 04/22] Implement stop subcommand --- imag-timetrack/src/stop.rs | 85 +++++++++++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) diff --git a/imag-timetrack/src/stop.rs b/imag-timetrack/src/stop.rs index 61ca02a8..b9e9cd49 100644 --- a/imag-timetrack/src/stop.rs +++ b/imag-timetrack/src/stop.rs @@ -17,7 +17,90 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // +use std::str::FromStr; + +use libimagerror::trace::trace_error; +use libimagerror::iter::TraceIterator; +use libimagrt::runtime::Runtime; +use libimagrt::setup::generate_runtime_setup; + +use libimagentrytimetrack::timetracking::TimeTracking; +use libimagentrytimetrack::tag::TimeTrackingTag; +use libimagentrytimetrack::timetrackingstore::*; +use libimagentrytimetrack::iter::get::GetTimeTrackIter; + pub fn stop(rt: &Runtime) -> i32 { - unimplemented!() + let (_, cmd) = rt.cli().subcommand(); + let cmd = cmd.unwrap(); // checked in main() + + let stop_time = match cmd.value_of("stop-time").map(::chrono::naive::NaiveDateTime::from_str) { + None => ::chrono::offset::Local::now().naive_local(), + Some(Ok(ndt)) => ndt, + Some(Err(e)) => { + trace_error(&e); + error!("Cannot continue, not having stop time"); + return 1 + }, + }; + + // TODO: We do not yet support stopping all tags by simply calling the "stop" subcommand! + + let tags : Vec = cmd.values_of("tags") + .unwrap() // enforced by clap + .map(String::from) + .map(TimeTrackingTag::from) + .collect(); + + let iter : GetTimeTrackIter = match rt.store().get_timetrackings() { + Ok(i) => i, + Err(e) => { + error!("Getting timetrackings failed"); + trace_error(&e); + return 1 + } + + }; + + // Filter all timetrackings for the ones that are not yet ended. + iter.trace_unwrap() + .filter_map(|elem| { + // check whether end-time is set + let has_end_time = match elem.get_end_datetime() { + Ok(x) => x.is_some(), + Err(e) => { + warn!("Error checking {} whether End-time is set", elem.get_location()); + trace_error(&e); + false + } + }; + + // Filter the not-yet-ended timetrackings for the ones that should be ended via + // the tag specification + let stopping_tag_is_present : bool = elem + .get_timetrack_tag() + .map(|t| tags.contains(&t)) + .unwrap_or(false); + + if (!has_end_time) && stopping_tag_is_present { + Some(elem) + } else { + None + } + }) + + // for each of these timetrackings, end them + // for each result, print the backtrace (if any) + .fold(0, |acc, mut elem| match elem.set_end_datetime(stop_time.clone()) { + Err(e) => { // if there was an error + trace_error(&e); // trace + 1 // set exit code to 1 + }, + Ok(_) => { + debug!("Setting end time worked: {:?}", elem); + + // Keep the exit code + acc + } + }) } From 8cdb3d24de339da4841a482603eefcf20b73a1ec Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sat, 15 Jul 2017 19:57:13 +0200 Subject: [PATCH 05/22] Add missing extern crates --- imag-timetrack/src/main.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/imag-timetrack/src/main.rs b/imag-timetrack/src/main.rs index c396e19a..364ff629 100644 --- a/imag-timetrack/src/main.rs +++ b/imag-timetrack/src/main.rs @@ -17,6 +17,23 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // +#[macro_use] +extern crate log; + +#[macro_use] +extern crate version; + +extern crate clap; +extern crate semver; +extern crate toml; +extern crate chrono; + +extern crate libimagerror; +extern crate libimagstore; +extern crate libimagrt; +extern crate libimagentrytimetrack; +extern crate libimagutil; + mod cont; mod day; mod month; From de06fbb86bef8dd171e779c9699c168cb4e6d6c2 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sat, 15 Jul 2017 20:39:06 +0200 Subject: [PATCH 06/22] Fix: Remove unused type parameter --- libimagentrytimetrack/src/timetrackingstore.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libimagentrytimetrack/src/timetrackingstore.rs b/libimagentrytimetrack/src/timetrackingstore.rs index 9312a1c7..94e993ac 100644 --- a/libimagentrytimetrack/src/timetrackingstore.rs +++ b/libimagentrytimetrack/src/timetrackingstore.rs @@ -44,7 +44,7 @@ pub trait TimeTrackStore<'a> { fn create_timetracking_at(&'a self, start: &NDT, ts: &TTT) -> Result>; fn create_timetracking(&'a self, start: &NDT, end: &NDT, ts: &TTT) -> Result>; - fn get_timetrackings(&'a self) -> Result>; + fn get_timetrackings(&'a self) -> Result>; } fn now() -> NDT { @@ -104,7 +104,7 @@ impl<'a> TimeTrackStore<'a> for Store { }) } - fn get_timetrackings(&'a self) -> Result> { + fn get_timetrackings(&'a self) -> Result> { self.retrieve_for_module(CRATE_NAME) .map_err_into(TTEK::StoreReadError) .map(|iter| GetTimeTrackIter::new(iter, self)) From 2c4dbc0a4ad002f1847e6a58b2dabefadd318df8 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sat, 15 Jul 2017 20:54:28 +0200 Subject: [PATCH 07/22] Implement "track" subcommand --- imag-timetrack/src/track.rs | 52 ++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/imag-timetrack/src/track.rs b/imag-timetrack/src/track.rs index c2c36bc0..a723b909 100644 --- a/imag-timetrack/src/track.rs +++ b/imag-timetrack/src/track.rs @@ -17,9 +17,59 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // +use std::str::FromStr; + +use clap::ArgMatches; +use chrono::naive::NaiveDateTime; + use libimagrt::runtime::Runtime; +use libimagerror::trace::trace_error; +use libimagentrytimetrack::tag::TimeTrackingTag; +use libimagentrytimetrack::timetrackingstore::TimeTrackStore; +use libimagerror::trace::MapErrTrace; pub fn track(rt: &Runtime) -> i32 { - unimplemented!() + let (_, cmd) = rt.cli().subcommand(); + let cmd = cmd.unwrap(); // checked in main() + + // Gets the appropriate time from the commandline or None on error (errors already logged, so + // callee can directly return in case of error + fn get_time(cmd: &ArgMatches, clap_name: &str, errname: &str) -> Option { + let val = cmd + .value_of(clap_name) + .map(::chrono::naive::NaiveDateTime::from_str) + .unwrap(); // clap has our back + + match val { + Ok(ndt) => Some(ndt), + Err(e) => { + trace_error(&e); + error!("Cannot continue, not having {} time", errname); + None + }, + } + } + + let start = match get_time(&cmd, "start-time", "start") { + Some(t) => t, + None => return 1, + }; + + let stop = match get_time(&cmd, "stop-time", "stop") { + Some(t) => t, + None => return 1, + }; + + cmd.values_of("tags") + .unwrap() // enforced by clap + .map(String::from) + .map(TimeTrackingTag::from) + .fold(0, |acc, ttt| { + rt.store() + .create_timetracking(&start, &stop, &ttt) + .map_err_trace() + .map(|_| acc) + .unwrap_or(1) + }) } From ea1de45a89df882279a33a54fc7066f397df5426 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Wed, 19 Jul 2017 13:08:58 +0200 Subject: [PATCH 08/22] Move filter functionality in common module, use filters crate --- imag-timetrack/Cargo.toml | 1 + imag-timetrack/src/common.rs | 61 ++++++++++++++++++++++++++++++++++++ imag-timetrack/src/main.rs | 2 ++ imag-timetrack/src/stop.rs | 25 +++++---------- 4 files changed, 71 insertions(+), 18 deletions(-) create mode 100644 imag-timetrack/src/common.rs diff --git a/imag-timetrack/Cargo.toml b/imag-timetrack/Cargo.toml index 5f8956d0..290e5070 100644 --- a/imag-timetrack/Cargo.toml +++ b/imag-timetrack/Cargo.toml @@ -20,6 +20,7 @@ version = "2.0.1" semver = "0.2" toml = "^0.4" chrono = "^0.4" +filters = "0.1.1" [dependencies.libimagstore] path = "../libimagstore" diff --git a/imag-timetrack/src/common.rs b/imag-timetrack/src/common.rs new file mode 100644 index 00000000..e2c3f364 --- /dev/null +++ b/imag-timetrack/src/common.rs @@ -0,0 +1,61 @@ +// +// imag - the personal information management suite for the commandline +// Copyright (C) 2015, 2016 Matthias Beyer and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; version +// 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// + +use filters::filter::Filter; + +use libimagentrytimetrack::timetracking::TimeTracking; +use libimagentrytimetrack::tag::TimeTrackingTag; +use libimagstore::store::FileLockEntry; +use libimagerror::trace::trace_error; + +/// Check whether an timetracking has an end time. +/// +/// Trace, if TimeTracking::get_end_datetime() returns Err(_) +pub fn has_end_time(timetracking: &FileLockEntry) -> bool { + match timetracking.get_end_datetime() { + Ok(x) => x.is_some(), + Err(e) => { + warn!("Error checking {} whether End-time is set", timetracking.get_location()); + trace_error(&e); + false + } + } +} + +/// Check whether an timetracking has one of the passed tags +/// +/// Trace, if TimeTracking::get_end_datetime() returns Err(_) +pub struct HasTagFromList<'a> { + list: &'a Vec, +} + +impl<'a> HasTagFromList<'a> { + pub fn new(v: &'a Vec) -> HasTagFromList<'a> { + HasTagFromList { + list: v + } + } +} + +impl<'a, 'f> Filter> for HasTagFromList<'a> { + fn filter(&self, tracking: &FileLockEntry) -> bool { + tracking.get_timetrack_tag().map(|t| self.list.contains(&t)).unwrap_or(false) + } +} + diff --git a/imag-timetrack/src/main.rs b/imag-timetrack/src/main.rs index 364ff629..800f6a9d 100644 --- a/imag-timetrack/src/main.rs +++ b/imag-timetrack/src/main.rs @@ -27,6 +27,7 @@ extern crate clap; extern crate semver; extern crate toml; extern crate chrono; +extern crate filters; extern crate libimagerror; extern crate libimagstore; @@ -35,6 +36,7 @@ extern crate libimagentrytimetrack; extern crate libimagutil; mod cont; +mod common; mod day; mod month; mod start; diff --git a/imag-timetrack/src/stop.rs b/imag-timetrack/src/stop.rs index b9e9cd49..f3dfaade 100644 --- a/imag-timetrack/src/stop.rs +++ b/imag-timetrack/src/stop.rs @@ -19,6 +19,10 @@ use std::str::FromStr; +use filters::filter::Filter; + +use common::*; + use libimagerror::trace::trace_error; use libimagerror::iter::TraceIterator; use libimagrt::runtime::Runtime; @@ -61,27 +65,12 @@ pub fn stop(rt: &Runtime) -> i32 { }; + let filter = has_end_time.not().and(HasTagFromList::new(&tags)); + // Filter all timetrackings for the ones that are not yet ended. iter.trace_unwrap() .filter_map(|elem| { - // check whether end-time is set - let has_end_time = match elem.get_end_datetime() { - Ok(x) => x.is_some(), - Err(e) => { - warn!("Error checking {} whether End-time is set", elem.get_location()); - trace_error(&e); - false - } - }; - - // Filter the not-yet-ended timetrackings for the ones that should be ended via - // the tag specification - let stopping_tag_is_present : bool = elem - .get_timetrack_tag() - .map(|t| tags.contains(&t)) - .unwrap_or(false); - - if (!has_end_time) && stopping_tag_is_present { + if filter.filter(&elem) { Some(elem) } else { None From 0643d63b4e1930e86266702b6b14e74198154dad Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Wed, 19 Jul 2017 14:08:02 +0200 Subject: [PATCH 09/22] Add dependency: Itertools --- imag-timetrack/Cargo.toml | 1 + imag-timetrack/src/main.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/imag-timetrack/Cargo.toml b/imag-timetrack/Cargo.toml index 290e5070..f9b4ab03 100644 --- a/imag-timetrack/Cargo.toml +++ b/imag-timetrack/Cargo.toml @@ -21,6 +21,7 @@ semver = "0.2" toml = "^0.4" chrono = "^0.4" filters = "0.1.1" +itertools = "0.6" [dependencies.libimagstore] path = "../libimagstore" diff --git a/imag-timetrack/src/main.rs b/imag-timetrack/src/main.rs index 800f6a9d..e2896f28 100644 --- a/imag-timetrack/src/main.rs +++ b/imag-timetrack/src/main.rs @@ -28,6 +28,7 @@ extern crate semver; extern crate toml; extern crate chrono; extern crate filters; +extern crate itertools; extern crate libimagerror; extern crate libimagstore; From 76e316ba90e272a8fbac572533fe3acc78b8783a Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Wed, 19 Jul 2017 14:08:18 +0200 Subject: [PATCH 10/22] [WIP] Impl command "continue" --- imag-timetrack/src/cont.rs | 90 +++++++++++++++++++++++++++++++++++++- 1 file changed, 89 insertions(+), 1 deletion(-) diff --git a/imag-timetrack/src/cont.rs b/imag-timetrack/src/cont.rs index 4700485c..3f40d7f6 100644 --- a/imag-timetrack/src/cont.rs +++ b/imag-timetrack/src/cont.rs @@ -17,9 +17,97 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // +use std::cmp::Ord; +use std::cmp::Ordering; + +use filters::ops::not::Not; +use filters::filter::Filter; +use itertools::Itertools; +use itertools::MinMaxResult; +use chrono::NaiveDateTime; + +use common::has_end_time; + +use libimagerror::trace::trace_error; +use libimagerror::trace::MapErrTrace; +use libimagerror::iter::TraceIterator; +use libimagentrytimetrack::timetrackingstore::TimeTrackStore; +use libimagentrytimetrack::timetracking::TimeTracking; + use libimagrt::runtime::Runtime; pub fn cont(rt: &Runtime) -> i32 { - unimplemented!() + rt.store() + .get_timetrackings() + .and_then(|iter| { + let groups = iter + // unwrap everything, trace errors + .trace_unwrap() + + // I want all entries with an end time + .filter(|e| has_end_time.filter(&e)) + + // Now group them by the end time + .group_by(|elem| match elem.get_end_datetime() { + Ok(Some(dt)) => dt, + Ok(None) => { + // error. We expect all of them having an end-time. + error!("Has no end time, but should be filtered out: {:?}", elem); + error!("This is a bug. Please report."); + error!("Will panic now"); + panic!("Unknown bug") + } + Err(e) => { + trace_error(&e); + NaiveDateTime::from_timestamp(0, 0) // placeholder + } + }); + + // sort the trackings by key, so by end datetime + let elements = { + let mut v = vec![]; + for (key, value) in groups.into_iter() { + v.push((key, value)); + } + + v.into_iter() + .sorted_by(|t1, t2| { + let (k1, _) = *t1; + let (k2, _) = *t2; + Ord::cmp(&k1, &k2) + }) + .into_iter() + + // get the last one, which should be the highest one + .last() // -> Option<_> + }; + + match elements { + Some((_, trackings)) => { + // and then, for all trackings + trackings + .fold(Ok(0), |acc, tracking| { + debug!("Having tracking: {:?}", tracking); + + acc.and_then(|_| { + // create a new tracking with the same tag + tracking + .get_timetrack_tag() + .and_then(|tag| rt.store().create_timetracking_now(&tag)) + .map(|_| 0) + .map_err_trace() + }) + }) + }, + + None => { + info!("No trackings to continue"); + Ok(1) + }, + } + }) + .map(|_| 0) + .map_err_trace() + .unwrap_or(1) } From a98da2c01cbe837be91ce230dcb7dec6392041f5 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Thu, 20 Jul 2017 12:26:04 +0200 Subject: [PATCH 11/22] Add new dependency: is-match --- libimagentrytimetrack/Cargo.toml | 1 + libimagentrytimetrack/src/lib.rs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/libimagentrytimetrack/Cargo.toml b/libimagentrytimetrack/Cargo.toml index 316af990..02deca76 100644 --- a/libimagentrytimetrack/Cargo.toml +++ b/libimagentrytimetrack/Cargo.toml @@ -19,6 +19,7 @@ chrono = "0.4" toml = "0.4" toml-query = "0.3" lazy_static = "0.2" +is-match = "0.1" [dependencies.libimagerror] path = "../libimagerror" diff --git a/libimagentrytimetrack/src/lib.rs b/libimagentrytimetrack/src/lib.rs index 03346221..e3f9d2aa 100644 --- a/libimagentrytimetrack/src/lib.rs +++ b/libimagentrytimetrack/src/lib.rs @@ -23,6 +23,8 @@ extern crate toml; extern crate toml_query; #[macro_use] extern crate lazy_static; +#[macro_use] +extern crate is_match; #[macro_use] extern crate libimagerror; From e2e42232e6374643b885a95f6c58180cd1d61efe Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Thu, 20 Jul 2017 12:12:11 +0200 Subject: [PATCH 12/22] Start reimplementing filter module with filters crate --- libimagentrytimetrack/src/iter/filter.rs | 175 ++++++++++------------- 1 file changed, 79 insertions(+), 96 deletions(-) diff --git a/libimagentrytimetrack/src/iter/filter.rs b/libimagentrytimetrack/src/iter/filter.rs index b8299b46..0a3ae538 100644 --- a/libimagentrytimetrack/src/iter/filter.rs +++ b/libimagentrytimetrack/src/iter/filter.rs @@ -19,123 +19,106 @@ use result::Result; +use chrono::NaiveDateTime; +use filters::filter::Filter; + use libimagstore::store::FileLockEntry; use tag::TimeTrackingTag as TTT; use timetracking::TimeTracking; -pub struct WithOneOf<'a, I> - where I: Iterator>> -{ - iter: I, - allowed_tags: &'a Vec, + +pub fn has_start_time(entry: &FileLockEntry) -> bool { + is_match!(entry.get_start_datetime(), Ok(Some(_))) } -impl<'a, I> WithOneOf<'a, I> - where I: Iterator>> -{ +pub fn has_end_time(entry: &FileLockEntry) -> bool { + is_match!(entry.get_end_datetime(), Ok(Some(_))) +} - pub fn new(iter: I, allowed_tags: &'a Vec) -> WithOneOf<'a, I> { - WithOneOf { - iter: iter, - allowed_tags: allowed_tags +pub fn has_tag(entry: &FileLockEntry) -> bool { + is_match!(entry.get_timetrack_tag(), Ok(_)) +} + +pub fn has_start_time_where(f: F) -> HasStartTimeWhere + where F: Fn(&NaiveDateTime) -> bool +{ + HasStartTimeWhere::new(f) +} + +pub fn has_end_time_where(f: F) -> HasEndTimeWhere + where F: Fn(&NaiveDateTime) -> bool +{ + HasEndTimeWhere::new(f) +} + +pub fn has_one_of_tags<'a>(tags: &'a Vec) -> HasOneOfTags<'a> { + HasOneOfTags::new(tags) +} + +mod types { + use chrono::NaiveDateTime; + use filters::filter::Filter; + + use tag::TimeTrackingTag as TTT; + use timetracking::TimeTracking; + + use libimagstore::store::FileLockEntry; + + pub struct HasStartTimeWhere(F) + where F: Fn(&NaiveDateTime) -> bool; + + impl bool> HasStartTimeWhere { + pub fn new(f: F) -> HasStartTimeWhere { + HasStartTimeWhere(f) } } -} -impl<'a, I> Iterator for WithOneOf<'a, I> - where I: Iterator>> -{ - type Item = Result>; - - fn next(&mut self) -> Option { - loop { - match self.iter.next() { - Some(Ok(fle)) => { - match fle.get_timetrack_tag() { - Err(e) => return Some(Err(e)), - Ok(t) => if self.allowed_tags.contains(&t) { - return Some(Ok(fle)) - } else { - // loop - }, - } - }, - Some(Err(e)) => return Some(Err(e)), - None => return None, - } + impl<'a, F> Filter> for HasStartTimeWhere + where F: Fn(&NaiveDateTime) -> bool + { + fn filter(&self, entry: &FileLockEntry) -> bool { + entry.get_start_datetime() + .map(|o| o.map(|dt| (self.0)(&dt)).unwrap_or(false)) + .unwrap_or(false) } } -} -pub trait WithOneOfTags<'a> : Sized + Iterator>> { - fn with_timetracking_tags(self, tags: &'a Vec) -> WithOneOf<'a, Self>; -} + pub struct HasEndTimeWhere(F) + where F: Fn(&NaiveDateTime) -> bool; -impl<'a, I> WithOneOfTags<'a> for I - where I: Iterator>>, - Self: Sized -{ - fn with_timetracking_tags(self, tags: &'a Vec) -> WithOneOf<'a, Self> { - WithOneOf::new(self, tags) - } -} - - -pub struct WithNoneOf<'a, I> - where I: Iterator>> -{ - iter: I, - disallowed_tags: &'a Vec, -} - -impl<'a, I> WithNoneOf<'a, I> - where I: Iterator>> -{ - - pub fn new(iter: I, disallowed_tags: &'a Vec) -> WithNoneOf<'a, I> { - WithNoneOf { - iter: iter, - disallowed_tags: disallowed_tags + impl bool> HasEndTimeWhere { + pub fn new(f: F) -> HasEndTimeWhere { + HasEndTimeWhere(f) } } -} -impl<'a, I> Iterator for WithNoneOf<'a, I> - where I: Iterator>> -{ - type Item = Result>; - - fn next(&mut self) -> Option { - loop { - match self.iter.next() { - Some(Ok(fle)) => { - match fle.get_timetrack_tag() { - Err(e) => return Some(Err(e)), - Ok(t) => if !self.disallowed_tags.contains(&t) { - return Some(Ok(fle)) - } else { - // loop - }, - } - }, - Some(Err(e)) => return Some(Err(e)), - None => return None, - } + impl<'a, F> Filter> for HasEndTimeWhere + where F: Fn(&NaiveDateTime) -> bool + { + fn filter(&self, entry: &FileLockEntry) -> bool { + entry.get_end_datetime() + .map(|o| o.map(|dt| (self.0)(&dt)).unwrap_or(false)) + .unwrap_or(false) } } -} -pub trait WithNoneOfTags<'a> : Sized + Iterator>> { - fn without_timetracking_tags(self, tags: &'a Vec) -> WithNoneOf<'a, Self>; -} + pub struct HasOneOfTags<'a>(&'a Vec); -impl<'a, I> WithNoneOfTags<'a> for I - where I: Iterator>>, - Self: Sized -{ - fn without_timetracking_tags(self, tags: &'a Vec) -> WithNoneOf<'a, Self> { - WithNoneOf::new(self, tags) + impl<'a> HasOneOfTags<'a> { + pub fn new(tags: &'a Vec) -> HasOneOfTags<'a> { + HasOneOfTags(tags) + } } -} + + impl<'a, 'b> Filter> for HasOneOfTags<'a> { + fn filter(&self, entry: &FileLockEntry) -> bool { + entry.get_timetrack_tag().map(|t| self.0.contains(&t)).unwrap_or(false) + } + } + +} +pub use self::types::HasStartTimeWhere; +pub use self::types::HasEndTimeWhere; +pub use self::types::HasOneOfTags; From 85e76d6ff0acb7ff22518aa07af68bd4f57039ef Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Thu, 20 Jul 2017 12:29:25 +0200 Subject: [PATCH 13/22] Remove things that are implemented in libimagentrytimetrack now --- imag-timetrack/src/common.rs | 61 ------------------------------------ imag-timetrack/src/main.rs | 1 - 2 files changed, 62 deletions(-) delete mode 100644 imag-timetrack/src/common.rs diff --git a/imag-timetrack/src/common.rs b/imag-timetrack/src/common.rs deleted file mode 100644 index e2c3f364..00000000 --- a/imag-timetrack/src/common.rs +++ /dev/null @@ -1,61 +0,0 @@ -// -// imag - the personal information management suite for the commandline -// Copyright (C) 2015, 2016 Matthias Beyer and contributors -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; version -// 2.1 of the License. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -// - -use filters::filter::Filter; - -use libimagentrytimetrack::timetracking::TimeTracking; -use libimagentrytimetrack::tag::TimeTrackingTag; -use libimagstore::store::FileLockEntry; -use libimagerror::trace::trace_error; - -/// Check whether an timetracking has an end time. -/// -/// Trace, if TimeTracking::get_end_datetime() returns Err(_) -pub fn has_end_time(timetracking: &FileLockEntry) -> bool { - match timetracking.get_end_datetime() { - Ok(x) => x.is_some(), - Err(e) => { - warn!("Error checking {} whether End-time is set", timetracking.get_location()); - trace_error(&e); - false - } - } -} - -/// Check whether an timetracking has one of the passed tags -/// -/// Trace, if TimeTracking::get_end_datetime() returns Err(_) -pub struct HasTagFromList<'a> { - list: &'a Vec, -} - -impl<'a> HasTagFromList<'a> { - pub fn new(v: &'a Vec) -> HasTagFromList<'a> { - HasTagFromList { - list: v - } - } -} - -impl<'a, 'f> Filter> for HasTagFromList<'a> { - fn filter(&self, tracking: &FileLockEntry) -> bool { - tracking.get_timetrack_tag().map(|t| self.list.contains(&t)).unwrap_or(false) - } -} - diff --git a/imag-timetrack/src/main.rs b/imag-timetrack/src/main.rs index e2896f28..de09a859 100644 --- a/imag-timetrack/src/main.rs +++ b/imag-timetrack/src/main.rs @@ -37,7 +37,6 @@ extern crate libimagentrytimetrack; extern crate libimagutil; mod cont; -mod common; mod day; mod month; mod start; From ecb9bcc861147c35b8170364ce56ee74b5e8e79e Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Thu, 20 Jul 2017 12:29:42 +0200 Subject: [PATCH 14/22] Use filter fn from lib --- imag-timetrack/src/cont.rs | 3 +-- imag-timetrack/src/stop.rs | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/imag-timetrack/src/cont.rs b/imag-timetrack/src/cont.rs index 3f40d7f6..86563672 100644 --- a/imag-timetrack/src/cont.rs +++ b/imag-timetrack/src/cont.rs @@ -26,13 +26,12 @@ use itertools::Itertools; use itertools::MinMaxResult; use chrono::NaiveDateTime; -use common::has_end_time; - use libimagerror::trace::trace_error; use libimagerror::trace::MapErrTrace; use libimagerror::iter::TraceIterator; use libimagentrytimetrack::timetrackingstore::TimeTrackStore; use libimagentrytimetrack::timetracking::TimeTracking; +use libimagentrytimetrack::iter::filter::*; use libimagrt::runtime::Runtime; diff --git a/imag-timetrack/src/stop.rs b/imag-timetrack/src/stop.rs index f3dfaade..c1c5e682 100644 --- a/imag-timetrack/src/stop.rs +++ b/imag-timetrack/src/stop.rs @@ -21,8 +21,6 @@ use std::str::FromStr; use filters::filter::Filter; -use common::*; - use libimagerror::trace::trace_error; use libimagerror::iter::TraceIterator; use libimagrt::runtime::Runtime; @@ -32,6 +30,8 @@ use libimagentrytimetrack::timetracking::TimeTracking; use libimagentrytimetrack::tag::TimeTrackingTag; use libimagentrytimetrack::timetrackingstore::*; use libimagentrytimetrack::iter::get::GetTimeTrackIter; +use libimagentrytimetrack::iter::filter::has_end_time; +use libimagentrytimetrack::iter::filter::has_one_of_tags; pub fn stop(rt: &Runtime) -> i32 { let (_, cmd) = rt.cli().subcommand(); @@ -65,7 +65,7 @@ pub fn stop(rt: &Runtime) -> i32 { }; - let filter = has_end_time.not().and(HasTagFromList::new(&tags)); + let filter = has_end_time.not().and(has_one_of_tags(&tags)); // Filter all timetrackings for the ones that are not yet ended. iter.trace_unwrap() From d381702e8cb13f44831f735a0fdcafb04f15b4b4 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Thu, 20 Jul 2017 22:57:45 +0200 Subject: [PATCH 15/22] Fix: UI was build buggy, fix it. --- imag-timetrack/src/start.rs | 18 ++++++++++-------- imag-timetrack/src/stop.rs | 19 +++++++++++-------- imag-timetrack/src/ui.rs | 8 ++++---- 3 files changed, 25 insertions(+), 20 deletions(-) diff --git a/imag-timetrack/src/start.rs b/imag-timetrack/src/start.rs index 13b1f3e8..8518f566 100644 --- a/imag-timetrack/src/start.rs +++ b/imag-timetrack/src/start.rs @@ -29,14 +29,16 @@ pub fn start(rt: &Runtime) -> i32 { let (_, cmd) = rt.cli().subcommand(); let cmd = cmd.unwrap(); // checked in main() - let start = match cmd.value_of("start-time").map(::chrono::naive::NaiveDateTime::from_str) { - None => ::chrono::offset::Local::now().naive_local(), - Some(Ok(ndt)) => ndt, - Some(Err(e)) => { - trace_error(&e); - error!("Cannot continue, not having start time"); - return 1 - }, + let start = match cmd.value_of("start-time") { + None | Some("now") => ::chrono::offset::Local::now().naive_local(), + Some(ndt) => match ::chrono::naive::NaiveDateTime::from_str(ndt) { + Ok(ndt) => ndt, + Err(e) => { + trace_error(&e); + error!("Cannot continue, not having start time"); + return 1 + }, + } }; cmd.values_of("tags") diff --git a/imag-timetrack/src/stop.rs b/imag-timetrack/src/stop.rs index c1c5e682..29b03a02 100644 --- a/imag-timetrack/src/stop.rs +++ b/imag-timetrack/src/stop.rs @@ -37,16 +37,19 @@ pub fn stop(rt: &Runtime) -> i32 { let (_, cmd) = rt.cli().subcommand(); let cmd = cmd.unwrap(); // checked in main() - let stop_time = match cmd.value_of("stop-time").map(::chrono::naive::NaiveDateTime::from_str) { - None => ::chrono::offset::Local::now().naive_local(), - Some(Ok(ndt)) => ndt, - Some(Err(e)) => { - trace_error(&e); - error!("Cannot continue, not having stop time"); - return 1 - }, + let stop_time = match cmd.value_of("stop-time") { + None | Some("now") => ::chrono::offset::Local::now().naive_local(), + Some(ndt) => match ::chrono::naive::NaiveDateTime::from_str(ndt) { + Ok(ndt) => ndt, + Err(e) => { + trace_error(&e); + error!("Cannot continue, not having start time"); + return 1 + }, + } }; + // TODO: We do not yet support stopping all tags by simply calling the "stop" subcommand! let tags : Vec = cmd.values_of("tags") diff --git a/imag-timetrack/src/ui.rs b/imag-timetrack/src/ui.rs index f394057d..a27c4ced 100644 --- a/imag-timetrack/src/ui.rs +++ b/imag-timetrack/src/ui.rs @@ -25,8 +25,8 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { .version("0.1") .arg(Arg::with_name("start-time") .index(1) - .required(false) - .help("Start-time when to start the timetracking")) + .required(true) + .help("Start-time when to start the timetracking (use 'now' for current time)")) .arg(Arg::with_name("tags") .index(2) .required(true) @@ -39,8 +39,8 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { .version("0.1") .arg(Arg::with_name("end-time") .index(1) - .required(false) - .help("End-time when to stop the timetracking")) + .required(true) + .help("End-time when to stop the timetracking (use 'now' for current time)")) .arg(Arg::with_name("tags") .index(2) .required(true) From 157f6c0129f64145ea559e5144a23882028624c4 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Thu, 20 Jul 2017 23:02:48 +0200 Subject: [PATCH 16/22] Fix TimeTracking::get_timetrack_tag() for Entry --- libimagentrytimetrack/src/timetracking.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libimagentrytimetrack/src/timetracking.rs b/libimagentrytimetrack/src/timetracking.rs index 625238dd..ba40ac65 100644 --- a/libimagentrytimetrack/src/timetracking.rs +++ b/libimagentrytimetrack/src/timetracking.rs @@ -66,9 +66,10 @@ impl TimeTracking for Entry { self.get_header() .read(DATE_TIME_TAG_HEADER_PATH) .map_err_into(TTEK::HeaderReadError) - .map(|value| match value { - Some(&Value::String(ref s)) => s.clone().into(), - _ => unimplemented!(), + .and_then(|value| match value { + Some(&Value::String(ref s)) => Ok(s.clone().into()), + Some(_) => Err(TTEK::HeaderFieldTypeError.into_error()), + _ => Err(TTEK::HeaderReadError.into_error()) }) } From b4ff528a2cf439e0b173336391af913729e36bd7 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Thu, 20 Jul 2017 23:21:57 +0200 Subject: [PATCH 17/22] [WIP] Implement "imag timetrack list" command --- imag-timetrack/src/list.rs | 124 +++++++++++++++++++++++++++++++++++++ imag-timetrack/src/main.rs | 3 + imag-timetrack/src/ui.rs | 30 ++++++++- 3 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 imag-timetrack/src/list.rs diff --git a/imag-timetrack/src/list.rs b/imag-timetrack/src/list.rs new file mode 100644 index 00000000..b7d8dcd3 --- /dev/null +++ b/imag-timetrack/src/list.rs @@ -0,0 +1,124 @@ +// +// imag - the personal information management suite for the commandline +// Copyright (C) 2015, 2016 Matthias Beyer and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; version +// 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// + +use std::cmp::Ord; +use std::cmp::Ordering; +use std::str::FromStr; + +use filters::ops::not::Not; +use filters::filter::Filter; +use itertools::Itertools; +use itertools::MinMaxResult; +use chrono::NaiveDateTime; + +use libimagerror::trace::trace_error; +use libimagerror::trace::MapErrTrace; +use libimagerror::iter::TraceIterator; +use libimagstore::store::FileLockEntry; +use libimagentrytimetrack::timetrackingstore::TimeTrackStore; +use libimagentrytimetrack::timetracking::TimeTracking; +use libimagentrytimetrack::iter::filter::*; + +use libimagrt::runtime::Runtime; + +pub fn list(rt: &Runtime) -> i32 { + let (_, cmd) = rt.cli().subcommand(); + let cmd = cmd.unwrap(); // checked in main() + + let start = match cmd.value_of("start-time").map(::chrono::naive::NaiveDateTime::from_str) { + None => None, + Some(Ok(dt)) => Some(dt), + Some(Err(e)) => { + trace_error(&e); + None + } + }; + let end = match cmd.value_of("end-time").map(::chrono::naive::NaiveDateTime::from_str) { + None => None, + Some(Ok(dt)) => Some(dt), + Some(Err(e)) => { + trace_error(&e); + None + } + }; + + let list_not_ended = cmd.is_present("list-not-ended"); + + let start_time_filter = |timetracking: &FileLockEntry| { + start.map(|s| match timetracking.get_start_datetime() { + Ok(Some(dt)) => dt >= s, + Ok(None) => { + warn!("Funny things are happening: Timetracking has no start time"); + false + } + Err(e) => { + trace_error(&e); + false + } + }) + .unwrap_or(true) + }; + + let end_time_filter = |timetracking: &FileLockEntry| { + start.map(|s| match timetracking.get_end_datetime() { + Ok(Some(dt)) => dt <= s, + Ok(None) => list_not_ended, + Err(e) => { + trace_error(&e); + false + } + }) + .unwrap_or(true) + }; + + let filter = start_time_filter.and(end_time_filter); + + rt.store() + .get_timetrackings() + .and_then(|iter| { + iter.trace_unwrap() + .filter(|e| filter.filter(e)) + .fold(Ok(()), |acc, e| { + acc.and_then(|_| { + debug!("Processing {:?}", e.get_location()); + + let tag = try!(e.get_timetrack_tag()); + debug!(" -> tag = {:?}", tag); + + let start = try!(e.get_start_datetime()); + debug!(" -> start = {:?}", start); + + let end = try!(e.get_end_datetime()); + debug!(" -> end = {:?}", end); + + match (start, end) { + (None, _) => println!("{} has no start time.", tag), + (Some(s), None) => println!("{} | {} - ...", tag, s), + (Some(s), Some(e)) => println!("{} | {} - {}", tag, s, e), + } + + Ok(()) + }) + }) + }) + .map(|_| 0) + .map_err_trace() + .unwrap_or(1) +} + diff --git a/imag-timetrack/src/main.rs b/imag-timetrack/src/main.rs index de09a859..7546e6b9 100644 --- a/imag-timetrack/src/main.rs +++ b/imag-timetrack/src/main.rs @@ -38,6 +38,7 @@ extern crate libimagutil; mod cont; mod day; +mod list; mod month; mod start; mod stop; @@ -48,6 +49,7 @@ mod year; use cont::cont; use day::day; +use list::list; use month::month; use start::start; use stop::stop; @@ -70,6 +72,7 @@ fn main() { match command { "continue" => cont(&rt), "day" => day(&rt), + "list" => list(&rt), "month" => month(&rt), "start" => start(&rt), "stop" => stop(&rt), diff --git a/imag-timetrack/src/ui.rs b/imag-timetrack/src/ui.rs index a27c4ced..cefcef86 100644 --- a/imag-timetrack/src/ui.rs +++ b/imag-timetrack/src/ui.rs @@ -20,7 +20,35 @@ use clap::{Arg, App, SubCommand}; pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { - app.subcommand(SubCommand::with_name("start") + app + .subcommand(SubCommand::with_name("list") + .about("List time trackings") + .version("0.1") + .arg(Arg::with_name("start-time") + .short("f") + .long("from") + .takes_value(true) + .multiple(false) + .required(false) + .help("Set earliest time from which on time trackings should be shown (use 'now' for current time)")) + .arg(Arg::with_name("end-time") + .short("t") + .long("to") + .takes_value(true) + .multiple(false) + .required(false) + .help("Set latest time of time trackings to be shown (use 'now' for current time)")) + + .arg(Arg::with_name("list-not-ended") + .short("l") + .long("list-not-ended") + .takes_value(false) + .multiple(false) + .required(false) + .help("List not yet ended timetrackings even if after 'end-time'")) + ) + + .subcommand(SubCommand::with_name("start") .about("Start time tracking") .version("0.1") .arg(Arg::with_name("start-time") From 03e70721e669584d876910f3815c61087f323cde Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sun, 23 Jul 2017 10:35:04 +0200 Subject: [PATCH 18/22] Impl Display for TimeTrackingTag --- libimagentrytimetrack/src/tag.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/libimagentrytimetrack/src/tag.rs b/libimagentrytimetrack/src/tag.rs index ecf9c15c..df2f9795 100644 --- a/libimagentrytimetrack/src/tag.rs +++ b/libimagentrytimetrack/src/tag.rs @@ -17,6 +17,9 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // +use std::fmt::Display; +use std::fmt::Error as FmtError; +use std::fmt::Formatter; use std::path::PathBuf; use libimagstore::store::Result as StoreResult; @@ -34,6 +37,12 @@ impl TimeTrackingTag { } } +impl Display for TimeTrackingTag { + fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> { + self.0.fmt(f) + } +} + impl Into for TimeTrackingTag { fn into(self) -> String { self.0 From a2584f90e9fb494c46c7dd8d879224edd8f4f512 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Fri, 25 Aug 2017 14:42:58 +0200 Subject: [PATCH 19/22] Impl "day" command --- imag-timetrack/src/day.rs | 96 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 95 insertions(+), 1 deletion(-) diff --git a/imag-timetrack/src/day.rs b/imag-timetrack/src/day.rs index 0c3fdcd1..04d36044 100644 --- a/imag-timetrack/src/day.rs +++ b/imag-timetrack/src/day.rs @@ -17,9 +17,103 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // +use std::cmp::Ord; +use std::cmp::Ordering; +use std::str::FromStr; + +use filters::ops::not::Not; +use filters::filter::Filter; +use itertools::Itertools; +use itertools::MinMaxResult; +use chrono::NaiveDateTime; + +use libimagerror::trace::trace_error; +use libimagerror::trace::MapErrTrace; +use libimagerror::iter::TraceIterator; +use libimagstore::store::FileLockEntry; +use libimagentrytimetrack::timetrackingstore::TimeTrackStore; +use libimagentrytimetrack::timetracking::TimeTracking; +use libimagentrytimetrack::tag::TimeTrackingTag; +use libimagentrytimetrack::iter::filter::*; + use libimagrt::runtime::Runtime; + pub fn day(rt: &Runtime) -> i32 { - unimplemented!() + let (_, cmd) = rt.cli().subcommand(); + let cmd = cmd.unwrap(); // checked in main() + + let filter = { + let start = match cmd.value_of("start").map(::chrono::naive::NaiveDateTime::from_str) { + None => ::chrono::offset::Local::today().and_hms(0, 0, 0).naive_local(), + Some(Ok(dt)) => dt, + Some(Err(e)) => { + trace_error(&e); + return 1 + } + }; + + let end = match cmd.value_of("end").map(::chrono::naive::NaiveDateTime::from_str) { + None => ::chrono::offset::Local::today().and_hms(23, 59, 59).naive_local(), + Some(Ok(dt)) => dt, + Some(Err(e)) => { + trace_error(&e); + return 1 + } + }; + + let tags = cmd + .values_of("tags") + .map(|ts| ts.into_iter().map(String::from).map(TimeTrackingTag::from).collect()); + + let start_time_filter = has_start_time_where(move |dt: &NaiveDateTime| { + start <= *dt + }); + + let end_time_filter = has_end_time_where(move |dt: &NaiveDateTime| { + end >= *dt + }); + + let tags_filter = move |fle: &FileLockEntry| { + match tags { + Some(ref tags) => has_one_of_tags(&tags).filter(fle), + None => true, + } + }; + + tags_filter.and(start_time_filter).and(end_time_filter) + }; + + rt.store() + .get_timetrackings() + .and_then(|iter| { + iter.trace_unwrap() + .filter(|e| filter.filter(e)) + .fold(Ok(()), |acc, e| { + acc.and_then(|_| { + debug!("Processing {:?}", e.get_location()); + + let tag = try!(e.get_timetrack_tag()); + debug!(" -> tag = {:?}", tag); + + let start = try!(e.get_start_datetime()); + debug!(" -> start = {:?}", start); + + let end = try!(e.get_end_datetime()); + debug!(" -> end = {:?}", end); + + match (start, end) { + (None, _) => println!("{} has no start time.", tag), + (Some(s), None) => println!("{} | {} - ...", tag, s), + (Some(s), Some(e)) => println!("{} | {} - {}", tag, s, e), + } + + Ok(()) + }) + }) + }) + .map(|_| 0) + .map_err_trace() + .unwrap_or(1) } From 1479f5b0fb63c9ccd4585ce63deffc0908f05a90 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Fri, 25 Aug 2017 14:57:31 +0200 Subject: [PATCH 20/22] Impl "week" subcommand --- imag-timetrack/src/week.rs | 103 ++++++++++++++++++++++++++++++++++++- 1 file changed, 102 insertions(+), 1 deletion(-) diff --git a/imag-timetrack/src/week.rs b/imag-timetrack/src/week.rs index f81b6db0..4793c4e2 100644 --- a/imag-timetrack/src/week.rs +++ b/imag-timetrack/src/week.rs @@ -17,9 +17,110 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // +use std::cmp::Ord; +use std::cmp::Ordering; +use std::str::FromStr; + +use filters::ops::not::Not; +use filters::filter::Filter; +use itertools::Itertools; +use itertools::MinMaxResult; +use chrono::NaiveDateTime; + +use libimagerror::trace::trace_error; +use libimagerror::trace::MapErrTrace; +use libimagerror::iter::TraceIterator; +use libimagstore::store::FileLockEntry; +use libimagentrytimetrack::timetrackingstore::TimeTrackStore; +use libimagentrytimetrack::timetracking::TimeTracking; +use libimagentrytimetrack::tag::TimeTrackingTag; +use libimagentrytimetrack::iter::filter::*; + use libimagrt::runtime::Runtime; pub fn week(rt: &Runtime) -> i32 { - unimplemented!() + let cmd = rt.cli().subcommand().1.unwrap(); // checked in main + + let filter = { + use chrono::offset::Local; + use chrono::naive::NaiveDate; + use chrono::Weekday; + use chrono::Datelike; + + let this_week = Local::now().iso_week(); + + let start = match cmd.value_of("start").map(::chrono::naive::NaiveDateTime::from_str) { + None => NaiveDate::from_isoywd(this_week.year(), this_week.week(), Weekday::Mon) + .and_hms(0, 0, 0), + Some(Ok(dt)) => dt, + Some(Err(e)) => { + trace_error(&e); + return 1 + } + }; + + let end = match cmd.value_of("end").map(::chrono::naive::NaiveDateTime::from_str) { + None => NaiveDate::from_isoywd(this_week.year(), this_week.week(), Weekday::Sun) + .and_hms(23, 59, 59), + Some(Ok(dt)) => dt, + Some(Err(e)) => { + trace_error(&e); + return 1 + } + }; + + let tags = cmd + .values_of("tags") + .map(|ts| ts.into_iter().map(String::from).map(TimeTrackingTag::from).collect()); + + let start_time_filter = has_start_time_where(move |dt: &NaiveDateTime| { + start <= *dt + }); + + let end_time_filter = has_end_time_where(move |dt: &NaiveDateTime| { + end >= *dt + }); + + let tags_filter = move |fle: &FileLockEntry| { + match tags { + Some(ref tags) => has_one_of_tags(&tags).filter(fle), + None => true, + } + }; + + tags_filter.and(start_time_filter).and(end_time_filter) + }; + + rt.store() + .get_timetrackings() + .and_then(|iter| { + iter.trace_unwrap() + .filter(|e| filter.filter(e)) + .fold(Ok(()), |acc, e| { + acc.and_then(|_| { + debug!("Processing {:?}", e.get_location()); + + let tag = try!(e.get_timetrack_tag()); + debug!(" -> tag = {:?}", tag); + + let start = try!(e.get_start_datetime()); + debug!(" -> start = {:?}", start); + + let end = try!(e.get_end_datetime()); + debug!(" -> end = {:?}", end); + + match (start, end) { + (None, _) => println!("{} has no start time.", tag), + (Some(s), None) => println!("{} | {} - ...", tag, s), + (Some(s), Some(e)) => println!("{} | {} - {}", tag, s, e), + } + + Ok(()) + }) + }) + }) + .map(|_| 0) + .map_err_trace() + .unwrap_or(1) } From e25478e4d7e5ae8df93f3cab4948e71fd0652119 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Fri, 25 Aug 2017 15:03:46 +0200 Subject: [PATCH 21/22] Impl "month" subcommand --- imag-timetrack/src/month.rs | 112 +++++++++++++++++++++++++++++++++++- 1 file changed, 111 insertions(+), 1 deletion(-) diff --git a/imag-timetrack/src/month.rs b/imag-timetrack/src/month.rs index 61d974d7..0f9872c3 100644 --- a/imag-timetrack/src/month.rs +++ b/imag-timetrack/src/month.rs @@ -17,9 +17,119 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // +use std::cmp::Ord; +use std::cmp::Ordering; +use std::str::FromStr; + +use filters::ops::not::Not; +use filters::filter::Filter; +use itertools::Itertools; +use itertools::MinMaxResult; +use chrono::NaiveDateTime; + +use libimagerror::trace::trace_error; +use libimagerror::trace::MapErrTrace; +use libimagerror::iter::TraceIterator; +use libimagstore::store::FileLockEntry; +use libimagentrytimetrack::timetrackingstore::TimeTrackStore; +use libimagentrytimetrack::timetracking::TimeTracking; +use libimagentrytimetrack::tag::TimeTrackingTag; +use libimagentrytimetrack::iter::filter::*; + use libimagrt::runtime::Runtime; pub fn month(rt: &Runtime) -> i32 { - unimplemented!() + let cmd = rt.cli().subcommand().1.unwrap(); // checked in main + + let filter = { + use chrono::offset::Local; + use chrono::naive::NaiveDate; + use chrono::Weekday; + use chrono::Datelike; + + let now = Local::now(); + + let start = match cmd.value_of("start").map(::chrono::naive::NaiveDateTime::from_str) { + None => NaiveDate::from_ymd(now.year(), now.month(), 1).and_hms(0, 0, 0), + Some(Ok(dt)) => dt, + Some(Err(e)) => { + trace_error(&e); + return 1 + } + }; + + let end = match cmd.value_of("end").map(::chrono::naive::NaiveDateTime::from_str) { + None => { + + // Is it much harder to go to the last second of the current month than to the first + // second of the next month, right? + let (year, month) = if now.month() == 12 { + (now.year() + 1, 1) + } else { + (now.year(), now.month()) + }; + + NaiveDate::from_ymd(year, month, 1).and_hms(0, 0, 0) + }, + Some(Ok(dt)) => dt, + Some(Err(e)) => { + trace_error(&e); + return 1 + } + }; + + let tags = cmd + .values_of("tags") + .map(|ts| ts.into_iter().map(String::from).map(TimeTrackingTag::from).collect()); + + let start_time_filter = has_start_time_where(move |dt: &NaiveDateTime| { + start <= *dt + }); + + let end_time_filter = has_end_time_where(move |dt: &NaiveDateTime| { + end >= *dt + }); + + let tags_filter = move |fle: &FileLockEntry| { + match tags { + Some(ref tags) => has_one_of_tags(&tags).filter(fle), + None => true, + } + }; + + tags_filter.and(start_time_filter).and(end_time_filter) + }; + + rt.store() + .get_timetrackings() + .and_then(|iter| { + iter.trace_unwrap() + .filter(|e| filter.filter(e)) + .fold(Ok(()), |acc, e| { + acc.and_then(|_| { + debug!("Processing {:?}", e.get_location()); + + let tag = try!(e.get_timetrack_tag()); + debug!(" -> tag = {:?}", tag); + + let start = try!(e.get_start_datetime()); + debug!(" -> start = {:?}", start); + + let end = try!(e.get_end_datetime()); + debug!(" -> end = {:?}", end); + + match (start, end) { + (None, _) => println!("{} has no start time.", tag), + (Some(s), None) => println!("{} | {} - ...", tag, s), + (Some(s), Some(e)) => println!("{} | {} - {}", tag, s, e), + } + + Ok(()) + }) + }) + }) + .map(|_| 0) + .map_err_trace() + .unwrap_or(1) } From 004b7887ff4728131ec1f5d39af5ed280e8d4457 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Fri, 25 Aug 2017 15:04:54 +0200 Subject: [PATCH 22/22] Impl "year" subcommand --- imag-timetrack/src/year.rs | 103 ++++++++++++++++++++++++++++++++++++- 1 file changed, 102 insertions(+), 1 deletion(-) diff --git a/imag-timetrack/src/year.rs b/imag-timetrack/src/year.rs index 7f7ddeb8..8534de1a 100644 --- a/imag-timetrack/src/year.rs +++ b/imag-timetrack/src/year.rs @@ -17,9 +17,110 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // +use std::cmp::Ord; +use std::cmp::Ordering; +use std::str::FromStr; + +use filters::ops::not::Not; +use filters::filter::Filter; +use itertools::Itertools; +use itertools::MinMaxResult; +use chrono::NaiveDateTime; + +use libimagerror::trace::trace_error; +use libimagerror::trace::MapErrTrace; +use libimagerror::iter::TraceIterator; +use libimagstore::store::FileLockEntry; +use libimagentrytimetrack::timetrackingstore::TimeTrackStore; +use libimagentrytimetrack::timetracking::TimeTracking; +use libimagentrytimetrack::tag::TimeTrackingTag; +use libimagentrytimetrack::iter::filter::*; + use libimagrt::runtime::Runtime; pub fn year(rt: &Runtime) -> i32 { - unimplemented!() + let cmd = rt.cli().subcommand().1.unwrap(); // checked in main + + let filter = { + use chrono::offset::Local; + use chrono::naive::NaiveDate; + use chrono::Weekday; + use chrono::Datelike; + + let now = Local::now(); + + let start = match cmd.value_of("start").map(::chrono::naive::NaiveDateTime::from_str) { + None => NaiveDate::from_ymd(now.year(), 1, 1).and_hms(0, 0, 0), + Some(Ok(dt)) => dt, + Some(Err(e)) => { + trace_error(&e); + return 1 + } + }; + + let end = match cmd.value_of("end").map(::chrono::naive::NaiveDateTime::from_str) { + None => { + NaiveDate::from_ymd(now.year() + 1, 1, 1).and_hms(0, 0, 0) + }, + Some(Ok(dt)) => dt, + Some(Err(e)) => { + trace_error(&e); + return 1 + } + }; + + let tags = cmd + .values_of("tags") + .map(|ts| ts.into_iter().map(String::from).map(TimeTrackingTag::from).collect()); + + let start_time_filter = has_start_time_where(move |dt: &NaiveDateTime| { + start <= *dt + }); + + let end_time_filter = has_end_time_where(move |dt: &NaiveDateTime| { + end >= *dt + }); + + let tags_filter = move |fle: &FileLockEntry| { + match tags { + Some(ref tags) => has_one_of_tags(&tags).filter(fle), + None => true, + } + }; + + tags_filter.and(start_time_filter).and(end_time_filter) + }; + + rt.store() + .get_timetrackings() + .and_then(|iter| { + iter.trace_unwrap() + .filter(|e| filter.filter(e)) + .fold(Ok(()), |acc, e| { + acc.and_then(|_| { + debug!("Processing {:?}", e.get_location()); + + let tag = try!(e.get_timetrack_tag()); + debug!(" -> tag = {:?}", tag); + + let start = try!(e.get_start_datetime()); + debug!(" -> start = {:?}", start); + + let end = try!(e.get_end_datetime()); + debug!(" -> end = {:?}", end); + + match (start, end) { + (None, _) => println!("{} has no start time.", tag), + (Some(s), None) => println!("{} | {} - ...", tag, s), + (Some(s), Some(e)) => println!("{} | {} - {}", tag, s, e), + } + + Ok(()) + }) + }) + }) + .map(|_| 0) + .map_err_trace() + .unwrap_or(1) }