Skip to content

Commit

Permalink
feat: ajout notifs manuels
Browse files Browse the repository at this point in the history
  • Loading branch information
Mzem committed Dec 5, 2024
1 parent d89626e commit 6aaac2f
Show file tree
Hide file tree
Showing 5 changed files with 265 additions and 2 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@
"tasks:charger-vues": "IS_WEB=false TASK_NAME=CHARGER_LES_VUES_ANALYTICS node dist/main",
"tasks:initialiser-les-vues": "IS_WEB=false TASK_NAME=INITIALISER_LES_VUES node dist/main",
"tasks:creer-tables-ae-annuelles": "IS_WEB=false TASK_NAME=CREER_TABLES_AE_ANNUELLES_ANALYTICS node dist/main",
"tasks:notifier-bonne-alternance": "IS_WEB=false TASK_NAME=NOTIFIER_BONNE_ALTERNANCE node dist/main",
"tasks:notifier-cje": "IS_WEB=false TASK_NAME=NOTIFIER_CJE node dist/main",
"dump-restore-db": "scripts/analytics/0_db_dump_restore.sh 2>&1",
"dump-restore-db:local": "dotenv -e .environment yarn dump-restore-db",
"release:patch": "scripts/release_version_only.sh patch",
Expand Down
6 changes: 5 additions & 1 deletion src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,8 @@ import { configureLoggerModule } from './utils/logger.module'
import { RateLimiterService } from './utils/rate-limiter.service'
import { CJEController } from './infrastructure/routes/cje.controller'
import { GetCJETokenQueryHandler } from './application/queries/get-cje-token.query.handler'
import { NotifierBonneAlternanceJobHandler } from './application/jobs/notifier-bonne-alternance.job.handler.db'
import { NotifierCJEJobHandler } from './application/jobs/notifier-cje.job.handler.db'

export const buildModuleMetadata = (): ModuleMetadata => ({
imports: [
Expand Down Expand Up @@ -830,7 +832,9 @@ export const JobHandlerProviders = [
CreerTablesAEAnnuellesJobHandler,
QualifierActionsJobHandler,
RecupererAnalyseAntivirusJobHandler,
NotifierRappelCreationActionsDemarchesJobHandler
NotifierRappelCreationActionsDemarchesJobHandler,
NotifierBonneAlternanceJobHandler,
NotifierCJEJobHandler
]

@Module(buildModuleMetadata())
Expand Down
122 changes: 122 additions & 0 deletions src/application/jobs/notifier-bonne-alternance.job.handler.db.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import { Inject, Injectable } from '@nestjs/common'
import { Op } from 'sequelize'
import { Job } from '../../building-blocks/types/job'
import { JobHandler } from '../../building-blocks/types/job-handler'
import { Core } from '../../domain/core'
import {
Notification,
NotificationRepositoryToken
} from '../../domain/notification/notification'
import {
Planificateur,
PlanificateurRepositoryToken,
ProcessJobType
} from '../../domain/planificateur'
import { SuiviJob, SuiviJobServiceToken } from '../../domain/suivi-job'
import { JeuneSqlModel } from '../../infrastructure/sequelize/models/jeune.sql-model'
import { DateService } from '../../utils/date-service'

interface Stats {
nbPersonnesNotifies: number
estLaDerniereExecution: boolean
}

const PAGINATION_NOMBRE_DE_JEUNES_MAXIMUM = 2000

@Injectable()
@ProcessJobType(Planificateur.JobType.NOTIFIER_BONNE_ALTERNANCE)
export class NotifierBonneAlternanceJobHandler extends JobHandler<Job> {
constructor(
@Inject(NotificationRepositoryToken)
private notificationRepository: Notification.Repository,
@Inject(SuiviJobServiceToken)
suiviJobService: SuiviJob.Service,
private dateService: DateService,
@Inject(PlanificateurRepositoryToken)
private planificateurRepository: Planificateur.Repository
) {
super(Planificateur.JobType.NOTIFIER_BONNE_ALTERNANCE, suiviJobService)
}

async handle(
job?: Planificateur.Job<Planificateur.JobNotifierParGroupe>
): Promise<SuiviJob> {
let succes = true
const stats: Stats = {
nbPersonnesNotifies: job?.contenu?.nbPersonnesNotifies || 0,
estLaDerniereExecution: false
}
const maintenant = this.dateService.now()

try {
const structuresConcernees = [
Core.Structure.MILO,
Core.Structure.POLE_EMPLOI,
Core.Structure.POLE_EMPLOI_AIJ
]

const offset = job?.contenu?.offset || 0

const idsJeunesANotifier = await JeuneSqlModel.findAll({
where: {
structure: {
[Op.in]: structuresConcernees
},
pushNotificationToken: {
[Op.ne]: null
}
},
attributes: ['id', 'pushNotificationToken']
})

this.logger.log(`${idsJeunesANotifier.length} ids jeunes à notifier`)

stats.nbPersonnesNotifies += idsJeunesANotifier.length
for (const jeune of idsJeunesANotifier) {
try {
const notification: Notification.Message = {
token: jeune.pushNotificationToken!,
notification: {
title: `La bonne alternance`,
body: `La bonne alternance`
},
data: {
type: 'BONNE_ALTERNANCE'
}
}
await this.notificationRepository.send(notification)
this.logger.log(`Notification envoyée pour le jeune ${jeune.id}`)
} catch (e) {
this.logger.error(e)
this.logger.log(`Echec envoi notif pour le jeune ${jeune.id}`)
}
await new Promise(resolve => setTimeout(resolve, 250))
}

if (idsJeunesANotifier.length === PAGINATION_NOMBRE_DE_JEUNES_MAXIMUM) {
this.planificateurRepository.creerJob({
dateExecution: maintenant.plus({ seconds: 30 }).toJSDate(),
type: Planificateur.JobType.NOTIFIER_BONNE_ALTERNANCE,
contenu: {
offset: offset + PAGINATION_NOMBRE_DE_JEUNES_MAXIMUM,
nbPersonnesNotifies: stats.nbPersonnesNotifies
}
})
} else {
stats.estLaDerniereExecution = true
}
} catch (e) {
this.logger.error(e)
succes = false
}

return {
jobType: this.jobType,
nbErreurs: 0,
succes,
dateExecution: maintenant,
tempsExecution: DateService.calculerTempsExecution(maintenant),
resultat: stats
}
}
}
128 changes: 128 additions & 0 deletions src/application/jobs/notifier-cje.job.handler.db.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import { Inject, Injectable } from '@nestjs/common'
import { Op } from 'sequelize'
import { Job } from '../../building-blocks/types/job'
import { JobHandler } from '../../building-blocks/types/job-handler'
import { Core } from '../../domain/core'
import {
Notification,
NotificationRepositoryToken
} from '../../domain/notification/notification'
import {
Planificateur,
PlanificateurRepositoryToken,
ProcessJobType
} from '../../domain/planificateur'
import { SuiviJob, SuiviJobServiceToken } from '../../domain/suivi-job'
import { JeuneSqlModel } from '../../infrastructure/sequelize/models/jeune.sql-model'
import { DateService } from '../../utils/date-service'

interface Stats {
nbPersonnesNotifies: number
estLaDerniereExecution: boolean
}

const PAGINATION_NOMBRE_DE_JEUNES_MAXIMUM = 2000

@Injectable()
@ProcessJobType(Planificateur.JobType.NOTIFIER_CJE)
export class NotifierCJEJobHandler extends JobHandler<Job> {
constructor(
@Inject(NotificationRepositoryToken)
private notificationRepository: Notification.Repository,
@Inject(SuiviJobServiceToken)
suiviJobService: SuiviJob.Service,
private dateService: DateService,
@Inject(PlanificateurRepositoryToken)
private planificateurRepository: Planificateur.Repository
) {
super(Planificateur.JobType.NOTIFIER_CJE, suiviJobService)
}

async handle(
job?: Planificateur.Job<Planificateur.JobNotifierParGroupe>
): Promise<SuiviJob> {
let succes = true
const stats: Stats = {
nbPersonnesNotifies: job?.contenu?.nbPersonnesNotifies || 0,
estLaDerniereExecution: false
}
const maintenant = this.dateService.now()

try {
const structuresConcernees = [
Core.Structure.MILO,
Core.Structure.POLE_EMPLOI
]

const offset = job?.contenu?.offset || 0

const idsJeunesANotifier = await JeuneSqlModel.findAll({
where: {
structure: {
[Op.in]: structuresConcernees
},
pushNotificationToken: {
[Op.ne]: null
}
},
attributes: ['id', 'pushNotificationToken']
})

this.logger.log(`${idsJeunesANotifier.length} ids jeunes à notifier`)

stats.nbPersonnesNotifies += idsJeunesANotifier.length
for (const jeune of idsJeunesANotifier) {
try {
const notification: Notification.Message = {
token: jeune.pushNotificationToken!,
notification: {
title: `👋 Retrouvez vos avantages du CEJ`,
body: `+ de 65 réductions disponibles grâce à la carte "Jeune Engagé"`
},
data: {
type: 'CJE'
}
}
await this.notificationRepository.send(notification)
this.logger.log(`Notification envoyée pour le jeune ${jeune.id}`)
} catch (e) {
this.logger.error(e)
this.logger.log(`Echec envoi notif pour le jeune ${jeune.id}`)
}
await new Promise(resolve => setTimeout(resolve, 500))
}

if (idsJeunesANotifier.length === PAGINATION_NOMBRE_DE_JEUNES_MAXIMUM) {
const dateExecution =
maintenant.hour <= 19 && maintenant.hour >= 8
? maintenant.plus({ minutes: 30 }).toJSDate()
: maintenant
.plus({ days: 1 })
.set({ hour: 8, minute: 0, second: 0, millisecond: 0 })
.toJSDate()
this.planificateurRepository.creerJob({
dateExecution,
type: Planificateur.JobType.NOTIFIER_CJE,
contenu: {
offset: offset + PAGINATION_NOMBRE_DE_JEUNES_MAXIMUM,
nbPersonnesNotifies: stats.nbPersonnesNotifies
}
})
} else {
stats.estLaDerniereExecution = true
}
} catch (e) {
this.logger.error(e)
succes = false
}

return {
jobType: this.jobType,
nbErreurs: 0,
succes,
dateExecution: maintenant,
tempsExecution: DateService.calculerTempsExecution(maintenant),
resultat: stats
}
}
}
9 changes: 8 additions & 1 deletion src/domain/planificateur.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@ export namespace Planificateur {
CREER_TABLES_AE_ANNUELLES_ANALYTICS = 'CREER_TABLES_AE_ANNUELLES_ANALYTICS',
QUALIFIER_ACTIONS = 'QUALIFIER_ACTIONS',
RECUPERER_ANALYSE_ANTIVIRUS = 'RECUPERER_ANALYSE_ANTIVIRUS',
NOTIFIER_RAPPEL_CREATION_ACTIONS_DEMARCHES = 'NOTIFIER_RAPPEL_CREATION_ACTIONS_DEMARCHES'
NOTIFIER_RAPPEL_CREATION_ACTIONS_DEMARCHES = 'NOTIFIER_RAPPEL_CREATION_ACTIONS_DEMARCHES',
NOTIFIER_BONNE_ALTERNANCE = 'NOTIFIER_BONNE_ALTERNANCE',
NOTIFIER_CJE = 'NOTIFIER_CJE'
}

export interface JobRendezVous {
Expand All @@ -92,6 +94,11 @@ export namespace Planificateur {
nbJeunesNotifies?: number
}

export interface JobNotifierParGroupe {
offset: number
nbPersonnesNotifies: number
}

export interface JobRappelAction {
idAction: string
}
Expand Down

0 comments on commit 6aaac2f

Please sign in to comment.