Enhance grid import/export functionality to support dynamic groups and collections
ci / build-backend (push) Successful in 1m43s
ci / build-frontend (push) Successful in 58s
ci / tests (push) Successful in 4m14s
ci / sonar (push) Successful in 4m38s

Updated the grid import/export process to allow for the inclusion of groups and collections directly from the configuration file. This enhancement ensures that missing groups and collections are created during import, improving the flexibility of the grid management system. Adjusted related classes and methods to accommodate these changes, ensuring a cohesive integration. Enhanced documentation and user prompts to clarify the new functionality and its usage.
This commit is contained in:
Leonid Pershin
2026-07-28 02:23:43 +03:00
parent f7e7b5f7c3
commit fe11034b16
14 changed files with 539 additions and 28 deletions
@@ -3,12 +3,15 @@ using Microsoft.EntityFrameworkCore;
using TeleWave.Application.Broadcast;
using TeleWave.Application.Common.Interfaces;
using TeleWave.Application.Common.Models;
using TeleWave.Application.Programming.Groups;
using TeleWave.Domain.Programming;
namespace TeleWave.Application.Programming.Templates.Transfer;
public sealed class ExportGridQueryHandler(IAppDbContext dbContext)
: IQueryHandler<ExportGridQuery, Result<GridConfig>>
public sealed class ExportGridQueryHandler(
IAppDbContext dbContext,
DynamicGroupResolver dynamicResolver
) : IQueryHandler<ExportGridQuery, Result<GridConfig>>
{
public async Task<Result<GridConfig>> Handle(
ExportGridQuery query,
@@ -54,6 +57,21 @@ public sealed class ExportGridQueryHandler(IAppDbContext dbContext)
))
.ToList();
// Состав групп едет вместе с сеткой: на другой установке тех же групп нет, а слот без
// группы поставить некуда. Собираем только использованные — выгружать всю библиотеку
// ради одного канала незачем.
var used = layers
.SelectMany(l => l.Slots)
.Select(s => s.Group)
.Append(Name(template.FallbackGroupId, groups))
.OfType<string>()
.ToHashSet(StringComparer.OrdinalIgnoreCase);
var (exportedGroups, exportedCollections) = await ExportGroupsAsync(
used,
cancellationToken
);
return Result.Success(
new GridConfig(
layers,
@@ -64,11 +82,95 @@ public sealed class ExportGridQueryHandler(IAppDbContext dbContext)
),
Rules: PlanningRules.FromJson(template.RulesJson),
FallbackGroup: Name(template.FallbackGroupId, groups),
DefaultJunction: Name(template.DefaultJunctionId, junctions)
DefaultJunction: Name(template.DefaultJunctionId, junctions),
Collections: exportedCollections,
Groups: exportedGroups
)
);
}
/// <summary>
/// Состав использованных групп и коллекций именами. Динамическая группа выгружается снимком
/// вычисленного состава, а не правилом: правило ссылается на жанры и рейтинги этой установки,
/// и на чужой переехало бы бессмыслицей.
/// </summary>
private async Task<(
List<GridConfigGroup> Groups,
List<GridConfigCollection> Collections
)> ExportGroupsAsync(IReadOnlySet<string> used, CancellationToken cancellationToken)
{
var groups = await dbContext
.Groups.AsNoTracking()
.Include(g => g.Items)
.ToListAsync(cancellationToken);
var showNames = await dbContext
.Shows.AsNoTracking()
.ToDictionaryAsync(s => s.Id, s => s.Name, cancellationToken);
var collectionNames = await dbContext
.Collections.AsNoTracking()
.ToDictionaryAsync(c => c.Id, c => c.Name, cancellationToken);
var exported = new List<GridConfigGroup>();
var collectionIds = new HashSet<Guid>();
foreach (var group in groups.Where(g => used.Contains(g.Name.Trim())))
{
var composition = await dynamicResolver.ResolveAsync(group, cancellationToken);
var shows = composition
.Where(e => e.Kind == GroupElementKind.Show)
.Select(e => showNames.GetValueOrDefault(e.Id))
.OfType<string>()
.ToList();
var collections = new List<string>();
foreach (var element in composition.Where(e => e.Kind == GroupElementKind.Collection))
{
if (collectionNames.GetValueOrDefault(element.Id) is not { } name)
continue;
collections.Add(name);
collectionIds.Add(element.Id);
}
exported.Add(
new GridConfigGroup(
group.Name,
shows.Count > 0 ? shows : null,
collections.Count > 0 ? collections : null,
group.Description
)
);
}
var items = await dbContext
.CollectionItems.AsNoTracking()
.Where(i => collectionIds.Contains(i.CollectionId))
.OrderBy(i => i.Position)
.ToListAsync(cancellationToken);
var exportedCollections = await dbContext
.Collections.AsNoTracking()
.Where(c => collectionIds.Contains(c.Id))
.ToListAsync(cancellationToken);
return (
exported,
[
.. exportedCollections.Select(collection => new GridConfigCollection(
collection.Name,
[
.. items
.Where(i => i.CollectionId == collection.Id)
.Select(i => showNames.GetValueOrDefault(i.ShowId))
.OfType<string>(),
],
collection.Description
)),
]
);
}
private static GridConfigSlot ToConfig(
Slot slot,
IReadOnlyDictionary<Guid, string> groups,