diff --git a/code/backend/Cleanuparr.Api/Features/Seeker/Contracts/Responses/InstanceFunnelStat.cs b/code/backend/Cleanuparr.Api/Features/Seeker/Contracts/Responses/InstanceFunnelStat.cs new file mode 100644 index 00000000..c5d71533 --- /dev/null +++ b/code/backend/Cleanuparr.Api/Features/Seeker/Contracts/Responses/InstanceFunnelStat.cs @@ -0,0 +1,40 @@ +using Cleanuparr.Persistence.Models.State; + +namespace Cleanuparr.Api.Features.Seeker.Contracts.Responses; + +/// +/// How many items and units survived each Seeker stage on the cycle's latest run. +/// Items count movies or series, units count searches. +/// +public sealed record InstanceFunnelStat +{ + public int LibraryItems { get; init; } + public int ReleasedItems { get; init; } + public int MonitoredItems { get; init; } + public int TagFilteredItems { get; init; } + public int EligibleUnits { get; init; } + public int QueuedUnits { get; init; } + public int RecentlyGrabbedUnits { get; init; } + public int SearchedUnits { get; init; } + public int RemainingUnits { get; init; } + public DateTimeOffset CountedAt { get; init; } + + /// + /// Null until a run counted the cycle's funnel. + /// + public static InstanceFunnelStat? From(SeekerCycle? cycle) => cycle?.FunnelCountedAt is not { } countedAt + ? null + : new InstanceFunnelStat + { + LibraryItems = cycle.LibraryItems, + ReleasedItems = cycle.ReleasedItems, + MonitoredItems = cycle.MonitoredItems, + TagFilteredItems = cycle.TagFilteredItems, + EligibleUnits = cycle.EligibleNow, + QueuedUnits = cycle.QueuedUnits, + RecentlyGrabbedUnits = cycle.RecentlyGrabbedUnits, + SearchedUnits = Math.Max(0, cycle.EligibleNow - cycle.QueuedUnits - cycle.RecentlyGrabbedUnits - cycle.RemainingUnits), + RemainingUnits = cycle.RemainingUnits, + CountedAt = countedAt, + }; +} diff --git a/code/backend/Cleanuparr.Api/Features/Seeker/Contracts/Responses/InstanceSearchStat.cs b/code/backend/Cleanuparr.Api/Features/Seeker/Contracts/Responses/InstanceSearchStat.cs index ad79bb26..8accacfb 100644 --- a/code/backend/Cleanuparr.Api/Features/Seeker/Contracts/Responses/InstanceSearchStat.cs +++ b/code/backend/Cleanuparr.Api/Features/Seeker/Contracts/Responses/InstanceSearchStat.cs @@ -17,4 +17,5 @@ public sealed record InstanceSearchStat public DateTimeOffset? CycleCompletedAt { get; init; } public DateTimeOffset? LastCycleStartedAt { get; init; } public DateTimeOffset? LastCycleCompletedAt { get; init; } + public InstanceFunnelStat? Funnel { get; init; } } diff --git a/code/backend/Cleanuparr.Api/Features/Seeker/Controllers/SearchStatsController.cs b/code/backend/Cleanuparr.Api/Features/Seeker/Controllers/SearchStatsController.cs index 82d7e4ae..684c1a4a 100644 --- a/code/backend/Cleanuparr.Api/Features/Seeker/Controllers/SearchStatsController.cs +++ b/code/backend/Cleanuparr.Api/Features/Seeker/Controllers/SearchStatsController.cs @@ -111,6 +111,7 @@ public sealed class SearchStatsController : ControllerBase CycleCompletedAt = current?.CompletedAt, LastCycleStartedAt = last?.StartedAt, LastCycleCompletedAt = last?.CompletedAt, + Funnel = InstanceFunnelStat.From(current), }; }).ToList();