From 72ac747ec26b9e1048e48af4197c86bc613cdd0a Mon Sep 17 00:00:00 2001 From: Nikolas Schmidt-Voigt Date: Wed, 22 Sep 2021 17:11:05 +0200 Subject: [PATCH] continue n-th last activity --- src/controller/list.rs | 12 +++--- src/controller/manipulation.rs | 25 +++++++---- src/data/getter.rs | 16 ++++--- src/main.rs | 79 ++++++++++++++++++++++++---------- src/view/list.rs | 9 ++-- 5 files changed, 94 insertions(+), 47 deletions(-) diff --git a/src/controller/list.rs b/src/controller/list.rs index d401377..f7e54e3 100644 --- a/src/controller/list.rs +++ b/src/controller/list.rs @@ -29,12 +29,12 @@ pub fn list(file_name: &str, filter: getter::ActivityFilter, do_group_activities if do_group_activities { list::list_activities_grouped_by_date( - &filtered_activities[first_element..filtered_activities.len()], + &filtered_activities[first_element..], ); } else { let with_start_dates = !filter.date.is_some(); list::list_activities( - &filtered_activities[first_element..filtered_activities.len()], + &filtered_activities[first_element..], with_start_dates, ); } @@ -61,11 +61,13 @@ pub fn list_projects(file_name: &str) -> Result<()> { } // return last finished activity -pub fn display_last_activity(file_name: &str) -> Result<()> { +pub fn list_last_activities(file_name: &str, number: usize) -> Result<()> { let file_content = bartib_file::get_file_content(file_name)?; - let descriptions_and_projects : Vec<(&String, &String)> = getter::get_descriptions_and_projects(&file_content).into_iter().collect(); - list::list_descriptions_and_projects(&descriptions_and_projects[..]); + let descriptions_and_projects : Vec<(&String, &String)> = getter::get_descriptions_and_projects(&file_content); + let first_element = descriptions_and_projects.len().saturating_sub(number); + + list::list_descriptions_and_projects(&descriptions_and_projects[first_element..]); Ok(()) } \ No newline at end of file diff --git a/src/controller/manipulation.rs b/src/controller/manipulation.rs index 980cf27..02a2e54 100644 --- a/src/controller/manipulation.rs +++ b/src/controller/manipulation.rs @@ -67,24 +67,33 @@ pub fn continue_last_activity( project_name: Option<&str>, activity_description: Option<&str>, time: Option, + number: usize ) -> Result<()> { let mut file_content = bartib_file::get_file_content(file_name)?; - let optional_last_activity = - getter::get_last_activity_by_start(&file_content).or(getter::get_last_activity_by_end(&file_content)); + let descriptions_and_projects : Vec<(&String, &String)> = getter::get_descriptions_and_projects(&file_content); - if let Some(last_activity) = optional_last_activity { + if descriptions_and_projects.is_empty() { + bail!("No activity has been started before.") + } + + if number > descriptions_and_projects.len() { + bail!(format!("Less than {} distinct activities have been logged yet", number)); + } + + let i = descriptions_and_projects.len().saturating_sub(number).saturating_sub(1); + let optional_description_and_project = descriptions_and_projects.get(i); + + if let Some((description, project)) = optional_description_and_project { let new_activity = activity::Activity::start( - project_name.unwrap_or(&last_activity.project).to_string(), - activity_description - .unwrap_or(&last_activity.description) - .to_string(), + project_name.unwrap_or(project).to_string(), + activity_description.unwrap_or(description).to_string(), time, ); stop_all_running_activities(&mut file_content, time); save_new_activity(file_name, &mut file_content, new_activity) } else { - bail!("No activity has been started before.") + bail!(format!("Less than {} distinct activities have been logged yet", number)); } } diff --git a/src/data/getter.rs b/src/data/getter.rs index aa7294a..1054c5e 100644 --- a/src/data/getter.rs +++ b/src/data/getter.rs @@ -1,4 +1,4 @@ -use std::collections::{HashSet, VecDeque}; +use std::collections::HashSet; use chrono::{naive, NaiveDate}; use crate::data::activity; @@ -11,17 +11,19 @@ pub struct ActivityFilter { pub date: Option, } -pub fn get_descriptions_and_projects(file_content: &[bartib_file::Line]) -> VecDeque<(&String, &String)> { - let mut known_descriptions_and_projects : HashSet<(&String, &String)> = HashSet::new(); - let mut descriptions_and_projects : VecDeque<(&String, &String)> = VecDeque::new(); +pub fn get_descriptions_and_projects(file_content: &[bartib_file::Line]) -> Vec<(&String, &String)> { + let mut activities : Vec<&activity::Activity> = get_activities(file_content).collect(); + activities.sort_by_key(|activity| activity.start); + activities.reverse(); + + let mut known_descriptions_and_projects : HashSet<(&String, &String)> = HashSet::new(); + let mut descriptions_and_projects : Vec<(&String, &String)> = Vec::new(); - // TODO: sortierung nach Start-Zeit for description_and_project in get_activities(file_content).map(|a| (&a.description, &a.project)) { if !known_descriptions_and_projects.contains(&description_and_project) { known_descriptions_and_projects.insert(description_and_project); - descriptions_and_projects.push_back(description_and_project); + descriptions_and_projects.push(description_and_project); } - } descriptions_and_projects diff --git a/src/main.rs b/src/main.rs index a956de3..660d57f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -105,6 +105,16 @@ fn main() -> Result<()> { .help("a description of the new activity") .takes_value(true), ) + .arg( + Arg::with_name("number") + .short("n") + .long("number") + .value_name("NUMBER") + .help("maximum number of activities to display") + .required(false) + .takes_value(true) + .default_value("0") + ) .arg(&arg_time), ) .subcommand( @@ -147,7 +157,20 @@ fn main() -> Result<()> { .arg(&arg_today) .arg(&arg_yesterday), ) - .subcommand(SubCommand::with_name("last").about("displays last finished acitivity")) + .subcommand( + SubCommand::with_name("last") + .about("displays last finished acitivity") + .arg( + Arg::with_name("number") + .short("n") + .long("number") + .value_name("NUMBER") + .help("maximum number of activities to display") + .required(false) + .takes_value(true) + .default_value("10") + ) + ) .subcommand(SubCommand::with_name("projects").about("list all projects")) .subcommand( SubCommand::with_name("edit") @@ -184,8 +207,12 @@ fn run_subcommand(matches: &ArgMatches, file_name: &str) -> Result<()> { let activity_description = sub_m.value_of("description"); let time = get_time_argument_or_ignore(sub_m.value_of("time"), "-t/--time") .map(|t| Local::today().naive_local().and_time(t)); + let number = get_number_argument_or_ignore( + sub_m.value_of("number"), + "-n/--number", + ).unwrap_or(0); - bartib::controller::manipulation::continue_last_activity(file_name, project_name, activity_description, time) + bartib::controller::manipulation::continue_last_activity(file_name, project_name, activity_description, time, number) } ("stop", Some(sub_m)) => { let time = get_time_argument_or_ignore(sub_m.value_of("time"), "-t/--time") @@ -195,25 +222,6 @@ fn run_subcommand(matches: &ArgMatches, file_name: &str) -> Result<()> { } ("current", Some(_)) => bartib::controller::list::list_running(file_name), ("list", Some(sub_m)) => { - let mut filter = bartib::data::getter::ActivityFilter { - number_of_activities: None, - from_date: get_date_argument_or_ignore(sub_m.value_of("from_date"), "--from"), - to_date: get_date_argument_or_ignore(sub_m.value_of("to_date"), "--to"), - date: get_date_argument_or_ignore(sub_m.value_of("date"), "-d/--date"), - }; - - if sub_m.is_present("today") { - filter.date = Some(Local::now().naive_local().date()); - } - - if sub_m.is_present("yesterday") { - filter.date = Some(Local::now().naive_local().date() - Duration::days(1)); - } - - let do_group_activities = !sub_m.is_present("no_grouping") && !filter.date.is_some(); - bartib::controller::list::list(file_name, filter, do_group_activities) - } - ("report", Some(sub_m)) => { let mut filter = bartib::data::getter::ActivityFilter { number_of_activities: get_number_argument_or_ignore( sub_m.value_of("number"), @@ -232,10 +240,35 @@ fn run_subcommand(matches: &ArgMatches, file_name: &str) -> Result<()> { filter.date = Some(Local::now().naive_local().date() - Duration::days(1)); } + let do_group_activities = !sub_m.is_present("no_grouping") && !filter.date.is_some(); + bartib::controller::list::list(file_name, filter, do_group_activities) + } + ("report", Some(sub_m)) => { + let mut filter = bartib::data::getter::ActivityFilter { + number_of_activities: None, + from_date: get_date_argument_or_ignore(sub_m.value_of("from_date"), "--from"), + to_date: get_date_argument_or_ignore(sub_m.value_of("to_date"), "--to"), + date: get_date_argument_or_ignore(sub_m.value_of("date"), "-d/--date"), + }; + + if sub_m.is_present("today") { + filter.date = Some(Local::now().naive_local().date()); + } + + if sub_m.is_present("yesterday") { + filter.date = Some(Local::now().naive_local().date() - Duration::days(1)); + } + bartib::controller::report::show_report(file_name, filter) } - ("projects", Some(_)) => bartib::controller::list::list_projects(file_name), - ("last", Some(_)) => bartib::controller::list::display_last_activity(file_name), + ("projects", Some(_)) => bartib::controller::list::list_projects(file_name), + ("last", Some(sub_m)) => { + let number = get_number_argument_or_ignore( + sub_m.value_of("number"), + "-n/--number", + ).unwrap_or(10); + bartib::controller::list::list_last_activities(file_name, number) + } ("edit", Some(sub_m)) => { let optional_editor_command = sub_m.value_of("editor"); bartib::controller::manipulation::start_editor(file_name, optional_editor_command) diff --git a/src/view/list.rs b/src/view/list.rs index 5b92109..aa88754 100644 --- a/src/view/list.rs +++ b/src/view/list.rs @@ -97,18 +97,19 @@ pub fn list_descriptions_and_projects(descriptions_and_projects : &[(&String, &S println!("No activities have been tracked yet"); } else { let mut descriptions_and_projects_table = table::Table::new(vec![ - "Index".to_string(), + "#".to_string(), "Description".to_string(), "Project".to_string() ]); - let mut i = 0; + let mut i = descriptions_and_projects.len(); for (description, project) in descriptions_and_projects { + i = i.saturating_sub(1); + descriptions_and_projects_table.add_row( - table::Row::new(vec![i.to_string(), description.to_string(), project.to_string()]) + table::Row::new(vec![format!("[{}]", i), description.to_string(), project.to_string()]) ); - i += 1; } println!("\n{}", descriptions_and_projects_table);