v2 - 적용완료 / 2504회 A형 테스트중 (B,C형 JSON파일 작성후 테스트필요)
This commit is contained in:
Binary file not shown.
BIN
250516_DIW_2504회_A형_TEST.xlsx
Normal file
BIN
250516_DIW_2504회_A형_TEST.xlsx
Normal file
Binary file not shown.
BIN
250516_DIW_2504회_A형_채점결과.xlsx
Normal file
BIN
250516_DIW_2504회_A형_채점결과.xlsx
Normal file
Binary file not shown.
@@ -87,7 +87,7 @@
|
|||||||
"item": "문구 (클라우드컴퓨팅컨퍼런스)/⑤ 위치 (글자처럼 취급)"
|
"item": "문구 (클라우드컴퓨팅컨퍼런스)/⑤ 위치 (글자처럼 취급)"
|
||||||
},
|
},
|
||||||
"6": {
|
"6": {
|
||||||
"path":"//PARASHAPE[@Id=//P[.//TEXTART[@Text='클라우드컴퓨팅컨퍼런스']]/@ParaShape]/@Align",
|
"path":"//PARASHAPE[@Id=//P[.//TEXTART[@Text='{searchValue}']]/@ParaShape]/@Align",
|
||||||
"searchValue": "클라우드컴퓨팅컨퍼런스",
|
"searchValue": "클라우드컴퓨팅컨퍼런스",
|
||||||
"value": "Center",
|
"value": "Center",
|
||||||
"points": 2,
|
"points": 2,
|
||||||
@@ -313,8 +313,8 @@
|
|||||||
},
|
},
|
||||||
"2": {
|
"2": {
|
||||||
"1": {
|
"1": {
|
||||||
"path": "//SECTION[2]//PAGEBORDERFILL[@Type='Both' or @Type='Even']/@HeaderInside",
|
"path": "//PAGEBORDERFILL[@Type='Both' or @Type='Even']/@HeaderInside",
|
||||||
"path2": "//BORDERFILL[@Id=//SECTION[2]//PAGEBORDERFILL[@Type='Both' or @Type='Even']/@BorferFill]",
|
"path2": "//BORDERFILL[@Id=//PAGEBORDERFILL[@Type='Both' or @Type='Even']/@BorferFill]",
|
||||||
"value": {
|
"value": {
|
||||||
"header_inside": true,
|
"header_inside": true,
|
||||||
"all_double_slim": true
|
"all_double_slim": true
|
||||||
@@ -332,7 +332,7 @@
|
|||||||
"desc": "섹션이 1개 이상이면 점수부여"
|
"desc": "섹션이 1개 이상이면 점수부여"
|
||||||
},
|
},
|
||||||
"3": {
|
"3": {
|
||||||
"path": "//SECTION[2]//COLDEF/@Count",
|
"path": "//COLDEF/@Count",
|
||||||
"value": "2",
|
"value": "2",
|
||||||
"points": 3,
|
"points": 3,
|
||||||
"category": "OneAnswer",
|
"category": "OneAnswer",
|
||||||
@@ -686,14 +686,137 @@
|
|||||||
"desc": "option값에 합계는 SUM / 평균은 AVG"
|
"desc": "option값에 합계는 SUM / 평균은 AVG"
|
||||||
},
|
},
|
||||||
"45": {
|
"45": {
|
||||||
"path": "",
|
"chart_xpath": "",
|
||||||
"chart_type": "묶은세로막대형",
|
"chart_type": "묶은세로막대형",
|
||||||
"value": true,
|
"value": true,
|
||||||
"points": 2,
|
"points": 2,
|
||||||
"category": "chart_type",
|
"category": "ChartType",
|
||||||
"item": "① 종류 (묶은 세로 막대형)",
|
"item": "① 종류 (묶은 세로 막대형)",
|
||||||
"desc": "chart_type을 입력받아 차트타입에 맞는 xml요소가 있는지 내부적으로 검사, chart_type만 한글로 입력해주면 된다."
|
"desc": "chart_type을 입력받아 차트타입에 맞는 xml요소가 있는지 내부적으로 검사, chart_type만 한글로 입력해주면 된다."
|
||||||
},
|
},
|
||||||
"61": {}
|
"46": {
|
||||||
|
"chart_xpath": "//c:valAx/c:majorTickMark/@val",
|
||||||
|
"value": "out",
|
||||||
|
"points": 2,
|
||||||
|
"category": "OneAnswer",
|
||||||
|
"item": "② 값 축 주 눈금선",
|
||||||
|
"desc": "chart xml파일에서 답안을 가져오는 문항은 path키값 대신 chart_xpath키값을 이용해 xapth구문을 작성한다"
|
||||||
|
},
|
||||||
|
"47": {
|
||||||
|
"path": "//OLE[@BinItem=//BINITEM[@Format='OLE']/@BinData]//SIZE/@Width",
|
||||||
|
"value": "80",
|
||||||
|
"points": 2,
|
||||||
|
"category": "mmSize",
|
||||||
|
"item": "③ 크기-너비 (80mm)"
|
||||||
|
},
|
||||||
|
"48": {
|
||||||
|
"path": "//OLE[@BinItem=//BINITEM[@Format='OLE']/@BinData]//SIZE/@Height",
|
||||||
|
"value": "90",
|
||||||
|
"points": 2,
|
||||||
|
"category": "mmSize",
|
||||||
|
"item": "④ 크기-높이 (90mm)"
|
||||||
|
},
|
||||||
|
"49": {
|
||||||
|
"chart_xpath": "boolean(//c:chart and not(//c:pt[not(ancestor::c:tx)]/c:v[text()='합계' or text()='평균']))",
|
||||||
|
"value": true,
|
||||||
|
"points": 2,
|
||||||
|
"category": "Boolean",
|
||||||
|
"item": "⑤ 차트 데이터(표에서 블록계산식을 제외한 나머지 값만 이용)",
|
||||||
|
"desc": "차트가 존재하고 블록계산식(합계, 평균) 데이터가 없는 경우 정답 처리"
|
||||||
|
},
|
||||||
|
"50": {
|
||||||
|
"chart_xpath": "//a:t[text()='{searchValue}']/ancestor::a:r//a:ea/@typeface",
|
||||||
|
"searchValue": "클라우드 보안 투자",
|
||||||
|
"value": "굴림",
|
||||||
|
"points": 1,
|
||||||
|
"category": "OneAnswer",
|
||||||
|
"item": "제목 문구 (클라우드 보안 투자)/① 글씨체 (굴림)"
|
||||||
|
},
|
||||||
|
"51": {
|
||||||
|
"chart_xpath": "//a:t[text()='{searchValue}']/ancestor::a:r/a:rPr/@sz",
|
||||||
|
"searchValue": "클라우드 보안 투자",
|
||||||
|
"value": "1300",
|
||||||
|
"points": 1,
|
||||||
|
"category": "OneAnswer",
|
||||||
|
"item": "제목 문구 (클라우드 보안 투자)/② 크기 (13pt)"
|
||||||
|
},
|
||||||
|
"52": {
|
||||||
|
"chart_xpath": "//a:t[text()='{searchValue}']/ancestor::a:r/a:rPr/@b",
|
||||||
|
"searchValue": "클라우드 보안 투자",
|
||||||
|
"value": "1",
|
||||||
|
"points": 1,
|
||||||
|
"category": "OneAnswer",
|
||||||
|
"item": "제목 문구 (클라우드 보안 투자)/③ 진하게"
|
||||||
|
},
|
||||||
|
"53": {
|
||||||
|
"chart_xpath": "//c:catAx//a:ea/@typeface",
|
||||||
|
"value": "돋움",
|
||||||
|
"points": 1,
|
||||||
|
"category": "OneAnswer",
|
||||||
|
"item": "X축/① 글꼴 (돋움)"
|
||||||
|
},
|
||||||
|
"54": {
|
||||||
|
"chart_xpath": "//c:catAx//a:defRPr/@sz",
|
||||||
|
"value": "900",
|
||||||
|
"points": 1,
|
||||||
|
"category": "OneAnswer",
|
||||||
|
"item": "X축/② 크기 (9pt)"
|
||||||
|
},
|
||||||
|
"55": {
|
||||||
|
"chart_xpath": "//c:catAx//a:defRPr/@{option}",
|
||||||
|
"option": "i",
|
||||||
|
"value": "1",
|
||||||
|
"points": 1,
|
||||||
|
"category": "OneAnswer",
|
||||||
|
"item": "X축/③ 기울임",
|
||||||
|
"desc": "option값 - 기울임(Italic):i / 굵게(Bold):b"
|
||||||
|
},
|
||||||
|
"56": {
|
||||||
|
"chart_xpath": "//c:valAx//a:ea/@typeface",
|
||||||
|
"value": "돋움",
|
||||||
|
"points": 1,
|
||||||
|
"category": "OneAnswer",
|
||||||
|
"item": "Y축/① 글꼴 (돋움)"
|
||||||
|
},
|
||||||
|
"57": {
|
||||||
|
"chart_xpath": "//c:valAx//a:defRPr/@sz",
|
||||||
|
"value": "900",
|
||||||
|
"points": 1,
|
||||||
|
"category": "OneAnswer",
|
||||||
|
"item": "Y축/② 크기 (9pt)"
|
||||||
|
},
|
||||||
|
"58": {
|
||||||
|
"chart_xpath": "//c:valAx//a:defRPr/@{option}",
|
||||||
|
"option": "i",
|
||||||
|
"value": "1",
|
||||||
|
"points": 1,
|
||||||
|
"category": "OneAnswer",
|
||||||
|
"item": "Y축/③ 기울임",
|
||||||
|
"desc": "option값 - 기울임(Italic):i / 굵게(Bold):b"
|
||||||
|
|
||||||
|
},
|
||||||
|
"59": {
|
||||||
|
"chart_xpath": "//c:legend//a:ea/@typeface",
|
||||||
|
"value": "돋움",
|
||||||
|
"points": 1,
|
||||||
|
"category": "OneAnswer",
|
||||||
|
"item": "범례/① 글꼴 (돋움)"
|
||||||
|
},
|
||||||
|
"60": {
|
||||||
|
"chart_xpath": "//c:legend//a:defRPr/@sz",
|
||||||
|
"value": "900",
|
||||||
|
"points": 1,
|
||||||
|
"category": "OneAnswer",
|
||||||
|
"item": "범례/② 크기 (9pt)"
|
||||||
|
},
|
||||||
|
"61": {
|
||||||
|
"chart_xpath": "//c:legend//a:defRPr/@{option}",
|
||||||
|
"option": "i",
|
||||||
|
"value": "1",
|
||||||
|
"points": 1,
|
||||||
|
"category": "OneAnswer",
|
||||||
|
"item": "범례/③ 기울임",
|
||||||
|
"desc": "option값 - 기울임(Italic):i / 굵게(Bold):b"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -615,9 +615,9 @@ def main():
|
|||||||
|
|
||||||
# 250429기준 없는 시험 형식(A,B,C..)은 주석처리 하지 않으면 오류 발생
|
# 250429기준 없는 시험 형식(A,B,C..)은 주석처리 하지 않으면 오류 발생
|
||||||
exam_types = [
|
exam_types = [
|
||||||
# 'A',
|
'A',
|
||||||
# 'B',
|
# 'B',
|
||||||
'C',
|
# 'C',
|
||||||
]
|
]
|
||||||
# test_mode = False
|
# test_mode = False
|
||||||
test_mode = True
|
test_mode = True
|
||||||
|
|||||||
138
diwScoring2.py
138
diwScoring2.py
@@ -42,56 +42,6 @@ class XMLScorer:
|
|||||||
pt = math.trunc(mm * one_mm_per_pt * hwp_internal_conversion_method)
|
pt = math.trunc(mm * one_mm_per_pt * hwp_internal_conversion_method)
|
||||||
return pt
|
return pt
|
||||||
|
|
||||||
# XML 파일에서 element의 값을 찾아 반환
|
|
||||||
def query_xml(self, root, *args):
|
|
||||||
first_xpath = args[0]
|
|
||||||
second_xpath = args[1]
|
|
||||||
points = args[2]
|
|
||||||
category = args[3]
|
|
||||||
|
|
||||||
if ("특수문자" in category) and (second_xpath is not None):
|
|
||||||
try:
|
|
||||||
result = root.xpath(first_xpath)
|
|
||||||
# 결과값이 리스트형인데 내부에 정보가 없는경우
|
|
||||||
# 결과값이 없음
|
|
||||||
if type(result) is list and len(result) == 0:
|
|
||||||
return None
|
|
||||||
elif result < points:
|
|
||||||
result = root.xpath(second_xpath)
|
|
||||||
return result
|
|
||||||
else:
|
|
||||||
return result
|
|
||||||
|
|
||||||
except ET.XPathEvalError as e:
|
|
||||||
return None
|
|
||||||
|
|
||||||
elif second_xpath is not None:
|
|
||||||
try:
|
|
||||||
result1 = root.xpath(first_xpath)
|
|
||||||
result2 = root.xpath(second_xpath)
|
|
||||||
if (type(result1) is list and len(result1) == 0) and (type(result2) is list and len(result2) == 0):
|
|
||||||
return None
|
|
||||||
return result1 if result1 else result2
|
|
||||||
|
|
||||||
except ET.XPathEvalError as e:
|
|
||||||
return None
|
|
||||||
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
result = root.xpath(first_xpath)
|
|
||||||
if type(result) is list and len(result) == 0:
|
|
||||||
return None
|
|
||||||
return result
|
|
||||||
except ET.XPathEvalError as e:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def chart_query_xml(self, tree, xpath, namespaces):
|
|
||||||
result = tree.xpath(xpath, namespaces=namespaces)
|
|
||||||
if type(result) is list and len(result) == 0:
|
|
||||||
return None
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
# 유사한 텍스트 찾기
|
# 유사한 텍스트 찾기
|
||||||
def find_similar_text(self, root, target_text, threshold=0.7):
|
def find_similar_text(self, root, target_text, threshold=0.7):
|
||||||
"""
|
"""
|
||||||
@@ -199,6 +149,7 @@ class XMLScorer:
|
|||||||
xpath = criterion.get('path', None)
|
xpath = criterion.get('path', None)
|
||||||
xpath2 = criterion.get('path2', None)
|
xpath2 = criterion.get('path2', None)
|
||||||
xpath3 = criterion.get('path3', None)
|
xpath3 = criterion.get('path3', None)
|
||||||
|
chart_xpath = criterion.get('chart_xpath', None)
|
||||||
search_value = criterion.get('searchValue', None)
|
search_value = criterion.get('searchValue', None)
|
||||||
right_answer = criterion.get('value', None)
|
right_answer = criterion.get('value', None)
|
||||||
points = criterion.get('points', 0)
|
points = criterion.get('points', 0)
|
||||||
@@ -211,13 +162,14 @@ class XMLScorer:
|
|||||||
if search_value is not None:
|
if search_value is not None:
|
||||||
# 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) if xpath else ""
|
||||||
if xpath2 is not None:
|
xpath2 = xpath2.replace('{searchValue}', similar_text) if xpath2 else ""
|
||||||
xpath2 = xpath2.replace('{searchValue}', similar_text)
|
chart_xpath = chart_xpath.replace('{searchValue}', similar_text) if chart_xpath else ""
|
||||||
|
|
||||||
if option:
|
if option:
|
||||||
xpath = xpath.replace('{option}', option) if xpath else ""
|
xpath = xpath.replace('{option}', option) if xpath else ""
|
||||||
xpath2 = xpath2.replace('{option}', option) if xpath2 else ""
|
xpath2 = xpath2.replace('{option}', option) if xpath2 else ""
|
||||||
|
chart_xpath = chart_xpath.replace('{option}', option) if chart_xpath else ""
|
||||||
|
|
||||||
# 문항 별 채점 결과 저장
|
# 문항 별 채점 결과 저장
|
||||||
scoring = {
|
scoring = {
|
||||||
@@ -269,10 +221,12 @@ class XMLScorer:
|
|||||||
# 수험자 답안
|
# 수험자 답안
|
||||||
user_answer = {
|
user_answer = {
|
||||||
########################################################################
|
########################################################################
|
||||||
# 보통 정답은 글꼴이 [바탕]이어야 하지만 대부분 설정하지 않고
|
# 정답은 글꼴이 [바탕]이어야 하지만 대부분 설정하지 않고
|
||||||
# 기본 [함초롱바탕]으로 두는 경우가 많아
|
# 기본 [함초롱바탕]으로 두는 경우가 많아
|
||||||
# 폰트명을 정답과 비교하면 감점이 많이 발생해서
|
# 폰트명을 정답과 비교하면 감점이 많이 발생함
|
||||||
# 폰트명은 정답과 비교하지 않게 적용된 상태
|
#
|
||||||
|
# 수험자가 지정한 폰트명과 정답을 비교할때 'FontName': font_name[0], 적용
|
||||||
|
# 폰트명은 상관없이 정답을 비교하고자 할때 'FontName': "바탕", 적용
|
||||||
########################################################################
|
########################################################################
|
||||||
# 'FontName': font_name[0],
|
# 'FontName': font_name[0],
|
||||||
'FontName': "바탕",
|
'FontName': "바탕",
|
||||||
@@ -292,10 +246,11 @@ class XMLScorer:
|
|||||||
|
|
||||||
# 정답이 하나인 경우
|
# 정답이 하나인 경우
|
||||||
elif (category or "") == "OneAnswer":
|
elif (category or "") == "OneAnswer":
|
||||||
items = root.xpath(xpath)
|
items = root.xpath(xpath) if xpath else []
|
||||||
items2 = root.xpath(xpath2) if xpath2 else []
|
items2 = root.xpath(xpath2) if xpath2 else []
|
||||||
|
chart_items = chart_tree.xpath(chart_xpath, namespaces=namespaces) if chart_xpath else []
|
||||||
|
|
||||||
for item in chain(items, items2):
|
for item in chain(items, items2, chart_items):
|
||||||
user_answer = item
|
user_answer = item
|
||||||
|
|
||||||
self.evaluate_answer(scoring, user_answer, right_answer, points)
|
self.evaluate_answer(scoring, user_answer, right_answer, points)
|
||||||
@@ -304,7 +259,7 @@ class XMLScorer:
|
|||||||
break
|
break
|
||||||
|
|
||||||
elif (category or "") == "DoubleAnswer":
|
elif (category or "") == "DoubleAnswer":
|
||||||
items1 = root.xpath(xpath)
|
items1 = root.xpath(xpath) if xpath else []
|
||||||
items2 = root.xpath(xpath2) if xpath else []
|
items2 = root.xpath(xpath2) if xpath else []
|
||||||
|
|
||||||
user_answer = []
|
user_answer = []
|
||||||
@@ -323,7 +278,11 @@ class XMLScorer:
|
|||||||
|
|
||||||
for item in items:
|
for item in items:
|
||||||
user_answer = float(item)
|
user_answer = float(item)
|
||||||
right_answer = self.convert_mm_to_pt(float(right_answer))
|
|
||||||
|
# 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")
|
self.evaluate_answer(scoring, user_answer, right_answer, points, method="tolerance")
|
||||||
|
|
||||||
@@ -347,10 +306,11 @@ class XMLScorer:
|
|||||||
|
|
||||||
# Boolean 타입 정답인 경우
|
# Boolean 타입 정답인 경우
|
||||||
elif (category or "") == "Boolean":
|
elif (category or "") == "Boolean":
|
||||||
items = root.xpath(xpath)
|
items = root.xpath(xpath) if xpath else False
|
||||||
items2 = root.xpath(xpath2) if xpath2 else False
|
items2 = root.xpath(xpath2) if xpath2 else False
|
||||||
|
chart_items = chart_tree.xpath(chart_xpath, namespaces=namespaces) if chart_xpath else False
|
||||||
|
|
||||||
user_answer = bool( items or items2 )
|
user_answer = bool( items or items2 or chart_items )
|
||||||
|
|
||||||
self.evaluate_answer(scoring, user_answer, right_answer, points)
|
self.evaluate_answer(scoring, user_answer, right_answer, points)
|
||||||
|
|
||||||
@@ -453,7 +413,7 @@ class XMLScorer:
|
|||||||
self.evaluate_answer(scoring, user_answer, right_answer, points, method="equal")
|
self.evaluate_answer(scoring, user_answer, right_answer, points, method="equal")
|
||||||
|
|
||||||
# 특수문자 갯수 채점
|
# 특수문자 갯수 채점
|
||||||
elif "SpecialChar" in (category or ""):
|
elif (category or "") == "SpecialChar":
|
||||||
ch1 = criterion.get('char1', None)
|
ch1 = criterion.get('char1', None)
|
||||||
ch2 = criterion.get('char2', None)
|
ch2 = criterion.get('char2', None)
|
||||||
ch3 = criterion.get('char3', None)
|
ch3 = criterion.get('char3', None)
|
||||||
@@ -493,7 +453,7 @@ 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 (category or "") == "PageBorder":
|
||||||
user_answer = {
|
user_answer = {
|
||||||
"header_inside": False,
|
"header_inside": False,
|
||||||
"all_double_slim": False
|
"all_double_slim": False
|
||||||
@@ -505,33 +465,33 @@ class XMLScorer:
|
|||||||
# print("머릿말포함: ",header_inside)
|
# print("머릿말포함: ",header_inside)
|
||||||
if "true" in header_inside:
|
if "true" in header_inside:
|
||||||
user_answer["header_inside"] = True
|
user_answer["header_inside"] = True
|
||||||
|
break
|
||||||
|
|
||||||
# BORDERFILL요소의 자녀
|
# BORDERFILL요소의 자녀
|
||||||
# LEFTBORDER, RIGHTBORDER, TOPBORDER, BOTTOMBORDER 요소의 Type속성이
|
# LEFTBORDER, RIGHTBORDER, TOPBORDER, BOTTOMBORDER 요소의 Type속성이
|
||||||
# 모두 DoubleSlim이면 정답
|
# 모두 DoubleSlim이면 정답
|
||||||
border_fill_elements = root.xpath(xpath2)
|
|
||||||
for bf in border_fill_elements:
|
|
||||||
border_tags = ["LEFTBORDER", "RIGHTBORDER", "TOPBORDER", "BOTTOMBORDER"]
|
border_tags = ["LEFTBORDER", "RIGHTBORDER", "TOPBORDER", "BOTTOMBORDER"]
|
||||||
|
|
||||||
|
borderfill_elements = root.xpath(xpath2)
|
||||||
|
for borderfill in borderfill_elements:
|
||||||
all_double_slim = True
|
all_double_slim = True
|
||||||
|
|
||||||
for tag in border_tags:
|
for tag in border_tags:
|
||||||
element = bf.find(tag)
|
element = borderfill.find(tag)
|
||||||
if element is None or element.get("Type") != "DoubleSlim":
|
|
||||||
|
if (element is None) or (element.get("Type") != "DoubleSlim"):
|
||||||
all_double_slim = False
|
all_double_slim = False
|
||||||
break
|
break
|
||||||
|
|
||||||
#모든 BORDER 태그의 Type 속성이 'DoubleSlim'
|
#모든 BORDER 태그의 Type 속성이 'DoubleSlim'인 객체가 있다면 반복문 탈출
|
||||||
if all_double_slim:
|
if all_double_slim:
|
||||||
user_answer["all_double_slim"] = True
|
user_answer["all_double_slim"] = True
|
||||||
|
break
|
||||||
# 하나 이상의 BORDER 태그가 'DoubleSlim'이 아님
|
|
||||||
else:
|
|
||||||
user_answer["all_double_slim"] = False
|
|
||||||
|
|
||||||
self.evaluate_answer(scoring, user_answer, right_answer, points, method="equal")
|
self.evaluate_answer(scoring, user_answer, right_answer, points, method="equal")
|
||||||
|
|
||||||
# 한자
|
# 한자
|
||||||
elif "Hanja" in (category or ""):
|
elif (category or "") == "Hanja":
|
||||||
word_list = criterion.get('word', [])
|
word_list = criterion.get('word', [])
|
||||||
|
|
||||||
# 점수 계산
|
# 점수 계산
|
||||||
@@ -557,7 +517,7 @@ 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 (category or "") == "chart_type":
|
elif (category or "") == "ChartType":
|
||||||
chart_type_list = {
|
chart_type_list = {
|
||||||
'꺾은선형': "//c:lineChart",
|
'꺾은선형': "//c:lineChart",
|
||||||
'가로막대형': "//c:barChart[c:barDir[@val='bar']]",
|
'가로막대형': "//c:barChart[c:barDir[@val='bar']]",
|
||||||
@@ -627,7 +587,7 @@ class XMLScorer:
|
|||||||
|
|
||||||
return xml_data
|
return xml_data
|
||||||
|
|
||||||
def typo_check(self, correct_answer_file, user_answer_file):
|
def typo_check(self, correct_answer_file, user_answer_file, chart_xml):
|
||||||
user_answer_tree = ET.parse(user_answer_file)
|
user_answer_tree = ET.parse(user_answer_file)
|
||||||
user_answer_root = user_answer_tree.getroot()
|
user_answer_root = user_answer_tree.getroot()
|
||||||
correct_answer_tree = ET.parse(correct_answer_file)
|
correct_answer_tree = ET.parse(correct_answer_file)
|
||||||
@@ -642,6 +602,21 @@ class XMLScorer:
|
|||||||
correct_table_text = correct_answer_root.xpath('//TABLE//CHAR//text()')
|
correct_table_text = correct_answer_root.xpath('//TABLE//CHAR//text()')
|
||||||
correct_input_text += correct_table_text
|
correct_input_text += correct_table_text
|
||||||
|
|
||||||
|
# 차트 XML에서 제목 추출
|
||||||
|
if chart_xml is not None:
|
||||||
|
chart_xml_tree = ET.fromstring(chart_xml)
|
||||||
|
|
||||||
|
# 차트 제목 추출
|
||||||
|
user_chart_title = chart_xml_tree.xpath('/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p/a:r/a:t', namespaces={'c': 'http://schemas.openxmlformats.org/drawingml/2006/chart', 'a': 'http://schemas.openxmlformats.org/drawingml/2006/main'})
|
||||||
|
|
||||||
|
# 차트 제목이 존재하는 경우
|
||||||
|
if user_chart_title:
|
||||||
|
user_input_text.append(user_chart_title[0].text)
|
||||||
|
|
||||||
|
# 차트 제목 정답 텍스트 추출
|
||||||
|
correct_chart_title = self.scoring_criteria["2"]["50"]["searchValue"]
|
||||||
|
correct_input_text.append(correct_chart_title)
|
||||||
|
|
||||||
# 각 요소에서 공백 제거
|
# 각 요소에서 공백 제거
|
||||||
user_input_text = [text.replace(' ', '') for text in user_input_text]
|
user_input_text = [text.replace(' ', '') for text in user_input_text]
|
||||||
correct_input_text = [text.replace(' ', '') for text in correct_input_text]
|
correct_input_text = [text.replace(' ', '') for text in correct_input_text]
|
||||||
@@ -652,8 +627,9 @@ class XMLScorer:
|
|||||||
correct_input_text = [re.sub(r'\d+\.\s*|-', '', text) for text in correct_input_text]
|
correct_input_text = [re.sub(r'\d+\.\s*|-', '', text) for text in correct_input_text]
|
||||||
|
|
||||||
try :
|
try :
|
||||||
xpath = self.scoring_criteria["2-29"]['path'].split("'")[1]
|
# xpath = self.scoring_criteria["2"]["29"]['path'].split("'")[1]
|
||||||
ignore_word = xpath.split("'")[1]
|
# ignore_word = xpath.split("'")[1]
|
||||||
|
ignore_word = self.scoring_criteria["2"]["29"]["ignoreWord"]
|
||||||
# 특정 단어 제거
|
# 특정 단어 제거
|
||||||
# 오타와 누락의 경우만 판단하면 정상작동하지만
|
# 오타와 누락의 경우만 판단하면 정상작동하지만
|
||||||
# 추가 된 단어의 경우를 채점기준에 추가하면 정확하게 채점 되지 않을 수 있음
|
# 추가 된 단어의 경우를 채점기준에 추가하면 정확하게 채점 되지 않을 수 있음
|
||||||
@@ -746,7 +722,7 @@ class XMLScorer:
|
|||||||
for user_answer_file in xml_files:
|
for user_answer_file in xml_files:
|
||||||
score_result = {}
|
score_result = {}
|
||||||
chart_xml = self.binary_to_chartxml(user_answer_file)
|
chart_xml = self.binary_to_chartxml(user_answer_file)
|
||||||
score_result['typo'] = self.typo_check(correct_answer_file, user_answer_file)
|
score_result['typo'] = self.typo_check(correct_answer_file, user_answer_file, chart_xml)
|
||||||
score_result['score'] = self._score_xml_file(user_answer_file, chart_xml)
|
score_result['score'] = self._score_xml_file(user_answer_file, chart_xml)
|
||||||
# score_result['score']['score_results'][2]['points'] = score_result['typo'][0]
|
# score_result['score']['score_results'][2]['points'] = score_result['typo'][0]
|
||||||
score_results.append(score_result)
|
score_results.append(score_result)
|
||||||
@@ -869,8 +845,8 @@ def main():
|
|||||||
# 'B',
|
# 'B',
|
||||||
# 'C',
|
# 'C',
|
||||||
]
|
]
|
||||||
# test_mode = False
|
test_mode = False
|
||||||
test_mode = True
|
# test_mode = True
|
||||||
|
|
||||||
output_excel_paths = []
|
output_excel_paths = []
|
||||||
for exam_type in exam_types:
|
for exam_type in exam_types:
|
||||||
|
|||||||
25
filtered_score_diff.py
Normal file
25
filtered_score_diff.py
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
# 엑셀 파일 경로
|
||||||
|
file_path = "./diff.xlsx"
|
||||||
|
|
||||||
|
# 엑셀 파일 읽기
|
||||||
|
df = pd.read_excel(file_path)
|
||||||
|
|
||||||
|
# 데이터 미리 보기
|
||||||
|
df.head()
|
||||||
|
|
||||||
|
# '총점' 컬럼 기준으로 점수가 다른 경우만 남기기
|
||||||
|
filtered_df = df.groupby("Unnamed: 0").filter(lambda x: x["총점"].nunique() > 1)
|
||||||
|
|
||||||
|
# 결과 확인
|
||||||
|
filtered_df.head()
|
||||||
|
|
||||||
|
# 점수가 다른 경우만 남기는 필터링
|
||||||
|
filtered_df = df.groupby("Unnamed: 0").filter(lambda x: x["총점"].nunique() > 1)
|
||||||
|
|
||||||
|
# 결과 저장
|
||||||
|
output_path = "./filtered_score_diff.xlsx"
|
||||||
|
filtered_df.to_excel(output_path, index=False)
|
||||||
|
|
||||||
|
output_path
|
||||||
BIN
filtered_score_diff.xlsx
Normal file
BIN
filtered_score_diff.xlsx
Normal file
Binary file not shown.
@@ -1 +1 @@
|
|||||||
[{"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":"//RECTANGLE[.//CHAR[text()='클라우드 컴퓨팅']]/SHAPEOBJECT/POSITION/@TreatAsChar"},{"kind":2,"language":"xpath","value":"//PARASHAPE[@Id=//RECTANGLE//CHAR[text()='{searchValue}']/ancestor::P[last()]/@ParaShape]/@Align"},{"kind":2,"language":"xpath","value":"//CHARSHAPE[@Id=//RECTANGLE//CHAR[text()='클라우드 컴퓨팅']/parent::TEXT/@CharShape]/@Height"},{"kind":2,"language":"xpath","value":"//PARASHAPE[@Id=//RECTANGLE//P[.//CHAR[text()='{searchValue}']]/@ParaShape]/@Align"},{"kind":2,"language":"xpath","value":"//IMAGE[@BinItem=//BINITEM[@Format='JPG']/@BinData]/preceding-sibling::SHAPEOBJECT/SIZE/@Width"},{"kind":2,"language":"xpath","value":"//CHAR[text()='{searchValue}']/parent::TEXT/@CharShape"},{"kind":2,"language":"xpath","value":"//TEXT[CHAR[text()='{searchValue}']]/@CharShape"},{"kind":2,"language":"xpath","value":"//PARASHAPE[@Id=//P[.//TEXTART[@Text='클라우드컴퓨팅컨퍼런스']]/@ParaShape]/@Align"},{"kind":2,"language":"xpath","value":"//CHARSHAPE[@Id=//TEXT[CHAR[contains(text(),'클라우드 보안(단위: 백만 달러)')]]/@CharShape]/@Height"},{"kind":2,"language":"xpath","value":"//TEXT[CHAR[contains(text(),'클라우드')]]/FOOTNOTE[CHAR[contains(text(),'인터넷을 통해 액세스할 수 있는 가상화된 서버에서 실행되는 프로그램과 데이터베이스를 제공하는 환경')]]"},{"kind":2,"language":"xpath","value":"//TEXT[CHAR[contains(text(),'클라우드')]]/FOOTNOTE[.//CHAR[contains(text(),'인터넷을 통해 액세스할 수 있는 가상화된 서버에서 실행되는 프로그램과 데이터베이스를 제공하는 환경')]]"},{"kind":2,"language":"xpath","value":"//TEXT[CHAR[contains(text(),'클라우드')]]/FOOTNOTE"},{"kind":2,"language":"xpath","value":"//CHARSHAPE[@Id=//TEXT[CHAR[text()='클라우드 보안(단위: 백만 달러)']]/@CharShape]"},{"kind":2,"language":"xpath","value":"//PARASHAPE[@Id=//P[.//CHAR[text()='클라우드 보안(단위: 백만 달러)']]/@ParaShape]/@Align"},{"kind":2,"language":"xpath","value":"//PARASHAPE[@Id=//CHAR[contains(text(),'클라우드 보안(단위: 백만 달러)')]/ancestor::P/@ParaShape]/@Align"},{"kind":2,"language":"xpath","value":"//BORDERFILL[@Id=//CELLZONE[@StartRowAddr='0' and @EndRowAddr='0' and @StartColAddr='0' and @EndColAddr=(ancestor::TABLE[1]/@ColCount)-1]/@BorderFill]/FILLBRUSH/WINDOWBRUSH/@FaceColor"},{"kind":2,"language":"xpath","value":"//BORDERFILL[@Id=//CELLZONE[@EndColAddr=../../@ColCount]]"},{"kind":2,"language":"xpath","value":"//CHARSHAPE[@Id=//TABLE//TEXT/@CharShape]/@Height"},{"kind":2,"language":"xpath","value":"//PARASHAPE[@Id=//TABLE/ROW//P/@ParaShape]/@Align"},{"kind":2,"language":"xpath","value":"//TABLE[1]/ROW[last()]/CELL[last()-1]//FIELDBEGIN[starts-with(@Command, '=SUM')]"},{"kind":2,"language":"xpath","value":"//TABLE[1]/ROW[last()]/CELL[position() = last() or position() = last() - 1]//FIELDBEGIN[starts-with(@Command, '=SUM')]\r\n"}]
|
[{"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":"//RECTANGLE[.//CHAR[text()='클라우드 컴퓨팅']]/SHAPEOBJECT/POSITION/@TreatAsChar"},{"kind":2,"language":"xpath","value":"//PARASHAPE[@Id=//RECTANGLE//CHAR[text()='{searchValue}']/ancestor::P[last()]/@ParaShape]/@Align"},{"kind":2,"language":"xpath","value":"//CHARSHAPE[@Id=//RECTANGLE//CHAR[text()='클라우드 컴퓨팅']/parent::TEXT/@CharShape]/@Height"},{"kind":2,"language":"xpath","value":"//PARASHAPE[@Id=//RECTANGLE//P[.//CHAR[text()='{searchValue}']]/@ParaShape]/@Align"},{"kind":2,"language":"xpath","value":"//IMAGE[@BinItem=//BINITEM[@Format='JPG']/@BinData]/preceding-sibling::SHAPEOBJECT/SIZE/@Width"},{"kind":2,"language":"xpath","value":"//CHAR[text()='{searchValue}']/parent::TEXT/@CharShape"},{"kind":2,"language":"xpath","value":"//TEXT[CHAR[text()='{searchValue}']]/@CharShape"},{"kind":2,"language":"xpath","value":"//PARASHAPE[@Id=//P[.//TEXTART[@Text='클라우드컴퓨팅컨퍼런스']]/@ParaShape]/@Align"},{"kind":2,"language":"xpath","value":"//CHARSHAPE[@Id=//TEXT[CHAR[contains(text(),'클라우드 보안(단위: 백만 달러)')]]/@CharShape]/@Height"},{"kind":2,"language":"xpath","value":"//TEXT[CHAR[contains(text(),'클라우드')]]/FOOTNOTE[CHAR[contains(text(),'인터넷을 통해 액세스할 수 있는 가상화된 서버에서 실행되는 프로그램과 데이터베이스를 제공하는 환경')]]"},{"kind":2,"language":"xpath","value":"//TEXT[CHAR[contains(text(),'클라우드')]]/FOOTNOTE[.//CHAR[contains(text(),'인터넷을 통해 액세스할 수 있는 가상화된 서버에서 실행되는 프로그램과 데이터베이스를 제공하는 환경')]]"},{"kind":2,"language":"xpath","value":"//TEXT[CHAR[contains(text(),'클라우드')]]/FOOTNOTE"},{"kind":2,"language":"xpath","value":"//CHARSHAPE[@Id=//TEXT[CHAR[text()='클라우드 보안(단위: 백만 달러)']]/@CharShape]"},{"kind":2,"language":"xpath","value":"//PARASHAPE[@Id=//P[.//CHAR[text()='클라우드 보안(단위: 백만 달러)']]/@ParaShape]/@Align"},{"kind":2,"language":"xpath","value":"//PARASHAPE[@Id=//CHAR[contains(text(),'클라우드 보안(단위: 백만 달러)')]/ancestor::P/@ParaShape]/@Align"},{"kind":2,"language":"xpath","value":"//BORDERFILL[@Id=//CELLZONE[@StartRowAddr='0' and @EndRowAddr='0' and @StartColAddr='0' and @EndColAddr=(ancestor::TABLE[1]/@ColCount)-1]/@BorderFill]/FILLBRUSH/WINDOWBRUSH/@FaceColor"},{"kind":2,"language":"xpath","value":"//BORDERFILL[@Id=//CELLZONE[@EndColAddr=../../@ColCount]]"},{"kind":2,"language":"xpath","value":"//CHARSHAPE[@Id=//TABLE//TEXT/@CharShape]/@Height"},{"kind":2,"language":"xpath","value":"//PARASHAPE[@Id=//TABLE/ROW//P/@ParaShape]/@Align"},{"kind":2,"language":"xpath","value":"//TABLE[1]/ROW[last()]/CELL[last()-1]//FIELDBEGIN[starts-with(@Command, '=SUM')]"},{"kind":2,"language":"xpath","value":"//c:chart and not(//c:pt[not(ancestor::c:tx)]/c:v[text()='합계' or text()='평균'])"},{"kind":2,"language":"xpath","value":"//BORDERFILL[@Id=//PAGEBORDERFILL[@Type='Both' or @Type='Even']/@BorferFill]"},{"kind":2,"language":"xpath","value":"//PAGEBORDERFILL[@Type='Both' or @Type='Even']/@HeaderInside"}]
|
||||||
BIN
구버전신버전점수비교A형.xlsx
Normal file
BIN
구버전신버전점수비교A형.xlsx
Normal file
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user