From cf71ae26ca487f88655845e7c287246f4173390d Mon Sep 17 00:00:00 2001 From: gzero-ser7 Date: Thu, 7 Nov 2024 17:14:35 +0900 Subject: [PATCH] init --- hwp_conversion.log | 12 + score.py | 227 ++++++++++++++++++ scoring_criteria.json | 20 ++ .../scoring_results_20241106_224444.csv | 3 + .../scoring_summary_20241106_224444.json | 22 ++ scoring_results_20241107_170410.xlsx | Bin 0 -> 5945 bytes scoring_results_20241107_170915.xlsx | Bin 0 -> 5949 bytes test.py | 98 ++++++++ ~$scoring_results_20241107_170915.xlsx | Bin 0 -> 648 bytes 9 files changed, 382 insertions(+) create mode 100644 hwp_conversion.log create mode 100644 score.py create mode 100644 scoring_criteria.json create mode 100644 scoring_results/scoring_results_20241106_224444.csv create mode 100644 scoring_results/scoring_summary_20241106_224444.json create mode 100644 scoring_results_20241107_170410.xlsx create mode 100644 scoring_results_20241107_170915.xlsx create mode 100644 test.py create mode 100644 ~$scoring_results_20241107_170915.xlsx 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 0000000000000000000000000000000000000000..00c0ccc73f76942c39dde503e33e3b3ce4e9867e GIT binary patch literal 5945 zcmZ`-1yqx7`yMeQg%N_3NGTyL-AK2j)Tq%VAObq6(Vz_Jlo%k5AUV3b1f-FWmPSD0 zoBID9fAasn?Y!^K&e?VCxu5%fuKRv34P^{WQUCye4G0Xeej?YYst}9%){nY~P?x2% zg@&uMiyNnziwlRRql4<8Dqag0KGs6xV~?uDFxu>!kHo^#kb@l00xMY@VP`wrm=aE2 zUR_j~9HI2gl?k$Y9LtWpJe>rR;|yowy*(LW8OtwW^Rrekk-tedDJh?ib)MY)yHGY@7APRK4&w(}Vp_u`;mbWSa z(9?Er=rd+2f0+Xo2#K|#Av>N!>Fx(sqbV0gS`lP%eJ|%0VIvq&x&lopniAzPDbvVD z1;W^RNgdA1&6KQML;6*Ypy0#aP-QAb&snFm+?9iz@IsO7!eV?@{Orj@d48haID+kH)!JlHO!oC31G4g~Yl2<} z-#E0^BHi?T~;#)`J&y@)?{Zbp=;kcJfh(1-wllc)+!!n zjgn#W5Qqq;>JNClJZ{@gkLa0l=X0c7SlaUxtn3{LccI2y(5y>qNj-FE%$CBb$g&-> z{CqrB4A04r)KVY)4xQgjwn@Q_G*ln8e?B|bhdk1LX+bO3!9kMX_{j5rgPvu81slQea+QbxP_--JRzmmTexY_5$JzVAU}zS6&q;*DRhZgZ`sLv9F&x3f zGq`h#yQwB>eMdL0)Oc`kIW5AKp>FnAA9}1V@5C_izNwskB1Vo;HofaX?J`L4qapzh zi3E0PP=cKXHF`Dvld!bjV3M7|o zh^fnzQOVsHPR27O_#4}DhFo{%{xx38X1NloXTdfjqTZLl)0Bs})Nm>qbZe?|xw|mi zx?S25#@9(jEg&{&ar4{!l&&9d`BgrB4>L6&?ilt|8>s-DKPi0pR@}5}s~`SsU>trE z6m24a`t3k?6sVClNKh$hJM*bN=0%!Gt77w-f3MX=^{b#UUI+Hq-{n7%D=iZ;=Sr;e z@ZHEtdV*6A)3r;_59SlkqvgQ`?$ywWuXuan3dD6FHH!UTpj{153A7ZP_%{jpa6ktU zJ2i)586#B!3{dOl(Qv~#IVZ!mAI02L6MP)^E%aRbyE?T?-m-iaN^jb_un|w&+di9Q zgq0*cdBAWhO4+Ymin01Uyl0oS4QI{LkO`+(dlVuNMw}*1<+QGKq!c@`Qc`ojTGViP zy(Gh3oPF?A6uer|kH;%h<*K|<8Po8QSy1V)tj|S_wijQ-S~Qxb^7%WOhd+*uJ(d{| zTw3um)s)f=7>>26Gs1#t3Gf=+(Z^BFt*7hlo|KD1lbAKo1U<*q&%?t%R+}|Tvvx$| zXgWjoGw*)O2uc`il?nEG0Zy=v?a<&Ob#y3XWS6Tlt)AFpCta{Keemu^K#3;${$m|e zZo>`?wy4aPDiN}4F01iI;HBZ{rRf~VN2d~&=eHf{9}BhECXiSc^vprZnssOfbqx1Q z8|=g?$kYZepA>ba6)BOvVZh68LkyY@a zu}tx+8=CK?j?~w^@7UOl5 z`K--!P&~zq7dXAB-Csx9z-+4A>g|;J;NI1=$tST2axuAsti%P{@?f`Q@9GxW!Ylo8 z+Ebo+A(v_4ISiApB;sFg)Y~zacd*$|)=AnBvCw^3^}U&#)4l50*`$ zk(mBOU~^2sM-2y>a3@ysXDzGYiq2f_5R|sGz5cQt-iY>h1BoKjX)Z-|)c_0tfZ}%p zdG73L?`8vmxVv%w`uNpFpj!P-pSXx#k@XUm&h?|q>JYy-ge~}Le?%Xcv;0uDk#0KZ z)Pv!&=|`T|mdGnV7qUV_Au?GSTU-B`cj>g@w8LVx{-7!YDCuEcK>&0H)w$i39`lEr zeHAWs9}#qBjNTNWcc-;Xm6Qv25cgq<(2k4u3Nt?Cd2aZrrzO_f%s%1}toP0sybF}T zEFQ~gCnbW+brZ}0UTtAZ7AJVxL7K2u*bjzC&&{7U7%)HTImv!UD9<0>Uzl1KIq;)) z;UEu(Pn&{;(NUxtvxtF&<&-CS|4No&|Fif|qw2+53S>y*lTN`Y? z^wi;y3$E>EsR@Lkfk?I&Q#nE%U7UHyYl&J-je6>Er^n~-EE8D;(~qG80D-vwGs`?b zXN97;X}f7IicWC*30h{GP~;-T$R|XAV+_B@Onzu|!jvvEo9go_X}kc)P_aWy^aqoO zW~CeJYjS2UGTWfRiXS!+vOO7x%;W3d;~RVqC(|d}EqQJ3KKkf5coYylmswE{kkqih zONhlXBPX70Yq}`aQ=5(B&o5QVK{p7L6E+6Wy1$&|Y5&YxrZSUJyp*Mr38WbeSnj8H zeOi@meVFX`Fn2Q39H4JS#Xl^ZIXI1PY89>&|Ikw0zU= z2(8?r=u3r(jXX3F4&&BYGSbc%a{q^0Sc`g_a^aKR_Nl(+oI#TfX-o3z)zGDO3R6om z7>K09m_CdC12;T1v8(;jHx5k=z$ryY<_*o079^8G284KK3>!S~$y4SWh;E%GB;Lrm z8B_Ws`01zGZ$~X}8?(yANHmx`Z%93mBfppRrQON1*5_iqb8V{*R;-c}tQVlUv+c60 z>Cd4$d2%taTfr&6&GLoThk_=p)FOE2wbQ8Y?f7bI#&5TAGgQJ51%13^8Xv(rz4l|; zWR6;ihO8Xtl@dq_SZDl~^n>B}gUx_~VKWKQDIAGA_&(dbY?hZ|*$FhEd*p+0v5Lv2 z%I5m~-0UJHW~_Al4p0QrE;p`BJza{hJ4Fiz-)9F_cd@&Q=*##a>EY3bBjfalgPUHU z1jM=!2ZBu4p7D@ty8};2L5V=tMI5^>r@U0EaFeG5`-+ENfz=sdCP0;5DzfJ#NG}C$ z5eb<&QsP$J1Iiq9s{yLmrZ!{^p(T0Fswy{krXY&av=XT~yn)mNwsN=HC zZ;ZMTDA*U8-;DD}C#YX3yd9NoS&6J;<7tPf3D}F}zgP*fw3ge>KjcvT&|mw|`|*R( zcfR#1d&`O4tg}2Uc5E$HIWSSIixGPdIm%?gkRZMFmV%)8QZrCIgm}=ggeM=FxoJG;s1{AL5BaZ-oHoi=rH4&xy4uM5ffUhVc zmk8sk78fRhJ#c+_8tM81(vIpyU&Wc1kP%KcPA^&Yt0A*_b&x80t`}D)!q$4;qdZ{*)#o#KymWXbvdUGts=dsT91<01tziW(Kcm<6zKg&Ot)*GA4U{(v!RR(IEQZ!@#-A%6(o8E?$101cDE}wO^JtF}|)a28O1O7OS1SIKXNHK8i`7OJXIMGXMELLA&UIey+# zqm`Y|x$z}Sh#_E=_igChg`Z1hLh!0XSF;k^;1+F%$C00#=xjjBS7~VvN(lMhc~HAe zBOE%o+KeW39~J>V*t3|XJBm_1QS0W))vlpoG7V z?$24p+}YXw=i!Zn_batZk_LjA`Y;msY??>hEi~~m%@IFrT}O?CCCsd*?4H}*X$qvT z-Unh{-QqY5w4-fFgxO@!6^R5 ziRh*^8Yp6vz36_gcAKv6&^dsup2d!uUdD|UzCYmWNey&Oks*frdJ4ZShrV~=2+SzF z!`7|;h%%NN0O2xu`-9^v3(dx3ZgT>fQJ2R9wnh#6Tc$!G8V z#F*|0g=vPCS)8!B#T4YJt83f@v80i=w;t`U*q)wg&difmrE|PFdZ8HGufNulogX-^ zQJ>vXk`-1EZoYT<>{NF~toU7vgoasc#p`&HaOo53jM+GV)k*Lg2{?5$rD*ShTepO_9v?^`@7m-{Eu`58{ zaXmAyz-i_dCz1Pka~rHLW@DSJRwE7Is??QHdVcJA*V5uue2Jb4pn z6S5faSf77^uZO7F4RN#Ec5%K!M>D=?&flPK^?N>kT9cNVSg`(s360HE#Mb*IN4{oq zdOumX*y|>f0wQ5KdnHvPy%%7pR<{HhU{=8mAf*g8pD}sc_QZPy0Ng5!o%=q zvx(qZ$}V+t+;I&eZ&WZt`(kC%7!zOY<7B8K{Wq!>MZbKHnz~D(-O2;)1G|au&D&Y- zrOVAp+gzQsd@pdHI(dusP?WuZP(v9FofPoD4N6o2{d)PLcKQF?mDkbNy}{pDluDqr z{u}*|Q+OSI-9h^UpF#cofAQ0<6I_3S`iG#>j|Te}!GC?8y3TTaLjS{pftuM-%ec<+ zYhJ(3a(%e@hs77|CTi9}vHTfzu0yYPkw4G{l-B;Mr(6eKSLHvzZj{^ne=Gk-qpyRn v3&$U@1@SNN|B#XEJl7B9A0Bm-E&5-MsD?5SWt##3xTrS_rCRAf%MtJ&0q$=Y literal 0 HcmV?d00001 diff --git a/scoring_results_20241107_170915.xlsx b/scoring_results_20241107_170915.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..f783cc9dad659faffb17c815efea856a32b90640 GIT binary patch literal 5949 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..b8f42e1458119cb5b4dd55eea7e7911dc5276bc2 GIT binary patch literal 648 zcmeZZaArtl$N|D&h75*8h9V%(jUf}rPi63A$YV%j$Y&rHFfuSQxG=Z^P4Z?4Ven@N zV9;goVh97W4S~=bXiPdo70}!wpqaW1#bA~>kW~(}FAuDSSer)G4fPOUgnGaim_j^& F7y#o+5sm-= literal 0 HcmV?d00001