feat(domain): add WrapUpStatsQuery port and in-memory fake
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, NaiveDateTime, Utc};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::DomainError,
|
errors::DomainError,
|
||||||
@@ -12,6 +13,7 @@ use crate::{
|
|||||||
ReviewHistory, SearchQuery, SearchResults, User, UserStats, UserSummary, UserTrends,
|
ReviewHistory, SearchQuery, SearchResults, User, UserStats, UserSummary, UserTrends,
|
||||||
WatchEvent, WatchEventStatus, WatchlistEntry, WatchlistWithMovie, WebhookToken,
|
WatchEvent, WatchEventStatus, WatchlistEntry, WatchlistWithMovie, WebhookToken,
|
||||||
collections::{self, PageParams, Paginated},
|
collections::{self, PageParams, Paginated},
|
||||||
|
wrapup::{DateRange, WrapUpScope},
|
||||||
},
|
},
|
||||||
value_objects::{
|
value_objects::{
|
||||||
Email, ExternalMetadataId, ImportProfileId, ImportSessionId, MovieId, MovieTitle,
|
Email, ExternalMetadataId, ImportProfileId, ImportSessionId, MovieId, MovieTitle,
|
||||||
@@ -468,3 +470,33 @@ pub trait WebhookTokenRepository: Send + Sync {
|
|||||||
async fn delete(&self, id: &WebhookTokenId, user_id: &UserId) -> Result<(), DomainError>;
|
async fn delete(&self, id: &WebhookTokenId, user_id: &UserId) -> Result<(), DomainError>;
|
||||||
async fn touch_last_used(&self, id: &WebhookTokenId) -> Result<(), DomainError>;
|
async fn touch_last_used(&self, id: &WebhookTokenId) -> Result<(), DomainError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Wrap-up / Year-in-Review ─────────────────────────────────────────────────
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct WrapUpMovieRow {
|
||||||
|
pub movie_id: Uuid,
|
||||||
|
pub title: String,
|
||||||
|
pub release_year: u16,
|
||||||
|
pub director: Option<String>,
|
||||||
|
pub poster_path: Option<String>,
|
||||||
|
pub rating: u8,
|
||||||
|
pub watched_at: NaiveDateTime,
|
||||||
|
pub user_id: Uuid,
|
||||||
|
pub runtime_minutes: Option<u32>,
|
||||||
|
pub budget_usd: Option<i64>,
|
||||||
|
pub original_language: Option<String>,
|
||||||
|
pub genres: Vec<String>,
|
||||||
|
pub keywords: Vec<String>,
|
||||||
|
pub cast_names: Vec<(String, u32)>,
|
||||||
|
pub cast_profile_paths: Vec<Option<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait WrapUpStatsQuery: Send + Sync {
|
||||||
|
async fn get_reviews_with_profiles(
|
||||||
|
&self,
|
||||||
|
scope: &WrapUpScope,
|
||||||
|
range: &DateRange,
|
||||||
|
) -> Result<Vec<WrapUpMovieRow>, DomainError>;
|
||||||
|
}
|
||||||
|
|||||||
@@ -991,3 +991,47 @@ impl crate::ports::WebhookTokenRepository for PanicWebhookTokenRepository {
|
|||||||
panic!("PanicWebhookTokenRepository called")
|
panic!("PanicWebhookTokenRepository called")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── InMemoryWrapUpStatsQuery ────────────────────────────────────────────────
|
||||||
|
|
||||||
|
pub struct InMemoryWrapUpStatsQuery {
|
||||||
|
pub rows: Mutex<Vec<crate::ports::WrapUpMovieRow>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InMemoryWrapUpStatsQuery {
|
||||||
|
pub fn new() -> Arc<Self> {
|
||||||
|
Arc::new(Self {
|
||||||
|
rows: Mutex::new(Vec::new()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_rows(rows: Vec<crate::ports::WrapUpMovieRow>) -> Arc<Self> {
|
||||||
|
Arc::new(Self {
|
||||||
|
rows: Mutex::new(rows),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl crate::ports::WrapUpStatsQuery for InMemoryWrapUpStatsQuery {
|
||||||
|
async fn get_reviews_with_profiles(
|
||||||
|
&self,
|
||||||
|
scope: &crate::models::wrapup::WrapUpScope,
|
||||||
|
range: &crate::models::wrapup::DateRange,
|
||||||
|
) -> Result<Vec<crate::ports::WrapUpMovieRow>, DomainError> {
|
||||||
|
let rows = self.rows.lock().unwrap();
|
||||||
|
let filtered: Vec<_> = rows
|
||||||
|
.iter()
|
||||||
|
.filter(|r| {
|
||||||
|
let date = r.watched_at.date();
|
||||||
|
date >= range.start && date < range.end
|
||||||
|
})
|
||||||
|
.filter(|r| match scope {
|
||||||
|
crate::models::wrapup::WrapUpScope::User(uid) => r.user_id == *uid,
|
||||||
|
crate::models::wrapup::WrapUpScope::Global => true,
|
||||||
|
})
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
Ok(filtered)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user