diff --git a/data/patchCourses.js b/data/patchCourses.js new file mode 100644 index 0000000..7d30a5d --- /dev/null +++ b/data/patchCourses.js @@ -0,0 +1,17 @@ +import { connect } from "./db.js"; +import dotenv from "dotenv"; +import SISCourseV from '../model/SISCourseV.js'; +// import mongoose from 'mongoose'; + +// dotenv.config(); +// mongoose.set('strictQuery', true); + +async function patchCourses() { + connect(); + + const courses = await SISCourseV.find(); + + console.log(courses); +} + +patchCourses() \ No newline at end of file diff --git a/data/replaceOldMajors.js b/data/replaceOldMajors.js new file mode 100644 index 0000000..1061862 --- /dev/null +++ b/data/replaceOldMajors.js @@ -0,0 +1,79 @@ +import { connect } from "./db.js"; +import User from '../model/User.js'; +import Plan from '../model/Plan.js'; + +async function replaceOldWithNew() { + connect(); + + const users = await User.find({}); + for (let user of users) { + for (let plan_id of user.plan_ids) { + let plan = await Plan.findById(plan_id).exec(); + if (plan !== null && plan.majors != null && plan.majors.length > 0) { + // console.log("before: " + plan.majors); + for (let ind in plan.majors) { + let major = plan.majors[ind]; + if (major.includes("B.A. Computer Science (NEW - 2021 & after)")) { + plan.majors[ind] = "B.A. Computer Science"; + } else if (major.includes("B.S. Computer Science (OLD - Pre-2021)") + || major.includes("B.S. Computer Science (NEW - 2021 & after)")) { + plan.majors[ind] = "B.S. Computer Science"; + } else if (major.includes("Minor Computer Science (OLD - Pre-2021)")) { + plan.majors[ind] = "Minor Computer Science"; + } else if (major.includes("Minor Computer Science (NEW - 2021 & after)")) { + plan.majors[ind] = "Minor Computer Science"; + } else if (major.includes("Minor Applied Mathematics & Statistics (OLD - Pre-2021)") + || major.includes("Minor Applied Mathematics & Statistics (NEW - 2021 & after)")) { + plan.majors[ind] = "Minor Applied Mathematics & Statistics"; + } + } + plan.majors = [...new Set(plan.majors)]; + // console.log("after: " + plan.majors + ""); + // console.log("done!\n"); + } + if (plan !== null && plan.major_ids != null && plan.major_ids.length > 0) { + console.log("before: " + plan.major_ids); + for (let ind in plan.major_ids) { + let major = plan.major_ids[ind]; + if (major.includes("B.A. Computer Science (NEW - 2021 & after)")) { + plan.major_ids[ind] = "B.A. Computer Science"; + } else if (major.includes("B.S. Computer Science (OLD - Pre-2021)") + || major.includes("B.S. Computer Science (NEW - 2021 & after)")) { + plan.major_ids[ind] = "B.S. Computer Science"; + } else if (major.includes("Minor Computer Science (OLD - Pre-2021)")) { + plan.major_ids[ind] = "Minor Computer Science"; + } else if (major.includes("Minor Computer Science (NEW - 2021 & after)")) { + plan.major_ids[ind] = "Minor Computer Science"; + } else if (major.includes("Minor Applied Mathematics & Statistics (OLD - Pre-2021)") + || major.includes("Minor Applied Mathematics & Statistics (NEW - 2021 & after)")) { + plan.major_ids[ind] = "Minor Applied Mathematics & Statistics"; + } + } + plan.major_ids = [...new Set(plan.major_ids)]; + console.log("after: " + plan.major_ids + ""); + console.log("done!\n"); + } + await plan.save(); + } + } + console.log("done") +} + +replaceOldWithNew(); + + +async function checkResult() { + connect(); + + const users = await User.find({}); + for (let user of users) { + for (let plan_id of user.plan_ids) { + let plan = await Plan.findById(plan_id).exec(); + if (plan !== null && plan.majors != null && plan.majors.length > 0) { + console.log(plan.majors); + } + } + } +} + +// checkResult(); \ No newline at end of file diff --git a/data/updatePrereq.js b/data/updatePrereq.js new file mode 100644 index 0000000..ef2caaa --- /dev/null +++ b/data/updatePrereq.js @@ -0,0 +1,342 @@ +import { connect } from "./db.js"; +import dotenv from "dotenv"; +import SISCourseV from '../model/SISCourseV.js'; + +dotenv.config(); + +async function updateIntroAlgo() { + connect(); + const number = "EN.601.433"; + const oldPrereq = [ + { + "Description": "EN.600.226/EN.601.226 AND (EN.553.171/EN.550.171 OR EN.553.172/EN.550.170 OR EN.600.271/EN.601.231", + "Expression": "EN.600.226[C]^AND^(^EN.553.171[C]^OR^EN.553.172[C]^OR^EN.600.271[C]^)", + "IsNegative": "N" + }, + { + "Description": "Students may receive credit for only one of EN.600.363, EN.600.463, EN.601.433, EN.601.633.", + "Expression": "EN.600.363[C]^OR^EN.601.633[C]", + "IsNegative": "Y" + } + ]; + + const newPrereq = [ + { + "Description": "EN.600.226/EN.601.226 AND (EN.553.171/EN.550.171 OR EN.553.172/EN.550.170 OR EN.600.271/EN.601.231 OR EN.601.230", + "Expression": "EN.600.226[C]^AND^(^EN.553.171[C]^OR^EN.553.172[C]^OR^EN.600.271[C]^OR^EN.601.230[C]^)", + "IsNegative": "N" + }, + { + "Description": "Students may receive credit for only one of EN.600.363, EN.600.463, EN.601.433, EN.601.633.", + "Expression": "EN.600.363[C]^OR^EN.601.633[C]", + "IsNegative": "Y" + } + ]; + + let data = await SISCourseV.find({number}); + let course = data[0]; + for (let version of course.versions) { + version.preReq = newPrereq; + } + await course.save(); + console.log("done!") +} + +// updateIntroAlgo(); + +async function checkUpdated() { + connect(); + const number = "EN.601.433"; + let data = await SISCourseV.find({number}); + let course = data[0]; + + for (let version of course.versions) { + console.log(version.preReq); + } +} + +checkUpdated(); + + + +/** Intro Algo + * { + "data": { + "_id": "635df458662f1ebee559601f", + "terms": [ + "Fall 2021", + "Spring 2023", + "Fall 2022", + "Spring 2022", + "Spring 2021", + "Fall 2020", + "Spring 2020", + "Fall 2019" + ], + "title": "Intro Algorithms", + "number": "EN.601.433", + "versions": [ + { + "department": "EN Computer Science", + "tags": [ + "BMED-BDS", + "COGS-COMPCG", + "CSCI-THRY" + ], + "preReq": [ + { + "Description": "EN.600.226/EN.601.226 AND (EN.553.171/EN.550.171 OR EN.553.172/EN.550.170 OR EN.600.271/EN.601.231 OR EN.601.230", + "Expression": "EN.600.226[C]^AND^(^EN.553.171[C]^OR^EN.553.172[C]^OR^EN.600.271[C]^OR^EN.601.230[C]^)", + "IsNegative": "N" + }, + { + "Description": "Students may receive credit for only one of EN.600.363, EN.600.463, EN.601.433, EN.601.633.", + "Expression": "EN.600.363[C]^OR^EN.601.633[C]", + "IsNegative": "Y" + } + ], + "coReq": [], + "restrictions": [], + "_id": "637042cbbfb975c236485bf9", + "areas": "EQ", + "term": "Fall 2021", + "school": "Whiting School of Engineering", + "credits": 3, + "wi": false, + "level": "Upper Level Undergraduate", + "bio": "This course concentrates on the design of algorithms and the rigorous analysis of their efficiency. topics include the basic definitions of algorithmic complexity (worst case, average case); basic tools such as dynamic programming, sorting, searching, and selection; advanced data structures and their applications (such as union-find); graph algorithms and searching techniques such as minimum spanning trees, depth-first search, shortest paths, design of online algorithms and competitive analysis. [Analysis]" + }, + { + "department": "EN Computer Science", + "tags": [ + "COGS-COMPCG", + "BMED-BDS", + "ARCH-ARCH", + "CSCI-THRY" + ], + "preReq": [ + { + "Description": "EN.600.226/EN.601.226 AND (EN.553.171/EN.550.171 OR EN.553.172/EN.550.170 OR EN.600.271/EN.601.231 OR EN.601.230", + "Expression": "EN.600.226[C]^AND^(^EN.553.171[C]^OR^EN.553.172[C]^OR^EN.600.271[C]^OR^EN.601.230[C]^)", + "IsNegative": "N" + }, + { + "Description": "Students may receive credit for only one of EN.600.363, EN.600.463, EN.601.433, EN.601.633.", + "Expression": "EN.600.363[C]^OR^EN.601.633[C]", + "IsNegative": "Y" + } + ], + "coReq": [], + "restrictions": [ + { + "RestrictionName": "Computer Science minors", + "Description": "" + }, + { + "RestrictionName": "UGrad Computer Engineering majors", + "Description": "" + }, + { + "RestrictionName": "UGrad Computer Science majors", + "Description": "" + } + ], + "_id": "635df5c8662f1ebee559ed3c", + "areas": "EQ", + "term": "Spring 2023", + "school": "Whiting School of Engineering", + "credits": 3, + "wi": false, + "level": "Upper Level Undergraduate", + "bio": "This course concentrates on the design of algorithms and the rigorous analysis of their efficiency. topics include the basic definitions of algorithmic complexity (worst case, average case); basic tools such as dynamic programming, sorting, searching, and selection; advanced data structures and their applications (such as union-find); graph algorithms and searching techniques such as minimum spanning trees, depth-first search, shortest paths, design of online algorithms and competitive analysis. [Analysis]" + }, + { + "department": "EN Computer Science", + "tags": [ + "BMED-BDS", + "COGS-COMPCG", + "CSCI-THRY" + ], + "preReq": [ + { + "Description": "EN.600.226/EN.601.226 AND (EN.553.171/EN.550.171 OR EN.553.172/EN.550.170 OR EN.600.271/EN.601.231 OR EN.601.230", + "Expression": "EN.600.226[C]^AND^(^EN.553.171[C]^OR^EN.553.172[C]^OR^EN.600.271[C]^OR^EN.601.230[C]^)", + "IsNegative": "N" + }, + { + "Description": "Students may receive credit for only one of EN.600.363, EN.600.463, EN.601.433, EN.601.633.", + "Expression": "EN.600.363[C]^OR^EN.601.633[C]", + "IsNegative": "Y" + } + ], + "coReq": [], + "restrictions": [], + "_id": "635df59b662f1ebee559da76", + "areas": "EQ", + "term": "Fall 2022", + "school": "Whiting School of Engineering", + "credits": 3, + "wi": false, + "level": "Upper Level Undergraduate", + "bio": "This course concentrates on the design of algorithms and the rigorous analysis of their efficiency. topics include the basic definitions of algorithmic complexity (worst case, average case); basic tools such as dynamic programming, sorting, searching, and selection; advanced data structures and their applications (such as union-find); graph algorithms and searching techniques such as minimum spanning trees, depth-first search, shortest paths, design of online algorithms and competitive analysis. [Analysis]" + }, + { + "department": "EN Computer Science", + "tags": [ + "COGS-COMPCG", + "BMED-BDS", + "ARCH-ARCH", + "CSCI-THRY" + ], + "preReq": [ + { + "Description": "EN.600.226/EN.601.226 AND (EN.553.171/EN.550.171 OR EN.553.172/EN.550.170 OR EN.600.271/EN.601.231 OR EN.601.230", + "Expression": "EN.600.226[C]^AND^(^EN.553.171[C]^OR^EN.553.172[C]^OR^EN.600.271[C]^OR^EN.601.230[C]^)", + "IsNegative": "N" + }, + { + "Description": "Students may receive credit for only one of EN.600.363, EN.600.463, EN.601.433, EN.601.633.", + "Expression": "EN.600.363[C]^OR^EN.601.633[C]", + "IsNegative": "Y" + } + ], + "coReq": [], + "restrictions": [], + "_id": "635df571662f1ebee559c810", + "areas": "EQ", + "term": "Spring 2022", + "school": "Whiting School of Engineering", + "credits": 3, + "wi": false, + "level": "Upper Level Undergraduate", + "bio": "This course concentrates on the design of algorithms and the rigorous analysis of their efficiency. topics include the basic definitions of algorithmic complexity (worst case, average case); basic tools such as dynamic programming, sorting, searching, and selection; advanced data structures and their applications (such as union-find); graph algorithms and searching techniques such as minimum spanning trees, depth-first search, shortest paths, design of online algorithms and competitive analysis. [Analysis]" + }, + { + "department": "EN Computer Science", + "tags": [ + "COGS-COMPCG", + "BMED-BDS", + "ARCH-ARCH", + "CSCI-THRY" + ], + "preReq": [ + { + "Description": "EN.600.226/EN.601.226 AND (EN.553.171/EN.550.171 OR EN.553.172/EN.550.170 OR EN.600.271/EN.601.231 OR EN.601.230", + "Expression": "EN.600.226[C]^AND^(^EN.553.171[C]^OR^EN.553.172[C]^OR^EN.600.271[C]^OR^EN.601.230[C]^)", + "IsNegative": "N" + }, + { + "Description": "Students may receive credit for only one of EN.600.363, EN.600.463, EN.601.433, EN.601.633.", + "Expression": "EN.600.363[C]^OR^EN.601.633[C]", + "IsNegative": "Y" + } + ], + "coReq": [], + "restrictions": [], + "_id": "635df501662f1ebee5599dcf", + "areas": "EQ", + "term": "Spring 2021", + "school": "Whiting School of Engineering", + "credits": 3, + "wi": false, + "level": "Upper Level Undergraduate", + "bio": "This course concentrates on the design of algorithms and the rigorous analysis of their efficiency. topics include the basic definitions of algorithmic complexity (worst case, average case); basic tools such as dynamic programming, sorting, searching, and selection; advanced data structures and their applications (such as union-find); graph algorithms and searching techniques such as minimum spanning trees, depth-first search, shortest paths, design of online algorithms and competitive analysis. [Analysis]" + }, + { + "department": "EN Computer Science", + "tags": [ + "BMED-BDS", + "COGS-COMPCG", + "CSCI-THRY" + ], + "preReq": [ + { + "Description": "EN.600.226/EN.601.226 AND (EN.553.171/EN.550.171 OR EN.553.172/EN.550.170 OR EN.600.271/EN.601.231 OR EN.601.230", + "Expression": "EN.600.226[C]^AND^(^EN.553.171[C]^OR^EN.553.172[C]^OR^EN.600.271[C]^OR^EN.601.230[C]^)", + "IsNegative": "N" + }, + { + "Description": "Students may receive credit for only one of EN.600.363, EN.600.463, EN.601.433, EN.601.633.", + "Expression": "EN.600.363[C]^OR^EN.601.633[C]", + "IsNegative": "Y" + } + ], + "coReq": [], + "restrictions": [], + "_id": "635df4c8662f1ebee559885b", + "areas": "EQ", + "term": "Fall 2020", + "school": "Whiting School of Engineering", + "credits": 3, + "wi": false, + "level": "Upper Level Undergraduate", + "bio": "This course concentrates on the design of algorithms and the rigorous analysis of their efficiency. topics include the basic definitions of algorithmic complexity (worst case, average case); basic tools such as dynamic programming, sorting, searching, and selection; advanced data structures and their applications (such as union-find); graph algorithms and searching techniques such as minimum spanning trees, depth-first search, shortest paths, design of online algorithms and competitive analysis. [Analysis]" + }, + { + "department": "EN Computer Science", + "tags": [ + "COGS-COMPCG", + "BMED-BDS", + "ARCH-ARCH", + "CSCI-THRY" + ], + "preReq": [ + { + "Description": "EN.600.226/EN.601.226 AND (EN.553.171/EN.550.171 OR EN.553.172/EN.550.170 OR EN.600.271/EN.601.231 OR EN.601.230", + "Expression": "EN.600.226[C]^AND^(^EN.553.171[C]^OR^EN.553.172[C]^OR^EN.600.271[C]^OR^EN.601.230[C]^)", + "IsNegative": "N" + }, + { + "Description": "Students may receive credit for only one of EN.600.363, EN.600.463, EN.601.433, EN.601.633.", + "Expression": "EN.600.363[C]^OR^EN.601.633[C]", + "IsNegative": "Y" + } + ], + "coReq": [], + "restrictions": [], + "_id": "635df489662f1ebee5596c56", + "areas": "EQ", + "term": "Spring 2020", + "school": "Whiting School of Engineering", + "credits": 3, + "wi": false, + "level": "Upper Level Undergraduate", + "bio": "This course concentrates on the design of algorithms and the rigorous analysis of their efficiency. topics include the basic definitions of algorithmic complexity (worst case, average case); basic tools such as dynamic programming, sorting, searching, and selection; advanced data structures and their applications (such as union-find); graph algorithms and searching techniques such as minimum spanning trees, depth-first search, shortest paths, design of online algorithms and competitive analysis. [Analysis]" + }, + { + "department": "EN Computer Science", + "tags": [ + "COGS-COMPCG", + "BME-BDS", + "BMED-BDS", + "CSCI-THRY" + ], + "preReq": [ + { + "Description": "EN.600.226/EN.601.226 AND (EN.553.171/EN.550.171 OR EN.553.172/EN.550.170 OR EN.600.271/EN.601.231 OR EN.601.230", + "Expression": "EN.600.226[C]^AND^(^EN.553.171[C]^OR^EN.553.172[C]^OR^EN.600.271[C]^OR^EN.601.230[C]^)", + "IsNegative": "N" + }, + { + "Description": "Students may receive credit for only one of EN.600.363, EN.600.463, EN.601.433, EN.601.633.", + "Expression": "EN.600.363[C]^OR^EN.601.633[C]", + "IsNegative": "Y" + } + ], + "coReq": [], + "restrictions": [], + "_id": "635df458662f1ebee5596020", + "areas": "EQ", + "term": "Fall 2019", + "school": "Whiting School of Engineering", + "credits": 3, + "wi": false, + "level": "Upper Level Undergraduate", + "bio": "This course concentrates on the design of algorithms and the rigorous analysis of their efficiency. topics include the basic definitions of algorithmic complexity (worst case, average case); basic tools such as dynamic programming, sorting, searching, and selection; advanced data structures and their applications (such as union-find); graph algorithms and searching techniques such as minimum spanning trees, depth-first search, shortest paths, design of online algorithms and competitive analysis. [Analysis]" + } + ], + "__v": 9 + } +} + * + */ \ No newline at end of file diff --git a/routes/course.js b/routes/course.js index 40f58af..e09f0b6 100644 --- a/routes/course.js +++ b/routes/course.js @@ -262,4 +262,53 @@ router.delete("/api/courses/:course_id", auth, async (req, res) => { } }); + + +// delete and create a new course +// need to provide new course as json object in request body +// distribution field is also updated +router.patch("/api/courses/:course_id", auth, async (req, res) => { + // delete the course + const c_id = req.params.course_id; + try { + // verify that course belongs to req user + const course = await Courses.findById(c_id).exec(); + if (!course) { + return errorHandler(res, 404, "Course not found."); + } else if (req.user._id !== course.user_id) { + return forbiddenHandler(res); + } + await Courses.findByIdAndDelete(c_id).exec(); + await Years + .findByIdAndUpdate( + course.year_id, + { $pull: { courses: course._id }}, + { runValidators: true } + ) + .exec(); + returnData(course, res); + } catch (err) { + errorHandler(res, 500, err); + } + + // insert a new course + const course = req.body.new_course; + if (!course || Object.keys(course).length == 0) { + return missingHandler(res, { course }); + } + if (course.user_id !== req.user._id) { + return forbiddenHandler(res); + } + + const retrievedCourse = await Courses.create(course); + // update year with new course + await Years + .findByIdAndUpdate(retrievedCourse.year_id, { + $push: { courses: retrievedCourse._id }, + }) + .exec(); + returnData(retrievedCourse, res); + +}); + export default router; diff --git a/tests/routes/course.spec.js b/tests/routes/course.spec.js index 1f57ec0..f071006 100644 --- a/tests/routes/course.spec.js +++ b/tests/routes/course.spec.js @@ -529,3 +529,63 @@ describe("Course Routes: PATCH /api/courses/dragged", () => { expect(res.status).toBe(500); }); }); + + + +describe("Course Routes: PATCH /api/courses/:course_id", () => { + it("Should return the new course added", async () => { + courses = await Courses.find({}); + let oldCourse = courses[0]; + const newCourse = SAMEPLE_COURSES[0]; + // change taken status to true + const res = await request + .patch(`/api/courses/${course._id}`) + .set("Authorization", `Bearer ${TEST_TOKEN_1}`) + .send(newCourse); + + expect(res.status).toBe(200); + // check course deleted + oldCourse = await Courses.findById(oldCourse._id); + expect(oldCourse).toBeNull(); + // check new course returned + const returnedCourse = res.body.data; + expect(returnedCourse.title).toBe(newCourse.title); + expect(returnedCourse.term).toBe(newCourse.term); + expect(returnedCourse.number).toBe(newCourse.number); + expect(returnedCourse.level).toBe(newCourse.level); + expect(returnedCourse.user_id).toBe(newCourse.user_id); + expect(returnedCourse.plan_id).toBe(newCourse.plan_id); + }); + + it("Should return status 403 for invalid user", async () => { + // course.taken is false by default + courses = await Courses.find({}); + let course = courses[0]; + // attempt to change taken status + const res = await request + .patch(`/api/courses/${course._id}`) + .set("Authorization", `Bearer ${TEST_TOKEN_2}`) + .send(SAMEPLE_COURSES[0]); + expect(res.status).toBe(403); + // check old course still there + const thatCourse = await Courses.findById(course._id); + expect(thatCourse.title).toBe(course.title); + }); + + it("Should return status 500 for invalid id", async () => { + const res = await request + .patch(`/api/courses/changeStatus/${INVALID_ID}`) + .set("Authorization", `Bearer ${TEST_TOKEN_1}`) + .send(SAMEPLE_COURSES[0]); + expect(res.status).toBe(500); + }); + + it("Should return status 400 for missing taken", async () => { + courses = await Courses.find({}); + let course = courses[0]; + const res = await request + .patch(`/api/courses/changeStatus/${course._id}`) + .set("Authorization", `Bearer ${TEST_TOKEN_1}`); + expect(res.status).toBe(400); + }); +});