v2 - 문제2 6번문항까지

This commit is contained in:
2025-05-13 19:46:19 +09:00
parent ac655d58d3
commit 8c93c6d446
5 changed files with 163 additions and 59 deletions

Binary file not shown.

View File

@@ -37,7 +37,6 @@
}, },
"3": { "3": {
"path": "", "path": "",
"searchValue": null,
"value": null, "value": null,
"points": 40, "points": 40,
"category": "오타감점", "category": "오타감점",
@@ -97,7 +96,6 @@
}, },
"7": { "7": {
"path": "//TEXTART[@Text='{searchValue}']", "path": "//TEXTART[@Text='{searchValue}']",
"path2": null,
"searchValue": "클라우드컴퓨팅컨퍼런스", "searchValue": "클라우드컴퓨팅컨퍼런스",
"value": true, "value": true,
"points": 2, "points": 2,
@@ -106,7 +104,6 @@
}, },
"8": { "8": {
"path": "//RECTANGLE[.//CHAR[text()='{searchValue}']]/SHAPEOBJECT/SIZE", "path": "//RECTANGLE[.//CHAR[text()='{searchValue}']]/SHAPEOBJECT/SIZE",
"path2": null,
"searchValue": "전", "searchValue": "전",
"value": { "value": {
"Height": 2800, "Height": 2800,
@@ -119,7 +116,6 @@
}, },
"9": { "9": {
"path": "//TEXT[CHAR[text()='{searchValue}']]/@CharShape", "path": "//TEXT[CHAR[text()='{searchValue}']]/@CharShape",
"path2": null,
"searchValue": "전", "searchValue": "전",
"value": "궁서체", "value": "궁서체",
"points": 1, "points": 1,
@@ -128,7 +124,6 @@
}, },
"10": { "10": {
"path": "//RECTANGLE[.//CHAR[text()='{searchValue}']]//WINDOWBRUSH/@FaceColor", "path": "//RECTANGLE[.//CHAR[text()='{searchValue}']]//WINDOWBRUSH/@FaceColor",
"path2": null,
"searchValue": "전", "searchValue": "전",
"value": "255,132,58", "value": "255,132,58",
"points": 2, "points": 2,
@@ -137,9 +132,8 @@
}, },
"11": { "11": {
"path": "//RECTANGLE[.//CHAR[text()='{searchValue}']]//OUTSIDEMARGIN/@Right", "path": "//RECTANGLE[.//CHAR[text()='{searchValue}']]//OUTSIDEMARGIN/@Right",
"path2": null,
"searchValue": "전", "searchValue": "전",
"value": "850", "value": "3.0",
"tolerance": 1, "tolerance": 1,
"points": 2, "points": 2,
"category": "mmSize", "category": "mmSize",
@@ -147,20 +141,19 @@
}, },
"12": { "12": {
"path": "//CHARSHAPE[@Id=//CHAR[contains(text(),'{searchValue}')]/parent::TEXT/@CharShape]", "path": "//CHARSHAPE[@Id=//CHAR[contains(text(),'{searchValue}')]/parent::TEXT/@CharShape]",
"path2": null, "searchValue": "글로벌 클라우드 컴퓨팅 컨퍼런스",
"searchValue": "한옥에 대한 체험과 교육이 준비된 사생대회",
"value": "BOLD", "value": "BOLD",
"points": 2, "points": 2,
"category": "FontAttribute", "category": "FontAttribute",
"item": "문구 (한옥에 대한 체험과 교육이 준비된 사생대회)/① 진하게" "item": "문구 (글로벌 클라우드 컴퓨팅 컨퍼런스)/① 진하게"
}, },
"13": { "13": {
"path": "//CHARSHAPE[@Id=//CHAR[contains(text(),'{searchValue}')]/parent::TEXT/@CharShape]", "path": "//CHARSHAPE[@Id=//CHAR[contains(text(),'{searchValue}')]/parent::TEXT/@CharShape]",
"searchValue": "한옥에 대한 체험과 교육이 준비된 사생대회", "searchValue": "글로벌 클라우드 컴퓨팅 컨퍼런스",
"value": "UNDERLINE", "value": "UNDERLINE",
"points": 2, "points": 2,
"category": "FontAttribute", "category": "FontAttribute",
"item": "문구 (한옥에 대한 체험과 교육이 준비된 사생대회)/② 밑줄" "item": "문구 (글로벌 클라우드 컴퓨팅 컨퍼런스)/② 밑줄"
}, },
"14": { "14": {
"path": "//CHAR[contains(text(),'{char1}')]", "path": "//CHAR[contains(text(),'{char1}')]",
@@ -176,35 +169,35 @@
}, },
"15": { "15": {
"path": "//CHAR[contains(text(),'{searchValue}')]/parent::TEXT/@CharShape", "path": "//CHAR[contains(text(),'{searchValue}')]/parent::TEXT/@CharShape",
"searchValue": "■ 행사안내 ■", "searchValue": "참여안내",
"value": "돋움", "value": "궁서",
"points": 1, "points": 1,
"category": "FontName", "category": "FontName",
"item": "문구 (■ 행사안내 )/① 글씨체 (돋움)" "item": "문구 (● 참여안내 )/① 글씨체 (궁서)"
}, },
"16": { "16": {
"path": "//PARASHAPE[@Id=//CHAR[contains(text(),'{searchValue}')]/ancestor::P/@ParaShape]/@Align", "path": "//PARASHAPE[@Id=//CHAR[contains(text(),'{searchValue}')]/ancestor::P/@ParaShape]/@Align",
"searchValue": "■ 행사안내 ■", "searchValue": "참여안내",
"value": "Center", "value": "Center",
"points": 1, "points": 1,
"category": "SingleAnswer", "category": "SingleAnswer",
"item": "문구 (■ 행사안내 )/② 정렬 (가운데 정렬)" "item": "문구 (● 참여안내 )/② 정렬 (가운데 정렬)"
}, },
"17": { "17": {
"path": "//CHARSHAPE[@Id=//CHAR[text()='{searchValue}']/parent::TEXT/@CharShape]", "path": "//CHARSHAPE[@Id=//CHAR[text()='{searchValue}']/parent::TEXT/@CharShape]",
"searchValue": "홈페이지(http://www.ihd.or.kr)에서 개별 신청, 선착순 접수", "searchValue": "홈페이지(http://www.ihd.or.kr) 참조",
"value": "ITALIC", "value": "ITALIC",
"points": 1, "points": 1,
"category": "FontAttribute", "category": "FontAttribute",
"item": "문구 (홈페이지(http://www.ihd.or.kr)에서 개별 신청, 선착순 접수)/① 기울임" "item": "문구 (홈페이지(http://www.ihd.or.kr) 참조)/① 기울임"
}, },
"18": { "18": {
"path": "//CHARSHAPE[@Id=//CHAR[text()='{searchValue}']/parent::TEXT/@CharShape]", "path": "//CHARSHAPE[@Id=//CHAR[text()='{searchValue}']/parent::TEXT/@CharShape]",
"searchValue": "홈페이지(http://www.ihd.or.kr)에서 개별 신청, 선착순 접수", "searchValue": "홈페이지(http://www.ihd.or.kr) 참조",
"value": "UNDERLINE", "value": "UNDERLINE",
"points": 1, "points": 1,
"category": "FontAttribute", "category": "FontAttribute",
"item": "문구 (홈페이지(http://www.ihd.or.kr)에서 개별 신청, 선착순 접수)/② 밑줄" "item": "문구 (홈페이지(http://www.ihd.or.kr) 참조)/② 밑줄"
}, },
"19": { "19": {
"path": "//PARASHAPE[@Id=//CHAR[contains(text(),'{searchValue}')]/ancestor::P/following-sibling::P[1]/@ParaShape]/PARAMARGIN", "path": "//PARASHAPE[@Id=//CHAR[contains(text(),'{searchValue}')]/ancestor::P/following-sibling::P[1]/@ParaShape]/PARAMARGIN",
@@ -216,55 +209,56 @@
"points": 2, "points": 2,
"category": "ParaShape", "category": "ParaShape",
"item": "문구 (※ 기타… 이하 문단)/왼쪽여백 (15pt), 내어쓰기 (12pt)", "item": "문구 (※ 기타… 이하 문단)/왼쪽여백 (15pt), 내어쓰기 (12pt)",
"desc": "내부적으로 내어쓰기는 음수값" "desc": "내부적으로 내어쓰기는 음수값 / JSON value값은 양수로 입력"
}, },
"20": { "20": {
"path": "//CHARSHAPE[@Id=//CHAR[contains(text(),'{searchValue}')]/parent::TEXT/@CharShape]/@Height", "path": "//CHARSHAPE[@Id=//CHAR[contains(text(),'{searchValue}')]/parent::TEXT/@CharShape]/@Height",
"searchValue": "2025. 03. 22.", "searchValue": "2025. 04. 26.",
"value": "1300", "value": "1400",
"points": 1, "points": 1,
"category": "SingleAnswer", "category": "SingleAnswer",
"item": "문구 (2025. 03. 22.)/① 크기 (13pt)" "item": "문구 (2025. 04. 26.)/① 크기 (14pt)",
"desc": "1pt당 100"
}, },
"21": { "21": {
"path": "//PARASHAPE[@Id=//CHAR[contains(text(),'{searchValue}')]/ancestor::P/@ParaShape]/@Align", "path": "//PARASHAPE[@Id=//CHAR[contains(text(),'{searchValue}')]/ancestor::P/@ParaShape]/@Align",
"searchValue": "2025. 03. 22.", "searchValue": "2025. 04. 26.",
"value": "Center", "value": "Center",
"points": 1, "points": 1,
"category": "SingleAnswer", "category": "SingleAnswer",
"item": "문구 (2025. 03. 22.)/② 정렬 (가운데 정렬)" "item": "문구 (2025. 04. 26.)/② 정렬 (가운데 정렬)"
}, },
"22": { "22": {
"path": "//CHAR[text()='{searchValue}']/parent::TEXT/@CharShape", "path": "//CHAR[text()='{searchValue}']/parent::TEXT/@CharShape",
"searchValue": "한국고건축협회", "searchValue": "글로벌멀티클라우드협의회",
"value": "궁서", "value": "궁서",
"points": 1, "points": 1,
"category": "FontName", "category": "FontName",
"item": "문구 (한국고건축협회)/① 글씨체 (궁서)" "item": "문구 (글로벌멀티클라우드협의회)/① 글씨체 (궁서)"
}, },
"23": { "23": {
"path": "//CHARSHAPE[@Id=//CHAR[text()='{searchValue}']/parent::TEXT/@CharShape]/@Height", "path": "//CHARSHAPE[@Id=//CHAR[text()='{searchValue}']/parent::TEXT/@CharShape]/@Height",
"searchValue": "한국고건축협회", "searchValue": "글로벌멀티클라우드협의회",
"value": "2400", "value": "2600",
"points": 1, "points": 1,
"category": "SingleAnswer", "category": "SingleAnswer",
"item": "문구 (한국고건축협회)/② 크기 (24pt)" "item": "문구 (글로벌멀티클라우드협의회)/② 크기 (26pt)"
}, },
"24": { "24": {
"path": "//PARASHAPE[@Id=//CHAR[text()='{searchValue}']/ancestor::P/@ParaShape]/@Align", "path": "//PARASHAPE[@Id=//CHAR[text()='{searchValue}']/ancestor::P/@ParaShape]/@Align",
"searchValue": "한국고건축협회", "searchValue": "글로벌멀티클라우드협의회",
"value": "Center", "value": "Center",
"points": 1, "points": 1,
"category": "SingleAnswer", "category": "SingleAnswer",
"item": "문구 (한국고건축협회)/③ 정렬 (가운데 정렬)" "item": "문구 (글로벌멀티클라우드협의회)/③ 정렬 (가운데 정렬)"
}, },
"25": { "25": {
"path": "//CHAR[text()='{searchValue}']/parent::TEXT/@CharShape", "path": "//CHAR[text()='{searchValue}']/parent::TEXT/@CharShape",
"searchValue": "DIAT", "searchValue": "DIAT",
"value": "굴림", "value": "궁서",
"points": 1, "points": 1,
"category": "FontName", "category": "FontName",
"item": "문구 (DIAT)/① 글꼴 (굴림)" "item": "문구 (DIAT)/① 글꼴 (궁서)"
}, },
"26": { "26": {
"path": "//CHARSHAPE[@Id=//SECTION[1]//CHAR[text()='{searchValue}']/parent::TEXT/@CharShape]/@Height", "path": "//CHARSHAPE[@Id=//SECTION[1]//CHAR[text()='{searchValue}']/parent::TEXT/@CharShape]/@Height",
@@ -284,36 +278,92 @@
}, },
"28": { "28": {
"path": "//PAGENUM/@FormatType", "path": "//PAGENUM/@FormatType",
"value": "HangulSyllable", "value": "LatinCapital",
"points": 2, "points": 2,
"category": "SingleAnswer", "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": { "29": {
"path": "//PAGENUM/@Pos", "path": "//PAGENUM/@Pos",
"value": "BottomCenter", "value": "BottomRight",
"points": 2, "points": 2,
"category": "SingleAnswer", "category": "SingleAnswer",
"item": "② 가운데 아래" "item": "오른쪽 아래"
}, },
"30": { "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, "value": true,
"points": 2, "points": 2,
"category": "Boolean", "category": "Boolean",
"item": "문제 1 줄간격 180% 설정", "item": "문제 1 줄간격 200% 설정",
"desc": "1페이지 문단의 줄간격이 180%가 아닌 문단이 있으면 False(감점)" "desc": "1페이지 문단의 줄간격이 200%가 아닌 문단이 있으면 False(감점)"
} }
}, },
"2": { "2": {
"1": { "1": {
"path": "//SECTION[2]//PAGEBORDERFILL[@Type='Both' or @Type='Even']", "path": "//SECTION[2]//PAGEBORDERFILL[@Type='Both' or @Type='Even']/@HeaderInside",
"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')", "path2": "//BORDERFILL[@Id=//SECTION[2]//PAGEBORDERFILL[@Type='Both' or @Type='Even']/@BorferFill]",
"value": true, "value": {
"header_inside": true,
"all_double_slim": true
},
"points": 4, "points": 4,
"category": "PageBorder", "category": "PageBorder",
"item": "문제2 쪽테두리(이중 실선, 머리말 포함) 설정" "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": {} "61": {}
} }
} }

View File

@@ -209,7 +209,8 @@ class XMLScorer:
# search_value를 포함하는 텍스트 찾기 # search_value를 포함하는 텍스트 찾기
similar_text = self.find_similar_text(root, search_value) similar_text = self.find_similar_text(root, search_value)
xpath = xpath.replace('{searchValue}', similar_text) xpath = xpath.replace('{searchValue}', similar_text)
# xpath2 = xpath2.replace('{searchValue}', similar_text) if xpath2 is not None:
xpath2 = xpath2.replace('{searchValue}', similar_text)
# 문항 별 채점 결과 저장 # 문항 별 채점 결과 저장
scoring = { scoring = {
@@ -260,7 +261,14 @@ class XMLScorer:
# 수험자 답안 # 수험자 답안
user_answer = { user_answer = {
'FontName': font_name[0], ########################################################################
# 보통 정답은 글꼴이 [바탕]이어야 하지만 대부분 설정하지 않고
# 기본 [함초롱바탕]으로 두는 경우가 많아
# 폰트명을 정답과 비교하면 감점이 많이 발생해서
# 폰트명은 정답과 비교하지 않게 적용된 상태
########################################################################
#'FontName': font_name[0],
'FontName': "바탕",
'FontSize': font_size[0], 'FontSize': font_size[0],
'Alignment': alignment[0], 'Alignment': alignment[0],
'LineSpacing': line_spacing[0] 'LineSpacing': line_spacing[0]
@@ -287,13 +295,30 @@ class XMLScorer:
if scoring['points'] > 0: if scoring['points'] > 0:
break 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 ""): elif "mmSize" in (category or ""):
items = root.xpath(xpath) items = root.xpath(xpath)
error_range = criterion.get('tolerance', 0) error_range = criterion.get('tolerance', 0)
for item in items: for item in items:
user_answer = int(item) user_answer = float(item)
right_answer = self.convert_mm_to_pt(int(right_answer)) right_answer = self.convert_mm_to_pt(float(right_answer))
self.evaluate_answer(scoring, user_answer, right_answer, points, method="tolerance") self.evaluate_answer(scoring, user_answer, right_answer, points, method="tolerance")
@@ -402,6 +427,7 @@ class XMLScorer:
break break
# char2 요소에서 특수문자 갯수 세기 (최대 1점) # char2 요소에서 특수문자 갯수 세기 (최대 1점)
# char1과 char2가 다른 경우 (예: ▶ 행사안내 ◀)
if (ch1 != ch2) and char2_ele: if (ch1 != ch2) and char2_ele:
count_char2 = char2_ele[0].text.count(ch2) count_char2 = char2_ele[0].text.count(ch2)
if count_char2 > 1: if count_char2 > 1:
@@ -419,16 +445,44 @@ class XMLScorer:
self.evaluate_answer(scoring, user_answer, right_answer, points, method="partial_score") self.evaluate_answer(scoring, user_answer, right_answer, points, method="partial_score")
# 쪽 테두리 # 쪽 테두리 (이중 실선, 머리말 포함) 설정
elif "PageBorder" in (category or ""): 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') header_inside_elements = root.xpath(xpath)
borderfill_id = pageBorderfill.get('BorferFill') for header_inside in header_inside_elements:
print( f"headerInside: {headerInside}, borderfill_id: {borderfill_id}") # 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 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) onePersonResult['score_results'].append(scoring)
print(f'scoring: {scoring}') print(f'scoring: {scoring}')

View File

@@ -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(),'※')], '※', ''))"}] [{"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']]"}]