Add mapping profile validation
This commit is contained in:
@@ -14,6 +14,13 @@ namespace TornadoAce_CJOnStyle.Domain
|
||||
string Attribute,
|
||||
string Rule);
|
||||
|
||||
public sealed record MappingValidationResult(
|
||||
string CheckId,
|
||||
string Title,
|
||||
string Status,
|
||||
string Detail,
|
||||
string Severity);
|
||||
|
||||
public sealed record SceneDataSnapshot(
|
||||
string SceneName,
|
||||
string TemplateId,
|
||||
|
||||
@@ -28,6 +28,49 @@ namespace TornadoAce_CJOnStyle.Services
|
||||
return BuildSnapshot(cueSheet, template, BuildVisibleTextObjects(cueSheet, template));
|
||||
}
|
||||
|
||||
public IReadOnlyList<MappingValidationResult> ValidateMappingProfile(SceneTemplateDefinition template)
|
||||
{
|
||||
var requiredFields = new[] { "programTitle", "productName", "priceText", "compositionText", "promotionText" };
|
||||
var supportedAttributes = new[] { "Text", "Font", "Color", "Style", "Image" };
|
||||
var mappedFields = template.Mappings
|
||||
.Select(mapping => mapping.CueSheetField)
|
||||
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
var missingFields = requiredFields
|
||||
.Where(field => !mappedFields.Contains(field))
|
||||
.ToArray();
|
||||
var duplicateSceneObjects = template.Mappings
|
||||
.GroupBy(mapping => mapping.SceneObjectName, StringComparer.OrdinalIgnoreCase)
|
||||
.Where(group => group.Count() > 1)
|
||||
.Select(group => group.Key)
|
||||
.ToArray();
|
||||
var unsupportedAttributes = template.Mappings
|
||||
.Where(mapping => !supportedAttributes.Contains(mapping.Attribute, StringComparer.OrdinalIgnoreCase))
|
||||
.Select(mapping => $"{mapping.SceneObjectName}:{mapping.Attribute}")
|
||||
.ToArray();
|
||||
|
||||
return
|
||||
[
|
||||
BuildValidationResult(
|
||||
"MAP-01",
|
||||
"필수 큐시트 필드",
|
||||
missingFields.Length == 0,
|
||||
missingFields.Length == 0 ? "5개 필드 매핑 완료" : $"누락: {string.Join(", ", missingFields)}",
|
||||
"High"),
|
||||
BuildValidationResult(
|
||||
"MAP-02",
|
||||
"Tornado 객체 중복",
|
||||
duplicateSceneObjects.Length == 0,
|
||||
duplicateSceneObjects.Length == 0 ? "중복 없음" : $"중복: {string.Join(", ", duplicateSceneObjects)}",
|
||||
"High"),
|
||||
BuildValidationResult(
|
||||
"MAP-03",
|
||||
"지원 속성",
|
||||
unsupportedAttributes.Length == 0,
|
||||
unsupportedAttributes.Length == 0 ? "Text 속성 기준 통과" : $"확인: {string.Join(", ", unsupportedAttributes)}",
|
||||
"Medium")
|
||||
];
|
||||
}
|
||||
|
||||
public ErpScheduleRecord? FindSchedule(CueSheetRecord cueSheet)
|
||||
{
|
||||
return workspace.ErpSchedules
|
||||
@@ -148,6 +191,21 @@ namespace TornadoAce_CJOnStyle.Services
|
||||
return $@"D:\CJOnStyle\Products\{directoryName}\main.png";
|
||||
}
|
||||
|
||||
private static MappingValidationResult BuildValidationResult(
|
||||
string checkId,
|
||||
string title,
|
||||
bool isPassed,
|
||||
string detail,
|
||||
string failureSeverity)
|
||||
{
|
||||
return new MappingValidationResult(
|
||||
checkId,
|
||||
title,
|
||||
isPassed ? "통과" : "확인",
|
||||
detail,
|
||||
isPassed ? "Low" : failureSeverity);
|
||||
}
|
||||
|
||||
private static bool IsImportantSceneObject(string sceneObjectName)
|
||||
{
|
||||
return sceneObjectName.Contains("PRICE", StringComparison.OrdinalIgnoreCase)
|
||||
|
||||
@@ -44,6 +44,7 @@ namespace TornadoAce_CJOnStyle.ViewModels
|
||||
LoadStatusCards();
|
||||
LoadPipelineSteps();
|
||||
LoadFieldMappings();
|
||||
LoadMappingValidation();
|
||||
LoadEndpoints();
|
||||
LoadRequirementCoverage();
|
||||
LoadScheduleMatches();
|
||||
@@ -126,6 +127,8 @@ namespace TornadoAce_CJOnStyle.ViewModels
|
||||
|
||||
public ObservableCollection<FieldMappingViewModel> FieldMappings { get; } = [];
|
||||
|
||||
public ObservableCollection<MappingValidationViewModel> MappingValidations { get; } = [];
|
||||
|
||||
public ObservableCollection<IntegrationEndpointViewModel> Endpoints { get; } = [];
|
||||
|
||||
public ObservableCollection<PocRequirementViewModel> RequirementCoverage { get; } = [];
|
||||
@@ -246,6 +249,21 @@ namespace TornadoAce_CJOnStyle.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadMappingValidation()
|
||||
{
|
||||
MappingValidations.Clear();
|
||||
|
||||
foreach (var validation in automationEngine.ValidateMappingProfile(workspace.ActiveTemplate))
|
||||
{
|
||||
MappingValidations.Add(new MappingValidationViewModel(
|
||||
validation.CheckId,
|
||||
validation.Title,
|
||||
validation.Status,
|
||||
validation.Detail,
|
||||
SeverityBrush(validation.Severity)));
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadEndpoints()
|
||||
{
|
||||
Endpoints.Add(new IntegrationEndpointViewModel("큐시트 플랫폼", "GET /api/cuesheets/{broadcastDate}", "AI ON Studio", "Mock", Brush(0xFF45D6D1)));
|
||||
@@ -260,7 +278,7 @@ namespace TornadoAce_CJOnStyle.ViewModels
|
||||
RequirementCoverage.Add(new PocRequirementViewModel("RFP-02", "ERP 편성 연동", "PoC", "편성 JSON 매칭", Brush(0xFF37D67A)));
|
||||
RequirementCoverage.Add(new PocRequirementViewModel("RFP-03", "Data Field Mapping", "PoC", "Scene 스냅샷 생성", Brush(0xFF37D67A)));
|
||||
RequirementCoverage.Add(new PocRequirementViewModel("RFP-04", "Tornado Scene API", "PoC", "Text/Image 추출 샘플", Brush(0xFF37D67A)));
|
||||
RequirementCoverage.Add(new PocRequirementViewModel("RFP-05", "관리자 매핑 기능", "초안", "매핑표 UI", Brush(0xFFB48CFF)));
|
||||
RequirementCoverage.Add(new PocRequirementViewModel("RFP-05", "관리자 매핑 기능", "PoC", "프로파일 검증", Brush(0xFF37D67A)));
|
||||
RequirementCoverage.Add(new PocRequirementViewModel("RFP-06", "1·2단계 자동 검수", "PoC", "필드 상세 Diff + LLM 후보", Brush(0xFF45D6D1)));
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,13 @@ namespace TornadoAce_CJOnStyle.ViewModels
|
||||
string BindingRule,
|
||||
string Attribute);
|
||||
|
||||
public sealed record MappingValidationViewModel(
|
||||
string CheckId,
|
||||
string Title,
|
||||
string Status,
|
||||
string Detail,
|
||||
SolidColorBrush StatusBrush);
|
||||
|
||||
public sealed record IntegrationEndpointViewModel(
|
||||
string Name,
|
||||
string Endpoint,
|
||||
|
||||
@@ -340,6 +340,57 @@
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
<Border
|
||||
Height="1"
|
||||
Background="{StaticResource PanelStrokeBrush}" />
|
||||
<Grid>
|
||||
<TextBlock Style="{StaticResource SectionTitleTextStyle}" Text="관리자 매핑 검증" />
|
||||
<TextBlock
|
||||
HorizontalAlignment="Right"
|
||||
Style="{StaticResource LabelTextStyle}"
|
||||
Text="Profile Check" />
|
||||
</Grid>
|
||||
<ItemsControl ItemsSource="{x:Bind ViewModel.MappingValidations, Mode=OneWay}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate x:DataType="vm:MappingValidationViewModel">
|
||||
<Grid
|
||||
Margin="0,0,0,8"
|
||||
ColumnSpacing="10">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="58" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
FontFamily="Cascadia Mono"
|
||||
FontSize="12"
|
||||
Foreground="{StaticResource TextMutedBrush}"
|
||||
Text="{x:Bind CheckId}" />
|
||||
<StackPanel Grid.Column="1" Spacing="2">
|
||||
<TextBlock
|
||||
FontFamily="Bahnschrift SemiBold"
|
||||
Foreground="{StaticResource TextPrimaryBrush}"
|
||||
Text="{x:Bind Title}" />
|
||||
<TextBlock
|
||||
Style="{StaticResource LabelTextStyle}"
|
||||
Text="{x:Bind Detail}"
|
||||
TextTrimming="CharacterEllipsis" />
|
||||
</StackPanel>
|
||||
<Border
|
||||
Grid.Column="2"
|
||||
Padding="8,3"
|
||||
Background="{x:Bind StatusBrush}"
|
||||
CornerRadius="8">
|
||||
<TextBlock
|
||||
FontFamily="Bahnschrift SemiBold"
|
||||
FontSize="12"
|
||||
Foreground="#071013"
|
||||
Text="{x:Bind Status}" />
|
||||
</Border>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
| RFP-02 | ERP 편성데이터 연동 | JSON Mock 편성 데이터 매칭 구현 | `Data/PocWorkspace.json`, `ErpScheduleRecord`, `ScheduleMatches` |
|
||||
| RFP-03 | CG 포맷 제작 자동화, Data Field Mapping | 큐시트 필드와 Scene 객체 매핑 구현 | `SceneTemplateDefinition`, `PocAutomationEngine.GenerateScene` |
|
||||
| RFP-04 | Tornado Scene API | Scene visible Text/Image 스냅샷 샘플 구현 | `SceneDataSnapshot.ToPreviewJson` |
|
||||
| RFP-05 | 큐시트 데이터와 Tornado Scene 연동 관리자 기능 | 매핑표 UI 초안 구현 | `FieldMappings` 화면 |
|
||||
| RFP-05 | 큐시트 데이터와 Tornado Scene 연동 관리자 기능 | JSON 매핑 프로파일과 검증 UI 구현 | `FieldMappings`, `MappingValidations`, `ValidateMappingProfile` |
|
||||
| RFP-06 | 자동 검수 1단계 Text Diffing | 필드별 기대값/실제값/일치율 비교 구현 | `PocAutomationEngine.InspectWorkspace`, `InspectionFieldResult` |
|
||||
| RFP-07 | 자동 검수 2단계 LLM 중요도 피드백 | LLM 검토 후보 분류와 제작자 상세 UI 구현 | 검수 결과 `Severity`, `Decision`, `InspectionDetails` |
|
||||
| RFP-08 | 제작자가 결과를 볼 수 있는 GUI | WinUI 3 운영 콘솔 초안 구현 | `MainPage.xaml` |
|
||||
@@ -33,6 +33,7 @@
|
||||
## 다음 구현 후보
|
||||
|
||||
- 실제 큐시트/ERP API 응답 DTO와 현재 JSON seed 스키마를 대조한다.
|
||||
- 관리자용 매핑 프로파일 편집/저장 권한 정책을 정한다.
|
||||
- Tornado2/Tornado3 어댑터를 동일 인터페이스로 유지하고, 버전별 구현체를 분리한다.
|
||||
- Scene 내부 비노출 객체를 구분할 메타데이터 규칙을 정의한다.
|
||||
- 1차 검수 실패 사유를 필드 단위로 누적하고 LLM 요청 후보로 전달한다.
|
||||
|
||||
Reference in New Issue
Block a user