파일 삭제 및 새로운 채점 기준 추가

This commit is contained in:
devdra9
2025-01-16 18:34:07 +09:00
parent ba29501ed6
commit 894d6be884
75 changed files with 1647 additions and 3362 deletions

110
score5.py
View File

@@ -1,13 +1,15 @@
from datetime import datetime
import json
import glob
from pathlib import Path
import os
from lxml import etree as ET
import re
from difflib import SequenceMatcher
import pandas as pd
# from xpathSearch import XMLPathHandler
from binaryToChartxml import binaryToChartxml
class XMLScorer:
# 채점 기준 경로 초기화
@@ -21,21 +23,38 @@ class XMLScorer:
return json.load(f)
# XML 파일에서 element의 값을 찾아 반환
def query_xml(self, root, query):
try:
result = root.xpath(query)
if type(result) is list and len(result) == 0:
def query_xml(self, root, *args):
points = args[2]
if args[1] is not None:
try:
result = root.xpath(args[0])
if type(result) is list and len(result) == 0:
return None
elif result < points:
result = root.xpath(args[1])
return result
else:
return result
# result = root.xpath(args[1])
# print(f'result : {result}')
# return result
except ET.XPathEvalError as e:
return None
else:
try:
result = root.xpath(args[0])
if type(result) is list and len(result) == 0:
return None
return result
except ET.XPathEvalError as e:
return None
return result
except ET.XPathEvalError as e:
return None
# 유사한 텍스트 찾기
def find_similar_text(self, root, target_text, threshold=0.3):
def find_similar_text(self, root, target_text, threshold=0.5):
"""
전체 문서에서 유사한 텍스트를 찾아 반환
Args:
root (_type_): xml root element 객체
target_text (_type_): 찾을 텍스트
@@ -47,6 +66,7 @@ class XMLScorer:
# 전체 텍스트 추출
# all_text = root.xpath(f"//CHAR/text()")
# all_text.append(root.xpath(f"//TEXTART/@text"))
all_text = root.xpath(f"//CHAR/text() | //TEXTART/@Text")
# 유사도 비교
@@ -64,7 +84,7 @@ class XMLScorer:
return similar_text
else:
return None
# 하나의 XML 파일 채점
def _score_xml_file(self, xml_path):
try:
@@ -84,24 +104,24 @@ class XMLScorer:
for criterion_id, criterion in self.scoring_criteria.items():
xpath = criterion['path']
xpath2 = criterion['path2']
search_value = criterion['searchValue']
right_answer = criterion['value']
points = criterion['points']
category = criterion['category']
item = criterion['item']
simliar_text = None
# searchValue가 있을 경우 유사한 텍스트 찾기
if search_value is not None:
simliar_text = self.find_similar_text(root, search_value)
if simliar_text is None:
xpath = xpath.replace('{searchValue}', '')
xpath = xpath.replace('{searchValue}', search_value)
else:
xpath = xpath.replace('{searchValue}', simliar_text)
# xpath로 실제 작성 답안 찾기
result = self.query_xml(root, xpath)
result = self.query_xml(root, xpath, xpath2, points)
# [ boolean 타입 ]
# 1. 이텔릭체, 굵게, 밑줄 등 효과가 적용 여부에 따라
@@ -112,18 +132,16 @@ class XMLScorer:
# [ float 타입 ]
# 1. 부분점수의 합산으로 반환되는 경우 float 타입으로 반환
if type(result) is not list:
actual_answer = result
# 표 같이 여러 조건을 동시에 검사 해야하는 경우우
# elif type(result) is list and len(result) > 1:
# xpath2 = criterion['path2']
# for i in result:
# xpath2 = xpath2.replace('{path_result_list}', str(i))
# print(f"xpath2: {xpath2}")
if type(result) is float and (result > points):
actual_answer = float(points)
else:
actual_answer = result
else:
actual_answer = result[0]
if type(right_answer) is int:
actual_answer = int(result[0])
else:
actual_answer = result[0]
scoring = {
'category': category, # 채점 분류
'item': item, # 채점 항목
@@ -132,26 +150,31 @@ class XMLScorer:
'points': 0,
'deductions': [] # 각 기준별 감점 내역
}
scoring['points'] = points
# 점수 차감 조건
# 1. 정답이 실수형으로 반환받은 경우는 채점항목의 부분점수 합산 결과이므로
# 반환받은 값 그대로를 점수로 사용
# 2. 그 외의 경우 정답과 실제 작성 답안이 다른 경우 점수 차감
# 2. 정답이 정수형(사이즈 비교)의 경우 오차범위를 넘는다면 감점
# 3. 그 외의 경우 정답과 실제 작성 답안이 다른 경우 점수 차감
if type(actual_answer) is float:
scoring['points'] = actual_answer
scoring['points'] = actual_answer
elif type(actual_answer) is int:
# 오차범위 5 이상이면 감점
if abs(actual_answer - right_answer) > 5:
scoring['points'] -= points
else:
# right_answer(JSON파일 내 valuer값) null일 경우 점수감점 없이 진행
if right_answer != actual_answer:
scoring['points'] -= points
# 점수 차감 이유 작성 (개발중)
results['score_results'].append(scoring)
total_score += scoring['points']
if scoring['points'] > 0:
print(f'scoring: {scoring}')
print(f'scoring: {scoring}')
results['total_score'] = total_score
return results
@@ -162,6 +185,7 @@ class XMLScorer:
'error': f"XML 파싱 오류: {str(e)}",
'total_score': 0
}
# def binary_to_chartxml(self, xml_path):
# XML 파일 채점
def score_directory(self, xml_directory):
@@ -173,11 +197,21 @@ class XMLScorer:
results = []
for xml_file in xml_files:
self.binary_to_chartxml(xml_file)
result = self._score_xml_file(xml_file)
results.append(result)
results.append(result)
return results
def parse_filename(self, filename):
if isinstance(filename, dict):
filename = filename.get('파일명', '')
match = re.match(r'.*-(\d+)-(.+)\.hml', filename)
if match:
number = match.group(1)
name = match.group(2)
return number, name
return None, None
def export_to_excel(self, results, output_path=None):
if output_path is None:
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
@@ -185,7 +219,6 @@ class XMLScorer:
summary_data = []
detail_data = []
header_added = False
for result in results:
# 요약 정보
@@ -200,7 +233,10 @@ class XMLScorer:
# 상세 정보
if 'score_results' in result:
detail_row = {'파일명': result['filename']}
filename = {'파일명': result['filename']}
number, name = self.parse_filename(filename)
detail_row = {'수험자':f"{number}-{name}"}
for i, scoring in enumerate(result['score_results']):
detail_row[f'점수_{i+1}'] = scoring['points']
@@ -208,7 +244,7 @@ class XMLScorer:
detail_data.append(detail_row)
summary_df = pd.DataFrame(summary_data)
detail_df = pd.DataFrame(detail_data)
detail_df = pd.DataFrame(detail_data).transpose()
# detail_df = pd.DataFrame(detail_data)
# ExcelWriter 객체 생성