v2 - 2504회 B형 채점 및 추가 수정
This commit is contained in:
117
diwScoring2.py
117
diwScoring2.py
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user