From 9c40b2522b0bd34a2c0798fc8842c70cdcbe7460 Mon Sep 17 00:00:00 2001 From: Koen Bolhuis Date: Wed, 13 Jan 2021 13:06:48 +0100 Subject: [PATCH] Implement StatsUserListeningActivity and StatsUserDailyActivity --- src/client.rs | 38 ++++++++++++++++++++++++--- src/endpoint.rs | 8 +++--- src/models/response.rs | 58 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 97 insertions(+), 7 deletions(-) diff --git a/src/client.rs b/src/client.rs index 6764618..9837c4c 100644 --- a/src/client.rs +++ b/src/client.rs @@ -10,7 +10,7 @@ use crate::models::response::*; const API_ROOT_URL: &str = "https://api.listenbrainz.org/1/"; -/// Low-level client that more-or-less directly wraps the ListenBrainz HTTP API. +/// Low-level client that directly wraps the ListenBrainz HTTP API. /// /// Client exposes functions that map one-to-one to the API methods described /// in the [ListenBrainz API docs](https://listenbrainz.readthedocs.io/en/production/dev/api/). @@ -175,9 +175,41 @@ impl Client { request.call()?.into_json().map_err(Error::ResponseJson) } - // /// Endpoint: `stats/user/{user_name}/listening-activity` + /// Endpoint: `stats/user/{user_name}/listening-activity` + pub fn stats_user_listening_activity( + &mut self, + user_name: &str, + range: Option<&str>, + ) -> Result { + let endpoint = format!("{}{}", API_ROOT_URL, + Endpoint::StatsUserListeningActivity(user_name)); - // /// Endpoint: `stats/user/{user_name}/daily-activity` + let mut request = self.agent.get(&endpoint); + + if let Some(range) = range { + request = request.query("range", range); + } + + request.call()?.into_json().map_err(Error::ResponseJson) + } + + /// Endpoint: `stats/user/{user_name}/daily-activity` + pub fn stats_user_daily_activity( + &mut self, + user_name: &str, + range: Option<&str>, + ) -> Result { + let endpoint = format!("{}{}", API_ROOT_URL, + Endpoint::StatsUserDailyActivity(user_name)); + + let mut request = self.agent.get(&endpoint); + + if let Some(range) = range { + request = request.query("range", range); + } + + request.call()?.into_json().map_err(Error::ResponseJson) + } // /// Endpoint: `stats/user/{user_name}/recordings` diff --git a/src/endpoint.rs b/src/endpoint.rs index 8d825c4..e82110b 100644 --- a/src/endpoint.rs +++ b/src/endpoint.rs @@ -10,8 +10,8 @@ pub enum Endpoint<'a> { UserListens(&'a str), LatestImport, StatsSitewideArtists, - // StatsUserListeningActivity(&'a str), - // StatsUserDailyActivity(&'a str), + StatsUserListeningActivity(&'a str), + StatsUserDailyActivity(&'a str), // StatsUserRecordings(&'a str), // StatsUserArtistMap(&'a str), // StatsUserReleases(&'a str), @@ -34,8 +34,8 @@ impl<'a> fmt::Display for Endpoint<'a> { Self::UserListens(user) => return write!(f, "user/{}/listens", user), Self::LatestImport => "latest-import", Self::StatsSitewideArtists => "stats/sitewide/artists", - // Self::StatsUserListeningActivity(user) => return write!(f, "stats/user/{}/listening-activity", user), - // Self::StatsUserDailyActivity(user) => return write!(f, "stats/user/{}/daily-activity", user), + Self::StatsUserListeningActivity(user) => return write!(f, "stats/user/{}/listening-activity", user), + Self::StatsUserDailyActivity(user) => return write!(f, "stats/user/{}/daily-activity", user), // Self::StatsUserRecordings(user) => return write!(f, "stats/user/{}/recordings", user), // Self::StatsUserArtistMap(user) => return write!(f, "stats/user/{}/artist-map", user), // Self::StatsUserReleases(user) => return write!(f, "stats/user/{}/releases", user), diff --git a/src/models/response.rs b/src/models/response.rs index b4ec79d..5149055 100644 --- a/src/models/response.rs +++ b/src/models/response.rs @@ -214,6 +214,64 @@ pub struct StatsSitewideArtistsArtist { pub listen_count: u64, } +// --------- stats/user/{user_name}/listening-activity + +/// Response type for [`Client::stats_user_listening_activity`](crate::Client::stats_user_listening_activity). +#[derive(Debug, Deserialize)] +pub struct StatsUserListeningActivityResponse { + pub payload: StatsUserListeningActivityPayload, +} + +/// Type of the [`StatsUserListeningActivityResponse::payload`] field. +#[derive(Debug, Deserialize)] +pub struct StatsUserListeningActivityPayload { + pub user_id: String, + pub listening_activity: Vec, + pub from_ts: i64, + pub to_ts: i64, + pub last_updated: i64, +} + +/// Type of the [`StatsUserListeningActivityPayload::listening_activity`] field. +#[derive(Debug, Deserialize)] +pub struct StatsUserListeningActivityListeningActivity { + pub listen_count: u64, + pub from_ts: i64, + pub to_ts: i64, + pub time_range: String, +} + +// --------- stats/user/{user_name}/daily-activity + +/// Response type for [`Client::stats_user_daily_activity`](crate::Client::stats_user_daily_activity). +#[derive(Debug, Deserialize)] +pub struct StatsUserDailyActivityResponse { + pub payload: StatsUserDailyActivityPayload, +} + +/// Type of the [`StatsUserDailyActivityResponse::payload`] field. +#[derive(Debug, Deserialize)] +pub struct StatsUserDailyActivityPayload { + pub user_id: String, + pub daily_activity: StatsUserDailyActivityDailyActivity, + pub from_ts: i64, + pub to_ts: i64, + pub stats_range: String, +} + +/// Type of the [`StatsUserDailyActivityPayload::daily_activity`] field. +#[derive(Debug, Deserialize)] +pub struct StatsUserDailyActivityDailyActivity { + pub days: HashMap>, +} + +/// Type of the [`StatsUserDailyActivityDailyActivity::days`] field. +#[derive(Debug, Deserialize)] +pub struct StatsUserDailyActivityHour { + pub hour: u8, + pub listen_count: u64, +} + // --------- status/get-dump-info /// Response type for [`Client::status_get_dump_info`](crate::Client::status_get_dump_info).