alpha version of the archive-by-hours feature finished

This commit is contained in:
Abraham Toriz 2022-08-01 21:58:56 +08:00
parent 3c2e5acbc1
commit 6889945e1e
No known key found for this signature in database
GPG Key ID: D5B4A746DB5DD42A
1 changed files with 103 additions and 6 deletions

View File

@ -126,7 +126,17 @@ impl<'a> Command<'a> for ArchiveCommand {
}
}
(text::format_hours(accumulated_time), selected_entries, new, String::from("\nAdditionally an entry will be split so that the total archived time is exact."))
let msg = if new.is_some() {
String::from("\nAdditionally an entry will be split so that the total archived time is exact.")
} else if accumulated_time < requested_time {
let requested_time_str = text::format_hours(requested_time);
let missing_time_str = text::format_hours(requested_time - accumulated_time);
format!("\nThere were not enough entries to fulfill the requested time of {requested_time_str} (difference: {missing_time_str}).")
} else {
String::new()
};
(text::format_hours(accumulated_time), selected_entries, new, msg)
} else {
(text::format_hours(
entries
@ -139,17 +149,24 @@ impl<'a> Command<'a> for ArchiveCommand {
};
let n = entries.len();
let n_entries = if n == 1 {
String::from("1 entry")
} else {
format!("{n} entries")
};
if args.fake {
let (entries, _) = entries_or_warning(entries, &streams.db)?;
writeln!(streams.out, "These entries would be archived:\n")?;
text::print_formatted(
entries,
&mut streams.out,
facts,
true,
)?;
} else if ask(streams, &format!("A total of {n} entries accounting for {time} will be archived.{extra_msg}\nProceed?"))? {
} else if ask(streams, &format!("A total of {n_entries} accounting for {time} will be archived.{extra_msg}\nProceed?"))? {
for entry in entries {
streams.db.entry_update(entry.id, entry.start, entry.end, entry.note, &format!("_{}", entry.sheet))?;
}
@ -228,7 +245,7 @@ mod tests {
let remaining = streams.db.entries_by_sheet("foo", None, None).unwrap();
let archived = streams.db.entries_by_sheet("_foo", None, None).unwrap();
assert_eq!(String::from_utf8_lossy(&streams.out), "A total of 1 entries accounting for 1h 30m will be archived.\nProceed? [y/N] ");
assert_eq!(String::from_utf8_lossy(&streams.out), "A total of 1 entry accounting for 1h 30m will be archived.\nProceed? [y/N] ");
// First entry gets archived whole, second entry gets split in two:
// - a 30 minute piece to complete the requested 2 hour span
@ -332,12 +349,92 @@ Proceed? [y/N] ");
#[test]
fn not_enough_entries_to_archive_time() {
assert!(false, "Like above, but there are no enough entries and the message should be accurate");
let args = Args {
hours: Some(2),
..Default::default()
};
let mut streams = Streams::fake(b"y\n");
let facts = Facts::new();
streams.db.set_current_sheet("foo").unwrap();
streams.db.entry_insert(facts.now - Duration::hours(3), Some(facts.now - Duration::minutes(90)), Some("first".into()), "foo").unwrap();
ArchiveCommand::handle(args, &mut streams, &facts).unwrap();
let remaining = streams.db.entries_by_sheet("foo", None, None).unwrap();
let archived = streams.db.entries_by_sheet("_foo", None, None).unwrap();
assert_str_eq!(String::from_utf8_lossy(&streams.out), "A total of 1 entry accounting for 1h 30m will be archived.
There were not enough entries to fulfill the requested time of 2h (difference: 30m).
Proceed? [y/N] ");
// First entry gets archived whole, second entry gets split in two:
// - a 30 minute piece to complete the requested 2 hour span
// - a 1 hour piece that remains unarchived
assert_eq!(archived, vec![
Entry {
id: 1,
note: Some("first".into()),
start: facts.now - Duration::hours(3),
end: Some(facts.now - Duration::minutes(90)),
sheet: "_foo".into(),
},
]);
assert_eq!(remaining, vec![]);
}
#[test]
fn fake_and_split_work_well_together() {
assert!(false, "When using --fake expect to have the same output but the database must not be touched");
assert!(false, "Also a nice explanation of what would happen is shown");
let args = Args {
hours: Some(2),
fake: true,
..Default::default()
};
let mut streams = Streams::fake(b"y\n");
let facts = Facts::new();
let time_a = Utc.ymd(2022, 8, 1).and_hms(10, 0, 0);
let time_b = time_a + Duration::minutes(90);
let time_d = time_a + Duration::hours(3);
streams.db.set_current_sheet("foo").unwrap();
streams.db.entry_insert(time_a, Some(time_b), Some("first".into()), "foo").unwrap();
streams.db.entry_insert(time_b, Some(time_d), Some("second".into()), "foo").unwrap();
ArchiveCommand::handle(args, &mut streams, &facts).unwrap();
let remaining = streams.db.entries_by_sheet("foo", None, None).unwrap();
let archived = streams.db.entries_by_sheet("_foo", None, None).unwrap();
assert_str_eq!(String::from_utf8_lossy(&streams.out), "These entries would be archived:
Timesheet: foo
ID Day Start End Duration Notes
1 Mon Aug 01, 2022 18:00:00 - 19:30:00 1:30:00 first
2 19:30:00 - 20:00:00 0:30:00 second
2:00:00
------------------------------------------------------------
Total 2:00:00
");
// First entry gets archived whole, second entry gets split in two:
// - a 30 minute piece to complete the requested 2 hour span
// - a 1 hour piece that remains unarchived
assert_eq!(archived, vec![]);
assert_eq!(remaining, vec![
Entry {
id: 1,
note: Some("first".into()),
start: time_a,
end: Some(time_b),
sheet: "foo".into(),
},
Entry {
id: 2,
note: Some("second".into()),
start: time_b,
end: Some(time_d),
sheet: "foo".into(),
},
]);
}
}