DPI 1-4문항 레이어명 불일치시 "확인필요"로 처리

This commit is contained in:
2026-02-19 16:35:49 +09:00
parent a03dabcdf6
commit aadc68d2a7
32 changed files with 71 additions and 14 deletions

View File

@@ -76,7 +76,7 @@ if __name__ == "__main__":
exam_round = "2622" exam_round = "2622"
exam_codes = ["DIC", "DPI"] exam_codes = ["DIC", "DPI"]
# exam_codes = ["DIC"] # exam_codes = ["DIC"]
source_dir = r"D:\project\GOM\DIC\회차별채점자료\2622" # source_dir = r"D:\project\GOM\DIC\회차별채점자료\2622"
# source_dir = r"D:\project\data\제2522회 특별\(추가)과목별_답안파일" source_dir = r"D:\project\data\제2622회 특별\과목별답안파일 (2)"
copy_exam_files(exam_round, exam_codes, source_dir) copy_exam_files(exam_round, exam_codes, source_dir)

View File

@@ -9,7 +9,7 @@ const stringSimilarity = require("string-similarity");
* @param {number} threshold - 유사도 기준 (0~1) * @param {number} threshold - 유사도 기준 (0~1)
* @returns {string} - 유사한 문자열 * @returns {string} - 유사한 문자열
*/ */
function findSimilarString(xmlDoc, targetString, threshold = 0.8) { function findSimilarString(xmlDoc, targetString, threshold = 0.7) {
// XML 내부의 비교 대상 텍스트 리스트 찾기 // XML 내부의 비교 대상 텍스트 리스트 찾기
function getTextNodes(xmlDoc, paths = ["//CRCUnitArr/@Name"]) { function getTextNodes(xmlDoc, paths = ["//CRCUnitArr/@Name"]) {
const stringList = []; const stringList = [];

View File

@@ -172,7 +172,7 @@ function getGpdpScore(gpdpData, scoringJson, index) {
* > 멀티라인 텍스트 유사도 판별하기 어려움 * > 멀티라인 텍스트 유사도 판별하기 어려움
*/ */
if (search !== undefined) { if (search !== undefined) {
let result = findSimilarString(gpdpXmlDoc, search, 0.8); let result = findSimilarString(gpdpXmlDoc, search, 0.7);
if (result !== null) { if (result !== null) {
result = result.replace(/"/g, "'"); result = result.replace(/"/g, "'");
search = result; search = result;
@@ -202,6 +202,11 @@ function getGpdpScore(gpdpData, scoringJson, index) {
// [1-4] 사진1 > 조정 // [1-4] 사진1 > 조정
else if (type === "layer.Effects") { else if (type === "layer.Effects") {
if (!ele) {
scoringResult[key] = '확인필요';
console.log("❌ 오답: 레이어명 확인 필요");
continue;
}
const effects = ele ? xpath.select(ele, gpdpXmlDoc) : []; const effects = ele ? xpath.select(ele, gpdpXmlDoc) : [];
let isMatched = false; let isMatched = false;

View File

@@ -14,20 +14,20 @@ const todayDate = getToday();
const examRound = '2622'; const examRound = '2622';
const codeTypes = [ const codeTypes = [
'DIC', // 'DIC',
'DPI', 'DPI',
]; ];
const examTypes = [ const examTypes = [
'A', 'A',
'B', // 'B',
'C', // 'C',
// 'D' // 'D'
]; ];
// testMode가 true일 경우 TEST 폴더에 있는 답안 파일을 읽어옴 // testMode가 true일 경우 TEST 폴더에 있는 답안 파일을 읽어옴
const testMode = false; // const testMode = false;
// const testMode = true; const testMode = true;
const outputExcelFiles = []; const outputExcelFiles = [];
codeTypes.forEach(codeType => { codeTypes.forEach(codeType => {
@@ -36,9 +36,15 @@ codeTypes.forEach(codeType => {
if (!fs.existsSync(jsonPath)) return if (!fs.existsSync(jsonPath)) return
const scoringJson = require(jsonPath); const scoringJson = require(jsonPath);
const answerFilesDir = `./output/${examRound}/${type}/${testMode ? 'TEST' : codeType}`; const answerFilesDir = `./output/${examRound}/${type}/${testMode ? 'TEST' : codeType}`;
let outputExcelFile = `./score_result/${todayDate}_${codeType}_${examRound}${type}_채점결과.xlsx`; let outputExcelFile =
`./score_result/${examRound}/${todayDate}_${codeType}_${examRound}${type}_채점결과.xlsx`;
// 폴더 경로 추출
const dirPath = path.dirname(outputExcelFile);
// 폴더가 없으면 생성
fs.mkdirSync(dirPath, { recursive: true });
if (testMode) { if (testMode) {
outputExcelFile = `./00_${codeType}_${examRound}${type}_TEST.xlsx`; outputExcelFile = `./score_result/test/00_${codeType}_${examRound}${type}_TEST.xlsx`;
} }
// 답안 폴더 내부에 디렉토리가 아닌 일반 파일이 있을 경우 디렉토리만 필터링 해서 불러옴 // 답안 폴더 내부에 디렉토리가 아닌 일반 파일이 있을 경우 디렉토리만 필터링 해서 불러옴
@@ -91,17 +97,59 @@ codeTypes.forEach(codeType => {
console.error(`Error reading PSD file: ${psdPath}`, error); console.error(`Error reading PSD file: ${psdPath}`, error);
} }
}); });
// 기존 XML 파일 채점 방식 주석 처리 (20260213)
gpdpFiles.forEach((gpdpFile, index) => { gpdpFiles.forEach((gpdpFile, index) => {
const gpdpPath = path.join('./', studentDir, gpdpFile); const gpdpPath = path.join('./', studentDir, gpdpFile);
console.log(`Reading ${gpdpPath}...`); console.log(`Reading ${gpdpPath}...`);
const xmlString = fs.readFileSync(gpdpPath, 'utf8'); const xmlString = fs.readFileSync(gpdpPath, 'utf8');
// XML 문자열을 파싱하여 XML 문서 객체로 변환
const xmlDocument = new DOMParser().parseFromString(xmlString, 'application/xml'); const xmlDocument = new DOMParser().parseFromString(xmlString, 'application/xml');
// console.log('xmlDocument:', xmlDocument);
scoringResult[index + 1] = getGpdpScore(xmlDocument, scoringJson, index + 4); if (!xmlString.trim().startsWith("<")) {
console.warn(`XML 형태 아님 → 스킵: ${gpdpFile}`);
return;
}
// 260219 - 파일명에서 번호 추출하여 점수 계산에 활용
// 파일명에서 번호 추출 (dpi_01_, dpi_02_ )
const fileNumberMatch = gpdpFile.match(/dpi_(\d+)_/);
const fileNumber = fileNumberMatch ? parseInt(fileNumberMatch[1], 10) : index + 1;
// 추출한 번호를 사용하여 scoringResult에 저장
scoringResult[fileNumber] = getGpdpScore(xmlDocument, scoringJson, fileNumber + 3);
}); });
// gpdpFiles.forEach((gpdpFile, index) => {
// const gpdpPath = path.join("./", studentDir, gpdpFile);
// console.log(`Reading ${gpdpPath}...`);
// let xmlString;
// try {
// xmlString = fs.readFileSync(gpdpPath, "utf8");
// } catch (err) {
// console.warn(`파일 읽기 실패, 스킵: ${gpdpPath}`);
// return;
// }
// const xmlDocument = new DOMParser().parseFromString(
// xmlString,
// "application/xml"
// );
// // ✅ XML 파싱 실패 여부 확인
// const parseError = xmlDocument.getElementsByTagName("parsererror");
// if (parseError.length > 0) {
// console.warn(`XML 아님 → 스킵: ${gpdpFile}`);
// return; // 그냥 패스
// }
// // 정상 XML만 점수 계산
// scoringResult[index + 1] = getGpdpScore(
// xmlDocument,
// scoringJson,
// index + 4
// );
// });
if (gmepFile.length === 0) { if (gmepFile.length === 0) {
// 곰믹스 채점 항목 갯수 // 곰믹스 채점 항목 갯수
const gmepItemCount = Object.keys(scoringJson[2]).length; const gmepItemCount = Object.keys(scoringJson[2]).length;
@@ -139,6 +187,10 @@ codeTypes.forEach(codeType => {
const workbook = XLSX.utils.book_new(); const workbook = XLSX.utils.book_new();
// 열 너비 계산 // 열 너비 계산
if (!Array.isArray(transposedData) || transposedData.length === 0) {
console.warn("transposedData 비어 있음 → 엑셀 컬럼 생성 스킵");
return; // 또는 continue / break (문맥에 따라)
}
const columnWidths = Object.keys(transposedData[0]).map(key => { const columnWidths = Object.keys(transposedData[0]).map(key => {
// 각 열의 최대 길이를 계산 // 각 열의 최대 길이를 계산
const maxLength = Math.max( const maxLength = Math.max(

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.