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

This commit is contained in:
2025-05-21 17:57:41 +09:00
parent 220374145e
commit c8554adbb7
10 changed files with 936 additions and 85 deletions

View File

@@ -164,12 +164,6 @@ 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 ""
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 ""
@@ -192,22 +186,35 @@ class XMLScorer:
'right_answer': right_answer, # 정답
'user_answer': None, # 실제 작성 답안
'points': 0, # 점수
'deductions': [] # 각 기준별 감점 내역
}
if (category or "") == "PageSetting":
items = root.xpath(xpath)
error_range = criterion.get('tolerance', 0)
right_answer = {
'Top' : float(right_answer.get("Top", 0)),
'Bottom' : float(right_answer.get("Bottom", 0)),
'Left' : float(right_answer.get("Left", 0)),
'Right' : float(right_answer.get("Right", 0)),
'Header' : float(right_answer.get("Header", 0)),
'Footer' : float(right_answer.get("Footer", 0)),
'Gutter' : float(right_answer.get("Gutter", 0)),
}
right_answer = {
k: self.convert_mm_to_pt(v)
for k, v in right_answer.items()
}
for item in items:
user_answer = {
'Bottom' : int(item.get("Bottom", 0)),
'Footer' : int(item.get("Footer", 0)),
'Gutter' : int(item.get("Gutter", 0)),
'Header' : int(item.get("Header", 0)),
'Left' : int(item.get("Left", 0)),
'Right' : int(item.get("Right", 0)),
'Top' : int(item.get("Top", 0)),
'Top' : float(item.get("Top", 0)),
'Bottom' : float(item.get("Bottom", 0)),
'Left' : float(item.get("Left", 0)),
'Right' : float(item.get("Right", 0)),
'Header' : float(item.get("Header", 0)),
'Footer' : float(item.get("Footer", 0)),
'Gutter' : float(item.get("Gutter", 0)),
}
self.evaluate_answer(scoring, user_answer, right_answer, points, method="tolerance", tolerance=error_range)
@@ -274,19 +281,32 @@ class XMLScorer:
self.partial_score += points
scoring['points'] = points
# 정답이 하나인 경우
elif (category or "") == "OneAnswer":
# 정답이 하나 또는 테이블의 모든 값이 정답인 경우
elif (category or "") in ["OneAnswer", "TableOneAnswer"]:
items = root.xpath(xpath) if xpath else []
items2 = root.xpath(xpath2) if xpath2 else []
chart_items = chart_tree.xpath(chart_xpath, namespaces=namespaces) if chart_xpath else []
require_all_match = (category == "TableOneAnswer")
any_match = False
all_match = True
for item in chain(items, items2, chart_items):
user_answer = item
self.evaluate_answer(scoring, user_answer, right_answer, points)
if scoring['points'] > 0:
break
if user_answer == right_answer:
any_match = True
else:
all_match = False
if require_all_match:
break # 하나라도 다르면 바로 탈출
if require_all_match:
score = points if all_match else 0
else:
score = points if any_match else 0
self.evaluate_answer(scoring, user_answer, right_answer, score)
elif (category or "") == "DoubleAnswer":
items1 = root.xpath(xpath) if xpath else []
@@ -312,14 +332,14 @@ class XMLScorer:
# 이 경우를 대비하여 tolerance를 10으로 설정 (1pt=약0.04mm 만큼의 오차 혀용)
error_range = criterion.get('tolerance', 10)
# JSON 파일 value키값에 mm나 공백이 입력될 경우 제거
# 예) "80.2 mm" >> 80.2 로 변환
float_string = right_answer.strip().replace("mm", "")
right_answer = self.convert_mm_to_pt(float(float_string))
for item in items:
user_answer = float(item)
# JSON 파일 value키값에 mm나 공백이 입력될 경우 제거
# 예) "80.2 mm" >> 80.2 로 변환
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", tolerance=error_range)
if scoring['points'] > 0:
@@ -356,7 +376,14 @@ class XMLScorer:
items2 = root.xpath(xpath2) if xpath2 else []
rgb_text = right_answer
r, g, b = map(int, rgb_text.split(','))
# 정규식을 이용해 숫자만 리스트로 추출
numbers = re.findall(r'\d+', rgb_text)
r, g, b = map(int, numbers) if len(numbers) == 3 else None
# 콤마(,)로 구분된 문자열을 정수형으로 변환
# r, g, b = map(int, rgb_text.split(','))
rgb_int = (b << 16) + (g << 8) + r
# items, items2를 순차적으로 순회
@@ -381,58 +408,48 @@ class XMLScorer:
break
# 폰트명
elif (category or "") == "FontName":
elif (category or "") in ["FontName", "TableFontName"]:
charshape_list = root.xpath(xpath)
if not charshape_list:
user_answer = ""
else:
for charshape_id in charshape_list:
font_id = root.xpath(f"//CHARSHAPE[@Id='{charshape_id}']/FONTID/@Hangul")
font_name = root.xpath(f"//FONTFACE[@Lang='Hangul']/FONT[@Id='{font_id[0]}']/@Name")
user_answer = font_name[0]
# 폰트 "견고딕"과 "중고딕"은
# 한글프로그램 내부적으로 "한양견고딕", "한양중고딕"으로 저장되므로
# 수험자 답변에서 "한양"을 제거
if right_answer in ["견고딕", "중고딕"]:
user_answer = user_answer.replace("한양", "")
self.evaluate_answer(scoring, user_answer, right_answer, points, method="equal")
if scoring['points'] > 0:
break
# 테이블 폰트명
# 테이블 내부 모든 셀의 폰트가 정답과 일치해야 함
elif (category or "") == "TableFontName":
charshape_list = root.xpath(xpath)
# 문자속성이 없는 경우
if not charshape_list:
user_answer = ""
self.evaluate_answer(scoring, user_answer, right_answer, points, method="equal")
else:
all_match = True # 모든 항목이 정답과 일치해야 함
require_all_match = (category == "TableFontName")
any_match = False
all_match = True
for charshape_id in charshape_list:
font_id = root.xpath(f"//CHARSHAPE[@Id='{charshape_id}']/FONTID/@Hangul")
if not font_id:
all_match = False
continue
font_name = root.xpath(f"//FONTFACE[@Lang='Hangul']/FONT[@Id='{font_id[0]}']/@Name")
if not font_name:
all_match = False
continue
user_answer = font_name[0]
# 내부 저장된 접두어 제거
# 접두어 제거
if right_answer in ["견고딕", "중고딕"]:
user_answer = user_answer.replace("한양", "")
# 하나라도 다르면 바로 오답 처리
if user_answer != right_answer:
if user_answer == right_answer:
any_match = True
else:
all_match = False
break
if require_all_match:
break
if all_match:
self.evaluate_answer(scoring, user_answer, right_answer, points, method="equal")
if require_all_match:
score = points if all_match else 0
else:
self.evaluate_answer(scoring, user_answer, right_answer, 0, method="equal") # 오답 처리
score = points if any_match else 0
self.evaluate_answer(scoring, user_answer, right_answer, score, method="equal")
# 폰트 속성
elif (category or "") == "FontAttribute":
@@ -960,8 +977,8 @@ def main():
exam_round = '2504'
exam_types = [
# 'A',
'B',
# 'C',
# 'B',
'C',
]
test_mode = False
# test_mode = True