Narratif généré : StructureMap EHR2FSL #xD; map "https://interop.aphp.fr/ig/fhir/dm/StructureMap/EHR2FSL" = "EHR2FSL"#xD; #xD; // Transforms EHR logical model data to FHIR Semantic Layer resources using Bundle as container#xD; #xD; uses "https://interop.aphp.fr/ig/fhir/dm/StructureDefinition/ehr" alias EHR as source#xD; uses "http://hl7.org/fhir/StructureDefinition/Bundle" alias Bundle as target#xD; #xD; group EHR2FSL(source src : EHR, target bundle : Bundle) {#xD; // Initialize Bundle#xD; src -> bundle.id = uuid() "bundleid";#xD; src -> bundle.type = 'collection' "bundletype";#xD; src -> (now()) as timestamp then {#xD; src -> bundle.timestamp = timestamp "affectTimestamp";#xD; } "setTimestamp";#xD; // Transform Patient#xD; src.patient as patient -> bundle.entry as patientEntry then {#xD; patient -> patientEntry.resource = create('Patient') as dmPatient then {#xD; patient then TransformPatient(patient, dmPatient) "transformpatient";#xD; patient then setEntryRequestAndFullUrl(dmPatient, patientEntry) "setRequestAndFullUrl";#xD; src.patientAdresse as patientAdresse where patientAdresse.patientId = %patient.patientId then setAddress(patientAdresse, dmPatient) "transformpatientAdresse";#xD; // Transform PMSI Encounters#xD; src.donneesPmsi as pmsi where pmsi.patientId = %patient.patientId -> bundle.entry as encounterEntry then {#xD; pmsi -> encounterEntry.resource = create('Encounter') as dmEncounter then {#xD; pmsi then TransformEncounter(pmsi, dmEncounter, dmPatient) "transformencounter";#xD; pmsi then setEntryRequestAndFullUrl(dmEncounter, encounterEntry) "setRequestAndFullUrl";#xD; // Transform Conditions (Diagnostics)#xD; src.diagnostics as diag where (diag.patientId = %patient.patientId) and (diag.pmsiId = %pmsi.pmsiId) -> bundle.entry as conditionEntry then {#xD; diag -> conditionEntry.resource = create('Condition') as dmCondition then {#xD; diag then TransformCondition(diag, dmCondition, dmPatient, dmEncounter) "transformcondition";#xD; diag then setEntryRequestAndFullUrl(dmCondition, conditionEntry) "setRequestAndFullUrl";#xD; } "createcondition";#xD; } "conditionentries";#xD; // Transform Procedures (Actes)#xD; src.actes as acte where (acte.patientId = %patient.patientId) and (acte.pmsiId = %pmsi.pmsiId) -> bundle.entry as procedureEntry then {#xD; acte -> procedureEntry.resource = create('Procedure') as dmProcedure then {#xD; acte then TransformProcedure(acte, dmProcedure, dmPatient, dmEncounter) "transformprocedure";#xD; acte then setEntryRequestAndFullUrl(dmProcedure, procedureEntry) "setRequestAndFullUrl";#xD; } "createprocedure";#xD; } "procedureentries";#xD; } "createencounter";#xD; } "encounterentries";#xD; // Transform Laboratory Observations#xD; src.biologie as lab where lab.patientId = %patient.patientId -> bundle.entry as labEntry then {#xD; lab -> labEntry.resource = create('Observation') as dmLab then {#xD; lab then TransformLabObservation(lab, dmLab, dmPatient) "transformLabObs";#xD; lab then setEntryRequestAndFullUrl(dmLab, labEntry) "setRequestAndFullUrl";#xD; } "createlabobs";#xD; } "labentries";#xD; // Transform Medication Requests#xD; src.prescription as pres where pres.patientId = %patient.patientId -> bundle.entry as medReqEntry then {#xD; pres -> medReqEntry.resource = create('MedicationRequest') as dmMedReq then {#xD; src then TransformMedicationRequest(pres, dmMedReq, dmPatient) "createmedrequest";#xD; pres then setEntryRequestAndFullUrl(dmMedReq, medReqEntry) "setRequestAndFullUrl";#xD; src.posologie as poso where %poso.prescriptionId = %pres.prescriptionId then setPoso(poso, pres, dmMedReq) "transformPoso";#xD; // Transform Administration (W prescription)#xD; src.administration as admin where (admin.patientId = %patient.patientId) and (admin.prescriptionId = pres.prescriptionId) -> bundle.entry as adminEntry then {#xD; admin -> adminEntry.resource = create('MedicationAdministration') as dmAdmin then {#xD; admin then TransformMedicationAdministrationWOrder(admin, dmAdmin, dmPatient, dmMedReq) "createmedadmin";#xD; admin then setEntryRequestAndFullUrl(dmAdmin, adminEntry) "setRequestAndFullUrl";#xD; } "createmedadmin";#xD; } "administrationentries";#xD; } "createmedreq";#xD; } "medicationentries";#xD; // Transform Lifestyle Observations - separate observation for each lifestyle element#xD; src.styleVie as lifestyle where lifestyle.patientId = %patient.patientId then {#xD; // Tobacco consumption observation#xD; lifestyle.consommationTabac as tobacco where tobacco.exists() -> bundle.entry as tobaccoEntry then {#xD; tobacco -> tobaccoEntry.resource = create('Observation') as tobaccoObs then {#xD; lifestyle then TransformTobaccoObservation(lifestyle, tobaccoObs, dmPatient) "createtobaccoobs";#xD; lifestyle then setEntryRequestAndFullUrl(tobaccoObs, tobaccoEntry) "setRequestAndFullUrl";#xD; } "createtobaccoobs";#xD; } "tobaccoentries";#xD; // Alcohol consumption observation#xD; lifestyle.consommationAlcool as alcohol where alcohol.exists() -> bundle.entry as alcoholEntry then {#xD; alcohol -> alcoholEntry.resource = create('Observation') as alcoholObs then {#xD; lifestyle then TransformAlcoholObservation(lifestyle, alcoholObs, dmPatient) "createalcoholobs";#xD; lifestyle then setEntryRequestAndFullUrl(alcoholObs, alcoholEntry) "setRequestAndFullUrl";#xD; } "createalcoholobs";#xD; } "alcoholentries";#xD; // Drug consumption observation#xD; lifestyle.consommationAutresDrogues as drugs where drugs.exists() -> bundle.entry as drugEntry then {#xD; drugs -> drugEntry.resource = create('Observation') as drugObs then {#xD; lifestyle then TransformDrugObservation(lifestyle, drugObs, dmPatient) "createdrugobs";#xD; lifestyle then setEntryRequestAndFullUrl(drugObs, drugEntry) "setRequestAndFullUrl";#xD; } "createdrugobs";#xD; } "drugentries";#xD; // Physical activity observation#xD; lifestyle.activitePhysique as physical where physical.exists() -> bundle.entry as physicalEntry then {#xD; physical -> physicalEntry.resource = create('Observation') as physicalObs then {#xD; lifestyle then TransformPhysicalActivityObservation(lifestyle, physicalObs, dmPatient) "createphysicalobs";#xD; lifestyle then setEntryRequestAndFullUrl(physicalObs, physicalEntry) "setRequestAndFullUrl";#xD; } "createphysicalobs";#xD; } "physicalentries";#xD; } "lifestyleentries";#xD; // Transform Care Observations (s)#xD; src.dossierSoins as soin where soin.patientId = %patient.patientId -> bundle.entry as vitalEntry then {#xD; soin -> vitalEntry.resource = create('Observation') as dmObs then {#xD; soin then TransformVitalSigns(soin, dmObs, dmPatient) "createmesureobs";#xD; soin then setEntryRequestAndFullUrl(dmObs, vitalEntry) "setRequestAndFullUrl";#xD; } "createmesureobs";#xD; } "vitalentries";#xD; // Transform Administration (WO prescription)#xD; src.administration as admin where (admin.patientId = %patient.patientId) and admin.prescriptionId.empty() -> bundle.entry as adminEntry then {#xD; admin -> adminEntry.resource = create('MedicationAdministration') as dmAdmin then {#xD; admin then TransformMedicationAdministrationWOOrder(admin, dmAdmin, dmPatient) "createmedadmin";#xD; admin then setEntryRequestAndFullUrl(dmAdmin, adminEntry) "setRequestAndFullUrl";#xD; } "createmedadmin";#xD; } "administrationentries";#xD; } "createpatient";#xD; } "patiententry";#xD; }#xD; #xD; // ---------------------------------------------------------#xD; // PATIENT TRANSFORMATION#xD; // ---------------------------------------------------------#xD; group TransformPatient(source src, target tgt : Patient) {#xD; src -> tgt.id = uuid() "setId";#xD; // src.patientId as id -> tgt.id = id "patient-id";#xD; // Identifiers#xD; src.patientId as pid -> tgt.identifier as identifier then {#xD; pid -> identifier.use = 'usual' "iduse";#xD; pid -> identifier.type = cc('http://terminology.hl7.org/CodeSystem/v2-0203', 'PI', 'Patient Identifier') "idtype";#xD; pid -> identifier.system = 'https://hospital.eu/ehr/patient-id' "idsystem";#xD; pid -> identifier.value = pid "idvalue";#xD; } "patientidentifier";#xD; // NIR identifier <- C'est pas terrible côté expression de besoin : qu'est ce qu'ils veulent quand ils disent NIR ? ça peut être le NSS, mais du coup c'est pas un identifier (un NSS peut correspondre à plusieurs ayant droits), et ce serait plutôt une info qui irait côté claim. ça peut aussi être l'ins-nir, mais du coup qu'est ce qu'ils attendent dans l'ins ? Bref, à mon avis, faut discuter cette variable au niveau du GT.#xD; // src.nir as nir where nir.exists() -> tgt.identifier as insIdentifier then { nir -> insIdentifier.use = 'official' "ins-use"; nir -> insIdentifier.type = cc('http://hl7.fr/fhir/CodeSystem/fr-v2-0203', 'INS-NIR') "ins-type"; nir -> insIdentifier.system = 'urn:oid:1.2.250.1.213.1.4.8' "ins-system"; nir -> insIdentifier.value = nir "ins-value"; } "ins-identifier";#xD; // Alternative INS from ins field#xD; src.ins as ins where ins.exists() -> tgt.identifier as insIdentifier then {#xD; ins -> insIdentifier.use = 'official' "insuse"; // On part du principe qu'on n'est pas sur un old.#xD; ins -> insIdentifier.type = cc('https://hl7.fr/ig/fhir/core/CodeSystem/fr-core-cs-v2-0203', 'INS-NIR', 'NIR définitif') "instype";#xD; ins -> insIdentifier.system = 'urn:oid:1.2.250.1.213.1.4.8' "inssystem";#xD; ins -> insIdentifier.value = ins "insvalue";#xD; } "insidentifier";#xD; // Name#xD; src where src.nom.exists() or src.prenom.exists() -> tgt.name as name then {#xD; src.nom as lastName -> name.family = lastName "familyname";#xD; src.prenom as firstName -> name.given = firstName "givenname";#xD; src -> name.use = 'official' "nameuse"; // hypothèse un peu forte.#xD; } "patientname";#xD; // Demographics On aurait pu faire plus élégant avec un ConceptMap#xD; src.dateNaissance as birthDate -> tgt.birthDate = birthDate "birthdate";#xD; src.sexe as gender where gender = 'h' -> tgt.gender = 'male' "gendermale";#xD; src.sexe as gender where gender = 'f' -> tgt.gender = 'female' "genderfemale";#xD; src.sexe as gender where gender.exists() and (gender != 'h') and (gender != 'f') -> tgt.gender = 'unknown' "genderunknown"; // n'a pas grand sens en l'état des contraintes SQL#xD; // Death information#xD; src.dateDeces as deathDate where deathDate.exists() -> tgt.deceased = cast(deathDate, 'dateTime') as deceasedDate then {#xD; src.sourceDeces as deathSource where deathSource.exists() -> deceasedDate.extension as DeathSourceExtension then {#xD; deathSource -> DeathSourceExtension.url = 'https://interop.aphp.fr/ig/fhir/dm/StructureDefinition/DeathSource' "deathSourceUrl";#xD; deathSource -> DeathSourceExtension.value = cast(deathSource, 'code') "deathSourceValue";#xD; } "deathSource";#xD; } "deathDate";#xD; // Multiple birth#xD; src.rangGemellaire as twin where twin.exists() -> tgt.multipleBirth = twin "multiplebirth";#xD; }#xD; #xD; // ========================================================================#xD; // PATIENT_LOCATION TRANSFORMATION#xD; // ========================================================================#xD; group setAddress(source src, target tgtPat : Patient) {#xD; src where src.latitude.exists() or src.longitude.exists() or src.codeIris.exists() or src.libelleIris.exists() or src.codeGeographiqueResidence.exists() then {#xD; src -> tgtPat.address as newAddress then {#xD; src where src.latitude.exists() or src.longitude.exists() -> newAddress.extension as geolocationExtension, geolocationExtension.url = 'http://hl7.org/fhir/StructureDefinition/geolocation' then {#xD; src.latitude as srcLat -> geolocationExtension.extension as tgtLat, tgtLat.url = 'latitude', tgtLat.value = srcLat "setLat";#xD; src.longitude as srcLong -> geolocationExtension.extension as tgtLong, tgtLong.url = 'longitude', tgtLong.value = srcLong "setLong";#xD; } "setCoordinates";#xD; src where src.codeIris.exists() or src.libelleIris.exists() -> newAddress.line = (%src.libelleIris) as irisLine, irisLine.extension = create('Extension') as irisExtension, irisExtension.url = 'http://hl7.org/fhir/StructureDefinition/iso21090-ADXP-censusTract', irisExtension.value = (%src.codeIris) "setIris";#xD; // src where src.codeIris.exists() or src.libelleIris.exists() -> newAddress.extension as irisExtension,#xD; // irisExtension.url = 'http://hl7.org/fhir/StructureDefinition/iso21090-ADXP-censusTract',#xD; // irisExtension.value = (iif(%src.codeIris.exists() and %src.libelleIris.exists(), %src.codeIris & ' - ' & %src.libelleIris, %src.codeIris & %src.libelleIris)) "setIris";#xD; src.codeGeographiqueResidence as srcCodeGeographiqueResidence where src.codeGeographiqueResidence.exists() -> newAddress.extension as residencePmsiExtension, residencePmsiExtension.url = 'https://interop.aphp.fr/ig/fhir/dm/StructureDefinition/PmsiCodeGeo', residencePmsiExtension.value = cast(srcCodeGeographiqueResidence, 'code') "SetResidencePmsi";#xD; src.dateRecueil as dateRecueil -> newAddress.period as newAddressPeriod, newAddressPeriod.start = dateRecueil "setDateRecueil";#xD; } "createAddress";#xD; } "checkSource";#xD; }#xD; #xD; // ========================================================================#xD; // ENCOUNTER TRANSFORMATION#xD; // ========================================================================#xD; group TransformEncounter(source src, target tgtEnc : Encounter, source patient) {#xD; src -> tgtEnc.id = uuid() "setId";#xD; // src.pmsiId as id -> tgt.id = id "encounter-id";#xD; // Identifier#xD; src -> tgtEnc.identifier as identifier then {#xD; src -> identifier.type = cc('https://hl7.fr/ig/fhir/core/CodeSystem/fr-core-cs-identifier-type', 'VN', 'Visit Number') "idtype";#xD; src -> identifier.system = 'https://hospital.eu/ehr/pmsi-id' "encounteridsystem";#xD; src.pmsiId as srcEncoutnerId -> identifier.value = srcEncoutnerId "encounteridvalue";#xD; } "encounteridentifier";#xD; // Status - default to finished for historical data#xD; src -> tgtEnc.status = 'finished' "encounterstatus";#xD; // Class - assume inpatient for PMSI data#xD; src -> tgtEnc.class = c('http://terminology.hl7.org/CodeSystem/v3-ActCode', 'IMP', 'inpatient encounter') "encounterclass";#xD; // Reference patient#xD; src -> tgtEnc.subject = create('Reference') as ref then {#xD; patient.id as patientId -> ref.reference = ('Patient/' + %patientId), ref.type = 'Patient' "subjectreference";#xD; } "encountersubject";#xD; // Period#xD; src where src.dateDebutSejour.exists() or src.dateFinSejour.exists() -> tgtEnc.period as period then {#xD; src.dateDebutSejour as startDate -> period.start = startDate "periodstart";#xD; src.dateFinSejour as endDate -> period.end = endDate "periodend";#xD; } "encounterperiod";#xD; // Désactivé dans le modèle physique // Duration src.dureeSejour as duration -> tgtEnc.length as length then { duration -> length.value = duration "length-value"; duration -> length.unit = 'd' "length-unit"; duration -> length.system = 'http://unitsofmeasure.org' "length-system"; duration -> length.code = 'd' "length-code"; } "encounter-length";#xD; // Hospitalization#xD; src where src.modeEntree.exists() or src.modeSortie.exists() -> tgtEnc.hospitalization as hosp then {#xD; src.modeEntree as admitSource -> hosp.admitSource as admitSrc then {#xD; admitSource -> admitSrc.text = admitSource "admitsourcetext";#xD; } "admitsource";#xD; src.modeSortie as dischargeDisp -> hosp.dischargeDisposition as dischargeDsp then {#xD; dischargeDisp -> dischargeDsp.text = dischargeDisp "dischargedisptext";#xD; } "dischargedisposition";#xD; } "encounterhospitalization";#xD; // Service Provider#xD; src where src.uniteFonctionnelle.exists() or src.service.exists() or src.etablissement.exists() -> tgtEnc.serviceProvider as provider then {#xD; src -> provider.display = (iif(%src.uniteFonctionnelle.exists(), %src.uniteFonctionnelle, iif(%src.service.exists(), %src.service, %src.etablissement))) "providerdisplay";#xD; } "provider";#xD; // // Location - On n'a pas de location à gérer à ce stade (établissement, service et UF, halluciné par l'IA, sont des Organization que j'ai du coup mi dans Provider. ) src.service as service -> tgt.location as location then { service -> location.location as loc then { service -> loc.display = service "location-display"; } "location-ref"; } "encounter-location";#xD; }#xD; #xD; // ========================================================================#xD; // CONDITION TRANSFORMATION#xD; // ========================================================================#xD; group TransformCondition(source src, target tgtCond : Condition, source patient, source encounter) {#xD; src -> tgtCond.id = uuid() "setId";#xD; // Identifier#xD; src.diagnosticId as diagId -> tgtCond.identifier as identifier then {#xD; diagId -> identifier.system = 'https://hospital.eu/ehr/diagnostic-id' "conditionidsystem";#xD; diagId -> identifier.value = diagId "conditionidvalue";#xD; } "conditionidentifier";#xD; // Category#xD; // Représentation des codes diagnostics du PMSI dans condition , plusieurs options :#xD; // - ce sont des infos de claim, pas de condition#xD; // - utiliser la catégorie 'encounter-diagnosis'#xD; // - utiliser les catégories du PMSI : DP, DR, DAS, DAD#xD; // - mettre les deux précédent <- j'ai fait ça.#xD; src -> tgtCond.category = cc('http://terminology.hl7.org/CodeSystem/condition-category', 'encounter-diagnosis', 'Encounter Diagnosis') "categoryencounterDiag";#xD; src.typeDiagnostic as diagType -> tgtCond.category as category then {#xD; diagType -> category.text = diagType "categorytext";#xD; } "conditioncategory";#xD; // Code (ICD-10)#xD; src.codeDiagnostic as srcCode -> tgtCond.code as conditionCode then {#xD; // la fonction cc ne peuple pas le display...#xD; src.libelleDiagnostic as label -> conditionCode.coding as tgtCoding, tgtCoding.system = 'http://hl7.org/fhir/sid/icd-10', tgtCoding.code = srcCode "conditioncode";#xD; src.libelleDiagnostic as text -> conditionCode.text = text "codetext";#xD; } "varcode";#xD; // Subject#xD; src -> tgtCond.subject = create('Reference') as ref then {#xD; patient.id as patientId -> ref.reference = ('Patient/' + %patientId), ref.type = 'Patient' "subjectref";#xD; } "conditionsubject";#xD; // Encounter reference#xD; src -> tgtCond.encounter = create('Reference') as ref then {#xD; encounter.id as encounterId -> ref.reference = ('Encounter/' + %encounterId), ref.type = 'Encounter' "encounterref";#xD; } "conditionencounter";#xD; // Recorded date#xD; src.dateRecueil as entryDate -> tgtCond.recordedDate = entryDate "conditionrecordeddate";#xD; // Clinical status - assume active for recorded diagnoses#xD; src -> tgtCond.clinicalStatus = cc('http://terminology.hl7.org/CodeSystem/condition-clinical', 'active') "conditionclinicalstatus";#xD; // Verification status - assume confirmed for coded diagnoses#xD; src -> tgtCond.verificationStatus = cc('http://terminology.hl7.org/CodeSystem/condition-ver-status', 'confirmed') "conditionverificationstatus";#xD; }#xD; #xD; // ========================================================================#xD; // PROCEDURE TRANSFORMATION#xD; // ========================================================================#xD; group TransformProcedure(source src, target tgtProc : Procedure, source patient, source encounter) {#xD; src -> tgtProc.id = uuid() "setId";#xD; // Identifier#xD; src.acteId as acteId -> tgtProc.identifier as identifier then {#xD; acteId -> identifier.system = 'https://hospital.eu/ehr/acte-id' "procedureidsystem";#xD; acteId -> identifier.value = acteId "procedureidvalue";#xD; } "procedureidentifier";#xD; // Status - assume completed for historical data#xD; src -> tgtProc.status = 'completed' "procedurestatus";#xD; // Code (CCAM)#xD; src.codeActe as code -> tgtProc.code as procedureCode then {#xD; code -> procedureCode.coding as coding, coding.system = 'https://interop.aphp.fr/ig/fhir/dm/CodeSystem/Ccam', coding.code = code "procedurecoding";#xD; src.libelleActe as text -> procedureCode.text = text "codetext";#xD; } "procedurecode";#xD; // Subject#xD; src -> tgtProc.subject = create('Reference') as ref then {#xD; patient.id as patientId -> ref.reference = ('Patient/' + %patientId), ref.type = 'Patient' "subjectref";#xD; } "proceduresubject";#xD; // Encounter reference#xD; src -> tgtProc.encounter = create('Reference') as ref then {#xD; encounter.id as encounterId -> ref.reference = ('Encounter/' + %encounterId), ref.type = 'Encounter' "encounterref";#xD; } "procedureencounter";#xD; // Performed date/time#xD; src.dateActe as performedDate where %performedDate.exists() -> tgtProc.performed = performedDate "procedureperformed";#xD; // Performer#xD; src.executant as performer -> tgtProc.performer as perf then {#xD; performer -> perf.actor as actor then {#xD; performer -> actor.display = performer "performerdisplay";#xD; performer -> actor.type = 'Practitioner' "performerType";#xD; } "performeractor";#xD; performer -> perf.function as function, function.text = 'Exécutant' "performerFunction";#xD; } "procedureperformer";#xD; }#xD; #xD; // ========================================================================#xD; // LABORATORY OBSERVATION TRANSFORMATION#xD; // ========================================================================#xD; group TransformLabObservation(source src, target tgtObs : Observation, source patient) {#xD; src -> tgtObs.id = uuid() "setId";#xD; // Identifier#xD; src.biologieId as bioId -> tgtObs.identifier as identifier then {#xD; bioId -> identifier.system = 'https://hospital.eu/ehr/biologie-id' "labidsystem";#xD; bioId -> identifier.value = bioId "labidvalue";#xD; } "labidentifier";#xD; // Status - map validation status or default to final#xD; src.statutValidation as validation where validation = 'VALIDE' -> tgtObs.status = 'final' "statusvalidated";#xD; src where src.statutValidation.exists().not() -> tgtObs.status = 'final' "statusdefault";#xD; // Category#xD; src -> tgtObs.category as category then {#xD; src -> category.coding as coding then {#xD; src -> coding.system = 'http://terminology.hl7.org/CodeSystem/observation-category' "categorysystem";#xD; src -> coding.code = 'laboratory' "categorycode";#xD; src -> coding.display = 'Laboratory' "categorydisplay";#xD; } "categorycoding";#xD; } "labcategory";#xD; // Code (LOINC)#xD; src.codeLoinc as loinc -> tgtObs.code as code then {#xD; loinc -> code.coding as coding then {#xD; loinc -> coding.system = 'http://loinc.org' "codesystem";#xD; loinc -> coding.code = loinc "codevalue";#xD; } "labcoding";#xD; src.libelleTest as text -> code.text = text "codetext";#xD; } "labcode";#xD; // Subject#xD; src -> tgtObs.subject = create('Reference') as ref then {#xD; patient.id as patientId -> ref.reference = ('Patient/' + %patientId), ref.type = 'Patient' "subjectref";#xD; } "labsubject";#xD; // Effective date/time#xD; src.datePrelevement as collectionDate where %collectionDate.exists() -> tgtObs.effective = cast(collectionDate, 'dateTime') "labeffective";#xD; // Value (numeric or text)#xD; src.valeur as numValue where numValue.exists() -> tgtObs.value = create('Quantity') as qty then {#xD; numValue -> qty.value = numValue "quantityvalue";#xD; src.unite as unit -> qty.unit = unit "quantityunit";#xD; src.unite as unit -> qty.code = unit "quantitycode";#xD; src -> qty.system = 'http://unitsofmeasure.org' "quantitysystem";#xD; } "labvaluequantity";#xD; // On n'a pas de résultats textuels de prévu#xD; // src.valeurTexte as textValue where textValue.exists() and src.valeur.exists().not() -> tgtObs.value = textValue "lab-value-string";#xD; // Reference range#xD; src where src.borneInfNormale.exists() or src.borneSupNormale.exists() -> tgtObs.referenceRange as range then {#xD; src.borneInfNormale as low -> range.low as lowQty then {#xD; low -> lowQty.value = low "reflowvalue";#xD; src.unite as unit -> lowQty.unit = unit "reflowunit";#xD; src.unite as unit -> lowQty.code = unit "reflowcode";#xD; src -> lowQty.system = 'http://unitsofmeasure.org' "reflowsystem";#xD; } "referencelow";#xD; src.borneSupNormale as high -> range.high as highQty then {#xD; high -> highQty.value = high "refhighvalue";#xD; src.unite as unit -> highQty.unit = unit "refhighunit";#xD; src.unite as unit -> highQty.code = unit "refhighcode";#xD; src -> highQty.system = 'http://unitsofmeasure.org' "refhighsystem";#xD; } "referencehigh";#xD; } "labreferencerange";#xD; // Performer#xD; src.laboratoire as lab -> tgtObs.performer as performer then {#xD; lab -> performer.display = lab "performerdisplay";#xD; lab -> performer.type = 'Organization' "performerType";#xD; } "labperformer";#xD; }#xD; #xD; // ========================================================================#xD; // MEDICATION REQUEST TRANSFORMATION#xD; // ========================================================================#xD; group TransformMedicationRequest(source src, target tgtMedReq : MedicationRequest, source patient) {#xD; src -> tgtMedReq.id = uuid() "setId";#xD; // Identifier#xD; src.prescriptionId as presId -> tgtMedReq.identifier as identifier then {#xD; presId -> identifier.system = 'https://hospital.eu/ehr/prescription-id' "medrequestidsystem";#xD; presId -> identifier.value = presId "medrequestidvalue";#xD; } "medrequestidentifier";#xD; // Status - assume unknown for historical prescriptions#xD; src -> tgtMedReq.status = 'unknown' "medrequeststatus";#xD; // Intent#xD; src -> tgtMedReq.intent = 'order' "medrequestintent";#xD; // Subject#xD; src -> tgtMedReq.subject = create('Reference') as ref then {#xD; patient.id as patientId -> ref.reference = ('Patient/' + %patientId), ref.type = 'Patient' "subjectref";#xD; } "medrequestsubject";#xD; // Medication#xD; src.denomination as denomination -> tgtMedReq.medication = create('CodeableConcept') as medication then {#xD; denomination as text -> medication.text = text "medicationtext";#xD; src.codeAtc as atc -> medication.coding as coding then {#xD; atc -> coding.system = 'http://www.whocc.no/atc' "atcsystem";#xD; atc -> coding.code = atc "atccode";#xD; } "atccoding";#xD; } "medicationdenomination";#xD; // Authored date#xD; src.datePrescription as prescDate -> tgtMedReq.authoredOn = prescDate "medauthored";#xD; // Requester#xD; src.prescripteur as prescriber -> tgtMedReq.requester as requesterRef then {#xD; prescriber -> requesterRef.display = prescriber "requesterdisplay";#xD; prescriber -> requesterRef.type = 'Practitioner' "requesterType";#xD; } "medrequester";#xD; }#xD; #xD; // ========================================================================#xD; // POSOLOGIE TRANSFORMATION#xD; // ========================================================================#xD; group setPoso(source srcPoso, source srcPres, target tgtMedReq) {#xD; srcPres -> tgtMedReq.dosageInstruction as dosageInstruction then {#xD; // Human-readable dosage text - construct from available fields#xD; srcPoso -> dosageInstruction.text = ('Prendre' + iif(%srcPoso.quantite.exists(), ' ' + %srcPoso.quantite.toString(), '1') + iif(%srcPoso.uniteQuantite.exists(), ' ' + %srcPoso.uniteQuantite, ' comprimé') + iif(%srcPoso.nombrePrisesParJour.exists(), ' ' + %srcPoso.nombrePrisesParJour.toString() + ' fois par jour', ' selon prescription')) "dosagetext";#xD; // Route of administration from prescription#xD; srcPres.voieAdministration as route -> dosageInstruction.route as routeCC then {#xD; route -> routeCC.coding as routeCoding then {#xD; route -> routeCoding.code = route "routecode";#xD; route -> routeCoding.system = 'https://smt.esante.gouv.fr/terminologie-standardterms' "routesystem";#xD; } "routecoding";#xD; } "dosageroute";#xD; // Timing#xD; srcPoso -> dosageInstruction.timing as timing then {#xD; srcPoso -> timing.repeat as timingRepeat then {#xD; // Set prescription period as bounds if available#xD; srcPres where srcPres.dateDebutPrescription.exists() or srcPres.dateFinPrescription.exists() -> timingRepeat.bounds = create('Period') as medReqPeriod then {#xD; srcPres.dateDebutPrescription as startDate -> medReqPeriod.start = startDate "periodstart";#xD; srcPres.dateFinPrescription as endDate -> medReqPeriod.end = endDate "periodend";#xD; } "prescriptionperiod";#xD; // Frequency from posology#xD; srcPoso.nombrePrisesParJour as frequency -> timingRepeat.frequency = frequency "timingfrequency";#xD; srcPoso.nombrePrisesParJour as frequency -> timingRepeat.period = '1' "timingperiod";#xD; srcPoso.nombrePrisesParJour as frequency -> timingRepeat.periodUnit = 'd' "timingperiodunit";#xD; } "timingrepeat";#xD; } "dosagetiming";#xD; // Dose and rate#xD; srcPoso.quantite as quantity -> dosageInstruction.doseAndRate as doseRate then {#xD; quantity -> doseRate.dose = create('Quantity') as doseQuantity then {#xD; quantity -> doseQuantity.value = quantity "dosevalue";#xD; srcPoso.uniteQuantite as unit -> doseQuantity.unit = unit "doseunit";#xD; srcPoso.uniteQuantite as unit -> doseQuantity.code = unit "dosecode";#xD; srcPoso -> doseQuantity.system = 'http://unitsofmeasure.org' "dosesystem";#xD; } "dosequantity";#xD; } "doseandrate";#xD; } "dosageinstruction";#xD; }#xD; #xD; // ========================================================================#xD; // VITAL SIGNS TRANSFORMATION#xD; // ========================================================================#xD; group TransformVitalSigns(source src, target tgtObs : Observation, source patient) {#xD; src -> tgtObs.id = uuid() "setId";#xD; // Identifier#xD; src.soinId as soinId -> tgtObs.identifier as identifier then {#xD; soinId -> identifier.system = 'https://hospital.eu/ehr/soin-id' "soinidsystem";#xD; soinId -> identifier.value = soinId "soinidvalue";#xD; } "medrequestidentifier";#xD; // Status - assume final#xD; src -> tgtObs.status = 'final' "status";#xD; // Category - vital-signs#xD; src -> tgtObs.category as category then {#xD; src -> category.coding as coding then {#xD; src -> coding.system = 'http://terminology.hl7.org/CodeSystem/observation-category' "categorysystem";#xD; src -> coding.code = 'vital-signs' "categorycode";#xD; } "categorycoding";#xD; } "category";#xD; // code (LOINC)#xD; src -> tgtObs.code as code then {#xD; src -> code.coding as coding then {#xD; src -> coding.system = 'http://loinc.org' "codesystem";#xD; src.codeLoinc as codeLoinc -> coding.code = codeLoinc "codevalue";#xD; } "coding";#xD; src.libelleTest as label -> code.text = label "codetext";#xD; } "code";#xD; // Subject#xD; src -> tgtObs.subject = create('Reference') as ref then {#xD; patient.id as patientId -> ref.reference = ('Patient/' + %patientId), ref.type = 'Patient' "subjectref";#xD; } "obssubject";#xD; // Date de l'observation#xD; src.dateMesure as date where %date.exists() -> tgtObs.effective = cast(date, 'dateTime') "effective";#xD; // Valeur (quantitiative)#xD; src -> tgtObs.value = create('Quantity') as qty then {#xD; src.valeur as mesure -> qty.value = mesure "value";#xD; src.unite as unit -> qty.unit = unit "unit";#xD; src.unite as unit -> qty.code = unit "code";#xD; src -> qty.system = 'http://unitsofmeasure.org' "system";#xD; } "quantity";#xD; // Blood pressure observation - would need separate entry with components#xD; }#xD; #xD; // ========================================================================#xD; // LIFESTYLE TRANSFORMATION - SEPARATE OBSERVATIONS#xD; // ========================================================================#xD; group TransformTobaccoObservation(source src, target tgtObs : Observation, source patient) {#xD; src -> tgtObs.id = uuid() "setId";#xD; // Identifier#xD; src.styleVieId as lsId -> tgtObs.identifier as identifier then {#xD; lsId -> identifier.system = 'https://hospital.eu/ehr/lifestyle-id' "lifestyleidsystem";#xD; lsId -> identifier.value = (%lsId & '-tobacco') "lifestyleidvalue";#xD; } "lifestyleidentifier";#xD; // Status#xD; src -> tgtObs.status = 'final' "lifestylestatus";#xD; // Category - social-history#xD; src -> tgtObs.category as category then {#xD; src -> category.coding as coding then {#xD; src -> coding.system = 'http://terminology.hl7.org/CodeSystem/observation-category' "categorysystem";#xD; src -> coding.code = 'social-history' "categorycode";#xD; src -> coding.display = 'Social History' "categorydisplay";#xD; } "categorycoding";#xD; } "lifestylecategory";#xD; // Code - tobacco use status#xD; src -> tgtObs.code as code then {#xD; src -> code.coding as coding then {#xD; src -> coding.system = 'http://loinc.org' "codesystem";#xD; src -> coding.code = '72166-2' "codevalue";#xD; src -> coding.display = 'Tobacco smoking status' "codedisplay";#xD; } "tobaccocoding";#xD; } "tobaccocode";#xD; // Subject#xD; src -> tgtObs.subject = create('Reference') as ref then {#xD; patient.id as patientId -> ref.reference = ('Patient/' + %patientId), ref.type = 'Patient' "subjectref";#xD; } "lifestylesubject";#xD; // Effective date#xD; src.dateRecueil as collectDate where %collectDate.exists() -> tgtObs.effective = cast(collectDate, 'dateTime') "lifestyleeffective";#xD; // Value#xD; src.consommationTabac as tobacco -> tgtObs.value = create('CodeableConcept') as valueCC then {#xD; tobacco -> valueCC.text = tobacco "tobaccovaluetext";#xD; } "tobaccovalue";#xD; }#xD; #xD; group TransformAlcoholObservation(source src, target tgtObs : Observation, source patient) {#xD; src -> tgtObs.id = uuid() "setId";#xD; // Identifier#xD; src.styleVieId as lsId -> tgtObs.identifier as identifier then {#xD; lsId -> identifier.system = 'https://hospital.eu/ehr/lifestyle-id' "lifestyleidsystem";#xD; lsId -> identifier.value = (%lsId & '-alcohol') "lifestyleidvalue";#xD; } "lifestyleidentifier";#xD; // Status#xD; src -> tgtObs.status = 'final' "lifestylestatus";#xD; // Category - social-history#xD; src -> tgtObs.category as category then {#xD; src -> category.coding as coding then {#xD; src -> coding.system = 'http://terminology.hl7.org/CodeSystem/observation-category' "categorysystem";#xD; src -> coding.code = 'social-history' "categorycode";#xD; src -> coding.display = 'Social History' "categorydisplay";#xD; } "categorycoding";#xD; } "lifestylecategory";#xD; // Code - alcohol use#xD; src -> tgtObs.code as code then {#xD; src -> code.coding as coding then {#xD; src -> coding.system = 'http://loinc.org' "codesystem";#xD; src -> coding.code = '11331-6' "codevalue";#xD; src -> coding.display = 'History of alcohol use' "codedisplay";#xD; } "alcoholcoding";#xD; } "alcoholcode";#xD; // Subject#xD; src -> tgtObs.subject = create('Reference') as ref then {#xD; patient.id as patientId -> ref.reference = ('Patient/' + %patientId), ref.type = 'Patient' "subjectref";#xD; } "lifestylesubject";#xD; // Effective date#xD; src.dateRecueil as collectDate where %collectDate.exists() -> tgtObs.effective = cast(collectDate, 'dateTime') "lifestyleeffective";#xD; // Value#xD; src.consommationAlcool as alcohol -> tgtObs.value = create('CodeableConcept') as valueCC then {#xD; alcohol -> valueCC.text = alcohol "alcoholvaluetext";#xD; } "alcoholvalue";#xD; }#xD; #xD; group TransformDrugObservation(source src, target tgtObs : Observation, source patient) {#xD; src -> tgtObs.id = uuid() "setId";#xD; // Identifier#xD; src.styleVieId as lsId -> tgtObs.identifier as identifier then {#xD; lsId -> identifier.system = 'https://hospital.eu/ehr/lifestyle-id' "lifestyleidsystem";#xD; lsId -> identifier.value = (%lsId & '-drugs') "lifestyleidvalue";#xD; } "lifestyleidentifier";#xD; // Status#xD; src -> tgtObs.status = 'final' "lifestylestatus";#xD; // Category - social-history#xD; src -> tgtObs.category as category then {#xD; src -> category.coding as coding then {#xD; src -> coding.system = 'http://terminology.hl7.org/CodeSystem/observation-category' "categorysystem";#xD; src -> coding.code = 'social-history' "categorycode";#xD; src -> coding.display = 'Social History' "categorydisplay";#xD; } "categorycoding";#xD; } "lifestylecategory";#xD; // Code - drug use#xD; src -> tgtObs.code as code then {#xD; src -> code.coding as coding then {#xD; src -> coding.system = 'http://loinc.org' "codesystem";#xD; src -> coding.code = '11342-3' "codevalue";#xD; src -> coding.display = 'History of drug use' "codedisplay";#xD; } "drugcoding";#xD; } "drugcode";#xD; // Subject#xD; src -> tgtObs.subject = create('Reference') as ref then {#xD; patient.id as patientId -> ref.reference = ('Patient/' + %patientId), ref.type = 'Patient' "subjectref";#xD; } "lifestylesubject";#xD; // Effective date#xD; src.dateRecueil as collectDate where %collectDate.exists() -> tgtObs.effective = cast(collectDate, 'dateTime') "lifestyleeffective";#xD; // Value#xD; src.consommationAutresDrogues as drugs -> tgtObs.value = create('CodeableConcept') as valueCC then {#xD; drugs -> valueCC.text = drugs "drugvaluetext";#xD; } "drugvalue";#xD; }#xD; #xD; group TransformPhysicalActivityObservation(source src, target tgtObs : Observation, source patient) {#xD; src -> tgtObs.id = uuid() "setId";#xD; // Identifier#xD; src.styleVieId as lsId -> tgtObs.identifier as identifier then {#xD; lsId -> identifier.system = 'https://hospital.eu/ehr/lifestyle-id' "lifestyleidsystem";#xD; lsId -> identifier.value = (%lsId & '-physical') "lifestyleidvalue";#xD; } "lifestyleidentifier";#xD; // Status#xD; src -> tgtObs.status = 'final' "lifestylestatus";#xD; // Category - social-history#xD; src -> tgtObs.category as category then {#xD; src -> category.coding as coding then {#xD; src -> coding.system = 'http://terminology.hl7.org/CodeSystem/observation-category' "categorysystem";#xD; src -> coding.code = 'social-history' "categorycode";#xD; src -> coding.display = 'Social History' "categorydisplay";#xD; } "categorycoding";#xD; } "lifestylecategory";#xD; // Code - physical activity#xD; src -> tgtObs.code as code then {#xD; src -> code.coding as coding then {#xD; src -> coding.system = 'http://loinc.org' "codesystem";#xD; src -> coding.code = '67504-6' "codevalue";#xD; src -> coding.display = 'Exercise activity' "codedisplay";#xD; } "physicalcoding";#xD; } "physicalcode";#xD; // Subject#xD; src -> tgtObs.subject = create('Reference') as ref then {#xD; patient.id as patientId -> ref.reference = ('Patient/' + %patientId), ref.type = 'Patient' "subjectref";#xD; } "lifestylesubject";#xD; // Effective date#xD; src.dateRecueil as collectDate where %collectDate.exists() -> tgtObs.effective = cast(collectDate, 'dateTime') "lifestyleeffective";#xD; // Value#xD; src.activitePhysique as physical -> tgtObs.value = create('CodeableConcept') as valueCC then {#xD; physical -> valueCC.text = physical "physicalvaluetext";#xD; } "physicalvalue";#xD; }#xD; #xD; // ========================================================================#xD; // MEDICATION ADMINISTRATION TRANSFORMATION#xD; // ========================================================================#xD; group TransformMedicationAdministrationWOOrder(source src, target tgtMedAdmin : MedicationAdministration, source patient) {#xD; src -> tgtMedAdmin.id = uuid() "setId";#xD; // Identifier#xD; src.administrationId as adminId -> tgtMedAdmin.identifier as identifier then {#xD; adminId -> identifier.system = 'https://hospital.eu/ehr/administration-id' "adminidsystem";#xD; adminId -> identifier.value = adminId "adminidvalue";#xD; } "adminidentifier";#xD; // Status - assume completed#xD; src -> tgtMedAdmin.status = 'completed' "adminstatus";#xD; // Subject#xD; src -> tgtMedAdmin.subject = create('Reference') as ref then {#xD; patient.id as patientId -> ref.reference = ('Patient/' + %patientId), ref.type = 'Patient' "subjectref";#xD; } "adminsubject";#xD; // Medication#xD; src.denomination as denomination -> tgtMedAdmin.medication = create('CodeableConcept') as medication then {#xD; denomination -> medication.text = denomination "medicationtext";#xD; src.codeAtc as atc -> medication.coding as coding then {#xD; atc -> coding.system = 'http://www.whocc.no/atc' "atcsystem";#xD; atc -> coding.code = atc "atccode";#xD; } "atccoding";#xD; } "adminmedication";#xD; // Effective period#xD; src where src.dateHeureDebut.exists() or src.dateHeureFin.exists() -> tgtMedAdmin.effective = create('Period') as period then {#xD; src.dateHeureDebut as startDate -> period.start = startDate "periodstart";#xD; src.dateHeureFin as endDate -> period.end = endDate "periodend";#xD; } "admineffective";#xD; // Dosage#xD; src where src.quantite.exists() or src.voieAdministration.exists() -> tgtMedAdmin.dosage as dosage then {#xD; src.quantite as quantity -> dosage.dose = create('Quantity') as dose then {#xD; quantity -> dose.value = quantity "dosevalue";#xD; src.uniteQuantite as unit -> dose.unit = unit "doseunit";#xD; } "admindose";#xD; src.voieAdministration as route -> dosage.route as routeCC then {#xD; route -> routeCC.coding as routeCoding then {#xD; route -> routeCoding.code = route "routecode";#xD; route -> routeCoding.system = 'https://smt.esante.gouv.fr/terminologie-standardterms' "routesystem";#xD; } "routecoding";#xD; } "dosageroute";#xD; } "admindosage";#xD; }#xD; #xD; group TransformMedicationAdministrationWOrder(source src, target tgtMedAdmin : MedicationAdministration, source patient, source order) extends TransformMedicationAdministrationWOOrder {#xD; // Context (encounter) reference#xD; src -> tgtMedAdmin.request = create('Reference') as ref then {#xD; order.id as orderId -> ref.reference = ('MedicationRequest/' + %orderId), ref.type = 'MedicationRequest' "encounterref";#xD; } "admincontext";#xD; }#xD; #xD; group setEntryRequestAndFullUrl(source newRes, target tgtEntry) {#xD; newRes.id as newResId then {#xD; newResId -> tgtEntry.fullUrl = ('urn:ehr:' + %newRes.type().name + '/' + %newResId) "setFullUrl";#xD; } "setResourceType";#xD; }#xD; #xD;
Transforms EHR logical model data to FHIR Semantic Layer resources using Bundle as container
patientAdresse.patientId = %patient.patientId
(diag.patientId = %patient.patientId) and (diag.pmsiId = %pmsi.pmsiId)
(acte.patientId = %patient.patientId) and (acte.pmsiId = %pmsi.pmsiId)
%poso.prescriptionId = %pres.prescriptionId
(admin.patientId = %patient.patientId) and (admin.prescriptionId = pres.prescriptionId)
lifestyle.patientId = %patient.patientId
(admin.patientId = %patient.patientId) and admin.prescriptionId.empty()
On part du principe qu'on n'est pas sur un old.
gender.exists() and (gender != 'h') and (gender != 'f')
n'a pas grand sens en l'état des contraintes SQL
src.latitude.exists() or src.longitude.exists() or src.codeIris.exists() or src.libelleIris.exists() or src.codeGeographiqueResidence.exists()
src.latitude.exists() or src.longitude.exists()
src.codeIris.exists() or src.libelleIris.exists()
src.dateDebutSejour.exists() or src.dateFinSejour.exists()
src.modeEntree.exists() or src.modeSortie.exists()
src.uniteFonctionnelle.exists() or src.service.exists() or src.etablissement.exists()
iif(%src.uniteFonctionnelle.exists(), %src.uniteFonctionnelle, iif(%src.service.exists(), %src.service, %src.etablissement))
src.borneInfNormale.exists() or src.borneSupNormale.exists()
'Prendre' + iif(%srcPoso.quantite.exists(), ' ' + %srcPoso.quantite.toString(), '1') + iif(%srcPoso.uniteQuantite.exists(), ' ' + %srcPoso.uniteQuantite, ' comprimé') + iif(%srcPoso.nombrePrisesParJour.exists(), ' ' + %srcPoso.nombrePrisesParJour.toString() + ' fois par jour', ' selon prescription')
srcPres.dateDebutPrescription.exists() or srcPres.dateFinPrescription.exists()
src.dateHeureDebut.exists() or src.dateHeureFin.exists()
src.quantite.exists() or src.voieAdministration.exists()
'urn:ehr:' + %newRes.type().name + '/' + %newResId