@phenyxhealth/importer 1.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,346 @@
1
+ import path from 'path';
2
+ import { loadJson } from '@dmbejar/json-files';
3
+ import ApiInstance from '../services/phenyxApiInstance.js';
4
+ import { login } from '../controllers/login.js';
5
+ import { getListFromField } from '../utils/text.js';
6
+ import { checkGlobals } from '../controllers/global.js';
7
+ import { beSureDoctorExists } from '../controllers/doctors.js';
8
+ import { beSureLinacsExists } from '../controllers/linacs.js';
9
+ import { group } from 'console';
10
+ import { get } from 'http';
11
+
12
+ const importer = async (config) => {
13
+ // Config
14
+ ApiInstance.showLogs(config.showLogs);
15
+
16
+ const {
17
+ inputFile,
18
+ overrideExistingPatients,
19
+ } = config;
20
+
21
+ console.log(`Importing data from "${inputFile}"...`);
22
+
23
+ const {
24
+ ok: loadOk,
25
+ error: patientsError,
26
+ data,
27
+ } = loadJson({ fileName: path.resolve(process.cwd(), inputFile) });
28
+
29
+ if (!loadOk) {
30
+ console.error('Error loading input file:', patientsError);
31
+ return;
32
+ }
33
+
34
+ console.log('\nLogin...');
35
+ await login(config);
36
+
37
+ // check globals
38
+ console.log('\nVerifying globals...');
39
+ await checkGlobals(ApiInstance.globalTypes.sendingDepartment, getListFromField(data.patients, 'registration.sendingDepartment'));
40
+ await checkGlobals(ApiInstance.globalTypes.cei9, getListFromField(data.patients, 'consultation.cei9'));
41
+ await checkGlobals(ApiInstance.globalTypes.treatmentClass, getListFromField(data.patients, 'consultation.treatmentClass'));
42
+ await checkGlobals(ApiInstance.globalTypes.treatmentSubClass, getListFromField(data.patients, 'consultation.treatmentSubClass'));
43
+ await checkGlobals(ApiInstance.globalTypes.treatmentTechnique, getListFromField(data.patients, 'treatmentDefinition.treatmentTechnique'));
44
+ await checkGlobals(ApiInstance.globalTypes.treatmentType, getListFromField(data.patients, 'consultation.treatmentType'));
45
+ await checkGlobals(ApiInstance.globalTypes.treatmentGoal, getListFromField(data.patients, 'consultation.treatmentGoal'));
46
+ await checkGlobals(ApiInstance.globalTypes.treatmentParticles, getListFromField(data.patients, 'consultation.treatmentParticle'));
47
+ await checkGlobals(ApiInstance.globalTypes.noTreatmentReasons, getListFromField(data.patients, 'consultation.treatmentReason'));
48
+ await checkGlobals(ApiInstance.globalTypes.ctSimProtocols, getListFromField(data.patients, 'treatmentVerification.protocol'));
49
+ await checkGlobals(ApiInstance.globalTypes.contouringTools, getListFromField(data.patients, 'contouringOar.tool'));
50
+ await checkGlobals(ApiInstance.globalTypes.fractionationTypes, getListFromField(data.patients, 'treatmentDefinition.fractionationType'));
51
+ await checkGlobals(ApiInstance.globalTypes.sessionDurationCriteria, getListFromField(data.patients, 'treatmentDefinition.sessionDurationCriteria'));
52
+ await checkGlobals(ApiInstance.globalTypes.dosimetryCalculationMethods, getListFromField(data.patients, 'dosimetry.main.calculationMethod'));
53
+ await checkGlobals(ApiInstance.globalTypes.dosimetrySecondaryCalculationMethods, getListFromField(data.patients, 'dosimetry.secondCalculation.calculationMethod'));
54
+ await checkGlobals(ApiInstance.globalTypes.tps, getListFromField(data.patients, 'dosimetry.main.tpsUsed'));
55
+ await checkGlobals(ApiInstance.globalTypes.treatmentVerificationProtocols, getListFromField(data.patients, 'treatmentVerification.protocol'));
56
+ checkGlobals(ApiInstance.globalTypes.humanResources, getListFromField(data.doctors, 'hrGroup'));
57
+
58
+ // Dar de alta cada LINAC que no exista
59
+ const linacs = await beSureLinacsExists(getListFromField(data.patients, 'dosimetry.confirmedLinac', 'dosimetry.proposedLinac'));
60
+
61
+ // Verificar los defaults
62
+ let defaultError = false;
63
+
64
+ console.log('\nCheck defaults...');
65
+
66
+ const defaultCompatibleLinacs = ApiInstance.defaultCompatibleLinacs;
67
+
68
+ if (!defaultCompatibleLinacs.length) {
69
+ console.log('ERROR: No default compatible linacs found');
70
+ defaultError = true;
71
+ }
72
+
73
+ const defaultDuration = ApiInstance.defaultDuration;
74
+ if (!defaultDuration) {
75
+ console.log('ERROR: No default duration found');
76
+ defaultError = true;
77
+ }
78
+
79
+ if (defaultError) {
80
+ console.log('\nERROR: Please, set defaults and try again.');
81
+ return;
82
+ }
83
+
84
+ // Dar de alta cada médico que no exista
85
+ console.log('\nCheck doctors...');
86
+ await beSureDoctorExists(data.doctors);
87
+
88
+ // Los pacientes
89
+ const allPatients = await ApiInstance.getPatients();
90
+ const allPatientsIds = {};
91
+
92
+ for (const p of allPatients) {
93
+ allPatientsIds[p.patientId] = p.id;
94
+ }
95
+
96
+ let contadorPacientes = 0;
97
+ const ignoredPatients = [];
98
+
99
+ console.log('\nCheck patients...');
100
+
101
+ const keys = [
102
+ 'registration',
103
+ 'consultation',
104
+ 'contouringOar',
105
+ 'contouringVolumes',
106
+ 'dosimetry',
107
+ 'treatmentSimulation',
108
+ 'treatmentDefinition',
109
+ 'treatmentVerification',
110
+ 'treatmentDelivery',
111
+ ];
112
+
113
+ for (const p of data.patients) {
114
+ for (const key of keys) {
115
+ if (!p[key]) p[key] = {};
116
+ }
117
+
118
+ if (!p.registration.id || !p.registration.name) {
119
+ console.log('ERROR: All patients must have "registration.id" and "registration.name"');
120
+
121
+ return;
122
+ }
123
+ }
124
+
125
+ console.log('\nUploading patients...');
126
+
127
+ // Alta de los pacientes
128
+ for (const patientInfo of data.patients) {
129
+ const {
130
+ registration,
131
+ consultation,
132
+ contouringOar,
133
+ contouringVolumes,
134
+ dosimetry,
135
+ treatmentSimulation,
136
+ treatmentDefinition,
137
+ treatmentVerification,
138
+ treatmentDelivery,
139
+ } = patientInfo;
140
+
141
+ if (!!allPatientsIds[registration.id]) {
142
+ if (!overrideExistingPatients) {
143
+ console.log(`El paciente ${registration.id} ya existe`);
144
+ ignoredPatients.push(registration.id);
145
+ continue;
146
+ }
147
+
148
+ await ApiInstance.deletePatient(allPatientsIds[registration.id]);
149
+ console.log(`El paciente ${registration.id} ya existía. Se ha eliminado para sobreescribirlo.`);
150
+ }
151
+
152
+ console.log(`Procesando paciente: ${registration.id} (${registration.name})`);
153
+
154
+ // Crear el formData
155
+ const formData = {
156
+ registration: {
157
+ patientId: registration.id,
158
+ registrationDate: registration.date,
159
+ firstConsultProposal: registration.firstConsult,
160
+ assignedRadOnc: registration.assignedRadOnc,
161
+ assignedMedPhys: registration.assignedMedPhys,
162
+ sendingDepartment: ApiInstance.getSendingDepartmentIdByName(registration.sendingDepartment),
163
+ },
164
+ consultation: {
165
+ cei: ApiInstance.getGlobalInfoElementId(ApiInstance.globalTypes.cei9, consultation.cei9),
166
+ tnm: consultation.tnm,
167
+ estadio: consultation.stage,
168
+ treatmentClass: ApiInstance.getGlobalInfoElementId(ApiInstance.globalTypes.treatmentClass, consultation.treatmentClass),
169
+ treatmentSubClass: ApiInstance.getGlobalInfoElementId(ApiInstance.globalTypes.treatmentSubClass, consultation.treatmentSubClass),
170
+ earlierStartDate: consultation.earlierStartDate,
171
+ latestStartDate: consultation.latestStartDate,
172
+ mandatoryStartDate: consultation.mandatoryStartDate,
173
+ priority: consultation.priority,
174
+ timeWindowPreference_Start: consultation.preferredStartTime,
175
+ timeWindowPreference_End: consultation.preferredEndTime,
176
+ treatmentType: ApiInstance.getGlobalInfoElementId(ApiInstance.globalTypes.treatmentType, consultation.treatmentType),
177
+ treatmentGoal: ApiInstance.getGlobalInfoElementId(ApiInstance.globalTypes.treatmentGoal, consultation.treatmentGoal),
178
+ noTreatment: consultation.noTreatment === 'Yes',
179
+ why: ApiInstance.getGlobalInfoElementId(ApiInstance.globalTypes.noTreatmentReasons, consultation.noTreatmentReason),
180
+ state: ApiInstance.getQaStatus(consultation),
181
+ compatibleLinacs: defaultCompatibleLinacs,
182
+ firstSesionDuration: defaultDuration.firstSession,
183
+ regularSesionDuration: defaultDuration.regularSessions,
184
+ linacAllocationCriteria: ApiInstance.getGlobalInfoElementId(ApiInstance.globalTypes.linacsAllocationCriteria, treatmentDefinition.linacAllocationCriteria || 'Default'),
185
+ sessionDurationCriteria: ApiInstance.getGlobalInfoElementId(ApiInstance.globalTypes.sessionDurationCriteria, treatmentDefinition.sessionDurationCriteria || 'Default'),
186
+ treatmentParticle: ApiInstance.getGlobalInfoElementId(ApiInstance.globalTypes.treatmentParticles, treatmentDefinition.treatmentParticle),
187
+ treatmentTechnique: ApiInstance.getGlobalInfoElementId(ApiInstance.globalTypes.treatmentTechnique, treatmentDefinition.treatmentTechnique),
188
+ numberOfFractions: treatmentDelivery.totalSessions,
189
+ fractionation: "",
190
+ fractionPattern: treatmentDefinition.fractionationPattern,
191
+ weeklySessions: "",
192
+ weeklySessions_Min: consultation.minWeeklySessions || "",
193
+ weeklySessions_Max: consultation.maxWeeklySessions || "",
194
+ daysBetweenSessions: "",
195
+ comments: consultation.comments || "",
196
+ ctSim: "",
197
+ stateChanges: []
198
+ },
199
+ treatmentSimulation: {
200
+ simulationProtocol: ApiInstance.getGlobalInfoElementId(ApiInstance.globalTypes.ctSimProtocols, treatmentVerification.protocol),
201
+ ctGuidelines: "",
202
+ comments: treatmentSimulation.comments || "",
203
+ state: ApiInstance.getQaStatus(treatmentSimulation),
204
+ stateChanges: [],
205
+ atachedFiles: []
206
+ },
207
+ contouringOar: {
208
+ guidelines: "",
209
+ proposedRrhh: ApiInstance.getDoctorTypeNameByName(contouringOar.who),
210
+ rrhhId: ApiInstance.getDoctorIdByName(contouringOar.who),
211
+ countouringTool: ApiInstance.getGlobalInfoElementId(ApiInstance.globalTypes.contouringTools, contouringOar.tool),
212
+ oarTableId: ApiInstance.getElementIdByName('oars', contouringOar.table),
213
+ comments: contouringOar.comments || "",
214
+ state: "NOT_STARTED",
215
+ stateChanges: []
216
+ },
217
+ contouringVolumes: {
218
+ guidelines: "",
219
+ proposedRrhh: ApiInstance.getDoctorTypeNameByName(contouringVolumes.who),
220
+ rrhhId: ApiInstance.getDoctorIdByName(contouringVolumes.who),
221
+ countouringTool: ApiInstance.getGlobalInfoElementId(ApiInstance.globalTypes.contouringTools, contouringVolumes.tool),
222
+ oarTableId: ApiInstance.getElementIdByName('oars', contouringVolumes.table),
223
+ comments: contouringVolumes.comments || "",
224
+ state: ApiInstance.getQaStatus(contouringVolumes) || "NOT_STARTED",
225
+ stateChanges: []
226
+ },
227
+ treatmentDefinition: {
228
+ compatibleLinacs: defaultCompatibleLinacs,
229
+ firstSesionDuration: defaultDuration.firstSession,
230
+ regularSesionDuration: defaultDuration.regularSessions,
231
+ linacAllocationCriteria: ApiInstance.getGlobalInfoElementId(ApiInstance.globalTypes.linacsAllocationCriteria, treatmentDefinition.linacAllocationCriteria || 'Default'),
232
+ sessionDurationCriteria: ApiInstance.getGlobalInfoElementId(ApiInstance.globalTypes.sessionDurationCriteria, treatmentDefinition.sessionDurationCriteria || 'Default'),
233
+ treatmentParticle: ApiInstance.getGlobalInfoElementId(ApiInstance.globalTypes.treatmentParticles, treatmentDefinition.treatmentParticle),
234
+ treatmentTechnique: ApiInstance.getGlobalInfoElementId(ApiInstance.globalTypes.treatmentTechnique, treatmentDefinition.treatmentTechnique),
235
+ numberOfFractions: treatmentDefinition.numberOfFractions,
236
+ fractionation: ApiInstance.getGlobalInfoElementId(ApiInstance.globalTypes.fractionationTypes, treatmentDefinition.fractionationType),
237
+ fractionPattern: treatmentDefinition.fractionationPattern,
238
+ weeklySessions: "",
239
+ daysBetweenSessions: "",
240
+ comments: treatmentDefinition.comments || "",
241
+ checkListPeerReview: {
242
+ targetVolumes: false,
243
+ oarVolumes: false,
244
+ targetDoses: false,
245
+ oarToleranceTables: false,
246
+ treatmentTechnique: false,
247
+ fractionation: false
248
+ },
249
+ state: "NOT_STARTED",
250
+ stateChanges: [],
251
+ peerReviewedGroupRrhh: ApiInstance.getDoctorTypeNameByName(treatmentDefinition.peerReview.who),
252
+ peerReviewedIdRrhh: ApiInstance.getDoctorIdByName(treatmentDefinition.peerReview.who)
253
+ },
254
+ dosimetry: {
255
+ proposedLinac: linacs.find(linac => linac.name === dosimetry.proposedLinac)?.id || '',
256
+ confirmedLinac: linacs.find(linac => linac.name === dosimetry.confirmedLinac)?.id || '',
257
+ idRrhh: ApiInstance.getDoctorIdByName(dosimetry?.main?.who),
258
+ groupRrhh: ApiInstance.getDoctorTypeNameByName(dosimetry?.main?.who),
259
+ calculationMethod: ApiInstance.getGlobalInfoElementId(ApiInstance.globalTypes.dosimetryCalculationMethods, dosimetry?.main?.calculationMethod),
260
+ typeOfPlanning: dosimetry.main?.planningType,
261
+ tpsUsed: ApiInstance.getGlobalInfoElementId(ApiInstance.globalTypes.tps, dosimetry.main?.tpsUsed),
262
+ secondGroupRrhh: ApiInstance.getDoctorTypeNameByName(dosimetry?.secondCalculation?.who),
263
+ secondIdRrhh: ApiInstance.getDoctorIdByName(dosimetry?.secondCalculation?.who),
264
+ secondCalculationMethod: ApiInstance.getGlobalInfoElementId(ApiInstance.globalTypes.dosimetrySecondaryCalculationMethods, dosimetry?.secondCalculation?.calculationMethod),
265
+ dosimetryComplete: false,
266
+ planDefinitionComplete: false,
267
+ dosimetricReportComplete: false,
268
+ state: ApiInstance.getQaStatus(dosimetry) || "NOT_STARTED",
269
+ stateChanges: [],
270
+ dosimetricReportFiles: [],
271
+ treatmentPlanFiles: [],
272
+ peerReviewedGroupRrhh: ApiInstance.getDoctorTypeNameByName(dosimetry.peerReview.who),
273
+ peerReviewedIdRrhh: ApiInstance.getDoctorIdByName(dosimetry.peerReview.who)
274
+ },
275
+ treatmentVerification: {
276
+ description: "",
277
+ comments: treatmentVerification.comments || "",
278
+ verificationProtocol: ApiInstance.getGlobalInfoElementId(ApiInstance.globalTypes.treatmentVerificationProtocols, treatmentVerification.protocol),
279
+ state: ApiInstance.getQaStatus(treatmentVerification) || "NOT_STARTED",
280
+ stateChanges: [],
281
+ verificationReportFiles: [],
282
+ uploadFiles: []
283
+ },
284
+ treatmentDelivery: {
285
+ numberOfFractions: treatmentDelivery.totalSessions || 0,
286
+ confirmedLinac: linacs.find(linac => linac.name === dosimetry.confirmedLinac)?.id || '',
287
+ treatmentTechnique: ApiInstance.getGlobalInfoElementId(ApiInstance.globalTypes.treatmentTechnique, treatmentDefinition.treatmentTechnique),
288
+ tentativeTreatmentStart: treatmentDelivery.tentativeTreatmentStartDate,
289
+ confirmedTreatmentStart: treatmentDelivery.confirmedTreatmentStartingDate,
290
+ numberOfCompleteSessions: treatmentDelivery.completedSessions || 0,
291
+ numberOfPendingSessions: treatmentDelivery.totalSessions
292
+ ? treatmentDelivery.totalSessions - (treatmentDelivery.completedSessions || 0)
293
+ : 0,
294
+ sessionsHistory: [],
295
+ startAssignedShift: "",
296
+ startConfirmedStartDate: treatmentDelivery.confirmedTreatmentStartDate,
297
+ startWeeklyAssignedDays: treatmentDelivery.weeklyAssignedDays.split(',').map(d => d.trim()),
298
+ interruptedEstimatedEndDate: treatmentDelivery.interruptedEndDate,
299
+ interruptedAssignedShift: "",
300
+ interruptedStartDate: treatmentDelivery.interruptedStartDate,
301
+ interruptedMotivation: "",
302
+ canceledStartDate: treatmentDelivery.canceledDate,
303
+ canceledMotivation: "",
304
+ stateChanges: []
305
+ },
306
+ onHold: {
307
+ "items": [
308
+ {
309
+ "id": 1,
310
+ "start": "",
311
+ "end": "",
312
+ "motivation": "",
313
+ "comments": ""
314
+ }
315
+ ],
316
+ "state": "NOT_STARTED",
317
+ "stateChanges": []
318
+ }
319
+ }
320
+
321
+ // Guardar el paciente
322
+ const createdId = await ApiInstance.createPatient({ firstName: registration.name.trim(), formData });
323
+
324
+ // Recuperar el cliente
325
+ const patient = await ApiInstance.getPatient(createdId);
326
+
327
+ // Meter los QA
328
+ for (const section of Object.keys(patientInfo)) {
329
+ const qa = patientInfo[section].qa;
330
+ if (!qa) continue;
331
+ const formDataId = patient.formData[section]?.id;
332
+ if (!formDataId) continue;
333
+ await ApiInstance.updateSectionQaStatus(qa, formDataId)
334
+ }
335
+
336
+ contadorPacientes++;
337
+ }
338
+
339
+ if (ignoredPatients.length) console.error('\n\nLos siguientes pacientes no se han podido importar porque ya existían en el sistema:', ignoredPatients.join(', '));
340
+
341
+ console.log('\nImport process finished');
342
+ };
343
+
344
+ export {
345
+ importer,
346
+ };