542 lines
13 KiB
JavaScript
542 lines
13 KiB
JavaScript
import response from "../../response.js";
|
|
import models from "../../models/index.js";
|
|
import {
|
|
clearFileBuffers,
|
|
saveFileToDisk,
|
|
} from "../../middlewares/Level/uploadLevel.js";
|
|
import fs from "fs";
|
|
import path from "path";
|
|
import {
|
|
updateOtherLevelsRoutes,
|
|
updateOtherLevelsRoutesOnDelete,
|
|
} from "../../middlewares/Level/checkLevel.js";
|
|
|
|
export const getLevels = async (req, res) => {
|
|
try {
|
|
const levels = await models.Level.findAll({
|
|
attributes: {
|
|
exclude: [
|
|
"ROUTE_1",
|
|
"ROUTE_2",
|
|
"ROUTE_3",
|
|
"ROUTE_4",
|
|
"ROUTE_5",
|
|
"ROUTE_6",
|
|
],
|
|
},
|
|
});
|
|
|
|
response(200, levels, "Success", res);
|
|
} catch (error) {
|
|
console.log(error);
|
|
response(500, null, "Error retrieving levels data!", res);
|
|
}
|
|
};
|
|
|
|
export const getLevelById = async (req, res) => {
|
|
try {
|
|
const { id } = req.params;
|
|
const level = await models.Level.findByPk(id, {
|
|
attributes: {
|
|
exclude: [
|
|
"ROUTE_1",
|
|
"ROUTE_2",
|
|
"ROUTE_3",
|
|
"ROUTE_4",
|
|
"ROUTE_5",
|
|
"ROUTE_6",
|
|
],
|
|
},
|
|
});
|
|
|
|
if (!level) {
|
|
return response(404, null, "Level not found", res);
|
|
}
|
|
|
|
response(200, level, "Success", res);
|
|
} catch (error) {
|
|
console.log(error);
|
|
response(500, null, "Internal Server Error", res);
|
|
}
|
|
};
|
|
|
|
export const getLevelsByTopicId = async (req, res) => {
|
|
try {
|
|
const { idTopic } = req.params;
|
|
const { ID } = req.user;
|
|
|
|
const topicExists = await models.Topic.findByPk(idTopic);
|
|
if (!topicExists) {
|
|
return response(404, null, "Topic not found", res);
|
|
}
|
|
|
|
const levels = await models.Level.findAll({
|
|
where: {
|
|
ID_TOPIC: idTopic,
|
|
},
|
|
attributes: {
|
|
exclude: [
|
|
"ROUTE_1",
|
|
"ROUTE_2",
|
|
"ROUTE_3",
|
|
"ROUTE_4",
|
|
"ROUTE_5",
|
|
"ROUTE_6",
|
|
],
|
|
},
|
|
include: [
|
|
{
|
|
model: models.StdLearning,
|
|
as: "stdLearning",
|
|
attributes: ["SCORE", "ID_STUDENT_LEARNING"],
|
|
where: {
|
|
ID: ID,
|
|
},
|
|
required: false,
|
|
},
|
|
],
|
|
});
|
|
|
|
if (!levels || levels.length === 0) {
|
|
return res
|
|
.status(404)
|
|
.json({ message: "No levels found for the given topic." });
|
|
}
|
|
|
|
const lastCompletedLearning = await models.StdLearning.findOne({
|
|
where: {
|
|
ID: ID,
|
|
STUDENT_FINISH: {
|
|
[models.Op.not]: null,
|
|
},
|
|
},
|
|
include: [
|
|
{
|
|
model: models.Level,
|
|
as: "level",
|
|
attributes: ["ID_LEVEL", "NAME_LEVEL"],
|
|
},
|
|
],
|
|
order: [["STUDENT_FINISH", "DESC"]],
|
|
});
|
|
|
|
const levelsWithScore = levels.map((level) => {
|
|
const SCORE =
|
|
level.stdLearning.length > 0 ? level.stdLearning[0].SCORE : 0;
|
|
const ID_STUDENT_LEARNING =
|
|
level.stdLearning.length > 0
|
|
? level.stdLearning[0].ID_STUDENT_LEARNING
|
|
: null;
|
|
|
|
const levelJSON = level.toJSON();
|
|
delete levelJSON.stdLearning;
|
|
|
|
return {
|
|
...levelJSON,
|
|
ID_STUDENT_LEARNING,
|
|
SCORE,
|
|
};
|
|
});
|
|
|
|
const sortedLevels = levelsWithScore.sort((a, b) => {
|
|
if (a.NAME_LEVEL === "Pretest") return -1;
|
|
if (b.NAME_LEVEL === "Pretest") return 1;
|
|
|
|
const levelA = parseInt(a.NAME_LEVEL.replace("Level ", ""));
|
|
const levelB = parseInt(b.NAME_LEVEL.replace("Level ", ""));
|
|
|
|
return levelA - levelB;
|
|
});
|
|
|
|
const responsePayload = {
|
|
lastCompletedLevel: lastCompletedLearning
|
|
? {
|
|
ID_STUDENT_LEARNING: lastCompletedLearning.ID_STUDENT_LEARNING,
|
|
ID_LEVEL: lastCompletedLearning.level.ID_LEVEL,
|
|
NAME_LEVEL: lastCompletedLearning.level.NAME_LEVEL,
|
|
FINISHED_AT: lastCompletedLearning.STUDENT_FINISH,
|
|
}
|
|
: null,
|
|
levels: sortedLevels,
|
|
};
|
|
|
|
res.status(200).json({ message: "Success", data: responsePayload });
|
|
} catch (error) {
|
|
console.log(error);
|
|
res.status(500).json({ message: "Internal Server Error" });
|
|
}
|
|
};
|
|
|
|
export const createLevel = async (req, res, next) => {
|
|
const { NAME_LEVEL, ID_SECTION, ID_TOPIC, CONTENT, VIDEO } = req.body;
|
|
const { AUDIO, IMAGE } = req.filesToSave || {};
|
|
|
|
if (!NAME_LEVEL) {
|
|
clearFileBuffers({ AUDIO, IMAGE });
|
|
return response(400, null, "Level name is required", res);
|
|
}
|
|
|
|
if (!ID_SECTION) {
|
|
clearFileBuffers({ AUDIO, IMAGE });
|
|
return response(400, null, "Section is required", res);
|
|
}
|
|
|
|
if (!ID_TOPIC) {
|
|
clearFileBuffers({ AUDIO, IMAGE });
|
|
return response(400, null, "Topic is required", res);
|
|
}
|
|
|
|
try {
|
|
const sectionWithTopic = await models.Topic.findOne({
|
|
where: { ID_SECTION, ID_TOPIC },
|
|
});
|
|
|
|
if (!sectionWithTopic) {
|
|
clearFileBuffers({ AUDIO, IMAGE });
|
|
return response(
|
|
400,
|
|
null,
|
|
"Topic does not relate to the provided Section!",
|
|
res
|
|
);
|
|
}
|
|
|
|
const existingLevel = await models.Level.findOne({
|
|
where: { NAME_LEVEL, ID_TOPIC },
|
|
});
|
|
|
|
if (existingLevel) {
|
|
clearFileBuffers({ AUDIO, IMAGE });
|
|
return response(
|
|
409,
|
|
null,
|
|
"A level with this name already exists under this topic",
|
|
res
|
|
);
|
|
}
|
|
|
|
const newLevel = await models.Level.create({
|
|
NAME_LEVEL,
|
|
ID_SECTION,
|
|
ID_TOPIC,
|
|
IS_PRETEST: req.body.IS_PRETEST || 0,
|
|
CONTENT,
|
|
VIDEO: VIDEO || null,
|
|
AUDIO: null,
|
|
IMAGE: null,
|
|
ROUTE_1: req.body.ROUTE_1,
|
|
ROUTE_2: req.body.ROUTE_2,
|
|
ROUTE_3: req.body.ROUTE_3,
|
|
ROUTE_4: req.body.ROUTE_4,
|
|
ROUTE_5: req.body.ROUTE_5,
|
|
ROUTE_6: req.body.ROUTE_6,
|
|
});
|
|
|
|
req.body.newLevelId = newLevel.ID_LEVEL;
|
|
|
|
const audioFilename = AUDIO
|
|
? saveFileToDisk(AUDIO, "AUDIO", ID_TOPIC, ID_SECTION, newLevel.ID_LEVEL)
|
|
: null;
|
|
const imageFilename = IMAGE
|
|
? saveFileToDisk(IMAGE, "IMAGE", ID_TOPIC, ID_SECTION, newLevel.ID_LEVEL)
|
|
: null;
|
|
|
|
newLevel.AUDIO = audioFilename;
|
|
newLevel.IMAGE = imageFilename;
|
|
await newLevel.save();
|
|
|
|
await updateOtherLevelsRoutes(req, res, next);
|
|
|
|
response(201, newLevel, "Level created successfully", res);
|
|
} catch (error) {
|
|
console.log(error);
|
|
clearFileBuffers({ AUDIO, IMAGE });
|
|
return response(500, null, "Internal Server Error", res);
|
|
}
|
|
};
|
|
|
|
export const updateLevelById = async (req, res, next) => {
|
|
const { id } = req.params;
|
|
const { NAME_LEVEL, ID_SECTION, ID_TOPIC, CONTENT, VIDEO } = req.body;
|
|
|
|
const { AUDIO, IMAGE } = req.filesToSave || {};
|
|
|
|
try {
|
|
const level = await models.Level.findByPk(id);
|
|
|
|
if (!level) {
|
|
clearFileBuffers({ AUDIO, IMAGE });
|
|
return response(404, null, "Level not found", res);
|
|
}
|
|
|
|
const sectionWithTopic = await models.Topic.findOne({
|
|
where: { ID_SECTION, ID_TOPIC },
|
|
});
|
|
|
|
if (!sectionWithTopic) {
|
|
clearFileBuffers({ AUDIO, IMAGE });
|
|
return response(
|
|
400,
|
|
null,
|
|
"Topic does not relate to the provided Section",
|
|
res
|
|
);
|
|
}
|
|
|
|
if (NAME_LEVEL && ID_TOPIC) {
|
|
const existingLevel = await models.Level.findOne({
|
|
where: {
|
|
NAME_LEVEL,
|
|
ID_TOPIC,
|
|
ID_LEVEL: { [models.Sequelize.Op.ne]: id },
|
|
},
|
|
});
|
|
|
|
if (existingLevel) {
|
|
clearFileBuffers({ AUDIO, IMAGE });
|
|
return response(
|
|
409,
|
|
null,
|
|
"A level with this name already exists under this topic",
|
|
res
|
|
);
|
|
}
|
|
}
|
|
|
|
if (NAME_LEVEL) {
|
|
level.NAME_LEVEL = NAME_LEVEL;
|
|
if (NAME_LEVEL === "Level 1") {
|
|
level.IS_PRETEST = 1;
|
|
} else {
|
|
level.IS_PRETEST = 0;
|
|
}
|
|
}
|
|
if (ID_SECTION) level.ID_SECTION = ID_SECTION;
|
|
if (ID_TOPIC) level.ID_TOPIC = ID_TOPIC;
|
|
if (CONTENT) level.CONTENT = CONTENT;
|
|
|
|
if (VIDEO) {
|
|
level.VIDEO = VIDEO;
|
|
}
|
|
|
|
if (AUDIO) {
|
|
if (level.AUDIO) {
|
|
const oldAudioPath = path.join(
|
|
"public/uploads/level/audio",
|
|
level.AUDIO
|
|
);
|
|
if (fs.existsSync(oldAudioPath)) {
|
|
fs.unlinkSync(oldAudioPath);
|
|
}
|
|
}
|
|
level.AUDIO = saveFileToDisk(
|
|
AUDIO,
|
|
"AUDIO",
|
|
ID_TOPIC || level.ID_TOPIC,
|
|
ID_SECTION || level.ID_SECTION,
|
|
level.ID_LEVEL
|
|
);
|
|
}
|
|
|
|
if (IMAGE) {
|
|
if (level.IMAGE) {
|
|
const oldImagePath = path.join(
|
|
"public/uploads/level/image",
|
|
level.IMAGE
|
|
);
|
|
if (fs.existsSync(oldImagePath)) {
|
|
fs.unlinkSync(oldImagePath);
|
|
}
|
|
}
|
|
level.IMAGE = saveFileToDisk(
|
|
IMAGE,
|
|
"IMAGE",
|
|
ID_TOPIC || level.ID_TOPIC,
|
|
ID_SECTION || level.ID_SECTION,
|
|
level.ID_LEVEL
|
|
);
|
|
}
|
|
|
|
await level.save();
|
|
|
|
req.body.newLevelId = level.ID_LEVEL;
|
|
|
|
await updateOtherLevelsRoutes(req, res, next);
|
|
|
|
response(200, level, "Level updated successfully", res);
|
|
} catch (error) {
|
|
console.log(error);
|
|
clearFileBuffers({ AUDIO, IMAGE });
|
|
return response(500, null, "Internal Server Error", res);
|
|
}
|
|
};
|
|
|
|
export const deleteLevelById = async (req, res, next) => {
|
|
const { id } = req.params;
|
|
|
|
try {
|
|
const level = await models.Level.findByPk(id);
|
|
|
|
if (!level) {
|
|
return response(404, null, "Level not found", res);
|
|
}
|
|
|
|
const deleteFile = (filePath) => {
|
|
if (fs.existsSync(filePath)) {
|
|
fs.unlinkSync(filePath);
|
|
}
|
|
};
|
|
|
|
if (level.AUDIO) {
|
|
const audioPath = path.join("public/uploads/level/audio", level.AUDIO);
|
|
deleteFile(audioPath);
|
|
}
|
|
|
|
if (level.IMAGE) {
|
|
const imagePath = path.join("public/uploads/level/image", level.IMAGE);
|
|
deleteFile(imagePath);
|
|
}
|
|
|
|
req.body.newLevelId = level.ID_LEVEL;
|
|
|
|
await level.destroy();
|
|
|
|
await updateOtherLevelsRoutesOnDelete(req, res, next);
|
|
|
|
response(200, null, "Level deleted successfully", res);
|
|
} catch (error) {
|
|
console.log(error);
|
|
return response(500, null, "Internal Server Error", res);
|
|
}
|
|
};
|
|
|
|
export const getPreviousLevel = async (req, res) => {
|
|
try {
|
|
const { next_learning } = req.params;
|
|
const { ID } = req.user;
|
|
|
|
const currentLevel = await models.Level.findByPk(next_learning, {
|
|
include: [
|
|
{
|
|
model: models.StdLearning,
|
|
as: "stdLearning",
|
|
attributes: ["SCORE", "ID_STUDENT_LEARNING"],
|
|
where: {
|
|
ID: ID,
|
|
},
|
|
required: false,
|
|
},
|
|
],
|
|
attributes: {
|
|
exclude: [
|
|
"ROUTE_1",
|
|
"ROUTE_2",
|
|
"ROUTE_3",
|
|
"ROUTE_4",
|
|
"ROUTE_5",
|
|
"ROUTE_6",
|
|
],
|
|
},
|
|
});
|
|
|
|
if (!currentLevel) {
|
|
return response(404, null, "Level not found", res);
|
|
}
|
|
|
|
const { NAME_LEVEL, ID_TOPIC } = currentLevel;
|
|
const levelNumber = parseInt(NAME_LEVEL.replace("Level ", ""));
|
|
|
|
if (isNaN(levelNumber)) {
|
|
return response(400, null, "Invalid level format", res);
|
|
}
|
|
|
|
const previousLevels = await models.Level.findAll({
|
|
where: {
|
|
ID_TOPIC: ID_TOPIC,
|
|
NAME_LEVEL: {
|
|
[models.Op.or]: [
|
|
{ [models.Op.like]: "Pretest" },
|
|
{ [models.Op.regexp]: `^Level [0-9]+$` },
|
|
],
|
|
},
|
|
[models.Op.or]: [
|
|
{ IS_PRETEST: 1 },
|
|
{
|
|
NAME_LEVEL: {
|
|
[models.Op.lt]: `Level ${levelNumber}`,
|
|
},
|
|
},
|
|
],
|
|
},
|
|
order: [["NAME_LEVEL", "ASC"]],
|
|
attributes: {
|
|
exclude: [
|
|
"ROUTE_1",
|
|
"ROUTE_2",
|
|
"ROUTE_3",
|
|
"ROUTE_4",
|
|
"ROUTE_5",
|
|
"ROUTE_6",
|
|
],
|
|
},
|
|
include: [
|
|
{
|
|
model: models.StdLearning,
|
|
as: "stdLearning",
|
|
attributes: ["SCORE", "ID_STUDENT_LEARNING"],
|
|
where: {
|
|
ID: ID,
|
|
},
|
|
required: false,
|
|
},
|
|
],
|
|
});
|
|
|
|
const previousLevelsWithScore = previousLevels.map((level) => {
|
|
const SCORE =
|
|
level.stdLearning.length > 0 ? level.stdLearning[0].SCORE : 0;
|
|
const ID_STUDENT_LEARNING =
|
|
level.stdLearning.length > 0
|
|
? level.stdLearning[0].ID_STUDENT_LEARNING
|
|
: null;
|
|
|
|
const levelJSON = level.toJSON();
|
|
delete levelJSON.stdLearning;
|
|
|
|
return {
|
|
...levelJSON,
|
|
ID_STUDENT_LEARNING,
|
|
SCORE,
|
|
};
|
|
});
|
|
|
|
const currentLevelWithScore = {
|
|
...currentLevel.toJSON(),
|
|
ID_STUDENT_LEARNING:
|
|
currentLevel.stdLearning.length > 0
|
|
? currentLevel.stdLearning[0].ID_STUDENT_LEARNING
|
|
: null,
|
|
SCORE:
|
|
currentLevel.stdLearning.length > 0
|
|
? currentLevel.stdLearning[0].SCORE
|
|
: 0,
|
|
};
|
|
|
|
delete currentLevelWithScore.stdLearning;
|
|
|
|
if (!previousLevelsWithScore.length && !currentLevelWithScore) {
|
|
return response(404, null, "No levels found", res);
|
|
}
|
|
|
|
const result = {
|
|
currentLevel: currentLevelWithScore,
|
|
previousLevels: previousLevelsWithScore,
|
|
};
|
|
|
|
response(200, result, "Success", res);
|
|
} catch (error) {
|
|
console.log(error);
|
|
res.status(500).json({ message: "Internal Server Error" });
|
|
}
|
|
};
|