2505회 [1-17,18] 하이퍼링크관련 채점기준수정, [2-17] 이미지 삽입 부분 '글저처럼취급', '쪽' 조건 수정

This commit is contained in:
2025-06-04 18:00:06 +09:00
parent be04a0896f
commit f2deb89517
17 changed files with 56 additions and 24 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
250604_DIW_2505C_TEST.xlsx Normal file

Binary file not shown.

Binary file not shown.

View File

@@ -454,14 +454,14 @@
"item": "③ 크기-높이 (40 mm)" "item": "③ 크기-높이 (40 mm)"
}, },
"17": { "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", "value": "0",
"points": 2, "points": 2,
"category": "mmSize", "category": "mmSize",
"item": "④ 위치 (어울림 : 가로-쪽의 왼쪽 0.0mm)" "item": "④ 위치 (어울림 : 가로-쪽의 왼쪽 0.0mm)"
}, },
"18": { "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", "value": "23",
"points": 2, "points": 2,
"category": "mmSize", "category": "mmSize",

View File

@@ -456,14 +456,14 @@
"item": "③ 크기-높이 (40 mm)" "item": "③ 크기-높이 (40 mm)"
}, },
"17": { "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", "value": "0",
"points": 2, "points": 2,
"category": "mmSize", "category": "mmSize",
"item": "④ 위치 (어울림 : 가로-쪽의 왼쪽 0.0mm)" "item": "④ 위치 (어울림 : 가로-쪽의 왼쪽 0.0mm)"
}, },
"18": { "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", "value": "24",
"points": 2, "points": 2,
"category": "mmSize", "category": "mmSize",

View File

@@ -456,14 +456,14 @@
"item": "③ 크기-높이 (40 mm)" "item": "③ 크기-높이 (40 mm)"
}, },
"17": { "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", "value": "0",
"points": 2, "points": 2,
"category": "mmSize", "category": "mmSize",
"item": "④ 위치 (어울림 : 가로-쪽의 왼쪽 0.0mm)" "item": "④ 위치 (어울림 : 가로-쪽의 왼쪽 0.0mm)"
}, },
"18": { "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", "value": "24",
"points": 2, "points": 2,
"category": "mmSize", "category": "mmSize",

View File

@@ -126,6 +126,19 @@ class XMLScorer:
# 하나의 XML 파일 채점 # 하나의 XML 파일 채점
def _score_xml_file(self, xml_file, chart_xml): def _score_xml_file(self, xml_file, chart_xml):
def extract_char_text_from_p(p_element):
"""
주어진 <P> 요소에서 모든 자손 <CHAR>의 텍스트를 추출해 문자열 리스트로 반환합니다.
"""
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: try:
tree = ET.parse(xml_file) tree = ET.parse(xml_file)
root = tree.getroot() root = tree.getroot()
@@ -373,6 +386,7 @@ class XMLScorer:
elif (category or "") == "mmSize": elif (category or "") == "mmSize":
items = root.xpath(xpath) items = root.xpath(xpath)
# 오차범위 설정 # 오차범위 설정
# 한글 프로그램 내부에서 드물게 0mm이지만 1pt로 저장되는 경우가 있음 # 한글 프로그램 내부에서 드물게 0mm이지만 1pt로 저장되는 경우가 있음
# #
@@ -385,6 +399,9 @@ class XMLScorer:
float_string = right_answer.strip().replace("mm", "") float_string = right_answer.strip().replace("mm", "")
right_answer = self.convert_mm_to_pt(float(float_string)) right_answer = self.convert_mm_to_pt(float(float_string))
if not items:
scoring['points'] = 0
else:
for item in items: for item in items:
user_answer = float(item) user_answer = float(item)
@@ -505,11 +522,27 @@ class XMLScorer:
# 폰트 속성 # 폰트 속성
elif (category or "") == "FontAttribute": 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가 아닌 경우(일반적인 텍스트 일 경우) # hyperlink가 아닌 경우(일반적인 텍스트 일 경우)
if not has_hyperlink_ptag: # 하이퍼링크를 포함한 P태그가 없거나 search_value값이 하이퍼링크텍스트에 포함되어 있지 않을 경우
if not hyperlink_ptag or not search_in_hyperlink:
charshape_list = root.xpath(xpath) charshape_list = root.xpath(xpath)
if not charshape_list: if not charshape_list:
charshape = None charshape = None
@@ -528,10 +561,9 @@ class XMLScorer:
break break
# 하이퍼링크인 경우 # 하이퍼링크인 경우
elif has_hyperlink_ptag: # elif hyperlink_ptag and search_in_hyperlink:
hyperlink_text = search_value.replace(" ", "") else:
p_elements = hyperlink_ptag
p_elements = has_hyperlink_ptag
for p in p_elements: for p in p_elements:
# 수험자가 입력한 텍스트 중 하이퍼링크가 들어간 문단의 모든 텍스트를 가져와 # 수험자가 입력한 텍스트 중 하이퍼링크가 들어간 문단의 모든 텍스트를 가져와
@@ -1050,13 +1082,13 @@ def main():
# 채점하고자 하는 유형은 주석 해제 # 채점하고자 하는 유형은 주석 해제
exam_types = [ exam_types = [
# 'A', # 'A',
'B', # 'B',
# 'C', 'C',
# 'D', # 'D',
] ]
test_mode = False # test_mode = False
# test_mode = True #/TEST 폴더 채점시 test_mode = True #/TEST 폴더 채점시
output_excel_paths = [] output_excel_paths = []
for exam_type in exam_types: for exam_type in exam_types:

View File

@@ -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"}] [{"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"}]