Skip to content

Commit

Permalink
Add artist plots to artists page
Browse files Browse the repository at this point in the history
  • Loading branch information
fsktom committed Oct 11, 2024
1 parent 7b4e423 commit 11f9ff0
Show file tree
Hide file tree
Showing 10 changed files with 529 additions and 30 deletions.
296 changes: 293 additions & 3 deletions endsong_web/Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions endsong_web/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] }
axum-extra = "0.9"
urlencoding = "2.1"
serde = "1.0"
plotly = { version = "0.10", features = ["with-axum"] }
136 changes: 136 additions & 0 deletions endsong_web/src/artist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use axum::{
response::{IntoResponse, Response},
};
use endsong::prelude::*;
use plotly::{Layout, Plot, Scatter};
use rinja_axum::Template;
use serde::Deserialize;
use tracing::debug;
Expand Down Expand Up @@ -125,3 +126,138 @@ pub async fn base(
}
.into_response()
}

/// GET `/artist/:artist_name(?id=usize)/absolute_plot`
///
/// Has to be in-lined in another base.html-derived template
pub async fn absolute_plot(
State(state): State<Arc<AppState>>,
Path(artist_name): Path<String>,
options: Option<Query<ArtistQuery>>,
) -> Response {
debug!(
artist_name = artist_name,
query = options.is_some(),
"GET /artist/:artist_name(?query)/absolute_plot"
);

let entries = &state.entries;

let Some(artists) = entries.find().artist(&artist_name) else {
return not_found().await.into_response();
};

let artist = if artists.len() == 1 {
artists.first()
} else if let Some(Query(options)) = options {
artists.get(options.id)
} else {
None
};

let Some(artist) = artist else {
// query if multiple artists with different capitalization
return ArtistSelectionTemplate { artists }.into_response();
};

// see endsong_ui::trace::absolute
let mut times = Vec::<String>::with_capacity(entries.len());
let mut plays = Vec::<usize>::with_capacity(entries.len());

// since each date represents a single listen, we can just count up
let mut artist_plays = 0;

for entry in entries.iter().filter(|entry| artist.is_entry(entry)) {
artist_plays += 1;
times.push(entry.timestamp.format("%Y-%m-%d %H:%M").to_string());
plays.push(artist_plays);
}

let trace = Scatter::new(times, plays).name(artist);

let mut plot = Plot::new();
plot.add_trace(trace);

let layout = Layout::new()
.template(plotly::layout::themes::PLOTLY_DARK.clone())
.title(format!("{artist} | absolute plays"));
plot.set_layout(layout);

let plot_html = plot.to_inline_html(Some("artist-absolute-plot"));

axum_extra::response::Html(plot_html).into_response()
}

/// GET `/artist/:artist_name(?id=usize)/relative_plot`
///
/// Has to be in-lined in another base.html-derived template
pub async fn relative_plot(
State(state): State<Arc<AppState>>,
Path(artist_name): Path<String>,
options: Option<Query<ArtistQuery>>,
) -> Response {
debug!(
artist_name = artist_name,
query = options.is_some(),
"GET /artist/:artist_name(?query)/relative_plot"
);

let entries = &state.entries;

let Some(artists) = entries.find().artist(&artist_name) else {
return not_found().await.into_response();
};

let artist = if artists.len() == 1 {
artists.first()
} else if let Some(Query(options)) = options {
artists.get(options.id)
} else {
None
};

let Some(artist) = artist else {
// query if multiple artists with different capitalization
return ArtistSelectionTemplate { artists }.into_response();
};

// see endsong_ui::trace::relative_to_all
let mut times = Vec::<String>::with_capacity(entries.len());
// percentages relative to the sum of all plays
let mut plays = Vec::<f64>::with_capacity(entries.len());

let mut artist_plays = 0.0;
let mut all_plays = 0.0;

// the plot should start at the first time the aspect is played
let mut artist_found = false;

for entry in entries.iter() {
all_plays += 1.0;

if artist.is_entry(entry) {
artist_found = true;
artist_plays += 1.0;
}
if artist_found {
times.push(entry.timestamp.format("%Y-%m-%d %H:%M").to_string());
// *100 so that the percentage is easier to read...
plays.push(100.0 * (artist_plays / all_plays));
}
}

let title = format!("{artist} | relative to all plays");
let trace = Scatter::new(times, plays).name(title);

let mut plot = Plot::new();
plot.add_trace(trace);

let layout = Layout::new()
.template(plotly::layout::themes::PLOTLY_DARK.clone())
.title(format!("{artist} | relative to all plays"));
plot.set_layout(layout);

let plot_html = plot.to_inline_html(Some("artist-relative-plot"));

axum_extra::response::Html(plot_html).into_response()
}
9 changes: 9 additions & 0 deletions endsong_web/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,18 @@ async fn main() {
.route("/", get(index))
.route("/styles.css", get(r#static::styles))
.route("/htmx.js", get(r#static::htmx))
.route("/plotly.js", get(r#static::plotly))
.route("/artists", get(artists::base))
.route("/artists", post(artists::elements))
.route("/artist/:artist_name", get(artist::base))
.route(
"/artist/:artist_name/absolute_plot",
get(artist::absolute_plot),
)
.route(
"/artist/:artist_name/relative_plot",
get(artist::relative_plot),
)
.with_state(state)
.fallback(not_found)
.layer(compression);
Expand Down
34 changes: 27 additions & 7 deletions endsong_web/src/static.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,50 @@
//! Contains routes for static files
use axum::response::IntoResponse;
use axum::{
http::header::CACHE_CONTROL,
response::{AppendHeaders, IntoResponse},
};
use tracing::debug;

/// Tailwind-generated CSS used on this web page
///
/// `npx tailwindcss -i base_style.css -o ../static/tailwind_style.css --watch`
const STYLING: &str = include_str!("../static/tailwind_style.css");

/// HTMX code (<https://htmx.org/docs/#installing>)
/// HTMX code
///
/// <https://htmx.org/docs/#installing>
const HTMX: &str = include_str!("../static/htmx.min.2.0.3.js");

/// plotly.js code
///
/// <https://github.com/plotly/plotly.js#load-via-script-tag>
const PLOTLY: &str = include_str!("../static/plotly-2.35.2.min.js");

/// GET `/styles.css` - CSS
///
/// Idk yet how, but should be cached somehow for the future so that
/// it isn't requested on each load in full? idk
/// it isn't requested on each load in full? idk (but also is invalidated in rapid dev..)
pub async fn styles() -> impl IntoResponse {
debug!("GET /styles.css");

axum_extra::response::Css(STYLING)
}

/// GET `/htmx.js` - HTMX
///
/// Idk yet how, but should be cached somehow for the future so that
/// it isn't requested on each load in full? idk
pub async fn htmx() -> impl IntoResponse {
debug!("GET /htmx.js");

axum_extra::response::JavaScript(HTMX)
let headers = AppendHeaders([(CACHE_CONTROL, "public, max-age=31536000, immutable")]);

(headers, axum_extra::response::JavaScript(HTMX))
}

/// GET `/plotly.js` - plotly.js
pub async fn plotly() -> impl IntoResponse {
debug!("GET /plotly.js");

let headers = AppendHeaders([(CACHE_CONTROL, "public, max-age=31536000, immutable")]);

(headers, axum_extra::response::JavaScript(PLOTLY))
}
8 changes: 8 additions & 0 deletions endsong_web/static/plotly-2.35.2.min.js

Large diffs are not rendered by default.

25 changes: 14 additions & 11 deletions endsong_web/static/tailwind_style.css
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,14 @@ video {
display: none;
}

.absolute {
position: absolute;
}

.relative {
position: relative;
}

.ml-4 {
margin-left: 1rem;
}
Expand All @@ -566,22 +574,22 @@ video {
display: flex;
}

.list-item {
display: list-item;
.w-2\/3 {
width: 66.666667%;
}

.w-full {
width: 100%;
}

.list-none {
list-style-type: none;
}

.list-disc {
list-style-type: disc;
}

.list-none {
list-style-type: none;
}

.flex-col {
flex-direction: column;
}
Expand Down Expand Up @@ -761,9 +769,4 @@ https://www.joshwcomeau.com/css/interactive-guide-to-flexbox/
--tw-shadow-colored: 0 0 #0000;
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}

.dark\:shadow-white {
--tw-shadow-color: #fff;
--tw-shadow: var(--tw-shadow-colored);
}
}
39 changes: 39 additions & 0 deletions endsong_web/templates/artist.html
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,43 @@ <h2 class="self-center text-xl font-semibold">General info</h2>
</ul>
</article>
</section>
<section class="flex w-2/3 flex-col gap-4">
<article class="flex flex-col">
<button
id="absolute-plot-button"
class="self-center"
type="submit"
hx-target="#absolute-plot"
hx-get="/artist/{{ artist.name }}/absolute_plot"
>
Show absolute plot
</button>
<figure id="absolute-plot"></figure>
</article>
<article class="flex flex-col">
<button
id="relative-plot-button"
class="self-center"
type="submit"
hx-target="#relative-plot"
hx-get="/artist/{{ artist.name }}/relative_plot"
>
Show relative plot
</button>
<figure id="relative-plot"></figure>
</article>
</section>
<script>
// hide "show x plot" buttons on click
document
.getElementById("absolute-plot-button")
.addEventListener("click", function () {
this.style.display = "none";
});
document
.getElementById("relative-plot-button")
.addEventListener("click", function () {
this.style.display = "none";
});
</script>
{% endblock %}
1 change: 1 addition & 0 deletions endsong_web/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<title>{% block title %}Endsong Web{% endblock %}- Endsong Web</title>
<link rel="stylesheet" href="/styles.css" />
<script src="/htmx.js"></script>
<script src="/plotly.js" charset="utf-8"></script>
</head>
<body
class="flex flex-col items-center justify-evenly gap-4 bg-slate-100 dark:bg-slate-800 dark:text-white"
Expand Down
10 changes: 1 addition & 9 deletions endsong_web/templates/tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,7 @@

/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./base.html",
"./index.html",
"./404.html",
"./artists.html",
"./artists_search.html",
"./artist.html",
"./artist_selection.html",
],
content: ["./*.html"],
theme: {
extend: {},
},
Expand Down

0 comments on commit 11f9ff0

Please sign in to comment.