diff --git a/.kube-workflow/prod/templates/reportingDossierRefuse.yaml b/.kube-workflow/prod/templates/reportingDossierRefuse.yaml new file mode 100644 index 00000000..0675694d --- /dev/null +++ b/.kube-workflow/prod/templates/reportingDossierRefuse.yaml @@ -0,0 +1,28 @@ +apiVersion: batch/v1beta1 +kind: CronJob +metadata: + namespace: mon-psy-sante + name: reporting-dossier-refuse + labels: + app: mon-psy +spec: + schedule: "30 5 15 * *" + jobTemplate: + spec: + template: + spec: + containers: + - name: mon-psy + image: ghcr.io/socialgouv/mon-psy-sante/app:{{ .Values.global.imageTag }} + envFrom: + - secretRef: + name: app-sealed-secret + - secretRef: + name: pg-user + command: + - yarn + - run + - cron:launch + - reportingDossierRefuse + restartPolicy: Never + concurrencyPolicy: Forbid diff --git a/src/cron/launch.ts b/src/cron/launch.ts index 7a69b844..a59d0b9d 100644 --- a/src/cron/launch.ts +++ b/src/cron/launch.ts @@ -2,6 +2,7 @@ import * as Sentry from "@sentry/nextjs"; import * as demarchesSimplifiees from "./demarchesSimplifiees"; import { reportingDossierEligible } from "./reportingDossierEligible"; +import { reportingDossierRefuse } from "./reportingDossierRefuse"; import { reportingExpertWeekly } from "./reportingExpertWeekly"; import { reportingStatsByDepartment } from "./reportingStatsByDepartment"; @@ -31,6 +32,7 @@ const cronJobs = { reportingStatsByDepartment, reportingExpertWeekly, reportingDossierEligible, + reportingDossierRefuse, }; const jobName = process.argv[2]; diff --git a/src/cron/reportingDossierRefuse.ts b/src/cron/reportingDossierRefuse.ts new file mode 100644 index 00000000..b5fe4218 --- /dev/null +++ b/src/cron/reportingDossierRefuse.ts @@ -0,0 +1,114 @@ +// https://www.notion.so/fabnummas/Notification-toutes-les-2-semaines-de-d-un-tat-des-dossiers-refus-s-d63ae90f8db54eeda2fb382e9416cb8c +import { requestDossiersRefusesWithMessages } from "../services/demarchesSimplifiees/buildRequest"; +import { getAllPsychologistList } from "../services/demarchesSimplifiees/import"; +import { sendEmailWithAttachments } from "./cronUtils"; + +const ANNOTATION_EXPERT1_ID = "Q2hhbXAtMjUzMTM2Mw=="; +const ANNOTATION_EXPERT2_ID = "Q2hhbXAtMjUzMTM2NQ=="; +const COMMENT_STEP1 = "Q2hhbXAtMTg2MDUwOA=="; +const COMMENT_STEP2 = "Q2hhbXAtMTg0NDM5Ng=="; +const COMMENT_STEP3 = "Q2hhbXAtMjMwOTI5MA=="; +const MOTIF_REFUS = "Q2hhbXAtMjMyMzExNg=="; +const INSTRUCTEURS = { + "SW5zdHJ1Y3RldXItNjMyMjc=": "BS", + "SW5zdHJ1Y3RldXItNjMyMzU=": "ACB", + "SW5zdHJ1Y3RldXItNjMyMjk=": "ADS", + "SW5zdHJ1Y3RldXItNjMyMzE=": "ES", + "SW5zdHJ1Y3RldXItNjMyMzA=": "ADT", + "SW5zdHJ1Y3RldXItNjMyMjg=": "SCO", +}; + +function getFromAnnotation(psychologist, annotationId) { + return psychologist.annotations.find((a) => a.id === annotationId) + ?.stringValue; +} + +function csvCell(cell) { + return `"${cell.replace(/[\r\n]{2,}/g, "\n").replace(/"/g, '""')}"`; +} + +function formatMessages(messages) { + return messages + .map((m) => m.createdAt + " " + m.body.replace(/(?:\r\n|\r|\n)/g, " ")) + .join("\n"); +} + +export async function reportingDossierRefuse() { + console.log("Récupération des dossiers refusés"); + + const result = await getAllPsychologistList( + requestDossiersRefusesWithMessages + ).catch((e) => { + console.log(e); + process.exit(-1); + }); + + const psychologists = result.psychologists + .map((psychologist) => { + const expert1 = getFromAnnotation(psychologist, ANNOTATION_EXPERT1_ID); + const expert2 = getFromAnnotation(psychologist, ANNOTATION_EXPERT2_ID); + const commentStep1 = getFromAnnotation(psychologist, COMMENT_STEP1); + const commentStep2 = getFromAnnotation(psychologist, COMMENT_STEP2); + const commentStep3 = getFromAnnotation(psychologist, COMMENT_STEP3); + const motifRefus = getFromAnnotation(psychologist, MOTIF_REFUS); + + const instructeurs = psychologist.instructeurs + .filter((i) => Boolean(INSTRUCTEURS[i.id])) + .map((i) => INSTRUCTEURS[i.id]) + .join(","); + return { + ...psychologist, + expert1, + expert2, + instructeurs, + commentStep1, + commentStep2, + commentStep3, + motifRefus, + }; + }) + .map((psychologist) => { + return [ + psychologist.number, + psychologist.dateTraitement, + psychologist.expert1, + psychologist.expert2, + psychologist.instructeurs, + csvCell(psychologist.commentStep1), + csvCell(psychologist.commentStep2), + csvCell(psychologist.commentStep3), + csvCell(psychologist.motifRefus), + psychologist.messages.length + ? psychologist.messages[psychologist.messages.length - 1].createdAt + : "", + csvCell(formatMessages(psychologist.messages)), + ].join(";"); + }); + const header = [ + "Numéro", + "Date de traitement", + "Expert 1", + "Expert 2", + "Instructeurs", + "Commentaire étape 1", + "Commentaire étape 2", + "Commentaire étape 3", + "Motif de refus", + "Date du dernier message", + "Messages", + ].join(";"); + console.log([header, ...psychologists].join("\n")); + return sendEmailWithAttachments({ + subject: "Fichiers de suivi des dossiers refusés", + textSlices: [ + "Bonjour,", + "Ci-joint le fichier de suivi des dossiers refusés.", + ], + attachments: [ + { + filename: `dossiers-refusés.csv`, + content: Buffer.from([header, ...psychologists].join("\n")), + }, + ], + }); +} diff --git a/src/services/demarchesSimplifiees/buildRequest.ts b/src/services/demarchesSimplifiees/buildRequest.ts index 5877f3e0..8f25fb91 100644 --- a/src/services/demarchesSimplifiees/buildRequest.ts +++ b/src/services/demarchesSimplifiees/buildRequest.ts @@ -178,6 +178,16 @@ export const requestDossiersEnConstruction = async ( return requestDossiersByState(DossierState.enConstruction, afterCursor); }; +export const requestDossiersRefusesWithMessages = async ( + afterCursor: string | undefined +): Promise => { + return requestDossiersByState( + DossierState.refuse, + afterCursor, + "messages { body, createdAt }" + ); +}; + export const requestDossiersEnInstruction = async ( afterCursor: string | undefined ): Promise => { @@ -186,7 +196,8 @@ export const requestDossiersEnInstruction = async ( export const requestDossiersByState = async ( state: DossierState, - afterCursor: string | undefined + afterCursor: string | undefined, + extraInfos?: string | undefined ): Promise => { const paginationCondition = getWhereConditionAfterCursor(afterCursor); const query = gql` @@ -198,8 +209,10 @@ export const requestDossiersByState = async ( endCursor } nodes { - id, + id number + dateTraitement + ${extraInfos ?? ""} champs { id label diff --git a/src/types/psychologist.d.ts b/src/types/psychologist.d.ts index 57a8a840..a1ba27c8 100644 --- a/src/types/psychologist.d.ts +++ b/src/types/psychologist.d.ts @@ -37,6 +37,11 @@ export interface DSPsychologist { archived: boolean; number: number; state: string; + dateTraitement?: string; + messages?: { + createdAt?: string; + body?: string; + }[]; groupeInstructeur: { id: string; label: string;