diff --git a/.vscode/settings.json b/.vscode/settings.json index 945641d64..c8d51043d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -13,6 +13,7 @@ "java.test.defaultConfig": "test-config", "java.checkstyle.configuration": "${workspaceFolder}/config/checkstyle/checkstyle.xml", "java.checkstyle.version": "9.2.1", + "java.format.settings.url": "https://raw.githubusercontent.com/google/styleguide/gh-pages/eclipse-java-google-style.xml", "cSpell.enabledLanguageIds": [ "java", "json", @@ -34,6 +35,7 @@ "autoconfigure", "BCSEHEDISMY", "CAREGAP", + "cdshooks", "Checkstyle", "classpath", "Codeable", @@ -50,6 +52,7 @@ "mgsc", "multiversion", "mvnw", + "NOCHANGE", "numer", "opencds", "pdmp", diff --git a/core/pom.xml b/core/pom.xml index 9bac98dc7..52926f91c 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -1,5 +1,6 @@ - + 4.0.0 org.opencds.cqf.ruler @@ -117,4 +118,13 @@ + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + + diff --git a/core/src/main/java/org/opencds/cqf/ruler/Server.java b/core/src/main/java/org/opencds/cqf/ruler/Server.java index 81b39b948..10c5fdecf 100644 --- a/core/src/main/java/org/opencds/cqf/ruler/Server.java +++ b/core/src/main/java/org/opencds/cqf/ruler/Server.java @@ -1,13 +1,10 @@ package org.opencds.cqf.ruler; import static com.google.common.base.MoreObjects.firstNonNull; - import java.util.List; import java.util.Map; import java.util.stream.Collectors; - import javax.servlet.ServletException; - import org.hl7.fhir.dstu3.model.CapabilityStatement; import org.hl7.fhir.instance.model.api.IBaseConformance; import org.opencds.cqf.ruler.api.Interceptor; @@ -23,7 +20,6 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; - import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.jpa.api.config.DaoConfig; @@ -67,7 +63,7 @@ public Server() { } @Override - @SuppressWarnings({ "rawtypes", "unchecked" }) + @SuppressWarnings({"rawtypes", "unchecked"}) protected void initialize() throws ServletException { super.initialize(); @@ -76,7 +72,8 @@ protected void initialize() throws ServletException { this.registerProvider(valueSetOperationProvider); log.info("Loading operation providers from plugins"); - Map providers = applicationContext.getBeansOfType(OperationProvider.class); + Map providers = + applicationContext.getBeansOfType(OperationProvider.class); for (OperationProvider o : providers.values()) { log.info("Registering {}", o.getClass().getName()); this.registerProvider(o); @@ -88,9 +85,10 @@ protected void initialize() throws ServletException { log.info("Registering {} interceptor", o.getClass().getName()); this.registerInterceptor(o); } - + log.info("Loading metadata extenders from plugins"); - Map extenders = applicationContext.getBeansOfType(MetadataExtender.class); + Map extenders = + applicationContext.getBeansOfType(MetadataExtender.class); for (MetadataExtender o : extenders.values()) { log.info("Found {} extender", o.getClass().getName()); } @@ -101,38 +99,43 @@ protected void initialize() throws ServletException { if (fhirVersion == FhirVersionEnum.DSTU2) { List> extenderList = extenders.values().stream() .map(x -> (MetadataExtender) x).collect(Collectors.toList()); - ExtensibleJpaConformanceProviderDstu2 confProvider = new ExtensibleJpaConformanceProviderDstu2(this, - myFhirSystemDao, - myDaoConfig, extenderList); - confProvider.setImplementationDescription(firstNonNull(implementationDescription, "CQF RULER DSTU2 Server")); + ExtensibleJpaConformanceProviderDstu2 confProvider = + new ExtensibleJpaConformanceProviderDstu2(this, myFhirSystemDao, myDaoConfig, + extenderList); + confProvider.setImplementationDescription( + firstNonNull(implementationDescription, "CQF RULER DSTU2 Server")); setServerConformanceProvider(confProvider); } else { if (fhirVersion == FhirVersionEnum.DSTU3) { List> extenderList = extenders.values().stream() .map(x -> (MetadataExtender) x).collect(Collectors.toList()); - ExtensibleJpaConformanceProviderDstu3 confProvider = new ExtensibleJpaConformanceProviderDstu3(this, - myFhirSystemDao, myDaoConfig, mySearchParamRegistry, extenderList); - confProvider - .setImplementationDescription(firstNonNull(implementationDescription, "CQF RULER DSTU3 Server")); + ExtensibleJpaConformanceProviderDstu3 confProvider = + new ExtensibleJpaConformanceProviderDstu3(this, myFhirSystemDao, myDaoConfig, + mySearchParamRegistry, extenderList); + confProvider.setImplementationDescription( + firstNonNull(implementationDescription, "CQF RULER DSTU3 Server")); setServerConformanceProvider(confProvider); } else if (fhirVersion == FhirVersionEnum.R4) { List> extenderList = extenders.values().stream() .map(x -> (MetadataExtender) x).collect(Collectors.toList()); - ExtensibleJpaCapabilityStatementProvider confProvider = new ExtensibleJpaCapabilityStatementProvider(this, - myFhirSystemDao, myDaoConfig, mySearchParamRegistry, myValidationSupport, extenderList); - confProvider.setImplementationDescription(firstNonNull(implementationDescription, "CQF RULER R4 Server")); + ExtensibleJpaCapabilityStatementProvider confProvider = + new ExtensibleJpaCapabilityStatementProvider(this, myFhirSystemDao, myDaoConfig, + mySearchParamRegistry, myValidationSupport, extenderList); + confProvider.setImplementationDescription( + firstNonNull(implementationDescription, "CQF RULER R4 Server")); setServerConformanceProvider(confProvider); } else if (fhirVersion == FhirVersionEnum.R5) { List> extenderList = extenders.values().stream() .map(x -> (MetadataExtender) x).collect(Collectors.toList()); - ExtensibleJpaCapabilityStatementProvider confProvider = new ExtensibleJpaCapabilityStatementProvider(this, - myFhirSystemDao, myDaoConfig, mySearchParamRegistry, myValidationSupport, extenderList); - confProvider.setImplementationDescription(firstNonNull(implementationDescription, "CQF RULER R5 Server")); + ExtensibleJpaCapabilityStatementProvider confProvider = + new ExtensibleJpaCapabilityStatementProvider(this, myFhirSystemDao, myDaoConfig, + mySearchParamRegistry, myValidationSupport, extenderList); + confProvider.setImplementationDescription( + firstNonNull(implementationDescription, "CQF RULER R5 Server")); setServerConformanceProvider(confProvider); } else { throw new IllegalStateException(); } } - } } diff --git a/core/src/main/java/org/opencds/cqf/ruler/behavior/DaoRegistryUser.java b/core/src/main/java/org/opencds/cqf/ruler/behavior/DaoRegistryUser.java index f45418632..901d4d0f3 100644 --- a/core/src/main/java/org/opencds/cqf/ruler/behavior/DaoRegistryUser.java +++ b/core/src/main/java/org/opencds/cqf/ruler/behavior/DaoRegistryUser.java @@ -13,7 +13,6 @@ import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; /** * Simulate FhirDal operations until that's fully baked. This interface is diff --git a/plugin/atr/MAL-API's.postman_collection.json b/plugin/atr/MAL-API's.postman_collection.json new file mode 100644 index 000000000..9507e3cb4 --- /dev/null +++ b/plugin/atr/MAL-API's.postman_collection.json @@ -0,0 +1,248 @@ +{ + "info": { + "_postman_id": "aaf26af2-af7c-415a-b535-d0d938b5d838", + "name": "MAL-API's", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "POST Bundle", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"resourceType\": \"Bundle\",\r\n \"id\": \"member-attribution-bundle\",\r\n \"type\": \"transaction\",\r\n \"entry\": [\r\n {\r\n \"resource\": {\r\n \"resourceType\": \"Patient\",\r\n \"id\": \"denom-EXM130\",\r\n \"meta\": {\r\n \"versionId\": \"3\",\r\n \"lastUpdated\": \"2019-04-23T10:30:54.819-04:00\",\r\n \"tag\": [\r\n {\r\n \"system\": \"https://smarthealthit.org/tags\",\r\n \"code\": \"smart-7-2017\"\r\n }\r\n ]\r\n },\r\n \"text\": {\r\n \"status\": \"generated\",\r\n \"div\": \"
Dominique369 Ledner144
\"\r\n },\r\n \"identifier\": [\r\n {\r\n \"type\": {\r\n \"coding\": [\r\n {\r\n \"system\": \"http://terminology.hl7.org/CodeSystem/v2-0203\",\r\n \"code\": \"MC\",\r\n \"display\": \"Patient's Medicare number\"\r\n }\r\n ],\r\n \"text\": \"Patient's Medicare number\"\r\n },\r\n \"system\": \"http://hospital.smarthealthit.org\",\r\n \"value\": \"12345\"\r\n },\r\n {\r\n \"type\": {\r\n \"coding\": [\r\n {\r\n \"system\": \"http://terminology.hl7.org/CodeSystem/v2-0203\",\r\n \"code\": \"MA\",\r\n \"display\": \"Patient Medicaid number\"\r\n }\r\n ],\r\n \"text\": \"Patient Medicaid number\"\r\n },\r\n \"system\": \"http://hospital.smarthealthit.org\",\r\n \"value\": \"67890\"\r\n },\r\n {\r\n \"type\": {\r\n \"coding\": [\r\n {\r\n \"system\": \"http://terminology.hl7.org/CodeSystem/v2-0203\",\r\n \"code\": \"MR\",\r\n \"display\": \"Medical record number\"\r\n }\r\n ],\r\n \"text\": \"Medical record number\"\r\n },\r\n \"system\": \"http://hospital.smarthealthit.org\",\r\n \"value\": \"55555\"\r\n },\r\n {\r\n \"type\": {\r\n \"coding\": [\r\n {\r\n \"system\": \"http://terminology.hl7.org/CodeSystem/v2-0203\",\r\n \"code\": \"MB\",\r\n \"display\": \"Member Number\"\r\n }\r\n ],\r\n \"text\": \"Member Number\"\r\n },\r\n \"system\": \"http://hospital.smarthealthit.org\",\r\n \"value\": \"13579\"\r\n },\r\n {\r\n \"type\": {\r\n \"coding\": [\r\n {\r\n \"system\": \"http://terminology.hl7.org/CodeSystem/v2-0203\",\r\n \"code\": \"SS\",\r\n \"display\": \"Social Security number\"\r\n }\r\n ],\r\n \"text\": \"Social Security number\"\r\n },\r\n \"system\": \"http://hospital.smarthealthit.org\",\r\n \"value\": \"99999\"\r\n }\r\n ],\r\n \"active\": true,\r\n \"name\": [\r\n {\r\n \"use\": \"official\",\r\n \"family\": \"Ledner144\",\r\n \"given\": [\r\n \"Dominique369\"\r\n ]\r\n }\r\n ],\r\n \"telecom\": [\r\n {\r\n \"system\": \"phone\",\r\n \"value\": \"800-504-7344\",\r\n \"use\": \"home\"\r\n },\r\n {\r\n \"system\": \"email\",\r\n \"value\": \"brian.gracia@example.com\"\r\n }\r\n ],\r\n \"gender\": \"female\",\r\n \"birthDate\": \"1965-06-22\",\r\n \"deceasedBoolean\": false,\r\n \"address\": [\r\n {\r\n \"use\": \"home\",\r\n \"line\": [\r\n \"25 Church St\"\r\n ],\r\n \"city\": \"Bixby\",\r\n \"state\": \"OK\",\r\n \"postalCode\": \"74008\",\r\n \"country\": \"USA\"\r\n }\r\n ],\r\n \"maritalStatus\": {\r\n \"coding\": [\r\n {\r\n \"system\": \"http://snomed.info/sct\",\r\n \"code\": \"36629006\",\r\n \"display\": \"Legally married\"\r\n },\r\n {\r\n \"system\": \"http://terminology.hl7.org/CodeSystem/v3-MaritalStatus\",\r\n \"code\": \"M\"\r\n }\r\n ]\r\n },\r\n \"communication\": [\r\n {\r\n \"language\": {\r\n \"coding\": [\r\n {\r\n \"system\": \"urn:ietf:bcp:47\",\r\n \"code\": \"nl-NL\",\r\n \"display\": \"Dutch\"\r\n }\r\n ]\r\n },\r\n \"preferred\": true\r\n }\r\n ],\r\n \"generalPractitioner\": [\r\n {\r\n \"reference\": \"Practitioner/9bf77508-42d8-420f-a371-88ec287cc55d\"\r\n }\r\n ],\r\n \"managingOrganization\": {\r\n \"reference\": \"Organization/6c9380b5-2d9d-4675-89a1-f7048486a2f4\",\r\n \"display\": \"ACME Healthcare, Inc\"\r\n }\r\n },\r\n \"request\": {\r\n \"method\": \"PUT\",\r\n \"url\": \"Patient/denom-EXM130\"\r\n }\r\n },\r\n {\r\n \"resource\": {\r\n \"resourceType\": \"Patient\",\r\n \"id\": \"5f355ff8-9f2d-4047-b3dd-91a9a5e75bd8\",\r\n \"meta\": {\r\n \"versionId\": \"2\",\r\n \"lastUpdated\": \"2018-04-25T02:41:21.772-04:00\",\r\n \"tag\": [\r\n {\r\n \"system\": \"https://smarthealthit.org/tags\",\r\n \"code\": \"smart-7-2017\"\r\n }\r\n ]\r\n },\r\n \"text\": {\r\n \"status\": \"generated\",\r\n \"div\": \"
Emilie407 Cole117
\"\r\n },\r\n \"identifier\": [\r\n {\r\n \"type\": {\r\n \"coding\": [\r\n {\r\n \"system\": \"http://terminology.hl7.org/CodeSystem/v2-0203\",\r\n \"code\": \"MC\",\r\n \"display\": \"Patient's Medicare number\"\r\n }\r\n ],\r\n \"text\": \"Patient's Medicare number\"\r\n },\r\n \"system\": \"http://hospital.smarthealthit.org\",\r\n \"value\": \"54321\"\r\n },\r\n {\r\n \"type\": {\r\n \"coding\": [\r\n {\r\n \"system\": \"http://terminology.hl7.org/CodeSystem/v2-0203\",\r\n \"code\": \"MA\",\r\n \"display\": \"Patient Medicaid number\"\r\n }\r\n ],\r\n \"text\": \"Patient Medicaid number\"\r\n },\r\n \"system\": \"http://hospital.smarthealthit.org\",\r\n \"value\": \"09876\"\r\n },\r\n {\r\n \"type\": {\r\n \"coding\": [\r\n {\r\n \"system\": \"http://terminology.hl7.org/CodeSystem/v2-0203\",\r\n \"code\": \"MR\",\r\n \"display\": \"Medical record number\"\r\n }\r\n ],\r\n \"text\": \"Medical record number\"\r\n },\r\n \"system\": \"http://hospital.smarthealthit.org\",\r\n \"value\": \"56565\"\r\n },\r\n {\r\n \"type\": {\r\n \"coding\": [\r\n {\r\n \"system\": \"http://terminology.hl7.org/CodeSystem/v2-0203\",\r\n \"code\": \"MB\",\r\n \"display\": \"Member Number\"\r\n }\r\n ],\r\n \"text\": \"Member Number\"\r\n },\r\n \"system\": \"http://hospital.smarthealthit.org\",\r\n \"value\": \"909012\"\r\n },\r\n {\r\n \"type\": {\r\n \"coding\": [\r\n {\r\n \"system\": \"http://terminology.hl7.org/CodeSystem/v2-0203\",\r\n \"code\": \"SS\",\r\n \"display\": \"Social Security number\"\r\n }\r\n ],\r\n \"text\": \"Social Security number\"\r\n },\r\n \"system\": \"http://hospital.smarthealthit.org\",\r\n \"value\": \"321456\"\r\n }\r\n ],\r\n \"active\": true,\r\n \"name\": [\r\n {\r\n \"use\": \"official\",\r\n \"family\": \"Cole117\",\r\n \"given\": [\r\n \"Emilie407\"\r\n ]\r\n }\r\n ],\r\n \"telecom\": [\r\n {\r\n \"system\": \"phone\",\r\n \"value\": \"31612345678\",\r\n \"use\": \"mobile\"\r\n },\r\n {\r\n \"system\": \"phone\",\r\n \"value\": \"31201234567\",\r\n \"use\": \"home\"\r\n }\r\n ],\r\n \"gender\": \"male\",\r\n \"birthDate\": \"1950-07-06\",\r\n \"deceasedBoolean\": false,\r\n \"address\": [\r\n {\r\n \"use\": \"home\",\r\n \"line\": [\r\n \"Bos en Lommerplein 280\"\r\n ],\r\n \"city\": \"Amsterdam\",\r\n \"state\": \"RJ\",\r\n \"postalCode\": \"1055\",\r\n \"country\": \"NLD\"\r\n }\r\n ],\r\n \"maritalStatus\": {\r\n \"coding\": [\r\n {\r\n \"system\": \"http://snomed.info/sct\",\r\n \"code\": \"36629006\",\r\n \"display\": \"Legally married\"\r\n },\r\n {\r\n \"system\": \"http://terminology.hl7.org/CodeSystem/v3-MaritalStatus\",\r\n \"code\": \"M\"\r\n }\r\n ]\r\n },\r\n \"communication\": [\r\n {\r\n \"language\": {\r\n \"coding\": [\r\n {\r\n \"system\": \"urn:ietf:bcp:47\",\r\n \"code\": \"nl\",\r\n \"display\": \"Dutch\"\r\n }\r\n ],\r\n \"text\": \"Nederlands\"\r\n },\r\n \"preferred\": true\r\n }\r\n ]\r\n },\r\n \"request\": {\r\n \"method\": \"PUT\",\r\n \"url\": \"Patient/5f355ff8-9f2d-4047-b3dd-91a9a5e75bd8\"\r\n }\r\n },\r\n {\r\n \"resource\": {\r\n \"resourceType\": \"Patient\",\r\n \"id\": \"d3457d45-9085-4572-a78d-ed69c35d0d13\",\r\n \"meta\": {\r\n \"versionId\": \"1\",\r\n \"lastUpdated\": \"2018-04-25T02:41:22.204-04:00\",\r\n \"tag\": [\r\n {\r\n \"system\": \"https://smarthealthit.org/tags\",\r\n \"code\": \"smart-7-2017\"\r\n }\r\n ]\r\n },\r\n \"text\": {\r\n \"status\": \"generated\",\r\n \"div\": \"
Malcolm243 Erdman779
\"\r\n },\r\n \"identifier\": [\r\n {\r\n \"type\": {\r\n \"coding\": [\r\n {\r\n \"system\": \"http://terminology.hl7.org/CodeSystem/v2-0203\",\r\n \"code\": \"MC\",\r\n \"display\": \"Patient's Medicare number\"\r\n }\r\n ],\r\n \"text\": \"Patient's Medicare number\"\r\n },\r\n \"system\": \"http://hospital.smarthealthit.org\",\r\n \"value\": \"907861\"\r\n },\r\n {\r\n \"type\": {\r\n \"coding\": [\r\n {\r\n \"system\": \"http://terminology.hl7.org/CodeSystem/v2-0203\",\r\n \"code\": \"MA\",\r\n \"display\": \"Patient Medicaid number\"\r\n }\r\n ],\r\n \"text\": \"Patient Medicaid number\"\r\n },\r\n \"system\": \"http://hospital.smarthealthit.org\",\r\n \"value\": \"786543\"\r\n },\r\n {\r\n \"type\": {\r\n \"coding\": [\r\n {\r\n \"system\": \"http://terminology.hl7.org/CodeSystem/v2-0203\",\r\n \"code\": \"MR\",\r\n \"display\": \"Medical record number\"\r\n }\r\n ],\r\n \"text\": \"Medical record number\"\r\n },\r\n \"system\": \"http://hospital.smarthealthit.org\",\r\n \"value\": \"765432\"\r\n },\r\n {\r\n \"type\": {\r\n \"coding\": [\r\n {\r\n \"system\": \"http://terminology.hl7.org/CodeSystem/v2-0203\",\r\n \"code\": \"MB\",\r\n \"display\": \"Member Number\"\r\n }\r\n ],\r\n \"text\": \"Member Number\"\r\n },\r\n \"system\": \"http://hospital.smarthealthit.org\",\r\n \"value\": \"786778\"\r\n },\r\n {\r\n \"type\": {\r\n \"coding\": [\r\n {\r\n \"system\": \"http://terminology.hl7.org/CodeSystem/v2-0203\",\r\n \"code\": \"SS\",\r\n \"display\": \"Social Security number\"\r\n }\r\n ],\r\n \"text\": \"Social Security number\"\r\n },\r\n \"system\": \"http://hospital.smarthealthit.org\",\r\n \"value\": \"451231\"\r\n }\r\n ],\r\n \"active\": true,\r\n \"name\": [\r\n {\r\n \"use\": \"official\",\r\n \"family\": \"Erdman779\",\r\n \"given\": [\r\n \"Malcolm243\"\r\n ]\r\n }\r\n ],\r\n \"telecom\": [\r\n {\r\n \"system\": \"phone\",\r\n \"value\": \"800-613-1713\",\r\n \"use\": \"mobile\"\r\n },\r\n {\r\n \"system\": \"email\",\r\n \"value\": \"carol.allen@example.com\"\r\n }\r\n ],\r\n \"gender\": \"female\",\r\n \"birthDate\": \"1963-12-26\",\r\n \"deceasedBoolean\": false,\r\n \"address\": [\r\n {\r\n \"use\": \"home\",\r\n \"line\": [\r\n \"67 Meadow St\"\r\n ],\r\n \"city\": \"Sapulpa\",\r\n \"state\": \"OK\",\r\n \"postalCode\": \"74066\",\r\n \"country\": \"USA\"\r\n }\r\n ],\r\n \"maritalStatus\": {\r\n \"coding\": [\r\n {\r\n \"system\": \"http://terminology.hl7.org/CodeSystem/v3-MaritalStatus\",\r\n \"code\": \"M\",\r\n \"display\": \"Married\"\r\n }\r\n ],\r\n \"text\": \"Getrouwd\"\r\n },\r\n \"communication\": [\r\n {\r\n \"language\": {\r\n \"coding\": [\r\n {\r\n \"system\": \"urn:ietf:bcp:47\",\r\n \"code\": \"nl-NL\",\r\n \"display\": \"Dutch\"\r\n }\r\n ]\r\n },\r\n \"preferred\": true\r\n }\r\n ],\r\n \"managingOrganization\": {\r\n \"reference\": \"Organization/6c9380b5-2d9d-4675-89a1-f7048486a2f4\",\r\n \"display\": \"ACME Healthcare, Inc\"\r\n }\r\n },\r\n \"request\": {\r\n \"method\": \"PUT\",\r\n \"url\": \"Patient/d3457d45-9085-4572-a78d-ed69c35d0d13\"\r\n }\r\n },\r\n {\r\n \"resource\": {\r\n \"resourceType\": \"Practitioner\",\r\n \"id\": \"9bf77508-42d8-420f-a371-88ec287cc55d\",\r\n \"meta\": {\r\n \"versionId\": \"4\",\r\n \"lastUpdated\": \"2019-03-06T03:07:57.933-05:00\",\r\n \"tag\": [\r\n {\r\n \"system\": \"https://smarthealthit.org/tags\",\r\n \"code\": \"smart-7-2017\"\r\n }\r\n ]\r\n },\r\n \"text\": {\r\n \"status\": \"generated\",\r\n \"div\": \"
Joseph Nichols
\"\r\n },\r\n \"identifier\": [\r\n {\r\n \"use\": \"official\",\r\n \"type\": {\r\n \"coding\": [\r\n {\r\n \"system\": \"http://hl7.org/fhir/identifier-type\",\r\n \"code\": \"SB\",\r\n \"display\": \"Social Beneficiary Identifier\"\r\n }\r\n ],\r\n \"text\": \"US Social Security Number\"\r\n },\r\n \"system\": \"http://hl7.org/fhir/sid/us-ssn\",\r\n \"value\": \"000-00-0004\"\r\n }\r\n ],\r\n \"active\": true,\r\n \"name\": [\r\n {\r\n \"use\": \"official\",\r\n \"family\": \"Nichols\",\r\n \"given\": [\r\n \"Joseph\",\r\n \"P\"\r\n ],\r\n \"suffix\": [\r\n \"MD\"\r\n ]\r\n }\r\n ],\r\n \"telecom\": [\r\n {\r\n \"system\": \"phone\",\r\n \"value\": \"800-277-1993\",\r\n \"use\": \"mobile\"\r\n },\r\n {\r\n \"system\": \"email\",\r\n \"value\": \"joseph.shaw@example.com\"\r\n }\r\n ],\r\n \"address\": [\r\n {\r\n \"use\": \"home\",\r\n \"line\": [\r\n \"53 Sunset AveApt 9\"\r\n ],\r\n \"city\": \"Tulsa\",\r\n \"state\": \"OK\",\r\n \"postalCode\": \"74126\",\r\n \"country\": \"USA\"\r\n }\r\n ],\r\n \"gender\": \"male\",\r\n \"birthDate\": \"1963-12-14\"\r\n },\r\n \"request\": {\r\n \"method\": \"PUT\",\r\n \"url\": \"Practitioner/9bf77508-42d8-420f-a371-88ec287cc55d\"\r\n }\r\n },\r\n {\r\n \"resource\": {\r\n \"resourceType\": \"RelatedPerson\",\r\n \"id\": \"b2cf72e8-62c2-4a61-a2b9-dcf90094aae7\",\r\n \"meta\": {\r\n \"versionId\": \"1\",\r\n \"lastUpdated\": \"2019-04-23T10:30:54.819-04:00\"\r\n },\r\n \"text\": {\r\n \"status\": \"generated\",\r\n \"div\": \"
\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n
NameB�n�dicte du March�
Address43, Place du March� Sainte Catherine, 75004 Paris, France
ContactsPhone: +33 (237) 998327
\\n
\"\r\n },\r\n \"identifier\": [\r\n {\r\n \"use\": \"official\",\r\n \"type\": {\r\n \"coding\": [\r\n {\r\n \"system\": \"http://terminology.hl7.org/CodeSystem/v2-0203\",\r\n \"code\": \"NPI\",\r\n \"display\": \"National Provider Identifier\"\r\n }\r\n ]\r\n },\r\n \"system\": \"https://sitenv.org\",\r\n \"value\": \"1256\"\r\n }\r\n ],\r\n \"active\": true,\r\n \"patient\": {\r\n \"reference\": \"Patient/d3457d45-9085-4572-a78d-ed69c35d0d13\"\r\n },\r\n \"relationship\": [\r\n {\r\n \"coding\": [\r\n {\r\n \"system\": \"http://terminology.hl7.org/CodeSystem/v2-0131\",\r\n \"code\": \"N\",\r\n \"display\": \"Next-of-Kin\"\r\n },\r\n {\r\n \"system\": \"http://terminology.hl7.org/CodeSystem/v3-RoleCode\",\r\n \"code\": \"WIFE\",\r\n \"display\": \"wife\"\r\n }\r\n ]\r\n }\r\n ],\r\n \"name\": [\r\n {\r\n \"use\": \"official\",\r\n \"family\": \"Notsowell\",\r\n \"given\": [\r\n \"Simon\"\r\n ],\r\n \"prefix\": [\r\n \"Mr.\"\r\n ],\r\n \"suffix\": [\r\n \"MSc\"\r\n ]\r\n }\r\n ],\r\n \"telecom\": [\r\n {\r\n \"system\": \"phone\",\r\n \"value\": \"+33 (237) 998327\"\r\n }\r\n ],\r\n \"gender\": \"female\",\r\n \"address\": [\r\n {\r\n \"line\": [\r\n \"43, Place du March� Sainte Catherine\"\r\n ],\r\n \"city\": \"Paris\",\r\n \"postalCode\": \"75004\",\r\n \"country\": \"FRA\"\r\n }\r\n ]\r\n },\r\n \"request\": {\r\n \"method\": \"PUT\",\r\n \"url\": \"RelatedPerson/b2cf72e8-62c2-4a61-a2b9-dcf90094aae7\"\r\n }\r\n },\r\n {\r\n \"resource\": {\r\n \"resourceType\": \"RelatedPerson\",\r\n \"id\": \"65b8aaa8-697a-43a9-8f6e-5615ecad344a\",\r\n \"meta\": {\r\n \"versionId\": \"1\",\r\n \"lastUpdated\": \"2019-04-23T10:30:54.819-04:00\"\r\n },\r\n \"text\": {\r\n \"status\": \"generated\",\r\n \"div\": \"
\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n
NameB�n�dicte du March�
Address43, Place du March� Sainte Catherine, 75004 Paris, France
ContactsPhone: +33 (237) 998327
\\n
\"\r\n },\r\n \"identifier\": [\r\n {\r\n \"use\": \"official\",\r\n \"type\": {\r\n \"coding\": [\r\n {\r\n \"system\": \"http://terminology.hl7.org/CodeSystem/v2-0203\",\r\n \"code\": \"NPI\",\r\n \"display\": \"National Provider Identifier\"\r\n }\r\n ]\r\n },\r\n \"system\": \"https://sitenv.org\",\r\n \"value\": \"5678\"\r\n }\r\n ],\r\n \"active\": true,\r\n \"patient\": {\r\n \"reference\": \"Patient/5f355ff8-9f2d-4047-b3dd-91a9a5e75bd8\"\r\n },\r\n \"relationship\": [\r\n {\r\n \"coding\": [\r\n {\r\n \"system\": \"http://terminology.hl7.org/CodeSystem/v2-0131\",\r\n \"code\": \"N\",\r\n \"display\": \"Next-of-Kin\"\r\n },\r\n {\r\n \"system\": \"http://terminology.hl7.org/CodeSystem/v3-RoleCode\",\r\n \"code\": \"WIFE\",\r\n \"display\": \"wife\"\r\n }\r\n ]\r\n }\r\n ],\r\n \"name\": [\r\n {\r\n \"use\": \"official\",\r\n \"family\": \"Duck\",\r\n \"given\": [\r\n \"Donald\"\r\n ],\r\n \"prefix\": [\r\n \"Mr.\"\r\n ],\r\n \"suffix\": [\r\n \"MSc\"\r\n ]\r\n }\r\n ],\r\n \"telecom\": [\r\n {\r\n \"system\": \"phone\",\r\n \"value\": \"+33 (237) 998327\"\r\n }\r\n ],\r\n \"gender\": \"female\",\r\n \"address\": [\r\n {\r\n \"line\": [\r\n \"43, Place du March� Sainte Catherine\"\r\n ],\r\n \"city\": \"Paris\",\r\n \"postalCode\": \"75004\",\r\n \"country\": \"FRA\"\r\n }\r\n ]\r\n },\r\n \"request\": {\r\n \"method\": \"PUT\",\r\n \"url\": \"RelatedPerson/65b8aaa8-697a-43a9-8f6e-5615ecad344a\"\r\n }\r\n },\r\n {\r\n \"resource\": {\r\n \"resourceType\": \"RelatedPerson\",\r\n \"id\": \"074c4667-06e5-409b-a4ae-335c83fe6ed5\",\r\n \"meta\": {\r\n \"versionId\": \"1\",\r\n \"lastUpdated\": \"2019-04-23T10:30:54.819-04:00\"\r\n },\r\n \"text\": {\r\n \"status\": \"generated\",\r\n \"div\": \"
\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n
NameB�n�dicte du March�
Address43, Place du March� Sainte Catherine, 75004 Paris, France
ContactsPhone: +33 (237) 998327
\\n
\"\r\n },\r\n \"identifier\": [\r\n {\r\n \"use\": \"official\",\r\n \"type\": {\r\n \"coding\": [\r\n {\r\n \"system\": \"http://terminology.hl7.org/CodeSystem/v2-0203\",\r\n \"code\": \"NPI\",\r\n \"display\": \"National Provider Identifier\"\r\n }\r\n ]\r\n },\r\n \"system\": \"https://sitenv.org\",\r\n \"value\": \"1234\"\r\n }\r\n ],\r\n \"active\": true,\r\n \"patient\": {\r\n \"reference\": \"Patient/denom-EXM130\"\r\n },\r\n \"relationship\": [\r\n {\r\n \"coding\": [\r\n {\r\n \"system\": \"http://terminology.hl7.org/CodeSystem/v2-0131\",\r\n \"code\": \"N\",\r\n \"display\": \"Next-of-Kin\"\r\n },\r\n {\r\n \"system\": \"http://terminology.hl7.org/CodeSystem/v3-RoleCode\",\r\n \"code\": \"WIFE\",\r\n \"display\": \"wife\"\r\n }\r\n ]\r\n }\r\n ],\r\n \"name\": [\r\n {\r\n \"use\": \"official\",\r\n \"family\": \"Douglas\",\r\n \"given\": [\r\n \"Bradly\"\r\n ],\r\n \"prefix\": [\r\n \"Mr.\"\r\n ],\r\n \"suffix\": [\r\n \"MSc\"\r\n ]\r\n }\r\n ],\r\n \"telecom\": [\r\n {\r\n \"system\": \"phone\",\r\n \"value\": \"+33 (237) 998327\"\r\n }\r\n ],\r\n \"gender\": \"female\",\r\n \"address\": [\r\n {\r\n \"line\": [\r\n \"43, Place du March� Sainte Catherine\"\r\n ],\r\n \"city\": \"Paris\",\r\n \"postalCode\": \"75004\",\r\n \"country\": \"FRA\"\r\n }\r\n ]\r\n },\r\n \"request\": {\r\n \"method\": \"PUT\",\r\n \"url\": \"RelatedPerson/074c4667-06e5-409b-a4ae-335c83fe6ed5\"\r\n }\r\n },\r\n {\r\n \"resource\": {\r\n \"resourceType\": \"Coverage\",\r\n \"id\": \"c8e3f0b5-0599-43ba-ab90-792c127819a1\",\r\n \"meta\": {\r\n \"versionId\": \"1\",\r\n \"lastUpdated\": \"2019-05-06T03:04:12.348-04:00\"\r\n },\r\n \"text\": {\r\n \"status\": \"generated\",\r\n \"div\": \"
A human-readable rendering of the coverage
\"\r\n },\r\n \"identifier\": [\r\n {\r\n \"use\": \"official\",\r\n \"type\": {\r\n \"coding\": [\r\n {\r\n \"system\": \"http://terminology.hl7.org/CodeSystem/v2-0203\",\r\n \"code\": \"MB\",\r\n \"display\": \"Member Number\"\r\n }\r\n ]\r\n },\r\n \"system\": \"http://hospital.smarthealthit.org\",\r\n \"value\": \"786778\"\r\n }\r\n ],\r\n \"status\": \"active\",\r\n \"policyHolder\": {\r\n \"reference\": \"RelatedPerson/b2cf72e8-62c2-4a61-a2b9-dcf90094aae7\"\r\n },\r\n \"subscriber\": {\r\n \"reference\": \"Patient/d3457d45-9085-4572-a78d-ed69c35d0d13\"\r\n },\r\n \"subscriberId\": \"AB3658\",\r\n \"beneficiary\": {\r\n \"reference\": \"Patient/d3457d45-9085-4572-a78d-ed69c35d0d13\"\r\n },\r\n \"dependent\": \"0\",\r\n \"relationship\": {\r\n \"coding\": [\r\n {\r\n \"system\": \"http://terminology.hl7.org/CodeSystem/subscriber-relationship\",\r\n \"code\": \"self\",\r\n \"display\": \"Self\"\r\n }\r\n ]\r\n },\r\n \"period\": {\r\n \"start\": \"2013-05-23\",\r\n \"end\": \"2014-05-23\"\r\n },\r\n \"payor\": [\r\n {\r\n \"reference\": \"Organization/6c9380b5-2d9d-4675-89a1-f7048486a2f4\"\r\n }\r\n ],\r\n \"class\": [\r\n {\r\n \"type\": {\r\n \"coding\": [\r\n {\r\n \"system\": \"http://terminology.hl7.org/CodeSystem/coverage-class\",\r\n \"code\": \"plan\"\r\n }\r\n ]\r\n },\r\n \"value\": \"B37FC\"\r\n }\r\n ]\r\n },\r\n \"request\": {\r\n \"method\": \"PUT\",\r\n \"url\": \"Coverage/074c4667-06e5-409b-a4ae-335c83fe6ed5\"\r\n }\r\n },\r\n {\r\n \"resource\": {\r\n \"resourceType\": \"Coverage\",\r\n \"id\": \"9bf7ab75-3ead-4a90-8409-eade27aa9008\",\r\n \"meta\": {\r\n \"versionId\": \"1\",\r\n \"lastUpdated\": \"2019-04-06T03:04:12.348-04:00\"\r\n },\r\n \"text\": {\r\n \"status\": \"generated\",\r\n \"div\": \"
A human-readable rendering of the coverage
\"\r\n },\r\n \"identifier\": [\r\n {\r\n \"use\": \"official\",\r\n \"type\": {\r\n \"coding\": [\r\n {\r\n \"system\": \"http://terminology.hl7.org/CodeSystem/v2-0203\",\r\n \"code\": \"MB\",\r\n \"display\": \"Member Number\"\r\n }\r\n ]\r\n },\r\n \"system\": \"http://hospital.smarthealthit.org\",\r\n \"value\": \"909012\"\r\n }\r\n ],\r\n \"status\": \"active\",\r\n \"policyHolder\": {\r\n \"reference\": \"RelatedPerson/65b8aaa8-697a-43a9-8f6e-5615ecad344a\"\r\n },\r\n \"subscriber\": {\r\n \"reference\": \"Patient/5f355ff8-9f2d-4047-b3dd-91a9a5e75bd8\"\r\n },\r\n \"subscriberId\": \"AB3999\",\r\n \"beneficiary\": {\r\n \"reference\": \"Patient/5f355ff8-9f2d-4047-b3dd-91a9a5e75bd8\"\r\n },\r\n \"dependent\": \"0\",\r\n \"relationship\": {\r\n \"coding\": [\r\n {\r\n \"system\": \"http://terminology.hl7.org/CodeSystem/subscriber-relationship\",\r\n \"code\": \"self\",\r\n \"display\": \"Self\"\r\n }\r\n ]\r\n },\r\n \"period\": {\r\n \"start\": \"2012-05-23\",\r\n \"end\": \"2013-05-23\"\r\n },\r\n \"payor\": [\r\n {\r\n \"reference\": \"Organization/6c9380b5-2d9d-4675-89a1-f7048486a2f4\"\r\n }\r\n ],\r\n \"class\": [\r\n {\r\n \"type\": {\r\n \"coding\": [\r\n {\r\n \"system\": \"http://terminology.hl7.org/CodeSystem/coverage-class\",\r\n \"code\": \"plan\"\r\n }\r\n ]\r\n },\r\n \"value\": \"B37FC\"\r\n }\r\n ]\r\n },\r\n \"request\": {\r\n \"method\": \"PUT\",\r\n \"url\": \"Coverage/9bf7ab75-3ead-4a90-8409-eade27aa9008\"\r\n }\r\n },\r\n {\r\n \"resource\": {\r\n \"resourceType\": \"Coverage\",\r\n \"id\": \"b66b770a-66da-4330-8463-cd0e93ef6626\",\r\n \"meta\": {\r\n \"versionId\": \"1\",\r\n \"lastUpdated\": \"2019-06-06T03:04:12.348-04:00\"\r\n },\r\n \"text\": {\r\n \"status\": \"generated\",\r\n \"div\": \"
A human-readable rendering of the coverage
\"\r\n },\r\n \"identifier\": [\r\n {\r\n \"use\": \"official\",\r\n \"type\": {\r\n \"coding\": [\r\n {\r\n \"system\": \"http://terminology.hl7.org/CodeSystem/v2-0203\",\r\n \"code\": \"MB\",\r\n \"display\": \"Member Number\"\r\n }\r\n ]\r\n },\r\n \"system\": \"http://hospital.smarthealthit.org\",\r\n \"value\": \"13579\"\r\n }\r\n ],\r\n \"status\": \"active\",\r\n \"policyHolder\": {\r\n \"reference\": \"RelatedPerson/074c4667-06e5-409b-a4ae-335c83fe6ed5\"\r\n },\r\n \"subscriber\": {\r\n \"reference\": \"Patient/denom-EXM130\"\r\n },\r\n \"subscriberId\": \"AB3658\",\r\n \"beneficiary\": {\r\n \"reference\": \"Patient/denom-EXM130\"\r\n },\r\n \"dependent\": \"0\",\r\n \"relationship\": {\r\n \"coding\": [\r\n {\r\n \"system\": \"http://terminology.hl7.org/CodeSystem/subscriber-relationship\",\r\n \"code\": \"self\",\r\n \"display\": \"Self\"\r\n }\r\n ]\r\n },\r\n \"period\": {\r\n \"start\": \"2011-05-23\",\r\n \"end\": \"2012-05-23\"\r\n },\r\n \"payor\": [\r\n {\r\n \"reference\": \"Organization/6c9380b5-2d9d-4675-89a1-f7048486a2f4\"\r\n }\r\n ],\r\n \"class\": [\r\n {\r\n \"type\": {\r\n \"coding\": [\r\n {\r\n \"system\": \"http://terminology.hl7.org/CodeSystem/coverage-class\",\r\n \"code\": \"plan\"\r\n }\r\n ]\r\n },\r\n \"value\": \"B37FC\"\r\n }\r\n ]\r\n },\r\n \"request\": {\r\n \"method\": \"PUT\",\r\n \"url\": \"Coverage/b66b770a-66da-4330-8463-cd0e93ef6626\"\r\n }\r\n },\r\n {\r\n \"resource\": {\r\n \"resourceType\": \"Location\",\r\n \"id\": \"66584742-6b72-432c-9439-1478434607fa\",\r\n \"meta\": {\r\n \"versionId\": \"1\",\r\n \"lastUpdated\": \"2019-06-06T03:04:12.348-04:00\"\r\n },\r\n \"identifier\": [\r\n {\r\n \"use\": \"official\",\r\n \"type\": {\r\n \"coding\": [\r\n {\r\n \"system\": \"http://terminology.hl7.org/CodeSystem/v2-0203\",\r\n \"code\": \"PIN\",\r\n \"display\": \"Premises Identifier Number\"\r\n }\r\n ]\r\n },\r\n \"system\": \"https://sitenv.org\",\r\n \"value\": \"1233\"\r\n }\r\n ],\r\n \"status\": \"active\",\r\n \"operationalStatus\": {\r\n \"system\": \"http://terminology.hl7.org/CodeSystem/v2-0116\",\r\n \"code\": \"H\",\r\n \"display\": \"Housekeeping\"\r\n },\r\n \"name\": \"South Wing, second floor\",\r\n \"alias\": [\r\n \"BU MC, SW, F2\",\r\n \"Burgers University Medical Center, South Wing, second floor\"\r\n ],\r\n \"description\": \"Second floor of the Old South Wing, formerly in use by Psychiatry\",\r\n \"mode\": \"instance\",\r\n \"telecom\": [\r\n {\r\n \"system\": \"phone\",\r\n \"value\": \"2328\",\r\n \"use\": \"work\"\r\n },\r\n {\r\n \"system\": \"fax\",\r\n \"value\": \"2329\",\r\n \"use\": \"work\"\r\n },\r\n {\r\n \"system\": \"email\",\r\n \"value\": \"second wing admissions\"\r\n },\r\n {\r\n \"system\": \"url\",\r\n \"value\": \"http://sampleorg.com/southwing\",\r\n \"use\": \"work\"\r\n }\r\n ],\r\n \"address\": {\r\n \"use\": \"work\",\r\n \"line\": [\r\n \"Galapagosweg 91, Building A\"\r\n ],\r\n \"city\": \"Den Burg\",\r\n \"state\": \"MD\",\r\n \"postalCode\": \"9105 PZ\",\r\n \"country\": \"NLD\"\r\n },\r\n \"physicalType\": {\r\n \"coding\": [\r\n {\r\n \"system\": \"http://terminology.hl7.org/CodeSystem/location-physical-type\",\r\n \"code\": \"wi\",\r\n \"display\": \"Wing\"\r\n }\r\n ]\r\n },\r\n \"position\": {\r\n \"longitude\": -83.6945691,\r\n \"latitude\": 42.25475478,\r\n \"altitude\": 0\r\n },\r\n \"managingOrganization\": {\r\n \"reference\": \"Organization/6c9380b5-2d9d-4675-89a1-f7048486a2f4\"\r\n },\r\n \"endpoint\": [\r\n {\r\n \"reference\": \"Endpoint/example\"\r\n }\r\n ]\r\n },\r\n \"request\": {\r\n \"method\": \"PUT\",\r\n \"url\": \"Location/66584742-6b72-432c-9439-1478434607fa\"\r\n }\r\n },\r\n {\r\n \"resource\": {\r\n \"resourceType\": \"PractitionerRole\",\r\n \"id\": \"c6e70834-8d30-419e-b7c5-7150a9e9127e\",\r\n \"text\": {\r\n \"status\": \"generated\",\r\n \"div\": \"
\\n\\t\\t\\t

\\n\\t\\t\\t\\tDr Adam Careful is a Referring Practitioner for Acme Hospital from 1-Jan 2012 to 31-Mar\\n\\t\\t\\t\\t2012\\n\\t\\t\\t

\\n\\t\\t
\"\r\n },\r\n \"identifier\": [\r\n {\r\n \"system\": \"http://www.acme.org/practitioners\",\r\n \"value\": \"23\"\r\n }\r\n ],\r\n \"active\": true,\r\n \"period\": {\r\n \"start\": \"2012-01-01\",\r\n \"end\": \"2012-03-31\"\r\n },\r\n \"practitioner\": {\r\n \"reference\": \"Practitioner/9bf77508-42d8-420f-a371-88ec287cc55d\",\r\n \"display\": \"Dr Adam Careful\"\r\n },\r\n \"organization\": {\r\n \"reference\": \"Organization/6c9380b5-2d9d-4675-89a1-f7048486a2f4\"\r\n },\r\n \"code\": [\r\n {\r\n \"coding\": [\r\n {\r\n \"system\": \"http://terminology.hl7.org/CodeSystem/v2-0286\",\r\n \"code\": \"RP\"\r\n }\r\n ]\r\n }\r\n ],\r\n \"specialty\": [\r\n {\r\n \"coding\": [\r\n {\r\n \"system\": \"http://snomed.info/sct\",\r\n \"code\": \"408443003\",\r\n \"display\": \"General medical practice\"\r\n }\r\n ]\r\n }\r\n ],\r\n \"location\": [\r\n {\r\n \"reference\": \"Location/66584742-6b72-432c-9439-1478434607fa\",\r\n \"display\": \"South Wing, second floor\"\r\n }\r\n ],\r\n \"telecom\": [\r\n {\r\n \"system\": \"phone\",\r\n \"value\": \"(03) 5555 6473\",\r\n \"use\": \"work\"\r\n },\r\n {\r\n \"system\": \"email\",\r\n \"value\": \"adam.southern@example.org\",\r\n \"use\": \"work\"\r\n }\r\n ],\r\n \"availableTime\": [\r\n {\r\n \"daysOfWeek\": [\r\n \"mon\",\r\n \"tue\",\r\n \"wed\"\r\n ],\r\n \"availableStartTime\": \"09:00:00\",\r\n \"availableEndTime\": \"16:30:00\"\r\n },\r\n {\r\n \"daysOfWeek\": [\r\n \"thu\",\r\n \"fri\"\r\n ],\r\n \"availableStartTime\": \"09:00:00\",\r\n \"availableEndTime\": \"12:00:00\"\r\n }\r\n ],\r\n \"notAvailable\": [\r\n {\r\n \"description\": \"Adam will be on extended leave during May 2017\",\r\n \"during\": {\r\n \"start\": \"2017-05-01\",\r\n \"end\": \"2017-05-20\"\r\n }\r\n }\r\n ],\r\n \"availabilityExceptions\": \"Adam is generally unavailable on public holidays and during the Christmas/New Year break\",\r\n \"endpoint\": [\r\n {\r\n \"reference\": \"Endpoint/example\"\r\n }\r\n ]\r\n },\r\n \"request\": {\r\n \"method\": \"PUT\",\r\n \"url\": \"PractitionerRole/c6e70834-8d30-419e-b7c5-7150a9e9127e\"\r\n }\r\n },\r\n {\r\n \"resource\": {\r\n \"resourceType\": \"Organization\",\r\n \"id\": \"9118ee2c-9f6d-4a40-9988-4f26ff766864\",\r\n \"meta\": {\r\n \"versionId\": \"1\"\r\n },\r\n \"text\": {\r\n \"status\": \"generated\",\r\n \"div\": \"
\\n Health Level Seven International\\n
\\n\\t\\t\\t\\t3300 Washtenaw Avenue, Suite 227\\n
\\n\\t\\t\\t\\tAnn Arbor, MI 48104\\n
\\n\\t\\t\\t\\tUSA\\n
\\n\\t\\t\\t\\t(+1) 734-677-7777 (phone)\\n
\\n\\t\\t\\t\\t(+1) 734-677-6622 (fax)\\n
\\n\\t\\t\\t\\tE-mail: \\n hq@HL7.org\\n \\n
\"\r\n },\r\n \"identifier\": [\r\n {\r\n \"system\": \"http://www.acme.org.au/units\",\r\n \"value\": \"ClinLab\"\r\n }\r\n ],\r\n \"active\": true,\r\n \"type\": [\r\n {\r\n \"coding\": [\r\n {\r\n \"system\": \"http://terminology.hl7.org/CodeSystem/organization-type\",\r\n \"code\": \"dept\",\r\n \"display\": \"Hospital Department\"\r\n }\r\n ]\r\n }\r\n ],\r\n \"name\": \"Health Level Seven International\",\r\n \"alias\": [\r\n \"HL7 International\"\r\n ],\r\n \"telecom\": [\r\n {\r\n \"system\": \"phone\",\r\n \"value\": \"(+1) 734-677-7777\"\r\n },\r\n {\r\n \"system\": \"fax\",\r\n \"value\": \"(+1) 734-677-6622\"\r\n },\r\n {\r\n \"system\": \"email\",\r\n \"value\": \"hq@HL7.org\"\r\n }\r\n ],\r\n \"address\": [\r\n {\r\n \"line\": [\r\n \"3300 Washtenaw Avenue, Suite 227\"\r\n ],\r\n \"district\": \"Ann Arbor\",\r\n \"state\": \"MI\",\r\n \"postalCode\": \"48104\",\r\n \"country\": \"USA\"\r\n }\r\n ],\r\n \"endpoint\": [\r\n {\r\n \"reference\": \"Endpoint/example\"\r\n }\r\n ]\r\n },\r\n \"request\": {\r\n \"method\": \"PUT\",\r\n \"url\": \"Organization/9118ee2c-9f6d-4a40-9988-4f26ff766864\"\r\n }\r\n }\r\n ]\r\n}" + }, + "url": { + "raw": "http://localhost:8080/fhir", + "protocol": "http", + "host": [ + "52", + "70", + "192", + "201" + ], + "port": "8080", + "path": [ + "fhir" + ] + } + }, + "response": [] + }, + { + "name": "POST Group", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"resourceType\": \"Bundle\",\r\n \"id\": \"member-attribution-group\",\r\n \"type\": \"transaction\",\r\n \"entry\": [\r\n {\r\n \"resource\": {\r\n \"resourceType\": \"Group\",\r\n \"id\": \"atr-group\",\r\n \"meta\": {\r\n \"versionId\": \"1\",\r\n \"lastUpdated\": \"2019-06-06T03:04:12.348-04:00\"\r\n },\r\n \"text\": {\r\n \"status\": \"additional\",\r\n \"div\": \"
\\n

Selected Patients

\\n
    \\n
  • Patient Donald DUCK @ Acme Healthcare, Inc. MR = 654321
  • \\n
  • Patient Donald D DUCK @ Acme Healthcare, Inc. MR = 123456
  • \\n
  • Patient Simon Notsowell @ Acme Healthcare, Inc. MR = 123457, DECEASED
  • \\n
  • Patient Sandy Notsowell @ Acme Healthcare, Inc. MR = 123458, DECEASED
  • \\n
\\n
\"\r\n },\r\n \"extension\": [\r\n {\r\n \"url\": \"http://hl7.org/fhir/us/davinci-atr/StructureDefinition/ext-membershipValidityPeriod\",\r\n \"valuePeriod\": {\r\n \"start\": \"2020-07-25\",\r\n \"end\": \"2021-06-24\"\r\n }\r\n }\r\n ],\r\n \"identifier\": [\r\n {\r\n \"use\": \"official\",\r\n \"type\": {\r\n \"coding\": [\r\n {\r\n \"system\": \"http://terminology.hl7.org/CodeSystem/v2-0203\",\r\n \"code\": \"NPI\",\r\n \"display\": \"National Provider Identifier\"\r\n }\r\n ]\r\n },\r\n \"system\": \"https://sitenv.org\",\r\n \"value\": \"1316206220\"\r\n },\r\n {\r\n \"use\": \"official\",\r\n \"type\": {\r\n \"coding\": [\r\n {\r\n \"system\": \"http://terminology.hl7.org/CodeSystem/v2-0203\",\r\n \"code\": \"TAX\",\r\n \"display\": \"Tax ID Number\"\r\n }\r\n ]\r\n },\r\n \"system\": \"https://sitenv.org\",\r\n \"value\": \"789456231\"\r\n }\r\n ],\r\n \"active\": true,\r\n \"type\": \"person\",\r\n \"actual\": true,\r\n \"name\": \"Test Group 3\",\r\n \"managingEntity\": {\r\n \"reference\": \"Organization/6c9380b5-2d9d-4675-89a1-f7048486a2f4\",\r\n \"display\": \"Healthcare related organization\"\r\n },\r\n \"characteristic\": [\r\n {\r\n \"code\": {\r\n \"text\": \"gender\"\r\n },\r\n \"valueReference\": {\r\n \"reference\": \"Organzation/6c9380b5-2d9d-4675-89a1-f7048486a2f4\",\r\n \"display\": \"Healthcare related organization\"\r\n },\r\n \"exclude\": false\r\n }\r\n ]\r\n },\r\n \"request\": {\r\n \"method\": \"PUT\",\r\n \"url\": \"Group/atr-group\"\r\n }\r\n }\r\n ]\r\n}" + }, + "url": { + "raw": "http://localhost:8080/fhir", + "protocol": "http", + "host": [ + "52", + "70", + "192", + "201" + ], + "port": "8080", + "path": [ + "fhir" + ] + } + }, + "response": [] + }, + { + "name": "Group Member Add", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/fhir+json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n\t\"resourceType\": \"Parameters\",\r\n\t\"parameter\": [{\r\n\t\t\"name\": \"memberId\",\r\n\t\t\"valueIdentifier\": {\r\n\t\t\t\"system\": \"http://hospital.smarthealthit.org\",\r\n\t\t\t\"value\": \"13579\"\r\n\t\t}\r\n\t}, {\r\n\t\t\"name\": \"providerNpi\",\r\n\t\t\"valueIdentifier\": {\r\n\t\t\t\"system\": \"http://hl7.org/fhir/sid/us-ssn\",\r\n\t\t\t\"value\": \"000-00-0004\"\r\n\t\t}\r\n\t}, {\r\n\t\t\"name\": \"attributionPeriod\",\r\n\t\t\"valuePeriod\": {\r\n\t\t\t\"start\": \"2014-10-08T07:06:17+00:00\",\r\n\t\t\t\"end\": \"2025-10-08T07:06:17+00:00\"\r\n\t\t}\r\n\t}]\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:8080/fhir/Group/atr-group/$member-add", + "protocol": "http", + "host": [ + "52", + "70", + "192", + "201" + ], + "port": "8080", + "path": [ + "fhir", + "Group", + "atr-group", + "$member-add" + ] + } + }, + "response": [] + }, + { + "name": "Group Member Remove", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/fhir+json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n\t\"resourceType\": \"Parameters\",\r\n\t\"parameter\": [{\r\n\t\t\"name\": \"memberId\",\r\n\t\t\"valueIdentifier\": {\r\n\t\t\t\"system\": \"http://hospital.smarthealthit.org\",\r\n\t\t\t\"value\": \"786778\"\r\n\t\t}\r\n\t}, {\r\n\t\t\"name\": \"providerNpi\",\r\n\t\t\"valueIdentifier\": {\r\n\t\t\t\"system\": \"http://hl7.org/fhir/sid/us-ssn\",\r\n\t\t\t\"value\": \"000-00-0004\"\r\n\t\t}\r\n\t}, {\r\n\t\t\"name\": \"attributionPeriod\",\r\n\t\t\"valuePeriod\": {\r\n\t\t\t\"start\": \"2016-09-02T07:06:17+00:00\",\r\n\t\t\t\"end\": \"2022-05-10T07:06:17+00:00\"\r\n\t\t}\r\n\t}]\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://localhost:8080/fhir/Group/atr-group/$member-remove", + "protocol": "http", + "host": [ + "52", + "70", + "192", + "201" + ], + "port": "8080", + "path": [ + "fhir", + "Group", + "atr-group", + "$member-remove" + ] + } + }, + "response": [] + }, + { + "name": "Group-Export", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/fhir+json", + "type": "text" + }, + { + "key": "Prefer", + "value": "respond-async", + "type": "text" + } + ], + "url": { + "raw": "http://localhost:8080/fhir/Group/atr-group/$export?_type=Patient,Coverage,RelatedPerson", + "protocol": "http", + "host": [ + "52", + "70", + "192", + "201" + ], + "port": "8080", + "path": [ + "fhir", + "Group", + "atr-group", + "$export" + ], + "query": [ + { + "key": "_type", + "value": "Patient,Coverage,RelatedPerson" + } + ] + } + }, + "response": [] + }, + { + "name": "Polling-Location", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "http://localhost:8080/fhir/$export-poll-status?_jobId=0c52c5d4-475e-48b3-8296-e059a60dcd00", + "protocol": "http", + "host": [ + "52", + "70", + "192", + "201" + ], + "port": "8080", + "path": [ + "fhir", + "$export-poll-status" + ], + "query": [ + { + "key": "_jobId", + "value": "0c52c5d4-475e-48b3-8296-e059a60dcd00" + } + ] + } + }, + "response": [] + }, + { + "name": "Download-NDJSON", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "http://localhost:8080/fhir/Binary/17", + "protocol": "http", + "host": [ + "52", + "70", + "192", + "201" + ], + "port": "8080", + "path": [ + "fhir", + "Binary", + "17" + ] + } + }, + "response": [] + } + ] +} \ No newline at end of file diff --git a/plugin/atr/README.md b/plugin/atr/README.md new file mode 100644 index 000000000..29c76720a --- /dev/null +++ b/plugin/atr/README.md @@ -0,0 +1,26 @@ +# Plugin + +This is the repository for reference implementation of the DaVinci Risk Based Contracts Member Attribution List project. + +## Setup + +To setup the project follow the instructions mentioned [here](https://github.com/DBCG/cqf-ruler#development) + +## Usage + +To use the Member Attribution List API's import the [Postman collection](https://github.com/DBCG/cqf-ruler/blob/feature-mal/mal/MAL-API's.postman_collection.json) added as part of this plugin into the Postman client. + +### Instructions to test the MAL API's +1. Create a Bundle of type transaction and add Patient, Coverage, RelatedPerson, Practitioner, PractitionerRole, Organization, Location resources as bundle entries. +2. Use the POST Bundle request from postman collection to save all the resources into database. +3. Create a Group resource and use POST Group request from postman collection to save the Group resource into database. +4. Use Group Member add request from postman collection to add a new member into the `Group.member` data element. +5. Use Group Member remove request from postman collection to remove a member from the `Group.member` data element. +6. Use Group Export request from postman collection to initiate the request to export all the members data. +7. Use the Polling Location endpoint received in the response headers of Group Export Operation call to know the status of export operation. +8. Use the links received in the response body for each resource to download the data from Binary resource. + + +## Docker + +The Dockerfile builds on top of the base cqf-ruler image and simply copies the jar into the `plugin` directory of the image. diff --git a/plugin/atr/pom.xml b/plugin/atr/pom.xml new file mode 100644 index 000000000..5903728c2 --- /dev/null +++ b/plugin/atr/pom.xml @@ -0,0 +1,20 @@ + + + 4.0.0 + + org.opencds.cqf.ruler + cqf-ruler-plugin + 0.5.0-SNAPSHOT + + + cqf-ruler-atr + + + org.opencds.cqf.ruler + cqf-ruler-test + 0.5.0-SNAPSHOT + test + + + diff --git a/plugin/atr/src/main/java/org/opencds/cqf/ruler/atr/AtrConfig.java b/plugin/atr/src/main/java/org/opencds/cqf/ruler/atr/AtrConfig.java new file mode 100644 index 000000000..7d11c4cc9 --- /dev/null +++ b/plugin/atr/src/main/java/org/opencds/cqf/ruler/atr/AtrConfig.java @@ -0,0 +1,38 @@ +package org.opencds.cqf.ruler.atr; + +import org.opencds.cqf.ruler.api.OperationProvider; +import org.opencds.cqf.ruler.atr.service.MalService; +import org.opencds.cqf.ruler.external.annotations.OnR4Condition; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Conditional; +import org.springframework.context.annotation.Configuration; + + +/** + * The Class MALConfig. + */ +@Configuration +public class AtrConfig { + + /** + * Member add provider. + * + * @return the operation provider + */ + @Bean + @Conditional(OnR4Condition.class) + public OperationProvider memberAddProvider() { + return new MalProvider(); + } + + /** + * Mal service. + * + * @return the MAL service + */ + @Bean + @Conditional(OnR4Condition.class) + public MalService malService() { + return new MalService(); + } +} diff --git a/plugin/atr/src/main/java/org/opencds/cqf/ruler/atr/MalProvider.java b/plugin/atr/src/main/java/org/opencds/cqf/ruler/atr/MalProvider.java new file mode 100644 index 000000000..803ac863d --- /dev/null +++ b/plugin/atr/src/main/java/org/opencds/cqf/ruler/atr/MalProvider.java @@ -0,0 +1,44 @@ +package org.opencds.cqf.ruler.atr; + +import org.hl7.fhir.r4.model.Group; +import org.hl7.fhir.r4.model.IdType; +import org.hl7.fhir.r4.model.Parameters; +import org.opencds.cqf.ruler.atr.service.MalService; +import org.opencds.cqf.ruler.provider.DaoRegistryOperationProvider; +import org.springframework.beans.factory.annotation.Autowired; +import ca.uhn.fhir.model.api.annotation.Description; +import ca.uhn.fhir.rest.annotation.IdParam; +import ca.uhn.fhir.rest.annotation.Operation; +import ca.uhn.fhir.rest.annotation.ResourceParam; +import ca.uhn.fhir.rest.api.server.RequestDetails; + + +public class MalProvider extends DaoRegistryOperationProvider { + @Autowired + private MalService malService; + + /** + * Member add. + * + * @param groupId the group id + * @param requestDetails the request details + * @param request the request + * @param response the response + */ + @Description(shortDefinition = "Add new Member to the Group", + value = "Implements the $member-add operation") + @Operation(idempotent = true, name = "$member-add", type = Group.class) + public Group memberAdd(@IdParam IdType groupId, @ResourceParam Parameters theParameters, + RequestDetails requestDetails) { + return malService.processAddMemberToGroup(theParameters, groupId, requestDetails); + } + + + @Description(shortDefinition = "Remove Member from the Group", + value = "Implements the $member-remove operation") + @Operation(idempotent = true, name = "$member-remove") + public Group memberRemove(@IdParam IdType groupId, @ResourceParam Parameters theParameters, + RequestDetails requestDetails) { + return malService.processRemoveMemberToGroup(theParameters, groupId.getIdPart()); + } +} diff --git a/plugin/atr/src/main/java/org/opencds/cqf/ruler/atr/service/MalService.java b/plugin/atr/src/main/java/org/opencds/cqf/ruler/atr/service/MalService.java new file mode 100644 index 000000000..bed81d2cc --- /dev/null +++ b/plugin/atr/src/main/java/org/opencds/cqf/ruler/atr/service/MalService.java @@ -0,0 +1,801 @@ +package org.opencds.cqf.ruler.atr.service; + +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.apache.commons.lang3.StringUtils; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r4.model.BooleanType; +import org.hl7.fhir.r4.model.CodeType; +import org.hl7.fhir.r4.model.Coverage; +import org.hl7.fhir.r4.model.Extension; +import org.hl7.fhir.r4.model.Group; +import org.hl7.fhir.r4.model.Group.GroupMemberComponent; +import org.hl7.fhir.r4.model.IdType; +import org.hl7.fhir.r4.model.Meta; +import org.hl7.fhir.r4.model.Organization; +import org.hl7.fhir.r4.model.Parameters; +import org.hl7.fhir.r4.model.Patient; +import org.hl7.fhir.r4.model.Period; +import org.hl7.fhir.r4.model.Practitioner; +import org.hl7.fhir.r4.model.PractitionerRole; +import org.hl7.fhir.r4.model.Reference; +import org.hl7.fhir.r4.model.Resource; +import org.hl7.fhir.r4.model.ResourceType; +import org.opencds.cqf.ruler.atr.util.FhirUtil; +import org.opencds.cqf.ruler.atr.util.TextConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; +import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; +import ca.uhn.fhir.rest.api.server.IBundleProvider; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.param.TokenAndListParam; +import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; + +@Service +public class MalService { + @Autowired + private IFhirResourceDao patientDao; + + @Autowired + private IFhirResourceDao coverageDao; + + @Autowired + private IFhirResourceDao practitionerDao; + + @Autowired + private IFhirResourceDao practitionerRoleDao; + + @Autowired + private IFhirResourceDao organizationDao; + + @Autowired + private IFhirResourceDao groupDao; + + @Autowired + FhirContext fhirContext; + + private static final Logger logger = LoggerFactory.getLogger(MalService.class); + + /** + * Process add member to group. + * + * @param theParameters the the parameters + * @param theId the the id + * @param requestDetails the request details + * @return the group + */ + public Group processAddMemberToGroup(Parameters theParameters, IdType theId, + RequestDetails requestDetails) { + logger.info("RequestURL: {}", requestDetails.getFhirServerBase()); + String patientId = null; + String attributeProviderId = null; + String attributeProviderReferenceResource = null; + String coverageId = null; + Period attributionPeriod = null; + + Group group = groupDao.read(theId); + if (theParameters.getParameter("memberId") != null + && theParameters.getParameter("providerNpi") != null) { + // Creating the TokenAndListParam object to add into the SearchParamMap + SearchParameterMap patientParamMap = new SearchParameterMap(); + TokenAndListParam tokenParam = FhirUtil.createTokenAndListParam(theParameters, "memberId"); + patientParamMap.add(Patient.SP_IDENTIFIER, tokenParam); + // Invoking the Patient Search Dao API. + IBundleProvider bundle = patientDao.search(patientParamMap); + logger.info("Received Bundle with Size: {}", bundle.getAllResources().size()); + for (IBaseResource iBaseResource : bundle.getAllResources()) { + Resource resource = (Resource) iBaseResource; + if (resource.getResourceType().equals(ResourceType.Patient)) { + logger.info("patientId: {}", resource.getIdElement().getIdPart()); + patientId = resource.getIdElement().getIdPart(); + } + } + + // Get Practitioner Id using the NPI details from received Parameters. + // Creating the TokenAndListParam object to add into the SearchParamMap + Map providerMap = findProviderIdByIdentifier(theParameters); + if (providerMap != null && !providerMap.isEmpty()) { + for (Map.Entry entry : providerMap.entrySet()) { + attributeProviderId = entry.getValue(); + attributeProviderReferenceResource = entry.getKey(); + } + } + + // Get Coverage Id using the MemberId details from received Parameters. + // Creating the TokenAndListParam object to add into the SearchParamMap + SearchParameterMap coverageParamMap = new SearchParameterMap(); + coverageParamMap.add(Coverage.SP_IDENTIFIER, tokenParam); + // Invoking the Patient Search Dao API. + IBundleProvider coverageBundle = coverageDao.search(coverageParamMap); + logger.info("Received Bundle with Size: {}", coverageBundle.getAllResources().size()); + for (IBaseResource iBaseResource : coverageBundle.getAllResources()) { + Resource resource = (Resource) iBaseResource; + if (resource.getResourceType().equals(ResourceType.Coverage)) { + logger.info("coverageId: {}", resource.getIdElement().getIdPart()); + coverageId = resource.getIdElement().getIdPart(); + } + } + + if (theParameters.getParameter("attributionPeriod") != null) { + attributionPeriod = (Period) theParameters.getParameter("attributionPeriod"); + } + + } else if (theParameters.getParameter(TextConstants.PATIENT_REFERENCE) != null + && theParameters.getParameter(TextConstants.PROVIDER_REFERENCE) != null) { + String patientMemberId = findPatientIdByReference(theParameters); + if (StringUtils.isNotBlank(patientMemberId)) { + patientId = patientMemberId; + Map providerMap = findProviderIdByReference(theParameters); + if (providerMap != null && !providerMap.isEmpty()) { + for (Map.Entry entry : providerMap.entrySet()) { + attributeProviderId = entry.getValue(); + attributeProviderReferenceResource = entry.getKey(); + } + } else { + throw new ResourceNotFoundException( + "Couldn't find any Providers with given providerReference"); + } + String coverageResourceId = findCoverageIdByPatientId(patientId); + if (StringUtils.isNotBlank(coverageResourceId)) { + coverageId = coverageResourceId; + } + } else { + throw new ResourceNotFoundException( + "Couldn't find any Patient with given patientReference"); + } + } else { + throw new UnprocessableEntityException( + "Please provide memberId + providerNpi or patientReference + providerReference to $member-add."); + } + + if (patientId != null && attributeProviderId != null) { + logger.info("patientMemberId: {}", patientId); + logger.info("attributePeriod: {}", attributionPeriod); + logger.info("attributeProviderReferenceResource: {}", attributeProviderReferenceResource); + logger.info("attributeProviderId: {}", attributeProviderId); + logger.info("coverageReference: {}", coverageId); + if (attributionPeriod != null) { + logger.info("attributionPeriod.getStart(): {}", attributionPeriod.getStart()); + logger.info("attributionPeriod.getEnd(): {}", attributionPeriod.getEnd()); + } + addMemberToGroup(group, patientId, attributeProviderId, attributeProviderReferenceResource, + coverageId, attributionPeriod); + logger.info("After adding Member: {}", + fhirContext.newJsonParser().encodeResourceToString(group)); + groupDao.update(group); + if (group == null) { + throw new UnprocessableEntityException("Error while adding member to group"); + } + } else { + throw new ResourceNotFoundException( + "No Patient or Provider found. Please provide valid Patient/Provider"); + } + return group; + } + + /** + * Find coverage id by patient id. + * + * @param id the id + * @return the string + */ + private String findCoverageIdByPatientId(String id) { + String coverageId = null; + Coverage coverage = coverageDao.read(new IdType(id)); + if (coverage != null) { + coverageId = coverage.getIdElement().getIdPart(); + } + return coverageId; + } + + /** + * Find patient id by reference. + * + * @param theParameters the the parameters + * @return the string + */ + private String findPatientIdByReference(Parameters theParameters) { + String patientId = null; + Reference patientReference = + (Reference) theParameters.getParameter(TextConstants.PATIENT_REFERENCE); + + Patient patient = patientDao.read(patientReference.getReferenceElement()); + if (patient != null) { + patientId = patient.getIdElement().getIdPart(); + } + return patientId; + } + + /** + * Find provider id by reference. + * + * @param theParameters the the parameters + * @return the map + */ + private Map findProviderIdByReference(Parameters theParameters) { + Map providerMap = new HashMap<>(); + Reference providerReference = + (Reference) theParameters.getParameter(TextConstants.PROVIDER_REFERENCE); + String providerReferenceResource = providerReference.getReferenceElement().getResourceType(); + if (StringUtils.isNotBlank(providerReferenceResource) + && providerReferenceResource.equalsIgnoreCase("Practitioner")) { + Practitioner practitioner = practitionerDao.read(providerReference.getReferenceElement()); + if (practitioner != null && !practitioner.isEmpty()) { + providerMap.put("Practitioner", practitioner.getIdElement().getIdPart()); + } + } else if (StringUtils.isNotBlank(providerReferenceResource) + && providerReferenceResource.equalsIgnoreCase("PractitionerRole")) { + PractitionerRole practitionerRole = + practitionerRoleDao.read(providerReference.getReferenceElement()); + if (practitionerRole != null && !practitionerRole.isEmpty()) { + providerMap.put("PractitionerRole", practitionerRole.getIdElement().getIdPart()); + } + } else if (StringUtils.isNotBlank(providerReferenceResource) + && providerReferenceResource.equalsIgnoreCase("Organization")) { + Organization organization = organizationDao.read(providerReference.getReferenceElement()); + if (organization != null && !organization.isEmpty()) { + providerMap.put("Organization", organization.getIdElement().getIdPart()); + } + } + return providerMap; + } + + /** + * Find provider id by identifier. + * + * @param theParameters the the parameters + * @return the map + */ + private Map findProviderIdByIdentifier(Parameters theParameters) { + Map providerMap = new HashMap<>(); + SearchParameterMap attrProviderParamMap = new SearchParameterMap(); + TokenAndListParam practitionerTokenParam = + FhirUtil.createTokenAndListParam(theParameters, "providerNpi"); + attrProviderParamMap.add("identifier", practitionerTokenParam); + IBundleProvider practitionerBundle = practitionerDao.search(attrProviderParamMap); + if (practitionerBundle.getAllResources().isEmpty()) { + IBundleProvider practitionerRoleBundle = practitionerRoleDao.search(attrProviderParamMap); + if (practitionerRoleBundle.getAllResources().isEmpty()) { + IBundleProvider organizationBundle = organizationDao.search(attrProviderParamMap); + if (!organizationBundle.isEmpty()) { + addToMap(organizationBundle, providerMap); + } + } else { + addToMap(practitionerRoleBundle, providerMap); + } + } else { + addToMap(practitionerBundle, providerMap); + } + return providerMap; + } + + /** + * Adds the to map. + * + * @param bundle the bundle + * @param providerMap the provider map + * @return the map + */ + private Map addToMap(IBundleProvider bundle, Map providerMap) { + for (IBaseResource iBaseResource : bundle.getAllResources()) { + Resource resource = (Resource) iBaseResource; + if (resource.fhirType().equals("Organization")) { + providerMap.put("Organization", resource.getIdElement().getIdPart()); + } + if (resource.fhirType().equals("Practitioner")) { + providerMap.put("Practitioner", resource.getIdElement().getIdPart()); + } + if (resource.fhirType().equals("PractitionerRole")) { + providerMap.put("PractitionerRole", resource.getIdElement().getIdPart()); + } + } + return providerMap; + } + + /** + * Adds the member to group. + * + * @param group the group + * @param patientMemberId the patient member id + * @param providerId the provider id + * @param attrProviderResourceName the attr provider resource name + * @param coverageId the coverage id + * @param attributionPeriod the attribution period + */ + private void addMemberToGroup(Group group, String patientMemberId, String providerId, + String attrProviderResourceName, String coverageId, Period attributionPeriod) { + try { + boolean isAttributionCoverageFound = false; + boolean isMemberFound = false; + if (group.hasMember()) { + List memberList = group.getMember(); + for (GroupMemberComponent memberGroup : new ArrayList( + memberList)) { + String entityId = getEntityIdFromGroupMemberComponent(memberGroup); + String attributeProviderId = + getAttributeProviderIdFromGroupMemberComponent(memberGroup); + if (entityId != null && attributeProviderId != null) { + if (patientMemberId.equalsIgnoreCase(entityId) + && providerId.equalsIgnoreCase(attributeProviderId)) { + isMemberFound = true; + if (coverageId != null) { + isAttributionCoverageFound = + updateGroupMemberComponentCoverageReferenceExtension(memberGroup, + coverageId, isAttributionCoverageFound); + } + if (attributionPeriod != null) { + updateGroupMemberComponentAttributionPeriod(memberGroup, + isAttributionCoverageFound, attributionPeriod); + } + } + } + } + if (!isMemberFound) { + GroupMemberComponent theGroupMemberComponent = + FhirUtil.getGroupMemberComponent(patientMemberId, providerId, + attrProviderResourceName, coverageId, attributionPeriod); + if (theGroupMemberComponent != null) { + memberList.add(theGroupMemberComponent); + logger.info("Adding one new GroupMemberComponent"); + group.setMember(memberList); + } + } + } else { + List newGroupMemberComponentList = null; + GroupMemberComponent newGroupMemberComponent = + FhirUtil.getGroupMemberComponent(patientMemberId, providerId, + attrProviderResourceName, coverageId, attributionPeriod); + if (newGroupMemberComponent != null && !newGroupMemberComponent.isEmpty()) { + newGroupMemberComponentList = new ArrayList<>(); + newGroupMemberComponentList.add(newGroupMemberComponent); + logger.info("Adding new Member for first time for group"); + group.setMember(newGroupMemberComponentList); + } + } + if (group.hasMeta()) { + if (group.getMeta().hasVersionId()) { + String versionId = group.getMeta().getVersionId(); + int version = Integer.parseInt(versionId); + version = version + 1; + group.getMeta().setVersionId(String.valueOf(version)); + + } else { + group.getMeta().setVersionId("1"); + } + } else { + Meta meta = new Meta(); + meta.setVersionId("1"); + group.setMeta(meta); + } + } catch (Exception e) { + logger.error("Exception in addMemberToGroup of GroupServiceImpl ", e); + } + } + + /** + * Gets the group member component. + * + * @param patientMemberId the patient member id + * @param providerId the provider id + * @param providerReference the provider reference + * @param coverageReference the coverage reference + * @param attributionPeriod the attribution period + * @return the group member component + */ + public static GroupMemberComponent getGroupMemberComponent(String patientMemberId, + String providerId, String providerReference, String coverageReference, + Period attributionPeriod) { + GroupMemberComponent theGroupMemberComponent = new GroupMemberComponent(); + List theMemberExtensionList = null; + try { + if (StringUtils.isNotBlank(patientMemberId)) { + Reference theReference = FhirUtil.getReference(patientMemberId, "Patient"); + if (theReference != null) { + theGroupMemberComponent.setEntity(theReference); + BooleanType theBoolean = FhirUtil.getBooleanType(false); + theGroupMemberComponent.setInactiveElement(theBoolean); + } + } + theMemberExtensionList = FhirUtil.getGroupMemberComponentExtension(providerId, + providerReference, coverageReference, TextConstants.NEW_TYPE); + if (theMemberExtensionList != null && !theMemberExtensionList.isEmpty()) { + theGroupMemberComponent.setExtension(theMemberExtensionList); + } + if (attributionPeriod != null) { + Period thePeriod = FhirUtil.getPeriod(attributionPeriod.getStartElement(), + attributionPeriod.getEndElement()); + theGroupMemberComponent.setPeriod(thePeriod); + } + } catch (Exception ex) { + logger.error("\n Exception while setting getGroupMemberComponent in FhirUtility class ", + ex); + } + return theGroupMemberComponent; + } + + /** + * Gets the entity id from group member component. + * + * @param memberGroup the member group + * @return the entity id from group member component + */ + private String getEntityIdFromGroupMemberComponent(GroupMemberComponent memberGroup) { + String entityId = null; + try { + if (memberGroup.hasEntity() && memberGroup.getEntity().hasReferenceElement()) { + entityId = memberGroup.getEntity().getReferenceElement().getIdPart(); + } + } catch (Exception e) { + logger.info("Exception in getEntityIdFromGroupMemberComponent of GroupServiceImpl ", e); + } + return entityId; + } + + /** + * Gets the attribute provider id from group member component. + * + * @param memberGroup the member group + * @return the attribute provider id from group member component + */ + private String getAttributeProviderIdFromGroupMemberComponent(GroupMemberComponent memberGroup) { + String attributeProviderId = null; + try { + if (memberGroup.hasExtension(TextConstants.MEMBER_PROVIDER_SYSTEM)) { + if (memberGroup.getExtensionByUrl(TextConstants.MEMBER_PROVIDER_SYSTEM).hasValue()) { + Reference reference = (Reference) memberGroup + .getExtensionByUrl(TextConstants.MEMBER_PROVIDER_SYSTEM).getValue(); + attributeProviderId = reference.getReferenceElement().getIdPart(); + } + } + } catch (Exception e) { + logger.info( + "Exception in getAttributeProviderIdFromGroupMemberComponent of GroupServiceImpl ", + e); + } + return attributeProviderId; + } + + /** + * Update group member component coverage reference extension. + * + * @param memberGroup the member group + * @param coverageId the coverage id + * @param isAttributionCoverageFound the is attribution coverage found + * @return true, if successful + */ + private boolean updateGroupMemberComponentCoverageReferenceExtension( + GroupMemberComponent memberGroup, String coverageId, boolean isAttributionCoverageFound) { + try { + if (StringUtils.isNotBlank(coverageId)) { + if (memberGroup.hasExtension(TextConstants.MEMBER_COVERAGE_SYSTEM)) { + if (memberGroup.getExtensionByUrl(TextConstants.MEMBER_COVERAGE_SYSTEM).hasValue()) { + Reference reference = (Reference) memberGroup + .getExtensionByUrl(TextConstants.MEMBER_COVERAGE_SYSTEM).getValue(); + if (!coverageId.equalsIgnoreCase(reference.getReferenceElement().getIdPart())) { + Reference coverageReference = FhirUtil.getReference(coverageId, "Coverage"); + memberGroup.getExtensionByUrl(TextConstants.MEMBER_COVERAGE_SYSTEM) + .setValue(coverageReference); + updateGroupMemberComponentChangeTypeExtension(memberGroup, + TextConstants.CHANGE_TYPE); + isAttributionCoverageFound = true; + } else { + updateGroupMemberComponentChangeTypeExtension(memberGroup, + TextConstants.NOCHANGE_TYPE); + logger.info(" Coverage nochange "); + isAttributionCoverageFound = false; + } + } else { + Reference coverageReference = FhirUtil.getReference(coverageId, "Coverage"); + memberGroup.getExtensionByUrl(TextConstants.MEMBER_COVERAGE_SYSTEM) + .setValue(coverageReference); + updateGroupMemberComponentChangeTypeExtension(memberGroup, + TextConstants.CHANGE_TYPE); + isAttributionCoverageFound = true; + } + } else { + if (memberGroup.hasExtension()) { + List extensionList = memberGroup.getExtension(); + Extension coverageExtension = FhirUtil.getExtensionForReference(coverageId, + "Coverage", TextConstants.MEMBER_COVERAGE_SYSTEM); + if (coverageExtension != null && !coverageExtension.isEmpty()) { + extensionList.add(coverageExtension); + updateGroupMemberComponentChangeTypeExtension(memberGroup, + TextConstants.CHANGE_TYPE); + isAttributionCoverageFound = true; + } + } + } + } + } catch (Exception e) { + logger.error( + "Exception in updateGroupMemberComponentCoverageReferenceExtension of GroupServiceImpl ", + e); + } + return isAttributionCoverageFound; + } + + /** + * Update group member component change type extension. + * + * @param memberGroup the member group + * @param changeCode the change code + */ + private void updateGroupMemberComponentChangeTypeExtension(GroupMemberComponent memberGroup, + String changeCode) { + try { + if (StringUtils.isNotBlank(changeCode)) { + if (memberGroup.hasExtension(TextConstants.MEMBER_CHANGETYPE_SYSTEM)) { + CodeType codeType = FhirUtil.getCodeType(changeCode); + memberGroup.getExtensionByUrl(TextConstants.MEMBER_CHANGETYPE_SYSTEM) + .setValue(codeType); + } else { + if (memberGroup.hasExtension()) { + List extensionList = memberGroup.getExtension(); + Extension codeExtension = FhirUtil.getExtensionForCodeType(changeCode); + if (codeExtension != null && !codeExtension.isEmpty()) { + extensionList.add(codeExtension); + } + } + } + } + } catch (Exception e) { + logger.error( + "Exception in updateGroupMemberComponentChangeTypeExtension of GroupServiceImpl ", + e); + } + } + + /** + * Update group member component attribution period. + * + * @param memberGroup the member group + * @param isAttributionCoverageFound the is attribution coverage found + * @param attributionPeriod the attribution period + */ + private void updateGroupMemberComponentAttributionPeriod(GroupMemberComponent memberGroup, + boolean isAttributionCoverageFound, Period attributionPeriod) { + try { + if (attributionPeriod != null) { + Date startOne = null; + Date endOne = null; + Date memberStart = null; + Date memberEnd = null; + if (attributionPeriod.hasStart()) { + startOne = attributionPeriod.getStart(); + } + if (attributionPeriod.hasEnd()) { + endOne = attributionPeriod.getEnd(); + } + if (memberGroup.hasPeriod()) { + Period memberPeriod = memberGroup.getPeriod(); + if (memberPeriod.hasStart()) { + memberStart = memberPeriod.getStart(); + } + if (memberPeriod.hasEnd()) { + memberEnd = memberPeriod.getEnd(); + } + if (!startOne.equals(memberStart) || !endOne.equals(memberEnd)) { + memberGroup.setPeriod(attributionPeriod); + updateGroupMemberComponentChangeTypeExtension(memberGroup, + TextConstants.CHANGE_TYPE); + } else if (!isAttributionCoverageFound) { + updateGroupMemberComponentChangeTypeExtension(memberGroup, + TextConstants.NOCHANGE_TYPE); + } + } else { + memberGroup.setPeriod(attributionPeriod); + updateGroupMemberComponentChangeTypeExtension(memberGroup, + TextConstants.CHANGE_TYPE); + } + } + } catch (Exception e) { + logger.error( + "Exception in updateGroupMemberComponentAttributionPeriod of GroupServiceImpl ", e); + } + } + + public Group processRemoveMemberToGroup(Parameters theParameters, String groupId) { + String patientMemberId = null; + String attributeProviderId = null; + String attributeProviderReferenceResource = null; + String coverageReference = null; + Period attributionPeriod = null; + Group group = groupDao.read(new IdType(groupId)); + if (group != null && !group.isEmpty() && theParameters != null && !theParameters.isEmpty()) { + if (theParameters.getParameter(TextConstants.MEMBER_ID) != null) { + // Creating the TokenAndListParam object to add into the SearchParamMap + SearchParameterMap patientParamMap = new SearchParameterMap(); + TokenAndListParam tokenParam = + FhirUtil.createTokenAndListParam(theParameters, "memberId"); + patientParamMap.add(Patient.SP_IDENTIFIER, tokenParam); + // Invoking the Patient Search Dao API. + IBundleProvider bundle = patientDao.search(patientParamMap); + logger.info("Received Bundle with Size: {}", bundle.getAllResources().size()); + for (IBaseResource iBaseResource : bundle.getAllResources()) { + Resource resource = (Resource) iBaseResource; + if (resource.fhirType().equals("Patient")) { + logger.info("patientId: {}", resource.getIdElement().getIdPart()); + patientMemberId = resource.getIdElement().getIdPart(); + } + } + if (patientMemberId == null) { + throw new ResourceNotFoundException("Couldn't find any Patient with given memberId"); + } + + // Get Practitioner Id using the NPI details from received Parameters. + // Creating the TokenAndListParam object to add into the SearchParamMap + Map providerMap = findProviderIdByIdentifier(theParameters); + if (!providerMap.isEmpty()) { + for (Map.Entry entry : providerMap.entrySet()) { + attributeProviderId = entry.getValue(); + attributeProviderReferenceResource = entry.getKey(); + } + } + if (providerMap.isEmpty()) { + throw new ResourceNotFoundException( + "Couldn't find any Providers with given providerNpi"); + } + } else if (theParameters.getParameter(TextConstants.PATIENT_REFERENCE) != null) { + String patientId = findPatientIdByReference(theParameters); + if (StringUtils.isNotBlank(patientId)) { + patientMemberId = patientId; + if (theParameters.getParameter(TextConstants.PROVIDER_REFERENCE) != null) { + Map providerMap = findProviderIdByReference(theParameters); + if (providerMap != null && !providerMap.isEmpty()) { + for (Map.Entry entry : providerMap.entrySet()) { + attributeProviderId = entry.getValue(); + attributeProviderReferenceResource = entry.getKey(); + } + } else { + throw new ResourceNotFoundException( + "Couldn't find any Providers with given providerReference"); + } + } + String coverageId = findCoverageIdByPatientId(patientId); + if (StringUtils.isNotBlank(coverageId)) { + coverageReference = coverageId; + } + } else { + throw new ResourceNotFoundException( + "Couldn't find any Patient with given patientReference"); + } + } else { + throw new UnprocessableEntityException( + "Please provide memberId + providerNpi or patientReference + providerReference to $member-add."); + } + if (theParameters.getParameter(TextConstants.ATTRIBUTION_PERIOD) != null) { + attributionPeriod = + (Period) theParameters.getParameter(TextConstants.ATTRIBUTION_PERIOD); + } + if (StringUtils.isNotBlank(patientMemberId)) { + logger.info("patientMemberId: {}", patientMemberId); + logger.info("attributeProviderId: {}", attributeProviderId); + logger.info("attributeProviderReferenceResource: {}", + attributeProviderReferenceResource); + logger.info("coverageReference: {}", coverageReference); + if (attributionPeriod != null) { + logger.info("attributionPeriod.getStart(): {}", attributionPeriod.getStart()); + logger.info("attributionPeriod.getEnd(): {}", attributionPeriod.getEnd()); + } + removeMemberFromGroup(group, patientMemberId, attributeProviderId, + attributeProviderReferenceResource, coverageReference, attributionPeriod); + + groupDao.update(group); + + } else { + throw new ResourceNotFoundException("No patient found "); + } + } else { + throw new UnprocessableEntityException("No Parameters/Group Not found!"); + } + return group; + } + + public void removeMemberFromGroup(Group group, String patientMemberId, String providerId, + String providerReferenceResource, String coverageId, Period attributionPeriod) { + logger.info("patientMemberId: {}", patientMemberId); + logger.info("providerId: {}", providerId); + logger.info("providerReferenceResource: {}", providerReferenceResource); + logger.info("coverageId: {}", coverageId); + boolean isGroupMemberRemoved = false; + if (group.hasMember()) { + List memberList = new ArrayList<>(); + for (GroupMemberComponent memberGroup : new ArrayList(memberList)) { + if (memberGroup.hasEntity() && memberGroup.getEntity().hasReferenceElement()) { + String entityId = memberGroup.getEntity().getReferenceElement().getIdPart(); + logger.info("entityId: {}", entityId); + if (patientMemberId.equalsIgnoreCase(entityId)) { + if (StringUtils.isNotBlank(providerId) + && StringUtils.isNotBlank(providerReferenceResource)) { + if (memberGroup.hasExtension(TextConstants.MEMBER_PROVIDER_SYSTEM)) { + if (memberGroup.getExtensionByUrl(TextConstants.MEMBER_PROVIDER_SYSTEM) + .hasValue()) { + Reference reference = (Reference) memberGroup + .getExtensionByUrl(TextConstants.MEMBER_PROVIDER_SYSTEM) + .getValue(); + if (providerId + .equalsIgnoreCase(reference.getReferenceElement().getIdPart()) + && providerReferenceResource.equalsIgnoreCase( + reference.getReferenceElement().getResourceType())) { + if (StringUtils.isNotBlank(coverageId)) { + if (memberGroup + .hasExtension(TextConstants.MEMBER_COVERAGE_SYSTEM)) { + if (memberGroup + .getExtensionByUrl(TextConstants.MEMBER_COVERAGE_SYSTEM) + .hasValue()) { + Reference coverageReference = + (Reference) memberGroup + .getExtensionByUrl( + TextConstants.MEMBER_COVERAGE_SYSTEM) + .getValue(); + if (coverageId.equalsIgnoreCase( + coverageReference.getReferenceElement().getIdPart())) { + memberList.remove(memberGroup); + isGroupMemberRemoved = true; + logger.info( + " Removing member from Group.member for memberId+providerNpi+attributionPeriod / patientReference+providerReference+attributionPeriod. patientMemberId: {}, providerId: {}, coverageId: {}", + patientMemberId, providerId, coverageId); + } else { + throw new ResourceNotFoundException( + " No coverage found for given attributionPeriod " + + coverageId); + } + } + } + } else { + memberList.remove(memberGroup); + isGroupMemberRemoved = true; + logger.info( + "Removing member from Group.member for memberId+providerNpi / patientReference+providerReference. patientMemberId: {} providerId: {}", + patientMemberId, providerId); + } + } else { + throw new ResourceNotFoundException( + " No provider found for given provider " + providerId); + } + } + } + } else { + memberList.remove(memberGroup); + isGroupMemberRemoved = true; + logger.info( + "Removing member from Group.member for memberId/patientReference. patientMemberId : {}", + patientMemberId); + } + break; + } + } + } + } else { + logger.error("Group doesn't have any members"); + } + if (isGroupMemberRemoved) { + if (group.hasMeta()) { + if (group.getMeta().hasVersionId()) { + String versionId = group.getMeta().getVersionId(); + int version = Integer.parseInt(versionId); + version = version + 1; + group.getMeta().setVersionId(String.valueOf(version)); + + } else { + group.getMeta().setVersionId("1"); + } + } else { + Meta meta = new Meta(); + meta.setVersionId("1"); + group.setMeta(meta); + } + } else { + throw new UnprocessableEntityException( + "Group doesn't contain given memberId/patientReference"); + } + } +} diff --git a/plugin/atr/src/main/java/org/opencds/cqf/ruler/atr/util/FhirUtil.java b/plugin/atr/src/main/java/org/opencds/cqf/ruler/atr/util/FhirUtil.java new file mode 100644 index 000000000..889e4255f --- /dev/null +++ b/plugin/atr/src/main/java/org/opencds/cqf/ruler/atr/util/FhirUtil.java @@ -0,0 +1,342 @@ +package org.opencds.cqf.ruler.atr.util; + +import java.util.ArrayList; +import java.util.List; +import org.apache.commons.lang3.StringUtils; +import org.hl7.fhir.r4.model.BooleanType; +import org.hl7.fhir.r4.model.CodeType; +import org.hl7.fhir.r4.model.DateTimeType; +import org.hl7.fhir.r4.model.Extension; +import org.hl7.fhir.r4.model.Group.GroupMemberComponent; +import org.hl7.fhir.r4.model.Identifier; +import org.hl7.fhir.r4.model.Parameters; +import org.hl7.fhir.r4.model.Period; +import org.hl7.fhir.r4.model.Reference; +import org.hl7.fhir.r4.model.UriType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import ca.uhn.fhir.rest.param.TokenAndListParam; +import ca.uhn.fhir.rest.param.TokenOrListParam; +import ca.uhn.fhir.rest.param.TokenParam; + +// TODO: Auto-generated Javadoc +/** + * The Class FhirUtil. + */ +public class FhirUtil { + + private FhirUtil() {} + + /** The Constant logger. */ + private static final Logger logger = LoggerFactory.getLogger(FhirUtil.class); + + /** + * Creates the token and list param. + * + * @param theParameters the the parameters + * @param parameterName the parameter name + * @return the token and list param + */ + public static TokenAndListParam createTokenAndListParam(Parameters theParameters, + String parameterName) { + TokenAndListParam tokenParam = new TokenAndListParam(); + + TokenOrListParam tokenOrParam = new TokenOrListParam(); + + Identifier memberIdentifier = (Identifier) theParameters.getParameter(parameterName); + TokenParam param = new TokenParam(); + param.setSystem(memberIdentifier.getSystem()); + param.setValue(memberIdentifier.getValue()); + tokenOrParam.add(param); + tokenParam.addValue(tokenOrParam); + + return tokenParam; + } + + + /** + * Gets the reference. + * + * @param theId the the id + * @param resourceType the resource type + * @return the reference + */ + public static Reference getReference(String theId, String resourceType) { + Reference theReference = null; + try { + if (StringUtils.isNotBlank(theId) && StringUtils.isNotBlank(resourceType)) { + StringBuilder reference = new StringBuilder(); + reference.append(resourceType); + reference.append(TextConstants.SINGLE_FORWARD_SLASH); + reference.append(theId); + theReference = initReference(theReference); + theReference.setReference(reference.toString()); + } + } catch (Exception ex) { + logger.error("\n Exception while setting getReference in FhirUtils class ", ex); + } + return theReference; + } + + /** + * Inits the reference. + * + * @param theReference the the reference + * @return the reference + */ + private static Reference initReference(Reference theReference) { + if (theReference == null) { + return new Reference(); + } else { + return theReference; + } + } + + + /** + * Gets the code type. + * + * @param theValue the the value + * @return the code type + */ + public static CodeType getCodeType(String theValue) { + CodeType codeType = null; + try { + if (StringUtils.isNotBlank(theValue)) { + codeType = new CodeType(); + codeType.setValue(theValue); + } + } catch (Exception e) { + logger.error("Exception in getCodeType of FhirUtility ", e); + } + return codeType; + } + + + /** + * Gets the extension for code type. + * + * @param typeText the type text + * @return the extension for code type + */ + public static Extension getExtensionForCodeType(String typeText) { + Extension theExtension = null; + try { + if (StringUtils.isNotBlank(typeText)) { + theExtension = new Extension(); + UriType uri = getUriType(TextConstants.MEMBER_CHANGETYPE_SYSTEM); + theExtension.setUrlElement(uri); + CodeType theCode = getCodeType(typeText); + theExtension.setValue(theCode); + } + } catch (Exception ex) { + logger.error("\n Exception while setting getExtensionForCodeType in FhirUtility class ", + ex); + } + return theExtension; + } + + /** + * Gets the extension for reference. + * + * @param id the id + * @param resourceType the resource type + * @param system the system + * @return the extension for reference + */ + public static Extension getExtensionForReference(String id, String resourceType, String system) { + Extension theExtension = null; + try { + if (StringUtils.isNotBlank(id) && StringUtils.isNotBlank(resourceType)) { + theExtension = new Extension(); + UriType uri = getUriType(system); + theExtension.setUrlElement(uri); + Reference theReference = getReference(id, resourceType); + theExtension.setValue(theReference); + } + } catch (Exception ex) { + logger.error("\n Exception while setting getExtensionForReference in FhirUtility class ", + ex); + } + return theExtension; + } + + + /** + * Gets the uri type. + * + * @param theValue the the value + * @return the uri type + */ + public static UriType getUriType(String theValue) { + UriType uriType = null; + try { + if (StringUtils.isNotBlank(theValue)) { + uriType = new UriType(); + uriType.setValue(theValue); + } + } catch (Exception e) { + logger.error("Exception in getUriType of FhirUtility ", e); + } + return uriType; + } + + + /** + * Gets the boolean type. + * + * @param data the data + * @return the boolean type + */ + public static BooleanType getBooleanType(boolean data) { + BooleanType booleanType = null; + try { + booleanType = new BooleanType(); + booleanType.setValue(data); + } catch (Exception e) { + logger.error("Exception in getBooleanType of FhirUtility ", e); + } + return booleanType; + } + + + /** + * Gets the period. + * + * @param start the start + * @param end the end + * @return the period + */ + public static Period getPeriod(DateTimeType start, DateTimeType end) { + Period thePeriod = null; + try { + if (start != null) { + thePeriod = initPeriod(thePeriod); + thePeriod.setStartElement(start); + } + if (end != null) { + thePeriod = initPeriod(thePeriod); + thePeriod.setEndElement(end); + } + } catch (Exception ex) { + logger.error("\n Exception while setting getPeriod in FhirUtils class ", ex); + } + return thePeriod; + } + + + /** + * Inits the period. + * + * @param thePeriod the the period + * @return the period + */ + private static Period initPeriod(Period thePeriod) { + if (thePeriod == null) { + return new Period(); + } else { + return thePeriod; + } + } + + + /** + * Inits the extension list. + * + * @param extensionList the extension list + * @return the list + */ + public static List initExtensionList(List extensionList) { + if (extensionList == null) { + return new ArrayList(); + } else { + return extensionList; + } + } + + /** + * Gets the group member component. + * + * @param patientMemberId the patient member id + * @param providerId the provider id + * @param providerReference the provider reference + * @param coverageReference the coverage reference + * @param attributionPeriod the attribution period + * @return the group member component + */ + public static GroupMemberComponent getGroupMemberComponent(String patientMemberId, + String providerId, String providerReference, String coverageReference, + Period attributionPeriod) { + GroupMemberComponent theGroupMemberComponent = new GroupMemberComponent(); + List theMembeEextensionList = null; + try { + if (StringUtils.isNotBlank(patientMemberId)) { + Reference theReference = getReference(patientMemberId, "Patient"); + if (theReference != null) { + theGroupMemberComponent.setEntity(theReference); + BooleanType theBoolean = getBooleanType(false); + theGroupMemberComponent.setInactiveElement(theBoolean); + } + } + theMembeEextensionList = getGroupMemberComponentExtension(providerId, providerReference, + coverageReference, TextConstants.NEW_TYPE); + if (theMembeEextensionList != null && !theMembeEextensionList.isEmpty()) { + theGroupMemberComponent.setExtension(theMembeEextensionList); + } + if (attributionPeriod != null) { + Period thePeriod = + getPeriod(attributionPeriod.getStartElement(), attributionPeriod.getEndElement()); + theGroupMemberComponent.setPeriod(thePeriod); + } + } catch (Exception ex) { + logger.error("\n Exception while setting getGroupMemberComponent in FhirUtility class ", + ex); + } + return theGroupMemberComponent; + } + + + /** + * Gets the group member component extension. + * + * @param providerId the provider id + * @param providerReference the provider reference + * @param coverageReference the coverage reference + * @param changeCode the change code + * @return the group member component extension + */ + public static List getGroupMemberComponentExtension(String providerId, + String providerReference, String coverageReference, String changeCode) { + List theMembeEextensionList = null; + try { + if (StringUtils.isNotBlank(changeCode)) { + Extension codeExtension = FhirUtil.getExtensionForCodeType(changeCode); + if (codeExtension != null) { + theMembeEextensionList = FhirUtil.initExtensionList(theMembeEextensionList); + theMembeEextensionList.add(codeExtension); + } + } + if (StringUtils.isNotBlank(coverageReference)) { + Extension coverageExtension = FhirUtil.getExtensionForReference(coverageReference, + "Coverage", TextConstants.MEMBER_COVERAGE_SYSTEM); + if (coverageExtension != null) { + theMembeEextensionList = FhirUtil.initExtensionList(theMembeEextensionList); + theMembeEextensionList.add(coverageExtension); + } + } + if (StringUtils.isNotBlank(providerId) && StringUtils.isNotBlank(providerReference)) { + Extension providerExtension = FhirUtil.getExtensionForReference(providerId, + providerReference, TextConstants.MEMBER_PROVIDER_SYSTEM); + if (providerExtension != null) { + theMembeEextensionList = FhirUtil.initExtensionList(theMembeEextensionList); + theMembeEextensionList.add(providerExtension); + } + } + } catch (Exception ex) { + logger.error("\n Exception while setting getGroupMemberComponent in FhirUtility class ", + ex); + } + return theMembeEextensionList; + } + +} diff --git a/plugin/atr/src/main/java/org/opencds/cqf/ruler/atr/util/TextConstants.java b/plugin/atr/src/main/java/org/opencds/cqf/ruler/atr/util/TextConstants.java new file mode 100644 index 000000000..68d748300 --- /dev/null +++ b/plugin/atr/src/main/java/org/opencds/cqf/ruler/atr/util/TextConstants.java @@ -0,0 +1,36 @@ +package org.opencds.cqf.ruler.atr.util; + +public final class TextConstants { + + private TextConstants() {} + + public static final String SINGLE_FORWARD_SLASH = "/"; + + public static final String MEMBER_ID = "memberId"; + + public static final String PROVIDER_NPI = "providerNpi"; + + public static final String PATIENT_REFERENCE = "patientReference"; + + public static final String PROVIDER_REFERENCE = "providerReference"; + + public static final String ATTRIBUTION_PERIOD = "attributionPeriod"; + + public static final String MEMBER_CHANGETYPE_SYSTEM = + "http://hl7.org/fhir/us/davinci-atr/StructureDefinition/ext-changeType"; + + public static final String MEMBER_COVERAGE_SYSTEM = + "http://hl7.org/fhir/us/davinci-atr/StructureDefinition/ext-coverageReference"; + + public static final String MEMBER_PROVIDER_SYSTEM = + "http://hl7.org/fhir/us/davinci-atr/StructureDefinition/ext-attributedProvider"; + + public static final String NEW_TYPE = "new"; + + public static final String CHANGE_TYPE = "change"; + + public static final String NOCHANGE_TYPE = "nochange"; + + /** The Constant HTTP_POST. */ + public static final String HTTP_POST = "POST"; +} diff --git a/plugin/atr/src/main/resources/META-INF/spring.factories b/plugin/atr/src/main/resources/META-INF/spring.factories new file mode 100644 index 000000000..7c49745fc --- /dev/null +++ b/plugin/atr/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +org.hl7.davinci.atr.server.MALConfig diff --git a/plugin/atr/src/main/resources/application.yaml b/plugin/atr/src/main/resources/application.yaml new file mode 100644 index 000000000..8eb127206 --- /dev/null +++ b/plugin/atr/src/main/resources/application.yaml @@ -0,0 +1,3 @@ +hello: + world: + message: Hello \ No newline at end of file diff --git a/plugin/pom.xml b/plugin/pom.xml index 8600d3160..16c86c819 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -1,5 +1,6 @@ - + 4.0.0 org.opencds.cqf.ruler @@ -11,6 +12,7 @@ pom + atr cds-hooks cpg case-reporting @@ -65,30 +67,6 @@ org.apache.maven.plugins maven-checkstyle-plugin - - diff --git a/pom.xml b/pom.xml index 5f599f9ba..066603c53 100644 --- a/pom.xml +++ b/pom.xml @@ -32,12 +32,12 @@ + core external - test - core - plugin + plugin server + test diff --git a/server/pom.xml b/server/pom.xml index afaa1f7ac..4d981a041 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -271,6 +271,12 @@ 0.5.0-SNAPSHOT runtime + + org.opencds.cqf.ruler + cqf-ruler-atr + 0.5.0-SNAPSHOT + runtime + org.opencds.cqf.ruler cqf-ruler-ra