diff --git a/DeepDrftAPI/CLAUDE.md b/DeepDrftAPI/CLAUDE.md index 5980880..fa5d203 100644 --- a/DeepDrftAPI/CLAUDE.md +++ b/DeepDrftAPI/CLAUDE.md @@ -304,8 +304,10 @@ Aggregate figures behind the public home hero stat row (`NowPlayingStats`). A si - `CutReleaseTypeCounts` (`List`): per-`ReleaseType` Cut release counts; zero-count types are absent (zero-suppressed server-side). - `MixReleaseCount` (int): total non-deleted Mix-medium releases. - `MixRuntimeSeconds` (double): sum of `DurationSeconds` across all non-deleted tracks on Mix releases (null durations count as 0). -- Aggregated in `TrackRepository.GetHomeStatsAsync`, surfaced via `ITrackService`/`TrackManager`. Controller is `StatsController` — a thin HTTP boundary; no domain logic lives there. -- Returns 200 on success. Returns 500 on query error. + - `TotalPlays` (long): site-wide total plays — sum of every `play_counter` row's bucket columns (`PartialCount + SampledCount + CompleteCount`), all-time (Phase 16). Zero until the play-telemetry migration is applied. + - `UniqueListeners` (int): site-wide distinct anonymous listeners — distinct non-null `anon_id` across all play events, all-time (Phase 16). Zero until the migration is applied. +- `StatsController` injects **both** `ITrackService` (track-domain aggregation — Cuts/Mixes cards) and `IEventService` (event-domain aggregation — Plays card). Neither domain reaches into the other's tables; the controller is the thin composition seam. Track-domain aggregation comes from `TrackRepository.GetHomeStatsAsync` via `ITrackService.GetHomeStats`; play/listener figures come from `IEventService.GetTotalPlayCount` and `IEventService.GetDistinctListenerCount` (Phase 16 wave 16.5). Play/listener reads are **best-effort**: a telemetry failure or not-yet-applied migration leaves those fields at 0 rather than failing the whole endpoint with 500. +- Returns 200 on success. Returns 500 if the track-domain aggregation fails. ## The event endpoints (Phase 16 anonymous telemetry) diff --git a/DeepDrftModels/CLAUDE.md b/DeepDrftModels/CLAUDE.md index 151fe95..5cb755e 100644 --- a/DeepDrftModels/CLAUDE.md +++ b/DeepDrftModels/CLAUDE.md @@ -57,6 +57,8 @@ Aggregate figures behind the public home hero stat row (`NowPlayingStats`). A si - `CutReleaseTypeCounts` (`List`): per-`ReleaseType` Cut release counts; zero-count types are absent (suppressed server-side). - `MixReleaseCount` (int): total non-deleted Mix-medium releases. - `MixRuntimeSeconds` (double): sum of `DurationSeconds` across all non-deleted tracks on Mix releases (null durations count as 0). Rendered as `hh:mm` by `RuntimeFormat` on the client. +- `TotalPlays` (long): site-wide total plays — sum of every `play_counter` row's `PartialCount + SampledCount + CompleteCount`, all-time (Phase 16 §5). Zero until the play-telemetry migration is applied; that is expected, not an error. The Plays card's primary odometer figure. +- `UniqueListeners` (int): site-wide distinct anonymous listeners — distinct non-null `anon_id` values across all play events, all-time (Phase 16 §3 / D7). Zero until the migration is applied. The Plays card's secondary line ("N listeners"). `CutReleaseTypeCount` is a nested type (`ReleaseType`, `Count` int) defined in the same file.