import { ClosedQuestionStatus , LimboQuestionStatus , OpenQuestionStatus , } from '@koh/common' ; import { Injectable } from '@nestjs/common' ; import { Cron , CronExpression } from '@nestjs/schedule' ; import async from 'async' ; import { OfficeHourModel } from 'course/office-hour.entity' ; import { EventModel , EventType } from 'profile/event-model.entity' ; import { UserCourseModel } from 'profile/user-course.entity' ; import { Connection , LessThanOrEqual , MoreThanOrEqual } from 'typeorm' ; import { QuestionModel } from '../../question/question.entity' ; import { QueueModel } from '../queue.entity' ; import moment = require ( 'moment' ) ; /**
 * Clean the queue and mark stale
 */ @ Injectable ( ) export class QueueCleanService { constructor ( private connection ) { } @ Cron ( CronExpression . EVERY_DAY_AT_MIDNIGHT ) async cleanAllQueues ( ) { const queuesWithOpenQuestions = await QueueModel . getRepository ( ) . createQueryBuilder ( 'queue_model' ) . leftJoinAndSelect ( 'queue_model.questions' , 'question' ) . where ( 'question.status IN (:...status)' , { status : [ ... Object . values ( OpenQuestionStatus ) , ... Object . values ( LimboQuestionStatus ) , ] , } ) . getMany ( ) ; await async . mapLimit ( queuesWithOpenQuestions , 1 , async ( queue ) => await this . cleanQueue ( queue . id ) ) ; } @ Cron ( CronExpression . EVERY_DAY_AT_3AM ) public async checkoutAllStaff ( ) { const queuesWithCheckedInStaff = await QueueModel . getRepository ( ) . find ( { relations : [ 'staffList' ] } ) ; queuesWithCheckedInStaff . forEach ( async ( queue ) => { if ( ! ( await queue . areThereOfficeHoursRightNow ( ) ) ) { await queue . staffList . forEach ( async ( ta ) => { await EventModel . create ( { time : new Date ( ) , eventType : EventType . TA_CHECKED_OUT_FORCED , userId : ta . id , courseId : queue . courseId , queueId : queue . id , } ) . save ( ) ; } ) ; queue . staffList = [ ] ; } } ) ; await QueueModel . save ( queuesWithCheckedInStaff ) ; } @ Cron ( CronExpression . EVERY_DAY_AT_3AM ) public async cleanSelfEnrollOverrides ( ) { await UserCourseModel . delete ( { expires : true , } ) ; } public async cleanQueue ( queueId , force ? ) { const queue = await QueueModel . findOne ( queueId , { relations : [ 'staffList' ] , } ) ; if ( force || ! ( await queue . checkIsOpen ( ) ) ) { await this . unsafeClean ( queue . id ) ; } } public async shouldCleanQueue ( queue ) { if ( queue . staffList . length === 0 ) { const areAnyQuestionsOpen = ( await QuestionModel . inQueueWithStatus ( queue . id , Object . values ( OpenQuestionStatus ) ) . getCount ( ) ) > 0 ; if ( areAnyQuestionsOpen ) { const soon = moment ( ) . add ( 15 , 'minutes' ) . toDate ( ) ; const areOfficeHourSoon = ( await OfficeHourModel . count ( { where : { startTime : LessThanOrEqual ( soon ) , endTime : MoreThanOrEqual ( soon ) , } , } ) ) > 0 ; if ( ! areOfficeHourSoon ) { return true ; } } } return false ; } private async unsafeClean ( queueId ) { const questions = await QuestionModel . inQueueWithStatus ( queueId , [ ... Object . values ( OpenQuestionStatus ) , ... Object . values ( LimboQuestionStatus ) , ] ) . getMany ( ) ; questions . forEach ( ( q ) => { q . status = ClosedQuestionStatus . Stale ; q . closedAt = new Date ( ) ; } ) ; await QuestionModel . save ( questions ) ; } }