diff --git a/apps/backend/src/applications/application.entity.ts b/apps/backend/src/applications/application.entity.ts index c59ceb8..5ae7182 100644 --- a/apps/backend/src/applications/application.entity.ts +++ b/apps/backend/src/applications/application.entity.ts @@ -63,10 +63,13 @@ export class Application { @IsObject({ each: true }) reviews: Review[]; - toGetAllApplicationResponseDTO(): GetAllApplicationResponseDTO { - const meanRatingAllStages = 0; // TODO: calculate this - const meanRatingSingleStages = 0; // TODO: calculate this (should be an object) - + toGetAllApplicationResponseDTO( + meanRatingAllReviews, + meanRatingResume, + meanRatingChallenge, + meanRatingTechnicalChallenge, + meanRatingInterview, + ): GetAllApplicationResponseDTO { return { userId: this.user.id, firstName: this.user.firstName, @@ -75,8 +78,11 @@ export class Application { step: this.step, position: this.position, createdAt: this.createdAt, - meanRatingAllStages: meanRatingAllStages, - meanRatingSingleStages: meanRatingSingleStages, + meanRatingAllReviews, + meanRatingResume, + meanRatingChallenge, + meanRatingTechnicalChallenge, + meanRatingInterview, }; } diff --git a/apps/backend/src/applications/applications.service.ts b/apps/backend/src/applications/applications.service.ts index 45868f6..fc12b3d 100644 --- a/apps/backend/src/applications/applications.service.ts +++ b/apps/backend/src/applications/applications.service.ts @@ -99,7 +99,6 @@ export class ApplicationsService { where: { user: { id: userId } }, relations: ['user'], }); - return apps; } @@ -109,14 +108,80 @@ export class ApplicationsService { year: getCurrentYear(), semester: getCurrentSemester(), }, - relations: ['user'], + relations: ['reviews'], }); - const dtos: GetAllApplicationResponseDTO[] = applications.map((app) => - app.toGetAllApplicationResponseDTO(), - ); + const allApplicationsDto = applications.map((app) => { + // Initialize variables for storing mean ratings + let meanRatingAllReviews = null; + let meanRatingResume = null; + let meanRatingChallenge = null; // Default to null for DESIGNERS + let meanRatingTechnicalChallenge = null; + let meanRatingInterview = null; + + // Calculate mean rating of all reviews + if (app.reviews.length > 0) { + meanRatingAllReviews = + app.reviews.reduce((acc, review) => acc + review.rating, 0) / + app.reviews.length; + } + + // Filter reviews by stage and calculate mean ratings accordingly + const resumeReviews = app.reviews.filter( + (review) => review.stage === ApplicationStage.RESUME, + ); + const challengeReviews = app.reviews.filter( + (review) => + review.stage === ApplicationStage.TECHNICAL_CHALLENGE || + review.stage === ApplicationStage.PM_CHALLENGE, + ); + const technicalChallengeReviews = app.reviews.filter( + (review) => review.stage === ApplicationStage.TECHNICAL_CHALLENGE, + ); + const interviewReviews = app.reviews.filter( + (review) => review.stage === ApplicationStage.INTERVIEW, + ); + + // Mean rating for RESUME stage + if (resumeReviews.length > 0) { + meanRatingResume = + resumeReviews.reduce((acc, review) => acc + review.rating, 0) / + resumeReviews.length; + } + + // Mean rating for CHALLENGE stage (for DEVS and PMS) + if (challengeReviews.length > 0) { + meanRatingChallenge = + challengeReviews.reduce((acc, review) => acc + review.rating, 0) / + challengeReviews.length; + } + + // Mean rating for TECHNICAL_CHALLENGE stage (specifically for DEVS) + if (technicalChallengeReviews.length > 0) { + meanRatingTechnicalChallenge = + technicalChallengeReviews.reduce( + (acc, review) => acc + review.rating, + 0, + ) / technicalChallengeReviews.length; + } + + // Mean rating for INTERVIEW stage + if (interviewReviews.length > 0) { + meanRatingInterview = + interviewReviews.reduce((acc, review) => acc + review.rating, 0) / + interviewReviews.length; + } + + return app.toGetAllApplicationResponseDTO( + meanRatingAllReviews, + meanRatingResume, + meanRatingChallenge, + meanRatingTechnicalChallenge, + meanRatingInterview, + ); + }); - return dtos; + return allApplicationsDto; } async findCurrent(userId: number): Promise { diff --git a/apps/backend/src/applications/dto/get-all-application.response.dto.ts b/apps/backend/src/applications/dto/get-all-application.response.dto.ts index 5ed0264..ee6fd71 100644 --- a/apps/backend/src/applications/dto/get-all-application.response.dto.ts +++ b/apps/backend/src/applications/dto/get-all-application.response.dto.ts @@ -24,9 +24,17 @@ export class GetAllApplicationResponseDTO { createdAt: Date; @IsPositive() - meanRatingAllStages: number; + meanRatingAllReviews: number; - // TODO: Should be a JSON or similar that defines scores for each stage @IsPositive() - meanRatingSingleStages: number; + meanRatingResume: number; + + @IsPositive() + meanRatingChallenge: number; + + @IsPositive() + meanRatingTechnicalChallenge: number; + + @IsPositive() + meanRatingInterview: number; } diff --git a/apps/backend/src/reviews/dto/submit-review.request.dto.ts b/apps/backend/src/reviews/dto/submit-review.request.dto.ts index e9f60f9..3e1b3f3 100644 --- a/apps/backend/src/reviews/dto/submit-review.request.dto.ts +++ b/apps/backend/src/reviews/dto/submit-review.request.dto.ts @@ -1,15 +1,24 @@ -import { IsEnum, IsPositive, IsString } from 'class-validator'; -import { Rating, Stage } from '../types'; +import { + IsEnum, + IsNumber, + Min, + Max, + IsPositive, + IsString, +} from 'class-validator'; +import { ApplicationStage } from '../../applications/types'; export class SubmitReviewRequestDTO { @IsPositive() applicantId: number; - @IsEnum(Stage) - stage: Stage; + @IsEnum(ApplicationStage) + stage: ApplicationStage; - @IsEnum(Rating) - rating: Rating; + @IsNumber({}, { message: 'Rating must be a valid number' }) + @Min(1, { message: 'Rating must be at least 1' }) + @Max(5, { message: 'Rating must be at most 5' }) + rating: number; @IsString() content: string; diff --git a/apps/backend/src/reviews/review.entity.ts b/apps/backend/src/reviews/review.entity.ts index 6cd5b3b..c3f1b6a 100644 --- a/apps/backend/src/reviews/review.entity.ts +++ b/apps/backend/src/reviews/review.entity.ts @@ -1,6 +1,6 @@ import { Column, Entity, JoinColumn, ManyToOne } from 'typeorm'; import { Application } from '../applications/application.entity'; -import { Rating } from './types'; +import { ApplicationStage } from '../applications/types'; @Entity() export class Review { @@ -21,8 +21,11 @@ export class Review { @Column({ nullable: false }) reviewerId: number; + @Column({ nullable: false }) + rating: number; + @Column({ type: 'varchar', nullable: false }) - rating: Rating; + stage: ApplicationStage; @Column({ nullable: false }) content: string; diff --git a/apps/backend/src/reviews/reviews.controller.ts b/apps/backend/src/reviews/reviews.controller.ts index ad99b0a..51f6ddb 100644 --- a/apps/backend/src/reviews/reviews.controller.ts +++ b/apps/backend/src/reviews/reviews.controller.ts @@ -31,11 +31,6 @@ export class ReviewsController { ); } - return this.reviewsService.createReview( - req.user, - createReviewDTO.applicantId, - createReviewDTO.rating, - createReviewDTO.content, - ); + return this.reviewsService.createReview(req.user, createReviewDTO); } } diff --git a/apps/backend/src/reviews/reviews.service.ts b/apps/backend/src/reviews/reviews.service.ts index 6caa4d7..84dd311 100644 --- a/apps/backend/src/reviews/reviews.service.ts +++ b/apps/backend/src/reviews/reviews.service.ts @@ -2,9 +2,9 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { MongoRepository } from 'typeorm'; import { Review } from './review.entity'; -import { Rating } from './types'; import { ApplicationsService } from '../applications/applications.service'; import { User } from '../users/user.entity'; +import { SubmitReviewRequestDTO } from './dto/submit-review.request.dto'; @Injectable() export class ReviewsService { @@ -19,19 +19,20 @@ export class ReviewsService { */ async createReview( currentUser: User, - applicantId: number, - rating: Rating, - content: string, + createReviewDTO: SubmitReviewRequestDTO, ): Promise { - const application = await this.applicationsService.findCurrent(applicantId); + const application = await this.applicationsService.findCurrent( + createReviewDTO.applicantId, + ); const review = this.reviewsRepository.create({ reviewerId: currentUser.id, createdAt: new Date(), updatedAt: new Date(), application, - rating, - content, + rating: createReviewDTO.rating, + content: createReviewDTO.content, + stage: createReviewDTO.stage, }); return this.reviewsRepository.save(review); diff --git a/apps/backend/src/reviews/types.ts b/apps/backend/src/reviews/types.ts index 9d93853..e69de29 100644 --- a/apps/backend/src/reviews/types.ts +++ b/apps/backend/src/reviews/types.ts @@ -1,13 +0,0 @@ -export enum Stage { - ROUND_ONE = 'ROUND_ONE', - ROUND_TWO = 'ROUND_TWO', - ROUND_THREE = 'ROUND_THREE', -} - -export enum Rating { - STRONG_YES = 'STRONG_YES', - YES = 'YES', - NEUTRAL = 'NEUTRAL', - NO = 'NO', - STRONG = 'STRONG', -}