From c839a6637efc23e4a9052f0ef31c8343e3ac3865 Mon Sep 17 00:00:00 2001 From: Piotr Mankowski Date: Fri, 12 Aug 2022 11:13:55 -0700 Subject: [PATCH] Continued search fix for #45 (#48) * Patient search logic update to close #45 * ID reference fix * Testing and translation fixes * Test fixes --- .../api/impl/FhirMpiClientServiceImpl.java | 1206 +++++++++-------- .../configuration/MpiClientConfiguration.java | 8 +- .../santedb/mpiclient/model/MpiPatient.java | 40 +- .../santedb/mpiclient/util/FhirUtil.java | 380 +++--- .../santedb/mpiclient/util/FhirUtilTest.java | 41 + omod/src/main/resources/config.xml | 25 +- 6 files changed, 922 insertions(+), 778 deletions(-) create mode 100644 api/src/test/java/org/openmrs/module/santedb/mpiclient/util/FhirUtilTest.java diff --git a/api/src/main/java/org/openmrs/module/santedb/mpiclient/api/impl/FhirMpiClientServiceImpl.java b/api/src/main/java/org/openmrs/module/santedb/mpiclient/api/impl/FhirMpiClientServiceImpl.java index 47b3c54..0eaa3b1 100644 --- a/api/src/main/java/org/openmrs/module/santedb/mpiclient/api/impl/FhirMpiClientServiceImpl.java +++ b/api/src/main/java/org/openmrs/module/santedb/mpiclient/api/impl/FhirMpiClientServiceImpl.java @@ -1,13 +1,10 @@ /** * Portions Copyright 2015-2018 Mohawk College of Applied Arts and Technology * Portions Copyright (c) 2014-2020 Fyfe Software Inc. - * * Licensed under the Apache License, Version 2.0 (the "License"); you * may not use this file except in compliance with the License. You may * obtain a copy of the License at - * * http://www.apache.org/licenses/LICENSE-2.0 - * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the @@ -30,7 +27,6 @@ import java.util.Set; import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.client.api.IGenericClient; @@ -90,583 +86,629 @@ @Component public class FhirMpiClientServiceImpl implements MpiClientWorker, ApplicationContextAware { - // Lock object - private Object m_lockObject = new Object(); - // Audit logger - protected AuditLogger m_logger = null; - // Log - private static Log log = LogFactory.getLog(HL7MpiClientServiceImpl.class); - // Message utility - private FhirUtil fhirUtil = FhirUtil.getInstance(); - - // Get health information exchange information - private MpiClientConfiguration m_configuration = MpiClientConfiguration.getInstance(); - - private ApplicationContext applicationContext; - - @Autowired - private PatientTranslator patientTranslator; - - /** - * Get the client as configured in this copy of the OMOD - */ - // TODO: use existing FHIR context - - private IGenericClient getClient(boolean isSearch) throws MpiClientException { - FhirContext ctx = FhirContext.forR4(); - if (null != this.m_configuration.getProxy() && !this.m_configuration.getProxy().isEmpty()) { - String[] proxyData = this.m_configuration.getProxy().split(":"); - ctx.getRestfulClientFactory().setProxy(proxyData[0], Integer.parseInt(proxyData[1])); - } - - IGenericClient client = ctx.newRestfulGenericClient(isSearch ? - this.m_configuration.getPdqEndpoint() : - this.m_configuration.getPixEndpoint()); - - client.setEncoding(EncodingEnum.JSON); - ctx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER); - - // Is an IDP provided? - if (this.m_configuration.getIdentityProviderUrl() != null - && !this.m_configuration.getIdentityProviderUrl().isEmpty() - && "oauth".equals(this.m_configuration.getAuthenticationMode())) { - - // Call the IDP - CloseableHttpClient oauthClientCredentialsClient = HttpClientBuilder.create().build(); - - try { - HttpPost post = new HttpPost(this.m_configuration.getIdentityProviderUrl()); - post.addHeader("Content-Type", "application/x-www-form-urlencoded"); - - // HACK: SanteMPI requires either X.509 node authentication (configured via JKS) - // but can also use the X-Device-Authorization header - // Since the JKS / X.509 node authentication is not supported, we'll have to use - // the X-DeviceAuthorization - String clientSecret = this.m_configuration.getMsh8Security(), deviceSecret = null; - if (clientSecret.contains("+")) { - String[] clientParts = clientSecret.split("\\+"); - clientSecret = clientParts[1]; - deviceSecret = clientParts[0]; - - // Now append the proper header for device authentication - post.addHeader("X-Device-Authorization", - String.format("basic %s", - Base64.getEncoder() - .encodeToString(String - .format("%s|%s:%s", this.m_configuration.getLocalApplication(), - this.m_configuration.getLocalFacility(), deviceSecret) - .getBytes()))); - } - - post.setEntity(new StringEntity( - String.format("client_id=%s&client_secret=%s&grant_type=client_credentials&socpe=*", - this.m_configuration.getLocalApplication(), clientSecret))); - - HttpResponse response = oauthClientCredentialsClient.execute(post); - if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { - InputStream inStream = response.getEntity().getContent(); - try { - Reader reader = new InputStreamReader(inStream); - String jsonText = CharStreams.toString(reader); - JsonParser parser = new JsonParser(); - JsonObject oauthResponse = parser.parse(jsonText).getAsJsonObject(); - String token = oauthResponse.get("access_token").getAsString(); - log.warn(String.format("Using token: %s", token)); - client.registerInterceptor(new BearerTokenAuthInterceptor(token)); - - } finally { - inStream.close(); - } - } else - throw new Exception(String.format("Identity provider responded with %s", - response.getStatusLine().getStatusCode())); - - } catch (Exception e) { - // TODO Auto-generated catch block - e.printStackTrace(); - throw new MpiClientException( - String.format("Could not authenticate client %s", this.m_configuration.getLocalApplication()), - e); - } finally { - try { - oauthClientCredentialsClient.close(); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - } - // Basic Auth - else if ("basic".equals(this.m_configuration.getAuthenticationMode())) { - client.registerInterceptor(new BasicAuthInterceptor(this.m_configuration.getLocalApplication(), this.m_configuration.getMsh8Security())); - } - return client; - } - - /** - * Search for patients in the MPI - */ - @Override - public List searchPatient(String familyName, String givenName, Date dateOfBirth, boolean fuzzyDate, - String gender, String stateOrRegion, String cityOrTownship, PatientIdentifier patientIdentifier, - PatientIdentifier mothersIdentifier, String nextOfKinName, String birthPlace, - Map otherDataPoints) throws MpiClientException { - Set patientIdentifiers = new HashSet<>(); - patientIdentifiers.add(patientIdentifier); - - return searchPatient(familyName, givenName, dateOfBirth, fuzzyDate, gender, stateOrRegion, cityOrTownship, - patientIdentifiers, mothersIdentifier, nextOfKinName, birthPlace, otherDataPoints); - } - - @Override - public List searchPatient(String familyName, String givenName, Date dateOfBirth, boolean fuzzyDate, String gender, - String stateOrRegion, String cityOrTownship, Set patientIdentifiers, - PatientIdentifier mothersIdentifier, String nextOfKinName, String birthPlace, - Map otherDataPoints) throws MpiClientException { - - IQuery query = loadSearchQuery(familyName, givenName, dateOfBirth, fuzzyDate, gender, - stateOrRegion, cityOrTownship, patientIdentifiers, mothersIdentifier, nextOfKinName, birthPlace, otherDataPoints); - - // Send the message and construct the result set - return getMpiPatientMatches(query); - } - - private List getMpiPatientMatches(IQuery query) throws MpiClientException { - try { - - Bundle results = query.returnBundle(Bundle.class).execute(); - - List retVal = new ArrayList<>(); - Map patientMap = new HashMap<>(); - for (BundleEntryComponent result : results.getEntry()) { - org.hl7.fhir.r4.model.Patient pat; - if(result.hasResource() && result.getResource().hasType("Patient")){ - pat = (org.hl7.fhir.r4.model.Patient) result.getResource(); - patientMap.put(result.getResource().getId(),pat); - // TODO: Remove hard-coded "golden record" code 5c827da5-4858-4f3d-a50c-62ece001efea - if (pat.hasMeta() - && pat.getMeta().hasTag() - && pat.getMeta().getTagFirstRep().hasCode() - && pat.getMeta().getTagFirstRep().getCode().equals("5c827da5-4858-4f3d-a50c-62ece001efea") - && pat.hasLink()) { - for(PatientLinkComponent goldenRecordEntry : pat.getLink()) { - if(goldenRecordEntry.getOther().getResource() != null) { - MpiPatient mpiPatient = fhirUtil.parseFhirPatient( - (org.hl7.fhir.r4.model.Patient) goldenRecordEntry.getOther().getResource()); - retVal.add(mpiPatient); - } + // Lock object + private final Object m_lockObject = new Object(); + + // Audit logger + protected AuditLogger m_logger = null; + + // Log + private static final Log log = LogFactory.getLog(HL7MpiClientServiceImpl.class); + + // Get health information exchange information + private final MpiClientConfiguration m_configuration = MpiClientConfiguration.getInstance(); + + private ApplicationContext applicationContext; + + @Autowired + private PatientTranslator patientTranslator; + + @Autowired + private FhirUtil fhirUtil; + /** + * Get the client as configured in this copy of the OMOD + */ + // TODO: use existing FHIR context + private IGenericClient getClient(boolean isSearch) throws MpiClientException { + FhirContext ctx = FhirContext.forR4(); + if (null != this.m_configuration.getProxy() && !this.m_configuration.getProxy().isEmpty()) { + String[] proxyData = this.m_configuration.getProxy().split(":"); + ctx.getRestfulClientFactory().setProxy(proxyData[0], Integer.parseInt(proxyData[1])); + } + + IGenericClient client = ctx.newRestfulGenericClient(isSearch ? + this.m_configuration.getPdqEndpoint() : + this.m_configuration.getPixEndpoint()); + + client.setEncoding(EncodingEnum.JSON); + ctx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER); + + // Is an IDP provided? + if (this.m_configuration.getIdentityProviderUrl() != null + && !this.m_configuration.getIdentityProviderUrl().isEmpty() + && "oauth".equals(this.m_configuration.getAuthenticationMode())) { + + // Call the IDP + CloseableHttpClient oauthClientCredentialsClient = HttpClientBuilder.create().build(); + + try { + HttpPost post = new HttpPost(this.m_configuration.getIdentityProviderUrl()); + post.addHeader("Content-Type", "application/x-www-form-urlencoded"); + + // HACK: SanteMPI requires either X.509 node authentication (configured via JKS) + // but can also use the X-Device-Authorization header + // Since the JKS / X.509 node authentication is not supported, we'll have to use + // the X-DeviceAuthorization + String clientSecret = this.m_configuration.getMsh8Security(), deviceSecret = null; + if (clientSecret.contains("+")) { + String[] clientParts = clientSecret.split("\\+"); + clientSecret = clientParts[1]; + deviceSecret = clientParts[0]; + + // Now append the proper header for device authentication + post.addHeader("X-Device-Authorization", + String.format("basic %s", + Base64.getEncoder() + .encodeToString(String + .format("%s|%s:%s", this.m_configuration.getLocalApplication(), + this.m_configuration.getLocalFacility(), deviceSecret) + .getBytes()))); + } + + post.setEntity(new StringEntity( + String.format("client_id=%s&client_secret=%s&grant_type=client_credentials&scope=*", + this.m_configuration.getLocalApplication(), clientSecret))); + + HttpResponse response = oauthClientCredentialsClient.execute(post); + if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { + InputStream inStream = response.getEntity().getContent(); + try { + Reader reader = new InputStreamReader(inStream); + String jsonText = CharStreams.toString(reader); + JsonParser parser = new JsonParser(); + JsonObject oauthResponse = parser.parse(jsonText).getAsJsonObject(); + String token = oauthResponse.get("access_token").getAsString(); + log.warn(String.format("Using token: %s", token)); + client.registerInterceptor(new BearerTokenAuthInterceptor(token)); + + } + finally { + inStream.close(); + } + } else + throw new Exception(String.format("Identity provider responded with %s", + response.getStatusLine().getStatusCode())); + + } + catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + throw new MpiClientException( + String.format("Could not authenticate client %s", this.m_configuration.getLocalApplication()), + e); + } + finally { + try { + oauthClientCredentialsClient.close(); + } + catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + // Basic Auth + else if ("basic".equals(this.m_configuration.getAuthenticationMode())) { + client.registerInterceptor(new BasicAuthInterceptor(this.m_configuration.getLocalApplication(), + this.m_configuration.getMsh8Security())); + } + return client; + } + + /** + * Search for patients in the MPI + */ + @Override + public List searchPatient(String familyName, String givenName, Date dateOfBirth, boolean fuzzyDate, + String gender, String stateOrRegion, String cityOrTownship, PatientIdentifier patientIdentifier, + PatientIdentifier mothersIdentifier, String nextOfKinName, String birthPlace, + Map otherDataPoints) throws MpiClientException { + Set patientIdentifiers = new HashSet<>(); + patientIdentifiers.add(patientIdentifier); + + return searchPatient(familyName, givenName, dateOfBirth, fuzzyDate, gender, stateOrRegion, cityOrTownship, + patientIdentifiers, mothersIdentifier, nextOfKinName, birthPlace, otherDataPoints); + } + + @Override + public List searchPatient(String familyName, String givenName, Date dateOfBirth, boolean fuzzyDate, + String gender, + String stateOrRegion, String cityOrTownship, Set patientIdentifiers, + PatientIdentifier mothersIdentifier, String nextOfKinName, String birthPlace, + Map otherDataPoints) throws MpiClientException { + + IQuery query = loadSearchQuery(familyName, givenName, dateOfBirth, fuzzyDate, gender, + stateOrRegion, cityOrTownship, patientIdentifiers, mothersIdentifier, nextOfKinName, birthPlace, + otherDataPoints); + + // Send the message and construct the result set + return getMpiPatientMatches(query); + } + + private List getMpiPatientMatches(IQuery query) throws MpiClientException { + try { + + Bundle results = query.returnBundle(Bundle.class).execute(); + + List goldenRecordUuids = new ArrayList<>(); + List retVal = new ArrayList<>(); + Map patientMap = new HashMap<>(); + // First stage - loop through result set to get golden record uuids + for (BundleEntryComponent result : results.getEntry()) { + org.hl7.fhir.r4.model.Patient pat; + if (result.hasResource() && result.getResource().hasType("Patient")) { + pat = (org.hl7.fhir.r4.model.Patient) result.getResource(); + String grId = null; + if (pat.hasLink() && pat.getLink().size() == 1 && pat.getLinkFirstRep().hasOther()) { + grId = pat.getLinkFirstRep().getOther().getReferenceElement().getIdPart(); + } + // Create a list with all of the unique IDs for the golden records. + if (grId != null && !goldenRecordUuids.contains(grId)) + goldenRecordUuids.add(grId); + } + } + + if (!goldenRecordUuids.isEmpty()) { + results = goldenRecordSetQuery(goldenRecordUuids).returnBundle(Bundle.class).execute(); + + for (BundleEntryComponent result : results.getEntry()) { + org.hl7.fhir.r4.model.Patient gr = (org.hl7.fhir.r4.model.Patient) result.getResource(); + if (gr != null && gr.hasType("Patient") && gr.hasMeta() + && gr.getMeta().hasTag() + && gr.getMeta().getTagFirstRep().hasCode() + && gr.getMeta().getTagFirstRep().getCode().equals(m_configuration.getGoldenRecordUuid()) + && gr.hasLink()) { + for (PatientLinkComponent grPatLink : gr.getLink()) { + if (grPatLink.getOther().getResource() != null) { + + MpiPatient mpiPatient = fhirUtil.parseFhirPatient((org.hl7.fhir.r4.model.Patient) grPatLink.getOther().getResource(), patientTranslator.toOpenmrsType( + (org.hl7.fhir.r4.model.Patient) grPatLink.getOther().getResource())); + + retVal.add(mpiPatient); + } + } + + } + } + } + return retVal; + } + catch ( + + Exception e) { + e.printStackTrace(); + log.error("Error in FHIR Search", e); + log.error(ExceptionUtils.getFullStackTrace(e)); + throw new MpiClientException(e); + } + finally { + } + + } + + private IQuery loadSearchQuery(String familyName, String givenName, Date dateOfBirth, boolean fuzzyDate, + String gender, String stateOrRegion, String cityOrTownship, + Set patientIdentifiers, + PatientIdentifier mothersIdentifier, + String nextOfKinName, String birthPlace, + Map otherDataPoints) throws MpiClientException { + IQuery query = this.getClient(true).search().forResource("Patient"); + + if (familyName != null && !familyName.isEmpty()) + query = query.where(org.hl7.fhir.r4.model.Patient.FAMILY.contains().value(familyName)); + if (givenName != null && !givenName.isEmpty()) + query = query.where(org.hl7.fhir.r4.model.Patient.GIVEN.contains().value(givenName)); + if (dateOfBirth != null) { + if (fuzzyDate) { + if (this.m_configuration.getPdqDateFuzz() == 0) { + query = query.where( + org.hl7.fhir.r4.model.Patient.BIRTHDATE.after().day(new Date(dateOfBirth.getYear(), 0, 1))); + query = query.where(org.hl7.fhir.r4.model.Patient.BIRTHDATE.before() + .day(new Date(dateOfBirth.getYear(), 11, 31))); + } else { + query = query.where(org.hl7.fhir.r4.model.Patient.BIRTHDATE.after() + .day(new Date(dateOfBirth.getYear() - this.m_configuration.getPdqDateFuzz(), 0, 1))); + query = query.where(org.hl7.fhir.r4.model.Patient.BIRTHDATE.before() + .day(new Date(dateOfBirth.getYear() + this.m_configuration.getPdqDateFuzz(), 11, 31))); + } + } else + query = query.where(org.hl7.fhir.r4.model.Patient.BIRTHDATE.exactly().day(dateOfBirth)); + } + + if (gender != null && !gender.isEmpty()) + query = query.where(org.hl7.fhir.r4.model.Patient.GENDER.exactly().code(gender)); + + if (patientIdentifiers != null) { + for (PatientIdentifier patientIdentifier : patientIdentifiers) { + if (patientIdentifier.getIdentifierType() != null) { + + HashMap localPatientIdentifierTypeMap = this.m_configuration.getLocalPatientIdentifierTypeMap(); + String authority = localPatientIdentifierTypeMap + .get(patientIdentifier.getIdentifierType().getName()); + if (authority == null) + throw new MpiClientException( + String.format("Identity domain %s doesn't have an equivalent in the MPI configuration", + patientIdentifier.getIdentifierType().getName())); + query = query.where(org.hl7.fhir.r4.model.Patient.IDENTIFIER.exactly() + .systemAndIdentifier(patientIdentifier.getIdentifier(), authority)); + } else { + // Skip the search by the identifier + } + } + } + String localBiometricSubjectId = (String) otherDataPoints.get("localBiometricSubjectId"); + String nationalBiometricSubjectId = (String) otherDataPoints.get("nationalBiometricSubjectId"); + String phoneNumber = (String) otherDataPoints.get("phoneNumber"); + + if (StringUtils.isNotBlank(localBiometricSubjectId)) { + query = query.where(org.hl7.fhir.r4.model.Patient.IDENTIFIER.exactly() + .systemAndIdentifier(localBiometricSubjectId, "Biometrics Reference Code")); + } + if (StringUtils.isNotBlank(nationalBiometricSubjectId)) { + query = query.where(org.hl7.fhir.r4.model.Patient.IDENTIFIER.exactly() + .systemAndIdentifier(nationalBiometricSubjectId, "Biometrics National Reference Code")); + } + + if (StringUtils.isNotBlank(phoneNumber)) { + query = query.where(org.hl7.fhir.r4.model.Patient.PHONE.exactly().identifier(phoneNumber)); + } + + if (stateOrRegion != null && !stateOrRegion.isEmpty()) + query = query.where(org.hl7.fhir.r4.model.Patient.ADDRESS_STATE.contains().value(stateOrRegion)); + if (cityOrTownship != null && !cityOrTownship.isEmpty()) + query = query.where(org.hl7.fhir.r4.model.Patient.ADDRESS_CITY.contains().value(cityOrTownship)); + // query.include(new Include("Patient:link")); + + return query; + } + + // Queries the Client Registry for a set of golden record uuids to get the records and associated Patients + private IQuery goldenRecordSetQuery(List grUuids) throws MpiClientException { + + String uuidString = String.join(",", grUuids); + + IQuery query = this.getClient(true).search() + .byUrl("Patient?_id=" + uuidString + "&_include=Patient:link"); + + return query; + } + + /** + * Retrieves a specific patient from the MPI given their identifier + */ + @Override + public MpiPatient getPatient(String identifier, String assigningAuthority) throws MpiClientException { + + // Send the message and construct the result set + try { + Bundle results = this + .getClient(true).search().forResource("Patient").where(org.hl7.fhir.r4.model.Patient.IDENTIFIER + // .exactly().systemAndIdentifier(assigningAuthority, identifier)) + .exactly().identifier(identifier)) + .count(1).returnBundle(Bundle.class).execute(); + + for (BundleEntryComponent result : results.getEntry()) { + org.hl7.fhir.r4.model.Patient pat = (org.hl7.fhir.r4.model.Patient) result.getResource(); + MpiPatient mpiPatient = fhirUtil.parseFhirPatient(pat, patientTranslator.toOpenmrsType(pat)); + + return mpiPatient; + } + return null; // no results + } + catch (Exception e) { + e.printStackTrace(); + log.error("Error in PDQ Search", e); + throw new MpiClientException(e); + } + } + + /** + * Resolve patient identifier in the specified identity domain + */ + @Override + public PatientIdentifier resolvePatientIdentifier(Patient patient, String toAssigningAuthority) + throws MpiClientException { + // Send the message and construct the result set + try { + + String identifier = null, assigningAuthority = null; + // Preferred correlation identifier + if (!this.m_configuration.getPreferredCorrelationDomain().isEmpty()) { + for (PatientIdentifier pid : patient.getIdentifiers()) { + String domain = this.m_configuration.getLocalPatientIdentifierTypeMap() + .get(pid.getIdentifierType().getName()); + if (this.m_configuration.getPreferredCorrelationDomain().equals(domain)) { + identifier = pid.getIdentifier(); + assigningAuthority = domain; + break; + } + } + } else // use local identity + { + identifier = patient.getId().toString(); + assigningAuthority = this.m_configuration.getLocalPatientIdRoot(); + } + + // No identity domains to xref with + if (identifier == null || assigningAuthority == null) { + log.warn(String.format("Patient %s has no good cross reference identities to use", patient.getId())); + return null; + } + + Bundle results = this + .getClient(true).search().forResource("Patient").where(org.hl7.fhir.r4.model.Patient.IDENTIFIER + .exactly().systemAndIdentifier(assigningAuthority, identifier)) + .count(2).returnBundle(Bundle.class).execute(); + // ASSERT: Only 1 result + if (results.getEntry().size() != 1) + throw new MpiClientException( + String.format("Found ambiguous matches (%s matches) on MPI, can't reliably xref this patient", + results.getTotal())); + + // Is there a result? + for (BundleEntryComponent result : results.getEntry()) { + org.hl7.fhir.r4.model.Patient pat = (org.hl7.fhir.r4.model.Patient) result.getResource(); + + // Is this patient linked to another patient? + if (pat.getLink() != null) + for (PatientLinkComponent lnk : pat.getLink()) { + if (LinkType.REFER.equals(lnk.getType())) { + pat = (org.hl7.fhir.r4.model.Patient) lnk.getOtherTarget(); + } + } + + MpiPatient mpiPatient = fhirUtil.parseFhirPatient(pat, patientTranslator.toOpenmrsType(pat)); + + // Now look for the identity domain we want to xref to + for (PatientIdentifier pid : mpiPatient.getIdentifiers()) { + String domain = this.m_configuration.getLocalPatientIdentifierTypeMap() + .get(pid.getIdentifierType().getName()); + if (toAssigningAuthority.equals(domain)) + return pid; + } + return null; + } + return null; // no results + } + catch (Exception e) { + e.printStackTrace(); + log.error("Error in PDQ Search", e); + throw new MpiClientException(e); + } + } + + /** + * Imports patient from MPI? Was in not implemented due to lack of FHIR create capabilites in OpenMRS? + * + * @param patient + * @return + * @throws MpiClientException + */ + @Override + public Patient importPatient(MpiPatient patient) throws MpiClientException { + // TODO Auto-generated method stub + return null; + } + + /** + * Sends a patient to the MPI in FHIR format + */ + @Override + public void exportPatient(MpiPatientExport patientExport) throws MpiClientException { + org.hl7.fhir.r4.model.Patient admitMessage = null; + + try { + admitMessage = patientTranslator.toFhirResource(patientExport.getPatient()); + admitMessage.getNameFirstRep().setUse(HumanName.NameUse.OFFICIAL); + + // Set mother's name + if (patientExport.getMothersMaidenName() != null) { + Extension mothersMaidenName = new Extension(); + mothersMaidenName.setUrl("http://hl7.org/fhir/StructureDefinition/patient-mothersMaidenName"); + mothersMaidenName.setValue(new StringType(patientExport.getMothersMaidenName().getValue())); + admitMessage.addExtension(mothersMaidenName); + } + // Set Patient Phone number + PersonAttribute patientTelephone = patientExport.getPatientTelephone(); + if (patientTelephone != null) { + ContactPoint contactPoint = new ContactPoint(); + contactPoint.setId(patientTelephone.getUuid()); + contactPoint.setValue(patientTelephone.getValue()); + contactPoint.setSystem(ContactPoint.ContactPointSystem.PHONE); + contactPoint.setUse(ContactPoint.ContactPointUse.MOBILE); + admitMessage.addTelecom(contactPoint); + } + + // Patient Obs processing + Set patientObs = patientExport.getPatientObs(); + if (patientObs != null) { + for (Obs obs : patientObs) { + switch (obs.getConcept().getConceptId()) { + case 165194: {//Place of birth address construct + // 165195=>locality, 165198=>country of residence, 1354=>village, 165197=>province, 165196=>communal section, 162725=> address + Extension birthplace = new Extension(); + birthplace.setUrl("http://hl7.org/fhir/StructureDefinition/patient-birthPlace"); + Address address = parseAddress(obs); + birthplace.setValue(address); + admitMessage.addExtension(birthplace); + break; + } + case 165210: + case 165213: + case 165212: {//Emergency contact construct, Primary medical disclosure construct,secondary medical disclosure construct + // 159635=> phone number, 164352=> relationship to patient, 163258 => name of contact, + // 165196 => communal section, 165195=> locality, 165198=> country of residence, 1354=> village, 165197=> province, 162725=> address + admitMessage.addContact(translatePatientContact(obs)); + break; } - } - } - } - return retVal; - } catch (Exception e) { - e.printStackTrace(); - log.error("Error in FHIR Search", e); - log.error(ExceptionUtils.getFullStackTrace(e)); - throw new MpiClientException(e); - } finally { - } - } - - private IQuery loadSearchQuery(String familyName, String givenName, Date dateOfBirth, boolean fuzzyDate, - String gender, String stateOrRegion, String cityOrTownship, - Set patientIdentifiers, - PatientIdentifier mothersIdentifier, - String nextOfKinName, String birthPlace, - Map otherDataPoints) throws MpiClientException { - IQuery query = this.getClient(true).search().forResource("Patient"); - - if (familyName != null && !familyName.isEmpty()) - query = query.where(org.hl7.fhir.r4.model.Patient.FAMILY.contains().value(familyName)); - if (givenName != null && !givenName.isEmpty()) - query = query.where(org.hl7.fhir.r4.model.Patient.GIVEN.contains().value(givenName)); - if (dateOfBirth != null) { - if (fuzzyDate) { - if (this.m_configuration.getPdqDateFuzz() == 0) { - query = query.where( - org.hl7.fhir.r4.model.Patient.BIRTHDATE.after().day(new Date(dateOfBirth.getYear(), 0, 1))); - query = query.where(org.hl7.fhir.r4.model.Patient.BIRTHDATE.before() - .day(new Date(dateOfBirth.getYear(), 11, 31))); - } else { - query = query.where(org.hl7.fhir.r4.model.Patient.BIRTHDATE.after() - .day(new Date(dateOfBirth.getYear() - this.m_configuration.getPdqDateFuzz(), 0, 1))); - query = query.where(org.hl7.fhir.r4.model.Patient.BIRTHDATE.before() - .day(new Date(dateOfBirth.getYear() + this.m_configuration.getPdqDateFuzz(), 11, 31))); - } - } else - query = query.where(org.hl7.fhir.r4.model.Patient.BIRTHDATE.exactly().day(dateOfBirth)); - } - - if (gender != null && !gender.isEmpty()) - query = query.where(org.hl7.fhir.r4.model.Patient.GENDER.exactly().code(gender)); - - if (patientIdentifiers != null) { - for (PatientIdentifier patientIdentifier : patientIdentifiers) { - if (patientIdentifier.getIdentifierType() != null) { - - HashMap localPatientIdentifierTypeMap = this.m_configuration.getLocalPatientIdentifierTypeMap(); - String authority = localPatientIdentifierTypeMap - .get(patientIdentifier.getIdentifierType().getName()); - if (authority == null) - throw new MpiClientException( - String.format("Identity domain %s doesn't have an equivalent in the MPI configuration", - patientIdentifier.getIdentifierType().getName())); - query = query.where(org.hl7.fhir.r4.model.Patient.IDENTIFIER.exactly() - .systemAndIdentifier(patientIdentifier.getIdentifier(), authority)); - } else { -// Skip the search by the identifier - } - } - } - String localBiometricSubjectId = (String) otherDataPoints.get("localBiometricSubjectId"); - String nationalBiometricSubjectId = (String) otherDataPoints.get("nationalBiometricSubjectId"); - String phoneNumber = (String) otherDataPoints.get("phoneNumber"); - - if (StringUtils.isNotBlank(localBiometricSubjectId)) { - query = query.where(org.hl7.fhir.r4.model.Patient.IDENTIFIER.exactly() - .systemAndIdentifier(localBiometricSubjectId, "Biometrics Reference Code")); - } - if (StringUtils.isNotBlank(nationalBiometricSubjectId)) { - query = query.where(org.hl7.fhir.r4.model.Patient.IDENTIFIER.exactly() - .systemAndIdentifier(nationalBiometricSubjectId, "Biometrics National Reference Code")); - } - - if (StringUtils.isNotBlank(phoneNumber)) { - query = query.where(org.hl7.fhir.r4.model.Patient.PHONE.exactly().identifier(phoneNumber)); - } - - - if (stateOrRegion != null && !stateOrRegion.isEmpty()) - query = query.where(org.hl7.fhir.r4.model.Patient.ADDRESS_STATE.contains().value(stateOrRegion)); - if (cityOrTownship != null && !cityOrTownship.isEmpty()) - query = query.where(org.hl7.fhir.r4.model.Patient.ADDRESS_CITY.contains().value(cityOrTownship)); - query.include(new Include("Patient:link")); - - return query; - } - - - /** - * Retrieves a specific patient from the MPI given their identifier - */ - @Override - public MpiPatient getPatient(String identifier, String assigningAuthority) throws MpiClientException { - - // Send the message and construct the result set - try { - Bundle results = this - .getClient(true).search().forResource("Patient").where(org.hl7.fhir.r4.model.Patient.IDENTIFIER -// .exactly().systemAndIdentifier(assigningAuthority, identifier)) - .exactly().identifier(identifier)) - .count(1).returnBundle(Bundle.class).execute(); - - for (BundleEntryComponent result : results.getEntry()) { - org.hl7.fhir.r4.model.Patient pat = (org.hl7.fhir.r4.model.Patient) result.getResource(); - MpiPatient mpiPatient = fhirUtil.parseFhirPatient(pat); - return mpiPatient; - } - return null; // no results - } catch (Exception e) { - e.printStackTrace(); - log.error("Error in PDQ Search", e); - throw new MpiClientException(e); - } finally { - } - } - - /** - * Resolve patient identifier in the specified identity domain - */ - @Override - public PatientIdentifier resolvePatientIdentifier(Patient patient, String toAssigningAuthority) - throws MpiClientException { - // Send the message and construct the result set - try { - - String identifier = null, assigningAuthority = null; - // Preferred correlation identifier - if (!this.m_configuration.getPreferredCorrelationDomain().isEmpty()) { - for (PatientIdentifier pid : patient.getIdentifiers()) { - String domain = this.m_configuration.getLocalPatientIdentifierTypeMap().get(pid.getIdentifierType().getName()); - if (this.m_configuration.getPreferredCorrelationDomain().equals(domain)) { - identifier = pid.getIdentifier(); - assigningAuthority = domain; - break; - } - } - } else // use local identity - { - identifier = patient.getId().toString(); - assigningAuthority = this.m_configuration.getLocalPatientIdRoot(); - } - - // No identity domains to xref with - if (identifier == null || assigningAuthority == null) { - log.warn(String.format("Patient %s has no good cross reference identities to use", patient.getId())); - return null; - } - - Bundle results = this - .getClient(true).search().forResource("Patient").where(org.hl7.fhir.r4.model.Patient.IDENTIFIER - .exactly().systemAndIdentifier(assigningAuthority, identifier)) - .count(2).returnBundle(Bundle.class).execute(); - // ASSERT: Only 1 result - if (results.getEntry().size() != 1) - throw new MpiClientException(String.format("Found ambiguous matches (%s matches) on MPI, can't reliably xref this patient", results.getTotal())); - - // Is there a result? - for (BundleEntryComponent result : results.getEntry()) { - org.hl7.fhir.r4.model.Patient pat = (org.hl7.fhir.r4.model.Patient) result.getResource(); - - // Is this patient linked to another patient? - if (pat.getLink() != null) - for (PatientLinkComponent lnk : pat.getLink()) { - if (LinkType.REFER.equals(lnk.getType())) { - pat = (org.hl7.fhir.r4.model.Patient) lnk.getOtherTarget(); - } - } - - MpiPatient mpiPatient = fhirUtil.parseFhirPatient(pat); - // Now look for the identity domain we want to xref to - for (PatientIdentifier pid : mpiPatient.getIdentifiers()) { - String domain = this.m_configuration.getLocalPatientIdentifierTypeMap().get(pid.getIdentifierType().getName()); - if (toAssigningAuthority.equals(domain)) - return pid; - } - return null; - } - return null; // no results - } catch (Exception e) { - e.printStackTrace(); - log.error("Error in PDQ Search", e); - throw new MpiClientException(e); - } finally { - } - } - - /** - * Imports patient from MPI? Was in not implemented due to lack of FHIR create capabilites in OpenMRS? - * - * @param patient - * @return - * @throws MpiClientException - */ - @Override - public Patient importPatient(MpiPatient patient) throws MpiClientException { - // TODO Auto-generated method stub - return null; - } - - - /** - * Sends a patient to the MPI in FHIR format - */ - @Override - public void exportPatient(MpiPatientExport patientExport) throws MpiClientException { - org.hl7.fhir.r4.model.Patient admitMessage = null; - - try { - admitMessage = patientTranslator.toFhirResource(patientExport.getPatient()); - admitMessage.getNameFirstRep().setUse(HumanName.NameUse.OFFICIAL); - - // Set mother's name - if (patientExport.getMothersMaidenName() != null) { - Extension mothersMaidenName = new Extension(); - mothersMaidenName.setUrl("http://hl7.org/fhir/StructureDefinition/patient-mothersMaidenName"); - mothersMaidenName.setValue(new StringType(patientExport.getMothersMaidenName().getValue())); - admitMessage.addExtension(mothersMaidenName); - } - // Set Patient Phone number - PersonAttribute patientTelephone = patientExport.getPatientTelephone(); - if (patientTelephone != null) { - ContactPoint contactPoint = new ContactPoint(); - contactPoint.setId(patientTelephone.getUuid()); - contactPoint.setValue(patientTelephone.getValue()); - contactPoint.setSystem(ContactPoint.ContactPointSystem.PHONE); - contactPoint.setUse(ContactPoint.ContactPointUse.MOBILE); - admitMessage.addTelecom(contactPoint); - } - - -// Patient Obs processing - Set patientObs = patientExport.getPatientObs(); - if (patientObs != null) { - for (Obs obs : patientObs) { - switch (obs.getConcept().getConceptId()) { - case 165194: {//Place of birth address construct -// 165195=>locality, 165198=>country of residence, 1354=>village, 165197=>province, 165196=>communal section, 162725=> address - Extension birthplace = new Extension(); - birthplace.setUrl("http://hl7.org/fhir/StructureDefinition/patient-birthPlace"); - Address address = parseAddress(obs); - birthplace.setValue(address); - admitMessage.addExtension(birthplace); - break; - } - case 165210: - case 165213: - case 165212: {//Emergency contact construct, Primary medical disclosure construct,secondary medical disclosure construct -// 159635=> phone number, 164352=> relationship to patient, 163258 => name of contact, -// 165196 => communal section, 165195=> locality, 165198=> country of residence, 1354=> village, 165197=> province, 162725=> address - admitMessage.addContact(translatePatientContact(obs)); - break; - } - } - } - } - - IGenericClient client = this.getClient(false); - MethodOutcome result = client.create().resource(admitMessage).execute(); - - if (!result.getCreated()) - throw new MpiClientException( - String.format("Error from MPI :> %s", result.getResource().getClass().getName())); - - } catch (FhirClientConnectionException e) { - log.error("Error in FHIR PIX message", e); - } catch (MpiClientException e) { - log.error("Error in FHIR PIX message", e); - e.printStackTrace(); - throw e; - } catch (Exception e) { - e.printStackTrace(); - log.error(e); - } finally { - } - - } - - private Address parseAddress(Obs obs) { - Address fhirAddress = new Address(); - fhirAddress.setUse(Address.AddressUse.HOME); - obs.getGroupMembers().forEach(member -> { - switch (member.getConcept().getConceptId()) { - case 165197: { -// Province of residence - fhirAddress.setState(member.getValueText()); - break; - } - case 165196: { -// Communal section - fhirAddress.addExtension("Communal section", new StringType(member.getValueText())); - break; - } - case 165198: { -// Country - fhirAddress.setCountry(member.getValueText()); - break; - } - case 162725: { -// Address (text) - fhirAddress.addExtension("Address Text", new StringType(member.getValueText())); - break; - } - case 1354: { -// Village - fhirAddress.setCity(member.getValueText()); - break; - } - case 165195: { -// Locality - fhirAddress.addExtension("Locality", new StringType(member.getValueText())); - break; - } - default: { -// Do nothing - } - } - }); - - return fhirAddress; - } - - private org.hl7.fhir.r4.model.Patient.ContactComponent translatePatientContact(org.openmrs.Obs patientOb) { - Set contactMembers = patientOb.getGroupMembers(false); -// Add patient contact - - org.hl7.fhir.r4.model.Patient.ContactComponent contactComponent = new org.hl7.fhir.r4.model.Patient.ContactComponent(); - - Address address = parseAddress(patientOb); - contactComponent.setAddress(address); - - Reference reference = new Reference(); - reference.setDisplay(patientOb.getConcept().getName().getName()); - contactComponent.setOrganization(reference); - - - for (org.openmrs.Obs cm : contactMembers) { - if (cm.getConcept().getConceptId() == 163258) { -// Process contact name - HumanName contactName = new HumanName(); - String[] names = cm.getValueText().split(" "); - - if (names.length > 1) { - contactName.setFamily(names[1]); - List ns = new ArrayList() {{ - add(new StringType(names[0])); - }}; - contactName.setGiven(ns); - } else if (names.length == 1) { - contactName.setFamily(names[0]); - } - contactComponent.setName(contactName); - } else if (cm.getConcept().getConceptId() == 159635) { -// Process contact's phone number - ContactPoint telco = new ContactPoint(); - telco.setSystem(ContactPoint.ContactPointSystem.PHONE); - telco.setValue(cm.getValueText()); - telco.setUse(ContactPoint.ContactPointUse.MOBILE); - List contactPoints = new ArrayList() {{ - add(telco); - }}; - contactComponent.setTelecom(contactPoints); - } else if (cm.getConcept().getConceptId() == 164352) { -// Process relationship to patient - CodeableConcept concept = new CodeableConcept(); - concept.setText(cm.getValueCoded().getName().getName()); - contactComponent.addRelationship(concept); - } - } - return contactComponent; - } - - - /** - * Updates the MPI patient information with local patient information - */ - @Override - public void updatePatient(MpiPatientExport patientExport) throws MpiClientException { - org.hl7.fhir.r4.model.Patient admitMessage = null; - - try { - admitMessage = patientTranslator.toFhirResource(patientExport.getPatient()); - admitMessage.getNameFirstRep().setUse(HumanName.NameUse.OFFICIAL); - // Temporary URI identifier - admitMessage.addIdentifier().setSystem("urn:ietf:rfc:3986").setValue(this.m_configuration.getLocalPatientIdRoot() + patientExport.getPatient().getUuid()); - IGenericClient client = this.getClient(false); - MethodOutcome result = client.update().resource(admitMessage).execute(); - if (!result.getCreated()) - throw new MpiClientException( - String.format("Error from MPI :> %s", result.getResource().getClass().getName())); - } catch (MpiClientException e) { - log.error("Error in FHIR PIX message", e); - e.printStackTrace(); - throw e; - } catch (Exception e) { - e.printStackTrace(); - log.error(e); - throw new MpiClientException(e); - } finally { - } - } - - @Override - public AuditLogger getAuditLogger() { - // TODO Auto-generated method stub - return null; - } - - @Override - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { - this.applicationContext = applicationContext; - } + } + } + } + + IGenericClient client = this.getClient(false); + MethodOutcome result = client.create().resource(admitMessage).execute(); + + if (!result.getCreated()) + throw new MpiClientException( + String.format("Error from MPI :> %s", result.getResource().getClass().getName())); + + } + catch (FhirClientConnectionException e) { + log.error("Error in FHIR PIX message", e); + } + catch (MpiClientException e) { + log.error("Error in FHIR PIX message", e); + e.printStackTrace(); + throw e; + } + catch (Exception e) { + e.printStackTrace(); + log.error(e); + } + + } + + private Address parseAddress(Obs obs) { + Address fhirAddress = new Address(); + fhirAddress.setUse(Address.AddressUse.HOME); + obs.getGroupMembers().forEach(member -> { + switch (member.getConcept().getConceptId()) { + case 165197: { + // Province of residence + fhirAddress.setState(member.getValueText()); + break; + } + case 165196: { + // Communal section + fhirAddress.addExtension("Communal section", new StringType(member.getValueText())); + break; + } + case 165198: { + // Country + fhirAddress.setCountry(member.getValueText()); + break; + } + case 162725: { + // Address (text) + fhirAddress.addExtension("Address Text", new StringType(member.getValueText())); + break; + } + case 1354: { + // Village + fhirAddress.setCity(member.getValueText()); + break; + } + case 165195: { + // Locality + fhirAddress.addExtension("Locality", new StringType(member.getValueText())); + break; + } + default: { + // Do nothing + } + } + }); + + return fhirAddress; + } + + private org.hl7.fhir.r4.model.Patient.ContactComponent translatePatientContact(org.openmrs.Obs patientOb) { + Set contactMembers = patientOb.getGroupMembers(false); + // Add patient contact - + org.hl7.fhir.r4.model.Patient.ContactComponent contactComponent = new org.hl7.fhir.r4.model.Patient.ContactComponent(); + + Address address = parseAddress(patientOb); + contactComponent.setAddress(address); + + Reference reference = new Reference(); + reference.setDisplay(patientOb.getConcept().getName().getName()); + contactComponent.setOrganization(reference); + + for (org.openmrs.Obs cm : contactMembers) { + if (cm.getConcept().getConceptId() == 163258) { + // Process contact name + HumanName contactName = new HumanName(); + String[] names = cm.getValueText().split(" "); + + if (names.length > 1) { + contactName.setFamily(names[1]); + List ns = new ArrayList() {{ + add(new StringType(names[0])); + }}; + contactName.setGiven(ns); + } else if (names.length == 1) { + contactName.setFamily(names[0]); + } + contactComponent.setName(contactName); + } else if (cm.getConcept().getConceptId() == 159635) { + // Process contact's phone number + ContactPoint telco = new ContactPoint(); + telco.setSystem(ContactPoint.ContactPointSystem.PHONE); + telco.setValue(cm.getValueText()); + telco.setUse(ContactPoint.ContactPointUse.MOBILE); + List contactPoints = new ArrayList() {{ + add(telco); + }}; + contactComponent.setTelecom(contactPoints); + } else if (cm.getConcept().getConceptId() == 164352) { + // Process relationship to patient + CodeableConcept concept = new CodeableConcept(); + concept.setText(cm.getValueCoded().getName().getName()); + contactComponent.addRelationship(concept); + } + } + return contactComponent; + } + + /** + * Updates the MPI patient information with local patient information + */ + @Override + public void updatePatient(MpiPatientExport patientExport) throws MpiClientException { + org.hl7.fhir.r4.model.Patient admitMessage = null; + + try { + admitMessage = patientTranslator.toFhirResource(patientExport.getPatient()); + admitMessage.getNameFirstRep().setUse(HumanName.NameUse.OFFICIAL); + // Temporary URI identifier + admitMessage.addIdentifier().setSystem("urn:ietf:rfc:3986") + .setValue(this.m_configuration.getLocalPatientIdRoot() + patientExport.getPatient().getUuid()); + IGenericClient client = this.getClient(false); + MethodOutcome result = client.update().resource(admitMessage).execute(); + if (!result.getCreated()) + throw new MpiClientException( + String.format("Error from MPI :> %s", result.getResource().getClass().getName())); + } + catch (MpiClientException e) { + log.error("Error in FHIR PIX message", e); + e.printStackTrace(); + throw e; + } + catch (Exception e) { + e.printStackTrace(); + log.error(e); + throw new MpiClientException(e); + } + } + + @Override + public AuditLogger getAuditLogger() { + // TODO Auto-generated method stub + return null; + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + } } diff --git a/api/src/main/java/org/openmrs/module/santedb/mpiclient/configuration/MpiClientConfiguration.java b/api/src/main/java/org/openmrs/module/santedb/mpiclient/configuration/MpiClientConfiguration.java index c3bd962..037dcc3 100644 --- a/api/src/main/java/org/openmrs/module/santedb/mpiclient/configuration/MpiClientConfiguration.java +++ b/api/src/main/java/org/openmrs/module/santedb/mpiclient/configuration/MpiClientConfiguration.java @@ -413,6 +413,8 @@ public HashMap getExtensionMap() { private static final String PROP_NAME_PATIENT_TELEPHONE_ATTRIBUTE_NAME = "mpi-client.reg.patientTelephoneNumber"; + private static final String PROP_NAME_GOLDEN_RECORD_UUID_ATTRIBUTE_NAME = "mpi-client.goldenRecordUuid"; + public String getRegistrationConceptUuid() { return this.getOrCreateGlobalProperty(PROP_NAME_REG_CONCEPT, "165194AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); } @@ -421,6 +423,10 @@ public String getMothersAttributeName() { return this.getOrCreateGlobalProperty(PROP_NAME_MOTHERS_ATTRIBUTE_NAME, "First Name of Mother"); } + public String getGoldenRecordUuid() { + return this.getOrCreateGlobalProperty(PROP_NAME_GOLDEN_RECORD_UUID_ATTRIBUTE_NAME, "5c827da5-4858-4f3d-a50c-62ece001efea"); + } + public String getEmergencyContactConceptUuid() { return this.getOrCreateGlobalProperty(PROP_NAME_REG_CONCEPT, "165210AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); } @@ -448,4 +454,4 @@ public String getBirthPlaceConceptUuid() { public String getPatientTelephoneAttribute() { return this.getOrCreateGlobalProperty(PROP_NAME_PATIENT_TELEPHONE_ATTRIBUTE_NAME, "Telephone Number"); } -} \ No newline at end of file +} diff --git a/api/src/main/java/org/openmrs/module/santedb/mpiclient/model/MpiPatient.java b/api/src/main/java/org/openmrs/module/santedb/mpiclient/model/MpiPatient.java index 0309197..9429ba8 100644 --- a/api/src/main/java/org/openmrs/module/santedb/mpiclient/model/MpiPatient.java +++ b/api/src/main/java/org/openmrs/module/santedb/mpiclient/model/MpiPatient.java @@ -36,7 +36,45 @@ public class MpiPatient extends Patient { private Set patientObservations = new HashSet<>(); private String sourceLocation; - + + public MpiPatient() { + + } + public MpiPatient(Patient patient) { + + for(PersonAddress pa : patient.getAddresses()){ + pa.setPerson(this); + pa.setUuid(null); + pa.setId(null); + } + this.setAddresses(patient.getAddresses()); + for(PersonName pn : patient.getNames()){ + pn.setPerson(this); + pn.setUuid(null); + pn.setId(null); + } + this.setNames(patient.getNames()); + for(PatientIdentifier pi : patient.getIdentifiers()){ + pi.setPatient(this); + pi.setUuid(null); + pi.setId(null); + } + this.setIdentifiers(patient.getIdentifiers()); + for(PersonAttribute pa : patient.getAttributes()){ + pa.setPerson(this); + pa.setUuid(null); + pa.setId(null); + } + this.setAttributes(patient.getAttributes()); + + this.setDeathDate(patient.getDeathDate()); + this.setGender(patient.getGender()); + this.setBirthdateEstimated(patient.getBirthdateEstimated()); + this.setBirthdate(patient.getBirthdate()); + // retVal.setAttributes(this.getAttributes()); + this.setDead(patient.getDead()); + } + /** * Get relationships of the patient * @return diff --git a/api/src/main/java/org/openmrs/module/santedb/mpiclient/util/FhirUtil.java b/api/src/main/java/org/openmrs/module/santedb/mpiclient/util/FhirUtil.java index dd52445..27c49b6 100644 --- a/api/src/main/java/org/openmrs/module/santedb/mpiclient/util/FhirUtil.java +++ b/api/src/main/java/org/openmrs/module/santedb/mpiclient/util/FhirUtil.java @@ -1,55 +1,69 @@ /** * Portions Copyright 2015-2018 Mohawk College of Applied Arts and Technology * Portions Copyright (c) 2014-2020 Fyfe Software Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you - * may not use this file except in compliance with the License. You may - * obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * Licensed under the Apache License, Version 2.0 (the "License"); you + * may not use this file except in compliance with the License. You may + * obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under * the License. */ package org.openmrs.module.santedb.mpiclient.util; +import static org.apache.commons.lang3.Validate.notNull; + import java.text.ParseException; import java.text.SimpleDateFormat; -import java.util.*; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; import ca.uhn.hl7v2.HL7Exception; import ca.uhn.hl7v2.model.DataTypeException; -import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.hl7.fhir.exceptions.FHIRException; -import org.hl7.fhir.r4.model.*; +import org.hl7.fhir.r4.model.Address; import org.hl7.fhir.r4.model.Address.AddressUse; +import org.hl7.fhir.r4.model.BooleanType; +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.DateType; import org.hl7.fhir.r4.model.Enumerations.AdministrativeGender; +import org.hl7.fhir.r4.model.Extension; +import org.hl7.fhir.r4.model.HumanName; import org.hl7.fhir.r4.model.HumanName.NameUse; +import org.hl7.fhir.r4.model.Identifier; import org.hl7.fhir.r4.model.Patient.ContactComponent; -import org.openmrs.Patient; +import org.hl7.fhir.r4.model.Reference; +import org.hl7.fhir.r4.model.StringType; import org.openmrs.Obs; +import org.openmrs.Patient; import org.openmrs.PatientIdentifier; import org.openmrs.PatientIdentifierType; import org.openmrs.PersonAddress; import org.openmrs.PersonName; import org.openmrs.api.ConceptService; import org.openmrs.api.context.Context; -import org.openmrs.module.fhir2.FhirConstants; -import org.openmrs.module.fhir2.api.translators.TelecomTranslator; -import org.openmrs.module.fhir2.api.translators.impl.TelecomTranslatorImpl; +import org.openmrs.module.fhir2.api.translators.PatientTranslator; import org.openmrs.module.santedb.mpiclient.configuration.MpiClientConfiguration; import org.openmrs.module.santedb.mpiclient.model.MpiPatient; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; -import static org.apache.commons.lang3.Validate.notNull; - +@Component +public class FhirUtil implements ApplicationContextAware { -public class FhirUtil { - private final Log log = LogFactory.getLog(this.getClass()); // locking object @@ -59,12 +73,17 @@ public class FhirUtil { private static FhirUtil s_instance = null; // Get the HIE config - private MpiClientConfiguration m_configuration = MpiClientConfiguration.getInstance(); + private MpiClientConfiguration m_configuration; + + private ApplicationContext applicationContext; + + @Autowired + private PatientTranslator patientTranslator; /** * Creates a new message utility */ - private FhirUtil() { + FhirUtil() { } /** @@ -81,7 +100,7 @@ public static FhirUtil getInstance() { /** * Update the specified FHIR identifier - * + * * @param fhirIdentifier The FHIR identifier to be updated * @param patientIdentifier The patient identifier to be mapped * @param domain The domain in which the identifier belongs @@ -102,7 +121,7 @@ else if (domain.matches("^(\\d+?\\.){1,}\\d+$")) // domain is an oid /** * Interpret the XAD as a person address - * + * * @return */ private PersonAddress interpretFhirAddress(Address addr) { @@ -113,17 +132,17 @@ private PersonAddress interpretFhirAddress(Address addr) { } // Set the address pa.setUuid(addr.getId()); - if(addr.hasLine()) + if (addr.hasLine()) pa.setAddress1(addr.getLine().get(0).asStringValue()); - if(addr.hasCity()) + if (addr.hasCity()) pa.setCityVillage(addr.getCity()); - if(addr.hasCountry()) + if (addr.hasCountry()) pa.setCountry(addr.getCountry()); - if(addr.hasDistrict()) + if (addr.hasDistrict()) pa.setCountyDistrict(addr.getDistrict()); - if(addr.hasPostalCode()) + if (addr.hasPostalCode()) pa.setPostalCode(addr.getPostalCode()); - if(addr.hasState()) + if (addr.hasState()) pa.setStateProvince(addr.getState()); if (AddressUse.HOME.equals(addr.getUse())) @@ -131,10 +150,10 @@ private PersonAddress interpretFhirAddress(Address addr) { return pa; } - + /** * Interpret the CX ID as a patient Identifier - * + * * @param id */ private PatientIdentifier interpretFhirId(Identifier id) { @@ -174,14 +193,14 @@ private PatientIdentifier interpretFhirId(Identifier id) { return patId; } - + /** * Updates the provided fhir name with the person name - * + * * @param name The name to be updated with the contents of pn * @param pn The person name to convert */ - private void updateFhirName(HumanName name, PersonName pn) { + private void updateFhirName(HumanName name, PersonName pn) { String nameRewrite = this.m_configuration.getNameRewriteRule(); @@ -205,7 +224,7 @@ private void updateFhirName(HumanName name, PersonName pn) { /** * Interpret the FHIR HumanName as a Patient Name - * + * * @return The interpreted name */ private PersonName interpretFhirName(HumanName name) { @@ -222,7 +241,7 @@ private PersonName interpretFhirName(HumanName name) { pn.setFamilyName(name.getFamily()); // Given name - if(!name.hasGiven()) + if (!name.hasGiven()) pn.setGivenName("(NULL)"); else pn.setGivenName(name.getGivenAsSingleString()); @@ -237,7 +256,7 @@ private PersonName interpretFhirName(HumanName name) { /** * Updates the specified HL7v2 address - * + * * @param pa * @throws DataTypeException */ @@ -359,19 +378,16 @@ else if ("M".equals(patient.getGender())) * @return The OpenMRS patient */ // TODO: replace with fhir2 functionality (translator MpiPatient <-> Patient) - public MpiPatient parseFhirPatient(org.hl7.fhir.r4.model.Patient fhirPatient) - { - MpiPatient patient = new MpiPatient(); - - notNull(patient, "The existing Openmrs Patient object should not be null"); - notNull(fhirPatient, "The Patient object should not be null"); - -// Set UUID -// patient.setUuid(fhirPatient.getId()); + public MpiPatient parseFhirPatient(org.hl7.fhir.r4.model.Patient fhirPatient, Patient patient) { + notNull(patient, "The Patient object should not be null"); + notNull(fhirPatient, "The FHIR Patient object should not be null"); + MpiPatient mpiPatient = new MpiPatient(patient); + notNull(mpiPatient, "The existing Openmrs Patient object should not be null"); // Attempt to load a patient by identifier +/* Iterator identifierIterator = fhirPatient.getIdentifier().iterator(); while(identifierIterator.hasNext()) { Identifier identifier = (Identifier)identifierIterator.next(); @@ -381,23 +397,25 @@ public MpiPatient parseFhirPatient(org.hl7.fhir.r4.model.Patient fhirPatient) } } - - - // Enterprise root? - if(null != this.m_configuration.getEnterprisePatientIdRoot() && !this.m_configuration.getEnterprisePatientIdRoot().isEmpty()) - { - Identifier fhirSysId = new Identifier(); - fhirSysId.setSystem(this.m_configuration.getEnterprisePatientIdRoot()); - fhirSysId.setValue(fhirPatient.getIdElement().toUnqualifiedVersionless().getValue()); - log.warn(String.format("Enterprise ID %s will be mapped", fhirPatient.getIdElement().toUnqualifiedVersionless().getValue())); - PatientIdentifier sysId = this.interpretFhirId(fhirSysId); - if(sysId != null) { - patient.addIdentifier(sysId); +*/ + + try { + // Enterprise root? + if (this.m_configuration != null && null != this.m_configuration.getEnterprisePatientIdRoot() + && !this.m_configuration.getEnterprisePatientIdRoot().isEmpty()) { + Identifier fhirSysId = new Identifier(); + fhirSysId.setSystem(this.m_configuration.getEnterprisePatientIdRoot()); + fhirSysId.setValue(fhirPatient.getIdElement().toUnqualifiedVersionless().getValue()); + log.warn(String.format("Enterprise ID %s will be mapped", + fhirPatient.getIdElement().toUnqualifiedVersionless().getValue())); + PatientIdentifier sysId = this.interpretFhirId(fhirSysId); + if (sysId != null) { + mpiPatient.addIdentifier(sysId); + } } - } - - // Attempt to copy names - for (HumanName name : fhirPatient.getName()) { + + // Attempt to copy names + /*for (HumanName name : fhirPatient.getName()) { PersonName pn = this.interpretFhirName(name); patient.addName(pn); } @@ -407,7 +425,7 @@ public MpiPatient parseFhirPatient(org.hl7.fhir.r4.model.Patient fhirPatient) patient.setGender("F"); else if(AdministrativeGender.MALE.equals(fhirPatient.getGender())) patient.setGender("M"); - else + else patient.setGender("U"); // Copy DOB @@ -431,7 +449,7 @@ else if(AdministrativeGender.MALE.equals(fhirPatient.getGender())) } catch (FHIRException ignored) {} } - + // Addresses for (Address addr : fhirPatient.getAddress()) { // Skip bad addresses @@ -460,59 +478,63 @@ else if(AdministrativeGender.MALE.equals(fhirPatient.getGender())) } } - -// Patient Contacts - for (ContactComponent contactComponent : fhirPatient.getContact()) { - Obs obs = null; - try { - obs = translateContactComponent(contactComponent); - if (obs != null) { - patient.addPatientObservation(obs); + */ + // Patient Contacts + for (ContactComponent contactComponent : fhirPatient.getContact()) { + Obs obs = null; + try { + obs = translateContactComponent(contactComponent); + if (obs != null) { + mpiPatient.addPatientObservation(obs); + } + } + catch (ParseException e) { + e.printStackTrace(); } - } catch (ParseException e) { - e.printStackTrace(); } - } - + // Source Location + Identifier identifierFirstRep = fhirPatient.getIdentifierFirstRep(); + if (identifierFirstRep.hasExtension("http://fhir.openmrs.org/ext/patient/identifier#location")) { + Extension locationExtension = identifierFirstRep.getExtensionByUrl( + "http://fhir.openmrs.org/ext/patient/identifier#location"); + Reference value = (Reference) locationExtension.getValue(); + mpiPatient.setSourceLocation(value.getDisplay()); + } -// Source Location - Identifier identifierFirstRep = fhirPatient.getIdentifierFirstRep(); - if(identifierFirstRep.hasExtension("http://fhir.openmrs.org/ext/patient/identifier#location")){ - Extension locationExtension = identifierFirstRep.getExtensionByUrl("http://fhir.openmrs.org/ext/patient/identifier#location"); - Reference value = (Reference) locationExtension.getValue(); - patient.setSourceLocation(value.getDisplay()); + // Mother's maiden name + Extension mothersMaidenName = fhirPatient.getExtensionByUrl( + "http://hl7.org/fhir/StructureDefinition/patient-mothersMaidenName"); + if (mothersMaidenName != null) { + org.openmrs.PersonAttributeType attributeType = Context.getPersonService() + .getPersonAttributeTypeByName(m_configuration.getMothersAttributeName()); + org.openmrs.PersonAttribute attribute = new org.openmrs.PersonAttribute(attributeType, + ((StringType) mothersMaidenName.getValue()).getValue()); + mpiPatient.addAttribute(attribute); + } } - -// Mother's maiden name - Extension mothersMaidenName = fhirPatient.getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/patient-mothersMaidenName"); - if(mothersMaidenName != null){ - org.openmrs.PersonAttributeType attributeType = Context.getPersonService().getPersonAttributeTypeByName(m_configuration.getMothersAttributeName()); - org.openmrs.PersonAttribute attribute = new org.openmrs.PersonAttribute(attributeType,((StringType)mothersMaidenName.getValue()).getValue()); - patient.addAttribute(attribute); + catch (Exception e) { + this.log.error("Could not finish parsing FHIR patient!"); + e.printStackTrace(); } - - - - - return patient; + return mpiPatient; } private Obs translateAddressComponent(Address birthPlaceAddress) throws ParseException { ConceptService conceptService = Context.getConceptService(); org.openmrs.Location defaultLocation = Context.getLocationService().getDefaultLocation(); - org.openmrs.Obs parent = new org.openmrs.Obs(); + org.openmrs.Obs parent = new org.openmrs.Obs(); Set contactMembers = new HashSet<>(); -// Set type + // Set type parent.setConcept(conceptService.getConceptByUuid("165194AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")); -// Set location + // Set location parent.setLocation(defaultLocation); -// Process City + // Process City org.openmrs.Obs obs; String city = birthPlaceAddress.getCity(); obs = new org.openmrs.Obs(); @@ -522,8 +544,6 @@ private Obs translateAddressComponent(Address birthPlaceAddress) throws ParseExc obs.setObsDatetime(new Date()); contactMembers.add(obs); - - String state = birthPlaceAddress.getState(); obs = new org.openmrs.Obs(); obs.setConcept(conceptService.getConcept(165197)); @@ -532,9 +552,8 @@ private Obs translateAddressComponent(Address birthPlaceAddress) throws ParseExc obs.setObsDatetime(new Date()); contactMembers.add(obs); - String country = birthPlaceAddress.getCountry(); -// 165198 + // 165198 obs = new org.openmrs.Obs(); obs.setConcept(conceptService.getConcept(165198)); obs.setValueAsString(String.valueOf(country)); @@ -544,63 +563,62 @@ private Obs translateAddressComponent(Address birthPlaceAddress) throws ParseExc Iterator iterator = birthPlaceAddress.getExtension().iterator(); - while (iterator.hasNext()){ + while (iterator.hasNext()) { Extension nextExtension = iterator.next(); processAddressExtension(conceptService, contactMembers, nextExtension); } parent.setGroupMembers(contactMembers); - return parent; } public ContactComponent translatePatientContact(Obs patientOb) { -// Process patient contact - - ContactComponent contactComponent = new ContactComponent(); - Set contactMembers = patientOb.getGroupMembers(false); - for (Obs cm : contactMembers) { -// TODO move to global peroperties and use UUID instead - if (cm.getConcept().getConceptId() == 163258) { -// Process contact name - HumanName contactName = new HumanName(); - String[] names = cm.getValueText().split(" "); - - if (names.length > 1) { - contactName.setFamily(names[1]); - List ns = new ArrayList() {{ - add(new StringType(names[0])); - }}; - contactName.setGiven(ns); - } else if (names.length == 1) { - contactName.setFamily(names[0]); - } - contactComponent.setName(contactName); - } else if (cm.getConcept().getConceptId() == 159635) { -// Process contact's phone number - ContactPoint telco = new ContactPoint(); - telco.setSystem(ContactPoint.ContactPointSystem.PHONE); - telco.setValue(cm.getValueText()); - List contactPoints = new ArrayList() {{ - add(telco); - }}; - contactPoints.add(telco); - contactComponent.setTelecom(contactPoints); - } else if (cm.getConcept().getConceptId() == 164352) { -// Process relationship to patient - CodeableConcept concept = new CodeableConcept(); - concept.setText(cm.getValueCodedName().getName()); - contactComponent.addRelationship(concept); - - } else if (cm.getConcept().getConceptId() == 164958) { -// Wrong mapping for address - - } - } - return contactComponent; - } - - public org.openmrs.PersonAttribute translateTelecom(ContactPoint contactPoint){ + // Process patient contact - + ContactComponent contactComponent = new ContactComponent(); + Set contactMembers = patientOb.getGroupMembers(false); + for (Obs cm : contactMembers) { + // TODO move to global peroperties and use UUID instead + if (cm.getConcept().getConceptId() == 163258) { + // Process contact name + HumanName contactName = new HumanName(); + String[] names = cm.getValueText().split(" "); + + if (names.length > 1) { + contactName.setFamily(names[1]); + List ns = new ArrayList() {{ + add(new StringType(names[0])); + }}; + contactName.setGiven(ns); + } else if (names.length == 1) { + contactName.setFamily(names[0]); + } + contactComponent.setName(contactName); + } else if (cm.getConcept().getConceptId() == 159635) { + // Process contact's phone number + ContactPoint telco = new ContactPoint(); + telco.setSystem(ContactPoint.ContactPointSystem.PHONE); + telco.setValue(cm.getValueText()); + List contactPoints = new ArrayList() {{ + add(telco); + }}; + contactPoints.add(telco); + contactComponent.setTelecom(contactPoints); + } else if (cm.getConcept().getConceptId() == 164352) { + // Process relationship to patient + CodeableConcept concept = new CodeableConcept(); + concept.setText(cm.getValueCodedName().getName()); + contactComponent.addRelationship(concept); + + } else if (cm.getConcept().getConceptId() == 164958) { + // Wrong mapping for address + + } + } + return contactComponent; + } + + public org.openmrs.PersonAttribute translateTelecom(ContactPoint contactPoint) { org.openmrs.PersonAttribute personAttribute = new org.openmrs.PersonAttribute(); if (contactPoint == null) { return personAttribute; @@ -610,30 +628,29 @@ public org.openmrs.PersonAttribute translateTelecom(ContactPoint contactPoint){ // TODO figure out why this was taken out of FhirConstants (PERSON_CONTACT_ATTRIBUTE_TYPE) in parent fork personAttribute.setAttributeType(Context.getPersonService().getPersonAttributeTypeByUuid( Context.getAdministrationService().getGlobalProperty("fhir2.personAttributeTypeUuid"))); - return personAttribute; + return personAttribute; } - private org.openmrs.Obs translateContactComponent(ContactComponent contactComponent) throws ParseException { ConceptService conceptService = Context.getConceptService(); org.openmrs.Location defaultLocation = Context.getLocationService().getDefaultLocation(); - org.openmrs.Obs parent = new org.openmrs.Obs(); + org.openmrs.Obs parent = new org.openmrs.Obs(); Set contactMembers = new HashSet<>(); -// Set type + // Set type Reference organization = contactComponent.getOrganization(); - if(organization != null){ + if (organization != null) { parent.setConcept(conceptService.getConceptByName(organization.getDisplay())); } -// Set location + // Set location parent.setLocation(defaultLocation); -// set name - if(contactComponent.hasName()){ + // set name + if (contactComponent.hasName()) { HumanName humanName = contactComponent.getName(); - if(humanName.hasFamily()){ + if (humanName.hasFamily()) { String contactName = ""; - if(humanName.hasGiven()){ + if (humanName.hasGiven()) { contactName += humanName.getGiven().toString() + " "; } contactName += humanName.getFamily(); @@ -646,9 +663,8 @@ private org.openmrs.Obs translateContactComponent(ContactComponent contactCompon } } - -// Process phone number - if(contactComponent.hasTelecom()){ + // Process phone number + if (contactComponent.hasTelecom()) { ContactPoint telecomComponent = contactComponent.getTelecomFirstRep(); org.openmrs.Obs telecomObs = new org.openmrs.Obs(); telecomObs.setConcept(conceptService.getConcept(159635)); @@ -658,8 +674,8 @@ private org.openmrs.Obs translateContactComponent(ContactComponent contactCompon contactMembers.add(telecomObs); } -// Process relationship to patient - if(contactComponent.hasRelationship()){ + // Process relationship to patient + if (contactComponent.hasRelationship()) { CodeableConcept patientRelationship = contactComponent.getRelationshipFirstRep(); org.openmrs.Obs relationshipObs = new org.openmrs.Obs(); relationshipObs.setConcept(conceptService.getConcept(164352)); @@ -669,8 +685,8 @@ private org.openmrs.Obs translateContactComponent(ContactComponent contactCompon contactMembers.add(relationshipObs); } -// Process contact address - if(contactComponent.hasAddress()){ + // Process contact address + if (contactComponent.hasAddress()) { org.openmrs.Obs obs; Address contactAddress = contactComponent.getAddress(); String city = contactAddress.getCity(); @@ -681,8 +697,6 @@ private org.openmrs.Obs translateContactComponent(ContactComponent contactCompon obs.setObsDatetime(new Date()); contactMembers.add(obs); - - String state = contactAddress.getState(); obs = new org.openmrs.Obs(); obs.setConcept(conceptService.getConcept(165197)); @@ -691,9 +705,8 @@ private org.openmrs.Obs translateContactComponent(ContactComponent contactCompon obs.setObsDatetime(new Date()); contactMembers.add(obs); - String country = contactAddress.getCountry(); -// 165198 + // 165198 obs = new org.openmrs.Obs(); obs.setConcept(conceptService.getConcept(165198)); obs.setValueAsString(String.valueOf(country)); @@ -703,7 +716,7 @@ private org.openmrs.Obs translateContactComponent(ContactComponent contactCompon Iterator iterator = contactAddress.getExtension().iterator(); - while (iterator.hasNext()){ + while (iterator.hasNext()) { Extension nextExtension = iterator.next(); processAddressExtension(conceptService, contactMembers, nextExtension); } @@ -712,15 +725,15 @@ private org.openmrs.Obs translateContactComponent(ContactComponent contactCompon parent.setGroupMembers(contactMembers); - return parent; } - private void processAddressExtension(ConceptService conceptService, Set contactMembers, Extension nextExtension) throws ParseException { + private void processAddressExtension(ConceptService conceptService, Set contactMembers, Extension nextExtension) + throws ParseException { Obs obs = new Obs(); org.openmrs.Concept obsConcept = null; - switch (nextExtension.getUrl()){ + switch (nextExtension.getUrl()) { case "Communal section": { obsConcept = conceptService.getConcept(165196); break; @@ -734,16 +747,25 @@ private void processAddressExtension(ConceptService conceptService, Set con break; } } - if(obsConcept!=null){ + if (obsConcept != null) { obs.setConcept(obsConcept); obs.setValueAsString(String.valueOf(nextExtension.getValue())); obs.setLocation(Context.getLocationService().getDefaultLocation()); obs.setObsDatetime(new Date()); contactMembers.add(obs); - }else{ -// Not processed given concept is missing + } else { + // Not processed given concept is missing } } + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + } + + public void setPatientTranslator(PatientTranslator patientTranslator) { + this.patientTranslator = patientTranslator; + } + } diff --git a/api/src/test/java/org/openmrs/module/santedb/mpiclient/util/FhirUtilTest.java b/api/src/test/java/org/openmrs/module/santedb/mpiclient/util/FhirUtilTest.java new file mode 100644 index 0000000..49840d8 --- /dev/null +++ b/api/src/test/java/org/openmrs/module/santedb/mpiclient/util/FhirUtilTest.java @@ -0,0 +1,41 @@ +package org.openmrs.module.santedb.mpiclient.util; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.when; + +import org.hl7.fhir.r4.model.Patient; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.openmrs.module.fhir2.api.translators.PatientTranslator; +import org.openmrs.module.santedb.mpiclient.model.MpiPatient; + +@RunWith(MockitoJUnitRunner.class) +public class FhirUtilTest { + + private FhirUtil fhirUtil; + + @Mock + private PatientTranslator patientTranslator; + + @Before + public void setup() { + fhirUtil = FhirUtil.getInstance(); + fhirUtil.setPatientTranslator(patientTranslator); + } + + @Test + public void testParseFhirPatient_shouldCreateEmptyPatient() { + Patient emptyPatient = new Patient(); + org.openmrs.Patient omrsPatient = new org.openmrs.Patient(); + + when(patientTranslator.toOpenmrsType(any())).thenReturn(omrsPatient); + + MpiPatient result = fhirUtil.parseFhirPatient(emptyPatient, omrsPatient); + + Assert.assertTrue(result != null); + } +} diff --git a/omod/src/main/resources/config.xml b/omod/src/main/resources/config.xml index 03f8e15..3d22388 100644 --- a/omod/src/main/resources/config.xml +++ b/omod/src/main/resources/config.xml @@ -66,25 +66,15 @@ /Required Global Properties --> - mpi-client.endpoint.pdq.addr - mpi.domain.com - Identifies the location of the PDQ endpoint + mpi-client.endpoint.cr.addr + https://openhim.sedish-haiti.org/CR/fhir + Identifies the location of the Client Registry endpoint - mpi-client.endpoint.pdq.port - 2100 + mpi-client.endpoint.cr.port + Identifies the port of the PDQ interface - - mpi-client.endpoint.pix.addr - mpi.domain.com - Identifies the location of the PIX endpoint - - - mpi-client.endpoint.pix.port - 2100 - Identifies the port of the PIX interface - mpi-client.pid.correlation LOCAL @@ -249,6 +239,11 @@ mpi-client.reg.patientTelephoneNumber Telephone Number The telephone number for the patient + + + mpi-client.goldenRecordUuid + + The UUID for the Golden Record tag in OpenCR