Expose critical curation and source image APIs
This commit is contained in:
@@ -13,6 +13,9 @@ public sealed record CriticalCellEditorResponse(
|
||||
string ColumnKey,
|
||||
string ColumnLabel,
|
||||
string ColumnRole,
|
||||
bool IsCurated,
|
||||
int? SourcePageNumber,
|
||||
string? SourceImageUrl,
|
||||
string RawCellText,
|
||||
string QuickParseInput,
|
||||
string DescriptionText,
|
||||
|
||||
@@ -9,6 +9,7 @@ public sealed record CriticalCellUpdateRequest(
|
||||
string? RawAffixText,
|
||||
string ParseStatus,
|
||||
string ParsedJson,
|
||||
bool IsCurated,
|
||||
bool IsDescriptionOverridden,
|
||||
bool IsRawAffixTextOverridden,
|
||||
bool AreEffectsOverridden,
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
namespace RolemasterDb.App.Features;
|
||||
|
||||
public sealed class CriticalImportArtifactLocator
|
||||
{
|
||||
public CriticalImportArtifactLocator(IHostEnvironment hostEnvironment)
|
||||
{
|
||||
ArtifactsRootPath = DiscoverArtifactsRootPath(hostEnvironment.ContentRootPath);
|
||||
}
|
||||
|
||||
public string ArtifactsRootPath { get; }
|
||||
|
||||
public string? ResolveStoredPath(string? relativePath)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(relativePath))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var candidate = Path.GetFullPath(Path.Combine(
|
||||
ArtifactsRootPath,
|
||||
relativePath.Replace('/', Path.DirectorySeparatorChar)));
|
||||
|
||||
return candidate.StartsWith(ArtifactsRootPath, StringComparison.OrdinalIgnoreCase)
|
||||
? candidate
|
||||
: null;
|
||||
}
|
||||
|
||||
private static string DiscoverArtifactsRootPath(string contentRootPath)
|
||||
{
|
||||
var probe = new DirectoryInfo(contentRootPath);
|
||||
|
||||
while (probe is not null)
|
||||
{
|
||||
if (File.Exists(Path.Combine(probe.FullName, "RolemasterDB.slnx")))
|
||||
{
|
||||
return Path.Combine(probe.FullName, "artifacts", "import", "critical");
|
||||
}
|
||||
|
||||
probe = probe.Parent;
|
||||
}
|
||||
|
||||
return Path.Combine(contentRootPath, "artifacts", "import", "critical");
|
||||
}
|
||||
}
|
||||
@@ -110,6 +110,7 @@ public sealed record CriticalTableCellDetail(
|
||||
string ColumnRole,
|
||||
string? GroupKey,
|
||||
string? GroupLabel,
|
||||
bool IsCurated,
|
||||
string? Description,
|
||||
IReadOnlyList<CriticalEffectLookupResponse> Effects,
|
||||
IReadOnlyList<CriticalBranchLookupResponse> Branches);
|
||||
|
||||
@@ -9,7 +9,9 @@ using SharedParsing = RolemasterDb.CriticalParsing;
|
||||
|
||||
namespace RolemasterDb.App.Features;
|
||||
|
||||
public sealed class LookupService(IDbContextFactory<RolemasterDbContext> dbContextFactory)
|
||||
public sealed class LookupService(
|
||||
IDbContextFactory<RolemasterDbContext> dbContextFactory,
|
||||
CriticalImportArtifactLocator? artifactLocator = null)
|
||||
{
|
||||
public async Task<LookupReferenceData> GetReferenceDataAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
@@ -245,6 +247,7 @@ public sealed class LookupService(IDbContextFactory<RolemasterDbContext> dbConte
|
||||
result.CriticalColumn.Role,
|
||||
result.CriticalGroup?.GroupKey,
|
||||
result.CriticalGroup?.Label,
|
||||
result.IsCurated,
|
||||
result.DescriptionText,
|
||||
result.Effects
|
||||
.OrderBy(effect => effect.Id)
|
||||
@@ -300,6 +303,28 @@ public sealed class LookupService(IDbContextFactory<RolemasterDbContext> dbConte
|
||||
return CreateCellEditorResponse(result, currentState, generatedContent.ValidationErrors, CreateComparisonState(generatedContent));
|
||||
}
|
||||
|
||||
public async Task<string?> GetCriticalSourceImagePathAsync(string slug, int resultId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (artifactLocator is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
await using var dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var normalizedSlug = NormalizeSlug(slug);
|
||||
var relativePath = await dbContext.CriticalResults
|
||||
.AsNoTracking()
|
||||
.Where(item => item.Id == resultId && item.CriticalTable.Slug == normalizedSlug)
|
||||
.Select(item => item.SourceImagePath)
|
||||
.SingleOrDefaultAsync(cancellationToken);
|
||||
|
||||
var fullPath = artifactLocator.ResolveStoredPath(relativePath);
|
||||
return fullPath is not null && File.Exists(fullPath)
|
||||
? fullPath
|
||||
: null;
|
||||
}
|
||||
|
||||
public async Task<CriticalCellEditorResponse?> ReparseCriticalCellAsync(
|
||||
string slug,
|
||||
int resultId,
|
||||
@@ -363,6 +388,7 @@ public sealed class LookupService(IDbContextFactory<RolemasterDbContext> dbConte
|
||||
result.RawAffixText = NormalizeOptionalText(request.RawAffixText);
|
||||
result.ParseStatus = request.ParseStatus.Trim();
|
||||
result.ParsedJson = CriticalCellEditorSnapshot.FromRequest(request).ToJson();
|
||||
result.IsCurated = request.IsCurated;
|
||||
|
||||
ReplaceBaseEffects(dbContext, result, request.Effects);
|
||||
ReplaceBranches(dbContext, result, request.Branches);
|
||||
@@ -450,6 +476,9 @@ public sealed class LookupService(IDbContextFactory<RolemasterDbContext> dbConte
|
||||
result.CriticalColumn.ColumnKey,
|
||||
result.CriticalColumn.Label,
|
||||
result.CriticalColumn.Role,
|
||||
state.IsCurated,
|
||||
result.SourcePageNumber,
|
||||
CreateSourceImageUrl(result),
|
||||
state.RawCellText,
|
||||
state.QuickParseInput,
|
||||
state.DescriptionText,
|
||||
@@ -596,6 +625,7 @@ public sealed class LookupService(IDbContextFactory<RolemasterDbContext> dbConte
|
||||
result.RawAffixText,
|
||||
result.ParseStatus,
|
||||
result.ParsedJson,
|
||||
result.IsCurated,
|
||||
snapshot.IsDescriptionOverridden,
|
||||
snapshot.IsRawAffixTextOverridden,
|
||||
snapshot.AreEffectsOverridden,
|
||||
@@ -620,6 +650,7 @@ public sealed class LookupService(IDbContextFactory<RolemasterDbContext> dbConte
|
||||
result.RawAffixText,
|
||||
result.ParseStatus,
|
||||
result.ParsedJson,
|
||||
result.IsCurated,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
@@ -645,6 +676,7 @@ public sealed class LookupService(IDbContextFactory<RolemasterDbContext> dbConte
|
||||
RawAffixText: content.RawAffixText,
|
||||
ParseStatus: ResolveParseStatus(content.Effects, content.Branches),
|
||||
ParsedJson: SerializeParsedEffects(content.Effects),
|
||||
IsCurated: false,
|
||||
IsDescriptionOverridden: false,
|
||||
IsRawAffixTextOverridden: false,
|
||||
AreEffectsOverridden: false,
|
||||
@@ -663,6 +695,7 @@ public sealed class LookupService(IDbContextFactory<RolemasterDbContext> dbConte
|
||||
currentState.IsRawAffixTextOverridden ? currentState.RawAffixText : generatedState.RawAffixText,
|
||||
generatedState.ParseStatus,
|
||||
generatedState.ParsedJson,
|
||||
currentState.IsCurated,
|
||||
currentState.IsDescriptionOverridden,
|
||||
currentState.IsRawAffixTextOverridden,
|
||||
currentState.AreEffectsOverridden,
|
||||
@@ -992,4 +1025,9 @@ public sealed class LookupService(IDbContextFactory<RolemasterDbContext> dbConte
|
||||
|
||||
private static string NormalizeSlug(string value) =>
|
||||
value.Trim().Replace(' ', '_').ToLowerInvariant();
|
||||
|
||||
private static string? CreateSourceImageUrl(CriticalResult result) =>
|
||||
string.IsNullOrWhiteSpace(result.SourceImagePath)
|
||||
? null
|
||||
: $"/api/tables/critical/{result.CriticalTable.Slug}/cells/{result.Id}/source-image";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user