307 lines
12 KiB
Python
307 lines
12 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
CSV 저장 유틸리티 모듈
|
|
DXF 타이틀블럭 Attribute 정보를 CSV 형식으로 저장
|
|
"""
|
|
|
|
import csv
|
|
import os
|
|
import logging
|
|
from typing import List, Dict, Any, Optional
|
|
from datetime import datetime
|
|
|
|
from config import Config
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class TitleBlockCSVExporter:
|
|
"""타이틀블럭 속성 정보 CSV 저장 클래스"""
|
|
|
|
def __init__(self, output_dir: str = None):
|
|
"""CSV 저장기 초기화"""
|
|
self.output_dir = output_dir or Config.RESULTS_FOLDER
|
|
os.makedirs(self.output_dir, exist_ok=True)
|
|
|
|
def export_title_block_attributes(
|
|
self,
|
|
title_block_info: Dict[str, Any],
|
|
filename: str = None
|
|
) -> Optional[str]:
|
|
"""
|
|
타이틀블럭 속성 정보를 CSV 파일로 저장
|
|
|
|
Args:
|
|
title_block_info: 타이틀블럭 정보 딕셔너리
|
|
filename: 저장할 파일명 (없으면 자동 생성)
|
|
|
|
Returns:
|
|
저장된 파일 경로 또는 None (실패시)
|
|
"""
|
|
try:
|
|
if not title_block_info or not title_block_info.get('all_attributes'):
|
|
logger.warning("타이틀블럭 속성 정보가 없습니다.")
|
|
return None
|
|
|
|
# 파일명 생성
|
|
if not filename:
|
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
block_name = title_block_info.get('block_name', 'Unknown_Block')
|
|
filename = f"title_block_attributes_{block_name}_{timestamp}.csv"
|
|
|
|
# 확장자 확인
|
|
if not filename.endswith('.csv'):
|
|
filename += '.csv'
|
|
|
|
filepath = os.path.join(self.output_dir, filename)
|
|
|
|
# CSV 헤더 정의
|
|
headers = [
|
|
'block_name', # block_ref.name
|
|
'attr_prompt', # attr.prompt
|
|
'attr_text', # attr.text
|
|
'attr_tag', # attr.tag
|
|
'attr_insert_x', # attr.insert_x
|
|
'attr_insert_y', # attr.insert_y
|
|
'bounding_box_min_x', # attr.bounding_box.min_x
|
|
'bounding_box_min_y', # attr.bounding_box.min_y
|
|
'bounding_box_max_x', # attr.bounding_box.max_x
|
|
'bounding_box_max_y', # attr.bounding_box.max_y
|
|
'bounding_box_width', # attr.bounding_box.width
|
|
'bounding_box_height', # attr.bounding_box.height
|
|
'attr_height', # 추가: 텍스트 높이
|
|
'attr_rotation', # 추가: 회전각
|
|
'attr_layer', # 추가: 레이어
|
|
'attr_style', # 추가: 스타일
|
|
'entity_handle' # 추가: 엔티티 핸들
|
|
]
|
|
|
|
# CSV 데이터 준비
|
|
csv_rows = []
|
|
block_name = title_block_info.get('block_name', '')
|
|
|
|
for attr in title_block_info.get('all_attributes', []):
|
|
row = {
|
|
'block_name': block_name,
|
|
'attr_prompt': attr.get('prompt', '') or '',
|
|
'attr_text': attr.get('text', '') or '',
|
|
'attr_tag': attr.get('tag', '') or '',
|
|
'attr_insert_x': attr.get('insert_x', '') or '',
|
|
'attr_insert_y': attr.get('insert_y', '') or '',
|
|
'attr_height': attr.get('height', '') or '',
|
|
'attr_rotation': attr.get('rotation', '') or '',
|
|
'attr_layer': attr.get('layer', '') or '',
|
|
'attr_style': attr.get('style', '') or '',
|
|
'entity_handle': attr.get('entity_handle', '') or '',
|
|
}
|
|
|
|
# 바운딩 박스 정보 추가
|
|
bbox = attr.get('bounding_box')
|
|
if bbox:
|
|
row.update({
|
|
'bounding_box_min_x': bbox.get('min_x', ''),
|
|
'bounding_box_min_y': bbox.get('min_y', ''),
|
|
'bounding_box_max_x': bbox.get('max_x', ''),
|
|
'bounding_box_max_y': bbox.get('max_y', ''),
|
|
'bounding_box_width': bbox.get('max_x', 0) - bbox.get('min_x', 0) if bbox.get('max_x') and bbox.get('min_x') else '',
|
|
'bounding_box_height': bbox.get('max_y', 0) - bbox.get('min_y', 0) if bbox.get('max_y') and bbox.get('min_y') else '',
|
|
})
|
|
else:
|
|
row.update({
|
|
'bounding_box_min_x': '',
|
|
'bounding_box_min_y': '',
|
|
'bounding_box_max_x': '',
|
|
'bounding_box_max_y': '',
|
|
'bounding_box_width': '',
|
|
'bounding_box_height': '',
|
|
})
|
|
|
|
csv_rows.append(row)
|
|
|
|
# CSV 파일 저장
|
|
with open(filepath, 'w', newline='', encoding='utf-8-sig') as csvfile:
|
|
writer = csv.DictWriter(csvfile, fieldnames=headers)
|
|
|
|
# 헤더 작성
|
|
writer.writeheader()
|
|
|
|
# 데이터 작성
|
|
writer.writerows(csv_rows)
|
|
|
|
logger.info(f"타이틀블럭 속성 CSV 저장 완료: {filepath}")
|
|
return filepath
|
|
|
|
except Exception as e:
|
|
logger.error(f"CSV 저장 중 오류: {e}")
|
|
return None
|
|
|
|
def create_attribute_table_data(
|
|
self,
|
|
title_block_info: Dict[str, Any]
|
|
) -> List[Dict[str, str]]:
|
|
"""
|
|
UI 테이블 표시용 데이터 생성
|
|
|
|
Args:
|
|
title_block_info: 타이틀블럭 정보 딕셔너리
|
|
|
|
Returns:
|
|
테이블 표시용 데이터 리스트
|
|
"""
|
|
try:
|
|
if not title_block_info or not title_block_info.get('all_attributes'):
|
|
return []
|
|
|
|
table_data = []
|
|
block_name = title_block_info.get('block_name', '')
|
|
|
|
for i, attr in enumerate(title_block_info.get('all_attributes', [])):
|
|
# 바운딩 박스 정보 포맷팅
|
|
bbox_str = ""
|
|
bbox = attr.get('bounding_box')
|
|
if bbox:
|
|
bbox_str = f"({bbox.get('min_x', 0):.1f}, {bbox.get('min_y', 0):.1f}) - ({bbox.get('max_x', 0):.1f}, {bbox.get('max_y', 0):.1f})"
|
|
|
|
row = {
|
|
'No.': str(i + 1),
|
|
'Block Name': block_name,
|
|
'Tag': attr.get('tag', ''),
|
|
'Text': attr.get('text', '')[:30] + ('...' if len(attr.get('text', '')) > 30 else ''), # 텍스트 길이 제한
|
|
'Prompt': attr.get('prompt', '') or 'N/A',
|
|
'X': f"{attr.get('insert_x', 0):.1f}",
|
|
'Y': f"{attr.get('insert_y', 0):.1f}",
|
|
'Bounding Box': bbox_str or 'N/A',
|
|
'Height': f"{attr.get('height', 0):.1f}",
|
|
'Layer': attr.get('layer', ''),
|
|
}
|
|
|
|
table_data.append(row)
|
|
|
|
return table_data
|
|
|
|
except Exception as e:
|
|
logger.error(f"테이블 데이터 생성 중 오류: {e}")
|
|
return []
|
|
|
|
|
|
def main():
|
|
"""테스트용 메인 함수"""
|
|
logging.basicConfig(level=logging.INFO)
|
|
|
|
# 테스트 데이터
|
|
test_title_block = {
|
|
'block_name': 'TEST_TITLE_BLOCK',
|
|
'all_attributes': [
|
|
{
|
|
'tag': 'DRAWING_NAME',
|
|
'text': '테스트 도면',
|
|
'prompt': '도면명을 입력하세요',
|
|
'insert_x': 100.0,
|
|
'insert_y': 200.0,
|
|
'height': 5.0,
|
|
'rotation': 0.0,
|
|
'layer': '0',
|
|
'style': 'Standard',
|
|
'entity_handle': 'ABC123',
|
|
'bounding_box': {
|
|
'min_x': 100.0,
|
|
'min_y': 200.0,
|
|
'max_x': 180.0,
|
|
'max_y': 210.0
|
|
}
|
|
},
|
|
{
|
|
'tag': 'DRAWING_NUMBER',
|
|
'text': 'TEST-001',
|
|
'prompt': '도면번호를 입력하세요',
|
|
'insert_x': 100.0,
|
|
'insert_y': 190.0,
|
|
'height': 4.0,
|
|
'rotation': 0.0,
|
|
'layer': '0',
|
|
'style': 'Standard',
|
|
'entity_handle': 'DEF456',
|
|
'bounding_box': {
|
|
'min_x': 100.0,
|
|
'min_y': 190.0,
|
|
'max_x': 150.0,
|
|
'max_y': 198.0
|
|
}
|
|
}
|
|
]
|
|
}
|
|
|
|
# CSV 저장 테스트
|
|
exporter = TitleBlockCSVExporter()
|
|
|
|
# 테이블 데이터 생성 테스트
|
|
table_data = exporter.create_attribute_table_data(test_title_block)
|
|
print("테이블 데이터:")
|
|
for row in table_data:
|
|
print(row)
|
|
|
|
# CSV 저장 테스트
|
|
saved_path = exporter.export_title_block_attributes(test_title_block, "test_export.csv")
|
|
if saved_path:
|
|
print(f"\nCSV 저장 성공: {saved_path}")
|
|
else:
|
|
print("\nCSV 저장 실패")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|
|
|
|
import json
|
|
|
|
def export_analysis_results_to_csv(data: List[Dict[str, Any]], file_path: str):
|
|
"""
|
|
분석 결과를 CSV 파일로 저장합니다. pdf_analysis_result 컬럼의 JSON 데이터를 평탄화합니다.
|
|
Args:
|
|
data: 분석 결과 딕셔너리 리스트
|
|
file_path: 저장할 CSV 파일 경로
|
|
"""
|
|
if not data:
|
|
logger.warning("내보낼 데이터가 없습니다.")
|
|
return
|
|
|
|
all_keys = set()
|
|
processed_data = []
|
|
|
|
for row in data:
|
|
new_row = row.copy()
|
|
if 'pdf_analysis_result' in new_row and new_row['pdf_analysis_result']:
|
|
try:
|
|
json_data = new_row['pdf_analysis_result']
|
|
if isinstance(json_data, str):
|
|
json_data = json.loads(json_data)
|
|
|
|
if isinstance(json_data, dict):
|
|
for k, v in json_data.items():
|
|
new_row[f"pdf_analysis_result_{k}"] = v
|
|
del new_row['pdf_analysis_result']
|
|
else:
|
|
new_row['pdf_analysis_result'] = str(json_data)
|
|
except (json.JSONDecodeError, TypeError) as e:
|
|
logger.warning(f"pdf_analysis_result 파싱 오류: {e}, 원본 데이터 유지: {new_row['pdf_analysis_result']}")
|
|
new_row['pdf_analysis_result'] = str(new_row['pdf_analysis_result'])
|
|
|
|
processed_data.append(new_row)
|
|
all_keys.update(new_row.keys())
|
|
|
|
# 'pdf_analysis_result'가 평탄화된 경우 최종 키에서 제거
|
|
if 'pdf_analysis_result' in all_keys:
|
|
all_keys.remove('pdf_analysis_result')
|
|
|
|
sorted_keys = sorted(list(all_keys))
|
|
|
|
try:
|
|
with open(file_path, 'w', newline='', encoding='utf-8-sig') as output_file:
|
|
dict_writer = csv.DictWriter(output_file, sorted_keys)
|
|
dict_writer.writeheader()
|
|
dict_writer.writerows(processed_data)
|
|
logger.info(f"분석 결과 CSV 저장 완료: {file_path}")
|
|
except Exception as e:
|
|
logger.error(f"분석 결과 CSV 저장 중 오류: {e}")
|
|
|