diff --git a/250526_DIW_2505회_A형_TEST.xlsx b/250526_DIW_2505회_A형_TEST.xlsx deleted file mode 100644 index bb80e7a..0000000 Binary files a/250526_DIW_2505회_A형_TEST.xlsx and /dev/null differ diff --git a/250526_DIW_2505회_B형_TEST.xlsx b/250526_DIW_2505회_B형_TEST.xlsx deleted file mode 100644 index a393c31..0000000 Binary files a/250526_DIW_2505회_B형_TEST.xlsx and /dev/null differ diff --git a/250526_DIW_2505회_C형_TEST.xlsx b/250526_DIW_2505회_C형_TEST.xlsx deleted file mode 100644 index be780ff..0000000 Binary files a/250526_DIW_2505회_C형_TEST.xlsx and /dev/null differ diff --git a/250526_DIW_2505회_D형_TEST.xlsx b/250526_DIW_2505회_D형_TEST.xlsx deleted file mode 100644 index 31db4e6..0000000 Binary files a/250526_DIW_2505회_D형_TEST.xlsx and /dev/null differ diff --git a/250604_DIW_2505C_TEST.xlsx b/250604_DIW_2505C_TEST.xlsx new file mode 100644 index 0000000..3ab8645 Binary files /dev/null and b/250604_DIW_2505C_TEST.xlsx differ diff --git a/250604_DIW_2505C_채점결과.xlsx b/250604_DIW_2505C_채점결과.xlsx new file mode 100644 index 0000000..2842b65 Binary files /dev/null and b/250604_DIW_2505C_채점결과.xlsx differ diff --git a/DIW_2505A.json b/DIW_2505A.json index 3ac036c..44d9a8f 100644 --- a/DIW_2505A.json +++ b/DIW_2505A.json @@ -454,14 +454,14 @@ "item": "③ 크기-높이 (40 mm)" }, "17": { - "path": "//PICTURE[./IMAGE[@BinItem=//BINITEM[@Format='JPG']/@BinData]]/SHAPEOBJECT/POSITION/@HorzOffset", + "path": "//PICTURE[./IMAGE[@BinItem=//BINITEM[@Format='JPG']/@BinData]]/SHAPEOBJECT/POSITION[not(@TreatAsChar='true') and @HorzRelTo='Page']/@HorzOffset", "value": "0", "points": 2, "category": "mmSize", "item": "④ 위치 (어울림 : 가로-쪽의 왼쪽 0.0mm)" }, "18": { - "path": "//PICTURE[./IMAGE[@BinItem=//BINITEM[@Format='JPG']/@BinData]]/SHAPEOBJECT/POSITION/@VertOffset", + "path": "//PICTURE[./IMAGE[@BinItem=//BINITEM[@Format='JPG']/@BinData]]/SHAPEOBJECT/POSITION[not(@TreatAsChar='true') and @HorzRelTo='Page']/@VertOffset", "value": "23", "points": 2, "category": "mmSize", diff --git a/DIW_2505B.json b/DIW_2505B.json index 5a7444c..316213b 100644 --- a/DIW_2505B.json +++ b/DIW_2505B.json @@ -456,14 +456,14 @@ "item": "③ 크기-높이 (40 mm)" }, "17": { - "path": "//PICTURE[./IMAGE[@BinItem=//BINITEM[@Format='JPG']/@BinData]]/SHAPEOBJECT/POSITION/@HorzOffset", + "path": "//PICTURE[./IMAGE[@BinItem=//BINITEM[@Format='JPG']/@BinData]]/SHAPEOBJECT/POSITION[not(@TreatAsChar='true') and @HorzRelTo='Page']/@HorzOffset", "value": "0", "points": 2, "category": "mmSize", "item": "④ 위치 (어울림 : 가로-쪽의 왼쪽 0.0mm)" }, "18": { - "path": "//PICTURE[./IMAGE[@BinItem=//BINITEM[@Format='JPG']/@BinData]]/SHAPEOBJECT/POSITION/@VertOffset", + "path": "//PICTURE[./IMAGE[@BinItem=//BINITEM[@Format='JPG']/@BinData]]/SHAPEOBJECT/POSITION[not(@TreatAsChar='true') and @HorzRelTo='Page']/@VertOffset", "value": "24", "points": 2, "category": "mmSize", diff --git a/DIW_2505C.json b/DIW_2505C.json index fd15754..f9ab7ef 100644 --- a/DIW_2505C.json +++ b/DIW_2505C.json @@ -456,14 +456,14 @@ "item": "③ 크기-높이 (40 mm)" }, "17": { - "path": "//PICTURE[./IMAGE[@BinItem=//BINITEM[@Format='JPG']/@BinData]]/SHAPEOBJECT/POSITION/@HorzOffset", + "path": "//PICTURE[./IMAGE[@BinItem=//BINITEM[@Format='JPG']/@BinData]]/SHAPEOBJECT/POSITION[not(@TreatAsChar='true') and @HorzRelTo='Page']/@HorzOffset", "value": "0", "points": 2, "category": "mmSize", "item": "④ 위치 (어울림 : 가로-쪽의 왼쪽 0.0mm)" }, "18": { - "path": "//PICTURE[./IMAGE[@BinItem=//BINITEM[@Format='JPG']/@BinData]]/SHAPEOBJECT/POSITION/@VertOffset", + "path": "//PICTURE[./IMAGE[@BinItem=//BINITEM[@Format='JPG']/@BinData]]/SHAPEOBJECT/POSITION[not(@TreatAsChar='true') and @HorzRelTo='Page']/@VertOffset", "value": "24", "points": 2, "category": "mmSize", diff --git a/diwScoring2.py b/diwScoring2.py index 85e32de..787585c 100644 --- a/diwScoring2.py +++ b/diwScoring2.py @@ -126,6 +126,19 @@ class XMLScorer: # 하나의 XML 파일 채점 def _score_xml_file(self, xml_file, chart_xml): + + def extract_char_text_from_p(p_element): + """ + 주어진

요소에서 모든 자손 의 텍스트를 추출해 문자열 리스트로 반환합니다. + """ + full_text = [] + for p in p_element: + char_elements = p.xpath('.//CHAR') + combined_text = ''.join([char.text for char in char_elements if char.text]) + no_space_text = re.sub(r'\s+', '', combined_text) # 공백 문자 제거 + full_text.append(no_space_text) + return full_text + try: tree = ET.parse(xml_file) root = tree.getroot() @@ -373,6 +386,7 @@ class XMLScorer: elif (category or "") == "mmSize": items = root.xpath(xpath) + # 오차범위 설정 # 한글 프로그램 내부에서 드물게 0mm이지만 1pt로 저장되는 경우가 있음 # @@ -385,13 +399,16 @@ class XMLScorer: float_string = right_answer.strip().replace("mm", "") right_answer = self.convert_mm_to_pt(float(float_string)) - for item in items: - user_answer = float(item) + if not items: + scoring['points'] = 0 + else: + for item in items: + user_answer = float(item) - self.evaluate_answer(scoring, user_answer, right_answer, points, method="tolerance", tolerance=error_range) - - if scoring['points'] > 0: - break + self.evaluate_answer(scoring, user_answer, right_answer, points, method="tolerance", tolerance=error_range) + + if scoring['points'] > 0: + break elif (category or "") == "ParaShape": items = root.xpath(xpath) @@ -505,11 +522,27 @@ class XMLScorer: # 폰트 속성 elif (category or "") == "FontAttribute": # 하이퍼링크 처리 - hyperlink_ptag = criterion.get('hyperlink_ptag', None) - has_hyperlink_ptag = root.xpath(hyperlink_ptag) if hyperlink_ptag else False + + # 1. 하이퍼링크를 포함하는 P요소를 가져옴 + # 2. 그 P요소의 자손 CHAR태그에 있는 텍스트를 하나의 문자열로 변환 + # 3. P요소의 문자열과 채점하려는 문자열이 일치하는지 확인 + hyperlink_xpath = criterion.get('hyperlink_ptag', None) + hyperlink_ptag = root.xpath(hyperlink_xpath) if hyperlink_xpath else None + + p_tag_text_list = extract_char_text_from_p(hyperlink_ptag) if hyperlink_ptag else [] + hyperlink_text = search_value.replace(" ", "") if search_value else "" + + # search_value가 hyperlink문자열에 포함되어 있는지 확인 + # search_value가 hyperlink인 경우와 아닌경우를 구분해 채점 + search_in_hyperlink = False + if hyperlink_text and any(hyperlink_text in text for text in p_tag_text_list): + search_in_hyperlink = True + else: + search_in_hyperlink = False # hyperlink가 아닌 경우(일반적인 텍스트 일 경우) - if not has_hyperlink_ptag: + # 하이퍼링크를 포함한 P태그가 없거나 search_value값이 하이퍼링크텍스트에 포함되어 있지 않을 경우 + if not hyperlink_ptag or not search_in_hyperlink: charshape_list = root.xpath(xpath) if not charshape_list: charshape = None @@ -528,10 +561,9 @@ class XMLScorer: break # 하이퍼링크인 경우 - elif has_hyperlink_ptag: - hyperlink_text = search_value.replace(" ", "") - - p_elements = has_hyperlink_ptag + # elif hyperlink_ptag and search_in_hyperlink: + else: + p_elements = hyperlink_ptag for p in p_elements: # 수험자가 입력한 텍스트 중 하이퍼링크가 들어간 문단의 모든 텍스트를 가져와 @@ -1050,13 +1082,13 @@ def main(): # 채점하고자 하는 유형은 주석 해제 exam_types = [ # 'A', - 'B', - # 'C', + # 'B', + 'C', # 'D', ] - test_mode = False - # test_mode = True #/TEST 폴더 채점시 + # test_mode = False + test_mode = True #/TEST 폴더 채점시 output_excel_paths = [] for exam_type in exam_types: diff --git a/zzz.xbook b/zzz.xbook index 165fd1c..fba88b1 100644 --- a/zzz.xbook +++ b/zzz.xbook @@ -1 +1 @@ -[{"kind":2,"language":"xpath","value":"//a:t[text()='클라우드 보안투자']/ancestor::a:r//a:ea/@typeface"},{"kind":2,"language":"xpath","value":"boolean(//FONTFACE[@Lang='Hangul']/FONT[@Id=//CHARSHAPE/FONTID/@Hangul]/@Name='바탕' and //CHARSHAPE/@Height='1000' and //PARASHAPE/PARAMARGIN/@LineSpacing='160' and //PARASHAPE/@Align='Justify')"},{"kind":2,"language":"xpath","value":"//FONTFACE[@Lang='Hangul']/FONT[@Id=//CHARSHAPE/FONTID/@Hangul]/@Name='바탕'"},{"kind":2,"language":"xpath","value":"//FONTFACE[@Lang='Hangul']/FONT[@Id=//CHARSHAPE/FONTID/@Hangul]/@Name='바탕' and //CHARSHAPE/@Height='1000' and //PARASHAPE/PARAMARGIN/@LineSpacing='160' and //PARASHAPE/@Align='Justify')"},{"kind":2,"language":"xpath","value":"//FONTFACE[@Lang='Hangul']/FONT/@Name"},{"kind":2,"language":"xpath","value":"//CHARSHAPE/@Height"},{"kind":2,"language":"xpath","value":"//c:valAx//a:defRPr/@sz"},{"kind":2,"language":"xpath","value":"//c:catAx/c:txPr//a:defRPr/@i"},{"kind":2,"language":"xpath","value":"//BORDERFILL[@Id=//TABLE/ROW[1]/CELL/@BorderFill]/BOTTOMBORDER/@Width"}] \ No newline at end of file +[{"kind":2,"language":"xpath","value":"//a:t[text()='클라우드 보안투자']/ancestor::a:r//a:ea/@typeface"},{"kind":2,"language":"xpath","value":"boolean(//FONTFACE[@Lang='Hangul']/FONT[@Id=//CHARSHAPE/FONTID/@Hangul]/@Name='바탕' and //CHARSHAPE/@Height='1000' and //PARASHAPE/PARAMARGIN/@LineSpacing='160' and //PARASHAPE/@Align='Justify')"},{"kind":2,"language":"xpath","value":"//FONTFACE[@Lang='Hangul']/FONT[@Id=//CHARSHAPE/FONTID/@Hangul]/@Name='바탕'"},{"kind":2,"language":"xpath","value":"//FONTFACE[@Lang='Hangul']/FONT[@Id=//CHARSHAPE/FONTID/@Hangul]/@Name='바탕' and //CHARSHAPE/@Height='1000' and //PARASHAPE/PARAMARGIN/@LineSpacing='160' and //PARASHAPE/@Align='Justify')"},{"kind":2,"language":"xpath","value":"//FONTFACE[@Lang='Hangul']/FONT/@Name"},{"kind":2,"language":"xpath","value":"//CHARSHAPE/@Height"},{"kind":2,"language":"xpath","value":"//P[.//FIELDBEGIN[@Type='Hyperlink'] and .//CHAR[contains(., 'http')]]"},{"kind":2,"language":"xpath","value":"//P[.//FIELDBEGIN[@Type='Hyperlink'] and .//CHAR[contains(., 'http')]]"},{"kind":2,"language":"xpath","value":"//PICTURE[./IMAGE[@BinItem=//BINITEM[@Format='JPG']/@BinData]]/SHAPEOBJECT/POSITION[not(@TreatAsChar='true')]/@HorzOffset"},{"kind":2,"language":"xpath","value":"//BORDERFILL[@Id=//TABLE/ROW[1]/CELL/@BorderFill]/BOTTOMBORDER/@Width"}] \ No newline at end of file diff --git a/250527_DIW_2505회_A형_채점결과.xlsx b/회차별채점자료/2505/excel_채점결과/250527_DIW_2505회_A형_채점결과.xlsx similarity index 100% rename from 250527_DIW_2505회_A형_채점결과.xlsx rename to 회차별채점자료/2505/excel_채점결과/250527_DIW_2505회_A형_채점결과.xlsx diff --git a/250527_DIW_2505회_B형_채점결과.xlsx b/회차별채점자료/2505/excel_채점결과/250527_DIW_2505회_B형_채점결과.xlsx similarity index 100% rename from 250527_DIW_2505회_B형_채점결과.xlsx rename to 회차별채점자료/2505/excel_채점결과/250527_DIW_2505회_B형_채점결과.xlsx diff --git a/250527_DIW_2505회_C형_채점결과.xlsx b/회차별채점자료/2505/excel_채점결과/250527_DIW_2505회_C형_채점결과.xlsx similarity index 100% rename from 250527_DIW_2505회_C형_채점결과.xlsx rename to 회차별채점자료/2505/excel_채점결과/250527_DIW_2505회_C형_채점결과.xlsx diff --git a/250529_DIW_2505회_B형_채점결과.xlsx b/회차별채점자료/2505/excel_채점결과/250529_DIW_2505회_B형_채점결과.xlsx similarity index 100% rename from 250529_DIW_2505회_B형_채점결과.xlsx rename to 회차별채점자료/2505/excel_채점결과/250529_DIW_2505회_B형_채점결과.xlsx diff --git a/250529_DIW_2505회_C형_채점결과.xlsx b/회차별채점자료/2505/excel_채점결과/250529_DIW_2505회_C형_채점결과.xlsx similarity index 100% rename from 250529_DIW_2505회_C형_채점결과.xlsx rename to 회차별채점자료/2505/excel_채점결과/250529_DIW_2505회_C형_채점결과.xlsx diff --git a/250529_DIW_2505회_D형_채점결과.xlsx b/회차별채점자료/2505/excel_채점결과/250529_DIW_2505회_D형_채점결과.xlsx similarity index 100% rename from 250529_DIW_2505회_D형_채점결과.xlsx rename to 회차별채점자료/2505/excel_채점결과/250529_DIW_2505회_D형_채점결과.xlsx