use std::cmp::PartialEq;
use std::fmt::{Display, Formatter};
use anyhow::Result;
use derive_builder::Builder;
use jiff::tz::TimeZone;
use jiff::{Timestamp, Zoned};
use serde::{Deserialize, Serialize};
use crate::db;
use crate::profiles::profile_section::ProfileSection;
use crate::profiles::ProfileSource;
use crate::types::plex::plex_id::PlexId;
use crate::types::profiles::profile_source_id::ProfileSourceId;
use crate::types::profiles::refresh_interval::RefreshInterval;
use crate::types::Title;
#[derive(Builder, Clone, Debug, Default, Deserialize, Serialize, PartialEq, sqlx::FromRow)]
#[builder(default)]
pub struct Profile {
profile_id: i32,
playlist_id: PlexId,
title: Title,
summary: String,
#[builder(default = "true")]
enabled: bool,
profile_source: ProfileSource,
profile_source_id: Option<ProfileSourceId>,
refresh_interval: RefreshInterval,
time_limit: u32,
track_limit: u32,
#[builder(default)]
num_sections: u32,
#[builder(default)]
section_time_limit: f64,
#[builder(default)]
refreshes_per_hour: u32,
#[builder(default)]
current_refresh: i64,
#[builder(default)]
next_refresh_at: i64,
#[builder(default)]
eligible_for_refresh: bool,
}
impl AsRef<Profile> for Profile {
fn as_ref(&self) -> &Profile {
self
}
}
impl Profile {
pub fn get_profile_id(&self) -> i32 {
self.profile_id
}
pub fn get_playlist_id(&self) -> &PlexId {
&self.playlist_id
}
pub fn set_playlist_id(&mut self, playlist_id: PlexId) {
self.playlist_id = playlist_id
}
pub fn get_title(&self) -> &str {
&self.title
}
pub fn get_summary(&self) -> &str {
&self.summary
}
pub fn get_enabled(&self) -> bool {
self.enabled
}
pub fn get_profile_source(&self) -> &ProfileSource {
&self.profile_source
}
pub fn get_profile_source_id(&self) -> Option<&ProfileSourceId> {
self.profile_source_id.as_ref()
}
pub fn get_profile_source_id_str(&self) -> Option<&str> {
if let Some(id) = &self.profile_source_id {
Some(id.as_ref())
} else {
None
}
}
pub async fn fetch_sections(&self) -> Result<Vec<ProfileSection>> {
let sections = db::profiles::fetch_profile_sections_for_profile(self.profile_id).await?;
Ok(sections)
}
pub fn get_refresh_interval(&self) -> &u32 {
self.refresh_interval.as_ref()
}
pub fn get_time_limit(&self) -> u32 {
if self.time_limit == 0 {
365 * 24
} else {
self.time_limit
}
}
pub fn get_track_limit(&self) -> u32 {
self.track_limit
}
pub fn get_section_time_limit(&self) -> f64 {
self.section_time_limit
}
pub fn get_refreshes_per_hour(&self) -> u32 {
self.refreshes_per_hour
}
pub fn check_for_refresh(&self, force_refresh: bool) -> bool {
if force_refresh {
return true;
}
self.eligible_for_refresh
}
pub fn get_next_refresh_hour_minute(&self) -> String {
Timestamp::from_second(self.next_refresh_at)
.unwrap()
.to_zoned(TimeZone::system())
.strftime("%H:%M")
.to_string()
}
pub fn get_next_refresh_str(&self) -> String {
let now = Zoned::now().strftime("%F %T").to_string();
format!(
"LAST UPDATE: {now}\nNEXT UPDATE: {}",
self.get_next_refresh_hour_minute()
)
}
pub fn get_profile_source_and_id(&self) -> (&ProfileSource, Option<&ProfileSourceId>) {
(self.get_profile_source(), self.get_profile_source_id())
}
fn refresh_interval_str(&self) -> String {
format!(
"Every {} minutes ({} refreshes per hour)",
self.refresh_interval, self.refreshes_per_hour
)
}
fn time_limit_str(&self) -> String {
if self.time_limit == 0 {
"No Limit".to_string()
} else {
format!("{} hours", self.time_limit)
}
}
fn get_track_limit_str(&self) -> String {
if self.track_limit == 0 {
"No Limit".to_string()
} else {
format!("{} tracks", self.track_limit)
}
}
}
impl Display for Profile {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let mut str = format!("\n{}", self.title);
str += &format!("\n{}", self.summary);
str += &format!("\nEnabled: {}", self.enabled);
str += &format!("\nSource: {}", self.profile_source);
str += &format!("\nRefresh Interval: {}", self.refresh_interval_str());
str += &format!("\nTime Limit: {}", self.time_limit_str());
str += &format!("\nTrack Limit: {}", self.get_track_limit_str());
str += "\n\nSections:";
write!(f, "{str}")
}
}