2510회 채점자료 업데이트 / 스크립트 블럭 순서 재정렬 방식 변경

This commit is contained in:
2025-11-06 17:40:57 +09:00
parent 99250cd161
commit eb4e6071ad
12 changed files with 1383 additions and 50 deletions

View File

@@ -47,8 +47,8 @@ def copy_ent_files(source_root, target_root):
# 사용법 # 사용법
source_directory = r"D:\project\Entry\Entry-Scoring\시험자료\2509\A" # 원본 디렉토리 경로 source_directory = r"D:\project\data\CAS_제2510회 정기\제2510회 코딩활용능력 2급 정기 답안파일" # 원본 디렉토리 경로
target_directory = r".\ent\2509_CAT_3_A" target_directory = r".\ent\2510_CAS_2_A"
# target_directory_a = r"./output/A" # '1교시'의 타겟 경로 # target_directory_a = r"./output/A" # '1교시'의 타겟 경로
# target_directory_b = r"./output/B" # '2교시'의 타겟 경로 # target_directory_b = r"./output/B" # '2교시'의 타겟 경로
# target_directory_c = r"./output/C" # '3교시'의 타겟 경로 # target_directory_c = r"./output/C" # '3교시'의 타겟 경로

View File

@@ -50,8 +50,9 @@ def process_ent_files(ent_dir, output_dir):
# 실행 예 # 실행 예
if __name__ == "__main__": 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 = ["2508_CAS_2_A","2508_CAS_2_B"]
test_names = ["2510_CAS_2_A"]
for test_name in test_names: for test_name in test_names:
ent_dir = f".\\ent\\{test_name}" ent_dir = f".\\ent\\{test_name}"
output_dir = f".\\output\\{test_name}" output_dir = f".\\output\\{test_name}"

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

1008
correct/2510_CAS_2_A.json Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -34,3 +34,226 @@ Traceback (most recent call last):
File "D:\project\Entry\Entry-Scoring\main.py", line 162, in process_project File "D:\project\Entry\Entry-Scoring\main.py", line 162, in process_project
total_points += question_points total_points += question_points
TypeError: unsupported operand type(s) for +=: 'int' and 'NoneType' 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

191
main.py
View File

@@ -1,6 +1,5 @@
from jsonpath_ng.ext import parse from jsonpath_ng.ext import parse
import json import json
from itertools import chain
import os import os
import pandas as pd # 추가된 import import pandas as pd # 추가된 import
import unicodedata # 상단에 import 추가 import unicodedata # 상단에 import 추가
@@ -9,6 +8,8 @@ from datetime import datetime
import logging import logging
from logging_config import setup_logging # logging 설정을 위한 import from logging_config import setup_logging # logging 설정을 위한 import
import traceback import traceback
import itertools
import copy
from script_utils import extract_and_format_scripts # 스크립트 추출 함수 import from script_utils import extract_and_format_scripts # 스크립트 추출 함수 import
@@ -21,12 +22,12 @@ def read_json(file_path):
# 요소 탐색 함수 # 요소 탐색 함수
def find_element(project_data, jsonpath_expr): def find_element(project_data, jsonpath_expr):
jsonpath_expr = parse(jsonpath_expr) """
result = [] 주어진 데이터(project_data)에서 jsonpath 표현식에 일치하는 모든 값들을 찾아
for match in jsonpath_expr.find(project_data): 리스트로 반환합니다.
value = match.value """
result.append(value) parsed_expr = parse(jsonpath_expr)
return result return [match.value for match in parsed_expr.find(project_data)]
# 요소 탐색 함수 # 요소 탐색 함수
def find_script_element(project_data, jsonpath_expr): def find_script_element(project_data, jsonpath_expr):
@@ -40,13 +41,14 @@ def find_script_element(project_data, jsonpath_expr):
# jsonpath_expr_list 로 넘어온 jsonpath들을 하나씩 parse 해주고 결과를 result 리스트로 반환 # jsonpath_expr_list 로 넘어온 jsonpath들을 하나씩 parse 해주고 결과를 result 리스트로 반환
def find_list_element(data, jsonpath_expr_list): def find_list_element(data, jsonpath_expr_list):
result = [] """
주어진 데이터(data)에서 여러 jsonpath 표현식들에 일치하는 값들을 찾아
for jsonpath_expr in jsonpath_expr_list: 결과를 리스트의 리스트 형태로 반환합니다.
jsonpath_expr = parse(jsonpath_expr) """
result.append([match.value for match in jsonpath_expr.find(data)]) return [
[match.value for match in parse(expr).find(data)]
return result for expr in jsonpath_expr_list
]
# 스크립트 채점 진행 전 스크립트 블럭 순서가 when_run_button_click 1번째, when_clone_start 2번째 배열에 없으면 # 스크립트 채점 진행 전 스크립트 블럭 순서가 when_run_button_click 1번째, when_clone_start 2번째 배열에 없으면
# 리스트 순서 스왑해서 각각 0, 1번 순서로 배치될 수 있도록 함 # 리스트 순서 스왑해서 각각 0, 1번 순서로 배치될 수 있도록 함
@@ -113,10 +115,11 @@ def swap_script(origin):
result.extend([block for _, block in key_press_blocks]) result.extend([block for _, block in key_press_blocks])
# 3. clone_start와 message_cast 블록 추가 # 3. clone_start와 message_cast 블록 추가
if clone_start_block:
result.append(clone_start_block)
if message_cast_block: if message_cast_block:
result.extend(message_cast_block) result.extend(message_cast_block)
if clone_start_block:
result.append(clone_start_block)
# 4. 나머지 블록 추가 # 4. 나머지 블록 추가
result.extend(other_blocks) result.extend(other_blocks)
@@ -124,6 +127,49 @@ def swap_script(origin):
# 결과가 비어있으면 원본 반환 # 결과가 비어있으면 원본 반환
return result if result else 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): def clean_string(text):
"""문자열 끝의 . 또는 ! 제거""" """문자열 끝의 . 또는 ! 제거"""
if isinstance(text, str): if isinstance(text, str):
@@ -141,12 +187,34 @@ def process_project(project_data, scoring_data):
total_points = 0 total_points = 0
score_list = [] 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(): for question_key, question_info in scoring_data.items():
element_path = question_info.get('ele') element_path = question_info.get('ele')
question_type = question_info.get('type') question_type = question_info.get('type')
block_list = question_info.get('blocks') block_list = question_info.get('blocks')
expected_answer = question_info.get('answer') 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}") print(f"▶ Processing question: {question_key}")
@@ -173,14 +241,16 @@ def process_project(project_data, scoring_data):
elif question_type == "script": elif question_type == "script":
script_raw = find_script_element(project_data, element_path) script_raw = find_script_element(project_data, element_path)
script_json = json.loads(script_raw) if script_raw else None 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 block_index = 1
for block in block_list: for block in block_list:
block_type = block.get('type') block_type = block.get('type')
block_path = block.get('ele') block_path = block.get('ele')
block_answer = block.get('answer', None) block_answer = block.get('answer', None)
block_points = block.get('points') block_points = block.get('point')
if script_data is None: if script_data is None:
print(f"{question_key}-{block_index}: Script Not Found") print(f"{question_key}-{block_index}: Script Not Found")
@@ -188,35 +258,65 @@ def process_project(project_data, scoring_data):
block_index += 1 block_index += 1
continue continue
# 블록 요소 검색 # 1. 현재 블록(문제)이 정답인지 판별하는 플래그
if block_type == "list": is_block_correct = False
block_elements = find_list_element(script_data, block_path)
else:
block_elements = find_element(script_data, block_path)
# 결과값 정리 # 2. 로깅을 위해 마지막으로 찾은 값을 저장할 변수
if block_elements and isinstance(block_answer, list): last_found_values = None
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_answer) if block_answer is not None else None # 3. script_data가 단일 객체여도 처리 가능하도록 리스트로 통일
scripts_to_check = script_data if isinstance(script_data, list) else [script_data]
# 비교 및 점수 처리 # 4. 여러 스크립트 뭉치를 순회하며 정답이 하나라도 있는지 확인
if block_elements: for single_script in scripts_to_check:
if block_answer is not None and expected_str != found_values: # 단일 스크립트 객체(single_script)에 대해 블록 요소 검색
print(f"{question_key}-{block_index}: ❌ {expected_str} != {found_values}") if block_type == "list":
score_list.append(0) block_elements = find_list_element(single_script, block_path)
elif block_answer is not None and expected_str == found_values: else:
print(f"{question_key}-{block_index}: ✅ {expected_str} == {found_values}") block_elements = find_element(single_script, block_path)
total_points += block_points
score_list.append(block_points) # 결과값 정리
elif block_answer is None: if block_elements and isinstance(block_answer, list):
total_points += block_points # 1. 비어있는 sublist를 ['None']으로 먼저 치환합니다.
score_list.append(block_points) # - 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:
# 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: 루프를 중단
# 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") print(f"{question_key}-{block_index}: Element Exists")
total_points += block_points
score_list.append(block_points)
else: 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) score_list.append(0)
block_index += 1 block_index += 1
@@ -234,8 +334,9 @@ def main():
timestamp = datetime.now().strftime("%y%m%d") timestamp = datetime.now().strftime("%y%m%d")
test_mode = False # 테스트 모드 설정 test_mode = False # 테스트 모드 설정
# test_mode = True # 테스트 모드 설정 # test_mode = True # 테스트 모드 설정
exam_round = "2509" exam_round = "2510"
exam_names = ["CAT_3_A"] # 여러 시험명을 리스트로 설정 # exam_names = ["CAT_3_A"] # 여러 시험명을 리스트로 설정
exam_names = ["CAS_2_A"] # 여러 시험명을 리스트로 설정
excel_list = [] excel_list = []
for exam_name in exam_names: for exam_name in exam_names: