2622 채점자료 업데이트

This commit is contained in:
2026-02-11 16:06:09 +09:00
parent 28c0a9c4e1
commit e3cf4ea53f
40 changed files with 4402 additions and 816 deletions

View File

@@ -3,11 +3,11 @@ import shutil
from pathlib import Path
import re
# 채점자료 정답 파일만 복사하는 스크립트
# 회차별채점자료/회차명 폴더안 정답 파일만 복사하는 스크립트
# ===== 사용자 설정 =====
source_dir = r"D:\project\HWP\HWP-Scoring\회차별채점자료\2601"
exam_round = "2601" # 회차명
source_dir = r"D:\project\HWP\HWP-Scoring\회차별채점자료\2622"
exam_round = "2622" # 회차명
exam_code = "DIW" # 코드명
# =======================
@@ -16,8 +16,9 @@ def get_exam_type(filename: str):
"""
파일명에서 확장자 앞의 마지막 알파벳을 추출 (예: 국어A.hwpx → A)
"""
match = re.search(r"([A-Za-z])\.hwpx$", filename)
return match.group(1).upper() if match else None
# match = re.search(r"([A-Za-z])\.hwpx$", filename)
match = re.search(r'[A-Za-z](?=[^A-Za-z]*\.[^.]*$)', filename)
return match.group().upper() if match else None
def copy_exam_files():
@@ -30,14 +31,14 @@ def copy_exam_files():
copied = 0
for path in src.rglob("*"):
if path.is_file() and path.suffix.lower() == ".hwpx":
if path.is_file() and path.suffix.lower() == ".hwpx" and "정답" in path.name:
exam_type = get_exam_type(path.name)
if not exam_type:
continue # 마지막 문자가 알파벳이 아니면 건너뜀
dest_dir = base_dest / exam_type / exam_code
dest_dir.mkdir(parents=True, exist_ok=True)
dest_path = dest_dir / path.name
dest_path = dest_dir / f"DIW_{exam_round}{exam_type}.hwpx"
# 같은 이름의 파일이 있을 경우 숫자 붙이기
counter = 1

View File

@@ -41,10 +41,10 @@ def copy_dic_subdirs(source_root, target_root_a, target_root_b, target_root_c, t
print(f"Skipping {dir_name} under {parent_dir}, as it doesn't match '2교시' or '3교시'.")
# 사용법
exam_round = "2601_2"
exam_round = "2601"
# exam_round = "2510_4"
# source_directory = r"D:\project\data\제2510회 수시2(제주)\답안파일\제2510회 수시2 제주지부_답안파일"
source_directory = r"D:\project\data\제2601회 수시2(제주)\제2601회 수시2(제주)\답안파일\2601회 수시2 답안파일"
source_directory = r"D:\project\data\제2601회 정기\제2601회 정기\과목별답안파일"
target_directory_a = f".\\input\\{exam_round}\\A" # '1교시'의 타겟 경로

View File

@@ -76,8 +76,8 @@ def copy_exam_files(exam_round, exam_codes, source_dir):
# 사용 예시
if __name__ == "__main__":
# [source_dir경로\DIW] 디렉토리 안에 A형, B형... 폴더가 존재해야 함
exam_round = "2512"
exam_round = "2601"
exam_codes = ["DIW"]
source_dir = r"D:\project\data\제2512회 정기_DIAT\답안파일"
source_dir = r"D:\project\data\제2601회 정기\과목별답안파일"
copy_exam_files(exam_round, exam_codes, source_dir)

View File

@@ -166,7 +166,7 @@ if __name__ == "__main__":
setup_logging()
exam_rounds = [
"2601",
"2622",
]
# 변환할 폴더 경로 설정
@@ -178,6 +178,8 @@ if __name__ == "__main__":
(f"D:\\project\\HWP\\HWP-Scoring\\input\\{exam_round}\\C\\DIW",f"D:\\project\\HWP\\HWP-Scoring\\output\\{exam_round}\\C\\DIW"),
(f"D:\\project\\HWP\\HWP-Scoring\\input\\{exam_round}\\D\\DIW",f"D:\\project\\HWP\\HWP-Scoring\\output\\{exam_round}\\D\\DIW"),
(f"D:\\project\\HWP\\HWP-Scoring\\input\\{exam_round}\\E\\DIW",f"D:\\project\\HWP\\HWP-Scoring\\output\\{exam_round}\\E\\DIW"),
# (f"D:\\project\\HWP\\HWP-Scoring\\input\\{exam_round}",f"D:\\project\\HWP\\HWP-Scoring\\output\\{exam_round}"),
]
# folders = [

850
JSON/2622/DIW_2622A.json Normal file
View File

@@ -0,0 +1,850 @@
{
"0": {
"0": {
"path": "",
"path2": "",
"points": 0,
"category": "파일저장",
"item": "파일명 (수검번호.hwp/hwpx)"
},
"1": {
"path": "//PAGEMARGIN",
"value": {
"Top": 20,
"Bottom": 20,
"Left": 20,
"Right": 20,
"Header": 10,
"Footer": 10,
"Gutter": 0
},
"tolerance": 1,
"points": 4,
"category": "PageSetting",
"item": "A4용지, 왼쪽/오른쪽/위쪽/아래쪽 (각20mm), 머리말/꼬리말 (10mm), 제본(0mm)"
},
"2": {
"path": "",
"value": {
"FontName": "바탕",
"FontSize": "1000",
"Alignment": "Justify",
"LineSpacing": "160"
},
"points": 4,
"category": "BasicSetting",
"item": "글꼴 (바탕, 10pt), 양쪽정렬, 줄간격 (160%)"
},
"3": {
"path": "",
"value": null,
"points": 40,
"category": "오타감점",
"item": "오타 1개 -1점 / 2503회부터 오타 1개 -1점으로 변경"
}
},
"1": {
"1": {
"path": "//TEXTART[@Text='{searchValue}']/TEXTARTSHAPE/@FontName",
"searchValue": "나라사랑고궁사랑걷기대회",
"value": "돋움체",
"points": 1,
"category": "OneAnswer",
"item": "문구 (나라사랑고궁사랑걷기대회)/① 글씨체 (돋움체)"
},
"2": {
"path": "//TEXTART[@Text='{searchValue}']/descendant::WINDOWBRUSH/@FaceColor",
"searchValue": "나라사랑고궁사랑걷기대회",
"value": "241,31,18",
"points": 2,
"category": "Color",
"item": "문구 (나라사랑고궁사랑걷기대회)/② 채우기 : 색상(RGB:241,31,18)"
},
"3": {
"path": "//TEXTART[@Text='{searchValue}']/SHAPEOBJECT/SIZE/@Width",
"searchValue": "나라사랑고궁사랑걷기대회",
"value": "120",
"tolerance": 1,
"points": 2,
"category": "mmSize",
"item": "문구 (나라사랑고궁사랑걷기대회)/③ 크기-너비 (120 mm)"
},
"4": {
"path": "//TEXTART[@Text='{searchValue}']/SHAPEOBJECT/SIZE/@Height",
"searchValue": "나라사랑고궁사랑걷기대회",
"value": "20",
"tolerance": 1,
"points": 2,
"category": "mmSize",
"item": "문구 (나라사랑고궁사랑걷기대회)/④ 크기-높이 (20 mm)"
},
"5": {
"path": "//TEXTART[@Text='{searchValue}']/SHAPEOBJECT/POSITION/@TreatAsChar",
"searchValue": "나라사랑고궁사랑걷기대회",
"value": "true",
"points": 2,
"category": "OneAnswer",
"item": "문구 (나라사랑고궁사랑걷기대회)/⑤ 위치 (글자처럼 취급)"
},
"6": {
"path": "//PARASHAPE[@Id=//P[.//TEXTART[@Text='{searchValue}']]/@ParaShape]/@Align",
"searchValue": "나라사랑고궁사랑걷기대회",
"value": "Center",
"points": 2,
"category": "OneAnswer",
"item": "문구 (나라사랑고궁사랑걷기대회)/⑥ 정렬 (가운데 정렬)"
},
"7": {
"path": "//TEXTART[@Text='{searchValue}']",
"searchValue": "나라사랑고궁사랑걷기대회",
"value": true,
"points": 2,
"category": "Boolean",
"item": "문구 (나라사랑고궁사랑걷기대회)/⑦ 글맵시모양 (육안확인)"
},
"8": {
"path": "//RECTANGLE[.//CHAR[text()='{searchValue}']]/SHAPEOBJECT/SIZE",
"searchValue": "도",
"value": {
"Height": 2800,
"Width": 2800
},
"tolerance": 200,
"points": 1,
"category": "TwoLineSize",
"item": "어/① 모양 (2줄)"
},
"9": {
"path": "//TEXT[CHAR[text()='{searchValue}']]/@CharShape",
"searchValue": "도",
"value": "중고딕",
"points": 1,
"category": "FontName",
"item": "어/② 글씨체 (중고딕)"
},
"10": {
"path": "//RECTANGLE[.//CHAR[text()='{searchValue}']]//WINDOWBRUSH/@FaceColor",
"searchValue": "도",
"value": "43,224,208",
"points": 2,
"category": "Color",
"item": "어/③ 면색 : 색상(RGB:43,224,208)"
},
"11": {
"path": "//RECTANGLE[.//CHAR[text()='{searchValue}']]//OUTSIDEMARGIN/@Right",
"searchValue": "도",
"value": "3.0",
"tolerance": 1,
"points": 2,
"category": "mmSize",
"item": "어/④ 본문과의 간격 : 3.0mm"
},
"12": {
"path": "//CHARSHAPE[@Id=//CHAR[contains(text(),'{searchValue}')]/parent::TEXT/@CharShape]",
"searchValue": "나라 사랑 고궁 사랑 걷기대회",
"value": "BOLD",
"points": 1,
"category": "FontAttribute",
"item": "문구 (평생교육사 양성 교육과정 현황과 개선 방안 탐색)/① BOLD"
},
"13": {
"path": "//CHARSHAPE[@Id=//CHAR[contains(text(),'{searchValue}')]/parent::TEXT/@CharShape]",
"searchValue": "나라 사랑 고궁 사랑 걷기대회",
"value": "ITALIC",
"points": 1,
"category": "FontAttribute",
"item": "문구 (평생교육사 양성 교육과정 현황과 개선 방안 탐색)/② ITALIC"
},
"14": {
"path": "//CHAR[contains(string(.),'{char1}')]/text()",
"path2": "//CHAR[contains(string(.),'{char2}')]/text()",
"path3": "//CHAR[contains(string(.),'{char3}')]/text()",
"char1": "◈",
"char2": "◈",
"char3": "※",
"value": 3,
"points": 3,
"category": "SpecialChar",
"item": "① ◈, ② ◈, ③ ※"
},
"15": {
"path": "//CHAR[contains(text(),'{searchValue}')]/parent::TEXT/@CharShape",
"searchValue": "참가안내",
"value": "궁서",
"points": 1,
"category": "FontName",
"item": "문구 (◈ 참가안내 ◈)/① 글씨체 (궁서)"
},
"16": {
"path": "//PARASHAPE[@Id=//CHAR[contains(text(),'{match_str}')]/ancestor::P/@ParaShape]/@Align",
"match_str": "참가안내",
"value": "Center",
"points": 1,
"category": "Align",
"item": "문구 (◈ 참가안내 ◈)/② 정렬 (가운데 정렬)"
},
"17": {
"path": "//CHARSHAPE[@Id=//TEXT[CHAR[text()='{searchValue}']]/@CharShape]",
"hyperlink_ptag": "//P[.//FIELDBEGIN[@Type='Hyperlink']]",
"searchValue": "나라사랑고궁사랑걷기대회 홈페이지(http://www.ihd.or.kr)",
"value": "ITALIC",
"points": 1,
"category": "FontAttribute",
"item": "문구 (나라사랑고궁사랑걷기대회 홈페이지(http://www.ihd.or.kr))/① ITALIC"
},
"18": {
"path": "//CHARSHAPE[@Id=//TEXT[CHAR[text()='{searchValue}']]/@CharShape]",
"hyperlink_ptag": "//P[.//FIELDBEGIN[@Type='Hyperlink']]",
"searchValue": "나라사랑고궁사랑걷기대회 홈페이지(http://www.ihd.or.kr)",
"value": "UNDERLINE",
"points": 1,
"category": "FontAttribute",
"item": "문구 (나라사랑고궁사랑걷기대회 홈페이지(http://www.ihd.or.kr))/② UNDERLINE"
},
"19": {
"path": "//PARASHAPE[@Id=//CHAR[contains(text(),'{searchValue}')]/ancestor::P/following-sibling::P[1]/@ParaShape]/PARAMARGIN",
"searchValue": "기타사항",
"value": {
"Left": 10,
"Indent": 12
},
"points": 2,
"category": "ParaShape",
"item": "문구 (※ 기타… 이하 문단)/왼쪽여백 (10), 내어쓰기 (12)",
"desc": "내부적으로 내어쓰기는 음수값 / JSON value값은 양수로 입력"
},
"20": {
"path": "//CHARSHAPE[@Id=//CHAR[contains(text(),'{searchValue}')]/parent::TEXT/@CharShape]/@Height",
"searchValue": "2026. 02. 08.",
"value": "1400",
"points": 1,
"category": "OneAnswer",
"item": "문구 (2026. 02. 08.)/① 크기 (1400)",
"desc": "1pt당 100"
},
"21": {
"path": "//PARASHAPE[@Id=//CHAR[contains(text(),'{searchValue}')]/ancestor::P/@ParaShape]/@Align",
"searchValue": "2026. 02. 08.",
"value": "Center",
"points": 1,
"category": "OneAnswer",
"item": "문구 (2026. 02. 08.)/② 정렬 (가운데 정렬)"
},
"22": {
"path": "//TEXT[CHAR[text()='{searchValue}']]/@CharShape",
"searchValue": "고궁걷기대회추진위원회",
"value": "돋움",
"points": 1,
"category": "FontName",
"item": "문구 (고궁걷기대회추진위원회)/① 글씨체 (돋움)"
},
"23": {
"path": "//CHARSHAPE[@Id=//TEXT[CHAR[text()='{searchValue}']]/@CharShape]/@Height",
"searchValue": "고궁걷기대회추진위원회",
"value": "2200",
"points": 1,
"category": "OneAnswer",
"item": "문구 (고궁걷기대회추진위원회)/② 크기 (2200)"
},
"24": {
"path": "//PARASHAPE[@Id=//CHAR[text()='{searchValue}']/ancestor::P/@ParaShape]/@Align",
"searchValue": "고궁걷기대회추진위원회",
"value": "Center",
"points": 1,
"category": "OneAnswer",
"item": "문구 (고궁걷기대회추진위원회)/③ 정렬 (가운데 정렬)"
},
"25": {
"path": "//TEXT[CHAR[text()='{searchValue}']]/@CharShape",
"searchValue": "DIAT",
"value": "굴림",
"points": 1,
"category": "Header.FontName",
"item": "문구 (DIAT)/① 글꼴 (굴림)"
},
"26": {
"path": "//CHARSHAPE[@Id=//TEXT[CHAR[text()='{searchValue}']]/@CharShape]/@Height",
"searchValue": "DIAT",
"value": "900",
"points": 1,
"category": "Header.OneAnswer",
"item": "문구 (DIAT)/② 크기 (9pt)"
},
"27": {
"path": "//PARASHAPE[@Id=//CHAR[text()='{searchValue}']/parent::TEXT/parent::P/@ParaShape]/@Align",
"searchValue": "DIAT",
"value": "Right",
"points": 1,
"category": "Header.OneAnswer",
"item": "문구 (DIAT)/③ 정렬 (오른쪽 정렬)"
},
"28": {
"path": "//PAGENUM/@FormatType",
"value": "LatinCapital",
"points": 2,
"category": "PageNumber",
"item": "① 쪽 번호 매기기 (가,나,다 순으로)",
"desc1": {
"가,나,다": "HangulSyllable",
"1,2,3": "Digit",
"일,이,삼": "HangulPhonetic",
"갑,을,병": "DecagonCircle",
"A,B,C": "LatinCapital",
"a,b,c": "LatinSmall",
"Ⓐ,Ⓑ,Ⓒ": "CircledLatinCapital",
"①,②,③": "CircledDigit",
"一,二,三": "Ideograph",
"㉠,㉡,㉢": "CircledHangulJamo",
"ⓐ,ⓑ,ⓒ": "CircledLatinSmall",
"㊀,㊁,㊂": "CircledIdeograph",
"i,ii,iii": "RomanSmall",
"I,II,III": "RomanCapital",
"甲,乙,丙": "DecagonCircleHanja",
"+,++,+++": "UserChar",
"*,**,***": "UserChar",
"정답에 맞는 값 value에 입력": ""
},
"desc2": "1, 2페이지 모두 정답이어야 점수 부여"
},
"29": {
"path": "//PAGENUM/@Pos",
"value": "BottomCenter",
"points": 2,
"category": "PageNumber",
"item": "가운데 아래",
"desc": "1, 2페이지 모두 정답이어야 점수 부여",
"desc2": {
"가운데 아래": "BottomCenter",
"오른쪽 아래": "BottomRight",
"왼쪽 아래": "BottomLeft"
}
},
"30": {
"path": "//CHAR[contains(text(),'{searchValue}')]",
"searchValue": "http",
"value": true,
"points": 2,
"category": "hyperlink",
"item": "문구 (http://www.ihd.or.kr)/하이퍼링크 없이 작성",
"desc": "searchValue에 해당하는 주소 문구에 하이퍼링크가 하나라도 설정되어 있으면 오답"
},
"31": {
"path": "//PARASHAPE[@Id='{parashape_id}']/PARAMARGIN/@LineSpacing",
"value": "190",
"first_word": "도",
"points": 2,
"category": "LineSpacing",
"item": "문제 1 줄간격 190% 설정",
"desc": "1페이지 문단의 줄간격이 정답이 아닌 문단이 있으면 False(감점), first_word 속성에 [문단 첫글자 장식]에 해당하는 글자를 입력해준다."
}
},
"2": {
"1": {
"path": "//PAGEBORDERFILL[@Type='Both' or @Type='Even']/@HeaderInside",
"path2": "//BORDERFILL[@Id=//PAGEBORDERFILL[@Type='Both' or @Type='Even']/@BorferFill]",
"value": {
"header_inside": true,
"all_double_slim": true
},
"points": 4,
"category": "PageBorder",
"item": "문제2 쪽테두리(이중 실선, 머리말 포함) 설정"
},
"2": {
"path": "count(//SECTION)>1",
"value": true,
"points": 3,
"category": "Boolean",
"item": "① 구역나누기",
"desc": "섹션이 1개 이상이면 점수부여"
},
"3": {
"path": "./TEXT/COLDEF/@Count",
"value": "2",
"points": 3,
"category": "TwoColumn",
"item": "② 다단 2단"
},
"4": {
"path": "//RECTANGLE/SHAPEOBJECT/SIZE/@Width",
"value": "65",
"points": 2,
"category": "Rectangle.mmSize",
"item": "문구 (궁궐 건축 양식)/① 크기-너비 (65 mm)"
},
"5": {
"path": "//RECTANGLE/SHAPEOBJECT/SIZE/@Height",
"value": "12",
"points": 2,
"category": "Rectangle.mmSize",
"item": "문구 (궁궐 건축 양식)/② 크기-높이 (12 mm)"
},
"6": {
"path": "//RECTANGLE//LINESHAPE",
"value": {
"Style": "DoubleSlim",
"Width": "283"
},
"points": 2,
"category": "Rectangle.LineShape",
"item": "문구 (궁궐 건축 양식)/③ 테두리 : 이중 실선(1.00mm)",
"desc": "1mm = 283pt value['Width']에 pt값 입력"
},
"7": {
"path": "//RECTANGLE/@Ratio",
"value": "50",
"points": 2,
"category": "Rectangle.OneAnswer",
"item": "문구 (궁궐 건축 양식)/④ 글상자 모서리 (둥근모양)",
"desc": "모서리 비율 반원:50 / 둥근모양:20"
},
"8": {
"path": "//RECTANGLE//WINDOWBRUSH/@FaceColor",
"value": "240,199,123",
"points": 2,
"category": "Rectangle.Color",
"item": "문구 (궁궐 건축 양식)/⑤ 채우기 : 색상(RGB:240,199,123)"
},
"9": {
"path": "//RECTANGLE/SHAPEOBJECT/POSITION/@TreatAsChar",
"value": "true",
"points": 1,
"category": "Rectangle.OneAnswer",
"item": "문구 (궁궐 건축 양식)/⑥ 글상자 위치 (글자처럼 취급)"
},
"10": {
"path": "//PARASHAPE[@Id='{rect_parashape_id}']/@Align",
"value": "Center",
"points": 1,
"category": "Rectangle.TextBoxAlign",
"item": "문구 (궁궐 건축 양식)/⑦ 글상자 정렬 (가운데 정렬)"
},
"11": {
"path": ".//RECTANGLE//TEXT/@CharShape",
"value": "견고딕",
"points": 1,
"category": "Rectangle.FontName",
"item": "문구 (궁궐 건축 양식)/⑧ 글씨체 (견고딕)"
},
"12": {
"path": "//CHARSHAPE[@Id='{rect_charshape_id}']/@Height",
"value": "2000",
"points": 1,
"category": "Rectangle.FontSize",
"item": "문구 (궁궐 건축 양식)/⑨ 글씨크기 (2000)",
"desc": "1pt당 100"
},
"13": {
"path": "//PARASHAPE[@Id={rect_parashape_id}]/@Align",
"value": "Center",
"points": 1,
"category": "Rectangle.TextBoxAlign",
"item": "문구 (궁궐 건축 양식)/⑩ 정렬 (가운데 정렬)"
},
"14": {
"path": "//BINITEM[@BinData=//PICTURE/IMAGE/@BinItem][@Format='JPG' or @Format='JPEG' or @Format='PNG']",
"value": true,
"points": 2,
"category": "Boolean",
"item": "① 파일명 \"그림A.jpg\" 삽입",
"desc": "첨부 이미지 파일명 손상으로 정상적인 채점이 불가한 경우가 발견되어서 이미지 첨부 여부로 채점 방식 변경 (7/3)"
},
"15": {
"path": "//PICTURE[./IMAGE[@BinItem=//BINITEM[@Format='JPG' or @Format='JPEG' or @Format='PNG']/@BinData]]/SHAPEOBJECT/SIZE/@Width",
"value": "85",
"points": 2,
"category": "mmSize",
"item": "② 크기-너비 (85 mm)"
},
"16": {
"path": "//PICTURE[./IMAGE[@BinItem=//BINITEM[@Format='JPG' or @Format='JPEG' or @Format='PNG']/@BinData]]/SHAPEOBJECT/SIZE/@Height",
"value": "40",
"points": 2,
"category": "mmSize",
"item": "③ 크기-높이 (40 mm)"
},
"17": {
"path": "//PICTURE[./IMAGE[@BinItem=//BINITEM[@Format='JPG' or @Format='JPEG' or @Format='PNG']/@BinData]]/SHAPEOBJECT/POSITION[not(@TreatAsChar='true') and @HorzRelTo='Page']/@HorzOffset",
"value": "0",
"points": 2,
"category": "mmSize",
"item": "④ 위치 (어울림 : 가로-쪽의 왼쪽 0mm)"
},
"18": {
"path": "//PICTURE[./IMAGE[@BinItem=//BINITEM[@Format='JPG' or @Format='JPEG' or @Format='PNG']/@BinData]]/SHAPEOBJECT/POSITION[not(@TreatAsChar='true') and @HorzRelTo='Page']/@VertOffset",
"value": "24",
"points": 2,
"category": "mmSize",
"item": "⑤ 위치 (어울림 : 세로-쪽의 위 24 mm)"
},
"19": {
"path": "//TEXT[CHAR[text()='{searchValue}']]/@CharShape",
"searchValue": "1. 궁궐 건축의 이해",
"value": "돋움",
"points": 1,
"category": "FontName",
"item": "문구① (1. 궁궐 건축의 이해)/① 글씨체 (돋움)"
},
"20": {
"path": "//CHARSHAPE[@Id=//TEXT[CHAR[text()='{searchValue}']]/@CharShape]/@Height",
"searchValue": "1. 궁궐 건축의 이해",
"value": "1200",
"points": 1,
"category": "OneAnswer",
"item": "문구① (1. 궁궐 건축의 이해)/② 크기 (1200)"
},
"21": {
"path": "//CHARSHAPE[@Id=//TEXT[CHAR[text()='{searchValue}']]/@CharShape]",
"searchValue": "1. 궁궐 건축의 이해",
"value": "BOLD",
"points": 1,
"category": "FontAttribute",
"item": "문구① (1. 궁궐 건축의 이해)/③ 진하게"
},
"22": {
"path": "//TEXT[CHAR[text()='{searchValue}']]/@CharShape",
"searchValue": "2. 경복궁",
"value": "돋움",
"points": 1,
"category": "FontName",
"item": "문구② (2. 경복궁)/① 글씨체 (돋움)"
},
"23": {
"path": "//CHARSHAPE[@Id=//TEXT[CHAR[text()='{searchValue}']]/@CharShape]/@Height",
"searchValue": "2. 경복궁",
"value": "1200",
"points": 1,
"category": "OneAnswer",
"item": "문구② (2. 경복궁)/② 크기 (1200)"
},
"24": {
"path": "//CHARSHAPE[@Id=//TEXT[CHAR[text()='{searchValue}']]/@CharShape]",
"searchValue": "2. 경복궁",
"value": "BOLD",
"points": 1,
"category": "FontAttribute",
"item": "문구② (2. 경복궁)/③ 진하게"
},
"25": {
"path": "boolean(//TEXT[CHAR[contains(text(),'{option}')]]/FOOTNOTE)",
"path2": "boolean(//CHAR[substring(., string-length(.) - string-length('{option}') + 1) = '{option}']/following-sibling::FOOTNOTE/descendant::CHAR)",
"option": "종묘",
"value": true,
"points": 2,
"category": "Boolean",
"item": "문구 (종묘)/① 각주 설정 및 문구 입력"
},
"26": {
"path": "//CHAR[contains(text(),'{searchValue}')]/parent::TEXT/@CharShape",
"searchValue": "조선 시대에 임금과 왕비의 위패를 모시던 사당",
"value": "궁서체",
"points": 1,
"category": "FontName",
"item": "문구 (종묘)/② 글씨체 (궁서체)"
},
"27": {
"path": "//CHARSHAPE[@Id=//TEXT[CHAR[contains(text(),'{searchValue}')]]/@CharShape]/@Height",
"searchValue": "조선 시대에 임금과 왕비의 위패를 모시던 사당",
"value": "900",
"points": 1,
"category": "OneAnswer",
"item": "문구 (종묘)/③ 크기 (9pt)"
},
"28": {
"path": "//P[TEXT[CHAR[contains(text(), '{searchValue}')]]]//AUTONUMFORMAT/@Type",
"searchValue": "조선 시대에 임금과 왕비의 위패를 모시던 사당",
"value": "UserChar",
"points": 2,
"category": "OneAnswer",
"item": "문구 (전당)/④ 각주 번호모양",
"desc": {
"가,나,다": "HangulSyllable",
"1,2,3": "Digit",
"일,이,삼": "HangulPhonetic",
"갑,을,병": "DecagonCircle",
"A,B,C": "LatinCapital",
"a,b,c": "LatinSmall",
"Ⓐ,Ⓑ,Ⓒ": "CircledLatinCapital",
"①,②,③": "CircledDigit",
"一,二,三": "Ideograph",
"㉠,㉡,㉢": "CircledHangulJamo",
"ⓐ,ⓑ,ⓒ": "CircledLatinSmall",
"㊀,㊁,㊂": "CircledIdeograph",
"i,ii,iii": "RomanSmall",
"I,II,III": "RomanCapital",
"甲,乙,丙": "DecagonCircleHanja",
"+,++,+++": "UserChar",
"*,**,***": "UserChar",
"정답에 맞는 값 value에 입력": ""
}
},
"29": {
"path": "boolean(//CHAR[contains(text(),'{ignoreWord}')])",
"ignoreWord": "Palace",
"value": true,
"points": 3,
"category": "Boolean",
"item": "Palace/영단어 미입력, 대소문자/오타 시 전체 감점",
"desc": ""
},
"30": {
"path": "//CHAR[contains(text(),'{kor}')][contains(text(),'{chn}')]",
"word": [
["별궁", "別宮"],
["행궁", "行宮"],
["도읍", "都邑"],
["남면", "南面"],
["영추", "迎秋"]
],
"value": 10,
"points": 10,
"category": "Hanja",
"item": "① 별궁(別宮), ② 행궁(行宮), ③ 도읍(都邑), ④ 남면(南面), ⑤ 영추(迎秋)"
},
"31": {
"path": "boolean(//CHAR[contains(translate(text(), ' ', ''),'르는지방')])",
"value": true,
"points": 3,
"category": "Boolean",
"item": "문구 (…일정 기간씩 머무르은 지방에…)>'은' → '는' 글자바꿈"
},
"32": {
"path": "boolean(//CHAR[contains(translate(text(), ' ', ''),'물로하층')])",
"value": true,
"points": 3,
"category": "Boolean",
"item": "문구 (…누각 하층은 건물로 네모진…)>'하층은' / '건물로' 순서바꿈"
},
"33": {
"path": "//TEXT[CHAR[contains(text(),'{searchValue}')]]/@CharShape",
"searchValue": "연령별 참가 만족도 조사(단위:%)",
"value": "굴림체",
"points": 1,
"category": "FontName",
"item": "제목 문구 (연령별 참가 만족도 조사(단위:%))/① 글씨체 (굴림체)"
},
"34": {
"path": "//CHARSHAPE[@Id=//TEXT[CHAR[text()='{searchValue}']]/@CharShape]/@Height",
"searchValue": "연령별 참가 만족도 조사(단위:%)",
"value": "1200",
"points": 1,
"category": "OneAnswer",
"item": "제목 문구 (연령별 참가 만족도 조사(단위:%))/② 크기 (1200)"
},
"35": {
"path": "//CHARSHAPE[@Id=//TEXT[CHAR[text()='{searchValue}']]/@CharShape]",
"searchValue": "연령별 참가 만족도 조사(단위:%)",
"value": "BOLD",
"points": 1,
"category": "FontAttribute",
"item": "제목 문구 (연령별 참가 만족도 조사(단위:%))/③ 진하게"
},
"36": {
"path": "//PARASHAPE[@Id=//P[.//CHAR[text()='{searchValue}']]/@ParaShape]/@Align",
"searchValue": "연령별 참가 만족도 조사(단위:%)",
"value": "Center",
"points": 1,
"category": "OneAnswer",
"item": "제목 문구 (연령별 참가 만족도 조사(단위:%))/④ 정렬 (가운데 정렬)"
},
"37": {
"path": "//BORDERFILL[@Id=//TABLE/ROW[1]/CELL/@BorderFill]/FILLBRUSH/WINDOWBRUSH/@FaceColor",
"path2": "//BORDERFILL[@Id=//CELLZONE[@StartRowAddr='0' and @EndRowAddr='0' and @StartColAddr='0' and @EndColAddr=(ancestor::TABLE[1]/@ColCount)-1]/@BorderFill]/FILLBRUSH/WINDOWBRUSH/@FaceColor",
"value": "205,170,213",
"points": 2,
"category": "Color",
"item": "위쪽 제목 셀/① 색상(RGB:205,170,213)"
},
"38": {
"path": "//CHARSHAPE[@Id=//TABLE/ROW[1]/descendant::TEXT/@CharShape]",
"value": "BOLD",
"points": 1,
"category": "FontAttribute",
"item": "위쪽 제목 셀/② 진하게",
"desc": "글자 속성이라 CELLZONE으로 적용 되지 않음"
},
"39": {
"path": "//BORDERFILL[@Id=//TABLE/ROW[1]/CELL/@BorderFill]/BOTTOMBORDER/@Type",
"path2": "//BORDERFILL[@Id=//CELLZONE[@StartRowAddr='0' and @EndRowAddr='0' and @StartColAddr='0' and @EndColAddr=(ancestor::TABLE[1]/@ColCount)-1]/@BorderFill]/BOTTOMBORDER/@Type",
"value": "DoubleSlim",
"points": 2,
"category": "TableAnswer",
"item": "제목 셀 아래선/① 이중실선"
},
"40": {
"path": "//BORDERFILL[@Id=//TABLE/ROW[1]/CELL/@BorderFill]/BOTTOMBORDER/@Width",
"path2": "//BORDERFILL[@Id=//CELLZONE[@StartRowAddr='0' and @EndRowAddr='0' and @StartColAddr='0' and @EndColAddr=(ancestor::TABLE[1]/@ColCount)-1]/@BorderFill]/BOTTOMBORDER/@Width",
"value": "0.5mm",
"points": 2,
"category": "TableAnswer",
"item": "제목 셀 아래선/② 0.5mm"
},
"41": {
"path": "//TABLE//TEXT/@CharShape",
"path2": "//FONTFACE[@Lang='Hangul']/FONT[@Id=//CHARSHAPE[@Id=//TABLE/ROW/descendant::TEXT/@CharShape]/FONTID/@Hangul]/@Name",
"value": "맑은고딕",
"points": 1,
"category": "TableFontName",
"category_tmp": "FontName",
"item": "글자모양/① 글씨체 (맑은고딕)",
"desc": "테이블 폰트명 문항은 테이블의 모든 셀이 정답폰트와 일치해야 함, 하나만 일치해도 정답으로 채점할 경우 category값을 FontName으로 변경"
},
"42": {
"path": "//CHARSHAPE[@Id=//TABLE//TEXT/@CharShape]/@Height",
"value": "1000",
"points": 1,
"category": "TableAnswer",
"item": "글자모양/② 크기 (1000)"
},
"43": {
"path": "//PARASHAPE[@Id=//TABLE/ROW//P/@ParaShape]/@Align",
"value": "Center",
"points": 1,
"category": "TableAnswer",
"item": "글자모양/③ 정렬 (가운데 정렬)"
},
"44": {
"path": "boolean(//TABLE[1]/ROW[last()]/CELL[position()=last()]//FIELDBEGIN[starts-with(@Command, '={option}')]) and boolean(//TABLE[1]/ROW[last()]/CELL[position()=last()-1]//FIELDBEGIN[starts-with(@Command, '={option}')])",
"option": "AVG",
"value": true,
"points": 4,
"category": "Boolean",
"item": "블록 계산식/합계",
"desc": "option값에 합계는 SUM / 평균은 AVG"
},
"45": {
"chart_xpath": "",
"chart_type": "묶은 가로 막대형",
"value": true,
"points": 2,
"category": "ChartType",
"item": "① 종류 (묶은 가로 막대형)",
"desc": "chart_type을 입력받아 차트타입에 맞는 xml요소가 있는지 내부적으로 검사, chart_type만 한글로 입력해주면 된다. (공백무시)"
},
"46": {
"chart_xpath": "//c:valAx/c:majorTickMark/@val",
"value": "out",
"points": 2,
"category": "ChartOneAnswer",
"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": "③ 크기-너비 (80 mm)"
},
"48": {
"path": "//OLE[@BinItem=//BINITEM[@Format='OLE']/@BinData]//SIZE/@Height",
"value": "90",
"points": 2,
"category": "mmSize",
"item": "④ 크기-높이 (90 mm)"
},
"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": "1400",
"points": 1,
"category": "OneAnswer",
"item": "제목 문구 (연령별 참가 만족도 조사)/② 크기 (1400)"
},
"52": {
"chart_xpath": "//a:t[text()='{searchValue}']/ancestor::a:r/a:rPr/@{option}",
"option": "b",
"searchValue": "연령별 참가 만족도 조사",
"value": "1",
"points": 1,
"category": "OneAnswer",
"item": "제목 문구 (연령별 참가 만족도 조사)/③ 기울임",
"desc": "option값 - 기울임(Italic):i / 굵게(Bold):b"
},
"53": {
"chart_xpath": "//c:catAx/c:txPr//a:ea/@typeface",
"value": "굴림체",
"points": 1,
"category": "ChartOneAnswer",
"item": "X축/① 글꼴 (굴림체)"
},
"54": {
"chart_xpath": "//c:catAx/c:txPr//a:defRPr/@sz",
"value": "900",
"points": 1,
"category": "ChartOneAnswer",
"item": "X축/② 크기 (9pt)"
},
"55": {
"chart_xpath": "//c:catAx/c:txPr//a:defRPr/@{option}",
"option": "i",
"value": "1",
"points": 1,
"category": "ChartOneAnswer",
"item": "X축/③ 기울임",
"desc": "option값 - 기울임(Italic):i / 굵게(Bold):b"
},
"56": {
"chart_xpath": "//c:valAx/c:txPr//a:ea/@typeface",
"value": "굴림체",
"points": 1,
"category": "ChartOneAnswer",
"item": "Y축/① 글꼴 (굴림체)"
},
"57": {
"chart_xpath": "//c:valAx/c:txPr//a:defRPr/@sz",
"value": "900",
"points": 1,
"category": "ChartOneAnswer",
"item": "Y축/② 크기 (9pt)"
},
"58": {
"chart_xpath": "//c:valAx/c:txPr//a:defRPr/@{option}",
"option": "i",
"value": "1",
"points": 1,
"category": "ChartOneAnswer",
"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"
}
}
}

View File

@@ -46,65 +46,65 @@
"1": {
"1": {
"path": "//TEXTART[@Text='{searchValue}']/TEXTARTSHAPE/@FontName",
"searchValue": "재활용의 날 행사",
"value": "맑은고딕",
"searchValue": "광양매실축제안내",
"value": "맑은 고딕",
"points": 1,
"category": "OneAnswer",
"item": "문구 (재활용의 날 행사)/① 글씨체 (맑은고딕)"
"item": "문구 (광양매실축제안내)/① 글씨체 (맑은 고딕)"
},
"2": {
"path": "//TEXTART[@Text='{searchValue}']/descendant::WINDOWBRUSH/@FaceColor",
"searchValue": "재활용의 날 행사",
"value": "49,95,151",
"searchValue": "광양매실축제안내",
"value": "199,80,124",
"points": 2,
"category": "Color",
"item": "문구 (재활용의 날 행사)/② 채우기 : 색상(RGB:49,95,151)"
"item": "문구 (광양매실축제안내)/② 채우기 : 색상(RGB:199,80,124)"
},
"3": {
"path": "//TEXTART[@Text='{searchValue}']/SHAPEOBJECT/SIZE/@Width",
"searchValue": "재활용의 날 행사",
"value": "100",
"searchValue": "광양매실축제안내",
"value": "110",
"tolerance": 1,
"points": 2,
"category": "mmSize",
"item": "문구 (재활용의 날 행사)/③ 크기-너비 (100 mm)"
"item": "문구 (광양매실축제안내)/③ 크기-너비 (110 mm)"
},
"4": {
"path": "//TEXTART[@Text='{searchValue}']/SHAPEOBJECT/SIZE/@Height",
"searchValue": "재활용의 날 행사",
"searchValue": "광양매실축제안내",
"value": "20",
"tolerance": 1,
"points": 2,
"category": "mmSize",
"item": "문구 (재활용의 날 행사)/④ 크기-높이 (20 mm)"
"item": "문구 (광양매실축제안내)/④ 크기-높이 (20 mm)"
},
"5": {
"path": "//TEXTART[@Text='{searchValue}']/SHAPEOBJECT/POSITION/@TreatAsChar",
"searchValue": "재활용의 날 행사",
"searchValue": "광양매실축제안내",
"value": "true",
"points": 2,
"category": "OneAnswer",
"item": "문구 (재활용의 날 행사)/⑤ 위치 (글자처럼 취급)"
"item": "문구 (광양매실축제안내)/⑤ 위치 (글자처럼 취급)"
},
"6": {
"path": "//PARASHAPE[@Id=//P[.//TEXTART[@Text='{searchValue}']]/@ParaShape]/@Align",
"searchValue": "재활용의 날 행사",
"searchValue": "광양매실축제안내",
"value": "Center",
"points": 2,
"category": "OneAnswer",
"item": "문구 (재활용의 날 행사)/⑥ 정렬 (가운데 정렬)"
"item": "문구 (광양매실축제안내)/⑥ 정렬 (가운데 정렬)"
},
"7": {
"path": "//TEXTART[@Text='{searchValue}']",
"searchValue": "재활용의 날 행사",
"searchValue": "광양매실축제안내",
"value": true,
"points": 2,
"category": "Boolean",
"item": "문구 (재활용의 날 행사)/⑦ 글맵시모양 (육안확인)"
"item": "문구 (광양매실축제안내)/⑦ 글맵시모양 (육안확인)"
},
"8": {
"path": "//RECTANGLE[.//CHAR[text()='{searchValue}']]/SHAPEOBJECT/SIZE",
"searchValue": "",
"searchValue": "",
"value": {
"Height": 2800,
"Width": 2800
@@ -116,23 +116,23 @@
},
"9": {
"path": "//TEXT[CHAR[text()='{searchValue}']]/@CharShape",
"searchValue": "",
"value": "돋움체",
"searchValue": "",
"value": "궁서체",
"points": 1,
"category": "FontName",
"item": "어/② 글씨체 (돋움체)"
"item": "어/② 글씨체 (궁서체)"
},
"10": {
"path": "//RECTANGLE[.//CHAR[text()='{searchValue}']]//WINDOWBRUSH/@FaceColor",
"searchValue": "",
"value": "210,154,216",
"searchValue": "",
"value": "186,255,26",
"points": 2,
"category": "Color",
"item": "어/③ 면색 : 색상(RGB:210,154,216)"
"item": "어/③ 면색 : 색상(RGB:186,255,26)"
},
"11": {
"path": "//RECTANGLE[.//CHAR[text()='{searchValue}']]//OUTSIDEMARGIN/@Right",
"searchValue": "",
"searchValue": "",
"value": "3.0",
"tolerance": 1,
"points": 2,
@@ -141,126 +141,126 @@
},
"12": {
"path": "//CHARSHAPE[@Id=//CHAR[contains(text(),'{searchValue}')]/parent::TEXT/@CharShape]",
"searchValue": "자원재활용이 이루어지는 과정을 담은 영상물 관람과 시설 견학이 있을 예정",
"value": "BOLD",
"searchValue": "봄 매화, 여름 매실로 우리 함께 힐링합시다",
"value": "ITALIC",
"points": 1,
"category": "FontAttribute",
"item": "문구 (자원재활용이 이루어지는 과정을 담은 영상물 관람과 시설 견학이 있을 예정)/① BOLD"
"item": "문구 (봄 매화, 여름 매실로 우리 함께 힐링합시다)/① ITALIC"
},
"13": {
"path": "//CHARSHAPE[@Id=//CHAR[contains(text(),'{searchValue}')]/parent::TEXT/@CharShape]",
"searchValue": "자원재활용이 이루어지는 과정을 담은 영상물 관람과 시설 견학이 있을 예정",
"searchValue": "봄 매화, 여름 매실로 우리 함께 힐링합시다",
"value": "UNDERLINE",
"points": 1,
"category": "FontAttribute",
"item": "문구 (자원재활용이 이루어지는 과정을 담은 영상물 관람과 시설 견학이 있을 예정)/② UNDERLINE"
"item": "문구 (봄 매화, 여름 매실로 우리 함께 힐링합시다)/② UNDERLINE"
},
"14": {
"path": "//CHAR[contains(string(.),'{char1}')]/text()",
"path2": "//CHAR[contains(string(.),'{char2}')]/text()",
"path3": "//CHAR[contains(string(.),'{char3}')]/text()",
"char1": "",
"char2": "",
"char1": "",
"char2": "",
"char3": "※",
"value": 3,
"points": 3,
"category": "SpecialChar",
"item": "① , ② , ③ ※"
"item": "① , ② , ③ ※"
},
"15": {
"path": "//CHAR[contains(text(),'{searchValue}')]/parent::TEXT/@CharShape",
"searchValue": "행사안내",
"value": "돋움",
"searchValue": "축제안내",
"value": "굴림체",
"points": 1,
"category": "FontName",
"item": "문구 (▣ 행사안내 )/① 글씨체 (돋움)"
"item": "문구 (→ 축제안내 )/① 글씨체 (굴림체)"
},
"16": {
"path": "//PARASHAPE[@Id=//CHAR[contains(text(),'{match_str}')]/ancestor::P/@ParaShape]/@Align",
"match_str": "행사안내",
"match_str": "축제안내",
"value": "Center",
"points": 1,
"category": "Align",
"item": "문구 (▣ 행사안내 )/② 정렬 (가운데 정렬)"
"item": "문구 (→ 축제안내 )/② 정렬 (가운데 정렬)"
},
"17": {
"path": "//CHARSHAPE[@Id=//TEXT[CHAR[text()='{searchValue}']]/@CharShape]",
"hyperlink_ptag": "//P[.//FIELDBEGIN[@Type='Hyperlink']]",
"searchValue": "부산시청 앞 광장, 부산시 쓰레기 매립장 등",
"searchValue": "광양매실축제 홈페이지(http://www.ihd.or.kr)",
"value": "BOLD",
"points": 1,
"category": "FontAttribute",
"item": "문구 (부산시청 앞 광장, 부산시 쓰레기 매립장 등)/① BOLD"
"item": "문구 (광양매실축제 홈페이지(http://www.ihd.or.kr))/① BOLD"
},
"18": {
"path": "//CHARSHAPE[@Id=//TEXT[CHAR[text()='{searchValue}']]/@CharShape]",
"hyperlink_ptag": "//P[.//FIELDBEGIN[@Type='Hyperlink']]",
"searchValue": "부산시청 앞 광장, 부산시 쓰레기 매립장 등",
"searchValue": "광양매실축제 홈페이지(http://www.ihd.or.kr)",
"value": "ITALIC",
"points": 1,
"category": "FontAttribute",
"item": "문구 (부산시청 앞 광장, 부산시 쓰레기 매립장 등)/② ITALIC"
"item": "문구 (광양매실축제 홈페이지(http://www.ihd.or.kr))/② ITALIC"
},
"19": {
"path": "//PARASHAPE[@Id=//CHAR[contains(text(),'{searchValue}')]/ancestor::P/following-sibling::P[1]/@ParaShape]/PARAMARGIN",
"searchValue": "기타사항",
"value": {
"Left": 12,
"Left": 15,
"Indent": 12
},
"points": 2,
"category": "ParaShape",
"item": "문구 (※ 기타… 이하 문단)/왼쪽여백 (12), 내어쓰기 (12)",
"item": "문구 (※ 기타… 이하 문단)/왼쪽여백 (15), 내어쓰기 (12)",
"desc": "내부적으로 내어쓰기는 음수값 / JSON value값은 양수로 입력"
},
"20": {
"path": "//CHARSHAPE[@Id=//CHAR[contains(text(),'{searchValue}')]/parent::TEXT/@CharShape]/@Height",
"searchValue": "2026. 1. 21.",
"value": "1200",
"searchValue": "2026. 02. 08.",
"value": "1400",
"points": 1,
"category": "OneAnswer",
"item": "문구 (2026. 1. 21.)/① 크기 (1200)",
"item": "문구 (2026. 02. 08.)/① 크기 (1400)",
"desc": "1pt당 100"
},
"21": {
"path": "//PARASHAPE[@Id=//CHAR[contains(text(),'{searchValue}')]/ancestor::P/@ParaShape]/@Align",
"searchValue": "2026. 1. 21.",
"searchValue": "2026. 02. 08.",
"value": "Center",
"points": 1,
"category": "OneAnswer",
"item": "문구 (2026. 1. 21.)/② 정렬 (가운데 정렬)"
"item": "문구 (2026. 02. 08.)/② 정렬 (가운데 정렬)"
},
"22": {
"path": "//TEXT[CHAR[text()='{searchValue}']]/@CharShape",
"searchValue": "한국환경보전단체",
"value": "궁서",
"searchValue": "광양매실축제위원회",
"value": "견고딕",
"points": 1,
"category": "FontName",
"item": "문구 (한국환경보전단체)/① 글씨체 (궁서)"
"item": "문구 (광양매실축제위원회)/① 글씨체 (견고딕)"
},
"23": {
"path": "//CHARSHAPE[@Id=//TEXT[CHAR[text()='{searchValue}']]/@CharShape]/@Height",
"searchValue": "한국환경보전단체",
"value": "2400",
"searchValue": "광양매실축제위원회",
"value": "2600",
"points": 1,
"category": "OneAnswer",
"item": "문구 (한국환경보전단체)/② 크기 (2400)"
"item": "문구 (광양매실축제위원회)/② 크기 (2600)"
},
"24": {
"path": "//PARASHAPE[@Id=//CHAR[text()='{searchValue}']/ancestor::P/@ParaShape]/@Align",
"searchValue": "한국환경보전단체",
"searchValue": "광양매실축제위원회",
"value": "Center",
"points": 1,
"category": "OneAnswer",
"item": "문구 (한국환경보전단체)/③ 정렬 (가운데 정렬)"
"item": "문구 (광양매실축제위원회)/③ 정렬 (가운데 정렬)"
},
"25": {
"path": "//TEXT[CHAR[text()='{searchValue}']]/@CharShape",
"searchValue": "DIAT",
"value": "돋움",
"value": "굴림",
"points": 1,
"category": "Header.FontName",
"item": "문구 (DIAT)/① 글꼴 (돋움)"
"item": "문구 (DIAT)/① 글꼴 (굴림)"
},
"26": {
"path": "//CHARSHAPE[@Id=//TEXT[CHAR[text()='{searchValue}']]/@CharShape]/@Height",
@@ -280,10 +280,10 @@
},
"28": {
"path": "//PAGENUM/@FormatType",
"value": "HangulSyllable",
"value": "CircledDigit",
"points": 2,
"category": "PageNumber",
"item": "① 쪽 번호 매기기 (가,나,다 순으로)",
"item": "① 쪽 번호 매기기 (①,②,③ 순으로)",
"desc1": {
"가,나,다": "HangulSyllable",
"1,2,3": "Digit",
@@ -331,7 +331,7 @@
"31": {
"path": "//PARASHAPE[@Id='{parashape_id}']/PARAMARGIN/@LineSpacing",
"value": "190",
"first_word": "",
"first_word": "",
"points": 2,
"category": "LineSpacing",
"item": "문제 1 줄간격 190% 설정",
@@ -367,17 +367,17 @@
},
"4": {
"path": "//RECTANGLE/SHAPEOBJECT/SIZE/@Width",
"value": "50",
"value": "55",
"points": 2,
"category": "Rectangle.mmSize",
"item": "문구 (재활용 방법)/① 크기-너비 (50 mm)"
"item": "문구 (매화와 매실)/① 크기-너비 (55 mm)"
},
"5": {
"path": "//RECTANGLE/SHAPEOBJECT/SIZE/@Height",
"value": "12",
"points": 2,
"category": "Rectangle.mmSize",
"item": "문구 (재활용 방법)/② 크기-높이 (12 mm)"
"item": "문구 (매화와 매실)/② 크기-높이 (12 mm)"
},
"6": {
"path": "//RECTANGLE//LINESHAPE",
@@ -387,7 +387,7 @@
},
"points": 2,
"category": "Rectangle.LineShape",
"item": "문구 (재활용 방법)/③ 테두리 : 이중 실선(1.00mm)",
"item": "문구 (매화와 매실)/③ 테두리 : 이중 실선(1.00mm)",
"desc": "1mm = 283pt value['Width']에 pt값 입력"
},
"7": {
@@ -395,43 +395,43 @@
"value": "20",
"points": 2,
"category": "Rectangle.OneAnswer",
"item": "문구 (재활용 방법)/④ 글상자 모서리 (둥근모양)",
"item": "문구 (매화와 매실)/④ 글상자 모서리 (둥근모양)",
"desc": "모서리 비율 반원:50 / 둥근모양:20"
},
"8": {
"path": "//RECTANGLE//WINDOWBRUSH/@FaceColor",
"value": "227,220,193",
"value": "122,179,64",
"points": 2,
"category": "Rectangle.Color",
"item": "문구 (재활용 방법)/⑤ 채우기 : 색상(RGB:227,220,193)"
"item": "문구 (매화와 매실)/⑤ 채우기 : 색상(RGB:122,179,64)"
},
"9": {
"path": "//RECTANGLE/SHAPEOBJECT/POSITION/@TreatAsChar",
"value": "true",
"points": 1,
"category": "Rectangle.OneAnswer",
"item": "문구 (재활용 방법)/⑥ 글상자 위치 (글자처럼 취급)"
"item": "문구 (매화와 매실)/⑥ 글상자 위치 (글자처럼 취급)"
},
"10": {
"path": "//PARASHAPE[@Id='{rect_parashape_id}']/@Align",
"value": "Center",
"points": 1,
"category": "Rectangle.TextBoxAlign",
"item": "문구 (재활용 방법)/⑦ 글상자 정렬 (가운데 정렬)"
"item": "문구 (매화와 매실)/⑦ 글상자 정렬 (가운데 정렬)"
},
"11": {
"path": ".//RECTANGLE//TEXT/@CharShape",
"value": "굴림체",
"value": "중고딕",
"points": 1,
"category": "Rectangle.FontName",
"item": "문구 (재활용 방법)/⑧ 글씨체 (굴림체)"
"item": "문구 (매화와 매실)/⑧ 글씨체 (중고딕)"
},
"12": {
"path": "//CHARSHAPE[@Id='{rect_charshape_id}']/@Height",
"value": "1800",
"value": "2000",
"points": 1,
"category": "Rectangle.FontSize",
"item": "문구 (재활용 방법)/⑨ 글씨크기 (1800)",
"item": "문구 (매화와 매실)/⑨ 글씨크기 (2000)",
"desc": "1pt당 100"
},
"13": {
@@ -439,22 +439,22 @@
"value": "Center",
"points": 1,
"category": "Rectangle.TextBoxAlign",
"item": "문구 (재활용 방법)/⑩ 정렬 (가운데 정렬)"
"item": "문구 (매화와 매실)/⑩ 정렬 (가운데 정렬)"
},
"14": {
"path": "//BINITEM[@BinData=//PICTURE/IMAGE/@BinItem][@Format='JPG' or @Format='JPEG' or @Format='PNG']",
"value": true,
"points": 2,
"category": "Boolean",
"item": "① 파일명 \"그림A.jpg\" 삽입",
"item": "① 파일명 \"그림B.jpg\" 삽입",
"desc": "첨부 이미지 파일명 손상으로 정상적인 채점이 불가한 경우가 발견되어서 이미지 첨부 여부로 채점 방식 변경 (7/3)"
},
"15": {
"path": "//PICTURE[./IMAGE[@BinItem=//BINITEM[@Format='JPG' or @Format='JPEG' or @Format='PNG']/@BinData]]/SHAPEOBJECT/SIZE/@Width",
"value": "80",
"value": "85",
"points": 2,
"category": "mmSize",
"item": "② 크기-너비 (80 mm)"
"item": "② 크기-너비 (85 mm)"
},
"16": {
"path": "//PICTURE[./IMAGE[@BinItem=//BINITEM[@Format='JPG' or @Format='JPEG' or @Format='PNG']/@BinData]]/SHAPEOBJECT/SIZE/@Height",
@@ -472,87 +472,87 @@
},
"18": {
"path": "//PICTURE[./IMAGE[@BinItem=//BINITEM[@Format='JPG' or @Format='JPEG' or @Format='PNG']/@BinData]]/SHAPEOBJECT/POSITION[not(@TreatAsChar='true') and @HorzRelTo='Page']/@VertOffset",
"value": "22",
"value": "24",
"points": 2,
"category": "mmSize",
"item": "⑤ 위치 (어울림 : 세로-쪽의 위 22 mm)"
"item": "⑤ 위치 (어울림 : 세로-쪽의 위 24 mm)"
},
"19": {
"path": "//TEXT[CHAR[text()='{searchValue}']]/@CharShape",
"searchValue": "1. 재활용이란?",
"value": "돋움",
"searchValue": "1. 매화의 특징",
"value": "돋움",
"points": 1,
"category": "FontName",
"item": "문구① (1. 재활용이란?)/① 글씨체 (돋움)"
"item": "문구① (1. 매화의 특징)/① 글씨체 (돋움)"
},
"20": {
"path": "//CHARSHAPE[@Id=//TEXT[CHAR[text()='{searchValue}']]/@CharShape]/@Height",
"searchValue": "1. 재활용이란?",
"searchValue": "1. 매화의 특징",
"value": "1200",
"points": 1,
"category": "OneAnswer",
"item": "문구① (1. 재활용이란?)/② 크기 (1200)"
"item": "문구① (1. 매화의 특징)/② 크기 (1200)"
},
"21": {
"path": "//CHARSHAPE[@Id=//TEXT[CHAR[text()='{searchValue}']]/@CharShape]",
"searchValue": "1. 재활용이란?",
"searchValue": "1. 매화의 특징",
"value": "BOLD",
"points": 1,
"category": "FontAttribute",
"item": "문구① (1. 재활용이란?)/③ 진하게"
"item": "문구① (1. 매화의 특징)/③ 진하게"
},
"22": {
"path": "//TEXT[CHAR[text()='{searchValue}']]/@CharShape",
"searchValue": "2. 식재료 재활용 방법",
"value": "돋움",
"searchValue": "2. 매실의 효능",
"value": "돋움",
"points": 1,
"category": "FontName",
"item": "문구② (2. 식재료 재활용 방법)/① 글씨체 (돋움)"
"item": "문구② (2. 매실의 효능)/① 글씨체 (돋움)"
},
"23": {
"path": "//CHARSHAPE[@Id=//TEXT[CHAR[text()='{searchValue}']]/@CharShape]/@Height",
"searchValue": "2. 식재료 재활용 방법",
"searchValue": "2. 매실의 효능",
"value": "1200",
"points": 1,
"category": "OneAnswer",
"item": "문구② (2. 식재료 재활용 방법)/② 크기 (1200)"
"item": "문구② (2. 매실의 효능)/② 크기 (1200)"
},
"24": {
"path": "//CHARSHAPE[@Id=//TEXT[CHAR[text()='{searchValue}']]/@CharShape]",
"searchValue": "2. 식재료 재활용 방법",
"searchValue": "2. 매실의 효능",
"value": "BOLD",
"points": 1,
"category": "FontAttribute",
"item": "문구② (2. 식재료 재활용 방법)/③ 진하게"
"item": "문구② (2. 매실의 효능)/③ 진하게"
},
"25": {
"path": "boolean(//TEXT[CHAR[contains(text(),'{option}')]]/FOOTNOTE)",
"path2": "boolean(//CHAR[substring(., string-length(.) - string-length('{option}') + 1) = '{option}']/following-sibling::FOOTNOTE/descendant::CHAR)",
"option": "얼룩",
"option": "문인화",
"value": true,
"points": 2,
"category": "Boolean",
"item": "문구 (얼룩)/① 각주 설정 및 문구 입력"
"item": "문구 (문인화)/① 각주 설정 및 문구 입력"
},
"26": {
"path": "//CHAR[contains(text(),'{searchValue}')]/parent::TEXT/@CharShape",
"searchValue": "본바탕에 다른 빛깔의 점이나 줄 따위가 뚜렷하게 섞인 자국",
"searchValue": "그림을 직업으로 하지 않는 선비나 사대부들이 여흥으로 자신들의 심중을 표현하여 그린 그림",
"value": "돋움체",
"points": 1,
"category": "FontName",
"item": "문구 (얼룩)/② 글씨체 (돋움체)"
"item": "문구 (문인화)/② 글씨체 (돋움체)"
},
"27": {
"path": "//CHARSHAPE[@Id=//TEXT[CHAR[contains(text(),'{searchValue}')]]/@CharShape]/@Height",
"searchValue": "본바탕에 다른 빛깔의 점이나 줄 따위가 뚜렷하게 섞인 자국",
"searchValue": "그림을 직업으로 하지 않는 선비나 사대부들이 여흥으로 자신들의 심중을 표현하여 그린 그림",
"value": "900",
"points": 1,
"category": "OneAnswer",
"item": "문구 (얼룩)/③ 크기 (9pt)"
"item": "문구 (문인화)/③ 크기 (9pt)"
},
"28": {
"path": "//P[TEXT[CHAR[contains(text(), '{searchValue}')]]]//AUTONUMFORMAT/@Type",
"searchValue": "본바탕에 다른 빛깔의 점이나 줄 따위가 뚜렷하게 섞인 자국",
"searchValue": "그림을 직업으로 하지 않는 선비나 사대부들이 여흥으로 자신들의 심중을 표현하여 그린 그림",
"value": "LatinSmall",
"points": 2,
"category": "OneAnswer",
@@ -580,80 +580,80 @@
},
"29": {
"path": "boolean(//CHAR[contains(text(),'{ignoreWord}')])",
"ignoreWord": "Refrigerator",
"ignoreWord": "Vitamin",
"value": true,
"points": 3,
"category": "Boolean",
"item": "Refrigerator/영단어 미입력, 대소문자/오타 시 전체 감점",
"item": "Vitamin/영단어 미입력, 대소문자/오타 시 전체 감점",
"desc": "유사도 검사를 진행하지 않고 영단어가 모두 일치해야 하므로 xpath구문 내 단어도 수정필요"
},
"30": {
"path": "//CHAR[contains(text(),'{kor}')][contains(text(),'{chn}')]",
"word": [
["적자", "赤字"],
["가치", "價値"],
["제거", "除去"],
["청소", "淸掃"],
["광택", "光澤"]
["매향", "梅香"],
["불의", "不義"],
["국화", "菊花"],
["매실", "梅實"],
["해독", "解毒"]
],
"value": 10,
"points": 10,
"category": "Hanja",
"item": "① 적자(赤字), 가치(價値), 제거(除去), ④ 청소(淸掃), ⑤ 광택(光澤)"
"item": "① 매향(梅香), ② 불의(不義), ③ 국화(菊花), ④ 매실(梅實), ⑤ 해독(解毒)"
},
"31": {
"path": "boolean(//CHAR[contains(translate(text(), ' ', ''),'용은주로')])",
"path": "boolean(//CHAR[contains(translate(text(), ' ', ''),'흰색,분')])",
"value": true,
"points": 3,
"category": "Boolean",
"item": "문구 (…재활용이 주로 정치적…)>'이'→'은' 글자바꿈"
"item": "문구 (…청색, 분홍색, 붉은색 등 다양한 색을…)>'청 → 흰' 글자바꿈"
},
"32": {
"path": "boolean(//CHAR[contains(translate(text(), ' ', ''),'량만재활')])",
"path": "boolean(//CHAR[contains(translate(text(), ' ', ''),'킨과폴리')])",
"value": true,
"points": 3,
"category": "Boolean",
"item": "문구 (상징적으로 재활용 소량만 하는 경우도)>'재활용 / 소량만' 순서바꿈"
"item": "문구 (…항산화 성분인 폴리페놀은 카테킨과 해독(解讀)과…)>'폴리페놀은 / 카테킨과' 순서바꿈"
},
"33": {
"path": "//TEXT[CHAR[contains(text(),'{searchValue}')]]/@CharShape",
"searchValue": "재활용 주민 참여율(%)",
"value": "궁서",
"searchValue": "광양매실축제 만족도(단위 : %)",
"value": "굴림체",
"points": 1,
"category": "FontName",
"item": "제목 문구 (재활용 주민 참여율(%))/① 글씨체 (궁서)"
"item": "제목 문구 (광양매실축제 만족도(단위 : %))/① 글씨체 (굴림체)"
},
"34": {
"path": "//CHARSHAPE[@Id=//TEXT[CHAR[text()='{searchValue}']]/@CharShape]/@Height",
"searchValue": "재활용 주민 참여율(%)",
"searchValue": "광양매실축제 만족도(단위 : %)",
"value": "1200",
"points": 1,
"category": "OneAnswer",
"item": "제목 문구 (재활용 주민 참여율(%))/② 크기 (1200)"
"item": "제목 문구 (광양매실축제 만족도(단위 : %))/② 크기 (1200)"
},
"35": {
"path": "//CHARSHAPE[@Id=//TEXT[CHAR[text()='{searchValue}']]/@CharShape]",
"searchValue": "재활용 주민 참여율(%)",
"searchValue": "광양매실축제 만족도(단위 : %)",
"value": "BOLD",
"points": 1,
"category": "FontAttribute",
"item": "제목 문구 (재활용 주민 참여율(%))/③ 진하게"
"item": "제목 문구 (광양매실축제 만족도(단위 : %))/③ 진하게"
},
"36": {
"path": "//PARASHAPE[@Id=//P[.//CHAR[text()='{searchValue}']]/@ParaShape]/@Align",
"searchValue": "재활용 주민 참여율(%)",
"searchValue": "광양매실축제 만족도(단위 : %)",
"value": "Center",
"points": 1,
"category": "OneAnswer",
"item": "제목 문구 (재활용 주민 참여율(%))/④ 정렬 (가운데 정렬)"
"item": "제목 문구 (광양매실축제 만족도(단위 : %))/④ 정렬 (가운데 정렬)"
},
"37": {
"path": "//BORDERFILL[@Id=//TABLE/ROW[1]/CELL/@BorderFill]/FILLBRUSH/WINDOWBRUSH/@FaceColor",
"path2": "//BORDERFILL[@Id=//CELLZONE[@StartRowAddr='0' and @EndRowAddr='0' and @StartColAddr='0' and @EndColAddr=(ancestor::TABLE[1]/@ColCount)-1]/@BorderFill]/FILLBRUSH/WINDOWBRUSH/@FaceColor",
"value": "33,174,201",
"value": "251,205,151",
"points": 2,
"category": "Color",
"item": "위쪽 제목 셀/① 색상(RGB:33,174,201)"
"item": "위쪽 제목 셀/① 색상(RGB:251,205,151)"
},
"38": {
"path": "//CHARSHAPE[@Id=//TABLE/ROW[1]/descendant::TEXT/@CharShape]",
@@ -682,11 +682,11 @@
"41": {
"path": "//TABLE//TEXT/@CharShape",
"path2": "//FONTFACE[@Lang='Hangul']/FONT[@Id=//CHARSHAPE[@Id=//TABLE/ROW/descendant::TEXT/@CharShape]/FONTID/@Hangul]/@Name",
"value": "굴림",
"value": "돋움체",
"points": 1,
"category": "TableFontName",
"category_tmp": "FontName",
"item": "글자모양/① 글씨체 (굴림)",
"item": "글자모양/① 글씨체 (돋움체)",
"desc": "테이블 폰트명 문항은 테이블의 모든 셀이 정답폰트와 일치해야 함, 하나만 일치해도 정답으로 채점할 경우 category값을 FontName으로 변경"
},
"42": {
@@ -714,11 +714,11 @@
},
"45": {
"chart_xpath": "",
"chart_type": "표식만 있는 분산형",
"chart_type": "곡선이 있는 분산형",
"value": true,
"points": 2,
"category": "ChartType",
"item": "① 종류 (표식만 있는 분산형)",
"item": "① 종류 (곡선이 있는 분산형)",
"desc": "chart_type을 입력받아 차트타입에 맞는 xml요소가 있는지 내부적으로 검사, chart_type만 한글로 입력해주면 된다. (공백무시)"
},
"46": {
@@ -738,10 +738,10 @@
},
"48": {
"path": "//OLE[@BinItem=//BINITEM[@Format='OLE']/@BinData]//SIZE/@Height",
"value": "80",
"value": "90",
"points": 2,
"category": "mmSize",
"item": "④ 크기-높이 (80 mm)"
"item": "④ 크기-높이 (90 mm)"
},
"49": {
"chart_xpath": "boolean(//c:chart and not(//c:pt[not(ancestor::c:tx)]/c:v[text()='합계' or text()='평균']))",
@@ -753,28 +753,28 @@
},
"50": {
"chart_xpath": "//a:t[text()='{searchValue}']/ancestor::a:r//a:ea/@typeface",
"searchValue": "도시별 재활용 참여율",
"value": "돋움",
"searchValue": "광양매실축제 만족도",
"value": "궁서체",
"points": 1,
"category": "OneAnswer",
"item": "제목 문구 (도시별 재활용 참여율)/① 글씨체 (돋움)"
"item": "제목 문구 (광양매실축제 만족도)/① 글씨체 (궁서체)"
},
"51": {
"chart_xpath": "//a:t[text()='{searchValue}']/ancestor::a:r/a:rPr/@sz",
"searchValue": "도시별 재활용 참여율",
"searchValue": "광양매실축제 만족도",
"value": "1300",
"points": 1,
"category": "OneAnswer",
"item": "제목 문구 (도시별 재활용 참여율)/② 크기 (1300)"
"item": "제목 문구 (광양매실축제 만족도)/② 크기 (1300)"
},
"52": {
"chart_xpath": "//a:t[text()='{searchValue}']/ancestor::a:r/a:rPr/@{option}",
"option": "b",
"searchValue": "도시별 재활용 참여율",
"searchValue": "광양매실축제 만족도",
"value": "1",
"points": 1,
"category": "OneAnswer",
"item": "제목 문구 (도시별 재활용 참여율)/③ 기울임",
"item": "제목 문구 (광양매실축제 만족도)/③ 기울임",
"desc": "option값 - 기울임(Italic):i / 굵게(Bold):b"
},
"53": {

850
JSON/2622/DIW_2622C.json Normal file
View File

@@ -0,0 +1,850 @@
{
"0": {
"0": {
"path": "",
"path2": "",
"points": 0,
"category": "파일저장",
"item": "파일명 (수검번호.hwp/hwpx)"
},
"1": {
"path": "//PAGEMARGIN",
"value": {
"Top": 20,
"Bottom": 20,
"Left": 20,
"Right": 20,
"Header": 10,
"Footer": 10,
"Gutter": 0
},
"tolerance": 1,
"points": 4,
"category": "PageSetting",
"item": "A4용지, 왼쪽/오른쪽/위쪽/아래쪽 (각20mm), 머리말/꼬리말 (10mm), 제본(0mm)"
},
"2": {
"path": "",
"value": {
"FontName": "바탕",
"FontSize": "1000",
"Alignment": "Justify",
"LineSpacing": "160"
},
"points": 4,
"category": "BasicSetting",
"item": "글꼴 (바탕, 10pt), 양쪽정렬, 줄간격 (160%)"
},
"3": {
"path": "",
"value": null,
"points": 40,
"category": "오타감점",
"item": "오타 1개 -1점 / 2503회부터 오타 1개 -1점으로 변경"
}
},
"1": {
"1": {
"path": "//TEXTART[@Text='{searchValue}']/TEXTARTSHAPE/@FontName",
"searchValue": "새콤달콤토마토축제",
"value": "돋움",
"points": 1,
"category": "OneAnswer",
"item": "문구 (새콤달콤토마토축제)/① 글씨체 (돋움)"
},
"2": {
"path": "//TEXTART[@Text='{searchValue}']/descendant::WINDOWBRUSH/@FaceColor",
"searchValue": "새콤달콤토마토축제",
"value": "217,113,111",
"points": 2,
"category": "Color",
"item": "문구 (새콤달콤토마토축제)/② 채우기 : 색상(RGB:217,113,111)"
},
"3": {
"path": "//TEXTART[@Text='{searchValue}']/SHAPEOBJECT/SIZE/@Width",
"searchValue": "새콤달콤토마토축제",
"value": "130",
"tolerance": 1,
"points": 2,
"category": "mmSize",
"item": "문구 (새콤달콤토마토축제)/③ 크기-너비 (130 mm)"
},
"4": {
"path": "//TEXTART[@Text='{searchValue}']/SHAPEOBJECT/SIZE/@Height",
"searchValue": "새콤달콤토마토축제",
"value": "20",
"tolerance": 1,
"points": 2,
"category": "mmSize",
"item": "문구 (새콤달콤토마토축제)/④ 크기-높이 (20 mm)"
},
"5": {
"path": "//TEXTART[@Text='{searchValue}']/SHAPEOBJECT/POSITION/@TreatAsChar",
"searchValue": "새콤달콤토마토축제",
"value": "true",
"points": 2,
"category": "OneAnswer",
"item": "문구 (새콤달콤토마토축제)/⑤ 위치 (글자처럼 취급)"
},
"6": {
"path": "//PARASHAPE[@Id=//P[.//TEXTART[@Text='{searchValue}']]/@ParaShape]/@Align",
"searchValue": "새콤달콤토마토축제",
"value": "Center",
"points": 2,
"category": "OneAnswer",
"item": "문구 (새콤달콤토마토축제)/⑥ 정렬 (가운데 정렬)"
},
"7": {
"path": "//TEXTART[@Text='{searchValue}']",
"searchValue": "새콤달콤토마토축제",
"value": true,
"points": 2,
"category": "Boolean",
"item": "문구 (새콤달콤토마토축제)/⑦ 글맵시모양 (육안확인)"
},
"8": {
"path": "//RECTANGLE[.//CHAR[text()='{searchValue}']]/SHAPEOBJECT/SIZE",
"searchValue": "토",
"value": {
"Height": 2800,
"Width": 2800
},
"tolerance": 200,
"points": 1,
"category": "TwoLineSize",
"item": "어/① 모양 (2줄)"
},
"9": {
"path": "//TEXT[CHAR[text()='{searchValue}']]/@CharShape",
"searchValue": "토",
"value": "궁서체",
"points": 1,
"category": "FontName",
"item": "어/② 글씨체 (궁서체)"
},
"10": {
"path": "//RECTANGLE[.//CHAR[text()='{searchValue}']]//WINDOWBRUSH/@FaceColor",
"searchValue": "토",
"value": "163,232,239",
"points": 2,
"category": "Color",
"item": "어/③ 면색 : 색상(RGB:163,232,239)"
},
"11": {
"path": "//RECTANGLE[.//CHAR[text()='{searchValue}']]//OUTSIDEMARGIN/@Right",
"searchValue": "토",
"value": "3.0",
"tolerance": 1,
"points": 2,
"category": "mmSize",
"item": "어/④ 본문과의 간격 : 3.0mm"
},
"12": {
"path": "//CHARSHAPE[@Id=//CHAR[contains(text(),'{searchValue}')]/parent::TEXT/@CharShape]",
"searchValue": "세계 10대 건강식품",
"value": "ITALIC",
"points": 1,
"category": "FontAttribute",
"item": "문구 (세계 10대 건강식품)/① ITALIC"
},
"13": {
"path": "//CHARSHAPE[@Id=//CHAR[contains(text(),'{searchValue}')]/parent::TEXT/@CharShape]",
"searchValue": "세계 10대 건강식품",
"value": "UNDERLINE",
"points": 1,
"category": "FontAttribute",
"item": "문구 (세계 10대 건강식품)/② UNDERLINE"
},
"14": {
"path": "//CHAR[contains(string(.),'{char1}')]/text()",
"path2": "//CHAR[contains(string(.),'{char2}')]/text()",
"path3": "//CHAR[contains(string(.),'{char3}')]/text()",
"char1": "◎",
"char2": "◎",
"char3": "※",
"value": 3,
"points": 3,
"category": "SpecialChar",
"item": "① ◎, ② ◎, ③ ※"
},
"15": {
"path": "//CHAR[contains(text(),'{searchValue}')]/parent::TEXT/@CharShape",
"searchValue": "축제안내",
"value": "굴림",
"points": 1,
"category": "FontName",
"item": "문구 (◎ 축제안내 ◎)/① 글씨체 (굴림)"
},
"16": {
"path": "//PARASHAPE[@Id=//CHAR[contains(text(),'{match_str}')]/ancestor::P/@ParaShape]/@Align",
"match_str": "축제안내",
"value": "Center",
"points": 1,
"category": "Align",
"item": "문구 (◎ 축제안내 ◎)/② 정렬 (가운데 정렬)"
},
"17": {
"path": "//CHARSHAPE[@Id=//TEXT[CHAR[text()='{searchValue}']]/@CharShape]",
"hyperlink_ptag": "//P[.//FIELDBEGIN[@Type='Hyperlink']]",
"searchValue": "다양한 유기농 농산물(토마토, 블루베리, 새싹 등)",
"value": "BOLD",
"points": 1,
"category": "FontAttribute",
"item": "문구 (다양한 유기농 농산물(토마토, 블루베리, 새싹 등))/① BOLD"
},
"18": {
"path": "//CHARSHAPE[@Id=//TEXT[CHAR[text()='{searchValue}']]/@CharShape]",
"hyperlink_ptag": "//P[.//FIELDBEGIN[@Type='Hyperlink']]",
"searchValue": "다양한 유기농 농산물(토마토, 블루베리, 새싹 등)",
"value": "UNDERLINE",
"points": 1,
"category": "FontAttribute",
"item": "문구 (다양한 유기농 농산물(토마토, 블루베리, 새싹 등))/② UNDERLINE"
},
"19": {
"path": "//PARASHAPE[@Id=//CHAR[contains(text(),'{searchValue}')]/ancestor::P/following-sibling::P[1]/@ParaShape]/PARAMARGIN",
"searchValue": "기타사항",
"value": {
"Left": 13,
"Indent": 12
},
"points": 2,
"category": "ParaShape",
"item": "문구 (※ 기타… 이하 문단)/왼쪽여백 (13), 내어쓰기 (12)",
"desc": "내부적으로 내어쓰기는 음수값 / JSON value값은 양수로 입력"
},
"20": {
"path": "//CHARSHAPE[@Id=//CHAR[contains(text(),'{searchValue}')]/parent::TEXT/@CharShape]/@Height",
"searchValue": "2026. 02. 08.",
"value": "1300",
"points": 1,
"category": "OneAnswer",
"item": "문구 (2026. 02. 08.)/① 크기 (1300)",
"desc": "1pt당 100"
},
"21": {
"path": "//PARASHAPE[@Id=//CHAR[contains(text(),'{searchValue}')]/ancestor::P/@ParaShape]/@Align",
"searchValue": "2026. 02. 08.",
"value": "Center",
"points": 1,
"category": "OneAnswer",
"item": "문구 (2026. 02. 08.)/② 정렬 (가운데 정렬)"
},
"22": {
"path": "//TEXT[CHAR[text()='{searchValue}']]/@CharShape",
"searchValue": "한국유기농토마토협회",
"value": "견고딕",
"points": 1,
"category": "FontName",
"item": "문구 (한국유기농토마토협회)/① 글씨체 (견고딕)"
},
"23": {
"path": "//CHARSHAPE[@Id=//TEXT[CHAR[text()='{searchValue}']]/@CharShape]/@Height",
"searchValue": "한국유기농토마토협회",
"value": "2400",
"points": 1,
"category": "OneAnswer",
"item": "문구 (한국유기농토마토협회)/② 크기 (2400)"
},
"24": {
"path": "//PARASHAPE[@Id=//CHAR[text()='{searchValue}']/ancestor::P/@ParaShape]/@Align",
"searchValue": "한국유기농토마토협회",
"value": "Center",
"points": 1,
"category": "OneAnswer",
"item": "문구 (한국유기농토마토협회)/③ 정렬 (가운데 정렬)"
},
"25": {
"path": "//TEXT[CHAR[text()='{searchValue}']]/@CharShape",
"searchValue": "DIAT",
"value": "궁서",
"points": 1,
"category": "Header.FontName",
"item": "문구 (DIAT)/① 글꼴 (궁서)"
},
"26": {
"path": "//CHARSHAPE[@Id=//TEXT[CHAR[text()='{searchValue}']]/@CharShape]/@Height",
"searchValue": "DIAT",
"value": "900",
"points": 1,
"category": "Header.OneAnswer",
"item": "문구 (DIAT)/② 크기 (9pt)"
},
"27": {
"path": "//PARASHAPE[@Id=//CHAR[text()='{searchValue}']/parent::TEXT/parent::P/@ParaShape]/@Align",
"searchValue": "DIAT",
"value": "Right",
"points": 1,
"category": "Header.OneAnswer",
"item": "문구 (DIAT)/③ 정렬 (오른쪽 정렬)"
},
"28": {
"path": "//PAGENUM/@FormatType",
"value": "HangulSyllable",
"points": 2,
"category": "PageNumber",
"item": "① 쪽 번호 매기기 (가,나,다 순으로)",
"desc1": {
"가,나,다": "HangulSyllable",
"1,2,3": "Digit",
"일,이,삼": "HangulPhonetic",
"갑,을,병": "DecagonCircle",
"A,B,C": "LatinCapital",
"a,b,c": "LatinSmall",
"Ⓐ,Ⓑ,Ⓒ": "CircledLatinCapital",
"①,②,③": "CircledDigit",
"一,二,三": "Ideograph",
"㉠,㉡,㉢": "CircledHangulJamo",
"ⓐ,ⓑ,ⓒ": "CircledLatinSmall",
"㊀,㊁,㊂": "CircledIdeograph",
"i,ii,iii": "RomanSmall",
"I,II,III": "RomanCapital",
"甲,乙,丙": "DecagonCircleHanja",
"+,++,+++": "UserChar",
"*,**,***": "UserChar",
"정답에 맞는 값 value에 입력": ""
},
"desc2": "1, 2페이지 모두 정답이어야 점수 부여"
},
"29": {
"path": "//PAGENUM/@Pos",
"value": "BottomCenter",
"points": 2,
"category": "PageNumber",
"item": "가운데 아래",
"desc": "1, 2페이지 모두 정답이어야 점수 부여",
"desc2": {
"가운데 아래": "BottomCenter",
"오른쪽 아래": "BottomRight",
"왼쪽 아래": "BottomLeft"
}
},
"30": {
"path": "//CHAR[contains(text(),'{searchValue}')]",
"searchValue": "http",
"value": true,
"points": 2,
"category": "hyperlink",
"item": "문구 (http://www.ihd.or.kr)/하이퍼링크 없이 작성",
"desc": "searchValue에 해당하는 주소 문구에 하이퍼링크가 하나라도 설정되어 있으면 오답"
},
"31": {
"path": "//PARASHAPE[@Id='{parashape_id}']/PARAMARGIN/@LineSpacing",
"value": "200",
"first_word": "토",
"points": 2,
"category": "LineSpacing",
"item": "문제 1 줄간격 200% 설정",
"desc": "1페이지 문단의 줄간격이 정답이 아닌 문단이 있으면 False(감점), first_word 속성에 [문단 첫글자 장식]에 해당하는 글자를 입력해준다."
}
},
"2": {
"1": {
"path": "//PAGEBORDERFILL[@Type='Both' or @Type='Even']/@HeaderInside",
"path2": "//BORDERFILL[@Id=//PAGEBORDERFILL[@Type='Both' or @Type='Even']/@BorferFill]",
"value": {
"header_inside": true,
"all_double_slim": true
},
"points": 4,
"category": "PageBorder",
"item": "문제2 쪽테두리(이중 실선, 머리말 포함) 설정"
},
"2": {
"path": "count(//SECTION)>1",
"value": true,
"points": 3,
"category": "Boolean",
"item": "① 구역나누기",
"desc": "섹션이 1개 이상이면 점수부여"
},
"3": {
"path": "./TEXT/COLDEF/@Count",
"value": "2",
"points": 3,
"category": "TwoColumn",
"item": "② 다단 2단"
},
"4": {
"path": "//RECTANGLE/SHAPEOBJECT/SIZE/@Width",
"value": "60",
"points": 2,
"category": "Rectangle.mmSize",
"item": "문구 (슈퍼푸드 토마토)/① 크기-너비 (60 mm)"
},
"5": {
"path": "//RECTANGLE/SHAPEOBJECT/SIZE/@Height",
"value": "12",
"points": 2,
"category": "Rectangle.mmSize",
"item": "문구 (슈퍼푸드 토마토)/② 크기-높이 (12 mm)"
},
"6": {
"path": "//RECTANGLE//LINESHAPE",
"value": {
"Style": "DoubleSlim",
"Width": "283"
},
"points": 2,
"category": "Rectangle.LineShape",
"item": "문구 (슈퍼푸드 토마토)/③ 테두리 : 이중 실선(1.00mm)",
"desc": "1mm = 283pt value['Width']에 pt값 입력"
},
"7": {
"path": "//RECTANGLE/@Ratio",
"value": "50",
"points": 2,
"category": "Rectangle.OneAnswer",
"item": "문구 (슈퍼푸드 토마토)/④ 글상자 모서리 (둥근모양)",
"desc": "모서리 비율 반원:50 / 둥근모양:20"
},
"8": {
"path": "//RECTANGLE//WINDOWBRUSH/@FaceColor",
"value": "250,171,226",
"points": 2,
"category": "Rectangle.Color",
"item": "문구 (슈퍼푸드 토마토)/⑤ 채우기 : 색상(RGB:250,171,226)"
},
"9": {
"path": "//RECTANGLE/SHAPEOBJECT/POSITION/@TreatAsChar",
"value": "true",
"points": 1,
"category": "Rectangle.OneAnswer",
"item": "문구 (슈퍼푸드 토마토)/⑥ 글상자 위치 (글자처럼 취급)"
},
"10": {
"path": "//PARASHAPE[@Id='{rect_parashape_id}']/@Align",
"value": "Center",
"points": 1,
"category": "Rectangle.TextBoxAlign",
"item": "문구 (슈퍼푸드 토마토)/⑦ 글상자 정렬 (가운데 정렬)"
},
"11": {
"path": ".//RECTANGLE//TEXT/@CharShape",
"value": "돋움체",
"points": 1,
"category": "Rectangle.FontName",
"item": "문구 (슈퍼푸드 토마토)/⑧ 글씨체 (돋움체)"
},
"12": {
"path": "//CHARSHAPE[@Id='{rect_charshape_id}']/@Height",
"value": "2000",
"points": 1,
"category": "Rectangle.FontSize",
"item": "문구 (슈퍼푸드 토마토)/⑨ 글씨크기 (2000)",
"desc": "1pt당 100"
},
"13": {
"path": "//PARASHAPE[@Id={rect_parashape_id}]/@Align",
"value": "Center",
"points": 1,
"category": "Rectangle.TextBoxAlign",
"item": "문구 (슈퍼푸드 토마토)/⑩ 정렬 (가운데 정렬)"
},
"14": {
"path": "//BINITEM[@BinData=//PICTURE/IMAGE/@BinItem][@Format='JPG' or @Format='JPEG' or @Format='PNG']",
"value": true,
"points": 2,
"category": "Boolean",
"item": "① 파일명 \"그림C.jpg\" 삽입",
"desc": "첨부 이미지 파일명 손상으로 정상적인 채점이 불가한 경우가 발견되어서 이미지 첨부 여부로 채점 방식 변경 (7/3)"
},
"15": {
"path": "//PICTURE[./IMAGE[@BinItem=//BINITEM[@Format='JPG' or @Format='JPEG' or @Format='PNG']/@BinData]]/SHAPEOBJECT/SIZE/@Width",
"value": "80",
"points": 2,
"category": "mmSize",
"item": "② 크기-너비 (80 mm)"
},
"16": {
"path": "//PICTURE[./IMAGE[@BinItem=//BINITEM[@Format='JPG' or @Format='JPEG' or @Format='PNG']/@BinData]]/SHAPEOBJECT/SIZE/@Height",
"value": "40",
"points": 2,
"category": "mmSize",
"item": "③ 크기-높이 (40 mm)"
},
"17": {
"path": "//PICTURE[./IMAGE[@BinItem=//BINITEM[@Format='JPG' or @Format='JPEG' or @Format='PNG']/@BinData]]/SHAPEOBJECT/POSITION[not(@TreatAsChar='true') and @HorzRelTo='Page']/@HorzOffset",
"value": "0",
"points": 2,
"category": "mmSize",
"item": "④ 위치 (어울림 : 가로-쪽의 왼쪽 0mm)"
},
"18": {
"path": "//PICTURE[./IMAGE[@BinItem=//BINITEM[@Format='JPG' or @Format='JPEG' or @Format='PNG']/@BinData]]/SHAPEOBJECT/POSITION[not(@TreatAsChar='true') and @HorzRelTo='Page']/@VertOffset",
"value": "23",
"points": 2,
"category": "mmSize",
"item": "⑤ 위치 (어울림 : 세로-쪽의 위 23 mm)"
},
"19": {
"path": "//TEXT[CHAR[text()='{searchValue}']]/@CharShape",
"searchValue": "1. 토마토의 종류",
"value": "굴림체",
"points": 1,
"category": "FontName",
"item": "문구① (1. 토마토의 종류)/① 글씨체 (굴림체)"
},
"20": {
"path": "//CHARSHAPE[@Id=//TEXT[CHAR[text()='{searchValue}']]/@CharShape]/@Height",
"searchValue": "1. 토마토의 종류",
"value": "1200",
"points": 1,
"category": "OneAnswer",
"item": "문구① (1. 토마토의 종류)/② 크기 (1200)"
},
"21": {
"path": "//CHARSHAPE[@Id=//TEXT[CHAR[text()='{searchValue}']]/@CharShape]",
"searchValue": "1. 토마토의 종류",
"value": "BOLD",
"points": 1,
"category": "FontAttribute",
"item": "문구① (1. 토마토의 종류)/③ 진하게"
},
"22": {
"path": "//TEXT[CHAR[text()='{searchValue}']]/@CharShape",
"searchValue": "2. 토마토의 효능",
"value": "굴림체",
"points": 1,
"category": "FontName",
"item": "문구② (2. 토마토의 효능)/① 글씨체 (굴림체)"
},
"23": {
"path": "//CHARSHAPE[@Id=//TEXT[CHAR[text()='{searchValue}']]/@CharShape]/@Height",
"searchValue": "2. 토마토의 효능",
"value": "1200",
"points": 1,
"category": "OneAnswer",
"item": "문구② (2. 토마토의 효능)/② 크기 (1200)"
},
"24": {
"path": "//CHARSHAPE[@Id=//TEXT[CHAR[text()='{searchValue}']]/@CharShape]",
"searchValue": "2. 토마토의 효능",
"value": "BOLD",
"points": 1,
"category": "FontAttribute",
"item": "문구② (2. 토마토의 효능)/③ 진하게"
},
"25": {
"path": "boolean(//TEXT[CHAR[contains(text(),'{option}')]]/FOOTNOTE)",
"path2": "boolean(//CHAR[substring(., string-length(.) - string-length('{option}') + 1) = '{option}']/following-sibling::FOOTNOTE/descendant::CHAR)",
"option": "혈관",
"value": true,
"points": 2,
"category": "Boolean",
"item": "문구 (혈관)/① 각주 설정 및 문구 입력"
},
"26": {
"path": "//CHAR[contains(text(),'{searchValue}')]/parent::TEXT/@CharShape",
"searchValue": "혈액을 온몸으로 순환시키는 통로",
"value": "굴림",
"points": 1,
"category": "FontName",
"item": "문구 (혈관)/② 글씨체 (굴림)"
},
"27": {
"path": "//CHARSHAPE[@Id=//TEXT[CHAR[contains(text(),'{searchValue}')]]/@CharShape]/@Height",
"searchValue": "혈액을 온몸으로 순환시키는 통로",
"value": "900",
"points": 1,
"category": "OneAnswer",
"item": "문구 (혈관)/③ 크기 (9pt)"
},
"28": {
"path": "//P[TEXT[CHAR[contains(text(), '{searchValue}')]]]//AUTONUMFORMAT/@Type",
"searchValue": "혈액을 온몸으로 순환시키는 통로",
"value": "CircledLatinSmall",
"points": 2,
"category": "OneAnswer",
"item": "문구 (전당)/④ 각주 번호모양",
"desc": {
"가,나,다": "HangulSyllable",
"1,2,3": "Digit",
"일,이,삼": "HangulPhonetic",
"갑,을,병": "DecagonCircle",
"A,B,C": "LatinCapital",
"a,b,c": "LatinSmall",
"Ⓐ,Ⓑ,Ⓒ": "CircledLatinCapital",
"①,②,③": "CircledDigit",
"一,二,三": "Ideograph",
"㉠,㉡,㉢": "CircledHangulJamo",
"ⓐ,ⓑ,ⓒ": "CircledLatinSmall",
"㊀,㊁,㊂": "CircledIdeograph",
"i,ii,iii": "RomanSmall",
"I,II,III": "RomanCapital",
"甲,乙,丙": "DecagonCircleHanja",
"+,++,+++": "UserChar",
"*,**,***": "UserChar",
"정답에 맞는 값 value에 입력": ""
}
},
"29": {
"path": "boolean(//CHAR[contains(text(),'{ignoreWord}')])",
"ignoreWord": "Diabetes",
"value": true,
"points": 3,
"category": "Boolean",
"item": "Diabetes/영단어 미입력, 대소문자/오타 시 전체 감점",
"desc": "유사도 검사를 진행하지 않고 영단어가 모두 일치해야 하므로 xpath구문 내 단어도 수정필요"
},
"30": {
"path": "//CHAR[contains(text(),'{kor}')][contains(text(),'{chn}')]",
"word": [
["재배", "栽培"],
["전후", "前後"],
["모양", "模樣"],
["당도", "糖度"],
["개선", "改善"]
],
"value": 10,
"points": 10,
"category": "Hanja",
"item": "① 재배(栽培), ② 전후(前後), ③ 모양(模樣), ④ 당도(糖度), ⑤ 개선(改善)"
},
"31": {
"path": "boolean(//CHAR[contains(translate(text(), ' ', ''),'토와150g')])",
"value": true,
"points": 3,
"category": "Boolean",
"item": "문구 (…전후(前後)인 방울토마토가 150g 이상인…)>'가'→'와' 글자바꿈"
},
"32": {
"path": "boolean(//CHAR[contains(translate(text(), ' ', ''),'광의기능')])",
"value": true,
"points": 3,
"category": "Boolean",
"item": "문구 (…원활하게 해주어 기능을 방광의 촉진 시켜주기…)>'기능을 / 방광의' 순서바꿈"
},
"33": {
"path": "//TEXT[CHAR[contains(text(),'{searchValue}')]]/@CharShape",
"searchValue": "연령별 축제 만족도(단위:%)",
"value": "궁서체",
"points": 1,
"category": "FontName",
"item": "제목 문구 (연령별 축제 만족도(단위:%))/① 글씨체 (궁서체)"
},
"34": {
"path": "//CHARSHAPE[@Id=//TEXT[CHAR[text()='{searchValue}']]/@CharShape]/@Height",
"searchValue": "연령별 축제 만족도(단위:%)",
"value": "1200",
"points": 1,
"category": "OneAnswer",
"item": "제목 문구 (연령별 축제 만족도(단위:%))/② 크기 (1200)"
},
"35": {
"path": "//CHARSHAPE[@Id=//TEXT[CHAR[text()='{searchValue}']]/@CharShape]",
"searchValue": "연령별 축제 만족도(단위:%)",
"value": "BOLD",
"points": 1,
"category": "FontAttribute",
"item": "제목 문구 (연령별 축제 만족도(단위:%))/③ 진하게"
},
"36": {
"path": "//PARASHAPE[@Id=//P[.//CHAR[text()='{searchValue}']]/@ParaShape]/@Align",
"searchValue": "연령별 축제 만족도(단위:%)",
"value": "Center",
"points": 1,
"category": "OneAnswer",
"item": "제목 문구 (연령별 축제 만족도(단위:%))/④ 정렬 (가운데 정렬)"
},
"37": {
"path": "//BORDERFILL[@Id=//TABLE/ROW[1]/CELL/@BorderFill]/FILLBRUSH/WINDOWBRUSH/@FaceColor",
"path2": "//BORDERFILL[@Id=//CELLZONE[@StartRowAddr='0' and @EndRowAddr='0' and @StartColAddr='0' and @EndColAddr=(ancestor::TABLE[1]/@ColCount)-1]/@BorderFill]/FILLBRUSH/WINDOWBRUSH/@FaceColor",
"value": "152,250,148",
"points": 2,
"category": "Color",
"item": "위쪽 제목 셀/① 색상(RGB:152,250,148)"
},
"38": {
"path": "//CHARSHAPE[@Id=//TABLE/ROW[1]/descendant::TEXT/@CharShape]",
"value": "BOLD",
"points": 1,
"category": "FontAttribute",
"item": "위쪽 제목 셀/② 진하게",
"desc": "글자 속성이라 CELLZONE으로 적용 되지 않음"
},
"39": {
"path": "//BORDERFILL[@Id=//TABLE/ROW[1]/CELL/@BorderFill]/BOTTOMBORDER/@Type",
"path2": "//BORDERFILL[@Id=//CELLZONE[@StartRowAddr='0' and @EndRowAddr='0' and @StartColAddr='0' and @EndColAddr=(ancestor::TABLE[1]/@ColCount)-1]/@BorderFill]/BOTTOMBORDER/@Type",
"value": "DoubleSlim",
"points": 2,
"category": "TableAnswer",
"item": "제목 셀 아래선/① 이중실선"
},
"40": {
"path": "//BORDERFILL[@Id=//TABLE/ROW[1]/CELL/@BorderFill]/BOTTOMBORDER/@Width",
"path2": "//BORDERFILL[@Id=//CELLZONE[@StartRowAddr='0' and @EndRowAddr='0' and @StartColAddr='0' and @EndColAddr=(ancestor::TABLE[1]/@ColCount)-1]/@BorderFill]/BOTTOMBORDER/@Width",
"value": "0.5mm",
"points": 2,
"category": "TableAnswer",
"item": "제목 셀 아래선/② 0.5mm"
},
"41": {
"path": "//TABLE//TEXT/@CharShape",
"path2": "//FONTFACE[@Lang='Hangul']/FONT[@Id=//CHARSHAPE[@Id=//TABLE/ROW/descendant::TEXT/@CharShape]/FONTID/@Hangul]/@Name",
"value": "중고딕",
"points": 1,
"category": "TableFontName",
"category_tmp": "FontName",
"item": "글자모양/① 글씨체 (중고딕)",
"desc": "테이블 폰트명 문항은 테이블의 모든 셀이 정답폰트와 일치해야 함, 하나만 일치해도 정답으로 채점할 경우 category값을 FontName으로 변경"
},
"42": {
"path": "//CHARSHAPE[@Id=//TABLE//TEXT/@CharShape]/@Height",
"value": "1000",
"points": 1,
"category": "TableAnswer",
"item": "글자모양/② 크기 (1000)"
},
"43": {
"path": "//PARASHAPE[@Id=//TABLE/ROW//P/@ParaShape]/@Align",
"value": "Center",
"points": 1,
"category": "TableAnswer",
"item": "글자모양/③ 정렬 (가운데 정렬)"
},
"44": {
"path": "boolean(//TABLE[1]/ROW[last()]/CELL[position()=last()]//FIELDBEGIN[starts-with(@Command, '={option}')]) and boolean(//TABLE[1]/ROW[last()]/CELL[position()=last()-1]//FIELDBEGIN[starts-with(@Command, '={option}')])",
"option": "AVG",
"value": true,
"points": 4,
"category": "Boolean",
"item": "블록 계산식/합계",
"desc": "option값에 합계는 SUM / 평균은 AVG"
},
"45": {
"chart_xpath": "",
"chart_type": "묶은 가로 막대형",
"value": true,
"points": 2,
"category": "ChartType",
"item": "① 종류 (묶은 가로 막대형)",
"desc": "chart_type을 입력받아 차트타입에 맞는 xml요소가 있는지 내부적으로 검사, chart_type만 한글로 입력해주면 된다. (공백무시)"
},
"46": {
"chart_xpath": "//c:valAx/c:majorTickMark/@val",
"value": "out",
"points": 2,
"category": "ChartOneAnswer",
"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": "③ 크기-너비 (80 mm)"
},
"48": {
"path": "//OLE[@BinItem=//BINITEM[@Format='OLE']/@BinData]//SIZE/@Height",
"value": "90",
"points": 2,
"category": "mmSize",
"item": "④ 크기-높이 (90 mm)"
},
"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": "1400",
"points": 1,
"category": "OneAnswer",
"item": "제목 문구 (연령별 축제 만족도)/② 크기 (1400)"
},
"52": {
"chart_xpath": "//a:t[text()='{searchValue}']/ancestor::a:r/a:rPr/@{option}",
"option": "b",
"searchValue": "연령별 축제 만족도",
"value": "1",
"points": 1,
"category": "OneAnswer",
"item": "제목 문구 (연령별 축제 만족도)/③ 기울임",
"desc": "option값 - 기울임(Italic):i / 굵게(Bold):b"
},
"53": {
"chart_xpath": "//c:catAx/c:txPr//a:ea/@typeface",
"value": "바탕체",
"points": 1,
"category": "ChartOneAnswer",
"item": "X축/① 글꼴 (바탕체)"
},
"54": {
"chart_xpath": "//c:catAx/c:txPr//a:defRPr/@sz",
"value": "900",
"points": 1,
"category": "ChartOneAnswer",
"item": "X축/② 크기 (9pt)"
},
"55": {
"chart_xpath": "//c:catAx/c:txPr//a:defRPr/@{option}",
"option": "i",
"value": "1",
"points": 1,
"category": "ChartOneAnswer",
"item": "X축/③ 기울임",
"desc": "option값 - 기울임(Italic):i / 굵게(Bold):b"
},
"56": {
"chart_xpath": "//c:valAx/c:txPr//a:ea/@typeface",
"value": "바탕체",
"points": 1,
"category": "ChartOneAnswer",
"item": "Y축/① 글꼴 (바탕체)"
},
"57": {
"chart_xpath": "//c:valAx/c:txPr//a:defRPr/@sz",
"value": "900",
"points": 1,
"category": "ChartOneAnswer",
"item": "Y축/② 크기 (9pt)"
},
"58": {
"chart_xpath": "//c:valAx/c:txPr//a:defRPr/@{option}",
"option": "i",
"value": "1",
"points": 1,
"category": "ChartOneAnswer",
"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"
}
}
}

841
JSON/2622/DIW_2622D.json Normal file
View File

@@ -0,0 +1,841 @@
{
"0": {
"0": {
"path": "",
"path2": "",
"points": 0,
"category": "파일저장",
"item": "파일명 (수검번호.hwp/hwpx)"
},
"1": {
"path": "//PAGEMARGIN",
"value": {
"Top": 20,
"Bottom": 20,
"Left": 20,
"Right": 20,
"Header": 10,
"Footer": 10,
"Gutter": 0
},
"tolerance": 1,
"points": 4,
"category": "PageSetting",
"item": "A4용지, 왼쪽/오른쪽/위쪽/아래쪽 (각20mm), 머리말/꼬리말 (10mm), 제본(0mm)"
},
"2": {
"path": "",
"value": {
"FontName": "바탕",
"FontSize": "1000",
"Alignment": "Justify",
"LineSpacing": "160"
},
"points": 4,
"category": "BasicSetting",
"item": "글꼴 (바탕, 10pt), 양쪽정렬, 줄간격 (160%)"
},
"3": {
"path": "",
"value": null,
"points": 40,
"category": "오타감점",
"item": "오타 1개 -1점 / 2503회부터 오타 1개 -1점으로 변경"
}
},
"1": {
"1": {
"path": "//TEXTART[@Text='{searchValue}']/TEXTARTSHAPE/@FontName",
"searchValue": "별사랑천문대캠프안내",
"value": "궁서체",
"points": 1,
"category": "OneAnswer",
"item": "문구 (별사랑천문대캠프안내)/① 글씨체 (궁서체)"
},
"2": {
"path": "//TEXTART[@Text='{searchValue}']/descendant::WINDOWBRUSH/@FaceColor",
"searchValue": "별사랑천문대캠프안내",
"value": "53,164,78",
"points": 2,
"category": "Color",
"item": "문구 (별사랑천문대캠프안내)/② 채우기 : 색상(RGB:53,164,78)"
},
"3": {
"path": "//TEXTART[@Text='{searchValue}']/SHAPEOBJECT/SIZE/@Width",
"searchValue": "별사랑천문대캠프안내",
"value": "120",
"tolerance": 1,
"points": 2,
"category": "mmSize",
"item": "문구 (별사랑천문대캠프안내)/③ 크기-너비 (120 mm)"
},
"4": {
"path": "//TEXTART[@Text='{searchValue}']/SHAPEOBJECT/SIZE/@Height",
"searchValue": "별사랑천문대캠프안내",
"value": "20",
"tolerance": 1,
"points": 2,
"category": "mmSize",
"item": "문구 (별사랑천문대캠프안내)/④ 크기-높이 (20 mm)"
},
"5": {
"path": "//TEXTART[@Text='{searchValue}']/SHAPEOBJECT/POSITION/@TreatAsChar",
"searchValue": "별사랑천문대캠프안내",
"value": "true",
"points": 2,
"category": "OneAnswer",
"item": "문구 (별사랑천문대캠프안내)/⑤ 위치 (글자처럼 취급)"
},
"6": {
"path": "//PARASHAPE[@Id=//P[.//TEXTART[@Text='{searchValue}']]/@ParaShape]/@Align",
"searchValue": "별사랑천문대캠프안내",
"value": "Center",
"points": 2,
"category": "OneAnswer",
"item": "문구 (별사랑천문대캠프안내)/⑥ 정렬 (가운데 정렬)"
},
"7": {
"path": "//TEXTART[@Text='{searchValue}']",
"searchValue": "별사랑천문대캠프안내",
"value": true,
"points": 2,
"category": "Boolean",
"item": "문구 (별사랑천문대캠프안내)/⑦ 글맵시모양 (육안확인)"
},
"8": {
"path": "//RECTANGLE[.//CHAR[text()='{searchValue}']]/SHAPEOBJECT/SIZE",
"searchValue": "별",
"value": {
"Height": 2800,
"Width": 2800
},
"tolerance": 200,
"points": 1,
"category": "TwoLineSize",
"item": "어/① 모양 (2줄)"
},
"9": {
"path": "//TEXT[CHAR[text()='{searchValue}']]/@CharShape",
"searchValue": "별",
"value": "굴림체",
"points": 1,
"category": "FontName",
"item": "어/② 글씨체 (굴림체)"
},
"10": {
"path": "//RECTANGLE[.//CHAR[text()='{searchValue}']]//WINDOWBRUSH/@FaceColor",
"searchValue": "별",
"value": "211,202,24",
"points": 2,
"category": "Color",
"item": "어/③ 면색 : 색상(RGB:211,202,24)"
},
"11": {
"path": "//RECTANGLE[.//CHAR[text()='{searchValue}']]//OUTSIDEMARGIN/@Right",
"searchValue": "별",
"value": "3.0",
"tolerance": 1,
"points": 2,
"category": "mmSize",
"item": "어/④ 본문과의 간격 : 3.0mm"
},
"12": {
"path": "//CHARSHAPE[@Id=//CHAR[contains(text(),'{searchValue}')]/parent::TEXT/@CharShape]",
"searchValue": "천문우주과학 체험학습",
"value": "BOLD",
"points": 2,
"category": "FontAttribute",
"item": "문구 (천문우주과학 체험학습)/① BOLD"
},
"13": {
"path": "//CHARSHAPE[@Id=//CHAR[contains(text(),'{searchValue}')]/parent::TEXT/@CharShape]",
"searchValue": "천문우주과학 체험학습",
"value": "UNDERLINE",
"points": 2,
"category": "FontAttribute",
"item": "문구 (천문우주과학 체험학습)/② UNDERLINE"
},
"14": {
"path": "//CHAR[contains(string(.),'{char1}')]/text()",
"path2": "//CHAR[contains(string(.),'{char2}')]/text()",
"path3": "//CHAR[contains(string(.),'{char3}')]/text()",
"char1": "●",
"char2": "●",
"char3": "※",
"value": 3,
"points": 3,
"category": "SpecialChar",
"item": "① ● , ② ● , ③ ※"
},
"15": {
"path": "//CHAR[contains(text(),'{searchValue}')]/parent::TEXT/@CharShape",
"searchValue": "참가안내",
"value": "굴림",
"points": 1,
"category": "FontName",
"item": "문구 (● 참가안내 ●)/① 글씨체 (굴림)"
},
"16": {
"path": "//PARASHAPE[@Id=//CHAR[contains(text(),'{match_str}')]/ancestor::P/@ParaShape]/@Align",
"match_str": "참가안내",
"value": "Center",
"points": 1,
"category": "Align",
"item": "문구 (● 참가안내 ●)/② 정렬 (가운데 정렬)"
},
"17": {
"path": "//CHARSHAPE[@Id=//TEXT[CHAR[text()='{searchValue}']]/@CharShape]",
"hyperlink_ptag": "//P[.//FIELDBEGIN[@Type='Hyperlink']]",
"searchValue": "그룹별 20명(선착순 마감)",
"value": "ITALIC",
"points": 1,
"category": "FontAttribute",
"item": "문구 (그룹별 20명(선착순 마감))/① ITALIC"
},
"18": {
"path": "//CHARSHAPE[@Id=//TEXT[CHAR[text()='{searchValue}']]/@CharShape]",
"hyperlink_ptag": "//P[.//FIELDBEGIN[@Type='Hyperlink']]",
"searchValue": "그룹별 20명(선착순 마감)",
"value": "UNDERLINE",
"points": 1,
"category": "FontAttribute",
"item": "문구 (그룹별 20명(선착순 마감))/② UNDERLINE"
},
"19": {
"path": "//PARASHAPE[@Id=//CHAR[contains(text(),'{searchValue}')]/ancestor::P/following-sibling::P[1]/@ParaShape]/PARAMARGIN",
"searchValue": "기타사항",
"value": {
"Left": 15,
"Indent": 14
},
"points": 2,
"category": "ParaShape",
"item": "문구 (※ 기타… 이하 문단)/왼쪽여백 (15), 내어쓰기 (14)",
"desc": "내부적으로 내어쓰기는 음수값 / JSON value값은 양수로 입력"
},
"20": {
"path": "//CHARSHAPE[@Id=//CHAR[contains(text(),'{searchValue}')]/parent::TEXT/@CharShape]/@Height",
"searchValue": "2026. 02. 08.",
"value": "1400",
"points": 1,
"category": "OneAnswer",
"item": "문구 (2026. 02. 08.)/① 크기 (1400)",
"desc": "1pt당 100"
},
"21": {
"path": "//PARASHAPE[@Id=//CHAR[contains(text(),'{searchValue}')]/ancestor::P/@ParaShape]/@Align",
"searchValue": "2026. 02. 08.",
"value": "Center",
"points": 1,
"category": "OneAnswer",
"item": "문구 (2026. 02. 08.)/② 정렬 (가운데 정렬)"
},
"22": {
"path": "//TEXT[CHAR[text()='{searchValue}']]/@CharShape",
"searchValue": "별사랑천문대",
"value": "맑은 고딕",
"points": 1,
"category": "FontName",
"item": "문구 (별사랑천문대)/① 글씨체 (맑은 고딕)"
},
"23": {
"path": "//CHARSHAPE[@Id=//TEXT[CHAR[text()='{searchValue}']]/@CharShape]/@Height",
"searchValue": "별사랑천문대",
"value": "2600",
"points": 1,
"category": "OneAnswer",
"item": "문구 (별사랑천문대)/② 크기 (2600)"
},
"24": {
"path": "//PARASHAPE[@Id=//CHAR[text()='{searchValue}']/ancestor::P/@ParaShape]/@Align",
"searchValue": "별사랑천문대",
"value": "Center",
"points": 1,
"category": "OneAnswer",
"item": "문구 (별사랑천문대)/③ 정렬 (가운데 정렬)"
},
"25": {
"path": "//TEXT[CHAR[text()='{searchValue}']]/@CharShape",
"searchValue": "DIAT",
"value": "돋움",
"points": 1,
"category": "Header.FontName",
"item": "문구 (DIAT)/① 글꼴 (돋움)"
},
"26": {
"path": "//CHARSHAPE[@Id=//TEXT[CHAR[text()='{searchValue}']]/@CharShape]/@Height",
"searchValue": "DIAT",
"value": "900",
"points": 1,
"category": "Header.OneAnswer",
"item": "문구 (DIAT)/② 크기 (9pt)"
},
"27": {
"path": "//PARASHAPE[@Id=//CHAR[text()='{searchValue}']/parent::TEXT/parent::P/@ParaShape]/@Align",
"searchValue": "DIAT",
"value": "Right",
"points": 1,
"category": "Header.OneAnswer",
"item": "문구 (DIAT)/③ 정렬 (오른쪽 정렬)"
},
"28": {
"path": "//PAGENUM/@FormatType",
"value": "LatinCapital",
"points": 2,
"category": "PageNumber",
"item": "① 쪽 번호 매기기 (A,B,C 순으로)",
"desc1": {
"가,나,다": "HangulSyllable",
"1,2,3": "Digit",
"일,이,삼": "HangulPhonetic",
"갑,을,병": "DecagonCircle",
"A,B,C": "LatinCapital",
"a,b,c": "LatinSmall",
"Ⓐ,Ⓑ,Ⓒ": "CircledLatinCapital",
"①,②,③": "CircledDigit",
"一,二,三": "Ideograph",
"㉠,㉡,㉢": "CircledHangulJamo",
"ⓐ,ⓑ,ⓒ": "CircledLatinSmall",
"㊀,㊁,㊂": "CircledIdeograph",
"i,ii,iii": "RomanSmall",
"I,II,III": "RomanCapital",
"甲,乙,丙": "DecagonCircleHanja",
"+,++,+++": "UserChar",
"*,**,***": "UserChar",
"정답에 맞는 값 value에 입력": ""
},
"desc2": "1, 2페이지 모두 정답이어야 점수 부여"
},
"29": {
"path": "//PAGENUM/@Pos",
"value": "BottomCenter",
"points": 2,
"category": "PageNumber",
"item": "가운데 아래",
"desc": "1, 2페이지 모두 정답이어야 점수 부여",
"desc2": {
"가운데 아래": "BottomCenter",
"오른쪽 아래": "BottomRight",
"왼쪽 아래": "BottomLeft"
}
},
"30": {
"path": "//PARASHAPE[@Id='{parashape_id}']/PARAMARGIN/@LineSpacing",
"value": "180",
"first_word": "별",
"points": 2,
"category": "LineSpacing",
"item": "문제 1 줄간격 180% 설정",
"desc": "1페이지 문단의 줄간격이 정답이 아닌 문단이 있으면 False(감점), first_word 속성에 [문단 첫글자 장식]에 해당하는 글자를 입력해준다."
}
},
"2": {
"1": {
"path": "//PAGEBORDERFILL[@Type='Both' or @Type='Even']/@HeaderInside",
"path2": "//BORDERFILL[@Id=//PAGEBORDERFILL[@Type='Both' or @Type='Even']/@BorferFill]",
"value": {
"header_inside": true,
"all_double_slim": true
},
"points": 4,
"category": "PageBorder",
"item": "문제2 쪽테두리(이중 실선, 머리말 포함) 설정"
},
"2": {
"path": "count(//SECTION)>1",
"value": true,
"points": 3,
"category": "Boolean",
"item": "① 구역나누기",
"desc": "섹션이 1개 이상이면 점수부여"
},
"3": {
"path": "./TEXT/COLDEF/@Count",
"value": "2",
"points": 3,
"category": "TwoColumn",
"item": "② 다단 2단"
},
"4": {
"path": "//RECTANGLE/SHAPEOBJECT/SIZE/@Width",
"value": "60",
"points": 2,
"category": "Rectangle.mmSize",
"item": "문구 (하늘의 꽃, 별자리)/① 크기-너비 (60 mm)"
},
"5": {
"path": "//RECTANGLE/SHAPEOBJECT/SIZE/@Height",
"value": "12",
"points": 2,
"category": "Rectangle.mmSize",
"item": "문구 (하늘의 꽃, 별자리)/② 크기-높이 (12 mm)"
},
"6": {
"path": "//RECTANGLE//LINESHAPE",
"value": {
"Style": "DoubleSlim",
"Width": "283"
},
"points": 2,
"category": "Rectangle.LineShape",
"item": "문구 (하늘의 꽃, 별자리)/③ 테두리 : 이중 실선(1.00mm)",
"desc": "1mm = 283pt value['Width']에 pt값 입력"
},
"7": {
"path": "//RECTANGLE/@Ratio",
"value": "20",
"points": 2,
"category": "Rectangle.OneAnswer",
"item": "문구 (하늘의 꽃, 별자리)/④ 글상자 모서리 (둥근모양)",
"desc": "모서리 비율 반원:50 / 둥근모양:20"
},
"8": {
"path": "//RECTANGLE//WINDOWBRUSH/@FaceColor",
"value": "191,179,233",
"points": 2,
"category": "Rectangle.Color",
"item": "문구 (하늘의 꽃, 별자리)/⑤ 채우기 : 색상(RGB:191,179,233)"
},
"9": {
"path": "//RECTANGLE/SHAPEOBJECT/POSITION/@TreatAsChar",
"value": "true",
"points": 1,
"category": "Rectangle.OneAnswer",
"item": "문구 (하늘의 꽃, 별자리)/⑥ 글상자 위치 (글자처럼 취급)"
},
"10": {
"path": "//PARASHAPE[@Id='{rect_parashape_id}']/@Align",
"value": "Center",
"points": 1,
"category": "Rectangle.TextBoxAlign",
"item": "문구 (하늘의 꽃, 별자리)/⑦ 글상자 정렬 (가운데 정렬)"
},
"11": {
"path": ".//RECTANGLE//TEXT/@CharShape",
"value": "중고딕",
"points": 1,
"category": "Rectangle.FontName",
"item": "문구 (하늘의 꽃, 별자리)/⑧ 글씨체 (중고딕)"
},
"12": {
"path": "//CHARSHAPE[@Id='{rect_charshape_id}']/@Height",
"value": "1600",
"points": 1,
"category": "Rectangle.FontSize",
"item": "문구 (하늘의 꽃, 별자리)/⑨ 글씨크기 (1600)",
"desc": "1pt당 100"
},
"13": {
"path": "//PARASHAPE[@Id={rect_parashape_id}]/@Align",
"value": "Center",
"points": 1,
"category": "Rectangle.TextBoxAlign",
"item": "문구 (하늘의 꽃, 별자리)/⑩ 정렬 (가운데 정렬)"
},
"14": {
"path": "//BINITEM[@BinData=//PICTURE/IMAGE/@BinItem][@Format='JPG' or @Format='JPEG' or @Format='PNG']",
"value": true,
"points": 2,
"category": "Boolean",
"item": "① 파일명 \"그림D.jpg\" 삽입",
"desc": "첨부 이미지 파일명 손상으로 정상적인 채점이 불가한 경우가 발견되어서 이미지 첨부 여부로 채점 방식 변경 (7/3)"
},
"15": {
"path": "//PICTURE[./IMAGE[@BinItem=//BINITEM[@Format='JPG' or @Format='JPEG' or @Format='PNG']/@BinData]]/SHAPEOBJECT/SIZE/@Width",
"value": "80",
"points": 2,
"category": "mmSize",
"item": "② 크기-너비 (80 mm)"
},
"16": {
"path": "//PICTURE[./IMAGE[@BinItem=//BINITEM[@Format='JPG' or @Format='JPEG' or @Format='PNG']/@BinData]]/SHAPEOBJECT/SIZE/@Height",
"value": "40",
"points": 2,
"category": "mmSize",
"item": "③ 크기-높이 (40 mm)"
},
"17": {
"path": "//PICTURE[./IMAGE[@BinItem=//BINITEM[@Format='JPG' or @Format='JPEG' or @Format='PNG']/@BinData]]/SHAPEOBJECT/POSITION[not(@TreatAsChar='true') and @HorzRelTo='Page']/@HorzOffset",
"value": "0",
"points": 2,
"category": "mmSize",
"item": "④ 위치 (어울림 : 가로-쪽의 왼쪽 0mm)"
},
"18": {
"path": "//PICTURE[./IMAGE[@BinItem=//BINITEM[@Format='JPG' or @Format='JPEG' or @Format='PNG']/@BinData]]/SHAPEOBJECT/POSITION[not(@TreatAsChar='true') and @HorzRelTo='Page']/@VertOffset",
"value": "24",
"points": 2,
"category": "mmSize",
"item": "⑤ 위치 (어울림 : 세로-쪽의 위 24 mm)"
},
"19": {
"path": "//TEXT[CHAR[text()='{searchValue}']]/@CharShape",
"searchValue": "1. 별자리란?",
"value": "돋움체",
"points": 1,
"category": "FontName",
"item": "문구① (1. 별자리란?)/① 글씨체 (돋움체)"
},
"20": {
"path": "//CHARSHAPE[@Id=//TEXT[CHAR[text()='{searchValue}']]/@CharShape]/@Height",
"searchValue": "1. 별자리란?",
"value": "1200",
"points": 1,
"category": "OneAnswer",
"item": "문구① (1. 별자리란?)/② 크기 (1200)"
},
"21": {
"path": "//CHARSHAPE[@Id=//TEXT[CHAR[text()='{searchValue}']]/@CharShape]",
"searchValue": "1. 별자리란?",
"value": "BOLD",
"points": 1,
"category": "FontAttribute",
"item": "문구① (1. 별자리란?)/③ 진하게"
},
"22": {
"path": "//TEXT[CHAR[text()='{searchValue}']]/@CharShape",
"searchValue": "2. 우리나라 계절별 별자리",
"value": "돋움체",
"points": 1,
"category": "FontName",
"item": "문구② (2. 우리나라 계절별 별자리)/① 글씨체 (돋움체)"
},
"23": {
"path": "//CHARSHAPE[@Id=//TEXT[CHAR[text()='{searchValue}']]/@CharShape]/@Height",
"searchValue": "2. 우리나라 계절별 별자리",
"value": "1200",
"points": 1,
"category": "OneAnswer",
"item": "문구② (2. 우리나라 계절별 별자리)/② 크기 (1200)"
},
"24": {
"path": "//CHARSHAPE[@Id=//TEXT[CHAR[text()='{searchValue}']]/@CharShape]",
"searchValue": "2. 우리나라 계절별 별자리",
"value": "BOLD",
"points": 1,
"category": "FontAttribute",
"item": "문구② (2. 우리나라 계절별 별자리)/③ 진하게"
},
"25": {
"path": "boolean(//TEXT[CHAR[contains(text(),'{option}')]]/FOOTNOTE)",
"path2": "boolean(//CHAR[substring(., string-length(.) - string-length('{option}') + 1) = '{option}']/following-sibling::FOOTNOTE/descendant::CHAR)",
"option": "북두칠성",
"value": true,
"points": 2,
"category": "Boolean",
"item": "문구 (북두칠성)/① 각주 설정 및 문구 입력"
},
"26": {
"path": "//CHAR[contains(text(),'{searchValue}')]/parent::TEXT/@CharShape",
"searchValue": "큰곰자리의 꼬리에 해당하는 7개의 별",
"value": "굴림",
"points": 1,
"category": "FontName",
"item": "문구 (북두칠성)/② 글씨체 (굴림)"
},
"27": {
"path": "//CHARSHAPE[@Id=//TEXT[CHAR[contains(text(),'{searchValue}')]]/@CharShape]/@Height",
"searchValue": "큰곰자리의 꼬리에 해당하는 7개의 별",
"value": "900",
"points": 1,
"category": "OneAnswer",
"item": "문구 (북두칠성)/③ 크기 (9pt)"
},
"28": {
"path": "//P[TEXT[CHAR[contains(text(), '{searchValue}')]]]//AUTONUMFORMAT/@Type",
"searchValue": "큰곰자리의 꼬리에 해당하는 7개의 별",
"value": "CircledLatinCapital",
"points": 2,
"category": "OneAnswer",
"item": "문구 (전당)/④ 각주 번호모양",
"desc": {
"가,나,다": "HangulSyllable",
"1,2,3": "Digit",
"일,이,삼": "HangulPhonetic",
"갑,을,병": "DecagonCircle",
"A,B,C": "LatinCapital",
"a,b,c": "LatinSmall",
"Ⓐ,Ⓑ,Ⓒ": "CircledLatinCapital",
"①,②,③": "CircledDigit",
"一,二,三": "Ideograph",
"㉠,㉡,㉢": "CircledHangulJamo",
"ⓐ,ⓑ,ⓒ": "CircledLatinSmall",
"㊀,㊁,㊂": "CircledIdeograph",
"i,ii,iii": "RomanSmall",
"I,II,III": "RomanCapital",
"甲,乙,丙": "DecagonCircleHanja",
"+,++,+++": "UserChar",
"*,**,***": "UserChar",
"정답에 맞는 값 value에 입력": ""
}
},
"29": {
"path": "boolean(//CHAR[contains(text(),'{ignoreWord}')])",
"ignoreWord": "Babylonia",
"value": true,
"points": 3,
"category": "Boolean",
"item": "Babylonia/영단어 미입력, 대소문자/오타 시 전체 감점",
"desc": "유사도 검사를 진행하지 않고 영단어가 모두 일치해야 하므로 xpath구문 내 단어도 수정필요"
},
"30": {
"path": "//CHAR[contains(text(),'{kor}')][contains(text(),'{chn}')]",
"word": [
["성좌", "星座"],
["유래", "由來"],
["표석", "標石"],
["천구", "天球"],
["계절", "季節"]
],
"value": 10,
"points": 10,
"category": "Hanja",
"item": "① 성좌(星座), ② 유래(由來), ③ 표석(標石), ④ 천구(天球), ⑤ 계절(季節)"
},
"31": {
"path": "boolean(//CHAR[contains(translate(text(), ' ', ''),'역에해당')])",
"value": true,
"points": 3,
"category": "Boolean",
"item": "문구 (…지역이 해당하는 티그리스강과…)>'이 → 에' 글자바꿈"
},
"32": {
"path": "boolean(//CHAR[contains(translate(text(), ' ', ''),'나는반드')])",
"value": true,
"points": 3,
"category": "Boolean",
"item": "문구 (…둘 중 반드시 하나는…)>'반드시 / 하나는' 순서바꿈"
},
"33": {
"path": "//TEXT[CHAR[contains(text(),'{searchValue}')]]/@CharShape",
"searchValue": "지역별 천문대 수",
"value": "돋움체",
"points": 1,
"category": "FontName",
"item": "제목 문구 (지역별 천문대 수)/① 글씨체 (돋움체)"
},
"34": {
"path": "//CHARSHAPE[@Id=//TEXT[CHAR[text()='{searchValue}']]/@CharShape]/@Height",
"searchValue": "지역별 천문대 수",
"value": "1200",
"points": 1,
"category": "OneAnswer",
"item": "제목 문구 (지역별 천문대 수)/② 크기 (1200)"
},
"35": {
"path": "//CHARSHAPE[@Id=//TEXT[CHAR[text()='{searchValue}']]/@CharShape]",
"searchValue": "지역별 천문대 수",
"value": "BOLD",
"points": 1,
"category": "FontAttribute",
"item": "제목 문구 (지역별 천문대 수)/③ 진하게"
},
"36": {
"path": "//PARASHAPE[@Id=//P[.//CHAR[text()='{searchValue}']]/@ParaShape]/@Align",
"searchValue": "지역별 천문대 수",
"value": "Center",
"points": 1,
"category": "OneAnswer",
"item": "제목 문구 (지역별 천문대 수)/④ 정렬 (가운데 정렬)"
},
"37": {
"path": "//BORDERFILL[@Id=//TABLE/ROW[1]/CELL/@BorderFill]/FILLBRUSH/WINDOWBRUSH/@FaceColor",
"path2": "//BORDERFILL[@Id=//CELLZONE[@StartRowAddr='0' and @EndRowAddr='0' and @StartColAddr='0' and @EndColAddr=(ancestor::TABLE[1]/@ColCount)-1]/@BorderFill]/FILLBRUSH/WINDOWBRUSH/@FaceColor",
"value": "255,164,95",
"points": 2,
"category": "Color",
"item": "위쪽 제목 셀/① 색상(RGB:255,164,95)"
},
"38": {
"path": "//CHARSHAPE[@Id=//TABLE/ROW[1]/descendant::TEXT/@CharShape]",
"value": "BOLD",
"points": 1,
"category": "FontAttribute",
"item": "위쪽 제목 셀/② 진하게",
"desc": "글자 속성이라 CELLZONE으로 적용 되지 않음"
},
"39": {
"path": "//BORDERFILL[@Id=//TABLE/ROW[1]/CELL/@BorderFill]/BOTTOMBORDER/@Type",
"path2": "//BORDERFILL[@Id=//CELLZONE[@StartRowAddr='0' and @EndRowAddr='0' and @StartColAddr='0' and @EndColAddr=(ancestor::TABLE[1]/@ColCount)-1]/@BorderFill]/BOTTOMBORDER/@Type",
"value": "DoubleSlim",
"points": 2,
"category": "TableAnswer",
"item": "제목 셀 아래선/① 이중실선"
},
"40": {
"path": "//BORDERFILL[@Id=//TABLE/ROW[1]/CELL/@BorderFill]/BOTTOMBORDER/@Width",
"path2": "//BORDERFILL[@Id=//CELLZONE[@StartRowAddr='0' and @EndRowAddr='0' and @StartColAddr='0' and @EndColAddr=(ancestor::TABLE[1]/@ColCount)-1]/@BorderFill]/BOTTOMBORDER/@Width",
"value": "0.5mm",
"points": 2,
"category": "TableAnswer",
"item": "제목 셀 아래선/② 0.5mm"
},
"41": {
"path": "//TABLE//TEXT/@CharShape",
"path2": "//FONTFACE[@Lang='Hangul']/FONT[@Id=//CHARSHAPE[@Id=//TABLE/ROW/descendant::TEXT/@CharShape]/FONTID/@Hangul]/@Name",
"value": "궁서",
"points": 1,
"category": "TableFontName",
"category_tmp": "FontName",
"item": "글자모양/① 글씨체 (궁서)",
"desc": "테이블 폰트명 문항은 테이블의 모든 셀이 정답폰트와 일치해야 함, 하나만 일치해도 정답으로 채점할 경우 category값을 FontName으로 변경"
},
"42": {
"path": "//CHARSHAPE[@Id=//TABLE//TEXT/@CharShape]/@Height",
"value": "1000",
"points": 1,
"category": "TableAnswer",
"item": "글자모양/② 크기 (1000)"
},
"43": {
"path": "//PARASHAPE[@Id=//TABLE/ROW//P/@ParaShape]/@Align",
"value": "Center",
"points": 1,
"category": "TableAnswer",
"item": "글자모양/③ 정렬 (가운데 정렬)"
},
"44": {
"path": "boolean(//TABLE[1]/ROW[last()]/CELL[position()=last()]//FIELDBEGIN[starts-with(@Command, '={option}')]) and boolean(//TABLE[1]/ROW[last()]/CELL[position()=last()-1]//FIELDBEGIN[starts-with(@Command, '={option}')])",
"option": "SUM",
"value": true,
"points": 4,
"category": "Boolean",
"item": "블록 계산식/합계",
"desc": "option값에 합계는 SUM / 평균은 AVG"
},
"45": {
"chart_xpath": "",
"chart_type": "묶은 가로 막대형",
"value": true,
"points": 2,
"category": "ChartType",
"item": "① 종류 (묶은 가로 막대형)",
"desc": "chart_type을 입력받아 차트타입에 맞는 xml요소가 있는지 내부적으로 검사, chart_type만 한글로 입력해주면 된다. (공백무시)"
},
"46": {
"chart_xpath": "//c:valAx/c:majorTickMark/@val",
"value": "out",
"points": 2,
"category": "ChartOneAnswer",
"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": "③ 크기-너비 (80 mm)"
},
"48": {
"path": "//OLE[@BinItem=//BINITEM[@Format='OLE']/@BinData]//SIZE/@Height",
"value": "90",
"points": 2,
"category": "mmSize",
"item": "④ 크기-높이 (90 mm)"
},
"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": "1400",
"points": 1,
"category": "OneAnswer",
"item": "제목 문구 (지역별 천문대 수)/② 크기 (1400)"
},
"52": {
"chart_xpath": "//a:t[text()='{searchValue}']/ancestor::a:r/a:rPr/@{option}",
"option": "b",
"searchValue": "지역별 천문대 수",
"value": "1",
"points": 1,
"category": "OneAnswer",
"item": "제목 문구 (지역별 천문대 수)/③ 기울임",
"desc": "option값 - 기울임(Italic):i / 굵게(Bold):b"
},
"53": {
"chart_xpath": "//c:catAx/c:txPr//a:ea/@typeface",
"value": "바탕",
"points": 1,
"category": "ChartOneAnswer",
"item": "X축/① 글꼴 (바탕)"
},
"54": {
"chart_xpath": "//c:catAx/c:txPr//a:defRPr/@sz",
"value": "900",
"points": 1,
"category": "ChartOneAnswer",
"item": "X축/② 크기 (9pt)"
},
"55": {
"chart_xpath": "//c:catAx/c:txPr//a:defRPr/@{option}",
"option": "i",
"value": "1",
"points": 1,
"category": "ChartOneAnswer",
"item": "X축/③ 기울임",
"desc": "option값 - 기울임(Italic):i / 굵게(Bold):b"
},
"56": {
"chart_xpath": "//c:valAx/c:txPr//a:ea/@typeface",
"value": "바탕",
"points": 1,
"category": "ChartOneAnswer",
"item": "Y축/① 글꼴 (바탕)"
},
"57": {
"chart_xpath": "//c:valAx/c:txPr//a:defRPr/@sz",
"value": "900",
"points": 1,
"category": "ChartOneAnswer",
"item": "Y축/② 크기 (9pt)"
},
"58": {
"chart_xpath": "//c:valAx/c:txPr//a:defRPr/@{option}",
"option": "i",
"value": "1",
"points": 1,
"category": "ChartOneAnswer",
"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"
}
}
}

View File

@@ -1 +1 @@
[{"kind":1,"language":"markdown","value":"# XPath Notebook\nDate: 2025-01-22     Time: 16:12:58"},{"kind":1,"language":"markdown","value":"* mm > pt 변환비율 = 2.83465 \r\n* 283.465"},{"kind":1,"language":"markdown","value":"- 색상 demical 코드 [1-2] [1-10] [2-8] [2-37]"},{"kind":2,"language":"xpath","value":"//TEXTART[@Text='클라우드컴퓨팅컨퍼런스']/descendant::WINDOWBRUSH/@FaceColor"},{"kind":2,"language":"xpath","value":"//RECTANGLE[.//CHAR[text()='전']]//WINDOWBRUSH/@FaceColor"},{"kind":2,"language":"xpath","value":"//RECTANGLE//CHAR[text()='클라우드 컴퓨팅']/ancestor::RECTANGLE/descendant::WINDOWBRUSH/@FaceColor"},{"kind":2,"language":"xpath","value":"//BORDERFILL[@Id=//TABLE/ROW[1]/CELL/@BorderFill]/FILLBRUSH/WINDOWBRUSH/@FaceColor"},{"kind":1,"language":"markdown","value":"- [1-10] ① ●, ② ●, ③ ※"},{"kind":2,"language":"xpath","value":"\"path\": \"count(//CHAR[contains(text(),'●')]) + count(//CHAR[contains(text(),'※')])\",\r\n \"path2\": \"string-length(//CHAR[contains(text(),'●')]) - string-length(translate(//CHAR[contains(text(),'●')], '●', '')) + string-length(//CHAR[contains(text(),'※')]) - string-length(translate(//CHAR[contains(text(),'※')], '※', ''))\","},{"kind":1,"language":"markdown","value":"- [1-28] [2-28] @FormatType 종류\r\n - HangulSyllable : 가나다\r\n - Digit : 123\r\n - DecagonCircle : 갑을병정\r\n - LatinCapital : ABC\r\n - CircledDigit : ①,②,③\r\n - Ideograph : 一,二,三\r\n - CircledHangulJamo : ㉠,㉡,㉢\r\n - CircledLatinSmall : ⓐ,ⓑ,ⓒ\r\n - RomanSmall : i,ii,iii"},{"kind":2,"language":"xpath","value":"//SECTION[1]//PAGENUM/@FormatType"},{"kind":2,"language":"xpath","value":"//P[TEXT[CHAR[contains(text(), '눈으로 읽는 대신 귀로 들을 수 있게 책의 내용(문자)을 음성으로 녹음하여 기록한 것을 의미함')]]]//AUTONUMFORMAT/@Type"},{"kind":1,"language":"markdown","value":"- [2-30] ① 저감(低減), ② 화석(化石), ③ 투자(投資), ④ 달성(達成), ⑤ 세금(稅金)"},{"kind":2,"language":"xpath","value":"(count(//CHAR[contains(text(),'저감')][contains(text(),'低減')])+count(//CHAR[contains(text(),'화석')][contains(text(),'化石')])+count(//CHAR[contains(text(),'투자')][contains(text(),'投資')])+count(//CHAR[contains(text(),'달성')][contains(text(),'達成')])+count(//CHAR[contains(text(),'세금')][contains(text(),'稅金')]))*2"},{"kind":1,"language":"markdown","value":"- [2-37] [2-39] [2-40] @EndColAddr 속성값 \r\n - 표의 열 갯수-1\r\n - 4개=3 / 3개=2 / 2개=1"},{"kind":2,"language":"xpath","value":"@EndColAddr='2'"},{"kind":1,"language":"markdown","value":"- [2-45]\r\n - 꺾은선형 //c:lineChart[c:grouping[@val='standard']]\r\n - 묶은가로막대형 //c:barChart[c:barDir[@val='bar'] and c:grouping[@val='clustered']]\r\n - 묶은세로막대형 //c:barChart[c:barDir[@val='col'] and c:grouping[@val='clustered']]\r\n - 원형 //c:pieChart\r\n - 분산형 //c:scatterChart"},{"kind":1,"language":"markdown","value":"//c:{chart_type}Chart/"},{"kind":2,"language":"xpath","value":"boolean(//c:barChart[c:barDir[@val='col'] and c:grouping[@val='clustered']])"},{"kind":2,"language":"xpath","value":"//c:valAx/c:majorTickMark/@val"},{"kind":2,"language":"xpath","value":"boolean(//TABLE[1]/ROW[last()]/CELL[position()=last()]//FIELDBEGIN[starts-with(@Command, '={option}')]) and boolean(//TABLE[1]/ROW[last()]/CELL[position()=last()-1]//FIELDBEGIN[starts-with(@Command, '={option}')])"},{"kind":2,"language":"xpath","value":"//TABLE[1]/ROW[last()]/CELL[position()=last()]//FIELDBEGIN[starts-with(@Command, '=AVG')]"},{"kind":2,"language":"xpath","value":"//TABLE[1]/ROW[last()]/CELL[position()=last()-1]//FIELDBEGIN[starts-with(@Command, '=AVG')]"}]
[{"kind":1,"language":"markdown","value":"# XPath Notebook\nDate: 2025-01-22     Time: 16:12:58"},{"kind":2,"language":"xpath","value":""},{"kind":1,"language":"markdown","value":"* mm > pt 변환비율 = 2.83465 \r\n* 283.465"},{"kind":1,"language":"markdown","value":"- 색상 demical 코드 [1-2] [1-10] [2-8] [2-37]"},{"kind":2,"language":"xpath","value":"//TEXTART[@Text='클라우드컴퓨팅컨퍼런스']/descendant::WINDOWBRUSH/@FaceColor"},{"kind":2,"language":"xpath","value":"//RECTANGLE[.//CHAR[text()='전']]//WINDOWBRUSH/@FaceColor"},{"kind":2,"language":"xpath","value":"//RECTANGLE//CHAR[text()='클라우드 컴퓨팅']/ancestor::RECTANGLE/descendant::WINDOWBRUSH/@FaceColor"},{"kind":2,"language":"xpath","value":"//BORDERFILL[@Id=//TABLE/ROW[1]/CELL/@BorderFill]/FILLBRUSH/WINDOWBRUSH/@FaceColor"},{"kind":1,"language":"markdown","value":"- [1-10] ① ●, ② ●, ③ ※"},{"kind":2,"language":"xpath","value":"\"path\": \"count(//CHAR[contains(text(),'●')]) + count(//CHAR[contains(text(),'※')])\",\r\n \"path2\": \"string-length(//CHAR[contains(text(),'●')]) - string-length(translate(//CHAR[contains(text(),'●')], '●', '')) + string-length(//CHAR[contains(text(),'※')]) - string-length(translate(//CHAR[contains(text(),'※')], '※', ''))\","},{"kind":1,"language":"markdown","value":"- [1-28] [2-28] @FormatType 종류\r\n - HangulSyllable : 가나다\r\n - Digit : 123\r\n - DecagonCircle : 갑을병정\r\n - LatinCapital : ABC\r\n - CircledDigit : ①,②,③\r\n - Ideograph : 一,二,三\r\n - CircledHangulJamo : ㉠,㉡,㉢\r\n - CircledLatinSmall : ⓐ,ⓑ,ⓒ\r\n - RomanSmall : i,ii,iii"},{"kind":2,"language":"xpath","value":"//SECTION[1]//PAGENUM/@FormatType"},{"kind":2,"language":"xpath","value":"//P[TEXT[CHAR[contains(text(), '눈으로 읽는 대신 귀로 들을 수 있게 책의 내용(문자)을 음성으로 녹음하여 기록한 것을 의미함')]]]//AUTONUMFORMAT/@Type"},{"kind":1,"language":"markdown","value":"- [2-30] ① 저감(低減), ② 화석(化石), ③ 투자(投資), ④ 달성(達成), ⑤ 세금(稅金)"},{"kind":2,"language":"xpath","value":"(count(//CHAR[contains(text(),'저감')][contains(text(),'低減')])+count(//CHAR[contains(text(),'화석')][contains(text(),'化石')])+count(//CHAR[contains(text(),'투자')][contains(text(),'投資')])+count(//CHAR[contains(text(),'달성')][contains(text(),'達成')])+count(//CHAR[contains(text(),'세금')][contains(text(),'稅金')]))*2"},{"kind":1,"language":"markdown","value":"- [2-37] [2-39] [2-40] @EndColAddr 속성값 \r\n - 표의 열 갯수-1\r\n - 4개=3 / 3개=2 / 2개=1"},{"kind":2,"language":"xpath","value":"@EndColAddr='2'"},{"kind":1,"language":"markdown","value":"- [2-45]\r\n - 꺾은선형 //c:lineChart[c:grouping[@val='standard']]\r\n - 묶은가로막대형 //c:barChart[c:barDir[@val='bar'] and c:grouping[@val='clustered']]\r\n - 묶은세로막대형 //c:barChart[c:barDir[@val='col'] and c:grouping[@val='clustered']]\r\n - 원형 //c:pieChart\r\n - 분산형 //c:scatterChart"},{"kind":1,"language":"markdown","value":"//c:{chart_type}Chart/"},{"kind":2,"language":"xpath","value":"boolean(//c:barChart[c:barDir[@val='col'] and c:grouping[@val='clustered']])"},{"kind":2,"language":"xpath","value":"//c:valAx/c:majorTickMark/@val"},{"kind":2,"language":"xpath","value":"boolean(//TABLE[1]/ROW[last()]/CELL[position()=last()]//FIELDBEGIN[starts-with(@Command, '={option}')]) and boolean(//TABLE[1]/ROW[last()]/CELL[position()=last()-1]//FIELDBEGIN[starts-with(@Command, '={option}')])"},{"kind":2,"language":"xpath","value":"//TABLE[1]/ROW[last()]/CELL[position()=last()]//FIELDBEGIN[starts-with(@Command, '=AVG')]"},{"kind":2,"language":"xpath","value":"//TABLE[1]/ROW[last()]/CELL[position()=last()-1]//FIELDBEGIN[starts-with(@Command, '=AVG')]"}]

View File

@@ -1,659 +0,0 @@
import tkinter as tk
from tkinter import filedialog, messagebox
from datetime import datetime
import difflib
import json
from pathlib import Path
import os
from lxml import etree as ET
import re
from difflib import SequenceMatcher
import pandas as pd
import base64
# from xpathSearch import XMLPathHandler
class XMLScorer:
# 채점 기준 경로 초기화
def __init__(self, scoring_criteria_path):
# 채점 기준 로드
self.scoring_criteria = self._load_scoring_criteria(scoring_criteria_path)
def set_typo_score(self, score):
self.typo_score = score
def get_typo_score(self):
return self.typo_score
# 채점 기준파일 로드(JSON 파일)
def _load_scoring_criteria(self, file_path):
with open(file_path, 'r', encoding='utf-8') as f:
return json.load(f)
# XML 파일에서 element의 값을 찾아 반환
def query_xml(self, root, *args):
first_xpath = args[0]
second_xpath = args[1]
points = args[2]
category = args[3]
right_answer = args[4]
if "Hyperlink" in category:
is_hyperlink = self.scoring_criteria["1"]["17"]["hyperlink"]
hyperlink_xpath = self.scoring_criteria["1"]["17"]["hyperlink_xpath"]
right_text = self.scoring_criteria["1"]["17"]["searchValue"].replace(" ","")
try:
# 하이퍼링크가 포함된 p태그 인지 확인
p_elements = root.xpath(is_hyperlink)
for p in p_elements:
text_list = p.xpath(".//CHAR/text()")
full_text = ''.join(text_list).replace(" ", "")
# right_text의 첫 문자
first_char = right_text[0]
# full_text에서 첫 문자 위치 찾기
index = full_text.find(first_char)
if index != -1:
trimmed_full_text = full_text[index:]
else:
trimmed_full_text = full_text # 일치 문자 없으면 원본 그대로
similarity = difflib.SequenceMatcher(None, trimmed_full_text, right_text).ratio()
# 두 문자열이 같을 경우만 하이퍼링크 확인
if similarity >= 0.7:
inside_field = False
charshape_values = []
for elem in p.iter():
# 시작 지점 확인
if elem.tag == "FIELDBEGIN":
inside_field = True
elif elem.tag == "FIELDEND":
inside_field = False
elif inside_field and elem.tag == "TEXT":
charshape = elem.get("CharShape")
if charshape:
charshape_values.append(charshape)
# 하이퍼링크에 해당하는 P태그 내 존재하는 charshape ID값 모두를 비교해 해당 속성(ITALIC, BOLD, UNDERLINE) 확인
if charshape_values:
for charshape in charshape_values:
result = root.xpath(hyperlink_xpath.replace('{charshape_id}', charshape))
# 해당 속성이 하나라도 적용되어있지 않으면 False 반환
if not result:
return result
return True
else:
return False
except ET.XPathEvalError as e:
return None
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:
result2 = root.xpath(second_xpath)
return max(result, result2)
else:
return result
except ET.XPathEvalError as e:
return None
# xpath2가 있는 경우
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
# xpath1과 xpath2의 결과값이 모두 리스트인 경우
# 두 결과값 중 정답이 포함된 리스트를 반환
if type(result1) is list and type(result2) is list:
if right_answer in result1:
return result1
elif right_answer in result2:
return result2
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):
"""
전체 문서에서 유사한 텍스트를 찾아 반환
Args:
root (_type_): xml root element 객체
target_text (_type_): 찾을 텍스트
threshold (float, optional): 유사도 설정 Defaults to 0.3.
Returns:
str: 유사도 기준을 만족하는 텍스트
"""
# 전체 텍스트 추출
# all_text = root.xpath(f"//CHAR/text()")
# all_text.append(root.xpath(f"//TEXTART/@text"))
namespaces = {
'a': 'http://schemas.openxmlformats.org/drawingml/2006/main',
'c': 'http://schemas.openxmlformats.org/drawingml/2006/chart'
}
if type(root) is str:
all_text = root
else:
all_text = root.xpath(f"//BODY//text() | //TEXTART/@Text | //c:chart//text()", namespaces=namespaces)
# 유사도 비교
max_score = 0
similar_text = ''
for text in all_text:
score = SequenceMatcher(None, target_text, text).ratio()
if score > max_score:
max_score = score
similar_text = text
if max_score >= threshold:
return similar_text
else:
return None
# 하나의 XML 파일 채점
def _score_xml_file(self, xml_file, chart_xml):
try:
tree = ET.parse(xml_file)
root = tree.getroot()
# 네임스페이스 정의
namespaces = {
'a': 'http://schemas.openxmlformats.org/drawingml/2006/main',
'c': 'http://schemas.openxmlformats.org/drawingml/2006/chart'
}
# 차트 XML 파일이 없는 경우 0점 채점을 위헤 빈 XML 생성
if chart_xml is None:
chart_tree = ET.fromstring('<xml></xml>')
else:
chart_tree = ET.fromstring(chart_xml)
# 결과값을 Dictionary로 저장
results = {
'filename': os.path.basename(xml_file),
'score_results': [],
'total_score': 0,
'partial_scores': []
}
print(f"File name: {results['filename']}")
total_score = 0
for section_id, section in self.scoring_criteria.items():
partial_score = 0
for criterion_id, criterion in section.items():
id = criterion_id
xpath = criterion['path'] if 'path' in criterion else None
xpath2 = criterion['path2'] if 'path2' in criterion else None
search_value = criterion['searchValue'] if 'searchValue' in criterion else None
right_answer = criterion['value'] if 'value' in criterion else None
points = criterion['points'] if 'points' in criterion else None
category = criterion['category'] if 'category' in criterion else None
item = criterion['item']
similar_text = None
# chart xml 파일에서 채점하는 경우
if "chart_xml" in category:
if search_value is not None:
similar_text = self.find_similar_text(chart_tree, search_value)
if similar_text is None:
xpath = xpath.replace('{searchValue}', search_value)
else:
xpath = xpath.replace('{searchValue}', similar_text)
result = self.chart_query_xml(chart_tree, xpath, namespaces)
# 그 외의 hml 파일에서 채점하는 경우
else:
if search_value is not None:
similar_text = self.find_similar_text(root, search_value)
if similar_text is None:
xpath = xpath.replace('{searchValue}', search_value)
else:
xpath = xpath.replace('{searchValue}', similar_text)
result = self.query_xml(root, xpath, xpath2, points, category, right_answer)
# [ boolean 타입 ]
# 1. 이텔릭체, 굵게, 밑줄 등 효과가 적용 여부에 따라
# [ITALIC] [BOLD] [UNDERLINE] 태그가 있거나 없을 수 있으므로
# 존재 유무에 따라 True, False로 판단
# 2. 두 가지 이상의 조건을 모두 만족해야 하는 경우 and 연산자로 연결되어
# 반환값 True/False로 판단
# [ float 타입 ]
# 1. 부분점수의 합산으로 반환되는 경우 float 타입으로 반환
if type(result) is not list:
if type(result) is float and (result > points):
actual_answer = float(points)
else:
actual_answer = result
else:
if type(right_answer) is int:
actual_answer = int(result[0])
else:
actual_answer = result[0]
if "오타감점" in category:
points = self.get_typo_score()
scoring = {
'section': section_id,
'id': id,
'category': category, # 채점 분류
'item': item, # 채점 항목
'right_answer': right_answer, # 정답
'actual_answer': actual_answer, # 실제 작성 답안
'points': points,
'deductions': [] # 각 기준별 감점 내역
}
# 점수 차감 조건
# 1. 정답이 실수형으로 반환받은 경우는 채점항목의 부분점수 합산 결과이므로
# 반환받은 값 그대로를 점수로 사용
# 2. 정답이 정수형(사이즈 비교)의 경우 오차범위를 넘는다면 감점
# 3. 그 외의 경우 정답과 실제 작성 답안이 다른 경우 점수 차감
if type(actual_answer) is float:
scoring['points'] = actual_answer
elif type(actual_answer) is int:
# 오차범위 3 이상이면 감점
if abs(actual_answer - right_answer) > 3:
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']
partial_score += scoring['points']
print(f'scoring: {scoring}')
results['partial_scores'].append({
'section': section_id,
'score': partial_score
})
results['total_score'] = total_score
return results
except ET.ParseError as e:
return {
'filename': os.path.basename(xml_file),
'error': f"XML 파싱 오류: {str(e)}",
'total_score': 0
}
def binary_to_chartxml(self, xml_path):
tree = ET.parse(xml_path)
root = tree.getroot()
binary_data = root.xpath('//BINDATA[@Id=//BINITEM[@Format="OLE"]/@BinData]/text()')
if not binary_data:
return None
binary_data = binary_data[0].encode('utf-8')
# <BINDATA ...> 태그와 그 내부 내용을 삭제합니다.
encoded_data = re.sub(b'<BINDATA.*?>', b'', binary_data)
encoded_data = encoded_data.replace(b'</BINDATA>', b'')
encoded_data = encoded_data.replace(b'\r\n', b'')
# base64 디코딩을 수행합니다.
decoded_data = base64.b64decode(encoded_data+b'==')
# 디코딩된 데이터 내용 중 xml 형식만 추출할 때 <c:chartSpace>, </c:chartSpace> 사이의 데이터만 추출.
start = decoded_data.find(b'<?xml')
print(start)
end = decoded_data.find(b'</c:chartSpace>')
print(end)
xml_data = decoded_data[start:end+len(b'</c:chartSpace>')]
# xml 데이터가 없는 경우 None을 반환합니다.
if -1 in [start, end]:
return None
# 디코딩된 데이터를 파일로 저장합니다.
base_filename = os.path.splitext(xml_path)[0]
new_filename = f'{base_filename}.xml'
with open(new_filename, 'wb') as file:
file.write(xml_data)
return xml_data
def typo_check(self, correct_answer_file, user_answer_file, chart_xml):
user_answer_tree = ET.parse(user_answer_file)
user_answer_root = user_answer_tree.getroot()
correct_answer_tree = ET.parse(correct_answer_file)
correct_answer_root = correct_answer_tree.getroot()
# xpath로 바이너리 부분추출
user_input_text = user_answer_root.xpath('//CHAR//text()[not(ancestor::HEADER) and not(ancestor::TABLE)]')
user_table_text = user_answer_root.xpath('//TABLE//CHAR//text()')
user_input_text += user_table_text
correct_input_text = correct_answer_root.xpath('//CHAR//text()[not(ancestor::HEADER) and not(ancestor::TABLE)]')
correct_table_text = correct_answer_root.xpath('//TABLE//CHAR//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]
correct_input_text = [text.replace(' ', '') for text in correct_input_text]
# 숫자와 특정 형식 제거 (예: 1., 2., 3., -)
user_input_text = [re.sub(r'\d+\.\s*|-', '', text) for text in user_input_text]
correct_input_text = [re.sub(r'\d+\.\s*|-', '', text) for text in correct_input_text]
try :
# xpath = self.scoring_criteria["2"]["29"]['path'].split("'")[1]
# ignore_word = xpath.split("'")[1]
ignore_word = self.scoring_criteria["2"]["29"]["ignoreWord"]
# 특정 단어 제거
# 오타와 누락의 경우만 판단하면 정상작동하지만
# 추가 된 단어의 경우를 채점기준에 추가하면 정확하게 채점 되지 않을 수 있음
# [정답] Hybrid [실제작성]
user_input_text = [text.replace(ignore_word, '') for text in user_input_text]
correct_input_text = [text.replace(ignore_word, '') for text in correct_input_text]
except (KeyError, IndexError, AttributeError):
ignore_word = None
print(f"ignore_word: {ignore_word}")
# 리스트를 하나의 문자열로 변경
user_input_text_str = ''.join(user_input_text)
currect_input_text_str = ''.join(correct_input_text)
print("user_input_text as string:")
print(user_input_text_str)
print("\ncurrect_input_text_answer as string:")
print(currect_input_text_str)
# 문자열의 차이를 비교
diff = difflib.ndiff(currect_input_text_str, user_input_text_str)
diff_list = list(diff)
# 차이점을 정리하여 result_diff에 저장
result_diff = []
# 누락 된 단어만 따로 리스트로 저장
missing_list = []
# 오타와 누락된 단어 리스트 저장
error_missing_list = []
skip_next = False
for i, line in enumerate(diff_list):
if skip_next:
skip_next = False
continue
# diff_list의 line 시작이 '-'이면서 다음 line이 '+'이면 두 line을 붙여서 맞춤법이 틀린 단어로 판단
if line.startswith('- '):
# 오타
if i + 1 < len(diff_list) and diff_list[i + 1].startswith('+ '):
line = line.replace('- ', '-')
next = diff_list[i + 1].replace('+ ', '')
result_diff.append(line+'=>'+next)
error_missing_list.append(line+'=>'+next)
skip_next = True
# 누락
else:
line = line.replace('- ', '-')
result_diff.append(line)
missing_list.append(line)
error_missing_list.append(line)
# 없어도 되는 글자가 있는 경우 (추가)
elif line.startswith('+ '):
line = line.replace('+ ', '+')
result_diff.append(line)
# result_diff 출력
# print("\nResult Differences:")
# for diff in result_diff:
# print(diff)
# result_diff 배열의 길이를 맨 앞에 저장
# 모든 차이를 계산해 점수 차감
# temp = 40 - min(len(result_diff)*2, 40)
# 누락된 텍스트만 계산해 점수 차감
# temp = 40 - min(len(missing_list)*2, 40)
# 2503회 기준 오타 1개당 [2점]->[1점] 차감
temp = 40 - min(len(error_missing_list)*1, 40)
self.set_typo_score(temp)
result_diff.insert(0, temp)
return result_diff
# XML 파일 채점
def score_directory(self, xml_directory, correct_answer_file):
# xml 파일 불러오기
xml_files = Path(xml_directory).glob('*.hml')
# 결과 저장할 리스트
results = []
for user_answer_file in xml_files:
result = {}
chart_xml = self.binary_to_chartxml(user_answer_file)
result['typo'] = self.typo_check(correct_answer_file, user_answer_file, chart_xml)
result['score'] = self._score_xml_file(user_answer_file, chart_xml)
# result['score']['score_results'][2]['points'] = result['typo'][0]
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") #연월일_시분초
# timestamp = datetime.now().strftime("%Y%m%d") #연월일
output_path = f"scoring_results_{timestamp}.xlsx"
summary_data = []
detail_data = []
typo_data = []
for temp in results:
# 요약 정보
result = temp['score']
summary_row = {
'파일명': result['filename'],
'총점': result.get('total_score', 0)
}
if 'error' in result:
summary_row['오류'] = result['error']
summary_data.append(summary_row)
# 상세 정보
if 'score_results' in result:
filename = {'파일명': result['filename']}
number, name = self.parse_filename(filename)
if (number or name) is None:
detail_row = {'채점항목': result['filename'] }
else:
detail_row = {'채점항목':f"{number}-{name}"}
section_num = None
row_index = []
for i, score_result in enumerate(result['score_results']):
current_section = score_result['section']
if section_num is None:
section_num = current_section
# 다음 섹션(문제0 => 문제1)로 넘어갔을 경우 or 마지막 문제일 경우
if current_section != section_num:
# 이전 섹션의 부분합을 출력
detail_row[f'문제{section_num}'] = result['partial_scores'][int(section_num)]['score']
row_index.append(f'문제{section_num}')
section_num = current_section
detail_row[f'{i+1}'] = score_result['points']
row_index.append(score_result['id'])
# 마지막 섹션(문제2)부분합 점수를 출력
if i == len(result['score_results']) - 1:
detail_row[f'문제{current_section}'] = result['partial_scores'][int(current_section)]['score']
row_index.append(f'문제{current_section}')
detail_row['총점'] = result.get('total_score', 0)
row_index.append('총점')
detail_data.append(detail_row)
summary_df = pd.DataFrame(summary_data)
detail_df = pd.DataFrame(detail_data).transpose()
detail_df.columns = detail_df.iloc[0]
detail_df = detail_df[1:]
detail_df.index = row_index
# detail_df = pd.DataFrame(detail_data)
for temp in results:
result = temp['typo']
typo_data.append(result)
typo_df = pd.DataFrame(typo_data).transpose()
# detail_df = pd.DataFrame(detail_data)
# ExcelWriter 객체 생성
with pd.ExcelWriter(output_path, engine='openpyxl') as writer:
summary_df.to_excel(writer, sheet_name='채점결과요약', index=False)
detail_df.to_excel(writer, sheet_name='채점상세내역', index=True)
typo_df.to_excel(writer, sheet_name='오타내역', index=False)
# 열 너비 자동 조정
# for sheet_name in writer.sheets:
# worksheet = writer.sheets[sheet_name]
# for column_cells in worksheet.columns:
# max_length = 0
# column = column_cells[0].column_letter # 열의 문자
# for cell in column_cells:
# try:
# if cell.value:
# max_length = max(max_length, len(str(cell.value)))
# except:
# pass
# adjusted_width = (max_length + 2)
# worksheet.column_dimensions[column].width = adjusted_width
return output_path
def main():
# 시험회차 및 유형
exam_round = '2504'
# 250429기준 없는 시험 형식(A,B,C..)은 주석처리 하지 않으면 오류 발생
exam_types = [
'A',
# 'B',
# 'C',
]
# test_mode = False
test_mode = True
output_excel_paths = []
for exam_type in exam_types:
# JSON 채점기준표 파일 (예시:DIW_2503A.json)
# scoring_criteria_path = f'./DIW_{exam_round}.json'
scoring_criteria_path = f'./DIW_{exam_round}{exam_type}.json'
# xml(hml)파일 디렉토리 경로 (예시:./output/A/DIW)
# xml_directory = f'./output/{exam_type}/{"TEST" if test_mode else "DIW"}'
# 회차가 여러개인 경우
xml_directory = f'./output/{exam_round}/{exam_type}/{"TEST" if test_mode else "DIW"}'
# 오탈자 체크를 위한 정답 파일 경로 (예시:./output/A/DIW/DIW_2503A.hml)
# correct_answer_file = f'./output/{exam_type}/DIW/DIW_{exam_round}{exam_type}.hml'
correct_answer_file = f'./output/{exam_round}/{exam_type}/DIW/DIW_{exam_round}{exam_type}.hml'
# 엑셀 파일명 (비어있으면 자동생성) (예시:241001_DIW_2503A_채점결과.xlsx)
timestamp = datetime.now().strftime("%y%m%d")
output_path = f'{timestamp}_DIW_{exam_round}회_{exam_type}형_{"TEST" if test_mode else "채점결과"}.xlsx'
# 채점 클래스 초기화
scorer = XMLScorer(scoring_criteria_path)
# 폴더 내 모든 xml 파일 채점
results = scorer.score_directory(xml_directory, correct_answer_file)
# 채점 결과 엑셀로 저장
output_excel_paths.append(scorer.export_to_excel(results, output_path))
print(f"채점 결과 엑셀 파일: {output_excel_paths}")
if __name__ == '__main__':
main()

View File

@@ -1074,6 +1074,7 @@ class XMLScorer:
'원형': "//c:pieChart",
'분산형': "//c:scatterChart",
'표식만있는분산형': "//c:scatterChart[c:scatterStyle[@val='marker']]",
'곡선이있는분산형': "//c:scatterChart[c:scatterStyle[@val='smooth']]"
}
chart_type = criterion.get('chart_type').replace(" ","")
@@ -1474,15 +1475,15 @@ class XMLScorer:
def main():
# 시험회차 및 유형
exam_round = '2601'
exam_round = '2622'
# exam_round = '2522'
# 채점하고자 하는 유형은 주석 해제
exam_types = [
# 'A',
# 'B',
'A',
'B',
'C',
# 'D',
'D',
]
test_mode = False
@@ -1491,7 +1492,7 @@ def main():
output_excel_paths = []
for exam_type in exam_types:
# JSON 채점기준표 파일 (예시:DIW_2503A.json)
scoring_criteria_path = f'./DIW_{exam_round}{exam_type}.json'
scoring_criteria_path = f'./JSON/{exam_round}/DIW_{exam_round}{exam_type}.json'
# xml(hml)파일 디렉토리 경로 (예시:./output/2503/A/DIW)
xml_directory = f'./output/{exam_round}/{exam_type}/{"TEST" if test_mode else "DIW"}'
@@ -1501,7 +1502,7 @@ def main():
# 엑셀 파일명 (비어있으면 자동생성) (예시:241001_DIW_2503A_채점결과.xlsx)
timestamp = datetime.now().strftime("%y%m%d")
output_path = f'{timestamp}_DIW_{exam_round}{exam_type}_{"TEST" if test_mode else "채점결과"}.xlsx'
output_path = f'./score_result/{timestamp}_DIW_{exam_round}{exam_type}_{"TEST" if test_mode else "채점결과"}.xlsx'
# 채점 클래스 초기화
scorer = XMLScorer(scoring_criteria_path)

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.