refactor: exercise model and auto grading middleware
This commit is contained in:
parent
24cba3b988
commit
ee4f1814e4
|
|
@ -141,12 +141,32 @@ export const getExerciseById = async (req, res) => {
|
|||
const questionType = exercise.QUESTION_TYPE;
|
||||
|
||||
if (questionType === "MCQ") {
|
||||
if (exerciseData.multipleChoices) {
|
||||
exerciseData.multipleChoices = exerciseData.multipleChoices.map(
|
||||
(choice) => {
|
||||
const { ANSWER_KEY, ...rest } = choice.dataValues;
|
||||
return rest;
|
||||
}
|
||||
);
|
||||
}
|
||||
delete exerciseData.matchingPairs;
|
||||
delete exerciseData.trueFalse;
|
||||
} else if (questionType === "MPQ") {
|
||||
if (exerciseData.matchingPairs) {
|
||||
exerciseData.matchingPairs = exerciseData.matchingPairs.map((pair) => {
|
||||
const { LEFT_PAIR, RIGHT_PAIR, ...rest } = pair.dataValues;
|
||||
return rest;
|
||||
});
|
||||
}
|
||||
delete exerciseData.multipleChoices;
|
||||
delete exerciseData.trueFalse;
|
||||
} else if (questionType === "TFQ") {
|
||||
if (exerciseData.trueFalse) {
|
||||
exerciseData.trueFalse = exerciseData.trueFalse.map((tf) => {
|
||||
const { IS_TRUE, ...rest } = tf.dataValues;
|
||||
return rest;
|
||||
});
|
||||
}
|
||||
delete exerciseData.multipleChoices;
|
||||
delete exerciseData.matchingPairs;
|
||||
} else {
|
||||
|
|
@ -167,7 +187,6 @@ export const getExerciseByLevelId = async (req, res) => {
|
|||
const { idLevel } = req.params;
|
||||
|
||||
const levelExists = await models.Level.findByPk(idLevel);
|
||||
|
||||
if (!levelExists) {
|
||||
return response(404, null, "Level not found", res);
|
||||
}
|
||||
|
|
@ -199,12 +218,34 @@ export const getExerciseByLevelId = async (req, res) => {
|
|||
const questionType = exercise.QUESTION_TYPE;
|
||||
|
||||
if (questionType === "MCQ") {
|
||||
if (exerciseData.multipleChoices) {
|
||||
exerciseData.multipleChoices = exerciseData.multipleChoices.map(
|
||||
(choice) => {
|
||||
const { ANSWER_KEY, ...rest } = choice.dataValues; // Exclude ANSWER_KEY
|
||||
return rest;
|
||||
}
|
||||
);
|
||||
}
|
||||
delete exerciseData.matchingPairs;
|
||||
delete exerciseData.trueFalse;
|
||||
} else if (questionType === "MPQ") {
|
||||
if (exerciseData.matchingPairs) {
|
||||
exerciseData.matchingPairs = exerciseData.matchingPairs.map(
|
||||
(pair) => {
|
||||
const { LEFT_PAIR, RIGHT_PAIR, ...rest } = pair.dataValues; // Exclude LEFT_PAIR, RIGHT_PAIR
|
||||
return rest;
|
||||
}
|
||||
);
|
||||
}
|
||||
delete exerciseData.multipleChoices;
|
||||
delete exerciseData.trueFalse;
|
||||
} else if (questionType === "TFQ") {
|
||||
if (exerciseData.trueFalse) {
|
||||
exerciseData.trueFalse = exerciseData.trueFalse.map((tf) => {
|
||||
const { IS_TRUE, ...rest } = tf.dataValues; // Exclude IS_TRUE
|
||||
return rest;
|
||||
});
|
||||
}
|
||||
delete exerciseData.multipleChoices;
|
||||
delete exerciseData.matchingPairs;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ export const getLevels = async (req, res) => {
|
|||
export const getLevelById = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
const level = await models.Level.findByPk(id, {
|
||||
attributes: {
|
||||
exclude: [
|
||||
|
|
@ -47,13 +48,38 @@ export const getLevelById = async (req, res) => {
|
|||
"ROUTE_6",
|
||||
],
|
||||
},
|
||||
include: [
|
||||
{
|
||||
model: models.Topic,
|
||||
as: "levelTopic",
|
||||
attributes: ["NAME_TOPIC"],
|
||||
include: {
|
||||
model: models.Section,
|
||||
as: "topicSection",
|
||||
attributes: ["NAME_SECTION"],
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
if (!level) {
|
||||
return response(404, null, "Level not found", res);
|
||||
}
|
||||
|
||||
response(200, level, "Success", 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);
|
||||
|
|
@ -65,7 +91,14 @@ export const getLevelsByTopicId = async (req, res) => {
|
|||
const { idTopic } = req.params;
|
||||
const { ID } = req.user;
|
||||
|
||||
const topicExists = await models.Topic.findByPk(idTopic);
|
||||
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);
|
||||
}
|
||||
|
|
@ -94,6 +127,16 @@ export const getLevelsByTopicId = async (req, res) => {
|
|||
},
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
model: models.Topic,
|
||||
as: "levelTopic",
|
||||
attributes: ["NAME_TOPIC"],
|
||||
include: {
|
||||
model: models.Section,
|
||||
as: "topicSection",
|
||||
attributes: ["NAME_SECTION"],
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
|
|
@ -129,9 +172,17 @@ export const getLevelsByTopicId = async (req, res) => {
|
|||
: 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,
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ export const getTopicBySectionId = async (req, res) => {
|
|||
};
|
||||
|
||||
export const createTopic = async (req, res) => {
|
||||
const { ID_SECTION, NAME_TOPIC, DESCRIPTION_TOPIC, OBJECTIVES } = req.body;
|
||||
const { ID_SECTION, NAME_TOPIC, DESCRIPTION_TOPIC } = req.body;
|
||||
|
||||
if (!ID_SECTION) {
|
||||
return response(400, null, "Section ID is required", res);
|
||||
|
|
@ -66,10 +66,6 @@ export const createTopic = async (req, res) => {
|
|||
return response(400, null, "Topic description is required", res);
|
||||
}
|
||||
|
||||
if (!OBJECTIVES) {
|
||||
return response(400, null, "Topic objectives are required", res);
|
||||
}
|
||||
|
||||
try {
|
||||
const section = await models.Section.findByPk(ID_SECTION);
|
||||
if (!section) {
|
||||
|
|
@ -80,7 +76,6 @@ export const createTopic = async (req, res) => {
|
|||
ID_SECTION,
|
||||
NAME_TOPIC,
|
||||
DESCRIPTION_TOPIC,
|
||||
OBJECTIVES,
|
||||
});
|
||||
|
||||
response(201, newTopic, "Topic created successfully", res);
|
||||
|
|
@ -92,7 +87,7 @@ export const createTopic = async (req, res) => {
|
|||
|
||||
export const updateTopicById = async (req, res) => {
|
||||
const { id } = req.params;
|
||||
const { ID_SECTION, NAME_TOPIC, DESCRIPTION_TOPIC, OBJECTIVES } = req.body;
|
||||
const { ID_SECTION, NAME_TOPIC, DESCRIPTION_TOPIC } = req.body;
|
||||
|
||||
try {
|
||||
const topic = await models.Topic.findByPk(id);
|
||||
|
|
@ -117,10 +112,6 @@ export const updateTopicById = async (req, res) => {
|
|||
topic.DESCRIPTION_TOPIC = DESCRIPTION_TOPIC;
|
||||
}
|
||||
|
||||
if (OBJECTIVES) {
|
||||
topic.OBJECTIVES = OBJECTIVES;
|
||||
}
|
||||
|
||||
await topic.save();
|
||||
|
||||
response(200, topic, "Topic updated successfully", res);
|
||||
|
|
@ -171,7 +162,6 @@ export const getCompletedTopics = async (req, res) => {
|
|||
"ID_TOPIC",
|
||||
"NAME_TOPIC",
|
||||
"DESCRIPTION_TOPIC",
|
||||
"OBJECTIVES",
|
||||
],
|
||||
},
|
||||
],
|
||||
|
|
@ -200,7 +190,6 @@ export const getCompletedTopics = async (req, res) => {
|
|||
ID_TOPIC: topic.ID_TOPIC,
|
||||
NAME_TOPIC: topic.NAME_TOPIC,
|
||||
DESCRIPTION_TOPIC: topic.DESCRIPTION_TOPIC,
|
||||
OBJECTIVES: topic.OBJECTIVES,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,14 @@ export const getStdExerciseById = async (req, res) => {
|
|||
|
||||
export const stdAnswerExercise = async (req, res, next) => {
|
||||
try {
|
||||
const { ID_STUDENT_LEARNING, ID_ADMIN_EXERCISE, ANSWER_STUDENT } = req.body;
|
||||
const { answers } = req.body;
|
||||
|
||||
if (!Array.isArray(answers) || answers.length === 0) {
|
||||
return response(400, null, "Answers array is required", res);
|
||||
}
|
||||
|
||||
for (const answer of answers) {
|
||||
const { ID_STUDENT_LEARNING, ID_ADMIN_EXERCISE, ANSWER_STUDENT } = answer;
|
||||
|
||||
if (!ID_STUDENT_LEARNING) {
|
||||
return response(400, null, "Id student learning is required", res);
|
||||
|
|
@ -69,7 +76,6 @@ export const stdAnswerExercise = async (req, res, next) => {
|
|||
|
||||
if (existingStdExercise) {
|
||||
existingStdExercise.ANSWER_STUDENT = ANSWER_STUDENT;
|
||||
|
||||
await existingStdExercise.save();
|
||||
} else {
|
||||
await models.StdExercise.create({
|
||||
|
|
@ -78,8 +84,9 @@ export const stdAnswerExercise = async (req, res, next) => {
|
|||
ANSWER_STUDENT,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
req.params.id = ID_STUDENT_LEARNING;
|
||||
req.params.id = answers[0].ID_STUDENT_LEARNING;
|
||||
next();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
|
|
|||
|
|
@ -117,6 +117,8 @@ export const updateStudentClassByName = async (req, res) => {
|
|||
|
||||
const updateResults = [];
|
||||
let hasError = false;
|
||||
let successCount = 0;
|
||||
let failureCount = 0;
|
||||
|
||||
for (const { NAME_USERS, NISN } of STUDENTS) {
|
||||
if (!NAME_USERS || !NISN) {
|
||||
|
|
@ -126,6 +128,7 @@ export const updateStudentClassByName = async (req, res) => {
|
|||
error: "User name and NISN are required for each student",
|
||||
});
|
||||
hasError = true;
|
||||
failureCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -148,6 +151,7 @@ export const updateStudentClassByName = async (req, res) => {
|
|||
error: "Student with the given name and NISN not found",
|
||||
});
|
||||
hasError = true;
|
||||
failureCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -158,6 +162,7 @@ export const updateStudentClassByName = async (req, res) => {
|
|||
error: "Student is already in the selected class",
|
||||
});
|
||||
hasError = true;
|
||||
failureCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -197,6 +202,8 @@ export const updateStudentClassByName = async (req, res) => {
|
|||
"Student's class and related monitoring updated successfully",
|
||||
studentUpdateResults,
|
||||
});
|
||||
|
||||
successCount++;
|
||||
} catch (error) {
|
||||
console.error("Error processing student:", error.message);
|
||||
updateResults.push({
|
||||
|
|
@ -206,24 +213,22 @@ export const updateStudentClassByName = async (req, res) => {
|
|||
details: error.message,
|
||||
});
|
||||
hasError = true;
|
||||
failureCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasError) {
|
||||
return response(
|
||||
400,
|
||||
{ updateResults },
|
||||
"Some students could not be updated due to errors",
|
||||
res
|
||||
);
|
||||
} else {
|
||||
return response(
|
||||
200,
|
||||
{ updateResults },
|
||||
"Students classes updated successfully",
|
||||
res
|
||||
);
|
||||
let responseMessage = "";
|
||||
|
||||
if (failureCount === STUDENTS.length) {
|
||||
responseMessage = "Failed to update all students.";
|
||||
} else if (successCount > 0 && failureCount > 0) {
|
||||
responseMessage =
|
||||
"Some students updated successfully, but there were errors with others.";
|
||||
} else if (successCount === STUDENTS.length) {
|
||||
responseMessage = "All students updated successfully.";
|
||||
}
|
||||
|
||||
return response(200, { updateResults }, responseMessage, res);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
response(500, null, "Internal Server Error", res);
|
||||
|
|
|
|||
|
|
@ -104,6 +104,13 @@ export const getStudents = async (req, res) => {
|
|||
model: models.Student,
|
||||
as: "students",
|
||||
attributes: ["NISN"],
|
||||
include: [
|
||||
{
|
||||
model: models.Class,
|
||||
as: "studentClass",
|
||||
attributes: ["NAME_CLASS"],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
raw: true,
|
||||
|
|
@ -115,6 +122,7 @@ export const getStudents = async (req, res) => {
|
|||
NAME_USERS: student.NAME_USERS,
|
||||
EMAIL: student.EMAIL,
|
||||
NISN: student.students.NISN,
|
||||
NAME_CLASS: student.students.studentClass.NAME_CLASS,
|
||||
ROLE: student.ROLE,
|
||||
}));
|
||||
|
||||
|
|
@ -125,6 +133,50 @@ export const getStudents = async (req, res) => {
|
|||
}
|
||||
};
|
||||
|
||||
export const getStudentsWithNoClass = async (req, res) => {
|
||||
try {
|
||||
const studentsWithoutClass = await models.User.findAll({
|
||||
where: {
|
||||
ROLE: "student",
|
||||
},
|
||||
attributes: ["ID", "NAME_USERS", "EMAIL", "ROLE"],
|
||||
include: [
|
||||
{
|
||||
model: models.Student,
|
||||
as: "students",
|
||||
attributes: ["NISN", "ID_CLASS"],
|
||||
include: [
|
||||
{
|
||||
model: models.Class,
|
||||
as: "studentClass",
|
||||
attributes: ["NAME_CLASS"],
|
||||
},
|
||||
],
|
||||
where: {
|
||||
ID_CLASS: null,
|
||||
},
|
||||
},
|
||||
],
|
||||
raw: true,
|
||||
nest: true,
|
||||
});
|
||||
|
||||
const formattedStudents = studentsWithoutClass.map((student) => ({
|
||||
ID: student.ID,
|
||||
NAME_USERS: student.NAME_USERS,
|
||||
EMAIL: student.EMAIL,
|
||||
NISN: student.students.NISN,
|
||||
NAME_CLASS: student.students.studentClass.NAME_CLASS,
|
||||
ROLE: student.ROLE,
|
||||
}));
|
||||
|
||||
response(200, formattedStudents, "Success", res);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
response(500, null, "Error retrieving students with no class", res);
|
||||
}
|
||||
};
|
||||
|
||||
export const getUserById = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ export const checkCorrectAnswers = async (req, res, next) => {
|
|||
const exercise = await models.Exercise.findByPk(ID_ADMIN_EXERCISE);
|
||||
if (!exercise) continue;
|
||||
|
||||
const weight = parseFloat(exercise.SCORE_WEIGHT); // Ensure weight is a float
|
||||
const weight = parseFloat(exercise.SCORE_WEIGHT);
|
||||
const questionType = exercise.QUESTION_TYPE;
|
||||
|
||||
switch (questionType) {
|
||||
|
|
@ -79,7 +79,7 @@ export const checkCorrectAnswers = async (req, res, next) => {
|
|||
|
||||
const correctPercentage = correctCount / matchingPairs.length;
|
||||
stdExercise.IS_CORRECT = correctCount > 0 ? 1 : 0;
|
||||
stdExercise.RESULT_SCORE_STUDENT = correctPercentage * weight; // Use float arithmetic
|
||||
stdExercise.RESULT_SCORE_STUDENT = correctPercentage * weight;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,13 +37,6 @@ const TopicModel = (DataTypes) => {
|
|||
notEmpty: true,
|
||||
},
|
||||
},
|
||||
OBJECTIVES: {
|
||||
type: DataTypes.STRING(1024),
|
||||
allowNull: false,
|
||||
validate: {
|
||||
notEmpty: true,
|
||||
},
|
||||
},
|
||||
TIME_TOPIC: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: true,
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ const ClassModel = (DataTypes) => {
|
|||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
},
|
||||
TIME_REPORT: {
|
||||
TIME_CLASS: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: true,
|
||||
defaultValue: DataTypes.NOW,
|
||||
|
|
|
|||
|
|
@ -13,6 +13,17 @@ const StudentModel = (DataTypes) => {
|
|||
notEmpty: true,
|
||||
},
|
||||
},
|
||||
ID_CLASS: {
|
||||
type: DataTypes.UUID,
|
||||
allowNull: false,
|
||||
validate: {
|
||||
notEmpty: true,
|
||||
},
|
||||
references: {
|
||||
model: "class",
|
||||
key: "ID_CLASS",
|
||||
},
|
||||
},
|
||||
ID: {
|
||||
type: DataTypes.UUID,
|
||||
allowNull: false,
|
||||
|
|
|
|||
BIN
public/uploads/avatar/user-0d4423b278e286084768458758c5d8f3.jpg
Normal file
BIN
public/uploads/avatar/user-0d4423b278e286084768458758c5d8f3.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.2 MiB |
|
|
@ -1,5 +1,5 @@
|
|||
import express from "express";
|
||||
import { getUsers, getAdmins, getTeachers, getStudents, getUserById, getUserByName, updateUserById, updateUserPasswordById, deleteUserById, getMe } from "../../controllers/usersControllers/user.js";
|
||||
import { getUsers, getAdmins, getTeachers, getStudents, getStudentsWithNoClass, getUserById, getUserByName, updateUserById, updateUserPasswordById, deleteUserById, getMe } from "../../controllers/usersControllers/user.js";
|
||||
import { verifyLoginUser, adminOnly, adminOrTeacherOnly } from "../../middlewares/User/authUser.js";
|
||||
import handleUpload from "../../middlewares/User/uploadUser.js";
|
||||
|
||||
|
|
@ -14,6 +14,8 @@ router.get("/user/teacher", verifyLoginUser, adminOnly, getTeachers);
|
|||
|
||||
router.get("/user/student", verifyLoginUser, adminOrTeacherOnly, getStudents);
|
||||
|
||||
router.get("/user/student/unassigned", verifyLoginUser, adminOrTeacherOnly, getStudentsWithNoClass);
|
||||
|
||||
router.get("/user/:id", verifyLoginUser, getUserById);
|
||||
|
||||
router.get("/user/name/:name", verifyLoginUser, adminOrTeacherOnly, getUserByName);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user