Refactor BumperPlaceholders and BumperFacts for improved logic and readability
Updated the BumperPlaceholders class to streamline token extraction from texts, enhancing performance and clarity. Modified BumperFacts to initialize slot titles with an empty array instead of a dictionary for better consistency. Renamed methods in BumperResolver for clarity, and refactored GridScheduleGenerator to simplify return logic. Additionally, improved the BumperLinesEditor component by implementing a keyed list for better state management and user experience.
This commit is contained in:
@@ -95,11 +95,12 @@ public static partial class BumperPlaceholders
|
||||
/// <summary>Какие плейсхолдеры встречаются в текстах — по ним планировщик решает, что грузить.</summary>
|
||||
public static IReadOnlySet<string> TokensIn(IEnumerable<string> texts)
|
||||
{
|
||||
var used = new HashSet<string>(StringComparer.Ordinal);
|
||||
foreach (var text in texts.Where(t => !string.IsNullOrWhiteSpace(t)))
|
||||
foreach (Match match in TokenPattern().Matches(text))
|
||||
used.Add(match.Groups[1].Value);
|
||||
return used;
|
||||
var matches = texts
|
||||
.Where(t => !string.IsNullOrWhiteSpace(t))
|
||||
.SelectMany(text => TokenPattern().Matches(text))
|
||||
.Select(match => match.Groups[1].Value);
|
||||
|
||||
return new HashSet<string>(matches, StringComparer.Ordinal);
|
||||
}
|
||||
|
||||
/// <summary>Плейсхолдеры текста, которых нет в списке допустимых.</summary>
|
||||
|
||||
@@ -59,7 +59,7 @@ internal sealed class BumperFacts
|
||||
|
||||
var slotTitles = tokens.Contains("slot")
|
||||
? await LoadSlotTitlesAsync(dbContext, items, cancellationToken)
|
||||
: new Dictionary<Guid, string>();
|
||||
: [];
|
||||
|
||||
return new BumperFacts(items, channel, shows, slotTitles);
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ public sealed class BumperResolver(
|
||||
variant.Id,
|
||||
linesJson,
|
||||
posterShowId,
|
||||
Signature(template, variant.Id, linesJson, posterShowId)
|
||||
ComputeSignature(template, variant.Id, linesJson, posterShowId)
|
||||
)
|
||||
)
|
||||
);
|
||||
@@ -210,7 +210,7 @@ public sealed class BumperResolver(
|
||||
/// и пары шоу в ней нет намеренно: одинаковая заставка на трёх каналах рендерится один раз, а
|
||||
/// <c>{channel}</c> в тексте разводит их сам собой.
|
||||
/// </summary>
|
||||
private static string Signature(
|
||||
private static string ComputeSignature(
|
||||
BumperTemplate template,
|
||||
Guid variantId,
|
||||
string linesJson,
|
||||
|
||||
@@ -447,13 +447,13 @@ public sealed class GridScheduleGenerator(
|
||||
);
|
||||
}
|
||||
|
||||
return elements.Count == 0
|
||||
? null
|
||||
: new PlanningJunction(
|
||||
id,
|
||||
elements,
|
||||
template.MaxTotalSeconds is { } seconds ? TimeSpan.FromSeconds(seconds) : null
|
||||
);
|
||||
if (elements.Count == 0)
|
||||
return null;
|
||||
|
||||
var cap = template.MaxTotalSeconds is { } seconds
|
||||
? TimeSpan.FromSeconds(seconds)
|
||||
: (TimeSpan?)null;
|
||||
return new PlanningJunction(id, elements, cap);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -360,9 +360,28 @@ public sealed class GridPlanner(
|
||||
PlanRun Run
|
||||
)
|
||||
{
|
||||
/// <summary>
|
||||
/// Длина слота и число единиц в нём. Блок из N единиц занимает ровно столько, сколько эти
|
||||
/// единицы идут, — округление до пяти минут; хвост короче минимального слота приклеивается
|
||||
/// к текущему, чтобы в сетке не появлялось огрызков.
|
||||
/// </summary>
|
||||
private (int Duration, int Units) Measure(GroupCandidate group, int available)
|
||||
{
|
||||
var unit = UnitMinutes(group);
|
||||
|
||||
// Полоса, которая меряется временем (марафон, ночь), берёт свой кусок целиком.
|
||||
if (Band.UnitsPerBlock <= 0 || unit == 0)
|
||||
return (Fit(Band.BlockMinutes > 0 ? Band.BlockMinutes : available, available), 0);
|
||||
|
||||
var duration = Fit(Math.Max(1, Band.UnitsPerBlock) * unit, available);
|
||||
// Число единиц — по фактической длине слота: полоса задаёт замысел, а сколько влезло,
|
||||
// решает контент.
|
||||
return (duration, Math.Max(1, (int)Math.Round(duration / (double)unit)));
|
||||
}
|
||||
|
||||
public PlannedSlot Content(GroupCandidate group, int available)
|
||||
{
|
||||
var (duration, units) = Measure(Band, group, available);
|
||||
var (duration, units) = Measure(group, available);
|
||||
Run.Reserve(group, units, duration, Target.AiringsPerWeek);
|
||||
|
||||
return Build(
|
||||
@@ -442,29 +461,6 @@ public sealed class GridPlanner(
|
||||
private static int RepeatMinutes(GridBand band) =>
|
||||
band.BlockMinutes > 0 ? band.BlockMinutes : 120;
|
||||
|
||||
/// <summary>
|
||||
/// Длина слота и число единиц в нём. Блок из N единиц занимает ровно столько, сколько эти
|
||||
/// единицы идут, — округление до пяти минут; хвост короче минимального слота приклеивается
|
||||
/// к текущему, чтобы в сетке не появлялось огрызков.
|
||||
/// </summary>
|
||||
private static (int Duration, int Units) Measure(
|
||||
GridBand band,
|
||||
GroupCandidate group,
|
||||
int available
|
||||
)
|
||||
{
|
||||
var unit = UnitMinutes(group);
|
||||
|
||||
// Полоса, которая меряется временем (марафон, ночь), берёт свой кусок целиком.
|
||||
if (band.UnitsPerBlock <= 0 || unit == 0)
|
||||
return (Fit(band.BlockMinutes > 0 ? band.BlockMinutes : available, available), 0);
|
||||
|
||||
var duration = Fit(Math.Max(1, band.UnitsPerBlock) * unit, available);
|
||||
// Число единиц — по фактической длине слота: полоса задаёт замысел, а сколько влезло,
|
||||
// решает контент.
|
||||
return (duration, Math.Max(1, (int)Math.Round(duration / (double)unit)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Длина слота: желаемая, но не больше свободного места. Остаток короче минимального слота
|
||||
/// приклеивается к текущему — огрызок в сетке читается как ошибка, а не как решение. Остаток
|
||||
|
||||
Reference in New Issue
Block a user