mirror of https://github.com/ErsatzTV/ErsatzTV.git
Browse Source
* Add shading to filler rows in the playout view * Insert rows in Playout listing for gaps in the playout (station offline) * Make FillerKind in PlayoutItemViewModel optional. Remove Unscheduled enum in FillerKind. * Correctly handle "Show Filler" also for Unscheduled fillers. * Moved the Unscheduled item generation for the playout view to GetFuturePlayoutItemsByIdHandle to handle ShowFiller * Includes for the PlayoutItemDetails moved to an extension for maintainability. * Bugfix: Page size was more than the desired for pagination because of the inserted unscheduled items. * Add specified colours for playout fillers to make them less intense. * use common queryable * add playout gap model and migrations * insert playout gaps after playout build * optimize get future playout items handler * update changelog --------- Co-authored-by: Jason Dove <1695733+jasongdove@users.noreply.github.com>pull/2409/head
21 changed files with 13131 additions and 58 deletions
@ -0,0 +1,3 @@ |
|||||||
|
namespace ErsatzTV.Application.Playouts; |
||||||
|
|
||||||
|
public record InsertPlayoutGaps(int PlayoutId) : IRequest, IBackgroundServiceRequest; |
||||||
@ -0,0 +1,53 @@ |
|||||||
|
using EFCore.BulkExtensions; |
||||||
|
using ErsatzTV.Core.Domain; |
||||||
|
using ErsatzTV.Infrastructure.Data; |
||||||
|
using Microsoft.EntityFrameworkCore; |
||||||
|
|
||||||
|
namespace ErsatzTV.Application.Playouts; |
||||||
|
|
||||||
|
public class InsertPlayoutGapsHandler(IDbContextFactory<TvContext> dbContextFactory) |
||||||
|
: IRequestHandler<InsertPlayoutGaps> |
||||||
|
{ |
||||||
|
public async Task Handle(InsertPlayoutGaps request, CancellationToken cancellationToken) |
||||||
|
{ |
||||||
|
await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken); |
||||||
|
|
||||||
|
var toAdd = new List<PlayoutGap>(); |
||||||
|
|
||||||
|
IOrderedQueryable<PlayoutItem> query = dbContext.PlayoutItems |
||||||
|
.Filter(pi => pi.PlayoutId == request.PlayoutId) |
||||||
|
.OrderBy(i => i.Start); |
||||||
|
|
||||||
|
var queue = new Queue<PlayoutItem>(query); |
||||||
|
while (queue.Count > 1) |
||||||
|
{ |
||||||
|
PlayoutItem one = queue.Dequeue(); |
||||||
|
PlayoutItem two = queue.Peek(); |
||||||
|
|
||||||
|
DateTimeOffset start = one.FinishOffset; |
||||||
|
DateTimeOffset finish = two.StartOffset; |
||||||
|
|
||||||
|
if (start == finish) |
||||||
|
{ |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
var gap = new PlayoutGap |
||||||
|
{ |
||||||
|
PlayoutId = request.PlayoutId, |
||||||
|
Start = start.UtcDateTime, |
||||||
|
Finish = finish.UtcDateTime |
||||||
|
}; |
||||||
|
|
||||||
|
toAdd.Add(gap); |
||||||
|
} |
||||||
|
|
||||||
|
// delete all existing gaps
|
||||||
|
await dbContext.PlayoutGaps |
||||||
|
.Where(pg => pg.PlayoutId == request.PlayoutId) |
||||||
|
.ExecuteDeleteAsync(cancellationToken); |
||||||
|
|
||||||
|
// insert new gaps
|
||||||
|
await dbContext.BulkInsertAsync(toAdd, cancellationToken: cancellationToken); |
||||||
|
} |
||||||
|
} |
||||||
@ -1,3 +1,5 @@ |
|||||||
namespace ErsatzTV.Application.Playouts; |
using ErsatzTV.Core.Domain.Filler; |
||||||
|
|
||||||
public record PlayoutItemViewModel(string Title, DateTimeOffset Start, DateTimeOffset Finish, string Duration); |
namespace ErsatzTV.Application.Playouts; |
||||||
|
|
||||||
|
public record PlayoutItemViewModel(string Title, DateTimeOffset Start, DateTimeOffset Finish, string Duration, Option<FillerKind> FillerKind); |
||||||
|
|||||||
@ -0,0 +1,12 @@ |
|||||||
|
namespace ErsatzTV.Core.Domain; |
||||||
|
|
||||||
|
public class PlayoutGap |
||||||
|
{ |
||||||
|
public int Id { get; set; } |
||||||
|
public int PlayoutId { get; set; } |
||||||
|
public Playout Playout { get; set; } |
||||||
|
public DateTime Start { get; set; } |
||||||
|
public DateTime Finish { get; set; } |
||||||
|
public DateTimeOffset StartOffset => new DateTimeOffset(Start, TimeSpan.Zero).ToLocalTime(); |
||||||
|
public DateTimeOffset FinishOffset => new DateTimeOffset(Finish, TimeSpan.Zero).ToLocalTime(); |
||||||
|
} |
||||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,55 @@ |
|||||||
|
using System; |
||||||
|
using Microsoft.EntityFrameworkCore.Metadata; |
||||||
|
using Microsoft.EntityFrameworkCore.Migrations; |
||||||
|
|
||||||
|
#nullable disable |
||||||
|
|
||||||
|
namespace ErsatzTV.Infrastructure.MySql.Migrations |
||||||
|
{ |
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class Add_PlayoutGap : Migration |
||||||
|
{ |
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder) |
||||||
|
{ |
||||||
|
migrationBuilder.CreateTable( |
||||||
|
name: "PlayoutGap", |
||||||
|
columns: table => new |
||||||
|
{ |
||||||
|
Id = table.Column<int>(type: "int", nullable: false) |
||||||
|
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), |
||||||
|
PlayoutId = table.Column<int>(type: "int", nullable: false), |
||||||
|
Start = table.Column<DateTime>(type: "datetime(6)", nullable: false), |
||||||
|
Finish = table.Column<DateTime>(type: "datetime(6)", nullable: false) |
||||||
|
}, |
||||||
|
constraints: table => |
||||||
|
{ |
||||||
|
table.PrimaryKey("PK_PlayoutGap", x => x.Id); |
||||||
|
table.ForeignKey( |
||||||
|
name: "FK_PlayoutGap_Playout_PlayoutId", |
||||||
|
column: x => x.PlayoutId, |
||||||
|
principalTable: "Playout", |
||||||
|
principalColumn: "Id", |
||||||
|
onDelete: ReferentialAction.Cascade); |
||||||
|
}) |
||||||
|
.Annotation("MySql:CharSet", "utf8mb4"); |
||||||
|
|
||||||
|
migrationBuilder.CreateIndex( |
||||||
|
name: "IX_PlayoutGap_PlayoutId", |
||||||
|
table: "PlayoutGap", |
||||||
|
column: "PlayoutId"); |
||||||
|
|
||||||
|
migrationBuilder.CreateIndex( |
||||||
|
name: "IX_PlayoutGap_Start_Finish", |
||||||
|
table: "PlayoutGap", |
||||||
|
columns: new[] { "Start", "Finish" }); |
||||||
|
} |
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder) |
||||||
|
{ |
||||||
|
migrationBuilder.DropTable( |
||||||
|
name: "PlayoutGap"); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,53 @@ |
|||||||
|
using System; |
||||||
|
using Microsoft.EntityFrameworkCore.Migrations; |
||||||
|
|
||||||
|
#nullable disable |
||||||
|
|
||||||
|
namespace ErsatzTV.Infrastructure.Sqlite.Migrations |
||||||
|
{ |
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class Add_PlayoutGap : Migration |
||||||
|
{ |
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder) |
||||||
|
{ |
||||||
|
migrationBuilder.CreateTable( |
||||||
|
name: "PlayoutGap", |
||||||
|
columns: table => new |
||||||
|
{ |
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false) |
||||||
|
.Annotation("Sqlite:Autoincrement", true), |
||||||
|
PlayoutId = table.Column<int>(type: "INTEGER", nullable: false), |
||||||
|
Start = table.Column<DateTime>(type: "TEXT", nullable: false), |
||||||
|
Finish = table.Column<DateTime>(type: "TEXT", nullable: false) |
||||||
|
}, |
||||||
|
constraints: table => |
||||||
|
{ |
||||||
|
table.PrimaryKey("PK_PlayoutGap", x => x.Id); |
||||||
|
table.ForeignKey( |
||||||
|
name: "FK_PlayoutGap_Playout_PlayoutId", |
||||||
|
column: x => x.PlayoutId, |
||||||
|
principalTable: "Playout", |
||||||
|
principalColumn: "Id", |
||||||
|
onDelete: ReferentialAction.Cascade); |
||||||
|
}); |
||||||
|
|
||||||
|
migrationBuilder.CreateIndex( |
||||||
|
name: "IX_PlayoutGap_PlayoutId", |
||||||
|
table: "PlayoutGap", |
||||||
|
column: "PlayoutId"); |
||||||
|
|
||||||
|
migrationBuilder.CreateIndex( |
||||||
|
name: "IX_PlayoutGap_Start_Finish", |
||||||
|
table: "PlayoutGap", |
||||||
|
columns: new[] { "Start", "Finish" }); |
||||||
|
} |
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder) |
||||||
|
{ |
||||||
|
migrationBuilder.DropTable( |
||||||
|
name: "PlayoutGap"); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,17 @@ |
|||||||
|
using ErsatzTV.Core.Domain; |
||||||
|
using Microsoft.EntityFrameworkCore; |
||||||
|
using Microsoft.EntityFrameworkCore.Metadata.Builders; |
||||||
|
|
||||||
|
namespace ErsatzTV.Infrastructure.Data.Configurations; |
||||||
|
|
||||||
|
public class PlayoutGapConfiguration : IEntityTypeConfiguration<PlayoutGap> |
||||||
|
|
||||||
|
{ |
||||||
|
public void Configure(EntityTypeBuilder<PlayoutGap> builder) |
||||||
|
{ |
||||||
|
builder.ToTable("PlayoutGap"); |
||||||
|
|
||||||
|
builder.HasIndex(p => new { p.Start, p.Finish }) |
||||||
|
.HasDatabaseName("IX_PlayoutGap_Start_Finish"); |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue