diff --git a/_apidoc.js b/_apidoc.js index 2d034d4..665d45e 100644 --- a/_apidoc.js +++ b/_apidoc.js @@ -665,3 +665,152 @@ * @apiSuccess {String[]} module.cognitiveLevels Array of cognitive levels of * attainment as per Bloom's Taxanomy (L1-L6). */ + +// ------------------------------------------------------------------------------------------ +// Activity. +// ------------------------------------------------------------------------------------------ + +/** + * @api {post} /activity/add Add Activty. + * @apiName AddActivity + * @apiGroup Activity + * + * @apiBody {Date} startTime The startTime of the activity. + * @apiBody {Number} duration The duration of the activity (in minutes). + * @apiBody {ObjectId} course The course of the activity (ObjectId). + * @apiBody {ObjectId} faculty The faculty alloted for the activity(ObjectId). + * @apiBody {String} type The type of activity.One of possible LECTURE, PRACTICAL, TUTORIAL. + * @apiBody {ObjectId} task The task of the activity (ObjectId).One of possible Topic,Practical,Tutorial. + * @apiBody {ObjectId} group The group of the activity (ObjectId). + * @apiBody {ObjectId} students the students who gonna attend the activity(ObjectId). + * + * @apiSuccess {String} res Response message. + * + * @apiError (Error 500) DatabaseError Error while inserting in the database. + * + * @apiDescription Adds a new Activity to the system. + */ + +/** + * + * @apiSuccessExample Success-Response: + * HTTP/1.1 200 OK + * { + * "res": "Added activity" + * } + * + * @apiErrorExample Error-Response: + * HTTP/1.1 500 Internal Server Error + * { + * "err": "Error while inserting in DB" + * } + */ + +/** + * @api {delete} /timetable/delete/:timetableId Delete Timetable + * @apiName DeleteTimetable + * @apiGroup Timetable + * + * @apiParam {String} timetableId The ID of the timetable 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 {delete} /activity/delete/:activity Delete Activity. + * @apiName DeleteActivity + * @apiGroup Activity + * + * @apiParam {String} Activity The activity 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} /timetable/update Update Timetable + * @apiName UpdateTimetable + * @apiGroup Timetable + * @apiDescription Update existing timetable data. + * + * @apiBody {Date} startTime The startTime of the activity. + * @apiBody {Number} duration The duration of the activity (in minutes). + * @apiBody {ObjectId} course The course of the activity (ObjectId). + * @apiBody {ObjectId} faculty The faculty alloted for the activity(ObjectId). + * @apiBody {String} type The type of activity.One of possible LECTURE, PRACTICAL, TUTORIAL. + * @apiBody {ObjectId} task The task of the activity (ObjectId).One of possible Topic,Practical,Tutorial. + * @apiBody {ObjectId} group The group of the activity (ObjectId). + * @apiBody {ObjectId} students the students who gonna attend the activity(ObjectId). + * + * @apiSuccess {String} res Timetable updated. + */ + +/** + * @api {post} /activity/update Update Activity. + * @apiName UpdateActivity + * @apiGroup Activity + * @apiDescription Update existing activity data. + * + * @apiBody {Date} startTime The startTime of the activity. + * @apiBody {Number} duration The duration of the activity (in minutes). + * @apiBody {ObjectId} course The course of the activity (ObjectId). + * @apiBody {ObjectId} faculty The faculty alloted for the activity(ObjectId). + * @apiBody {String} type The type of activity.One of possible LECTURE, PRACTICAL, TUTORIAL. + * @apiBody {ObjectId} task The task of the activity (ObjectId).One of possible Topic,Practical,Tutorial. + * @apiBody {ObjectId} group The group of the activity (ObjectId). + * @apiBody {ObjectId} students the students who gonna attend the activity(ObjectId). + * + * @apiSuccess {String} res Activity updated. + * @apiError (Error 500) DatabaseError Error in updating the database. + */ + +/** + * @api {get} /timetable/list Get Timetable List + * @apiName GetTimetableList + * @apiGroup Timetable + * + * @apiQuery {Date} startTime The startTime of the activity. + * @apiQuery {Number} duration The duration of the activity (in minutes). + * @apiQUERY {ObjectId} course The course of the activity (ObjectId). + * @apiQuery {ObjectId} faculty The faculty alloted for the activity(ObjectId). + * @apiQuery {String} type The type of activity.One of possible LECTURE, PRACTICAL, TUTORIAL. + * @apiQuery {ObjectId} task The task of the activity (ObjectId).One of possible Topic,Practical,Tutorial. + * @apiQuery {ObjectId} group The group of the activity (ObjectId). + * @apiQuery {ObjectId} students the students who gonna attend the activity(ObjectId). + * + * @apiSuccess {Date} startTime The startTime of the activity. + * @apiSuccess {Number} duration The duration of the activity (in minutes). + * @apiSuccess {ObjectId} course The course of the activity (ObjectId). + * @apiSuccess {ObjectId} faculty The faculty alloted for the activity(ObjectId). + * @apiSuccess {String} type The type of activity.One of possible LECTURE, PRACTICAL, TUTORIAL. + * @apiSuccess {ObjectId} task The task of the activity (ObjectId).One of possible Topic,Practical,Tutorial. + * @apiSuccess {ObjectId} group The group of the activity (ObjectId). + * @apiSucess {ObjectId} students the students who gonna attend the activity(ObjectId). + */ + +/** + * @api {get} /activity/list Get Activity List + * @apiName GetActivityList + * @apiGroup Activity + * + * @apiQuery {Date} startTime The startTime of the activity. + * @apiQuery {Number} duration The duration of the activity (in minutes). + * @apiQUERY {ObjectId} course The course of the activity (ObjectId). + * @apiQuery {ObjectId} faculty The faculty alloted for the activity(ObjectId). + * @apiQuery {String} type The type of activity.One of possible LECTURE, PRACTICAL, TUTORIAL. + * @apiQuery {ObjectId} task The task of the activity (ObjectId).One of possible Topic,Practical,Tutorial. + * @apiQuery {ObjectId} group The group of the activity (ObjectId). + * @apiQuery {ObjectId} students the students who gonna attend the activity(ObjectId). + * + * @apiSuccess {Date} startTime The startTime of the activity. + * @apiSuccess {Number} duration The duration of the activity (in minutes). + * @apiSuccess {ObjectId} course The course of the activity (ObjectId). + * @apiSuccess {ObjectId} faculty The faculty alloted for the activity(ObjectId). + * @apiSuccess {String} type The type of activity.One of possible LECTURE, PRACTICAL, TUTORIAL. + * @apiSuccess {ObjectId} task The task of the activity (ObjectId).One of possible Topic,Practical,Tutorial. + * @apiSuccess {ObjectId} group The group of the activity (ObjectId). + * @apiSucess {ObjectId} students the students who gonna attend the activity(ObjectId). + */ \ No newline at end of file diff --git a/app.js b/app.js index ddecdf4..e0d4c08 100644 --- a/app.js +++ b/app.js @@ -17,6 +17,7 @@ import tutorialRouter from "#routes/tutorial"; import assignmentRouter from "#routes/assignment"; import timetableRouter from "#routes/timetable"; import courseworkRouter from "#routes/coursework"; +import activityRouter from "#routes/activity"; import moduleRouter from "#routes/module"; import { identifyUser } from "#middleware/identifyUser"; import departmentRouter from "#routes/department"; @@ -46,6 +47,7 @@ app.use("/department", departmentRouter); app.use("/practical", practicalRouter); app.use("/organization", organizationRouter); app.use("/student", studentRouter); +app.use("/activity", activityRouter); app.use("/tutorial", tutorialRouter); app.use("/assignment", assignmentRouter); app.use("/timetable", timetableRouter); diff --git a/controller/activity.js b/controller/activity.js new file mode 100644 index 0000000..2e1ceb3 --- /dev/null +++ b/controller/activity.js @@ -0,0 +1,64 @@ +import { + createActivity,deleteActivityById, activityList ,updateActivityById, +}from "#services/activity"; +import {logger} from "#util" ; + +async function addActivity(req,res) { + const{ + activityBlueprint, + startTime, + duration, + course, + faculty, + type, + task, + group, + students, + }=req.body; + try{ + const newActivity = await createActivity(activityBlueprint,startTime,duration,course,faculty,type,task,group,students); + res.json ({res: `added activity ${newActivity.id}`, id: newActivity.id}); + } catch (error){ + logger.error ("Error while inserting",error); + res.status(500); + res.json({err:"Error while inserting in DB"}); + } +} + +async function updateActivity(req,res){ + const { id }=req.params; + const { + ...data + }=req.body; + try { + await updateActivityById(id,data); + res.json({res:`updated activity with id ${id}`}); + }catch (error){ + logger.error("Error while updating",error); + res.status(500); + res.json({err:"Error while updating in DB"}); + } +} + +async function getActivity(req,res){ + const filter = req.query; + const activitylist =await activityList(filter); + res.json({res:activitylist}); +} + + +async function deleteActivity(res,req){ + const { id }=req.params; + try{ + await deleteActivityById(id); + + res.json({res:`Deleted activity with ID ${id}`}); + }catch(error){ + logger.error ("Error while deleting",error); + res.status(500).json({error:"Error while deleting from DB"}); + } +} + +export default { + addActivity, deleteActivity ,getActivity ,updateActivity, +}; \ No newline at end of file diff --git a/models/activity.js b/models/activity.js index 4ea2742..08dee96 100644 --- a/models/activity.js +++ b/models/activity.js @@ -22,37 +22,41 @@ const activitySchema = { const Activity = connector.model("Activity", activitySchema); -// crud +///crud operation/// -async function create(activityData) { +//add a activity to the database +async function create(activityData){ const { - activityBlueprint, startTime, duration, course, faculty, type, task, group, students, - } = activityData; - const activity = new Activity({ - activityBlueprint, startTime, duration, course, faculty, type, task, group, students, + activityBlueprint, startTime,duration,course,faculty,type,task,group,students, + }=activityData; + const activity= new Activity({ + activityBlueprint, startTime,duration,course,faculty,type,task,group,students, }); - const activityDoc = await activity.save(); + const activityDoc =await activity.save(); return activityDoc; } -async function read(filter, limit = 1) { - const activityDoc = await Activity.find(filter).limit(limit); - return activityDoc; +//Retrieve activity based on a given filter and limit +async function read(filter,limit=1){ + const activityDoc = await Activity.find (filter).limit(limit); + return activityDoc ; } -async function update(filter, updateObject, options = { multi: true }) { - const updateResult = await Activity.updateMany(filter, { $set: updateObject }, options); - return updateResult.acknowledged; +//update activity based on a given filter +async function update(filter,updateObject,options={multi:true}){ + const updateActivity= await Activity.updateMany(filter,{$set:updateObject},options); +return updateActivity.acknowledged; } -async function remove(filter) { - const deleteResult = await Activity.deleteMany(filter); - return deleteResult.acknowledged; +//Delete activity based on a given filter +async function remove(filter){ + const deleteActivity= await Activity.deleteMany(filter).exec(); + return deleteActivity.acknowledged } -export default { - create, - read, - update, - remove, +//export crud functions + +export default{ + create,read,update,remove, }; + diff --git a/routes/activity.js b/routes/activity.js new file mode 100644 index 0000000..f5c40a2 --- /dev/null +++ b/routes/activity.js @@ -0,0 +1,10 @@ +import express from "express"; +import activityController from "#controller/activity"; + +const router=express.Router(); +router.post("/add",activityController.addActivity); +router.get("/list",activityController.getActivity); +router.post("/update/:id",activityController.updateActivity); +router.delete("/delete/:id",activityController.deleteActivity); + +export default router; \ No newline at end of file diff --git a/services/activity.js b/services/activity.js new file mode 100644 index 0000000..ae14c05 --- /dev/null +++ b/services/activity.js @@ -0,0 +1,33 @@ +import Activity from "#models/activity" ; +import databaseError from "#error/database"; + +export async function createActivity (activityBlueprint,startTime,duration,course,faculty,type,task,group,students){ + const newActivity = await Activity.create({ + activityBlueprint,startTime,duration,course,faculty,task,type,group,students, + }); + if (newActivity){ + return newActivity; + } + throw new databaseError.DataEntryError("actvity"); +} + +export async function updateActivityById(id,data){ +const updated= await Activity.update({_id:id},data); +if (updated){ + return updated; +} +throw new databaseError.DataEntryError("activity"); +} + +export async function activityList(filter){ + const activitylist = await Activity.read(filter,0); + return activitylist; +} + +export async function deleteActivityById(id){ + const deleted = await Activity.remove({_id:id},data); + if(deleted){ + return deleted; + } + throw new databaseError.DataDeleteError("activity"); +} \ No newline at end of file diff --git a/test/routes/activity.test.js b/test/routes/activity.test.js new file mode 100644 index 0000000..abdeb36 --- /dev/null +++ b/test/routes/activity.test.js @@ -0,0 +1,116 @@ +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 +import connector from "#models/databaseUtil"; //Update this import +import activityModel from "#models/activity"; //Update this import + +jest.mock("#util"); + +let server; +let agent; + +beforeAll((done) => { + server = app.listen(null, () => { + agent = request.agent(server); + connector.set("debug", false); + done(); + }); +}); + +function cleanUp(callback) { + activityModel + .remove({ + startTime: "2023-06-18T14:11:30Z", + duration: 2, + course: "64fc3c8bde9fa947ea1f412f", + faculty: "64fc3c8bde9fa947ea1f412f", + type: "LECTURE", + task: ["64fc3c8bde9fa947ea1f412f"], + group: "64fc3c8bde9fa947ea1f412f", + students: ["64fc3c8bde9fa947ea1f412f"] + }) + .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("Activity API", () => { + it("should create activity", async () => { + const response = await agent.post("/activity/add").send({ + activityBlueprint: "5f8778b54b553439ac49a03a", + startTime: "2023-06-18T14:11:30Z", + duration: 2, + course: "5f8778b54b553439ac49a03a", + faculty: "5f8778b54b553439ac49a03a", + type: "LECTURE", + task: ["5f8778b54b553439ac49a03a"], + group: "5f8778b54b553439ac49a03a", + students: ["5f8778b54b553439ac49a03a"] + }); + + expect(response.status).toBe(200); + expect(response.body.res).toMatch(/added activity/); + }); + + describe("after adding activity", () => { + let id; + beforeEach(async () => { + id = await agent.post("/activity/add").send({ + activityBlueprint: "64fc3c8bde9fa947ea1f412f", + startTime: "2023-06-18T14:11:30Z", + duration: 2, + course: "64fc3c8bde9fa947ea1f412f", + faculty: "64fc3c8bde9fa947ea1f412f", + type: "LECTURE", + task: ["64fc3c8bde9fa947ea1f412f"], + group: "64fc3c8bde9fa947ea1f412f", + students: ["64fc3c8bde9fa947ea1f412f"] + }); + id = JSON.parse(id.res.text).id; + }); + + afterEach(async () => { + await activityModel.remove({ + activityBlueprint: "64fc3c8bde9fa947ea1f412f", + startTime: "2023-06-18T14:11:30Z", + duration: 2, + course: "64fc3c8bde9fa947ea1f412f", + faculty: "64fc3c8bde9fa947ea1f412f", + type: "LECTURE", + task: ["64fc3c8bde9fa947ea1f412f"], + group: "64fc3c8bde9fa947ea1f412f", + students: ["64fc3c8bde9fa947ea1f412f"] + }); + }); + + it("should read activity", async () => { + const response = await agent + .get("/activity/list") + .send({ startTime: "2023-06-18T14:11:30Z" }); + expect(response.status).toBe(200); + expect(response.body.res).toBeDefined(); + }); + + it("should update activity", async () => { + const response = await agent + .post(`/activity/update/${id}`) + .send({ + duration: 5, + }); + + expect(response.status).toBe(200); + expect(response.body.res).toMatch(/updated activity/); + }); + }); +});