?? ?? ?? ???? PPTX ??? ? ?? ??
?? ?? ??? PPTX/DOCX/XLSX/HTML ???? ? ?????, PPTX? ???? ??? ? ???? ?? ???? ? ??? ?? ?? ???? ????. ?? ????: - ExcelSkill? conditional_formats? ??? ?? ???? ??? ? ?????? OpenXML? ?? ???? workbook quality review? ?? - DocxSkill? style_map? ??? ???? ??/??/?? ???? ?? ?? ParagraphStyleId? ?? - HtmlSkill? print_header/print_footer ?? ?? ???? ???? ArtifactQualityReviewService? ?? ?? ?? ???? ?? - PptxTemplatePackRegistry? PptxSkill template_pack ????? ??? strategy/board/pmo/finance/sales/operating_model ??? ??? ?? ?? ?? ??? ?? - ?????, ????, ?? ???, ??? ?? ?? ?? ???? ???? ?? ?? ?? ???? ???? ???? ?? ?? ??: - dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_next_doc_ppt\\ -p:IntermediateOutputPath=obj\\verify_next_doc_ppt\\ => ?? 0 / ?? 0 - dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter "ArtifactQualityReviewServiceTests|ExcelSkillDataValidationTests|ExcelSkillConditionalFormattingTests|ExcelSkillExecutiveSummaryLinkTests|ExcelSkillSummarySheetTests|DocxSkillTemplateFeaturesTests|DocxSkillStyleMapTests|HtmlSkillConsultingSectionsTests|HtmlSkillPrintFrameTests|DocumentAssemblerDocxFeaturesTests|PptxSkillConsultingDeckTests|PptxSkillAutoRepairTests|PptxSkillTemplatePackTests" -p:OutputPath=bin\\verify_next_doc_ppt_tests\\ -p:IntermediateOutputPath=obj\\verify_next_doc_ppt_tests\\ => ?? 15
This commit is contained in:
@@ -20,15 +20,19 @@ public sealed record ArtifactQualityReport(
|
||||
public string ToToolSummary()
|
||||
{
|
||||
var strengths = Strengths.Take(3).ToList();
|
||||
var issues = Issues.OrderByDescending(i => i.Severity).Take(3).Select(i => i.Message).ToList();
|
||||
var issues = Issues
|
||||
.OrderByDescending(issue => issue.Severity)
|
||||
.Take(3)
|
||||
.Select(issue => issue.Message)
|
||||
.ToList();
|
||||
|
||||
var parts = new List<string> { $"품질 점수 {Score}/100" };
|
||||
var parts = new List<string> { $"Quality score {Score}/100" };
|
||||
if (strengths.Count > 0)
|
||||
parts.Add("강점: " + string.Join(", ", strengths));
|
||||
parts.Add("Strengths: " + string.Join(", ", strengths));
|
||||
if (issues.Count > 0)
|
||||
parts.Add("보완: " + string.Join(", ", issues));
|
||||
parts.Add("Needs work: " + string.Join(", ", issues));
|
||||
else
|
||||
parts.Add("보완 필요 사항 없음");
|
||||
parts.Add("Needs work: none");
|
||||
|
||||
return string.Join(" | ", parts);
|
||||
}
|
||||
@@ -59,17 +63,48 @@ public sealed record WorkbookReviewInput(
|
||||
int FormulaCount,
|
||||
int HyperlinkCount,
|
||||
int DataValidationCount,
|
||||
int ConditionalFormattingCount,
|
||||
bool HasSummarySheet,
|
||||
bool HasHighlightSection,
|
||||
bool HasActionSection);
|
||||
|
||||
public static class ArtifactQualityReviewService
|
||||
{
|
||||
private static readonly string[] ExecutiveKeywords = ["executive summary", "요약", "핵심 요약", "summary"];
|
||||
private static readonly string[] RecommendationKeywords = ["recommendation", "권고", "제안", "next steps", "실행 과제"];
|
||||
private static readonly string[] AppendixKeywords = ["appendix", "부록", "참고", "reference"];
|
||||
private static readonly string[] ExecutiveKeywords =
|
||||
[
|
||||
"executive summary",
|
||||
"summary",
|
||||
"\uC694\uC57D",
|
||||
"\uD575\uC2EC \uC694\uC57D",
|
||||
];
|
||||
|
||||
public static ArtifactQualityReport ReviewHtml(string title, string html, bool hasCover, bool hasTableOfContents, bool printReady)
|
||||
private static readonly string[] RecommendationKeywords =
|
||||
[
|
||||
"recommendation",
|
||||
"proposal",
|
||||
"next steps",
|
||||
"action plan",
|
||||
"\uAD8C\uACE0",
|
||||
"\uC81C\uC548",
|
||||
"\uC2E4\uD589",
|
||||
];
|
||||
|
||||
private static readonly string[] AppendixKeywords =
|
||||
[
|
||||
"appendix",
|
||||
"reference",
|
||||
"supplement",
|
||||
"\uBD80\uB85D",
|
||||
"\uCC38\uACE0",
|
||||
];
|
||||
|
||||
public static ArtifactQualityReport ReviewHtml(
|
||||
string title,
|
||||
string html,
|
||||
bool hasCover,
|
||||
bool hasTableOfContents,
|
||||
bool printReady,
|
||||
bool hasPrintFrame = false)
|
||||
{
|
||||
title ??= string.Empty;
|
||||
html ??= string.Empty;
|
||||
@@ -78,7 +113,6 @@ public static class ArtifactQualityReviewService
|
||||
var issues = new List<ArtifactReviewIssue>();
|
||||
|
||||
var sectionCount = Regex.Matches(html, @"<h2\b", RegexOptions.IgnoreCase).Count;
|
||||
var subSectionCount = Regex.Matches(html, @"<h3\b", RegexOptions.IgnoreCase).Count;
|
||||
var paragraphCount = Regex.Matches(html, @"<p\b", RegexOptions.IgnoreCase).Count;
|
||||
var tableCount = Regex.Matches(html, @"<table\b", RegexOptions.IgnoreCase).Count;
|
||||
var calloutCount = Regex.Matches(html, @"callout-(info|warning|tip|danger)", RegexOptions.IgnoreCase).Count;
|
||||
@@ -90,26 +124,27 @@ public static class ArtifactQualityReviewService
|
||||
var kpiCount = Regex.Matches(html, @"kpi-card|kpi-grid|metric-card", RegexOptions.IgnoreCase).Count;
|
||||
var placeholderCount = CountPlaceholders(html);
|
||||
|
||||
if (hasCover) strengths.Add("커버 페이지 포함");
|
||||
if (hasTableOfContents) strengths.Add("목차 자동 생성");
|
||||
if (printReady) strengths.Add("인쇄 최적화 CSS 포함");
|
||||
if (sectionCount >= 5) strengths.Add($"핵심 섹션 {sectionCount}개 구성");
|
||||
if (hasCover) strengths.Add("Includes cover page");
|
||||
if (hasTableOfContents) strengths.Add("Includes table of contents");
|
||||
if (printReady) strengths.Add("Includes print-ready CSS");
|
||||
if (hasPrintFrame) strengths.Add("Includes print header/footer frame");
|
||||
if (sectionCount >= 5) strengths.Add($"Contains {sectionCount} major sections");
|
||||
if (tableCount > 0 || comparisonCount > 0 || roadmapCount > 0 || matrixCount > 0 || decisionCount > 0 || evidenceCardCount > 0 || kpiCount > 0)
|
||||
strengths.Add("표/비교/로드맵/KPI 등 구조화 블록 활용");
|
||||
if (calloutCount > 0) strengths.Add("핵심 메시지 강조 블록 포함");
|
||||
strengths.Add("Uses structured business blocks");
|
||||
if (calloutCount > 0) strengths.Add("Uses callout blocks for emphasis");
|
||||
|
||||
if (html.Length < 1800)
|
||||
issues.Add(new("문서 본문이 짧아 메시지와 근거 밀도가 낮을 수 있습니다", ArtifactReviewSeverity.Warning));
|
||||
issues.Add(new("Body content may be too short for an executive-quality document.", ArtifactReviewSeverity.Warning));
|
||||
if (sectionCount < 4)
|
||||
issues.Add(new("업무 문서 기준 핵심 섹션 수가 부족합니다", ArtifactReviewSeverity.Warning));
|
||||
issues.Add(new("Major section count is low for a business report.", ArtifactReviewSeverity.Warning));
|
||||
if (paragraphCount < sectionCount * 2)
|
||||
issues.Add(new("일부 섹션의 서술이 충분하지 않습니다", ArtifactReviewSeverity.Warning));
|
||||
issues.Add(new("Several sections may need more supporting paragraphs.", ArtifactReviewSeverity.Warning));
|
||||
if (tableCount + comparisonCount + roadmapCount + matrixCount + decisionCount + evidenceCardCount + kpiCount == 0)
|
||||
issues.Add(new("구조화 시각 요소가 없어 보고서 완성도가 낮을 수 있습니다", ArtifactReviewSeverity.Warning));
|
||||
issues.Add(new("Structured visual blocks are limited.", ArtifactReviewSeverity.Warning));
|
||||
if (placeholderCount > 0)
|
||||
issues.Add(new($"플레이스홀더/미완성 표현 {placeholderCount}건이 남아 있습니다", ArtifactReviewSeverity.Critical));
|
||||
issues.Add(new($"Found {placeholderCount} placeholder or unfinished marker(s).", ArtifactReviewSeverity.Critical));
|
||||
if (!ContainsAny(html, ExecutiveKeywords))
|
||||
issues.Add(new("요약 또는 핵심 메시지 섹션이 명확하지 않습니다", ArtifactReviewSeverity.Warning));
|
||||
issues.Add(new("Executive summary or summary section is missing.", ArtifactReviewSeverity.Warning));
|
||||
|
||||
return BuildReport("html", strengths, issues);
|
||||
}
|
||||
@@ -119,27 +154,27 @@ public static class ArtifactQualityReviewService
|
||||
var strengths = new List<string>();
|
||||
var issues = new List<ArtifactReviewIssue>();
|
||||
|
||||
if (input.HasTemplate) strengths.Add("템플릿 기반 스타일 상속");
|
||||
if (input.HasCoverPage) strengths.Add("커버 페이지 포함");
|
||||
if (input.HasTableOfContents) strengths.Add("목차 포함");
|
||||
if (input.HasHeaderFooter) strengths.Add("머리글/바닥글 적용");
|
||||
if (input.SectionCount >= 5) strengths.Add($"핵심 섹션 {input.SectionCount}개 구성");
|
||||
if (input.TableCount > 0) strengths.Add($"테이블 {input.TableCount}개 포함");
|
||||
if (input.CalloutCount + input.HighlightCount > 0) strengths.Add("강조 블록 활용");
|
||||
if (input.ListCount > 0) strengths.Add("실행 항목/목록 구조 포함");
|
||||
if (input.HasTemplate) strengths.Add("Uses template-based styling");
|
||||
if (input.HasCoverPage) strengths.Add("Includes cover page");
|
||||
if (input.HasTableOfContents) strengths.Add("Includes table of contents");
|
||||
if (input.HasHeaderFooter) strengths.Add("Includes header/footer");
|
||||
if (input.SectionCount >= 5) strengths.Add($"Contains {input.SectionCount} major sections");
|
||||
if (input.TableCount > 0) strengths.Add($"Includes {input.TableCount} table(s)");
|
||||
if (input.CalloutCount + input.HighlightCount > 0) strengths.Add("Uses emphasis blocks");
|
||||
if (input.ListCount > 0) strengths.Add("Uses structured list sections");
|
||||
|
||||
if (input.BodyCharacterCount < 1400)
|
||||
issues.Add(new("문서 분량이 짧아 경영 보고용 밀도가 부족할 수 있습니다", ArtifactReviewSeverity.Warning));
|
||||
issues.Add(new("Body content may be too short for an executive document.", ArtifactReviewSeverity.Warning));
|
||||
if (input.SectionCount < 4)
|
||||
issues.Add(new("업무 문서 기준 핵심 섹션 수가 부족합니다", ArtifactReviewSeverity.Warning));
|
||||
issues.Add(new("Major section count is low for a business document.", ArtifactReviewSeverity.Warning));
|
||||
if (!input.HasExecutiveSummarySection)
|
||||
issues.Add(new("Executive Summary 또는 요약 섹션이 없습니다", ArtifactReviewSeverity.Warning));
|
||||
issues.Add(new("Executive Summary section is missing.", ArtifactReviewSeverity.Warning));
|
||||
if (!input.HasRecommendationSection)
|
||||
issues.Add(new("권고안 또는 다음 단계 섹션이 없습니다", ArtifactReviewSeverity.Warning));
|
||||
issues.Add(new("Recommendation or next-step section is missing.", ArtifactReviewSeverity.Warning));
|
||||
if (input.SectionCount >= 6 && !input.HasAppendixSection)
|
||||
issues.Add(new("긴 문서인데 부록/참고 섹션이 없습니다", ArtifactReviewSeverity.Info));
|
||||
issues.Add(new("Appendix or reference section is limited for a long document.", ArtifactReviewSeverity.Info));
|
||||
if (input.TableCount + input.ListCount + input.CalloutCount + input.HighlightCount == 0)
|
||||
issues.Add(new("표, 목록, 강조 블록이 없어 문서가 단조로울 수 있습니다", ArtifactReviewSeverity.Warning));
|
||||
issues.Add(new("Document structure is mostly plain paragraphs.", ArtifactReviewSeverity.Warning));
|
||||
|
||||
return BuildReport("docx", strengths, issues);
|
||||
}
|
||||
@@ -149,22 +184,25 @@ public static class ArtifactQualityReviewService
|
||||
var strengths = new List<string>();
|
||||
var issues = new List<ArtifactReviewIssue>();
|
||||
|
||||
if (input.HasSummarySheet) strengths.Add("요약 시트 포함");
|
||||
if (input.DetailSheetCount > 1) strengths.Add($"상세 시트 {input.DetailSheetCount}개 구성");
|
||||
if (input.FormulaCount > 0) strengths.Add($"수식 {input.FormulaCount}개 포함");
|
||||
if (input.HyperlinkCount > 0) strengths.Add("시트 간 빠른 이동 링크 포함");
|
||||
if (input.DataValidationCount > 0) strengths.Add("데이터 검증 규칙 포함");
|
||||
if (input.HasHighlightSection) strengths.Add("핵심 인사이트 영역 포함");
|
||||
if (input.HasActionSection) strengths.Add("후속 액션 영역 포함");
|
||||
if (input.HasSummarySheet) strengths.Add("Includes summary sheet");
|
||||
if (input.DetailSheetCount > 1) strengths.Add($"Contains {input.DetailSheetCount} detail sheets");
|
||||
if (input.FormulaCount > 0) strengths.Add($"Includes {input.FormulaCount} formula cell(s)");
|
||||
if (input.HyperlinkCount > 0) strengths.Add("Includes navigation links");
|
||||
if (input.DataValidationCount > 0) strengths.Add("Includes data validation rules");
|
||||
if (input.ConditionalFormattingCount > 0) strengths.Add("Includes conditional formatting");
|
||||
if (input.HasHighlightSection) strengths.Add("Includes highlight section");
|
||||
if (input.HasActionSection) strengths.Add("Includes action section");
|
||||
|
||||
if (input.SheetCount > 1 && !input.HasSummarySheet)
|
||||
issues.Add(new("멀티시트 워크북인데 요약 시트가 없습니다", ArtifactReviewSeverity.Warning));
|
||||
issues.Add(new("Workbook has multiple sheets but no summary sheet.", ArtifactReviewSeverity.Warning));
|
||||
if (input.DataRowCount >= 10 && input.FormulaCount == 0)
|
||||
issues.Add(new("데이터 행 수 대비 수식/집계가 부족합니다", ArtifactReviewSeverity.Warning));
|
||||
issues.Add(new("Workbook has enough data to benefit from more formulas or rollups.", ArtifactReviewSeverity.Warning));
|
||||
if (input.HasSummarySheet && input.HyperlinkCount == 0)
|
||||
issues.Add(new("요약 시트에서 상세 시트로 이동하는 링크가 없습니다", ArtifactReviewSeverity.Warning));
|
||||
issues.Add(new("Summary sheet does not link to detail sheets.", ArtifactReviewSeverity.Warning));
|
||||
if (input.DataValidationCount == 0 && input.DataRowCount >= 5)
|
||||
issues.Add(new("입력 통제를 위한 데이터 검증 규칙이 없습니다", ArtifactReviewSeverity.Info));
|
||||
issues.Add(new("Input controls are limited for a workbook with editable data.", ArtifactReviewSeverity.Info));
|
||||
if (input.ConditionalFormattingCount == 0 && input.DataRowCount >= 8)
|
||||
issues.Add(new("Conditional formatting is limited for a workbook with enough data to prioritize or flag.", ArtifactReviewSeverity.Info));
|
||||
|
||||
return BuildReport("xlsx", strengths, issues);
|
||||
}
|
||||
@@ -194,21 +232,17 @@ public static class ArtifactQualityReviewService
|
||||
|
||||
private static int CountPlaceholders(string text)
|
||||
{
|
||||
var count = 0;
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
return count;
|
||||
return 0;
|
||||
|
||||
var patterns = new[]
|
||||
{
|
||||
@"\[(todo|placeholder|내용.*작성|fill me)\]",
|
||||
@"\[(todo|placeholder|fill me)\]",
|
||||
@"lorem ipsum",
|
||||
@"tbd",
|
||||
@"\bTBD\b",
|
||||
};
|
||||
|
||||
foreach (var pattern in patterns)
|
||||
count += Regex.Matches(text, pattern, RegexOptions.IgnoreCase).Count;
|
||||
|
||||
return count;
|
||||
return patterns.Sum(pattern => Regex.Matches(text, pattern, RegexOptions.IgnoreCase).Count);
|
||||
}
|
||||
|
||||
private static bool ContainsAny(string text, IEnumerable<string> keywords)
|
||||
|
||||
Reference in New Issue
Block a user