diff --git a/api-2.5/pom.xml b/api-2.5/pom.xml new file mode 100644 index 000000000..bbbfceffc --- /dev/null +++ b/api-2.5/pom.xml @@ -0,0 +1,76 @@ + + + + + org.openmrs.module + initializer + 2.7.0-SNAPSHOT + + 4.0.0 + + initializer-api-2.5 + jar + Initializer API 2.5 + API 2.5 project for Initializer + + + ${openmrsVersion2.5} + + + + + ${project.parent.groupId} + ${project.parent.artifactId}-api + ${project.parent.version} + provided + + + + ${project.parent.groupId} + ${project.parent.artifactId}-api + ${project.parent.version} + test + test-jar + + + + ${project.parent.groupId} + ${project.parent.artifactId}-api-2.4 + ${project.parent.version} + provided + + + + ${project.parent.groupId} + ${project.parent.artifactId}-api-2.3 + ${project.parent.version} + provided + + + + ${project.parent.groupId} + ${project.parent.artifactId}-api-2.3 + ${project.parent.version} + test + test-jar + + + + ${project.parent.groupId} + ${project.parent.artifactId}-api-2.2 + ${project.parent.version} + provided + + + + ${project.parent.groupId} + ${project.parent.artifactId}-api-2.2 + ${project.parent.version} + test + test-jar + + + + diff --git a/api-2.5/src/main/java/org/openmrs/module/initializer/api/fhir/cpm/FhirContactPointMapCsvParser.java b/api-2.5/src/main/java/org/openmrs/module/initializer/api/fhir/cpm/FhirContactPointMapCsvParser.java new file mode 100644 index 000000000..d70b62797 --- /dev/null +++ b/api-2.5/src/main/java/org/openmrs/module/initializer/api/fhir/cpm/FhirContactPointMapCsvParser.java @@ -0,0 +1,140 @@ +package org.openmrs.module.initializer.api.fhir.cpm; + +import org.openmrs.PersonAttributeType; +import org.openmrs.annotation.OpenmrsProfile; +import org.openmrs.api.LocationService; +import org.openmrs.api.PersonService; +import org.openmrs.api.ProviderService; +import org.openmrs.attribute.BaseAttributeType; +import org.openmrs.module.fhir2.api.FhirContactPointMapService; +import org.openmrs.module.initializer.Domain; +import org.openmrs.module.fhir2.model.FhirContactPointMap; +import org.openmrs.module.initializer.api.BaseLineProcessor; +import org.openmrs.module.initializer.api.CsvLine; +import org.openmrs.module.initializer.api.CsvParser; +import org.springframework.beans.factory.annotation.Autowired; + +@OpenmrsProfile(modules = { "fhir2:1.11.* - 9.*" }, openmrsPlatformVersion = "2.5.13 - 2.5.*, 2.6.2 - 2.6.*, 2.7.* - 9.*") +public class FhirContactPointMapCsvParser extends CsvParser> { + + public static final String ATTRIBUTE_TYPE_DOMAIN_HEADER = "Entity name"; + + public static final String ATTRIBUTE_TYPE = "Attribute type"; + + private static final String LOCATION = "location"; + + private static final String PERSON = "person"; + + private static final String PROVIDER = "provider"; + + private final LocationService locationService; + + private final PersonService personService; + + private final ProviderService providerService; + + private final FhirContactPointMapService fhirContactPointMapService; + + @Autowired + protected FhirContactPointMapCsvParser(FhirContactPointMapService fhirContactPointMapService,BaseLineProcessor lineProcessor, + LocationService locationService, PersonService personService, ProviderService providerService) { + super(lineProcessor); + this.fhirContactPointMapService = fhirContactPointMapService; + this.locationService = locationService; + this.personService = personService; + this.providerService = providerService; + } + + @Override + public FhirContactPointMap bootstrap(CsvLine line) throws IllegalArgumentException { + FhirContactPointMap contactPointMap = null; + if (line.getUuid() != null) { + contactPointMap = fhirContactPointMapService.getFhirContactPointMapByUuid(line.getUuid()) + .orElse(null); + } + + if (contactPointMap != null) { + return contactPointMap; + } + + String attributeTypeDomain = line.get(ATTRIBUTE_TYPE_DOMAIN_HEADER, true); + String attributeType = line.get(ATTRIBUTE_TYPE, true); + + if (attributeTypeDomain.equals(PERSON)) { + PersonAttributeType personAttributeType = getPersonAttributeType(attributeType); + + if (personAttributeType == null) { + throw new IllegalArgumentException("PersonAttributeType " + attributeType + + " does not exist. Please ensure your Initializer configuration contains this attribute type."); + } + + contactPointMap = fhirContactPointMapService.getFhirContactPointMapForPersonAttributeType(personAttributeType) + .orElse(null); + } else { + BaseAttributeType baseAttributeType = getBaseAttributeType(attributeTypeDomain, attributeType); + + if (baseAttributeType == null) { + throw new IllegalArgumentException( + "Could not find attribute type " + attributeType + " for attribute domain " + attributeTypeDomain); + } + + contactPointMap = fhirContactPointMapService.getFhirContactPointMapForAttributeType(baseAttributeType) + .orElse(null); + } + + if (contactPointMap != null) { + return contactPointMap; + } + + return new FhirContactPointMap(); + } + + + @Override + public FhirContactPointMap save(FhirContactPointMap instance) { + return fhirContactPointMapService.saveFhirContactPointMap(instance); + } + + @Override + public Domain getDomain() { + return Domain.FHIR_CONTACT_POINT_MAP; + } + + protected PersonAttributeType getPersonAttributeType(String attributeType) { + PersonAttributeType personAttributeType = personService.getPersonAttributeTypeByName(attributeType); + + if (personAttributeType != null) { + return personAttributeType; + } + + personAttributeType = personService.getPersonAttributeTypeByUuid(attributeType); + + return personAttributeType; + } + + protected BaseAttributeType getBaseAttributeType(String attributeDomain, String attributeType) { + BaseAttributeType baseAttributeType = null; + + switch (attributeDomain) { + case LOCATION: + baseAttributeType = locationService.getLocationAttributeTypeByName(attributeType); + + if (baseAttributeType != null) { + return baseAttributeType; + } + + return locationService.getLocationAttributeTypeByUuid(attributeType); + break; + case PROVIDER: + baseAttributeType = providerService.getProviderAttributeTypeByName(attributeType); + + if (baseAttributeType != null) { + return baseAttributeType; + } + + return providerService.getProviderAttributeTypeByUuid(attributeType); + break; + } + return baseAttributeType; + } +} diff --git a/api-2.5/src/main/java/org/openmrs/module/initializer/api/fhir/cpm/FhirContactPointMapLineProcessor.java b/api-2.5/src/main/java/org/openmrs/module/initializer/api/fhir/cpm/FhirContactPointMapLineProcessor.java new file mode 100644 index 000000000..92323ce95 --- /dev/null +++ b/api-2.5/src/main/java/org/openmrs/module/initializer/api/fhir/cpm/FhirContactPointMapLineProcessor.java @@ -0,0 +1,50 @@ +package org.openmrs.module.initializer.api.fhir.cpm; + +import org.apache.commons.lang3.StringUtils; +import org.hl7.fhir.r4.model.ContactPoint; +import org.openmrs.annotation.OpenmrsProfile; +import org.openmrs.module.fhir2.model.FhirContactPointMap; +import org.openmrs.module.initializer.api.BaseLineProcessor; +import org.openmrs.module.initializer.api.CsvLine; + +import static org.openmrs.module.initializer.api.fhir.cpm.FhirContactPointMapCsvParser.ATTRIBUTE_TYPE_DOMAIN_HEADER; + +@OpenmrsProfile(modules = { "fhir2:1.11.* - 9.*" }, openmrsPlatformVersion = "2.5.13 - 2.5.*, 2.6.2 - 2.6.*, 2.7.* - 9.*") +public class FhirContactPointMapLineProcessor extends BaseLineProcessor { + + private static final String SYSTEM_HEADER = "system"; + private static final String USE_HEADER = "use"; + private static final String RANK_HEADER = "rank"; + @Override + public FhirContactPointMap fill(FhirContactPointMap instance, CsvLine line) throws IllegalArgumentException { + String uuid = line.getUuid(); + + if (StringUtils.isNotBlank(uuid)) { + instance.setUuid(line.getUuid()); + } + + line.get(ATTRIBUTE_TYPE_DOMAIN_HEADER, true); + + String system = line.get(SYSTEM_HEADER, false); + String use = line.get(USE_HEADER, false); + String rank = line.get(RANK_HEADER, false); + + if (system != null) { + instance.setSystem(ContactPoint.ContactPointSystem.valueOf(system)); + } + + if (use != null) { + instance.setUse(ContactPoint.ContactPointUse.valueOf(use)); + } + + if (rank != null) { + int rankInt = Integer.parseInt(rank); + if (rankInt < 1) { + throw new IllegalArgumentException("Rank must be a positive integer, i.e., 1+"); + } + instance.setRank(rankInt); + } + + return instance; + } +} diff --git a/api-2.5/src/main/java/org/openmrs/module/initializer/api/fhir/cpm/FhirContactPointMapLoader.java b/api-2.5/src/main/java/org/openmrs/module/initializer/api/fhir/cpm/FhirContactPointMapLoader.java new file mode 100644 index 000000000..02d3cda9e --- /dev/null +++ b/api-2.5/src/main/java/org/openmrs/module/initializer/api/fhir/cpm/FhirContactPointMapLoader.java @@ -0,0 +1,16 @@ +package org.openmrs.module.initializer.api.fhir.cpm; + +import org.openmrs.annotation.OpenmrsProfile; +import org.openmrs.module.fhir2.model.FhirContactPointMap; +import org.openmrs.module.initializer.api.loaders.BaseCsvLoader; +import org.springframework.beans.factory.annotation.Autowired; + +@OpenmrsProfile(modules = { "fhir2:1.11.* - 9.*" }, openmrsPlatformVersion = "2.5.13 - 2.5.*, 2.6.2 - 2.6.*, 2.7.* - 9.*") +public class FhirContactPointMapLoader extends BaseCsvLoader { + + @Autowired + public void setParser(FhirContactPointMapCsvParser parser) { + this.parser = parser; + } + +} diff --git a/api-2.5/src/test/java/org/openmrs/module/initializer/DomainBaseModuleContextSensitive_2_5_Test.java b/api-2.5/src/test/java/org/openmrs/module/initializer/DomainBaseModuleContextSensitive_2_5_Test.java new file mode 100644 index 000000000..422b23a84 --- /dev/null +++ b/api-2.5/src/test/java/org/openmrs/module/initializer/DomainBaseModuleContextSensitive_2_5_Test.java @@ -0,0 +1,18 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under + * the terms of the Healthcare Disclaimer located at http://openmrs.org/license. + * + * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS + * graphic logo is a trademark of OpenMRS Inc. + */ +package org.openmrs.module.initializer; + +public abstract class DomainBaseModuleContextSensitive_2_5_Test extends DomainBaseModuleContextSensitiveTest { + + @Override + public void updateSearchIndex() { + // to prevent Data Filter's 'Illegal Record Access' + } +} diff --git a/api-2.5/src/test/resources/testAppDataDir/configuration/fhircontactpointmap/contactPointMap.csv b/api-2.5/src/test/resources/testAppDataDir/configuration/fhircontactpointmap/contactPointMap.csv new file mode 100644 index 000000000..7f35b5098 --- /dev/null +++ b/api-2.5/src/test/resources/testAppDataDir/configuration/fhircontactpointmap/contactPointMap.csv @@ -0,0 +1,5 @@ +Uuid,Void/Retire,Entity name,Attribute Type,System,Use,Rank,_order:1000 +fa48acc4-ef1f-46d6-b0af-150b00ddee9d,,person,717ec942-3c4a-11ea-b024-ffc81a23382e,phone,work,1, +,,person,PAT_RENAME_NEW_NAME,phone,home,, +bcf23315-a236-42aa-be95-b9e0931e22b0,,provider,Provider Speciality,email,home,2, +800e48ba-666c-445c-b871-68e54eec6de8,,location,e7aacc6e-d151-4d9e-a808-6ed9ff761212,phone,temp,3, diff --git a/api/src/main/java/org/openmrs/module/initializer/Domain.java b/api/src/main/java/org/openmrs/module/initializer/Domain.java index 9f7ac0d3a..541fd10f5 100644 --- a/api/src/main/java/org/openmrs/module/initializer/Domain.java +++ b/api/src/main/java/org/openmrs/module/initializer/Domain.java @@ -46,6 +46,7 @@ public enum Domain { COHORT_ATTRIBUTE_TYPES, FHIR_CONCEPT_SOURCES, FHIR_PATIENT_IDENTIFIER_SYSTEMS, + FHIR_CONTACT_POINT_MAP, AMPATH_FORMS, AMPATH_FORMS_TRANSLATIONS, HTML_FORMS; diff --git a/api/src/test/java/org/openmrs/module/initializer/api/FhirPatientContactPointMapIntegrationTest.java b/api/src/test/java/org/openmrs/module/initializer/api/FhirPatientContactPointMapIntegrationTest.java new file mode 100644 index 000000000..2416d461f --- /dev/null +++ b/api/src/test/java/org/openmrs/module/initializer/api/FhirPatientContactPointMapIntegrationTest.java @@ -0,0 +1,99 @@ +package org.openmrs.module.initializer.api; + +import org.hl7.fhir.r4.model.ContactPoint; +import org.junit.Before; +import org.junit.Test; +import org.openmrs.LocationAttributeType; +import org.openmrs.PersonAttributeType; +import org.openmrs.ProviderAttributeType; +import org.openmrs.api.LocationService; +import org.openmrs.api.PersonService; +import org.openmrs.api.ProviderService; +import org.openmrs.attribute.BaseAttributeType; +import org.openmrs.module.fhir2.api.FhirContactPointMapService; +import org.openmrs.module.fhir2.model.FhirContactPointMap; +import org.openmrs.module.initializer.DomainBaseModuleContextSensitiveTest; +import org.openmrs.module.initializer.api.fhir.cpm.FhirContactPointMapLoader; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.Optional; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; + +public class FhirPatientContactPointMapIntegrationTest extends DomainBaseModuleContextSensitiveTest { + + @Autowired + private FhirContactPointMapService fhirContactPointMapService; + + @Autowired + private FhirContactPointMapLoader fhirContactPointMapLoader; + + @Autowired + private LocationService locationService; + + @Autowired + private PersonService personService; + + @Autowired + private ProviderService providerService; + + FhirContactPointMap fhirContactPointMap; + + PersonAttributeType personAttributeType; + + LocationAttributeType locationAttributeType; + + ProviderAttributeType providerAttributeType; + + @Before + public void setup() { + personAttributeType = new PersonAttributeType(); + providerAttributeType = new ProviderAttributeType(); + locationAttributeType = new LocationAttributeType(); + + fhirContactPointMap = new FhirContactPointMap(); + fhirContactPointMap.setUuid("fa48acc4-ef1f-46d6-b0af-150b00ddee9d"); + fhirContactPointMap.setAttributeTypeDomain("person"); + fhirContactPointMap.setAttributeTypeId(10001); + fhirContactPointMap.setSystem(ContactPoint.ContactPointSystem.PHONE); + fhirContactPointMap.setUse(ContactPoint.ContactPointUse.WORK); + fhirContactPointMap.setRank(1); + fhirContactPointMapService.saveFhirContactPointMap(fhirContactPointMap); + personService.savePersonAttributeType(personAttributeType); + } + + @Test + public void loader_shouldLoadFhirContactPointMapAccordingToCSVFiles() { + fhirContactPointMapLoader.load(); + + FhirContactPointMap firstFhirContactPointMap = assertPersonAttributeType(personAttributeType); + FhirContactPointMap secondFhirContactPointMap = assertBaseAttributeType(providerAttributeType); + FhirContactPointMap thirdFhirContactPointMap = assertBaseAttributeType(locationAttributeType); + + assertThat(firstFhirContactPointMap.getAttributeTypeDomain(), equalTo("person")); + assertThat(firstFhirContactPointMap.getSystem(), equalTo(ContactPoint.ContactPointSystem.PHONE)); + assertThat(firstFhirContactPointMap.getUse(), equalTo(ContactPoint.ContactPointUse.WORK)); + + assertThat(secondFhirContactPointMap.getAttributeTypeDomain(), equalTo("provider")); + assertThat(firstFhirContactPointMap.getSystem(), equalTo(ContactPoint.ContactPointSystem.EMAIL)); + assertThat(firstFhirContactPointMap.getUse(), equalTo(ContactPoint.ContactPointUse.HOME)); + + assertThat(thirdFhirContactPointMap.getAttributeTypeDomain(), equalTo("location")); + assertThat(firstFhirContactPointMap.getSystem(), equalTo(ContactPoint.ContactPointSystem.URL)); + assertThat(firstFhirContactPointMap.getUse(), equalTo(ContactPoint.ContactPointUse.TEMP)); + } + + protected FhirContactPointMap assertPersonAttributeType(PersonAttributeType attributeType) { + Optional contactPointMap = fhirContactPointMapService.getFhirContactPointMapForPersonAttributeType(attributeType); + assertThat(contactPointMap.isPresent(), is(true)); + return contactPointMap.get(); + } + + protected FhirContactPointMap assertBaseAttributeType(BaseAttributeType attributeType) { + Optional contactPointMap = fhirContactPointMapService.getFhirContactPointMapForAttributeType(attributeType); + assertThat(contactPointMap.isPresent(), is(true)); + return contactPointMap.get(); + } +} diff --git a/api/src/test/resources/testAppDataDir/configuration/attributetypes/attribute_types.csv b/api/src/test/resources/testAppDataDir/configuration/attributetypes/attribute_types.csv index 581eaba89..8801b8f8c 100644 --- a/api/src/test/resources/testAppDataDir/configuration/attributetypes/attribute_types.csv +++ b/api/src/test/resources/testAppDataDir/configuration/attributetypes/attribute_types.csv @@ -1,5 +1,6 @@ Uuid,Void/Retire,Entity name,Name,Description,Min occurs,Max occurs,Datatype classname,Datatype config,Preferred handler classname,Handler config,_order:1000 0bb29984-3193-11e7-93ae-92367f002671,,Location,Location Height,Location Height's description,1,1,org.openmrs.customdatatype.datatype.FloatDatatype,,,, +e7aacc6e-d151-4d9e-a808-6ed9ff761212,,Location,Location Phone Number,Location Phone Numbers's description,1,1,org.openmrs.customdatatype.datatype.FreeTextDataType,,,, 0bc29982-3193-11e3-93ae-92367f222671,,Visit,Visit Color,Visit Color's description,1,1,org.openmrs.customdatatype.datatype.FreeTextDatatype,,,, 9eca4f4e-707f-4bb8-8289-2f9b6e93803c,,Location,Location ISO Code,Location ISO Code's description,1,10,org.openmrs.customdatatype.datatype.FreeTextDatatype,,,, ,,Provider,Provider Speciality,Clinical speciality for this provider,0,7,org.openmrs.customdatatype.datatype.FreeTextDatatype,,,, @@ -8,4 +9,4 @@ Uuid,Void/Retire,Entity name,Name,Description,Min occurs,Max occurs,Datatype cla 3884c889-35f5-47b4-a6b7-5b1165cee218,,Program,Program Assessment,Program Assessment's description,1,,org.openmrs.customdatatype.datatype.FreeTextDatatype,,,, 9398c839-4f39-428c-9022-e457980ccfa8,,Program,CodedConcept attribute type,This is a Program's CodedConcept attribute type,0,1,org.bahmni.module.bahmnicore.customdatatype.datatype.CodedConceptDatatype,8295308d-e5b2-41c7-adc1-2e0e83f0f16e,,, b1d98f27-c058-46f2-9c12-87dd7c92f7e3,,Program,Program Efficiency Indicator,Metric of the program efficiency,0,1,org.openmrs.customdatatype.datatype.FloatDatatype,,,, -,TRUE,Concept,Concept Family,,,,,,,, \ No newline at end of file +,TRUE,Concept,Concept Family,,,,,,,, diff --git a/omod/src/main/resources/config.xml b/omod/src/main/resources/config.xml index c1ed69e08..49bf609a3 100644 --- a/omod/src/main/resources/config.xml +++ b/omod/src/main/resources/config.xml @@ -17,7 +17,11 @@ /lib/initializer-api-2.4-${project.version}.jar - 2.4.* - 2.* + 2.4.* - 9.* + + + /lib/initializer-api-2.5-${project.version}.jar + 2.5.13 - 9.* diff --git a/pom.xml b/pom.xml index 37ae77927..81c9c1c68 100644 --- a/pom.xml +++ b/pom.xml @@ -36,6 +36,7 @@ api-2.2 api-2.3 api-2.4 + api-2.5 omod @@ -54,6 +55,7 @@ 2.2.0 2.3.6 2.4.0 + 2.5.13-SNAPSHOT ${openmrsVersion2.1} @@ -67,7 +69,7 @@ 1.2.2 1.3.4 1.2.9 - 1.6.0 + 1.11.0 1.0.0 diff --git a/readme/fhir.md b/readme/fhir.md index 97b9adfb5..3651376e7 100644 --- a/readme/fhir.md +++ b/readme/fhir.md @@ -73,4 +73,41 @@ This is the URL of the code system in FHIR. For terminologies identified [in the FHIR CodeSystem registry](https://www.hl7.org/fhir/terminologies-systems.html), this should be the preferred URL for that code system, e.g. SNOMED CT is "http://snomed.info/sct". If the code system is not defined by HL7 or that table, then the code systems own preferred URL should be used, e.g., for CIEL we tend to use -"https://api.openconceptlab.org/orgs/CIEL/sources/CIEL". \ No newline at end of file +"https://api.openconceptlab.org/orgs/CIEL/sources/CIEL". + +## Domain 'fhirContactPointMap' + +The **fhirContactPointMap** subfolder contains CSV import files for defining contact point fields like system, use, rank for any of person, provider and location in your OpenMRS instance. +These are used in data exchange to store contact attributes a person, provider or location may have. If none are provided, the identifier type UUID is used as the "system" for these attributes. + +This is a possible example of its contents: +```bash +fhirContactPointMap/ + ├──contactPointMap.csv + └── ... +``` +The format of this CSV should be as follows: + +| Uuid | Void/Retire | Entity name | Attribute Type | System | Use | Rank | _order:1000 | +|--------------------------------------|-------------|-------------|--------------------------------------|--------|------|------|-------------| +| fa48acc4-ef1f-46d6-b0af-150b00ddee9d | | person | 717ec942-3c4a-11ea-b024-ffc81a23382e | phone | work | 1 | | +| | | person | PAT_RENAME_NEW_NAME | phone | home | | | +| bcf23315-a236-42aa-be95-b9e0931e22b0 | | provider | Provider Speciality | email | home | 2 | | +| 800e48ba-666c-445c-b871-68e54eec6de8 | | location | e7aacc6e-d151-4d9e-a808-6ed9ff761212 | phone | temp | 3 | | + +Headers that start with an underscore such as `_order:1000` are metadata headers. The values in the columns under those headers are never read by the CSV parser. + +###### Attribute Type Domain(mandatory) +This is a *required* field for every entry. This can either be person, provider or location which represent the **Person Attribute Type**, **Provider Attribute Type** and **Location Attribute Type** respectively. + +###### System(mandatory) + +This can be any of phone, fax, email, pager, url, sms. Take a look at [ContactPointSystem](https://www.hl7.org/fhir/valueset-contact-point-system.html) for more information. + +###### Use(mandatory) + +This represents the actual purpose of the contact point. It could be any of home ,work ,temp ,old ,mobile. Take a deeper look at [ContactPointUse](https://www.hl7.org/fhir/valueset-contact-point-use.html). + +###### Rank(mandatory) + +This specifies the preferred order of use (1 = highest).