From 42b91003e8172a5ab29715b33e366730ad6593bf Mon Sep 17 00:00:00 2001 From: dragdra Date: Fri, 7 Feb 2025 17:33:38 +0900 Subject: [PATCH] =?UTF-8?q?=ED=85=8D=EC=8A=A4=ED=8A=B8=20=EC=9C=A0?= =?UTF-8?q?=EC=82=AC=EB=8F=84=20=EA=B2=80=EC=82=AC=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/launch.json | 18 - README.md | 2 +- .../제2501회 정기 DIC B형.xlsx | Bin .../제2501회 정기 DIC B형_250204.xlsx | Bin .../제2501회 정기 DIC C형.xlsx | Bin .../제2501회 정기 DIC C형_250204.xlsx | Bin findSimilarString.js | 41 ++ output.xlsx | Bin 0 -> 40940 bytes output2.xlsx | Bin 0 -> 43056 bytes package-lock.json | 8 + package.json | 3 +- psdExport.js | 18 +- psdExport_2.js | 427 ++++++++++++++++++ scoring.json | 2 +- z.xbook | 1 + 제2501회 정기 DIC B형.json | 12 +- 16 files changed, 499 insertions(+), 33 deletions(-) delete mode 100644 .vscode/launch.json rename 제2501회 정기 DIC B형.xlsx => _old_excel/제2501회 정기 DIC B형.xlsx (100%) rename 제2501회 정기 DIC B형_250204.xlsx => _old_excel/제2501회 정기 DIC B형_250204.xlsx (100%) rename 제2501회 정기 DIC C형.xlsx => _old_excel/제2501회 정기 DIC C형.xlsx (100%) rename 제2501회 정기 DIC C형_250204.xlsx => _old_excel/제2501회 정기 DIC C형_250204.xlsx (100%) create mode 100644 findSimilarString.js create mode 100644 output.xlsx create mode 100644 output2.xlsx create mode 100644 psdExport_2.js create mode 100644 z.xbook diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index 1ea0c6b..0000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - - { - "type": "node", - "request": "launch", - "name": "Launch Program", - "skipFiles": [ - "/**" - ], - "program": "${workspaceFolder}/psdExport.js" - } - ] -} \ No newline at end of file diff --git a/README.md b/README.md index 2942f35..8645029 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ xpath 테스트 웹: http://xpather.com/ * PSD 관련해서 좀 더 추가적인 채점이 되도록 기능 개선 * PSD 라이브러리 변경도 생각해볼 것 -### psdExxport.js +### psdExport.js nodejs 기반, scoring.json 파일에 채점기준표 만들어서 채점 1. 지정 폴더 내 PSD 파일 scoring.json 파일 내 1,2 참조해서 채점 diff --git a/제2501회 정기 DIC B형.xlsx b/_old_excel/제2501회 정기 DIC B형.xlsx similarity index 100% rename from 제2501회 정기 DIC B형.xlsx rename to _old_excel/제2501회 정기 DIC B형.xlsx diff --git a/제2501회 정기 DIC B형_250204.xlsx b/_old_excel/제2501회 정기 DIC B형_250204.xlsx similarity index 100% rename from 제2501회 정기 DIC B형_250204.xlsx rename to _old_excel/제2501회 정기 DIC B형_250204.xlsx diff --git a/제2501회 정기 DIC C형.xlsx b/_old_excel/제2501회 정기 DIC C형.xlsx similarity index 100% rename from 제2501회 정기 DIC C형.xlsx rename to _old_excel/제2501회 정기 DIC C형.xlsx diff --git a/제2501회 정기 DIC C형_250204.xlsx b/_old_excel/제2501회 정기 DIC C형_250204.xlsx similarity index 100% rename from 제2501회 정기 DIC C형_250204.xlsx rename to _old_excel/제2501회 정기 DIC C형_250204.xlsx diff --git a/findSimilarString.js b/findSimilarString.js new file mode 100644 index 0000000..23a21ba --- /dev/null +++ b/findSimilarString.js @@ -0,0 +1,41 @@ +const xpath = require("xpath"); +const { DOMParser } = require("xmldom"); +const stringSimilarity = require("string-similarity"); + +/** + * XML 문서에서 유사한 문자열 찾기 + * @param {string} xmlDoc - XML 문자열 + * @param {string} targetString - 비교할 문자열 + * @param {number} threshold - 유사도 기준 (0~1) + * @returns {string} - 유사한 문자열 + */ +function findSimilarString(xmlDoc, targetString, threshold = 0.8) { + // XML 내부의 비교 대상 텍스트 리스트 찾기 + function getTextNodes(xmlDoc, stringList = []) { + const stringNodes = xpath.select("//CRCUnitArr/@Name", xmlDoc); + stringNodes.forEach(stringNode => { + console.log("🚀 ~ getTextNodes ~ stringNode:", stringNode.value); + stringList.push(stringNode.value); + }); + return stringList; + } + + // XML에서 모든 텍스트 추출 + const stringList = getTextNodes(xmlDoc); + + // 유사도 비교하여 가장 유사한 문자열 찾기 + let bestMatch = null; + let highestSimilarity = 0; + + stringList.forEach(text => { + const similarity = stringSimilarity.compareTwoStrings(targetString, text); + if (similarity > highestSimilarity && similarity >= threshold) { + highestSimilarity = similarity; + bestMatch = text; + } + }); + + return bestMatch; +} + +module.exports = findSimilarString; \ No newline at end of file diff --git a/output.xlsx b/output.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..3d57cf53eaab1ee96d6365c649c5bbde7208f693 GIT binary patch literal 40940 zcmeHQTW=i6b>`ZeAlSfg4BJ3pz`%fj{bEqVAt_3fMH2MY+84^@wAu)oetITu2Q3__ z$4S%g;Ll{Q-1e*c{$5ZX8y%f2BZ8if;hCJz(o$`DI9aMyhD+N=ZOr_5-*LSjR)e{? zde9779bpQ6{%Io!I&kVIdqHPbwM>$8fnT;ITM?d)L{!czWG(
  • !MeiIa4*90wX>xG}WXr0eT;6baC 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 { + const filePath = path.join(answerFilesDir, file); + return fs.statSync(filePath).isDirectory(); +}); + +//const students = fs.readdirSync(answerFilesDir); // 기준표 파일 읽기 -const scoring = require('./제2501회 정기 DIC C형.json'); +const scoring = require('./제2501회 정기 DIC B형.json'); const psdData = []; +// 채점 결과 리스트 const gradingResults = []; -students.forEach(student => { +studentDirs.forEach(student => { // 맥에서 한글 디렉토리 이름을 읽어서 엑셀에 저장 할 시 자소 분리가 되어 저장되는 문제 노말라이즈해서 해결 const name = student.normalize('NFC'); - const studentDir = path.join(sampleDir, student); + const studentDir = path.join(answerFilesDir, student); const psdFiles = fs.readdirSync(studentDir).filter(file => file.endsWith('.psd')); const gmepFile = fs.readdirSync(studentDir).filter(file => file.endsWith('.gmep')); diff --git a/psdExport_2.js b/psdExport_2.js new file mode 100644 index 0000000..857ac0a --- /dev/null +++ b/psdExport_2.js @@ -0,0 +1,427 @@ +const jsonPath = require('jsonpath'); +const XLSX = require('xlsx'); +const psd = require('psd'); +const fs = require('fs'); +const path = require('path'); +const xpath = require('xpath'); +const { DOMParser } = require('xmldom'); + +const findSimilarString = require('./findSimilarString'); // 함수 가져오기 + +// 복사된 답안파일 폴더 +const answerFilesDir = './output/B/DIC/'; + +// 답안 폴더 내부에 디렉토리가 아닌 일반 파일이 있을 경우 디렉토리만 필터링 해서 불러옴 +const studentDirs = fs.readdirSync(answerFilesDir).filter(file => { + const filePath = path.join(answerFilesDir, file); + return fs.statSync(filePath).isDirectory(); +}); + +// 기준표 파일 읽기 +const scoringJson = require('./제2501회 정기 DIC B형.json'); + +// 채점 결과 리스트 +const scoringResultList = []; +const psdData = []; + +studentDirs.forEach(student => { + // 맥에서 한글 디렉토리 이름을 읽어서 엑셀에 저장 할 시 자소 분리가 되어 저장되는 문제 노말라이즈해서 해결 + const name = student.normalize('NFC'); + const studentDir = path.join(answerFilesDir, student); + const psdFiles = fs.readdirSync(studentDir).filter(file => file.endsWith('.psd')); + const gmepFile = fs.readdirSync(studentDir).filter(file => file.endsWith('.gmep')); + + // 학생 이름을 key로 하는 객체 생성 + // 채점결과 + const scoringResult = { + 0: name + }; + + // psdFiles + psdFiles.forEach((psdFile, index) => { + const psdPath = path.join('./', studentDir, psdFile); + console.log(`Reading ${psdPath}...`); + psdData[index] = psd.fromFile(psdPath); + psdData[index].parse(); + scoringResult[index + 1] = getScore(psdData, scoringJson, index); + }); + gmepFile.forEach((gmep, index) => { + const gmepPath = path.join('./', studentDir, gmep); + console.log(`Reading ${gmepPath}...`); + const xmlString = fs.readFileSync(gmepPath, 'utf8'); + // XML 문자열을 파싱하여 XML 문서 객체로 변환 + const xmlDocument = new DOMParser().parseFromString(xmlString, 'application/xml'); + // console.log('xmlDocument:', xmlDocument); + + scoringResult[3] = getGmepScore(xmlDocument, scoringJson, 2); + }); + scoringResultList.push(scoringResult); +}); + +// 엑셀 파일 생성 + +// Flatten the resultData for better representation in Excel +const flattenedData = scoringResultList.map(student => { + const name = student["0"] + const flattened = { "학생": student["0"] }; + Object.keys(student).forEach(key => { + if (key !== "0") { + Object.keys(student[key]).forEach(subKey => { + flattened[`${key}_${subKey}`] = student[key][subKey]; + }); + } + }); + return flattened; +}); +// console.log(flattenedData); + +const worksheet = XLSX.utils.json_to_sheet(flattenedData); +const workbook = XLSX.utils.book_new(); + +// Add the worksheet to the workbook +XLSX.utils.book_append_sheet(workbook, worksheet, '채점 결과'); + +// 엑셀 파일 저장 +XLSX.writeFile(workbook, 'output2.xlsx'); +console.log('채점 결과가 output2.xlsx 파일에 저장되었습니다.'); + + +// xml 형식의 gmep 파일을 읽어서 점수를 계산 +// scoring.json 파일 내에 있는 ele 요소는 xpath 형식으로 접근하여 요소를 탐색하고 나오는 값을 value와 비교하여 점수를 계산 +// scoring.json 파일 내에 있는 type은 비교할 값의 타입을 의미하며, boolean, array 등이 있음 +// scoring.json 파일 내에 있는 type에 따라 비교하는 방식이 달라짐 +// 채점 결과를 scoringResultList 배열에 저장 +function getGmepScore(gmepData, scoringJson, index) { + const gmepXmlDoc = gmepData; + const scoringResult = {}; + + const scoringData = scoringJson[index]; + // console.log(scoringData); + + let totalScore = 0; + + // 채점기준표 문항별 분류 + for (const key in scoringData) { + let ele = scoringData[key].ele; + const value = scoringData[key].value; + const point = scoringData[key].point; + const type = scoringData[key].type; + const search = scoringData[key].search; + + // search 값이 undefined 아니면 ele의 {search}부분을 search로 치환 + /** + * JSON파일 곰믹스 5번문항/22번 문항 + * type : "subtitle" 인 항목들 + * GPString태그 VID7속성 찾는 xpath구문 + * CRCUnitArr태그 Name속성 찾는 구문으로 변환 + * > 멀티라인 텍스트 유사도 판별하기 어려움 + */ + + if (search !== undefined) { + let result = findSimilarString(gmepXmlDoc, search, 0.2) + console.log("🚀 ~ getGmepScore ~ result:", result) + + ele = ele.replace('{search}', result); + } + console.log(`example number: ${key}`); + + // xpath + if (ele === 'none') { + scoringResult[key] = "확인필요"; + continue; + } + + if (type == "boolean") { + scoringResult[key] = result.length > 0 ? point : 0; + } + else if (type == "array") { + // result: Path="동영상.mp4", Path="음악.mp3", Path="이미지2.jpg", Path="이미지1.jpg" + // value: 동영상.mp4,이미지1.jpg,이미지3.jpg,이미지2.jpg + // result 와 value를 순서대로 비교하여 모두 같으면 점수 point 부여 + + // XPath를 사용하여 CRTrackList Name="비디오1" 요소 찾기 + const trackListNode = xpath.select1('//CRVideoTrackArr/CRTrackList[@Name="비디오1"]', gmepXmlDoc); + const values = []; + let isSame = true; + + if (trackListNode) { + // CRTrackClip 요소의 ClipIndex를 참조하여 CRClip 요소의 Path와 Type 출력 + const clipIndexes = xpath.select('CRTrackClip/@ClipIndex', trackListNode); + clipIndexes.forEach(indexNode => { + const clipIndex = parseInt(indexNode.value, 10) + 1; // XPath는 1-based index를 사용 + console.log(`clipIndex: ${clipIndex}`); + if (clipIndex === 0) { + return; + } + const clipPathNode = xpath.select1(`//CRClipArr/CRClip[${clipIndex}]/@Path`, gmepXmlDoc); + + if (clipPathNode === undefined) { + console.log(`clipPathNode: undefined`); + return; + } + console.log(`clipPathNode: ${clipPathNode.value}`); + values.push(clipPathNode.value); + }); + // values에 값이 있는지 확인 + if (values.length == 0) { + console.log('values length 0'); + scoringResult[key] = 0; + continue; + } + values.forEach((v, i) => { + console.log(`values: ${v} value: ${value[i]}`); + if (value[i] !== v) { + isSame = false; + } + }); + totalScore += isSame ? point : 0; + scoringResult[key] = isSame ? point : 0; + } else { + console.log('CRTrackList with Name="비디오1" not found.'); + scoringResult[key] = 0; + } + } + else if (type == "startend") { + console.log('type:', type); + const start = scoringData[key].start; + const end = scoringData[key].end; + // XPath를 사용하여 CRClip 요소 중 Path가 '동영상.mp4'인 요소의 위치 찾기 + const clipIndexNode = xpath.select1(ele, gmepXmlDoc); + console.log(`clipIndexNode: ${clipIndexNode}`); + + // XPath를 사용하여 해당 ClipIndex를 사용하는 CRTrackClip 요소 찾기 + const trackClipNodes = xpath.select1(`//CRTrackClip[@ClipIndex='${clipIndexNode}']`, gmepXmlDoc); + + if (!trackClipNodes) { + scoringResult[key] = 0; + continue; + } + const posNode = xpath.select1('@Pos', trackClipNodes); + const lengthNode = xpath.select1('@Length', trackClipNodes); + console.log(`Pos: ${posNode.value}, Length: ${lengthNode.value}`); + + scoringResult[key] = posNode.value === start && lengthNode.value === end ? point : 0; + totalScore += posNode.value === start && lengthNode.value === end ? point : 0; + + } + // else if (type == "subtitle") { + // const result = xpath.select(ele, gmepXmlDoc); + // const length = scoringData[key].length; + + // // 결과는 배열로 나오는데 2개 일 경우가 있음 + // if (result.length !== length) { + // scoringResult[key] = 0; + // continue; + // } + + // scoringResult[key] = point; + // totalScore += point; + // } + else if (type == "color") { + const result = xpath.select(ele, gmepXmlDoc); + + if (result.length == 0) { + scoringResult[key] = 0; + continue; + } + + console.log(`value: ${value} result: ${result[0].value}`); + // value와 result[0].value를 비교하여 같으면 점수 point 부여 + totalScore += result.length > 0 && value === result[0].value ? point : 0; + scoringResult[key] = result.length > 0 && value === result[0].value ? point : 0; + + } + else if (type == "multi") { + try { + const result = xpath.select(ele, gmepXmlDoc); + let isSame = true; + // console.log(`ele: ${ele}, value: ${value} result: ${result}`); + + if (result.length == 0) { + console.log('result length 0'); + scoringResult[key] = 0; + continue; + } + + result.forEach((v, i) => { + // value[i] 값이 정수형인 경우에는 float로 변환하여 비교 + // 정수형 v값을 float 형으로 변환하고 소수점 3자리까지 버림 + let temp = v.value; + let answer = value[i]; + + if (Number.isFinite(value[i]) && !Number.isInteger(value[i])) { + temp = parseFloat(v.value); + answer = parseFloat(value[i]); + // 소수점 3자리까지 버림 + temp = Math.floor(temp * 1000) / 1000; + } + // answer 문자열 중 : 가 포함되어 있다면 각각 분리하고 그 값의 차이를 구함 + if (typeof answer == "string" && answer.indexOf(':') > -1) { + const [answerStart, answerEnd] = answer.split(':').map(Number); + const [tempStart, tempEnd] = temp.split(':').map(Number); + answer = answerEnd - answerStart; + temp = tempEnd - tempStart; + } + + console.log(`temp: ${temp} answer: ${answer}`); + if (answer !== temp) { + console.log(`answer !== temp`); + isSame = false; + } + }); + totalScore += isSame ? point : 0; + scoringResult[key] = isSame ? point : 0; + } catch (e) { + console.log('err :', e); + scoringResult[key] = 0; + } + } + else if (type == "searchIndex") { + let existEle = scoringData[key].existEle; + // XPath를 사용하여 ELE 요소가 존재하는지 확인 + const crcUnitArrNode = xpath.select1(existEle, gmepXmlDoc); + + if (crcUnitArrNode) { + // ELE 요소가 몇번째 요소인지 찾고 필요한 요소 확인 + const unitOrderNode = xpath.select1(ele, gmepXmlDoc); + console.log(`unitOrderNode: ${unitOrderNode}`); + if (unitOrderNode === undefined) { + scoringResult[key] = 0; + continue; + } + if (unitOrderNode.value === value) { + console.log(`unit: ${unitOrderNode.value} === ${value}`); + scoringResult[key] = point; + totalScore += point; + } + else if (unitOrderNode === value) { + console.log(`unitValue: ${unitOrderNode} === ${value}`); + scoringResult[key] = point; + totalScore += point; + } + else { + scoringResult[key] = 0; + } + + } else { + console.log(`not found. ${existEle} `); + scoringResult[key] = 0; + } + } + else { + const ele2 = scoringData[key].ele2; + + console.log('Unknown type:', ele); + let result = xpath.select(ele, gmepXmlDoc); + let result2 = null; + let isCheck = false; + + if (result.length == 0) { + isCheck = true; + } + if (isCheck && ele2) { + result2 = xpath.select(ele2, gmepXmlDoc); + + if (result2.length == 0) { + scoringResult[key] = 0; + continue; + } + + result = result2; + console.log(`1st isChecked: ${isCheck}, result: ${result}`) + } + + // console.log(`result: ${result[0].value}`); + // value와 result[0].value를 비교하여 같으면 점수 point 부여 + // console.log(`${(value === result[0].value)}, ${result.length > 0 && value === result[0].value} `) + console.log(`2nd isChecked: ${isCheck}, result: ${result}`) + totalScore += result.length > 0 ? point : 0; + scoringResult[key] = result.length > 0 ? point : 0; + } + } + + + scoringResult['총점'] = totalScore; + return scoringResult; +} + +// psdData를 scoring.json 파일 내에 있는 ele 요소의 jsonpath로 접근하여 요소를 탐색하고 나오는 값을 value와 비교하여 점수를 계산 +// 학생 별로 psdData는 2개씩 있으므로 PSD 파일 1번과 2번에 대해서 채점 +// scoring.json 파일에 있는 항목 수만큼 점수를 계산 +// scoring.json 파일의 1번째 배열은 PSD 파일 1번에 대한 점수 계산 +// scoring.json 파일의 2번째 배열은 PSD 파일 2번에 대한 점수 계산 +// 채점 결과를 scoringResultList 배열에 저장 +function getScore(psdData, scoring, index) { + const psdTree = psdData[index].tree().export(); + const jsonData = JSON.stringify(psdTree, null, 2); + // console.log(jsonData); + const scoringResult = {}; + + const scoringData = scoring[index]; + let totalScore = 0; + for (const key in scoringData) { + const ele = scoringData[key].ele; + const value = scoringData[key].value; + const point = scoringData[key].point; + const type = scoringData[key].type; + + if (ele === 'none') { + scoringResult[key] = "확인필요"; + continue; + } + + try { + const result = jsonPath.query(psdTree, ele); + console.log(`ele: ${ele}, value: ${value} result: ${result}`); + if (result.length == 0) { + scoringResult[key] = 0; + continue; + } + if (type == "boolean") { + // console.log(`result ${result.length}`); + + scoringResult[key] = result.length > 0 ? point : 0; + } + // value가 color code인 경우 R,G,B를 16진수로 변환하여 비교하고 같다면 점수 부여 + // value: "ffa200" + // result: [255,162,0,255] + // 255,162,0,255 -> ffa200 + else if (type == "color") { + // console.log(`result ${result}`); // result 255,162,0,255 + const temp = result[0].slice(0, 3).join(','); // 255,162,0 + color = temp.split(',').map(v => parseInt(v).toString(16)).join(''); // ffa20 + // ffa20 -> ffa200 + if (color.length == 5) { + color = color + '0'; + } + + // console.log(`color: ${color}`); + scoringResult[key] = result.length > 0 && value === color ? point : 0; + } + + + // type이 font인 경우 font의 이름만 추출하여 비교 + // value: "Arial" + // result: ["Arial-BoldItalicMT"] + else if (type == "font") { + // console.log(`result ${result}`); + const font = result[0].split('-')[0]; + // console.log(`font: ${font}`); + scoringResult[key] = result.length > 0 && value === font ? point : 0; + } + + else if (result[0] === value) { + scoringResult[key] = point; + totalScore += point; + } else { + scoringResult[key] = 0; + } + } catch (error) { + console.error(`Error processing JSONPath query for ele: ${ele}`, error); + scoringResult[key] = 0; + } + } + + scoringResult['총점'] = totalScore; + return scoringResult; +} \ No newline at end of file diff --git a/scoring.json b/scoring.json index 271cc2a..0e5b32a 100644 --- a/scoring.json +++ b/scoring.json @@ -158,7 +158,7 @@ }, "2": { "1": { - "ele": "//CRClipArr/CRClip[position() = //CRTrackList[1]/CRTrackClip/@ClipIndex]/@Path", + "ele": "//CRClipArr/CRClip[position()=//CRTrackList[1]/CRTrackClip/@ClipIndex]/@Path", "type": "array", "value": [ "동영상.mp4", diff --git a/z.xbook b/z.xbook new file mode 100644 index 0000000..8267503 --- /dev/null +++ b/z.xbook @@ -0,0 +1 @@ +[{"kind":1,"language":"markdown","value":"# XPath Notebook\nDate: 2025-02-04     Time: 17:56:45"},{"kind":2,"language":"xpath","value":"//CRClipArr/CRClip[position()=//CRTrackList[1]/CRTrackClip/@ClipIndex]/@Path"},{"kind":2,"language":"xpath","value":"//CRTrackList[@Name='텍스트']/CRTrackClip[@ClipIndex=count(//CROwneUnit[.//CRCUnitArr[@Name='추억의 말뚝박기 놀이']]/preceding::CROwneUnit)]/@Length"},{"kind":2,"language":"xpath","value":"//CRTrackList[@Name='텍스트']/CRTrackClip[(@ClipIndex=count(//CROwneUnit[1]/CRCUnitArr/preceding::CROwneUnit))][@Length='120']"},{"kind":2,"language":"xpath","value":"//CRTrackList[@Name='텍스트']/CRTrackClip"},{"kind":2,"language":"xpath","value":""},{"kind":2,"language":"xpath","value":"/CRTrackClip[][@Length='120']"},{"kind":2,"language":"xpath","value":"(@ClipIndex=count(//CROwneUnit[.//CRCUnitArr[@Name='재미있는 놀이공원']/preceding::CROwneUnit))"},{"kind":2,"language":"xpath","value":"//CRTrackList[@Name='텍스트']/CRTrackClip[(@ClipIndex=count(//CROwneUnit[.//CRCUnitArr[@Name='재미있는 놀이공원']]/preceding::CROwneUnit))][@Length='120']"},{"kind":2,"language":"xpath","value":"//CRTrackList[@Name='텍스트']/CRTrackClip[(@ClipIndex=count(//CROwneUnit[.//CRCUnitArr[@Name='재미있는 놀이공원']]/preceding::CROwneUnit))][@Length='120']"},{"kind":2,"language":"xpath","value":"//CRCUnitArr/@Name"},{"kind":2,"language":"xpath","value":"//GPStrLineArr//GPString/@VID7"}] \ No newline at end of file diff --git a/제2501회 정기 DIC B형.json b/제2501회 정기 DIC B형.json index e97c7eb..5a87ef1 100644 --- a/제2501회 정기 DIC B형.json +++ b/제2501회 정기 DIC B형.json @@ -184,9 +184,8 @@ "point": 3 }, "5": { - "ele": "//GPString[@VID7='추억의 말뚝박기 놀이']/@VID7", - "type": "subtitle", - "length": 1, + "ele": "//CRCUnitArr[@Name='{search}']", + "search": "추억의 말뚝박기 놀이", "point": 3 }, "6": { @@ -222,7 +221,7 @@ "point": 2 }, "11": { - "ele": "//CRTrackList[@Name='텍스트']/CRTrackClip[(@ClipIndex=count(//CROwneUnit[.//CRCUnitArr[@Name='재미있는 놀이공원']/preceding::CROwneUnit))][@Length='120']", + "ele": "//CRTrackList[@Name='텍스트']/CRTrackClip[(@ClipIndex=count(//CROwneUnit[.//CRCUnitArr[@Name='재미있는 놀이공원']]/preceding::CROwneUnit))][@Length='120']", "ele2": "//CRTrackList[@Name='텍스트']/CRTrackClip[(@ClipIndex=count(//CROwneUnit[1]/CRCUnitArr/preceding::CROwneUnit))][@Length='120']", "point": 2 }, @@ -312,9 +311,8 @@ "point": 2 }, "22": { - "ele": "//GPString[@VID7='동네 풍경' or @VID7='(Neighborhood Scene)']/@VID7", - "type": "subtitle", - "length": 2, + "ele": "//CRCUnitArr[@Name='{search}']", + "search": "동네 풍경 (Neighborhood Scene)", "point": 3 }, "23": {