v2 - 2504회 B형 채점 및 추가 수정

This commit is contained in:
2025-05-20 18:02:04 +09:00
parent 8e8abfd9f1
commit 220374145e
24 changed files with 927 additions and 2968 deletions

View File

@@ -43,7 +43,7 @@ class XMLScorer:
return pt
# 유사한 텍스트 찾기
def find_similar_text(self, root, chart_tree, target_text, threshold=0.7):
def find_similar_text(self, root, target_text, xml_type, threshold=0.7):
"""
전체 문서에서 유사한 텍스트를 찾아 반환
@@ -64,10 +64,11 @@ class XMLScorer:
'c': 'http://schemas.openxmlformats.org/drawingml/2006/chart'
}
hwp_text = root.xpath(f"//BODY//text() | //TEXTART/@Text")
chart_text = chart_tree.xpath(f"//c:chart//text()", namespaces=namespaces) if chart_tree is not None else []
if xml_type == "hml":
all_text = root.xpath(f"//BODY//text() | //TEXTART/@Text") if root is not None else []
all_text = hwp_text + chart_text
if xml_type == "chart":
all_text = root.xpath(f"//c:chart//text()", namespaces=namespaces) if root is not None else []
# 유사도 비교
max_score = 0
@@ -163,11 +164,19 @@ class XMLScorer:
# search_value가 있는 경우
if search_value is not None:
# search_value를 포함하는 텍스트 찾기
similar_text = self.find_similar_text(root, chart_tree, search_value)
xpath = xpath.replace('{searchValue}', similar_text) if xpath else ""
xpath2 = xpath2.replace('{searchValue}', similar_text) if xpath2 else ""
chart_xpath = chart_xpath.replace('{searchValue}', similar_text) if chart_xpath else ""
# # search_value를 포함하는 텍스트 찾기
# similar_text = self.find_similar_text(root, chart_tree, search_value)
# xpath = xpath.replace('{searchValue}', similar_text) if xpath else ""
# xpath2 = xpath2.replace('{searchValue}', similar_text) if xpath2 else ""
# chart_xpath = chart_xpath.replace('{searchValue}', similar_text) if chart_xpath else ""
if xpath or xpath2:
similar_text = self.find_similar_text(root, search_value, xml_type="hml")
xpath = xpath.replace('{searchValue}', similar_text) if xpath else ""
xpath2 = xpath2.replace('{searchValue}', similar_text) if xpath2 else ""
if chart_xpath:
similar_text = self.find_similar_text(chart_tree, search_value, xml_type="chart")
chart_xpath = chart_xpath.replace('{searchValue}', similar_text) if chart_xpath else ""
if option:
xpath = xpath.replace('{option}', option) if xpath else ""
@@ -207,38 +216,39 @@ class XMLScorer:
break
elif (category or "") == "BasicSetting":
# 바탕글(기본설정) 요소
normal_style = root.xpath("//STYLE[@Name='바탕글']")
# CHARSHAPE, PARASHAPE, FONTID 속성값 추출
charshape_id = normal_style[0].get("CharShape")
parashape_id = normal_style[0].get("ParaShape")
font_id = root.xpath(f"//CHARSHAPE[@Id='{charshape_id}']/FONTID/@Hangul")
# 필요한 속성값을 이용하여 정답과 비교
font_name = root.xpath(f"//FONT[@Id='{font_id[0]}']/@Name")
font_size = root.xpath(f"//CHARSHAPE[@Id='{charshape_id}']/@Height")
alignment = root.xpath(f"//PARASHAPE[@Id='{parashape_id}']/@Align")
line_spacing = root.xpath(f"//PARASHAPE[@Id='{parashape_id}']/PARAMARGIN/@LineSpacing")
# 수험자 답안
user_answer = {
########################################################################
# 정답은 글꼴이 [바탕]이어야 하지만 대부분 설정하지 않고
# 기본 [함초롱바탕]으로 두는 경우가 많아
# 폰트명을 정답과 비교하면 감점이 많이 발생함
#
# 수험자가 지정한 폰트명과 정답을 비교할때 'FontName': font_name[0], 적용
# 폰트명은 상관없이 정답을 비교하고자 할때 'FontName': "바탕", 적용
########################################################################
# 'FontName': font_name[0],
'FontName': "바탕",
'FontSize': font_size[0],
'Alignment': alignment[0],
'LineSpacing': line_spacing[0]
}
self.evaluate_answer(scoring, user_answer, right_answer, points, method="equal")
# FontName, FontSize, Alignment, LineSpacing
# 해당 속성의 요소(텍스트)가 문서 내부에 존재하면 정답처리
matches = set()
# P 태그 순회
for p_tag in root.xpath(".//P"):
parashape = p_tag.get("ParaShape")
for text_tag in p_tag.xpath(".//TEXT"):
charshape = text_tag.get("CharShape")
if parashape is not None and charshape is not None:
matches.add((parashape, charshape))
# 출력
for para, char in matches:
# print(f"ParaShape = {para}, CharShape = {char}")
font_id = root.xpath(f"//CHARSHAPE[@Id='{char}']/FONTID/@Hangul")
font_name = root.xpath(f"//FONTFACE[@Lang='Hangul']/FONT[@Id='{font_id[0]}']/@Name")
user_answer = {
'FontName': font_name[0],
'FontSize': root.xpath(f"//CHARSHAPE[@Id='{char}']/@Height")[0],
'Alignment': root.xpath(f"//PARASHAPE[@Id='{para}']/@Align")[0],
'LineSpacing': root.xpath(f"//PARASHAPE[@Id='{para}']/PARAMARGIN/@LineSpacing")[0]
}
# 정답과 수험자 답안 비교
self.evaluate_answer(scoring, user_answer, right_answer, points, method="equal")
if scoring['points'] > 0:
break
# 1, 2페이지 모두 정답이어야 함
elif (category or "") == "PageNumber":
@@ -294,7 +304,13 @@ class XMLScorer:
# 사용자 입력값이 mm단위인 경우
elif (category or "") == "mmSize":
items = root.xpath(xpath)
error_range = criterion.get('tolerance', 0)
# 오차범위 설정
# 한글 프로그램 내부에서 드물게 mm단위는 0mm이지만 pt단위는 1pt로 저장되는 경우가 있음
#
# XML파일의 요소 옵션값은 내부적으로 1=0.01pt
# 이 경우를 대비하여 tolerance를 10으로 설정 (1pt=약0.04mm 만큼의 오차 혀용)
error_range = criterion.get('tolerance', 10)
for item in items:
user_answer = float(item)
@@ -304,7 +320,7 @@ class XMLScorer:
float_string = right_answer.strip().replace("mm", "")
right_answer = self.convert_mm_to_pt(float(float_string))
self.evaluate_answer(scoring, user_answer, right_answer, points, method="tolerance")
self.evaluate_answer(scoring, user_answer, right_answer, points, method="tolerance", tolerance=error_range)
if scoring['points'] > 0:
break
@@ -367,7 +383,7 @@ class XMLScorer:
# 폰트명
elif (category or "") == "FontName":
charshape_list = root.xpath(xpath)
if not charshape_id:
if not charshape_list:
user_answer = ""
else:
for charshape_id in charshape_list:
@@ -618,17 +634,20 @@ class XMLScorer:
elif (category or "") == "ChartType":
chart_type_list = {
'꺾은선형': "//c:lineChart",
'가로막대형': "//c:barChart[c:barDir[@val='bar']]",
'세로막대형': "//c:barChart[c:barDir[@val='col']]",
'꺾은선형': "//c:lineChart[c:grouping[@val='standard']]",
'가로막대형': "//c:barChart[c:barDir[@val='bar'] and c:grouping[@val='clustered']]",
'세로막대형': "//c:barChart[c:barDir[@val='col'] and c:grouping[@val='clustered']]",
'원형': "//c:pieChart",
'분산형': "//c:scatterChart"
}
chart_type = criterion.get('chart_type').replace(" ","")
if "묶은" in chart_type:
chart_type = chart_type.replace("묶은", "")
# 입력한 chart_type에 해당하는 xpath를 가져옴
chart_xpath = chart_type_list[chart_type]
# xpath를 사용하여 차트 요소가 있는지 확인
user_answer = bool(chart_tree.xpath(chart_xpath, namespaces=namespaces))
self.evaluate_answer(scoring, user_answer, right_answer, points)
@@ -940,8 +959,8 @@ def main():
# 시험회차 및 유형
exam_round = '2504'
exam_types = [
'A',
# 'B',
# 'A',
'B',
# 'C',
]
test_mode = False