2505회 오프닝, 동영상 자막 판단기준 변경 [2-10,11,28,29]
This commit is contained in:
244
psdExport_2.js
244
psdExport_2.js
@@ -15,8 +15,8 @@ const examRound = '2505';
|
||||
const dic_or_dpi = 'DIC'
|
||||
// const dic_or_dpi = 'DPI'
|
||||
const examTypes = [
|
||||
'A',
|
||||
// 'B',
|
||||
// 'A',
|
||||
'B',
|
||||
// 'C',
|
||||
// 'D'
|
||||
];
|
||||
@@ -172,7 +172,6 @@ outputExcelFiles.forEach((outputFile, index) => {
|
||||
// scoring.json 파일 내에 있는 type에 따라 비교하는 방식이 달라짐
|
||||
// 채점 결과를 scoringResultList 배열에 저장
|
||||
function getGmepScore(gmepData, scoringJson, index) {
|
||||
|
||||
function compareAndScore(userAnswer, rightAnswer, point, key, scoringResult) {
|
||||
let score = 0;
|
||||
|
||||
@@ -192,9 +191,6 @@ function getGmepScore(gmepData, scoringJson, index) {
|
||||
scoringResult[key] = score;
|
||||
return score;
|
||||
}
|
||||
function getMediaOrderbyClipIndex(gmepXmlDoc, clipIndex) {
|
||||
|
||||
}
|
||||
|
||||
const gmepXmlDoc = gmepData;
|
||||
const scoringResult = {};
|
||||
@@ -207,6 +203,116 @@ function getGmepScore(gmepData, scoringJson, index) {
|
||||
|
||||
// 채점기준표 문항별 분류
|
||||
for (const key in scoringData) {
|
||||
function getClipIndexByMediaPath(mediaName) {
|
||||
// CRClipArr/CRClip 요소의 Path속성 리스트를 구함
|
||||
// 모션 클립 이미지도 고려해 처리
|
||||
const mediaPathList = xpath.select("//CRClipArr/CRClip[@Type='11']/CRCUnitArr/@Path | //CRClipArr/CRClip[not(@Type='11')]/@Path", gmepXmlDoc);
|
||||
|
||||
// "동영상.mp4"의 clipIndex를 구함
|
||||
const videoClipIndex = mediaPathList.findIndex(mediaPath => mediaPath.value === mediaName);
|
||||
let xpathList = [ele, ele2];
|
||||
xpathList = xpathList.map(e => e ? e
|
||||
.replace(/{videoClipIndex}/g, videoClipIndex)
|
||||
: e
|
||||
);
|
||||
[ele, ele2] = xpathList;
|
||||
// clipIndex가 -1이면 해당 미디어가 존재하지 않는 것
|
||||
|
||||
return videoClipIndex;
|
||||
}
|
||||
|
||||
// 자막 텍스트로 자막클립인덱스 반환
|
||||
function getClipIndexByText(text) {
|
||||
const crOwneUnits = xpath.select(`//CROwneUnitArr/CROwneUnit`, gmepXmlDoc);
|
||||
|
||||
let subtitleClipIndex = null;
|
||||
// 자막 텍스트와 일치하는 요소의 인덱스를 반환
|
||||
for (let i = 0; i < crOwneUnits.length; i++) {
|
||||
const crcUnitArr = xpath.select1('.//CRCUnitArr', crOwneUnits[i]);
|
||||
if (crcUnitArr && crcUnitArr.getAttribute('Name') === text) {
|
||||
subtitleClipIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
console.log('🟢 자막 텍스트로 검색한 CROwneUnit 인덱스 : ', subtitleClipIndex);
|
||||
return subtitleClipIndex;
|
||||
}
|
||||
|
||||
function getClipIndexByOrder(order) {
|
||||
// 자막의 갯수가 2개 이상 (오프닝과 동영상 자막이 있을 경우)
|
||||
// 앞은 오프닝 뒤는 동영상 자막으로 판단
|
||||
// crTrackClips[0] : 오프닝 자막
|
||||
// crTrackClips[1] : 동영상 자막
|
||||
const crOwneUnits = xpath.select(`//CROwneUnitArr/CROwneUnit`, gmepXmlDoc);
|
||||
const crTrackClips = xpath.select("//CRTrackList[@Name='텍스트' or @Name='비디오2']/CRTrackClip[not(@Type='0') and not(@ClipIndex='-1')]", gmepXmlDoc);
|
||||
let subtitleClipIndex = null;
|
||||
if (subtitleClipIndex === null && crOwneUnits.length >= 2) {
|
||||
// if (crOwneUnits.length >= 2) {
|
||||
for (let i = 0; i < crTrackClips.length; i++) {
|
||||
if ((order - 1) === i) {
|
||||
subtitleClipIndex = parseInt(crTrackClips[i].getAttribute('ClipIndex'), 10);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log('🟡 자막 순서로 검색한 CROwneUnit 인덱스 : ', subtitleClipIndex);
|
||||
return subtitleClipIndex;
|
||||
}
|
||||
|
||||
// 자막들의 시작 시간을 가지는 리스트에서
|
||||
// 구하고자 하는 영상의 시작시간(startTime)과 일치하는 시간이 있다면 인덱스를 가져와
|
||||
// 자막의 인덱스를 구함
|
||||
function getCilpIndexByStartTime(startTime) {
|
||||
const crTrackClips = xpath.select("//CRTrackList[@Name='텍스트' or @Name='비디오2']/CRTrackClip", gmepXmlDoc);
|
||||
const subtitleStartTimeList = getSubtitleStartTime();
|
||||
const startTimeIndex = subtitleStartTimeList.findIndex(value => value === startTime);
|
||||
|
||||
let subtitleClipIndex = null;
|
||||
for (let i = 0; i < crTrackClips.length; i++) {
|
||||
if (parseInt(crTrackClips[i].getAttribute('ClipIndex'), 10) == -1) {
|
||||
continue;
|
||||
}
|
||||
const clipIndex = parseInt(crTrackClips[i].getAttribute('ClipIndex'), 10);
|
||||
if (startTimeIndex === i) {
|
||||
subtitleClipIndex = clipIndex;
|
||||
break;
|
||||
}
|
||||
}
|
||||
console.log('🟠 자막 시작시간으로 검색한 CROwneUnit 인덱스 : ', subtitleClipIndex);
|
||||
return subtitleClipIndex;
|
||||
}
|
||||
|
||||
// 영상내 존재하는 자막과 자막사이 공백의 시작시간 리스트를 구함
|
||||
function getSubtitleStartTime() {
|
||||
const trackClips = xpath.select(`//CRTrackList[@Name='텍스트' or @Name='비디오2']/CRTrackClip`, gmepXmlDoc);
|
||||
|
||||
let cumulativeLengths = [];
|
||||
let total = 0;
|
||||
|
||||
for (let i = 0; i < trackClips.length; i++) {
|
||||
const length = parseInt(trackClips[i].getAttribute('Length'), 10);
|
||||
|
||||
cumulativeLengths.push(total);
|
||||
total += length;
|
||||
}
|
||||
|
||||
console.log("🔵 자막 구간 시작시간 : ", cumulativeLengths);
|
||||
return cumulativeLengths;
|
||||
}
|
||||
|
||||
function getCrtrackClipIndex(clipIndex) {
|
||||
const crTrackClips = xpath.select(`//CRTrackList[@Name='텍스트' or @Name='비디오2']/CRTrackClip`, gmepXmlDoc);
|
||||
|
||||
let index = null;
|
||||
for (let i = 0; crTrackClips.length; i++) {
|
||||
if (clipIndex == parseInt(crTrackClips[i].getAttribute('ClipIndex'), 10)) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
let ele = scoringData[key].ele;
|
||||
let ele2 = scoringData[key].ele2;
|
||||
let ele3 = scoringData[key].ele3;
|
||||
@@ -226,10 +332,16 @@ function getGmepScore(gmepData, scoringJson, index) {
|
||||
// xpath 전처리
|
||||
const trackClipNode = getTrackClipNode(gmepXmlDoc, type, videoStartTime, openingStartTime);
|
||||
const subtitleIndex = trackClipNode ? parseInt(trackClipNode.getAttribute('ClipIndex'), 10) + 1 : null;
|
||||
const clipIndex = getClipIndexBySubtitle(gmepXmlDoc, search);
|
||||
// const subtitleOrder = type === 'video' ? 2 : type === 'opening' ? 1 : null;
|
||||
// 2503회 문제오류 처리를 위한 임시 변경
|
||||
const subtitleOrder = type === 'video' ? 2 : type === 'opening' ? 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 startTime = type === 'video' ? videoStartTime : type === 'opening' ? openingStartTime : null;
|
||||
|
||||
let xpathList = [ele, ele2, ele3, existEle];
|
||||
@@ -237,17 +349,12 @@ function getGmepScore(gmepData, scoringJson, index) {
|
||||
.replace(/{subtitleIndex}/g, subtitleIndex)
|
||||
.replace(/{subtitleOrder}/g, subtitleOrder)
|
||||
.replace(/{startTime}/g, startTime)
|
||||
.replace(/{clipIndex}/g, clipIndex)
|
||||
.replace(/{textClipIndex}/g, textClipIndex)
|
||||
.replace(/{image}/g, image)
|
||||
: e
|
||||
);
|
||||
[ele, ele2, ele3, existEle] = xpathList;
|
||||
|
||||
// [ele, ele2, ele3] = [ele, ele2, ele3].map(e => e?.replace(/{subtitleIndex}/g, subtitleIndex));
|
||||
// [ele, ele2, ele3] = [ele, ele2, ele3].map(e => e?.replace(/{subtitleOrder}/g, subtitleOrder));
|
||||
// [ele, ele2, ele3] = [ele, ele2, ele3].map(e => e?.replace(/{startTime}/g, startTime));
|
||||
// [ele, ele2, ele3] = [ele, ele2, ele3].map(e => e?.replace(/{clipIndex}/g, clipIndex));
|
||||
|
||||
// search 값이 undefined 아니면 ele의 {search}부분을 search로 치환
|
||||
/**
|
||||
* JSON파일 곰믹스 5번문항/22번 문항
|
||||
@@ -260,11 +367,9 @@ 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));
|
||||
[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);
|
||||
[ele, ele2, ele3] = [ele, ele2, ele3].map(e => e?.includes('{search}') ? null : e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -305,20 +410,24 @@ function getGmepScore(gmepData, scoringJson, index) {
|
||||
|
||||
// 미디어의 인덱스 순서
|
||||
const clipIndexOrder = xpath.select(ele, gmepXmlDoc);
|
||||
|
||||
clipIndexOrder.forEach((clipIndex) => {
|
||||
CRClipIndex = parseInt(clipIndex.value, 10) + 1; // XPath는 1-based index를 사용
|
||||
|
||||
// 인덱스 순서에 따른 CRClip 요소의 Path를 찾기
|
||||
const mediaPath = xpath.select1(`//CRClipArr/CRClip[${CRClipIndex}]/@Path`, gmepXmlDoc);
|
||||
|
||||
// 만약 CRClip 요소가 motion clip인 경우 CRCUnitArr의 Path를 찾기
|
||||
if (mediaPath == null) {
|
||||
const motionClipPath = xpath.select1(`//CRClipArr/CRClip[${CRClipIndex}]/CRCUnitArr/@Path`, gmepXmlDoc);
|
||||
if (motionClipPath !== null) {
|
||||
mediaOrderList.push(motionClipPath.value);
|
||||
const fileName = path.basename(motionClipPath.value);
|
||||
mediaOrderList.push(fileName);
|
||||
}
|
||||
}
|
||||
else if (mediaPath != null) {
|
||||
mediaOrderList.push(mediaPath.value);
|
||||
const fileName = path.basename(mediaPath.value);
|
||||
mediaOrderList.push(fileName);
|
||||
}
|
||||
});
|
||||
const userAnswer = mediaOrderList;
|
||||
@@ -326,38 +435,99 @@ function getGmepScore(gmepData, scoringJson, index) {
|
||||
}
|
||||
|
||||
else if (type == "startEnd") {
|
||||
// CRClipArr/CRClip 요소의 Path속성 리스트를 구함
|
||||
// 모션 클립 이미지도 고려해 처리
|
||||
const mediaPathList = xpath.select("//CRClipArr/CRClip[@Type='11']/CRCUnitArr/@Path | //CRClipArr/CRClip[not(@Type='11')]/@Path", gmepXmlDoc);
|
||||
const videoClipIndex = getClipIndexByMediaPath(media);
|
||||
|
||||
// "동영상.mp4"의 clipIndex를 구함
|
||||
let clipIndex = mediaPathList.findIndex(mediaPath => mediaPath.value === media);
|
||||
// clipIndex가 -1이면 해당 미디어가 존재하지 않는 것
|
||||
if (clipIndex === -1) {
|
||||
// 해당 미디어가 없을경우 clipIndex값 -1
|
||||
if (videoClipIndex == -1) {
|
||||
scoringResult[key] = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
else {
|
||||
// //CRTrackList[@Name='비디오1']/CRTrackClip[@ClipIndex='동영상.mp4'] 요소를 찾음
|
||||
const trackClipNode = xpath.select1(`//CRTrackList[@Name='비디오1']/CRTrackClip[@ClipIndex='${clipIndex}']`, gmepXmlDoc);
|
||||
if (!trackClipNode) {
|
||||
const CRTrackClipNode = xpath.select1(ele, gmepXmlDoc);
|
||||
if (!CRTrackClipNode) {
|
||||
scoringResult[key] = 0;
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
// CRTrackClip 요소의 Pos(시작시간)과 Length(재생길이)를 구함
|
||||
const pos = xpath.select1('@Pos', trackClipNode);
|
||||
const length = xpath.select1('@Length', trackClipNode);
|
||||
const pos = xpath.select1('@Pos', CRTrackClipNode);
|
||||
const length = xpath.select1('@Length', CRTrackClipNode);
|
||||
const userAnswer = { start: pos.value, end: length.value }
|
||||
|
||||
totalScore += compareAndScore(userAnswer, rightAnswer, point, key, scoringResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (type == "effect") {
|
||||
const videoClipIndex = getClipIndexByMediaPath(media);
|
||||
if (videoClipIndex == -1) {
|
||||
scoringResult[key] = 0;
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
//CRTrackList[@Name='비디오1']/CRTrackClip[@ClipIndex='{videoClipIndex}']//CRFilter
|
||||
const CRFilterNode = xpath.select1(ele, gmepXmlDoc);
|
||||
if (!CRFilterNode) {
|
||||
scoringResult[key] = 0;
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
const userAnswer = {}
|
||||
const attributes = CRFilterNode.attributes;
|
||||
|
||||
|
||||
// rightAnswer의 key값을 순회하면서
|
||||
// CRFilterNode의 속성명과 일치하는 값을 userAnswer에 저장
|
||||
// for (const [keyName, expectedValue] of Object.keys(rightAnswer)) {
|
||||
for (const keyName of Object.keys(rightAnswer)) {
|
||||
const attr = attributes.getNamedItem(keyName);
|
||||
userAnswer[keyName] = attr ? attr.value : null;
|
||||
}
|
||||
totalScore += compareAndScore(userAnswer, rightAnswer, point, key, scoringResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (type === 'openingStartTime' || type === 'openingLength'
|
||||
|| type === 'videoStartTime' || type === 'videoLength') {
|
||||
// 자막의 정보를 이용해 CROwneUnit의 인덱스를 구함
|
||||
// 1. 텍스트
|
||||
// 2. 순서
|
||||
// 3. 시작시간
|
||||
const indexByText = getClipIndexByText(ele);
|
||||
const indexByOrder = getClipIndexByOrder(subtitleOrder);
|
||||
|
||||
if (type.includes('opening')) time = openingStartTime;
|
||||
else if (type.includes('video')) time = videoStartTime;
|
||||
else time = null;
|
||||
const indexByStartTime = getCilpIndexByStartTime(time);
|
||||
|
||||
// 1, 2, 3순으로 자막을 찾음
|
||||
const index = indexByText ?? indexByOrder ?? indexByStartTime;
|
||||
if (index != null) {
|
||||
// 자막 시작시간
|
||||
if (type.includes('StartTime')) {
|
||||
const crtrackClipIndex = getCrtrackClipIndex(index)
|
||||
const startTimeList = getSubtitleStartTime();
|
||||
const startTime = startTimeList[crtrackClipIndex];
|
||||
userAnswer = startTime;
|
||||
}
|
||||
|
||||
// 자막 길이
|
||||
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);
|
||||
}
|
||||
}
|
||||
else {
|
||||
userAnswer = null;
|
||||
}
|
||||
|
||||
totalScore += compareAndScore(userAnswer, rightAnswer, point, key, scoringResult);
|
||||
}
|
||||
|
||||
else if (type == "color") {
|
||||
const result = xpath.select(ele, gmepXmlDoc);
|
||||
|
||||
@@ -468,7 +638,7 @@ function getGmepScore(gmepData, scoringJson, index) {
|
||||
|
||||
// 찾으려는 자막이 존재하지 않는 경우
|
||||
// (2-28) 문항의 경우 오프닝 자막이 없어도 xpath구문의 sum함수 결과값이 0이 반환되는것을 방지
|
||||
if (trackClipNode === undefined && clipIndex === null) {
|
||||
if (trackClipNode === undefined && textClipIndex === null) {
|
||||
scoringResult[key] = 0;
|
||||
continue;
|
||||
}
|
||||
@@ -703,7 +873,7 @@ function getTrackClipNode(xmlDoc, type, videoStartTime, openingStartTime) {
|
||||
* 1. 자막 텍스트의 유사도를 판별
|
||||
* 2. 자막텍스트와 일치하는 자막요소(CROWneUnit)의 순서를 구함
|
||||
*/
|
||||
function getClipIndexBySubtitle(xmlDoc, search) {
|
||||
function getTextClipIndex(xmlDoc, search) {
|
||||
// 1. search값이 일치하지 않는 경우 : count가 0이 되어 @ClipIndex = 0 / CROwneUnit[1]을 가리킴 [오류]
|
||||
// 2. search값이 일치하는 경우
|
||||
// 1) search값이 CROwneUnit[1]이면 : preceding-sibling::CROwneUnit이 없어서 @ClipIndex = 0 / CROwneUnit[1]을 가리킴 [정상]
|
||||
|
||||
Reference in New Issue
Block a user