CDA-FHIR-Maps (R4)
0.1.1 - CI build

CDA-FHIR-Maps (R4), published by ahdis. This is not an authorized publication; it is the continuous build for version 0.1.1). This version is based on the current content of https://github.com/hl7ch/cda-fhir-maps/ and changes regularly. See the Directory of published versions

StructureMap: Bundle to CDA-CH

map "http://fhir.ch/ig/cda-fhir-maps/StructureMap/BundleToCdaCh" = "BundleToCdaCh"

// CDA-CH document, 2.16.756.5.30.1.1.10.1.14
// 2020-01-16 Oliver Egger, copyright ahdis ag, Apache License
// CDA-CH:  https://art-decor.org/art-decor/decor-templates--hl7chcda-
// FHIR CH-Core: http://fhir.ch/ig/ch-core/index.html

uses "http://hl7.org/fhir/cda/StructureDefinition/ClinicalDocument" alias ClinicalDocument as target
uses "http://hl7.org/fhir/cda/StructureDefinition/Author" alias Author as target
uses "http://hl7.org/fhir/cda/StructureDefinition/RecordTarget" alias RecordTarget as target
uses "http://hl7.org/fhir/cda/StructureDefinition/DataEnterer" alias DataEnterer as target
uses "http://hl7.org/fhir/cda/StructureDefinition/AssignedEntity" alias AssignedEntity as target
uses "http://hl7.org/fhir/StructureDefinition/Bundle" alias Bundle as source
uses "http://hl7.org/fhir/StructureDefinition/Composition" alias Composition as source
uses "http://hl7.org/fhir/StructureDefinition/Person" alias Person as source

imports "http://fhir.ch/ig/cda-fhir-maps/StructureMap/FhirToCdaTypes"
imports "http://fhir.ch/ig/cda-fhir-maps/StructureMap/BundleToCda"

// source: http://build.fhir.org/ig/hl7ch/ch-core/branches/master/StructureDefinition-ch-core-document.html
// target: https://art-decor.org/art-decor/decor-templates--hl7chcda-?id=2.16.756.5.30.1.1.10.1.14
group BundleToCdaCh(source bundle : Bundle, target cda : ClinicalDocument) {
  bundle ->  cda.recordTarget as recordTarget,  cda.author as author then BundleToClinicalDocumentCh(bundle, recordTarget, author, cda) "bundle";
}

// source: http://build.fhir.org/ig/hl7ch/ch-core/branches/master/StructureDefinition-ch-core-document.html
// target: https://art-decor.org/art-decor/decor-templates--hl7chcda-?id=2.16.756.5.30.1.1.10.1.14
group BundleToClinicalDocumentCh(source bundle : Bundle, target recordTarget : RecordTarget, target author : Author, target cda : ClinicalDocument) extends BundleToClinicalDocument {
  bundle ->  author.templateId as templateId,  templateId.root = '2.16.756.5.30.1.1.10.9.23' "tempalteId";
  bundle ->  recordTarget.templateId as templateId,  templateId.root = '2.16.756.5.30.1.1.10.2.1' "tempalteId";
  bundle ->  cda.custodian as custodian,  custodian.templateId as templateId,  templateId.root = '2.16.756.5.30.1.1.10.2.3' "tempalteId";
  bundle -> cda.realmCode as realmCode then {
    bundle -> realmCode.code = 'CHE' "CHE";
  } "realmCode";
  bundle -> cda.templateId as templateId then {
    bundle -> templateId.root = '2.16.756.5.30.1.1.10.1.9' "root";
  } "0-structuredBody";
  bundle -> cda.templateId as templateId then {
    bundle -> templateId.root = '2.16.840.1.113883.10.12.2' "root";
  } "componentAsStructuredBody";
  bundle -> cda.templateId as templateId then {
    bundle -> templateId.root = '2.16.840.1.113883.10.12.1' "root";
  } "HL7CdaR2-2005";
  bundle.entry as entry then {
    entry.resource as resource where $this.ofType(FHIR.Composition) then CompositionClinicalDocumentCH(bundle, resource, cda);
  };
}

// _________________________ Section Level Templates _________________________
// source: http://build.fhir.org/ig/hl7ch/ch-core/branches/master/StructureDefinition-ch-core-composition-definitions.html#Composition.section
// target: https://art-decor.org/art-decor/decor-templates--hl7chcda-?id=2.16.756.5.30.1.1.10.3.2
group SectionCdaRemarksCoded(source bundle : Bundle, source section : Section, target cdasection : CdaSection) extends SectionCdaSection {
  section ->  cdasection.templateId as templateId,  templateId.root = '2.16.756.5.30.1.1.10.3.2' "templateId";
  section ->  cdasection.entry as entry,  entry.act as act,  act.classCode = 'ACT',  act.moodCode = 'EVN' then {
    section ->  act.templateId as templateId,  templateId.root = '2.16.756.5.30.1.1.10.4.2' "templateId";
    section ->  act.templateId as templateId,  templateId.root = '2.16.840.1.113883.10.20.1.40' "templateId";
    section ->  act.templateId as templateId,  templateId.root = '1.3.6.1.4.1.19376.1.5.3.1.4.2' "templateId";
    section ->  act.code as code,  code.code = '48767-8',  code.codeSystem = '2.16.840.1.113883.6.1',  code.codeSystemName = 'LOINC',  code.displayName = 'Annotation comment' "code";
    // <span id="co1">
    section.text as text then {
      text.div as div ->  act.text as text,  text.reference as reference,  reference.value = ('#' + %div.substring(%div.indexOf('id') + %div.substring(%div.indexOf('id')).indexOf('\"') + 1, %div.substring(%div.indexOf('id') + %div.substring(%div.indexOf('id')).indexOf('\"') + 1).indexOf('\"'))) "id";
    };
    section ->  act.statusCode as statusCode,  statusCode.code = 'completed' "completed";
  } "entry";
}

group SectionCdaOriginalRepresentationObservationMedia(source binary : Binary, source section : Section, target cdasection : CdaSection) {
  section -> cdasection.text = '<div xmlns=\"http://www.w3.org/1999/xhtml\">Representation of the original view:<img src=\"pdf1\"/></div>' "Section";
  section -> cdasection.entry as entry then {
    binary -> entry.typeCode = 'DRIV' "driv";
    binary ->  entry.observationMedia as observationMedia,  observationMedia.classCode = 'OBS',  observationMedia.moodCode = 'EVN' then {
      binary ->  observationMedia.templateId as templateId,  templateId.root = '2.16.756.5.30.1.1.10.4.83' "templateId";
      binary -> observationMedia.ID = 'pdf1' "reference";
      binary ->  observationMedia.value as value,  value.mediaType = '2.16.756.5.30.1.1.10.4.83',  value.representation = 'B64' then {
        binary.contentType as contentType -> value.mediaType = contentType;
        binary.data as data -> value.data = data;
        binary.language as lang ->  observationMedia.languageCode as languageCode,  languageCode.code = lang "lang";
        binary where $this.language.exists() = false ->  observationMedia.languageCode as languageCode,  languageCode.nullFlavor = 'UNK' "langUnknown";
      } "value";
    } "observationMedia";
  } "entry";
}

// source: http://build.fhir.org/ig/hl7ch/ch-core/branches/master/StructureDefinition-ch-core-composition-definitions.html#Composition.section
// target: https://art-decor.org/art-decor/decor-templates--hl7chcda-?id=2.16.756.5.30.1.1.10.1.14
// target: https://art-decor.org/art-decor/decor-templates--hl7chcda-?id=2.16.756.5.30.1.1.10.4.83
group SectionCdaOriginalRepresentation(source bundle : Bundle, source section : Section, target cdasection : CdaSection) extends SectionCdaSection {
  section ->  cdasection.templateId as templateId,  templateId.root = '2.16.756.5.30.1.1.10.3.45' "templateId";
  section where title.exists().not() then {
    section where (%bundle.entry[0].resource.language.startsWith('de')) ->  cdasection.title as title,  title.data = 'Original Darstellung' "titleDE";
    section where (%bundle.entry[0].resource.language.startsWith('fr')) ->  cdasection.title as title,  title.data = 'Représentation originale' "titleFR";
    section where (%bundle.entry[0].resource.language.startsWith('it')) ->  cdasection.title as title,  title.data = 'Rappresentazione originale' "titleIT";
    section where (%bundle.entry[0].resource.language.startsWith('en')) ->  cdasection.title as title,  title.data = 'Original representation' "titleEN";
  } "defaulttitles";
  bundle.entry as entry then {
    entry.fullUrl where ($this in %section.entry.reference) and $this.startsWith('urn:uuid') then {
      entry.resource as binary then SectionCdaOriginalRepresentationObservationMedia(binary, section, cdasection) "patient";
    } "fullUrlAsUuid";
    entry.resource as binary where ('Binary' + '/' + $this.id) in %section.entry.reference then SectionCdaOriginalRepresentationObservationMedia(binary, section, cdasection) "patient";
  } "patient";
}

// _________________________ Entry Level Templates   ________________________
// _________________________ Header Level Templates _________________________
// source: http://build.fhir.org/ig/hl7ch/ch-core/branches/master/StructureDefinition-ch-core-composition-epr.html
// target: https://art-decor.org/art-decor/decor-templates--hl7chcda-?id=2.16.756.5.30.1.1.10.9.12
group PersonAssignedEntityWithId(source practitioner : Practitioner, target assignedEntity : AssignedEntity) {
  practitioner.identifier as identifier -> assignedEntity.id as id then IdentifierII(identifier, id);
  practitioner.address as address -> assignedEntity.addr as addr then AddressAD(address, addr);
  practitioner.name as humanname ->  assignedEntity.assignedPerson as assignedPerson,  assignedPerson.name as en then HumanNameEN(humanname, en);
}

// source: http://build.fhir.org/ig/hl7ch/ch-core/branches/master/StructureDefinition-ch-core-composition-epr.html
// target: https://art-decor.org/art-decor/decor-templates--hl7chcda-?id=2.16.756.5.30.1.1.10.9.12
group DataEnterer(source bundle : Bundle, source practitionerRole : PractitionerRole, target assignedEntity : AssignedEntity) {
  practitionerRole.practitioner as practitioner then {
    practitioner.reference as reference then {
      bundle.entry as entry then {
        entry.fullUrl where ($this in %reference.value) and $this.startsWith('urn:uuid') then {
          entry.resource : Practitioner as practitioner then PersonAssignedEntityWithId(practitioner, assignedEntity) "AssignedEntityWithId";
        } "fullUrl as urn:uuid";
        entry.resource : Practitioner as practitioner where ('Practitioner' + '/' + $this.id) in %reference.value then PersonAssignedEntityWithId(practitioner, assignedEntity) "AssignedEntityWithId";
      } "practitioner";
    } "valueReference";
  } "extensionPerson";
  practitionerRole.organization as organization -> assignedEntity.representedOrganization as representedOrganization then {
    organization.reference as reference then {
      bundle.entry as entry then {
        entry.fullUrl where ($this in %reference.value) and $this.startsWith('urn:uuid') then {
          entry.resource : Organization as organization then Organization2CdaOrganization(organization, representedOrganization) "representedOrganization";
        } "fullUrl as urn:uuid";
        entry.resource : Organization as organization where ('Organization' + '/' + $this.id) in %reference.value then Organization2CdaOrganization(organization, representedOrganization) "representedOrganization";
      } "practitioner";
    } "valueReference";
  } "extensionPerson";
}

// _________________________ Template Type not specified  ___________________
// source: http://hl7.org/fhir/R4/person.html
// target: https://art-decor.org/art-decor/decor-templates--hl7chcda-?id=2.16.756.5.30.1.1.10.9.36
group CompositionClinicalDocumentCH(source bundle : Bundle, source composition : Composition, target cda : ClinicalDocument) {
  composition.type as type then {
    type.coding as coding where $this.system = 'http://snomed.info/sct' ->  cda.code as code,  code.translation as translation then CodingCE(coding, translation) "translation";
  };
  composition.confidentiality as conf where $this.extension.where(url = 'http://fhir.ch/ig/ch-core/StructureDefinition/ch-ext-epr-confidentialitycode').exists() -> cda.confidentialityCode as cdaconf then {
    conf.extension as extension then {
      extension.valueCodeableConcept as valueCodeableConcept then {
        valueCodeableConcept.coding as coding then CodingCE(coding, cdaconf);
      };
    };
  } "confidentialityCode";
  // If setId exists as Composition.ch-ext-epr-setid, then map to ClinicalDocument.setId
  composition.identifier as identifier where $this.system = 'urn:ietf:rfc:3986' -> cda.setId as setId then IdentifierII(identifier, setId) "IdentifierII";
  // If setId doesn't exist, used bundle.identifer
  composition where $this.identifier.where(system = 'urn:ietf:rfc:3986').exists() = false then {
    bundle.identifier as identifier -> cda.setId as id then IdentifierII(identifier, id) "setId";
  } "setIdCreated";
  // If versionNumber exists as Composition.ch-ext-epr-versionnumber, then map to ClinicalDocument.versionNumber
  composition.extension as ext where $this.url = 'http://fhir.ch/ig/ch-core/StructureDefinition/ch-ext-epr-versionnumber' -> cda.versionNumber as versionNumber then {
    ext.valueUnsignedInt as valueUnsignedInt then IntegerINT(valueUnsignedInt, versionNumber) "IntegerInt";
  } "versionNumberMapped";
  // If versionNumber doesn't exist, create ClinicalDocument.versionNumber = 1
  composition as composition where $this.extension.where(url = 'http://fhir.ch/ig/ch-core/StructureDefinition/ch-ext-epr-versionnumber').exists() = false -> cda.versionNumber as versionNumber then {
    composition -> versionNumber.value = '1' "versionNumber";
  } "versionNumberCreated";
  composition.extension as extension where $this.url = 'http://fhir.ch/ig/ch-core/StructureDefinition/ch-ext-epr-informationrecipient' -> cda.informationRecipient as informationRecipient then {
    extension -> informationRecipient.typeCode = 'PRCP' "PRCP";
    extension ->  informationRecipient.templateId as templateId,  templateId.root = '2.16.756.5.30.1.1.10.2.4' "templateId";
    extension.valueReference as valueReference then {
      bundle.entry as entry then {
        entry.fullUrl where ($this in %valueReference.reference) and $this.startsWith('urn:uuid') then {
          entry.resource : Patient as patient -> informationRecipient.intendedRecipient as intendedRecipient share intendedRecipient then {
            patient.address as address -> intendedRecipient.addr as addr then AddressAD(address, addr);
            patient.name as humanname ->  intendedRecipient.informationRecipient as informationRecipient2,  informationRecipient2.name as en then HumanNameEN(humanname, en);
          } "fullUrl patient";
          entry.resource : Organization as organization ->  informationRecipient.intendedRecipient as intendedRecipient share intendedRecipient,  intendedRecipient.receivedOrganization as receivedOrganization then Organization2CdaOrganization(organization, receivedOrganization) "organization";
        } "fullUrl as urn:uuid";
        entry.resource : Patient as patient where (($this.id = %valueReference.reference.substring(8)) or ($this.id = %valueReference.reference.substring(9))) -> informationRecipient.intendedRecipient as intendedRecipient share intendedRecipient then {
          patient.address as address -> intendedRecipient.addr as addr then AddressAD(address, addr);
          patient.name as humanname ->  intendedRecipient.informationRecipient as informationRecipient2,  informationRecipient2.name as en then HumanNameEN(humanname, en);
        } "intendedRecipient";
        entry.resource : Organization as organization where (($this.id = %valueReference.reference.substring(13)) or ($this.id = %valueReference.reference.substring(9))) ->  informationRecipient.intendedRecipient as intendedRecipient share intendedRecipient,  intendedRecipient.receivedOrganization as receivedOrganization then Organization2CdaOrganization(organization, receivedOrganization) "organization";
      };
    };
  } "informationRecipient";
  composition.extension as extension where $this.url = 'http://fhir.ch/ig/ch-core/StructureDefinition/ch-ext-epr-dataenterer' -> cda.dataEnterer as dataEnterer then {
    extension ->  dataEnterer.templateId as templateId,  templateId.root = '2.16.756.5.30.1.1.10.2.7' "templateId";
    extension.extension as extensionTime where $this.url = 'http://fhir.ch/ig/ch-core/StructureDefinition/ch-ext-epr-time' then {
      extensionTime.valueDateTime as valueDateTime -> dataEnterer.time as time then DateTimeTS(valueDateTime, time) "DateTime";
    } "extensionTime";
    extension.extension as extensionPerson where $this.url = 'enterer' then {
      extensionPerson.valueReference as valueReference then {
        bundle.entry as entry then {
          entry.fullUrl where ($this in %valueReference.reference) and $this.startsWith('urn:uuid') then {
            entry.resource : PractitionerRole as practitionerRole -> dataEnterer.assignedEntity as assignedEntity then DataEnterer(bundle, practitionerRole, assignedEntity) "AssignedEntityWithId";
          } "fullUrl as urn:uuid";
          entry.resource : PractitionerRole as practitionerRole where ('PractitionerRole' + '/' + $this.id) in %valueReference.reference -> dataEnterer.assignedEntity as assignedEntity then DataEnterer(bundle, practitionerRole, assignedEntity) "AssignedEntityWithId";
        } "practitioner";
      };
    } "extensionPerson";
  } "dataEnterer";
}