동영상, 오프닝 자막 관련 문항 처리 방식 개선
This commit is contained in:
126
psdExport_2.js
126
psdExport_2.js
@@ -172,12 +172,24 @@ outputExcelFiles.forEach((outputFile, index) => {
|
||||
// scoring.json 파일 내에 있는 type에 따라 비교하는 방식이 달라짐
|
||||
// 채점 결과를 scoringResultList 배열에 저장
|
||||
function getGmepScore(gmepData, scoringJson, index) {
|
||||
function compareAndScore(userAnswer, rightAnswer, point, key, scoringResult) {
|
||||
function compareAndScore(userAnswer, rightAnswer, point, key, scoringResult, tolerance = 0) {
|
||||
let score = 0;
|
||||
|
||||
const isEqual = (typeof rightAnswer === "object" && typeof userAnswer === "object")
|
||||
? JSON.stringify(userAnswer) === JSON.stringify(rightAnswer)
|
||||
: userAnswer == rightAnswer;
|
||||
let isEqual;
|
||||
|
||||
if (Array.isArray(rightAnswer) && Array.isArray(userAnswer)) {
|
||||
// 배열 길이 같아야 비교 가능
|
||||
if (rightAnswer.length === userAnswer.length) {
|
||||
isEqual = rightAnswer.every((val, idx) => Math.abs(val - userAnswer[idx]) <= tolerance);
|
||||
} else {
|
||||
isEqual = false;
|
||||
}
|
||||
} else if (typeof rightAnswer === "object" && typeof userAnswer === "object") {
|
||||
// 객체일 때는 기존 방식 유지 (원하면 별도 로직 추가 가능)
|
||||
isEqual = JSON.stringify(userAnswer) === JSON.stringify(rightAnswer);
|
||||
} else {
|
||||
isEqual = userAnswer == rightAnswer;
|
||||
}
|
||||
|
||||
if (isEqual) {
|
||||
score = point;
|
||||
@@ -192,6 +204,25 @@ function getGmepScore(gmepData, scoringJson, index) {
|
||||
return score;
|
||||
}
|
||||
|
||||
function convertColorToHex(colorValue) {
|
||||
// 문자열이면 정수로 변환
|
||||
const intValue = typeof colorValue === 'string' ? parseInt(colorValue, 10) : colorValue;
|
||||
|
||||
// 부호 없는 32비트 정수로 변환 → 8자리 16진수 문자열로
|
||||
const hex = (intValue >>> 0).toString(16).padStart(8, '0');
|
||||
|
||||
// 하위 6자리 추출 (BGR 순서)
|
||||
const bgr = hex.slice(2); // 예: "fff1b01d" → "f1b01d"
|
||||
|
||||
// BGR → RGB로 재배열
|
||||
const b = bgr.slice(0, 2);
|
||||
const g = bgr.slice(2, 4);
|
||||
const r = bgr.slice(4, 6);
|
||||
|
||||
// RGB 순서로 합치고 소문자로 반환
|
||||
return (r + g + b).toLowerCase(); // e.g., "fd5721"
|
||||
}
|
||||
|
||||
const gmepXmlDoc = gmepData;
|
||||
const scoringResult = {};
|
||||
|
||||
@@ -320,7 +351,7 @@ function getGmepScore(gmepData, scoringJson, index) {
|
||||
const rightAnswer = scoringData[key].value;
|
||||
const point = scoringData[key].point;
|
||||
const type = scoringData[key].type;
|
||||
const search = scoringData[key].search;
|
||||
let search = scoringData[key].search;
|
||||
const media = scoringData[key].media;
|
||||
const videoStartTime = scoringData.videoStartTime;
|
||||
const openingStartTime = scoringData.openingStartTime;
|
||||
@@ -333,15 +364,18 @@ function getGmepScore(gmepData, scoringJson, index) {
|
||||
const trackClipNode = getTrackClipNode(gmepXmlDoc, type, videoStartTime, openingStartTime);
|
||||
const subtitleIndex = trackClipNode ? parseInt(trackClipNode.getAttribute('ClipIndex'), 10) + 1 : null;
|
||||
const textClipIndex = getTextClipIndex(gmepXmlDoc, search);
|
||||
const typeToOrderMap = {
|
||||
opening: 1,
|
||||
openingStartTime: 1,
|
||||
openingLength: 1,
|
||||
video: 2,
|
||||
videoStartTime: 2,
|
||||
videoLength: 2,
|
||||
};
|
||||
const subtitleOrder = typeToOrderMap[type] ?? null;
|
||||
// const typeToOrderMap = {
|
||||
// opening: 1,
|
||||
// openingStartTime: 1,
|
||||
// openingLength: 1,
|
||||
// openingText: 1,
|
||||
// video: 2,
|
||||
// videoStartTime: 2,
|
||||
// videoLength: 2,
|
||||
// videoText: 2,
|
||||
// };
|
||||
// const subtitleOrder = typeToOrderMap[type] ?? null;
|
||||
const subtitleOrder = type.includes('opening') ? 1 : type.includes('video') ? 2 : null;
|
||||
const startTime = type === 'video' ? videoStartTime : type === 'opening' ? openingStartTime : null;
|
||||
|
||||
let xpathList = [ele, ele2, ele3, existEle];
|
||||
@@ -367,7 +401,8 @@ function getGmepScore(gmepData, scoringJson, index) {
|
||||
let result = findSimilarString(gmepXmlDoc, search, 0.8);
|
||||
if (result !== null) {
|
||||
result = result.replace(/"/g, "'");
|
||||
[ele, ele2, ele3, existEle] = [ele, ele2, ele3, existEle].map(e => e?.replace(/{search}/g, result));
|
||||
search = result;
|
||||
[ele, ele2, ele3, existEle] = [ele, ele2, ele3, existEle].map(e => e?.replace(/{search}/g, search));
|
||||
} else {
|
||||
[ele, ele2, ele3] = [ele, ele2, ele3].map(e => e?.includes('{search}') ? null : e);
|
||||
}
|
||||
@@ -488,13 +523,21 @@ function getGmepScore(gmepData, scoringJson, index) {
|
||||
}
|
||||
}
|
||||
|
||||
else if (type === 'openingStartTime' || type === 'openingLength'
|
||||
|| type === 'videoStartTime' || type === 'videoLength') {
|
||||
// 자막관련 type검사하는 구문을 opening포함 video포함으로 변경
|
||||
// 6/16(월)시작 지점
|
||||
// 1. JSON파일 10,11번 처럼 변경
|
||||
else if (type.includes('opening') || type.includes('video')) {
|
||||
function toHexColor(value) {
|
||||
|
||||
}
|
||||
// else if (type === 'openingStartTime' || type === 'openingLength'
|
||||
// || type === 'videoStartTime' || type === 'videoLength') {
|
||||
|
||||
// 자막의 정보를 이용해 CROwneUnit의 인덱스를 구함
|
||||
// 1. 텍스트
|
||||
// 2. 순서
|
||||
// 3. 시작시간
|
||||
const indexByText = getClipIndexByText(ele);
|
||||
const indexByText = getClipIndexByText(search);
|
||||
const indexByOrder = getClipIndexByOrder(subtitleOrder);
|
||||
|
||||
if (type.includes('opening')) time = openingStartTime;
|
||||
@@ -505,29 +548,62 @@ function getGmepScore(gmepData, scoringJson, index) {
|
||||
// 1, 2, 3순으로 자막을 찾음
|
||||
const index = indexByText ?? indexByOrder ?? indexByStartTime;
|
||||
if (index != null) {
|
||||
// 자막 시작시간
|
||||
// 자막 시작시간 [2-10] [2-28]
|
||||
if (type.includes('StartTime')) {
|
||||
const crtrackClipIndex = getCrtrackClipIndex(index)
|
||||
const startTimeList = getSubtitleStartTime();
|
||||
const startTime = startTimeList[crtrackClipIndex];
|
||||
userAnswer = startTime;
|
||||
}
|
||||
|
||||
// 자막 길이
|
||||
userAnswer = startTime;
|
||||
totalScore += compareAndScore(userAnswer, rightAnswer, point, key, scoringResult);
|
||||
}
|
||||
// 자막 길이 [2-11] [2-29]
|
||||
else if (type.includes('Length')) {
|
||||
const crtrackClipIndex = getCrtrackClipIndex(index) + 1 // XML 1-based index
|
||||
const clipLength = xpath.select1(`//CRTrackList[@Name='텍스트' or @Name='비디오2']/CRTrackClip[${crtrackClipIndex}]/@Length`, gmepXmlDoc);
|
||||
|
||||
userAnswer = parseInt(clipLength.value, 10);
|
||||
totalScore += compareAndScore(userAnswer, rightAnswer, point, key, scoringResult);
|
||||
}
|
||||
// 자막 텍스트(글자, 폰트, 크기, 색상) [2-5, 6, 7, 8] [2-22, 23, 24, 25]
|
||||
else if (type.includes('Text') || type.includes('Color')) {
|
||||
const xmlIndex = index + 1 // XML 1-based index
|
||||
const subtitleXpath = ele?.replace(/{index}/g, xmlIndex);
|
||||
const subtitleResult = xpath.select1(subtitleXpath, gmepXmlDoc);
|
||||
|
||||
if (subtitleResult) {
|
||||
if (type.includes('Color')) {
|
||||
const hex = convertColorToHex(subtitleResult.value);
|
||||
userAnswer = hex;
|
||||
}
|
||||
else {
|
||||
userAnswer = subtitleResult.value;
|
||||
}
|
||||
} else {
|
||||
userAnswer = null;
|
||||
}
|
||||
|
||||
totalScore += compareAndScore(userAnswer, rightAnswer, point, key, scoringResult);
|
||||
}
|
||||
|
||||
// 자막 위치 [2-9] (화면 정가운데 아래)
|
||||
// 정답 좌표를 기준으로
|
||||
else if (type.includes('Location')) {
|
||||
const xmlIndex = index + 1 // XML 1-based index
|
||||
const subtitleXpath = ele?.replace(/{index}/g, xmlIndex);
|
||||
const subtitleResult = xpath.select(subtitleXpath, gmepXmlDoc);
|
||||
|
||||
userAnswer = subtitleResult.map(r => r.value);
|
||||
const errorRange = 0.1;
|
||||
|
||||
totalScore += compareAndScore(userAnswer, rightAnswer, point, key, scoringResult, tolerance = errorRange);
|
||||
}
|
||||
}
|
||||
else {
|
||||
userAnswer = null;
|
||||
}
|
||||
|
||||
totalScore += compareAndScore(userAnswer, rightAnswer, point, key, scoringResult);
|
||||
}
|
||||
|
||||
|
||||
else if (type == "color") {
|
||||
const result = xpath.select(ele, gmepXmlDoc);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user