Compare commits
5 commits
35a8191938
...
6213f3941c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6213f3941c | ||
|
|
169df9715e | ||
|
|
7093b344ed | ||
|
|
fb00495840 | ||
|
|
1d3728b48d |
8 changed files with 415 additions and 23 deletions
|
|
@ -1,5 +1,6 @@
|
||||||
import Colle from '#models/colle'
|
import Colle from '#models/colle'
|
||||||
import { ColleService } from '#services/colle_service'
|
import { ColleService } from '#services/colle_service'
|
||||||
|
import { NotificationService } from '#services/notification_service'
|
||||||
import { createColleValidator, createUpcomingCollesValidator } from '#validators/colle'
|
import { createColleValidator, createUpcomingCollesValidator } from '#validators/colle'
|
||||||
import { inject } from '@adonisjs/core'
|
import { inject } from '@adonisjs/core'
|
||||||
import type { HttpContext } from '@adonisjs/core/http'
|
import type { HttpContext } from '@adonisjs/core/http'
|
||||||
|
|
@ -8,7 +9,10 @@ import { DateTime } from 'luxon'
|
||||||
|
|
||||||
@inject()
|
@inject()
|
||||||
export default class CollesController {
|
export default class CollesController {
|
||||||
constructor(private service: ColleService) {}
|
constructor(
|
||||||
|
private service: ColleService,
|
||||||
|
private notificationService: NotificationService
|
||||||
|
) {}
|
||||||
|
|
||||||
async index({ request, response, auth }: HttpContext) {
|
async index({ request, response, auth }: HttpContext) {
|
||||||
const { startDate: rawStartDate } = request.qs()
|
const { startDate: rawStartDate } = request.qs()
|
||||||
|
|
@ -137,6 +141,7 @@ export default class CollesController {
|
||||||
// If it exists, update the existing colle
|
// If it exists, update the existing colle
|
||||||
if (existing) {
|
if (existing) {
|
||||||
// Merge the new data with the existing colle
|
// Merge the new data with the existing colle
|
||||||
|
const beforeColle = existing.serialize()
|
||||||
Object.assign(existing, colleData)
|
Object.assign(existing, colleData)
|
||||||
|
|
||||||
// Handle attachments if any
|
// Handle attachments if any
|
||||||
|
|
@ -161,6 +166,19 @@ export default class CollesController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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()
|
return existing.save()
|
||||||
}
|
}
|
||||||
const colle = await Colle.create(colleData)
|
const colle = await Colle.create(colleData)
|
||||||
|
|
@ -226,25 +244,49 @@ export default class CollesController {
|
||||||
|
|
||||||
// Create a new colle if it doesn't exist
|
// Create a new colle if it doesn't exist
|
||||||
if (!oldColle) {
|
if (!oldColle) {
|
||||||
await Colle.create(updatedColle)
|
const colle = await Colle.create(updatedColle)
|
||||||
|
await this.notificationService.sendNotification('COLLE_ADDED', colle)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the colle with the new data
|
// Update the colle with the new data
|
||||||
// and remove it from the list
|
// and remove it from the list
|
||||||
|
const beforeColle = oldColle.serialize()
|
||||||
Object.assign(oldColle, updatedColle)
|
Object.assign(oldColle, updatedColle)
|
||||||
await oldColle.save()
|
await oldColle.save()
|
||||||
|
|
||||||
|
const afterColle = oldColle.serialize()
|
||||||
upcomingCollesIds.delete(oldColle.id)
|
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
|
// Delete the colles that were not updated
|
||||||
const deleted = await Colle.query()
|
const collesToDelete = await Promise.all(
|
||||||
.whereHas('student', (query) => {
|
Array.from(upcomingCollesIds).map((id) => {
|
||||||
query.where('className', payload.className)
|
return Colle.query()
|
||||||
|
.where('id', id)
|
||||||
|
.whereHas('student', (query) => {
|
||||||
|
query.where('className', payload.className)
|
||||||
|
})
|
||||||
|
.first()
|
||||||
})
|
})
|
||||||
.whereIn('id', Array.from(upcomingCollesIds))
|
)
|
||||||
.delete()
|
for (const colle of collesToDelete) {
|
||||||
|
if (colle) {
|
||||||
|
await this.notificationService.sendNotification('COLLE_REMOVED', colle)
|
||||||
|
await colle.delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
console.log(`Deleted ${deleted} upcoming colles that were not updated`)
|
console.log(`Deleted ${collesToDelete.length} upcoming colles that were not updated`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function deepEqual(a: any, b: any) {
|
||||||
|
return JSON.stringify(a) === JSON.stringify(b)
|
||||||
|
}
|
||||||
|
|
|
||||||
58
app/controllers/grades_controller.ts
Normal file
58
app/controllers/grades_controller.ts
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
import { GradeService } from '#services/grade_service'
|
||||||
|
import { inject } from '@adonisjs/core'
|
||||||
|
import type { HttpContext } from '@adonisjs/core/http'
|
||||||
|
import { DateTime } from 'luxon'
|
||||||
|
|
||||||
|
const PERIODS = ['MONTH', 'TRIMESTER', 'YEAR']
|
||||||
|
|
||||||
|
@inject()
|
||||||
|
export default class GradesController {
|
||||||
|
constructor(private gradeService: GradeService) {}
|
||||||
|
|
||||||
|
// GET /grades
|
||||||
|
async index({ auth, request, response }: HttpContext) {
|
||||||
|
const period = request.input('period', PERIODS[0]).toUpperCase()
|
||||||
|
if (!PERIODS.includes(period)) {
|
||||||
|
return response.badRequest({
|
||||||
|
message: `Invalid period. Allowed values are: ${PERIODS.join(', ')}`,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Choose a start date
|
||||||
|
// 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 userId = auth.user!.id
|
||||||
|
if (period === PERIODS[0]) {
|
||||||
|
const startDate = DateTime.now().minus({ month: 1 })
|
||||||
|
return this.gradeService.getMonthGrade(userId, startDate)
|
||||||
|
}
|
||||||
|
const months = period === PERIODS[1] ? 3 : 12
|
||||||
|
const startDate = DateTime.now().minus({ months })
|
||||||
|
return this.gradeService.getPeriodGrade(userId, startDate, months)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET /averages
|
||||||
|
async averages({ auth, request, response }: HttpContext) {
|
||||||
|
const period = request.input('period', PERIODS[0]).toUpperCase()
|
||||||
|
if (!PERIODS.includes(period)) {
|
||||||
|
return response.badRequest({
|
||||||
|
message: `Invalid period. Allowed values are: ${PERIODS.join(', ')}`,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const userId = auth.user!.id
|
||||||
|
if (period === PERIODS[0]) {
|
||||||
|
const startDate = DateTime.now().minus({ month: 1 })
|
||||||
|
return this.gradeService.getSubjectsAverages(userId, startDate, 1)
|
||||||
|
}
|
||||||
|
const months = period === PERIODS[1] ? 3 : 12
|
||||||
|
const startDate = DateTime.now().minus({ months })
|
||||||
|
return this.gradeService.getSubjectsAverages(userId, startDate, months)
|
||||||
|
}
|
||||||
|
}
|
||||||
46
app/controllers/internals_controller.ts
Normal file
46
app/controllers/internals_controller.ts
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
import type { HttpContext } from '@adonisjs/core/http'
|
||||||
|
import redis from '@adonisjs/redis/services/main'
|
||||||
|
import { DateTime } from 'luxon'
|
||||||
|
|
||||||
|
export default class InternalsController {
|
||||||
|
// POST /internals/back-fetch
|
||||||
|
async backFetch({ response, request }: HttpContext) {
|
||||||
|
const className = request.input('className')
|
||||||
|
if (!className) {
|
||||||
|
return response.badRequest({ message: 'className is required' })
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start a new thread to avoid blocking the event loop
|
||||||
|
response.send({
|
||||||
|
success: true,
|
||||||
|
message: `Fetching colles for class ${className}...`,
|
||||||
|
})
|
||||||
|
setImmediate(async () => {
|
||||||
|
await queue(className)
|
||||||
|
console.log(`Colles fetching for class ${className} completed.`)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function queue(className: string) {
|
||||||
|
// 1er Septembre 2019 début de BJColle
|
||||||
|
const startDate = DateTime.fromISO('2019-09-01T00:00:00')
|
||||||
|
const endDate = DateTime.now()
|
||||||
|
let date = endDate
|
||||||
|
|
||||||
|
// Loop through all days from startDate to endDate
|
||||||
|
while (date >= startDate) {
|
||||||
|
await redis.publish(
|
||||||
|
'jobs_queue',
|
||||||
|
JSON.stringify({
|
||||||
|
type: 1, // Fetch day colles
|
||||||
|
// Format DD/MM/YYYY
|
||||||
|
date: date.toFormat('dd/MM/yyyy'),
|
||||||
|
class_name: className,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
date = date.minus({ days: 1 })
|
||||||
|
// Wait for 1 second to avoid overwhelming the queue
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 1000))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -24,7 +24,7 @@ export default class NotificationsController {
|
||||||
const device = this.service.getUserSignature(ua)
|
const device = this.service.getUserSignature(ua)
|
||||||
|
|
||||||
const data = await request.validateUsing(subscribeValidator)
|
const data = await request.validateUsing(subscribeValidator)
|
||||||
await Subscription.create({
|
return Subscription.create({
|
||||||
userId: auth.user!.id,
|
userId: auth.user!.id,
|
||||||
device,
|
device,
|
||||||
events: 0, // Default to no events
|
events: 0, // Default to no events
|
||||||
|
|
|
||||||
185
app/services/grade_service.ts
Normal file
185
app/services/grade_service.ts
Normal file
|
|
@ -0,0 +1,185 @@
|
||||||
|
import Colle from '#models/colle'
|
||||||
|
import { DateTime } from 'luxon'
|
||||||
|
|
||||||
|
export class GradeService {
|
||||||
|
private calculateAverage(colles: Colle[]) {
|
||||||
|
const total = colles
|
||||||
|
.map((colle) => parseFloat(colle.grade?.toString()))
|
||||||
|
.filter(Boolean)
|
||||||
|
.reduce((sum, grade) => sum + grade, 0)
|
||||||
|
return (total / colles.length).toFixed(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
private calculateSubjectAverage(colles: Colle[], subject: string) {
|
||||||
|
const subjectColles = colles.filter((colle) => colle.subject.name === subject && colle.grade)
|
||||||
|
return this.calculateAverage(subjectColles)
|
||||||
|
}
|
||||||
|
|
||||||
|
private getSubjects(colles: Colle[]) {
|
||||||
|
return Array.from(new Set(colles.map((colle) => colle.subject.name)))
|
||||||
|
}
|
||||||
|
|
||||||
|
private getPeriodColles(colles: Colle[], startDate: DateTime, endDate: DateTime) {
|
||||||
|
return colles.filter((colle) => colle.date >= startDate && colle.date < endDate)
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getSubjectsAverages(userId: number, startDate: DateTime, months: number = 0) {
|
||||||
|
const colles = await this.getColles(userId, startDate, months)
|
||||||
|
const subjects = this.getSubjects(colles)
|
||||||
|
const subjectAverages: SubjectPerformance[] = []
|
||||||
|
for (const subject of subjects) {
|
||||||
|
const average = this.calculateSubjectAverage(colles, subject)
|
||||||
|
subjectAverages.push({ subject, average })
|
||||||
|
}
|
||||||
|
const globalAverage = this.calculateAverage(colles)
|
||||||
|
return {
|
||||||
|
globalAverage,
|
||||||
|
subjectAverages,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public getColles(userId: number, startDate: DateTime, months: number = 0) {
|
||||||
|
const endDate = startDate.plus({ months })
|
||||||
|
return Colle.query()
|
||||||
|
.where('studentId', userId)
|
||||||
|
.where('date', '>=', startDate.toJSDate())
|
||||||
|
.where('date', '<', endDate.toJSDate())
|
||||||
|
.preload('subject')
|
||||||
|
.orderBy('date', 'asc')
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getGlobalAverage(userId: number, subjectId: number, beforeDate: DateTime) {
|
||||||
|
const colles = await Colle.query()
|
||||||
|
.where('studentId', userId)
|
||||||
|
.where('subjectId', subjectId)
|
||||||
|
.where('date', '<', beforeDate.toJSDate())
|
||||||
|
return colles.length ? this.calculateAverage(colles) : 0
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getMonthGrade(userId: number, startDate: DateTime) {
|
||||||
|
const colles = await this.getColles(userId, startDate, 1)
|
||||||
|
const subjects = this.getSubjects(colles)
|
||||||
|
|
||||||
|
const results: any = []
|
||||||
|
let periodStartDate = startDate
|
||||||
|
|
||||||
|
for (let week = 1; week <= 4; week++) {
|
||||||
|
const periodEndDate = periodStartDate.plus({ weeks: 1 })
|
||||||
|
const periodColles = this.getPeriodColles(colles, periodStartDate, periodEndDate)
|
||||||
|
|
||||||
|
const periodAverage = this.calculateAverage(periodColles)
|
||||||
|
const subjectAverages = await this.getSubjectAverages(
|
||||||
|
subjects,
|
||||||
|
periodColles,
|
||||||
|
colles,
|
||||||
|
results,
|
||||||
|
week,
|
||||||
|
userId,
|
||||||
|
periodStartDate
|
||||||
|
)
|
||||||
|
|
||||||
|
results.push({
|
||||||
|
period: `Week ${week}`,
|
||||||
|
average: periodAverage,
|
||||||
|
...this.reduce(subjectAverages),
|
||||||
|
})
|
||||||
|
|
||||||
|
periodStartDate = periodEndDate
|
||||||
|
}
|
||||||
|
|
||||||
|
return { grades: results, subjects }
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getPeriodGrade(userId: number, startDate: DateTime, months: number = 0) {
|
||||||
|
const colles = await this.getColles(userId, startDate, months)
|
||||||
|
const subjects = this.getSubjects(colles)
|
||||||
|
|
||||||
|
const results: any = []
|
||||||
|
let periodStartDate = startDate
|
||||||
|
|
||||||
|
let index = 1
|
||||||
|
|
||||||
|
while (periodStartDate < startDate.plus({ months })) {
|
||||||
|
const periodEndDate = periodStartDate.endOf('month')
|
||||||
|
const periodColles = this.getPeriodColles(colles, startDate, periodEndDate)
|
||||||
|
|
||||||
|
const periodAverage = this.calculateAverage(periodColles)
|
||||||
|
const subjectAverages = await this.getSubjectAverages(
|
||||||
|
subjects,
|
||||||
|
periodColles,
|
||||||
|
colles,
|
||||||
|
results,
|
||||||
|
index,
|
||||||
|
userId,
|
||||||
|
periodStartDate
|
||||||
|
)
|
||||||
|
|
||||||
|
results.push({
|
||||||
|
period: periodStartDate.toFormat('MMM'),
|
||||||
|
average: periodAverage,
|
||||||
|
...this.reduce(subjectAverages),
|
||||||
|
})
|
||||||
|
|
||||||
|
periodStartDate = periodEndDate.plus({ days: 1 })
|
||||||
|
index++
|
||||||
|
}
|
||||||
|
|
||||||
|
return { grades: results, subjects }
|
||||||
|
}
|
||||||
|
|
||||||
|
private reduce(subjectAverages: SubjectPerformance[]) {
|
||||||
|
return subjectAverages.reduce((acc: any, { subject, average }) => {
|
||||||
|
acc[subject] = average
|
||||||
|
return acc
|
||||||
|
}, {})
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getSubjectAverages(
|
||||||
|
subjects: string[],
|
||||||
|
periodColles: Colle[],
|
||||||
|
allColles: Colle[],
|
||||||
|
previousResults: Record<string, number>[],
|
||||||
|
unitIndex: number,
|
||||||
|
userId: number,
|
||||||
|
periodStart: DateTime
|
||||||
|
) {
|
||||||
|
const results = await Promise.all(
|
||||||
|
subjects.map(async (subject) => {
|
||||||
|
const subjectColles = periodColles.filter(
|
||||||
|
(colle) => colle.subject.name === subject && colle.grade
|
||||||
|
)
|
||||||
|
if (subjectColles.length > 0) {
|
||||||
|
return {
|
||||||
|
subject,
|
||||||
|
average: this.calculateSubjectAverage(periodColles, subject),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to use the previous unit's average
|
||||||
|
if (unitIndex > 1) {
|
||||||
|
const prev = previousResults[unitIndex - 2]?.[subject]
|
||||||
|
if (prev) {
|
||||||
|
return { subject, average: prev }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise fall back to global average before this period
|
||||||
|
const subjectId = allColles.find((colle) => colle.subject.name === subject)!.subjectId
|
||||||
|
|
||||||
|
const beforeAverage = await this.getGlobalAverage(userId, subjectId, periodStart)
|
||||||
|
if (beforeAverage) {
|
||||||
|
return { subject, average: beforeAverage }
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
return results.filter((s): s is SubjectPerformance => Boolean(s))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SubjectPerformance {
|
||||||
|
subject: string
|
||||||
|
average: string
|
||||||
|
}
|
||||||
|
|
@ -5,6 +5,8 @@ import { DateTime } from 'luxon'
|
||||||
import { UAParser } from 'ua-parser-js'
|
import { UAParser } from 'ua-parser-js'
|
||||||
import webpush, { PushSubscription } from 'web-push'
|
import webpush, { PushSubscription } from 'web-push'
|
||||||
|
|
||||||
|
const MAX_FAILED_ATTEMPTS = 5
|
||||||
|
|
||||||
export const EVENTS = {
|
export const EVENTS = {
|
||||||
SYSTEM: 1 << 0,
|
SYSTEM: 1 << 0,
|
||||||
|
|
||||||
|
|
@ -45,18 +47,38 @@ export class NotificationService {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
public async sendNotification(notificationId: NotificationId, colle: Colle) {
|
public async sendNotification(notificationId: NotificationId, colle: Colle, args?: any) {
|
||||||
const payload = Object.assign(DEFAULT_NOTIFICATION, NOTIFICATIONS[notificationId](colle))
|
await colle.load('subject')
|
||||||
|
if (notificationId === 'ROOM_UPDATED') {
|
||||||
|
await colle.load('room')
|
||||||
|
}
|
||||||
|
const payload = Object.assign(DEFAULT_NOTIFICATION, NOTIFICATIONS[notificationId](colle, args))
|
||||||
|
|
||||||
const subscriptions = await Subscription.query()
|
const subscriptions = await Subscription.query()
|
||||||
.where('enabled', true)
|
.where('enabled', true)
|
||||||
.where('userId', colle.studentId)
|
.where('userId', colle.studentId)
|
||||||
// TODO: Check if working??
|
|
||||||
.whereRaw(`(events & ${EVENTS[notificationId]}) > 0`)
|
.whereRaw(`(events & ${EVENTS[notificationId]}) > 0`)
|
||||||
|
|
||||||
for (const subscription of subscriptions) {
|
for (const subscription of subscriptions) {
|
||||||
await this.pushNotification(subscription.data, payload)
|
try {
|
||||||
// TODO: Count failed attempts and disable subscription if too many failures
|
await this.pushNotification(subscription.data, payload)
|
||||||
|
// Reset failed attempts on successful notification
|
||||||
|
if (subscription.failedAttempts) {
|
||||||
|
subscription.failedAttempts = 0
|
||||||
|
await subscription.save()
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(
|
||||||
|
`Error sending notification for ${notificationId} to user ${colle.studentId}:`,
|
||||||
|
err
|
||||||
|
)
|
||||||
|
// Increment failed attempts and disable subscription if too many failures
|
||||||
|
subscription.failedAttempts = (subscription.failedAttempts || 0) + 1
|
||||||
|
if (subscription.failedAttempts >= MAX_FAILED_ATTEMPTS) {
|
||||||
|
subscription.enabled = false
|
||||||
|
}
|
||||||
|
await subscription.save()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -85,14 +107,14 @@ export class NotificationService {
|
||||||
? device.type
|
? device.type
|
||||||
: 'Desktop'
|
: 'Desktop'
|
||||||
|
|
||||||
return [deviceStr, osStr, browserStr].filter(Boolean).join(' | ')
|
return [deviceStr, osStr, browserStr].filter(Boolean).join(' - ')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const NOTIFICATIONS = {
|
const NOTIFICATIONS = {
|
||||||
COLLE_ADDED: (colle: Colle) => ({
|
COLLE_ADDED: (colle: Colle) => ({
|
||||||
title: 'Nouvelle colle',
|
title: 'Nouvelle colle',
|
||||||
body: `Colle de ${colle.subject} ajoutée le ${formatDate(colle.date)}.`,
|
body: `Colle de ${colle.subject.name} ajoutée le ${formatDate(colle.date)}.`,
|
||||||
data: {
|
data: {
|
||||||
id: colle.id,
|
id: colle.id,
|
||||||
},
|
},
|
||||||
|
|
@ -100,12 +122,12 @@ const NOTIFICATIONS = {
|
||||||
}),
|
}),
|
||||||
COLLE_REMOVED: (colle: Colle) => ({
|
COLLE_REMOVED: (colle: Colle) => ({
|
||||||
title: 'Colle supprimée',
|
title: 'Colle supprimée',
|
||||||
body: `Votre colle de ${colle.subject}, le ${formatDate(colle.date)} a été supprimée.`,
|
body: `Votre colle de ${colle.subject.name}, le ${formatDate(colle.date)} a été supprimée.`,
|
||||||
actions: [HOME_ACTION],
|
actions: [HOME_ACTION],
|
||||||
}),
|
}),
|
||||||
COLLE_UPDATED: (colle: Colle) => ({
|
COLLE_UPDATED: (colle: Colle) => ({
|
||||||
title: 'Colle modifiée',
|
title: 'Colle modifiée',
|
||||||
body: `Votre colle de ${colle.subject} du ${formatDate(colle.date)} a été modifiée.`,
|
body: `Votre colle de ${colle.subject.name} du ${formatDate(colle.date)} a été modifiée.`,
|
||||||
data: {
|
data: {
|
||||||
id: colle.id,
|
id: colle.id,
|
||||||
},
|
},
|
||||||
|
|
@ -114,15 +136,15 @@ const NOTIFICATIONS = {
|
||||||
|
|
||||||
GRADE_ADDED: (colle: Colle) => ({
|
GRADE_ADDED: (colle: Colle) => ({
|
||||||
title: 'Nouvelle note',
|
title: 'Nouvelle note',
|
||||||
body: `Colle de ${colle.subject} : ${colle.grade}/20`,
|
body: `Colle de ${colle.subject.name} : ${colle.grade}/20`,
|
||||||
data: {
|
data: {
|
||||||
id: colle.id,
|
id: colle.id,
|
||||||
},
|
},
|
||||||
actions: [OPEN_ACION, HOME_ACTION],
|
actions: [OPEN_ACION, HOME_ACTION],
|
||||||
}),
|
}),
|
||||||
GRADE_UPDATED: (colle: Colle) => ({
|
GRADE_UPDATED: (colle: Colle, oldGrade: number) => ({
|
||||||
title: 'Note modifiée',
|
title: 'Note modifiée',
|
||||||
body: `Colle de ${colle.subject} : ${colle.grade}/20`,
|
body: `Colle de ${colle.subject.name} : ${oldGrade}/20 --> ${colle.grade}/20`,
|
||||||
data: {
|
data: {
|
||||||
id: colle.id,
|
id: colle.id,
|
||||||
},
|
},
|
||||||
|
|
@ -131,7 +153,7 @@ const NOTIFICATIONS = {
|
||||||
|
|
||||||
ROOM_UPDATED: (colle: Colle) => ({
|
ROOM_UPDATED: (colle: Colle) => ({
|
||||||
title: 'Salle modifiée',
|
title: 'Salle modifiée',
|
||||||
body: `Colle de ${colle.subject} en ${colle.room}.`,
|
body: `Colle de ${colle.subject.name} en ${colle.room.name}.`,
|
||||||
data: {
|
data: {
|
||||||
id: colle.id,
|
id: colle.id,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
import { BaseSchema } from '@adonisjs/lucid/schema'
|
||||||
|
|
||||||
|
export default class extends BaseSchema {
|
||||||
|
protected tableName = 'subscriptions'
|
||||||
|
|
||||||
|
async up() {
|
||||||
|
this.schema.createTable(this.tableName, (table) => {
|
||||||
|
table.increments('id')
|
||||||
|
table
|
||||||
|
.integer('user_id')
|
||||||
|
.notNullable()
|
||||||
|
.unsigned()
|
||||||
|
.references('id')
|
||||||
|
.inTable('users')
|
||||||
|
.onDelete('CASCADE')
|
||||||
|
table.string('device').notNullable()
|
||||||
|
table.integer('events').defaultTo(0).notNullable()
|
||||||
|
table.jsonb('data').notNullable()
|
||||||
|
table.integer('failed_attempts').defaultTo(0).notNullable()
|
||||||
|
table.boolean('enabled').defaultTo(true).notNullable()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async down() {
|
||||||
|
this.schema.dropTable(this.tableName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -52,10 +52,22 @@ const NotificationsController = () => import('#controllers/notifications_control
|
||||||
router
|
router
|
||||||
.group(() => {
|
.group(() => {
|
||||||
router.get('/', [NotificationsController, 'index'])
|
router.get('/', [NotificationsController, 'index'])
|
||||||
router.post('/subscribe', [NotificationsController, 'subscribe'])
|
router.post('/', [NotificationsController, 'subscribe'])
|
||||||
router.post('/:id/unsubscribe', [NotificationsController, 'unsubscribe'])
|
router.post('/:id/unsubscribe', [NotificationsController, 'unsubscribe'])
|
||||||
router.post('/:id', [NotificationsController, 'update'])
|
router.post('/:id', [NotificationsController, 'update'])
|
||||||
router.post('/:id/test', [NotificationsController, 'test']).use(middleware.auth())
|
router.post('/:id/test', [NotificationsController, 'test']).use(middleware.auth())
|
||||||
})
|
})
|
||||||
.prefix('/notifications')
|
.prefix('/notifications')
|
||||||
.use(middleware.auth())
|
.use(middleware.auth())
|
||||||
|
|
||||||
|
const GradesController = () => import('#controllers/grades_controller')
|
||||||
|
router.get('/grades', [GradesController, 'index']).use(middleware.auth())
|
||||||
|
router.get('/averages', [GradesController, 'averages']).use(middleware.auth())
|
||||||
|
|
||||||
|
const InternalsController = () => import('#controllers/internals_controller')
|
||||||
|
router
|
||||||
|
.group(() => {
|
||||||
|
router.post('/back-fetch', [InternalsController, 'backFetch'])
|
||||||
|
})
|
||||||
|
.prefix('/internals')
|
||||||
|
// TODO: Token authentication
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue