2510회 채점자료 업데이트 / 스크립트 블럭 순서 재정렬 방식 변경
This commit is contained in:
@@ -47,8 +47,8 @@ def copy_ent_files(source_root, target_root):
|
||||
|
||||
# 사용법
|
||||
|
||||
source_directory = r"D:\project\Entry\Entry-Scoring\시험자료\2509\A" # 원본 디렉토리 경로
|
||||
target_directory = r".\ent\2509_CAT_3_A"
|
||||
source_directory = r"D:\project\data\CAS_제2510회 정기\제2510회 코딩활용능력 2급 정기 답안파일" # 원본 디렉토리 경로
|
||||
target_directory = r".\ent\2510_CAS_2_A"
|
||||
# target_directory_a = r"./output/A" # '1교시'의 타겟 경로
|
||||
# target_directory_b = r"./output/B" # '2교시'의 타겟 경로
|
||||
# target_directory_c = r"./output/C" # '3교시'의 타겟 경로
|
||||
|
||||
@@ -50,8 +50,9 @@ def process_ent_files(ent_dir, output_dir):
|
||||
|
||||
# 실행 예
|
||||
if __name__ == "__main__":
|
||||
test_names = ["2509_CAT_3_A"]
|
||||
# test_names = ["2509_CAT_3_A"]
|
||||
# test_names = ["2508_CAS_2_A","2508_CAS_2_B"]
|
||||
test_names = ["2510_CAS_2_A"]
|
||||
for test_name in test_names:
|
||||
ent_dir = f".\\ent\\{test_name}"
|
||||
output_dir = f".\\output\\{test_name}"
|
||||
|
||||
BIN
251002_2509_CAT_3_A_채점결과.xlsx
Normal file
BIN
251002_2509_CAT_3_A_채점결과.xlsx
Normal file
Binary file not shown.
BIN
251030_2510_CAS_2_A_채점결과.xlsx
Normal file
BIN
251030_2510_CAS_2_A_채점결과.xlsx
Normal file
Binary file not shown.
BIN
251031_2510_CAS_2_A_채점결과.xlsx
Normal file
BIN
251031_2510_CAS_2_A_채점결과.xlsx
Normal file
Binary file not shown.
BIN
251103_2510_CAS_2_A_채점결과.xlsx
Normal file
BIN
251103_2510_CAS_2_A_채점결과.xlsx
Normal file
Binary file not shown.
BIN
251104_2510_CAS_2_A_채점결과.xlsx
Normal file
BIN
251104_2510_CAS_2_A_채점결과.xlsx
Normal file
Binary file not shown.
BIN
251105_2510_CAS_2_A_TEST.xlsx
Normal file
BIN
251105_2510_CAS_2_A_TEST.xlsx
Normal file
Binary file not shown.
BIN
251105_2510_CAS_2_A_채점결과.xlsx
Normal file
BIN
251105_2510_CAS_2_A_채점결과.xlsx
Normal file
Binary file not shown.
1008
correct/2510_CAS_2_A.json
Normal file
1008
correct/2510_CAS_2_A.json
Normal file
File diff suppressed because it is too large
Load Diff
223
logs/cat.log
223
logs/cat.log
@@ -34,3 +34,226 @@ Traceback (most recent call last):
|
||||
File "D:\project\Entry\Entry-Scoring\main.py", line 162, in process_project
|
||||
total_points += question_points
|
||||
TypeError: unsupported operand type(s) for +=: 'int' and 'NoneType'
|
||||
[2025-10-30 16:24:04] [ERROR] [main:301] 🚫Error processing ./output/2510_CAS_2_A/2510_CAS_2_A(정답)\project.json: unsupported operand type(s) for +=: 'int' and 'NoneType'
|
||||
Traceback (most recent call last):
|
||||
File "D:\project\Entry\Entry-Scoring\main.py", line 296, in main
|
||||
points = process_project(project_data, scoring_data)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\project\Entry\Entry-Scoring\main.py", line 162, in process_project
|
||||
total_points += question_points
|
||||
TypeError: unsupported operand type(s) for +=: 'int' and 'NoneType'
|
||||
[2025-10-30 16:26:32] [ERROR] [main:301] 🚫Error processing ./output/2510_CAS_2_A/2510_CAS_2_A(정답)\project.json: unsupported operand type(s) for +=: 'float' and 'NoneType'
|
||||
Traceback (most recent call last):
|
||||
File "D:\project\Entry\Entry-Scoring\main.py", line 296, in main
|
||||
points = process_project(project_data, scoring_data)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\project\Entry\Entry-Scoring\main.py", line 212, in process_project
|
||||
total_points += block_points
|
||||
TypeError: unsupported operand type(s) for +=: 'float' and 'NoneType'
|
||||
[2025-10-31 17:08:00] [ERROR] [main:322] 🚫Error processing ./output/2510_CAS_2_A/2510_CAS_2_A(정답)\project.json: 'NoneType' object is not iterable
|
||||
Traceback (most recent call last):
|
||||
File "D:\project\Entry\Entry-Scoring\main.py", line 317, in main
|
||||
points = process_project(project_data, scoring_data)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\project\Entry\Entry-Scoring\main.py", line 167, in process_project
|
||||
for block in block_list
|
||||
^^^^^^^^^^
|
||||
TypeError: 'NoneType' object is not iterable
|
||||
[2025-10-31 17:14:04] [ERROR] [main:324] 🚫Error processing ./output/2510_CAS_2_A/2510_CAS_2_A(정답)\project.json: unhashable type: 'list'
|
||||
Traceback (most recent call last):
|
||||
File "D:\project\Entry\Entry-Scoring\main.py", line 319, in main
|
||||
points = process_project(project_data, scoring_data)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\project\Entry\Entry-Scoring\main.py", line 168, in process_project
|
||||
if block.get("answer") in target_event_answers
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
TypeError: unhashable type: 'list'
|
||||
[2025-10-31 17:17:42] [ERROR] [main:327] 🚫Error processing ./output/2510_CAS_2_A/2510_CAS_2_A(정답)\project.json: unhashable type: 'list'
|
||||
Traceback (most recent call last):
|
||||
File "D:\project\Entry\Entry-Scoring\main.py", line 322, in main
|
||||
points = process_project(project_data, scoring_data)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\project\Entry\Entry-Scoring\main.py", line 171, in process_project
|
||||
if answer in target_event_answers: # 조건에 맞는지 확인
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
TypeError: unhashable type: 'list'
|
||||
[2025-10-31 17:51:14] [ERROR] [main:360] 🚫Error processing ./output/2510_CAS_2_A/2510_CAS_2_A(정답)\project.json: 'list' object has no attribute 'get'
|
||||
Traceback (most recent call last):
|
||||
File "D:\project\Entry\Entry-Scoring\main.py", line 355, in main
|
||||
points = process_project(project_data, scoring_data)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\project\Entry\Entry-Scoring\main.py", line 234, in process_project
|
||||
script_data = reorder_script_by_event_order(script_json, block_event_order)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\project\Entry\Entry-Scoring\main.py", line 149, in reorder_script_by_event_order
|
||||
if script.get("type") == event_type:
|
||||
^^^^^^^^^^
|
||||
AttributeError: 'list' object has no attribute 'get'
|
||||
[2025-10-31 17:56:20] [ERROR] [main:360] 🚫Error processing ./output/2510_CAS_2_A/2510_CAS_2_A(정답)\project.json: 'list' object has no attribute 'get'
|
||||
Traceback (most recent call last):
|
||||
File "D:\project\Entry\Entry-Scoring\main.py", line 355, in main
|
||||
points = process_project(project_data, scoring_data)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\project\Entry\Entry-Scoring\main.py", line 234, in process_project
|
||||
script_data = reorder_script_by_event_order(script_json, block_event_order)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\project\Entry\Entry-Scoring\main.py", line 149, in reorder_script_by_event_order
|
||||
if script.get("type") == event_type:
|
||||
^^^^^^^^^^
|
||||
AttributeError: 'list' object has no attribute 'get'
|
||||
[2025-10-31 17:56:40] [ERROR] [main:360] 🚫Error processing ./output/2510_CAS_2_A/2510_CAS_2_A(정답)\project.json: 'list' object has no attribute 'get'
|
||||
Traceback (most recent call last):
|
||||
File "D:\project\Entry\Entry-Scoring\main.py", line 355, in main
|
||||
points = process_project(project_data, scoring_data)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\project\Entry\Entry-Scoring\main.py", line 234, in process_project
|
||||
script_data = reorder_script_by_event_order(script_json, block_event_order)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\project\Entry\Entry-Scoring\main.py", line 149, in reorder_script_by_event_order
|
||||
if script.get("type") == event_type:
|
||||
^^^^^^^^^^
|
||||
AttributeError: 'list' object has no attribute 'get'
|
||||
[2025-10-31 17:58:18] [ERROR] [main:361] 🚫Error processing ./output/2510_CAS_2_A/2510_CAS_2_A(정답)\project.json: 'list' object has no attribute 'get'
|
||||
Traceback (most recent call last):
|
||||
File "D:\project\Entry\Entry-Scoring\main.py", line 356, in main
|
||||
points = process_project(project_data, scoring_data)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\project\Entry\Entry-Scoring\main.py", line 235, in process_project
|
||||
script_data = reorder_script_by_event_order(script_json, block_event_order)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\project\Entry\Entry-Scoring\main.py", line 150, in reorder_script_by_event_order
|
||||
if script.get("type") == event_type:
|
||||
^^^^^^^^^^
|
||||
AttributeError: 'list' object has no attribute 'get'
|
||||
[2025-11-03 16:09:35] [ERROR] [main:399] 🚫Error processing ./output/2510_CAS_2_A/2510_CAS_2_A(정답)\project.json: list indices must be integers or slices, not str
|
||||
Traceback (most recent call last):
|
||||
File "D:\project\Entry\Entry-Scoring\main.py", line 394, in main
|
||||
points = process_project(project_data, scoring_data)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\project\Entry\Entry-Scoring\main.py", line 270, in process_project
|
||||
script_data = reorder_script_all_cases(script_json, block_event_order)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\project\Entry\Entry-Scoring\main.py", line 167, in reorder_script_all_cases
|
||||
type_map.setdefault(s["type"], []).append(s)
|
||||
~^^^^^^^^
|
||||
TypeError: list indices must be integers or slices, not str
|
||||
[2025-11-03 17:15:12] [ERROR] [main:433] 🚫Error processing ./output/2510_CAS_2_A/2510_CAS_2_A(정답)\project.json: 0
|
||||
Traceback (most recent call last):
|
||||
File "D:\project\Entry\Entry-Scoring\main.py", line 428, in main
|
||||
points = process_project(project_data, scoring_data)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\project\Entry\Entry-Scoring\main.py", line 310, in process_project
|
||||
block_elements = find_element(single_script, block_path)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\project\Entry\Entry-Scoring\main.py", line 33, in find_element
|
||||
for match in jsonpath_expr.find(item):
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "d:\project\Entry\Entry-Scoring\.venv\Lib\site-packages\jsonpath_ng\jsonpath.py", line 268, in find
|
||||
for subdata in self.left.find(datum)
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
File "d:\project\Entry\Entry-Scoring\.venv\Lib\site-packages\jsonpath_ng\jsonpath.py", line 270, in find
|
||||
for submatch in self.right.find(subdata)]
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "d:\project\Entry\Entry-Scoring\.venv\Lib\site-packages\jsonpath_ng\jsonpath.py", line 680, in find
|
||||
return self._find_base(datum, create=False)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "d:\project\Entry\Entry-Scoring\.venv\Lib\site-packages\jsonpath_ng\jsonpath.py", line 692, in _find_base
|
||||
return [DatumInContext(datum.value[self.index], path=self, context=datum)]
|
||||
~~~~~~~~~~~^^^^^^^^^^^^
|
||||
KeyError: 0
|
||||
[2025-11-03 17:20:20] [ERROR] [main:417] 🚫Error processing ./output/2510_CAS_2_A/2510_CAS_2_A(정답)\project.json: 0
|
||||
Traceback (most recent call last):
|
||||
File "D:\project\Entry\Entry-Scoring\main.py", line 412, in main
|
||||
points = process_project(project_data, scoring_data)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\project\Entry\Entry-Scoring\main.py", line 300, in process_project
|
||||
block_elements = find_element(data, block_path)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\project\Entry\Entry-Scoring\main.py", line 33, in find_element
|
||||
for match in jsonpath_expr.find(item):
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "d:\project\Entry\Entry-Scoring\.venv\Lib\site-packages\jsonpath_ng\jsonpath.py", line 268, in find
|
||||
for subdata in self.left.find(datum)
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
File "d:\project\Entry\Entry-Scoring\.venv\Lib\site-packages\jsonpath_ng\jsonpath.py", line 270, in find
|
||||
for submatch in self.right.find(subdata)]
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "d:\project\Entry\Entry-Scoring\.venv\Lib\site-packages\jsonpath_ng\jsonpath.py", line 680, in find
|
||||
return self._find_base(datum, create=False)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "d:\project\Entry\Entry-Scoring\.venv\Lib\site-packages\jsonpath_ng\jsonpath.py", line 692, in _find_base
|
||||
return [DatumInContext(datum.value[self.index], path=self, context=datum)]
|
||||
~~~~~~~~~~~^^^^^^^^^^^^
|
||||
KeyError: 0
|
||||
[2025-11-03 17:21:39] [ERROR] [main:407] 🚫Error processing ./output/2510_CAS_2_A/2510_CAS_2_A(정답)\project.json: 1
|
||||
Traceback (most recent call last):
|
||||
File "D:\project\Entry\Entry-Scoring\main.py", line 402, in main
|
||||
points = process_project(project_data, scoring_data)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\project\Entry\Entry-Scoring\main.py", line 288, in process_project
|
||||
block_elements = find_list_element(data, block_path)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\project\Entry\Entry-Scoring\main.py", line 48, in find_list_element
|
||||
result.append([match.value for match in jsonpath_expr.find(data)])
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "d:\project\Entry\Entry-Scoring\.venv\Lib\site-packages\jsonpath_ng\jsonpath.py", line 268, in find
|
||||
for subdata in self.left.find(datum)
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
File "d:\project\Entry\Entry-Scoring\.venv\Lib\site-packages\jsonpath_ng\jsonpath.py", line 270, in find
|
||||
for submatch in self.right.find(subdata)]
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "d:\project\Entry\Entry-Scoring\.venv\Lib\site-packages\jsonpath_ng\jsonpath.py", line 680, in find
|
||||
return self._find_base(datum, create=False)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "d:\project\Entry\Entry-Scoring\.venv\Lib\site-packages\jsonpath_ng\jsonpath.py", line 692, in _find_base
|
||||
return [DatumInContext(datum.value[self.index], path=self, context=datum)]
|
||||
~~~~~~~~~~~^^^^^^^^^^^^
|
||||
KeyError: 1
|
||||
[2025-11-04 15:36:56] [ERROR] [main:423] 🚫Error processing ./output/2510_CAS_2_A/코딩활용능력2급(엔트리)-000139-성지환\project.json: 'NoneType' object is not iterable
|
||||
Traceback (most recent call last):
|
||||
File "D:\project\Entry\Entry-Scoring\main.py", line 418, in main
|
||||
points = process_project(project_data, scoring_data)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\project\Entry\Entry-Scoring\main.py", line 268, in process_project
|
||||
script_data = reorder_script_all_cases(script_json, block_event_order)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\project\Entry\Entry-Scoring\main.py", line 167, in reorder_script_all_cases
|
||||
for s in script_json:
|
||||
^^^^^^^^^^^
|
||||
TypeError: 'NoneType' object is not iterable
|
||||
[2025-11-04 16:20:44] [ERROR] [main:423] 🚫Error processing ./output/2510_CAS_2_A/코딩활용능력2급(엔트리)-000139-성지환\project.json: 'NoneType' object is not iterable
|
||||
Traceback (most recent call last):
|
||||
File "D:\project\Entry\Entry-Scoring\main.py", line 418, in main
|
||||
points = process_project(project_data, scoring_data)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\project\Entry\Entry-Scoring\main.py", line 268, in process_project
|
||||
script_data = reorder_script_all_cases(script_json, block_event_order)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\project\Entry\Entry-Scoring\main.py", line 167, in reorder_script_all_cases
|
||||
for s in script_json:
|
||||
^^^^^^^^^^^
|
||||
TypeError: 'NoneType' object is not iterable
|
||||
[2025-11-04 17:11:33] [ERROR] [main:423] 🚫Error processing ./output/2510_CAS_2_A/코딩활용능력2급(엔트리)-000139-성지환\project.json: 'NoneType' object is not iterable
|
||||
Traceback (most recent call last):
|
||||
File "D:\project\Entry\Entry-Scoring\main.py", line 418, in main
|
||||
points = process_project(project_data, scoring_data)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\project\Entry\Entry-Scoring\main.py", line 268, in process_project
|
||||
script_data = reorder_script_all_cases(script_json, block_event_order)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\project\Entry\Entry-Scoring\main.py", line 167, in reorder_script_all_cases
|
||||
for s in script_json:
|
||||
^^^^^^^^^^^
|
||||
TypeError: 'NoneType' object is not iterable
|
||||
[2025-11-05 16:02:42] [ERROR] [main:423] 🚫Error processing ./output/00_test/코딩활용능력2급(엔트리)-000139-성지환\project.json: 'NoneType' object is not iterable
|
||||
Traceback (most recent call last):
|
||||
File "D:\project\Entry\Entry-Scoring\main.py", line 418, in main
|
||||
points = process_project(project_data, scoring_data)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\project\Entry\Entry-Scoring\main.py", line 268, in process_project
|
||||
script_data = reorder_script_all_cases(script_json, block_event_order)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "D:\project\Entry\Entry-Scoring\main.py", line 167, in reorder_script_all_cases
|
||||
for s in script_json:
|
||||
^^^^^^^^^^^
|
||||
TypeError: 'NoneType' object is not iterable
|
||||
|
||||
195
main.py
195
main.py
@@ -1,6 +1,5 @@
|
||||
from jsonpath_ng.ext import parse
|
||||
import json
|
||||
from itertools import chain
|
||||
import os
|
||||
import pandas as pd # 추가된 import
|
||||
import unicodedata # 상단에 import 추가
|
||||
@@ -9,6 +8,8 @@ from datetime import datetime
|
||||
import logging
|
||||
from logging_config import setup_logging # logging 설정을 위한 import
|
||||
import traceback
|
||||
import itertools
|
||||
import copy
|
||||
|
||||
from script_utils import extract_and_format_scripts # 스크립트 추출 함수 import
|
||||
|
||||
@@ -21,13 +22,13 @@ def read_json(file_path):
|
||||
|
||||
# 요소 탐색 함수
|
||||
def find_element(project_data, jsonpath_expr):
|
||||
jsonpath_expr = parse(jsonpath_expr)
|
||||
result = []
|
||||
for match in jsonpath_expr.find(project_data):
|
||||
value = match.value
|
||||
result.append(value)
|
||||
return result
|
||||
|
||||
"""
|
||||
주어진 데이터(project_data)에서 jsonpath 표현식에 일치하는 모든 값들을 찾아
|
||||
리스트로 반환합니다.
|
||||
"""
|
||||
parsed_expr = parse(jsonpath_expr)
|
||||
return [match.value for match in parsed_expr.find(project_data)]
|
||||
|
||||
# 요소 탐색 함수
|
||||
def find_script_element(project_data, jsonpath_expr):
|
||||
jsonpath_expr = parse(jsonpath_expr)
|
||||
@@ -40,13 +41,14 @@ def find_script_element(project_data, jsonpath_expr):
|
||||
|
||||
# jsonpath_expr_list 로 넘어온 jsonpath들을 하나씩 parse 해주고 결과를 result 리스트로 반환
|
||||
def find_list_element(data, jsonpath_expr_list):
|
||||
result = []
|
||||
|
||||
for jsonpath_expr in jsonpath_expr_list:
|
||||
jsonpath_expr = parse(jsonpath_expr)
|
||||
result.append([match.value for match in jsonpath_expr.find(data)])
|
||||
|
||||
return result
|
||||
"""
|
||||
주어진 데이터(data)에서 여러 jsonpath 표현식들에 일치하는 값들을 찾아
|
||||
결과를 리스트의 리스트 형태로 반환합니다.
|
||||
"""
|
||||
return [
|
||||
[match.value for match in parse(expr).find(data)]
|
||||
for expr in jsonpath_expr_list
|
||||
]
|
||||
|
||||
# 스크립트 채점 진행 전 스크립트 블럭 순서가 when_run_button_click 1번째, when_clone_start 2번째 배열에 없으면
|
||||
# 리스트 순서 스왑해서 각각 0, 1번 순서로 배치될 수 있도록 함
|
||||
@@ -113,10 +115,11 @@ def swap_script(origin):
|
||||
result.extend([block for _, block in key_press_blocks])
|
||||
|
||||
# 3. clone_start와 message_cast 블록 추가
|
||||
if clone_start_block:
|
||||
result.append(clone_start_block)
|
||||
if message_cast_block:
|
||||
result.extend(message_cast_block)
|
||||
if clone_start_block:
|
||||
result.append(clone_start_block)
|
||||
|
||||
|
||||
# 4. 나머지 블록 추가
|
||||
result.extend(other_blocks)
|
||||
@@ -124,6 +127,49 @@ def swap_script(origin):
|
||||
# 결과가 비어있으면 원본 반환
|
||||
return result if result else origin
|
||||
|
||||
def reorder_script_all_cases(script_json, block_event_order):
|
||||
"""
|
||||
script_json의 'type' 순서를 block_event_order 순서에 맞게 재정렬합니다.
|
||||
|
||||
Args:
|
||||
script_json (list[dict]): 각 요소가 {'type': '...', ...} 형태의 리스트
|
||||
block_event_order (list[str]): 원하는 type 순서 예: ['when_run_button_click', 'when_message_cast']
|
||||
|
||||
Returns:
|
||||
list[dict]: block_event_order 순서대로 재정렬된 리스트
|
||||
"""
|
||||
# 타입별로 스크립트를 분류
|
||||
type_map = {}
|
||||
for s in script_json:
|
||||
type_map.setdefault(s[0]["type"], []).append(s)
|
||||
|
||||
results = []
|
||||
|
||||
def backtrack(order_idx, used_counts, current):
|
||||
# ✅ 모든 이벤트 순서를 처리했으면 결과에 추가
|
||||
if order_idx == len(block_event_order):
|
||||
results.append(copy.deepcopy(current))
|
||||
return
|
||||
|
||||
event_type = block_event_order[order_idx]
|
||||
available_scripts = type_map.get(event_type, [])
|
||||
|
||||
# 아직 사용하지 않은 script만 선택
|
||||
for i, script in enumerate(available_scripts):
|
||||
if used_counts[event_type][i]:
|
||||
continue
|
||||
used_counts[event_type][i] = True
|
||||
current.append(script)
|
||||
backtrack(order_idx + 1, used_counts, current)
|
||||
current.pop()
|
||||
used_counts[event_type][i] = False
|
||||
|
||||
# 사용 여부 초기화
|
||||
used_counts = {t: [False] * len(lst) for t, lst in type_map.items()}
|
||||
backtrack(0, used_counts, [])
|
||||
|
||||
return results
|
||||
|
||||
def clean_string(text):
|
||||
"""문자열 끝의 . 또는 ! 제거"""
|
||||
if isinstance(text, str):
|
||||
@@ -140,14 +186,36 @@ def convert_to_str(value):
|
||||
def process_project(project_data, scoring_data):
|
||||
total_points = 0
|
||||
score_list = []
|
||||
|
||||
# - 시작하기 버튼을 클릭했을 때 : when_run_button_click
|
||||
# - (특정) 신호를 받았을 때 : when_message_cast
|
||||
# - 복제본이 생성 되었을 때 : when_clone_start
|
||||
# - 장면이 시작 되었을 때 : when_scene_start
|
||||
# - 오브젝트를 클릭 했을 때 : when_object_click
|
||||
# 이벤트 블록이 존재하는지 여부 확인용 변수
|
||||
target_event_type = {
|
||||
"when_run_button_click",
|
||||
"when_message_cast",
|
||||
"when_clone_start",
|
||||
"when_scene_start",
|
||||
"when_object_click"
|
||||
}
|
||||
|
||||
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')
|
||||
question_points = question_info.get('point')
|
||||
|
||||
block_event_order = [] # 결과를 저장할 리스트 생성
|
||||
|
||||
if isinstance(block_list, list) and block_list:
|
||||
for block in block_list:
|
||||
answer = block.get("answer") # answer 키값을 안전하게 가져오기
|
||||
if isinstance(answer, str) and answer in target_event_type:
|
||||
block_event_order.append(answer) # 리스트에 추가
|
||||
|
||||
print(f"▶ Processing question: {question_key}")
|
||||
|
||||
# ✅ SCENE TYPE 처리
|
||||
@@ -173,50 +241,82 @@ def process_project(project_data, scoring_data):
|
||||
elif question_type == "script":
|
||||
script_raw = find_script_element(project_data, element_path)
|
||||
script_json = json.loads(script_raw) if script_raw else None
|
||||
script_data = swap_script(script_json) if script_json else None
|
||||
|
||||
# 스크립트 블록 순서 재정렬
|
||||
script_data = reorder_script_all_cases(script_json, block_event_order) if script_json else None
|
||||
|
||||
block_index = 1
|
||||
for block in block_list:
|
||||
block_type = block.get('type')
|
||||
block_path = block.get('ele')
|
||||
block_answer = block.get('answer', None)
|
||||
block_points = block.get('points')
|
||||
block_points = block.get('point')
|
||||
|
||||
if script_data is None:
|
||||
print(f"{question_key}-{block_index}: Script Not Found")
|
||||
score_list.append("확인 필요")
|
||||
block_index += 1
|
||||
continue
|
||||
|
||||
# 1. 현재 블록(문제)이 정답인지 판별하는 플래그
|
||||
is_block_correct = False
|
||||
|
||||
# 2. 로깅을 위해 마지막으로 찾은 값을 저장할 변수
|
||||
last_found_values = None
|
||||
|
||||
# 3. script_data가 단일 객체여도 처리 가능하도록 리스트로 통일
|
||||
scripts_to_check = script_data if isinstance(script_data, list) else [script_data]
|
||||
|
||||
# 블록 요소 검색
|
||||
if block_type == "list":
|
||||
block_elements = find_list_element(script_data, block_path)
|
||||
else:
|
||||
block_elements = find_element(script_data, block_path)
|
||||
# 4. 여러 스크립트 뭉치를 순회하며 정답이 하나라도 있는지 확인
|
||||
for single_script in scripts_to_check:
|
||||
# 단일 스크립트 객체(single_script)에 대해 블록 요소 검색
|
||||
if block_type == "list":
|
||||
block_elements = find_list_element(single_script, block_path)
|
||||
else:
|
||||
block_elements = find_element(single_script, block_path)
|
||||
|
||||
# 결과값 정리
|
||||
if block_elements and isinstance(block_answer, list):
|
||||
# 1. 비어있는 sublist를 ['None']으로 먼저 치환합니다.
|
||||
# - sublist가 비어있지 않으면(if sublist) sublist를 그대로 사용하고,
|
||||
# - 비어있으면 ['None']으로 대체합니다.
|
||||
processed_elements = [sublist if sublist else ['None'] for sublist in block_elements]
|
||||
found_values = [convert_to_str(x) for x in itertools.chain.from_iterable(processed_elements)]
|
||||
else:
|
||||
found_values = convert_to_str(block_elements[0]) if block_elements else None
|
||||
|
||||
# 실패 시 출력할 값을 위해 마지막으로 찾은 값을 저장
|
||||
last_found_values = found_values
|
||||
expected_str = convert_to_str(block_answer) if block_answer is not None else None
|
||||
|
||||
# 결과값 정리
|
||||
if block_elements and isinstance(block_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
|
||||
# 정답 조건 확인
|
||||
if block_elements:
|
||||
# 5-1. 정답(block_answer)이 없고, 요소만 찾으면 되는 경우
|
||||
if block_answer is None:
|
||||
is_block_correct = True
|
||||
# 5-2. 정답이 있고, 찾은 값과 일치하는 경우
|
||||
elif expected_str == found_values:
|
||||
is_block_correct = True
|
||||
|
||||
# 6. 정답을 찾았으면, 더 이상 다른 스크립트를 확인할 필요 없이 내부 반복문 탈출
|
||||
if is_block_correct:
|
||||
break # for single_script in scripts_to_check: 루프를 중단
|
||||
|
||||
expected_str = convert_to_str(block_answer) if block_answer is not None else None
|
||||
|
||||
# 비교 및 점수 처리
|
||||
if block_elements:
|
||||
if block_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_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_answer is None:
|
||||
total_points += block_points
|
||||
score_list.append(block_points)
|
||||
# 7. 내부 반복문 종료 후, 플래그를 기반으로 최종 점수 처리
|
||||
if is_block_correct:
|
||||
# 정답을 맞힌 경우
|
||||
if block_answer is not None:
|
||||
print(f"{question_key}-{block_index}: ✅ {expected_str} == {last_found_values}")
|
||||
else:
|
||||
print(f"{question_key}-{block_index}: Element Exists")
|
||||
total_points += block_points
|
||||
score_list.append(block_points)
|
||||
else:
|
||||
print(f"{question_key}-{block_index}: No elements found for {block_path}")
|
||||
# 모든 스크립트를 확인했지만 정답이 없는 경우
|
||||
if last_found_values is not None: # 요소는 찾았으나 값이 틀린 경우
|
||||
print(f"{question_key}-{block_index}: ❌ {expected_str} != {last_found_values}")
|
||||
else: # 요소를 전혀 찾지 못한 경우
|
||||
print(f"{question_key}-{block_index}: No elements found for {block_path}")
|
||||
score_list.append(0)
|
||||
|
||||
block_index += 1
|
||||
@@ -234,8 +334,9 @@ def main():
|
||||
timestamp = datetime.now().strftime("%y%m%d")
|
||||
test_mode = False # 테스트 모드 설정
|
||||
# test_mode = True # 테스트 모드 설정
|
||||
exam_round = "2509"
|
||||
exam_names = ["CAT_3_A"] # 여러 시험명을 리스트로 설정
|
||||
exam_round = "2510"
|
||||
# exam_names = ["CAT_3_A"] # 여러 시험명을 리스트로 설정
|
||||
exam_names = ["CAS_2_A"] # 여러 시험명을 리스트로 설정
|
||||
excel_list = []
|
||||
|
||||
for exam_name in exam_names:
|
||||
|
||||
Reference in New Issue
Block a user