Skip to content

Commit 210dfa8

Browse files
FM2-649: Implementation of _has parameter for patients (openmrs#545)
1 parent c6ea1b3 commit 210dfa8

File tree

12 files changed

+441
-104
lines changed

12 files changed

+441
-104
lines changed

api/src/main/java/org/openmrs/module/fhir2/FhirConstants.java

+5
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@ private FhirConstants() {
137137

138138
public static final String MEDICATION = "Medication";
139139

140+
public static final String GROUP = "Group";
141+
140142
public static final String MEDICATION_DISPENSE = "MedicationDispense";
141143

142144
public static final String MEDICATION_REQUEST = "MedicationRequest";
@@ -364,4 +366,7 @@ private FhirConstants() {
364366
public static final String EXACT_TOTAL_SEARCH_PARAMETER = "_exactTotal";
365367

366368
public static final String COUNT_QUERY_CACHE = "countQueryCache";
369+
370+
public static final String INCLUDE_MEMBER_PARAM = "member";
371+
367372
}

api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirPatientDaoImpl.java

+58
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,15 @@
1919

2020
import java.util.ArrayList;
2121
import java.util.Collection;
22+
import java.util.Collections;
23+
import java.util.HashSet;
2224
import java.util.List;
2325
import java.util.NoSuchElementException;
2426
import java.util.Optional;
27+
import java.util.Set;
2528

2629
import ca.uhn.fhir.rest.param.DateRangeParam;
30+
import ca.uhn.fhir.rest.param.HasAndListParam;
2731
import ca.uhn.fhir.rest.param.StringAndListParam;
2832
import ca.uhn.fhir.rest.param.StringParam;
2933
import ca.uhn.fhir.rest.param.TokenAndListParam;
@@ -32,18 +36,25 @@
3236
import org.apache.commons.lang3.StringUtils;
3337
import org.hibernate.Criteria;
3438
import org.hibernate.criterion.Criterion;
39+
import org.hibernate.criterion.Projections;
3540
import org.hibernate.sql.JoinType;
41+
import org.openmrs.CohortMembership;
3642
import org.openmrs.Patient;
3743
import org.openmrs.PatientIdentifierType;
3844
import org.openmrs.module.fhir2.FhirConstants;
45+
import org.openmrs.module.fhir2.api.dao.FhirGroupDao;
3946
import org.openmrs.module.fhir2.api.dao.FhirPatientDao;
4047
import org.openmrs.module.fhir2.api.search.param.SearchParameterMap;
48+
import org.springframework.beans.factory.annotation.Autowired;
4149
import org.springframework.stereotype.Component;
4250

4351
@Component
4452
@Setter(AccessLevel.PACKAGE)
4553
public class FhirPatientDaoImpl extends BasePersonDao<Patient> implements FhirPatientDao {
4654

55+
@Autowired
56+
private FhirGroupDao groupDao;
57+
4758
@Override
4859
public Patient getPatientById(@Nonnull Integer id) {
4960
return (Patient) getSessionFactory().getCurrentSession().createCriteria(Patient.class).add(eq("patientId", id))
@@ -112,10 +123,57 @@ protected void setupSearchParams(Criteria criteria, SearchParameterMap theParams
112123
case FhirConstants.COMMON_SEARCH_HANDLER:
113124
handleCommonSearchParameters(entry.getValue()).ifPresent(criteria::add);
114125
break;
126+
case FhirConstants.HAS_SEARCH_HANDLER:
127+
entry.getValue().forEach(param -> handleHasAndListParam(criteria, (HasAndListParam) param.getParam()));
128+
break;
115129
}
116130
});
117131
}
118132

133+
protected void handleHasAndListParam(Criteria criteria, HasAndListParam hasAndListParam) {
134+
if (hasAndListParam != null) {
135+
List<String> groupIds = new ArrayList<>();
136+
hasAndListParam.getValuesAsQueryTokens().forEach(hasOrListParam -> {
137+
hasOrListParam.getValuesAsQueryTokens().forEach(hasParam -> {
138+
if (hasParam != null) {
139+
String paramValue = hasParam.getParameterValue();
140+
switch (hasParam.getTargetResourceType()) {
141+
case FhirConstants.GROUP:
142+
switch (hasParam.getReferenceFieldName()) {
143+
case FhirConstants.INCLUDE_MEMBER_PARAM:
144+
switch (hasParam.getParameterName()) {
145+
case "id":
146+
groupIds.add(paramValue);
147+
}
148+
break;
149+
}
150+
break;
151+
}
152+
}
153+
});
154+
});
155+
156+
if (!groupIds.isEmpty()) {
157+
verifyPatientInGroups(criteria, groupIds);
158+
}
159+
}
160+
}
161+
162+
private void verifyPatientInGroups(Criteria criteria, List<String> groupIds) {
163+
Set<Integer> patientIds = new HashSet<>();
164+
groupIds.forEach(groupId -> patientIds.addAll(getGroupMemberIds(groupId)));
165+
166+
criteria.add(in("patientId", patientIds.isEmpty() ? Collections.emptyList() : patientIds));
167+
}
168+
169+
private List<Integer> getGroupMemberIds(String groupId) {
170+
Criteria subquery = getSessionFactory().getCurrentSession().createCriteria(CohortMembership.class, "cm")
171+
.createAlias("cm.cohort", "co").add(eq("co.uuid", groupId))
172+
.setProjection(Projections.property("cm.patientId"));
173+
174+
return subquery.list();
175+
}
176+
119177
private void handlePatientQuery(Criteria criteria, @Nonnull StringAndListParam query) {
120178
if (query == null) {
121179
return;

api/src/main/java/org/openmrs/module/fhir2/api/search/param/OpenmrsPatientSearchParams.java

+8-3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import ca.uhn.fhir.model.api.Include;
1515
import ca.uhn.fhir.rest.api.SortSpec;
1616
import ca.uhn.fhir.rest.param.DateRangeParam;
17+
import ca.uhn.fhir.rest.param.HasAndListParam;
1718
import ca.uhn.fhir.rest.param.StringAndListParam;
1819
import ca.uhn.fhir.rest.param.TokenAndListParam;
1920
import lombok.Builder;
@@ -45,11 +46,13 @@ public class OpenmrsPatientSearchParams extends BaseResourceSearchParams {
4546

4647
private StringAndListParam country;
4748

49+
private HasAndListParam hasAndListParam;
50+
4851
@Builder
4952
public OpenmrsPatientSearchParams(StringAndListParam query, TokenAndListParam gender, DateRangeParam birthDate,
5053
DateRangeParam deathDate, TokenAndListParam deceased, StringAndListParam city, StringAndListParam state,
51-
StringAndListParam postalCode, StringAndListParam country, TokenAndListParam id, DateRangeParam lastUpdated,
52-
SortSpec sort, HashSet<Include> revIncludes) {
54+
StringAndListParam postalCode, StringAndListParam country, TokenAndListParam id, HasAndListParam hasAndListParam,
55+
DateRangeParam lastUpdated, SortSpec sort, HashSet<Include> revIncludes) {
5356

5457
super(id, lastUpdated, sort, null, revIncludes);
5558

@@ -62,6 +65,7 @@ public OpenmrsPatientSearchParams(StringAndListParam query, TokenAndListParam ge
6265
this.state = state;
6366
this.postalCode = postalCode;
6467
this.country = country;
68+
this.hasAndListParam = hasAndListParam;
6569
}
6670

6771
@Override
@@ -74,6 +78,7 @@ public SearchParameterMap toSearchParameterMap() {
7478
.addParameter(FhirConstants.ADDRESS_SEARCH_HANDLER, FhirConstants.CITY_PROPERTY, getCity())
7579
.addParameter(FhirConstants.ADDRESS_SEARCH_HANDLER, FhirConstants.STATE_PROPERTY, getState())
7680
.addParameter(FhirConstants.ADDRESS_SEARCH_HANDLER, FhirConstants.POSTAL_CODE_PROPERTY, getPostalCode())
77-
.addParameter(FhirConstants.ADDRESS_SEARCH_HANDLER, FhirConstants.COUNTRY_PROPERTY, getCountry());
81+
.addParameter(FhirConstants.ADDRESS_SEARCH_HANDLER, FhirConstants.COUNTRY_PROPERTY, getCountry())
82+
.addParameter(FhirConstants.HAS_SEARCH_HANDLER, getHasAndListParam());
7883
}
7984
}

api/src/main/java/org/openmrs/module/fhir2/api/search/param/PatientSearchParams.java

+8-3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import ca.uhn.fhir.model.api.Include;
1515
import ca.uhn.fhir.rest.api.SortSpec;
1616
import ca.uhn.fhir.rest.param.DateRangeParam;
17+
import ca.uhn.fhir.rest.param.HasAndListParam;
1718
import ca.uhn.fhir.rest.param.StringAndListParam;
1819
import ca.uhn.fhir.rest.param.TokenAndListParam;
1920
import lombok.Builder;
@@ -51,12 +52,14 @@ public class PatientSearchParams extends BaseResourceSearchParams {
5152

5253
private StringAndListParam country;
5354

55+
private HasAndListParam hasAndListParam;
56+
5457
@Builder
5558
public PatientSearchParams(StringAndListParam name, StringAndListParam given, StringAndListParam family,
5659
TokenAndListParam identifier, TokenAndListParam gender, DateRangeParam birthDate, DateRangeParam deathDate,
5760
TokenAndListParam deceased, StringAndListParam city, StringAndListParam state, StringAndListParam postalCode,
58-
StringAndListParam country, TokenAndListParam id, DateRangeParam lastUpdated, SortSpec sort,
59-
HashSet<Include> revIncludes) {
61+
StringAndListParam country, TokenAndListParam id, HasAndListParam hasAndListParam, DateRangeParam lastUpdated,
62+
SortSpec sort, HashSet<Include> revIncludes) {
6063

6164
super(id, lastUpdated, sort, null, revIncludes);
6265

@@ -72,6 +75,7 @@ public PatientSearchParams(StringAndListParam name, StringAndListParam given, St
7275
this.state = state;
7376
this.postalCode = postalCode;
7477
this.country = country;
78+
this.hasAndListParam = hasAndListParam;
7579
}
7680

7781
@Override
@@ -88,6 +92,7 @@ public SearchParameterMap toSearchParameterMap() {
8892
.addParameter(FhirConstants.ADDRESS_SEARCH_HANDLER, FhirConstants.CITY_PROPERTY, getCity())
8993
.addParameter(FhirConstants.ADDRESS_SEARCH_HANDLER, FhirConstants.STATE_PROPERTY, getState())
9094
.addParameter(FhirConstants.ADDRESS_SEARCH_HANDLER, FhirConstants.POSTAL_CODE_PROPERTY, getPostalCode())
91-
.addParameter(FhirConstants.ADDRESS_SEARCH_HANDLER, FhirConstants.COUNTRY_PROPERTY, getCountry());
95+
.addParameter(FhirConstants.ADDRESS_SEARCH_HANDLER, FhirConstants.COUNTRY_PROPERTY, getCountry())
96+
.addParameter(FhirConstants.HAS_SEARCH_HANDLER, getHasAndListParam());
9297
}
9398
}

api/src/main/java/org/openmrs/module/fhir2/providers/r3/PatientFhirResourceProvider.java

+8-4
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import ca.uhn.fhir.rest.api.SortSpec;
3333
import ca.uhn.fhir.rest.api.server.IBundleProvider;
3434
import ca.uhn.fhir.rest.param.DateRangeParam;
35+
import ca.uhn.fhir.rest.param.HasAndListParam;
3536
import ca.uhn.fhir.rest.param.StringAndListParam;
3637
import ca.uhn.fhir.rest.param.TokenAndListParam;
3738
import ca.uhn.fhir.rest.param.TokenParam;
@@ -53,6 +54,7 @@
5354
import org.hl7.fhir.dstu3.model.ProcedureRequest;
5455
import org.hl7.fhir.instance.model.api.IBaseResource;
5556
import org.hl7.fhir.r4.model.ServiceRequest;
57+
import org.openmrs.module.fhir2.FhirConstants;
5658
import org.openmrs.module.fhir2.api.FhirPatientService;
5759
import org.openmrs.module.fhir2.api.annotations.R3Provider;
5860
import org.openmrs.module.fhir2.api.search.SearchQueryBundleProviderR3Wrapper;
@@ -128,6 +130,7 @@ public IBundleProvider searchPatients(@OptionalParam(name = Patient.SP_NAME) Str
128130
@OptionalParam(name = Patient.SP_ADDRESS_POSTALCODE) StringAndListParam postalCode,
129131
@OptionalParam(name = Patient.SP_ADDRESS_COUNTRY) StringAndListParam country,
130132
@OptionalParam(name = Patient.SP_RES_ID) TokenAndListParam id,
133+
@OptionalParam(name = FhirConstants.HAS_SEARCH_HANDLER) HasAndListParam hasAndListParam,
131134
@OptionalParam(name = "_lastUpdated") DateRangeParam lastUpdated, @Sort SortSpec sort,
132135
@IncludeParam(reverse = true, allow = { "Observation:" + Observation.SP_PATIENT,
133136
"AllergyIntolerance:" + AllergyIntolerance.SP_PATIENT, "DiagnosticReport:" + DiagnosticReport.SP_PATIENT,
@@ -138,9 +141,9 @@ public IBundleProvider searchPatients(@OptionalParam(name = Patient.SP_NAME) Str
138141
revIncludes = null;
139142
}
140143

141-
return new SearchQueryBundleProviderR3Wrapper(
142-
patientService.searchForPatients(new PatientSearchParams(name, given, family, identifier, gender, birthDate,
143-
deathDate, deceased, city, state, postalCode, country, id, lastUpdated, sort, revIncludes)));
144+
return new SearchQueryBundleProviderR3Wrapper(patientService
145+
.searchForPatients(new PatientSearchParams(name, given, family, identifier, gender, birthDate, deathDate,
146+
deceased, city, state, postalCode, country, id, hasAndListParam, lastUpdated, sort, revIncludes)));
144147
}
145148

146149
@Search(queryName = "openmrsPatients")
@@ -155,6 +158,7 @@ public IBundleProvider searchOpenmrsPatients(@OptionalParam(name = "q") StringAn
155158
@OptionalParam(name = org.hl7.fhir.r4.model.Patient.SP_ADDRESS_POSTALCODE) StringAndListParam postalCode,
156159
@OptionalParam(name = org.hl7.fhir.r4.model.Patient.SP_ADDRESS_COUNTRY) StringAndListParam country,
157160
@OptionalParam(name = org.hl7.fhir.r4.model.Patient.SP_RES_ID) TokenAndListParam id,
161+
@OptionalParam(name = FhirConstants.HAS_SEARCH_HANDLER) HasAndListParam hasAndListParam,
158162
@OptionalParam(name = "_lastUpdated") DateRangeParam lastUpdated, @Sort SortSpec sort,
159163
@IncludeParam(reverse = true, allow = { "Observation:" + org.hl7.fhir.r4.model.Observation.SP_PATIENT,
160164
"AllergyIntolerance:" + org.hl7.fhir.r4.model.AllergyIntolerance.SP_PATIENT,
@@ -169,7 +173,7 @@ public IBundleProvider searchOpenmrsPatients(@OptionalParam(name = "q") StringAn
169173

170174
return new SearchQueryBundleProviderR3Wrapper(
171175
patientService.searchForPatients(new OpenmrsPatientSearchParams(query, gender, birthDate, deathDate,
172-
deceased, city, state, postalCode, country, id, lastUpdated, sort, revIncludes)));
176+
deceased, city, state, postalCode, country, id, hasAndListParam, lastUpdated, sort, revIncludes)));
173177
}
174178

175179
/**

api/src/main/java/org/openmrs/module/fhir2/providers/r4/PatientFhirResourceProvider.java

+6-2
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import ca.uhn.fhir.rest.api.server.IBundleProvider;
3636
import ca.uhn.fhir.rest.api.server.RequestDetails;
3737
import ca.uhn.fhir.rest.param.DateRangeParam;
38+
import ca.uhn.fhir.rest.param.HasAndListParam;
3839
import ca.uhn.fhir.rest.param.StringAndListParam;
3940
import ca.uhn.fhir.rest.param.TokenAndListParam;
4041
import ca.uhn.fhir.rest.param.TokenParam;
@@ -54,6 +55,7 @@
5455
import org.hl7.fhir.r4.model.OperationOutcome;
5556
import org.hl7.fhir.r4.model.Patient;
5657
import org.hl7.fhir.r4.model.ServiceRequest;
58+
import org.openmrs.module.fhir2.FhirConstants;
5759
import org.openmrs.module.fhir2.api.FhirPatientService;
5860
import org.openmrs.module.fhir2.api.annotations.R4Provider;
5961
import org.openmrs.module.fhir2.api.search.param.OpenmrsPatientSearchParams;
@@ -136,6 +138,7 @@ public IBundleProvider searchPatients(@OptionalParam(name = Patient.SP_NAME) Str
136138
@OptionalParam(name = Patient.SP_ADDRESS_POSTALCODE) StringAndListParam postalCode,
137139
@OptionalParam(name = Patient.SP_ADDRESS_COUNTRY) StringAndListParam country,
138140
@OptionalParam(name = Patient.SP_RES_ID) TokenAndListParam id,
141+
@OptionalParam(name = FhirConstants.HAS_SEARCH_HANDLER) HasAndListParam hasAndListParam,
139142
@OptionalParam(name = "_lastUpdated") DateRangeParam lastUpdated, @Sort SortSpec sort,
140143
@IncludeParam(reverse = true, allow = { "Observation:" + Observation.SP_PATIENT,
141144
"AllergyIntolerance:" + AllergyIntolerance.SP_PATIENT, "DiagnosticReport:" + DiagnosticReport.SP_PATIENT,
@@ -147,7 +150,7 @@ public IBundleProvider searchPatients(@OptionalParam(name = Patient.SP_NAME) Str
147150
}
148151

149152
return patientService.searchForPatients(new PatientSearchParams(name, given, family, identifier, gender, birthDate,
150-
deathDate, deceased, city, state, postalCode, country, id, lastUpdated, sort, revIncludes));
153+
deathDate, deceased, city, state, postalCode, country, id, hasAndListParam, lastUpdated, sort, revIncludes));
151154
}
152155

153156
@Search(queryName = "openmrsPatients")
@@ -162,6 +165,7 @@ public IBundleProvider searchOpenmrsPatients(@OptionalParam(name = "q") StringAn
162165
@OptionalParam(name = Patient.SP_ADDRESS_POSTALCODE) StringAndListParam postalCode,
163166
@OptionalParam(name = Patient.SP_ADDRESS_COUNTRY) StringAndListParam country,
164167
@OptionalParam(name = Patient.SP_RES_ID) TokenAndListParam id,
168+
@OptionalParam(name = FhirConstants.HAS_SEARCH_HANDLER) HasAndListParam hasAndListParam,
165169
@OptionalParam(name = "_lastUpdated") DateRangeParam lastUpdated, @Sort SortSpec sort,
166170
@IncludeParam(reverse = true, allow = { "Observation:" + Observation.SP_PATIENT,
167171
"AllergyIntolerance:" + AllergyIntolerance.SP_PATIENT, "DiagnosticReport:" + DiagnosticReport.SP_PATIENT,
@@ -173,7 +177,7 @@ public IBundleProvider searchOpenmrsPatients(@OptionalParam(name = "q") StringAn
173177
}
174178

175179
return patientService.searchForPatients(new OpenmrsPatientSearchParams(query, gender, birthDate, deathDate, deceased,
176-
city, state, postalCode, country, id, lastUpdated, sort, revIncludes));
180+
city, state, postalCode, country, id, hasAndListParam, lastUpdated, sort, revIncludes));
177181
}
178182

179183
/**

0 commit comments

Comments
 (0)