diff --git a/migrations/1775842011163-migrations.ts b/migrations/1775842011163-migrations.ts new file mode 100644 index 0000000..bda7651 --- /dev/null +++ b/migrations/1775842011163-migrations.ts @@ -0,0 +1,26 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class Migrations1775842011163 implements MigrationInterface { + name = 'Migrations1775842011163'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "cohort" DROP COLUMN "endDate"`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "cohort" ADD "endDate" TIMESTAMP WITH TIME ZONE`, + ); + await queryRunner.query(` + UPDATE "cohort" c + SET "endDate" = ( + SELECT MAX(cw."scheduledDate") + FROM "cohort_week" cw + WHERE cw."cohortId" = c."id" + ) + `); + await queryRunner.query( + `ALTER TABLE "cohort" ALTER COLUMN "endDate" SET NOT NULL`, + ); + } +} diff --git a/src/certificates/certificates-generation.service.ts b/src/certificates/certificates-generation.service.ts index 5f7e448..10b20f5 100644 --- a/src/certificates/certificates-generation.service.ts +++ b/src/certificates/certificates-generation.service.ts @@ -154,7 +154,7 @@ export class CertificatesGenerationService { certificate.name, certificate.cohort.type, certificate.type, - certificate.cohort.endDate, + certificate.cohort.getEndDate(), certificate.withExercises, certificate.rank, ); diff --git a/src/certificates/certificates.service.ts b/src/certificates/certificates.service.ts index bacfcec..698a14e 100644 --- a/src/certificates/certificates.service.ts +++ b/src/certificates/certificates.service.ts @@ -50,13 +50,14 @@ export class CertificatesService { ): Promise<{ cohort: Cohort; certificateEntities: Certificate[] }> { const cohort = await this.cohortRepository.findOne({ where: { id: cohortId }, + relations: { weeks: true }, }); if (!cohort) { throw new ServiceError(`Cohort with id ${cohortId} not found`); } - if (cohort.endDate > new Date()) { + if (cohort.getEndDate() > new Date()) { throw new BadRequestException( `Cohort with id ${cohortId} has not ended yet. Certificates can only be generated after the cohort ends.`, ); @@ -212,7 +213,7 @@ export class CertificatesService { const certificates = await this.certificateRepository.find({ where: { cohort: { id: cohortId } }, - relations: { cohort: true, user: true }, + relations: { cohort: { weeks: true }, user: true }, }); if (certificates.length === 0) { @@ -279,7 +280,7 @@ export class CertificatesService { const certificate = await this.certificateRepository.findOne({ where: { id }, relations: { - cohort: true, + cohort: { weeks: true }, user: true, }, }); @@ -310,7 +311,7 @@ export class CertificatesService { }> { const certificates = await this.certificateRepository.find({ where: { cohort: { id: cohortId } }, - relations: { cohort: true, user: true }, + relations: { cohort: { weeks: true }, user: true }, }); if (certificates.length === 0) { diff --git a/src/cohorts/cohort-reminder.service.ts b/src/cohorts/cohort-reminder.service.ts index 17183f6..481a513 100644 --- a/src/cohorts/cohort-reminder.service.ts +++ b/src/cohorts/cohort-reminder.service.ts @@ -185,7 +185,7 @@ export class CohortReminderService { const cohort = await this.cohortRepository.findOne({ where: { id: cohortId }, - relations: { users: true }, + relations: { users: true, weeks: true }, }); if (!cohort) { @@ -271,7 +271,7 @@ export class CohortReminderService { const nextExecuteOnTime = new Date(task.executeOnTime); nextExecuteOnTime.setUTCDate(nextExecuteOnTime.getUTCDate() + 7); - const cutoffDate = new Date(cohort.endDate); + const cutoffDate = new Date(cohort.getEndDate()); cutoffDate.setUTCDate(cutoffDate.getUTCDate() + 7); if (nextExecuteOnTime <= cutoffDate) { diff --git a/src/cohorts/cohorts.response.dto.ts b/src/cohorts/cohorts.response.dto.ts index 896a7a7..f0827c6 100644 --- a/src/cohorts/cohorts.response.dto.ts +++ b/src/cohorts/cohorts.response.dto.ts @@ -56,7 +56,7 @@ export class GetCohortResponseDto { type: cohort.type, season: cohort.season, startDate: cohort.startDate.toISOString(), - endDate: cohort.endDate.toISOString(), + endDate: cohort.getEndDate().toISOString(), registrationDeadline: cohort.registrationDeadline.toISOString(), hasExercises: cohort.hasExercises, classroomId: cohort.classroomId ?? null, diff --git a/src/cohorts/cohorts.service.ts b/src/cohorts/cohorts.service.ts index d5d70cb..cfa7726 100644 --- a/src/cohorts/cohorts.service.ts +++ b/src/cohorts/cohorts.service.ts @@ -161,7 +161,7 @@ export class CohortsService { type: cohort.type, season: cohort.season, startDate: cohort.startDate.toISOString(), - endDate: cohort.endDate.toISOString(), + endDate: cohort.getEndDate().toISOString(), registrationDeadline: cohort.registrationDeadline.toISOString(), }), @@ -189,6 +189,7 @@ export class CohortsService { async listPublicCohorts(): Promise { const latestCohorts = await this.cohortRepository .createQueryBuilder('c') + .leftJoinAndSelect('c.weeks', 'weeks') .distinctOn(['c.type']) .orderBy('c.type', 'ASC') .addOrderBy('c.season', 'DESC') @@ -303,7 +304,6 @@ export class CohortsService { cohort.type = cohortData.type; cohort.season = season; cohort.startDate = startDate; - cohort.endDate = endDate; cohort.registrationDeadline = registrationDeadline; cohort.hasExercises = hasExercises; cohort.weeks = []; @@ -411,12 +411,6 @@ export class CohortsService { const startDate = new Date(cohortData.startDate); startDate.setUTCHours(0, 0, 0, 0); cohort.startDate = startDate; - - const config = this.cohortConfigService.getConfig(cohort.type); - const totalWeeks = config.gdSessions + 2; - const endDate = new Date(startDate); - endDate.setUTCDate(endDate.getUTCDate() + totalWeeks * 7); - cohort.endDate = endDate; } if (cohortData.registrationDeadline) { const registrationDeadline = new Date( diff --git a/src/entities/cohort.entity.ts b/src/entities/cohort.entity.ts index d471d02..37c7895 100644 --- a/src/entities/cohort.entity.ts +++ b/src/entities/cohort.entity.ts @@ -34,15 +34,20 @@ export class Cohort extends BaseEntity { @Column('timestamptz') startDate!: Date; - @Column('timestamptz') - endDate!: Date; - @Column('boolean') hasExercises!: boolean; @Column('text', { nullable: true }) classroomId!: string | null; + getEndDate(): Date { + return this.weeks.reduce( + (max, week) => + week.scheduledDate > max ? week.scheduledDate : max, + this.weeks[0].scheduledDate, + ); + } + @ManyToMany(() => User, (u) => u.cohorts) @JoinTable() users!: User[]; diff --git a/src/github-classroom/github-classroom.service.ts b/src/github-classroom/github-classroom.service.ts index baad891..11c65dd 100644 --- a/src/github-classroom/github-classroom.service.ts +++ b/src/github-classroom/github-classroom.service.ts @@ -84,7 +84,7 @@ export class GitHubClassroomService { } const endDatePlusBuffer = new Date( - cohort.endDate.getTime() + TWENTY_FOUR_HOURS_MS, + cohort.getEndDate().getTime() + TWENTY_FOUR_HOURS_MS, ); const hasCohortEnded = endDatePlusBuffer < new Date();