Merge branch 'main' of https://repo.teamsf.co.kr/g-zero/DIC
This commit is contained in:
265
psdExport_2.js
265
psdExport_2.js
@@ -14,8 +14,8 @@ const todayDate = getToday();
|
||||
// --------------------------------------------------------
|
||||
// const scoringJson = require('./DIC_2502A.json');
|
||||
// const scoringJson = require('./DIC_2502B.json');
|
||||
// const scoringJson = require('./DIC_2502C.json');
|
||||
const scoringJson = require('./DIC_2502D.json');
|
||||
const scoringJson = require('./DIC_2502C.json');
|
||||
// const scoringJson = require('./DIC_2502D.json');
|
||||
|
||||
// TEST
|
||||
// const scoringJson = require('./DIC_2502A_TEST.json');
|
||||
@@ -31,19 +31,19 @@ const answerFilesDir = './samples/';
|
||||
// TEST
|
||||
// const answerFilesDir = './output/A/TEST';
|
||||
// const answerFilesDir = './output/B/TEST';
|
||||
// const answerFilesDir = './output/C/TEST';
|
||||
const answerFilesDir = './output/C/TEST';
|
||||
// const answerFilesDir = './output/D/TEST';
|
||||
|
||||
// --------------------------------------------------------
|
||||
// const outputExcelFile = './'+todayDate+'_DIC_2502A_채점결과.xlsx';
|
||||
// const outputExcelFile = './'+todayDate+'_DIC_2502B_채점결과.xlsx';
|
||||
// const outputExcelFile = './'+todayDate+'_DIC_2502C_채점결과.xlsx';
|
||||
const outputExcelFile = './' + todayDate + '_DIC_2502D_채점결과.xlsx';
|
||||
// const outputExcelFile = './'+todayDate+'_DIC_2502D_채점결과.xlsx';
|
||||
|
||||
// TEST
|
||||
// const outputExcelFile = './'+todayDate+'_DIC_2502A_TEST.xlsx';
|
||||
// const outputExcelFile = './'+todayDate+'_DIC_2502B_TEST.xlsx';
|
||||
// const outputExcelFile = './'+todayDate+'_DIC_2502C_TEST.xlsx';
|
||||
const outputExcelFile = './' + todayDate + '_DIC_2502C_TEST.xlsx';
|
||||
// const outputExcelFile = './'+todayDate+'_DIC_2502D_TEST.xlsx';
|
||||
// --------------------------------------------------------
|
||||
|
||||
@@ -54,7 +54,6 @@ const studentDirs = fs.readdirSync(answerFilesDir).filter(file => {
|
||||
return fs.statSync(filePath).isDirectory();
|
||||
});
|
||||
|
||||
|
||||
// 채점 결과 리스트
|
||||
const scoringResultList = [];
|
||||
const psdData = [];
|
||||
@@ -145,8 +144,26 @@ XLSX.utils.book_append_sheet(workbook, worksheet, '채점 결과');
|
||||
XLSX.writeFile(workbook, outputExcelFile);
|
||||
console.log('채점 결과가 ' + outputExcelFile + ' 파일에 저장되었습니다.');
|
||||
|
||||
/**
|
||||
* 자막 태그의 인덱스를 구할 때 사용
|
||||
* 1. CRTrackClip 요소의 순서에 따라 그 요소에 해당하는 CROwneUnit 태그의 순서를 구함
|
||||
* 2. CRTrackClip 요소의 시작시간에 따라 그 요소에 해당하는 CROwneUnit 태그의 순서를 구함
|
||||
* @returns subtitle index
|
||||
*
|
||||
*/
|
||||
function getTrackClipNode(xmlDoc, type, subtitleStartTime, openingStartTime) {
|
||||
let trackClipNode = null;
|
||||
|
||||
// 동영상 자막이면 2, 오프닝 자막이면 1, 그 외는 0
|
||||
const subtitleOrder = type === 'subtitle' ? 2 : type === 'opening' ? 1 : 0;
|
||||
const startTime = type === 'subtitle' ? subtitleStartTime : openingStartTime;
|
||||
|
||||
// xpath 구문을 통해 CRTrackClip 요소의 ClipIndex를 찾음
|
||||
const trackClipNode1 = xpath.select1(`//CRTrackList[@Name="텍스트"]/CRTrackClip[not(@ClipIndex='-1')][${subtitleOrder}]`, xmlDoc);
|
||||
const trackClipNode2 = xpath.select1(`//CRTrackList[@Name='텍스트']/CRTrackClip[sum(preceding-sibling::CRTrackClip/@Length) = ${startTime}]`, xmlDoc);
|
||||
|
||||
return trackClipNode = trackClipNode1 ?? trackClipNode2;
|
||||
}
|
||||
|
||||
// xml 형식의 gmep 파일을 읽어서 점수를 계산
|
||||
// scoring.json 파일 내에 있는 ele 요소는 xpath 형식으로 접근하여 요소를 탐색하고 나오는 값을 value와 비교하여 점수를 계산
|
||||
@@ -160,17 +177,36 @@ function getGmepScore(gmepData, scoringJson, index) {
|
||||
const scoringData = scoringJson[index];
|
||||
// console.log(scoringData);
|
||||
|
||||
|
||||
let totalScore = 0;
|
||||
|
||||
// 채점기준표 문항별 분류
|
||||
for (const key in scoringData) {
|
||||
let ele = scoringData[key].ele;
|
||||
const ele2 = scoringData[key].ele2;
|
||||
let ele2 = scoringData[key].ele2;
|
||||
let ele3 = scoringData[key].ele3;
|
||||
let existEle = scoringData[key].existEle;
|
||||
const rightAnswer = scoringData[key].value;
|
||||
const point = scoringData[key].point;
|
||||
const type = scoringData[key].type;
|
||||
const search = scoringData[key].search;
|
||||
const subtitleStartTime = scoringData.subtitleStartTime;
|
||||
const openingStartTime = scoringData.openingStartTime;
|
||||
|
||||
// xpath 전처리
|
||||
const trackClipNode = getTrackClipNode(gmepXmlDoc, type, subtitleStartTime,
|
||||
openingStartTime);
|
||||
const subtitleIndex = trackClipNode ? parseInt(trackClipNode.getAttribute('ClipIndex'), 10) + 1 : null;
|
||||
const subtitleOrder = type === 'subtitle' ? 2 : type === 'opening' ? 1 : null;
|
||||
const startTime = type === 'subtitle' ? subtitleStartTime
|
||||
: type === 'opening' ? openingStartTime : null;
|
||||
|
||||
[ele, ele2] = [ele, ele2].map(e => e?.replace(/{subtitleIndex}/g, subtitleIndex));
|
||||
[ele, ele2] = [ele, ele2].map(e => e?.replace(/{subtitleOrder}/g, subtitleOrder));
|
||||
[ele, ele2] = [ele, ele2].map(e => e?.replace(/{startTime}/g, startTime));
|
||||
|
||||
console.log("🚀 ~ getGmepScore ~ ele:", ele)
|
||||
console.log("🚀 ~ getGmepScore ~ ele2:", ele2)
|
||||
|
||||
// search 값이 undefined 아니면 ele의 {search}부분을 search로 치환
|
||||
/**
|
||||
@@ -181,16 +217,17 @@ function getGmepScore(gmepData, scoringJson, index) {
|
||||
* > 멀티라인 텍스트 유사도 판별하기 어려움
|
||||
*/
|
||||
if (search !== undefined) {
|
||||
let result = findSimilarString(gmepXmlDoc, search, 0.8)
|
||||
// xpath 내부 "(큰따옴표) 필터링
|
||||
let result = findSimilarString(gmepXmlDoc, search, 0.8);
|
||||
if (result !== null) {
|
||||
result = result.replace(/"/g, "'");
|
||||
}
|
||||
ele = ele.replace(/{search}/g, result);
|
||||
if (existEle !== undefined) {
|
||||
existEle = existEle.replace(/{search}/g, result);
|
||||
[ele, ele2, ele3, existEle]
|
||||
= [ele, ele2, ele3, existEle].map(e => e?.replace(/{search}/g, result));
|
||||
} else {
|
||||
[ele, ele2, ele3]
|
||||
= [ele, ele2, ele3].map(e => e?.includes('{search}') ? null : e);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`example number: ${key}`)
|
||||
|
||||
// xpath
|
||||
@@ -229,10 +266,8 @@ function getGmepScore(gmepData, scoringJson, index) {
|
||||
const notUndefinedClipNode = clipPathNode ?? motionClipPathNode;
|
||||
|
||||
if (notUndefinedClipNode === undefined) {
|
||||
console.log("🚀 ~ getGmepScore ~ notUndefinedClipNode:", notUndefinedClipNode)
|
||||
return;
|
||||
}
|
||||
console.log("🚀 ~ getGmepScore ~ notUndefinedClipNode:", notUndefinedClipNode.value)
|
||||
values.push(notUndefinedClipNode.value);
|
||||
});
|
||||
// values에 값이 있는지 확인
|
||||
@@ -315,17 +350,17 @@ function getGmepScore(gmepData, scoringJson, index) {
|
||||
|
||||
try {
|
||||
let result = xpath.select(ele, gmepXmlDoc);
|
||||
if (result.length == 0) {
|
||||
if (!result) {
|
||||
result = xpath.select(ele2, gmepXmlDoc);
|
||||
if (result.length == 0) {
|
||||
if (!result) {
|
||||
scoringResult[key] = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// 수험자 답안 자막 좌표 (x,y)
|
||||
const x = parseFloat(result[0].value);
|
||||
const y = parseFloat(result[1].value);
|
||||
const x = parseFloat(result[0]?.value);
|
||||
const y = parseFloat(result[1]?.value);
|
||||
// 최소 좌표 (x1, y1)
|
||||
const x1 = parseFloat(start[0]);
|
||||
const y1 = parseFloat(start[1]);
|
||||
@@ -421,10 +456,15 @@ function getGmepScore(gmepData, scoringJson, index) {
|
||||
scoringResult[key] = 0;
|
||||
}
|
||||
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
console.log(`not found. ${existEle} `);
|
||||
const result = xpath.select1(ele, gmepXmlDoc);
|
||||
console.log("🚀 ~ getGmepScore ~ result:", result)
|
||||
let result;
|
||||
|
||||
if (ele2 !== undefined) {
|
||||
result = xpath.select1(ele2, gmepXmlDoc);
|
||||
}
|
||||
|
||||
if (result == rightAnswer) {
|
||||
totalScore += point;
|
||||
scoringResult[key] = point;
|
||||
@@ -434,34 +474,169 @@ function getGmepScore(gmepData, scoringJson, index) {
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
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);
|
||||
/* 현재 문제점 ****************************************************
|
||||
* ele2 xpath구문을 수행했을때
|
||||
* /CROwneUnit[position() = //CRTrackList/CRTrackClip[sum(preceding-sibling::CRTrackClip/@Length) = 170]/@ClipIndex + 1]/CRCUnitArr/@Name
|
||||
* position() = //CRTrackList/CRTrackClip[sum(preceding-sibling::CRTrackClip/@Length) = 170 부분에서
|
||||
* 시작시간이 170이 아닌 경우 false값이 반환되고 0으로 인식되어
|
||||
* //CROwneUnit[0]/CRCUnitArr/@Name 의 값이 반환됨
|
||||
****************************************************************/
|
||||
else if (type == "subtitle" || type == "opening") {
|
||||
const result = ele ? xpath.select(ele, gmepXmlDoc) : [];
|
||||
const result2 = ele2 ? xpath.select(ele2, gmepXmlDoc) : [];
|
||||
const result3 = ele3 ? xpath.select(ele3, gmepXmlDoc) : [];
|
||||
|
||||
if (result2.length == 0) {
|
||||
scoringResult[key] = 0;
|
||||
continue;
|
||||
const resultValues = Array.isArray(result) ? result.map(r => (typeof r === 'object' ? r.value : r)) : [result];
|
||||
const resultValues2 = Array.isArray(result2) ? result2.map(r => (typeof r === 'object' ? r.value : r)) : [result2];
|
||||
const resultValues3 = Array.isArray(result3) ? result3.map(r => (typeof r === 'object' ? r.value : r)) : [result3];
|
||||
|
||||
const allResults = [...[resultValues], ...[resultValues2], ...[resultValues3]];
|
||||
console.log("🚀 ~ allResults:", allResults)
|
||||
|
||||
// rightAnswer가 배열일 경우 배열로 변환
|
||||
const rightAnswerArray = Array.isArray(rightAnswer) ? rightAnswer : [rightAnswer];
|
||||
|
||||
// 결과값이 범위값인 경우 소수점 3자리까지 비교
|
||||
const formattedResults = allResults.map(result => {
|
||||
// result의 길이가 1이상인 조건은 result값이 [x, y] 좌표값인 경우를 말한다
|
||||
if (Array.isArray(result) && result.length > 1) {
|
||||
return result.map(r => {
|
||||
if (typeof r === 'string') {
|
||||
return (Math.floor(parseFloat(r) * 1000) / 1000).toFixed(3);
|
||||
}
|
||||
return r;
|
||||
});
|
||||
}
|
||||
return result;
|
||||
});
|
||||
console.log("🚀 ~ formattedResults:", formattedResults)
|
||||
|
||||
// 배열 비교 함수
|
||||
function arraysEqual(arr1, arr2) {
|
||||
if (arr1.length !== arr2.length) return false;
|
||||
if (arr2.length === 1) {
|
||||
for (let i = 0; i < arr1.length; i++) {
|
||||
if (arr1[i] !== arr2[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (arr2.length > 1) {
|
||||
for (let i = 0; i < arr1.length; i++) {
|
||||
if (Math.abs(arr1[i] - arr2[i]) > 0.1) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
result = result2;
|
||||
// console.log(`1st isChecked: ${isCheck}, result: ${result}`)
|
||||
}
|
||||
// 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;
|
||||
|
||||
// allResults에 rightAnswerArray와 일치하는 배열이 있는지 확인
|
||||
const isIncluded = formattedResults.some(arr => arraysEqual(arr, rightAnswerArray));
|
||||
|
||||
if (isIncluded) {
|
||||
console.log("🚀 ~ getGmepScore ~ 정답:", rightAnswerArray);
|
||||
totalScore += point;
|
||||
scoringResult[key] = point;
|
||||
} else {
|
||||
console.log("🚀 ~ getGmepScore ~ 오답:", rightAnswerArray);
|
||||
scoringResult[key] = 0;
|
||||
}
|
||||
}
|
||||
// else if (type == "subtitle" || type == "opening") {
|
||||
// const result = ele && xpath.select(ele, gmepXmlDoc);
|
||||
// const result2 = ele2 && xpath.select(ele2, gmepXmlDoc);
|
||||
// const result3 = ele3 && xpath.select(ele3, gmepXmlDoc);
|
||||
|
||||
// /**
|
||||
// * 1. result가 배열이 아닌 경우 배열로 변환
|
||||
// * (2-9)는 xpath구문을 통해 클립(자막) 시작시간(number자료형)을 반환받으므로 배열로 변환하여 비교
|
||||
// * 2. result가 배열이고 배열의 요소가 객체인 경우 value값만 추출하여 배열로 변환
|
||||
// * 3. result가 배열이고 배열의 요소가 객체가 아닌 문자열이나 숫자일 경우 그대로 배열로 변환
|
||||
// */
|
||||
// const resultValues = Array.isArray(result) ? result.map(r => (typeof r === 'object' ? r.value : r)) : [result];
|
||||
// const resultValues2 = Array.isArray(result2) ? result2.map(r => (typeof r === 'object' ? r.value : r)) : [result2];
|
||||
// const resultValues3 = Array.isArray(result3) ? result3.map(r => (typeof r === 'object' ? r.value : r)) : [result3];
|
||||
|
||||
// /**
|
||||
// * 정답과 일치하는 답안이 있는지 비교를 위해 배열로 변환
|
||||
// * 정답 rightAnswer의 값이 2개 이상인 경우 배열로 변환하여 비교
|
||||
// */
|
||||
// let allResults = [...[resultValues], ...[resultValues2], ...[resultValues3]];
|
||||
// console.log("🚀 ~ getGmepScore ~ allResults:", allResults)
|
||||
|
||||
// // allResults.forEach((result, i) => {
|
||||
// // if (result.length > 1) {
|
||||
// // result.forEach((r, j) => {
|
||||
// // if (Math.abs(r - rightAnswer[j]) <= 0.1) {
|
||||
// // true;
|
||||
// // } else {
|
||||
// // scoringResult[key] = 0;
|
||||
// // }
|
||||
// // });
|
||||
// // });
|
||||
|
||||
// // (2-11) 자막의 위치 좌표값 비교시 소수점 3자리까지 비교
|
||||
// // rightAnswer값이 배열일 경우
|
||||
// const rightAnswerArray = Array.isArray(rightAnswer) ? rightAnswer : [rightAnswer];
|
||||
// console.log("🚀 ~ getGmepScore ~ rightAnswerArray:", rightAnswerArray);
|
||||
|
||||
// // 결과값이 범위값인 경우 소수점 3자리까지 비교
|
||||
// allResults = allResults.map(result => {
|
||||
// if (result.length > 1) {
|
||||
// return result.map(r => {
|
||||
// if (typeof r === 'string') {
|
||||
// return parseFloat(r).toFixed(3);
|
||||
// }
|
||||
// return r;
|
||||
// });
|
||||
// }
|
||||
// });
|
||||
// console.log("🚀 ~ //allResults.forEach ~ allResults:", allResults)
|
||||
|
||||
// const isIncluded = allResults.some(arr =>
|
||||
// arr.length === rightAnswerArray.length && arr.every((val, index) => val === rightAnswerArray[index])
|
||||
// );
|
||||
// if (isIncluded) {
|
||||
// console.log("🚀 ~ getGmepScore ~ 정답:", rightAnswerArray);
|
||||
// totalScore += point;
|
||||
// scoringResult[key] = point;
|
||||
// }
|
||||
// else {
|
||||
// console.log("🚀 ~ getGmepScore ~ 오답:");
|
||||
// scoringResult[key] = 0;
|
||||
// }
|
||||
// }
|
||||
|
||||
else {
|
||||
try {
|
||||
console.log('Unknown type:', ele);
|
||||
let result = ele ? xpath.select(ele, gmepXmlDoc) : null;
|
||||
let result2 = null;
|
||||
let isCheck = false;
|
||||
|
||||
if (!result || result.length === 0) {
|
||||
isCheck = true;
|
||||
}
|
||||
if (isCheck && ele2) {
|
||||
result2 = ele2 ? xpath.select(ele2, gmepXmlDoc) : null;
|
||||
|
||||
if (!result2 || result2.length == 0) {
|
||||
scoringResult[key] = 0;
|
||||
continue;
|
||||
}
|
||||
result = result2;
|
||||
// console.log(`1st isChecked: ${isCheck}, result: ${result}`)
|
||||
}
|
||||
// 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;
|
||||
} catch (error) {
|
||||
console.error(`Error processing XPath query for ele: ${ele}`, error);
|
||||
scoringResult[key] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
scoringResult['총점'] = totalScore;
|
||||
return scoringResult;
|
||||
}
|
||||
@@ -545,4 +720,4 @@ function getScore(psdData, scoring, index) {
|
||||
|
||||
scoringResult['총점'] = totalScore;
|
||||
return scoringResult;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user