diff --git a/_apidoc.js b/_apidoc.js index 4a3c908..c9aa07a 100644 --- a/_apidoc.js +++ b/_apidoc.js @@ -311,6 +311,94 @@ */ // ------------------------------------------------------------------------------------------ +// Coursework. +// ------------------------------------------------------------------------------------------ + +/** + * @api {post} /coursework/add Add Coursework + * @apiName AddCoursework + * @apiGroup Coursework + * @apiDescription Add a new coursework entry. + * + * @apiBody {ObjectId} student ID of the student (ObjectId). + * @apiBody {String} Coursework type that is either onCampus or offCampus. + * @apiBody {ObjectId} course ID of the Course in Coursework (ObjectId). + * @apiBody {ObjectId} task ID of the task in Coursework (ObjectId). + * @apiBody {String} objectID either its practicals or tutorial or assignment . + * @apiBody {ObjectId} activity Id of the activity in Coursework. + * @apiBody {Number} Marks in the Coursework. + * + * @apiSuccess {String} res Response message. + * @apiError (Error 500) DatabaseError Err message if there is an error inserting into the database. + * + * @apiSuccessExample Success-Response: + * HTTP/1.1 200 OK + * { + * "res": "Added coursework" + * } + * + * @apiErrorExample Error-Response: + * HTTP/1.1 500 Internal Server Error + * { + * "err": "Error while inserting in DB" + * } + */ + +/** + * @api {delete} /coursework/delete/:courseworkId Delete Coursework + * @apiName DeleteCoursework + * @apiGroup Coursework + * + * @apiParam {String} courseworkId The ID of the Coursework document to delete. + * + * @apiSuccess {String} res Success message indicating the deletion. + * + * @apiError (Error 500) DatabaseError Error message if there was an error during the deletion. + */ + +/** + * @api {post} /coursework/update Update Coursework + * @apiName UpdateCoursework + * @apiGroup Coursework + * @apiDescription Update existing coursework data. + * + * @apiBody {String} id ID of the Coursework to be updated. + * @apiBody {ObjectId} student ID of the student (ObjectId). + * @apiBody {String} Coursework type that is either onCampus or offCampus. + * @apiBody {ObjectId} course ID of the Course in Coursework (ObjectId). + * @apiBody {ObjectId} task ID of the task in Coursework (ObjectId). + * @apiBody {String} objectID either its practicals or tutorial or assignment . + * @apiBody {ObjectId} activity Id of the activity in Coursework. + * @apiBody {Number} Marks in the Coursework. + * + * @apiSuccess {String} res Coursework updated. + * @apiError (Error 500) DatabaseError Error in updating the database. + */ + +/** + * @api {get} /coursework/list Get Coursework List + * @apiName GetCourseworkList + * @apiGroup Coursework + * + * @apiQuery {ObjectId} student ID of the student (ObjectId). + * @apiQuery {String} Coursework type that is either onCampus or offCampus. + * @apiQuery {ObjectId} course ID of the Course in Coursework (ObjectId). + * @apiQuery {ObjectId} task ID of the task in Coursework (ObjectId). + * @apiQuery {String} objectID either its practicals or tutorial or assignment . + * @apiQuery {ObjectId} activity Id of the activity in Coursework. + * @apiQuery {Number} Marks in the Coursework. + * + * @apiSuccess {Coursework[]} res Array of filtered coursework documents. + * @apiSuccess {String} coursework._id ID of the coursework document given by the database. + * @apiSuccess {ObjectId} coursework.student ID of the student (ObjectId). + * @apiSuccess {String} coursework.type Coursework type that is either onCampus or offCampus. + * @apiSuccess {ObjectId} coursework.course ID of the Course in Coursework (ObjectId). + * @apiSuccess {ObjectId} coursework.task ID of the task in Coursework (ObjectId). + * @apiSuccess {String} coursework.objectID objectID either Practicals or Tutorial or Assignment . + * @apiSuccess {ObjectId} coursework.activity Id of the activity in Coursework. + * @apiSuccess {Number} coursework.marks Marks in the Coursework. + */ +======= // Module. // ------------------------------------------------------------------------------------------ @@ -335,4 +423,3 @@ * @apiSuccess {Number} module.hrsPerModule Number of hours required per module. * @apiSuccess {String[]} module.cognitiveLevels Array of cognitive levels of attainment as per Bloom's Taxanomy (L1-L6). */ - diff --git a/app.js b/app.js index 6801828..60dde7d 100644 --- a/app.js +++ b/app.js @@ -10,6 +10,7 @@ import usersRouter from "#routes/users"; import authRouter from "#routes/auth"; import accreditationRouter from "#routes/accreditation"; import infrastructureRouter from "#routes/infrastructure"; +import courseworkRouter from "#routes/coursework"; import moduleRouter from "#routes/module"; import { identifyUser } from "#middleware/identifyUser"; @@ -34,6 +35,7 @@ app.use("/users", usersRouter); app.use("/auth", authRouter); app.use("/accreditation", accreditationRouter); app.use("/infrastructure", infrastructureRouter); +app.use("/coursework", courseworkRouter); app.use("/module", moduleRouter); export default app; diff --git a/controller/coursework.js b/controller/coursework.js new file mode 100644 index 0000000..98178cb --- /dev/null +++ b/controller/coursework.js @@ -0,0 +1,67 @@ +// Import Coursework-related services and utilities +import { + createCoursework, + deleteCourseworkById, + listCoursework, + updateCourseworkById, +} from "#services/coursework"; + +import { logger } from "#util"; // Import the logger utility + +// Controller function to add a new Coursework entity +async function addCoursework(req, res) { + const { + student, type, course, task, objectID, activity, marks, + } = req.body; + try { + const newCoursework = await createCoursework({ + student, type, course, task, objectID, activity, marks, + }); + res.json({ res: `Added Coursework with ID ${newCoursework.id}` }); + } catch (error) { + logger.error("Error while inserting Coursework", error); + res.status(500); + res.json({ err: "Error while inserting Coursework in DB" }); + } +} + +// Controller function to update a Coursework entity +async function updateCoursework(req, res) { + const { + id, ...data + } = req.body; + try { + await updateCourseworkById(id, data); + res.json({ res: "/Updated Coursework/" }); + } catch (error) { + logger.error("Error while updating Coursework", error); + res.status(500); + res.json({ err: "Error while updating Coursework in DB" }); + } +} + +// Controller function to get a list of Coursework entities +async function getCoursework(req, res) { + const filter = req.query; + const courseworkList = await listCoursework(filter); + res.json({ res: courseworkList }); +} + +// Controller function to delete a Coursework entity +async function deleteCoursework(req, res) { + const { courseworkId } = req.params; + try { + await deleteCourseworkById(courseworkId); + res.json({ res: `Deleted Coursework with ID ${courseworkId}` }); + } catch (error) { + logger.error("Error while deleting Coursework", error); + res.status(500).json({ error: "Error while deleting Coursework from DB" }); + } +} + +export default { + addCoursework, + deleteCoursework, + getCoursework, + updateCoursework, +}; diff --git a/models/activityBlueprint.js b/models/activityBlueprint.js index dd6e451..504e834 100644 --- a/models/activityBlueprint.js +++ b/models/activityBlueprint.js @@ -6,7 +6,7 @@ const activityBluePrintSchema = { type: String, required: true, validate: { - validator: (value) => /^20\d{2}$/.test(value), //changed the valid year format starting from "20" !! + validator: (value) => /^20\d{2}$/.test(value), // changed the valid year format starting from "20" !! message: (props) => `${props.value} is not a valid year format starting with "2"!`, }, }, @@ -29,9 +29,9 @@ async function create(activityBlueprintData) { } = activityBlueprintData; const activityblueprint = new ActivityBlueprint({ number, - academicYear, - type, - startDate, + academicYear, + type, + startDate, endDate, }); const activityblueprintDoc = await activityblueprint.save(); @@ -48,6 +48,6 @@ async function update(filter, updateObject, options = { multi: true }) { return deleteResult.acknowledged; } -export default{ +export default { create, read, update, remove, -} \ No newline at end of file +}; diff --git a/models/coursework.js b/models/coursework.js index b719e97..aa59cee 100644 --- a/models/coursework.js +++ b/models/coursework.js @@ -10,5 +10,43 @@ const courseworkSchema = { marks: { type: Number, required: true }, }; -// eslint-disable-next-line no-unused-vars const Coursework = connector.model("Coursework", courseworkSchema); + +async function create(courseworkData) { + const { + student, type, course, task, objectID, activity, marks, + } = courseworkData; + const coursework = new Coursework({ + student, + type, + course, + task, + objectID, + activity, + marks, + }); + const courseworkDoc = await coursework.save(); + return courseworkDoc; +} + +async function read(filter, limit = 1) { + const courseworkDoc = await Coursework.find(filter).limit(limit); + return courseworkDoc; +} + +async function update(filter, updateObject, options = { multi: true }) { + const updateResult = await Coursework.updateMany(filter, { $set: updateObject }, options); + return updateResult.acknowledged; +} + +async function remove(filter) { + const deleteResult = await Coursework.deleteMany(filter).exec(); + return deleteResult.acknowledged; +} + +export default { + create, + read, + update, + remove, +}; diff --git a/routes/coursework.js b/routes/coursework.js new file mode 100644 index 0000000..5be18b4 --- /dev/null +++ b/routes/coursework.js @@ -0,0 +1,10 @@ +import express from "express"; +import courseworkController from "#controller/coursework"; + +const router = express.Router(); +router.post("/add", courseworkController.addCoursework); +router.get("/list", courseworkController.getCoursework); +router.post("/update", courseworkController.updateCoursework); +router.post("/delete/:courseworkId", courseworkController.deleteCoursework); + +export default router; diff --git a/services/coursework.js b/services/coursework.js new file mode 100644 index 0000000..b4b1620 --- /dev/null +++ b/services/coursework.js @@ -0,0 +1,53 @@ +// Import coursework model and databaseError module +import Coursework from "#models/coursework"; +import databaseError from "#error/database"; + +// Service function to create a new Coursework entity +export async function createCoursework({ + student, type, course, task, objectID, activity, marks, +}) { + try { + const newCoursework = await Coursework.create({ + student, type, course, task, objectID, activity, marks, + }); + return newCoursework; + } catch (error) { + throw new databaseError.DataEntryError("coursework"); + } +} + +// Service function to update a Coursework entity by ID +export async function updateCourseworkById(id, data) { + try { + const updated = await Coursework.update({ _id: id }, data); + if (updated) { + return updated; + } + throw new databaseError.DataEntryError("coursework"); + } catch (error) { + throw new databaseError.DataEntryError("coursework"); + } +} + +// Service function to retrieve a list of Coursework entities based on filters +export async function listCoursework(filter) { + try { + const courseworkList = await Coursework.read(filter, 0); + return courseworkList; + } catch (error) { + throw new databaseError.DataRetrievalError("coursework"); + } +} + +// Service function to delete a Coursework entity by ID +export async function deleteCourseworkById(courseworkId) { + try { + const deleted = await Coursework.deleteOne({ _id: courseworkId }); + if (deleted.deletedCount > 0) { + return deleted; + } + throw new databaseError.DataDeleteError("coursework"); + } catch (error) { + throw new databaseError.DataDeleteError("coursework"); + } +} diff --git a/test/routes/coursework.test.js b/test/routes/coursework.test.js new file mode 100644 index 0000000..9f64fbf --- /dev/null +++ b/test/routes/coursework.test.js @@ -0,0 +1,98 @@ +import { jest } from "@jest/globals"; // eslint-disable-line import/no-extraneous-dependencies +import request from "supertest"; +import app from "#app"; // Update this import based on your app"s structure +import connector from "#models/databaseUtil"; // Update this import +import courseworkModel from "#models/coursework"; // Update this import + +jest.mock("#util"); + +let server; +let agent; + +beforeAll((done) => { + server = app.listen(5000, () => { + agent = request.agent(server); + connector.set("debug", false); + done(); + }); +}); + +function cleanUp(callback) { + courseworkModel + .remove({ + student: "64fc3c8bde9fa947ea1f412f", + type: "onCampus", + course: "64fc3c8bde9fa947ea1f412f", + task: "64fc3c8bde9fa947ea1f412f", + objectID: "Practical", + activity: "64fc3c8bde9fa947ea1f412f", + marks: 97, + }) + .then(() => { + connector.disconnect((DBerr) => { + if (DBerr) console.log("Database disconnect error: ", DBerr); + server.close((serverErr) => { + if (serverErr) console.log(serverErr); + callback(); + }); + }); + }); +} + +afterAll((done) => { + cleanUp(done); +}); + +describe("Coursework API", () => { + it("should create coursework", async () => { + const response = await agent.post("/coursework/add").send({ + student: "64fc3c8bde9fa947ea1f412f", + type: "onCampus", + course: "64fc3c8bde9fa947ea1f412f", + task: "64fc3c8bde9fa947ea1f412f", + objectID: "Practical", + activity: "64fc3c8bde9fa947ea1f412f", + marks: 97, + }); + + expect(response.status).toBe(200); + expect(response.body.res).toMatch(/Added Coursework with ID \w+/); + }); + + describe("after adding coursework", () => { + beforeEach(async () => { + await agent.post("/coursework/add").send({ + student: "64fc3c8bde9fa947ea1f412f", + type: "onCampus", + course: "64fc3c8bde9fa947ea1f412f", + task: "64fc3c8bde9fa947ea1f412f", + objectID: "Practical", + activity: "64fc3c8bde9fa947ea1f412f", + marks: 97, + }); + }); + + afterEach(async () => { + await courseworkModel.remove({ + student: "64fc3c8bde9fa947ea1f412f", + }); + }); + + it("should read coursework", async () => { + const response = await agent + .get("/coursework/list") + .send({ student: "64fc3c8bde9fa947ea1f412f" }); + expect(response.status).toBe(200); + expect(response.body.res).toBeDefined(); + }); + + it("should update coursework", async () => { + const response = await agent + .post("/coursework/update") + .send({ student: "64fc3c8bde9fa947ea1f412f" }); + + expect(response.status).toBe(200); + expect(response.body.res).toMatch(/Updated Coursework/); + }); + }); +});