74 lines
2.8 KiB
C#
74 lines
2.8 KiB
C#
using System.Globalization;
|
|
using ErsatzTV.Core.Domain;
|
|
|
|
namespace ErsatzTV.Application.ProgramSchedules;
|
|
|
|
/// <summary>
|
|
/// Pure computation of per-item runtime estimates from pre-aggregated collection durations.
|
|
/// Kept separate from the query handler so the (non-trivial) per-mode math can be unit-tested
|
|
/// without a database. See <see cref="ProgramScheduleItemViewModel.DurationEstimate" /> for the
|
|
/// documented semantics this implements.
|
|
/// </summary>
|
|
internal static class ScheduleItemDurationEstimator
|
|
{
|
|
/// <summary>
|
|
/// Aggregate runtime of a single referenced collection: the total runtime of every item with a
|
|
/// known non-zero duration, and how many such items there are.
|
|
/// </summary>
|
|
public sealed record CollectionDuration(TimeSpan Total, int ItemCount)
|
|
{
|
|
public TimeSpan? Average => ItemCount > 0 ? Total / ItemCount : null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Estimate the runtime of one pass of <paramref name="item" />, or <c>null</c> when no bounded
|
|
/// estimate is possible. <paramref name="durationsByCollectionId" /> holds aggregates only for the
|
|
/// plain collections that were resolved; a missing entry yields <c>null</c>.
|
|
/// </summary>
|
|
public static TimeSpan? Estimate(
|
|
ProgramScheduleItemViewModel item,
|
|
IReadOnlyDictionary<int, CollectionDuration> durationsByCollectionId)
|
|
{
|
|
if (item is ProgramScheduleItemDurationViewModel durationItem)
|
|
{
|
|
return durationItem.PlayoutDuration;
|
|
}
|
|
|
|
// only plain collections are aggregated in this pass
|
|
if (item.CollectionType is not CollectionType.Collection || item.Collection is null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if (!durationsByCollectionId.TryGetValue(item.Collection.Id, out CollectionDuration duration))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return item switch
|
|
{
|
|
// one item per pass -> the average item runtime
|
|
ProgramScheduleItemOneViewModel => duration.Average,
|
|
|
|
// a fixed count of items, or the whole collection once
|
|
ProgramScheduleItemMultipleViewModel multiple => EstimateMultiple(multiple, duration),
|
|
|
|
// Flood is an unbounded fill; Duration is handled above from its explicit playoutDuration.
|
|
_ => null
|
|
};
|
|
}
|
|
|
|
private static TimeSpan? EstimateMultiple(
|
|
ProgramScheduleItemMultipleViewModel multiple,
|
|
CollectionDuration duration) =>
|
|
multiple.MultipleMode switch
|
|
{
|
|
MultipleMode.Count when
|
|
int.TryParse(multiple.Count, NumberStyles.Integer, CultureInfo.InvariantCulture, out int count)
|
|
&& count > 0
|
|
&& duration.Average is { } average => average * count,
|
|
MultipleMode.CollectionSize => duration.Total,
|
|
_ => null
|
|
};
|
|
}
|