backend_adaptive_learning/controllers/monitoringControllers/monitoring.js

1433 lines
39 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";
2024-10-28 02:36:05 +00:00
import { createObjectCsvWriter } from "csv-writer";
import { Readable } from "stream";
import fs from "fs";
import os from "os";
import path from "path";
2024-09-13 13:03:35 +00:00
export const getMonitorings = async (req, res) => {
try {
const monitoring = await models.Monitoring.findAll();
response(200, monitoring, "Success", res);
} catch (error) {
console.log(error);
response(500, null, "Error retrieving monitoring data!", res);
}
};
export const getMonitoringById = async (req, res) => {
try {
const { id } = req.params;
const monitoring = await models.Monitoring.findByPk(id);
if (!monitoring) {
return response(404, null, "Monitoring data not found", res);
}
response(200, monitoring, "Success", res);
} catch (error) {
console.log(error);
response(500, null, "Internal Server Error", res);
}
};
export const createMonitoring = async (req) => {
const { ID_STUDENT_LEARNING } = req.body;
if (!req.user) {
throw new Error("User not authenticated");
}
if (!ID_STUDENT_LEARNING) {
throw new Error("Student Learning ID is required");
}
try {
const existingMonitoring = await models.Monitoring.findOne({
where: { ID_STUDENT_LEARNING: ID_STUDENT_LEARNING },
});
if (existingMonitoring) {
return existingMonitoring.toJSON();
}
const stdLearning = await models.StdLearning.findByPk(ID_STUDENT_LEARNING);
if (!stdLearning) {
throw new Error("Student learning data not found");
}
const userID = stdLearning.ID;
const student = await models.Student.findOne({ where: { ID: userID } });
if (!student) {
throw new Error("Student data not found");
}
const { ID_SISWA } = student;
const studentClass = await models.Student.findOne({
where: { ID_SISWA: ID_SISWA },
attributes: ["ID_CLASS"],
});
const ID_CLASS =
studentClass && studentClass.ID_CLASS ? studentClass.ID_CLASS : null;
const newMonitoring = await models.Monitoring.create({
ID_STUDENT_LEARNING,
ID_CLASS,
});
return newMonitoring.toJSON();
} catch (error) {
console.error(error);
throw new Error("Internal Server Error");
}
};
export const updateMonitoringClass = async ({
ID_STUDENT_LEARNING,
ID_CLASS,
}) => {
if (!ID_STUDENT_LEARNING || !ID_CLASS) {
throw new Error("Student Learning ID and Class ID are required");
}
try {
const monitoring = await models.Monitoring.findOne({
where: { ID_STUDENT_LEARNING },
});
if (!monitoring) {
throw new Error("Monitoring data not found");
}
monitoring.ID_CLASS = ID_CLASS;
await monitoring.save();
return monitoring;
} catch (error) {
console.error(error);
throw error;
}
};
export const monitoringStudentsProgress = async (req, res) => {
2024-10-14 01:23:37 +00:00
const { page = 1, limit = 10, search = "", sort = "time" } = req.query;
2024-09-13 13:03:35 +00:00
try {
2024-10-14 01:23:37 +00:00
const { count, rows: monitorings } =
await models.Monitoring.findAndCountAll({
include: [
{
model: models.StdLearning,
as: "stdLearningMonitoring",
include: [
{
model: models.User,
as: "learningUser",
attributes: ["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"],
},
],
},
],
},
],
},
],
where: {
...(search && {
[models.Op.or]: [
{
"$stdLearningMonitoring.learningUser.students.NISN$": {
[models.Op.like]: `%${search}%`,
2024-09-13 13:03:35 +00:00
},
2024-10-14 01:23:37 +00:00
},
{
"$stdLearningMonitoring.learningUser.NAME_USERS$": {
[models.Op.like]: `%${search}%`,
2024-09-13 13:03:35 +00:00
},
2024-10-14 01:23:37 +00:00
},
{
"$stdLearningMonitoring.level.levelTopic.topicSection.NAME_SECTION$":
{
[models.Op.like]: `%${search}%`,
},
},
{
"$stdLearningMonitoring.level.levelTopic.NAME_TOPIC$": {
[models.Op.like]: `%${search}%`,
},
},
],
}),
2024-09-13 13:03:35 +00:00
},
2024-10-14 01:23:37 +00:00
distinct: true,
});
2024-09-13 13:03:35 +00:00
2024-10-14 01:23:37 +00:00
const formattedResult = monitorings.map((monitoring) => ({
ID_MONITORING: monitoring.ID_MONITORING,
NISN: monitoring.stdLearningMonitoring?.learningUser?.students?.NISN,
NAME_USERS: monitoring.stdLearningMonitoring?.learningUser?.NAME_USERS,
NAME_SECTION:
monitoring.stdLearningMonitoring?.level?.levelTopic?.topicSection
?.NAME_SECTION,
NAME_TOPIC:
monitoring.stdLearningMonitoring?.level?.levelTopic?.NAME_TOPIC,
NAME_CLASS:
monitoring.stdLearningMonitoring?.learningUser?.students?.studentClass
?.NAME_CLASS ?? null,
}));
if (sort === "nisn") {
2024-10-23 04:09:33 +00:00
formattedResult.sort((a, b) =>
String(a.NISN).localeCompare(String(b.NISN))
);
2024-10-14 01:23:37 +00:00
} else if (sort === "name") {
formattedResult.sort((a, b) => a.NAME_USERS.localeCompare(b.NAME_USERS));
} else if (sort === "section") {
formattedResult.sort((a, b) =>
a.NAME_SECTION.localeCompare(b.NAME_SECTION)
);
} else if (sort === "topic") {
formattedResult.sort((a, b) => a.NAME_TOPIC.localeCompare(b.NAME_TOPIC));
} else {
formattedResult.sort((a, b) => new Date(b.TIME) - new Date(a.TIME));
}
const paginatedResult = formattedResult.slice(
(page - 1) * limit,
page * limit
);
const totalPages = Math.ceil(count / limit);
const currentPage = parseInt(page);
2024-09-13 13:03:35 +00:00
response(
200,
2024-10-14 01:23:37 +00:00
{
monitorings: paginatedResult,
currentPage,
totalPages,
totalItems: count,
},
2024-09-13 13:03:35 +00:00
"Success retrieving student monitoring progress!",
res
);
} catch (error) {
console.error(error);
response(500, null, "Error retrieving student monitoring progress!", res);
}
};
export const monitoringStudentProgressById = async (req, res) => {
const { id } = req.params;
2024-10-14 01:23:37 +00:00
const { page = 1, limit = 10, search = "", sort = "start" } = req.query;
2024-09-13 13:03:35 +00:00
try {
const monitoring = await models.Monitoring.findOne({
where: { ID_MONITORING: id },
include: [
{
model: models.StdLearning,
as: "stdLearningMonitoring",
attributes: [
"ID",
"ID_LEVEL",
"SCORE",
"FEEDBACK_STUDENT",
"STUDENT_START",
"STUDENT_FINISH",
"ID_STUDENT_LEARNING",
],
include: [
{
model: models.Level,
as: "level",
attributes: ["ID_TOPIC", "NAME_LEVEL"],
2024-10-14 01:23:37 +00:00
include: [
{
model: models.Topic,
as: "levelTopic",
attributes: ["NAME_TOPIC"],
include: [
{
model: models.Section,
as: "topicSection",
attributes: ["NAME_SECTION"],
},
],
},
],
},
{
model: models.User,
as: "learningUser",
attributes: ["NAME_USERS"],
include: [
{
model: models.Student,
as: "students",
attributes: ["NISN"],
},
],
2024-09-13 13:03:35 +00:00
},
],
},
],
});
if (!monitoring) {
return response(404, null, "Monitoring data not found!", res);
}
const stdLearning = monitoring.stdLearningMonitoring;
if (!stdLearning || stdLearning.length === 0) {
return response(404, null, "No student learning data found!", res);
}
const userID = stdLearning.ID;
const topicID = stdLearning.level.ID_TOPIC;
2024-10-14 01:23:37 +00:00
const studentName = stdLearning.learningUser.NAME_USERS;
const nisn = stdLearning.learningUser.students.NISN;
const topicName = stdLearning.level.levelTopic.NAME_TOPIC;
const sectionName = stdLearning.level.levelTopic.topicSection.NAME_SECTION;
2024-09-13 13:03:35 +00:00
2024-10-14 01:23:37 +00:00
const levels = await models.StdLearning.findAndCountAll({
2024-09-13 13:03:35 +00:00
where: {
ID: userID,
2024-10-14 01:23:37 +00:00
...(search && {
[models.Op.or]: [
{
"$level.NAME_LEVEL$": { [models.Op.like]: `%${search}%` },
},
{
SCORE: { [models.Op.like]: `%${search}%` },
},
{
FEEDBACK_STUDENT: { [models.Op.like]: `%${search}%` },
},
],
}),
2024-10-23 04:09:33 +00:00
STUDENT_FINISH: {
[models.Sequelize.Op.ne]: null,
},
2024-09-13 13:03:35 +00:00
},
include: [
{
model: models.Level,
as: "level",
attributes: ["NAME_LEVEL", "ID_TOPIC"],
where: {
ID_TOPIC: topicID,
},
2024-09-13 13:03:35 +00:00
},
],
attributes: [
"SCORE",
"FEEDBACK_STUDENT",
"STUDENT_START",
"STUDENT_FINISH",
],
2024-10-14 01:23:37 +00:00
distinct: true,
2024-09-13 13:03:35 +00:00
});
2024-10-14 01:23:37 +00:00
const levelArray = levels.rows
.map((learning) => ({
NAME_LEVEL: learning.level.NAME_LEVEL,
SCORE: learning.SCORE,
FEEDBACK_STUDENT: learning.FEEDBACK_STUDENT,
STUDENT_START: learning.STUDENT_START,
STUDENT_FINISH: learning.STUDENT_FINISH,
}))
.sort((a, b) => {
2024-10-14 01:23:37 +00:00
if (sort === "level") {
return a.NAME_LEVEL.localeCompare(b.NAME_LEVEL);
} else if (sort === "score") {
return b.SCORE - a.SCORE;
} else if (sort === "feedback") {
2024-10-28 02:36:05 +00:00
if (a.FEEDBACK_STUDENT === null && b.FEEDBACK_STUDENT !== null) {
return 1;
} else if (
a.FEEDBACK_STUDENT !== null &&
b.FEEDBACK_STUDENT === null
) {
return -1;
} else if (
a.FEEDBACK_STUDENT === null &&
b.FEEDBACK_STUDENT === null
) {
return 0;
} else {
return a.FEEDBACK_STUDENT.localeCompare(b.FEEDBACK_STUDENT);
}
2024-10-14 01:23:37 +00:00
} else if (sort === "start") {
return new Date(a.STUDENT_START) - new Date(b.STUDENT_START);
} else if (sort === "finish") {
return new Date(a.STUDENT_FINISH) - new Date(b.STUDENT_FINISH);
}
return 0;
});
2024-09-13 13:03:35 +00:00
2024-10-14 01:23:37 +00:00
const paginatedResult = levelArray.slice((page - 1) * limit, page * limit);
const totalPages = Math.ceil(levels.count / limit);
const currentPage = parseInt(page);
2024-09-13 13:03:35 +00:00
const result = {
ID_MONITORING: monitoring.ID_MONITORING,
2024-10-14 01:23:37 +00:00
NAME_SECTION: sectionName,
NAME_TOPIC: topicName,
NAME_USERS: studentName,
NISN: nisn,
levels: paginatedResult,
currentPage,
totalPages,
totalItems: levels.count,
2024-09-13 13:03:35 +00:00
};
response(200, result, "Success retrieving student progress!", res);
} catch (error) {
console.error(error);
response(500, null, "Error retrieving student progress!", res);
}
};
export const monitoringFeedback = async (req, res) => {
const { id } = req.params;
const { 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: ID },
attributes: ["ID_GURU"],
});
if (!teacher) {
return response(404, null, "Teacher not found!", res);
}
const [updatedRows] = await models.Monitoring.update(
{
ID_GURU: teacher.ID_GURU,
FEEDBACK_GURU: feedback,
},
{
where: { ID_MONITORING: id },
returning: true,
}
);
if (updatedRows === 0) {
return response(404, null, "Monitoring data not found!", res);
}
const monitoringWithRelations = await models.Monitoring.findOne({
where: { ID_MONITORING: id },
include: [
{
model: models.Teacher,
as: "monitoringTeacher",
attributes: ["ID_GURU"],
include: [
{
model: models.User,
as: "teacherUser",
attributes: ["NAME_USERS"],
},
],
},
],
});
if (!monitoringWithRelations) {
return response(404, null, "Updated monitoring data not found!", res);
}
const result = {
ID_MONITORING: monitoringWithRelations.ID_MONITORING,
FEEDBACK_GURU: monitoringWithRelations.FEEDBACK_GURU,
ID_GURU: monitoringWithRelations.monitoringTeacher?.ID_GURU,
TEACHER_NAME:
monitoringWithRelations.monitoringTeacher?.teacherUser?.NAME_USERS,
};
response(200, result, "Success updating teacher feedback!", res);
} catch (error) {
console.error("Error in monitoringFeedback:", error);
response(500, null, "Error updating teacher feedback!", res);
}
};
export const getMonitoringByTopicId = async (req, res) => {
const { topicId } = req.params;
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 stdLearning = await models.StdLearning.findOne({
where: {
ID: ID,
},
include: [
{
model: models.Level,
as: "level",
where: { ID_TOPIC: topicId },
attributes: ["ID_LEVEL", "NAME_LEVEL"],
include: [
{
model: models.Topic,
as: "levelTopic",
attributes: ["NAME_TOPIC"],
},
],
},
],
});
if (!stdLearning) {
return response(
404,
null,
"Student learning data not found for this topic!",
res
);
}
const monitoringData = await models.Monitoring.findOne({
where: {
ID_STUDENT_LEARNING: stdLearning.ID_STUDENT_LEARNING,
},
include: [
{
model: models.Teacher,
as: "monitoringTeacher",
attributes: ["ID_GURU"],
include: [
{
model: models.User,
as: "teacherUser",
attributes: ["NAME_USERS"],
},
],
},
],
});
if (!monitoringData) {
return response(
404,
null,
"Monitoring data not found for this learning record!",
res
);
}
const result = {
ID_MONITORING: monitoringData.ID_MONITORING,
NAME_TOPIC: stdLearning.level?.levelTopic?.NAME_TOPIC,
NAME_LEVEL: stdLearning.level?.NAME_LEVEL,
TEACHER_NAME: monitoringData.monitoringTeacher?.teacherUser?.NAME_USERS,
FEEDBACK_GURU: monitoringData.FEEDBACK_GURU,
TIME_MONITORING: monitoringData.TIME_MONITORING,
};
response(200, result, "Success retrieving monitoring data!", res);
} catch (error) {
console.error("Error in getMonitoringByTopicId:", error);
response(500, null, "Error retrieving monitoring data!", res);
}
};
2024-10-23 04:09:33 +00:00
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",
],
2024-10-28 02:36:05 +00:00
order: [["STUDENT_FINISH", "DESC"]],
2024-10-23 04:09:33 +00:00
});
const sortedRecords = allStdLearning.sort((a, b) => {
2024-10-28 02:36:05 +00:00
const userComparison = a.learningUser.NAME_USERS.localeCompare(
b.learningUser.NAME_USERS
);
if (userComparison !== 0) {
return userComparison;
}
2024-10-23 04:09:33 +00:00
if (sort === "nisn") {
return String(a.learningUser.students.NISN).localeCompare(
String(b.learningUser.students.NISN)
);
2024-10-28 02:36:05 +00:00
} else if (sort === "name") {
return userComparison;
2024-10-23 04:09:33 +00:00
} 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") {
2024-10-28 02:36:05 +00:00
if (a.FEEDBACK_STUDENT === null && b.FEEDBACK_STUDENT !== null) {
return 1;
} else if (a.FEEDBACK_STUDENT !== null && b.FEEDBACK_STUDENT === null) {
return -1;
} else if (a.FEEDBACK_STUDENT === null && b.FEEDBACK_STUDENT === null) {
return 0;
} else {
return a.FEEDBACK_STUDENT.localeCompare(b.FEEDBACK_STUDENT);
}
2024-10-23 04:09:33 +00:00
} else if (sort === "start") {
return new Date(a.STUDENT_START) - new Date(b.STUDENT_START);
}
2024-10-28 02:36:05 +00:00
2024-10-23 04:09:33 +00:00
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: [],
},
],
},
],
});
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);
}
};
2024-10-28 02:36:05 +00:00
export const monitoringStudentProgressCSVById = async (req, res) => {
const { id } = req.params;
try {
const monitoring = await models.Monitoring.findOne({
where: { ID_MONITORING: id },
include: [
{
model: models.StdLearning,
as: "stdLearningMonitoring",
attributes: [
"ID",
"ID_LEVEL",
"SCORE",
"FEEDBACK_STUDENT",
"STUDENT_START",
"STUDENT_FINISH",
"ID_STUDENT_LEARNING",
],
include: [
{
model: models.Level,
as: "level",
attributes: ["ID_TOPIC", "NAME_LEVEL"],
include: [
{
model: models.Topic,
as: "levelTopic",
attributes: ["NAME_TOPIC"],
include: [
{
model: models.Section,
as: "topicSection",
attributes: ["NAME_SECTION"],
},
],
},
],
},
{
model: models.User,
as: "learningUser",
attributes: ["NAME_USERS"],
include: [
{
model: models.Student,
as: "students",
attributes: ["NISN"],
},
],
},
],
},
],
});
if (!monitoring) {
return response(404, null, "Monitoring data not found!", res);
}
const stdLearning = monitoring.stdLearningMonitoring;
if (!stdLearning || stdLearning.length === 0) {
return response(404, null, "No student learning data found!", res);
}
const userID = stdLearning.ID;
const topicID = stdLearning.level.ID_TOPIC;
const studentName = stdLearning.learningUser.NAME_USERS;
const nisn = stdLearning.learningUser.students.NISN;
const topicName = stdLearning.level.levelTopic.NAME_TOPIC;
const sectionName = stdLearning.level.levelTopic.topicSection.NAME_SECTION;
const levels = await models.StdLearning.findAll({
where: {
ID: userID,
STUDENT_FINISH: {
[models.Sequelize.Op.ne]: null,
},
},
include: [
{
model: models.Level,
as: "level",
attributes: ["NAME_LEVEL", "ID_TOPIC"],
where: {
ID_TOPIC: topicID,
},
},
],
attributes: [
"SCORE",
"FEEDBACK_STUDENT",
"STUDENT_START",
"STUDENT_FINISH",
],
order: [["STUDENT_FINISH", "DESC"]],
distinct: true,
});
const levelArray = levels.map((learning) => ({
NAME_LEVEL: learning.level.NAME_LEVEL,
SCORE: learning.SCORE,
FEEDBACK_STUDENT: learning.FEEDBACK_STUDENT,
STUDENT_START: learning.STUDENT_START,
STUDENT_FINISH: learning.STUDENT_FINISH,
}));
const tempDir = os.tmpdir();
const tempFilePath = path.join(
tempDir,
`Student_Monitoring_${nisn}_${studentName}.csv`
);
const csvWriter = createObjectCsvWriter({
path: tempFilePath,
header: [
{ id: "field", title: "Field" },
{ id: "value", title: "Value" },
],
});
const records = [
{ field: "Section Name", value: sectionName },
{ field: "Topic Name", value: topicName },
{ field: "Student Name", value: studentName },
{ field: "NISN", value: nisn },
];
await csvWriter.writeRecords(records);
const levelCsvWriter = createObjectCsvWriter({
path: tempFilePath,
append: true,
header: [
{ id: "NAME_LEVEL", title: "Level Name" },
{ id: "SCORE", title: "Score" },
{ id: "FEEDBACK_STUDENT", title: "Student Feedback" },
{ id: "STUDENT_START", title: "Start Exercise" },
{ id: "STUDENT_FINISH", title: "Finish Exercise" },
],
});
fs.appendFileSync(tempFilePath, "\n");
fs.appendFileSync(
tempFilePath,
"Name Level,Score,Student Feedback,Start Exercise,Finish Exercise\n"
);
await levelCsvWriter.writeRecords(levelArray);
res.setHeader("Content-Type", "text/csv");
res.setHeader(
"Content-Disposition",
`attachment; filename="Student_Monitoring_${nisn}_${studentName}.csv.csv"`
);
const fileStream = fs.createReadStream(tempFilePath);
fileStream.pipe(res).on("finish", () => {
fs.unlinkSync(tempFilePath);
});
} catch (error) {
console.error(error);
response(500, null, "Error retrieving student progress!", res);
}
};
export const classMonitoringCSVByClassAndTopic = async (req, res) => {
const { ID_CLASS, ID_TOPIC } = req.body;
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 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 sectionName = topicData.topicSection?.NAME_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: classData.ID_CLASS,
ID_SECTION: topicData.topicSection?.ID_SECTION,
ID_TOPIC: topicData.ID_TOPIC,
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 allStdLearning = await models.StdLearning.findAll({
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,
},
},
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",
],
order: [
[{ model: models.User, as: "learningUser" }, "NAME_USERS", "ASC"],
["STUDENT_FINISH", "DESC"],
],
});
const formattedData = allStdLearning.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 sanitizedClassName = className.replace(/\s+/g, "_");
const sanitizedTopicName = topicName.replace(/\s+/g, "_");
const tempDir = os.tmpdir();
const tempFilePath = path.join(
tempDir,
`Class_Monitoring_${sanitizedClassName}_${sanitizedTopicName}.csv`
);
const csvWriter = createObjectCsvWriter({
path: tempFilePath,
header: [
{ id: "field", title: "Field" },
{ id: "value", title: "Value" },
],
});
const records = [
{ field: "Class Name", value: className },
{ field: "Section Name", value: sectionName },
{ field: "Topic Name", value: topicName },
];
await csvWriter.writeRecords(records);
const levelCsvWriter = createObjectCsvWriter({
path: tempFilePath,
append: true,
header: [
{ id: "NISN", title: "NISN" },
{ id: "NAME_USERS", title: "Student Name" },
{ id: "NAME_LEVEL", title: "Level Name" },
{ id: "SCORE", title: "Score" },
{ id: "FEEDBACK_STUDENT", title: "Student Feedback" },
{ id: "STUDENT_START", title: "Start Exercise" },
{ id: "STUDENT_FINISH", title: "Finish Exercise" },
],
});
fs.appendFileSync(tempFilePath, "\n");
fs.appendFileSync(
tempFilePath,
"NISN,Student Name,Level Name,Score,Student Feedback,Start Exercise,Finish Exercise\n"
);
await levelCsvWriter.writeRecords(formattedData);
res.setHeader("Content-Type", "text/csv");
res.setHeader(
"Content-Disposition",
`attachment; filename=Class_Monitoring_${sanitizedClassName}_${sanitizedTopicName}.csv`
);
const fileStream = fs.createReadStream(tempFilePath);
fileStream.pipe(res).on("finish", () => {
fs.unlinkSync(tempFilePath);
});
} catch (error) {
console.error("Error fetching monitoring data:", error);
response(500, null, "Error retrieving monitoring data!", res);
}
};