diff --git a/config/corsOptions.js b/config/corsOptions.js index 0adeef893..b2b778f6f 100644 --- a/config/corsOptions.js +++ b/config/corsOptions.js @@ -2,7 +2,6 @@ const allowedOrigins = require('./allowedOrigins'); const corsOptions = { origin: (origin, callback) => { - console.log('origin: ', origin); if (origin && allowedOrigins.includes(origin)) { callback(null, true); } else { diff --git a/controllers/getCoursePlanAdmin.js b/controllers/getCoursePlanAdmin.js index fffa96d59..0d491a12e 100644 --- a/controllers/getCoursePlanAdmin.js +++ b/controllers/getCoursePlanAdmin.js @@ -13,44 +13,12 @@ async function getCoursePlanAdmin(programmeId, studentCourseCodes, programmeCour eligibleCourses = getEligibleCourses(programmeId, studentCourseCodes, programmeCourses, semCourses, prereqs, antireqs, coursegroups); - // console.log("eligibleCourses: ", eligibleCourses); + degreeProgress = getDegreeProgress(programmeId, studentCourseCodes, programmeCourses, courses, programmeCreditRequirements, types); - // console.log("Degree Progress: ", degreeProgress); + plannedCourses = await getPlannedCourses(studentId, semesterId); - // console.log("Planned Courses: ", plannedCourses); - - - - - // -----------------FORMAT OUTPUT------------------------- - - - - // // get planned courses - // for (let course of plannedCourses) { - // const progCourse = programmeCourses.find(progCourse => progCourse.courseCode === course && progCourse.programmeId === programmeId); - // let typeId = progCourse ? progCourse.typeId : null; - // const typeObj = types.find(type => type.id === typeId); - // let typeName = typeObj ? typeObj.type : null; - // // console.log("typeName: ",typeName); - // const courseObj = courses.find(c => c.courseCode === course); - // let courseName = courseObj ? courseObj.courseTitle : null; - // let credits = courseObj ? courseObj.credits : null; - // // console.log("credits:: ", credits); - - // plannedCoursesObjs.push({ - // "courseCode": progCourse.courseCode, - // "courseTitle": courseName, - // "type": typeName, - // "credits": credits - // }) - - // } - - - // console.log("degreeprogress Requiremtns: ", degreeProgress.Requirements); if (plannedCoursesObjs) { @@ -60,34 +28,6 @@ async function getCoursePlanAdmin(programmeId, studentCourseCodes, programmeCour } - - // for (type in degreeProgress.Requirements) { - // let planData = {}; - // let plancourses =[]; - // planData["creditType"] = type; - // planData["creditsRemaining"] = degreeProgress.Requirements[type][0]; - // // console.log(type); - // // console.log(degreeProgress.Requirements[type][0]); - // for (let plannedCoursesObj of plannedCoursesObjs) { - // if (plannedCoursesObj.type === type) { - // // console.log(plannedCoursesObj); - - // planData["creditsRemaining"] -= plannedCoursesObj.credits; - // // console.log("planData.creditsRemaining",planData.creditsRemaining); - // // console.log("remaining credits",planData["creditsRemaining"]); - // plancourses.push(plannedCoursesObj); - // } - // } - // planData["creditsRemaining"] = [planData.creditsRemaining, degreeProgress.Requirements[type][1]]; - // planData["selectedCourses"] = plancourses; - // coursePlan.push(planData); - - // } - - - - // console.log("COURSEPLAN:::> ",coursePlan); - return coursePlan; } diff --git a/controllers/getDegreeProgress.js b/controllers/getDegreeProgress.js index cc1f5f838..f84b26a4d 100644 --- a/controllers/getDegreeProgress.js +++ b/controllers/getDegreeProgress.js @@ -44,7 +44,7 @@ async function getDegreeProgress(student_id) { for (i = 0; i < programmeCourse.length; i++) { programmeCourses.push(programmeCourse[i].dataValues); } - //console.log("programmeCourse: ", programmeCourses); + // get courses let course = await Course.findAll(); @@ -52,7 +52,7 @@ async function getDegreeProgress(student_id) { for (i = 0; i < course.length; i++) { courses.push(course[i].dataValues); } - //console.log("courses: ", courses); + // get programmeCreditRequirements let pcrs = await PCR.findAll({ where: { programmeId } }); @@ -60,7 +60,7 @@ async function getDegreeProgress(student_id) { for (i = 0; i < pcrs.length; i++) { programmeCreditRequirements.push(pcrs[i].dataValues); } - //console.log("PCR: ", programmeCreditRequirements); + // get types let type = await Type.findAll(); @@ -68,7 +68,7 @@ async function getDegreeProgress(student_id) { for (i = 0; i < type.length; i++) { types.push(type[i].dataValues); } - //console.log("types: ", types); + let actualTotalCredits = 0; @@ -89,7 +89,7 @@ async function getDegreeProgress(student_id) { try { let course = courses.find((c) => c.code === studentCourseCodes[i]); const type = types.find(type => type.type === creditType); - //console.log(type); + let programmeCourse = programmeCourses.find( (c) => c.courseCode === studentCourseCodes[i] && c.programmeId === programmeId && c.typeId === type.id); @@ -103,7 +103,7 @@ async function getDegreeProgress(student_id) { completedCourses.push(course.code); creditRequirements[creditType][0] -= credits; actualTotalCredits += credits; - //console.log(completedCourses); + } } catch (error) { @@ -125,9 +125,7 @@ async function getDegreeProgress(student_id) { } -// (async () =>{ -// console.log(await getDegreeProgress('816031565')) -// })() + module.exports = { getDegreeProgress }; diff --git a/controllers/getEligibleCourses.js b/controllers/getEligibleCourses.js index 7d3ff0332..6e56e9cd5 100644 --- a/controllers/getEligibleCourses.js +++ b/controllers/getEligibleCourses.js @@ -241,32 +241,5 @@ async function getAllEligibleCourses(studentId) { } } -// testing without Postman - -// (async () =>{ -// const dummyStudentCourses_db = await StudentCourse.findAll({ -// attributes : ['courseCode'], -// where: { -// studentId : '816031565' -// } -// }); -// const dummyStudentCourses = [ -// 'COMP1600', -// 'COMP1601', -// 'INFO1600', -// 'MATH1115', -// 'FOUN1101' -// ] -// const test_e = await getEligibleCourses('816031565',2); -// console.log(test_e); -// })() -/** - * Expected - * 'COMP1602', - * 'COMP1603', - * 'COMP1604', - * 'FOUN1105', - * 'INFO1601' - */ module.exports = { getEligibleCourses, getAllEligibleCourses }; diff --git a/controllers/getPlannedCourses.js b/controllers/getPlannedCourses.js index 5217179da..c431fd72a 100644 --- a/controllers/getPlannedCourses.js +++ b/controllers/getPlannedCourses.js @@ -13,7 +13,7 @@ async function getPlannedCourses(studentId, semesterId) { ] } }); - // console.log("Advising Session: ",advisingSession); + if (advisingSession) { let advisingSessionId = advisingSession.dataValues.id; let SelectedCourses = await SelectedCourse.findAll({ diff --git a/controllers/getStudentCoursePlan.js b/controllers/getStudentCoursePlan.js index 62bdf74a6..d02938c0e 100644 --- a/controllers/getStudentCoursePlan.js +++ b/controllers/getStudentCoursePlan.js @@ -6,8 +6,11 @@ const Course = require("../models/Course"); const ProgrammeCourse = require("../models/ProgrammeCourse"); const Type = require("../models/Type"); const ElectiveRequirement = require("../models/ElectiveRequirement"); +const Semester = require("../models/Semester"); const { getCoursePlan } = require("./getCoursePlan"); +const { getAllEligibleCourses } = require("../controllers/getEligibleCourses"); const { Op, where } = require('sequelize'); +const db = require('../db'); async function getStudentCoursePlan(studentId, semesterId) { try { @@ -50,160 +53,251 @@ async function getStudentCoursePlan(studentId, semesterId) { async function getStudentCoursePlanByStudentIdAndSemesterId(studentId, semesterId) { - try { - // Fetch the student to get their programmeId - const student = await Student.findByPk(studentId, { - attributes: ['programmeId'] - }); + const result = await db.transaction(async (t) => { + // Fetch the student and advising session + const [student, advisingSession] = await Promise.all([ + Student.findByPk(studentId, { + attributes: ['programmeId'], + transaction: t + }), + AdvisingSession.findOne({ + where: { studentId, semesterId }, + attributes: ['updatedAt', 'planStatus', 'id'], + transaction: t + }) + ]); - if (!student) { - throw new Error("Student not found."); - } - // Fetch the advising session for the student and semester - const advisingSession = await AdvisingSession.findOne({ - where: { studentId, semesterId }, - attributes: ['updatedAt', 'planStatus'] - }); + if (!student) throw new Error("Student not found."); + if (!advisingSession) throw new Error("No advising session found."); - if (!advisingSession) { - throw new Error("No advising session found for the given student and semester."); - } - - // Fetch the selected courses for the advising session - const selectedCourses = await SelectedCourse.findAll({ - include: [ - { - model: AdvisingSession, - where: { - studentId: studentId, - semesterId: semesterId - }, - attributes: [] - }, - { - model: Course, - attributes: ['code', 'title', 'credits'], + // Fetch selected and completed courses in parallel + const [selectedCourses, studentCourses, electiveRequirements] = await Promise.all([ + SelectedCourse.findAll({ + where: { advisingSessionId: advisingSession.id }, + include: [ + { + model: Course, + attributes: ['code', 'title', 'credits'], + include: [{ + model: ProgrammeCourse, + where: { programmeId: student.programmeId }, + attributes: ['typeId'], + include: [{ model: Type, attributes: ['type'] }] + }] + } + ], + attributes: [], + transaction: t + }), + StudentCourse.findAll({ + where: { studentId }, include: [{ - model: ProgrammeCourse, - where: { programmeId: student.programmeId }, - attributes: ['typeId'], + model: Course, + attributes: ['code', 'title', 'credits'], include: [{ - model: Type, - attributes: ['type'] + model: ProgrammeCourse, + where: { programmeId: student.programmeId }, + attributes: ['typeId'], + include: [{ model: Type, attributes: ['type'] }] }] - }] + }], + attributes: [], + transaction: t + }), + ElectiveRequirement.findAll({ + where: { programmeId: student.programmeId }, + include: [{ model: Type, attributes: ['type'] }], + attributes: ['amount'], + transaction: t + }) + ]); + + // Transform data and calculate credits remaining (similar to original logic) + const completedCourseData = studentCourses.map(sc => ({ + courseCode: sc.course.code, + courseTitle: sc.course.title, + credits: sc.course.credits, + type: sc.course.programmeCourses[0]?.type?.type || 'Unknown', + selected: false, + completed: true + })); + + const selectedCourseData = selectedCourses.map(sc => ({ + courseCode: sc.course.code, + courseTitle: sc.course.title, + credits: sc.course.credits, + type: sc.course.programmeCourses[0]?.type?.type || 'Unknown', + selected: true, + completed: false + })); + + const electiveRequirementsMap = electiveRequirements.reduce((acc, req) => { + acc[req.type.type] = req.amount; + return acc; + }, {}); + + const allCourses = [...completedCourseData, ...selectedCourseData]; + + const coursesByType = allCourses.reduce((acc, course) => { + if (!acc[course.type]) acc[course.type] = []; + acc[course.type].push(course); + return acc; + }, {}); + + const calculateCreditsRemaining = (courses, type) => { + const requiredCredits = electiveRequirementsMap[type] || 0; + const completedCredits = courses.filter(course => course.completed).reduce((sum, course) => sum + course.credits, 0); + return Math.max(requiredCredits - completedCredits, 0); + }; + + const plan = Object.entries(coursesByType).map(([category, courses]) => ({ + category, + creditsRemaining: calculateCreditsRemaining(courses, category), + totalCreditsForCategory: electiveRequirementsMap[category] || 0, + courses: courses.map(course => ({ + courseCode: course.courseCode, + courseTitle: course.courseTitle, + type: course.type, + selected: course.selected, + completed: course.completed, + credits: course.credits + })) + })); + + return { + [studentId]: { + lastUpdated: advisingSession.updatedAt.toISOString().split('T')[0], + status: advisingSession.planStatus || "Pending", + plan: plan, + limit: 15 } - ], - attributes: [] + }; }); - // Fetch the completed courses for the student in the semester - const studentCourses = await StudentCourse.findAll({ - where: { - studentId: studentId, - }, - include: [{ - model: Course, - attributes: ['code', 'title', 'credits'], - include: [{ - model: ProgrammeCourse, - where: { programmeId: student.programmeId }, - attributes: ['typeId'], - include: [{ - model: Type, - attributes: ['type'] - }] - }] - }], - attributes: [] - }); + return result; + } catch (error) { + console.error("Error fetching student course plan:", error.message); + throw new Error(error.message || "Internal server error"); + } +} +async function getNewStudentCoursePlan(studentId, semesterId, programmeId) { + try { + // Fetch completed courses and semester in parallel + const [completedCourses, semester, programmeCourses, eligibleCourses] = await Promise.all([ + StudentCourse.findAll({ + attributes: ['courseCode'], + where: { + studentId, + grade: { + [Op.in]: ['A+', 'A', 'A-', 'B+', 'B', 'B-', 'C+', 'C', 'EX'] + } + } + }).then(courses => new Set(courses.map(c => c.courseCode))), // Use Set for faster lookups + Semester.findByPk(semesterId, { attributes: ['num'] }), + ProgrammeCourse.findAll({ + attributes: ['courseCode', 'typeId'], + where: { programmeId }, + include: [{ model: Type, attributes: ['type'] }] + }), + getAllEligibleCourses(studentId).then(courses => new Set(courses.map(course => course.dataValues.code))) // Use Set for faster lookups + ]); - // Transform the data - const completedCourseData = studentCourses.map(sc => ({ - courseCode: sc.course.code, - courseTitle: sc.course.title, - credits: sc.course.credits, - type: sc.course.programmeCourses[0]?.type?.type || 'Unknown', - selected: false, - completed: true - })); + const listOfCoursesBySemesterNum = await Course.findAll({ + attributes: ['code'], + where: { semester: semester.num } + }).then(courses => new Set(courses.map(course => course.code))); // Use Set for faster lookups + // Initialize course categories + const courseCategories = { + "L1CORE": initializeCategory(), + "L2CORE": initializeCategory(), + "L3CORE": initializeCategory(), + "ADVELECTIVE": initializeCategory(), + "CIELECTIVE": initializeCategory(), + "CIMELECTIVE": initializeCategory(), + "FOUN": initializeCategory() + }; - const selectedCourseData = selectedCourses.map(sc => ({ - courseCode: sc.course.code, - courseTitle: sc.course.title, - credits: sc.course.credits, - type: sc.course.programmeCourses[0]?.type?.type || 'Unknown', - selected: true, - completed: false - })); + // Populate course categories + programmeCourses.forEach(pc => { + const course = pc.courseCode; + const category = pc.type.dataValues.type; + if (courseCategories[category]) { + courseCategories[category].courses.push(createCourseInfo(course, completedCourses.has(course), listOfCoursesBySemesterNum.has(course))); + } + }); - // Fetch elective requirements for the student's programme - const electiveRequirements = await ElectiveRequirement.findAll({ - where: { programmeId: student.programmeId }, - include: [{ - model: Type, - attributes: ['type'] - }], - attributes: ['amount'] + // Fetch course details and update categories + const coursesData = await Course.findAll({ + where: { code: { [Op.in]: Object.keys(courseCategories).flatMap(cat => courseCategories[cat].courses.map(c => c.courseId)) } }, + attributes: ['code', 'title', 'credits'] }); - // Create a map of elective requirements - const electiveRequirementsMap = electiveRequirements.reduce((acc, req) => { - acc[req.type.type] = req.amount; - return acc; - }, {}); + const courseDataMap = new Map(coursesData.map(course => [course.code, { title: course.title, credits: course.credits }])); + for (let category in courseCategories) { + courseCategories[category].courses.forEach(courseInfo => { + const courseDetails = courseDataMap.get(courseInfo.courseId); + if (courseDetails) { + courseInfo.courseName = courseDetails.title; + courseInfo.credits = courseDetails.credits; + courseCategories[category].totalCredits += courseDetails.credits; + if (courseInfo.completed) { + courseCategories[category].completedCredits += courseDetails.credits; + } + } + }); + } - // Combine completed and selected courses - const allCourses = [...completedCourseData, ...selectedCourseData]; + // Fetch elective requirements and calculate remaining credits + const electiveRequirements = await ElectiveRequirement.findAll({ + attributes: ['amount', 'typeId'], + where: { programmeId }, + include: [{ model: Type, attributes: ['type'] }] + }); - // Group courses by type (category) - const coursesByType = allCourses.reduce((acc, course) => { - if (!acc[course.type]) { - acc[course.type] = []; + electiveRequirements.forEach(req => { + const category = req.type.dataValues.type; + if (courseCategories[category]) { + courseCategories[category].requiredCredits = req.amount; + courseCategories[category].creditsRemaining = req.amount - courseCategories[category].completedCredits; } - acc[course.type].push(course); - return acc; - }, {}); - - // Calculate credits remaining for each category - const calculateCreditsRemaining = (courses, type) => { - const requiredCredits = electiveRequirementsMap[type] || 0; - const completedCredits = courses.filter(course => course.completed).reduce((sum, course) => sum + course.credits, 0); - return Math.max(requiredCredits - completedCredits, 0); - }; + }); - - // Construct the plan array - const plan = Object.entries(coursesByType).map(([category, courses]) => ({ + // Prepare final output + const plan = Object.entries(courseCategories).map(([category, courses]) => ({ category, - creditsRemaining: calculateCreditsRemaining(courses, category), - totalCreditsForCategory: electiveRequirementsMap[category] || 0, - courses: courses.map(course => ({ - courseCode: course.courseCode, - courseTitle: course.courseTitle, - type: course.type, - selected: course.selected, - completed: course.completed, - credits: course.credits - })) + creditsRemaining: courses.creditsRemaining || 0, + totalCreditsForCategory: courses.requiredCredits || 0, + courses: courses.courses })); - // Prepare the response object in the specified format + const totalCreditsCompleted = Object.values(courseCategories).reduce((sum, { completedCredits }) => sum + completedCredits, 0); + const totalRequiredCredits = Object.values(courseCategories).reduce((sum, { requiredCredits }) => sum + requiredCredits, 0); + const totalCreditsRemaining = totalRequiredCredits - totalCreditsCompleted; + return { - [studentId]: { - lastUpdated: advisingSession.updatedAt.toISOString().split('T')[0], // Use the updatedAt from AdvisingSession - status: advisingSession.planStatus || "Pending", // Use the status from AdvisingSession if it exists, otherwise default to "Pending" - plan: plan, - limit: 15 - } + status: "New", + plan, + totalCreditsCompleted, + totalCreditsRemaining, + totalRequiredCredits, + limit: 15 }; - } catch (error) { - console.error("Error fetching student course plan:", error.message); - throw new Error(error.message || "Internal server error"); + console.error('Error in getNewStudentCoursePlan:', error); + throw new Error('Failed to generate new course plan'); } } -module.exports = { getStudentCoursePlan, getStudentCoursePlanByStudentIdAndSemesterId } \ No newline at end of file + +function initializeCategory() { + return { courses: [], totalCredits: 0, completedCredits: 0, requiredCredits: 0, creditsRemaining: 0 }; +} + +function createCourseInfo(courseId, completed, available) { + return { courseId, courseName: '', credits: 0, completed, selected: completed, available }; +} + + +module.exports = { getStudentCoursePlan, getStudentCoursePlanByStudentIdAndSemesterId, getNewStudentCoursePlan } \ No newline at end of file diff --git a/controllers/getStudentCourses.js b/controllers/getStudentCourses.js index 1bcb23558..6d3eecd6d 100644 --- a/controllers/getStudentCourses.js +++ b/controllers/getStudentCourses.js @@ -23,7 +23,7 @@ async function getStudentsCourses(studentId) { ], attributes: ['id', 'courseCode', 'grade', 'semesterId'] }); - console.log(studentCourses); + // Process the data in one step without the need for nested promises const processedCourses = studentCourses.map(course => { const { diff --git a/controllers/getStudentYear.js b/controllers/getStudentYear.js index a95628b3b..2f0434396 100644 --- a/controllers/getStudentYear.js +++ b/controllers/getStudentYear.js @@ -12,7 +12,7 @@ function getStudentYear( transcript ) { const yearsPassed = currentYear - admitYear; - // console.log(`Years passed since admit term: ${yearsPassed}`); + return yearsPassed; } diff --git a/controllers/updateCoursePlan.js b/controllers/updateCoursePlan.js index 5c05afeb1..e5f44661e 100644 --- a/controllers/updateCoursePlan.js +++ b/controllers/updateCoursePlan.js @@ -13,7 +13,6 @@ async function updatePlanStatus(studentId, semesterId, newStatus) { } } ); - console.log(plan); return plan } catch (error) { console.log("Unable to update CoursePlan Status:", error.message) diff --git a/database.sqlite b/database.sqlite index 95dd02291..a224fd232 100644 Binary files a/database.sqlite and b/database.sqlite differ diff --git a/docker-compose.yml b/docker-compose.yml index a8b27d940..84a4a1b1a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,8 +4,8 @@ services: image: postgres:13.5 restart: always environment: - - POSTGRES_USER=myadvisor - - POSTGRES_PASSWORD=myadvisorpassword + - POSTGRES_USER=postgres + - POSTGRES_PASSWORD=postgres - POSTGRES_DB=myadvisor volumes: - ./postgres-data:/var/lib/postgresql/data diff --git a/eligible_courses_query.js b/eligible_courses_query.js index e70e1611a..9baa5486d 100644 --- a/eligible_courses_query.js +++ b/eligible_courses_query.js @@ -350,6 +350,29 @@ async function getAllStudentInformation() { } } +async function getCountOfGradesByStudentId(studentId) { + const query = ` + SELECT grade, COUNT(*) AS grade_count + FROM studentcourses + WHERE studentId = :studentId + GROUP BY grade + ORDER BY grade; + `; + + try { + const results = await sequelize.query(query, { + replacements: { studentId: studentId }, + type: sequelize.QueryTypes.SELECT + }); + + console.log(results); + return results; + } catch (error) { + console.error('Error executing query:', error); + } +} + + async function getColumnNamesAndTypes(tableName) { const query = ` SELECT diff --git a/middleware/studentAccountVerification.js b/middleware/studentAccountVerification.js index 487dff722..28d92c4f7 100644 --- a/middleware/studentAccountVerification.js +++ b/middleware/studentAccountVerification.js @@ -20,7 +20,6 @@ module.exports = async (req, res, next) => { } const payload = jwt.verify(token, process.env.studentSecret); - //console.log(payload); req.user = payload.user; next(); diff --git a/package-lock.json b/package-lock.json index e1ea5b6ff..eb9f6759f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,6 @@ "dotenv": "^8.6.0", "express": "^4.19.2", "express-session": "^1.17.1", - "express-validator": "^7.2.0", "fs": "^0.0.1-security", "html-pdf": "^3.0.1", "jsonwebtoken": "^8.5.1", @@ -2919,18 +2918,6 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==" }, - "node_modules/express-validator": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/express-validator/-/express-validator-7.2.0.tgz", - "integrity": "sha512-I2ByKD8panjtr8Y05l21Wph9xk7kk64UMyvJCl/fFM/3CTJq8isXYPLeKW/aZBCdb/LYNv63PwhY8khw8VWocA==", - "dependencies": { - "lodash": "^4.17.21", - "validator": "~13.12.0" - }, - "engines": { - "node": ">= 8.0.0" - } - }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", diff --git a/package.json b/package.json index 766ce2774..1b01ffb24 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,6 @@ "dotenv": "^8.6.0", "express": "^4.19.2", "express-session": "^1.17.1", - "express-validator": "^7.2.0", "fs": "^0.0.1-security", "html-pdf": "^3.0.1", "jsonwebtoken": "^8.5.1", diff --git a/routes/admin.js b/routes/admin.js index a86519a12..77410711f 100644 --- a/routes/admin.js +++ b/routes/admin.js @@ -72,8 +72,7 @@ router.post("/create/admin", staffAccountVerification, async (req, res) => { // Create Student Account router.post("/create/student", async (req, res) => { try { - // destructure data entered - console.log(req.body); + const { studentId, firstName, lastName, email, year, password } = req.body; let { programmeId } = req.body; @@ -117,7 +116,6 @@ router.post("/create/student", async (req, res) => { router.get("/course-plan/all/:semesterId", staffAccountVerification, async (req, res) => { try { const semesterId = req.params.semesterId; - console.log("Requested semester ID:", semesterId); if (!semesterId) { return res.status(400).json({ message: 'Semester ID is required' }); @@ -126,7 +124,6 @@ router.get("/course-plan/all/:semesterId", staffAccountVerification, async (req, const coursePlans = await getAllCoursePlans(semesterId); if (coursePlans && coursePlans.advisingsessions) { - console.log(coursePlans); const formattedData = { semesterId: coursePlans.id, semesterNum: coursePlans.num, @@ -159,7 +156,6 @@ router.get("/course-plan/all/:semesterId", staffAccountVerification, async (req, router.get("/course-plan/:semesterId/:studentId", staffAccountVerification, async (req, res) => { const coursePlan = await getStudentCoursePlan(req.params.studentId, req.params.semesterId); if (coursePlan) { - console.log(coursePlan); res.status(200).json(coursePlan); } else { res.status(404).send("Course Plan for Student Not Found"); @@ -169,17 +165,14 @@ router.get("/course-plan/:semesterId/:studentId", staffAccountVerification, asyn // post/update courseplan(advisingSession) of a student for a semester Confirm or Return router.put("/course-plan/review/:semesterId/:studentId/:decision", async (req, res) => { const decision = req.params.decision; - console.log(decision); const plan = await updatePlanStatus(req.params.studentId, req.params.semesterId, decision); if (decision === 'Confirmed' && plan) { - console.log("Approved"); res.status(200).send({ message: "Course Plan Approved", student: req.params.studentId }); } else if (decision === 'Rejected' && plan) { - console.log("Rejected"); res.status(200).send({ message: "Course Plan Not Approved", student: req.params.studentId @@ -248,25 +241,19 @@ router.post('/parse/programmeCourse', upload.single('file'), async (req, res) => const csvData = req.file.buffer.toString('utf8'); const results = await parseCSVData(csvData); - //console.log("data found", results); // Create Programme Entries for (let i = 0; i < results[0].data.length; i++) { - //console.log("item number:: ", i); const programme = await Programme.findOne({ where: { id: results[0].data[i], name: results[1].data[i] } }); - // console.log("programme::> ", programme); - // console.log(" prog Id: ", results[0].data[i]); - // console.log(" name: ", results[1].data[i]); if (programme === null) { - console.log("new programme: ", results[1].data[i]); await Programme.create({ id: results[0].data[i], @@ -292,13 +279,6 @@ router.post('/parse/programmeCourse', upload.single('file'), async (req, res) => } }); - // console.log("courseCode: ", results[4].data[i] ); - // console.log("courseTitle: ", results[5].data[i] ); - // console.log("level: ", results[6].data[i] ); - // console.log("semester: ", results[7].data[i] ); - // console.log("credits: ", results[8].data[i] ); - // console.log("description: ", results[9].data[i] ); - if (!course) { await Course.create({ courseCode: results[4].data[i], @@ -368,8 +348,6 @@ router.post('/parse/programmeCourse', upload.single('file'), async (req, res) => } }); - // console.log("courseCode: ", results[4].data[i]); - // console.log("prereq: ", prereqCourseCodes[j]); if (!prerequisite) { await Prerequisite.create({ @@ -395,8 +373,6 @@ router.post('/parse/programmeCourse', upload.single('file'), async (req, res) => } }); - // console.log("courseCode: ", results[4].data[i]); - // console.log("prereq: ", prereqCourseCodes[j]); if (!antirequisite) { await Antirequisite.create({ @@ -453,7 +429,6 @@ router.post('/parse/programmeCourseXLSX', upload.single('file'), async (req, res // ==========--------put courses in database /**/ for (let i = 0; i < courses.length; i++) { - // console.log("courseCode::> ",courses[i].courseCode); try { // check if courses is already added @@ -490,7 +465,7 @@ router.post('/parse/programmeCourseXLSX', upload.single('file'), async (req, res // check if programme is already added const programme = await Programme.findOne({ where: { id: programmes[i].programmeID } }); - // console.log("programme::> ",programme); + if (programme) { console.log("programme exist"); diff --git a/routes/student.js b/routes/student.js index 9449087ee..07389af01 100644 --- a/routes/student.js +++ b/routes/student.js @@ -16,7 +16,7 @@ const { getStudentsCourses } = require("../controllers/getStudentCourses"); const { getDegreeProgress } = require("../controllers/getDegreeProgress"); const { getPlannedCourses } = require("../controllers/getPlannedCourses"); const { getCoursePlan } = require("../controllers/getCoursePlan"); -const { getStudentCoursePlan, getStudentCoursePlanByStudentIdAndSemesterId } = require("../controllers/getStudentCoursePlan"); +const { getStudentCoursePlan, getStudentCoursePlanByStudentIdAndSemesterId, getNewStudentCoursePlan } = require("../controllers/getStudentCoursePlan"); const { getAllCoursePlans } = require("../controllers/getAllCoursePlans"); const { Sequelize } = require('sequelize'); @@ -193,7 +193,29 @@ router.get("/degreeProgress", studentAccountVerification, async (req, res) => { degreeProgress ); -}) +}); + +router.get("/grades/:studentId", studentAccountVerification, async (req, res) => { + const studentId = req.params.studentId; + const grades = await StudentCourse.findAll({ + where: { studentId }, + attributes: ['grade', [Sequelize.fn('COUNT', Sequelize.col('grade')), 'count']], + group: ['grade'], + order: [['grade', 'ASC']], + }); + + if(!grades){ + return res.status(404).json({ error: `Grades for student ID ${studentId} not found.` }); + } + if(grades.length === 0){ + return res.status(200).json(grades); + } + const formattedData = grades.map(grade => ({ + grade: grade.dataValues.grade, + count: grade.dataValues.count + })); + res.status(200).json(formattedData); +}); /** * GET /courses/:studentId @@ -252,13 +274,6 @@ router.get("/:studentId/course-plan/:semesterId", studentAccountVerification, as const studentId = req.params.studentId; const semesterId = req.params.semesterId; - let response = { - lastUpdated: new Date(), - status: "New", - plan: [], - limit: 15 - }; - try { // Validate inputs if (!studentId || !semesterId) { @@ -266,166 +281,26 @@ router.get("/:studentId/course-plan/:semesterId", studentAccountVerification, as } // Fetch student's program ID - const student = await Student.findByPk(studentId); + const student = await Student.findByPk(studentId, { attributes: ['programmeId'] }); if (!student) { return res.status(404).json({ error: `Student with ID ${studentId} not found` }); } const programmeId = student.programmeId; + // Fetch advising session if exists const advisingSession = await AdvisingSession.findOne({ - where: { - studentId, - semesterId - } + where: { studentId, semesterId }, + attributes: ['updatedAt', 'planStatus'] }); if (advisingSession) { - const studentCoursePlanByStudentIdAndSemesterId = await getStudentCoursePlanByStudentIdAndSemesterId(studentId, semesterId); - response = studentCoursePlanByStudentIdAndSemesterId; + const studentCoursePlan = await getStudentCoursePlanByStudentIdAndSemesterId(studentId, semesterId); + return res.status(200).json({ message: 'Course plan retrieved successfully', data: studentCoursePlan }); } else { - // Fetch completed courses - const completedCourses = await StudentCourse.findAll({ - attributes: ['courseCode'], - where: { - studentId, - grade: { - [Op.in]: ['A+', 'A', 'A-', 'B+', 'B', 'B-', 'C+', 'C', 'EX'] - } - } - }).then(courses => courses.map(c => c.courseCode)); - - // Fetch the semester and courses for that semester - const semester = await Semester.findByPk(semesterId); - const coursesBySemesterNum = await Course.findAll({ - where: { semester: semester.num } - }); - - let listOfCoursesBySemesterNum = coursesBySemesterNum.map(course => course.code); - - // Fetch all programme courses - const programmeCourses = await ProgrammeCourse.findAll({ - attributes: ['courseCode', 'typeId'], - where: { programmeId }, - include: [{ model: Type, attributes: ['type'] }] - }); - - // Fetch eligible courses - const eligibleCourses = await getAllEligibleCourses(studentId); - const listOfEligibleCourses = eligibleCourses.map(course => course.dataValues.code); - - // Structure the courses by categories using the Types table - const courseCategories = { - "L1CORE": { courses: [], totalCredits: 0, completedCredits: 0, requiredCredits: 0, creditsRemaining: 0 }, - "L2CORE": { courses: [], totalCredits: 0, completedCredits: 0, requiredCredits: 0, creditsRemaining: 0 }, - "L3CORE": { courses: [], totalCredits: 0, completedCredits: 0, requiredCredits: 0, creditsRemaining: 0 }, - "ADVELECTIVE": { courses: [], totalCredits: 0, completedCredits: 0, requiredCredits: 0, creditsRemaining: 0 }, - "CIELECTIVE": { courses: [], totalCredits: 0, completedCredits: 0, requiredCredits: 0, creditsRemaining: 0 }, - "CIMELECTIVE": { courses: [], totalCredits: 0, completedCredits: 0, requiredCredits: 0, creditsRemaining: 0 }, - "FOUN": { courses: [], totalCredits: 0, completedCredits: 0, requiredCredits: 0, creditsRemaining: 0 } - }; - - // Add programme courses to their respective categories - programmeCourses.forEach(pc => { - const course = pc.courseCode; - const category = pc.type.dataValues.type; - - const courseInfo = { - courseId: course, - courseName: '', // Will be filled below - credits: 0, // Will be filled below - completed: completedCourses.includes(course), - selected: completedCourses.includes(course), - available: listOfCoursesBySemesterNum.includes(course) - }; - - if (courseCategories[category]) { - courseCategories[category].courses.push(courseInfo); - } - }); - - // Fill in course names and credits - const coursesData = await Course.findAll({ - where: { - code: { [Op.in]: programmeCourses.map(pc => pc.courseCode) } - } - }); - - coursesData.forEach(course => { - for (let category in courseCategories) { - const courseInfo = courseCategories[category].courses.find(c => c.courseId === course.code); - if (courseInfo) { - courseInfo.courseName = course.title; - courseInfo.credits = course.credits; - courseCategories[category].totalCredits += course.credits; - if (courseInfo.completed) { - courseCategories[category].completedCredits += course.credits; - } - } - } - }); - - // Fetch the total amount of credits required for the programme using ElectiveRequirement - const electiveRequirements = await PCR.findAll({ - include: [{ model: Type, attributes: ['type'] }], - attributes: ['amount', 'typeId'], - where: { programmeId } - }); - - // Map the elective requirements to their respective categories - electiveRequirements.forEach(req => { - const category = req.type.dataValues.type; - if (courseCategories[category]) { - courseCategories[category].requiredCredits = req.amount; - } - courseCategories[category].creditsRemaining = courseCategories[category].requiredCredits - courseCategories[category].completedCredits; - }); - - // Calculate total credits completed and remaining - let totalCreditsCompleted = 0; - let totalRequiredCredits = 0; - for (let category in courseCategories) { - totalCreditsCompleted += courseCategories[category].completedCredits; - totalRequiredCredits += courseCategories[category].requiredCredits; - } - const totalCreditsRemaining = totalRequiredCredits - totalCreditsCompleted; - - // Update availability based on eligible courses - for (let category in courseCategories) { - courseCategories[category].courses.forEach(course => { - if (listOfEligibleCourses.includes(course.courseId)) { - course.available = true; - } - }); - } - // construct the plan array - const plan = Object.entries(courseCategories).map(([category, courses]) => ({ - category, - creditsRemaining: courses.creditsRemaining, - totalCreditsForCategory: courses.requiredCredits, - courses: courses.courses.map(course => ({ - courseId: course.courseId, - courseName: course.courseName, - credits: course.credits, - completed: course.completed, - selected: course.selected, - available: course.available - })) - })); - - // Add credits summary to the response - response = { - [studentId]:{ - status: "New", - plan: plan, - totalCreditsCompleted, - totalCreditsRemaining, - totalRequiredCredits, - limit: 15 - } - }; + // Generate a new course plan + const newCoursePlan = await getNewStudentCoursePlan(studentId, semesterId, programmeId); + return res.status(200).json({ message: 'New course plan generated successfully', data: { [studentId]: newCoursePlan } }); } - - return res.status(200).json({ message: 'Course plan data retrieved successfully', data: response }); } catch (error) { console.error('Error fetching course plan:', error.message); return res.status(500).json({ error: 'Failed to retrieve course plan data' }); @@ -473,7 +348,3 @@ router.get("/course-plans/:semesterId", studentAccountVerification, async (req, module.exports = router; - - - - diff --git a/utilities/validationSchema.js b/utilities/validationSchema.js deleted file mode 100644 index 0937f30a6..000000000 --- a/utilities/validationSchema.js +++ /dev/null @@ -1,90 +0,0 @@ -const createUserValidationSchema = { - username: { - isLength: { - options: { - min: 5, - max: 32 - }, - errorMessage: 'Username must be between 5 and 32 characters' - }, - notEmpty: { - errorMessage: 'Username is required' - }, - isString: { - errorMessage: 'Username must be a string' - }, - }, - name: { - isLength: { - options: { - min: 5, - max: 32 - }, - errorMessage: 'Name must be between 5 and 32 characters' - }, - notEmpty: { - errorMessage: 'Name is required' - }, - isString: { - errorMessage: 'Name must be a string' - }, - }, - email: { - isEmail: { - errorMessage: 'Invalid email' - }, - notEmpty: { - errorMessage: 'Email is required' - }, - isString: { - errorMessage: 'Email must be a string' - }, - }, - password: { - isLength: { - options: { - min: 8, - max: 32 - }, - errorMessage: 'Password must be between 8 and 32 characters' - }, - notEmpty: { - errorMessage: 'Password is required' - }, - isString: { - errorMessage: 'Password must be a string' - }, - }, - age: { - isInt: { - options: { - min: 18, - max: 99 - }, - errorMessage: 'Age must be between 18 and 99' - }, - notEmpty: { - errorMessage: 'Age is required' - }, - isNumeric: { - errorMessage: 'Age must be a number' - }, - }, - gender: { - notEmpty: { - errorMessage: 'Gender is required' - }, - }, - ip_address: { - isIP: { - errorMessage: 'Invalid IP address' - }, - } -}; - - - - -module.exports = { - createUserValidationSchema -} \ No newline at end of file