From e8b2d4f7eefc90a1ca4cc14c57aa7faf5b05f437 Mon Sep 17 00:00:00 2001 From: Gabriel Kaszewski Date: Tue, 2 Jun 2026 21:40:43 +0200 Subject: [PATCH] feat(domain): add WrapUpReport model and supporting types --- crates/domain/src/models/mod.rs | 2 + crates/domain/src/models/wrapup.rs | 119 +++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 crates/domain/src/models/wrapup.rs diff --git a/crates/domain/src/models/mod.rs b/crates/domain/src/models/mod.rs index 6eb3da1..67f6fd2 100644 --- a/crates/domain/src/models/mod.rs +++ b/crates/domain/src/models/mod.rs @@ -19,6 +19,8 @@ pub use watchlist::{WatchlistEntry, WatchlistWithMovie}; pub mod remote_watchlist; pub use remote_watchlist::RemoteWatchlistEntry; pub mod watch_event; +pub mod wrapup; +pub use wrapup::*; pub use watch_event::{ ParsedPlaybackEvent, PersistedWatchEvent, WatchEvent, WatchEventSource, WatchEventStatus, WebhookToken, diff --git a/crates/domain/src/models/wrapup.rs b/crates/domain/src/models/wrapup.rs new file mode 100644 index 0000000..ad304d2 --- /dev/null +++ b/crates/domain/src/models/wrapup.rs @@ -0,0 +1,119 @@ +use chrono::NaiveDate; +use uuid::Uuid; + +#[derive(Clone, Debug)] +pub struct DateRange { + pub start: NaiveDate, + pub end: NaiveDate, +} + +#[derive(Clone, Debug)] +pub struct MovieRef { + pub title: String, + pub year: u16, + pub runtime_minutes: Option, + pub poster_path: Option, +} + +#[derive(Clone, Debug)] +pub struct UserRef { + pub user_id: Uuid, + pub display_name: String, +} + +#[derive(Clone, Debug)] +pub struct PersonStat { + pub name: String, + pub count: u32, + pub avg_rating: f64, +} + +#[derive(Clone, Debug)] +pub struct GenreStat { + pub genre: String, + pub count: u32, + pub avg_rating: f64, +} + +#[derive(Clone, Debug)] +pub struct KeywordStat { + pub keyword: String, + pub count: u32, +} + +#[derive(Clone, Debug)] +pub struct LangStat { + pub language: String, + pub count: u32, +} + +#[derive(Clone, Debug)] +pub struct MonthCount { + pub year_month: String, + pub label: String, + pub count: u32, +} + +#[derive(Clone, Debug)] +pub enum WrapUpScope { + User(Uuid), + Global, +} + +#[derive(Clone, Debug)] +pub struct WrapUpReport { + pub scope: WrapUpScope, + pub date_range: DateRange, + pub generated_at: chrono::DateTime, + + // Core viewing + pub total_movies: u32, + pub total_watch_time_minutes: u32, + pub movies_per_month: Vec, + pub busiest_month: Option, + pub busiest_day_of_week: Option, + pub avg_rating: Option, + pub rating_distribution: [u32; 5], + pub longest_movie: Option, + pub shortest_movie: Option, + + // People insights + pub top_directors: Vec, + pub top_actors: Vec, + pub director_diversity: u32, + pub actor_diversity: u32, + + // Genre & taste + pub top_genres: Vec, + pub genre_diversity: u32, + pub highest_rated_genre: Option, + pub lowest_rated_genre: Option, + pub top_keywords: Vec, + + // Financial/production (None when data unavailable) + pub total_budget_watched: Option, + pub avg_budget: Option, + pub language_distribution: Vec, + pub oldest_movie: Option, + pub newest_movie: Option, + + // Rewatch stats + pub total_rewatches: u32, + pub most_rewatched_movie: Option, + pub avg_rating_change_on_rewatch: Option, + + // Highlights + pub highest_rated_movie: Option, + pub lowest_rated_movie: Option, + pub first_movie_of_period: Option, + pub last_movie_of_period: Option, + + // Global-only (None for per-user) + pub most_active_user: Option, + pub most_watched_movie_global: Option, + pub total_users_active: Option, + + // Asset references for renderers + pub poster_paths: Vec, + pub top_cast_profile_paths: Vec, +}