Enhance code readability and maintainability by refactoring various components. Update formatting commands in CLAUDE.md for csharpier integration. Simplify method signatures in SegmentFiles, UploadLimits, and multiple endpoint classes for improved clarity. Adjust query handlers and command handlers to streamline parameter handling and enhance consistency across the application.
This commit is contained in:
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"isRoot": true,
|
||||||
|
"tools": {
|
||||||
|
"csharpier": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"commands": [
|
||||||
|
"csharpier"
|
||||||
|
],
|
||||||
|
"rollForward": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -120,6 +120,10 @@
|
|||||||
Backend (из `backend/`):
|
Backend (из `backend/`):
|
||||||
```bash
|
```bash
|
||||||
dotnet build
|
dotnet build
|
||||||
|
# Форматирование — csharpier, версия закреплена в .config/dotnet-tools.json (один раз: dotnet tool restore).
|
||||||
|
# Глобально установленный csharpier может быть другой версии — вызывать только через dotnet:
|
||||||
|
dotnet csharpier format .
|
||||||
|
dotnet csharpier check .
|
||||||
# Юнит-тесты (по одному проекту за вызов — MSBuild не принимает несколько):
|
# Юнит-тесты (по одному проекту за вызов — MSBuild не принимает несколько):
|
||||||
dotnet test tests/TeleWave.Domain.Tests
|
dotnet test tests/TeleWave.Domain.Tests
|
||||||
dotnet test tests/TeleWave.Application.Tests
|
dotnet test tests/TeleWave.Application.Tests
|
||||||
|
|||||||
@@ -17,11 +17,7 @@ internal static partial class SegmentFiles
|
|||||||
public static bool IsSegmentName(string file) => SegmentName().IsMatch(file);
|
public static bool IsSegmentName(string file) => SegmentName().IsMatch(file);
|
||||||
|
|
||||||
/// <summary>Путь к существующему файлу нарезки, либо null — если имя опасно или файла нет.</summary>
|
/// <summary>Путь к существующему файлу нарезки, либо null — если имя опасно или файла нет.</summary>
|
||||||
public static string? TryResolveExisting(
|
public static string? TryResolveExisting(MediaPathResolver paths, Guid assetId, string fileName)
|
||||||
MediaPathResolver paths,
|
|
||||||
Guid assetId,
|
|
||||||
string fileName
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
string path;
|
string path;
|
||||||
try
|
try
|
||||||
|
|||||||
@@ -8,10 +8,7 @@ namespace TeleWave.Api.Common;
|
|||||||
/// (<c>Media</c> и <c>Storage</c>), но проверяются всегда вместе и только на входе загрузки —
|
/// (<c>Media</c> и <c>Storage</c>), но проверяются всегда вместе и только на входе загрузки —
|
||||||
/// хендлеру незачем знать про обе секции и тащить два <see cref="IOptions{T}"/> в сигнатуре.
|
/// хендлеру незачем знать про обе секции и тащить два <see cref="IOptions{T}"/> в сигнатуре.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class UploadLimits(
|
public sealed class UploadLimits(IOptions<MediaOptions> media, IOptions<StorageOptions> storage)
|
||||||
IOptions<MediaOptions> media,
|
|
||||||
IOptions<StorageOptions> storage
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
/// <summary>Потолок размера загружаемого файла.</summary>
|
/// <summary>Потолок размера загружаемого файла.</summary>
|
||||||
public long MaxUploadBytes { get; } = media.Value.MaxUploadBytes;
|
public long MaxUploadBytes { get; } = media.Value.MaxUploadBytes;
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ using TeleWave.Application.Broadcast.GetSchedule;
|
|||||||
using TeleWave.Application.Broadcast.ListChannels;
|
using TeleWave.Application.Broadcast.ListChannels;
|
||||||
using TeleWave.Application.Broadcast.UpdateChannelSettings;
|
using TeleWave.Application.Broadcast.UpdateChannelSettings;
|
||||||
using TeleWave.Application.Broadcast.UpdateChannelTime;
|
using TeleWave.Application.Broadcast.UpdateChannelTime;
|
||||||
using TeleWave.Domain.Broadcast;
|
|
||||||
using TeleWave.Infrastructure.Identity;
|
|
||||||
using TeleWave.Application.Broadcast.UpdateViewerSettings;
|
using TeleWave.Application.Broadcast.UpdateViewerSettings;
|
||||||
using TeleWave.Application.Programming.Planning.Trace;
|
using TeleWave.Application.Programming.Planning.Trace;
|
||||||
|
using TeleWave.Domain.Broadcast;
|
||||||
|
using TeleWave.Infrastructure.Identity;
|
||||||
|
|
||||||
namespace TeleWave.Api.Endpoints;
|
namespace TeleWave.Api.Endpoints;
|
||||||
|
|
||||||
@@ -177,12 +177,7 @@ public static partial class ChannelEndpoints
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
var result = await sender.Send(
|
var result = await sender.Send(
|
||||||
new UpdateChannelTimeCommand(
|
new UpdateChannelTimeCommand(id, body.Number, body.UtcOffsetMinutes, body.DayStartTime),
|
||||||
id,
|
|
||||||
body.Number,
|
|
||||||
body.UtcOffsetMinutes,
|
|
||||||
body.DayStartTime
|
|
||||||
),
|
|
||||||
cancellationToken
|
cancellationToken
|
||||||
);
|
);
|
||||||
return result.ToHttpResult();
|
return result.ToHttpResult();
|
||||||
@@ -209,7 +204,6 @@ public static partial class ChannelEndpoints
|
|||||||
return result.ToHttpResult();
|
return result.ToHttpResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static async Task<IResult> GetSchedule(
|
private static async Task<IResult> GetSchedule(
|
||||||
Guid id,
|
Guid id,
|
||||||
DateTimeOffset? from,
|
DateTimeOffset? from,
|
||||||
|
|||||||
@@ -30,7 +30,8 @@ public static class GroupEndpoints
|
|||||||
admin.MapDelete("/{id:guid}", DeleteGroup).Produces(StatusCodes.Status204NoContent);
|
admin.MapDelete("/{id:guid}", DeleteGroup).Produces(StatusCodes.Status204NoContent);
|
||||||
|
|
||||||
// Подбор по правилу набора: правило можно передать в теле, чтобы крутить его до сохранения.
|
// Подбор по правилу набора: правило можно передать в теле, чтобы крутить его до сохранения.
|
||||||
admin.MapPost("/{id:guid}/candidates", FindCandidates)
|
admin
|
||||||
|
.MapPost("/{id:guid}/candidates", FindCandidates)
|
||||||
.Produces<IReadOnlyList<GroupCandidateDto>>();
|
.Produces<IReadOnlyList<GroupCandidateDto>>();
|
||||||
|
|
||||||
admin.MapPost("/{id:guid}/items", AddElements).Produces<AddedCountResponse>();
|
admin.MapPost("/{id:guid}/items", AddElements).Produces<AddedCountResponse>();
|
||||||
@@ -45,7 +46,10 @@ public static class GroupEndpoints
|
|||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<IResult> ListGroups(ISender sender, CancellationToken cancellationToken)
|
private static async Task<IResult> ListGroups(
|
||||||
|
ISender sender,
|
||||||
|
CancellationToken cancellationToken
|
||||||
|
)
|
||||||
{
|
{
|
||||||
var result = await sender.Send(new ListGroupsQuery(), cancellationToken);
|
var result = await sender.Send(new ListGroupsQuery(), cancellationToken);
|
||||||
return Results.Ok(result);
|
return Results.Ok(result);
|
||||||
|
|||||||
@@ -24,7 +24,9 @@ public static class JunctionEndpoints
|
|||||||
admin
|
admin
|
||||||
.MapPost("/channels/{channelId:guid}/junctions", Create)
|
.MapPost("/channels/{channelId:guid}/junctions", Create)
|
||||||
.Produces<CreatedIdResponse>(StatusCodes.Status201Created);
|
.Produces<CreatedIdResponse>(StatusCodes.Status201Created);
|
||||||
admin.MapPut("/junctions/{junctionId:guid}", Rename).Produces(StatusCodes.Status204NoContent);
|
admin
|
||||||
|
.MapPut("/junctions/{junctionId:guid}", Rename)
|
||||||
|
.Produces(StatusCodes.Status204NoContent);
|
||||||
admin
|
admin
|
||||||
.MapDelete("/junctions/{junctionId:guid}", Delete)
|
.MapDelete("/junctions/{junctionId:guid}", Delete)
|
||||||
.Produces(StatusCodes.Status204NoContent);
|
.Produces(StatusCodes.Status204NoContent);
|
||||||
|
|||||||
@@ -141,7 +141,10 @@ public static class MediaEndpoints
|
|||||||
return result.ToHttpResult();
|
return result.ToHttpResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<IResult> ListManual(ISender sender, CancellationToken cancellationToken)
|
private static async Task<IResult> ListManual(
|
||||||
|
ISender sender,
|
||||||
|
CancellationToken cancellationToken
|
||||||
|
)
|
||||||
{
|
{
|
||||||
var result = await sender.Send(new ListManualInboxQuery(), cancellationToken);
|
var result = await sender.Send(new ListManualInboxQuery(), cancellationToken);
|
||||||
return Results.Ok(result);
|
return Results.Ok(result);
|
||||||
|
|||||||
@@ -66,7 +66,10 @@ public static class ShowEndpoints
|
|||||||
bool interstitials = false
|
bool interstitials = false
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
var result = await sender.Send(new ListShowsQuery(genreId, interstitials), cancellationToken);
|
var result = await sender.Send(
|
||||||
|
new ListShowsQuery(genreId, interstitials),
|
||||||
|
cancellationToken
|
||||||
|
);
|
||||||
return Results.Ok(result);
|
return Results.Ok(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,9 @@ public static class StreamingEndpoints
|
|||||||
CancellationToken cancellationToken
|
CancellationToken cancellationToken
|
||||||
) =>
|
) =>
|
||||||
Results.Ok(
|
Results.Ok(
|
||||||
new ViewerFeaturesDto(await siteSettings.AreChannelNumbersEnabledAsync(cancellationToken))
|
new ViewerFeaturesDto(
|
||||||
|
await siteSettings.AreChannelNumbersEnabledAsync(cancellationToken)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -56,13 +56,18 @@ public static class TemplateEndpoints
|
|||||||
.MapGet("/channels/{channelId:guid}/template/diff", DiffTemplate)
|
.MapGet("/channels/{channelId:guid}/template/diff", DiffTemplate)
|
||||||
.Produces<ScheduleDiffDto>();
|
.Produces<ScheduleDiffDto>();
|
||||||
admin
|
admin
|
||||||
.MapPost("/channels/{channelId:guid}/template/copy-to/{targetChannelId:guid}", CopyTemplate)
|
.MapPost(
|
||||||
|
"/channels/{channelId:guid}/template/copy-to/{targetChannelId:guid}",
|
||||||
|
CopyTemplate
|
||||||
|
)
|
||||||
.Produces<CopyTemplateResultDto>();
|
.Produces<CopyTemplateResultDto>();
|
||||||
|
|
||||||
admin
|
admin
|
||||||
.MapPost("/templates/{templateId:guid}/layers", CreateLayer)
|
.MapPost("/templates/{templateId:guid}/layers", CreateLayer)
|
||||||
.Produces<CreatedIdResponse>(StatusCodes.Status201Created);
|
.Produces<CreatedIdResponse>(StatusCodes.Status201Created);
|
||||||
admin.MapPut("/layers/{layerId:guid}", UpdateLayer).Produces(StatusCodes.Status204NoContent);
|
admin
|
||||||
|
.MapPut("/layers/{layerId:guid}", UpdateLayer)
|
||||||
|
.Produces(StatusCodes.Status204NoContent);
|
||||||
admin
|
admin
|
||||||
.MapDelete("/layers/{layerId:guid}", DeleteLayer)
|
.MapDelete("/layers/{layerId:guid}", DeleteLayer)
|
||||||
.Produces(StatusCodes.Status204NoContent);
|
.Produces(StatusCodes.Status204NoContent);
|
||||||
@@ -71,7 +76,9 @@ public static class TemplateEndpoints
|
|||||||
.MapPost("/layers/{layerId:guid}/slots", CreateSlot)
|
.MapPost("/layers/{layerId:guid}/slots", CreateSlot)
|
||||||
.Produces<CreatedIdResponse>(StatusCodes.Status201Created);
|
.Produces<CreatedIdResponse>(StatusCodes.Status201Created);
|
||||||
admin.MapPut("/slots/{slotId:guid}", UpdateSlot).Produces(StatusCodes.Status204NoContent);
|
admin.MapPut("/slots/{slotId:guid}", UpdateSlot).Produces(StatusCodes.Status204NoContent);
|
||||||
admin.MapDelete("/slots/{slotId:guid}", DeleteSlot).Produces(StatusCodes.Status204NoContent);
|
admin
|
||||||
|
.MapDelete("/slots/{slotId:guid}", DeleteSlot)
|
||||||
|
.Produces(StatusCodes.Status204NoContent);
|
||||||
|
|
||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
@@ -197,7 +204,10 @@ public static class TemplateEndpoints
|
|||||||
cancellationToken
|
cancellationToken
|
||||||
);
|
);
|
||||||
return result.IsSuccess
|
return result.IsSuccess
|
||||||
? Results.Created($"/api/admin/layers/{result.Value}", new CreatedIdResponse(result.Value))
|
? Results.Created(
|
||||||
|
$"/api/admin/layers/{result.Value}",
|
||||||
|
new CreatedIdResponse(result.Value)
|
||||||
|
)
|
||||||
: result.ToHttpResult();
|
: result.ToHttpResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,7 +250,10 @@ public static class TemplateEndpoints
|
|||||||
{
|
{
|
||||||
var result = await sender.Send(new CreateSlotCommand(layerId, input), cancellationToken);
|
var result = await sender.Send(new CreateSlotCommand(layerId, input), cancellationToken);
|
||||||
return result.IsSuccess
|
return result.IsSuccess
|
||||||
? Results.Created($"/api/admin/slots/{result.Value}", new CreatedIdResponse(result.Value))
|
? Results.Created(
|
||||||
|
$"/api/admin/slots/{result.Value}",
|
||||||
|
new CreatedIdResponse(result.Value)
|
||||||
|
)
|
||||||
: result.ToHttpResult();
|
: result.ToHttpResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-4
@@ -108,11 +108,10 @@ public sealed class RenderBumperPreviewCommandHandler(
|
|||||||
var names = await (
|
var names = await (
|
||||||
from slot in dbContext.Slots.AsNoTracking()
|
from slot in dbContext.Slots.AsNoTracking()
|
||||||
join layer in dbContext.GridLayers.AsNoTracking() on slot.LayerId equals layer.Id
|
join layer in dbContext.GridLayers.AsNoTracking() on slot.LayerId equals layer.Id
|
||||||
join item in dbContext.GroupItems.AsNoTracking()
|
join item in dbContext.GroupItems.AsNoTracking() on slot.GroupId equals item.GroupId
|
||||||
on slot.GroupId equals item.GroupId
|
|
||||||
join show in dbContext.Shows.AsNoTracking() on item.ElementId equals show.Id
|
join show in dbContext.Shows.AsNoTracking() on item.ElementId equals show.Id
|
||||||
where layer.TemplateId == channel.TemplateId
|
where
|
||||||
&& item.ElementKind == GroupElementKind.Show
|
layer.TemplateId == channel.TemplateId && item.ElementKind == GroupElementKind.Show
|
||||||
select show.Name
|
select show.Name
|
||||||
)
|
)
|
||||||
.Distinct()
|
.Distinct()
|
||||||
|
|||||||
@@ -50,5 +50,4 @@ public static class ChannelErrors
|
|||||||
"Channels.InvalidBumperFile",
|
"Channels.InvalidBumperFile",
|
||||||
"Недопустимый файл заставки (формат или размер)."
|
"Недопустимый файл заставки (формат или размер)."
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-1
@@ -20,7 +20,10 @@ public sealed class ListCollectionsQueryHandler(IAppDbContext dbContext)
|
|||||||
|
|
||||||
// Единиц воспроизведения может быть больше, чем частей: сериал внутри коллекции
|
// Единиц воспроизведения может быть больше, чем частей: сериал внутри коллекции
|
||||||
// разворачивается в свои серии.
|
// разворачивается в свои серии.
|
||||||
var showIds = collections.SelectMany(c => c.Items.Select(i => i.ShowId)).Distinct().ToList();
|
var showIds = collections
|
||||||
|
.SelectMany(c => c.Items.Select(i => i.ShowId))
|
||||||
|
.Distinct()
|
||||||
|
.ToList();
|
||||||
var episodeCounts = await dbContext
|
var episodeCounts = await dbContext
|
||||||
.Shows.AsNoTracking()
|
.Shows.AsNoTracking()
|
||||||
.Where(s => showIds.Contains(s.Id))
|
.Where(s => showIds.Contains(s.Id))
|
||||||
|
|||||||
+2
-1
@@ -4,4 +4,5 @@ using TeleWave.Application.Common.Models;
|
|||||||
namespace TeleWave.Application.Library.Collections.SetCollectionPoster;
|
namespace TeleWave.Application.Library.Collections.SetCollectionPoster;
|
||||||
|
|
||||||
/// <summary>Привязать/снять постер коллекции (<paramref name="ImageId"/> = null — отвязать).</summary>
|
/// <summary>Привязать/снять постер коллекции (<paramref name="ImageId"/> = null — отвязать).</summary>
|
||||||
public sealed record SetCollectionPosterCommand(Guid CollectionId, Guid? ImageId) : ICommand<Result>;
|
public sealed record SetCollectionPosterCommand(Guid CollectionId, Guid? ImageId)
|
||||||
|
: ICommand<Result>;
|
||||||
|
|||||||
@@ -28,7 +28,12 @@ public sealed class GetShowQueryHandler(IAppDbContext dbContext)
|
|||||||
var genreNames = await dbContext
|
var genreNames = await dbContext
|
||||||
.Genres.AsNoTracking()
|
.Genres.AsNoTracking()
|
||||||
.Where(g => genreIds.Contains(g.Id))
|
.Where(g => genreIds.Contains(g.Id))
|
||||||
.Select(g => new { g.Id, g.Name, g.SortOrder })
|
.Select(g => new
|
||||||
|
{
|
||||||
|
g.Id,
|
||||||
|
g.Name,
|
||||||
|
g.SortOrder,
|
||||||
|
})
|
||||||
.ToListAsync(cancellationToken);
|
.ToListAsync(cancellationToken);
|
||||||
|
|
||||||
var genreDtos = genreNames
|
var genreDtos = genreNames
|
||||||
|
|||||||
+5
-6
@@ -21,15 +21,14 @@ public sealed class ListInterstitialBlocksQueryHandler(IAppDbContext dbContext)
|
|||||||
|
|
||||||
// «Блок» — не отдельная сущность, а признак состава: коллекция целиком из роликов. Смешанные
|
// «Блок» — не отдельная сущность, а признак состава: коллекция целиком из роликов. Смешанные
|
||||||
// коллекции (франшизы) остаются на своём экране и сюда не попадают.
|
// коллекции (франшизы) остаются на своём экране и сюда не попадают.
|
||||||
var showIds = collections.SelectMany(c => c.Items.Select(i => i.ShowId)).Distinct().ToList();
|
var showIds = collections
|
||||||
|
.SelectMany(c => c.Items.Select(i => i.ShowId))
|
||||||
|
.Distinct()
|
||||||
|
.ToList();
|
||||||
var clips = await dbContext
|
var clips = await dbContext
|
||||||
.Shows.AsNoTracking()
|
.Shows.AsNoTracking()
|
||||||
.Where(s => showIds.Contains(s.Id) && s.Kind == ShowKind.Interstitial)
|
.Where(s => showIds.Contains(s.Id) && s.Kind == ShowKind.Interstitial)
|
||||||
.Select(s => new
|
.Select(s => new { s.Id, AssetIds = s.Episodes.Select(e => e.MediaAssetId).ToList() })
|
||||||
{
|
|
||||||
s.Id,
|
|
||||||
AssetIds = s.Episodes.Select(e => e.MediaAssetId).ToList(),
|
|
||||||
})
|
|
||||||
.ToDictionaryAsync(s => s.Id, s => s.AssetIds, cancellationToken);
|
.ToDictionaryAsync(s => s.Id, s => s.AssetIds, cancellationToken);
|
||||||
|
|
||||||
var assetIds = clips.Values.SelectMany(ids => ids).Distinct().ToList();
|
var assetIds = clips.Values.SelectMany(ids => ids).Distinct().ToList();
|
||||||
|
|||||||
+4
-3
@@ -22,7 +22,9 @@ public sealed class ListInterstitialsQueryHandler(IAppDbContext dbContext)
|
|||||||
|
|
||||||
// У ролика ровно одна «серия» — берём её ассет, чтобы показать длительность и статус обработки.
|
// У ролика ровно одна «серия» — берём её ассет, чтобы показать длительность и статус обработки.
|
||||||
var assetIds = shows
|
var assetIds = shows
|
||||||
.Select(s => s.Episodes.OrderBy(e => e.Position).Select(e => e.MediaAssetId).FirstOrDefault())
|
.Select(s =>
|
||||||
|
s.Episodes.OrderBy(e => e.Position).Select(e => e.MediaAssetId).FirstOrDefault()
|
||||||
|
)
|
||||||
.Where(id => id != Guid.Empty)
|
.Where(id => id != Guid.Empty)
|
||||||
.Distinct()
|
.Distinct()
|
||||||
.ToList();
|
.ToList();
|
||||||
@@ -44,8 +46,7 @@ public sealed class ListInterstitialsQueryHandler(IAppDbContext dbContext)
|
|||||||
.Episodes.OrderBy(e => e.Position)
|
.Episodes.OrderBy(e => e.Position)
|
||||||
.Select(e => (Guid?)e.MediaAssetId)
|
.Select(e => (Guid?)e.MediaAssetId)
|
||||||
.FirstOrDefault();
|
.FirstOrDefault();
|
||||||
var asset =
|
var asset = assetId is { } id && assets.TryGetValue(id, out var a) ? a : null;
|
||||||
assetId is { } id && assets.TryGetValue(id, out var a) ? a : null;
|
|
||||||
return new InterstitialDto(
|
return new InterstitialDto(
|
||||||
s.Id,
|
s.Id,
|
||||||
s.Name,
|
s.Name,
|
||||||
|
|||||||
@@ -78,7 +78,8 @@ public sealed class ListShowsQueryHandler(IAppDbContext dbContext)
|
|||||||
s.Year,
|
s.Year,
|
||||||
s.PosterImageId is not null,
|
s.PosterImageId is not null,
|
||||||
s.CreatedAt,
|
s.CreatedAt,
|
||||||
s.PrimaryGenreId is { } primaryId && genreNames.TryGetValue(primaryId, out var g)
|
s.PrimaryGenreId is { } primaryId
|
||||||
|
&& genreNames.TryGetValue(primaryId, out var g)
|
||||||
? g
|
? g
|
||||||
: null
|
: null
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -28,8 +28,7 @@ public sealed record ImportManualInboxResultDto(
|
|||||||
|
|
||||||
public sealed record ImportFailureDto(string RelativePath, string Reason);
|
public sealed record ImportFailureDto(string RelativePath, string Reason);
|
||||||
|
|
||||||
public sealed class ImportManualInboxCommandValidator
|
public sealed class ImportManualInboxCommandValidator : AbstractValidator<ImportManualInboxCommand>
|
||||||
: AbstractValidator<ImportManualInboxCommand>
|
|
||||||
{
|
{
|
||||||
public ImportManualInboxCommandValidator()
|
public ImportManualInboxCommandValidator()
|
||||||
{
|
{
|
||||||
|
|||||||
+2
-4
@@ -6,10 +6,8 @@ using TeleWave.Domain.Programming;
|
|||||||
|
|
||||||
namespace TeleWave.Application.Programming.Groups.AddGroupElements;
|
namespace TeleWave.Application.Programming.Groups.AddGroupElements;
|
||||||
|
|
||||||
public sealed class AddGroupElementsCommandHandler(
|
public sealed class AddGroupElementsCommandHandler(IAppDbContext dbContext, GroupStatsService stats)
|
||||||
IAppDbContext dbContext,
|
: ICommandHandler<AddGroupElementsCommand, Result<int>>
|
||||||
GroupStatsService stats
|
|
||||||
) : ICommandHandler<AddGroupElementsCommand, Result<int>>
|
|
||||||
{
|
{
|
||||||
public async Task<Result<int>> Handle(
|
public async Task<Result<int>> Handle(
|
||||||
AddGroupElementsCommand command,
|
AddGroupElementsCommand command,
|
||||||
|
|||||||
+4
-1
@@ -8,7 +8,10 @@ namespace TeleWave.Application.Programming.Groups.CreateGroup;
|
|||||||
public sealed class CreateGroupCommandHandler(IAppDbContext dbContext)
|
public sealed class CreateGroupCommandHandler(IAppDbContext dbContext)
|
||||||
: ICommandHandler<CreateGroupCommand, Result<Guid>>
|
: ICommandHandler<CreateGroupCommand, Result<Guid>>
|
||||||
{
|
{
|
||||||
public Task<Result<Guid>> Handle(CreateGroupCommand command, CancellationToken cancellationToken)
|
public Task<Result<Guid>> Handle(
|
||||||
|
CreateGroupCommand command,
|
||||||
|
CancellationToken cancellationToken
|
||||||
|
)
|
||||||
{
|
{
|
||||||
var group = Group.Create(command.Name, command.Description);
|
var group = Group.Create(command.Name, command.Description);
|
||||||
dbContext.Groups.Add(group);
|
dbContext.Groups.Add(group);
|
||||||
|
|||||||
+4
-1
@@ -8,7 +8,10 @@ namespace TeleWave.Application.Programming.Groups.DeleteGroup;
|
|||||||
public sealed class DeleteGroupCommandHandler(IAppDbContext dbContext)
|
public sealed class DeleteGroupCommandHandler(IAppDbContext dbContext)
|
||||||
: ICommandHandler<DeleteGroupCommand, Result>
|
: ICommandHandler<DeleteGroupCommand, Result>
|
||||||
{
|
{
|
||||||
public async Task<Result> Handle(DeleteGroupCommand command, CancellationToken cancellationToken)
|
public async Task<Result> Handle(
|
||||||
|
DeleteGroupCommand command,
|
||||||
|
CancellationToken cancellationToken
|
||||||
|
)
|
||||||
{
|
{
|
||||||
var group = await dbContext.Groups.FirstOrDefaultAsync(
|
var group = await dbContext.Groups.FirstOrDefaultAsync(
|
||||||
g => g.Id == command.GroupId,
|
g => g.Id == command.GroupId,
|
||||||
|
|||||||
+1
-3
@@ -58,9 +58,7 @@ public sealed class FindGroupCandidatesQueryHandler(
|
|||||||
}
|
}
|
||||||
|
|
||||||
var info = await resolver.ResolveAsync(elements, cancellationToken);
|
var info = await resolver.ResolveAsync(elements, cancellationToken);
|
||||||
var inGroup = group
|
var inGroup = group.Items.Select(i => (i.ElementKind, i.ElementId)).ToHashSet();
|
||||||
.Items.Select(i => (i.ElementKind, i.ElementId))
|
|
||||||
.ToHashSet();
|
|
||||||
|
|
||||||
var result = new List<GroupCandidateDto>();
|
var result = new List<GroupCandidateDto>();
|
||||||
foreach (var (kind, id) in elements)
|
foreach (var (kind, id) in elements)
|
||||||
|
|||||||
+2
-1
@@ -36,7 +36,8 @@ public sealed class GetGroupQueryHandler(IAppDbContext dbContext, GroupElementRe
|
|||||||
i.ElementId,
|
i.ElementId,
|
||||||
// Элемент мог исчезнуть из библиотеки между чисткой и чтением — показываем прочерк,
|
// Элемент мог исчезнуть из библиотеки между чисткой и чтением — показываем прочерк,
|
||||||
// а не роняем весь экран группы.
|
// а не роняем весь экран группы.
|
||||||
element?.Name ?? "—",
|
element?.Name
|
||||||
|
?? "—",
|
||||||
i.Weight,
|
i.Weight,
|
||||||
i.Position,
|
i.Position,
|
||||||
element?.UnitCount ?? 0,
|
element?.UnitCount ?? 0,
|
||||||
|
|||||||
@@ -27,7 +27,9 @@ public sealed record GroupElementInfo(
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class GroupElementResolver(IAppDbContext dbContext)
|
public sealed class GroupElementResolver(IAppDbContext dbContext)
|
||||||
{
|
{
|
||||||
public async Task<IReadOnlyDictionary<(GroupElementKind Kind, Guid Id), GroupElementInfo>> ResolveAsync(
|
public async Task<
|
||||||
|
IReadOnlyDictionary<(GroupElementKind Kind, Guid Id), GroupElementInfo>
|
||||||
|
> ResolveAsync(
|
||||||
IEnumerable<(GroupElementKind Kind, Guid Id)> elements,
|
IEnumerable<(GroupElementKind Kind, Guid Id)> elements,
|
||||||
CancellationToken cancellationToken
|
CancellationToken cancellationToken
|
||||||
)
|
)
|
||||||
@@ -126,7 +128,9 @@ public sealed class GroupElementResolver(IAppDbContext dbContext)
|
|||||||
DurationOf(partAssets),
|
DurationOf(partAssets),
|
||||||
null,
|
null,
|
||||||
// Рейтинг коллекции — самый строгий среди частей: по нему отбирают в детское время.
|
// Рейтинг коллекции — самый строгий среди частей: по нему отбирают в детское время.
|
||||||
parts.Count == 0 ? null : parts.Max(p => p!.Audience),
|
parts.Count == 0
|
||||||
|
? null
|
||||||
|
: parts.Max(p => p!.Audience),
|
||||||
parts.Count == 0 ? null : parts.Min(p => p!.Year),
|
parts.Count == 0 ? null : parts.Min(p => p!.Year),
|
||||||
collection.PosterImageId
|
collection.PosterImageId
|
||||||
);
|
);
|
||||||
|
|||||||
+4
-1
@@ -8,7 +8,10 @@ namespace TeleWave.Application.Programming.Groups.UpdateGroup;
|
|||||||
public sealed class UpdateGroupCommandHandler(IAppDbContext dbContext)
|
public sealed class UpdateGroupCommandHandler(IAppDbContext dbContext)
|
||||||
: ICommandHandler<UpdateGroupCommand, Result>
|
: ICommandHandler<UpdateGroupCommand, Result>
|
||||||
{
|
{
|
||||||
public async Task<Result> Handle(UpdateGroupCommand command, CancellationToken cancellationToken)
|
public async Task<Result> Handle(
|
||||||
|
UpdateGroupCommand command,
|
||||||
|
CancellationToken cancellationToken
|
||||||
|
)
|
||||||
{
|
{
|
||||||
var group = await dbContext.Groups.FirstOrDefaultAsync(
|
var group = await dbContext.Groups.FirstOrDefaultAsync(
|
||||||
g => g.Id == command.GroupId,
|
g => g.Id == command.GroupId,
|
||||||
|
|||||||
+12
-4
@@ -13,10 +13,18 @@ public sealed class UpdateGroupCommandValidator : AbstractValidator<UpdateGroupC
|
|||||||
x => x.Filter is not null,
|
x => x.Filter is not null,
|
||||||
() =>
|
() =>
|
||||||
{
|
{
|
||||||
RuleFor(x => x.Filter!.YearMin).InclusiveBetween(1870, 2200).When(x => x.Filter!.YearMin is not null);
|
RuleFor(x => x.Filter!.YearMin)
|
||||||
RuleFor(x => x.Filter!.YearMax).InclusiveBetween(1870, 2200).When(x => x.Filter!.YearMax is not null);
|
.InclusiveBetween(1870, 2200)
|
||||||
RuleFor(x => x.Filter!.UnitMinutesMin).GreaterThanOrEqualTo(0).When(x => x.Filter!.UnitMinutesMin is not null);
|
.When(x => x.Filter!.YearMin is not null);
|
||||||
RuleFor(x => x.Filter!.UnitMinutesMax).GreaterThanOrEqualTo(0).When(x => x.Filter!.UnitMinutesMax is not null);
|
RuleFor(x => x.Filter!.YearMax)
|
||||||
|
.InclusiveBetween(1870, 2200)
|
||||||
|
.When(x => x.Filter!.YearMax is not null);
|
||||||
|
RuleFor(x => x.Filter!.UnitMinutesMin)
|
||||||
|
.GreaterThanOrEqualTo(0)
|
||||||
|
.When(x => x.Filter!.UnitMinutesMin is not null);
|
||||||
|
RuleFor(x => x.Filter!.UnitMinutesMax)
|
||||||
|
.GreaterThanOrEqualTo(0)
|
||||||
|
.When(x => x.Filter!.UnitMinutesMax is not null);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-4
@@ -12,9 +12,6 @@ namespace TeleWave.Application.Programming.Planning.ApplyTemplate;
|
|||||||
public sealed record ApplyChannelTemplateCommand(Guid ChannelId) : ICommand<Result<ApplyResultDto>>;
|
public sealed record ApplyChannelTemplateCommand(Guid ChannelId) : ICommand<Result<ApplyResultDto>>;
|
||||||
|
|
||||||
/// <summary>Итог применения: сколько записей получилось и что стоит показать админу.</summary>
|
/// <summary>Итог применения: сколько записей получилось и что стоит показать админу.</summary>
|
||||||
public sealed record ApplyResultDto(
|
public sealed record ApplyResultDto(int Added, IReadOnlyList<PlanningWarningDto> Warnings);
|
||||||
int Added,
|
|
||||||
IReadOnlyList<PlanningWarningDto> Warnings
|
|
||||||
);
|
|
||||||
|
|
||||||
public sealed record PlanningWarningDto(PlanningWarningKind Kind, Guid? SlotId, string Details);
|
public sealed record PlanningWarningDto(PlanningWarningKind Kind, Guid? SlotId, string Details);
|
||||||
|
|||||||
@@ -11,7 +11,12 @@ using TeleWave.Domain.Programming.Planning;
|
|||||||
namespace TeleWave.Application.Programming.Planning;
|
namespace TeleWave.Application.Programming.Planning;
|
||||||
|
|
||||||
/// <summary>Ключ отрендеренной заставки: блок, подблок и пара шоу, между которыми она стоит.</summary>
|
/// <summary>Ключ отрендеренной заставки: блок, подблок и пара шоу, между которыми она стоит.</summary>
|
||||||
public readonly record struct BumperKey(Guid TemplateId, Guid VariantId, Guid FromShowId, Guid ToShowId);
|
public readonly record struct BumperKey(
|
||||||
|
Guid TemplateId,
|
||||||
|
Guid VariantId,
|
||||||
|
Guid FromShowId,
|
||||||
|
Guid ToShowId
|
||||||
|
);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Подставляет ассеты заставкам, которые планировщик зарезервировал. Резерв и рендер разделены
|
/// Подставляет ассеты заставкам, которые планировщик зарезервировал. Резерв и рендер разделены
|
||||||
|
|||||||
+4
-1
@@ -80,7 +80,10 @@ public sealed class PreviewApplyDiffQueryHandler(
|
|||||||
|
|
||||||
private sealed record Described(DateTimeOffset StartsAtUtc, string Label);
|
private sealed record Described(DateTimeOffset StartsAtUtc, string Label);
|
||||||
|
|
||||||
private static Described Describe(ScheduleEntry entry, IReadOnlyDictionary<Guid, string> names) =>
|
private static Described Describe(
|
||||||
|
ScheduleEntry entry,
|
||||||
|
IReadOnlyDictionary<Guid, string> names
|
||||||
|
) =>
|
||||||
new(
|
new(
|
||||||
entry.StartsAtUtc,
|
entry.StartsAtUtc,
|
||||||
entry.Kind switch
|
entry.Kind switch
|
||||||
|
|||||||
@@ -89,7 +89,13 @@ public static class EffectiveGridBuilder
|
|||||||
if (taken.Any(t => from < t.To && t.From < to))
|
if (taken.Any(t => from < t.To && t.From < to))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
result.Add(new ScheduledSlot(slot, date, ToUtc(date, slot.TargetStart, offset, dayStartTime)));
|
result.Add(
|
||||||
|
new ScheduledSlot(
|
||||||
|
slot,
|
||||||
|
date,
|
||||||
|
ToUtc(date, slot.TargetStart, offset, dayStartTime)
|
||||||
|
)
|
||||||
|
);
|
||||||
added.Add((from, to));
|
added.Add((from, to));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -132,7 +132,10 @@ public sealed class GridScheduleGenerator(
|
|||||||
foreach (var item in result.Items)
|
foreach (var item in result.Items)
|
||||||
{
|
{
|
||||||
var assetId = item.MediaAssetId;
|
var assetId = item.MediaAssetId;
|
||||||
if (item.Kind == PlannedItemKind.Bumper && !TryResolveBumper(item, bumperAssets, out assetId))
|
if (
|
||||||
|
item.Kind == PlannedItemKind.Bumper
|
||||||
|
&& !TryResolveBumper(item, bumperAssets, out assetId)
|
||||||
|
)
|
||||||
continue; // Без ассета запись стала бы дырой в ленте.
|
continue; // Без ассета запись стала бы дырой в ленте.
|
||||||
|
|
||||||
dbContext.ScheduleEntries.Add(
|
dbContext.ScheduleEntries.Add(
|
||||||
@@ -207,14 +210,21 @@ public sealed class GridScheduleGenerator(
|
|||||||
cancellationToken
|
cancellationToken
|
||||||
);
|
);
|
||||||
|
|
||||||
return result with { Warnings = [.. result.Warnings, .. postWarnings] };
|
return result with
|
||||||
|
{
|
||||||
|
Warnings = [.. result.Warnings, .. postWarnings],
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Чистит прошлое сверх окна хранения. Окно должно покрывать самое долгое остывание среди правил —
|
/// Чистит прошлое сверх окна хранения. Окно должно покрывать самое долгое остывание среди правил —
|
||||||
/// история показов берётся из самой ленты, отдельного журнала нет.
|
/// история показов берётся из самой ленты, отдельного журнала нет.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private Task CleanupAsync(Guid channelId, DateTimeOffset now, CancellationToken cancellationToken)
|
private Task CleanupAsync(
|
||||||
|
Guid channelId,
|
||||||
|
DateTimeOffset now,
|
||||||
|
CancellationToken cancellationToken
|
||||||
|
)
|
||||||
{
|
{
|
||||||
var cutoff = now.AddDays(-Math.Max(1, _options.RetentionDays));
|
var cutoff = now.AddDays(-Math.Max(1, _options.RetentionDays));
|
||||||
return dbContext
|
return dbContext
|
||||||
@@ -326,7 +336,11 @@ public sealed class GridScheduleGenerator(
|
|||||||
|
|
||||||
var strategy = ToPlanningStrategy(SlotStrategy.FromJson(slot.StrategyJson));
|
var strategy = ToPlanningStrategy(SlotStrategy.FromJson(slot.StrategyJson));
|
||||||
var cursor = states.TryGetValue(slot.Id, out var state)
|
var cursor = states.TryGetValue(slot.Id, out var state)
|
||||||
? new PlanningCursor(state.CurrentElementKind, state.CurrentElementId, state.NextUnitIndex)
|
? new PlanningCursor(
|
||||||
|
state.CurrentElementKind,
|
||||||
|
state.CurrentElementId,
|
||||||
|
state.NextUnitIndex
|
||||||
|
)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
slots.Add(
|
slots.Add(
|
||||||
@@ -354,7 +368,9 @@ public sealed class GridScheduleGenerator(
|
|||||||
),
|
),
|
||||||
rules?.AudienceAt(
|
rules?.AudienceAt(
|
||||||
TimeOnly.FromDateTime(
|
TimeOnly.FromDateTime(
|
||||||
item.StartUtc.ToOffset(TimeSpan.FromMinutes(channel.UtcOffsetMinutes)).DateTime
|
item.StartUtc.ToOffset(
|
||||||
|
TimeSpan.FromMinutes(channel.UtcOffsetMinutes)
|
||||||
|
).DateTime
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
repeatLimit
|
repeatLimit
|
||||||
@@ -489,7 +505,12 @@ public sealed class GridScheduleGenerator(
|
|||||||
|
|
||||||
var offset = TimeSpan.FromMinutes(channel.UtcOffsetMinutes);
|
var offset = TimeSpan.FromMinutes(channel.UtcOffsetMinutes);
|
||||||
var sourceDate = scheduled.BroadcastDate.AddDays(-Math.Max(1, source.DaysAgo));
|
var sourceDate = scheduled.BroadcastDate.AddDays(-Math.Max(1, source.DaysAgo));
|
||||||
var from = EffectiveGridBuilder.ToUtc(sourceDate, source.Time, offset, channel.DayStartTime);
|
var from = EffectiveGridBuilder.ToUtc(
|
||||||
|
sourceDate,
|
||||||
|
source.Time,
|
||||||
|
offset,
|
||||||
|
channel.DayStartTime
|
||||||
|
);
|
||||||
var to = from.AddMinutes(Math.Max(1, source.DurationMinutes));
|
var to = from.AddMinutes(Math.Max(1, source.DurationMinutes));
|
||||||
|
|
||||||
var entries = await dbContext
|
var entries = await dbContext
|
||||||
|
|||||||
@@ -115,7 +115,10 @@ public sealed class PostCheckRunner(IAppDbContext dbContext)
|
|||||||
.Select(s => new
|
.Select(s => new
|
||||||
{
|
{
|
||||||
s.Id,
|
s.Id,
|
||||||
GenreId = s.Genres.Where(g => g.IsPrimary).Select(g => (Guid?)g.GenreId).FirstOrDefault(),
|
GenreId = s
|
||||||
|
.Genres.Where(g => g.IsPrimary)
|
||||||
|
.Select(g => (Guid?)g.GenreId)
|
||||||
|
.FirstOrDefault(),
|
||||||
})
|
})
|
||||||
.Where(s => s.GenreId != null)
|
.Where(s => s.GenreId != null)
|
||||||
.ToDictionaryAsync(s => s.Id, s => s.GenreId!.Value, cancellationToken);
|
.ToDictionaryAsync(s => s.Id, s => s.GenreId!.Value, cancellationToken);
|
||||||
|
|||||||
+4
-3
@@ -32,8 +32,7 @@ public sealed class GetEntryTraceQueryHandler(IAppDbContext dbContext)
|
|||||||
if (entry is null)
|
if (entry is null)
|
||||||
return Result.Failure<EntryTraceDto>(ChannelErrors.NotFound);
|
return Result.Failure<EntryTraceDto>(ChannelErrors.NotFound);
|
||||||
|
|
||||||
var showName =
|
var showName = entry.ShowId is { } showId
|
||||||
entry.ShowId is { } showId
|
|
||||||
? await dbContext
|
? await dbContext
|
||||||
.Shows.AsNoTracking()
|
.Shows.AsNoTracking()
|
||||||
.Where(s => s.Id == showId)
|
.Where(s => s.Id == showId)
|
||||||
@@ -80,7 +79,9 @@ public sealed class GetEntryTraceQueryHandler(IAppDbContext dbContext)
|
|||||||
// Слот мог быть удалён или изменён после генерации — трейс от этого не портится, просто
|
// Слот мог быть удалён или изменён после генерации — трейс от этого не портится, просто
|
||||||
// часть подписей окажется пустой.
|
// часть подписей окажется пустой.
|
||||||
var slot = trace.SlotId is { } slotId
|
var slot = trace.SlotId is { } slotId
|
||||||
? await dbContext.Slots.AsNoTracking().FirstOrDefaultAsync(s => s.Id == slotId, cancellationToken)
|
? await dbContext
|
||||||
|
.Slots.AsNoTracking()
|
||||||
|
.FirstOrDefaultAsync(s => s.Id == slotId, cancellationToken)
|
||||||
: null;
|
: null;
|
||||||
var layer = slot is null
|
var layer = slot is null
|
||||||
? null
|
? null
|
||||||
|
|||||||
+4
-1
@@ -70,7 +70,10 @@ public sealed class CopyTemplateCommandHandler(IAppDbContext dbContext)
|
|||||||
var copyTemplate = ScheduleTemplate.Create(target.Id, source.Name);
|
var copyTemplate = ScheduleTemplate.Create(target.Id, source.Name);
|
||||||
copyTemplate.SetFallbackGroup(source.FallbackGroupId);
|
copyTemplate.SetFallbackGroup(source.FallbackGroupId);
|
||||||
copyTemplate.SetRules(source.RulesJson);
|
copyTemplate.SetRules(source.RulesJson);
|
||||||
if (source.DefaultJunctionId is { } defaultJunction && junctionMap.TryGetValue(defaultJunction, out var mappedDefault))
|
if (
|
||||||
|
source.DefaultJunctionId is { } defaultJunction
|
||||||
|
&& junctionMap.TryGetValue(defaultJunction, out var mappedDefault)
|
||||||
|
)
|
||||||
copyTemplate.SetDefaultJunction(mappedDefault);
|
copyTemplate.SetDefaultJunction(mappedDefault);
|
||||||
|
|
||||||
var (layers, slots) = CopyGrid(source, copyTemplate, junctionMap);
|
var (layers, slots) = CopyGrid(source, copyTemplate, junctionMap);
|
||||||
|
|||||||
+1
-2
@@ -24,8 +24,7 @@ public sealed class CreateChannelTemplateCommandHandler(IAppDbContext dbContext)
|
|||||||
|
|
||||||
// Шаблон мог остаться от прошлой жизни канала, потеряв ссылку на себя, — тогда просто
|
// Шаблон мог остаться от прошлой жизни канала, потеряв ссылку на себя, — тогда просто
|
||||||
// возвращаем его, а не заводим второй: одна сетка на канал.
|
// возвращаем его, а не заводим второй: одна сетка на канал.
|
||||||
var existing = await dbContext
|
var existing = await dbContext.ScheduleTemplates.FirstOrDefaultAsync(
|
||||||
.ScheduleTemplates.FirstOrDefaultAsync(
|
|
||||||
t => t.ChannelId == channel.Id,
|
t => t.ChannelId == channel.Id,
|
||||||
cancellationToken
|
cancellationToken
|
||||||
);
|
);
|
||||||
|
|||||||
+18
-4
@@ -31,9 +31,16 @@ public sealed class CreateLayerCommandHandler(IAppDbContext dbContext)
|
|||||||
public sealed class UpdateLayerCommandHandler(IAppDbContext dbContext)
|
public sealed class UpdateLayerCommandHandler(IAppDbContext dbContext)
|
||||||
: ICommandHandler<UpdateLayerCommand, Result>
|
: ICommandHandler<UpdateLayerCommand, Result>
|
||||||
{
|
{
|
||||||
public async Task<Result> Handle(UpdateLayerCommand command, CancellationToken cancellationToken)
|
public async Task<Result> Handle(
|
||||||
|
UpdateLayerCommand command,
|
||||||
|
CancellationToken cancellationToken
|
||||||
|
)
|
||||||
{
|
{
|
||||||
var template = await LayerLoader.ByLayerAsync(dbContext, command.LayerId, cancellationToken);
|
var template = await LayerLoader.ByLayerAsync(
|
||||||
|
dbContext,
|
||||||
|
command.LayerId,
|
||||||
|
cancellationToken
|
||||||
|
);
|
||||||
var layer = template?.FindLayer(command.LayerId);
|
var layer = template?.FindLayer(command.LayerId);
|
||||||
if (template is null || layer is null)
|
if (template is null || layer is null)
|
||||||
return Result.Failure(TemplateErrors.LayerNotFound);
|
return Result.Failure(TemplateErrors.LayerNotFound);
|
||||||
@@ -52,9 +59,16 @@ public sealed class UpdateLayerCommandHandler(IAppDbContext dbContext)
|
|||||||
public sealed class DeleteLayerCommandHandler(IAppDbContext dbContext)
|
public sealed class DeleteLayerCommandHandler(IAppDbContext dbContext)
|
||||||
: ICommandHandler<DeleteLayerCommand, Result>
|
: ICommandHandler<DeleteLayerCommand, Result>
|
||||||
{
|
{
|
||||||
public async Task<Result> Handle(DeleteLayerCommand command, CancellationToken cancellationToken)
|
public async Task<Result> Handle(
|
||||||
|
DeleteLayerCommand command,
|
||||||
|
CancellationToken cancellationToken
|
||||||
|
)
|
||||||
{
|
{
|
||||||
var template = await LayerLoader.ByLayerAsync(dbContext, command.LayerId, cancellationToken);
|
var template = await LayerLoader.ByLayerAsync(
|
||||||
|
dbContext,
|
||||||
|
command.LayerId,
|
||||||
|
cancellationToken
|
||||||
|
);
|
||||||
var layer = template?.FindLayer(command.LayerId);
|
var layer = template?.FindLayer(command.LayerId);
|
||||||
if (template is null || layer is null)
|
if (template is null || layer is null)
|
||||||
return Result.Failure(TemplateErrors.LayerNotFound);
|
return Result.Failure(TemplateErrors.LayerNotFound);
|
||||||
|
|||||||
+4
-1
@@ -292,7 +292,10 @@ public sealed class ValidateTemplateQueryHandler(IAppDbContext dbContext)
|
|||||||
|
|
||||||
// Список id собираем до запроса: проекция по материализованной коллекции внутри дерева
|
// Список id собираем до запроса: проекция по материализованной коллекции внутри дерева
|
||||||
// выражений заставляет EF пересобирать её на каждый вызов.
|
// выражений заставляет EF пересобирать её на каждый вызов.
|
||||||
var neededShowIds = showIds.Concat(partsByCollection.Select(p => p.ShowId)).Distinct().ToList();
|
var neededShowIds = showIds
|
||||||
|
.Concat(partsByCollection.Select(p => p.ShowId))
|
||||||
|
.Distinct()
|
||||||
|
.ToList();
|
||||||
|
|
||||||
var audiences = await dbContext
|
var audiences = await dbContext
|
||||||
.Shows.AsNoTracking()
|
.Shows.AsNoTracking()
|
||||||
|
|||||||
@@ -33,7 +33,12 @@ public class Genre
|
|||||||
|
|
||||||
private Genre() { }
|
private Genre() { }
|
||||||
|
|
||||||
public static Genre Create(string name, string slug, int sortOrder = 0, bool isSystem = false) =>
|
public static Genre Create(
|
||||||
|
string name,
|
||||||
|
string slug,
|
||||||
|
int sortOrder = 0,
|
||||||
|
bool isSystem = false
|
||||||
|
) =>
|
||||||
new()
|
new()
|
||||||
{
|
{
|
||||||
Id = Guid.NewGuid(),
|
Id = Guid.NewGuid(),
|
||||||
|
|||||||
@@ -36,7 +36,10 @@ public class GenreAlias
|
|||||||
var trimmed = value.Trim().ToLowerInvariant();
|
var trimmed = value.Trim().ToLowerInvariant();
|
||||||
return string.Join(
|
return string.Join(
|
||||||
' ',
|
' ',
|
||||||
trimmed.Split(' ', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
|
trimmed.Split(
|
||||||
|
' ',
|
||||||
|
StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -84,7 +84,8 @@ public class Show
|
|||||||
if (ids.Count == 0)
|
if (ids.Count == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var primary = primaryGenreId is { } candidate && ids.Contains(candidate) ? candidate : ids[0];
|
var primary =
|
||||||
|
primaryGenreId is { } candidate && ids.Contains(candidate) ? candidate : ids[0];
|
||||||
foreach (var id in ids)
|
foreach (var id in ids)
|
||||||
_genres.Add(ShowGenre.Create(Id, id, id == primary));
|
_genres.Add(ShowGenre.Create(Id, id, id == primary));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,7 +68,9 @@ public static class ElementSelector
|
|||||||
if (current is not null && HasUnitsLeft(slot, current))
|
if (current is not null && HasUnitsLeft(slot, current))
|
||||||
return Continue(slot, current);
|
return Continue(slot, current);
|
||||||
|
|
||||||
var currentIndex = current is null ? -1 : ordered.FindIndex(e => e.ElementId == current.ElementId);
|
var currentIndex = current is null
|
||||||
|
? -1
|
||||||
|
: ordered.FindIndex(e => e.ElementId == current.ElementId);
|
||||||
var nextIndex = currentIndex + 1;
|
var nextIndex = currentIndex + 1;
|
||||||
|
|
||||||
if (nextIndex >= ordered.Count)
|
if (nextIndex >= ordered.Count)
|
||||||
@@ -104,7 +106,8 @@ public static class ElementSelector
|
|||||||
var withinLimit = ApplyRepeatLimit(slot, playable, moment);
|
var withinLimit = ApplyRepeatLimit(slot, playable, moment);
|
||||||
|
|
||||||
var cooldown = TimeSpan.FromDays(Math.Max(0, slot.Strategy.CooldownDays));
|
var cooldown = TimeSpan.FromDays(Math.Max(0, slot.Strategy.CooldownDays));
|
||||||
var eligible = cooldown <= TimeSpan.Zero
|
var eligible =
|
||||||
|
cooldown <= TimeSpan.Zero
|
||||||
? withinLimit
|
? withinLimit
|
||||||
: withinLimit
|
: withinLimit
|
||||||
.Where(e => e.LastPlayedUtc is not { } last || moment - last >= cooldown)
|
.Where(e => e.LastPlayedUtc is not { } last || moment - last >= cooldown)
|
||||||
|
|||||||
@@ -123,16 +123,7 @@ public static class SchedulePlanner
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
trace = new PlanTrace(
|
trace = new PlanTrace(slot.SlotId, slot.SlotKind, null, null, null, null, drift, snapped);
|
||||||
slot.SlotId,
|
|
||||||
slot.SlotKind,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
drift,
|
|
||||||
snapped
|
|
||||||
);
|
|
||||||
return cursor;
|
return cursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -291,12 +282,7 @@ public static class SchedulePlanner
|
|||||||
slot.JunctionAfter,
|
slot.JunctionAfter,
|
||||||
cursor,
|
cursor,
|
||||||
limit,
|
limit,
|
||||||
new JunctionPlacement(
|
new JunctionPlacement(slot.SlotId, run.PreviousShowId, null, ElementChanged: true),
|
||||||
slot.SlotId,
|
|
||||||
run.PreviousShowId,
|
|
||||||
null,
|
|
||||||
ElementChanged: true
|
|
||||||
),
|
|
||||||
run.Junctions,
|
run.Junctions,
|
||||||
run.Items,
|
run.Items,
|
||||||
slotTrace
|
slotTrace
|
||||||
@@ -356,7 +342,8 @@ public static class SchedulePlanner
|
|||||||
{
|
{
|
||||||
SlotBlockMode.Count => placed < Math.Max(1, slot.BlockValue),
|
SlotBlockMode.Count => placed < Math.Max(1, slot.BlockValue),
|
||||||
// Последняя единица входит целиком: обрезать видеофайл нельзя.
|
// Последняя единица входит целиком: обрезать видеофайл нельзя.
|
||||||
SlotBlockMode.Duration => accumulated < TimeSpan.FromMinutes(Math.Max(1, slot.BlockValue)),
|
SlotBlockMode.Duration => accumulated
|
||||||
|
< TimeSpan.FromMinutes(Math.Max(1, slot.BlockValue)),
|
||||||
_ => cursor + unit.Duration <= budgetEnd,
|
_ => cursor + unit.Duration <= budgetEnd,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,7 +58,12 @@ public class ScheduleTemplate
|
|||||||
};
|
};
|
||||||
// Фоновый слой заводится сразу: без него первая же дыра в сетке осталась бы нечем закрыть.
|
// Фоновый слой заводится сразу: без него первая же дыра в сетке осталась бы нечем закрыть.
|
||||||
template._layers.Add(
|
template._layers.Add(
|
||||||
GridLayer.Create(template.Id, BackgroundLayerName, GridLayer.BackgroundPriority, isBackground: true)
|
GridLayer.Create(
|
||||||
|
template.Id,
|
||||||
|
BackgroundLayerName,
|
||||||
|
GridLayer.BackgroundPriority,
|
||||||
|
isBackground: true
|
||||||
|
)
|
||||||
);
|
);
|
||||||
return template;
|
return template;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,9 +60,7 @@ public sealed class MaintenanceBackgroundService(
|
|||||||
// Осиротевшей считается заставка, чей ассет не встречается ни в одной записи расписания.
|
// Осиротевшей считается заставка, чей ассет не встречается ни в одной записи расписания.
|
||||||
// Границу по времени не ставим: окно хранения расписания уже определяет, что живо.
|
// Границу по времени не ставим: окно хранения расписания уже определяет, что живо.
|
||||||
var removed = await db
|
var removed = await db
|
||||||
.BumperAssets.Where(b =>
|
.BumperAssets.Where(b => !db.ScheduleEntries.Any(e => e.MediaAssetId == b.MediaAssetId))
|
||||||
!db.ScheduleEntries.Any(e => e.MediaAssetId == b.MediaAssetId)
|
|
||||||
)
|
|
||||||
.ExecuteDeleteAsync(cancellationToken);
|
.ExecuteDeleteAsync(cancellationToken);
|
||||||
|
|
||||||
if (removed > 0)
|
if (removed > 0)
|
||||||
|
|||||||
@@ -41,8 +41,8 @@ internal sealed class BumperRenderBackgroundService(
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var spec = await WithScopeAsync<BumperSpecLoader, BumperRenderSpec?>(
|
var spec = await WithScopeAsync<BumperSpecLoader, BumperRenderSpec?>(loader =>
|
||||||
loader => loader.LoadAsync(job.AssetId, cancellationToken)
|
loader.LoadAsync(job.AssetId, cancellationToken)
|
||||||
);
|
);
|
||||||
if (spec is null)
|
if (spec is null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -156,12 +156,7 @@ internal abstract class MediaClaimingBackgroundService<TJob>(
|
|||||||
Guid assetId,
|
Guid assetId,
|
||||||
string error,
|
string error,
|
||||||
CancellationToken cancellationToken
|
CancellationToken cancellationToken
|
||||||
) =>
|
) => await WithAssetAsync(assetId, asset => asset.MarkFailed(error), cancellationToken);
|
||||||
await WithAssetAsync(
|
|
||||||
assetId,
|
|
||||||
asset => asset.MarkFailed(error),
|
|
||||||
cancellationToken
|
|
||||||
);
|
|
||||||
|
|
||||||
/// <summary>Находит ассет в свежем scope, применяет к нему изменение и сохраняет.</summary>
|
/// <summary>Находит ассет в свежем scope, применяет к нему изменение и сохраняет.</summary>
|
||||||
protected async Task WithAssetAsync(
|
protected async Task WithAssetAsync(
|
||||||
|
|||||||
@@ -48,7 +48,11 @@ internal sealed class MediaProcessingBackgroundService(
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var result = await processor.ProcessAsync(job.AssetId, job.Extension, cancellationToken);
|
var result = await processor.ProcessAsync(
|
||||||
|
job.AssetId,
|
||||||
|
job.Extension,
|
||||||
|
cancellationToken
|
||||||
|
);
|
||||||
await WithAssetAsync(
|
await WithAssetAsync(
|
||||||
job.AssetId,
|
job.AssetId,
|
||||||
asset =>
|
asset =>
|
||||||
|
|||||||
@@ -16,16 +16,28 @@ namespace TeleWave.Infrastructure.Migrations
|
|||||||
columns: table => new
|
columns: table => new
|
||||||
{
|
{
|
||||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
Name = table.Column<string>(type: "character varying(128)", maxLength: 128, nullable: false),
|
Name = table.Column<string>(
|
||||||
Slug = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: false),
|
type: "character varying(128)",
|
||||||
|
maxLength: 128,
|
||||||
|
nullable: false
|
||||||
|
),
|
||||||
|
Slug = table.Column<string>(
|
||||||
|
type: "character varying(64)",
|
||||||
|
maxLength: 64,
|
||||||
|
nullable: false
|
||||||
|
),
|
||||||
SortOrder = table.Column<int>(type: "integer", nullable: false),
|
SortOrder = table.Column<int>(type: "integer", nullable: false),
|
||||||
IsSystem = table.Column<bool>(type: "boolean", nullable: false),
|
IsSystem = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false)
|
CreatedAt = table.Column<DateTimeOffset>(
|
||||||
|
type: "timestamp with time zone",
|
||||||
|
nullable: false
|
||||||
|
),
|
||||||
},
|
},
|
||||||
constraints: table =>
|
constraints: table =>
|
||||||
{
|
{
|
||||||
table.PrimaryKey("PK_Genres", x => x.Id);
|
table.PrimaryKey("PK_Genres", x => x.Id);
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "GenreAliases",
|
name: "GenreAliases",
|
||||||
@@ -33,7 +45,11 @@ namespace TeleWave.Infrastructure.Migrations
|
|||||||
{
|
{
|
||||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
GenreId = table.Column<Guid>(type: "uuid", nullable: false),
|
GenreId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
Value = table.Column<string>(type: "character varying(128)", maxLength: 128, nullable: false)
|
Value = table.Column<string>(
|
||||||
|
type: "character varying(128)",
|
||||||
|
maxLength: 128,
|
||||||
|
nullable: false
|
||||||
|
),
|
||||||
},
|
},
|
||||||
constraints: table =>
|
constraints: table =>
|
||||||
{
|
{
|
||||||
@@ -43,8 +59,10 @@ namespace TeleWave.Infrastructure.Migrations
|
|||||||
column: x => x.GenreId,
|
column: x => x.GenreId,
|
||||||
principalTable: "Genres",
|
principalTable: "Genres",
|
||||||
principalColumn: "Id",
|
principalColumn: "Id",
|
||||||
onDelete: ReferentialAction.Cascade);
|
onDelete: ReferentialAction.Cascade
|
||||||
});
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "ShowGenres",
|
name: "ShowGenres",
|
||||||
@@ -52,7 +70,7 @@ namespace TeleWave.Infrastructure.Migrations
|
|||||||
{
|
{
|
||||||
ShowId = table.Column<Guid>(type: "uuid", nullable: false),
|
ShowId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
GenreId = table.Column<Guid>(type: "uuid", nullable: false),
|
GenreId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
IsPrimary = table.Column<bool>(type: "boolean", nullable: false)
|
IsPrimary = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
},
|
},
|
||||||
constraints: table =>
|
constraints: table =>
|
||||||
{
|
{
|
||||||
@@ -62,49 +80,53 @@ namespace TeleWave.Infrastructure.Migrations
|
|||||||
column: x => x.GenreId,
|
column: x => x.GenreId,
|
||||||
principalTable: "Genres",
|
principalTable: "Genres",
|
||||||
principalColumn: "Id",
|
principalColumn: "Id",
|
||||||
onDelete: ReferentialAction.Restrict);
|
onDelete: ReferentialAction.Restrict
|
||||||
|
);
|
||||||
table.ForeignKey(
|
table.ForeignKey(
|
||||||
name: "FK_ShowGenres_Shows_ShowId",
|
name: "FK_ShowGenres_Shows_ShowId",
|
||||||
column: x => x.ShowId,
|
column: x => x.ShowId,
|
||||||
principalTable: "Shows",
|
principalTable: "Shows",
|
||||||
principalColumn: "Id",
|
principalColumn: "Id",
|
||||||
onDelete: ReferentialAction.Cascade);
|
onDelete: ReferentialAction.Cascade
|
||||||
});
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_GenreAliases_GenreId",
|
name: "IX_GenreAliases_GenreId",
|
||||||
table: "GenreAliases",
|
table: "GenreAliases",
|
||||||
column: "GenreId");
|
column: "GenreId"
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_GenreAliases_Value",
|
name: "IX_GenreAliases_Value",
|
||||||
table: "GenreAliases",
|
table: "GenreAliases",
|
||||||
column: "Value",
|
column: "Value",
|
||||||
unique: true);
|
unique: true
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_Genres_Slug",
|
name: "IX_Genres_Slug",
|
||||||
table: "Genres",
|
table: "Genres",
|
||||||
column: "Slug",
|
column: "Slug",
|
||||||
unique: true);
|
unique: true
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_ShowGenres_GenreId",
|
name: "IX_ShowGenres_GenreId",
|
||||||
table: "ShowGenres",
|
table: "ShowGenres",
|
||||||
column: "GenreId");
|
column: "GenreId"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
{
|
{
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(name: "GenreAliases");
|
||||||
name: "GenreAliases");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(name: "ShowGenres");
|
||||||
name: "ShowGenres");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(name: "Genres");
|
||||||
name: "Genres");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,15 +16,27 @@ namespace TeleWave.Infrastructure.Migrations
|
|||||||
columns: table => new
|
columns: table => new
|
||||||
{
|
{
|
||||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
Name = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
Name = table.Column<string>(
|
||||||
Description = table.Column<string>(type: "character varying(2048)", maxLength: 2048, nullable: true),
|
type: "character varying(256)",
|
||||||
|
maxLength: 256,
|
||||||
|
nullable: false
|
||||||
|
),
|
||||||
|
Description = table.Column<string>(
|
||||||
|
type: "character varying(2048)",
|
||||||
|
maxLength: 2048,
|
||||||
|
nullable: true
|
||||||
|
),
|
||||||
PosterImageId = table.Column<Guid>(type: "uuid", nullable: true),
|
PosterImageId = table.Column<Guid>(type: "uuid", nullable: true),
|
||||||
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false)
|
CreatedAt = table.Column<DateTimeOffset>(
|
||||||
|
type: "timestamp with time zone",
|
||||||
|
nullable: false
|
||||||
|
),
|
||||||
},
|
},
|
||||||
constraints: table =>
|
constraints: table =>
|
||||||
{
|
{
|
||||||
table.PrimaryKey("PK_Collections", x => x.Id);
|
table.PrimaryKey("PK_Collections", x => x.Id);
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "CollectionItems",
|
name: "CollectionItems",
|
||||||
@@ -33,7 +45,7 @@ namespace TeleWave.Infrastructure.Migrations
|
|||||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
CollectionId = table.Column<Guid>(type: "uuid", nullable: false),
|
CollectionId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
ShowId = table.Column<Guid>(type: "uuid", nullable: false),
|
ShowId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
Position = table.Column<int>(type: "integer", nullable: false)
|
Position = table.Column<int>(type: "integer", nullable: false),
|
||||||
},
|
},
|
||||||
constraints: table =>
|
constraints: table =>
|
||||||
{
|
{
|
||||||
@@ -43,40 +55,44 @@ namespace TeleWave.Infrastructure.Migrations
|
|||||||
column: x => x.CollectionId,
|
column: x => x.CollectionId,
|
||||||
principalTable: "Collections",
|
principalTable: "Collections",
|
||||||
principalColumn: "Id",
|
principalColumn: "Id",
|
||||||
onDelete: ReferentialAction.Cascade);
|
onDelete: ReferentialAction.Cascade
|
||||||
|
);
|
||||||
table.ForeignKey(
|
table.ForeignKey(
|
||||||
name: "FK_CollectionItems_Shows_ShowId",
|
name: "FK_CollectionItems_Shows_ShowId",
|
||||||
column: x => x.ShowId,
|
column: x => x.ShowId,
|
||||||
principalTable: "Shows",
|
principalTable: "Shows",
|
||||||
principalColumn: "Id",
|
principalColumn: "Id",
|
||||||
onDelete: ReferentialAction.Cascade);
|
onDelete: ReferentialAction.Cascade
|
||||||
});
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_CollectionItems_CollectionId_Position",
|
name: "IX_CollectionItems_CollectionId_Position",
|
||||||
table: "CollectionItems",
|
table: "CollectionItems",
|
||||||
columns: new[] { "CollectionId", "Position" });
|
columns: new[] { "CollectionId", "Position" }
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_CollectionItems_CollectionId_ShowId",
|
name: "IX_CollectionItems_CollectionId_ShowId",
|
||||||
table: "CollectionItems",
|
table: "CollectionItems",
|
||||||
columns: new[] { "CollectionId", "ShowId" },
|
columns: new[] { "CollectionId", "ShowId" },
|
||||||
unique: true);
|
unique: true
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_CollectionItems_ShowId",
|
name: "IX_CollectionItems_ShowId",
|
||||||
table: "CollectionItems",
|
table: "CollectionItems",
|
||||||
column: "ShowId");
|
column: "ShowId"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
{
|
{
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(name: "CollectionItems");
|
||||||
name: "CollectionItems");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(name: "Collections");
|
||||||
name: "Collections");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,19 +16,34 @@ namespace TeleWave.Infrastructure.Migrations
|
|||||||
columns: table => new
|
columns: table => new
|
||||||
{
|
{
|
||||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
Name = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
Name = table.Column<string>(
|
||||||
Description = table.Column<string>(type: "character varying(2048)", maxLength: 2048, nullable: true),
|
type: "character varying(256)",
|
||||||
|
maxLength: 256,
|
||||||
|
nullable: false
|
||||||
|
),
|
||||||
|
Description = table.Column<string>(
|
||||||
|
type: "character varying(2048)",
|
||||||
|
maxLength: 2048,
|
||||||
|
nullable: true
|
||||||
|
),
|
||||||
FilterJson = table.Column<string>(type: "jsonb", nullable: true),
|
FilterJson = table.Column<string>(type: "jsonb", nullable: true),
|
||||||
ItemCount = table.Column<int>(type: "integer", nullable: false),
|
ItemCount = table.Column<int>(type: "integer", nullable: false),
|
||||||
UnitCount = table.Column<int>(type: "integer", nullable: false),
|
UnitCount = table.Column<int>(type: "integer", nullable: false),
|
||||||
TotalDuration = table.Column<TimeSpan>(type: "interval", nullable: false),
|
TotalDuration = table.Column<TimeSpan>(type: "interval", nullable: false),
|
||||||
StatsComputedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
StatsComputedAt = table.Column<DateTimeOffset>(
|
||||||
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false)
|
type: "timestamp with time zone",
|
||||||
|
nullable: true
|
||||||
|
),
|
||||||
|
CreatedAt = table.Column<DateTimeOffset>(
|
||||||
|
type: "timestamp with time zone",
|
||||||
|
nullable: false
|
||||||
|
),
|
||||||
},
|
},
|
||||||
constraints: table =>
|
constraints: table =>
|
||||||
{
|
{
|
||||||
table.PrimaryKey("PK_Groups", x => x.Id);
|
table.PrimaryKey("PK_Groups", x => x.Id);
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "GroupItems",
|
name: "GroupItems",
|
||||||
@@ -39,7 +54,7 @@ namespace TeleWave.Infrastructure.Migrations
|
|||||||
ElementKind = table.Column<int>(type: "integer", nullable: false),
|
ElementKind = table.Column<int>(type: "integer", nullable: false),
|
||||||
ElementId = table.Column<Guid>(type: "uuid", nullable: false),
|
ElementId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
Weight = table.Column<int>(type: "integer", nullable: false),
|
Weight = table.Column<int>(type: "integer", nullable: false),
|
||||||
Position = table.Column<int>(type: "integer", nullable: false)
|
Position = table.Column<int>(type: "integer", nullable: false),
|
||||||
},
|
},
|
||||||
constraints: table =>
|
constraints: table =>
|
||||||
{
|
{
|
||||||
@@ -49,34 +64,37 @@ namespace TeleWave.Infrastructure.Migrations
|
|||||||
column: x => x.GroupId,
|
column: x => x.GroupId,
|
||||||
principalTable: "Groups",
|
principalTable: "Groups",
|
||||||
principalColumn: "Id",
|
principalColumn: "Id",
|
||||||
onDelete: ReferentialAction.Cascade);
|
onDelete: ReferentialAction.Cascade
|
||||||
});
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_GroupItems_ElementKind_ElementId",
|
name: "IX_GroupItems_ElementKind_ElementId",
|
||||||
table: "GroupItems",
|
table: "GroupItems",
|
||||||
columns: new[] { "ElementKind", "ElementId" });
|
columns: new[] { "ElementKind", "ElementId" }
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_GroupItems_GroupId_ElementKind_ElementId",
|
name: "IX_GroupItems_GroupId_ElementKind_ElementId",
|
||||||
table: "GroupItems",
|
table: "GroupItems",
|
||||||
columns: new[] { "GroupId", "ElementKind", "ElementId" },
|
columns: new[] { "GroupId", "ElementKind", "ElementId" },
|
||||||
unique: true);
|
unique: true
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_GroupItems_GroupId_Position",
|
name: "IX_GroupItems_GroupId_Position",
|
||||||
table: "GroupItems",
|
table: "GroupItems",
|
||||||
columns: new[] { "GroupId", "Position" });
|
columns: new[] { "GroupId", "Position" }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
{
|
{
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(name: "GroupItems");
|
||||||
name: "GroupItems");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(name: "Groups");
|
||||||
name: "Groups");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+69
-48
@@ -16,26 +16,30 @@ namespace TeleWave.Infrastructure.Migrations
|
|||||||
table: "Channels",
|
table: "Channels",
|
||||||
type: "time without time zone",
|
type: "time without time zone",
|
||||||
nullable: false,
|
nullable: false,
|
||||||
defaultValue: new TimeOnly(0, 0, 0));
|
defaultValue: new TimeOnly(0, 0, 0)
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.AddColumn<int>(
|
migrationBuilder.AddColumn<int>(
|
||||||
name: "Number",
|
name: "Number",
|
||||||
table: "Channels",
|
table: "Channels",
|
||||||
type: "integer",
|
type: "integer",
|
||||||
nullable: true);
|
nullable: true
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.AddColumn<Guid>(
|
migrationBuilder.AddColumn<Guid>(
|
||||||
name: "TemplateId",
|
name: "TemplateId",
|
||||||
table: "Channels",
|
table: "Channels",
|
||||||
type: "uuid",
|
type: "uuid",
|
||||||
nullable: true);
|
nullable: true
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.AddColumn<int>(
|
migrationBuilder.AddColumn<int>(
|
||||||
name: "UtcOffsetMinutes",
|
name: "UtcOffsetMinutes",
|
||||||
table: "Channels",
|
table: "Channels",
|
||||||
type: "integer",
|
type: "integer",
|
||||||
nullable: false,
|
nullable: false,
|
||||||
defaultValue: 0);
|
defaultValue: 0
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "ScheduleTemplates",
|
name: "ScheduleTemplates",
|
||||||
@@ -43,16 +47,24 @@ namespace TeleWave.Infrastructure.Migrations
|
|||||||
{
|
{
|
||||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
ChannelId = table.Column<Guid>(type: "uuid", nullable: false),
|
ChannelId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
Name = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
Name = table.Column<string>(
|
||||||
|
type: "character varying(256)",
|
||||||
|
maxLength: 256,
|
||||||
|
nullable: false
|
||||||
|
),
|
||||||
FallbackGroupId = table.Column<Guid>(type: "uuid", nullable: true),
|
FallbackGroupId = table.Column<Guid>(type: "uuid", nullable: true),
|
||||||
Revision = table.Column<int>(type: "integer", nullable: false),
|
Revision = table.Column<int>(type: "integer", nullable: false),
|
||||||
AppliedRevision = table.Column<int>(type: "integer", nullable: false),
|
AppliedRevision = table.Column<int>(type: "integer", nullable: false),
|
||||||
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false)
|
CreatedAt = table.Column<DateTimeOffset>(
|
||||||
|
type: "timestamp with time zone",
|
||||||
|
nullable: false
|
||||||
|
),
|
||||||
},
|
},
|
||||||
constraints: table =>
|
constraints: table =>
|
||||||
{
|
{
|
||||||
table.PrimaryKey("PK_ScheduleTemplates", x => x.Id);
|
table.PrimaryKey("PK_ScheduleTemplates", x => x.Id);
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "GridLayers",
|
name: "GridLayers",
|
||||||
@@ -60,11 +72,15 @@ namespace TeleWave.Infrastructure.Migrations
|
|||||||
{
|
{
|
||||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
TemplateId = table.Column<Guid>(type: "uuid", nullable: false),
|
TemplateId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
Name = table.Column<string>(type: "character varying(128)", maxLength: 128, nullable: false),
|
Name = table.Column<string>(
|
||||||
|
type: "character varying(128)",
|
||||||
|
maxLength: 128,
|
||||||
|
nullable: false
|
||||||
|
),
|
||||||
Priority = table.Column<int>(type: "integer", nullable: false),
|
Priority = table.Column<int>(type: "integer", nullable: false),
|
||||||
ApplicabilityJson = table.Column<string>(type: "jsonb", nullable: true),
|
ApplicabilityJson = table.Column<string>(type: "jsonb", nullable: true),
|
||||||
IsEnabled = table.Column<bool>(type: "boolean", nullable: false),
|
IsEnabled = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
IsBackground = table.Column<bool>(type: "boolean", nullable: false)
|
IsBackground = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
},
|
},
|
||||||
constraints: table =>
|
constraints: table =>
|
||||||
{
|
{
|
||||||
@@ -74,8 +90,10 @@ namespace TeleWave.Infrastructure.Migrations
|
|||||||
column: x => x.TemplateId,
|
column: x => x.TemplateId,
|
||||||
principalTable: "ScheduleTemplates",
|
principalTable: "ScheduleTemplates",
|
||||||
principalColumn: "Id",
|
principalColumn: "Id",
|
||||||
onDelete: ReferentialAction.Cascade);
|
onDelete: ReferentialAction.Cascade
|
||||||
});
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "Slots",
|
name: "Slots",
|
||||||
@@ -84,9 +102,16 @@ namespace TeleWave.Infrastructure.Migrations
|
|||||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
LayerId = table.Column<Guid>(type: "uuid", nullable: false),
|
LayerId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
Weekday = table.Column<int>(type: "integer", nullable: true),
|
Weekday = table.Column<int>(type: "integer", nullable: true),
|
||||||
TargetStart = table.Column<TimeOnly>(type: "time without time zone", nullable: false),
|
TargetStart = table.Column<TimeOnly>(
|
||||||
|
type: "time without time zone",
|
||||||
|
nullable: false
|
||||||
|
),
|
||||||
TargetDurationMinutes = table.Column<int>(type: "integer", nullable: false),
|
TargetDurationMinutes = table.Column<int>(type: "integer", nullable: false),
|
||||||
Title = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
Title = table.Column<string>(
|
||||||
|
type: "character varying(256)",
|
||||||
|
maxLength: 256,
|
||||||
|
nullable: false
|
||||||
|
),
|
||||||
Daypart = table.Column<int>(type: "integer", nullable: false),
|
Daypart = table.Column<int>(type: "integer", nullable: false),
|
||||||
SlotKind = table.Column<int>(type: "integer", nullable: false),
|
SlotKind = table.Column<int>(type: "integer", nullable: false),
|
||||||
GroupId = table.Column<Guid>(type: "uuid", nullable: true),
|
GroupId = table.Column<Guid>(type: "uuid", nullable: true),
|
||||||
@@ -97,7 +122,7 @@ namespace TeleWave.Infrastructure.Migrations
|
|||||||
OverflowPolicy = table.Column<int>(type: "integer", nullable: false),
|
OverflowPolicy = table.Column<int>(type: "integer", nullable: false),
|
||||||
IsAnchor = table.Column<bool>(type: "boolean", nullable: false),
|
IsAnchor = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
MaxDriftMinutes = table.Column<int>(type: "integer", nullable: false),
|
MaxDriftMinutes = table.Column<int>(type: "integer", nullable: false),
|
||||||
SnapToMinutes = table.Column<int>(type: "integer", nullable: true)
|
SnapToMinutes = table.Column<int>(type: "integer", nullable: true),
|
||||||
},
|
},
|
||||||
constraints: table =>
|
constraints: table =>
|
||||||
{
|
{
|
||||||
@@ -107,14 +132,17 @@ namespace TeleWave.Infrastructure.Migrations
|
|||||||
column: x => x.LayerId,
|
column: x => x.LayerId,
|
||||||
principalTable: "GridLayers",
|
principalTable: "GridLayers",
|
||||||
principalColumn: "Id",
|
principalColumn: "Id",
|
||||||
onDelete: ReferentialAction.Cascade);
|
onDelete: ReferentialAction.Cascade
|
||||||
|
);
|
||||||
table.ForeignKey(
|
table.ForeignKey(
|
||||||
name: "FK_Slots_Groups_GroupId",
|
name: "FK_Slots_Groups_GroupId",
|
||||||
column: x => x.GroupId,
|
column: x => x.GroupId,
|
||||||
principalTable: "Groups",
|
principalTable: "Groups",
|
||||||
principalColumn: "Id",
|
principalColumn: "Id",
|
||||||
onDelete: ReferentialAction.Restrict);
|
onDelete: ReferentialAction.Restrict
|
||||||
});
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "SlotStates",
|
name: "SlotStates",
|
||||||
@@ -123,7 +151,7 @@ namespace TeleWave.Infrastructure.Migrations
|
|||||||
SlotId = table.Column<Guid>(type: "uuid", nullable: false),
|
SlotId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
CurrentElementKind = table.Column<int>(type: "integer", nullable: true),
|
CurrentElementKind = table.Column<int>(type: "integer", nullable: true),
|
||||||
CurrentElementId = table.Column<Guid>(type: "uuid", nullable: true),
|
CurrentElementId = table.Column<Guid>(type: "uuid", nullable: true),
|
||||||
NextUnitIndex = table.Column<int>(type: "integer", nullable: false)
|
NextUnitIndex = table.Column<int>(type: "integer", nullable: false),
|
||||||
},
|
},
|
||||||
constraints: table =>
|
constraints: table =>
|
||||||
{
|
{
|
||||||
@@ -133,71 +161,64 @@ namespace TeleWave.Infrastructure.Migrations
|
|||||||
column: x => x.SlotId,
|
column: x => x.SlotId,
|
||||||
principalTable: "Slots",
|
principalTable: "Slots",
|
||||||
principalColumn: "Id",
|
principalColumn: "Id",
|
||||||
onDelete: ReferentialAction.Cascade);
|
onDelete: ReferentialAction.Cascade
|
||||||
});
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_Channels_Number",
|
name: "IX_Channels_Number",
|
||||||
table: "Channels",
|
table: "Channels",
|
||||||
column: "Number",
|
column: "Number",
|
||||||
unique: true,
|
unique: true,
|
||||||
filter: "\"Number\" IS NOT NULL");
|
filter: "\"Number\" IS NOT NULL"
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_GridLayers_TemplateId_Priority",
|
name: "IX_GridLayers_TemplateId_Priority",
|
||||||
table: "GridLayers",
|
table: "GridLayers",
|
||||||
columns: new[] { "TemplateId", "Priority" });
|
columns: new[] { "TemplateId", "Priority" }
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_ScheduleTemplates_ChannelId",
|
name: "IX_ScheduleTemplates_ChannelId",
|
||||||
table: "ScheduleTemplates",
|
table: "ScheduleTemplates",
|
||||||
column: "ChannelId");
|
column: "ChannelId"
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_Slots_GroupId",
|
name: "IX_Slots_GroupId",
|
||||||
table: "Slots",
|
table: "Slots",
|
||||||
column: "GroupId");
|
column: "GroupId"
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_Slots_LayerId_TargetStart",
|
name: "IX_Slots_LayerId_TargetStart",
|
||||||
table: "Slots",
|
table: "Slots",
|
||||||
columns: new[] { "LayerId", "TargetStart" });
|
columns: new[] { "LayerId", "TargetStart" }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
{
|
{
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(name: "SlotStates");
|
||||||
name: "SlotStates");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(name: "Slots");
|
||||||
name: "Slots");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(name: "GridLayers");
|
||||||
name: "GridLayers");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(name: "ScheduleTemplates");
|
||||||
name: "ScheduleTemplates");
|
|
||||||
|
|
||||||
migrationBuilder.DropIndex(
|
migrationBuilder.DropIndex(name: "IX_Channels_Number", table: "Channels");
|
||||||
name: "IX_Channels_Number",
|
|
||||||
table: "Channels");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
migrationBuilder.DropColumn(name: "DayStartTime", table: "Channels");
|
||||||
name: "DayStartTime",
|
|
||||||
table: "Channels");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
migrationBuilder.DropColumn(name: "Number", table: "Channels");
|
||||||
name: "Number",
|
|
||||||
table: "Channels");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
migrationBuilder.DropColumn(name: "TemplateId", table: "Channels");
|
||||||
name: "TemplateId",
|
|
||||||
table: "Channels");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
migrationBuilder.DropColumn(name: "UtcOffsetMinutes", table: "Channels");
|
||||||
name: "UtcOffsetMinutes",
|
|
||||||
table: "Channels");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-12
@@ -13,24 +13,28 @@ namespace TeleWave.Infrastructure.Migrations
|
|||||||
{
|
{
|
||||||
migrationBuilder.DropIndex(
|
migrationBuilder.DropIndex(
|
||||||
name: "IX_ScheduleEntries_ChannelId_ShowId",
|
name: "IX_ScheduleEntries_ChannelId_ShowId",
|
||||||
table: "ScheduleEntries");
|
table: "ScheduleEntries"
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.AddColumn<Guid>(
|
migrationBuilder.AddColumn<Guid>(
|
||||||
name: "SlotId",
|
name: "SlotId",
|
||||||
table: "ScheduleEntries",
|
table: "ScheduleEntries",
|
||||||
type: "uuid",
|
type: "uuid",
|
||||||
nullable: true);
|
nullable: true
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.AddColumn<string>(
|
migrationBuilder.AddColumn<string>(
|
||||||
name: "TraceJson",
|
name: "TraceJson",
|
||||||
table: "ScheduleEntries",
|
table: "ScheduleEntries",
|
||||||
type: "jsonb",
|
type: "jsonb",
|
||||||
nullable: true);
|
nullable: true
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_ScheduleEntries_ChannelId_ShowId_StartsAtUtc",
|
name: "IX_ScheduleEntries_ChannelId_ShowId_StartsAtUtc",
|
||||||
table: "ScheduleEntries",
|
table: "ScheduleEntries",
|
||||||
columns: new[] { "ChannelId", "ShowId", "StartsAtUtc" });
|
columns: new[] { "ChannelId", "ShowId", "StartsAtUtc" }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -38,20 +42,18 @@ namespace TeleWave.Infrastructure.Migrations
|
|||||||
{
|
{
|
||||||
migrationBuilder.DropIndex(
|
migrationBuilder.DropIndex(
|
||||||
name: "IX_ScheduleEntries_ChannelId_ShowId_StartsAtUtc",
|
name: "IX_ScheduleEntries_ChannelId_ShowId_StartsAtUtc",
|
||||||
table: "ScheduleEntries");
|
table: "ScheduleEntries"
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
migrationBuilder.DropColumn(name: "SlotId", table: "ScheduleEntries");
|
||||||
name: "SlotId",
|
|
||||||
table: "ScheduleEntries");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
migrationBuilder.DropColumn(name: "TraceJson", table: "ScheduleEntries");
|
||||||
name: "TraceJson",
|
|
||||||
table: "ScheduleEntries");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_ScheduleEntries_ChannelId_ShowId",
|
name: "IX_ScheduleEntries_ChannelId_ShowId",
|
||||||
table: "ScheduleEntries",
|
table: "ScheduleEntries",
|
||||||
columns: new[] { "ChannelId", "ShowId" });
|
columns: new[] { "ChannelId", "ShowId" }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+61
-44
@@ -11,32 +11,21 @@ namespace TeleWave.Infrastructure.Migrations
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
{
|
{
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(name: "ChannelAd");
|
||||||
name: "ChannelAd");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(name: "ChannelShowHour");
|
||||||
name: "ChannelShowHour");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(name: "OverrideShow");
|
||||||
name: "OverrideShow");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(name: "ChannelShow");
|
||||||
name: "ChannelShow");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(name: "ProgrammingOverride");
|
||||||
name: "ProgrammingOverride");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
migrationBuilder.DropColumn(name: "AdInsertion", table: "Channels");
|
||||||
name: "AdInsertion",
|
|
||||||
table: "Channels");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
migrationBuilder.DropColumn(name: "AdsPerBreak", table: "Channels");
|
||||||
name: "AdsPerBreak",
|
|
||||||
table: "Channels");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
migrationBuilder.DropColumn(name: "NextAdIndex", table: "Channels");
|
||||||
name: "NextAdIndex",
|
|
||||||
table: "Channels");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -47,21 +36,24 @@ namespace TeleWave.Infrastructure.Migrations
|
|||||||
table: "Channels",
|
table: "Channels",
|
||||||
type: "integer",
|
type: "integer",
|
||||||
nullable: false,
|
nullable: false,
|
||||||
defaultValue: 0);
|
defaultValue: 0
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.AddColumn<int>(
|
migrationBuilder.AddColumn<int>(
|
||||||
name: "AdsPerBreak",
|
name: "AdsPerBreak",
|
||||||
table: "Channels",
|
table: "Channels",
|
||||||
type: "integer",
|
type: "integer",
|
||||||
nullable: false,
|
nullable: false,
|
||||||
defaultValue: 0);
|
defaultValue: 0
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.AddColumn<int>(
|
migrationBuilder.AddColumn<int>(
|
||||||
name: "NextAdIndex",
|
name: "NextAdIndex",
|
||||||
table: "Channels",
|
table: "Channels",
|
||||||
type: "integer",
|
type: "integer",
|
||||||
nullable: false,
|
nullable: false,
|
||||||
defaultValue: 0);
|
defaultValue: 0
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "ChannelAd",
|
name: "ChannelAd",
|
||||||
@@ -70,7 +62,7 @@ namespace TeleWave.Infrastructure.Migrations
|
|||||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
ChannelId = table.Column<Guid>(type: "uuid", nullable: false),
|
ChannelId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
MediaAssetId = table.Column<Guid>(type: "uuid", nullable: false),
|
MediaAssetId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
Position = table.Column<int>(type: "integer", nullable: false)
|
Position = table.Column<int>(type: "integer", nullable: false),
|
||||||
},
|
},
|
||||||
constraints: table =>
|
constraints: table =>
|
||||||
{
|
{
|
||||||
@@ -80,8 +72,10 @@ namespace TeleWave.Infrastructure.Migrations
|
|||||||
column: x => x.ChannelId,
|
column: x => x.ChannelId,
|
||||||
principalTable: "Channels",
|
principalTable: "Channels",
|
||||||
principalColumn: "Id",
|
principalColumn: "Id",
|
||||||
onDelete: ReferentialAction.Cascade);
|
onDelete: ReferentialAction.Cascade
|
||||||
});
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "ChannelShow",
|
name: "ChannelShow",
|
||||||
@@ -93,9 +87,13 @@ namespace TeleWave.Infrastructure.Migrations
|
|||||||
ChannelId = table.Column<Guid>(type: "uuid", nullable: false),
|
ChannelId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
IsEnabled = table.Column<bool>(type: "boolean", nullable: false),
|
IsEnabled = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
NextEpisodeIndex = table.Column<int>(type: "integer", nullable: false),
|
NextEpisodeIndex = table.Column<int>(type: "integer", nullable: false),
|
||||||
PreferredWeightMultiplier = table.Column<int>(type: "integer", nullable: false, defaultValue: 3),
|
PreferredWeightMultiplier = table.Column<int>(
|
||||||
|
type: "integer",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 3
|
||||||
|
),
|
||||||
ShowId = table.Column<Guid>(type: "uuid", nullable: false),
|
ShowId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
Weight = table.Column<int>(type: "integer", nullable: false)
|
Weight = table.Column<int>(type: "integer", nullable: false),
|
||||||
},
|
},
|
||||||
constraints: table =>
|
constraints: table =>
|
||||||
{
|
{
|
||||||
@@ -105,8 +103,10 @@ namespace TeleWave.Infrastructure.Migrations
|
|||||||
column: x => x.ChannelId,
|
column: x => x.ChannelId,
|
||||||
principalTable: "Channels",
|
principalTable: "Channels",
|
||||||
principalColumn: "Id",
|
principalColumn: "Id",
|
||||||
onDelete: ReferentialAction.Cascade);
|
onDelete: ReferentialAction.Cascade
|
||||||
});
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "ProgrammingOverride",
|
name: "ProgrammingOverride",
|
||||||
@@ -116,11 +116,17 @@ namespace TeleWave.Infrastructure.Migrations
|
|||||||
ChannelId = table.Column<Guid>(type: "uuid", nullable: false),
|
ChannelId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
DayOfWeek = table.Column<int>(type: "integer", nullable: true),
|
DayOfWeek = table.Column<int>(type: "integer", nullable: true),
|
||||||
EndMinute = table.Column<int>(type: "integer", nullable: true),
|
EndMinute = table.Column<int>(type: "integer", nullable: true),
|
||||||
EndsAtUtc = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
EndsAtUtc = table.Column<DateTimeOffset>(
|
||||||
|
type: "timestamp with time zone",
|
||||||
|
nullable: true
|
||||||
|
),
|
||||||
Mode = table.Column<int>(type: "integer", nullable: false),
|
Mode = table.Column<int>(type: "integer", nullable: false),
|
||||||
Recurrence = table.Column<int>(type: "integer", nullable: false),
|
Recurrence = table.Column<int>(type: "integer", nullable: false),
|
||||||
StartMinute = table.Column<int>(type: "integer", nullable: true),
|
StartMinute = table.Column<int>(type: "integer", nullable: true),
|
||||||
StartsAtUtc = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true)
|
StartsAtUtc = table.Column<DateTimeOffset>(
|
||||||
|
type: "timestamp with time zone",
|
||||||
|
nullable: true
|
||||||
|
),
|
||||||
},
|
},
|
||||||
constraints: table =>
|
constraints: table =>
|
||||||
{
|
{
|
||||||
@@ -130,8 +136,10 @@ namespace TeleWave.Infrastructure.Migrations
|
|||||||
column: x => x.ChannelId,
|
column: x => x.ChannelId,
|
||||||
principalTable: "Channels",
|
principalTable: "Channels",
|
||||||
principalColumn: "Id",
|
principalColumn: "Id",
|
||||||
onDelete: ReferentialAction.Cascade);
|
onDelete: ReferentialAction.Cascade
|
||||||
});
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "ChannelShowHour",
|
name: "ChannelShowHour",
|
||||||
@@ -140,7 +148,7 @@ namespace TeleWave.Infrastructure.Migrations
|
|||||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
ChannelShowId = table.Column<Guid>(type: "uuid", nullable: false),
|
ChannelShowId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
EndHour = table.Column<int>(type: "integer", nullable: false),
|
EndHour = table.Column<int>(type: "integer", nullable: false),
|
||||||
StartHour = table.Column<int>(type: "integer", nullable: false)
|
StartHour = table.Column<int>(type: "integer", nullable: false),
|
||||||
},
|
},
|
||||||
constraints: table =>
|
constraints: table =>
|
||||||
{
|
{
|
||||||
@@ -150,8 +158,10 @@ namespace TeleWave.Infrastructure.Migrations
|
|||||||
column: x => x.ChannelShowId,
|
column: x => x.ChannelShowId,
|
||||||
principalTable: "ChannelShow",
|
principalTable: "ChannelShow",
|
||||||
principalColumn: "Id",
|
principalColumn: "Id",
|
||||||
onDelete: ReferentialAction.Cascade);
|
onDelete: ReferentialAction.Cascade
|
||||||
});
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "OverrideShow",
|
name: "OverrideShow",
|
||||||
@@ -160,7 +170,7 @@ namespace TeleWave.Infrastructure.Migrations
|
|||||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
ProgrammingOverrideId = table.Column<Guid>(type: "uuid", nullable: false),
|
ProgrammingOverrideId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
ShowId = table.Column<Guid>(type: "uuid", nullable: false),
|
ShowId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
Weight = table.Column<int>(type: "integer", nullable: false)
|
Weight = table.Column<int>(type: "integer", nullable: false),
|
||||||
},
|
},
|
||||||
constraints: table =>
|
constraints: table =>
|
||||||
{
|
{
|
||||||
@@ -170,33 +180,40 @@ namespace TeleWave.Infrastructure.Migrations
|
|||||||
column: x => x.ProgrammingOverrideId,
|
column: x => x.ProgrammingOverrideId,
|
||||||
principalTable: "ProgrammingOverride",
|
principalTable: "ProgrammingOverride",
|
||||||
principalColumn: "Id",
|
principalColumn: "Id",
|
||||||
onDelete: ReferentialAction.Cascade);
|
onDelete: ReferentialAction.Cascade
|
||||||
});
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_ChannelAd_ChannelId_Position",
|
name: "IX_ChannelAd_ChannelId_Position",
|
||||||
table: "ChannelAd",
|
table: "ChannelAd",
|
||||||
columns: new[] { "ChannelId", "Position" });
|
columns: new[] { "ChannelId", "Position" }
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_ChannelShow_ChannelId_ShowId",
|
name: "IX_ChannelShow_ChannelId_ShowId",
|
||||||
table: "ChannelShow",
|
table: "ChannelShow",
|
||||||
columns: new[] { "ChannelId", "ShowId" });
|
columns: new[] { "ChannelId", "ShowId" }
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_ChannelShowHour_ChannelShowId",
|
name: "IX_ChannelShowHour_ChannelShowId",
|
||||||
table: "ChannelShowHour",
|
table: "ChannelShowHour",
|
||||||
column: "ChannelShowId");
|
column: "ChannelShowId"
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_OverrideShow_ProgrammingOverrideId",
|
name: "IX_OverrideShow_ProgrammingOverrideId",
|
||||||
table: "OverrideShow",
|
table: "OverrideShow",
|
||||||
column: "ProgrammingOverrideId");
|
column: "ProgrammingOverrideId"
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_ProgrammingOverride_ChannelId_StartsAtUtc_EndsAtUtc",
|
name: "IX_ProgrammingOverride_ChannelId_StartsAtUtc_EndsAtUtc",
|
||||||
table: "ProgrammingOverride",
|
table: "ProgrammingOverride",
|
||||||
columns: new[] { "ChannelId", "StartsAtUtc", "EndsAtUtc" });
|
columns: new[] { "ChannelId", "StartsAtUtc", "EndsAtUtc" }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+35
-26
@@ -15,19 +15,22 @@ namespace TeleWave.Infrastructure.Migrations
|
|||||||
name: "JunctionAfterId",
|
name: "JunctionAfterId",
|
||||||
table: "Slots",
|
table: "Slots",
|
||||||
type: "uuid",
|
type: "uuid",
|
||||||
nullable: true);
|
nullable: true
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.AddColumn<Guid>(
|
migrationBuilder.AddColumn<Guid>(
|
||||||
name: "JunctionBetweenId",
|
name: "JunctionBetweenId",
|
||||||
table: "Slots",
|
table: "Slots",
|
||||||
type: "uuid",
|
type: "uuid",
|
||||||
nullable: true);
|
nullable: true
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.AddColumn<Guid>(
|
migrationBuilder.AddColumn<Guid>(
|
||||||
name: "DefaultJunctionId",
|
name: "DefaultJunctionId",
|
||||||
table: "ScheduleTemplates",
|
table: "ScheduleTemplates",
|
||||||
type: "uuid",
|
type: "uuid",
|
||||||
nullable: true);
|
nullable: true
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "JunctionTemplates",
|
name: "JunctionTemplates",
|
||||||
@@ -35,13 +38,21 @@ namespace TeleWave.Infrastructure.Migrations
|
|||||||
{
|
{
|
||||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
ChannelId = table.Column<Guid>(type: "uuid", nullable: false),
|
ChannelId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
Name = table.Column<string>(type: "character varying(128)", maxLength: 128, nullable: false),
|
Name = table.Column<string>(
|
||||||
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false)
|
type: "character varying(128)",
|
||||||
|
maxLength: 128,
|
||||||
|
nullable: false
|
||||||
|
),
|
||||||
|
CreatedAt = table.Column<DateTimeOffset>(
|
||||||
|
type: "timestamp with time zone",
|
||||||
|
nullable: false
|
||||||
|
),
|
||||||
},
|
},
|
||||||
constraints: table =>
|
constraints: table =>
|
||||||
{
|
{
|
||||||
table.PrimaryKey("PK_JunctionTemplates", x => x.Id);
|
table.PrimaryKey("PK_JunctionTemplates", x => x.Id);
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "JunctionElements",
|
name: "JunctionElements",
|
||||||
@@ -56,7 +67,7 @@ namespace TeleWave.Infrastructure.Migrations
|
|||||||
AmountMode = table.Column<int>(type: "integer", nullable: false),
|
AmountMode = table.Column<int>(type: "integer", nullable: false),
|
||||||
AmountValue = table.Column<int>(type: "integer", nullable: false),
|
AmountValue = table.Column<int>(type: "integer", nullable: false),
|
||||||
IsRequired = table.Column<bool>(type: "boolean", nullable: false),
|
IsRequired = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
ConditionsJson = table.Column<string>(type: "jsonb", nullable: true)
|
ConditionsJson = table.Column<string>(type: "jsonb", nullable: true),
|
||||||
},
|
},
|
||||||
constraints: table =>
|
constraints: table =>
|
||||||
{
|
{
|
||||||
@@ -66,51 +77,49 @@ namespace TeleWave.Infrastructure.Migrations
|
|||||||
column: x => x.GroupId,
|
column: x => x.GroupId,
|
||||||
principalTable: "Groups",
|
principalTable: "Groups",
|
||||||
principalColumn: "Id",
|
principalColumn: "Id",
|
||||||
onDelete: ReferentialAction.Restrict);
|
onDelete: ReferentialAction.Restrict
|
||||||
|
);
|
||||||
table.ForeignKey(
|
table.ForeignKey(
|
||||||
name: "FK_JunctionElements_JunctionTemplates_JunctionTemplateId",
|
name: "FK_JunctionElements_JunctionTemplates_JunctionTemplateId",
|
||||||
column: x => x.JunctionTemplateId,
|
column: x => x.JunctionTemplateId,
|
||||||
principalTable: "JunctionTemplates",
|
principalTable: "JunctionTemplates",
|
||||||
principalColumn: "Id",
|
principalColumn: "Id",
|
||||||
onDelete: ReferentialAction.Cascade);
|
onDelete: ReferentialAction.Cascade
|
||||||
});
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_JunctionElements_GroupId",
|
name: "IX_JunctionElements_GroupId",
|
||||||
table: "JunctionElements",
|
table: "JunctionElements",
|
||||||
column: "GroupId");
|
column: "GroupId"
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_JunctionElements_JunctionTemplateId_Position",
|
name: "IX_JunctionElements_JunctionTemplateId_Position",
|
||||||
table: "JunctionElements",
|
table: "JunctionElements",
|
||||||
columns: new[] { "JunctionTemplateId", "Position" });
|
columns: new[] { "JunctionTemplateId", "Position" }
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_JunctionTemplates_ChannelId",
|
name: "IX_JunctionTemplates_ChannelId",
|
||||||
table: "JunctionTemplates",
|
table: "JunctionTemplates",
|
||||||
column: "ChannelId");
|
column: "ChannelId"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
{
|
{
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(name: "JunctionElements");
|
||||||
name: "JunctionElements");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(name: "JunctionTemplates");
|
||||||
name: "JunctionTemplates");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
migrationBuilder.DropColumn(name: "JunctionAfterId", table: "Slots");
|
||||||
name: "JunctionAfterId",
|
|
||||||
table: "Slots");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
migrationBuilder.DropColumn(name: "JunctionBetweenId", table: "Slots");
|
||||||
name: "JunctionBetweenId",
|
|
||||||
table: "Slots");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
migrationBuilder.DropColumn(name: "DefaultJunctionId", table: "ScheduleTemplates");
|
||||||
name: "DefaultJunctionId",
|
|
||||||
table: "ScheduleTemplates");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-16
@@ -16,21 +16,13 @@ namespace TeleWave.Infrastructure.Migrations
|
|||||||
"""UPDATE "Channels" SET "BumperSelection" = 3 WHERE "BumperSelection" = 0;"""
|
"""UPDATE "Channels" SET "BumperSelection" = 3 WHERE "BumperSelection" = 0;"""
|
||||||
);
|
);
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
migrationBuilder.DropColumn(name: "BumperEpisodeChangeChance", table: "Channels");
|
||||||
name: "BumperEpisodeChangeChance",
|
|
||||||
table: "Channels");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
migrationBuilder.DropColumn(name: "BumperMinIntervalMinutes", table: "Channels");
|
||||||
name: "BumperMinIntervalMinutes",
|
|
||||||
table: "Channels");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
migrationBuilder.DropColumn(name: "BumperShowChangeChance", table: "Channels");
|
||||||
name: "BumperShowChangeChance",
|
|
||||||
table: "Channels");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
migrationBuilder.DropColumn(name: "NextBumperIndex", table: "Channels");
|
||||||
name: "NextBumperIndex",
|
|
||||||
table: "Channels");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -41,28 +33,32 @@ namespace TeleWave.Infrastructure.Migrations
|
|||||||
table: "Channels",
|
table: "Channels",
|
||||||
type: "double precision",
|
type: "double precision",
|
||||||
nullable: false,
|
nullable: false,
|
||||||
defaultValue: 0.0);
|
defaultValue: 0.0
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.AddColumn<int>(
|
migrationBuilder.AddColumn<int>(
|
||||||
name: "BumperMinIntervalMinutes",
|
name: "BumperMinIntervalMinutes",
|
||||||
table: "Channels",
|
table: "Channels",
|
||||||
type: "integer",
|
type: "integer",
|
||||||
nullable: false,
|
nullable: false,
|
||||||
defaultValue: 0);
|
defaultValue: 0
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.AddColumn<double>(
|
migrationBuilder.AddColumn<double>(
|
||||||
name: "BumperShowChangeChance",
|
name: "BumperShowChangeChance",
|
||||||
table: "Channels",
|
table: "Channels",
|
||||||
type: "double precision",
|
type: "double precision",
|
||||||
nullable: false,
|
nullable: false,
|
||||||
defaultValue: 0.0);
|
defaultValue: 0.0
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.AddColumn<int>(
|
migrationBuilder.AddColumn<int>(
|
||||||
name: "NextBumperIndex",
|
name: "NextBumperIndex",
|
||||||
table: "Channels",
|
table: "Channels",
|
||||||
type: "integer",
|
type: "integer",
|
||||||
nullable: false,
|
nullable: false,
|
||||||
defaultValue: 0);
|
defaultValue: 0
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-4
@@ -14,15 +14,14 @@ namespace TeleWave.Infrastructure.Migrations
|
|||||||
name: "RulesJson",
|
name: "RulesJson",
|
||||||
table: "ScheduleTemplates",
|
table: "ScheduleTemplates",
|
||||||
type: "jsonb",
|
type: "jsonb",
|
||||||
nullable: true);
|
nullable: true
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
{
|
{
|
||||||
migrationBuilder.DropColumn(
|
migrationBuilder.DropColumn(name: "RulesJson", table: "ScheduleTemplates");
|
||||||
name: "RulesJson",
|
|
||||||
table: "ScheduleTemplates");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-20
@@ -16,58 +16,53 @@ namespace TeleWave.Infrastructure.Migrations
|
|||||||
table: "Channels",
|
table: "Channels",
|
||||||
type: "double precision",
|
type: "double precision",
|
||||||
nullable: false,
|
nullable: false,
|
||||||
defaultValue: 0.0);
|
defaultValue: 0.0
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.AddColumn<int>(
|
migrationBuilder.AddColumn<int>(
|
||||||
name: "LogoCorner",
|
name: "LogoCorner",
|
||||||
table: "Channels",
|
table: "Channels",
|
||||||
type: "integer",
|
type: "integer",
|
||||||
nullable: false,
|
nullable: false,
|
||||||
defaultValue: 0);
|
defaultValue: 0
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.AddColumn<Guid>(
|
migrationBuilder.AddColumn<Guid>(
|
||||||
name: "LogoImageId",
|
name: "LogoImageId",
|
||||||
table: "Channels",
|
table: "Channels",
|
||||||
type: "uuid",
|
type: "uuid",
|
||||||
nullable: true);
|
nullable: true
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.AddColumn<double>(
|
migrationBuilder.AddColumn<double>(
|
||||||
name: "LogoOpacity",
|
name: "LogoOpacity",
|
||||||
table: "Channels",
|
table: "Channels",
|
||||||
type: "double precision",
|
type: "double precision",
|
||||||
nullable: false,
|
nullable: false,
|
||||||
defaultValue: 0.0);
|
defaultValue: 0.0
|
||||||
|
);
|
||||||
|
|
||||||
migrationBuilder.AddColumn<bool>(
|
migrationBuilder.AddColumn<bool>(
|
||||||
name: "ShowClock",
|
name: "ShowClock",
|
||||||
table: "Channels",
|
table: "Channels",
|
||||||
type: "boolean",
|
type: "boolean",
|
||||||
nullable: false,
|
nullable: false,
|
||||||
defaultValue: false);
|
defaultValue: false
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
{
|
{
|
||||||
migrationBuilder.DropColumn(
|
migrationBuilder.DropColumn(name: "AnalogFilterStrength", table: "Channels");
|
||||||
name: "AnalogFilterStrength",
|
|
||||||
table: "Channels");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
migrationBuilder.DropColumn(name: "LogoCorner", table: "Channels");
|
||||||
name: "LogoCorner",
|
|
||||||
table: "Channels");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
migrationBuilder.DropColumn(name: "LogoImageId", table: "Channels");
|
||||||
name: "LogoImageId",
|
|
||||||
table: "Channels");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
migrationBuilder.DropColumn(name: "LogoOpacity", table: "Channels");
|
||||||
name: "LogoOpacity",
|
|
||||||
table: "Channels");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
migrationBuilder.DropColumn(name: "ShowClock", table: "Channels");
|
||||||
name: "ShowClock",
|
|
||||||
table: "Channels");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-4
@@ -15,15 +15,14 @@ namespace TeleWave.Infrastructure.Migrations
|
|||||||
name: "CollectionId",
|
name: "CollectionId",
|
||||||
table: "ScheduleEntries",
|
table: "ScheduleEntries",
|
||||||
type: "uuid",
|
type: "uuid",
|
||||||
nullable: true);
|
nullable: true
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
{
|
{
|
||||||
migrationBuilder.DropColumn(
|
migrationBuilder.DropColumn(name: "CollectionId", table: "ScheduleEntries");
|
||||||
name: "CollectionId",
|
|
||||||
table: "ScheduleEntries");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,8 @@ namespace TeleWave.Infrastructure.Migrations
|
|||||||
type: "integer",
|
type: "integer",
|
||||||
nullable: true,
|
nullable: true,
|
||||||
oldClrType: typeof(int),
|
oldClrType: typeof(int),
|
||||||
oldType: "integer");
|
oldType: "integer"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -30,7 +31,8 @@ namespace TeleWave.Infrastructure.Migrations
|
|||||||
defaultValue: 0,
|
defaultValue: 0,
|
||||||
oldClrType: typeof(int),
|
oldClrType: typeof(int),
|
||||||
oldType: "integer",
|
oldType: "integer",
|
||||||
oldNullable: true);
|
oldNullable: true
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-2
@@ -30,12 +30,14 @@ public class GroupItemConfiguration : IEntityTypeConfiguration<GroupItem>
|
|||||||
{
|
{
|
||||||
builder.HasIndex(x => new { x.GroupId, x.Position });
|
builder.HasIndex(x => new { x.GroupId, x.Position });
|
||||||
// Элемент входит в группу не более одного раза — иначе вес и порядок становятся неоднозначны.
|
// Элемент входит в группу не более одного раза — иначе вес и порядок становятся неоднозначны.
|
||||||
builder.HasIndex(x => new
|
builder
|
||||||
|
.HasIndex(x => new
|
||||||
{
|
{
|
||||||
x.GroupId,
|
x.GroupId,
|
||||||
x.ElementKind,
|
x.ElementKind,
|
||||||
x.ElementId,
|
x.ElementId,
|
||||||
}).IsUnique();
|
})
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
// По этому индексу чистятся позиции при удалении шоу/коллекции: внешнего ключа на
|
// По этому индексу чистятся позиции при удалении шоу/коллекции: внешнего ключа на
|
||||||
// полиморфную ссылку нет, удаление идёт командой.
|
// полиморфную ссылку нет, удаление идёт командой.
|
||||||
|
|||||||
@@ -33,7 +33,11 @@ public sealed class SiteSettings(IAppDbContext dbContext) : ISiteSettings
|
|||||||
dbContext.GetBoolSettingAsync(SettingKeys.ChannelNumbersEnabled, false, cancellationToken);
|
dbContext.GetBoolSettingAsync(SettingKeys.ChannelNumbersEnabled, false, cancellationToken);
|
||||||
|
|
||||||
public Task SetChannelNumbersEnabledAsync(bool enabled, CancellationToken cancellationToken) =>
|
public Task SetChannelNumbersEnabledAsync(bool enabled, CancellationToken cancellationToken) =>
|
||||||
UpsertAsync(SettingKeys.ChannelNumbersEnabled, enabled ? "true" : "false", cancellationToken);
|
UpsertAsync(
|
||||||
|
SettingKeys.ChannelNumbersEnabled,
|
||||||
|
enabled ? "true" : "false",
|
||||||
|
cancellationToken
|
||||||
|
);
|
||||||
|
|
||||||
private async Task UpsertAsync(string key, string value, CancellationToken cancellationToken)
|
private async Task UpsertAsync(string key, string value, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -35,9 +35,6 @@ public class ChannelHandlersTests
|
|||||||
Assert.Equal(ChannelErrors.DuplicateSlug, dup.Error);
|
Assert.Equal(ChannelErrors.DuplicateSlug, dup.Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task UpdateChannelSettings_UpdatesBumperChances()
|
public async Task UpdateChannelSettings_UpdatesBumperChances()
|
||||||
{
|
{
|
||||||
@@ -69,7 +66,4 @@ public class ChannelHandlersTests
|
|||||||
Assert.Equal(BumperFont.Sans, stored!.BumperFont);
|
Assert.Equal(BumperFont.Sans, stored!.BumperFont);
|
||||||
Assert.Equal(BumperSelection.WeightedRandom, stored.BumperSelection);
|
Assert.Equal(BumperSelection.WeightedRandom, stored.BumperSelection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ public class QueryHandlersTests
|
|||||||
private static GroupMembershipCleaner GroupCleaner(IAppDbContext db) =>
|
private static GroupMembershipCleaner GroupCleaner(IAppDbContext db) =>
|
||||||
new(db, new GroupStatsService(db, new GroupElementResolver(db)));
|
new(db, new GroupStatsService(db, new GroupElementResolver(db)));
|
||||||
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task GetChannel_UnknownId_ReturnsNotFound()
|
public async Task GetChannel_UnknownId_ReturnsNotFound()
|
||||||
{
|
{
|
||||||
@@ -152,7 +151,6 @@ public class QueryHandlersTests
|
|||||||
Assert.True(ok.IsSuccess);
|
Assert.True(ok.IsSuccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task AddAndRemoveBumperTemplate_WorkThroughStorage()
|
public async Task AddAndRemoveBumperTemplate_WorkThroughStorage()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -16,7 +16,9 @@ public class ImportManualInboxValidatorTests
|
|||||||
[InlineData(null, null)] // номера не заданы — сервер разберёт имя сам
|
[InlineData(null, null)] // номера не заданы — сервер разберёт имя сам
|
||||||
public void AllowsRealWorldNumbers(int? season, int? episode)
|
public void AllowsRealWorldNumbers(int? season, int? episode)
|
||||||
{
|
{
|
||||||
Assert.True(new ImportManualInboxCommandValidator().Validate(Command(season, episode)).IsValid);
|
Assert.True(
|
||||||
|
new ImportManualInboxCommandValidator().Validate(Command(season, episode)).IsValid
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
@@ -26,7 +28,9 @@ public class ImportManualInboxValidatorTests
|
|||||||
[InlineData(1, 1000)]
|
[InlineData(1, 1000)]
|
||||||
public void RejectsOutOfRange(int? season, int? episode)
|
public void RejectsOutOfRange(int? season, int? episode)
|
||||||
{
|
{
|
||||||
Assert.False(new ImportManualInboxCommandValidator().Validate(Command(season, episode)).IsValid);
|
Assert.False(
|
||||||
|
new ImportManualInboxCommandValidator().Validate(Command(season, episode)).IsValid
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|||||||
@@ -22,7 +22,16 @@ public class MediaStatsTests
|
|||||||
var a = Pending(name);
|
var a = Pending(name);
|
||||||
a.MarkProcessing();
|
a.MarkProcessing();
|
||||||
a.MarkReady(
|
a.MarkReady(
|
||||||
new MediaReadyInfo(TimeSpan.FromMinutes(20), 2, 600, 1920, 1080, "h264", "aac", "assets/x")
|
new MediaReadyInfo(
|
||||||
|
TimeSpan.FromMinutes(20),
|
||||||
|
2,
|
||||||
|
600,
|
||||||
|
1920,
|
||||||
|
1080,
|
||||||
|
"h264",
|
||||||
|
"aac",
|
||||||
|
"assets/x"
|
||||||
|
)
|
||||||
);
|
);
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,9 +26,7 @@ public class ApplyShowMetadataTests
|
|||||||
var result = await ApplyAsync(arranged);
|
var result = await ApplyAsync(arranged);
|
||||||
|
|
||||||
Assert.True(result);
|
Assert.True(result);
|
||||||
await arranged
|
await arranged.Provider.Received(1).GetShowAsync("42", kind, Arg.Any<CancellationToken>());
|
||||||
.Provider.Received(1)
|
|
||||||
.GetShowAsync("42", kind, Arg.Any<CancellationToken>());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -48,7 +46,10 @@ public class ApplyShowMetadataTests
|
|||||||
// «Not Rated» — это отсутствие данных. Снять им проставленный рейтинг нельзя: обновление
|
// «Not Rated» — это отсутствие данных. Снять им проставленный рейтинг нельзя: обновление
|
||||||
// метаданных тихо открыло бы взрослому шоу дорогу в детское время.
|
// метаданных тихо открыло бы взрослому шоу дорогу в детское время.
|
||||||
var arranged = await ArrangeAsync(ShowKind.Single, ShowAudience.R);
|
var arranged = await ArrangeAsync(ShowKind.Single, ShowAudience.R);
|
||||||
Respond(arranged.Provider, new ShowMetadata("42", "A", 2000, null, null, null, "Not Rated"));
|
Respond(
|
||||||
|
arranged.Provider,
|
||||||
|
new ShowMetadata("42", "A", 2000, null, null, null, "Not Rated")
|
||||||
|
);
|
||||||
|
|
||||||
await ApplyAsync(arranged);
|
await ApplyAsync(arranged);
|
||||||
|
|
||||||
@@ -63,11 +64,7 @@ public class ApplyShowMetadataTests
|
|||||||
Assert.False(await ApplyAsync(arranged));
|
Assert.False(await ApplyAsync(arranged));
|
||||||
await arranged
|
await arranged
|
||||||
.Provider.DidNotReceive()
|
.Provider.DidNotReceive()
|
||||||
.GetShowAsync(
|
.GetShowAsync(Arg.Any<string>(), Arg.Any<ShowKind>(), Arg.Any<CancellationToken>());
|
||||||
Arg.Any<string>(),
|
|
||||||
Arg.Any<ShowKind>(),
|
|
||||||
Arg.Any<CancellationToken>()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void Respond(IMetadataProvider provider, ShowMetadata meta) =>
|
private static void Respond(IMetadataProvider provider, ShowMetadata meta) =>
|
||||||
|
|||||||
@@ -38,9 +38,7 @@ public class LayerApplicabilityTests
|
|||||||
{
|
{
|
||||||
// «20 декабря — 8 января» задаётся один раз и работает в любом году, поэтому сравнение идёт
|
// «20 декабря — 8 января» задаётся один раз и работает в любом году, поэтому сравнение идёт
|
||||||
// по паре (месяц, день), а не по датам.
|
// по паре (месяц, день), а не по датам.
|
||||||
var applicability = new LayerApplicability(
|
var applicability = new LayerApplicability(AnnualRanges: [new AnnualRange(12, 20, 1, 8)]);
|
||||||
AnnualRanges: [new AnnualRange(12, 20, 1, 8)]
|
|
||||||
);
|
|
||||||
|
|
||||||
Assert.Equal(expected, applicability.Covers(new DateOnly(year, month, day)));
|
Assert.Equal(expected, applicability.Covers(new DateOnly(year, month, day)));
|
||||||
}
|
}
|
||||||
@@ -128,8 +126,12 @@ public class LayerApplicabilityTests
|
|||||||
Assert.Empty(Build(template));
|
Assert.Empty(Build(template));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AddSlot(GridLayer layer, string title, TimeOnly start, int durationMinutes) =>
|
private static void AddSlot(
|
||||||
layer.AddSlot(title, start, durationMinutes);
|
GridLayer layer,
|
||||||
|
string title,
|
||||||
|
TimeOnly start,
|
||||||
|
int durationMinutes
|
||||||
|
) => layer.AddSlot(title, start, durationMinutes);
|
||||||
|
|
||||||
private static IReadOnlyList<ScheduledSlot> Build(
|
private static IReadOnlyList<ScheduledSlot> Build(
|
||||||
ScheduleTemplate template,
|
ScheduleTemplate template,
|
||||||
|
|||||||
@@ -15,9 +15,9 @@ public class PlanningRulesTests
|
|||||||
[InlineData(2, 0, false)]
|
[InlineData(2, 0, false)]
|
||||||
public void AudienceAt_DayWindow(int hour, int minute, bool inside)
|
public void AudienceAt_DayWindow(int hour, int minute, bool inside)
|
||||||
{
|
{
|
||||||
var rules = new PlanningRules(
|
var rules = new PlanningRules([
|
||||||
[new AudienceWindow(new TimeOnly(6, 0), new TimeOnly(23, 0), ShowAudience.Pg13)]
|
new AudienceWindow(new TimeOnly(6, 0), new TimeOnly(23, 0), ShowAudience.Pg13),
|
||||||
);
|
]);
|
||||||
|
|
||||||
var result = rules.AudienceAt(new TimeOnly(hour, minute));
|
var result = rules.AudienceAt(new TimeOnly(hour, minute));
|
||||||
|
|
||||||
@@ -31,9 +31,9 @@ public class PlanningRulesTests
|
|||||||
public void AudienceAt_WindowCrossingMidnight(int hour, int minute, bool inside)
|
public void AudienceAt_WindowCrossingMidnight(int hour, int minute, bool inside)
|
||||||
{
|
{
|
||||||
// «С 23:00 до 06:00» — ночное окно, границы сравниваются в обратную сторону.
|
// «С 23:00 до 06:00» — ночное окно, границы сравниваются в обратную сторону.
|
||||||
var rules = new PlanningRules(
|
var rules = new PlanningRules([
|
||||||
[new AudienceWindow(new TimeOnly(23, 0), new TimeOnly(6, 0), ShowAudience.Nc17)]
|
new AudienceWindow(new TimeOnly(23, 0), new TimeOnly(6, 0), ShowAudience.Nc17),
|
||||||
);
|
]);
|
||||||
|
|
||||||
var result = rules.AudienceAt(new TimeOnly(hour, minute));
|
var result = rules.AudienceAt(new TimeOnly(hour, minute));
|
||||||
|
|
||||||
@@ -44,12 +44,10 @@ public class PlanningRulesTests
|
|||||||
public void AudienceAt_OverlappingWindows_TakesTheStrictest()
|
public void AudienceAt_OverlappingWindows_TakesTheStrictest()
|
||||||
{
|
{
|
||||||
// Широкое окно, случайно наложенное поверх детского, не должно его отменять.
|
// Широкое окно, случайно наложенное поверх детского, не должно его отменять.
|
||||||
var rules = new PlanningRules(
|
var rules = new PlanningRules([
|
||||||
[
|
|
||||||
new AudienceWindow(new TimeOnly(6, 0), new TimeOnly(23, 0), ShowAudience.R),
|
new AudienceWindow(new TimeOnly(6, 0), new TimeOnly(23, 0), ShowAudience.R),
|
||||||
new AudienceWindow(new TimeOnly(7, 0), new TimeOnly(10, 0), ShowAudience.G),
|
new AudienceWindow(new TimeOnly(7, 0), new TimeOnly(10, 0), ShowAudience.G),
|
||||||
]
|
]);
|
||||||
);
|
|
||||||
|
|
||||||
Assert.Equal(ShowAudience.G, rules.AudienceAt(new TimeOnly(8, 0)));
|
Assert.Equal(ShowAudience.G, rules.AudienceAt(new TimeOnly(8, 0)));
|
||||||
Assert.Equal(ShowAudience.R, rules.AudienceAt(new TimeOnly(12, 0)));
|
Assert.Equal(ShowAudience.R, rules.AudienceAt(new TimeOnly(12, 0)));
|
||||||
@@ -90,9 +88,9 @@ public class PlanningRulesTests
|
|||||||
// Рейтинг уезжает в jsonb шаблона и в API ровно тем написанием, каким приходит от источников.
|
// Рейтинг уезжает в jsonb шаблона и в API ровно тем написанием, каким приходит от источников.
|
||||||
// Round-trip этого не поймает: он одинаково зелёный и на «Pg13», а такое значение потом
|
// Round-trip этого не поймает: он одинаково зелёный и на «Pg13», а такое значение потом
|
||||||
// придётся переводить на каждой границе.
|
// придётся переводить на каждой границе.
|
||||||
var json = new PlanningRules(
|
var json = new PlanningRules([
|
||||||
[new AudienceWindow(new TimeOnly(6, 0), new TimeOnly(23, 0), ShowAudience.Pg13)]
|
new AudienceWindow(new TimeOnly(6, 0), new TimeOnly(23, 0), ShowAudience.Pg13),
|
||||||
).ToJson();
|
]).ToJson();
|
||||||
|
|
||||||
Assert.Contains("\"PG-13\"", json, StringComparison.Ordinal);
|
Assert.Contains("\"PG-13\"", json, StringComparison.Ordinal);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ namespace TeleWave.Application.Tests.Validators;
|
|||||||
|
|
||||||
public class ValidatorTests
|
public class ValidatorTests
|
||||||
{
|
{
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void UpdateBumperTextVariant_ChecksLengthsAndWeight()
|
public void UpdateBumperTextVariant_ChecksLengthsAndWeight()
|
||||||
{
|
{
|
||||||
@@ -55,7 +54,6 @@ public class ValidatorTests
|
|||||||
Assert.False(v.Validate(good with { Name = "" }).IsValid);
|
Assert.False(v.Validate(good with { Name = "" }).IsValid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ResetUserPassword_RequiresMinLength()
|
public void ResetUserPassword_RequiresMinLength()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -67,7 +67,16 @@ public class MediaAssetTests
|
|||||||
Assert.Null(asset.ProcessingDuration);
|
Assert.Null(asset.ProcessingDuration);
|
||||||
|
|
||||||
asset.MarkReady(
|
asset.MarkReady(
|
||||||
new MediaReadyInfo(TimeSpan.FromSeconds(120), 2, 60, 1920, 1080, "h264", "aac", "assets/abc")
|
new MediaReadyInfo(
|
||||||
|
TimeSpan.FromSeconds(120),
|
||||||
|
2,
|
||||||
|
60,
|
||||||
|
1920,
|
||||||
|
1080,
|
||||||
|
"h264",
|
||||||
|
"aac",
|
||||||
|
"assets/abc"
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
Assert.NotNull(asset.ProcessingDuration);
|
Assert.NotNull(asset.ProcessingDuration);
|
||||||
@@ -80,7 +89,16 @@ public class MediaAssetTests
|
|||||||
var asset = MediaAsset.Register("a.mp4", ".mp4", MediaSource.Upload);
|
var asset = MediaAsset.Register("a.mp4", ".mp4", MediaSource.Upload);
|
||||||
|
|
||||||
asset.MarkReady(
|
asset.MarkReady(
|
||||||
new MediaReadyInfo(TimeSpan.FromSeconds(120), 2, 60, 1920, 1080, "h264", "aac", "assets/abc")
|
new MediaReadyInfo(
|
||||||
|
TimeSpan.FromSeconds(120),
|
||||||
|
2,
|
||||||
|
60,
|
||||||
|
1920,
|
||||||
|
1080,
|
||||||
|
"h264",
|
||||||
|
"aac",
|
||||||
|
"assets/abc"
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
Assert.Null(asset.ProcessingDuration);
|
Assert.Null(asset.ProcessingDuration);
|
||||||
|
|||||||
@@ -125,7 +125,11 @@ public class CandidateFilterTests
|
|||||||
var teen = Element(ShowAudience.Pg13, position: 1);
|
var teen = Element(ShowAudience.Pg13, position: 1);
|
||||||
|
|
||||||
var pick = ElementSelector.Select(
|
var pick = ElementSelector.Select(
|
||||||
Slot([adult, teen], maxAudience: ShowAudience.Pg13, strategy: SlotStrategyKind.Sequential),
|
Slot(
|
||||||
|
[adult, teen],
|
||||||
|
maxAudience: ShowAudience.Pg13,
|
||||||
|
strategy: SlotStrategyKind.Sequential
|
||||||
|
),
|
||||||
T0,
|
T0,
|
||||||
new FirstAlways()
|
new FirstAlways()
|
||||||
);
|
);
|
||||||
@@ -193,9 +197,16 @@ public class CandidateFilterTests
|
|||||||
lastPlayed: T0.AddDays(-10),
|
lastPlayed: T0.AddDays(-10),
|
||||||
recentPlays: [T0.AddDays(-10), T0.AddDays(-9), T0.AddDays(-8)]
|
recentPlays: [T0.AddDays(-10), T0.AddDays(-9), T0.AddDays(-8)]
|
||||||
);
|
);
|
||||||
var recent = Element(position: 1, lastPlayed: T0.AddHours(-1), recentPlays: [T0.AddHours(-1)]);
|
var recent = Element(
|
||||||
|
position: 1,
|
||||||
|
lastPlayed: T0.AddHours(-1),
|
||||||
|
recentPlays: [T0.AddHours(-1)]
|
||||||
|
);
|
||||||
|
|
||||||
var slot = Slot([overCap, recent], repeatLimit: new RepeatLimit(WindowDays: 30, Max: 2)) with
|
var slot = Slot(
|
||||||
|
[overCap, recent],
|
||||||
|
repeatLimit: new RepeatLimit(WindowDays: 30, Max: 2)
|
||||||
|
) with
|
||||||
{
|
{
|
||||||
Strategy = new PlanningStrategy(SlotStrategyKind.RandomWithCooldown, CooldownDays: 2),
|
Strategy = new PlanningStrategy(SlotStrategyKind.RandomWithCooldown, CooldownDays: 2),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using TeleWave.Domain.Broadcast.Scheduling;
|
using TeleWave.Domain.Broadcast.Scheduling;
|
||||||
using GridPlanner = TeleWave.Domain.Programming.Planning.SchedulePlanner;
|
|
||||||
using TeleWave.Domain.Programming;
|
using TeleWave.Domain.Programming;
|
||||||
using TeleWave.Domain.Programming.Planning;
|
using TeleWave.Domain.Programming.Planning;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
using GridPlanner = TeleWave.Domain.Programming.Planning.SchedulePlanner;
|
||||||
|
|
||||||
namespace TeleWave.Domain.Tests.Programming;
|
namespace TeleWave.Domain.Tests.Programming;
|
||||||
|
|
||||||
@@ -43,7 +43,8 @@ public class GoldenScheduleTests
|
|||||||
.Items.OrderBy(i => i.StartsAtUtc)
|
.Items.OrderBy(i => i.StartsAtUtc)
|
||||||
.Select(item =>
|
.Select(item =>
|
||||||
{
|
{
|
||||||
var label = item.ShowId is { } showId && names.TryGetValue(showId, out var name)
|
var label =
|
||||||
|
item.ShowId is { } showId && names.TryGetValue(showId, out var name)
|
||||||
? $"{name}#{item.UnitIndex}"
|
? $"{name}#{item.UnitIndex}"
|
||||||
: item.Kind.ToString();
|
: item.Kind.ToString();
|
||||||
return string.Create(
|
return string.Create(
|
||||||
@@ -60,10 +61,7 @@ public class GoldenScheduleTests
|
|||||||
.Range(0, episodes)
|
.Range(0, episodes)
|
||||||
.Select(i => new PlanningUnit(Guid.NewGuid(), TimeSpan.FromMinutes(minutes), showId, i))
|
.Select(i => new PlanningUnit(Guid.NewGuid(), TimeSpan.FromMinutes(minutes), showId, i))
|
||||||
.ToList();
|
.ToList();
|
||||||
return (
|
return (new PlanningElement(GroupElementKind.Show, Guid.NewGuid(), 1, 0, units), showId);
|
||||||
new PlanningElement(GroupElementKind.Show, Guid.NewGuid(), 1, 0, units),
|
|
||||||
showId
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static PlanningSlot ContentSlot(
|
private static PlanningSlot ContentSlot(
|
||||||
@@ -129,12 +127,7 @@ public class GoldenScheduleTests
|
|||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
Assert.Equal(
|
Assert.Equal(
|
||||||
[
|
["07:00 Мультфильмы#0", "07:20 Мультфильмы#1", "07:40 Мультфильмы#2", "20:00 Кино#0"],
|
||||||
"07:00 Мультфильмы#0",
|
|
||||||
"07:20 Мультфильмы#1",
|
|
||||||
"07:40 Мультфильмы#2",
|
|
||||||
"20:00 Кино#0",
|
|
||||||
],
|
|
||||||
tape
|
tape
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -148,9 +141,7 @@ public class GoldenScheduleTests
|
|||||||
// и разбег накапливается, пока его не подберёт следующий целевой старт.
|
// и разбег накапливается, пока его не подберёт следующий целевой старт.
|
||||||
var slots = Enumerable
|
var slots = Enumerable
|
||||||
.Range(0, 3)
|
.Range(0, 3)
|
||||||
.Select(i =>
|
.Select(i => ContentSlot(Day.AddHours(i), 60, [series], SlotBlockMode.Count, 2))
|
||||||
ContentSlot(Day.AddHours(i), 60, [series], SlotBlockMode.Count, 2)
|
|
||||||
)
|
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
var input = new PlanningInput(
|
var input = new PlanningInput(
|
||||||
|
|||||||
@@ -22,10 +22,7 @@ public class JunctionFillerTests
|
|||||||
{
|
{
|
||||||
showId = Guid.NewGuid();
|
showId = Guid.NewGuid();
|
||||||
var id = showId;
|
var id = showId;
|
||||||
var units = Enumerable
|
var units = Enumerable.Range(0, episodes).Select(i => Unit(minutes, id, i)).ToList();
|
||||||
.Range(0, episodes)
|
|
||||||
.Select(i => Unit(minutes, id, i))
|
|
||||||
.ToList();
|
|
||||||
return new PlanningElement(GroupElementKind.Show, Guid.NewGuid(), 1, 0, units);
|
return new PlanningElement(GroupElementKind.Show, Guid.NewGuid(), 1, 0, units);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -116,7 +116,10 @@ public class SchedulePlannerTests
|
|||||||
{
|
{
|
||||||
var element = Element(10, 20);
|
var element = Element(10, 20);
|
||||||
var result = Run(
|
var result = Run(
|
||||||
Input([Slot(T0, 300, [element], SlotBlockMode.Duration, blockValue: 50)], horizonHours: 2)
|
Input(
|
||||||
|
[Slot(T0, 300, [element], SlotBlockMode.Duration, blockValue: 50)],
|
||||||
|
horizonHours: 2
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
// 20+20 < 50, третья добирает до 60 — обрезать видеофайл нельзя.
|
// 20+20 < 50, третья добирает до 60 — обрезать видеофайл нельзя.
|
||||||
|
|||||||
@@ -60,7 +60,9 @@ public sealed class GridScheduleGeneratorIntegrationTests(PostgresFixture fixtur
|
|||||||
Assert.True(state.NextUnitIndex > 0);
|
Assert.True(state.NextUnitIndex > 0);
|
||||||
|
|
||||||
// И шаблон помечен применённым: баннер «правила изменены» должен погаснуть.
|
// И шаблон помечен применённым: баннер «правила изменены» должен погаснуть.
|
||||||
Assert.False(verify.ScheduleTemplates.Single(t => t.Id == world.TemplateId).HasPendingChanges);
|
Assert.False(
|
||||||
|
verify.ScheduleTemplates.Single(t => t.Id == world.TemplateId).HasPendingChanges
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
[SkippableFact]
|
[SkippableFact]
|
||||||
@@ -154,7 +156,9 @@ public sealed class GridScheduleGeneratorIntegrationTests(PostgresFixture fixtur
|
|||||||
Assert.Empty(verify.ScheduleEntries.Where(e => e.ChannelId == world.ChannelId));
|
Assert.Empty(verify.ScheduleEntries.Where(e => e.ChannelId == world.ChannelId));
|
||||||
Assert.Empty(verify.SlotStates.Where(s => s.SlotId == world.SlotId));
|
Assert.Empty(verify.SlotStates.Where(s => s.SlotId == world.SlotId));
|
||||||
// Отметку о применении сухой прогон тоже не ставит: применять по-прежнему есть что.
|
// Отметку о применении сухой прогон тоже не ставит: применять по-прежнему есть что.
|
||||||
Assert.True(verify.ScheduleTemplates.Single(t => t.Id == world.TemplateId).HasPendingChanges);
|
Assert.True(
|
||||||
|
verify.ScheduleTemplates.Single(t => t.Id == world.TemplateId).HasPendingChanges
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
[SkippableFact]
|
[SkippableFact]
|
||||||
|
|||||||
@@ -29,12 +29,10 @@ public sealed class ManualInboxIntegrationTests(PostgresFixture fixture)
|
|||||||
var storage = Substitute.For<IMediaStorage>();
|
var storage = Substitute.For<IMediaStorage>();
|
||||||
storage
|
storage
|
||||||
.ListManualInbox(Arg.Any<int>())
|
.ListManualInbox(Arg.Any<int>())
|
||||||
.Returns(
|
.Returns([
|
||||||
[
|
|
||||||
new IMediaStorage.ManualInboxFile($"Сериал/{first}", first, 1000),
|
new IMediaStorage.ManualInboxFile($"Сериал/{first}", first, 1000),
|
||||||
new IMediaStorage.ManualInboxFile($"Сериал/{second}", second, 1000),
|
new IMediaStorage.ManualInboxFile($"Сериал/{second}", second, 1000),
|
||||||
]
|
]);
|
||||||
);
|
|
||||||
|
|
||||||
await using var db = fixture.CreateContext();
|
await using var db = fixture.CreateContext();
|
||||||
var result = await new ImportManualInboxCommandHandler(
|
var result = await new ImportManualInboxCommandHandler(
|
||||||
@@ -101,12 +99,10 @@ public sealed class ManualInboxIntegrationTests(PostgresFixture fixture)
|
|||||||
var storage = Substitute.For<IMediaStorage>();
|
var storage = Substitute.For<IMediaStorage>();
|
||||||
storage
|
storage
|
||||||
.ListManualInbox(Arg.Any<int>())
|
.ListManualInbox(Arg.Any<int>())
|
||||||
.Returns(
|
.Returns([
|
||||||
[
|
|
||||||
new IMediaStorage.ManualInboxFile(good, good, 1000),
|
new IMediaStorage.ManualInboxFile(good, good, 1000),
|
||||||
new IMediaStorage.ManualInboxFile("readme.txt", "readme.txt", 10),
|
new IMediaStorage.ManualInboxFile("readme.txt", "readme.txt", 10),
|
||||||
]
|
]);
|
||||||
);
|
|
||||||
|
|
||||||
await using var db = fixture.CreateContext();
|
await using var db = fixture.CreateContext();
|
||||||
var result = await new ImportManualInboxCommandHandler(
|
var result = await new ImportManualInboxCommandHandler(
|
||||||
@@ -148,7 +144,10 @@ public sealed class ManualInboxIntegrationTests(PostgresFixture fixture)
|
|||||||
db,
|
db,
|
||||||
storage,
|
storage,
|
||||||
Substitute.For<IMediaProcessingQueue>()
|
Substitute.For<IMediaProcessingQueue>()
|
||||||
).Handle(new ImportManualInboxCommand([new ManualImportItem(name, 4, 12)], showId), default);
|
).Handle(
|
||||||
|
new ImportManualInboxCommand([new ManualImportItem(name, 4, 12)], showId),
|
||||||
|
default
|
||||||
|
);
|
||||||
|
|
||||||
Assert.True(result.IsSuccess);
|
Assert.True(result.IsSuccess);
|
||||||
await db.SaveChangesAsync();
|
await db.SaveChangesAsync();
|
||||||
|
|||||||
@@ -147,7 +147,10 @@ public sealed class ManualInboxStorageTests : IDisposable
|
|||||||
|
|
||||||
private void Write(string relativePath)
|
private void Write(string relativePath)
|
||||||
{
|
{
|
||||||
var full = Path.Combine(_paths.ManualDir, relativePath.Replace('/', Path.DirectorySeparatorChar));
|
var full = Path.Combine(
|
||||||
|
_paths.ManualDir,
|
||||||
|
relativePath.Replace('/', Path.DirectorySeparatorChar)
|
||||||
|
);
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(full)!);
|
Directory.CreateDirectory(Path.GetDirectoryName(full)!);
|
||||||
File.WriteAllText(full, "x");
|
File.WriteAllText(full, "x");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,7 +100,11 @@ public sealed class TemplateOperationsIntegrationTests(PostgresFixture fixture)
|
|||||||
// Канал из старой ротации: сетки нет, ссылки на неё тоже.
|
// Канал из старой ротации: сетки нет, ссылки на неё тоже.
|
||||||
await using var seedDb = fixture.CreateContext();
|
await using var seedDb = fixture.CreateContext();
|
||||||
var suffix = Guid.NewGuid().ToString("N")[..8];
|
var suffix = Guid.NewGuid().ToString("N")[..8];
|
||||||
var channel = Channel.Create($"Без сетки {suffix}", $"nogrid-{suffix}", DateTimeOffset.UtcNow);
|
var channel = Channel.Create(
|
||||||
|
$"Без сетки {suffix}",
|
||||||
|
$"nogrid-{suffix}",
|
||||||
|
DateTimeOffset.UtcNow
|
||||||
|
);
|
||||||
seedDb.Channels.Add(channel);
|
seedDb.Channels.Add(channel);
|
||||||
await seedDb.SaveChangesAsync();
|
await seedDb.SaveChangesAsync();
|
||||||
|
|
||||||
@@ -131,7 +135,9 @@ public sealed class TemplateOperationsIntegrationTests(PostgresFixture fixture)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Два канала: у источника слой со слотом, группой и стыком; у приёмника — пустая сетка.</summary>
|
/// <summary>Два канала: у источника слой со слотом, группой и стыком; у приёмника — пустая сетка.</summary>
|
||||||
private static async Task<(Guid Source, Guid Target, Guid GroupId)> SeedPairAsync(AppDbContext db)
|
private static async Task<(Guid Source, Guid Target, Guid GroupId)> SeedPairAsync(
|
||||||
|
AppDbContext db
|
||||||
|
)
|
||||||
{
|
{
|
||||||
var suffix = Guid.NewGuid().ToString("N")[..8];
|
var suffix = Guid.NewGuid().ToString("N")[..8];
|
||||||
|
|
||||||
|
|||||||
@@ -53,7 +53,16 @@ public sealed class TransactionIntegrationTests(PostgresFixture fixture)
|
|||||||
var show = Show.Create("Show", ShowKind.Series);
|
var show = Show.Create("Show", ShowKind.Series);
|
||||||
var asset = MediaAsset.Register("ep.mkv", ".mkv", MediaSource.Upload);
|
var asset = MediaAsset.Register("ep.mkv", ".mkv", MediaSource.Upload);
|
||||||
asset.MarkReady(
|
asset.MarkReady(
|
||||||
new MediaReadyInfo(TimeSpan.FromMinutes(20), 2, 600, 1920, 1080, "h264", "aac", "assets/x")
|
new MediaReadyInfo(
|
||||||
|
TimeSpan.FromMinutes(20),
|
||||||
|
2,
|
||||||
|
600,
|
||||||
|
1920,
|
||||||
|
1080,
|
||||||
|
"h264",
|
||||||
|
"aac",
|
||||||
|
"assets/x"
|
||||||
|
)
|
||||||
);
|
);
|
||||||
show.AddEpisode(asset.Id);
|
show.AddEpisode(asset.Id);
|
||||||
var entry = ScheduleEntry.Program(
|
var entry = ScheduleEntry.Program(
|
||||||
|
|||||||
Reference in New Issue
Block a user