Skip to content

Commit

Permalink
#45 implement printing all listens for a day
Browse files Browse the repository at this point in the history
  • Loading branch information
fsktom committed Sep 8, 2024
1 parent 4381bb0 commit ad12c3c
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 8 deletions.
4 changes: 2 additions & 2 deletions endsong_ui/src/plot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,10 @@ fn write_and_open_plot(plot: &Plot, title: &str) {
};
}

/// Replaces Windows forbidden symbols in path with a '_'
/// Replaces Windows forbidden symbols in path with an '_'
///
/// Also removes whitespace and replaces empty
/// strings with "_"
/// strings with '_'
fn normalize_path(path: &str) -> String {
// https://stackoverflow.com/a/31976060
// Array > HashSet bc of overhead
Expand Down
83 changes: 83 additions & 0 deletions endsong_ui/src/print.rs
Original file line number Diff line number Diff line change
Expand Up @@ -404,3 +404,86 @@ fn normalize_dates<'a>(

(start, end)
}

/// Prints each [`SongEntry`] on a specific day
pub fn day(entries: &[SongEntry], date: DateTime<Local>) {
let day = date.date_naive();
let day_entries = entries.iter().filter(|e| e.timestamp.date_naive() == day);

let length = day_entries.clone().count();
if length == 0 {
println!("You haven't listened to any songs on {day}!");
return;
}
let time_played = pretty_duration(day_entries.clone().map(|e| e.time_played).sum());
let sng = if length == 1 { "song" } else { "songs" };
println!("You've listened to {length} {sng} on {day} for {time_played}!");

for entry in day_entries {
println!(
"{}: {} - {} ({}) for {}s",
entry.timestamp.time(),
entry.artist,
entry.track,
entry.album,
entry.time_played.num_seconds()
);
}
}

/// Formats [`TimeDelta`] in a convenient way
///
/// I.e. not include hours when only minutes necessary etc.
fn pretty_duration(duration: TimeDelta) -> String {
let hours = duration.num_hours();
let minutes = duration.num_minutes();

if minutes == 0 || minutes == 1 {
return format!("{} seconds", duration.num_seconds());
}

if hours > 0 {
let hrs = if hours == 1 { "hour" } else { "hours" };
let remaining_minutes = minutes % (60 * hours);
// yes, I know ignoring 1min :)
if remaining_minutes == 0 || remaining_minutes == 1 {
return format!("{hours} {hrs}");
}
return format!("{hours} {hrs} and {remaining_minutes} minutes");
}

format!("{minutes} minutes")
}

#[cfg(test)]
mod tests {
use super::*;

/// Tests [`pretty_duration`]
#[test]
fn pretty_dur() {
let thirty_secs = TimeDelta::seconds(30);
assert_eq!(pretty_duration(thirty_secs), "30 seconds");

let one_half_min = TimeDelta::seconds(90);
assert_eq!(pretty_duration(one_half_min), "90 seconds");

let two_mins = TimeDelta::minutes(2);
assert_eq!(pretty_duration(two_mins), "2 minutes");

let fifty_nine_mins = TimeDelta::minutes(59);
assert_eq!(pretty_duration(fifty_nine_mins), "59 minutes");

let one_hour = TimeDelta::hours(1);
assert_eq!(pretty_duration(one_hour), "1 hour");

let one_half_hours = TimeDelta::minutes(90);
assert_eq!(pretty_duration(one_half_hours), "1 hour and 30 minutes");

let three_half_hours = TimeDelta::minutes(3 * 60 + 30);
assert_eq!(pretty_duration(three_half_hours), "3 hours and 30 minutes");

let five_days = TimeDelta::days(5);
assert_eq!(pretty_duration(five_days), "120 hours");
}
}
5 changes: 5 additions & 0 deletions endsong_ui/src/ui/help.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,11 @@ const fn print_commands() -> &'static [Command] {
"psonsd",
"prints a song with all the albums it may be from within a date range",
),
Command(
"print day",
"pd",
"prints all song entries on a specific day",
),
]
}

Expand Down
20 changes: 20 additions & 0 deletions endsong_ui/src/ui/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ impl ShellHelper {
"print songs date",
"print top artists",
"print top songs",
"print day",
"plot",
"plot rel",
"plot compare",
Expand Down Expand Up @@ -314,6 +315,7 @@ fn match_input(
"print top artists" | "ptarts" => match_print_top(entries, rl, Aspect::Artists)?,
"print top albums" | "ptalbs" => match_print_top(entries, rl, Aspect::Albums)?,
"print top songs" | "ptsons" => match_print_top(entries, rl, Aspect::Songs(false))?,
"print day" | "pd" => match_print_day(entries, rl)?,
"plot" | "g" => match_plot(entries, rl)?,
"plot rel" | "gr" => match_plot_relative(entries, rl)?,
"plot compare" | "gc" => match_plot_compare(entries, rl)?,
Expand Down Expand Up @@ -577,6 +579,24 @@ fn match_print_top(
Ok(())
}

/// Used by [`match_input()`] for `print day` command
fn match_print_day(
entries: &SongEntries,
rl: &mut Editor<ShellHelper, FileHistory>,
) -> Result<(), UiError> {
// make sure no wrong autocompletes appear
rl.helper_mut().unwrap().reset();

// 1st prompt: start date
println!("What day's data do you want to see? YYYY-MM-DD");
let usr_input_date = rl.readline(PROMPT_SECONDARY)?;
let date = parse_date(&usr_input_date)?;

print::day(entries, date);

Ok(())
}

/// Used by [`match_input()`] for `plot` command
fn match_plot(
entries: &SongEntries,
Expand Down
7 changes: 1 addition & 6 deletions src/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -371,11 +371,6 @@ impl SongEntries {
///
/// Minimum duration is 1 day and maximum duration is the whole dataset, so
/// a check is performed and the timespan is adjusted accordingly
///
/// # Panics
///
/// Unwraps used on [`TimeDelta::try_days`], but won't panic since
/// only duration of 1 day created
#[must_use]
pub fn max_listening_time(
&self,
Expand All @@ -384,7 +379,7 @@ impl SongEntries {
let first = self.first_date();
let last = self.last_date();

let one_day = TimeDelta::try_days(1).unwrap();
let one_day = TimeDelta::days(1);

let actual_time_span = match time_span {
// maximum duration is whole dataset?
Expand Down

0 comments on commit ad12c3c

Please sign in to comment.