중간
This commit is contained in:
@@ -3,6 +3,15 @@ using System.Windows;
|
||||
using System.Diagnostics;
|
||||
using System.Windows.Threading;
|
||||
using DwgExtractorManual.Models;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
using System.Text;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace DwgExtractorManual
|
||||
{
|
||||
@@ -23,8 +32,8 @@ namespace DwgExtractorManual
|
||||
private void InitializeDefaultPaths()
|
||||
{
|
||||
// 기본 경로 설정 - 실제 환경에 맞게 수정
|
||||
txtSourceFolder.Text = @"D:\MyProjects\AI_TaskForce\AI_도면_dwg_pdf\대산당진_2공구_02도서성과품_01_설계도면\002_토공\01_본선\01_평면 및 종단면도";
|
||||
txtResultFolder.Text = @"D:\MyProjects\AI_TaskForce\AI_도면_dwg_pdf\대산당진_2공구_02도서성과품_01_설계도면";
|
||||
txtSourceFolder.Text = @"D:\dwgpdfcompare\test2";
|
||||
txtResultFolder.Text = @"D:\dwgpdfcompare\result";
|
||||
|
||||
// 경로가 존재하지 않으면 기본값으로 설정
|
||||
if (!Directory.Exists(txtSourceFolder.Text))
|
||||
@@ -94,29 +103,50 @@ namespace DwgExtractorManual
|
||||
try
|
||||
{
|
||||
var targetDir = new DirectoryInfo(folderPath);
|
||||
var files = targetDir.GetFiles("*.dwg", SearchOption.AllDirectories);
|
||||
var dwgFiles = targetDir.GetFiles("*.dwg", SearchOption.AllDirectories);
|
||||
var pdfFiles = targetDir.GetFiles("*.pdf", SearchOption.AllDirectories);
|
||||
|
||||
txtFileCount.Text = $"파일: {files.Length}개";
|
||||
txtFileCount.Text = $"DWG: {dwgFiles.Length}개, PDF: {pdfFiles.Length}개";
|
||||
|
||||
if (files.Length > 0)
|
||||
if (dwgFiles.Length > 0)
|
||||
{
|
||||
LogMessage($"✅ 총 {files.Length}개의 DWG 파일을 발견했습니다.");
|
||||
LogMessage($"✅ 총 {dwgFiles.Length}개의 DWG 파일을 발견했습니다.");
|
||||
|
||||
// 처음 몇 개 파일명 로깅
|
||||
int showCount = Math.Min(5, files.Length);
|
||||
int showCount = Math.Min(3, dwgFiles.Length);
|
||||
for (int i = 0; i < showCount; i++)
|
||||
{
|
||||
LogMessage($" 📄 {files[i].Name}");
|
||||
LogMessage($" 📄 {dwgFiles[i].Name}");
|
||||
}
|
||||
if (files.Length > 5)
|
||||
if (dwgFiles.Length > 3)
|
||||
{
|
||||
LogMessage($" ... 외 {files.Length - 5}개 파일");
|
||||
LogMessage($" ... 외 {dwgFiles.Length - 3}개 DWG 파일");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogMessage("⚠️ 선택한 폴더에 DWG 파일이 없습니다.");
|
||||
}
|
||||
|
||||
if (pdfFiles.Length > 0)
|
||||
{
|
||||
LogMessage($"✅ 총 {pdfFiles.Length}개의 PDF 파일을 발견했습니다.");
|
||||
|
||||
// 처음 몇 개 파일명 로깅
|
||||
int showCount = Math.Min(3, pdfFiles.Length);
|
||||
for (int i = 0; i < showCount; i++)
|
||||
{
|
||||
LogMessage($" 📄 {pdfFiles[i].Name}");
|
||||
}
|
||||
if (pdfFiles.Length > 3)
|
||||
{
|
||||
LogMessage($" ... 외 {pdfFiles.Length - 3}개 PDF 파일");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogMessage("⚠️ 선택한 폴더에 PDF 파일이 없습니다.");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -174,6 +204,8 @@ namespace DwgExtractorManual
|
||||
|
||||
// UI 비활성화
|
||||
btnExtract.IsEnabled = false;
|
||||
btnPdfExtract.IsEnabled = false;
|
||||
btnMerge.IsEnabled = false;
|
||||
btnBrowseSource.IsEnabled = false;
|
||||
btnBrowseResult.IsEnabled = false;
|
||||
rbExcel.IsEnabled = false;
|
||||
@@ -200,9 +232,11 @@ namespace DwgExtractorManual
|
||||
{
|
||||
// UI 활성화
|
||||
btnExtract.IsEnabled = true;
|
||||
btnPdfExtract.IsEnabled = true;
|
||||
btnMerge.IsEnabled = true;
|
||||
btnBrowseSource.IsEnabled = true;
|
||||
btnBrowseResult.IsEnabled = true;
|
||||
rbExcel.IsEnabled = true;
|
||||
rbExcel.IsChecked = true;
|
||||
rbDatabase.IsEnabled = true;
|
||||
progressBar.Value = 100;
|
||||
LogMessage(new string('=', 50));
|
||||
@@ -308,11 +342,18 @@ namespace DwgExtractorManual
|
||||
await Task.Delay(10);
|
||||
}
|
||||
|
||||
// Excel 파일 저장
|
||||
var excelFileName = Path.Combine(txtResultFolder.Text,
|
||||
$"{DateTime.Now:yyyyMMdd_HHmmss}_DwgToExcel.xlsx");
|
||||
// Excel 파일 및 매핑 데이터 저장
|
||||
var timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
|
||||
var excelFileName = Path.Combine(txtResultFolder.Text, $"{timestamp}_DwgToExcel.xlsx");
|
||||
var mappingDataFile = Path.Combine(txtResultFolder.Text, $"{timestamp}_mapping_data.json");
|
||||
|
||||
LogMessage("💾 Excel 파일을 저장합니다...");
|
||||
LogMessage("💾 Excel 파일과 매핑 데이터를 저장합니다...");
|
||||
|
||||
// 매핑 딕셔너리를 JSON 파일로 저장 (PDF 데이터 병합용)
|
||||
_exportExcel.SaveMappingDictionary(mappingDataFile);
|
||||
LogMessage($"✅ 매핑 데이터 저장 완료: {Path.GetFileName(mappingDataFile)}");
|
||||
|
||||
// Excel 파일 저장
|
||||
_exportExcel.SaveAndCloseExcel(excelFileName);
|
||||
LogMessage($"✅ Excel 파일 저장 완료: {Path.GetFileName(excelFileName)}");
|
||||
}
|
||||
@@ -429,6 +470,290 @@ namespace DwgExtractorManual
|
||||
"완료", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
|
||||
private async void BtnPdfExtract_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// 입력 유효성 검사
|
||||
if (string.IsNullOrEmpty(txtSourceFolder.Text) || !Directory.Exists(txtSourceFolder.Text))
|
||||
{
|
||||
System.Windows.MessageBox.Show("유효한 변환할 폴더를 선택하세요.", "오류",
|
||||
MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(txtResultFolder.Text) || !Directory.Exists(txtResultFolder.Text))
|
||||
{
|
||||
System.Windows.MessageBox.Show("유효한 결과 저장 폴더를 선택하세요.", "오류",
|
||||
MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
// PDF 파일 존재 여부 확인
|
||||
var targetDir = new DirectoryInfo(txtSourceFolder.Text);
|
||||
var pdfFiles = targetDir.GetFiles("*.pdf", SearchOption.AllDirectories);
|
||||
|
||||
if (pdfFiles.Length == 0)
|
||||
{
|
||||
System.Windows.MessageBox.Show("선택한 폴더에 PDF 파일이 없습니다.", "정보",
|
||||
MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
// UI 비활성화
|
||||
btnExtract.IsEnabled = false;
|
||||
btnPdfExtract.IsEnabled = false;
|
||||
btnMerge.IsEnabled = false;
|
||||
btnBrowseSource.IsEnabled = false;
|
||||
btnBrowseResult.IsEnabled = false;
|
||||
rbExcel.IsEnabled = false;
|
||||
rbDatabase.IsEnabled = false;
|
||||
progressBar.Value = 0;
|
||||
|
||||
UpdateStatus("📄 PDF 추출 작업을 시작합니다...");
|
||||
LogMessage(new string('=', 50));
|
||||
LogMessage("📄 PDF 정보 추출 작업 시작");
|
||||
LogMessage(new string('=', 50));
|
||||
|
||||
try
|
||||
{
|
||||
await ProcessPdfFiles(pdfFiles);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogMessage($"❌ PDF 추출 중 치명적 오류 발생: {ex.Message}");
|
||||
UpdateStatus("PDF 추출 중 오류가 발생했습니다.");
|
||||
System.Windows.MessageBox.Show($"PDF 추출 중 오류가 발생했습니다:\n\n{ex.Message}",
|
||||
"오류", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// UI 활성화
|
||||
btnExtract.IsEnabled = true;
|
||||
btnPdfExtract.IsEnabled = true;
|
||||
btnMerge.IsEnabled = true;
|
||||
btnBrowseSource.IsEnabled = true;
|
||||
btnBrowseResult.IsEnabled = true;
|
||||
rbExcel.IsEnabled = true;
|
||||
rbDatabase.IsEnabled = true;
|
||||
progressBar.Value = 100;
|
||||
LogMessage(new string('=', 50));
|
||||
LogMessage("🏁 PDF 추출 작업 완료");
|
||||
LogMessage(new string('=', 50));
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ProcessPdfFiles(FileInfo[] pdfFiles)
|
||||
{
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
|
||||
UpdateStatus($"총 {pdfFiles.Length}개의 PDF 파일을 처리합니다...");
|
||||
LogMessage($"📊 처리할 PDF 파일 수: {pdfFiles.Length}개");
|
||||
LogMessage($"📂 소스 폴더: {txtSourceFolder.Text}");
|
||||
LogMessage($"💾 결과 폴더: {txtResultFolder.Text}");
|
||||
|
||||
// 임시 파일 리스트 생성 (긴 명령줄 방지)
|
||||
string tempFileList = Path.Combine(Path.GetTempPath(), $"pdf_files_{Guid.NewGuid():N}.txt");
|
||||
|
||||
try
|
||||
{
|
||||
// PDF 파일 경로를 임시 파일에 저장
|
||||
await File.WriteAllLinesAsync(tempFileList, pdfFiles.Select(f => f.FullName));
|
||||
LogMessage($"📄 임시 파일 리스트 생성됨: {pdfFiles.Length}개 파일");
|
||||
|
||||
// Python 스크립트 실행 설정 - 가상환경의 Python 사용
|
||||
string baseDirectory = AppDomain.CurrentDomain.BaseDirectory;
|
||||
string fletAnalysisPath = Path.Combine(baseDirectory, "fletimageanalysis");
|
||||
string pythonPath = Path.Combine(fletAnalysisPath, "venv", "Scripts", "python.exe");
|
||||
string scriptPath = Path.Combine(fletAnalysisPath, "batch_cli.py");
|
||||
|
||||
string schema = "한국도로공사"; // Valid schema option
|
||||
int concurrent = 3;
|
||||
bool batchMode = true;
|
||||
bool saveIntermediate = false;
|
||||
bool includeErrors = true;
|
||||
string outputPath = Path.Combine(txtResultFolder.Text, $"{DateTime.Now:yyyyMMdd_HHmmss}_PdfExtraction.csv");
|
||||
|
||||
// 파일 리스트 방식으로 변경 (긴 명령줄 방지)
|
||||
string arguments = $"--file-list \"{tempFileList}\" --schema \"{schema}\" --concurrent {concurrent} " +
|
||||
$"--batch-mode {batchMode.ToString().ToLower()} " +
|
||||
$"--save-intermediate {saveIntermediate.ToString().ToLower()} " +
|
||||
$"--include-errors {includeErrors.ToString().ToLower()} " +
|
||||
$"--output \"{outputPath}\"";
|
||||
|
||||
LogMessage($"🐍 Python 스크립트 실행 준비...");
|
||||
LogMessage($"📁 Python 경로: {pythonPath}");
|
||||
LogMessage($"📁 스크립트 경로: {scriptPath}");
|
||||
LogMessage($"📄 파일 리스트: {Path.GetFileName(tempFileList)}");
|
||||
|
||||
// 가상환경 Python 확인
|
||||
if (!File.Exists(pythonPath))
|
||||
{
|
||||
throw new Exception($"가상환경 Python을 찾을 수 없습니다: {pythonPath}\n\n" +
|
||||
"cleanup_and_setup.bat을 실행하여 가상환경을 설정하세요.");
|
||||
}
|
||||
|
||||
// 스크립트 파일 존재 확인
|
||||
if (!File.Exists(scriptPath))
|
||||
{
|
||||
throw new Exception($"Python 스크립트를 찾을 수 없습니다: {scriptPath}");
|
||||
}
|
||||
|
||||
ProcessStartInfo startInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = pythonPath, // 가상환경의 Python 사용
|
||||
Arguments = $"\"{scriptPath}\" {arguments}",
|
||||
WorkingDirectory = fletAnalysisPath,
|
||||
UseShellExecute = false,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
CreateNoWindow = true
|
||||
};
|
||||
|
||||
using (Process process = Process.Start(startInfo))
|
||||
{
|
||||
if (process == null)
|
||||
{
|
||||
throw new Exception("Python 프로세스를 시작할 수 없습니다.");
|
||||
}
|
||||
|
||||
LogMessage("🚀 Python 스크립트 실행 시작");
|
||||
|
||||
// 비동기로 출력 읽기
|
||||
var outputTask = ReadProcessOutputAsync(process);
|
||||
|
||||
// 프로세스 완료 대기
|
||||
await Task.Run(() => process.WaitForExit());
|
||||
|
||||
var (output, errors) = await outputTask;
|
||||
|
||||
stopwatch.Stop();
|
||||
|
||||
if (process.ExitCode == 0)
|
||||
{
|
||||
LogMessage("✅ PDF 추출 완료!");
|
||||
LogMessage($"📄 결과 파일: {Path.GetFileName(outputPath)}");
|
||||
LogMessage($"🕐 총 소요 시간: {stopwatch.ElapsedMilliseconds}ms");
|
||||
|
||||
UpdateStatus("PDF 추출 완료!");
|
||||
|
||||
// 결과 파일 존재 확인
|
||||
if (File.Exists(outputPath))
|
||||
{
|
||||
LogMessage($"✅ 결과 파일 생성 확인됨: {outputPath}");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogMessage($"⚠️ 결과 파일이 예상 위치에 없습니다: {outputPath}");
|
||||
}
|
||||
|
||||
// 자동 Excel 매핑 업데이트 (새로운 효율적 방식)
|
||||
await AutoUpdateExcelMappingWithPdfResults(outputPath);
|
||||
|
||||
System.Windows.MessageBox.Show(
|
||||
$"PDF 추출이 완료되었습니다!\n\n" +
|
||||
$"📄 처리된 파일: {pdfFiles.Length}개\n" +
|
||||
$"⏱️ 총 소요시간: {stopwatch.Elapsed:mm\\:ss}\n\n" +
|
||||
$"📊 결과 파일: {Path.GetFileName(outputPath)}",
|
||||
"완료", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogMessage($"❌ PDF 추출 실패 (Exit Code: {process.ExitCode})");
|
||||
if (!string.IsNullOrEmpty(errors))
|
||||
{
|
||||
LogMessage($"❌ 오류 내용: {errors}");
|
||||
}
|
||||
|
||||
throw new Exception($"Python 스크립트 실행 실패 (Exit Code: {process.ExitCode})\n\n{errors}");
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
// 임시 파일 정리
|
||||
try
|
||||
{
|
||||
if (File.Exists(tempFileList))
|
||||
{
|
||||
File.Delete(tempFileList);
|
||||
LogMessage("🗑️ 임시 파일 리스트 정리 완료");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogMessage($"⚠️ 임시 파일 정리 중 오류: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<(string output, string errors)> ReadProcessOutputAsync(Process process)
|
||||
{
|
||||
var outputBuilder = new System.Text.StringBuilder();
|
||||
var errorBuilder = new System.Text.StringBuilder();
|
||||
|
||||
var outputTask = Task.Run(async () =>
|
||||
{
|
||||
string line;
|
||||
while ((line = await process.StandardOutput.ReadLineAsync()) != null)
|
||||
{
|
||||
outputBuilder.AppendLine(line);
|
||||
|
||||
// 진행 상태 업데이트 처리
|
||||
if (line.StartsWith("PROGRESS:"))
|
||||
{
|
||||
try
|
||||
{
|
||||
var progressPart = line.Substring("PROGRESS:".Length).Trim();
|
||||
var parts = progressPart.Split('/');
|
||||
if (parts.Length == 2 &&
|
||||
int.TryParse(parts[0], out int current) &&
|
||||
int.TryParse(parts[1], out int total))
|
||||
{
|
||||
var percentage = (current * 100.0) / total;
|
||||
await Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
progressBar.Value = percentage;
|
||||
UpdateStatus($"PDF 처리 중: {current}/{total} ({percentage:F1}%)");
|
||||
});
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 파싱 오류 무시
|
||||
}
|
||||
}
|
||||
else if (line.StartsWith("START:"))
|
||||
{
|
||||
var message = line.Substring("START:".Length).Trim();
|
||||
await Dispatcher.InvokeAsync(() => LogMessage($"🔄 {message}"));
|
||||
}
|
||||
else if (line.StartsWith("COMPLETED:"))
|
||||
{
|
||||
var message = line.Substring("COMPLETED:".Length).Trim();
|
||||
await Dispatcher.InvokeAsync(() => LogMessage($"✅ {message}"));
|
||||
}
|
||||
else if (line.StartsWith("ERROR:"))
|
||||
{
|
||||
var errorInfo = line.Substring("ERROR:".Length).Trim();
|
||||
await Dispatcher.InvokeAsync(() => LogMessage($"❌ 오류: {errorInfo}"));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var errorTask = Task.Run(async () =>
|
||||
{
|
||||
string line;
|
||||
while ((line = await process.StandardError.ReadLineAsync()) != null)
|
||||
{
|
||||
errorBuilder.AppendLine(line);
|
||||
await Dispatcher.InvokeAsync(() => LogMessage($"⚠️ {line}"));
|
||||
}
|
||||
});
|
||||
|
||||
await Task.WhenAll(outputTask, errorTask);
|
||||
|
||||
return (outputBuilder.ToString(), errorBuilder.ToString());
|
||||
}
|
||||
|
||||
private void UpdateStatus(string message)
|
||||
{
|
||||
txtStatus.Text = message;
|
||||
@@ -442,6 +767,505 @@ namespace DwgExtractorManual
|
||||
txtLog.ScrollToEnd();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// PDF 추출 결과를 사용하여 Excel 매핑을 효율적으로 업데이트합니다 (새로운 방식).
|
||||
/// </summary>
|
||||
/// <param name="csvFilePath">CSV 결과 파일 경로</param>
|
||||
private async Task AutoUpdateExcelMappingWithPdfResults(string csvFilePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
LogMessage("🔄 Excel 매핑 자동 업데이트 시작 (효율적 방식)...");
|
||||
|
||||
// JSON 파일 경로 구성
|
||||
string jsonFilePath = csvFilePath.Replace(".csv", ".json");
|
||||
|
||||
if (!File.Exists(jsonFilePath))
|
||||
{
|
||||
LogMessage($"⚠️ JSON 파일이 없습니다: {Path.GetFileName(jsonFilePath)}");
|
||||
return;
|
||||
}
|
||||
|
||||
LogMessage($"📄 JSON 파일 확인됨: {Path.GetFileName(jsonFilePath)}");
|
||||
|
||||
// 최신 매핑 데이터 파일 찾기
|
||||
string resultDir = Path.GetDirectoryName(csvFilePath) ?? txtResultFolder.Text;
|
||||
var mappingDataFiles = Directory.GetFiles(resultDir, "*_mapping_data.json", SearchOption.TopDirectoryOnly)
|
||||
.OrderByDescending(f => File.GetCreationTime(f))
|
||||
.ToArray();
|
||||
|
||||
if (mappingDataFiles.Length == 0)
|
||||
{
|
||||
LogMessage("⚠️ 매핑 데이터 파일이 없습니다. DWG 파일을 먼저 처리하세요.");
|
||||
LogMessage("💡 'DWG 정보 추출' 버튼을 먼저 실행하여 매핑 데이터를 생성하세요.");
|
||||
return;
|
||||
}
|
||||
|
||||
string latestMappingDataFile = mappingDataFiles[0];
|
||||
LogMessage($"📊 최신 매핑 데이터 파일 발견: {Path.GetFileName(latestMappingDataFile)}");
|
||||
|
||||
// 새로운 ExportExcel 인스턴스 생성 및 데이터 로드
|
||||
ExportExcel? exportExcel = null;
|
||||
|
||||
try
|
||||
{
|
||||
LogMessage("🔄 매핑 데이터 로드 및 PDF 값 업데이트 중...");
|
||||
|
||||
exportExcel = new ExportExcel();
|
||||
|
||||
// 기존 매핑 딕셔너리 로드
|
||||
exportExcel.LoadMappingDictionary(latestMappingDataFile);
|
||||
|
||||
// PDF 데이터로 업데이트
|
||||
exportExcel.UpdateWithPdfData(jsonFilePath);
|
||||
|
||||
// 완전한 Excel 파일 생성 (DWG + PDF 데이터)
|
||||
var timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
|
||||
string completeExcelPath = Path.Combine(resultDir, $"{timestamp}_Complete_Mapping.xlsx");
|
||||
|
||||
LogMessage("📊 통합 Excel 파일 생성 중...");
|
||||
|
||||
// ⭐ 중요: 매핑 데이터를 Excel 시트에 기록
|
||||
exportExcel.WriteCompleteMapping();
|
||||
|
||||
// 매핑 워크북만 저장 (완전한 매핑 데이터용)
|
||||
exportExcel.SaveMappingWorkbookOnly(completeExcelPath);
|
||||
|
||||
// 업데이트된 매핑 데이터 저장
|
||||
exportExcel.SaveMappingDictionary(latestMappingDataFile);
|
||||
|
||||
// Excel 객체 정리
|
||||
exportExcel.CloseExcelObjectsWithoutSaving();
|
||||
|
||||
LogMessage($"✅ Excel 매핑 자동 업데이트 완료: {Path.GetFileName(completeExcelPath)}");
|
||||
LogMessage("📊 DWG 값과 PDF 값이 모두 포함된 통합 Excel 파일이 생성되었습니다.");
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogMessage($"❌ Excel 매핑 자동 업데이트 중 오류: {ex.Message}");
|
||||
Debug.WriteLine($"Excel 매핑 자동 업데이트 오류: {ex}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogMessage($"❌ Excel 매핑 자동 업데이트 중 오류: {ex.Message}");
|
||||
Debug.WriteLine($"Excel 매핑 자동 업데이트 오류: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// PDF 추출 결과 JSON 파일을 사용하여 Excel 매핑 시트를 업데이트합니다 (기존 방식 - 사용 안함).
|
||||
/// </summary>
|
||||
/// <param name="csvFilePath">CSV 결과 파일 경로</param>
|
||||
[Obsolete("이 메서드는 비효율적입니다. AutoUpdateExcelMappingWithPdfResults를 사용하세요.")]
|
||||
private async Task UpdateExcelMappingWithPdfResults(string csvFilePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
LogMessage("🔄 Excel 매핑 시트 업데이트 시작...");
|
||||
|
||||
// JSON 파일 경로 구성
|
||||
string jsonFilePath = csvFilePath.Replace(".csv", ".json");
|
||||
|
||||
if (!File.Exists(jsonFilePath))
|
||||
{
|
||||
LogMessage($"⚠️ JSON 파일이 없습니다: {Path.GetFileName(jsonFilePath)}");
|
||||
return;
|
||||
}
|
||||
|
||||
LogMessage($"📄 JSON 파일 확인됨: {Path.GetFileName(jsonFilePath)}");
|
||||
|
||||
// 기존 Excel 매핑 파일 검색 (임시 파일 제외)
|
||||
string resultDir = Path.GetDirectoryName(csvFilePath) ?? txtResultFolder.Text;
|
||||
var allExcelFiles = Directory.GetFiles(resultDir, "*_Mapping.xlsx", SearchOption.TopDirectoryOnly);
|
||||
|
||||
// 임시 파일(~$로 시작하는 파일) 필터링
|
||||
var excelFiles = allExcelFiles
|
||||
.Where(f => !Path.GetFileName(f).StartsWith("~$"))
|
||||
.ToArray();
|
||||
|
||||
ExportExcel? exportExcel = null;
|
||||
string excelFilePath = "";
|
||||
|
||||
LogMessage($"🔍 Excel 파일 검색 결과: 전체 {allExcelFiles.Length}개, 유효 {excelFiles.Length}개");
|
||||
|
||||
if (excelFiles.Length > 0)
|
||||
{
|
||||
// 기존 매핑 파일이 있는 경우 (가장 최근 파일 선택)
|
||||
excelFilePath = excelFiles.OrderByDescending(f => File.GetCreationTime(f)).First();
|
||||
LogMessage($"📊 기존 Excel 매핑 파일 발견: {Path.GetFileName(excelFilePath)}");
|
||||
|
||||
// 파일이 다른 프로그램에서 열려 있는지 확인
|
||||
if (IsFileInUse(excelFilePath))
|
||||
{
|
||||
LogMessage("⚠️ Excel 파일이 다른 프로그램에서 열려 있습니다. 파일을 닫고 다시 시도하세요.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 기존 파일을 복사하여 새 버전 생성
|
||||
string newExcelPath = Path.Combine(resultDir,
|
||||
$"{DateTime.Now:yyyyMMdd_HHmmss}_Mapping_Updated.xlsx");
|
||||
File.Copy(excelFilePath, newExcelPath, true);
|
||||
excelFilePath = newExcelPath;
|
||||
|
||||
LogMessage($"📋 Excel 파일 복사됨: {Path.GetFileName(newExcelPath)}");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogMessage("⚠️ 기존 Excel 매핑 파일이 없습니다. DWG 파일을 먼저 처리하세요.");
|
||||
LogMessage("💡 'DWG 정보 추출' 버튼을 먼저 실행하여 매핑 파일을 생성하세요.");
|
||||
return;
|
||||
}
|
||||
|
||||
// ExportExcel 클래스로 JSON 데이터 업데이트
|
||||
LogMessage("🔄 PDF 값으로 Excel 업데이트 중...");
|
||||
|
||||
exportExcel = new ExportExcel();
|
||||
|
||||
// 기존 Excel 파일을 열어 JSON 값으로 업데이트
|
||||
bool success = exportExcel.UpdateExistingExcelWithJson(excelFilePath, jsonFilePath);
|
||||
|
||||
if (success)
|
||||
{
|
||||
// 업데이트된 Excel 파일 저장
|
||||
bool saveSuccess = exportExcel.SaveExcel();
|
||||
if (saveSuccess)
|
||||
{
|
||||
LogMessage($"✅ Excel 매핑 시트 업데이트 완료: {Path.GetFileName(excelFilePath)}");
|
||||
LogMessage("📊 PDF 값이 Excel 매핑 시트의 'Pdf_value' 컬럼에 추가되었습니다.");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogMessage("❌ Excel 파일 저장 실패");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogMessage("❌ Excel 매핑 시트 업데이트 실패");
|
||||
}
|
||||
|
||||
exportExcel?.Dispose();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogMessage($"❌ Excel 매핑 업데이트 중 오류: {ex.Message}");
|
||||
Debug.WriteLine($"Excel 매핑 업데이트 오류: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 파일이 다른 프로세스에서 사용 중인지 확인합니다.
|
||||
/// </summary>
|
||||
/// <param name="filePath">확인할 파일 경로</param>
|
||||
/// <returns>사용 중이면 true, 아니면 false</returns>
|
||||
private bool IsFileInUse(string filePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (FileStream stream = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.None))
|
||||
{
|
||||
// 파일을 열 수 있으면 사용 중이 아님
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
// 파일을 열 수 없으면 사용 중
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// 다른 오류가 발생하면 안전하게 사용 중으로 간주
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 가장 최근의 매핑 데이터 파일을 찾습니다.
|
||||
/// </summary>
|
||||
/// <param name="resultDir">검색할 디렉토리</param>
|
||||
/// <returns>최신 매핑 데이터 파일 경로 또는 null</returns>
|
||||
private string? FindLatestMappingDataFile(string resultDir)
|
||||
{
|
||||
try
|
||||
{
|
||||
var mappingDataFiles = Directory.GetFiles(resultDir, "*_mapping_data.json", SearchOption.TopDirectoryOnly)
|
||||
.OrderByDescending(f => File.GetCreationTime(f))
|
||||
.ToArray();
|
||||
|
||||
return mappingDataFiles.Length > 0 ? mappingDataFiles[0] : null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogMessage($"❌ 매핑 데이터 파일 검색 중 오류: {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private async void BtnMerge_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// 입력 유효성 검사
|
||||
if (string.IsNullOrEmpty(txtResultFolder.Text) || !Directory.Exists(txtResultFolder.Text))
|
||||
{
|
||||
System.Windows.MessageBox.Show("유효한 결과 저장 폴더를 선택하세요.", "오류",
|
||||
MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
// UI 비활성화
|
||||
btnExtract.IsEnabled = false;
|
||||
btnPdfExtract.IsEnabled = false;
|
||||
btnMerge.IsEnabled = false;
|
||||
btnBrowseSource.IsEnabled = false;
|
||||
btnBrowseResult.IsEnabled = false;
|
||||
rbExcel.IsEnabled = false;
|
||||
rbDatabase.IsEnabled = false;
|
||||
progressBar.Value = 0;
|
||||
|
||||
UpdateStatus("🔗 Excel 매핑 병합 작업을 시작합니다...");
|
||||
LogMessage(new string('=', 50));
|
||||
LogMessage("🔗 Excel 매핑 병합 작업 시작");
|
||||
LogMessage(new string('=', 50));
|
||||
|
||||
try
|
||||
{
|
||||
await MergeLatestPdfResults();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogMessage($"❌ 매핑 병합 중 치명적 오류 발생: {ex.Message}");
|
||||
UpdateStatus("매핑 병합 중 오류가 발생했습니다.");
|
||||
System.Windows.MessageBox.Show($"매핑 병합 중 오류가 발생했습니다:\n\n{ex.Message}",
|
||||
"오류", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// UI 활성화
|
||||
btnExtract.IsEnabled = true;
|
||||
btnPdfExtract.IsEnabled = true;
|
||||
btnMerge.IsEnabled = true;
|
||||
btnBrowseSource.IsEnabled = true;
|
||||
btnBrowseResult.IsEnabled = true;
|
||||
rbExcel.IsEnabled = true;
|
||||
rbDatabase.IsEnabled = true;
|
||||
progressBar.Value = 100;
|
||||
LogMessage(new string('=', 50));
|
||||
LogMessage("🏁 매핑 병합 작업 완료");
|
||||
LogMessage(new string('=', 50));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 가장 최근의 PDF 추출 JSON 파일을 찾아 Excel 매핑을 효율적으로 업데이트합니다.
|
||||
/// </summary>
|
||||
private async Task MergeLatestPdfResults()
|
||||
{
|
||||
try
|
||||
{
|
||||
LogMessage("🔍 최신 PDF 추출 결과 파일 검색 중...");
|
||||
|
||||
string resultDir = txtResultFolder.Text;
|
||||
|
||||
// 가장 최근의 PDF 추출 JSON 파일 찾기
|
||||
var jsonFiles = Directory.GetFiles(resultDir, "*_PdfExtraction.json", SearchOption.TopDirectoryOnly)
|
||||
.OrderByDescending(f => File.GetCreationTime(f))
|
||||
.ToArray();
|
||||
|
||||
if (jsonFiles.Length == 0)
|
||||
{
|
||||
LogMessage("❌ PDF 추출 JSON 파일을 찾을 수 없습니다.");
|
||||
LogMessage("💡 먼저 'PDF 추출' 버튼을 실행하여 JSON 파일을 생성하세요.");
|
||||
|
||||
System.Windows.MessageBox.Show(
|
||||
"PDF 추출 JSON 파일을 찾을 수 없습니다.\n\n" +
|
||||
"먼저 'PDF 추출' 버튼을 실행하여 JSON 파일을 생성하세요.",
|
||||
"파일 없음", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
// 최신 매핑 데이터 파일 찾기
|
||||
var mappingDataFiles = Directory.GetFiles(resultDir, "*_mapping_data.json", SearchOption.TopDirectoryOnly)
|
||||
.OrderByDescending(f => File.GetCreationTime(f))
|
||||
.ToArray();
|
||||
|
||||
if (mappingDataFiles.Length == 0)
|
||||
{
|
||||
LogMessage("❌ 매핑 데이터 파일을 찾을 수 없습니다.");
|
||||
LogMessage("💡 먼저 'DWG 정보 추출' 버튼을 실행하여 매핑 데이터를 생성하세요.");
|
||||
|
||||
System.Windows.MessageBox.Show(
|
||||
"매핑 데이터 파일을 찾을 수 없습니다.\n\n" +
|
||||
"먼저 'DWG 정보 추출' 버튼을 실행하여 매핑 데이터를 생성하세요.",
|
||||
"파일 없음", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
string latestJsonFile = jsonFiles[0];
|
||||
string latestMappingDataFile = mappingDataFiles[0];
|
||||
|
||||
LogMessage($"✅ 최신 JSON 파일 발견: {Path.GetFileName(latestJsonFile)}");
|
||||
LogMessage($"📅 생성일시: {File.GetCreationTime(latestJsonFile):yyyy-MM-dd HH:mm:ss}");
|
||||
LogMessage($"📊 최신 매핑 데이터 파일: {Path.GetFileName(latestMappingDataFile)}");
|
||||
|
||||
// JSON 파일 내용 미리보기
|
||||
await ShowJsonPreview(latestJsonFile);
|
||||
|
||||
// 사용자 확인
|
||||
var result = System.Windows.MessageBox.Show(
|
||||
$"다음 파일들로 Excel 매핑을 업데이트하시겠습니까?\n\n" +
|
||||
$"📄 PDF JSON: {Path.GetFileName(latestJsonFile)}\n" +
|
||||
$"📊 매핑 데이터: {Path.GetFileName(latestMappingDataFile)}\n" +
|
||||
$"📅 생성: {File.GetCreationTime(latestJsonFile):yyyy-MM-dd HH:mm:ss}\n" +
|
||||
$"📏 크기: {new FileInfo(latestJsonFile).Length / 1024:N0} KB",
|
||||
"매핑 업데이트 확인",
|
||||
MessageBoxButton.YesNo,
|
||||
MessageBoxImage.Question);
|
||||
|
||||
if (result != MessageBoxResult.Yes)
|
||||
{
|
||||
LogMessage("❌ 사용자에 의해 매핑 업데이트가 취소되었습니다.");
|
||||
UpdateStatus("매핑 업데이트가 취소되었습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 효율적인 Excel 매핑 업데이트 실행
|
||||
UpdateStatus("🔄 Excel 매핑 업데이트 중 (효율적 방식)...");
|
||||
progressBar.Value = 25;
|
||||
|
||||
ExportExcel? exportExcel = null;
|
||||
|
||||
try
|
||||
{
|
||||
LogMessage("🔄 매핑 데이터 로드 및 PDF 값 업데이트 중...");
|
||||
|
||||
exportExcel = new ExportExcel();
|
||||
|
||||
progressBar.Value = 40;
|
||||
|
||||
// 기존 매핑 딕셔너리 로드
|
||||
exportExcel.LoadMappingDictionary(latestMappingDataFile);
|
||||
|
||||
progressBar.Value = 60;
|
||||
|
||||
// PDF 데이터로 업데이트
|
||||
exportExcel.UpdateWithPdfData(latestJsonFile);
|
||||
|
||||
progressBar.Value = 80;
|
||||
|
||||
// 완전한 Excel 파일 생성 (DWG + PDF 데이터)
|
||||
var timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
|
||||
string completeExcelPath = Path.Combine(resultDir, $"{timestamp}_Complete_Mapping_Merged.xlsx");
|
||||
|
||||
LogMessage("📊 통합 Excel 파일 생성 중...");
|
||||
|
||||
// ⭐ 중요: 매핑 데이터를 Excel 시트에 기록
|
||||
exportExcel.WriteCompleteMapping();
|
||||
|
||||
// 매핑 워크북만 저장 (완전한 매핑 데이터용)
|
||||
exportExcel.SaveMappingWorkbookOnly(completeExcelPath);
|
||||
|
||||
// 업데이트된 매핑 데이터 저장
|
||||
exportExcel.SaveMappingDictionary(latestMappingDataFile);
|
||||
|
||||
// Excel 객체 정리
|
||||
exportExcel.CloseExcelObjectsWithoutSaving();
|
||||
|
||||
progressBar.Value = 100;
|
||||
UpdateStatus("✅ 매핑 병합 완료!");
|
||||
|
||||
LogMessage($"✅ Excel 매핑 병합 완료: {Path.GetFileName(completeExcelPath)}");
|
||||
LogMessage("📊 DWG 값과 PDF 값이 모두 포함된 통합 Excel 파일이 생성되었습니다.");
|
||||
|
||||
System.Windows.MessageBox.Show(
|
||||
$"Excel 매핑 병합이 완료되었습니다!\n\n" +
|
||||
$"📄 사용된 JSON: {Path.GetFileName(latestJsonFile)}\n" +
|
||||
$"📊 생성된 파일: {Path.GetFileName(completeExcelPath)}\n" +
|
||||
$"✅ DWG 값과 PDF 값이 모두 포함된 통합 Excel 파일",
|
||||
"완료", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogMessage($"❌ 매핑 병합 중 오류: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogMessage($"❌ 매핑 병합 중 오류: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// JSON 파일의 내용을 미리보기로 보여줍니다.
|
||||
/// </summary>
|
||||
/// <param name="jsonFilePath">JSON 파일 경로</param>
|
||||
private async Task ShowJsonPreview(string jsonFilePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
LogMessage("📋 JSON 파일 내용 미리보기:");
|
||||
|
||||
string jsonContent = await File.ReadAllTextAsync(jsonFilePath);
|
||||
using var jsonDocument = JsonDocument.Parse(jsonContent);
|
||||
var root = jsonDocument.RootElement;
|
||||
|
||||
// 메타데이터 정보 표시
|
||||
if (root.TryGetProperty("metadata", out var metadata))
|
||||
{
|
||||
if (metadata.TryGetProperty("total_files", out var totalFiles))
|
||||
{
|
||||
LogMessage($" 📊 총 파일 수: {totalFiles}");
|
||||
}
|
||||
if (metadata.TryGetProperty("success_files", out var successFiles))
|
||||
{
|
||||
LogMessage($" ✅ 성공 파일: {successFiles}");
|
||||
}
|
||||
if (metadata.TryGetProperty("failed_files", out var failedFiles))
|
||||
{
|
||||
LogMessage($" ❌ 실패 파일: {failedFiles}");
|
||||
}
|
||||
if (metadata.TryGetProperty("generated_at", out var generatedAt))
|
||||
{
|
||||
LogMessage($" 📅 생성시간: {generatedAt}");
|
||||
}
|
||||
}
|
||||
|
||||
// 결과 파일 수 표시
|
||||
if (root.TryGetProperty("results", out var results) && results.ValueKind == JsonValueKind.Array)
|
||||
{
|
||||
var resultsArray = results.EnumerateArray().ToArray();
|
||||
LogMessage($" 📄 분석 결과: {resultsArray.Length}개 파일");
|
||||
|
||||
// 처음 몇 개 파일 이름 표시
|
||||
int showCount = Math.Min(3, resultsArray.Length);
|
||||
for (int i = 0; i < showCount; i++)
|
||||
{
|
||||
if (resultsArray[i].TryGetProperty("file_info", out var fileInfo) &&
|
||||
fileInfo.TryGetProperty("name", out var fileName))
|
||||
{
|
||||
LogMessage($" - {fileName.GetString()}");
|
||||
}
|
||||
}
|
||||
|
||||
if (resultsArray.Length > 3)
|
||||
{
|
||||
LogMessage($" ... 외 {resultsArray.Length - 3}개 파일");
|
||||
}
|
||||
}
|
||||
|
||||
LogMessage("📋 JSON 미리보기 완료");
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogMessage($"⚠️ JSON 미리보기 중 오류: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnClosed(EventArgs e)
|
||||
{
|
||||
_timer?.Stop();
|
||||
|
||||
Reference in New Issue
Block a user