@claudebernard/fhir-mapper
Version:
A simple FHIR / BCB resource mapper to help stay interoperable while still using the Claude Bernard intelligence
919 lines (911 loc) • 38.6 kB
JavaScript
const unitsOfTime = {
's': { value: 0, label: 'seconde' },
'min': { value: 10, label: 'minute' },
'h': { value: 20, label: 'heure' },
'd': { value: 40, label: 'jour' },
'wk': { value: 50, label: 'semaine' },
'mo': { value: 70, label: 'mois' },
'a': { value: 100, label: 'an' }
};
function buildDoseAndRate(dosage) {
const coding = {
system: dosage.codeUnitePrise ? 'https://platform.claudebernard.fr/fhir/ImplementationGuides/ValueSet/intake-units' : undefined,
code: dosage.codeUnitePrise ?? undefined,
unit: dosage.codeUnitePrise ? dosage.libUnitePrise : undefined
};
const doseAndRate = dosage.quantite2 ? {
doseRange: {
low: {
value: dosage.quantite1,
...coding
},
high: {
value: dosage.quantite2,
...coding
}
}
} : {
doseQuantity: {
value: dosage.quantite1,
...coding
}
};
return [doseAndRate];
}
// The formula used here is count = dose * frequency * (duration / period)
function calculateCount(dose, frequency, period, periodUnit, duration, durationUnit) {
// Provide default values for null or undefined parameters
const actualDose = dose ?? 0;
const actualFrequency = frequency ?? 0;
const actualPeriod = period ?? 1;
const actualDuration = duration ?? 0;
const actualPeriodUnit = periodUnit ?? '';
const actualDurationUnit = durationUnit ?? '';
// Convert duration to the same unit as period
const convertedDuration = actualDurationUnit === actualPeriodUnit ? actualDuration : convertTime(actualDuration, actualDurationUnit, actualPeriodUnit);
// Check if dose, frequency, convertedDuration, or period is null or 0 and compute count accordingly
if (!actualDose) {
return actualFrequency * (convertedDuration / actualPeriod);
}
else if (!actualFrequency) {
return actualDose * (convertedDuration / actualPeriod);
}
else if (!convertedDuration) {
return actualDose * actualFrequency * actualPeriod;
}
else if (!actualPeriod) {
return actualDose * actualFrequency * convertedDuration;
}
else {
return actualDose * actualFrequency * (convertedDuration / actualPeriod);
}
}
function convertTime(value, fromUnit, toUnit) {
const conversionFactors = {
's': 1,
'min': 60,
'h': 3600,
'd': 86400,
'wk': 604800,
'mo': 2628000,
'a': 31536000
};
if (!conversionFactors[fromUnit]) {
throw new Error(`Invalid fromUnit: ${fromUnit}`);
}
if (!conversionFactors[toUnit]) {
throw new Error(`Invalid toUnit: ${toUnit}`);
}
// Convert the input value to seconds
const valueInSeconds = value * conversionFactors[fromUnit];
// Convert from seconds to the desired unit
const convertedValue = valueInSeconds / conversionFactors[toUnit];
return convertedValue;
}
async function handleCodifications(dosage, indicationMapper, routeMapper, intakeMapper) {
let conditionCode;
let conditionLabel;
let intakeCode;
let intakeLabel;
let routeCode;
let routeLabel;
let errors = [];
if (dosage.asNeededFor && dosage.asNeededFor[0].coding && dosage.asNeededFor[0].coding.length > 0) {
if (dosage.asNeededFor[0]?.coding?.[0]?.system === 'https://platform.claudebernard.fr/fhir/ImplementationGuides/ValueSet/indications') {
conditionCode = dosage.asNeededFor?.[0]?.coding?.[0]?.code;
conditionLabel = dosage.asNeededFor?.[0]?.coding?.[0]?.display;
}
else if (indicationMapper) {
const result = await indicationMapper(dosage?.asNeededFor?.[0]?.coding?.[0]);
let indication = undefined;
if (Array.isArray(result)) {
if (result.length > 0)
indication = result[0];
}
else if (result) {
indication = result;
}
if (indication) {
conditionCode = indication.code;
conditionLabel = indication.label;
}
else {
conditionCode = undefined;
conditionLabel = undefined;
errors.push({
field: 'codeIndication',
message: 'No mapping found for indication code'
});
}
}
else {
errors.push({
field: 'codeIndication',
message: 'The system used in the asNeededFor coding field is not supported and no custom mapper was provided'
});
}
}
if (dosage.route && dosage.route.coding && dosage.route.coding.length > 0) {
if (dosage.route?.coding?.[0]?.system === 'https://platform.claudebernard.fr/fhir/ImplementationGuides/ValueSet/routes') {
routeCode = parseInt(dosage?.route?.coding?.[0]?.code ?? '');
routeLabel = dosage.route?.coding?.[0]?.display;
}
else if (dosage.route && routeMapper) {
const result = await routeMapper(dosage.route?.coding?.[0]);
let route = undefined;
if (Array.isArray(result)) {
if (result.length > 0)
route = result[0];
}
else if (result) {
route = result;
}
if (route) {
routeCode = parseInt(route.code);
routeLabel = route.label;
}
else {
routeCode = undefined;
routeLabel = undefined;
errors.push({
field: 'codeVoie',
message: 'No mapping found for route code'
});
}
}
else {
errors.push({
field: 'codeVoie',
message: 'The system used in the route coding field is not supported and no custom mapper was provided'
});
}
}
if (dosage.doseAndRate && dosage.doseAndRate.length > 0) {
if (dosage.doseAndRate?.[0]?.doseQuantity?.system === 'https://platform.claudebernard.fr/fhir/ImplementationGuides/ValueSet/intake-units') {
intakeCode = parseInt(dosage.doseAndRate?.[0]?.doseQuantity.code ?? '');
intakeLabel = dosage.doseAndRate?.[0]?.doseQuantity.unit;
}
else if (dosage.doseAndRate && intakeMapper) {
const result = await intakeMapper(dosage.doseAndRate?.[0]?.doseQuantity);
let intake = undefined;
if (Array.isArray(result)) {
if (result.length > 0)
intake = result[0];
}
else if (result) {
intake = result;
}
if (intake) {
intakeCode = parseInt(intake.code);
intakeLabel = intake.label;
}
else {
intakeCode = undefined;
intakeLabel = undefined;
errors.push({
field: 'codeUnitePrise',
message: 'No mapping found for intake code'
});
}
}
else {
errors.push({
field: 'codeUnitePrise',
message: 'The system used in the doseQuantity coding field is not supported and no custom mapper was provided'
});
}
}
return {
conditionCode,
conditionLabel,
intakeCode,
intakeLabel,
routeCode,
routeLabel,
codificationErrors: errors
};
}
// adding this function to support new naming convention without breaking changes
async function fhirToBcb$1(dosageInstructions, indicationMapper, routeMapper, intakeMapper) {
return await mapToBcb(dosageInstructions, indicationMapper, routeMapper, intakeMapper);
}
async function mapToBcb(dosageInstructions, indicationMapper, routeMapper, intakeMapper) {
if (!dosageInstructions || dosageInstructions.length === 0) {
return [
{
result: undefined,
errors: [{
field: 'root',
message: 'Invalid Dosage array'
}]
}
];
}
const safeIndicationMapper = wrapMaybeAsync$1(indicationMapper);
const safeRouteMapper = wrapMaybeAsync$1(routeMapper);
const safeIntakeMapper = wrapMaybeAsync$1(intakeMapper);
const results = [];
for (const dosage of dosageInstructions) {
let errors = [];
const periodUnit = dosage.timing?.repeat?.periodUnit;
const quantite1 = dosage?.doseAndRate?.[0]?.doseQuantity?.value ?? dosage?.doseAndRate?.[0]?.doseRange?.low?.value;
const quantite2 = dosage?.doseAndRate?.[0]?.doseRange?.high?.value ?? null;
let maximum = null;
let codeDuree3 = null;
let maxDosePerDay = null;
if (dosage.maxDosePerPeriod) {
maxDosePerDay = dosage.maxDosePerPeriod.find(period => period?.denominator?.unit === 'd' && period?.denominator?.value === 1);
if (maxDosePerDay) {
maximum = maxDosePerDay?.numerator?.value;
codeDuree3 = maxDosePerDay?.denominator?.unit;
}
}
let libSpecifPrise1 = dosage.asNeeded ? 'selon besoin' : null;
const doseSpacing = dosage.maxDosePerPeriod?.find(period => period?.denominator?.unit === 'h');
if (doseSpacing) {
const spacingText = `en espaçant les prises de ${doseSpacing?.denominator?.value}h minimum`;
libSpecifPrise1 = libSpecifPrise1 ? `${libSpecifPrise1}, ${spacingText}` : spacingText;
}
// handleCodifications must be updated to support async mappers
const { conditionCode, conditionLabel, intakeCode, intakeLabel, routeCode, routeLabel, codificationErrors } = await handleCodifications(dosage, safeIndicationMapper, safeRouteMapper, safeIntakeMapper);
const codeDuree2 = dosage?.timing?.repeat?.boundsDuration?.code;
const bcbDosage = {
noPosologie: dosage.sequence ?? undefined,
libellePosologie: dosage.text ?? undefined,
codeIndication: conditionCode,
libIndication: conditionLabel,
codeDuree1: periodUnit ? unitsOfTime[periodUnit]?.value : undefined,
libDuree1: periodUnit ? unitsOfTime[periodUnit]?.label : undefined,
quantite1: quantite1 ?? undefined,
quantite2: quantite2 ?? undefined,
tousLes: dosage.timing?.repeat?.period ?? undefined,
combien1: dosage.timing?.repeat?.frequency ?? undefined,
combien2: dosage.timing?.repeat?.frequencyMax ?? undefined,
pendant1: dosage.timing?.repeat?.boundsDuration?.value ?? undefined,
codeUnitePrise: intakeCode,
libUnitePrise: intakeLabel,
codeVoie: routeCode,
libVoie: routeLabel,
codeDuree2: codeDuree2 ? unitsOfTime[codeDuree2]?.value : undefined,
libDuree2: codeDuree2 ? unitsOfTime[codeDuree2]?.label : undefined,
maximum: maximum ?? undefined,
codeDuree3: codeDuree3 ? unitsOfTime[codeDuree3]?.value : undefined,
libDuree3: codeDuree3 ? unitsOfTime[codeDuree3]?.label : undefined,
libSpecifPrise1: libSpecifPrise1 ?? undefined
};
results.push({
result: bcbDosage,
errors: codificationErrors.concat(errors)
});
}
return results;
}
// adding this function to support new naming convention without breaking changes
function bcbToFhir$1(bcbDosages) {
return mapToFhir(bcbDosages);
}
function mapToFhir(bcbDosages) {
if (!bcbDosages || bcbDosages.length === 0) {
throw new Error('Invalid BCBPosologieStructuree2 array');
}
return bcbDosages.map((dosage) => {
const keys = Object.keys(unitsOfTime);
const periodUnit = keys.find(key => unitsOfTime[key].value === dosage.codeDuree1) ?? undefined;
const durationUnit = keys.find(key => unitsOfTime[key].value === dosage.codeDuree2) ?? undefined;
const maximumUnit = keys.find(key => unitsOfTime[key].value === dosage.codeDuree3) ?? undefined;
const route = dosage.codeVoie ? {
coding: [{
system: 'https://platform.claudebernard.fr/fhir/ImplementationGuides/ValueSet/routes',
code: dosage.codeVoie.toString(),
display: dosage.libVoie
}]
} : undefined;
const intake = dosage.codeUnitePrise ? {
coding: [{
system: 'https://platform.claudebernard.fr/fhir/ImplementationGuides/ValueSet/intake-units',
code: dosage.codeUnitePrise.toString(),
display: dosage.libUnitePrise
}]
} : undefined;
const indication = dosage.codeIndication ? [{
coding: [{
system: 'https://platform.claudebernard.fr/fhir/ImplementationGuides/ValueSet/indications',
code: dosage.codeIndication,
display: dosage.libIndication
}]
}] : undefined;
const maxDosePerPeriod = dosage.maximum ? [{
numerator: {
value: dosage.maximum,
system: 'https://platform.claudebernard.fr/fhir/ImplementationGuides/ValueSet/intake-units',
code: intake?.coding?.[0]?.code,
unit: intake?.coding?.[0]?.display
},
denominator: {
value: 1,
unit: maximumUnit
}
}] : undefined;
const doseAndRate = buildDoseAndRate(dosage);
const boundsDuration = dosage.pendant1 ? { value: dosage.pendant1, unit: durationUnit } : undefined;
const asNeeded = dosage.libSpecifPrise1?.includes('besoin');
return {
result: {
text: dosage.libellePosologie,
sequence: dosage.noPosologie,
doseAndRate: doseAndRate,
maxDosePerPeriod: dosage.maximum ? maxDosePerPeriod : undefined,
route: route,
asNeeded: asNeeded,
asNeededFor: indication,
timing: {
repeat: {
frequency: dosage.combien1,
frequencyMax: dosage.combien2,
period: dosage.tousLes,
periodUnit: periodUnit,
boundsDuration: boundsDuration,
count: calculateCount(dosage.quantite1, dosage.combien1, dosage.tousLes, periodUnit, dosage.pendant1, durationUnit),
countMax: calculateCount(dosage.quantite2 ?? dosage.quantite1, dosage.combien2 ?? dosage.combien1, dosage.tousLes, periodUnit, dosage.pendant2 ?? dosage.pendant1, durationUnit)
}
}
},
errors: []
};
});
}
// Utility to wrap a possibly sync or async function so it always returns a Promise
function wrapMaybeAsync$1(fn) {
if (!fn)
return undefined;
return ((...args) => Promise.resolve(fn(...args)));
}
var dosageMapper = /*#__PURE__*/Object.freeze({
__proto__: null,
bcbToFhir: bcbToFhir$1,
fhirToBcb: fhirToBcb$1,
mapToBcb: mapToBcb,
mapToFhir: mapToFhir
});
const genderMap = {
male: 'M',
female: 'F'
};
const fhirGenderMap = {
M: 'male',
F: 'female'
};
const hepaticInsufficiencyMap = {
'A': { code: 'K72.90', display: 'Mild hepatic insufficiency' },
'B': { code: 'K72.91', display: 'Moderate hepatic insufficiency' },
'C': { code: 'K72.92', display: 'Severe hepatic insufficiency' }
};
const hepaticInsufficiencyReverseMap = {
'K72.90': 'A',
'K72.91': 'B',
'K72.92': 'C'
};
const calculateAgeInMonths = (birthDate) => {
const birth = new Date(birthDate);
const today = new Date();
const yearsDifference = today.getFullYear() - birth.getFullYear();
const monthsDifference = today.getMonth() - birth.getMonth();
const daysDifference = today.getDate() - birth.getDate();
let ageInMonths = yearsDifference * 12 + monthsDifference;
if (daysDifference < 0) {
ageInMonths--;
}
return ageInMonths;
};
const calculateBirthDateFromAgeInMonths = (ageInMonths) => {
const today = new Date();
const birthDate = new Date(today.setMonth(today.getMonth() - ageInMonths));
const year = birthDate.getFullYear();
const month = (birthDate.getMonth() + 1).toString().padStart(2, '0');
const day = birthDate.getDate().toString().padStart(2, '0');
return `${year}-${month}-${day}`;
};
const convertPatient = (bcbPatient) => {
return {
firstName: '',
lastName: '',
age: bcbPatient.age,
gender: bcbPatient.sexe,
weight: bcbPatient.poids,
pregnancy: bcbPatient.grossesse === 1,
breastfeeding: bcbPatient.allaitement === 1,
ammPathologies: bcbPatient.lstPathologiesAMM.map(code => ({
type: 'AMM',
code: code,
label: ''
})),
allergies: bcbPatient.lstIdComposantAllergie.map(code => ({
code: code,
label: ''
})),
hepaticStage: bcbPatient.insuffisanceHepatique,
height: bcbPatient.taille,
weeksOfPregnancy: 0,
creatinineClearance: bcbPatient.clairanceCreatinine,
molCreatinine: bcbPatient.creatininemieMol,
mglCreatinine: bcbPatient.creatininemieMg,
gfr: 0,
medicalTeam: {
firstNameDoctor: "",
lastNameDoctor: "",
firstNameSpecialist: "",
lastNameSpecialist: "",
firstNamePharmacist: "",
lastNamePharmacist: "",
firstNameNurse: "",
lastNameNurse: ""
}
};
};
const sortEntries = async (target, entries, allergiesMapper, snomedPathologiesMapper, errors = []) => {
const patientEntry = entries.find(entry => entry?.resource?.resourceType === "Patient")?.resource;
const observationEntries = entries.filter(entry => entry?.resource?.resourceType === "Observation").map(entry => entry?.resource);
const conditionEntries = entries.filter(entry => entry?.resource?.resourceType === "Condition").map(entry => entry?.resource);
const allergyIntoleranceEntries = entries.filter(entry => entry?.resource?.resourceType === "AllergyIntolerance").map(entry => entry?.resource);
const weightObservation = observationEntries.find(entry => entry?.code?.coding?.[0]?.code === '29463-7');
const heightObservation = observationEntries.find(entry => entry?.code?.coding?.[0]?.code === '8302-2');
const pregnancyObservation = observationEntries.find(entry => entry?.code?.coding?.some(coding => coding.code === '82810-3'));
const breastfeedingObservation = observationEntries.find(entry => entry?.code?.coding?.some(coding => coding.code === '63895-7'));
const hepaticCondition = conditionEntries.find(entry => entry?.code?.coding?.some(coding => coding.code === '2164-2' || coding.code === '14682-9' || coding.code === '2160-0'));
let lstIdComposantAllergie = [];
let errorField = target === 'bcb' ? 'lstIdComposantAllergie' : 'allergies';
for (const allergyEntry of allergyIntoleranceEntries) {
if (allergiesMapper) {
const mappingResult = await allergiesMapper(allergyEntry?.code?.coding?.[0]);
if (Array.isArray(mappingResult)) {
if (mappingResult.length > 0) {
lstIdComposantAllergie.push(...mappingResult);
}
else {
errors.push({
field: errorField,
message: `No mapping found for allergy code ${allergyEntry?.code?.coding?.[0]?.code}`
});
}
}
else if (mappingResult) {
lstIdComposantAllergie.push(mappingResult);
}
else {
errors.push({
field: errorField,
message: `No mapping found for allergy code ${allergyEntry?.code?.coding?.[0]?.code}`
});
}
}
else {
errors.push({
field: errorField,
message: 'Allergy Intolerance values were found but no allergies mapper was provided, the output field is thus left empty.'
});
}
}
const cim10PathologiesEntries = conditionEntries.filter(entry => entry?.code?.coding?.some(coding => coding.system === 'http://hl7.org/fhir/sid/icd-10')).map(pathology => ({ code: pathology?.code?.coding?.[0]?.code || '', label: '' }));
const snomedPathologiesEntries = conditionEntries.filter(entry => entry?.code?.coding?.some(coding => coding.system === 'http://snomed.info/sct'));
const ammPathologiesEntries = conditionEntries.filter(entry => entry?.code?.coding?.some(coding => coding.system === 'https://platform.claudebernard.fr/fhir/ImplementationGuides/CodeSystem/amm')).map(pathology => ({ code: pathology?.code?.coding?.[0]?.code || '', label: '' }));
let lstPathologies = [];
lstPathologies.push(...ammPathologiesEntries);
let lstCim10Pathologies = [];
lstCim10Pathologies.push(...cim10PathologiesEntries);
errorField = target === 'bcb' ? 'lstPathologiesAMM' : 'ammPathologies';
for (const pathology of snomedPathologiesEntries) {
if (snomedPathologiesMapper) {
const mappingResult = await snomedPathologiesMapper(pathology?.code?.coding?.[0]);
if (Array.isArray(mappingResult)) {
if (mappingResult.length > 0) {
lstPathologies.push(...mappingResult);
}
else {
errors.push({
field: errorField,
message: `No mapping found for snomed code ${pathology?.code?.coding?.[0]?.code}`
});
}
}
else if (mappingResult) {
lstPathologies.push(mappingResult);
}
else {
errors.push({
field: errorField,
message: `No mapping found for snomed code ${pathology?.code?.coding?.[0]?.code}`
});
}
}
else {
errors.push({
field: errorField,
message: "Snomed pathologies were found but no mapper was provided, the output field is thus left empty."
});
}
}
const clairanceCreatinineObservation = observationEntries.find(entry => entry?.code?.coding?.some(coding => coding.code === '2164-2'));
const creatininemieMolObservation = observationEntries.find(entry => entry?.code?.coding?.some(coding => coding.code === '14682-9'));
const creatininemieMgObservation = observationEntries.find(entry => entry?.code?.coding?.some(coding => coding.code === '2160-0'));
return {
patientEntry,
weightObservation,
heightObservation,
pregnancyObservation,
breastfeedingObservation,
hepaticCondition,
lstIdComposantAllergie,
lstCim10Pathologies,
lstPathologies,
clairanceCreatinineObservation,
creatininemieMolObservation,
creatininemieMgObservation
};
};
const patientToFhir = (patientData) => {
if (!patientData) {
return {
result: undefined,
errors: [{
field: 'root',
message: 'Invalid patient object'
}]
};
}
let errors = [];
const bundleEntries = [];
const patient = {
resourceType: 'Patient',
id: '1',
birthDate: calculateBirthDateFromAgeInMonths(patientData.age),
gender: fhirGenderMap[patientData.gender] || 'unknown'
};
const weightObservation = {
resourceType: 'Observation',
id: '2',
code: {
coding: [{
code: '29463-7',
display: 'Body Weight'
}]
},
status: 'final',
valueQuantity: {
value: patientData.weight,
unit: 'kg'
},
subject: {
reference: 'Patient/1'
}
};
const heightObservation = {
resourceType: 'Observation',
id: '3',
code: {
coding: [{
code: '8302-2',
display: 'Body Height'
}]
},
status: 'final',
valueQuantity: {
value: patientData.height,
unit: 'cm'
},
subject: {
reference: 'Patient/1'
}
};
let pregnancyObservation = undefined;
if (patientData.pregnancy) {
pregnancyObservation = {
resourceType: 'Observation',
id: '4',
code: {
coding: [{
code: '82810-3',
display: 'Pregnancy Status'
}]
},
status: 'final',
subject: {
reference: 'Patient/1'
}
};
}
if (pregnancyObservation && patientData.weeksOfPregnancy > 0) {
pregnancyObservation.valueQuantity = {
value: patientData.weeksOfPregnancy,
unit: 'weeks',
system: 'http://unitsofmeasure.org',
code: 'wk'
};
}
let breastfeedingObservation = undefined;
if (patientData.breastfeeding) {
breastfeedingObservation = {
resourceType: 'Observation',
id: '5',
code: {
coding: [{
code: '63895-7',
display: 'Breastfeeding Status'
}]
},
status: 'final',
subject: {
reference: 'Patient/1'
}
};
}
let creatinineClearanceObservation;
if (patientData.creatinineClearance > 0) {
creatinineClearanceObservation = {
resourceType: 'Observation',
id: '5',
code: {
coding: [{
system: 'http://loinc.org',
code: '2164-2',
display: 'Creatinine clearance'
}]
},
status: 'final',
valueQuantity: {
value: patientData.creatinineClearance,
unit: 'mL/min'
},
subject: {
reference: 'Patient/1'
}
};
}
let creatinineMolObservation;
if (patientData.molCreatinine > 0) {
creatinineMolObservation = {
resourceType: 'Observation',
id: '6',
code: {
coding: [{
system: 'http://loinc.org',
code: '14682-9',
display: 'Creatinine [Moles/volume] in Serum or Plasma'
}]
},
status: 'final',
valueQuantity: {
value: patientData.molCreatinine,
unit: 'mmol/L'
},
subject: {
reference: 'Patient/1'
}
};
}
let creatinineMgObservation;
if (patientData.mglCreatinine > 0) {
creatinineMgObservation = {
resourceType: 'Observation',
id: '7',
code: {
coding: [{
system: 'http://loinc.org',
code: '2160-0',
display: 'Creatinine [Mass/volume] in Serum or Plasma'
}]
},
status: 'final',
valueQuantity: {
value: patientData.mglCreatinine,
unit: 'mg/dL'
},
subject: {
reference: 'Patient/1'
}
};
}
let conditions = [];
if (patientData.ammPathologies) {
conditions = patientData.ammPathologies.map((pathology) => {
return {
resourceType: 'Condition',
id: pathology.code,
code: {
coding: [{
system: 'https://platform.claudebernard.fr/fhir/ImplementationGuides/CodeSystem/amm',
code: pathology.code,
display: pathology.label // TODO : find a way to add the label
}]
},
clinicalStatus: {
coding: [{
system: 'http://terminology.hl7.org/CodeSystem/condition-clinical',
code: 'active',
display: 'Active'
}]
},
subject: {
reference: 'Patient/1'
}
};
});
}
let hepaticInsufficiencyCondition;
if (patientData.hepaticStage && hepaticInsufficiencyMap[patientData.hepaticStage]) {
const stage = hepaticInsufficiencyMap[patientData.hepaticStage];
hepaticInsufficiencyCondition = {
resourceType: 'Condition',
id: 'hepatic-insufficiency',
code: {
coding: [{
system: 'http://hl7.org/fhir/sid/icd-10',
code: stage.code,
display: stage.display
}]
},
clinicalStatus: {
coding: [{
system: 'http://terminology.hl7.org/CodeSystem/condition-clinical',
code: 'active',
display: 'Active'
}]
},
verificationStatus: {
coding: [{
system: 'http://terminology.hl7.org/CodeSystem/condition-ver-status',
code: 'confirmed',
display: 'Confirmed'
}]
},
subject: {
reference: 'Patient/1'
}
};
}
let allergies = [];
if (patientData.allergies) {
allergies = patientData.allergies.map((allergy) => {
return {
resourceType: 'AllergyIntolerance',
id: allergy.code.toString(),
patient: {
reference: 'Patient/1'
},
code: {
coding: [{
system: 'https://platform.claudebernard.fr/fhir/ImplementationGuides/CodeSystem/allergies',
code: allergy.code.toString(),
display: allergy.label.toString() // TODO : find a way to add the label
}]
},
clinicalStatus: {
coding: [{
system: 'http://terminology.hl7.org/CodeSystem/condition-clinical',
code: 'active',
display: 'Active'
}]
},
};
});
}
bundleEntries.push(...[
{ resource: patient },
...(weightObservation ? [{ resource: weightObservation }] : []),
...(heightObservation ? [{ resource: heightObservation }] : []),
...(pregnancyObservation ? [{ resource: pregnancyObservation }] : []),
...(breastfeedingObservation ? [{ resource: breastfeedingObservation }] : []),
...(creatinineClearanceObservation ? [{ resource: creatinineClearanceObservation }] : []),
...(creatinineMolObservation ? [{ resource: creatinineMolObservation }] : []),
...(creatinineMgObservation ? [{ resource: creatinineMgObservation }] : []),
...conditions.map(condition => ({ resource: condition })),
...allergies.map(allergy => ({ resource: allergy })),
...(hepaticInsufficiencyCondition ? [{ resource: hepaticInsufficiencyCondition }] : []),
]);
return {
result: bundleEntries,
errors: errors
};
};
/* ================== FHIR <=> BCB ================== */
function bcbToFhir(bcbPatient) {
return patientToFhir(convertPatient(bcbPatient));
}
async function fhirToBcb(fhirPatient, allergiesMapper, snomedPathologiesMapper) {
if (!fhirPatient || fhirPatient.length === 0) {
return {
result: undefined,
errors: [{
field: 'root',
message: 'Invalid Patient array'
}]
};
}
let errors = [];
const { patientEntry, weightObservation, heightObservation, pregnancyObservation, breastfeedingObservation, hepaticCondition, lstIdComposantAllergie, lstCim10Pathologies, lstPathologies, clairanceCreatinineObservation, creatininemieMolObservation, creatininemieMgObservation } = await sortEntries('bcb', fhirPatient, wrapMaybeAsync(allergiesMapper), wrapMaybeAsync(snomedPathologiesMapper), errors);
const bcbPatient = {
lstIdComposantAllergie: lstIdComposantAllergie.map(codification => Number(codification.code)),
lstPathologiesCIM10: lstCim10Pathologies.map(codification => codification.code),
lstPathologiesAMM: lstPathologies.map(codification => codification.code),
age: patientEntry && patientEntry.birthDate ? calculateAgeInMonths(patientEntry.birthDate) : 0,
poids: weightObservation && weightObservation?.valueQuantity?.value ? weightObservation?.valueQuantity?.value : 0,
taille: heightObservation && heightObservation?.valueQuantity?.value ? heightObservation?.valueQuantity?.value : 0,
grossesse: pregnancyObservation ? 1 : 0,
allaitement: breastfeedingObservation ? 1 : 0,
sexe: patientEntry?.gender ? genderMap[patientEntry.gender] || '' : '',
clairanceCreatinine: clairanceCreatinineObservation && clairanceCreatinineObservation?.valueQuantity?.value ? clairanceCreatinineObservation?.valueQuantity?.value : 0,
creatininemieMol: creatininemieMolObservation && creatininemieMolObservation?.valueQuantity?.value ? creatininemieMolObservation?.valueQuantity?.value : 0,
creatininemieMg: creatininemieMgObservation && creatininemieMgObservation?.valueQuantity?.value ? creatininemieMgObservation?.valueQuantity?.value : 0,
insuffisanceHepatique: hepaticCondition?.code?.coding?.[0]?.code ? hepaticInsufficiencyReverseMap[hepaticCondition.code.coding[0].code] : '',
};
return {
result: bcbPatient,
errors: errors
};
}
/* ================== FHIR <=> CB ================== */
function cbToFhir(cbPatient) {
return patientToFhir(cbPatient);
}
async function fhirToCb(fhirPatient, allergiesMapper, snomedPathologiesMapper) {
if (!fhirPatient || fhirPatient.length === 0) {
return {
result: undefined,
errors: [{
field: 'root',
message: 'Invalid Patient array'
}]
};
}
let errors = [];
const { patientEntry, weightObservation, heightObservation, pregnancyObservation, breastfeedingObservation, hepaticCondition, lstIdComposantAllergie, lstCim10Pathologies, lstPathologies, clairanceCreatinineObservation, creatininemieMolObservation, creatininemieMgObservation } = await sortEntries('cb', fhirPatient, wrapMaybeAsync(allergiesMapper), wrapMaybeAsync(snomedPathologiesMapper), errors);
const pathologies = [
...lstPathologies.map(codification => ({ type: 'AMM', code: codification.code, label: codification.label })),
...lstCim10Pathologies.map(codification => ({ type: 'CIM10', code: codification.code, label: codification.label }))
];
return {
result: {
firstName: '',
lastName: '',
age: patientEntry && patientEntry.birthDate ? calculateAgeInMonths(patientEntry.birthDate) : 0,
gender: patientEntry?.gender ? genderMap[patientEntry.gender] || '' : '',
weight: weightObservation && weightObservation?.valueQuantity?.value ? weightObservation?.valueQuantity?.value : 0,
pregnancy: pregnancyObservation ? true : false,
breastfeeding: breastfeedingObservation ? true : false,
ammPathologies: pathologies,
allergies: lstIdComposantAllergie.map(codification => ({ code: Number(codification.code), label: codification.label })),
hepaticStage: hepaticCondition?.code?.coding?.[0]?.code || '',
height: heightObservation && heightObservation?.valueQuantity?.value ? heightObservation?.valueQuantity?.value : 0,
weeksOfPregnancy: pregnancyObservation && pregnancyObservation?.valueQuantity?.value ? pregnancyObservation?.valueQuantity?.value : 0,
creatinineClearance: clairanceCreatinineObservation && clairanceCreatinineObservation?.valueQuantity?.value ? clairanceCreatinineObservation?.valueQuantity?.value : 0,
molCreatinine: creatininemieMolObservation && creatininemieMolObservation?.valueQuantity?.value ? creatininemieMolObservation?.valueQuantity?.value : 0,
mglCreatinine: creatininemieMgObservation && creatininemieMgObservation?.valueQuantity?.value ? creatininemieMgObservation?.valueQuantity?.value : 0,
gfr: 0,
medicalTeam: {
firstNameDoctor: "",
lastNameDoctor: "",
firstNameSpecialist: "",
lastNameSpecialist: "",
firstNamePharmacist: "",
lastNamePharmacist: "",
firstNameNurse: "",
lastNameNurse: ""
}
},
errors: errors
};
}
// Utility to wrap a possibly sync or async function so it always returns a Promise
function wrapMaybeAsync(fn) {
if (!fn)
return undefined;
return ((...args) => Promise.resolve(fn(...args)));
}
var patientMapper = /*#__PURE__*/Object.freeze({
__proto__: null,
bcbToFhir: bcbToFhir,
cbToFhir: cbToFhir,
fhirToBcb: fhirToBcb,
fhirToCb: fhirToCb
});
export { dosageMapper, patientMapper };
//# sourceMappingURL=index.js.map