From 9768bdd91df63958a8c1b703746ed4780403df1a Mon Sep 17 00:00:00 2001 From: Arnaud AMBROSELLI Date: Fri, 6 Oct 2023 16:14:54 +0200 Subject: [PATCH 1/3] =?UTF-8?q?fix(dashboard):=20migration=20pour=20cleane?= =?UTF-8?q?r=20les=20dossiers=20m=C3=A9dicaux=20dupliqu=C3=A9s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/src/controllers/migration.js | 25 ++++++++- dashboard/src/components/DataMigrator.js | 64 ++++++++++++++++++++++++ dashboard/src/recoil/medicalFiles.ts | 2 +- 3 files changed, 89 insertions(+), 2 deletions(-) diff --git a/api/src/controllers/migration.js b/api/src/controllers/migration.js index b25a0a6ed..d8fff35ab 100644 --- a/api/src/controllers/migration.js +++ b/api/src/controllers/migration.js @@ -8,7 +8,7 @@ const { looseUuidRegex, dateRegex } = require("../utils"); const { capture } = require("../sentry"); const validateUser = require("../middleware/validateUser"); const { serializeOrganisation } = require("../utils/data-serializer"); -const { Organisation, Person, Action, Comment, Report, Team, Service, sequelize, Group } = require("../db/sequelize"); +const { Organisation, Person, Action, Comment, Report, Team, Service, sequelize, Group, MedicalFile } = require("../db/sequelize"); router.put( "/:migrationName", @@ -88,6 +88,29 @@ router.put( } } + if (req.params.migrationName === "clean-duplicated-medical-files") { + try { + z.array(z.string().regex(looseUuidRegex)).parse(req.body.medicalFileIdsToDelete); + z.array( + z.object({ + _id: z.string().regex(looseUuidRegex), + encrypted: z.string(), + encryptedEntityKey: z.string(), + }) + ).parse(req.body.medicalFilesToUpdate); + } catch (e) { + const error = new Error(`Invalid request in clean-duplicated-medical-files migration: ${e}`); + error.status = 400; + throw error; + } + for (const _id of req.body.medicalFileIdsToDelete) { + await MedicalFile.destroy({ where: { _id, organisation: req.user.organisation }, transaction: tx }); + } + for (const { _id, encrypted, encryptedEntityKey } of req.body.medicalFilesToUpdate) { + await MedicalFile.update({ encrypted, encryptedEntityKey }, { where: { _id }, transaction: tx, paranoid: false }); + } + } + organisation.set({ migrations: [...(organisation.migrations || []), req.params.migrationName], migrating: false, diff --git a/dashboard/src/components/DataMigrator.js b/dashboard/src/components/DataMigrator.js index b4df9933f..cd3218028 100644 --- a/dashboard/src/components/DataMigrator.js +++ b/dashboard/src/components/DataMigrator.js @@ -10,6 +10,7 @@ import { looseUuidRegex } from '../utils'; import { prepareCommentForEncryption } from '../recoil/comments'; import { prepareGroupForEncryption } from '../recoil/groups'; import { capture } from '../services/sentry'; +import { defaultMedicalFileCustomFields, prepareMedicalFileForEncryption } from '../recoil/medicalFiles'; const LOADING_TEXT = 'Mise à jour des données de votre organisation…'; @@ -111,6 +112,69 @@ export default function useDataMigrator() { migrationLastUpdateAt = response.organisation.migrationLastUpdateAt; } } + + if (['admin', 'normal'].includes(user.role) && !organisation.migrations?.includes('clean-duplicated-medical-files')) { + setLoadingText(LOADING_TEXT); + const customFieldsMedicalFile = Array.isArray(organisation.customFieldsMedicalFile) + ? organisation.customFieldsMedicalFile + : defaultMedicalFileCustomFields; + const medicalFiles = await API.get({ + path: '/medical-file', + query: { organisation: organisationId, after: 0, withDeleted: false }, + }).then((res) => res.decryptedData || []); + + const medicalFilesPerPersonId = {}; + const medicalFilesToUpdate = {}; + const medicalFileIdsToDelete = []; + for (const newMedicalFile of medicalFiles) { + const personId = newMedicalFile.person; + if (medicalFilesPerPersonId[personId]) { + const existingMedicalFile = medicalFilesPerPersonId[personId]; + const nextDocuments = {}; + const nextComments = {}; + for (const document of newMedicalFile.documents) { + nextDocuments[document._id] = document; + } + for (const document of existingMedicalFile.documents) { + nextDocuments[document._id] = document; + } + for (const comment of newMedicalFile.comments) { + nextComments[comment._id] = comment; + } + for (const comment of existingMedicalFile.comments) { + nextComments[comment._id] = comment; + } + medicalFilesPerPersonId[personId] = { + ...newMedicalFile, + ...existingMedicalFile, + documents: Object.values(nextDocuments), + comments: Object.values(nextComments), + }; + medicalFileIdsToDelete.push(newMedicalFile._id); + medicalFilesToUpdate[personId] = medicalFilesPerPersonId[personId]; + } else { + medicalFilesPerPersonId[personId] = newMedicalFile; + } + } + + const encryptedMedicalFilesToUpdate = await Promise.all( + Object.values(medicalFilesToUpdate) + .map((medicalFile) => prepareMedicalFileForEncryption(customFieldsMedicalFile)(medicalFile)) + .map(encryptItem) + ); + + console.log('encryptedMedicalFilesToUpdate', encryptedMedicalFilesToUpdate); + console.log('medicalFileIdsToDelete', medicalFileIdsToDelete); + const response = await API.put({ + path: `/migration/clean-duplicated-medical-files`, + body: { medicalFileIdsToDelete, medicalFilesToUpdate: encryptedMedicalFilesToUpdate }, + query: { migrationLastUpdateAt }, + }); + if (response.ok) { + setOrganisation(response.organisation); + migrationLastUpdateAt = response.organisation.migrationLastUpdateAt; + } + } }, }; } diff --git a/dashboard/src/recoil/medicalFiles.ts b/dashboard/src/recoil/medicalFiles.ts index 289576b1e..2d06fd6fe 100644 --- a/dashboard/src/recoil/medicalFiles.ts +++ b/dashboard/src/recoil/medicalFiles.ts @@ -55,7 +55,7 @@ export const prepareMedicalFileForEncryption = }; }; -const defaultMedicalFileCustomFields: CustomField[] = [ +export const defaultMedicalFileCustomFields: CustomField[] = [ { name: 'numeroSecuriteSociale', label: 'Numéro de sécurité sociale', From bd4960de9cea12a10944221b8d00427a71f6881d Mon Sep 17 00:00:00 2001 From: Arnaud AMBROSELLI Date: Fri, 6 Oct 2023 16:24:36 +0200 Subject: [PATCH 2/3] clean --- dashboard/src/components/DataMigrator.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/dashboard/src/components/DataMigrator.js b/dashboard/src/components/DataMigrator.js index cd3218028..d02df4267 100644 --- a/dashboard/src/components/DataMigrator.js +++ b/dashboard/src/components/DataMigrator.js @@ -163,8 +163,6 @@ export default function useDataMigrator() { .map(encryptItem) ); - console.log('encryptedMedicalFilesToUpdate', encryptedMedicalFilesToUpdate); - console.log('medicalFileIdsToDelete', medicalFileIdsToDelete); const response = await API.put({ path: `/migration/clean-duplicated-medical-files`, body: { medicalFileIdsToDelete, medicalFilesToUpdate: encryptedMedicalFilesToUpdate }, From 0fcaa594350c641306653de24d9e7c08dc477bc8 Mon Sep 17 00:00:00 2001 From: Arnaud AMBROSELLI Date: Fri, 6 Oct 2023 17:12:24 +0200 Subject: [PATCH 3/3] log a bit more --- dashboard/src/components/DataMigrator.js | 46 ++++++++++++++---------- dashboard/src/recoil/selectors.js | 7 ++++ 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/dashboard/src/components/DataMigrator.js b/dashboard/src/components/DataMigrator.js index d02df4267..fcd00cbbc 100644 --- a/dashboard/src/components/DataMigrator.js +++ b/dashboard/src/components/DataMigrator.js @@ -123,37 +123,40 @@ export default function useDataMigrator() { query: { organisation: organisationId, after: 0, withDeleted: false }, }).then((res) => res.decryptedData || []); + const medicalFilePerPersonId = {}; const medicalFilesPerPersonId = {}; const medicalFilesToUpdate = {}; const medicalFileIdsToDelete = []; for (const newMedicalFile of medicalFiles) { const personId = newMedicalFile.person; - if (medicalFilesPerPersonId[personId]) { - const existingMedicalFile = medicalFilesPerPersonId[personId]; + if (medicalFilePerPersonId[personId]) { + if (!medicalFilesPerPersonId[personId]) medicalFilesPerPersonId[personId] = [medicalFilePerPersonId[personId]]; + medicalFilesPerPersonId[personId].push(newMedicalFile); + const existingMedicalFile = medicalFilePerPersonId[personId]; const nextDocuments = {}; const nextComments = {}; - for (const document of newMedicalFile.documents) { + for (const document of newMedicalFile.documents || []) { nextDocuments[document._id] = document; } - for (const document of existingMedicalFile.documents) { + for (const document of existingMedicalFile.documents || []) { nextDocuments[document._id] = document; } - for (const comment of newMedicalFile.comments) { + for (const comment of newMedicalFile.comments || []) { nextComments[comment._id] = comment; } - for (const comment of existingMedicalFile.comments) { + for (const comment of existingMedicalFile.comments || []) { nextComments[comment._id] = comment; } - medicalFilesPerPersonId[personId] = { + medicalFilePerPersonId[personId] = { ...newMedicalFile, ...existingMedicalFile, documents: Object.values(nextDocuments), comments: Object.values(nextComments), }; medicalFileIdsToDelete.push(newMedicalFile._id); - medicalFilesToUpdate[personId] = medicalFilesPerPersonId[personId]; + medicalFilesToUpdate[personId] = medicalFilePerPersonId[personId]; } else { - medicalFilesPerPersonId[personId] = newMedicalFile; + medicalFilePerPersonId[personId] = newMedicalFile; } } @@ -163,15 +166,22 @@ export default function useDataMigrator() { .map(encryptItem) ); - const response = await API.put({ - path: `/migration/clean-duplicated-medical-files`, - body: { medicalFileIdsToDelete, medicalFilesToUpdate: encryptedMedicalFilesToUpdate }, - query: { migrationLastUpdateAt }, - }); - if (response.ok) { - setOrganisation(response.organisation); - migrationLastUpdateAt = response.organisation.migrationLastUpdateAt; - } + console.log('medicalFilesPerPersonId', medicalFilesPerPersonId); + console.log('medicalFilesToUpdate', medicalFilesToUpdate); + console.log( + 'encryptedMedicalFilesToUpdate', + encryptedMedicalFilesToUpdate.sort((a, b) => new Date(a.createdAt) - new Date(b.createdAt)) + ); + + // const response = await API.put({ + // path: `/migration/clean-duplicated-medical-files`, + // body: { medicalFileIdsToDelete, medicalFilesToUpdate: encryptedMedicalFilesToUpdate }, + // query: { migrationLastUpdateAt }, + // }); + // if (response.ok) { + // setOrganisation(response.organisation); + // migrationLastUpdateAt = response.organisation.migrationLastUpdateAt; + // } } }, }; diff --git a/dashboard/src/recoil/selectors.js b/dashboard/src/recoil/selectors.js index 5f8634faf..edb773a7f 100644 --- a/dashboard/src/recoil/selectors.js +++ b/dashboard/src/recoil/selectors.js @@ -260,12 +260,19 @@ export const itemsGroupedByPersonSelector = selector({ for (const medicalFile of medicalFiles) { if (!personsObject[medicalFile.person]) continue; if (personsObject[medicalFile.person].medicalFile) { + if ('0a0f7577-890e-4731-a71c-a78cbcf1d595' === medicalFile.person) { + console.log('medicalFile', medicalFile); + console.log('personsObject[medicalFile.person].medicalFile', personsObject[medicalFile.person].medicalFile); + } personsObject[medicalFile.person].medicalFile = { ...medicalFile, ...personsObject[medicalFile.person].medicalFile, documents: [...(medicalFile?.documents || []), ...(personsObject[medicalFile.person].medicalFile?.documents || [])], comments: [...(medicalFile?.comments || []), ...(personsObject[medicalFile.person].medicalFile?.comments || [])], }; + if ('0a0f7577-890e-4731-a71c-a78cbcf1d595' === medicalFile.person) { + console.log('new personsObject[medicalFile.person].medicalFile', personsObject[medicalFile.person].medicalFile); + } } else { personsObject[medicalFile.person].medicalFile = medicalFile; }