302 lines
		
	
	
	
		
			9.7 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			302 lines
		
	
	
	
		
			9.7 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import Colle from '#models/colle'
 | |
| import { ColleService } from '#services/colle_service'
 | |
| import { NotificationService } from '#services/notification_service'
 | |
| import { createColleValidator, createUpcomingCollesValidator } from '#validators/colle'
 | |
| import { inject } from '@adonisjs/core'
 | |
| import type { HttpContext } from '@adonisjs/core/http'
 | |
| import redis from '@adonisjs/redis/services/main'
 | |
| import { DateTime } from 'luxon'
 | |
| 
 | |
| @inject()
 | |
| export default class CollesController {
 | |
|   constructor(
 | |
|     private service: ColleService,
 | |
|     private notificationService: NotificationService
 | |
|   ) {}
 | |
| 
 | |
|   async index({ request, response, auth }: HttpContext) {
 | |
|     const { startDate: rawStartDate } = request.qs()
 | |
| 
 | |
|     // Validate startDate
 | |
|     const startDate = rawStartDate ? DateTime.fromISO(rawStartDate, { zone: 'local' }) : null
 | |
|     if (!rawStartDate || !startDate || !startDate.isValid) {
 | |
|       return response.badRequest({ message: 'Invalid start date format' })
 | |
|     }
 | |
|     const endDate = startDate.plus({ days: 6 }) // Sunday
 | |
| 
 | |
|     // Retrieve colles for the authenticated user
 | |
|     const data = await this.service.getColles(auth.user!, startDate.toISO(), endDate.toISO())
 | |
|     return {
 | |
|       success: true,
 | |
|       data,
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   async show({ params, response, auth }: HttpContext) {
 | |
|     const colleId = parseInt(params.colleId)
 | |
|     if (isNaN(colleId)) {
 | |
|       return response.badRequest({ message: 'Invalid colle ID' })
 | |
|     }
 | |
|     // Retrieve the colle by ID
 | |
|     const colle = await Colle.query()
 | |
|       .where('id', colleId)
 | |
|       .whereHas('student', (query) => {
 | |
|         query.where('className', auth.user!.className)
 | |
|       })
 | |
|       .preload('student')
 | |
|       .preload('examiner')
 | |
|       .preload('subject')
 | |
|       .preload('room')
 | |
|       .preload('attachments')
 | |
|       .first()
 | |
|     if (!colle) {
 | |
|       return response.notFound({ message: 'Colle not found' })
 | |
|     }
 | |
| 
 | |
|     const relatives = await Colle.query()
 | |
|       .where('subjectId', colle.subjectId)
 | |
|       .where('date', colle.date.toISO()!)
 | |
|       .where('id', '!=', colle.id) // Exclude the current colle
 | |
|       .whereHas('student', (query) => {
 | |
|         query.where('className', auth.user!.className)
 | |
|       })
 | |
|       .preload('student')
 | |
| 
 | |
|     return {
 | |
|       success: true,
 | |
|       data: {
 | |
|         ...colle.serialize(),
 | |
|         bjid: colle.bjid,
 | |
|         bjsecret: colle.bjsecret,
 | |
|         relatives,
 | |
|       },
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   async refresh({ params, response, auth }: HttpContext) {
 | |
|     const colleId = parseInt(params.colleId)
 | |
|     if (isNaN(colleId)) {
 | |
|       return response.badRequest({ message: 'Invalid colle ID' })
 | |
|     }
 | |
| 
 | |
|     // Retrieve the colle by ID
 | |
|     const colle = await Colle.query()
 | |
|       .where('id', colleId)
 | |
|       .whereHas('student', (query) => {
 | |
|         query.where('className', auth.user!.className)
 | |
|       })
 | |
|       .first()
 | |
|     if (!colle) {
 | |
|       return response.notFound({ message: 'Colle not found' })
 | |
|     }
 | |
| 
 | |
|     // Post Redis message to refresh the colle
 | |
|     await redis.publish(
 | |
|       'jobs_queue',
 | |
|       JSON.stringify({
 | |
|         type: 0, // Refresh colle
 | |
|         colle_id: colle.bjid,
 | |
|         colle_secret: colle.bjsecret,
 | |
|         class_name: auth.user!.className,
 | |
|       })
 | |
|     )
 | |
|     return response.ok({
 | |
|       success: true,
 | |
|       message: `Colle ${colleId} refresh request sent`,
 | |
|     })
 | |
|   }
 | |
| 
 | |
|   async create({ request, response }: HttpContext) {
 | |
|     const { colle: payload, className } = await request.validateUsing(createColleValidator)
 | |
| 
 | |
|     // TODO: Use Redis cache to avoid multiple queries
 | |
|     // Retrieve or create the necessary entities (relations)
 | |
|     const student = await this.service.getStudent(payload.student, className)
 | |
|     const examiner = await this.service.getExaminer(payload.examiner)
 | |
|     const subject = await this.service.getSubject(payload.subject)
 | |
|     const room = await this.service.getRoom(payload.room)
 | |
|     const date = DateTime.fromJSDate(payload.date)
 | |
|     if (!date.isValid) {
 | |
|       return response.badRequest({ message: 'Invalid date format' })
 | |
|     }
 | |
| 
 | |
|     // Prepare the content and comment for rendering
 | |
|     if (payload.comment) {
 | |
|       payload.comment = this.service.prepareHtmlForRendering(payload.comment)
 | |
|     }
 | |
|     if (payload.content) {
 | |
|       payload.content = this.service.prepareHtmlForRendering(payload.content)
 | |
|     }
 | |
| 
 | |
|     const colleData = {
 | |
|       studentId: student.id,
 | |
|       examinerId: examiner.id,
 | |
|       subjectId: subject.id,
 | |
|       roomId: room.id,
 | |
|       bjsecret: payload.bjsecret,
 | |
|       bjid: payload.bjid,
 | |
|       grade: payload.grade,
 | |
|       content: payload.content,
 | |
|       comment: payload.comment,
 | |
|       date,
 | |
|     }
 | |
| 
 | |
|     // Check if the colle already exists
 | |
|     const existing = await Colle.query()
 | |
|       .where('studentId', student.id)
 | |
|       .where('subjectId', subject.id)
 | |
|       .where('date', date.toISO())
 | |
|       .first()
 | |
| 
 | |
|     // If it exists, update the existing colle
 | |
|     if (existing) {
 | |
|       // Merge the new data with the existing colle
 | |
|       const beforeColle = existing.serialize()
 | |
|       Object.assign(existing, colleData)
 | |
| 
 | |
|       // Handle attachments if any
 | |
|       if (payload.attachments && payload.attachments.length > 0) {
 | |
|         // Retrieve existing attachments
 | |
|         const existingAttachments = await existing.related('attachments').query()
 | |
|         // Remove attachments that are not in the new payload
 | |
|         const existingAttachmentUrls = new Set(existingAttachments.map((a) => a.path))
 | |
|         for (const attachment of payload.attachments) {
 | |
|           if (!existingAttachmentUrls.has(attachment.url)) {
 | |
|             await existing.related('attachments').create({
 | |
|               name: attachment.name,
 | |
|               path: attachment.url,
 | |
|             })
 | |
|           }
 | |
|         }
 | |
|         // Remove attachments that are not in the new payload
 | |
|         for (const attachment of existingAttachments) {
 | |
|           if (!payload.attachments.some((a) => a.url === attachment.path)) {
 | |
|             await attachment.delete()
 | |
|           }
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       const afterColle = existing.serialize()
 | |
|       if (!beforeColle.grade && afterColle.grade) {
 | |
|         await this.notificationService.sendNotification('GRADE_ADDED', existing)
 | |
|       } else if (parseFloat(beforeColle.grade) !== afterColle.grade) {
 | |
|         await this.notificationService.sendNotification(
 | |
|           'GRADE_UPDATED',
 | |
|           existing,
 | |
|           parseFloat(beforeColle.grade)
 | |
|         )
 | |
|       }
 | |
|       if (!deepEqual(beforeColle, afterColle)) {
 | |
|         await this.notificationService.sendNotification('COLLE_UPDATED', existing)
 | |
|       }
 | |
|       return existing.save()
 | |
|     }
 | |
|     const colle = await Colle.create(colleData)
 | |
| 
 | |
|     // Handle attachments if any
 | |
|     if (payload.attachments && payload.attachments.length > 0) {
 | |
|       for (const attachment of payload.attachments) {
 | |
|         await colle.related('attachments').create({
 | |
|           path: attachment.url,
 | |
|           name: attachment.name,
 | |
|         })
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     return colle
 | |
|   }
 | |
| 
 | |
|   async createUpcoming({ request }: HttpContext) {
 | |
|     const payload = await request.validateUsing(createUpcomingCollesValidator)
 | |
|     // ONLY UPCOMING COLLES
 | |
|     const now = DateTime.now()
 | |
|     const colles = payload.colles.filter(
 | |
|       (colle) => colle.date && DateTime.fromJSDate(colle.date) > now
 | |
|     )
 | |
| 
 | |
|     // Retrieve all upcoming colles for the class
 | |
|     const upcomingColles = await Colle.query()
 | |
|       .whereHas('student', (query) => {
 | |
|         query.where('className', payload.className)
 | |
|       })
 | |
|       .where('date', '>=', now.toISO())
 | |
|       .orderBy('date', 'asc')
 | |
|     // Store the upcoming colles ids
 | |
|     const upcomingCollesIds = new Set(upcomingColles.map((colle) => colle.id))
 | |
| 
 | |
|     for await (const colle of colles) {
 | |
|       // Find the updated data for the colle
 | |
|       const student = await this.service.getStudent(colle.student, payload.className)
 | |
|       const examiner = await this.service.getExaminer(colle.examiner)
 | |
|       const subject = await this.service.getSubject(colle.subject)
 | |
|       const room = await this.service.getRoom(colle.room)
 | |
|       const date = DateTime.fromJSDate(colle.date)
 | |
| 
 | |
|       const oldColle = upcomingColles.find(
 | |
|         (c) =>
 | |
|           c.studentId === student.id &&
 | |
|           c.subjectId === subject.id &&
 | |
|           c.date.toISO() === date.toISO()
 | |
|       )
 | |
| 
 | |
|       const updatedColle = {
 | |
|         studentId: student.id,
 | |
|         examinerId: examiner.id,
 | |
|         subjectId: subject.id,
 | |
|         roomId: room.id,
 | |
|         bjsecret: colle.bjsecret,
 | |
|         bjid: colle.bjid,
 | |
|         grade: colle.grade,
 | |
|         content: colle.content,
 | |
|         comment: colle.comment,
 | |
|         date,
 | |
|       }
 | |
| 
 | |
|       // Create a new colle if it doesn't exist
 | |
|       if (!oldColle) {
 | |
|         const colle = await Colle.create(updatedColle)
 | |
|         await this.notificationService.sendNotification('COLLE_ADDED', colle)
 | |
|         continue
 | |
|       }
 | |
| 
 | |
|       // Update the colle with the new data
 | |
|       // and remove it from the list
 | |
|       const beforeColle = oldColle.serialize()
 | |
|       Object.assign(oldColle, updatedColle)
 | |
|       await oldColle.save()
 | |
| 
 | |
|       const afterColle = oldColle.serialize()
 | |
|       upcomingCollesIds.delete(oldColle.id)
 | |
| 
 | |
|       if (beforeColle.room !== afterColle.room) {
 | |
|         await this.notificationService.sendNotification('ROOM_UPDATED', oldColle)
 | |
|       } else if (!deepEqual(beforeColle, afterColle)) {
 | |
|         await this.notificationService.sendNotification('COLLE_UPDATED', oldColle)
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // Delete the colles that were not updated
 | |
|     const collesToDelete = await Promise.all(
 | |
|       Array.from(upcomingCollesIds).map((id) => {
 | |
|         return Colle.query()
 | |
|           .where('id', id)
 | |
|           .whereHas('student', (query) => {
 | |
|             query.where('className', payload.className)
 | |
|           })
 | |
|           .first()
 | |
|       })
 | |
|     )
 | |
|     for (const colle of collesToDelete) {
 | |
|       if (colle) {
 | |
|         await this.notificationService.sendNotification('COLLE_REMOVED', colle)
 | |
|         await colle.delete()
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     console.log(`Deleted ${collesToDelete.length} upcoming colles that were not updated`)
 | |
|   }
 | |
| }
 | |
| 
 | |
| function deepEqual(a: any, b: any) {
 | |
|   return JSON.stringify(a) === JSON.stringify(b)
 | |
| }
 | 
