feat: monitoring by class function
This commit is contained in:
parent
2e669bc281
commit
522090886c
|
|
@ -207,7 +207,9 @@ export const monitoringStudentsProgress = async (req, res) => {
|
|||
}));
|
||||
|
||||
if (sort === "nisn") {
|
||||
formattedResult.sort((a, b) => a.NISN.localeCompare(b.NISN));
|
||||
formattedResult.sort((a, b) =>
|
||||
String(a.NISN).localeCompare(String(b.NISN))
|
||||
);
|
||||
} else if (sort === "name") {
|
||||
formattedResult.sort((a, b) => a.NAME_USERS.localeCompare(b.NAME_USERS));
|
||||
} else if (sort === "section") {
|
||||
|
|
@ -332,19 +334,11 @@ export const monitoringStudentProgressById = async (req, res) => {
|
|||
{
|
||||
FEEDBACK_STUDENT: { [models.Op.like]: `%${search}%` },
|
||||
},
|
||||
// Search student_start dan student_finish belum bisa
|
||||
{
|
||||
[models.Sequelize.fn('DATE_FORMAT', models.Sequelize.col('STUDENT_START'), '%Y-%m-%d')]: {
|
||||
[models.Op.like]: `%${search}%`,
|
||||
},
|
||||
},
|
||||
{
|
||||
[models.Sequelize.fn('DATE_FORMAT', models.Sequelize.col('STUDENT_FINISH'), '%Y-%m-%d')]: {
|
||||
[models.Op.like]: `%${search}%`,
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
STUDENT_FINISH: {
|
||||
[models.Sequelize.Op.ne]: null,
|
||||
},
|
||||
},
|
||||
include: [
|
||||
{
|
||||
|
|
@ -575,3 +569,461 @@ export const getMonitoringByTopicId = async (req, res) => {
|
|||
response(500, null, "Error retrieving monitoring data!", res);
|
||||
}
|
||||
};
|
||||
|
||||
export const getClassMonitoringByClassId = async (req, res) => {
|
||||
const { classId } = req.params;
|
||||
const { page = 1, limit = 10, search = "", sort = "time" } = req.query;
|
||||
|
||||
try {
|
||||
const searchCondition = search
|
||||
? {
|
||||
[models.Op.or]: [
|
||||
{
|
||||
"$stdLearningMonitoring.level.levelTopic.topicSection.NAME_SECTION$":
|
||||
{ [models.Op.like]: `%${search}%` },
|
||||
},
|
||||
{
|
||||
"$stdLearningMonitoring.level.levelTopic.NAME_TOPIC$": {
|
||||
[models.Op.like]: `%${search}%`,
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
: {};
|
||||
|
||||
const { rows: monitoringRecords, count } =
|
||||
await models.Monitoring.findAndCountAll({
|
||||
where: { ID_CLASS: classId, ...searchCondition },
|
||||
include: [
|
||||
{
|
||||
model: models.StdLearning,
|
||||
as: "stdLearningMonitoring",
|
||||
include: [
|
||||
{
|
||||
model: models.Level,
|
||||
as: "level",
|
||||
attributes: ["ID_LEVEL"],
|
||||
include: [
|
||||
{
|
||||
model: models.Topic,
|
||||
as: "levelTopic",
|
||||
attributes: ["ID_TOPIC", "NAME_TOPIC"],
|
||||
include: [
|
||||
{
|
||||
model: models.Section,
|
||||
as: "topicSection",
|
||||
attributes: ["ID_SECTION", "NAME_SECTION"],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
distinct: true,
|
||||
});
|
||||
|
||||
if (!monitoringRecords || monitoringRecords.length === 0) {
|
||||
return response(
|
||||
404,
|
||||
null,
|
||||
"No monitoring data found for this class!",
|
||||
res
|
||||
);
|
||||
}
|
||||
|
||||
const uniqueRecords = new Map();
|
||||
monitoringRecords.forEach((monitoring) => {
|
||||
const sectionId =
|
||||
monitoring.stdLearningMonitoring?.level?.levelTopic?.topicSection
|
||||
?.ID_SECTION;
|
||||
const sectionName =
|
||||
monitoring.stdLearningMonitoring?.level?.levelTopic?.topicSection
|
||||
?.NAME_SECTION;
|
||||
const topicId =
|
||||
monitoring.stdLearningMonitoring?.level?.levelTopic?.ID_TOPIC;
|
||||
const topicName =
|
||||
monitoring.stdLearningMonitoring?.level?.levelTopic?.NAME_TOPIC;
|
||||
|
||||
const key = `${sectionId}-${topicId}`;
|
||||
if (!uniqueRecords.has(key)) {
|
||||
uniqueRecords.set(key, {
|
||||
ID_SECTION: sectionId,
|
||||
ID_TOPIC: topicId,
|
||||
NAME_SECTION: sectionName,
|
||||
NAME_TOPIC: topicName,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const sortedRecords = Array.from(uniqueRecords.values());
|
||||
|
||||
if (sort === "section") {
|
||||
sortedRecords.sort((a, b) =>
|
||||
a.NAME_SECTION.localeCompare(b.NAME_SECTION)
|
||||
);
|
||||
} else if (sort === "topic") {
|
||||
sortedRecords.sort((a, b) => a.NAME_TOPIC.localeCompare(b.NAME_TOPIC));
|
||||
}
|
||||
|
||||
const paginatedRecords = sortedRecords.slice(
|
||||
(page - 1) * limit,
|
||||
page * limit
|
||||
);
|
||||
|
||||
const totalPages = Math.ceil(uniqueRecords.size / limit);
|
||||
|
||||
const paginatedResult = {
|
||||
data: paginatedRecords,
|
||||
currentPage: parseInt(page),
|
||||
totalPages,
|
||||
totalItems: uniqueRecords.size,
|
||||
};
|
||||
|
||||
response(
|
||||
200,
|
||||
paginatedResult,
|
||||
"Success retrieving unique monitoring section and topic data!",
|
||||
res
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Error in getClassMonitoringByClassId:", error);
|
||||
response(
|
||||
500,
|
||||
null,
|
||||
"Error retrieving monitoring section and topic data!",
|
||||
res
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const getClassMonitoringDataByClassAndTopic = async (req, res) => {
|
||||
const { ID_CLASS, ID_TOPIC } = req.body;
|
||||
const { page = 1, limit = 10, search = "", sort = "finish" } = req.query;
|
||||
|
||||
try {
|
||||
const classData = await models.Class.findOne({
|
||||
where: { ID_CLASS: ID_CLASS },
|
||||
attributes: ["ID_CLASS", "NAME_CLASS"],
|
||||
});
|
||||
|
||||
if (!classData) {
|
||||
return response(404, null, "Class not found!", res);
|
||||
}
|
||||
|
||||
const className = classData.NAME_CLASS;
|
||||
const classId = classData.ID_CLASS;
|
||||
|
||||
const topicData = await models.Topic.findOne({
|
||||
where: { ID_TOPIC: ID_TOPIC },
|
||||
include: [
|
||||
{
|
||||
model: models.Section,
|
||||
as: "topicSection",
|
||||
attributes: ["ID_SECTION", "NAME_SECTION"],
|
||||
},
|
||||
],
|
||||
attributes: ["ID_TOPIC", "NAME_TOPIC"],
|
||||
});
|
||||
|
||||
if (!topicData) {
|
||||
return response(404, null, "Topic not found!", res);
|
||||
}
|
||||
|
||||
const topicName = topicData.NAME_TOPIC;
|
||||
const topicId = topicData.ID_TOPIC;
|
||||
const sectionName = topicData.topicSection?.NAME_SECTION;
|
||||
const sectionId = topicData.topicSection?.ID_SECTION;
|
||||
|
||||
const monitoringData = await models.Monitoring.findAll({
|
||||
where: { ID_CLASS: ID_CLASS },
|
||||
include: [
|
||||
{
|
||||
model: models.StdLearning,
|
||||
as: "stdLearningMonitoring",
|
||||
required: true,
|
||||
attributes: ["ID", "ID_LEVEL"],
|
||||
include: [
|
||||
{
|
||||
model: models.Level,
|
||||
as: "level",
|
||||
attributes: ["ID_LEVEL", "ID_TOPIC"],
|
||||
where: { ID_TOPIC: ID_TOPIC },
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
if (!monitoringData || monitoringData.length === 0) {
|
||||
const result = {
|
||||
ID_CLASS: classId,
|
||||
ID_SECTION: sectionId,
|
||||
ID_TOPIC: topicId,
|
||||
NAME_CLASS: className,
|
||||
NAME_SECTION: sectionName,
|
||||
NAME_TOPIC: topicName,
|
||||
};
|
||||
return response(
|
||||
200,
|
||||
result,
|
||||
"No monitoring data found for this class and topic!",
|
||||
res
|
||||
);
|
||||
}
|
||||
|
||||
const userIds = monitoringData.map(
|
||||
(monitoring) => monitoring.stdLearningMonitoring.ID
|
||||
);
|
||||
|
||||
const searchCondition = search
|
||||
? {
|
||||
[models.Sequelize.Op.or]: [
|
||||
{
|
||||
"$learningUser.NAME_USERS$": {
|
||||
[models.Sequelize.Op.like]: `%${search}%`,
|
||||
},
|
||||
},
|
||||
{
|
||||
"$learningUser.students.NISN$": {
|
||||
[models.Sequelize.Op.like]: `%${search}%`,
|
||||
},
|
||||
},
|
||||
{
|
||||
"$level.NAME_LEVEL$": {
|
||||
[models.Sequelize.Op.like]: `%${search}%`,
|
||||
},
|
||||
},
|
||||
{ $SCORE$: { [models.Sequelize.Op.like]: `%${search}%` } },
|
||||
{
|
||||
$FEEDBACK_STUDENT$: { [models.Sequelize.Op.like]: `%${search}%` },
|
||||
},
|
||||
],
|
||||
}
|
||||
: {};
|
||||
|
||||
const { rows: allStdLearning, count } =
|
||||
await models.StdLearning.findAndCountAll({
|
||||
where: {
|
||||
ID_LEVEL: {
|
||||
[models.Sequelize.Op.in]: (
|
||||
await models.Level.findAll({
|
||||
where: { ID_TOPIC: ID_TOPIC },
|
||||
attributes: ["ID_LEVEL"],
|
||||
})
|
||||
).map((level) => level.ID_LEVEL),
|
||||
},
|
||||
ID: {
|
||||
[models.Sequelize.Op.in]: userIds,
|
||||
},
|
||||
STUDENT_FINISH: {
|
||||
[models.Sequelize.Op.ne]: null,
|
||||
},
|
||||
...searchCondition,
|
||||
},
|
||||
include: [
|
||||
{
|
||||
model: models.Level,
|
||||
as: "level",
|
||||
attributes: ["NAME_LEVEL", "ID_TOPIC"],
|
||||
},
|
||||
{
|
||||
model: models.User,
|
||||
as: "learningUser",
|
||||
attributes: ["NAME_USERS"],
|
||||
include: [
|
||||
{
|
||||
model: models.Student,
|
||||
as: "students",
|
||||
attributes: ["NISN"],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
attributes: [
|
||||
"SCORE",
|
||||
"FEEDBACK_STUDENT",
|
||||
"STUDENT_START",
|
||||
"STUDENT_FINISH",
|
||||
],
|
||||
});
|
||||
|
||||
const sortedRecords = allStdLearning.sort((a, b) => {
|
||||
if (sort === "nisn") {
|
||||
return String(a.learningUser.students.NISN).localeCompare(
|
||||
String(b.learningUser.students.NISN)
|
||||
);
|
||||
} else if (sort === "user") {
|
||||
return a.learningUser.NAME_USERS.localeCompare(
|
||||
b.learningUser.NAME_USERS
|
||||
);
|
||||
} else if (sort === "level") {
|
||||
return a.level.NAME_LEVEL.localeCompare(b.level.NAME_LEVEL);
|
||||
} else if (sort === "score") {
|
||||
return b.SCORE - a.SCORE;
|
||||
} else if (sort === "feedback") {
|
||||
return a.FEEDBACK_STUDENT.localeCompare(b.FEEDBACK_STUDENT);
|
||||
} else if (sort === "start") {
|
||||
return new Date(a.STUDENT_START) - new Date(b.STUDENT_START);
|
||||
}
|
||||
// Default sorting by student finish
|
||||
return new Date(a.STUDENT_FINISH) - new Date(b.STUDENT_FINISH);
|
||||
});
|
||||
|
||||
const paginatedRecords = sortedRecords.slice(
|
||||
(page - 1) * limit,
|
||||
page * limit
|
||||
);
|
||||
const totalPages = Math.ceil(count / limit);
|
||||
|
||||
const formattedData = paginatedRecords.map((stdLearning) => {
|
||||
const level = stdLearning?.level;
|
||||
const user = stdLearning?.learningUser;
|
||||
const student = user?.students;
|
||||
|
||||
return {
|
||||
NISN: student?.NISN,
|
||||
NAME_USERS: user?.NAME_USERS,
|
||||
NAME_LEVEL: level?.NAME_LEVEL,
|
||||
SCORE: stdLearning?.SCORE,
|
||||
FEEDBACK_STUDENT: stdLearning?.FEEDBACK_STUDENT,
|
||||
STUDENT_START: stdLearning?.STUDENT_START,
|
||||
STUDENT_FINISH: stdLearning?.STUDENT_FINISH,
|
||||
};
|
||||
});
|
||||
|
||||
const result = {
|
||||
ID_CLASS: classId,
|
||||
ID_SECTION: sectionId,
|
||||
ID_TOPIC: topicId,
|
||||
NAME_CLASS: className,
|
||||
NAME_SECTION: sectionName,
|
||||
NAME_TOPIC: topicName,
|
||||
levels: formattedData,
|
||||
currentPage: parseInt(page),
|
||||
totalPages,
|
||||
totalItems: count,
|
||||
};
|
||||
|
||||
response(200, result, "Success retrieving monitoring data!", res);
|
||||
} catch (error) {
|
||||
console.error("Error fetching monitoring data:", error);
|
||||
response(500, null, "Error retrieving monitoring data!", res);
|
||||
}
|
||||
};
|
||||
|
||||
export const monitoringFeedbackByClassAndTopic = async (req, res) => {
|
||||
const { ID_CLASS, ID_TOPIC, FEEDBACK } = req.body;
|
||||
|
||||
if (!req.user) {
|
||||
return response(401, null, "User not authenticated", res);
|
||||
}
|
||||
|
||||
const { ID } = req.user;
|
||||
|
||||
if (!ID) {
|
||||
return response(401, null, "Unauthorized: User ID not provided", res);
|
||||
}
|
||||
|
||||
try {
|
||||
const teacher = await models.Teacher.findOne({
|
||||
where: { ID },
|
||||
attributes: ["ID_GURU"],
|
||||
});
|
||||
|
||||
if (!teacher) {
|
||||
return response(404, null, "Teacher not found!", res);
|
||||
}
|
||||
|
||||
const monitoringData = await models.Monitoring.findAll({
|
||||
where: { ID_CLASS },
|
||||
include: [
|
||||
{
|
||||
model: models.StdLearning,
|
||||
as: "stdLearningMonitoring",
|
||||
required: true,
|
||||
include: [
|
||||
{
|
||||
model: models.Level,
|
||||
as: "level",
|
||||
where: { ID_TOPIC },
|
||||
attributes: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
console.log("Monitoring Data:", JSON.stringify(monitoringData, null, 2));
|
||||
|
||||
if (!monitoringData || monitoringData.length === 0) {
|
||||
return response(
|
||||
404,
|
||||
null,
|
||||
"No monitoring data found for this class and topic!",
|
||||
res
|
||||
);
|
||||
}
|
||||
|
||||
for (const monitoring of monitoringData) {
|
||||
const [updatedRows] = await models.Monitoring.update(
|
||||
{
|
||||
ID_GURU: teacher.ID_GURU,
|
||||
FEEDBACK_GURU: FEEDBACK,
|
||||
},
|
||||
{
|
||||
where: { ID_MONITORING: monitoring.ID_MONITORING },
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const updatedMonitoringData = await models.Monitoring.findAll({
|
||||
where: { ID_CLASS: ID_CLASS },
|
||||
include: [
|
||||
{
|
||||
model: models.Teacher,
|
||||
as: "monitoringTeacher",
|
||||
attributes: ["ID_GURU"],
|
||||
include: [
|
||||
{
|
||||
model: models.User,
|
||||
as: "teacherUser",
|
||||
attributes: ["NAME_USERS"],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
model: models.StdLearning,
|
||||
as: "stdLearningMonitoring",
|
||||
required: true,
|
||||
include: [
|
||||
{
|
||||
model: models.Level,
|
||||
as: "level",
|
||||
where: { ID_TOPIC },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const result = updatedMonitoringData.map((monitoring) => ({
|
||||
ID_MONITORING: monitoring.ID_MONITORING,
|
||||
FEEDBACK_GURU: monitoring.FEEDBACK_GURU,
|
||||
ID_GURU: monitoring.monitoringTeacher?.ID_GURU,
|
||||
TEACHER_NAME: monitoring.monitoringTeacher?.teacherUser?.NAME_USERS,
|
||||
}));
|
||||
|
||||
response(
|
||||
200,
|
||||
result,
|
||||
"Success updating teacher feedback for class and topic!",
|
||||
res
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Error in monitoringFeedbackByClassAndTopic:", error);
|
||||
response(500, null, "Error updating teacher feedback!", res);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -218,7 +218,9 @@ export const getStudents = async (req, res) => {
|
|||
}));
|
||||
|
||||
if (sort === "nisn") {
|
||||
formattedStudents.sort((a, b) => a.NISN.localeCompare(b.NISN));
|
||||
formattedStudents.sort((a, b) =>
|
||||
String(a.NISN).localeCompare(String(b.NISN))
|
||||
);
|
||||
} else if (sort === "name") {
|
||||
formattedStudents.sort((a, b) =>
|
||||
a.NAME_USERS.localeCompare(b.NAME_USERS)
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ module.exports = {
|
|||
allowNull: false,
|
||||
},
|
||||
CONTENT: {
|
||||
type: Sequelize.STRING(1024),
|
||||
type: Sequelize.TEXT('long'),
|
||||
allowNull: true,
|
||||
},
|
||||
AUDIO: {
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ const LevelModel = (DataTypes) => {
|
|||
},
|
||||
},
|
||||
CONTENT: {
|
||||
type: DataTypes.STRING(1024),
|
||||
type: DataTypes.TEXT('long'),
|
||||
allowNull: true,
|
||||
},
|
||||
AUDIO: {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import express from "express";
|
||||
import { getMonitorings, getMonitoringById,monitoringStudentsProgress, monitoringStudentProgressById, getMonitoringByTopicId, monitoringFeedback } from "../../controllers/monitoringControllers/monitoring.js";
|
||||
import { getMonitorings, getMonitoringById,monitoringStudentsProgress, monitoringStudentProgressById, getClassMonitoringByClassId, getClassMonitoringDataByClassAndTopic, getMonitoringByTopicId, monitoringFeedback, monitoringFeedbackByClassAndTopic } from "../../controllers/monitoringControllers/monitoring.js";
|
||||
import { verifyLoginUser, adminOrTeacherOnly } from "../../middlewares/User/authUser.js";
|
||||
|
||||
const router = express.Router();
|
||||
|
|
@ -8,12 +8,18 @@ router.get("/monitoring", verifyLoginUser, getMonitorings);
|
|||
|
||||
router.get("/monitoring/progress", verifyLoginUser, adminOrTeacherOnly, monitoringStudentsProgress);
|
||||
|
||||
router.get("/monitoring/class", verifyLoginUser, getClassMonitoringDataByClassAndTopic);
|
||||
|
||||
router.get("/monitoring/:id", verifyLoginUser, getMonitoringById);
|
||||
|
||||
router.get("/monitoring/class/:classId", verifyLoginUser, adminOrTeacherOnly, getClassMonitoringByClassId);
|
||||
|
||||
router.get("/monitoring/topic/:topicId", verifyLoginUser, getMonitoringByTopicId);
|
||||
|
||||
router.get("/monitoring/progress/:id", verifyLoginUser, adminOrTeacherOnly, monitoringStudentProgressById);
|
||||
|
||||
router.post("/monitoring/feedback/class", verifyLoginUser, adminOrTeacherOnly, monitoringFeedbackByClassAndTopic);
|
||||
|
||||
router.post("/monitoring/feedback/:id", verifyLoginUser, adminOrTeacherOnly, monitoringFeedback);
|
||||
|
||||
export default router
|
||||
Loading…
Reference in New Issue
Block a user