Added English voice work updater.
This commit is contained in:
118
JSMR.Infrastructure/Ingestion/EnglishVoiceWorkUpdater.cs
Normal file
118
JSMR.Infrastructure/Ingestion/EnglishVoiceWorkUpdater.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
using JSMR.Application.Common;
|
||||
using JSMR.Application.Scanning.Contracts;
|
||||
using JSMR.Application.Scanning.Ports;
|
||||
using JSMR.Domain.Entities;
|
||||
using JSMR.Infrastructure.Common.Languages;
|
||||
using JSMR.Infrastructure.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace JSMR.Infrastructure.Ingestion;
|
||||
|
||||
public class EnglishVoiceWorkUpdater(AppDbContext dbContext, ILanguageIdentifier languageIdentifier) : IVoiceWorkUpdater
|
||||
{
|
||||
public async Task<int[]> UpsertAsync(IReadOnlyCollection<VoiceWorkIngest> ingests, CancellationToken cancellationToken)
|
||||
{
|
||||
EnglishVoiceWorkUpsertContext upsertContext = await CreateUpsertContextAsync(ingests, cancellationToken);
|
||||
|
||||
foreach (VoiceWorkIngest ingest in ingests)
|
||||
{
|
||||
Validate(ingest, upsertContext);
|
||||
|
||||
VoiceWorkUpsertResult result = upsertContext.Results[ingest.ProductId];
|
||||
|
||||
if (result.Issues.Count > 0)
|
||||
{
|
||||
result.Status = VoiceWorkUpsertStatus.Skipped;
|
||||
continue;
|
||||
}
|
||||
|
||||
UpsertEnglishVoiceWork(ingest, upsertContext);
|
||||
|
||||
result.Status = VoiceWorkUpsertStatus.Updated;
|
||||
}
|
||||
|
||||
await dbContext.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return [.. upsertContext.VoiceWorks.Select(x => x.Value.VoiceWorkId)];
|
||||
}
|
||||
|
||||
private async Task<EnglishVoiceWorkUpsertContext> CreateUpsertContextAsync(IReadOnlyCollection<VoiceWorkIngest> ingests, CancellationToken cancellationToken)
|
||||
{
|
||||
string[] makerIds = [.. ingests.Select(i => i.MakerId).Where(s => !string.IsNullOrWhiteSpace(s)).Distinct()];
|
||||
string[] productIds = [.. ingests.Select(i => i.ProductId).Distinct()];
|
||||
|
||||
EnglishVoiceWorkUpsertContext upsertContext = new(
|
||||
Circles: await dbContext.Circles
|
||||
.Where(c => makerIds.Contains(c.MakerId))
|
||||
.ToDictionaryAsync(c => c.MakerId, cancellationToken),
|
||||
VoiceWorks: await dbContext.VoiceWorks
|
||||
.Where(v => productIds.Contains(v.ProductId))
|
||||
.Include(v => v.EnglishVoiceWorks)
|
||||
.Include(v => v.Localizations)
|
||||
.ToDictionaryAsync(v => v.ProductId, cancellationToken),
|
||||
Results: productIds.ToDictionary(
|
||||
productId => productId,
|
||||
productId => new VoiceWorkUpsertResult()
|
||||
)
|
||||
);
|
||||
|
||||
return upsertContext;
|
||||
}
|
||||
|
||||
private void Validate(VoiceWorkIngest ingest, EnglishVoiceWorkUpsertContext upsertContext)
|
||||
{
|
||||
VoiceWorkUpsertResult result = upsertContext.Results[ingest.ProductId];
|
||||
|
||||
bool isTitleEnglish = languageIdentifier.GetLanguage(ingest.Title) == Language.English;
|
||||
bool isDescriptionEnglish = !string.IsNullOrWhiteSpace(ingest.Description) && languageIdentifier.GetLanguage(ingest.Description) == Language.English;
|
||||
|
||||
if (!isTitleEnglish && !isDescriptionEnglish)
|
||||
{
|
||||
string message = $"Prouct title and/or description is not in English";
|
||||
result.Issues.Add(new(message, VoiceWorkUpsertIssueSeverity.Information));
|
||||
return;
|
||||
}
|
||||
|
||||
if (upsertContext.Circles.TryGetValue(ingest.MakerId, out Circle? circle) == false)
|
||||
{
|
||||
string message = $"Unable to find circle for maker id: {ingest.MakerId}";
|
||||
result.Issues.Add(new(message, VoiceWorkUpsertIssueSeverity.Error));
|
||||
return;
|
||||
}
|
||||
|
||||
if (upsertContext.VoiceWorks.TryGetValue(ingest.ProductId, out VoiceWork? voiceWork) == false)
|
||||
{
|
||||
string message = $"Unable to find voice work for product id: {ingest.ProductId}";
|
||||
result.Issues.Add(new(message, VoiceWorkUpsertIssueSeverity.Error));
|
||||
}
|
||||
}
|
||||
|
||||
private void UpsertEnglishVoiceWork(VoiceWorkIngest ingest, EnglishVoiceWorkUpsertContext upsertContext)
|
||||
{
|
||||
EnglishVoiceWork englishVoiceWork = GetOrAddEnglishVoiceWork(ingest, upsertContext);
|
||||
englishVoiceWork.ProductName = ingest.Title;
|
||||
englishVoiceWork.Description = ingest.Description;
|
||||
englishVoiceWork.IsValid = true;
|
||||
}
|
||||
|
||||
private EnglishVoiceWork GetOrAddEnglishVoiceWork(VoiceWorkIngest ingest, EnglishVoiceWorkUpsertContext upsertContext)
|
||||
{
|
||||
VoiceWork voiceWork = upsertContext.VoiceWorks[ingest.ProductId];
|
||||
EnglishVoiceWork? englishVoiceWork = voiceWork.EnglishVoiceWorks.FirstOrDefault();
|
||||
|
||||
if (englishVoiceWork is null)
|
||||
{
|
||||
englishVoiceWork = new EnglishVoiceWork
|
||||
{
|
||||
VoiceWork = voiceWork,
|
||||
ProductName = string.Empty,
|
||||
Description = string.Empty
|
||||
};
|
||||
|
||||
dbContext.EnglishVoiceWorks.Add(englishVoiceWork);
|
||||
//upsertContext.VoiceWorks[ingest.ProductId] = voiceWork;
|
||||
}
|
||||
|
||||
return englishVoiceWork;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using JSMR.Domain.Entities;
|
||||
|
||||
namespace JSMR.Infrastructure.Ingestion;
|
||||
|
||||
public record EnglishVoiceWorkUpsertContext(
|
||||
Dictionary<string, Circle> Circles,
|
||||
Dictionary<string, VoiceWork> VoiceWorks,
|
||||
Dictionary<string, VoiceWorkUpsertResult> Results
|
||||
);
|
||||
108
JSMR.Infrastructure/Ingestion/VoiceWorkSearchUpdater.cs
Normal file
108
JSMR.Infrastructure/Ingestion/VoiceWorkSearchUpdater.cs
Normal file
@@ -0,0 +1,108 @@
|
||||
using JSMR.Application.Scanning.Ports;
|
||||
using JSMR.Domain.Entities;
|
||||
using JSMR.Infrastructure.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Text;
|
||||
|
||||
namespace JSMR.Infrastructure.Ingestion;
|
||||
|
||||
public class VoiceWorkSearchUpdater(AppDbContext dbContext) : IVoiceWorkSearchUpdater
|
||||
{
|
||||
public async Task UpdateAsync(int[] voiceWorkIds, CancellationToken cancellationToken)
|
||||
{
|
||||
List<VoiceWork> batch = await dbContext.VoiceWorks
|
||||
.Include(vw => vw.Circle)
|
||||
.Include(vw => vw.Tags)
|
||||
.ThenInclude(vwt => vwt.Tag)
|
||||
.Include(vw => vw.Creators)
|
||||
.ThenInclude(vwc => vwc.Creator)
|
||||
.Include(vw => vw.EnglishVoiceWorks)
|
||||
.Where(vw => voiceWorkIds.Contains(vw.VoiceWorkId))
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
foreach (var voiceWork in batch)
|
||||
{
|
||||
try
|
||||
{
|
||||
UpdateSearchText(voiceWork);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"⚠️ Error updating VoiceWorkId {voiceWork.VoiceWorkId}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
dbContext.SaveChanges();
|
||||
}
|
||||
|
||||
private void UpdateSearchText(VoiceWork voiceWork)
|
||||
{
|
||||
string searchText = GetSearchText(voiceWork);
|
||||
|
||||
var searchEntry = dbContext.VoiceWorkSearches
|
||||
.FirstOrDefault(s => s.VoiceWorkId == voiceWork.VoiceWorkId);
|
||||
|
||||
if (searchEntry == null)
|
||||
{
|
||||
dbContext.VoiceWorkSearches.Add(new VoiceWorkSearch
|
||||
{
|
||||
VoiceWorkId = voiceWork.VoiceWorkId,
|
||||
SearchText = searchText
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
searchEntry.SearchText = searchText;
|
||||
}
|
||||
}
|
||||
|
||||
private string GetSearchText(VoiceWork voiceWork)
|
||||
{
|
||||
var english = voiceWork.EnglishVoiceWorks.FirstOrDefault();
|
||||
|
||||
var sb = new StringBuilder();
|
||||
|
||||
AppendRaw(sb, voiceWork.ProductId);
|
||||
AppendRaw(sb, voiceWork.Circle?.MakerId);
|
||||
|
||||
AppendRaw(sb, english?.ProductName);
|
||||
AppendRaw(sb, english?.Description);
|
||||
|
||||
AppendRaw(sb, voiceWork.ProductName);
|
||||
AppendRaw(sb, voiceWork.Description);
|
||||
AppendRaw(sb, voiceWork.Circle?.Name);
|
||||
|
||||
foreach (var tag in voiceWork.Tags.Select(vwt => vwt.Tag))
|
||||
{
|
||||
if (tag is null)
|
||||
continue;
|
||||
|
||||
AppendRaw(sb, tag.Name);
|
||||
|
||||
var englishTag = dbContext.EnglishTags.FirstOrDefault(et => et.TagId == tag.TagId);
|
||||
|
||||
if (englishTag is null)
|
||||
continue;
|
||||
|
||||
AppendRaw(sb, englishTag?.Name);
|
||||
}
|
||||
|
||||
foreach (var creator in voiceWork.Creators.Select(vwc => vwc.Creator))
|
||||
{
|
||||
if (creator is null)
|
||||
continue;
|
||||
|
||||
AppendRaw(sb, creator?.Name);
|
||||
}
|
||||
|
||||
string searchText = sb.ToString().Trim();
|
||||
|
||||
return searchText;
|
||||
}
|
||||
|
||||
private static void AppendRaw(StringBuilder sb, string? text)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(text))
|
||||
sb.Append(text).Append(' ');
|
||||
}
|
||||
}
|
||||
337
JSMR.Infrastructure/Ingestion/VoiceWorkUpdater.cs
Normal file
337
JSMR.Infrastructure/Ingestion/VoiceWorkUpdater.cs
Normal file
@@ -0,0 +1,337 @@
|
||||
using JSMR.Application.Common;
|
||||
using JSMR.Application.Scanning.Contracts;
|
||||
using JSMR.Application.Scanning.Ports;
|
||||
using JSMR.Domain.Entities;
|
||||
using JSMR.Infrastructure.Common.Time;
|
||||
using JSMR.Infrastructure.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace JSMR.Infrastructure.Ingestion;
|
||||
|
||||
public class VoiceWorkUpdater(AppDbContext dbContext, ITimeProvider timeProvider) : IVoiceWorkUpdater
|
||||
{
|
||||
public async Task<int[]> UpsertAsync(IReadOnlyCollection<VoiceWorkIngest> ingests, CancellationToken cancellationToken)
|
||||
{
|
||||
VoiceWorkUpsertContext upsertContext = await CreateUpsertContextAsync(ingests, cancellationToken);
|
||||
|
||||
foreach (VoiceWorkIngest ingest in ingests)
|
||||
{
|
||||
Validate(ingest, upsertContext);
|
||||
|
||||
VoiceWorkUpsertResult result = upsertContext.Results[ingest.ProductId];
|
||||
|
||||
if (result.Issues.Count > 0)
|
||||
{
|
||||
result.Status = VoiceWorkUpsertStatus.Skipped;
|
||||
continue;
|
||||
}
|
||||
|
||||
Upsert(ingest, upsertContext);
|
||||
}
|
||||
|
||||
await dbContext.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return [.. upsertContext.VoiceWorks.Select(x => x.Value.VoiceWorkId)];
|
||||
}
|
||||
|
||||
private async Task<VoiceWorkUpsertContext> CreateUpsertContextAsync(IReadOnlyCollection<VoiceWorkIngest> ingests, CancellationToken cancellationToken)
|
||||
{
|
||||
string[] makerIds = [.. ingests.Select(i => i.MakerId).Where(s => !string.IsNullOrWhiteSpace(s)).Distinct()];
|
||||
string[] productIds = [.. ingests.Select(i => i.ProductId).Distinct()];
|
||||
string[] tagNames = [.. ingests.SelectMany(i => i.Tags).Distinct()];
|
||||
string[] creatorNames = [.. ingests.SelectMany(i => i.Creators).Distinct()];
|
||||
|
||||
DateTimeOffset currentScanAnchor = GetCurrentScanAnchor();
|
||||
DateTimeOffset previousScanAnchor = PreviousScanAnchor(currentScanAnchor);
|
||||
|
||||
VoiceWorkUpsertContext upsertContext = new(
|
||||
CurrentScanAnchor: currentScanAnchor,
|
||||
PreviousScanAnchor: previousScanAnchor,
|
||||
Circles: await dbContext.Circles
|
||||
.Where(c => makerIds.Contains(c.MakerId))
|
||||
.ToDictionaryAsync(c => c.MakerId, cancellationToken),
|
||||
VoiceWorks: await dbContext.VoiceWorks
|
||||
.Where(v => productIds.Contains(v.ProductId))
|
||||
.Include(v => v.Creators)
|
||||
.Include(v => v.Tags)
|
||||
.Include(v => v.Localizations)
|
||||
.Include(v => v.SupportedLanguages)
|
||||
.ToDictionaryAsync(v => v.ProductId, cancellationToken),
|
||||
Tags: await dbContext.Tags
|
||||
.Where(t => tagNames.Contains(t.Name))
|
||||
.ToDictionaryAsync(t => t.Name, cancellationToken),
|
||||
Creators: await dbContext.Creators
|
||||
.Where(cr => creatorNames.Contains(cr.Name))
|
||||
.ToDictionaryAsync(cr => cr.Name, cancellationToken),
|
||||
Results: productIds.ToDictionary(
|
||||
productId => productId,
|
||||
productId => new VoiceWorkUpsertResult()
|
||||
)
|
||||
);
|
||||
|
||||
return upsertContext;
|
||||
}
|
||||
|
||||
private DateTimeOffset GetCurrentScanAnchor()
|
||||
{
|
||||
DateTimeOffset now = timeProvider.Now();
|
||||
DateTimeOffset midnight = timeProvider.Local(now.Year, now.Month, now.Day, 0);
|
||||
DateTimeOffset fourPm = timeProvider.Local(now.Year, now.Month, now.Day, 16);
|
||||
|
||||
return now >= fourPm ? fourPm : midnight;
|
||||
}
|
||||
|
||||
private DateTimeOffset PreviousScanAnchor(DateTimeOffset scanAnchorTokyo)
|
||||
{
|
||||
// Normalize to Tokyo (no-op if already)
|
||||
var a = timeProvider.Local(scanAnchorTokyo);
|
||||
|
||||
return a.Hour == 16
|
||||
? timeProvider.Local(a.Year, a.Month, a.Day, 0)
|
||||
: timeProvider.Local(a.AddDays(-1).Year, a.AddDays(-1).Month, a.AddDays(-1).Day, 16);
|
||||
}
|
||||
|
||||
private static void Validate(VoiceWorkIngest ingest, VoiceWorkUpsertContext upsertContext)
|
||||
{
|
||||
VoiceWorkUpsertResult result = upsertContext.Results[ingest.ProductId];
|
||||
|
||||
if (upsertContext.Circles.TryGetValue(ingest.MakerId, out Circle? circle) == false)
|
||||
return;
|
||||
|
||||
if (circle.Spam)
|
||||
{
|
||||
string message = $"Circle {ingest.MakerName} ({ingest.MakerId}) is a spam circle";
|
||||
result.Issues.Add(new(message, VoiceWorkUpsertIssueSeverity.Error));
|
||||
return;
|
||||
}
|
||||
|
||||
if (upsertContext.VoiceWorks.TryGetValue(ingest.MakerId, out VoiceWork? voiceWork) == false)
|
||||
return;
|
||||
|
||||
int ingestDownloads = ingest.Downloads;
|
||||
int voiceWorkDownloads = voiceWork.Downloads ?? 0;
|
||||
|
||||
if (ingestDownloads < voiceWorkDownloads)
|
||||
{
|
||||
string message = $"Downloads have decreased from {voiceWorkDownloads} to {ingestDownloads}";
|
||||
result.Issues.Add(new(message, VoiceWorkUpsertIssueSeverity.Error));
|
||||
}
|
||||
|
||||
if (voiceWork.SalesDate is not null && ingest.SalesDate is null)
|
||||
{
|
||||
string message = $"Voice work has a sales date of {voiceWork.SalesDate.Value.ToShortDateString()}, but parsed ingest does not";
|
||||
result.Issues.Add(new(message, VoiceWorkUpsertIssueSeverity.Error));
|
||||
}
|
||||
}
|
||||
|
||||
private void Upsert(VoiceWorkIngest ingest, VoiceWorkUpsertContext upsertContext)
|
||||
{
|
||||
UpsertCircle(ingest, upsertContext);
|
||||
UpsertVoiceWork(ingest, upsertContext);
|
||||
UpsertTags(ingest, upsertContext);
|
||||
UpsertVoiceWorkTags(ingest, upsertContext);
|
||||
UpsertCreators(ingest, upsertContext);
|
||||
UpsertVoiceWorkCreators(ingest, upsertContext);
|
||||
UpsertVoiceWorkSupportedLanguages(ingest, upsertContext);
|
||||
}
|
||||
|
||||
private void UpsertCircle(VoiceWorkIngest ingest, VoiceWorkUpsertContext upsertContext)
|
||||
{
|
||||
Circle circle = GetOrAddCircle(ingest, upsertContext);
|
||||
circle.Name = ingest.MakerName;
|
||||
}
|
||||
|
||||
private Circle GetOrAddCircle(VoiceWorkIngest ingest, VoiceWorkUpsertContext upsertContext)
|
||||
{
|
||||
if (!upsertContext.Circles.TryGetValue(ingest.MakerId, out Circle? circle))
|
||||
{
|
||||
circle = new Circle
|
||||
{
|
||||
MakerId = ingest.MakerId,
|
||||
Name = ingest.MakerName,
|
||||
};
|
||||
|
||||
dbContext.Circles.Add(circle);
|
||||
upsertContext.Circles[ingest.MakerId] = circle;
|
||||
}
|
||||
|
||||
return circle;
|
||||
}
|
||||
|
||||
private void UpsertVoiceWork(VoiceWorkIngest ingest, VoiceWorkUpsertContext upsertContext)
|
||||
{
|
||||
VoiceWork voiceWork = GetOrAddVoiceWork(ingest, upsertContext);
|
||||
|
||||
bool isAdded = dbContext.Entry(voiceWork).State == EntityState.Added;
|
||||
bool isWithinCurrentScanAnchor = voiceWork.LastScannedDate == upsertContext.CurrentScanAnchor;
|
||||
bool isNewOnSale = voiceWork.SalesDate is null && ingest.SalesDate is not null;
|
||||
|
||||
bool isNew = isAdded || isWithinCurrentScanAnchor || isNewOnSale;
|
||||
|
||||
voiceWork.Circle = upsertContext.Circles[ingest.MakerId];
|
||||
voiceWork.ProductName = ingest.Title;
|
||||
voiceWork.Description = ingest.Description;
|
||||
voiceWork.HasImage = ingest.HasImage;
|
||||
voiceWork.Rating = (int)ingest.AgeRating;
|
||||
voiceWork.Downloads = ingest.Downloads;
|
||||
voiceWork.WishlistCount = ingest.WishlistCount;
|
||||
voiceWork.HasTrial = ingest.HasTrial;
|
||||
voiceWork.HasChobit = ingest.HasDLPlay;
|
||||
voiceWork.StarRating = ingest.StarRating;
|
||||
voiceWork.Votes = ingest.Votes;
|
||||
voiceWork.IsValid = true;
|
||||
|
||||
if (ingest.SalesDate.HasValue)
|
||||
{
|
||||
voiceWork.SalesDate = ingest.SalesDate.Value.ToDateTime(new TimeOnly(0, 0));
|
||||
voiceWork.ExpectedDate = null;
|
||||
voiceWork.PlannedReleaseDate = null;
|
||||
voiceWork.Status = isNew ? (byte)VoiceWorkStatus.NewRelease : (byte)VoiceWorkStatus.Available;
|
||||
}
|
||||
else
|
||||
{
|
||||
voiceWork.SalesDate = null;
|
||||
voiceWork.ExpectedDate = ingest.ExpectedDate?.ToDateTime(new TimeOnly(0, 0));
|
||||
voiceWork.PlannedReleaseDate = ingest.RegistrationDate > upsertContext.CurrentScanAnchor ? ingest.RegistrationDate : null;
|
||||
voiceWork.Status = isNew ? (byte)VoiceWorkStatus.NewAndUpcoming : (byte)VoiceWorkStatus.Upcoming;
|
||||
}
|
||||
}
|
||||
|
||||
private VoiceWork GetOrAddVoiceWork(VoiceWorkIngest ingest, VoiceWorkUpsertContext upsertContext)
|
||||
{
|
||||
if (!upsertContext.VoiceWorks.TryGetValue(ingest.ProductId, out VoiceWork? voiceWork))
|
||||
{
|
||||
voiceWork = new VoiceWork
|
||||
{
|
||||
ProductId = ingest.ProductId
|
||||
};
|
||||
|
||||
dbContext.VoiceWorks.Add(voiceWork);
|
||||
upsertContext.VoiceWorks[ingest.ProductId] = voiceWork;
|
||||
}
|
||||
|
||||
return voiceWork;
|
||||
}
|
||||
|
||||
private void UpsertTags(VoiceWorkIngest ingest, VoiceWorkUpsertContext upsertContext)
|
||||
{
|
||||
foreach (string tagName in ingest.Tags)
|
||||
{
|
||||
GetOrAddTag(tagName, upsertContext);
|
||||
}
|
||||
}
|
||||
|
||||
private Tag GetOrAddTag(string tagName, VoiceWorkUpsertContext upsertContext)
|
||||
{
|
||||
if (!upsertContext.Tags.TryGetValue(tagName, out Tag? tag))
|
||||
{
|
||||
tag = new Tag
|
||||
{
|
||||
Name = tagName
|
||||
};
|
||||
|
||||
dbContext.Tags.Add(tag);
|
||||
upsertContext.Tags[tagName] = tag;
|
||||
}
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
private void UpsertCreators(VoiceWorkIngest ingest, VoiceWorkUpsertContext upsertContext)
|
||||
{
|
||||
foreach (string creatorName in ingest.Creators)
|
||||
{
|
||||
GetOrAddCreator(creatorName, upsertContext);
|
||||
}
|
||||
}
|
||||
|
||||
private Creator GetOrAddCreator(string creatorName, VoiceWorkUpsertContext upsertContext)
|
||||
{
|
||||
if (!upsertContext.Creators.TryGetValue(creatorName, out Creator? creator))
|
||||
{
|
||||
creator = new Creator
|
||||
{
|
||||
Name = creatorName
|
||||
};
|
||||
|
||||
dbContext.Creators.Add(creator);
|
||||
upsertContext.Creators[creatorName] = creator;
|
||||
}
|
||||
|
||||
return creator;
|
||||
}
|
||||
|
||||
private void UpsertVoiceWorkTags(VoiceWorkIngest ingest, VoiceWorkUpsertContext upsertContext)
|
||||
{
|
||||
VoiceWork voiceWork = upsertContext.VoiceWorks[ingest.ProductId];
|
||||
Dictionary<int, VoiceWorkTag> existingTagLinks = voiceWork.Tags.ToDictionary(x => x.TagId);
|
||||
|
||||
int position = 1;
|
||||
|
||||
foreach (string tagName in ingest.Tags)
|
||||
{
|
||||
Tag tag = upsertContext.Tags[tagName];
|
||||
|
||||
if (!existingTagLinks.TryGetValue(tag.TagId, out VoiceWorkTag? voiceWorkTag))
|
||||
{
|
||||
voiceWorkTag = new VoiceWorkTag
|
||||
{
|
||||
VoiceWork = voiceWork,
|
||||
Tag = tag
|
||||
};
|
||||
|
||||
dbContext.VoiceWorkTags.Add(voiceWorkTag);
|
||||
}
|
||||
|
||||
voiceWorkTag.Position = position++;
|
||||
voiceWorkTag.IsValid = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpsertVoiceWorkCreators(VoiceWorkIngest ingest, VoiceWorkUpsertContext upsertContext)
|
||||
{
|
||||
VoiceWork voiceWork = upsertContext.VoiceWorks[ingest.ProductId];
|
||||
Dictionary<int, VoiceWorkCreator> existingCreatorLinks = voiceWork.Creators.ToDictionary(x => x.CreatorId);
|
||||
|
||||
int position = 1;
|
||||
|
||||
foreach (string creatorName in ingest.Creators)
|
||||
{
|
||||
Creator creator = upsertContext.Creators[creatorName];
|
||||
|
||||
if (!existingCreatorLinks.TryGetValue(creator.CreatorId, out VoiceWorkCreator? voiceWorkCreator))
|
||||
{
|
||||
voiceWorkCreator = new VoiceWorkCreator
|
||||
{
|
||||
VoiceWork = voiceWork,
|
||||
Creator = creator
|
||||
};
|
||||
|
||||
dbContext.VoiceWorkCreators.Add(voiceWorkCreator);
|
||||
}
|
||||
|
||||
voiceWorkCreator.Position = position++;
|
||||
voiceWorkCreator.IsValid = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpsertVoiceWorkSupportedLanguages(VoiceWorkIngest ingest, VoiceWorkUpsertContext upsertContext)
|
||||
{
|
||||
VoiceWork voiceWork = upsertContext.VoiceWorks[ingest.ProductId];
|
||||
Dictionary<string, VoiceWorkSupportedLanguage> existingLanguageLinks = voiceWork.SupportedLanguages.ToDictionary(x => x.Language);
|
||||
|
||||
foreach (ISupportedLanguage supportedLanguage in ingest.SupportedLanguages)
|
||||
{
|
||||
if (!existingLanguageLinks.TryGetValue(supportedLanguage.Code, out VoiceWorkSupportedLanguage? voiceWorkSupportedLanguage))
|
||||
{
|
||||
voiceWorkSupportedLanguage = new VoiceWorkSupportedLanguage
|
||||
{
|
||||
VoiceWork = voiceWork,
|
||||
Language = supportedLanguage.Code
|
||||
};
|
||||
|
||||
dbContext.VoiceWorkSupportedLanguages.Add(voiceWorkSupportedLanguage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
40
JSMR.Infrastructure/Ingestion/VoiceWorkUpsertContext.cs
Normal file
40
JSMR.Infrastructure/Ingestion/VoiceWorkUpsertContext.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using JSMR.Domain.Entities;
|
||||
|
||||
namespace JSMR.Infrastructure.Ingestion;
|
||||
|
||||
public record VoiceWorkUpsertContext(
|
||||
DateTimeOffset CurrentScanAnchor,
|
||||
DateTimeOffset PreviousScanAnchor,
|
||||
Dictionary<string, Circle> Circles,
|
||||
Dictionary<string, VoiceWork> VoiceWorks,
|
||||
Dictionary<string, Tag> Tags,
|
||||
Dictionary<string, Creator> Creators,
|
||||
Dictionary<string, VoiceWorkUpsertResult> Results
|
||||
);
|
||||
|
||||
public class VoiceWorkUpsertResult
|
||||
{
|
||||
public int? VoiceWorkId { get; set; }
|
||||
public ICollection<VoiceWorkUpsertIssue> Issues { get; } = [];
|
||||
public VoiceWorkUpsertStatus Status { get; set; } = VoiceWorkUpsertStatus.Unchanged;
|
||||
}
|
||||
|
||||
public record VoiceWorkUpsertIssue(
|
||||
string Message,
|
||||
VoiceWorkUpsertIssueSeverity Severity
|
||||
);
|
||||
|
||||
public enum VoiceWorkUpsertIssueSeverity
|
||||
{
|
||||
Information,
|
||||
Warning,
|
||||
Error
|
||||
}
|
||||
|
||||
public enum VoiceWorkUpsertStatus
|
||||
{
|
||||
Unchanged,
|
||||
Inserted,
|
||||
Updated,
|
||||
Skipped
|
||||
}
|
||||
Reference in New Issue
Block a user