Merge pull request #996 from matthiasbeyer/imag-timetrack
imag timetrack
This commit is contained in:
commit
057d919239
19 changed files with 1380 additions and 101 deletions
|
@ -11,6 +11,7 @@ members = [
|
|||
"imag-ref",
|
||||
"imag-store",
|
||||
"imag-tag",
|
||||
"imag-timetrack",
|
||||
"imag-todo",
|
||||
"imag-view",
|
||||
"libimagannotation",
|
||||
|
|
40
imag-timetrack/Cargo.toml
Normal file
40
imag-timetrack/Cargo.toml
Normal file
|
@ -0,0 +1,40 @@
|
|||
[package]
|
||||
name = "imag-timetrack"
|
||||
version = "0.3.0"
|
||||
authors = ["Matthias Beyer <mail@beyermatthias.de>"]
|
||||
|
||||
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"
|
||||
filters = "0.1.1"
|
||||
itertools = "0.6"
|
||||
|
||||
[dependencies.libimagstore]
|
||||
path = "../libimagstore"
|
||||
|
||||
[dependencies.libimagrt]
|
||||
path = "../libimagrt"
|
||||
|
||||
[dependencies.libimagerror]
|
||||
path = "../libimagerror"
|
||||
|
||||
[dependencies.libimagentrytimetrack]
|
||||
path = "../libimagentrytimetrack"
|
||||
|
||||
[dependencies.libimagutil]
|
||||
path = "../libimagutil"
|
||||
|
112
imag-timetrack/src/cont.rs
Normal file
112
imag-timetrack/src/cont.rs
Normal file
|
@ -0,0 +1,112 @@
|
|||
//
|
||||
// imag - the personal information management suite for the commandline
|
||||
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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 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 libimagentrytimetrack::timetrackingstore::TimeTrackStore;
|
||||
use libimagentrytimetrack::timetracking::TimeTracking;
|
||||
use libimagentrytimetrack::iter::filter::*;
|
||||
|
||||
use libimagrt::runtime::Runtime;
|
||||
|
||||
pub fn cont(rt: &Runtime) -> i32 {
|
||||
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)
|
||||
}
|
||||
|
119
imag-timetrack/src/day.rs
Normal file
119
imag-timetrack/src/day.rs
Normal file
|
@ -0,0 +1,119 @@
|
|||
//
|
||||
// imag - the personal information management suite for the commandline
|
||||
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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::tag::TimeTrackingTag;
|
||||
use libimagentrytimetrack::iter::filter::*;
|
||||
|
||||
use libimagrt::runtime::Runtime;
|
||||
|
||||
|
||||
pub fn day(rt: &Runtime) -> i32 {
|
||||
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)
|
||||
}
|
||||
|
124
imag-timetrack/src/list.rs
Normal file
124
imag-timetrack/src/list.rs
Normal file
|
@ -0,0 +1,124 @@
|
|||
//
|
||||
// imag - the personal information management suite for the commandline
|
||||
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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)
|
||||
}
|
||||
|
93
imag-timetrack/src/main.rs
Normal file
93
imag-timetrack/src/main.rs
Normal file
|
@ -0,0 +1,93 @@
|
|||
//
|
||||
// imag - the personal information management suite for the commandline
|
||||
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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
|
||||
//
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
#[macro_use]
|
||||
extern crate version;
|
||||
|
||||
extern crate clap;
|
||||
extern crate semver;
|
||||
extern crate toml;
|
||||
extern crate chrono;
|
||||
extern crate filters;
|
||||
extern crate itertools;
|
||||
|
||||
extern crate libimagerror;
|
||||
extern crate libimagstore;
|
||||
extern crate libimagrt;
|
||||
extern crate libimagentrytimetrack;
|
||||
extern crate libimagutil;
|
||||
|
||||
mod cont;
|
||||
mod day;
|
||||
mod list;
|
||||
mod month;
|
||||
mod start;
|
||||
mod stop;
|
||||
mod track;
|
||||
mod ui;
|
||||
mod week;
|
||||
mod year;
|
||||
|
||||
use cont::cont;
|
||||
use day::day;
|
||||
use list::list;
|
||||
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() {
|
||||
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),
|
||||
"list" => list(&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);
|
||||
}
|
135
imag-timetrack/src/month.rs
Normal file
135
imag-timetrack/src/month.rs
Normal file
|
@ -0,0 +1,135 @@
|
|||
//
|
||||
// imag - the personal information management suite for the commandline
|
||||
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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::tag::TimeTrackingTag;
|
||||
use libimagentrytimetrack::iter::filter::*;
|
||||
|
||||
use libimagrt::runtime::Runtime;
|
||||
|
||||
pub fn month(rt: &Runtime) -> i32 {
|
||||
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)
|
||||
}
|
||||
|
56
imag-timetrack/src/start.rs
Normal file
56
imag-timetrack/src/start.rs
Normal file
|
@ -0,0 +1,56 @@
|
|||
//
|
||||
// imag - the personal information management suite for the commandline
|
||||
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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::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 {
|
||||
let (_, cmd) = rt.cli().subcommand();
|
||||
let cmd = cmd.unwrap(); // checked in main()
|
||||
|
||||
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")
|
||||
.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)
|
||||
})
|
||||
}
|
||||
|
98
imag-timetrack/src/stop.rs
Normal file
98
imag-timetrack/src/stop.rs
Normal file
|
@ -0,0 +1,98 @@
|
|||
//
|
||||
// imag - the personal information management suite for the commandline
|
||||
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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::str::FromStr;
|
||||
|
||||
use filters::filter::Filter;
|
||||
|
||||
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;
|
||||
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();
|
||||
let cmd = cmd.unwrap(); // checked in main()
|
||||
|
||||
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<TimeTrackingTag> = 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
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
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()
|
||||
.filter_map(|elem| {
|
||||
if filter.filter(&elem) {
|
||||
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
|
||||
}
|
||||
})
|
||||
}
|
||||
|
75
imag-timetrack/src/track.rs
Normal file
75
imag-timetrack/src/track.rs
Normal file
|
@ -0,0 +1,75 @@
|
|||
//
|
||||
// imag - the personal information management suite for the commandline
|
||||
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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::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 {
|
||||
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<NaiveDateTime> {
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
178
imag-timetrack/src/ui.rs
Normal file
178
imag-timetrack/src/ui.rs
Normal file
|
@ -0,0 +1,178 @@
|
|||
//
|
||||
// imag - the personal information management suite for the commandline
|
||||
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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("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")
|
||||
.index(1)
|
||||
.required(true)
|
||||
.help("Start-time when to start the timetracking (use 'now' for current time)"))
|
||||
.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(true)
|
||||
.help("End-time when to stop the timetracking (use 'now' for current time)"))
|
||||
.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"))
|
||||
)
|
||||
|
||||
}
|
126
imag-timetrack/src/week.rs
Normal file
126
imag-timetrack/src/week.rs
Normal file
|
@ -0,0 +1,126 @@
|
|||
//
|
||||
// imag - the personal information management suite for the commandline
|
||||
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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::tag::TimeTrackingTag;
|
||||
use libimagentrytimetrack::iter::filter::*;
|
||||
|
||||
use libimagrt::runtime::Runtime;
|
||||
|
||||
pub fn week(rt: &Runtime) -> i32 {
|
||||
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)
|
||||
}
|
||||
|
126
imag-timetrack/src/year.rs
Normal file
126
imag-timetrack/src/year.rs
Normal file
|
@ -0,0 +1,126 @@
|
|||
//
|
||||
// imag - the personal information management suite for the commandline
|
||||
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> 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::tag::TimeTrackingTag;
|
||||
use libimagentrytimetrack::iter::filter::*;
|
||||
|
||||
use libimagrt::runtime::Runtime;
|
||||
|
||||
pub fn year(rt: &Runtime) -> i32 {
|
||||
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)
|
||||
}
|
||||
|
|
@ -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"
|
||||
|
|
|
@ -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<Item = Result<FileLockEntry<'a>>>
|
||||
{
|
||||
iter: I,
|
||||
allowed_tags: &'a Vec<TTT>,
|
||||
|
||||
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<Item = Result<FileLockEntry<'a>>>
|
||||
{
|
||||
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<TTT>) -> 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: F) -> HasStartTimeWhere<F>
|
||||
where F: Fn(&NaiveDateTime) -> bool
|
||||
{
|
||||
HasStartTimeWhere::new(f)
|
||||
}
|
||||
|
||||
pub fn has_end_time_where<F>(f: F) -> HasEndTimeWhere<F>
|
||||
where F: Fn(&NaiveDateTime) -> bool
|
||||
{
|
||||
HasEndTimeWhere::new(f)
|
||||
}
|
||||
|
||||
pub fn has_one_of_tags<'a>(tags: &'a Vec<TTT>) -> 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>(F)
|
||||
where F: Fn(&NaiveDateTime) -> bool;
|
||||
|
||||
impl<F: Fn(&NaiveDateTime) -> bool> HasStartTimeWhere<F> {
|
||||
pub fn new(f: F) -> HasStartTimeWhere<F> {
|
||||
HasStartTimeWhere(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, I> Iterator for WithOneOf<'a, I>
|
||||
where I: Iterator<Item = Result<FileLockEntry<'a>>>
|
||||
{
|
||||
type Item = Result<FileLockEntry<'a>>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
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<FileLockEntry<'a>> for HasStartTimeWhere<F>
|
||||
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<Item = Result<FileLockEntry<'a>>> {
|
||||
fn with_timetracking_tags(self, tags: &'a Vec<TTT>) -> WithOneOf<'a, Self>;
|
||||
}
|
||||
pub struct HasEndTimeWhere<F>(F)
|
||||
where F: Fn(&NaiveDateTime) -> bool;
|
||||
|
||||
impl<'a, I> WithOneOfTags<'a> for I
|
||||
where I: Iterator<Item = Result<FileLockEntry<'a>>>,
|
||||
Self: Sized
|
||||
{
|
||||
fn with_timetracking_tags(self, tags: &'a Vec<TTT>) -> WithOneOf<'a, Self> {
|
||||
WithOneOf::new(self, tags)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub struct WithNoneOf<'a, I>
|
||||
where I: Iterator<Item = Result<FileLockEntry<'a>>>
|
||||
{
|
||||
iter: I,
|
||||
disallowed_tags: &'a Vec<TTT>,
|
||||
}
|
||||
|
||||
impl<'a, I> WithNoneOf<'a, I>
|
||||
where I: Iterator<Item = Result<FileLockEntry<'a>>>
|
||||
{
|
||||
|
||||
pub fn new(iter: I, disallowed_tags: &'a Vec<TTT>) -> WithNoneOf<'a, I> {
|
||||
WithNoneOf {
|
||||
iter: iter,
|
||||
disallowed_tags: disallowed_tags
|
||||
impl<F: Fn(&NaiveDateTime) -> bool> HasEndTimeWhere<F> {
|
||||
pub fn new(f: F) -> HasEndTimeWhere<F> {
|
||||
HasEndTimeWhere(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, I> Iterator for WithNoneOf<'a, I>
|
||||
where I: Iterator<Item = Result<FileLockEntry<'a>>>
|
||||
{
|
||||
type Item = Result<FileLockEntry<'a>>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
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<FileLockEntry<'a>> for HasEndTimeWhere<F>
|
||||
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<Item = Result<FileLockEntry<'a>>> {
|
||||
fn without_timetracking_tags(self, tags: &'a Vec<TTT>) -> WithNoneOf<'a, Self>;
|
||||
}
|
||||
pub struct HasOneOfTags<'a>(&'a Vec<TTT>);
|
||||
|
||||
impl<'a, I> WithNoneOfTags<'a> for I
|
||||
where I: Iterator<Item = Result<FileLockEntry<'a>>>,
|
||||
Self: Sized
|
||||
{
|
||||
fn without_timetracking_tags(self, tags: &'a Vec<TTT>) -> WithNoneOf<'a, Self> {
|
||||
WithNoneOf::new(self, tags)
|
||||
impl<'a> HasOneOfTags<'a> {
|
||||
pub fn new(tags: &'a Vec<TTT>) -> HasOneOfTags<'a> {
|
||||
HasOneOfTags(tags)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Filter<FileLockEntry<'b>> 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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<String> for TimeTrackingTag {
|
||||
fn into(self) -> String {
|
||||
self.0
|
||||
|
|
|
@ -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())
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ pub trait TimeTrackStore<'a> {
|
|||
fn create_timetracking_at(&'a self, start: &NDT, ts: &TTT) -> Result<FileLockEntry<'a>>;
|
||||
fn create_timetracking(&'a self, start: &NDT, end: &NDT, ts: &TTT) -> Result<FileLockEntry<'a>>;
|
||||
|
||||
fn get_timetrackings<I>(&'a self) -> Result<GetTimeTrackIter<'a>>;
|
||||
fn get_timetrackings(&'a self) -> Result<GetTimeTrackIter<'a>>;
|
||||
}
|
||||
|
||||
fn now() -> NDT {
|
||||
|
@ -104,7 +104,7 @@ impl<'a> TimeTrackStore<'a> for Store {
|
|||
})
|
||||
}
|
||||
|
||||
fn get_timetrackings<I>(&'a self) -> Result<GetTimeTrackIter<'a>> {
|
||||
fn get_timetrackings(&'a self) -> Result<GetTimeTrackIter<'a>> {
|
||||
self.retrieve_for_module(CRATE_NAME)
|
||||
.map_err_into(TTEK::StoreReadError)
|
||||
.map(|iter| GetTimeTrackIter::new(iter, self))
|
||||
|
|
Loading…
Reference in a new issue