Skip to content

Commit

Permalink
Calculate mean ratings across stages (#63)
Browse files Browse the repository at this point in the history
* feat: getAllAplications endpoint resolves #44

* made Cycle fields private, and created env vars for year/semester

* Application Summary DTO

* feat: completed getAllApplications endpoint

* fix: updated semester logic

* fix: updated current semester function and dto mapping

* mean rating test

* modified mean ratings dto

* code cleanup

---------

Co-authored-by: OJisMe <[email protected]>
Co-authored-by: Jonathan Chen <[email protected]>
Co-authored-by: Harrison Kim <[email protected]>
  • Loading branch information
4 people authored Apr 12, 2024
1 parent f5fdd58 commit d684416
Show file tree
Hide file tree
Showing 8 changed files with 123 additions and 49 deletions.
18 changes: 12 additions & 6 deletions apps/backend/src/applications/application.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
};
}

Expand Down
77 changes: 71 additions & 6 deletions apps/backend/src/applications/applications.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,6 @@ export class ApplicationsService {
where: { user: { id: userId } },
relations: ['user'],
});

return apps;
}

Expand All @@ -141,14 +140,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<Application> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
21 changes: 15 additions & 6 deletions apps/backend/src/reviews/dto/submit-review.request.dto.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
7 changes: 5 additions & 2 deletions apps/backend/src/reviews/review.entity.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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;
Expand Down
7 changes: 1 addition & 6 deletions apps/backend/src/reviews/reviews.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
15 changes: 8 additions & 7 deletions apps/backend/src/reviews/reviews.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -19,19 +19,20 @@ export class ReviewsService {
*/
async createReview(
currentUser: User,
applicantId: number,
rating: Rating,
content: string,
createReviewDTO: SubmitReviewRequestDTO,
): Promise<Review> {
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);
Expand Down
13 changes: 0 additions & 13 deletions apps/backend/src/reviews/types.ts
Original file line number Diff line number Diff line change
@@ -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',
}

0 comments on commit d684416

Please sign in to comment.