• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

openmrs / openmrs-core / 32506752384

21 Aug 2026 05:07PM UTC coverage: 64.52% (+0.1%) from 64.381%
32506752384

push

github

ibacher
TRUNK-6729:  Let batch loading engage on the identifier and concept search paths (#6420)

56 of 58 new or added lines in 4 files covered. (96.55%)

22 existing lines in 4 files now uncovered.

24791 of 38424 relevant lines covered (64.52%)

0.65 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.69
/api/src/main/java/org/openmrs/api/impl/ObsServiceImpl.java
1
/**
2
 * This Source Code Form is subject to the terms of the Mozilla Public License,
3
 * v. 2.0. If a copy of the MPL was not distributed with this file, You can
4
 * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under
5
 * the terms of the Healthcare Disclaimer located at http://openmrs.org/license.
6
 *
7
 * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS
8
 * graphic logo is a trademark of OpenMRS Inc.
9
 */
10
package org.openmrs.api.impl;
11

12
import java.util.ArrayList;
13
import java.util.Date;
14
import java.util.LinkedHashMap;
15
import java.util.List;
16
import java.util.Map;
17

18
import org.openmrs.Concept;
19
import org.openmrs.ConceptName;
20
import org.openmrs.Encounter;
21
import org.openmrs.Location;
22
import org.openmrs.Obs;
23
import org.openmrs.Patient;
24
import org.openmrs.Person;
25
import org.openmrs.Visit;
26
import org.openmrs.aop.RequiredDataAdvice;
27
import org.openmrs.api.APIException;
28
import org.openmrs.api.EncounterService;
29
import org.openmrs.api.ObsService;
30
import org.openmrs.api.PatientService;
31
import org.openmrs.api.context.Context;
32
import org.openmrs.api.db.ObsDAO;
33
import org.openmrs.api.handler.SaveHandler;
34
import org.openmrs.obs.ComplexData;
35
import org.openmrs.obs.ComplexObsHandler;
36
import org.openmrs.parameter.ObsSearchCriteria;
37
import org.openmrs.parameter.ObsSearchCriteriaBuilder;
38
import org.openmrs.util.OpenmrsClassLoader;
39
import org.openmrs.util.OpenmrsConstants.PERSON_TYPE;
40
import org.openmrs.util.OpenmrsUtil;
41
import org.openmrs.util.PrivilegeConstants;
42
import org.slf4j.Logger;
43
import org.slf4j.LoggerFactory;
44
import org.springframework.transaction.annotation.Transactional;
45

46
/**
47
 * Default implementation of the Observation Service
48
 * 
49
 * @see org.openmrs.api.ObsService
50
 */
51
@Transactional
52
public class ObsServiceImpl extends BaseOpenmrsService implements ObsService {
53

54
        private static final Logger log = LoggerFactory.getLogger(ObsServiceImpl.class);
1✔
55

56
        /**
57
         * The data access object for the obs service
58
         */
59
        protected ObsDAO dao;
60
        
61
        /**
62
         * Report handlers that have been registered. This is filled via {@link #setHandlers(Map)} and
63
         * spring's applicationContext-service.xml object
64
         */
65
        private static Map<String, ComplexObsHandler> handlers = null;
1✔
66
        
67
        /**
68
         * Default empty constructor for this obs service
69
         */
70
        public ObsServiceImpl() {
1✔
71
        }
1✔
72
        
73
        /**
74
         * @see org.openmrs.api.ObsService#setObsDAO(org.openmrs.api.db.ObsDAO)
75
         */
76
        @Override
77
        public void setObsDAO(ObsDAO dao) {
78
                this.dao = dao;
1✔
79
        }
1✔
80
        
81
        /**
82
         * Clean up after this class. Set the static var to null so that the classloader can reclaim the
83
         * space.
84
         * 
85
         * @see org.openmrs.api.impl.BaseOpenmrsService#onShutdown()
86
         */
87
        @Override
88
        public void onShutdown() {
UNCOV
89
                setHandlers(null);
×
UNCOV
90
        }
×
91
        
92
        /**
93
         * @see org.openmrs.api.ObsService#saveObs(org.openmrs.Obs, String)
94
         */
95
        @Override
96
        public Obs saveObs(Obs obs, String changeMessage) throws APIException {
97
                if(obs == null){
1✔
98
                        throw new APIException("Obs.error.cannot.be.null", (Object[]) null);
1✔
99
                }
100

101
                if(obs.getId() != null && changeMessage == null){
1✔
UNCOV
102
                        throw new APIException("Obs.error.ChangeMessage.required", (Object[]) null);
×
103
                }
104
                
105
                ensureRequirePrivilege(obs);
1✔
106

107
                //Should allow updating a voided Obs, it seems to be pointless to restrict it,
108
                //otherwise operations like merge patients won't be possible when to moving voided obs
109
                if (obs.getObsId() == null || obs.getVoided()) {
1✔
110
                        return saveNewOrVoidedObs(obs,changeMessage);
1✔
111
                } else if(!obs.isDirty()){
1✔
112
                        setPersonFromEncounter(obs);
1✔
113
                        return saveObsNotDirty(obs, changeMessage);
1✔
114
                } else {
115
                        setPersonFromEncounter(obs);
1✔
116
                        return saveExistingObs(obs,changeMessage);
1✔
117
                }
118
        }
119

120
        private void setPersonFromEncounter(Obs obs) {
121
                Encounter encounter = obs.getEncounter();
1✔
122
                if (encounter != null) {
1✔
123
                        obs.setPerson(encounter.getPatient());
1✔
124
                }
125
        }
1✔
126

127
        private void voidExistingObs(Obs obs, String changeMessage, Obs newObs) {
128
                // void out the original observation to keep it around for
129
                // historical purposes
130
                try {
131
                        Context.addProxyPrivilege(PrivilegeConstants.DELETE_OBS);
1✔
132

133
                        // fetch a clean copy of this obs from the database so that
134
                        // we don't write the changes to the database when we save
135
                        // the fact that the obs is now voided
136
                        evictObsAndChildren(obs);
1✔
137
                        obs = Context.getObsService().getObs(obs.getObsId());
1✔
138
                        //delete the previous file from the appdata/complex_obs folder
139
                        if (newObs.hasPreviousVersion() && newObs.getPreviousVersion().isComplex()) {
1✔
140
                                ComplexObsHandler handler = getHandler(newObs.getPreviousVersion());
1✔
141
                                handler.purgeComplexData(newObs.getPreviousVersion());
1✔
142
                        }
143
                        // calling this via the service so that AOP hooks are called
144
                        Context.getObsService().voidObs(obs, changeMessage);
1✔
145

146
                }
147
                finally {
148
                        Context.removeProxyPrivilege(PrivilegeConstants.DELETE_OBS);
1✔
149
                }
150
        }
1✔
151

152
        private Obs saveExistingObs(Obs obs, String changeMessage) {
153
                // get a copy of the passed in obs and save it to the
154
                // database. This allows us to create a new row and new obs_id
155
                // this method doesn't copy the obs_id
156
                Obs newObs = Obs.newInstance(obs);
1✔
157

158
                unsetVoidedAndCreationProperties(newObs,obs);
1✔
159
                handleObsWithComplexConcept(newObs);
1✔
160
                Obs.Status originalStatus = dao.getSavedStatus(obs);
1✔
161
                updateStatusIfNecessary(newObs, originalStatus);
1✔
162

163
                RequiredDataAdvice.recursivelyHandle(SaveHandler.class, newObs, changeMessage);
1✔
164

165
                // save the new row to the database with the changes that
166
                // have been made to it
167
                dao.saveObs(newObs);
1✔
168

169
                saveObsGroup(newObs,null);
1✔
170

171
                voidExistingObs(obs, changeMessage, newObs);
1✔
172

173
                return newObs;
1✔
174

175
        }
176
        
177
        private void updateStatusIfNecessary(Obs newObs, Obs.Status originalStatus) {
178
                if (Obs.Status.FINAL.equals(originalStatus)) {
1✔
179
                        newObs.setStatus(Obs.Status.AMENDED);
1✔
180
                }
181
        }
1✔
182
        
183
        private void unsetVoidedAndCreationProperties(Obs newObs,Obs obs) {
184
                newObs.setVoided(false);
1✔
185
                newObs.setVoidReason(null);
1✔
186
                newObs.setDateVoided(null);
1✔
187
                newObs.setVoidedBy(null);
1✔
188
                newObs.setCreator(null);
1✔
189
                newObs.setDateCreated(null);
1✔
190
                newObs.setPreviousVersion(obs);
1✔
191
        }
1✔
192

193
        private Obs saveObsNotDirty(Obs obs, String changeMessage) {
194
                if(!obs.isObsGrouping()){
1✔
195
                        return obs;
1✔
196
                }
197

198
                ObsService os = Context.getObsService();
1✔
199
                boolean refreshNeeded = false;
1✔
200
                for (Obs o : obs.getGroupMembers(true)) {
1✔
201
                        if (o.getId() == null) {
1✔
202
                                os.saveObs(o, null);
1✔
203
                        } else {
204
                                Obs newObs = os.saveObs(o, changeMessage);
1✔
205
                                refreshNeeded = !newObs.equals(o) || refreshNeeded;
1✔
206
                        }
207
                }
1✔
208

209
                if(refreshNeeded) {
1✔
210
                        Context.refreshEntity(obs);
1✔
211
                }
212
                return obs;
1✔
213
        }
214

215
        private Obs saveNewOrVoidedObs(Obs obs, String changeMessage) {
216
                handleObsWithComplexConcept(obs);
1✔
217
                Obs ret = dao.saveObs(obs);
1✔
218
                saveObsGroup(ret,changeMessage);
1✔
219
                return ret;
1✔
220
        }
221

222
        private void evictObsAndChildren(Obs obs) {
223
                Context.evictFromSession(obs);
1✔
224
                if(obs.hasGroupMembers()) {
1✔
225
                        for(Obs member : obs.getGroupMembers()) {
1✔
226
                                evictObsAndChildren(member);
1✔
227
                        }
1✔
228
                }
229
        }
1✔
230

231
        private void ensureRequirePrivilege(Obs obs){
232
                if (obs.getObsId() == null) {
1✔
233
                        Context.requirePrivilege(PrivilegeConstants.ADD_OBS);
1✔
234
                } else {
235
                        Context.requirePrivilege(PrivilegeConstants.EDIT_OBS);
1✔
236
                }
237
        }
1✔
238

239
        private void saveObsGroup(Obs obs, String changeMessage){
240
                if (obs.isObsGrouping()) {
1✔
241
                        for (Obs o : obs.getGroupMembers(true)) {
1✔
242
                                Context.getObsService().saveObs(o, changeMessage);
1✔
243
                        }
1✔
244
                }
245
        }
1✔
246

247
        private void handleObsWithComplexConcept(Obs obs) {
248
                ComplexData complexData = obs.getComplexData();
1✔
249
                Concept concept = obs.getConcept();
1✔
250
                if (null != concept && concept.isComplex()
1✔
251
                        && null != complexData && null != complexData.getData()) {
1✔
252
                        // save or update complexData object on this obs
253
                        // this is done before the database save so that the obs.valueComplex
254
                        // can be filled in by the handler.
255
                        ComplexObsHandler handler = getHandler(obs);
1✔
256
                        if (null != handler) {
1✔
257
                                handler.saveObs(obs);
1✔
258
                        } else {
UNCOV
259
                                throw new APIException("unknown.handler", new Object[] {concept});
×
260
                        }
261
                }
262
        }
1✔
263

264
        /**
265
         * @see org.openmrs.api.ObsService#getObs(java.lang.Integer)
266
         */
267
        @Override
268
        @Transactional(readOnly = true)
269
        public Obs getObs(Integer obsId) throws APIException {
270
                Obs obs = dao.getObs(obsId);
1✔
271
                if (obs != null && obs.isComplex()) {
1✔
272
                        return getHandler(obs).getObs(obs,ComplexObsHandler.RAW_VIEW);
1✔
273
                }
274
                return obs;
1✔
275
        }
276
        
277
        /**
278
         * Voids an Obs If the Obs argument is an obsGroup, all group members will be voided.
279
         * 
280
         * @see org.openmrs.api.ObsService#voidObs(org.openmrs.Obs, java.lang.String)
281
         * @param obs the Obs to void
282
         * @param reason the void reason
283
         * @throws APIException
284
         */
285
        @Override
286
        public Obs voidObs(Obs obs, String reason) throws APIException {
287
                return dao.saveObs(obs);
1✔
288
        }
289
        
290
        /**
291
         * Unvoids an Obs
292
         * <p>
293
         * If the Obs argument is an obsGroup, all group members with the same dateVoided will also be
294
         * unvoided.
295
         * 
296
         * @see org.openmrs.api.ObsService#unvoidObs(org.openmrs.Obs)
297
         * @param obs the Obs to unvoid
298
         * @return the unvoided Obs
299
         * @throws APIException
300
         */
301
        @Override
302
        public Obs unvoidObs(Obs obs) throws APIException {
303
                return Context.getObsService().saveObs(obs,"unvoid obs");
1✔
304
        }
305
        
306
        /**
307
         * @see org.openmrs.api.ObsService#purgeObs(org.openmrs.Obs, boolean)
308
         */
309
        @Override
310
        public void purgeObs(Obs obs, boolean cascade) throws APIException {
311
                if (!purgeComplexData(obs)) {
1✔
312
                        // Log a warning instead of throwing an error.
313
                        // This allows purging the obs row even if the associated file is missing,
314
                        // which matches the behavior expected by modules like Attachments.
315
                        log.warn("purgeComplexData returned false for Obs ID: " + (obs != null ? obs.getObsId() : "null") + ". " + 
1✔
316
                                "This may mean the file is already missing. Proceeding to purge the database row.");
317
                }
318

319
                if (cascade) {
1✔
320
                        throw new APIException("Obs.error.cascading.purge.not.implemented", (Object[]) null);
1✔
321
                        // TODO delete any related objects here before deleting the obs
322
                        // obsGroups objects?
323
                        // orders?
324
                }
325
                dao.deleteObs(obs);
1✔
326
        }
1✔
327
        
328
        /**
329
         * @see org.openmrs.api.ObsService#purgeObs(org.openmrs.Obs)
330
         */
331
        @Override
332
        public void purgeObs(Obs obs) throws APIException {
333
                Context.getObsService().purgeObs(obs, false);
1✔
334
        }
1✔
335
        
336
        /**
337
         * @see org.openmrs.api.ObsService#getObservations(java.util.List, java.util.List,
338
         *      java.util.List, java.util.List, List, List, java.util.List, java.lang.Integer,
339
         *      java.lang.Integer, java.util.Date, java.util.Date, boolean)
340
         */
341
        @Override
342
        @Transactional(readOnly = true)
343
        public List<Obs> getObservations(List<Person> whom, List<Encounter> encounters, List<Concept> questions,
344
                List<Concept> answers, List<PERSON_TYPE> personTypes, List<Location> locations, List<String> sort,
345
                Integer mostRecentN, Integer obsGroupId, Date fromDate, Date toDate, boolean includeVoidedObs)
346
                throws APIException {
347

348
                return dao.getObservations(whom, encounters, questions, answers, personTypes, locations, sort, mostRecentN,
1✔
349
                    obsGroupId, fromDate, toDate, includeVoidedObs, null);
350
        }
351
        
352
        /**
353
         * @see org.openmrs.api.ObsService#getObservations(java.util.List, java.util.List,
354
         *      java.util.List, java.util.List, List, List, java.util.List, java.lang.Integer,
355
         *      java.lang.Integer, java.util.Date, java.util.Date, boolean, java.lang.String)
356
         */
357
        @Override
358
        @Transactional(readOnly = true)
359
        public List<Obs> getObservations(List<Person> whom, List<Encounter> encounters, List<Concept> questions,
360
                                         List<Concept> answers, List<PERSON_TYPE> personTypes, List<Location> locations,
361
                                         List<String> sort, Integer mostRecentN, Integer obsGroupId, Date fromDate, Date toDate,
362
                                         boolean includeVoidedObs, String accessionNumber) throws APIException {
363
                
364
                return this.getObservations(whom, encounters, questions, answers, personTypes, locations, sort, null,
1✔
365
                                mostRecentN, obsGroupId, fromDate, toDate, includeVoidedObs, accessionNumber);
366
        }
367
        
368
        /**
369
         * @see org.openmrs.api.ObsService#getObservations(java.util.List, java.util.List,
370
         *      java.util.List, java.util.List, List, List, java.util.List, java.util.List, java.lang.Integer,
371
         *      java.lang.Integer, java.util.Date, java.util.Date, boolean, java.lang.String)
372
         */
373
        @Override
374
        @Transactional(readOnly = true)
375
        public List<Obs> getObservations(List<Person> whom, List<Encounter> encounters, List<Concept> questions,
376
                List<Concept> answers, List<PERSON_TYPE> personTypes, List<Location> locations, List<String> sort,
377
                List<Visit> visits, Integer mostRecentN, Integer obsGroupId, Date fromDate, Date toDate,
378
                boolean includeVoidedObs, String accessionNumber) throws APIException {
379

380
                return dao.getObservations(whom, encounters, questions, answers, personTypes, locations, sort, visits, mostRecentN,
1✔
381
                    obsGroupId, fromDate, toDate, includeVoidedObs, accessionNumber);
382
        }
383
        
384
        /**
385
         * @see org.openmrs.api.ObsService#getObservations(ObsSearchCriteria)
386
         */
387
        @Override
388
        @Transactional(readOnly = true)
389
        public List<Obs> getObservations(ObsSearchCriteria obsSearchCriteria) throws APIException {
390
                requireSearchCriteria(obsSearchCriteria);
1✔
391

392
                return dao.getObservations(obsSearchCriteria);
1✔
393
        }
394

395
        /**
396
         * Every individual search parameter is optional, so passing null for the criteria object as a whole
397
         * is an easy mistake to make. Caught at the service boundary it names the problem, where the DAO
398
         * would only raise an NPE from inside a query.
399
         *
400
         * @param obsSearchCriteria the criteria to check
401
         * @throws IllegalArgumentException if obsSearchCriteria is null
402
         */
403
        private static void requireSearchCriteria(ObsSearchCriteria obsSearchCriteria) {
404
                if (obsSearchCriteria == null) {
1✔
405
                        throw new IllegalArgumentException("obsSearchCriteria cannot be null");
1✔
406
                }
407
        }
1✔
408

409
        /**
410
         * @see org.openmrs.api.ObsService#getObservations(java.util.List, java.util.List, java.util.List,
411
         *      java.util.List, List, List, java.util.List, java.util.List, java.lang.Integer,
412
         *      java.lang.Integer, java.util.Date, java.util.Date, boolean, java.lang.String,
413
         *      java.lang.Integer, java.lang.Integer)
414
         */
415
        @Override
416
        @SuppressWarnings("squid:S107")
417
        @Transactional(readOnly = true)
418
        public List<Obs> getObservations(List<Person> whom, List<Encounter> encounters, List<Concept> questions,
419
                List<Concept> answers, List<PERSON_TYPE> personTypes, List<Location> locations, List<String> sort,
420
                List<Visit> visits, Integer mostRecentN, Integer obsGroupId, Date fromDate, Date toDate,
421
                boolean includeVoidedObs, String accessionNumber, Integer startIndex, Integer maxResults) throws APIException {
422

423
                return this.getObservations(new ObsSearchCriteriaBuilder().setWhom(whom).setEncounters(encounters)
1✔
424
                        .setQuestions(questions).setAnswers(answers).setPersonTypes(personTypes).setLocations(locations)
1✔
425
                        .setSort(sort).setVisits(visits).setMostRecentN(mostRecentN).setObsGroupId(obsGroupId).setFromDate(fromDate)
1✔
426
                        .setToDate(toDate).setIncludeVoidedObs(includeVoidedObs).setAccessionNumber(accessionNumber)
1✔
427
                        .setStartIndex(startIndex).setMaxResults(maxResults).createObsSearchCriteria());
1✔
428
        }
429

430
        /**
431
         * @see org.openmrs.api.ObsService#getObservationCount(java.util.List, java.util.List,
432
         *      java.util.List, java.util.List, java.util.List, java.util.List, java.lang.Integer,
433
         *      java.util.Date, java.util.Date, boolean)
434
         */
435
        @Override
436
        @Transactional(readOnly = true)
437
        public Integer getObservationCount(List<Person> whom, List<Encounter> encounters, List<Concept> questions,
438
                                           List<Concept> answers, List<PERSON_TYPE> personTypes, List<Location> locations,
439
                                           Integer obsGroupId, Date fromDate, Date toDate, boolean includeVoidedObs)
440
            throws APIException {
441
                return OpenmrsUtil.convertToInteger(dao.getObservationCount(whom, encounters, questions, answers, personTypes,
1✔
442
                    locations, obsGroupId, fromDate, toDate, null, includeVoidedObs, null));
443
        }
444
        
445
        /**
446
         * @see org.openmrs.api.ObsService#getObservationCount(java.util.List, java.util.List,
447
         *      java.util.List, java.util.List, java.util.List, java.util.List, java.lang.Integer,
448
         *      java.util.Date, java.util.Date, boolean, java.lang.String)
449
         */
450
        @Override
451
        @Transactional(readOnly = true)
452
        public Integer getObservationCount(List<Person> whom, List<Encounter> encounters, List<Concept> questions,
453
                                           List<Concept> answers, List<PERSON_TYPE> personTypes, List<Location> locations,
454
                                           Integer obsGroupId, Date fromDate, Date toDate, boolean includeVoidedObs,
455
                                           String accessionNumber) throws APIException {
456
                return this.getObservationCount(whom, encounters, questions, answers, personTypes, locations, null, obsGroupId,
1✔
457
                                fromDate, toDate, includeVoidedObs, accessionNumber);
458
        }
459
        
460
        /**
461
         * @see org.openmrs.api.ObsService#getObservationCount(java.util.List, java.util.List,
462
         *      java.util.List, java.util.List, java.util.List, java.util.List, java.util.List, java.lang.Integer,
463
         *      java.util.Date, java.util.Date, boolean, java.lang.String)
464
         */
465
        @Override
466
        @Transactional(readOnly = true)
467
        public Integer getObservationCount(List<Person> whom, List<Encounter> encounters, List<Concept> questions,
468
                                           List<Concept> answers, List<PERSON_TYPE> personTypes, List<Location> locations, List<Visit> visits,
469
                                           Integer obsGroupId, Date fromDate, Date toDate, boolean includeVoidedObs,
470
                                           String accessionNumber) throws APIException {
471
                return OpenmrsUtil.convertToInteger(dao.getObservationCount(whom, encounters, questions, answers, personTypes,
1✔
472
                    locations, obsGroupId, fromDate, toDate, null, visits, includeVoidedObs, accessionNumber));
473
        }
474
        
475
        /**
476
         * @see org.openmrs.api.ObsService#getObservationCount(ObsSearchCriteria)
477
         */
478
        @Override
479
        @Transactional(readOnly = true)
480
        public Integer getObservationCount(ObsSearchCriteria obsSearchCriteria) throws APIException {
481
                requireSearchCriteria(obsSearchCriteria);
1✔
482

483
                // the sort and the row bounds the criteria carry say which of the matching rows come back, not
484
                // which rows match, so a count deliberately ignores them: a paging client counting the result set
485
                // it is walking wants the whole of it. valueCodedNameAnswers is passed as null because the search
486
                // criteria cannot express it, matching the other getObservationCount() overloads
487
                return OpenmrsUtil.convertToInteger(dao.getObservationCount(obsSearchCriteria.getWhom(),
1✔
488
                    obsSearchCriteria.getEncounters(), obsSearchCriteria.getQuestions(), obsSearchCriteria.getAnswers(),
1✔
489
                    obsSearchCriteria.getPersonTypes(), obsSearchCriteria.getLocations(), obsSearchCriteria.getObsGroupId(),
1✔
490
                    obsSearchCriteria.getFromDate(), obsSearchCriteria.getToDate(), null, obsSearchCriteria.getVisits(),
1✔
491
                    obsSearchCriteria.isIncludeVoidedObs(), obsSearchCriteria.getAccessionNumber()));
1✔
492
        }
493

494
        /**
495
         * This implementation queries the obs table comparing the given <code>searchString</code> with the
496
         * patient's identifier, encounterId, and obsId
497
         *
498
         * @see org.openmrs.api.ObsService#getObservations(java.lang.String)
499
         */
500
        @Override
501
        @Transactional(readOnly = true)
502
        public List<Obs> getObservations(String searchString) {
503
                
504
                // search on patient identifier
505
                PatientService ps = Context.getPatientService();
1✔
506
                List<Patient> patients = ps.getPatients(searchString);
1✔
507
                List<Person> persons = new ArrayList<>(patients);
1✔
508
                
509
                // try to search on encounterId
510
                EncounterService es = Context.getEncounterService();
1✔
511
                List<Encounter> encounters = new ArrayList<>();
1✔
512
                try {
513
                        Encounter e = es.getEncounter(Integer.valueOf(searchString));
1✔
514
                        if (e != null) {
1✔
515
                                encounters.add(e);
1✔
516
                        }
517
                }
518
                catch (NumberFormatException e) {
1✔
519
                        // pass
520
                }
1✔
521
                
522
                List<Obs> returnList = new ArrayList<>();
1✔
523
                
524
                if (!encounters.isEmpty() || !persons.isEmpty()) {
1✔
525
                        returnList = Context.getObsService().getObservations(persons, encounters, null, null, null, null, null, null,
1✔
526
                            null, null, null, false);
527
                }
528
                
529
                // try to search on obsId
530
                try {
531
                        Obs o = getObs(Integer.valueOf(searchString));
1✔
532
                        if (o != null) {
1✔
533
                                returnList.add(o);
1✔
534
                        }
535
                }
536
                catch (NumberFormatException e) {
1✔
537
                        // pass
538
                }
1✔
539
                
540
                return returnList;
1✔
541
        }
542
        
543
        /**
544
         * @see org.openmrs.api.ObsService#getObservationsByPerson(org.openmrs.Person)
545
         */
546
        @Override
547
        @Transactional(readOnly = true)
548
        public List<Obs> getObservationsByPerson(Person who) {
549
                List<Person> whom = new ArrayList<>();
1✔
550
                whom.add(who);
1✔
551
                return Context.getObsService().getObservations(whom, null, null, null, null, null, null, null, null, null, null,
1✔
552
                    false);
553
        }
554
        
555
        /**
556
         * @see org.openmrs.api.ObsService#getObservationsByPersonAndConcept(org.openmrs.Person,
557
         *      org.openmrs.Concept)
558
         */
559
        @Override
560
        @Transactional(readOnly = true)
561
        public List<Obs> getObservationsByPersonAndConcept(Person who, Concept question) throws APIException {
562
                List<Person> whom = new ArrayList<>();
1✔
563
                if (who != null && who.getPersonId() != null) {
1✔
564
                        whom.add(who);
1✔
565
                }
566
                List<Concept> questions = new ArrayList<>();
1✔
567
                questions.add(question);
1✔
568
                
569
                return Context.getObsService().getObservations(whom, null, questions, null, null, null, null, null, null, null,
1✔
570
                    null, false);
571
        }
572
        
573
        /**
574
         * @see org.openmrs.api.ObsService#getObsByUuid(java.lang.String)
575
         */
576
        @Override
577
        @Transactional(readOnly = true)
578
        public Obs getObsByUuid(String uuid) throws APIException {
579
                Obs obsByUuid = dao.getObsByUuid(uuid);
1✔
580
                if (obsByUuid != null && obsByUuid.isComplex()) {
1✔
581
                        return getHandler(obsByUuid).getObs(obsByUuid,ComplexObsHandler.RAW_VIEW);
1✔
582
                }
583
                return obsByUuid;
1✔
584
        }
585

586
        /**
587
         * @see org.openmrs.api.ObsService#getRevisionObs(org.openmrs.Obs)
588
         */
589
        @Transactional(readOnly = true)
590
        public Obs getRevisionObs(Obs initialObs) {
591
                return dao.getRevisionObs(initialObs);
1✔
592
        }
593

594
        /**
595
         * @see org.openmrs.api.ObsService#getComplexObs(Integer, String)
596
         */
597
        @Override
598
        @Transactional(readOnly = true)
599
        public Obs getComplexObs(Integer obsId, String view) throws APIException {
600
                Obs obs = dao.getObs(obsId);
1✔
601
                
602
                if (obs != null && obs.isComplex()) {
1✔
603
                        return getHandler(obs).getObs(obs, view);
1✔
604
                }
605
                
606
                return obs;
1✔
607
        }
608
        
609
        /**
610
         * Internal method to remove ComplexData when an Obs is purged.
611
         */
612
        protected boolean purgeComplexData(Obs obs) throws APIException {
613
                if (obs.isComplex()) {
1✔
614
                        ComplexObsHandler handler = getHandler(obs);
1✔
615
                        if (null != handler) {
1✔
616
                                return handler.purgeComplexData(obs);
1✔
617
                        }
618
                }
619
                
620
                return true;
1✔
621
        }
622
        
623
        /**
624
         * @see org.openmrs.api.ObsService#getHandler(org.openmrs.Obs)
625
         */
626
        @Override
627
        @Transactional(readOnly = true)
628
        public ComplexObsHandler getHandler(Obs obs) throws APIException {
629
                if (obs.getConcept().isComplex()) {
1✔
630
                        // Get the ConceptComplex from the ConceptService then return its
631
                        // handler.
632
                        if (obs.getConcept() == null) {
1✔
UNCOV
633
                                throw new APIException("Obs.error.unable.get.handler", new Object[] { obs });
×
634
                        }
635
                        
636
                        String handlerString = Context.getConceptService().getConceptComplex(obs.getConcept().getConceptId())
1✔
637
                                .getHandler();
1✔
638
                        
639
                        if (handlerString == null) {
1✔
UNCOV
640
                                throw new APIException("Obs.error.unable.get.handler.and.concept", new Object[] { obs, obs.getConcept() });
×
641
                        }
642
                        
643
                        return this.getHandler(handlerString);
1✔
644
                }
645
                
UNCOV
646
                return null;
×
647
        }
648
        
649
        /**
650
         * @see org.openmrs.api.ObsService#getHandler(java.lang.String)
651
         */
652
        @Override
653
        public ComplexObsHandler getHandler(String key) {
654
                return handlers.get(key);
1✔
655
        }
656
        
657
        /**
658
         * @see org.openmrs.api.ObsService#setHandlers(Map)
659
         * @see #registerHandler(String, ComplexObsHandler)
660
         */
661
        @Override
662
        public void setHandlers(Map<String, ComplexObsHandler> newHandlers) throws APIException {
663
                if (newHandlers == null) {
1✔
UNCOV
664
                        ObsServiceImpl.setStaticHandlers(null);
×
UNCOV
665
                        return;
×
666
                }
667
                for (Map.Entry<String, ComplexObsHandler> entry : newHandlers.entrySet()) {
1✔
668
                        registerHandler(entry.getKey(), entry.getValue());
1✔
669
                }
1✔
670
        }
1✔
671
        
672
        /**
673
         * Sets handlers using static method
674
         *
675
         * @param currentHandlers
676
         */
677
        private static void setStaticHandlers(Map<String, ComplexObsHandler> currentHandlers) {
UNCOV
678
                ObsServiceImpl.handlers = currentHandlers;
×
UNCOV
679
        }
×
680
        
681
        /**
682
         * @see org.openmrs.api.ObsService#getHandlers()
683
         */
684
        @Override
685
        @Transactional(readOnly = true)
686
        public Map<String, ComplexObsHandler> getHandlers() throws APIException {
687
                if (handlers == null) {
1✔
688
                        handlers = new LinkedHashMap<>();
1✔
689
                }
690
                
691
                return handlers;
1✔
692
        }
693
        
694
        /**
695
         * @see org.openmrs.api.ObsService#registerHandler(String, ComplexObsHandler)
696
         */
697
        @Override
698
        public void registerHandler(String key, ComplexObsHandler handler) throws APIException {
699
                getHandlers().put(key, handler);
1✔
700
        }
1✔
701
        
702
        /**
703
         * @see org.openmrs.api.ObsService#registerHandler(String, String)
704
         */
705
        @Override
706
        public void registerHandler(String key, String handlerClass) throws APIException {
707
                try {
708
                        Class<?> loadedClass = OpenmrsClassLoader.getInstance().loadClass(handlerClass);
1✔
709
                        registerHandler(key, (ComplexObsHandler) loadedClass.newInstance());
1✔
710
                        
711
                }
UNCOV
712
                catch (Exception e) {
×
UNCOV
713
                        throw new APIException("unable.load.and.instantiate.handler", null, e);
×
714
                }
1✔
715
        }
1✔
716
        
717
        /**
718
         * @see org.openmrs.api.ObsService#getObservationCount(java.util.List, boolean)
719
         */
720
        @Override
721
        @Transactional(readOnly = true)
722
        public Integer getObservationCount(List<ConceptName> conceptNames, boolean includeVoided) {
723
                return OpenmrsUtil.convertToInteger(dao.getObservationCount(null, null, null, null, null, null, null, null, null,
1✔
724
                    conceptNames, true, null));
725
        }
726
        
727
        /**
728
         * @see org.openmrs.api.ObsService#removeHandler(java.lang.String)
729
         */
730
        @Override
731
        public void removeHandler(String key) {
732
                handlers.remove(key);
1✔
733
        }
1✔
734
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc