diff --git a/annotation-server/src/app.module.ts b/annotation-server/src/app.module.ts index 74864477c..8158cd77b 100644 --- a/annotation-server/src/app.module.ts +++ b/annotation-server/src/app.module.ts @@ -4,6 +4,7 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { AppController } from './app.controller'; import { AppService } from './app.service'; +import { FetchDatesModule } from './fetch-dates/fetch-dates.module'; import { GuidelinesModule } from './guidelines/guidelines.module'; import { MedicationsModule } from './medications/medications.module'; import { PhenotypesModule } from './phenotypes/phenotypes.module'; @@ -34,6 +35,7 @@ import { PhenotypesModule } from './phenotypes/phenotypes.module'; PhenotypesModule, GuidelinesModule, MedicationsModule, + FetchDatesModule, ], controllers: [AppController], providers: [AppService], diff --git a/annotation-server/src/fetch-dates/fetch-date.entity.ts b/annotation-server/src/fetch-dates/fetch-date.entity.ts new file mode 100644 index 000000000..ff2969bcd --- /dev/null +++ b/annotation-server/src/fetch-dates/fetch-date.entity.ts @@ -0,0 +1,15 @@ +import { Entity, PrimaryColumn, UpdateDateColumn } from 'typeorm'; + +export enum FetchTarget { + MEDICATIONS = 'medications', + GUIDELINES = 'guidelines', +} + +@Entity() +export class FetchDate { + @PrimaryColumn({ type: 'enum', enum: FetchTarget }) + target: FetchTarget; + + @UpdateDateColumn({ type: 'timestamp with time zone' }) + date: Date; +} diff --git a/annotation-server/src/fetch-dates/fetch-dates.module.ts b/annotation-server/src/fetch-dates/fetch-dates.module.ts new file mode 100644 index 000000000..eafb5fb10 --- /dev/null +++ b/annotation-server/src/fetch-dates/fetch-dates.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { FetchDate } from './fetch-date.entity'; +import { FetchDatesService } from './fetch-dates.service'; + +@Module({ + imports: [TypeOrmModule.forFeature([FetchDate])], + providers: [FetchDatesService], + exports: [FetchDatesService], +}) +export class FetchDatesModule {} diff --git a/annotation-server/src/fetch-dates/fetch-dates.service.ts b/annotation-server/src/fetch-dates/fetch-dates.service.ts new file mode 100644 index 000000000..1c21317c5 --- /dev/null +++ b/annotation-server/src/fetch-dates/fetch-dates.service.ts @@ -0,0 +1,26 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { FetchDate, FetchTarget } from './fetch-date.entity'; + +@Injectable() +export class FetchDatesService { + constructor( + @InjectRepository(FetchDate) + private fetchDatesRepository: Repository, + ) {} + + async get(target: FetchTarget): Promise { + const fetchDate = await this.fetchDatesRepository.findOneBy({ target }); + return fetchDate?.date; + } + + async set(target: FetchTarget): Promise { + try { + await this.fetchDatesRepository.insert({ target }); + } catch { + await this.fetchDatesRepository.update({ target }, { target }); + } + } +} diff --git a/annotation-server/src/guidelines/guidelines.controller.ts b/annotation-server/src/guidelines/guidelines.controller.ts index da137ee51..6a7598024 100644 --- a/annotation-server/src/guidelines/guidelines.controller.ts +++ b/annotation-server/src/guidelines/guidelines.controller.ts @@ -24,6 +24,12 @@ export class GuidelinesController { return this.guidelinesService.fetchGuidelines(); } + @ApiOperation({ summary: `Get the previous CPIC data update's date` }) + @Get('last_update') + getLastUpdate(): Promise { + return this.guidelinesService.getLastUpdate(); + } + @ApiOperation({ summary: 'Fetch all guidelines' }) @ApiFindGuidelinesQueries() @Get() diff --git a/annotation-server/src/guidelines/guidelines.module.ts b/annotation-server/src/guidelines/guidelines.module.ts index 68b55374b..16d51d978 100644 --- a/annotation-server/src/guidelines/guidelines.module.ts +++ b/annotation-server/src/guidelines/guidelines.module.ts @@ -2,6 +2,7 @@ import { HttpModule } from '@nestjs/axios'; import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; +import { FetchDatesModule } from '../fetch-dates/fetch-dates.module'; import { MedicationsModule } from '../medications/medications.module'; import { PhenotypesModule } from '../phenotypes/phenotypes.module'; import { GuidelineError } from './entities/guideline-error.entity'; @@ -15,6 +16,7 @@ import { GuidelinesService } from './guidelines.service'; PhenotypesModule, MedicationsModule, TypeOrmModule.forFeature([Guideline, GuidelineError]), + FetchDatesModule, ], controllers: [GuidelinesController], providers: [GuidelinesService], diff --git a/annotation-server/src/guidelines/guidelines.service.ts b/annotation-server/src/guidelines/guidelines.service.ts index 4777abed7..a57d8cc03 100644 --- a/annotation-server/src/guidelines/guidelines.service.ts +++ b/annotation-server/src/guidelines/guidelines.service.ts @@ -7,6 +7,8 @@ import { lastValueFrom } from 'rxjs'; import { FindOptionsOrder, FindOptionsOrderValue, Repository } from 'typeorm'; import { fetchSpreadsheetCells } from '../common/utils/google-sheets'; +import { FetchTarget } from '../fetch-dates/fetch-date.entity'; +import { FetchDatesService } from '../fetch-dates/fetch-dates.service'; import { Medication } from '../medications/medication.entity'; import { MedicationsService } from '../medications/medications.service'; import { Phenotype } from '../phenotypes/entities/phenotype.entity'; @@ -46,6 +48,7 @@ export class GuidelinesService { private guidelineErrorRepository: Repository, private medicationsService: MedicationsService, private phenotypesService: PhenotypesService, + private fetchDatesService: FetchDatesService, ) { this.spreadsheetGeneResultHeader = []; this.medicationsByNameCache = new MedicationByNameCache( @@ -134,6 +137,11 @@ export class GuidelinesService { const guidelines = await this.fetchCpicGuidelines(); await this.addGuidelineURLS(guidelines); await this.complementAndSaveGuidelines(guidelines); + await this.fetchDatesService.set(FetchTarget.GUIDELINES); + } + + async getLastUpdate(): Promise { + return this.fetchDatesService.get(FetchTarget.GUIDELINES); } private async fetchCpicGuidelines(): Promise> { diff --git a/annotation-server/src/medications/medications.controller.ts b/annotation-server/src/medications/medications.controller.ts index 8ba7d2baa..c79b1eb72 100644 --- a/annotation-server/src/medications/medications.controller.ts +++ b/annotation-server/src/medications/medications.controller.ts @@ -14,6 +14,12 @@ import { MedicationsService } from './medications.service'; export class MedicationsController { constructor(private medicationsService: MedicationsService) {} + @ApiOperation({ summary: `Get the previous DrugBank data update's date` }) + @Get('last_update') + getLastUpdate(): Promise { + return this.medicationsService.getLastUpdate(); + } + @ApiOperation({ summary: 'Fetch all medications' }) @ApiFindMedicationsQueries() @Get() diff --git a/annotation-server/src/medications/medications.module.ts b/annotation-server/src/medications/medications.module.ts index bccfc1675..0846009a6 100644 --- a/annotation-server/src/medications/medications.module.ts +++ b/annotation-server/src/medications/medications.module.ts @@ -2,6 +2,7 @@ import { HttpModule } from '@nestjs/axios'; import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; +import { FetchDatesModule } from '../fetch-dates/fetch-dates.module'; import { Medication, MedicationSearchView } from './medication.entity'; import { MedicationsController } from './medications.controller'; import { MedicationsService } from './medications.service'; @@ -10,6 +11,7 @@ import { MedicationsService } from './medications.service'; imports: [ HttpModule, TypeOrmModule.forFeature([Medication, MedicationSearchView]), + FetchDatesModule, ], controllers: [MedicationsController], providers: [MedicationsService], diff --git a/annotation-server/src/medications/medications.service.spec.ts b/annotation-server/src/medications/medications.service.spec.ts index 8f68c710e..61f9c6216 100644 --- a/annotation-server/src/medications/medications.service.spec.ts +++ b/annotation-server/src/medications/medications.service.spec.ts @@ -2,6 +2,8 @@ import { ConfigModule } from '@nestjs/config'; import { Test, TestingModule } from '@nestjs/testing'; import { getRepositoryToken } from '@nestjs/typeorm'; +import { FetchDate } from '../fetch-dates/fetch-date.entity'; +import { FetchDatesService } from '../fetch-dates/fetch-dates.service'; import { Medication } from './medication.entity'; import { MedicationsService } from './medications.service'; @@ -19,6 +21,11 @@ describe('MedicationsService', () => { provide: getRepositoryToken(Medication), useValue: {}, }, + FetchDatesService, + { + provide: getRepositoryToken(FetchDate), + useValue: {}, + }, ], }).compile(); diff --git a/annotation-server/src/medications/medications.service.ts b/annotation-server/src/medications/medications.service.ts index 66cdf17da..50765ecf7 100644 --- a/annotation-server/src/medications/medications.service.ts +++ b/annotation-server/src/medications/medications.service.ts @@ -19,6 +19,8 @@ import { } from 'typeorm'; import { fetchSpreadsheetCells } from '../common/utils/google-sheets'; +import { FetchTarget } from '../fetch-dates/fetch-date.entity'; +import { FetchDatesService } from '../fetch-dates/fetch-dates.service'; import { DrugDto } from './dtos/drugbank.dto'; import { Medication, MedicationSearchView } from './medication.entity'; @@ -30,6 +32,7 @@ export class MedicationsService { private configService: ConfigService, @InjectRepository(Medication) private medicationRepository: Repository, + private fetchDatesService: FetchDatesService, ) {} async findAll( @@ -146,11 +149,16 @@ export class MedicationsService { const savedMedications = await this.medicationRepository.save( medications, ); + await this.fetchDatesService.set(FetchTarget.MEDICATIONS); this.logger.log( `Successfully saved ${savedMedications.length} medications!`, ); } + async getLastUpdate(): Promise { + return this.fetchDatesService.get(FetchTarget.MEDICATIONS); + } + async clearAllMedicationData(): Promise { await this.medicationRepository.delete({}); }