Skip to content

Commit

Permalink
Merge pull request #15 from oehf/14-small-fixes-to-xds-fhir-metadata-…
Browse files Browse the repository at this point in the history
…mappers

Small fixes to XDS <> FHIR metadata mappers
  • Loading branch information
Thopap authored Oct 27, 2024
2 parents 8c1c490 + 551808d commit b7c925a
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 65 deletions.
Original file line number Diff line number Diff line change
@@ -1,34 +1,19 @@
package org.openehealth.app.xdstofhir.registry.common.mapper;

import static org.openehealth.app.xdstofhir.registry.common.MappingSupport.urnDecodedScheme;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.ContactPoint.ContactPointSystem;
import org.openehealth.app.xdstofhir.registry.common.MappingSupport;
import org.openehealth.ipf.commons.ihe.xds.core.metadata.Organization;
import org.openehealth.ipf.commons.ihe.xds.core.metadata.Person;
import org.openehealth.ipf.commons.ihe.xds.core.metadata.*;
import org.openehealth.ipf.commons.ihe.xds.core.validate.OIDValidator;

import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import org.hl7.fhir.r4.model.BaseDateTimeType;
import org.hl7.fhir.r4.model.CodeableConcept;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.ContactPoint;
import org.hl7.fhir.r4.model.ContactPoint.ContactPointSystem;
import org.hl7.fhir.r4.model.HumanName;
import org.hl7.fhir.r4.model.Identifier;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.Practitioner;
import org.hl7.fhir.r4.model.PractitionerRole;
import org.hl7.fhir.r4.model.Reference;
import org.openehealth.app.xdstofhir.registry.common.MappingSupport;
import org.openehealth.ipf.commons.ihe.xds.core.metadata.AssigningAuthority;
import org.openehealth.ipf.commons.ihe.xds.core.metadata.Author;
import org.openehealth.ipf.commons.ihe.xds.core.metadata.Code;
import org.openehealth.ipf.commons.ihe.xds.core.metadata.Identifiable;
import org.openehealth.ipf.commons.ihe.xds.core.metadata.LocalizedString;
import org.openehealth.ipf.commons.ihe.xds.core.metadata.Organization;
import org.openehealth.ipf.commons.ihe.xds.core.metadata.Person;
import org.openehealth.ipf.commons.ihe.xds.core.metadata.Telecom;
import org.openehealth.ipf.commons.ihe.xds.core.metadata.Timestamp;
import org.openehealth.ipf.commons.ihe.xds.core.metadata.XcnName;
import static org.openehealth.app.xdstofhir.registry.common.MappingSupport.urnDecodedScheme;

public abstract class AbstractFhirToXdsMapper {

Expand Down Expand Up @@ -71,7 +56,7 @@ protected Author fromAuthor(Reference author) {
doc = (Practitioner) fhirPractRole.getPractitioner().getResource();
xdsAuthor.getAuthorRole().addAll(fhirPractRole.getCode().stream().map(this::fromCodeableConcept).toList());
xdsAuthor.getAuthorSpecialty().addAll(fhirPractRole.getSpecialty().stream().map(this::fromCodeableConcept).toList());
fromOrganization((org.hl7.fhir.r4.model.Organization)fhirPractRole.getOrganization().getResource()).ifPresent(org -> xdsAuthor.getAuthorInstitution().add(org));
fromOrganization((org.hl7.fhir.r4.model.Organization) fhirPractRole.getOrganization().getResource()).ifPresent(org -> xdsAuthor.getAuthorInstitution().add(org));
} else if (resource instanceof Practitioner docResource) {
doc = docResource;
}
Expand Down Expand Up @@ -105,13 +90,19 @@ protected XcnName mapName(HumanName name) {
return xdsName;
}

private Optional<Organization> fromOrganization(org.hl7.fhir.r4.model.Organization fhirAuthOrg){
private Optional<Organization> fromOrganization(org.hl7.fhir.r4.model.Organization fhirAuthOrg) {
if (fhirAuthOrg != null) {
var xdsOrg = new Organization();
var identifierFirstRep = fhirAuthOrg.getIdentifierFirstRep();
if (identifierFirstRep != null && !identifierFirstRep.isEmpty()) {
xdsOrg.setIdNumber(identifierFirstRep.getValue());
xdsOrg.setAssigningAuthority(new AssigningAuthority(urnDecodedScheme(identifierFirstRep.getSystem())));

if (identifierFirstRep.getSystem() != null) {
xdsOrg.setAssigningAuthority(new AssigningAuthority(urnDecodedScheme(identifierFirstRep.getSystem())));
} else {
// without assigning authority, the ID needs to be a valid OID.
new OIDValidator().validate(identifierFirstRep.getValue());
}
}
xdsOrg.setOrganizationName(fhirAuthOrg.getName());
return Optional.of(xdsOrg);
Expand All @@ -120,7 +111,7 @@ private Optional<Organization> fromOrganization(org.hl7.fhir.r4.model.Organizati
}
}

private Identifiable fromCodeableConcept(CodeableConcept codeConcept ) {
private Identifiable fromCodeableConcept(CodeableConcept codeConcept) {
var id = new Identifiable();
var code = codeConcept.getCodingFirstRep();
if (code.getSystem() != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,7 @@
import org.hl7.fhir.r4.model.Reference;
import org.hl7.fhir.r4.model.StringType;
import org.openehealth.app.xdstofhir.registry.common.MappingSupport;
import org.openehealth.ipf.commons.ihe.xds.core.metadata.Author;
import org.openehealth.ipf.commons.ihe.xds.core.metadata.Code;
import org.openehealth.ipf.commons.ihe.xds.core.metadata.Identifiable;
import org.openehealth.ipf.commons.ihe.xds.core.metadata.Name;
import org.openehealth.ipf.commons.ihe.xds.core.metadata.Telecom;
import org.openehealth.ipf.commons.ihe.xds.core.metadata.Timestamp;
import org.openehealth.ipf.commons.ihe.xds.core.metadata.XDSMetaClass;
import org.openehealth.ipf.commons.ihe.xds.core.metadata.*;

public abstract class AbstractXdsToFhirMapper {

Expand All @@ -51,7 +45,7 @@ protected Identifier fromIdentifier(final String urnIdValue, final Identifier.Id

protected Identifier fromIdentifier(final Identifiable id) {
var identifier = new Identifier();
identifier.setSystem(OID_URN + id.getAssigningAuthority().getUniversalId());
identifier.setSystem(nullSafeConvertToUrn(id.getAssigningAuthority()));
identifier.setValue(id.getId());
return identifier;
}
Expand All @@ -77,7 +71,7 @@ protected Coding map(final Code code) {
protected Reference fromAuthor(final Author author) {
var role = new PractitionerRole();
var doc = new Practitioner();
if((author.getAuthorPerson() != null) && !author.getAuthorPerson().isEmpty()) {
if (author.getAuthorPerson() != null && !author.getAuthorPerson().isEmpty()) {
if (!author.getAuthorPerson().getName().isEmpty())
doc.setName(singletonList(fromName(author.getAuthorPerson().getName())));
if (!author.getAuthorPerson().getId().isEmpty())
Expand All @@ -95,7 +89,7 @@ protected Reference fromAuthor(final Author author) {
org.setName(xdsAuthorOrg.getOrganizationName());
if (xdsAuthorOrg.getIdNumber() != null) {
var identifier = new Identifier();
identifier.setSystem(OID_URN + xdsAuthorOrg.getAssigningAuthority().getUniversalId());
identifier.setSystem(nullSafeConvertToUrn(xdsAuthorOrg.getAssigningAuthority()));
identifier.setValue(xdsAuthorOrg.getIdNumber());
org.addIdentifier(identifier);
}
Expand All @@ -105,6 +99,10 @@ protected Reference fromAuthor(final Author author) {
return reference;
}

private static String nullSafeConvertToUrn(AssigningAuthority xdsAuthorOrg) {
return xdsAuthorOrg == null ? null : OID_URN + xdsAuthorOrg.getUniversalId();
}

protected CodeableConcept convertToCode(final Identifiable id) {
var fhirConcept = new CodeableConcept();
Coding codeing;
Expand All @@ -117,7 +115,7 @@ protected CodeableConcept convertToCode(final Identifiable id) {
return fhirConcept;
}

protected ContactPoint fromTelecom (final Telecom xdsTelecom) {
protected ContactPoint fromTelecom(final Telecom xdsTelecom) {
var cp = new ContactPoint();
if (xdsTelecom.getEmail() != null) {
cp.setSystem(ContactPointSystem.EMAIL);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,37 +1,27 @@
package org.openehealth.app.xdstofhir.registry.common.mapper;

import static java.util.Collections.singletonList;
import static org.openehealth.app.xdstofhir.registry.common.MappingSupport.MHD_COMPREHENSIVE_PROFILE;
import static org.openehealth.app.xdstofhir.registry.common.MappingSupport.toUrnCoded;
import static org.openehealth.ipf.commons.ihe.xds.core.metadata.AvailabilityStatus.APPROVED;

import java.util.List;
import java.util.function.BiFunction;

import ca.uhn.hl7v2.model.v25.datatype.XCN;
import lombok.RequiredArgsConstructor;
import org.hl7.fhir.r4.model.Address;
import org.hl7.fhir.r4.model.Base64BinaryType;
import org.hl7.fhir.r4.model.CanonicalType;
import org.hl7.fhir.r4.model.DateTimeType;
import org.hl7.fhir.r4.model.DateType;
import org.hl7.fhir.r4.model.DocumentReference;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.DocumentReference.DocumentReferenceRelatesToComponent;
import org.hl7.fhir.r4.model.Enumerations.AdministrativeGender;
import org.hl7.fhir.r4.model.Enumerations.DocumentReferenceStatus;
import org.hl7.fhir.r4.model.Identifier;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.Period;
import org.hl7.fhir.r4.model.Practitioner;
import org.hl7.fhir.r4.model.Reference;
import org.openehealth.app.xdstofhir.registry.common.MappingSupport;
import org.openehealth.app.xdstofhir.registry.common.RegistryConfiguration;
import org.openehealth.ipf.commons.ihe.xds.core.metadata.DocumentEntry;
import org.openehealth.ipf.commons.ihe.xds.core.metadata.PatientInfo;
import org.openehealth.ipf.commons.ihe.xds.core.metadata.Person;
import org.openehealth.ipf.commons.ihe.xds.core.metadata.ReferenceId;
import org.openehealth.ipf.commons.ihe.xds.core.metadata.*;
import org.openehealth.ipf.commons.map.BidiMappingService;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.function.BiFunction;

import static java.util.Collections.singletonList;
import static org.openehealth.app.xdstofhir.registry.common.MappingSupport.MHD_COMPREHENSIVE_PROFILE;
import static org.openehealth.app.xdstofhir.registry.common.MappingSupport.toUrnCoded;
import static org.openehealth.ipf.commons.ihe.xds.core.metadata.AvailabilityStatus.APPROVED;

@Component
@RequiredArgsConstructor
public class XdsToFhirDocumentMapper extends AbstractXdsToFhirMapper
Expand Down Expand Up @@ -98,10 +88,19 @@ public DocumentReference apply(final DocumentEntry xdsDoc, List<DocumentReferenc
private Reference fromAuthenticator(Person xdsAuthenticator) {
var authenticator = new Reference();
authenticator.setType(Practitioner.class.getSimpleName());
var practitoner = new Practitioner();
practitoner.setName(singletonList(fromName(xdsAuthenticator.getName())));
practitoner.addIdentifier(fromIdentifier(xdsAuthenticator.getId()));
authenticator.setResource(practitoner);
var practitioner = new Practitioner();

// XCN parser already verifies presence of either name or ID
Name<XCN> name = xdsAuthenticator.getName();
if (!name.isEmpty()) {
practitioner.setName(singletonList(fromName(name)));
}
Identifiable id = xdsAuthenticator.getId();
if (!id.isEmpty()) {
practitioner.addIdentifier(fromIdentifier(id));
}

authenticator.setResource(practitioner);
return authenticator;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -394,9 +394,9 @@ private void assignRegistryValues(XDSMetaClass xdsObject, List<Association> asso
*/
private void assignRegistryValues(List<Association> associations) {
for (var assoc : associations) {
if (!assoc.getEntryUuid().startsWith(MappingSupport.UUID_URN)) {
var previousIdentifier = assoc.getEntryUuid();
if (assoc.getEntryUuid() == null || !assoc.getEntryUuid().startsWith(MappingSupport.UUID_URN)) {
assoc.assignEntryUuid();
var previousIdentifier = assoc.getEntryUuid();
associations.stream().forEach(as -> {
as.setSourceUuid(as.getSourceUuid().replace(previousIdentifier, assoc.getEntryUuid()));
as.setTargetUuid(as.getTargetUuid().replace(previousIdentifier, assoc.getEntryUuid()));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.openehealth.app.xdstofhir.registry.common.mapper;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrowsExactly;

import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.JAXBException;
Expand All @@ -21,6 +22,8 @@
import org.openehealth.ipf.commons.ihe.xds.core.metadata.AssigningAuthority;
import org.openehealth.ipf.commons.ihe.xds.core.metadata.DocumentEntry;
import org.openehealth.ipf.commons.ihe.xds.core.metadata.Identifiable;
import org.openehealth.ipf.commons.ihe.xds.core.metadata.Organization;
import org.openehealth.ipf.commons.ihe.xds.core.validate.XDSMetaDataException;
import org.openehealth.ipf.commons.spring.map.SpringBidiMappingService;
import org.openehealth.ipf.commons.xml.XmlUtils;
import org.springframework.core.io.ClassPathResource;
Expand Down Expand Up @@ -49,6 +52,49 @@ void verifyBirectionalMapping() throws JAXBException {
verifyFhirXdsMapping(testDocument);
}

@Test
void verifyXcnWithNoIdentifierShallBeMapped() throws JAXBException {
DocumentEntry testDocument = SampleData
.createDocumentEntry(new Identifiable("123", new AssigningAuthority("2.999.3.4")));
testDocument.setUri(null);
testDocument.getLegalAuthenticator().setId(null);

verifyFhirXdsMapping(testDocument);
}

@Test
void verifyXcnWithNoNameShallBeMapped() throws JAXBException {
DocumentEntry testDocument = SampleData
.createDocumentEntry(new Identifiable("123", new AssigningAuthority("2.999.3.4")));
testDocument.setUri(null);
testDocument.getLegalAuthenticator().setName(null);

verifyFhirXdsMapping(testDocument);
}

@Test
void verifyXonWithNoAssigningAuthorityShallBeMapped() throws JAXBException {
DocumentEntry testDocument = SampleData
.createDocumentEntry(new Identifiable("123", new AssigningAuthority("2.999.3.4")));
testDocument.setUri(null);
Organization organization = testDocument.getAuthors().getFirst().getAuthorInstitution().getFirst();
organization.setIdNumber("1.2.3");
organization.setAssigningAuthority(null);

verifyFhirXdsMapping(testDocument);
}

@Test
void verifyXonWithNonOidIdentifierAndNoAssigningAuthorityShallNotBeAccepted() {
DocumentEntry testDocument = SampleData
.createDocumentEntry(new Identifiable("123", new AssigningAuthority("2.999.3.4")));
testDocument.setUri(null);
Organization organization = testDocument.getAuthors().getFirst().getAuthorInstitution().getFirst();
organization.setIdNumber("112");
organization.setAssigningAuthority(null);

assertThrowsExactly(XDSMetaDataException.class, () -> verifyFhirXdsMapping(testDocument));
}

@Test
void minimalDocumentMapping() throws JAXBException, IOException {
Expand Down

0 comments on commit b7c925a

Please sign in to comment.