Skip to content

Commit

Permalink
#73 create barebones artist HTML summary functionality
Browse files Browse the repository at this point in the history
Only works on Mac
With this, the repo will no longer be 100% Rust according to GitHub :(
  • Loading branch information
fsktom committed Sep 12, 2024
1 parent a2301a6 commit 6960b94
Show file tree
Hide file tree
Showing 10 changed files with 201 additions and 37 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@ ex*.txt

# folder with plots generated at runtime
endsong_ui/plots/

# folder with HTML pages generated at runtime
endsong_ui/summaries/
20 changes: 10 additions & 10 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

45 changes: 23 additions & 22 deletions endsong_ui/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions endsong_ui/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ textwrap = "0.16"
thiserror = "1.0"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
endsong_macros = { path = "endsong_macros"}
rinja = "0.3"

[dev-dependencies]
criterion = "0.5"
Expand Down
4 changes: 4 additions & 0 deletions endsong_ui/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

pub mod plot;
pub mod print;
pub mod summarize;
pub mod trace;
pub mod ui;

Expand All @@ -46,10 +47,13 @@ pub const fn spaces(num: usize) -> &'static str {
pub mod prelude {
pub use crate::plot;
pub use crate::print;
pub use crate::summarize;
pub use crate::trace;
pub use crate::ui;

pub use print::Aspect;
pub use print::DurationUtils;
pub use print::Mode;

pub use trace::TraceType;
}
91 changes: 91 additions & 0 deletions endsong_ui/src/summarize.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
//! Generates HTML page summaries of aspects
use std::collections::HashMap;
use std::rc::Rc;

use endsong::prelude::*;
use itertools::Itertools;
use rinja::Template;

/// Used for generating an HTML summary page of an [`Artist`]
#[derive(Template)]
#[template(path = "artist.html", print = "none")]
struct ArtistSummary {
/// artist name
name: Rc<str>,
/// number of top songs/albums to be displayed
top: usize,
/// array of top song names with their playcount
songs: Vec<(Rc<str>, usize)>,
/// array of top album names with their playcount
albums: Vec<(Rc<str>, usize)>,
/// number of this artist's plays
plays: usize,
/// % of total plays
percentage_of_plays: String,
/// Date of first listen
first_listen: DateTime<Local>,
/// Date of last listen
last_listen: DateTime<Local>,
}

/// Generates an HTML summary page of an [`Artist`]
#[expect(clippy::missing_panics_doc, reason = "placeholder")]
#[expect(clippy::cast_precision_loss, reason = "necessary for %")]
pub fn artist(entries: &SongEntries, artist: &Artist) {
let top = 10;

let song_map = gather::songs_from_artist_summed_across_albums(entries, artist);
let songs = get_sorted_playcount_list(song_map, top);

let album_map = gather::albums_from_artist(entries, artist);
let albums = get_sorted_playcount_list(album_map, top);

let plays = gather::plays(entries, artist);
let percentage_of_plays = format!("{:.2}", (plays as f64 / entries.len() as f64) * 100.0);

let first_listen = entries
.iter()
.find(|entry| artist.is_entry(entry))
.unwrap()
.timestamp;
let last_listen = entries
.iter()
.rev()
.find(|entry| artist.is_entry(entry))
.unwrap()
.timestamp;

let page = ArtistSummary {
name: std::rc::Rc::clone(&artist.name),
top,
songs,
albums,
plays,
percentage_of_plays,
first_listen,
last_listen,
};
std::fs::create_dir_all("summaries").unwrap();
let path = format!("summaries/{} summary.html", artist.name);
std::fs::write(&path, page.render().unwrap()).unwrap();
std::process::Command::new("open")
.arg(&path)
.output()
.unwrap();
}

/// Makes a list of aspects with their total playcount sorted by their
/// playcount descending and then alphabetically
///
/// Use with maps gotten through [`gather`] functions
fn get_sorted_playcount_list<Asp: Music>(
map: HashMap<Asp, usize>,
top: usize,
) -> Vec<(Rc<str>, usize)> {
map.into_iter()
.sorted_unstable_by(|a, b| b.1.cmp(&a.1).then_with(|| a.0.cmp(&b.0)))
.take(top)
.map(|(asp, plays)| (asp.name(), plays))
.collect()
}
12 changes: 12 additions & 0 deletions endsong_ui/src/ui/help.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ pub fn help() {

// GRAPH COMMANDS
print("graph/plot", plot_commands());

// SUMMARIZE COMMANDS
print("summarize", summarize_commands());
}

/// Prints the commands
Expand Down Expand Up @@ -205,3 +208,12 @@ const fn plot_commands() -> &'static [Command] {
),
]
}

/// Returns summarize commands
const fn summarize_commands() -> &'static [Command] {
&[Command(
"summarize artist",
"sa",
"creates an HTML summary page of an artist and opens it in the browser",
)]
}
24 changes: 19 additions & 5 deletions endsong_ui/src/ui/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,7 @@ use rustyline::{
};
use thiserror::Error;

use crate::plot;
use crate::print;
use crate::trace;
use print::Aspect;
use trace::TraceType;
use crate::prelude::*;

/// Prompt used for top-level shell commands
///
Expand Down Expand Up @@ -118,6 +114,7 @@ impl ShellHelper {
"plot artist songs rel",
"plot album songs",
"plot album songs rel",
"summarize artist",
]);
}

Expand Down Expand Up @@ -327,6 +324,7 @@ fn match_input(
"plot artist songs rel" | "gartr" => match_plot_artist_songs_relative(entries, rl)?,
"plot album songs" | "galbs" => match_plot_album_songs(entries, rl)?,
"plot album songs rel" | "galbsr" => match_plot_album_songs_relative(entries, rl)?,
"summrize artist" | "sa" => match_summarize_artist(entries, rl)?,
// when you press ENTER -> nothing happens, new prompt
"" => (),
_ => {
Expand Down Expand Up @@ -1286,6 +1284,22 @@ fn match_plot_song_relative(
}
}

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

// prompt: artist
let art = read_artist(rl, entries)?;

summarize::artist(entries, &art);

Ok(())
}

/// Used by `*_date` functions for reading start and end dates from user
///
/// Returns `(start_date, end_date)`
Expand Down
Loading

0 comments on commit 6960b94

Please sign in to comment.