backend_adaptive_learning/controllers/contentControllers/level.js

593 lines
14 KiB
JavaScript
Raw Normal View History

2024-09-13 13:03:35 +00:00
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);
2024-09-13 13:03:35 +00:00
}
};
export const getLevelById = async (req, res) => {
try {
const { id } = req.params;
2024-09-13 13:03:35 +00:00
const level = await models.Level.findByPk(id, {
attributes: {
exclude: [
"ROUTE_1",
"ROUTE_2",
"ROUTE_3",
"ROUTE_4",
"ROUTE_5",
"ROUTE_6",
],
},
include: [
{
model: models.Topic,
as: "levelTopic",
attributes: ["NAME_TOPIC"],
include: {
model: models.Section,
as: "topicSection",
attributes: ["NAME_SECTION"],
},
},
],
2024-09-13 13:03:35 +00:00
});
if (!level) {
return response(404, null, "Level not found", res);
}
const levelJSON = level.toJSON();
const NAME_SECTION = levelJSON.levelTopic.topicSection.NAME_SECTION;
const NAME_TOPIC = levelJSON.levelTopic.NAME_TOPIC;
delete levelJSON.levelTopic;
delete levelJSON.levelTopic?.topicSection;
const responsePayload = {
NAME_SECTION,
NAME_TOPIC,
...levelJSON,
};
response(200, responsePayload, "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, {
include: {
model: models.Section,
as: "topicSection",
attributes: ["NAME_SECTION"],
},
});
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,
},
{
model: models.Topic,
as: "levelTopic",
attributes: ["NAME_TOPIC"],
include: {
model: models.Section,
as: "topicSection",
attributes: ["NAME_SECTION"],
},
},
],
});
if (!levels || levels.length === 0) {
return res
.status(404)
.json({ message: "No levels found for the given topic." });
}
2024-09-23 06:16:02 +00:00
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();
const NAME_SECTION = levelJSON.levelTopic.topicSection.NAME_SECTION;
const NAME_TOPIC = levelJSON.levelTopic.NAME_TOPIC;
delete levelJSON.stdLearning;
delete levelJSON.levelTopic;
delete levelJSON.levelTopic?.topicSection;
return {
NAME_SECTION,
NAME_TOPIC,
...levelJSON,
ID_STUDENT_LEARNING,
SCORE,
};
});
2024-09-23 06:16:02 +00:00
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 });
2024-09-13 13:03:35 +00:00
} 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 || {};
2024-09-13 13:03:35 +00:00
if (!NAME_LEVEL) {
clearFileBuffers({ AUDIO, IMAGE });
2024-09-13 13:03:35 +00:00
return response(400, null, "Level name is required", res);
}
if (!ID_SECTION) {
clearFileBuffers({ AUDIO, IMAGE });
2024-09-13 13:03:35 +00:00
return response(400, null, "Section is required", res);
}
if (!ID_TOPIC) {
clearFileBuffers({ AUDIO, IMAGE });
2024-09-13 13:03:35 +00:00
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 });
2024-09-13 13:03:35 +00:00
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 });
2024-09-13 13:03:35 +00:00
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,
2024-09-13 13:03:35 +00:00
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)
2024-09-13 13:03:35 +00:00
: null;
const imageFilename = IMAGE
? saveFileToDisk(IMAGE, "IMAGE", ID_TOPIC, ID_SECTION, newLevel.ID_LEVEL)
2024-09-13 13:03:35 +00:00
: 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 });
2024-09-13 13:03:35 +00:00
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;
2024-09-13 13:03:35 +00:00
const { AUDIO, IMAGE } = req.filesToSave || {};
2024-09-13 13:03:35 +00:00
try {
const level = await models.Level.findByPk(id);
if (!level) {
clearFileBuffers({ AUDIO, IMAGE });
2024-09-13 13:03:35 +00:00
return response(404, null, "Level not found", res);
}
const sectionWithTopic = await models.Topic.findOne({
where: { ID_SECTION, ID_TOPIC },
});
if (!sectionWithTopic) {
clearFileBuffers({ AUDIO, IMAGE });
2024-09-13 13:03:35 +00:00
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 });
2024-09-13 13:03:35 +00:00
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;
2024-09-13 13:03:35 +00:00
}
if (AUDIO) {
2024-09-13 13:03:35 +00:00
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",
2024-09-13 13:03:35 +00:00
ID_TOPIC || level.ID_TOPIC,
ID_SECTION || level.ID_SECTION,
level.ID_LEVEL
);
}
if (IMAGE) {
2024-09-13 13:03:35 +00:00
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",
2024-09-13 13:03:35 +00:00
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 });
2024-09-13 13:03:35 +00:00
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" });
}
2024-09-13 13:03:35 +00:00
};