diff --git a/250513_DIW_2504회_A형_TEST.xlsx b/250513_DIW_2504회_A형_TEST.xlsx new file mode 100644 index 0000000..ed29204 Binary files /dev/null and b/250513_DIW_2504회_A형_TEST.xlsx differ diff --git a/DIW_2504A_new.json b/DIW_2504A_new.json index f004b2e..c212857 100644 --- a/DIW_2504A_new.json +++ b/DIW_2504A_new.json @@ -37,7 +37,6 @@ }, "3": { "path": "", - "searchValue": null, "value": null, "points": 40, "category": "오타감점", @@ -97,7 +96,6 @@ }, "7": { "path": "//TEXTART[@Text='{searchValue}']", - "path2": null, "searchValue": "클라우드컴퓨팅컨퍼런스", "value": true, "points": 2, @@ -106,7 +104,6 @@ }, "8": { "path": "//RECTANGLE[.//CHAR[text()='{searchValue}']]/SHAPEOBJECT/SIZE", - "path2": null, "searchValue": "전", "value": { "Height": 2800, @@ -119,7 +116,6 @@ }, "9": { "path": "//TEXT[CHAR[text()='{searchValue}']]/@CharShape", - "path2": null, "searchValue": "전", "value": "궁서체", "points": 1, @@ -128,7 +124,6 @@ }, "10": { "path": "//RECTANGLE[.//CHAR[text()='{searchValue}']]//WINDOWBRUSH/@FaceColor", - "path2": null, "searchValue": "전", "value": "255,132,58", "points": 2, @@ -137,9 +132,8 @@ }, "11": { "path": "//RECTANGLE[.//CHAR[text()='{searchValue}']]//OUTSIDEMARGIN/@Right", - "path2": null, "searchValue": "전", - "value": "850", + "value": "3.0", "tolerance": 1, "points": 2, "category": "mmSize", @@ -147,20 +141,19 @@ }, "12": { "path": "//CHARSHAPE[@Id=//CHAR[contains(text(),'{searchValue}')]/parent::TEXT/@CharShape]", - "path2": null, - "searchValue": "한옥에 대한 체험과 교육이 준비된 사생대회", + "searchValue": "글로벌 클라우드 컴퓨팅 컨퍼런스", "value": "BOLD", "points": 2, "category": "FontAttribute", - "item": "문구 (한옥에 대한 체험과 교육이 준비된 사생대회)/① 진하게" + "item": "문구 (글로벌 클라우드 컴퓨팅 컨퍼런스)/① 진하게" }, "13": { "path": "//CHARSHAPE[@Id=//CHAR[contains(text(),'{searchValue}')]/parent::TEXT/@CharShape]", - "searchValue": "한옥에 대한 체험과 교육이 준비된 사생대회", + "searchValue": "글로벌 클라우드 컴퓨팅 컨퍼런스", "value": "UNDERLINE", "points": 2, "category": "FontAttribute", - "item": "문구 (한옥에 대한 체험과 교육이 준비된 사생대회)/② 밑줄" + "item": "문구 (글로벌 클라우드 컴퓨팅 컨퍼런스)/② 밑줄" }, "14": { "path": "//CHAR[contains(text(),'{char1}')]", @@ -176,35 +169,35 @@ }, "15": { "path": "//CHAR[contains(text(),'{searchValue}')]/parent::TEXT/@CharShape", - "searchValue": "■ 행사안내 ■", - "value": "돋움", + "searchValue": "참여안내", + "value": "궁서", "points": 1, "category": "FontName", - "item": "문구 (■ 행사안내 ■)/① 글씨체 (돋움)" + "item": "문구 (● 참여안내 ●)/① 글씨체 (궁서)" }, "16": { "path": "//PARASHAPE[@Id=//CHAR[contains(text(),'{searchValue}')]/ancestor::P/@ParaShape]/@Align", - "searchValue": "■ 행사안내 ■", + "searchValue": "참여안내", "value": "Center", "points": 1, "category": "SingleAnswer", - "item": "문구 (■ 행사안내 ■)/② 정렬 (가운데 정렬)" + "item": "문구 (● 참여안내 ●)/② 정렬 (가운데 정렬)" }, "17": { "path": "//CHARSHAPE[@Id=//CHAR[text()='{searchValue}']/parent::TEXT/@CharShape]", - "searchValue": "홈페이지(http://www.ihd.or.kr)에서 개별 신청, 선착순 접수", + "searchValue": "홈페이지(http://www.ihd.or.kr) 참조", "value": "ITALIC", "points": 1, "category": "FontAttribute", - "item": "문구 (홈페이지(http://www.ihd.or.kr)에서 개별 신청, 선착순 접수)/① 기울임" + "item": "문구 (홈페이지(http://www.ihd.or.kr) 참조)/① 기울임" }, "18": { "path": "//CHARSHAPE[@Id=//CHAR[text()='{searchValue}']/parent::TEXT/@CharShape]", - "searchValue": "홈페이지(http://www.ihd.or.kr)에서 개별 신청, 선착순 접수", + "searchValue": "홈페이지(http://www.ihd.or.kr) 참조", "value": "UNDERLINE", "points": 1, "category": "FontAttribute", - "item": "문구 (홈페이지(http://www.ihd.or.kr)에서 개별 신청, 선착순 접수)/② 밑줄" + "item": "문구 (홈페이지(http://www.ihd.or.kr) 참조)/② 밑줄" }, "19": { "path": "//PARASHAPE[@Id=//CHAR[contains(text(),'{searchValue}')]/ancestor::P/following-sibling::P[1]/@ParaShape]/PARAMARGIN", @@ -216,55 +209,56 @@ "points": 2, "category": "ParaShape", "item": "문구 (※ 기타… 이하 문단)/왼쪽여백 (15pt), 내어쓰기 (12pt)", - "desc": "내부적으로 내어쓰기는 음수값" + "desc": "내부적으로 내어쓰기는 음수값 / JSON value값은 양수로 입력" }, "20": { "path": "//CHARSHAPE[@Id=//CHAR[contains(text(),'{searchValue}')]/parent::TEXT/@CharShape]/@Height", - "searchValue": "2025. 03. 22.", - "value": "1300", + "searchValue": "2025. 04. 26.", + "value": "1400", "points": 1, "category": "SingleAnswer", - "item": "문구 (2025. 03. 22.)/① 크기 (13pt)" + "item": "문구 (2025. 04. 26.)/① 크기 (14pt)", + "desc": "1pt당 100" }, "21": { "path": "//PARASHAPE[@Id=//CHAR[contains(text(),'{searchValue}')]/ancestor::P/@ParaShape]/@Align", - "searchValue": "2025. 03. 22.", + "searchValue": "2025. 04. 26.", "value": "Center", "points": 1, "category": "SingleAnswer", - "item": "문구 (2025. 03. 22.)/② 정렬 (가운데 정렬)" + "item": "문구 (2025. 04. 26.)/② 정렬 (가운데 정렬)" }, "22": { "path": "//CHAR[text()='{searchValue}']/parent::TEXT/@CharShape", - "searchValue": "한국고건축협회", - "value": "궁서", + "searchValue": "글로벌멀티클라우드협의회", + "value": "궁서체", "points": 1, "category": "FontName", - "item": "문구 (한국고건축협회)/① 글씨체 (궁서)" + "item": "문구 (글로벌멀티클라우드협의회)/① 글씨체 (궁서체)" }, "23": { "path": "//CHARSHAPE[@Id=//CHAR[text()='{searchValue}']/parent::TEXT/@CharShape]/@Height", - "searchValue": "한국고건축협회", - "value": "2400", + "searchValue": "글로벌멀티클라우드협의회", + "value": "2600", "points": 1, "category": "SingleAnswer", - "item": "문구 (한국고건축협회)/② 크기 (24pt)" + "item": "문구 (글로벌멀티클라우드협의회)/② 크기 (26pt)" }, "24": { "path": "//PARASHAPE[@Id=//CHAR[text()='{searchValue}']/ancestor::P/@ParaShape]/@Align", - "searchValue": "한국고건축협회", + "searchValue": "글로벌멀티클라우드협의회", "value": "Center", "points": 1, "category": "SingleAnswer", - "item": "문구 (한국고건축협회)/③ 정렬 (가운데 정렬)" + "item": "문구 (글로벌멀티클라우드협의회)/③ 정렬 (가운데 정렬)" }, "25": { "path": "//CHAR[text()='{searchValue}']/parent::TEXT/@CharShape", "searchValue": "DIAT", - "value": "굴림", + "value": "궁서", "points": 1, "category": "FontName", - "item": "문구 (DIAT)/① 글꼴 (굴림)" + "item": "문구 (DIAT)/① 글꼴 (궁서)" }, "26": { "path": "//CHARSHAPE[@Id=//SECTION[1]//CHAR[text()='{searchValue}']/parent::TEXT/@CharShape]/@Height", @@ -284,36 +278,92 @@ }, "28": { "path": "//PAGENUM/@FormatType", - "value": "HangulSyllable", + "value": "LatinCapital", "points": 2, "category": "SingleAnswer", - "item": "① 쪽 번호 매기기 (가,나,다 순으로)" + "item": "① 쪽 번호 매기기 (A,B,C 순으로)", + "desc": { + "가,나,다":"HangulSyllable", + "1,2,3":"Digit", + "갑,을,병":"DecagonCircle", + "A,B,C":"LatinCapital", + "①,②,③":"CircledDigit", + "一,二,三":"Ideograph", + "㉠,㉡,㉢":"CircledHangulJamo", + "ⓐ,ⓑ,ⓒ":"CircledLatinSmall", + "i,ii,iii":"RomanSmall", + "정답에 맞는 값 value에 입력":"" + } }, "29": { "path": "//PAGENUM/@Pos", - "value": "BottomCenter", + "value": "BottomRight", "points": 2, "category": "SingleAnswer", - "item": "② 가운데 아래" + "item": "오른쪽 아래" }, "30": { - "path": "not(//PARASHAPE[@Id=//SECTION[1]/P/@ParaShape]/PARAMARGIN[@LineSpacing!='180'])", + "path": "not(//PARASHAPE[@Id=//SECTION[1]/P/@ParaShape]/PARAMARGIN[@LineSpacing!='200'])", "value": true, "points": 2, "category": "Boolean", - "item": "문제 1 줄간격 180% 설정", - "desc": "1페이지 문단의 줄간격이 180%가 아닌 문단이 있으면 False(감점)" + "item": "문제 1 줄간격 200% 설정", + "desc": "1페이지 문단의 줄간격이 200%가 아닌 문단이 있으면 False(감점)" } }, "2": { "1": { - "path": "//SECTION[2]//PAGEBORDERFILL[@Type='Both' or @Type='Even']", - "path2": "boolean(//PAGEBORDERFILL[@Type='Both' or @Type='Even']/@HeaderInside='true' and //BORDERFILL[@Id=//PAGEBORDERFILL[@Type='Both' or @Type='Even']/@BorferFill]/*[contains(local-name(), 'BORDER')]/@Type='DoubleSlim')", - "value": true, + "path": "//SECTION[2]//PAGEBORDERFILL[@Type='Both' or @Type='Even']/@HeaderInside", + "path2": "//BORDERFILL[@Id=//SECTION[2]//PAGEBORDERFILL[@Type='Both' or @Type='Even']/@BorferFill]", + "value": { + "header_inside": true, + "all_double_slim": true + }, "points": 4, "category": "PageBorder", "item": "문제2 쪽테두리(이중 실선, 머리말 포함) 설정" }, + "2": { + "path": "count(//SECTION)>1", + "value": true, + "points": 3, + "category": "Boolean", + "item": "① 구역나누기", + "desc": "섹션이 1개 이상이면 점수부여" + }, + "3": { + "path": "//SECTION[2]//COLDEF/@Count", + "value": "2", + "points": 3, + "category": "SingleAnswer", + "item": "② 다단 2단" + }, + "4": { + "path": "//RECTANGLE//CHAR[text()='{searchValue}']/ancestor::RECTANGLE/SHAPEOBJECT/SIZE/@Width", + "searchValue": "클라우드 컴퓨팅", + "value": "60", + "points": 2, + "category": "mmSize", + "item": "문구 (클라우드 컴퓨팅)/① 크기-너비 (60mm)" + }, + "5": { + "path": "//RECTANGLE//CHAR[text()='{searchValue}']/ancestor::RECTANGLE/SHAPEOBJECT/SIZE/@Height", + "searchValue": "클라우드 컴퓨팅", + "value": "12", + "points": 2, + "category": "mmSize", + "item": "문구 (클라우드 컴퓨팅)/② 크기-높이 (12mm)" + }, + "6": { + "path": "//RECTANGLE[.//CHAR[text()='{searchValue}']]//LINESHAPE/@Style", + "path2": "//RECTANGLE[.//CHAR[text()='{searchValue}']]//LINESHAPE/@Width", + "searchValue": "클라우드 컴퓨팅", + "value": [ "DoubleSlim", "283" ], + "points": 2, + "category": "DoubleAnswer", + "item": "문구 (클라우드 컴퓨팅)/③ 테두리 : 이중 실선(1.00mm)" + }, + "61": {} } } \ No newline at end of file diff --git a/diwScoring2.py b/diwScoring2.py index 6528393..27ddad3 100644 --- a/diwScoring2.py +++ b/diwScoring2.py @@ -209,7 +209,8 @@ class XMLScorer: # search_value를 포함하는 텍스트 찾기 similar_text = self.find_similar_text(root, search_value) xpath = xpath.replace('{searchValue}', similar_text) - # xpath2 = xpath2.replace('{searchValue}', similar_text) + if xpath2 is not None: + xpath2 = xpath2.replace('{searchValue}', similar_text) # 문항 별 채점 결과 저장 scoring = { @@ -260,7 +261,14 @@ class XMLScorer: # 수험자 답안 user_answer = { - 'FontName': font_name[0], + ######################################################################## + # 보통 정답은 글꼴이 [바탕]이어야 하지만 대부분 설정하지 않고 + # 기본 [함초롱바탕]으로 두는 경우가 많아 + # 폰트명을 정답과 비교하면 감점이 많이 발생해서 + # 폰트명은 정답과 비교하지 않게 적용된 상태 + ######################################################################## + #'FontName': font_name[0], + 'FontName': "바탕", 'FontSize': font_size[0], 'Alignment': alignment[0], 'LineSpacing': line_spacing[0] @@ -287,13 +295,30 @@ class XMLScorer: if scoring['points'] > 0: break + elif "DoubleAnswer" in (category or ""): + items1 = root.xpath(xpath) + items2 = root.xpath(xpath2) + + user_answer = [] + for item1, item2 in zip(items1, items2): + user_answer.append(item1) + user_answer.append(item2) + # user_answer[0] = item1 + # user_answer[1] = item2 + + self.evaluate_answer(scoring, user_answer, right_answer, points) + if scoring['points'] > 0: + break + + + # 사용자 입력값이 mm단위인 경우 elif "mmSize" in (category or ""): items = root.xpath(xpath) error_range = criterion.get('tolerance', 0) for item in items: - user_answer = int(item) - right_answer = self.convert_mm_to_pt(int(right_answer)) + user_answer = float(item) + right_answer = self.convert_mm_to_pt(float(right_answer)) self.evaluate_answer(scoring, user_answer, right_answer, points, method="tolerance") @@ -402,6 +427,7 @@ class XMLScorer: break # char2 요소에서 특수문자 갯수 세기 (최대 1점) + # char1과 char2가 다른 경우 (예: ▶ 행사안내 ◀) if (ch1 != ch2) and char2_ele: count_char2 = char2_ele[0].text.count(ch2) if count_char2 > 1: @@ -419,16 +445,44 @@ class XMLScorer: self.evaluate_answer(scoring, user_answer, right_answer, points, method="partial_score") - # 쪽 테두리 + # 쪽 테두리 (이중 실선, 머리말 포함) 설정 elif "PageBorder" in (category or ""): - pageBorderfills = root.xpath(xpath) + user_answer = { + "header_inside": False, + "all_double_slim": False + } - for pageBorderfill in pageBorderfills: - headerInside = pageBorderfill.get('HeaderInside') - borderfill_id = pageBorderfill.get('BorferFill') - print( f"headerInside: {headerInside}, borderfill_id: {borderfill_id}") - break + # 머릿말 포함 객체가 하나라도 있으면 정답 + header_inside_elements = root.xpath(xpath) + for header_inside in header_inside_elements: + # print("머릿말포함: ",header_inside) + if "true" in header_inside: + user_answer["header_inside"] = True + # BORDERFILL요소의 자녀 + # LEFTBORDER, RIGHTBORDER, TOPBORDER, BOTTOMBORDER 요소의 Type속성이 + # 모두 DoubleSlim이면 정답 + border_fill_elements = root.xpath(xpath2) + for bf in border_fill_elements: + border_tags = ["LEFTBORDER", "RIGHTBORDER", "TOPBORDER", "BOTTOMBORDER"] + + all_double_slim = True + for tag in border_tags: + element = bf.find(tag) + if element is None or element.get("Type") != "DoubleSlim": + all_double_slim = False + break + + #모든 BORDER 태그의 Type 속성이 'DoubleSlim' + if all_double_slim: + user_answer["all_double_slim"] = True + + # 하나 이상의 BORDER 태그가 'DoubleSlim'이 아님 + else: + user_answer["all_double_slim"] = False + + self.evaluate_answer(scoring, user_answer, right_answer, points, method="equal") + onePersonResult['score_results'].append(scoring) print(f'scoring: {scoring}') diff --git a/zzz.xbook b/zzz.xbook index 4169663..1cb54b0 100644 --- a/zzz.xbook +++ b/zzz.xbook @@ -1 +1 @@ -[{"kind":2,"language":"xpath","value":"//c:catAx//a:defRPr/@i"},{"kind":2,"language":"xpath","value":"count(//CHAR[contains(text(),'◆')]) + count(//CHAR[contains(text(),'※')])"},{"kind":2,"language":"xpath","value":"string-length(//CHAR[contains(text(),'◆')]) - string-length(translate(//CHAR[contains(text(),'◆')], '◆', '')) + string-length(//CHAR[contains(text(),'※')]) - string-length(translate(//CHAR[contains(text(),'※')], '※', ''))"}] \ No newline at end of file +[{"kind":2,"language":"xpath","value":"//BORDERFILL[@Id=//PAGEBORDERFILL[@Type='Both' or @Type='Even']/@BorferFill]/*[contains(local-name(), 'BORDER')]/@Type"},{"kind":2,"language":"xpath","value":"//BORDERFILL[@Id=//SECTION[2]//PAGEBORDERFILL[@Type='Both' or @Type='Even']/@BorferFill]"},{"kind":2,"language":"xpath","value":"//RECTANGLE//CHAR[text()='클라우드 컴퓨팅']/ancestor::RECTANGLE/descendant::LINESHAPE"},{"kind":2,"language":"xpath","value":"//RECTANGLE[.//CHAR[text()='클라우드 컴퓨팅']]//LINESHAPE"},{"kind":2,"language":"xpath","value":"//BORDERFILL[@Id=//SECTION[2]//PAGEBORDERFILL[@Type='Both' or @Type='Even']/@BorferFill][not(*[substring(name(), string-length(name()) - 5) = 'BORDER'][not(@Type = 'DoubleSlim')])]"},{"kind":2,"language":"xpath","value":"//BORDERFILL[*[substring(name(), string-length(name()) - 5) = 'BORDER' and @Type = 'DoubleSlim']]"}] \ No newline at end of file diff --git a/회차별채점자료/2504/excel_채점기준표/DIW_2504A.xlsx b/회차별채점자료/2504/excel_채점기준표/DIW_2504A.xlsx index a01420e..c5d70d8 100644 Binary files a/회차별채점자료/2504/excel_채점기준표/DIW_2504A.xlsx and b/회차별채점자료/2504/excel_채점기준표/DIW_2504A.xlsx differ