ent파일에서 project.json추출 자동화
This commit is contained in:
4
.bashrc.local
Normal file
4
.bashrc.local
Normal file
@@ -0,0 +1,4 @@
|
||||
# 프로젝트 루트 폴더/.bashrc.local
|
||||
if [ -f .venv/Scripts/activate ]; then
|
||||
source .venv/Scripts/activate
|
||||
fi
|
||||
16
.gitignore
vendored
16
.gitignore
vendored
@@ -1,4 +1,20 @@
|
||||
.venv/
|
||||
|
||||
output/
|
||||
sample/
|
||||
|
||||
.DS_Store
|
||||
|
||||
# 시험자료와 그 하위 회차 폴더는 포함
|
||||
!시험자료/
|
||||
!시험자료/*/
|
||||
# 기본적으로 모든 것을 무시
|
||||
시험자료/*/*
|
||||
# 하지만 회차 폴더 안의 파일은 다시 포함
|
||||
!시험자료/*/*.*
|
||||
# 모든 회차 폴더 안의 하위 폴더는 무시
|
||||
시험자료/*/*/
|
||||
|
||||
# ent파일
|
||||
ent/
|
||||
|
||||
|
||||
@@ -34,16 +34,18 @@ def copy_ent_files(source_root, target_root):
|
||||
for root, dirs, files in os.walk(source_root):
|
||||
for file in files:
|
||||
if file.endswith('.ent'):
|
||||
space_remove_file = file.replace(" ","")
|
||||
|
||||
source_file_path = os.path.join(root, file)
|
||||
target_file_path = os.path.join(target_root, file)
|
||||
target_file_path = os.path.join(target_root, space_remove_file)
|
||||
|
||||
# 파일 복사
|
||||
shutil.copy2(source_file_path, target_file_path)
|
||||
print(f"Copied {source_file_path} to {target_file_path}")
|
||||
|
||||
# 사용법
|
||||
source_directory = r"/Users/waterdrw/Downloads/제2505회 코딩활용능력 3급 답안파일" # 원본 디렉토리 경로
|
||||
target_directory = r"./output/2505CAT3A"
|
||||
source_directory = r"D:\project\data\CAS_제2506회 정기\답안파일\제2506회 코딩활용능력 2급 답안파일" # 원본 디렉토리 경로
|
||||
target_directory = r"./ent/2506_CAS_2_A"
|
||||
target_directory_a = r"./output/A" # '1교시'의 타겟 경로
|
||||
target_directory_b = r"./output/B" # '2교시'의 타겟 경로
|
||||
target_directory_c = r"./output/C" # '3교시'의 타겟 경로
|
||||
56
02_extract_project_json.py
Normal file
56
02_extract_project_json.py
Normal file
@@ -0,0 +1,56 @@
|
||||
import os
|
||||
import gzip
|
||||
import tarfile
|
||||
import json
|
||||
from io import BytesIO
|
||||
|
||||
def extract_project_json_from_gz(filepath, output_folder):
|
||||
# 1. .gz 압축 해제 → tar 형식 데이터 얻기
|
||||
with gzip.open(filepath, 'rb') as gz_file:
|
||||
tar_data = gz_file.read()
|
||||
|
||||
# 2. tar 데이터를 메모리에서 처리
|
||||
tar_bytes = BytesIO(tar_data)
|
||||
|
||||
with tarfile.open(fileobj=tar_bytes, mode='r:') as tar:
|
||||
try:
|
||||
target = tar.getmember("temp/project.json")
|
||||
except KeyError:
|
||||
print(f"❌ {os.path.basename(filepath)}: temp/project.json 없음")
|
||||
return
|
||||
|
||||
# 압축파일 내부 temp/project.json 파일의 파일 오브젝트 추출
|
||||
extracted = tar.extractfile(target)
|
||||
if not extracted:
|
||||
print(f"❌ {os.path.basename(filepath)}: 파일 추출 실패")
|
||||
return
|
||||
|
||||
# 파일 오브젝트를 JSON 객체로 로드
|
||||
project_data = json.load(extracted)
|
||||
|
||||
# 해당 경로에 project.json 파일로 저장
|
||||
os.makedirs(output_folder, exist_ok=True)
|
||||
output_json_path = os.path.join(output_folder, "project.json")
|
||||
with open(output_json_path, "w", encoding="utf-8") as out_file:
|
||||
json.dump(project_data, out_file, ensure_ascii=False, indent=2)
|
||||
|
||||
print(f"✅ 처리 완료: {filepath} → {output_json_path}")
|
||||
|
||||
def process_ent_files(ent_dir, output_dir):
|
||||
for filename in os.listdir(ent_dir):
|
||||
if filename.lower().endswith((".ent")):
|
||||
filename = filename.replace(" ","")
|
||||
filepath = os.path.join(ent_dir, filename)
|
||||
output_root = output_dir
|
||||
output_folder = os.path.join(output_root, os.path.splitext(filename)[0])
|
||||
try:
|
||||
extract_project_json_from_gz(filepath, output_folder)
|
||||
except Exception as e:
|
||||
print(f"❌ 오류 발생 ({filename}): {e}")
|
||||
|
||||
# 실행 예
|
||||
if __name__ == "__main__":
|
||||
test_name = "2506_CAS_2_A"
|
||||
ent_dir = f".\\ent\\{test_name}"
|
||||
output_dir = f".\\output\\{test_name}"
|
||||
process_ent_files(ent_dir, output_dir)
|
||||
8
Entry-Scoring.code-workspace
Normal file
8
Entry-Scoring.code-workspace
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "."
|
||||
}
|
||||
],
|
||||
"settings": {}
|
||||
}
|
||||
@@ -42,10 +42,10 @@
|
||||
"sort": 16
|
||||
},
|
||||
"1-7": {
|
||||
"type": "scene",
|
||||
"ele": "$..variables[?(@.name=='물고기 수')]",
|
||||
"points": 1,
|
||||
"desc": "문제 2/빨간 물고기/변수/'물고기 수' 변수 만들기",
|
||||
"type": "scene",
|
||||
"sort": 101
|
||||
},
|
||||
"2-0": {
|
||||
|
||||
230
main.py
230
main.py
@@ -5,10 +5,7 @@ import os
|
||||
import pandas as pd # 추가된 import
|
||||
import unicodedata # 상단에 import 추가
|
||||
import re # 상단에 추가
|
||||
|
||||
# 파일 경로 설정
|
||||
project_json_path = './output/2506_CAS_2_A/'
|
||||
scoring_json_path = './correct/2506_CAS_2_A.json'
|
||||
from datetime import datetime
|
||||
|
||||
# JSON 파일 읽기
|
||||
def read_json(file_path):
|
||||
@@ -18,7 +15,11 @@ def read_json(file_path):
|
||||
# 요소 탐색 함수
|
||||
def find_element(project_data, jsonpath_expr):
|
||||
jsonpath_expr = parse(jsonpath_expr)
|
||||
return [match.value for match in jsonpath_expr.find(project_data)]
|
||||
result = []
|
||||
for match in jsonpath_expr.find(project_data):
|
||||
value = match.value
|
||||
result.append(value)
|
||||
return result
|
||||
|
||||
# 요소 탐색 함수
|
||||
def find_script_element(project_data, jsonpath_expr):
|
||||
@@ -43,6 +44,8 @@ def find_list_element(data, jsonpath_expr_list):
|
||||
# 스크립트 채점 진행 전 스크립트 블럭 순서가 when_run_button_click 1번째, when_clone_start 2번째 배열에 없으면
|
||||
# 리스트 순서 스왑해서 각각 0, 1번 순서로 배치될 수 있도록 함
|
||||
# when_scene_start 1번째, when_message_cast 2번째 동일한 순서로 리스트 순서 스왑해서 정렬
|
||||
# when_scene_start = [장면이 시작되었을 때]
|
||||
# when_message_cast = ["성공"신호를 받았을 때]
|
||||
def swap_script(origin):
|
||||
"""스크립트 블록 순서 정렬 함수"""
|
||||
if not origin or len(origin) == 0:
|
||||
@@ -127,101 +130,201 @@ def convert_to_str(value):
|
||||
return [convert_to_str(v) for v in value]
|
||||
return str(value)
|
||||
|
||||
def process_project(project_data, scoring_data):
|
||||
# def process_project(project_data, scoring_data):
|
||||
total_points = 0
|
||||
points = []
|
||||
point_list = []
|
||||
|
||||
for key, value in scoring_data.items():
|
||||
ele = value.get('ele')
|
||||
type = value.get('type')
|
||||
blocks = value.get('blocks')
|
||||
answer = value.get('answer')
|
||||
for key, question_info in scoring_data.items():
|
||||
element_path = question_info.get('ele')
|
||||
type = question_info.get('type')
|
||||
block_list = question_info.get('blocks')
|
||||
answer = question_info.get('answer')
|
||||
points = question_info.get('points')
|
||||
|
||||
print(f"example: {key}")
|
||||
if type == "scene":
|
||||
exists = find_element(project_data, ele)
|
||||
if exists:
|
||||
print(f"elements found for {ele}")
|
||||
found_elements = find_element(project_data, element_path)
|
||||
if found_elements:
|
||||
print(f"🟨 Elements found for {element_path}")
|
||||
# scene type의 경우 문자열 변환
|
||||
exists = [convert_to_str(x) for x in exists]
|
||||
if exists == answer:
|
||||
total_points += value.get('points')
|
||||
points.append(value.get('points'))
|
||||
found_elements = [convert_to_str(x) for x in found_elements]
|
||||
if found_elements == answer:
|
||||
total_points += points
|
||||
point_list.append(points)
|
||||
|
||||
elif exists and answer == None:
|
||||
total_points += value.get('points')
|
||||
points.append(value.get('points'))
|
||||
print(f"{ele} found ");
|
||||
elif found_elements and answer == None:
|
||||
total_points += points
|
||||
point_list.append(points)
|
||||
print(f"{element_path} found ")
|
||||
else:
|
||||
points.append(0)
|
||||
print(f"{exists} not found");
|
||||
point_list.append(0)
|
||||
print(f"{found_elements} not found")
|
||||
else:
|
||||
print(f"Element '{ele}' not found")
|
||||
points.append(0)
|
||||
print(f"🟥 Element '{element_path}' not found")
|
||||
point_list.append(0)
|
||||
|
||||
# 스크립트 타입의 경우
|
||||
# 스크립트 블록을 찾고, 블록 내의 요소를 확인
|
||||
# 블록 내의 요소를 확인하고, 정답과 비교하여 점수 계산
|
||||
if type == "script":
|
||||
exists = find_script_element(project_data, ele)
|
||||
script_raw = find_script_element(project_data, element_path)
|
||||
|
||||
if exists == None:
|
||||
temp = None
|
||||
if script_raw == None:
|
||||
script_data = None
|
||||
else:
|
||||
temp = json.loads(exists)
|
||||
temp = swap_script(temp)
|
||||
script_data = json.loads(script_raw)
|
||||
script_data = swap_script(script_data)
|
||||
|
||||
innerKey = 1
|
||||
for block in blocks:
|
||||
innerType = block.get('type')
|
||||
block_index = 1
|
||||
for block in block_list:
|
||||
block_type = block.get('type')
|
||||
|
||||
if temp == None:
|
||||
print(f"{key}-{innerKey}: Script Not exist")
|
||||
points.append("확인 필요")
|
||||
if script_data == None:
|
||||
print(f"{key}-{block_index}: Script Not exist")
|
||||
point_list.append("확인 필요")
|
||||
continue
|
||||
|
||||
elif innerType == "list":
|
||||
block_exists = find_list_element(temp, block.get('ele'))
|
||||
elif block_type == "list":
|
||||
block_elements = find_list_element(script_data, block.get('ele'))
|
||||
else:
|
||||
block_exists = find_element(temp, block.get('ele'))
|
||||
block_elements = find_element(script_data, block.get('ele'))
|
||||
|
||||
answer = block.get('answer', None)
|
||||
|
||||
if block_exists and isinstance(answer, list):
|
||||
if block_elements and isinstance(answer, list):
|
||||
# 리스트의 모든 요소를 문자열로 변환
|
||||
flat_matches = [convert_to_str(x) for x in list(chain.from_iterable(block_exists))]
|
||||
flat_matches = [convert_to_str(x) for x in list(chain.from_iterable(block_elements))]
|
||||
else:
|
||||
if not block_exists:
|
||||
if not block_elements:
|
||||
flat_matches = None
|
||||
else:
|
||||
# 단일 값을 문자열로 변환
|
||||
flat_matches = convert_to_str(block_exists[0])
|
||||
flat_matches = convert_to_str(block_elements[0])
|
||||
|
||||
if block_exists:
|
||||
if block_elements:
|
||||
# answer도 문자열로 변환하여 비교
|
||||
str_answer = convert_to_str(answer) if answer is not None else None
|
||||
if answer is not None and str_answer != flat_matches:
|
||||
print(f"{key}-{innerKey}: {str_answer} != {flat_matches}")
|
||||
points.append(0)
|
||||
print(f"{key}-{block_index}: {str_answer} != {flat_matches}")
|
||||
point_list.append(0)
|
||||
elif answer is not None and str_answer == flat_matches:
|
||||
print(f"{key}-{innerKey}: {str_answer} == {flat_matches}")
|
||||
print(f"{key}-{block_index}: {str_answer} == {flat_matches}")
|
||||
total_points += block.get('points')
|
||||
points.append(block.get('points'))
|
||||
elif answer is None and block_exists:
|
||||
point_list.append(block.get('points'))
|
||||
elif answer is None and block_elements:
|
||||
total_points += block.get('points')
|
||||
points.append(block.get('points'))
|
||||
print(f"{key}-{innerKey}: exist ele: {block_exists}")
|
||||
point_list.append(block.get('points'))
|
||||
print(f"{key}-{block_index}: exist ele: {block_elements}")
|
||||
else:
|
||||
print(f"No elements found for {block.get('ele')}")
|
||||
points.append(0)
|
||||
print(f"No elements found for {block.get('ele')}")
|
||||
point_list.append(0)
|
||||
|
||||
innerKey = innerKey + 1
|
||||
block_index = block_index + 1
|
||||
|
||||
points.append(total_points)
|
||||
return points
|
||||
point_list.append(total_points)
|
||||
return point_list
|
||||
|
||||
def process_project(project_data, scoring_data):
|
||||
total_points = 0
|
||||
score_list = []
|
||||
|
||||
for question_key, question_info in scoring_data.items():
|
||||
element_path = question_info.get('ele')
|
||||
question_type = question_info.get('type')
|
||||
block_list = question_info.get('blocks')
|
||||
expected_answer = question_info.get('answer')
|
||||
question_points = question_info.get('points')
|
||||
|
||||
print(f"▶ Processing question: {question_key}")
|
||||
|
||||
# ✅ SCENE TYPE 처리
|
||||
if question_type == "scene":
|
||||
scene_elements = find_element(project_data, element_path)
|
||||
|
||||
if scene_elements:
|
||||
print(f"🟨 Found elements for '{element_path}'")
|
||||
scene_elements = [convert_to_str(x) for x in scene_elements]
|
||||
|
||||
if scene_elements == expected_answer or (expected_answer is None and scene_elements):
|
||||
total_points += question_points
|
||||
score_list.append(question_points)
|
||||
# print(f"✅ Matched: {scene_elements}")
|
||||
else:
|
||||
score_list.append(0)
|
||||
print(f"❌ Mismatch: {scene_elements} vs {expected_answer}")
|
||||
else:
|
||||
print(f"🟥 Element '{element_path}' not found")
|
||||
score_list.append(0)
|
||||
|
||||
# ✅ SCRIPT TYPE 처리
|
||||
elif question_type == "script":
|
||||
script_raw = find_script_element(project_data, element_path)
|
||||
script_data_1 = json.loads(script_raw) if script_raw else None
|
||||
script_data = swap_script(script_data_1) if script_data_1 else None
|
||||
|
||||
if script_data != script_data_1:
|
||||
print(f"⬜ Script data 순서 변경 ")
|
||||
|
||||
block_index = 1
|
||||
for block in block_list:
|
||||
block_type = block.get('type')
|
||||
block_path = block.get('ele')
|
||||
block_expected_answer = block.get('answer', None)
|
||||
block_points = block.get('points')
|
||||
|
||||
if script_data is None:
|
||||
print(f"{question_key}-{block_index}: Script Not Found")
|
||||
score_list.append("확인 필요")
|
||||
block_index += 1
|
||||
continue
|
||||
|
||||
# 블록 요소 검색
|
||||
if block_type == "list":
|
||||
block_elements = find_list_element(script_data, block_path)
|
||||
else:
|
||||
block_elements = find_element(script_data, block_path)
|
||||
|
||||
# 결과값 정리
|
||||
if block_elements and isinstance(block_expected_answer, list):
|
||||
found_values = [convert_to_str(x) for x in chain.from_iterable(block_elements)]
|
||||
else:
|
||||
found_values = convert_to_str(block_elements[0]) if block_elements else None
|
||||
|
||||
expected_str = convert_to_str(block_expected_answer) if block_expected_answer is not None else None
|
||||
|
||||
# 비교 및 점수 처리
|
||||
if block_elements:
|
||||
if block_expected_answer is not None and expected_str != found_values:
|
||||
print(f"{question_key}-{block_index}: ❌ {expected_str} != {found_values}")
|
||||
score_list.append(0)
|
||||
elif block_expected_answer is not None and expected_str == found_values:
|
||||
print(f"{question_key}-{block_index}: ✅ {expected_str} == {found_values}")
|
||||
total_points += block_points
|
||||
score_list.append(block_points)
|
||||
elif block_expected_answer is None:
|
||||
total_points += block_points
|
||||
score_list.append(block_points)
|
||||
print(f"{question_key}-{block_index}: Element Exists")
|
||||
else:
|
||||
print(f"{question_key}-{block_index}: No elements found for {block_path}")
|
||||
score_list.append(0)
|
||||
|
||||
block_index += 1
|
||||
|
||||
score_list.append(total_points)
|
||||
return score_list
|
||||
|
||||
def normalize_path(path):
|
||||
"""한글 경로명을 NFC 방식으로 정규화"""
|
||||
|
||||
return unicodedata.normalize('NFC', path)
|
||||
|
||||
def main():
|
||||
# 파일 경로 설정
|
||||
# project_json_path = './output/2506_CAS_2_A/'
|
||||
project_json_path = './output/00_test/'
|
||||
scoring_json_path = './correct/2506_CAS_2_A.json'
|
||||
|
||||
scoring_data = read_json(scoring_json_path)
|
||||
student_score_list = []
|
||||
|
||||
@@ -259,7 +362,12 @@ def main():
|
||||
try:
|
||||
# 디렉토리 패스 내에 학생 이름만 뽑아서 엑셀 컬럼 명으로 추가
|
||||
# output/cas-000040-이지원/temp/project.json
|
||||
student_id = normalize_path(full_path.split('/')[3])
|
||||
# student_id = normalize_path(full_path.split('/')[3])
|
||||
match = re.search(r'(\d{6}-[^\\/]+)[\\/]', full_path)
|
||||
if match:
|
||||
student_id = match.group(1)
|
||||
|
||||
# project.json 파일 내용
|
||||
project_data = read_json(full_path)
|
||||
points = process_project(project_data, scoring_data)
|
||||
points.insert(0, student_id)
|
||||
@@ -274,7 +382,9 @@ def main():
|
||||
df.columns = df.iloc[0]
|
||||
df = df[1:]
|
||||
|
||||
excel_path = project_json_path + '/' + 'results.xlsx'
|
||||
timestamp = datetime.now().strftime("%y%m%d")
|
||||
|
||||
excel_path = f'{project_json_path}/{timestamp}_results.xlsx'
|
||||
|
||||
df.to_excel(excel_path, index=False)
|
||||
print(f"\nResults saved to {excel_path}")
|
||||
|
||||
@@ -41,7 +41,7 @@ def calculate_points(project_data, scoring_data):
|
||||
return total_points
|
||||
|
||||
def main():
|
||||
project_data = load_json('sample/제2410회 코딩활용능력 2급 B형 정답/project.json')
|
||||
project_data = load_json(r'D:\project\Entry\Entry-Scoring\sample\제2506회 코딩활용능력 2급 A형 정답\temp\project.json')
|
||||
scoring_data = load_json('scoring.json')
|
||||
|
||||
total_points = calculate_points(project_data, scoring_data)
|
||||
|
||||
BIN
시험자료/2506/제2506회 코딩활용능력 2급 A형 문제.hwp
Normal file
BIN
시험자료/2506/제2506회 코딩활용능력 2급 A형 문제.hwp
Normal file
Binary file not shown.
BIN
시험자료/2506/제2506회 코딩활용능력 2급 A형 정답.ent
Normal file
BIN
시험자료/2506/제2506회 코딩활용능력 2급 A형 정답.ent
Normal file
Binary file not shown.
BIN
시험자료/2506/제2506회 코딩활용능력 2급 A형 채점기준표.xlsx
Normal file
BIN
시험자료/2506/제2506회 코딩활용능력 2급 A형 채점기준표.xlsx
Normal file
Binary file not shown.
Reference in New Issue
Block a user