@andes/fhir 1.12.0 → 1.12.1-beta.2
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.
- package/LICENSE +674 -674
- package/README.md +2 -2
- package/{src → dist}/data/patient.json +192 -192
- package/dist/helpers/patientFactory.d.ts +26 -0
- package/dist/{src/helpers → helpers}/patientFactory.js +35 -31
- package/dist/index.d.ts +15 -0
- package/dist/index.js +55 -52
- package/dist/lib/allergyIntolerance.d.ts +29 -0
- package/dist/{src/lib → lib}/allergyIntolerance.js +39 -40
- package/dist/lib/bundle.d.ts +14 -0
- package/dist/{src/lib → lib}/bundle.js +25 -26
- package/dist/lib/composition.d.ts +47 -0
- package/dist/{src/lib → lib}/composition.js +152 -152
- package/dist/lib/condition.d.ts +53 -0
- package/dist/{src/lib → lib}/condition.js +79 -80
- package/dist/lib/config.d.ts +9 -0
- package/dist/{src/lib → lib}/config.js +27 -21
- package/dist/lib/config.test.d.ts +1 -0
- package/dist/lib/config.test.js +19 -0
- package/dist/lib/device.d.ts +22 -0
- package/dist/{src/lib → lib}/device.js +38 -39
- package/dist/lib/device.test.d.ts +1 -0
- package/dist/lib/device.test.js +9 -0
- package/dist/lib/document-reference.d.ts +71 -0
- package/dist/{src/lib → lib}/document-reference.js +106 -107
- package/dist/lib/immunization.d.ts +44 -0
- package/dist/{src/lib → lib}/immunization.js +67 -68
- package/dist/lib/medication.d.ts +25 -0
- package/{src/lib/medication.ts → dist/lib/medication.js} +64 -61
- package/dist/lib/medicationRequest.d.ts +42 -0
- package/{src/lib/medicationRequest.ts → dist/lib/medicationRequest.js} +103 -100
- package/dist/lib/medicationStatement.d.ts +47 -0
- package/dist/{src/lib → lib}/medicationStatement.js +55 -56
- package/dist/lib/organization.d.ts +6 -0
- package/dist/lib/organization.js +93 -0
- package/dist/lib/patient.d.ts +14 -0
- package/dist/lib/patient.js +465 -0
- package/dist/lib/patient.test.d.ts +1 -0
- package/dist/{src/lib → lib}/patient.test.js +132 -113
- package/dist/lib/practitioner.d.ts +6 -0
- package/dist/lib/practitioner.js +186 -0
- package/dist/types/andes/organization.types.d.ts +39 -0
- package/dist/types/andes/organization.types.js +2 -0
- package/dist/types/andes/patient.types.d.ts +57 -0
- package/dist/types/andes/patient.types.js +2 -0
- package/dist/types/andes/practitioner.types.d.ts +72 -0
- package/dist/types/andes/practitioner.types.js +3 -0
- package/package.json +56 -45
- package/.circleci/config.yml +0 -72
- package/.editorconfig +0 -14
- package/.env.example +0 -2
- package/.gitattributes +0 -1
- package/.github/CODEOWNERS +0 -1
- package/.github/labeler.yml +0 -11
- package/.github/pull_request_template.md +0 -28
- package/.github/workflows/build_test.yml +0 -14
- package/.github/workflows/has_conflicts.yml +0 -13
- package/.vscode/settings.json +0 -2
- package/CHANGELOG.md +0 -351
- package/coverage/clover.xml +0 -209
- package/coverage/coverage-final.json +0 -5
- package/coverage/lcov-report/base.css +0 -224
- package/coverage/lcov-report/block-navigation.js +0 -87
- package/coverage/lcov-report/config.ts.html +0 -136
- package/coverage/lcov-report/device.ts.html +0 -193
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/helpers/index.html +0 -116
- package/coverage/lcov-report/helpers/patientFactory.ts.html +0 -124
- package/coverage/lcov-report/index.html +0 -131
- package/coverage/lcov-report/lib/config.ts.html +0 -136
- package/coverage/lcov-report/lib/device.ts.html +0 -193
- package/coverage/lcov-report/lib/index.html +0 -146
- package/coverage/lcov-report/lib/patient.ts.html +0 -1318
- package/coverage/lcov-report/patient.ts.html +0 -1207
- package/coverage/lcov-report/prettify.css +0 -1
- package/coverage/lcov-report/prettify.js +0 -2
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +0 -210
- package/coverage/lcov.info +0 -425
- package/dist/index.js.map +0 -1
- package/dist/src/data/patient.json +0 -192
- package/dist/src/helpers/patientFactory.js.map +0 -1
- package/dist/src/lib/allergyIntolerance.js.map +0 -1
- package/dist/src/lib/bundle.js.map +0 -1
- package/dist/src/lib/composition.js.map +0 -1
- package/dist/src/lib/condition.js.map +0 -1
- package/dist/src/lib/config.js.map +0 -1
- package/dist/src/lib/config.test.js +0 -13
- package/dist/src/lib/config.test.js.map +0 -1
- package/dist/src/lib/device.js.map +0 -1
- package/dist/src/lib/device.test.js +0 -10
- package/dist/src/lib/device.test.js.map +0 -1
- package/dist/src/lib/document-reference.js.map +0 -1
- package/dist/src/lib/immunization.js.map +0 -1
- package/dist/src/lib/medication.js +0 -65
- package/dist/src/lib/medication.js.map +0 -1
- package/dist/src/lib/medicationRequest.js +0 -103
- package/dist/src/lib/medicationRequest.js.map +0 -1
- package/dist/src/lib/medicationStatement.js.map +0 -1
- package/dist/src/lib/organization.js +0 -91
- package/dist/src/lib/organization.js.map +0 -1
- package/dist/src/lib/patient.js +0 -397
- package/dist/src/lib/patient.js.map +0 -1
- package/dist/src/lib/patient.test.js.map +0 -1
- package/dist/src/lib/practitioner.js +0 -161
- package/dist/src/lib/practitioner.js.map +0 -1
- package/index.ts +0 -30
- package/jest.config.js +0 -12
- package/release.config.js +0 -74
- package/src/helpers/patientFactory.ts +0 -13
- package/src/lib/allergyIntolerance.ts +0 -36
- package/src/lib/bundle.ts +0 -19
- package/src/lib/composition.ts +0 -153
- package/src/lib/condition.ts +0 -80
- package/src/lib/config.test.ts +0 -13
- package/src/lib/config.ts +0 -17
- package/src/lib/device.test.ts +0 -9
- package/src/lib/device.ts +0 -36
- package/src/lib/document-reference.ts +0 -102
- package/src/lib/immunization.ts +0 -64
- package/src/lib/medicationStatement.ts +0 -52
- package/src/lib/organization.ts +0 -89
- package/src/lib/patient.test.ts +0 -101
- package/src/lib/patient.ts +0 -411
- package/src/lib/practitioner.ts +0 -157
- package/tsconfig.test.json +0 -18
- package/tslint.json +0 -139
|
@@ -1,56 +1,55 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.encode = void 0;
|
|
4
|
-
/**
|
|
5
|
-
* Encode de Medicamentos from ANDES to FHIR
|
|
6
|
-
* @param {} medicationStatement
|
|
7
|
-
*/
|
|
8
|
-
// Muy incompleto hay que mejorarlo mucho todavía
|
|
9
|
-
function encode(patientReference, medicationReference, prestacionMedicamento) {
|
|
10
|
-
return {
|
|
11
|
-
resourceType: 'MedicationStatement',
|
|
12
|
-
status: (prestacionMedicamento.valor.estado === 'activo') ? 'active' : 'inactive',
|
|
13
|
-
id: prestacionMedicamento._id,
|
|
14
|
-
effectiveDateTime: prestacionMedicamento.createdAt,
|
|
15
|
-
medicationCodeableConcept: {
|
|
16
|
-
coding: [
|
|
17
|
-
{
|
|
18
|
-
system: 'http://snomed.info/sct',
|
|
19
|
-
code: prestacionMedicamento.concepto.conceptId,
|
|
20
|
-
display: prestacionMedicamento.concepto.term
|
|
21
|
-
}
|
|
22
|
-
]
|
|
23
|
-
},
|
|
24
|
-
meta: {
|
|
25
|
-
profile: [
|
|
26
|
-
'http://hl7.org/fhir/uv/ips/StructureDefinition/medication-ips'
|
|
27
|
-
]
|
|
28
|
-
},
|
|
29
|
-
// extension: null,
|
|
30
|
-
// identifier: '', // Business identifier, not a resource identifier
|
|
31
|
-
subject: {
|
|
32
|
-
reference: patientReference
|
|
33
|
-
},
|
|
34
|
-
medicationReference: {
|
|
35
|
-
reference: medicationReference
|
|
36
|
-
},
|
|
37
|
-
// context: null, // Encounter | Episode of care (podría ser el informe del encuentro o lo que definamos que brinda más información sobre el por qué)
|
|
38
|
-
dosage: [
|
|
39
|
-
{
|
|
40
|
-
text: prestacionMedicamento.valor,
|
|
41
|
-
route: { coding: [{ system: 'http://standardterms.edqm.eu', code: prestacionMedicamento._id, display: prestacionMedicamento.nombre }] },
|
|
42
|
-
timing: { coding: { system: 'http://terminology.hl7.org/CodeSystem/v3-GTSAbbreviation', code: 'QD', display: 'Diario' } }
|
|
43
|
-
}
|
|
44
|
-
],
|
|
45
|
-
// effectivePeriod: { // Esta parte se podría calcular entre la fecha de la prestación y la cant. de tiempo que se indica, pero hay problemas
|
|
46
|
-
// en este registro ya que no siempre está la unidad de tiempo (dias, mes, etc) hay q ver si no es un bug.
|
|
47
|
-
// start: '2015-03'
|
|
48
|
-
// },
|
|
49
|
-
text: {
|
|
50
|
-
status: 'generated',
|
|
51
|
-
div: `<div xmlns
|
|
52
|
-
}
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
exports.encode = encode;
|
|
56
|
-
//# sourceMappingURL=medicationStatement.js.map
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.encode = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Encode de Medicamentos from ANDES to FHIR
|
|
6
|
+
* @param {} medicationStatement
|
|
7
|
+
*/
|
|
8
|
+
// Muy incompleto hay que mejorarlo mucho todavía
|
|
9
|
+
function encode(patientReference, medicationReference, prestacionMedicamento) {
|
|
10
|
+
return {
|
|
11
|
+
resourceType: 'MedicationStatement',
|
|
12
|
+
status: (prestacionMedicamento.valor.estado === 'activo') ? 'active' : 'inactive',
|
|
13
|
+
id: prestacionMedicamento._id, // El código de la prestación donde está el registro del contexto de porque está consumiendo el medicamento
|
|
14
|
+
effectiveDateTime: prestacionMedicamento.createdAt,
|
|
15
|
+
medicationCodeableConcept: {
|
|
16
|
+
coding: [
|
|
17
|
+
{
|
|
18
|
+
system: 'http://snomed.info/sct',
|
|
19
|
+
code: prestacionMedicamento.concepto.conceptId,
|
|
20
|
+
display: prestacionMedicamento.concepto.term
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
},
|
|
24
|
+
meta: {
|
|
25
|
+
profile: [
|
|
26
|
+
'http://hl7.org/fhir/uv/ips/StructureDefinition/medication-ips'
|
|
27
|
+
]
|
|
28
|
+
},
|
|
29
|
+
// extension: null,
|
|
30
|
+
// identifier: '', // Business identifier, not a resource identifier
|
|
31
|
+
subject: {
|
|
32
|
+
reference: patientReference
|
|
33
|
+
},
|
|
34
|
+
medicationReference: {
|
|
35
|
+
reference: medicationReference
|
|
36
|
+
},
|
|
37
|
+
// context: null, // Encounter | Episode of care (podría ser el informe del encuentro o lo que definamos que brinda más información sobre el por qué)
|
|
38
|
+
dosage: [
|
|
39
|
+
{
|
|
40
|
+
text: prestacionMedicamento.valor, // indicación
|
|
41
|
+
route: { coding: [{ system: 'http://standardterms.edqm.eu', code: prestacionMedicamento._id, display: prestacionMedicamento.nombre }] },
|
|
42
|
+
timing: { coding: { system: 'http://terminology.hl7.org/CodeSystem/v3-GTSAbbreviation', code: 'QD', display: 'Diario' } }
|
|
43
|
+
}
|
|
44
|
+
],
|
|
45
|
+
// effectivePeriod: { // Esta parte se podría calcular entre la fecha de la prestación y la cant. de tiempo que se indica, pero hay problemas
|
|
46
|
+
// en este registro ya que no siempre está la unidad de tiempo (dias, mes, etc) hay q ver si no es un bug.
|
|
47
|
+
// start: '2015-03'
|
|
48
|
+
// },
|
|
49
|
+
text: {
|
|
50
|
+
status: 'generated',
|
|
51
|
+
div: `<div xmlns="http://www.w3.org/1999/xhtml"><p><b>Generated Narrative with Details</b></p><p><b>id</b>: Medicacion </p><p><b>meta</b>: </p><p><b>text</b>: ${prestacionMedicamento.concepto.term}</p></div>`
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
exports.encode = encode;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Organization } from 'fhir/r4';
|
|
2
|
+
import { AndesOrganization } from '../types/andes/organization.types';
|
|
3
|
+
/**
|
|
4
|
+
* Encode an ANDES Organization to FHIR Organization
|
|
5
|
+
*/
|
|
6
|
+
export declare function encode(organization: AndesOrganization | null | undefined): Organization | null;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.encode = void 0;
|
|
4
|
+
const config_1 = require("./config");
|
|
5
|
+
/**
|
|
6
|
+
* Encode an ANDES Organization to FHIR Organization
|
|
7
|
+
*/
|
|
8
|
+
function encode(organization) {
|
|
9
|
+
const data = organization;
|
|
10
|
+
if (!data)
|
|
11
|
+
return null;
|
|
12
|
+
// Identificadores
|
|
13
|
+
const identificadores = [];
|
|
14
|
+
if (data.codigo?.sisa) {
|
|
15
|
+
identificadores.push({
|
|
16
|
+
system: 'andes.gob.ar/sisa',
|
|
17
|
+
value: data.codigo.sisa
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
if (data.codigo?.cuie) {
|
|
21
|
+
identificadores.push({
|
|
22
|
+
system: 'andes.gob.ar/cuie',
|
|
23
|
+
value: data.codigo.cuie
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
if (data.codigo?.remediar) {
|
|
27
|
+
identificadores.push({
|
|
28
|
+
system: 'andes.gob.ar/remediar',
|
|
29
|
+
value: data.codigo.remediar
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
if (data.codigo?.sips) {
|
|
33
|
+
identificadores.push({
|
|
34
|
+
system: 'andes.gob.ar/sips',
|
|
35
|
+
value: data.codigo.sips
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
identificadores.push({
|
|
39
|
+
system: (0, config_1.makeUrl)('Organization'),
|
|
40
|
+
value: data._id ?? data.id
|
|
41
|
+
});
|
|
42
|
+
// Contactos → telecom
|
|
43
|
+
const contactos = (data.contacto ?? [])
|
|
44
|
+
.map((item) => {
|
|
45
|
+
const cp = {
|
|
46
|
+
value: item.valor,
|
|
47
|
+
rank: item.ranking
|
|
48
|
+
};
|
|
49
|
+
switch (item.tipo) {
|
|
50
|
+
case 'fijo':
|
|
51
|
+
case 'celular':
|
|
52
|
+
cp.system = 'phone';
|
|
53
|
+
break;
|
|
54
|
+
case 'email':
|
|
55
|
+
cp.system = 'email';
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
return cp;
|
|
59
|
+
});
|
|
60
|
+
// Dirección → address
|
|
61
|
+
const direcciones = [];
|
|
62
|
+
if (data.direccion) {
|
|
63
|
+
const dir = data.direccion;
|
|
64
|
+
direcciones.push({
|
|
65
|
+
postalCode: dir.codigoPostal ?? '',
|
|
66
|
+
line: [dir.valor],
|
|
67
|
+
city: dir.ubicacion?.localidad?.nombre ?? '',
|
|
68
|
+
state: dir.ubicacion?.provincia?.nombre ?? '',
|
|
69
|
+
country: dir.ubicacion?.pais?.nombre ?? ''
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
// Organización FHIR
|
|
73
|
+
const organizacionFHIR = {
|
|
74
|
+
resourceType: 'Organization',
|
|
75
|
+
id: data._id ?? data.id,
|
|
76
|
+
identifier: identificadores,
|
|
77
|
+
active: data.activo ?? undefined,
|
|
78
|
+
name: data.nombre ?? undefined,
|
|
79
|
+
type: data.tipoEstablecimiento
|
|
80
|
+
? [{
|
|
81
|
+
text: data.tipoEstablecimiento.nombre
|
|
82
|
+
}]
|
|
83
|
+
: undefined
|
|
84
|
+
};
|
|
85
|
+
if (contactos.length > 0) {
|
|
86
|
+
organizacionFHIR.telecom = contactos;
|
|
87
|
+
}
|
|
88
|
+
if (direcciones.length > 0) {
|
|
89
|
+
organizacionFHIR.address = direcciones;
|
|
90
|
+
}
|
|
91
|
+
return organizacionFHIR;
|
|
92
|
+
}
|
|
93
|
+
exports.encode = encode;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Patient } from 'fhir/r4';
|
|
2
|
+
import { AndesPatient } from '../types/andes/patient.types';
|
|
3
|
+
/**
|
|
4
|
+
* Encode a patient from ANDES to FHIR
|
|
5
|
+
*/
|
|
6
|
+
export declare function encode(patient: AndesPatient | null | undefined): Patient | null;
|
|
7
|
+
/**
|
|
8
|
+
* Decode a patient from FHIR to ANDES
|
|
9
|
+
*/
|
|
10
|
+
export declare function decode(patient: Patient): AndesPatient;
|
|
11
|
+
/**
|
|
12
|
+
* Verify if a patient has a FHIR format
|
|
13
|
+
*/
|
|
14
|
+
export declare function verify(patient: any): boolean;
|
|
@@ -0,0 +1,465 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.verify = exports.decode = exports.encode = void 0;
|
|
4
|
+
const config_1 = require("./config");
|
|
5
|
+
/**
|
|
6
|
+
* Helpers
|
|
7
|
+
*/
|
|
8
|
+
function formatDateOnly(value) {
|
|
9
|
+
if (!value)
|
|
10
|
+
return undefined;
|
|
11
|
+
const d = typeof value === 'string' ? new Date(value) : value;
|
|
12
|
+
if (isNaN(d.getTime()))
|
|
13
|
+
return undefined;
|
|
14
|
+
return d.toISOString().slice(0, 10);
|
|
15
|
+
}
|
|
16
|
+
function formatDateTime(value) {
|
|
17
|
+
if (!value)
|
|
18
|
+
return undefined;
|
|
19
|
+
const d = typeof value === 'string' ? new Date(value) : value;
|
|
20
|
+
if (isNaN(d.getTime()))
|
|
21
|
+
return undefined;
|
|
22
|
+
return d.toISOString();
|
|
23
|
+
}
|
|
24
|
+
function mapGeneroFHIRToAndes(gender) {
|
|
25
|
+
switch (gender) {
|
|
26
|
+
case 'female':
|
|
27
|
+
return 'femenino';
|
|
28
|
+
case 'male':
|
|
29
|
+
return 'masculino';
|
|
30
|
+
case 'other':
|
|
31
|
+
return 'otro';
|
|
32
|
+
default:
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function mapSexoAndesToGenderFHIR(sexo) {
|
|
37
|
+
switch (sexo) {
|
|
38
|
+
case 'femenino':
|
|
39
|
+
return 'female';
|
|
40
|
+
case 'masculino':
|
|
41
|
+
return 'male';
|
|
42
|
+
case 'otro':
|
|
43
|
+
return 'other';
|
|
44
|
+
default:
|
|
45
|
+
return undefined; // unknown
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function buildGenderIdentityExtension(genero) {
|
|
49
|
+
if (!genero) {
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
return [{
|
|
53
|
+
url: 'http://hl7.org/fhir/StructureDefinition/individual-genderIdentity',
|
|
54
|
+
valueCode: genero
|
|
55
|
+
}];
|
|
56
|
+
}
|
|
57
|
+
function buildStatusExtension(estado) {
|
|
58
|
+
const value = estado ?? 'temporal';
|
|
59
|
+
return [{
|
|
60
|
+
url: 'andes.gob.ar/fhir/StructureDefinition/patient-status',
|
|
61
|
+
valueCode: value
|
|
62
|
+
}];
|
|
63
|
+
}
|
|
64
|
+
function mapEstadoCivilAndesToFHIR(estadoCivil) {
|
|
65
|
+
switch (estadoCivil) {
|
|
66
|
+
case 'casado':
|
|
67
|
+
return 'Married';
|
|
68
|
+
case 'divorciado':
|
|
69
|
+
return 'Divorced';
|
|
70
|
+
case 'viudo':
|
|
71
|
+
return 'Widowed';
|
|
72
|
+
case 'soltero':
|
|
73
|
+
return 'unmarried';
|
|
74
|
+
default:
|
|
75
|
+
return 'unknown';
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function mapEstadoCivilFHIRToAndes(estadoCivil) {
|
|
79
|
+
switch (estadoCivil) {
|
|
80
|
+
case 'Married':
|
|
81
|
+
return 'casado';
|
|
82
|
+
case 'Divorced':
|
|
83
|
+
return 'divorciado';
|
|
84
|
+
case 'Widowed':
|
|
85
|
+
return 'viudo';
|
|
86
|
+
case 'unmarried':
|
|
87
|
+
return 'soltero';
|
|
88
|
+
default:
|
|
89
|
+
return 'otro';
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Encode a patient from ANDES to FHIR
|
|
94
|
+
*/
|
|
95
|
+
function encode(patient) {
|
|
96
|
+
if (!patient) {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
// Identificadores
|
|
100
|
+
const identificadores = [];
|
|
101
|
+
// Dominio local (id de ANDES o equivalente)
|
|
102
|
+
identificadores.push({
|
|
103
|
+
system: (0, config_1.getDominio)(),
|
|
104
|
+
value: patient._id ?? patient.id
|
|
105
|
+
});
|
|
106
|
+
if (patient.documento) {
|
|
107
|
+
identificadores.push({
|
|
108
|
+
system: 'http://www.renaper.gob.ar/dni',
|
|
109
|
+
value: patient.documento
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
if (patient.cuil) {
|
|
113
|
+
identificadores.push({
|
|
114
|
+
system: 'http://www.renaper.gob.ar/cuil',
|
|
115
|
+
value: patient.cuil
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
if (patient.numeroIdentificacion) {
|
|
119
|
+
if (patient.tipoIdentificacion === 'dni extranjero') {
|
|
120
|
+
identificadores.push({
|
|
121
|
+
system: 'andes.gob.ar/sid/foreign-id',
|
|
122
|
+
value: patient.numeroIdentificacion
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
if (patient.tipoIdentificacion === 'pasaporte') {
|
|
126
|
+
identificadores.push({
|
|
127
|
+
system: 'andes.gob.ar/sid/passport',
|
|
128
|
+
value: patient.numeroIdentificacion
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// Contactos → telecom
|
|
133
|
+
const contactos = (patient.contacto ?? [])
|
|
134
|
+
.filter((c) => !!c.valor)
|
|
135
|
+
.map((unContacto) => {
|
|
136
|
+
const cont = {
|
|
137
|
+
value: unContacto.valor,
|
|
138
|
+
rank: unContacto.ranking
|
|
139
|
+
};
|
|
140
|
+
switch (unContacto.tipo) {
|
|
141
|
+
case 'fijo':
|
|
142
|
+
case 'celular':
|
|
143
|
+
cont.system = 'phone';
|
|
144
|
+
break;
|
|
145
|
+
case 'email':
|
|
146
|
+
cont.system = 'email';
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
return cont;
|
|
150
|
+
});
|
|
151
|
+
// Direcciones → address
|
|
152
|
+
const direcciones = (patient.direccion ?? [])
|
|
153
|
+
.filter((dir) => !!dir.ubicacion?.localidad)
|
|
154
|
+
.map((unaDireccion) => {
|
|
155
|
+
const city = typeof unaDireccion.ubicacion.localidad === 'object'
|
|
156
|
+
? unaDireccion.ubicacion.localidad?.nombre
|
|
157
|
+
: unaDireccion.ubicacion.localidad;
|
|
158
|
+
const state = typeof unaDireccion.ubicacion.provincia === 'object'
|
|
159
|
+
? unaDireccion.ubicacion.provincia?.nombre
|
|
160
|
+
: unaDireccion.ubicacion.provincia;
|
|
161
|
+
const country = typeof unaDireccion.ubicacion.pais === 'object'
|
|
162
|
+
? unaDireccion.ubicacion.pais?.nombre
|
|
163
|
+
: unaDireccion.ubicacion.pais;
|
|
164
|
+
return {
|
|
165
|
+
postalCode: unaDireccion.codigoPostal ?? '',
|
|
166
|
+
line: [unaDireccion.valor],
|
|
167
|
+
city: city ?? '',
|
|
168
|
+
state: state ?? '',
|
|
169
|
+
country: country ?? ''
|
|
170
|
+
};
|
|
171
|
+
});
|
|
172
|
+
// Relaciones → contact
|
|
173
|
+
const relacionesFHIR = (patient.relaciones ?? [])
|
|
174
|
+
.filter((r) => !!r.relacion)
|
|
175
|
+
.map((unaRelacion) => {
|
|
176
|
+
return {
|
|
177
|
+
relationship: [{
|
|
178
|
+
text: unaRelacion.relacion.nombre
|
|
179
|
+
}],
|
|
180
|
+
name: {
|
|
181
|
+
family: unaRelacion.apellido,
|
|
182
|
+
given: unaRelacion.nombre.split(' ')
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
});
|
|
186
|
+
// Género
|
|
187
|
+
// const generoFHIR = mapGeneroAndesToFHIR(patient.genero ?? patient.sexo);
|
|
188
|
+
const generoFHIR = mapSexoAndesToGenderFHIR(patient.sexo ?? patient.genero);
|
|
189
|
+
const genderIdentityExtensions = buildGenderIdentityExtension(patient.genero);
|
|
190
|
+
const statusExtensions = buildStatusExtension(patient.estado);
|
|
191
|
+
const birthDate = formatDateOnly(patient.fechaNacimiento);
|
|
192
|
+
const extensions = [...statusExtensions, ...genderIdentityExtensions];
|
|
193
|
+
const pacienteFHIR = {
|
|
194
|
+
resourceType: 'Patient',
|
|
195
|
+
id: patient._id ?? patient.id,
|
|
196
|
+
identifier: identificadores,
|
|
197
|
+
active: patient.activo ?? undefined,
|
|
198
|
+
name: [{
|
|
199
|
+
use: 'official',
|
|
200
|
+
family: patient.apellido,
|
|
201
|
+
given: patient.nombre.split(' '),
|
|
202
|
+
text: `${patient.nombre} ${patient.apellido}`
|
|
203
|
+
}],
|
|
204
|
+
gender: generoFHIR,
|
|
205
|
+
birthDate,
|
|
206
|
+
...(extensions.length > 0 && { extension: extensions })
|
|
207
|
+
};
|
|
208
|
+
if (patient.fechaFallecimiento) {
|
|
209
|
+
const deceased = formatDateTime(patient.fechaFallecimiento);
|
|
210
|
+
if (deceased) {
|
|
211
|
+
pacienteFHIR.deceasedDateTime = deceased;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
if (patient.estadoCivil) {
|
|
215
|
+
pacienteFHIR.maritalStatus = {
|
|
216
|
+
text: mapEstadoCivilAndesToFHIR(patient.estadoCivil)
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
if (patient.foto) {
|
|
220
|
+
pacienteFHIR.photo = [{ data: patient.foto }];
|
|
221
|
+
}
|
|
222
|
+
if (contactos.length > 0) {
|
|
223
|
+
pacienteFHIR.telecom = contactos;
|
|
224
|
+
}
|
|
225
|
+
if (direcciones.length > 0) {
|
|
226
|
+
pacienteFHIR.address = direcciones;
|
|
227
|
+
}
|
|
228
|
+
if (relacionesFHIR.length > 0) {
|
|
229
|
+
pacienteFHIR.contact = relacionesFHIR;
|
|
230
|
+
}
|
|
231
|
+
if (patient.createdBy?.organizacion) {
|
|
232
|
+
const managingOrganization = {
|
|
233
|
+
reference: patient.createdBy.organizacion.id,
|
|
234
|
+
display: patient.createdBy.organizacion.nombre
|
|
235
|
+
};
|
|
236
|
+
pacienteFHIR.managingOrganization = managingOrganization;
|
|
237
|
+
}
|
|
238
|
+
return pacienteFHIR;
|
|
239
|
+
}
|
|
240
|
+
exports.encode = encode;
|
|
241
|
+
/**
|
|
242
|
+
* Decode a patient from FHIR to ANDES
|
|
243
|
+
*/
|
|
244
|
+
function decode(patient) {
|
|
245
|
+
let genero;
|
|
246
|
+
let sexo;
|
|
247
|
+
// Cuando el paciente viene por FHIR suponemos el valor del género
|
|
248
|
+
genero = mapGeneroFHIRToAndes(patient.gender);
|
|
249
|
+
sexo = genero;
|
|
250
|
+
function getValue(items, key) {
|
|
251
|
+
if (!items)
|
|
252
|
+
return undefined;
|
|
253
|
+
const element = items.find(el => el.system === key);
|
|
254
|
+
return element?.value;
|
|
255
|
+
}
|
|
256
|
+
const tiposIdentificacion = ['andes.gob.ar/sid/foreign-id', 'andes.gob.ar/sid/passport'];
|
|
257
|
+
const tipoAndes = ['dni extranjero', 'pasaporte'];
|
|
258
|
+
let tipoIdentificacion;
|
|
259
|
+
let numeroIdentificacion;
|
|
260
|
+
for (let i = 0; i < tiposIdentificacion.length; i++) {
|
|
261
|
+
const val = getValue(patient.identifier, tiposIdentificacion[i]);
|
|
262
|
+
if (val) {
|
|
263
|
+
tipoIdentificacion = tipoAndes[i];
|
|
264
|
+
numeroIdentificacion = val;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
const firstName = patient.name?.[0];
|
|
268
|
+
const nombre = firstName
|
|
269
|
+
? (firstName.given ?? []).join(' ').trim()
|
|
270
|
+
: '';
|
|
271
|
+
const apellido = firstName
|
|
272
|
+
? (Array.isArray(firstName.family)
|
|
273
|
+
? firstName.family.join(' ').trim()
|
|
274
|
+
: (firstName.family ?? ''))
|
|
275
|
+
: '';
|
|
276
|
+
const pacienteAndes = {
|
|
277
|
+
id: getValue(patient.identifier, (0, config_1.makeUrl)('Patient')) ?? patient.id,
|
|
278
|
+
documento: getValue(patient.identifier, 'http://www.renaper.gob.ar/dni'),
|
|
279
|
+
tipoIdentificacion,
|
|
280
|
+
numeroIdentificacion,
|
|
281
|
+
nombre,
|
|
282
|
+
apellido,
|
|
283
|
+
fechaNacimiento: patient.birthDate,
|
|
284
|
+
genero,
|
|
285
|
+
sexo,
|
|
286
|
+
estado: 'temporal'
|
|
287
|
+
};
|
|
288
|
+
const contactos = patient.telecom
|
|
289
|
+
? patient.telecom.map((unContacto) => {
|
|
290
|
+
const cont = {
|
|
291
|
+
valor: unContacto.value,
|
|
292
|
+
ranking: unContacto.rank
|
|
293
|
+
};
|
|
294
|
+
switch (unContacto.system) {
|
|
295
|
+
case 'phone':
|
|
296
|
+
cont.tipo = 'celular';
|
|
297
|
+
break;
|
|
298
|
+
case 'email':
|
|
299
|
+
cont.tipo = 'email';
|
|
300
|
+
break;
|
|
301
|
+
}
|
|
302
|
+
return cont;
|
|
303
|
+
})
|
|
304
|
+
: [];
|
|
305
|
+
const relaciones = patient.contact
|
|
306
|
+
? patient.contact.map((aContact) => {
|
|
307
|
+
const relText = aContact.relationship?.[0]?.text ?? '';
|
|
308
|
+
const givenName = (aContact.name?.given ?? []).join(' ').replace(',', ' ');
|
|
309
|
+
const familyName = Array.isArray(aContact.name?.family)
|
|
310
|
+
? (aContact.name?.family).join(' ').replace(',', ' ')
|
|
311
|
+
: aContact.name?.family ?? '';
|
|
312
|
+
return {
|
|
313
|
+
relacion: { nombre: relText },
|
|
314
|
+
nombre: givenName,
|
|
315
|
+
apellido: familyName
|
|
316
|
+
};
|
|
317
|
+
})
|
|
318
|
+
: [];
|
|
319
|
+
if (patient.maritalStatus?.text) {
|
|
320
|
+
pacienteAndes.estadoCivil = mapEstadoCivilFHIRToAndes(patient.maritalStatus.text);
|
|
321
|
+
}
|
|
322
|
+
const direcciones = patient.address
|
|
323
|
+
? patient.address.map((unaDireccion) => {
|
|
324
|
+
const valor = Array.isArray(unaDireccion.line)
|
|
325
|
+
? (unaDireccion.line[0] ?? '')
|
|
326
|
+
: unaDireccion.line ?? '';
|
|
327
|
+
const dir = {
|
|
328
|
+
activo: true,
|
|
329
|
+
valor,
|
|
330
|
+
codigoPostal: unaDireccion.postalCode,
|
|
331
|
+
ubicacion: {
|
|
332
|
+
pais: unaDireccion.country,
|
|
333
|
+
provincia: unaDireccion.state,
|
|
334
|
+
localidad: unaDireccion.city
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
return dir;
|
|
338
|
+
})
|
|
339
|
+
: [];
|
|
340
|
+
pacienteAndes.direccion = direcciones;
|
|
341
|
+
if (patient.active !== undefined) {
|
|
342
|
+
pacienteAndes.activo = patient.active;
|
|
343
|
+
}
|
|
344
|
+
if (contactos.length > 0) {
|
|
345
|
+
pacienteAndes.contacto = contactos;
|
|
346
|
+
}
|
|
347
|
+
if (patient.photo && patient.photo.length > 0) {
|
|
348
|
+
pacienteAndes.foto = patient.photo[0].data;
|
|
349
|
+
}
|
|
350
|
+
if (relaciones.length > 0) {
|
|
351
|
+
pacienteAndes.relaciones = relaciones;
|
|
352
|
+
}
|
|
353
|
+
if (patient.managingOrganization) {
|
|
354
|
+
pacienteAndes.createdBy = {
|
|
355
|
+
organizacion: {
|
|
356
|
+
id: patient.managingOrganization.reference ?? '',
|
|
357
|
+
nombre: patient.managingOrganization.display ?? ''
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
return pacienteAndes;
|
|
362
|
+
}
|
|
363
|
+
exports.decode = decode;
|
|
364
|
+
/**
|
|
365
|
+
* Verify if a patient has a FHIR format
|
|
366
|
+
*/
|
|
367
|
+
function verify(patient) {
|
|
368
|
+
let respuesta = true;
|
|
369
|
+
const fieldVerified = Object.keys(patient).every(pacienteFHIRFields);
|
|
370
|
+
if (fieldVerified) {
|
|
371
|
+
respuesta = ('resourceType' in patient) && patient.resourceType === 'Patient';
|
|
372
|
+
respuesta = respuesta && ('identifier' in patient);
|
|
373
|
+
if (patient.identifier?.length > 0) {
|
|
374
|
+
patient.identifier.forEach((anIdentifier) => {
|
|
375
|
+
respuesta = respuesta && Object.keys(anIdentifier).every(identifierFields);
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
patient.name?.forEach((aName) => {
|
|
379
|
+
respuesta = respuesta && validName(aName);
|
|
380
|
+
});
|
|
381
|
+
if (patient.active !== undefined) {
|
|
382
|
+
respuesta = respuesta && (typeof patient.active === 'boolean');
|
|
383
|
+
}
|
|
384
|
+
if (patient.telecom) {
|
|
385
|
+
patient.telecom.forEach((aTelecom) => {
|
|
386
|
+
respuesta = respuesta && Object.keys(aTelecom).every(telecomFields);
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
if (patient.gender) {
|
|
390
|
+
respuesta = respuesta && patient.gender.match('male|female|other|unknown') != null;
|
|
391
|
+
}
|
|
392
|
+
if (patient.birthDate) {
|
|
393
|
+
respuesta = respuesta && typeof patient.birthDate === 'string';
|
|
394
|
+
}
|
|
395
|
+
if (patient.deceasedDateTime) {
|
|
396
|
+
respuesta = respuesta && typeof patient.deceasedDateTime === 'string';
|
|
397
|
+
}
|
|
398
|
+
if (patient.address) {
|
|
399
|
+
patient.address.forEach((anAddress) => {
|
|
400
|
+
respuesta = respuesta && Object.keys(anAddress).every(addressFields);
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
if (patient.maritalStatus && patient.maritalStatus.text) {
|
|
404
|
+
respuesta = respuesta &&
|
|
405
|
+
Object.keys(patient.maritalStatus).every(codingFields) &&
|
|
406
|
+
typeof patient.maritalStatus.text === 'string' &&
|
|
407
|
+
patient.maritalStatus.text.match('Married|Divorced|Widowed|unmarried|unknown') != null;
|
|
408
|
+
}
|
|
409
|
+
if (patient.photo) {
|
|
410
|
+
patient.photo.forEach((aPhoto) => {
|
|
411
|
+
respuesta = respuesta && Object.keys(aPhoto).every(photoFields);
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
if (patient.contact) {
|
|
415
|
+
patient.contact.forEach((aContact) => {
|
|
416
|
+
if (aContact.relationship) {
|
|
417
|
+
aContact.relationship.forEach((aRelation) => {
|
|
418
|
+
respuesta = respuesta && Object.keys(aRelation).every(codingFields);
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
if (aContact.name) {
|
|
422
|
+
respuesta = respuesta && validName(aContact.name);
|
|
423
|
+
}
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
else {
|
|
428
|
+
respuesta = fieldVerified;
|
|
429
|
+
}
|
|
430
|
+
return respuesta;
|
|
431
|
+
}
|
|
432
|
+
exports.verify = verify;
|
|
433
|
+
/**
|
|
434
|
+
* Verify helpers
|
|
435
|
+
*/
|
|
436
|
+
function pacienteFHIRFields(elem) {
|
|
437
|
+
return elem.match('resourceType|identifier|active|name|telecom|gender|birthDate|deceasedBoolean|deceasedDateTime|address|maritalStatus|photo|contact') != null;
|
|
438
|
+
}
|
|
439
|
+
function identifierFields(elem) {
|
|
440
|
+
return elem.match('use|type|system|value|period|assigner') != null;
|
|
441
|
+
}
|
|
442
|
+
function nameFields(elem) {
|
|
443
|
+
return elem.match('resourceType|family|given') != null;
|
|
444
|
+
}
|
|
445
|
+
function telecomFields(elem) {
|
|
446
|
+
return elem.match('resourceType|system|value|use|rank|period') != null;
|
|
447
|
+
}
|
|
448
|
+
function addressFields(elem) {
|
|
449
|
+
return elem.match('resourceType|use|type|text|line|city|district|state|postalCode|country|period') != null;
|
|
450
|
+
}
|
|
451
|
+
function codingFields(elem) {
|
|
452
|
+
return elem.match('coding|text') != null;
|
|
453
|
+
}
|
|
454
|
+
function photoFields(elem) {
|
|
455
|
+
return elem.match('contentType|language|data|url|size|hash|title|creation') != null;
|
|
456
|
+
}
|
|
457
|
+
function validName(aName) {
|
|
458
|
+
return Object.keys(aName).every(nameFields) &&
|
|
459
|
+
('resourceType' in aName) && aName.resourceType === 'HumanName' &&
|
|
460
|
+
('family' in aName) && Array.isArray(aName.family) && aName.family.every(areStrings) &&
|
|
461
|
+
('given' in aName) && Array.isArray(aName.given) && aName.given.every(areStrings);
|
|
462
|
+
}
|
|
463
|
+
function areStrings(elem) {
|
|
464
|
+
return typeof elem === 'string';
|
|
465
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|