From 74ebec44dea0f0ab86d05ecc0d336754bbb6ff82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikolas=20G=C3=B6rlitz?= Date: Sat, 16 Mar 2024 23:15:14 +0100 Subject: [PATCH] update overview --- src/Router.ts | 11 ++-- .../fast-track/FastTrackAdminController.ts | 2 +- .../TrainingRequestAdminController.ts | 2 +- .../TrainingSessionAdminController.ts | 2 +- .../TrainingSessionController.ts | 23 ++++++- .../user/UserInformationController.ts | 63 +++++++++++++++++++ .../notification/NotificationLibrary.ts | 2 + src/models/Role.ts | 6 +- src/models/User.ts | 19 +++++- 9 files changed, 113 insertions(+), 17 deletions(-) create mode 100644 src/controllers/user/UserInformationController.ts diff --git a/src/Router.ts b/src/Router.ts index addf744..09d40b3 100644 --- a/src/Router.ts +++ b/src/Router.ts @@ -36,6 +36,7 @@ import UserEndorsementAdminController from "./controllers/user/UserEndorsementAd import UserStatisticsController from "./controllers/user/UserStatisticsController"; import SyslogAdminController from "./controllers/admin-logs/SyslogAdminController"; import JoblogAdminController from "./controllers/admin-logs/JoblogAdminController"; +import UserInformationController from "./controllers/user/UserInformationController"; const routerGroup = (callback: (router: Router) => void) => { const router = Router(); @@ -45,12 +46,6 @@ const routerGroup = (callback: (router: Router) => void) => { export const router = Router(); -router.post("/test", async (req, res) => { - console.log(req.body); - - res.sendStatus(400); -}); - router.use( "/auth", routerGroup((r: Router) => { @@ -69,12 +64,13 @@ router.use( r.use(authMiddleware); r.get("/user/update", LoginController.updateUserData); - r.patch("/settings", UserSettingsController.updateSettings); r.get("/sessions", SessionController.getUserSessions); r.delete("/session", SessionController.deleteUserSession); + r.get("/overview", UserInformationController.getOverviewStatistics); + r.get("/gdpr", GDPRController.getData); r.use( @@ -152,6 +148,7 @@ router.use( r.use( "/training-session", routerGroup((r: Router) => { + r.get("/upcoming", TrainingSessionController.getUpcoming); r.get("/:uuid", TrainingSessionController.getByUUID); r.delete("/withdraw/:uuid", TrainingSessionController.withdrawFromSessionByUUID); }) diff --git a/src/controllers/fast-track/FastTrackAdminController.ts b/src/controllers/fast-track/FastTrackAdminController.ts index 7a7ce0c..1610112 100644 --- a/src/controllers/fast-track/FastTrackAdminController.ts +++ b/src/controllers/fast-track/FastTrackAdminController.ts @@ -18,7 +18,7 @@ import Validator, { ValidationTypeEnum } from "../../utility/Validator"; async function getAll(request: Request, response: Response, next: NextFunction) { try { const user: User = response.locals.user; - PermissionHelper.checkUserHasPermission(user, "atd.fast-track.all.view", true); + PermissionHelper.checkUserHasPermission(user, "atd.fast_track.view", true); const fastTracks = await FastTrackRequest.findAll({ include: [FastTrackRequest.associations.user], diff --git a/src/controllers/training-request/TrainingRequestAdminController.ts b/src/controllers/training-request/TrainingRequestAdminController.ts index decd406..f6d59bd 100644 --- a/src/controllers/training-request/TrainingRequestAdminController.ts +++ b/src/controllers/training-request/TrainingRequestAdminController.ts @@ -170,7 +170,7 @@ async function destroyByUUID(request: Request, response: Response, next: NextFun }); if (trainingRequest == null) { - response.status(404).send({message: "Training request with this UUID not found"}); + response.status(404).send({ message: "Training request with this UUID not found" }); return; } diff --git a/src/controllers/training-session/TrainingSessionAdminController.ts b/src/controllers/training-session/TrainingSessionAdminController.ts index 231b25f..417bafc 100644 --- a/src/controllers/training-session/TrainingSessionAdminController.ts +++ b/src/controllers/training-session/TrainingSessionAdminController.ts @@ -204,7 +204,7 @@ async function deleteTrainingSession(request: Request, response: Response) { return; } - for (const participant of (session?.users ?? [])) { + for (const participant of session?.users ?? []) { await NotificationLibrary.sendUserNotification({ user_id: participant.id, author_id: user.id, diff --git a/src/controllers/training-session/TrainingSessionController.ts b/src/controllers/training-session/TrainingSessionController.ts index 20293be..3ea5275 100644 --- a/src/controllers/training-session/TrainingSessionController.ts +++ b/src/controllers/training-session/TrainingSessionController.ts @@ -1,4 +1,4 @@ -import { Request, Response } from "express"; +import { NextFunction, Request, Response } from "express"; import { User } from "../../models/User"; import { TrainingSession } from "../../models/TrainingSession"; import { TrainingSessionBelongsToUsers } from "../../models/through/TrainingSessionBelongsToUsers"; @@ -7,6 +7,26 @@ import dayjs from "dayjs"; import NotificationLibrary from "../../libraries/notification/NotificationLibrary"; import { HttpStatusCode } from "axios"; +/** + * Gets a list of upcoming training sessions + * @param request + * @param response + * @param next + */ +async function getUpcoming(request: Request, response: Response, next: NextFunction) { + try { + const user: User = response.locals.user; + + const sessions: TrainingSession[] = (await user.getTrainingSessionsWithCourseAndStation()).filter(session => { + return !session.completed && dayjs.utc(session.date).isAfter(dayjs.utc()); + }); + + response.send(sessions); + } catch (e) { + next(e); + } +} + /** * [User] * Gets all the associated data of a training session @@ -125,6 +145,7 @@ async function withdrawFromSessionByUUID(request: Request, response: Response) { } export default { + getUpcoming, getByUUID, withdrawFromSessionByUUID, }; diff --git a/src/controllers/user/UserInformationController.ts b/src/controllers/user/UserInformationController.ts new file mode 100644 index 0000000..6e3de9e --- /dev/null +++ b/src/controllers/user/UserInformationController.ts @@ -0,0 +1,63 @@ +import { NextFunction, Request, Response } from "express"; +import { User } from "../../models/User"; +import { HttpStatusCode } from "axios"; +import { EndorsementGroup } from "../../models/EndorsementGroup"; +import { TrainingSession } from "../../models/TrainingSession"; +import dayjs from "dayjs"; + +async function getOverviewStatistics(request: Request, response: Response, next: NextFunction) { + try { + const user: User = response.locals.user; + + const userInformation = await User.findOne({ + where: { + id: user.id, + }, + include: [ + { + association: User.associations.training_sessions, + through: { + attributes: [], + }, + include: [TrainingSession.associations.course, TrainingSession.associations.training_station], + }, + { + association: User.associations.endorsement_groups, + through: { + attributes: [], + }, + include: [ + { + association: EndorsementGroup.associations.stations, + through: { + attributes: [], + }, + }, + ], + }, + ], + }); + + if (userInformation == null) { + response.sendStatus(HttpStatusCode.NotFound); + return; + } + + const sessions = userInformation.training_sessions as TrainingSession[]; + const completedSessions = sessions?.filter(session => session.completed); + const upcomingSessions = sessions.filter(session => !session.completed && dayjs.utc(session.date).isAfter(dayjs.utc())); + + response.send({ + count: sessions?.length ?? 0, + completedCount: completedSessions?.length ?? 0, + upcomingSessions: upcomingSessions, + endorsementGroups: userInformation.endorsement_groups ?? [], + }); + } catch (e) { + next(e); + } +} + +export default { + getOverviewStatistics, +}; diff --git a/src/libraries/notification/NotificationLibrary.ts b/src/libraries/notification/NotificationLibrary.ts index be84159..ff137ba 100644 --- a/src/libraries/notification/NotificationLibrary.ts +++ b/src/libraries/notification/NotificationLibrary.ts @@ -14,6 +14,8 @@ type UserNotificationType = { }; async function sendUserNotification(notificationType: UserNotificationType) { + // Todo: Add optional email as well! + await Notification.create({ uuid: generateUUID(), user_id: notificationType.user_id, diff --git a/src/models/Role.ts b/src/models/Role.ts index 8145c84..fba4c9e 100644 --- a/src/models/Role.ts +++ b/src/models/Role.ts @@ -2,11 +2,7 @@ import { Association, CreationOptional, InferAttributes, InferCreationAttributes import { sequelize } from "../core/Sequelize"; import { Permission } from "./Permission"; import { User } from "./User"; -import { - ROLE_BELONGS_TO_USER_TABLE_NAME, - ROLE_TABLE_ATTRIBUTES, - ROLE_TABLE_NAME -} from "../../db/migrations/20221115171263-create-permission-tables"; +import { ROLE_BELONGS_TO_USER_TABLE_NAME, ROLE_TABLE_ATTRIBUTES, ROLE_TABLE_NAME } from "../../db/migrations/20221115171263-create-permission-tables"; export class Role extends Model, InferCreationAttributes> { // diff --git a/src/models/User.ts b/src/models/User.ts index 70ed685..a50c1fb 100644 --- a/src/models/User.ts +++ b/src/models/User.ts @@ -105,6 +105,22 @@ export class User extends Model, InferCreationAttributes { + const user = await User.findOne({ + where: { + id: this.id, + }, + include: [ + { + association: User.associations.training_sessions, + as: "training_sessions", + }, + ], + }); + + return user?.training_sessions ?? []; + } + + async getTrainingSessionsWithCourseAndStation(): Promise { const user = await User.findOne({ where: { id: this.id, @@ -114,8 +130,9 @@ export class User extends Model, InferCreationAttributes