commit cf71ae26ca487f88655845e7c287246f4173390d Author: gzero-ser7 Date: Thu Nov 7 17:14:35 2024 +0900 init diff --git a/hwp_conversion.log b/hwp_conversion.log new file mode 100644 index 0000000..3110fb2 --- /dev/null +++ b/hwp_conversion.log @@ -0,0 +1,12 @@ +2024-11-06 15:06:07,560 - INFO - 변환 성공: 1.hwp -> 1.xml +2024-11-06 15:06:07,857 - INFO - 변환 성공: 2.hwp -> 2.xml +2024-11-06 15:28:35,640 - INFO - 변환 성공: 1.hwp -> 1.xml +2024-11-06 15:28:35,654 - INFO - BINDATA 섹션 제거 완료: C:\Users\gzero-ser7-win11\Documents\hwpTest\Output\1.xml +2024-11-06 15:28:35,933 - INFO - 변환 성공: 2.hwp -> 2.xml +2024-11-06 15:28:35,948 - INFO - BINDATA 섹션 제거 완료: C:\Users\gzero-ser7-win11\Documents\hwpTest\Output\2.xml +2024-11-06 15:31:54,423 - INFO - 변환 성공: 1.hwp -> 1.xml +2024-11-06 15:31:54,437 - INFO - BINDATASTORAGE 섹션 제거 완료: C:\Users\gzero-ser7-win11\Documents\hwpTest\Output\1.xml +2024-11-06 15:31:54,697 - INFO - 변환 성공: 2.hwp -> 2.xml +2024-11-06 15:31:54,711 - INFO - BINDATASTORAGE 섹션 제거 완료: C:\Users\gzero-ser7-win11\Documents\hwpTest\Output\2.xml +2024-11-06 15:34:34,911 - INFO - 변환 성공: 1.hwp -> 1.xml +2024-11-06 15:34:35,179 - INFO - 변환 성공: 2.hwp -> 2.xml diff --git a/score.py b/score.py new file mode 100644 index 0000000..acc99e6 --- /dev/null +++ b/score.py @@ -0,0 +1,227 @@ +import json +import xml.etree.ElementTree as ET +import os +from pathlib import Path +import pandas as pd +from datetime import datetime + +class XMLScorer: + def __init__(self, scoring_criteria_path): + """ + 채점 기준표 JSON 파일을 로드하여 초기화합니다. + + Args: + scoring_criteria_path (str): 채점 기준표 JSON 파일 경로 + """ + self.scoring_criteria = self._load_scoring_criteria(scoring_criteria_path) + + def _load_scoring_criteria(self, file_path): + """ + JSON 채점 기준표를 로드합니다. + + Args: + file_path (str): JSON 파일 경로 + + Returns: + dict: 채점 기준표 데이터 + """ + with open(file_path, 'r', encoding='utf-8') as f: + return json.load(f) + + def _find_element_value(self, root, element_name, attribute_name): + """ + XML에서 특정 요소와 속성값을 찾습니다. + + Args: + root (Element): XML 루트 요소 + element_name (str): 찾을 요소 이름 + attribute_name (str): 찾을 속성 이름 + + Returns: + str: 찾은 속성값 또는 None + """ + element = root.find(f".//{element_name}") + if element is not None: + return element.get(attribute_name) + return None + + def score_xml_file(self, xml_path): + """ + 단일 XML 파일을 채점합니다. + + Args: + xml_path (str): XML 파일 경로 + + Returns: + dict: 채점 결과 + """ + try: + tree = ET.parse(xml_path) + root = tree.getroot() + + total_score = 0 + results = { + 'filename': os.path.basename(xml_path), + 'criteria_matches': [], + 'total_score': 0 + } + + # 각 채점 기준에 대해 검사 + for criterion_id, criterion in self.scoring_criteria.items(): + element_name = criterion['ele'] + attribute_name = criterion['arg'] + expected_value = criterion['value'] + points = criterion['points'] + + actual_value = self._find_element_value(root, element_name, attribute_name) + + match = { + 'criterion': f"{element_name}.{attribute_name}", + 'expected': expected_value, + 'actual': actual_value, + 'points': 0 + } + + # 값이 일치하면 점수 부여 + if actual_value == expected_value: + total_score += points + match['points'] = points + + results['criteria_matches'].append(match) + + results['total_score'] = total_score + return results + + except ET.ParseError as e: + return { + 'filename': os.path.basename(xml_path), + 'error': f"XML 파싱 오류: {str(e)}", + 'total_score': 0 + } + + def export_to_excel(self, results, output_path=None): + """ + 채점 결과를 엑셀 파일로 저장합니다. + + Args: + results (list): 채점 결과 리스트 + output_path (str, optional): 출력 파일 경로. + None이면 현재 시간으로 파일명 생성 + + Returns: + str: 저장된 엑셀 파일 경로 + """ + if output_path is None: + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + output_path = f"scoring_results_{timestamp}.xlsx" + + # 요약 시트용 데이터 준비 + summary_data = [] + detail_data = [] + + for result in results: + # 요약 정보 + summary_row = { + '파일명': result['filename'], + '총점': result.get('total_score', 0) + } + if 'error' in result: + summary_row['오류'] = result['error'] + summary_data.append(summary_row) + + # 상세 정보 + if 'criteria_matches' in result: + for match in result['criteria_matches']: + detail_row = { + '파일명': result['filename'], + '채점항목': match['criterion'], + '기대값': match['expected'], + '실제값': match['actual'], + '획득점수': match['points'] + } + detail_data.append(detail_row) + + # DataFrame 생성 + summary_df = pd.DataFrame(summary_data) + detail_df = pd.DataFrame(detail_data) + + # ExcelWriter 객체 생성 + with pd.ExcelWriter(output_path, engine='openpyxl') as writer: + # 요약 시트 작성 + summary_df.to_excel(writer, sheet_name='채점결과요약', index=False) + + # 상세 시트 작성 + detail_df.to_excel(writer, sheet_name='채점상세내역', index=False) + + # 열 너비 자동 조정 + for sheet_name in writer.sheets: + worksheet = writer.sheets[sheet_name] + for column in worksheet.columns: + max_length = 0 + column = [cell for cell in column] + for cell in column: + try: + if len(str(cell.value)) > max_length: + max_length = len(str(cell.value)) + except: + pass + adjusted_width = (max_length + 2) + worksheet.column_dimensions[column[0].column_letter].width = adjusted_width + + return output_path + + + def score_directory(self, xml_directory): + """ + 디렉토리 내의 모든 XML 파일을 채점합니다. + + Args: + xml_directory (str): XML 파일들이 있는 디렉토리 경로 + + Returns: + list: 모든 파일의 채점 결과 + """ + results = [] + xml_files = Path(xml_directory).glob('*.xml') + + for xml_file in xml_files: + result = self.score_xml_file(str(xml_file)) + results.append(result) + + return results + +# 사용 예시 +def main(): + # 채점기준표 파일 경로 + scoring_criteria_path = "scoring_criteria.json" + # XML 파일들이 있는 디렉토리 경로 + xml_directory = r"C:\Users\gzero-ser7-win11\Documents\hwpTest\Output" + + # 채점기 초기화 + scorer = XMLScorer(scoring_criteria_path) + + # 디렉토리 내 모든 XML 파일 채점 + results = scorer.score_directory(xml_directory) + + # 결과 출력 + for result in results: + print(f"\n파일: {result['filename']}") + if 'error' in result: + print(f"오류: {result['error']}") + continue + + print(f"총점: {result['total_score']}") + print("\n채점 세부사항:") + for match in result['criteria_matches']: + print(f"기준: {match['criterion']}") + print(f"기대값: {match['expected']}") + print(f"실제값: {match['actual']}") + print(f"획득 점수: {match['points']}") + print("---") + + # 결과를 엑셀 파일로 저장 + excel_path = scorer.export_to_excel(results) + print(f"\n채점 결과가 다음 경로에 저장되었습니다: {excel_path}") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/scoring_criteria.json b/scoring_criteria.json new file mode 100644 index 0000000..e7588bb --- /dev/null +++ b/scoring_criteria.json @@ -0,0 +1,20 @@ +{ + "0": { + "ele": "TEXTART", + "arg": "Text", + "value": "즐거운컬러푸드영양교실", + "points": 10 + }, + "1": { + "ele": "TEXTARTSHAPE", + "arg": "FontName", + "value": "궁서체", + "points": 2 + }, + "2": { + "ele": "TEXTARTSHAPE", + "arg": "Align", + "value": "Center", + "points": 2 + } +} \ No newline at end of file diff --git a/scoring_results/scoring_results_20241106_224444.csv b/scoring_results/scoring_results_20241106_224444.csv new file mode 100644 index 0000000..13090a9 --- /dev/null +++ b/scoring_results/scoring_results_20241106_224444.csv @@ -0,0 +1,3 @@ +filename,total_score,textart_score,textartshape_score,status +1.xml,0,0,0,error: 'textart_values' +2.xml,0,0,0,error: 'textart_values' diff --git a/scoring_results/scoring_summary_20241106_224444.json b/scoring_results/scoring_summary_20241106_224444.json new file mode 100644 index 0000000..da96cba --- /dev/null +++ b/scoring_results/scoring_summary_20241106_224444.json @@ -0,0 +1,22 @@ +{ + "timestamp": "20241106_224444", + "total_files": 2, + "successful_files": 0, + "average_score": 0.0, + "results": [ + { + "filename": "1.xml", + "total_score": 0, + "textart_score": 0, + "textartshape_score": 0, + "status": "error: 'textart_values'" + }, + { + "filename": "2.xml", + "total_score": 0, + "textart_score": 0, + "textartshape_score": 0, + "status": "error: 'textart_values'" + } + ] +} \ No newline at end of file diff --git a/scoring_results_20241107_170410.xlsx b/scoring_results_20241107_170410.xlsx new file mode 100644 index 0000000..00c0ccc Binary files /dev/null and b/scoring_results_20241107_170410.xlsx differ diff --git a/scoring_results_20241107_170915.xlsx b/scoring_results_20241107_170915.xlsx new file mode 100644 index 0000000..f783cc9 Binary files /dev/null and b/scoring_results_20241107_170915.xlsx differ diff --git a/test.py b/test.py new file mode 100644 index 0000000..73c3584 --- /dev/null +++ b/test.py @@ -0,0 +1,98 @@ +# from pyhwpx import Hwp # 임포트 + +# hwp = Hwp() # 아래아한글 실행(프로그램이 실행되어 있는 경우, 기존 한/글 프로그램에 연결됨) +# hwp.insert_text("Hello world!\r\n") # 문자열 삽입 +# hwp.create_table(5, 5, treat_as_char=True) # 5행5열의 표 삽입(글자처럼 취급) + +# for i in range(25): # 표에 내용 삽입 +# hwp.insert_text(i) +# hwp.TableRightCell() + +# hwp.MoveDown() + +# hwp.insert_picture("https://ultralytics.com/images/zidane.jpg") # 이미지 삽입 +# hwp.ShapeObjAttachCaption() # 캡션 삽입 +# hwp.insert_text("Zidane") # 캡션 문자열 입력 +# hwp.ParagraphShapeAlignCenter() # 캡션 가운데정렬 +# hwp.SelectAll() # 캡션 전체선택 +# hwp.set_font(Bold=True, FaceName="돋움", Height=20, TextColor="Red") # 캡션 글자모양 변경 +# hwp.Cancel() # 선택해제 +# hwp.Close() # 캡션 편집 종료 + +import win32com.client +import os +import logging +from pathlib import Path + +def setup_logging(): + """로깅 설정""" + logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(levelname)s - %(message)s', + handlers=[ + logging.FileHandler('hwp_conversion.log'), + logging.StreamHandler() + ] + ) + +def convert_hwp_to_xml(input_folder, output_folder): + """ + 지정된 폴더 내의 모든 HWP 파일을 XML로 변환 + + Args: + input_folder (str): HWP 파일이 있는 폴더 경로 + output_folder (str): XML 파일을 저장할 폴더 경로 + """ + try: + # 한글 애플리케이션 객체 생성 + hwp = win32com.client.Dispatch("HWPFrame.HwpObject") + + # 자동화 보안 설정 + hwp.XHwpWindows.Item(0).Visible = False + hwp.RegisterModule("FilePathCheckDLL", "FilePathCheckerModule") + + # 출력 폴더가 없으면 생성 + os.makedirs(output_folder, exist_ok=True) + + # HWP 파일 검색 및 변환 + input_path = Path(input_folder) + for hwp_file in input_path.glob("*.hwp"): + try: + # 파일 열기 + hwp.Open(str(hwp_file)) + + # XML 파일 경로 설정 + xml_filename = hwp_file.stem + ".xml" + xml_path = os.path.join(output_folder, xml_filename) + + # XML로 저장 + hwp.SaveAs(xml_path, "HWPML2X") + logging.info(f"변환 성공: {hwp_file.name} -> {xml_filename}") + + except Exception as e: + logging.error(f"파일 변환 실패: {hwp_file.name} - {str(e)}") + + finally: + # 현재 문서 닫기 + hwp.Clear(3) + + except Exception as e: + logging.error(f"프로그램 실행 오류: {str(e)}") + + finally: + # 한글 프로그램 종료 + try: + hwp.Quit() + except: + pass + +if __name__ == "__main__": + # 로깅 설정 + setup_logging() + + # 변환할 폴더 경로 설정 + input_folder = r"C:\Users\gzero-ser7-win11\Documents\hwpTest\Input" # HWP 파일이 있는 폴더 + output_folder = r"C:\Users\gzero-ser7-win11\Documents\hwpTest\Output" # XML 파일을 저장할 폴더 + + # 변환 실행 + convert_hwp_to_xml(input_folder, output_folder) \ No newline at end of file diff --git a/~$scoring_results_20241107_170915.xlsx b/~$scoring_results_20241107_170915.xlsx new file mode 100644 index 0000000..b8f42e1 Binary files /dev/null and b/~$scoring_results_20241107_170915.xlsx differ