Skip to content
Merged
116 changes: 116 additions & 0 deletions backend/src/controllers/calendar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/* eslint-disable @typescript-eslint/no-misused-promises */
/**
* Function handlers for calendar route requests
*/
import { RequestHandler } from "express";
import createHttpError from "http-errors";

import EnrollmentModel from "../models/enrollment";
import SessionModel from "../models/session";

import { Calendar, CalendarSlot } from "./types/calendarTypes";

/**
* Calendar Body: {
*
* studentId: string;
* programId: string;
* calendar: {
* date: Date;
* hours: number;
* session: string;
* }[]
*
* }
*/

/**
* Request handler for getting calendar for student in program
* @param req
* @param res
* @param next
*/
export const getCalendar: RequestHandler = async (req, res, next) => {
try {
const studentId = req.params.studentId;
const programId = req.params.programId;

const enrollment = EnrollmentModel.find({ studentId, programId });
if (!enrollment) {
throw createHttpError(404, "Enrollment not found");
}

// get all sessions with studentId and programId
const sessions = await SessionModel.find({ programId });

const calendar: Calendar = { studentId, programId, calendar: [] };
for (const session of sessions) {
for (const student of session.students) {
if (student.studentId.toString() === studentId) {
let hours = 0;
if (session.marked) {
hours = student.hoursAttended;
}
const date = session.date;
const sessionId = session._id.toString();
calendar.calendar.push({ date, hours, session: sessionId });
}
}
}
console.log(calendar);

return res.status(200).send(calendar);
} catch (error) {
next(error);
}
};

/**
* Handler for editing a day in a calendar
* @param req
* @param res
* @param next
*/
export const editCalendar: RequestHandler = async (req, res, next) => {
try {
const studentId = req.params.studentId;
const programId = req.params.programId;

const enrollment = await EnrollmentModel.findOne({ studentId, programId });
if (!enrollment) {
throw createHttpError(404, "Enrollment not found");
}

const { hours, session } = req.body as CalendarSlot;

const sessionObject = await SessionModel.findById(session);

if (!sessionObject) {
throw createHttpError(404, "Session not found");
}

if (sessionObject.programId.toString() !== programId) {
throw createHttpError(404, "Incorrect program for session");
}

const student = sessionObject.students.find((s) => s.studentId.toString() === studentId);

if (!student) {
throw createHttpError(404, "Student not in session");
}

const prevHoursAttended = student.hoursAttended;
let hoursLeft = enrollment.hoursLeft + prevHoursAttended;

student.hoursAttended = hours;
hoursLeft -= student.hoursAttended;
enrollment.hoursLeft = hoursLeft > 0 ? hoursLeft : 0;

await sessionObject.save();
await enrollment.save();

res.status(200).send("Updated");
} catch (error) {
next(error);
}
};
6 changes: 6 additions & 0 deletions backend/src/controllers/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ export const updateProgram: RequestHandler = async (req, res, next) => {
{ $set: { status: "Waitlisted", dateUpdated: Date.now() } },
);

// update days of week when changed
await EnrollmentModel.updateMany(
{ programId: { $eq: programId }, status: { $eq: "Joined" } },
{ $set: { schedule: programData.daysOfWeek } },
);

res.status(200).json(editedProgram);
} catch (error) {
next(error);
Expand Down
12 changes: 12 additions & 0 deletions backend/src/controllers/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@
};

// Call when creating a session from absence
export const createAbsenceSession: RequestHandler = async (req, res, next) => {

Check warning on line 223 in backend/src/controllers/session.ts

View workflow job for this annotation

GitHub Actions / Backend lint and style check

Promise-returning function provided to variable where a void return was expected
const errors = validationResult(req);

try {
Expand All @@ -247,7 +247,7 @@
};

// Call when attendance is marked
export const updateSession: RequestHandler = async (req, res, next) => {

Check warning on line 250 in backend/src/controllers/session.ts

View workflow job for this annotation

GitHub Actions / Backend lint and style check

Promise-returning function provided to variable where a void return was expected
const errors = validationResult(req);
try {
validationErrorParser(errors);
Expand All @@ -266,6 +266,18 @@
return res.status(404).json({ message: "No object in database with provided ID" });
}

programData.students.forEach(async (student: StudentInfo) => {

Check warning on line 269 in backend/src/controllers/session.ts

View workflow job for this annotation

GitHub Actions / Backend lint and style check

Promise returned in function argument where a void return was expected
const enrollment = await EnrollmentModel.findOne({
studentId: student.studentId,
programId: programData.programId,
});
if (enrollment) {
const hours = enrollment.hoursLeft - student.hoursAttended;
enrollment.hoursLeft = hours > 0 ? hours : 0;
await enrollment.save();
}
});

const absentStudents = programData.students.filter((student: StudentInfo) => !student.attended);

const absenceSessions = absentStudents.map((absentStudent) => ({
Expand All @@ -282,7 +294,7 @@
};

// Call when frontpage to load recent sessions is called
export const getRecentSessions: RequestHandler = async (_, res, next) => {

Check warning on line 297 in backend/src/controllers/session.ts

View workflow job for this annotation

GitHub Actions / Backend lint and style check

Promise-returning function provided to variable where a void return was expected
try {
await createMissingRegularSessions();
await createMissingVaryingSessions();
Expand All @@ -297,7 +309,7 @@
};

// Call when looking to populate absence cards
export const getAbsenceSessions: RequestHandler = async (_, res, next) => {

Check warning on line 312 in backend/src/controllers/session.ts

View workflow job for this annotation

GitHub Actions / Backend lint and style check

Promise-returning function provided to variable where a void return was expected
try {
const sessions = await AbsenceSessionModel.find();

Expand Down
12 changes: 10 additions & 2 deletions backend/src/controllers/student.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/no-misused-promises */
/**
* Functions that process task route requests.
* Functions that process student route requests.
*/

import { RequestHandler } from "express";
Expand All @@ -9,6 +9,7 @@

import EnrollmentModel from "../models/enrollment";
import { Image } from "../models/image";
import ProgramModel from "../models/program";
import ProgressNoteModel from "../models/progressNote";
import StudentModel from "../models/student";
import { Enrollment } from "../types/enrollment";
Expand Down Expand Up @@ -81,6 +82,10 @@
enrollments.map(async (enrollment: Enrollment) => {
const enrollmentExists = await EnrollmentModel.findById(enrollment._id);
const enrollmentBody = { ...enrollment, studentId: new mongoose.Types.ObjectId(studentId) };
const program = await ProgramModel.findById({ _id: enrollment.programId });
if (program?.type === "regular") {
enrollmentBody.schedule = program.daysOfWeek;
}
if (!enrollmentExists) {
return await createEnrollment(enrollmentBody);
} else {
Expand Down Expand Up @@ -117,11 +122,14 @@
const students = await StudentModel.find().populate("enrollments");

// Even though this is a get request, we have verifyAuthToken middleware that sets the accountType in the request body
const { accountType } = req.body;

Check warning on line 125 in backend/src/controllers/student.ts

View workflow job for this annotation

GitHub Actions / Backend lint and style check

Unsafe assignment of an `any` value

// Ensure that documents that are marked admin are not returned to non-admin users
if (accountType !== "admin") {
students.forEach((student) => {
if (!student.documents) {
return;
}
student.documents = student.documents.filter(
(doc) => !doc.markedAdmin,
) as typeof student.documents;
Expand All @@ -138,7 +146,7 @@
try {
const errors = validationResult(req);

const { accountType } = req.body;

Check warning on line 149 in backend/src/controllers/student.ts

View workflow job for this annotation

GitHub Actions / Backend lint and style check

Unsafe assignment of an `any` value

validationErrorParser(errors);

Expand All @@ -150,7 +158,7 @@
}

// Ensure that documents that are marked admin are not returned to non-admin users
if (accountType !== "admin") {
if (studentData.documents && accountType !== "admin") {
studentData.documents = studentData.documents.filter(
(doc) => !doc.markedAdmin,
) as typeof studentData.documents;
Expand Down
11 changes: 11 additions & 0 deletions backend/src/controllers/types/calendarTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export type CalendarSlot = {
date: Date;
hours: number;
session: string;
};

export type Calendar = {
studentId: string;
programId: string;
calendar: CalendarSlot[];
};
7 changes: 4 additions & 3 deletions backend/src/models/enrollment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ const enrollmentSchema = new mongoose.Schema({
schedule: { type: [String], required: true },
sessionTime: {
type: {
start_time: { type: String, required: true },
end_time: { type: String, required: true },
start_time: { type: String, required: false, default: "00:00" },
end_time: { type: String, required: false, default: "00:00" },
},
required: true,
required: false,
default: { start_time: "00:00", end_time: "00:00" },
},
startDate: { type: Date, required: true },
renewalDate: { type: Date, required: true },
Expand Down
41 changes: 21 additions & 20 deletions backend/src/models/student.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,22 @@ const studentSchema = new Schema({
student: {
lastName: { type: String, required: true },
firstName: { type: String, required: true },
email: { type: String, required: true },
phoneNumber: { type: String, required: true },
email: { type: String, required: false, default: "" },
phoneNumber: { type: String, required: false, default: "" },
},

emergency: {
lastName: { type: String, required: true },
firstName: { type: String, required: true },
email: { type: String, required: true },
phoneNumber: { type: String, required: true },
lastName: { type: String, required: false, default: "" },
firstName: { type: String, required: false, default: "" },
email: { type: String, required: false, default: "" },
phoneNumber: { type: String, required: false, default: "" },
},

serviceCoordinator: {
lastName: { type: String, required: true },
firstName: { type: String, required: true },
email: { type: String, required: true },
phoneNumber: { type: String, required: true },
lastName: { type: String, required: false, default: "" },
firstName: { type: String, required: false, default: "" },
email: { type: String, required: false, default: "" },
phoneNumber: { type: String, required: false, default: "" },
},

enrollments: {
Expand All @@ -33,18 +33,18 @@ const studentSchema = new Schema({
},

//Address of student
location: { type: String, required: true },
location: { type: String, required: false, default: "" },

//String list of medications
medication: { type: String, required: true },
medication: { type: String, required: false, default: "" },

birthday: { type: Date, required: true },
intakeDate: { type: Date, required: true },
tourDate: { type: Date, required: true },
birthday: { type: Date, required: false, default: null },
intakeDate: { type: Date, required: false, default: null },
tourDate: { type: Date, required: false, default: null },

conservation: { type: Boolean, required: true },
UCINumber: { type: String, required: true },
incidentForm: { type: String, required: true },
conservation: { type: Boolean, required: false, default: false },
UCINumber: { type: String, required: false, default: "" },
incidentForm: { type: String, required: false, default: "" },
documents: {
type: [
{
Expand All @@ -53,7 +53,8 @@ const studentSchema = new Schema({
markedAdmin: { type: Boolean, required: true, default: false },
},
],
required: true,
required: false,
default: [],
},
profilePicture: { type: String, ref: "Image", required: false, default: "default" },

Expand All @@ -65,7 +66,7 @@ const studentSchema = new Schema({
},

//Will contain list of all dietary restrictions
dietary: { type: [String] },
dietary: { type: [String], required: false, default: [] },
});

type Student = InferSchemaType<typeof studentSchema>;
Expand Down
2 changes: 2 additions & 0 deletions backend/src/routes/api.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import express from "express";

import calendarRouter from "./calendar";
import imageRouter from "./image";
import programRoutes from "./program";
import progressNoteRoutes from "./progressNote";
Expand All @@ -15,6 +16,7 @@ router.use("/student", studentRoutes);
router.use("/program", programRoutes);
router.use("/session", sessionRoutes);
router.use("/progressNote", progressNoteRoutes);
router.use("/calendar", calendarRouter);
router.use("/image", imageRouter);

export default router;
20 changes: 20 additions & 0 deletions backend/src/routes/calendar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* Calendar route requests
*/
import express from "express";

import * as CalendarController from "../controllers/calendar";
import { verifyAuthToken } from "../validators/auth";
import * as CalendarValidator from "../validators/calendar";

const router = express.Router();

router.get("/:studentId/:programId", [verifyAuthToken], CalendarController.getCalendar);
router.patch(
"/:studentId/:programId",
[verifyAuthToken],
CalendarValidator.editCalendar,
CalendarController.editCalendar,
);

export default router;
2 changes: 1 addition & 1 deletion backend/src/routes/student.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Task route requests.
* Student route requests.
*/

import express from "express";
Expand Down
2 changes: 1 addition & 1 deletion backend/src/util/student.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ export const programValidatorUtil = async (enrollments: Enrollment[]) => {
"status",
"hoursLeft",
"schedule",
"sessionTime",
"startDate",
"renewalDate",
"authNumber",
];

enrollments.forEach((enrollment: Enrollment) => {
requiredFields.forEach((field) => {
if (!enrollment[field as keyof Enrollment])
Expand Down
33 changes: 33 additions & 0 deletions backend/src/validators/calendar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { body } from "express-validator";

// const makeDateValidator = () =>
// body("date")
// .exists()
// .withMessage("date needed")
// .bail()
// .isDate()
// .bail();

const makeHoursValidator = () =>
body("hours")
.exists()
.withMessage("hours needed")
.bail()
.isNumeric()
.withMessage("needs to be number")
.bail();

const makeSessionValidator = () =>
body("session")
.exists()
.withMessage("sessionId needed")
.bail()
.isString()
.withMessage("needs to be string")
.bail();

export const editCalendar = [
// makeDateValidator(),
makeHoursValidator(),
makeSessionValidator(),
];
Loading
Loading