2024-09-13 13:03:35 +00:00
|
|
|
import response from "../../response.js";
|
|
|
|
|
import models from "../../models/index.js";
|
|
|
|
|
import { createMonitoring } from "../monitoringControllers/monitoring.js";
|
|
|
|
|
|
|
|
|
|
export const getStdLearnings = async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const stdLearning = await models.StdLearning.findAll();
|
|
|
|
|
response(200, stdLearning, "Success", res);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.log(error);
|
|
|
|
|
response(500, null, "Error retrieving student learning data!", res);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const getStdLearningById = async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const { id } = req.params;
|
|
|
|
|
const stdLearning = await models.StdLearning.findByPk(id);
|
|
|
|
|
|
|
|
|
|
if (!stdLearning) {
|
|
|
|
|
return response(404, null, "Student learning data not found", res);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
response(200, stdLearning, "Success", res);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
response(500, null, "Internal Server Error", res);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const createStdLearning = async (req, res) => {
|
|
|
|
|
const { ID_LEVEL } = req.body;
|
|
|
|
|
|
|
|
|
|
if (!req.user) {
|
|
|
|
|
return response(401, null, "User not authenticated", res);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const ID = req.user.ID;
|
|
|
|
|
|
|
|
|
|
try {
|
2024-09-30 03:20:31 +00:00
|
|
|
if (req.stdLearning) {
|
|
|
|
|
return response(
|
|
|
|
|
200,
|
|
|
|
|
req.stdLearning,
|
|
|
|
|
"Student Learning data found and reused",
|
|
|
|
|
res
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-13 13:03:35 +00:00
|
|
|
const level = await models.Level.findByPk(ID_LEVEL);
|
|
|
|
|
if (!level) {
|
|
|
|
|
return response(404, null, "Level not found", res);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const newStdLearning = await models.StdLearning.create({
|
|
|
|
|
ID,
|
|
|
|
|
ID_LEVEL,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
response(
|
|
|
|
|
201,
|
|
|
|
|
newStdLearning,
|
|
|
|
|
"Student Learning data created successfully",
|
|
|
|
|
res
|
|
|
|
|
);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.log(error);
|
|
|
|
|
response(500, null, "Internal Server Error", res);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const updateStdLearningById = async (req, res) => {
|
|
|
|
|
const { id } = req.params;
|
|
|
|
|
const { FEEDBACK_STUDENT } = req.body;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const stdLearning = await models.StdLearning.findByPk(id, {
|
|
|
|
|
include: [
|
|
|
|
|
{
|
|
|
|
|
model: models.Level,
|
|
|
|
|
as: "level",
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!stdLearning) {
|
|
|
|
|
return response(404, null, "Student Learning record not found", res);
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-10 02:55:13 +00:00
|
|
|
if (req.user.ID !== stdLearning.ID) {
|
|
|
|
|
return response(403, null, "Unauthorized to update this record", res);
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-13 13:03:35 +00:00
|
|
|
stdLearning.STUDENT_FINISH = new Date();
|
2024-10-10 02:55:13 +00:00
|
|
|
stdLearning.SCORE = req.body.SCORE || stdLearning.SCORE;
|
2024-10-10 05:39:09 +00:00
|
|
|
stdLearning.NEXT_LEARNING =
|
|
|
|
|
req.body.NEXT_LEARNING || stdLearning.NEXT_LEARNING;
|
2024-10-10 02:55:13 +00:00
|
|
|
stdLearning.IS_PASS = req.body.IS_PASS || stdLearning.IS_PASS;
|
2024-09-13 13:03:35 +00:00
|
|
|
|
|
|
|
|
if (FEEDBACK_STUDENT) {
|
|
|
|
|
stdLearning.FEEDBACK_STUDENT = FEEDBACK_STUDENT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await stdLearning.save();
|
|
|
|
|
|
|
|
|
|
const level6 = await models.Level.findOne({
|
|
|
|
|
where: {
|
|
|
|
|
NAME_LEVEL: "Level 6",
|
|
|
|
|
ID_TOPIC: stdLearning.level.ID_TOPIC,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!level6) {
|
|
|
|
|
return response(404, null, "Level 6 not found", res);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
stdLearning.level.ID_LEVEL === level6.ID_LEVEL &&
|
|
|
|
|
stdLearning.IS_PASS === 1
|
|
|
|
|
) {
|
|
|
|
|
req.body.ID_STUDENT_LEARNING = id;
|
|
|
|
|
|
2024-12-19 06:54:08 +00:00
|
|
|
const existingMonitoringForTopic = await models.Monitoring.findOne({
|
|
|
|
|
include: [
|
|
|
|
|
{
|
|
|
|
|
model: models.StdLearning,
|
|
|
|
|
as: "stdLearningMonitoring",
|
|
|
|
|
include: [
|
|
|
|
|
{
|
|
|
|
|
model: models.Level,
|
|
|
|
|
as: "level",
|
|
|
|
|
where: { ID_TOPIC: stdLearning.level.ID_TOPIC },
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
],
|
2024-09-13 13:03:35 +00:00
|
|
|
});
|
|
|
|
|
|
2024-12-19 06:54:08 +00:00
|
|
|
if (!existingMonitoringForTopic) {
|
2024-09-13 13:03:35 +00:00
|
|
|
const newMonitoring = await createMonitoring(req);
|
|
|
|
|
|
|
|
|
|
const { level, ...responseData } = stdLearning.toJSON();
|
|
|
|
|
|
|
|
|
|
const combinedPayload = {
|
|
|
|
|
...responseData,
|
|
|
|
|
MONITORING: newMonitoring,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return response(
|
|
|
|
|
200,
|
|
|
|
|
combinedPayload,
|
|
|
|
|
"Student Learning record updated and monitoring created successfully",
|
|
|
|
|
res
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
const monitoringToDelete = await models.Monitoring.findOne({
|
|
|
|
|
where: { ID_STUDENT_LEARNING: id },
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (monitoringToDelete) {
|
|
|
|
|
await monitoringToDelete.destroy();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const { level, ...responseData } = stdLearning.toJSON();
|
|
|
|
|
|
|
|
|
|
response(
|
|
|
|
|
200,
|
|
|
|
|
responseData,
|
|
|
|
|
"Student Learning record updated successfully",
|
|
|
|
|
res
|
|
|
|
|
);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.log(error);
|
|
|
|
|
response(500, null, "Internal Server Error", res);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2024-09-23 06:16:02 +00:00
|
|
|
export const learningScoreByStdLearningId = async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
if (!req.user) {
|
|
|
|
|
return response(401, null, "User not authenticated", res);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const { ID } = req.user;
|
|
|
|
|
const { stdLearningId } = req.params;
|
|
|
|
|
|
|
|
|
|
const stdLearning = await models.StdLearning.findOne({
|
|
|
|
|
where: {
|
|
|
|
|
ID_STUDENT_LEARNING: stdLearningId,
|
|
|
|
|
ID: ID,
|
|
|
|
|
},
|
|
|
|
|
include: [
|
2024-09-30 01:59:36 +00:00
|
|
|
{
|
|
|
|
|
model: models.Level,
|
|
|
|
|
as: "level",
|
|
|
|
|
attributes: ["ID_LEVEL", "NAME_LEVEL"],
|
|
|
|
|
},
|
2024-09-23 06:16:02 +00:00
|
|
|
{
|
|
|
|
|
model: models.Level,
|
|
|
|
|
as: "nextLevel",
|
2024-09-30 01:59:36 +00:00
|
|
|
attributes: ["NAME_LEVEL"],
|
2024-09-23 06:16:02 +00:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
model: models.User,
|
|
|
|
|
as: "learningUser",
|
|
|
|
|
attributes: ["NAME_USERS"],
|
|
|
|
|
include: [
|
|
|
|
|
{
|
|
|
|
|
model: models.Student,
|
|
|
|
|
as: "students",
|
|
|
|
|
attributes: ["NISN"],
|
2024-09-25 03:57:47 +00:00
|
|
|
},
|
|
|
|
|
],
|
2024-09-23 06:16:02 +00:00
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!stdLearning) {
|
|
|
|
|
return response(
|
|
|
|
|
404,
|
|
|
|
|
null,
|
|
|
|
|
"No matching learning data found for this user",
|
|
|
|
|
res
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!stdLearning.NEXT_LEARNING || !stdLearning.nextLevel) {
|
|
|
|
|
return response(200, null, "No next learning level found", res);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const nextLearningData = {
|
|
|
|
|
ID_STUDENT_LEARNING: stdLearning.ID_STUDENT_LEARNING,
|
|
|
|
|
NAME_USERS: stdLearning.learningUser.NAME_USERS,
|
|
|
|
|
NISN: stdLearning.learningUser.students
|
|
|
|
|
? stdLearning.learningUser.students.NISN
|
|
|
|
|
: null,
|
2024-09-30 01:59:36 +00:00
|
|
|
ID_LEVEL: stdLearning.level ? stdLearning.level.ID_LEVEL : null,
|
2024-09-30 03:20:31 +00:00
|
|
|
CURRENT_LEVEL_NAME: stdLearning.level
|
|
|
|
|
? stdLearning.level.NAME_LEVEL
|
|
|
|
|
: null,
|
2024-09-23 06:16:02 +00:00
|
|
|
SCORE: stdLearning.SCORE,
|
|
|
|
|
NEXT_LEARNING: stdLearning.NEXT_LEARNING,
|
|
|
|
|
NEXT_LEARNING_NAME: stdLearning.nextLevel.NAME_LEVEL,
|
2024-09-30 01:59:36 +00:00
|
|
|
IS_PASS: stdLearning.IS_PASS,
|
2024-09-23 06:16:02 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return response(200, nextLearningData, "Success", res);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.log(error);
|
|
|
|
|
return response(500, null, "Internal Server Error", res);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2024-09-13 13:03:35 +00:00
|
|
|
export const learningHistory = async (req, res) => {
|
2024-10-17 01:41:44 +00:00
|
|
|
const { page = 1, limit = 10 } = req.query;
|
|
|
|
|
|
2024-09-13 13:03:35 +00:00
|
|
|
try {
|
|
|
|
|
if (!req.user) {
|
|
|
|
|
return response(401, null, "User not authenticated", res);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const { ID } = req.user;
|
|
|
|
|
|
|
|
|
|
const stdLearnings = await models.StdLearning.findAll({
|
2024-10-24 02:46:11 +00:00
|
|
|
where: {
|
|
|
|
|
ID,
|
|
|
|
|
STUDENT_FINISH: {
|
|
|
|
|
[models.Sequelize.Op.ne]: null,
|
|
|
|
|
},
|
|
|
|
|
SCORE: {
|
|
|
|
|
[models.Sequelize.Op.ne]: null,
|
|
|
|
|
},
|
|
|
|
|
NEXT_LEARNING: {
|
|
|
|
|
[models.Sequelize.Op.ne]: null,
|
|
|
|
|
},
|
2024-10-24 02:19:12 +00:00
|
|
|
},
|
2024-09-13 13:03:35 +00:00
|
|
|
include: [
|
|
|
|
|
{
|
|
|
|
|
model: models.Level,
|
|
|
|
|
as: "level",
|
|
|
|
|
include: [
|
|
|
|
|
{
|
|
|
|
|
model: models.Topic,
|
|
|
|
|
as: "levelTopic",
|
2024-10-10 05:39:09 +00:00
|
|
|
required: true,
|
2024-09-13 13:03:35 +00:00
|
|
|
include: [
|
|
|
|
|
{
|
|
|
|
|
model: models.Section,
|
|
|
|
|
as: "topicSection",
|
2024-10-10 05:39:09 +00:00
|
|
|
required: true,
|
2024-09-13 13:03:35 +00:00
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
],
|
2024-10-10 05:39:09 +00:00
|
|
|
required: true,
|
2024-09-13 13:03:35 +00:00
|
|
|
},
|
|
|
|
|
],
|
2024-10-18 07:02:54 +00:00
|
|
|
order: [["STUDENT_FINISH", "DESC"]],
|
2024-09-13 13:03:35 +00:00
|
|
|
});
|
|
|
|
|
|
2024-10-10 05:39:09 +00:00
|
|
|
if (!stdLearnings.length) {
|
|
|
|
|
return response(
|
2024-12-02 07:27:15 +00:00
|
|
|
200,
|
2024-10-10 05:39:09 +00:00
|
|
|
null,
|
|
|
|
|
"No learning history found for this user",
|
|
|
|
|
res
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-17 01:41:44 +00:00
|
|
|
const formattedLearnings = await Promise.all(
|
2024-09-13 13:03:35 +00:00
|
|
|
stdLearnings.map(async (learning) => {
|
|
|
|
|
let nextLevelName = null;
|
|
|
|
|
if (learning.NEXT_LEARNING) {
|
|
|
|
|
const nextLevel = await models.Level.findOne({
|
|
|
|
|
where: { ID_LEVEL: learning.NEXT_LEARNING },
|
|
|
|
|
});
|
|
|
|
|
if (nextLevel) {
|
|
|
|
|
nextLevelName = nextLevel.NAME_LEVEL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
2024-11-28 03:03:06 +00:00
|
|
|
ID_STUDENT_LEARNING: learning.ID_STUDENT_LEARNING,
|
2024-09-13 13:03:35 +00:00
|
|
|
SCORE: learning.SCORE,
|
2024-10-10 05:39:09 +00:00
|
|
|
CURRENT_LEVEL: learning.level?.NAME_LEVEL || "No level",
|
2024-09-13 13:03:35 +00:00
|
|
|
NEXT_LEVEL: nextLevelName,
|
|
|
|
|
STUDENT_FINISH: learning.STUDENT_FINISH,
|
2024-10-10 05:39:09 +00:00
|
|
|
TOPIC_NAME: learning.level?.levelTopic?.NAME_TOPIC || "No topic",
|
|
|
|
|
SECTION_NAME:
|
|
|
|
|
learning.level?.levelTopic?.topicSection?.NAME_SECTION ||
|
|
|
|
|
"No section",
|
2024-10-17 01:41:44 +00:00
|
|
|
IS_PASS: learning.IS_PASS,
|
2024-09-13 13:03:35 +00:00
|
|
|
};
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
|
2024-10-17 01:41:44 +00:00
|
|
|
const paginatedLearnings = formattedLearnings.slice(
|
|
|
|
|
(page - 1) * limit,
|
|
|
|
|
page * limit
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const totalPages = Math.ceil(formattedLearnings.length / limit);
|
|
|
|
|
const currentPage = parseInt(page);
|
|
|
|
|
|
|
|
|
|
response(
|
|
|
|
|
200,
|
|
|
|
|
{
|
|
|
|
|
history: paginatedLearnings,
|
|
|
|
|
currentPage,
|
|
|
|
|
totalPages,
|
|
|
|
|
totalItems: formattedLearnings.length,
|
|
|
|
|
},
|
|
|
|
|
"Learning history retrieved successfully",
|
|
|
|
|
res
|
|
|
|
|
);
|
2024-09-13 13:03:35 +00:00
|
|
|
} catch (error) {
|
2024-10-10 05:39:09 +00:00
|
|
|
console.error(error);
|
2024-09-13 13:03:35 +00:00
|
|
|
response(500, null, "Internal Server Error", res);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const learningHistoryBySectionId = async (req, res) => {
|
2024-10-17 01:41:44 +00:00
|
|
|
const { page = 1, limit = 10 } = req.query;
|
|
|
|
|
|
2024-09-13 13:03:35 +00:00
|
|
|
try {
|
|
|
|
|
if (!req.user) {
|
|
|
|
|
return response(401, null, "User not authenticated", res);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const { ID } = req.user;
|
|
|
|
|
const { sectionId } = req.params;
|
|
|
|
|
|
|
|
|
|
const stdLearnings = await models.StdLearning.findAll({
|
2024-10-24 02:46:11 +00:00
|
|
|
where: {
|
|
|
|
|
ID,
|
|
|
|
|
STUDENT_FINISH: {
|
|
|
|
|
[models.Sequelize.Op.ne]: null,
|
|
|
|
|
},
|
|
|
|
|
SCORE: {
|
|
|
|
|
[models.Sequelize.Op.ne]: null,
|
|
|
|
|
},
|
|
|
|
|
NEXT_LEARNING: {
|
|
|
|
|
[models.Sequelize.Op.ne]: null,
|
|
|
|
|
},
|
2024-10-24 02:19:12 +00:00
|
|
|
},
|
2024-09-13 13:03:35 +00:00
|
|
|
include: [
|
|
|
|
|
{
|
|
|
|
|
model: models.Level,
|
|
|
|
|
as: "level",
|
|
|
|
|
include: [
|
|
|
|
|
{
|
|
|
|
|
model: models.Topic,
|
|
|
|
|
as: "levelTopic",
|
|
|
|
|
include: [
|
|
|
|
|
{
|
|
|
|
|
model: models.Section,
|
|
|
|
|
as: "topicSection",
|
|
|
|
|
where: { ID_SECTION: sectionId },
|
2024-10-10 05:39:09 +00:00
|
|
|
required: true,
|
2024-09-13 13:03:35 +00:00
|
|
|
},
|
|
|
|
|
],
|
2024-10-10 05:39:09 +00:00
|
|
|
required: true,
|
2024-09-13 13:03:35 +00:00
|
|
|
},
|
|
|
|
|
],
|
2024-10-10 05:39:09 +00:00
|
|
|
required: true,
|
2024-09-13 13:03:35 +00:00
|
|
|
},
|
|
|
|
|
],
|
2024-10-18 07:02:54 +00:00
|
|
|
order: [["STUDENT_FINISH", "DESC"]],
|
2024-09-13 13:03:35 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!stdLearnings.length) {
|
|
|
|
|
return response(
|
2024-12-02 07:27:15 +00:00
|
|
|
200,
|
2024-09-13 13:03:35 +00:00
|
|
|
null,
|
|
|
|
|
"No learning history found for the specified section",
|
|
|
|
|
res
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-17 01:41:44 +00:00
|
|
|
const formattedLearnings = await Promise.all(
|
2024-09-13 13:03:35 +00:00
|
|
|
stdLearnings.map(async (learning) => {
|
|
|
|
|
let nextLevelName = null;
|
|
|
|
|
if (learning.NEXT_LEARNING) {
|
|
|
|
|
const nextLevel = await models.Level.findOne({
|
|
|
|
|
where: { ID_LEVEL: learning.NEXT_LEARNING },
|
|
|
|
|
});
|
|
|
|
|
if (nextLevel) {
|
|
|
|
|
nextLevelName = nextLevel.NAME_LEVEL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
2024-11-28 03:03:06 +00:00
|
|
|
ID_STUDENT_LEARNING: learning.ID_STUDENT_LEARNING,
|
2024-09-13 13:03:35 +00:00
|
|
|
SCORE: learning.SCORE,
|
2024-10-10 05:39:09 +00:00
|
|
|
CURRENT_LEVEL: learning.level?.NAME_LEVEL || "No level",
|
2024-09-13 13:03:35 +00:00
|
|
|
NEXT_LEVEL: nextLevelName,
|
|
|
|
|
STUDENT_FINISH: learning.STUDENT_FINISH,
|
2024-10-10 05:39:09 +00:00
|
|
|
TOPIC_NAME: learning.level?.levelTopic?.NAME_TOPIC || "No topic",
|
|
|
|
|
SECTION_NAME:
|
|
|
|
|
learning.level?.levelTopic?.topicSection?.NAME_SECTION ||
|
|
|
|
|
"No section",
|
2024-10-17 01:41:44 +00:00
|
|
|
IS_PASS: learning.IS_PASS,
|
2024-09-13 13:03:35 +00:00
|
|
|
};
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
|
2024-10-17 01:41:44 +00:00
|
|
|
const paginatedLearnings = formattedLearnings.slice(
|
|
|
|
|
(page - 1) * limit,
|
|
|
|
|
page * limit
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const totalPages = Math.ceil(formattedLearnings.length / limit);
|
|
|
|
|
const currentPage = parseInt(page);
|
|
|
|
|
|
|
|
|
|
response(
|
|
|
|
|
200,
|
|
|
|
|
{
|
|
|
|
|
history: paginatedLearnings,
|
|
|
|
|
currentPage,
|
|
|
|
|
totalPages,
|
|
|
|
|
totalItems: formattedLearnings.length,
|
|
|
|
|
},
|
|
|
|
|
"Learning history retrieved successfully",
|
|
|
|
|
res
|
|
|
|
|
);
|
2024-09-13 13:03:35 +00:00
|
|
|
} catch (error) {
|
|
|
|
|
console.log(error);
|
|
|
|
|
response(500, null, "Internal Server Error", res);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const learningHistoryByTopicId = async (req, res) => {
|
2024-10-17 01:41:44 +00:00
|
|
|
const { page = 1, limit = 10 } = req.query;
|
|
|
|
|
|
2024-09-13 13:03:35 +00:00
|
|
|
try {
|
|
|
|
|
if (!req.user) {
|
|
|
|
|
return response(401, null, "User not authenticated", res);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const { ID } = req.user;
|
|
|
|
|
const { topicId } = req.params;
|
|
|
|
|
|
|
|
|
|
if (!topicId) {
|
|
|
|
|
return response(400, null, "Topic ID is required", res);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const stdLearnings = await models.StdLearning.findAll({
|
2024-10-24 02:46:11 +00:00
|
|
|
where: {
|
|
|
|
|
ID,
|
|
|
|
|
STUDENT_FINISH: {
|
|
|
|
|
[models.Sequelize.Op.ne]: null,
|
|
|
|
|
},
|
|
|
|
|
SCORE: {
|
|
|
|
|
[models.Sequelize.Op.ne]: null,
|
|
|
|
|
},
|
|
|
|
|
NEXT_LEARNING: {
|
|
|
|
|
[models.Sequelize.Op.ne]: null,
|
|
|
|
|
},
|
2024-10-24 02:19:12 +00:00
|
|
|
},
|
2024-09-13 13:03:35 +00:00
|
|
|
include: [
|
|
|
|
|
{
|
|
|
|
|
model: models.Level,
|
|
|
|
|
as: "level",
|
|
|
|
|
include: [
|
|
|
|
|
{
|
|
|
|
|
model: models.Topic,
|
|
|
|
|
as: "levelTopic",
|
|
|
|
|
where: { ID_TOPIC: topicId },
|
2024-10-10 05:39:09 +00:00
|
|
|
required: true,
|
2024-09-13 13:03:35 +00:00
|
|
|
include: [
|
|
|
|
|
{
|
|
|
|
|
model: models.Section,
|
|
|
|
|
as: "topicSection",
|
2024-10-10 05:39:09 +00:00
|
|
|
required: true,
|
2024-09-13 13:03:35 +00:00
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
],
|
2024-10-10 05:39:09 +00:00
|
|
|
required: true,
|
2024-09-13 13:03:35 +00:00
|
|
|
},
|
|
|
|
|
],
|
2024-10-18 07:02:54 +00:00
|
|
|
order: [["STUDENT_FINISH", "DESC"]],
|
2024-09-13 13:03:35 +00:00
|
|
|
});
|
|
|
|
|
|
2024-10-10 05:39:09 +00:00
|
|
|
if (!stdLearnings.length) {
|
|
|
|
|
return response(
|
2024-12-02 07:27:15 +00:00
|
|
|
200,
|
2024-10-10 05:39:09 +00:00
|
|
|
null,
|
|
|
|
|
"No learning history found for the specified topic",
|
|
|
|
|
res
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-17 01:41:44 +00:00
|
|
|
const formattedLearnings = await Promise.all(
|
2024-09-13 13:03:35 +00:00
|
|
|
stdLearnings.map(async (learning) => {
|
|
|
|
|
let nextLevelName = null;
|
|
|
|
|
if (learning.NEXT_LEARNING) {
|
|
|
|
|
const nextLevel = await models.Level.findOne({
|
|
|
|
|
where: { ID_LEVEL: learning.NEXT_LEARNING },
|
|
|
|
|
});
|
|
|
|
|
if (nextLevel) {
|
|
|
|
|
nextLevelName = nextLevel.NAME_LEVEL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
2024-11-28 03:03:06 +00:00
|
|
|
ID_STUDENT_LEARNING: learning.ID_STUDENT_LEARNING,
|
2024-09-13 13:03:35 +00:00
|
|
|
SCORE: learning.SCORE,
|
2024-10-10 05:39:09 +00:00
|
|
|
CURRENT_LEVEL: learning.level?.NAME_LEVEL || "No level",
|
2024-09-13 13:03:35 +00:00
|
|
|
NEXT_LEVEL: nextLevelName,
|
|
|
|
|
STUDENT_FINISH: learning.STUDENT_FINISH,
|
2024-10-10 05:39:09 +00:00
|
|
|
TOPIC_NAME: learning.level?.levelTopic?.NAME_TOPIC || "No topic",
|
|
|
|
|
SECTION_NAME:
|
|
|
|
|
learning.level?.levelTopic?.topicSection?.NAME_SECTION ||
|
|
|
|
|
"No section",
|
2024-10-17 01:41:44 +00:00
|
|
|
IS_PASS: learning.IS_PASS,
|
2024-09-13 13:03:35 +00:00
|
|
|
};
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
|
2024-10-17 01:41:44 +00:00
|
|
|
const paginatedLearnings = formattedLearnings.slice(
|
|
|
|
|
(page - 1) * limit,
|
|
|
|
|
page * limit
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const totalPages = Math.ceil(formattedLearnings.length / limit);
|
|
|
|
|
const currentPage = parseInt(page);
|
|
|
|
|
|
|
|
|
|
response(
|
|
|
|
|
200,
|
|
|
|
|
{
|
|
|
|
|
history: paginatedLearnings,
|
|
|
|
|
currentPage,
|
|
|
|
|
totalPages,
|
|
|
|
|
totalItems: formattedLearnings.length,
|
|
|
|
|
},
|
|
|
|
|
"Learning history retrieved successfully",
|
|
|
|
|
res
|
|
|
|
|
);
|
2024-09-13 13:03:35 +00:00
|
|
|
} catch (error) {
|
|
|
|
|
console.error(error);
|
|
|
|
|
response(500, null, "Internal Server Error", res);
|
|
|
|
|
}
|
|
|
|
|
};
|
2024-09-25 03:57:47 +00:00
|
|
|
|
2024-10-18 07:02:54 +00:00
|
|
|
export const recentStudentActivities = async (req, res) => {
|
2024-11-28 03:03:06 +00:00
|
|
|
const { page = 1, limit = 5, search = "" } = req.query;
|
|
|
|
|
|
2024-10-18 07:02:54 +00:00
|
|
|
try {
|
2024-11-28 03:03:06 +00:00
|
|
|
const { count, rows: stdLearnings } =
|
|
|
|
|
await models.StdLearning.findAndCountAll({
|
|
|
|
|
where: {
|
|
|
|
|
STUDENT_FINISH: {
|
|
|
|
|
[models.Sequelize.Op.ne]: null,
|
|
|
|
|
},
|
|
|
|
|
SCORE: {
|
|
|
|
|
[models.Sequelize.Op.ne]: null,
|
|
|
|
|
},
|
|
|
|
|
NEXT_LEARNING: {
|
|
|
|
|
[models.Sequelize.Op.ne]: null,
|
|
|
|
|
},
|
|
|
|
|
...(search && {
|
|
|
|
|
[models.Sequelize.Op.or]: [
|
|
|
|
|
{
|
|
|
|
|
SCORE: {
|
|
|
|
|
[models.Sequelize.Op.like]: `%${search}%`,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"$learningUser.NAME_USERS$": {
|
|
|
|
|
[models.Sequelize.Op.like]: `%${search}%`,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"$learningUser.students.NISN$": {
|
|
|
|
|
[models.Sequelize.Op.like]: `%${search}%`,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"$learningUser.students.studentClass.NAME_CLASS$": {
|
|
|
|
|
[models.Sequelize.Op.like]: `%${search}%`,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"$level.levelTopic.topicSection.NAME_SECTION$": {
|
|
|
|
|
[models.Sequelize.Op.like]: `%${search}%`,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"$level.levelTopic.NAME_TOPIC$": {
|
|
|
|
|
[models.Sequelize.Op.like]: `%${search}%`,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"$level.NAME_LEVEL$": {
|
|
|
|
|
[models.Sequelize.Op.like]: `%${search}%`,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
}),
|
2024-10-24 02:46:11 +00:00
|
|
|
},
|
2024-11-28 03:03:06 +00:00
|
|
|
include: [
|
|
|
|
|
{
|
|
|
|
|
model: models.User,
|
|
|
|
|
as: "learningUser",
|
|
|
|
|
attributes: ["ID", "NAME_USERS"],
|
|
|
|
|
include: [
|
|
|
|
|
{
|
|
|
|
|
model: models.Student,
|
|
|
|
|
as: "students",
|
|
|
|
|
attributes: ["NISN"],
|
|
|
|
|
include: [
|
|
|
|
|
{
|
|
|
|
|
model: models.Class,
|
|
|
|
|
as: "studentClass",
|
|
|
|
|
attributes: ["NAME_CLASS"],
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
model: models.Level,
|
|
|
|
|
as: "level",
|
|
|
|
|
attributes: ["NAME_LEVEL"],
|
|
|
|
|
include: [
|
|
|
|
|
{
|
|
|
|
|
model: models.Topic,
|
|
|
|
|
as: "levelTopic",
|
|
|
|
|
attributes: ["NAME_TOPIC"],
|
|
|
|
|
include: [
|
|
|
|
|
{
|
|
|
|
|
model: models.Section,
|
|
|
|
|
as: "topicSection",
|
|
|
|
|
attributes: ["NAME_SECTION"],
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
order: [["STUDENT_FINISH", "DESC"]],
|
|
|
|
|
distinct: true,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const recentActivities = stdLearnings.map((learning) => ({
|
|
|
|
|
ID_STUDENT_LEARNING: learning.ID_STUDENT_LEARNING || "N/A",
|
|
|
|
|
NISN: learning.learningUser?.students?.NISN || "N/A",
|
|
|
|
|
NAME_USERS: learning.learningUser?.NAME_USERS || "N/A",
|
|
|
|
|
NAME_CLASS:
|
|
|
|
|
learning.learningUser?.students?.studentClass?.NAME_CLASS || null,
|
|
|
|
|
NAME_SECTION:
|
|
|
|
|
learning.level?.levelTopic?.topicSection?.NAME_SECTION || "N/A",
|
|
|
|
|
NAME_TOPIC: learning.level?.levelTopic?.NAME_TOPIC || "N/A",
|
|
|
|
|
NAME_LEVEL: learning.level?.NAME_LEVEL || "N/A",
|
|
|
|
|
SCORE: learning.SCORE || "N/A",
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
if (!recentActivities.length) {
|
2024-12-02 07:27:15 +00:00
|
|
|
return res.status(200).json({ message: "No recent activities found" });
|
2024-11-28 03:03:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const paginatedActivities = recentActivities.slice(
|
|
|
|
|
(page - 1) * limit,
|
|
|
|
|
page * limit
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const totalPages = Math.ceil(count / limit);
|
|
|
|
|
const currentPage = parseInt(page);
|
|
|
|
|
|
|
|
|
|
response(
|
|
|
|
|
200,
|
|
|
|
|
{
|
|
|
|
|
studentActivities: paginatedActivities,
|
|
|
|
|
currentPage,
|
|
|
|
|
totalPages,
|
|
|
|
|
totalItems: count,
|
2024-10-24 02:46:11 +00:00
|
|
|
},
|
2024-11-28 03:03:06 +00:00
|
|
|
"Recent student activities retrieved successfully",
|
|
|
|
|
res
|
|
|
|
|
);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error(error);
|
|
|
|
|
response(500, null, "Internal Server Error", res);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const recentStudentActivitiesByClassId = async (req, res) => {
|
|
|
|
|
const { page = 1, limit = 5, search = "" } = req.query;
|
2024-12-05 07:10:35 +00:00
|
|
|
const { idClass } = req.params;
|
2024-11-28 03:03:06 +00:00
|
|
|
|
|
|
|
|
try {
|
2024-12-05 07:10:35 +00:00
|
|
|
const classData = await models.Class.findByPk(idClass);
|
2024-11-28 03:03:06 +00:00
|
|
|
|
|
|
|
|
if (!classData) {
|
|
|
|
|
return response(404, null, "Class not found", res);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const { count, rows: stdLearnings } =
|
|
|
|
|
await models.StdLearning.findAndCountAll({
|
|
|
|
|
where: {
|
|
|
|
|
STUDENT_FINISH: {
|
|
|
|
|
[models.Sequelize.Op.ne]: null,
|
|
|
|
|
},
|
|
|
|
|
SCORE: {
|
|
|
|
|
[models.Sequelize.Op.ne]: null,
|
|
|
|
|
},
|
|
|
|
|
NEXT_LEARNING: {
|
|
|
|
|
[models.Sequelize.Op.ne]: null,
|
|
|
|
|
},
|
2024-12-05 07:10:35 +00:00
|
|
|
...(idClass && {
|
|
|
|
|
"$learningUser.students.ID_CLASS$": idClass,
|
2024-11-28 03:03:06 +00:00
|
|
|
}),
|
|
|
|
|
...(search && {
|
|
|
|
|
[models.Sequelize.Op.or]: [
|
|
|
|
|
{
|
|
|
|
|
SCORE: {
|
|
|
|
|
[models.Sequelize.Op.like]: `%${search}%`,
|
2024-10-18 07:02:54 +00:00
|
|
|
},
|
2024-11-28 03:03:06 +00:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"$learningUser.NAME_USERS$": {
|
|
|
|
|
[models.Sequelize.Op.like]: `%${search}%`,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"$learningUser.students.NISN$": {
|
|
|
|
|
[models.Sequelize.Op.like]: `%${search}%`,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"$learningUser.students.studentClass.NAME_CLASS$": {
|
|
|
|
|
[models.Sequelize.Op.like]: `%${search}%`,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"$level.levelTopic.topicSection.NAME_SECTION$": {
|
|
|
|
|
[models.Sequelize.Op.like]: `%${search}%`,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"$level.levelTopic.NAME_TOPIC$": {
|
|
|
|
|
[models.Sequelize.Op.like]: `%${search}%`,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"$level.NAME_LEVEL$": {
|
|
|
|
|
[models.Sequelize.Op.like]: `%${search}%`,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
}),
|
2024-10-18 07:02:54 +00:00
|
|
|
},
|
2024-11-28 03:03:06 +00:00
|
|
|
include: [
|
|
|
|
|
{
|
|
|
|
|
model: models.User,
|
|
|
|
|
as: "learningUser",
|
|
|
|
|
attributes: ["ID", "NAME_USERS"],
|
|
|
|
|
include: [
|
|
|
|
|
{
|
|
|
|
|
model: models.Student,
|
|
|
|
|
as: "students",
|
|
|
|
|
attributes: ["NISN", "ID_CLASS"],
|
|
|
|
|
include: [
|
|
|
|
|
{
|
|
|
|
|
model: models.Class,
|
|
|
|
|
as: "studentClass",
|
|
|
|
|
attributes: ["NAME_CLASS"],
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
model: models.Level,
|
|
|
|
|
as: "level",
|
|
|
|
|
attributes: ["NAME_LEVEL"],
|
|
|
|
|
include: [
|
|
|
|
|
{
|
|
|
|
|
model: models.Topic,
|
|
|
|
|
as: "levelTopic",
|
|
|
|
|
attributes: ["NAME_TOPIC"],
|
|
|
|
|
include: [
|
|
|
|
|
{
|
|
|
|
|
model: models.Section,
|
|
|
|
|
as: "topicSection",
|
|
|
|
|
attributes: ["NAME_SECTION"],
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
order: [["STUDENT_FINISH", "DESC"]],
|
|
|
|
|
distinct: true,
|
|
|
|
|
});
|
2024-10-18 07:02:54 +00:00
|
|
|
|
|
|
|
|
const recentActivities = stdLearnings.map((learning) => ({
|
2024-11-28 03:03:06 +00:00
|
|
|
ID_STUDENT_LEARNING: learning.ID_STUDENT_LEARNING || "N/A",
|
2024-10-18 07:02:54 +00:00
|
|
|
NISN: learning.learningUser?.students?.NISN || "N/A",
|
|
|
|
|
NAME_USERS: learning.learningUser?.NAME_USERS || "N/A",
|
2024-11-28 03:03:06 +00:00
|
|
|
NAME_CLASS:
|
|
|
|
|
learning.learningUser?.students?.studentClass?.NAME_CLASS || null,
|
2024-10-18 07:02:54 +00:00
|
|
|
NAME_SECTION:
|
|
|
|
|
learning.level?.levelTopic?.topicSection?.NAME_SECTION || "N/A",
|
|
|
|
|
NAME_TOPIC: learning.level?.levelTopic?.NAME_TOPIC || "N/A",
|
|
|
|
|
NAME_LEVEL: learning.level?.NAME_LEVEL || "N/A",
|
|
|
|
|
SCORE: learning.SCORE || "N/A",
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
if (!recentActivities.length) {
|
2024-12-02 07:27:15 +00:00
|
|
|
return res.status(200).json({ message: "No recent activities found" });
|
2024-10-18 07:02:54 +00:00
|
|
|
}
|
|
|
|
|
|
2024-11-28 03:03:06 +00:00
|
|
|
const paginatedActivities = recentActivities.slice(
|
|
|
|
|
(page - 1) * limit,
|
|
|
|
|
page * limit
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const totalPages = Math.ceil(count / limit);
|
|
|
|
|
const currentPage = parseInt(page);
|
|
|
|
|
|
|
|
|
|
response(
|
|
|
|
|
200,
|
|
|
|
|
{
|
|
|
|
|
studentActivities: paginatedActivities,
|
|
|
|
|
currentPage,
|
|
|
|
|
totalPages,
|
|
|
|
|
totalItems: count,
|
|
|
|
|
},
|
|
|
|
|
"Recent student activities retrieved successfully",
|
|
|
|
|
res
|
|
|
|
|
);
|
2024-10-18 07:02:54 +00:00
|
|
|
} catch (error) {
|
|
|
|
|
console.error(error);
|
2024-11-28 03:03:06 +00:00
|
|
|
response(500, null, "Internal Server Error", res);
|
2024-10-18 07:02:54 +00:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2024-09-25 03:57:47 +00:00
|
|
|
export const getLastCreatedStdLearningByLevelId = async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
if (!req.user) {
|
|
|
|
|
return response(401, null, "User not authenticated", res);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const { ID } = req.user;
|
|
|
|
|
const { levelId } = req.params;
|
|
|
|
|
|
|
|
|
|
if (!levelId) {
|
|
|
|
|
return response(400, null, "Level ID is required", res);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const latestStdLearning = await models.StdLearning.findOne({
|
|
|
|
|
where: {
|
|
|
|
|
ID: ID,
|
|
|
|
|
ID_LEVEL: levelId,
|
|
|
|
|
},
|
|
|
|
|
order: [["STUDENT_START", "DESC"]],
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!latestStdLearning) {
|
|
|
|
|
return response(404, null, "No StdLearning data found", res);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
response(200, latestStdLearning, "Success", res);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error(error);
|
|
|
|
|
response(500, null, "Internal Server Error", res);
|
|
|
|
|
}
|
|
|
|
|
};
|