Browse Source

remove explicit priority; start to use common selection logic

pull/2682/head
Jason Dove 7 months ago
parent
commit
ae7be7a0de
No known key found for this signature in database
  1. 76
      ErsatzTV.Core/Scheduling/YamlScheduling/Models/YamlPlayoutParsedSchedule.cs
  2. 5
      ErsatzTV.Core/Scheduling/YamlScheduling/Models/YamlPlayoutSchedule.cs
  3. 141
      ErsatzTV.Core/Scheduling/YamlScheduling/SequentialPlayoutBuilder.cs

76
ErsatzTV.Core/Scheduling/YamlScheduling/Models/YamlPlayoutParsedSchedule.cs

@ -0,0 +1,76 @@ @@ -0,0 +1,76 @@
using System.Globalization;
using ErsatzTV.Core.Domain.Scheduling;
namespace ErsatzTV.Core.Scheduling.YamlScheduling.Models;
public class YamlPlayoutParsedSchedule : IAlternateScheduleItem
{
public YamlPlayoutParsedSchedule(YamlPlayoutSchedule schedule, int index)
{
Schedule = schedule;
Index = index;
// TODO: these should be sourced from the schedule
DaysOfWeek = AlternateScheduleSelector.AllDaysOfWeek();
DaysOfMonth = AlternateScheduleSelector.AllDaysOfMonth();
MonthsOfYear = AlternateScheduleSelector.AllMonthsOfYear();
Option<(int month, int day, int? year)> maybeStart = ParseDate(schedule.StartDate);
foreach ((int month, int day, int? year) result in maybeStart)
{
(StartMonth, StartDay, StartYear) = result;
}
Option<(int month, int day, int? year)> maybeEnd = ParseDate(schedule.EndDate);
foreach ((int month, int day, int? year) result in maybeEnd)
{
(EndMonth, EndDay, EndYear) = result;
}
if (maybeStart.IsSome && maybeEnd.IsSome)
{
LimitToDateRange = true;
}
}
public YamlPlayoutSchedule Schedule { get; }
public int Index { get; }
public ICollection<DayOfWeek> DaysOfWeek { get; }
public ICollection<int> DaysOfMonth { get; }
public ICollection<int> MonthsOfYear { get; }
public bool LimitToDateRange { get; }
public int StartMonth { get; }
public int StartDay { get; }
public int? StartYear { get; }
public int EndMonth { get; }
public int EndDay { get; }
public int? EndYear { get; }
/// <summary>
/// Parses a date string in MM-DD or YYYY-MM-DD format.
/// Returns (month, day, year) where year is null for MM-DD format.
/// </summary>
private static Option<(int month, int day, int? year)> ParseDate(string dateStr)
{
if (string.IsNullOrEmpty(dateStr))
{
return None;
}
string[] parts = dateStr.Split('-');
if (parts.Length == 2)
{
// MM-DD format
return (int.Parse(parts[0], CultureInfo.InvariantCulture), int.Parse(parts[1], CultureInfo.InvariantCulture), null);
}
if (parts.Length == 3)
{
// YYYY-MM-DD format
return (int.Parse(parts[1], CultureInfo.InvariantCulture), int.Parse(parts[2], CultureInfo.InvariantCulture), int.Parse(parts[0], CultureInfo.InvariantCulture));
}
throw new FormatException($"Invalid date format: {dateStr}. Expected MM-DD or YYYY-MM-DD.");
}
}

5
ErsatzTV.Core/Scheduling/YamlScheduling/Models/YamlPlayoutSchedule.cs

@ -12,11 +12,6 @@ public class YamlPlayoutSchedule @@ -12,11 +12,6 @@ public class YamlPlayoutSchedule
[YamlMember(Alias = "end_date", ApplyNamingConventions = false)]
public string EndDate { get; set; }
/// <summary>
/// Priority for schedule matching. Higher values are checked first. Default is 0.
/// </summary>
public int Priority { get; set; }
public List<YamlPlayoutInstruction> Reset { get; set; } = [];
public List<YamlPlayoutInstruction> Playout { get; set; } = [];

141
ErsatzTV.Core/Scheduling/YamlScheduling/SequentialPlayoutBuilder.cs

@ -203,11 +203,18 @@ public class SequentialPlayoutBuilder( @@ -203,11 +203,18 @@ public class SequentialPlayoutBuilder(
}
// Determine which schedule to use based on the start date
YamlPlayoutSchedule activeSchedule = GetActiveSchedule(playoutDefinition, start);
List<YamlPlayoutInstruction> resetInstructions;
List<YamlPlayoutInstruction> playoutInstructions;
Option<YamlPlayoutSchedule> maybeActiveSchedule = GetActiveSchedule(playoutDefinition, start);
List<YamlPlayoutInstruction> resetInstructions = [];
List<YamlPlayoutInstruction> playoutInstructions = [];
if (activeSchedule != null)
if (maybeActiveSchedule.IsNone)
{
logger.LogDebug("Using default playout for date {Date}", start.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture));
resetInstructions = playoutDefinition.Reset;
playoutInstructions = playoutDefinition.Playout;
}
foreach (YamlPlayoutSchedule activeSchedule in maybeActiveSchedule)
{
logger.LogInformation(
"Using scheduled playout '{Name}' for date {Date}",
@ -217,12 +224,6 @@ public class SequentialPlayoutBuilder( @@ -217,12 +224,6 @@ public class SequentialPlayoutBuilder(
resetInstructions = activeSchedule.Reset.Count > 0 ? activeSchedule.Reset : playoutDefinition.Reset;
playoutInstructions = activeSchedule.Playout;
}
else
{
logger.LogDebug("Using default playout for date {Date}", start.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture));
resetInstructions = playoutDefinition.Reset;
playoutInstructions = playoutDefinition.Playout;
}
if (playoutInstructions.Count == 0)
{
@ -300,15 +301,15 @@ public class SequentialPlayoutBuilder( @@ -300,15 +301,15 @@ public class SequentialPlayoutBuilder(
}
// handle all playout instructions
YamlPlayoutSchedule currentSchedule = activeSchedule;
Option<YamlPlayoutSchedule> currentSchedule = maybeActiveSchedule;
while (context.CurrentTime < finish)
{
// Check if we've crossed into a different schedule
YamlPlayoutSchedule newSchedule = GetActiveSchedule(playoutDefinition, context.CurrentTime);
if (!ReferenceEquals(currentSchedule, newSchedule))
Option<YamlPlayoutSchedule> maybeNewSchedule = GetActiveSchedule(playoutDefinition, context.CurrentTime);
if (currentSchedule != maybeNewSchedule)
{
string oldName = currentSchedule != null ? (string.IsNullOrWhiteSpace(currentSchedule.Name) ? "Unnamed" : currentSchedule.Name) : "Default";
string newName = newSchedule != null ? (string.IsNullOrWhiteSpace(newSchedule.Name) ? "Unnamed" : newSchedule.Name) : "Default";
string oldName = currentSchedule.Match(cs => string.IsNullOrWhiteSpace(cs.Name) ? "Unnamed" : cs.Name, () => "Default");
string newName = maybeNewSchedule.Match(ns => string.IsNullOrWhiteSpace(ns.Name) ? "Unnamed" : ns.Name, () => "Default");
logger.LogInformation(
"Schedule changed from '{OldSchedule}' to '{NewSchedule}' at {Time}",
oldName,
@ -316,18 +317,14 @@ public class SequentialPlayoutBuilder( @@ -316,18 +317,14 @@ public class SequentialPlayoutBuilder(
context.CurrentTime.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture));
// Switch to the new schedule
currentSchedule = newSchedule;
currentSchedule = maybeActiveSchedule;
// Get the new schedule's instructions
List<YamlPlayoutInstruction> newPlayoutInstructions;
if (newSchedule != null)
List<YamlPlayoutInstruction> newPlayoutInstructions = playoutDefinition.Playout;
foreach (var newSchedule in maybeNewSchedule)
{
newPlayoutInstructions = newSchedule.Playout;
}
else
{
newPlayoutInstructions = playoutDefinition.Playout;
}
if (newPlayoutInstructions.Count == 0)
{
@ -341,7 +338,7 @@ public class SequentialPlayoutBuilder( @@ -341,7 +338,7 @@ public class SequentialPlayoutBuilder(
Import = playoutDefinition.Import,
Content = playoutDefinition.Content,
Sequence = playoutDefinition.Sequence,
Reset = newSchedule != null && newSchedule.Reset.Count > 0 ? newSchedule.Reset : playoutDefinition.Reset,
Reset = maybeNewSchedule.Match(ns => ns.Reset.Count > 0 ? ns.Reset : playoutDefinition.Reset, () => playoutDefinition.Reset),
Playout = newPlayoutInstructions
};
@ -742,103 +739,17 @@ public class SequentialPlayoutBuilder( @@ -742,103 +739,17 @@ public class SequentialPlayoutBuilder(
}
/// <summary>
/// Finds the active schedule for the given date, or returns null if no schedule matches.
/// Schedules are checked in priority order (highest first), then by definition order.
/// Finds the active schedule for the given date, or returns None if no schedule matches.
/// Schedules are checked by definition order.
/// </summary>
private static YamlPlayoutSchedule GetActiveSchedule(YamlPlayoutDefinition definition, DateTimeOffset date)
private static Option<YamlPlayoutSchedule> GetActiveSchedule(YamlPlayoutDefinition definition, DateTimeOffset date)
{
if (definition.Schedules.Count == 0)
{
return null;
}
// Sort by priority (descending), then by original order
var sortedSchedules = definition.Schedules
.Select((schedule, index) => (schedule, index))
.OrderByDescending(x => x.schedule.Priority)
.ThenBy(x => x.index);
foreach ((YamlPlayoutSchedule schedule, int _) in sortedSchedules)
{
if (IsDateInSchedule(schedule, date))
{
return schedule;
}
}
return null;
}
/// <summary>
/// Determines if a date falls within a scheduled playout's date range.
/// </summary>
private static bool IsDateInSchedule(YamlPlayoutSchedule schedule, DateTimeOffset date)
{
if (string.IsNullOrWhiteSpace(schedule.StartDate) || string.IsNullOrWhiteSpace(schedule.EndDate))
{
return false;
}
(int startMonth, int startDay, int? startYear) = ParseDate(schedule.StartDate);
(int endMonth, int endDay, int? endYear) = ParseDate(schedule.EndDate);
int currentYear = date.Year;
int currentMonth = date.Month;
int currentDay = date.Day;
// Handle year-specific dates (YYYY-MM-DD format)
if (startYear.HasValue && endYear.HasValue)
{
// Dates with years are always non-recurring (one-time events)
var startDate = new DateTime(startYear.Value, startMonth, startDay);
var endDate = new DateTime(endYear.Value, endMonth, endDay);
var currentDate = new DateTime(currentYear, currentMonth, currentDay);
return currentDate >= startDate && currentDate <= endDate;
}
if (startYear.HasValue || endYear.HasValue)
{
// Mixed format - not supported
return false;
}
// Both dates are MM-DD format - always recurring
var currentDayOfYear = new DateTime(currentYear, currentMonth, currentDay).DayOfYear;
var startDayOfYear = new DateTime(currentYear, startMonth, startDay).DayOfYear;
var endDayOfYear = new DateTime(currentYear, endMonth, endDay).DayOfYear;
// Handle date ranges that span across the new year
if (startDayOfYear <= endDayOfYear)
{
// Normal range (e.g., Jan 1 - March 31)
return currentDayOfYear >= startDayOfYear && currentDayOfYear <= endDayOfYear;
}
// Range wraps around the year (e.g., Dec 1 - Jan 31)
return currentDayOfYear >= startDayOfYear || currentDayOfYear <= endDayOfYear;
}
/// <summary>
/// Parses a date string in MM-DD or YYYY-MM-DD format.
/// Returns (month, day, year) where year is null for MM-DD format.
/// </summary>
private static (int month, int day, int? year) ParseDate(string dateStr)
{
string[] parts = dateStr.Split('-');
if (parts.Length == 2)
{
// MM-DD format
return (int.Parse(parts[0], CultureInfo.InvariantCulture), int.Parse(parts[1], CultureInfo.InvariantCulture), null);
}
if (parts.Length == 3)
{
// YYYY-MM-DD format
return (int.Parse(parts[1], CultureInfo.InvariantCulture), int.Parse(parts[2], CultureInfo.InvariantCulture), int.Parse(parts[0], CultureInfo.InvariantCulture));
return Option<YamlPlayoutSchedule>.None;
}
throw new FormatException($"Invalid date format: {dateStr}. Expected MM-DD or YYYY-MM-DD.");
var parsedSchedules = definition.Schedules.Select((s, i) => new YamlPlayoutParsedSchedule(s, i)).ToList();
return AlternateScheduleSelector.GetScheduleForDate(parsedSchedules, date).Map(s => s.Schedule);
}
}

Loading…
Cancel
Save