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

openmrs / openmrs-core / 18207530234

02 Oct 2025 10:40PM UTC coverage: 65.252% (+0.01%) from 65.24%
18207530234

push

github

dkayiwa
TRUNK-6433 Methods for getting days,weeks,months,years between dates

(cherry picked from commit d7b22f03f)

33 of 41 new or added lines in 1 file covered. (80.49%)

5 existing lines in 2 files now uncovered.

23642 of 36232 relevant lines covered (65.25%)

0.65 hits per line

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

83.33
/api/src/main/java/org/openmrs/util/ConceptReferenceRangeUtility.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.util;
11

12
import java.io.StringWriter;
13
import java.time.LocalDate;
14
import java.time.ZoneId;
15
import java.time.temporal.ChronoUnit;
16
import java.util.Collections;
17
import java.util.Date;
18
import java.util.List;
19
import java.util.Locale;
20
import java.util.Properties;
21

22
import org.apache.commons.lang.StringUtils;
23
import org.apache.velocity.VelocityContext;
24
import org.apache.velocity.app.VelocityEngine;
25
import org.apache.velocity.exception.ParseErrorException;
26
import org.apache.velocity.runtime.log.Log4JLogChute;
27
import org.joda.time.LocalTime;
28
import org.openmrs.Concept;
29
import org.openmrs.Obs;
30
import org.openmrs.Person;
31
import org.openmrs.api.APIException;
32
import org.openmrs.api.context.Context;
33

34
/**
35
 * A utility class that evaluates the concept ranges 
36
 * 
37
 * @since 2.7.0
38
 */
39
public class ConceptReferenceRangeUtility {
40
        
41
        private final long NULL_DATE_RETURN_VALUE = -1;
1✔
42
        
43
        public ConceptReferenceRangeUtility() {
1✔
44
        }
1✔
45
        
46
        /**
47
         * This method evaluates the given criteria against the provided {@link Obs}.
48
         *
49
         * @param criteria the criteria string to evaluate e.g. "$patient.getAge() > 1"
50
         * @param obs The observation (Obs) object containing the values to be used in the criteria evaluation.
51
         *                  
52
         * @return true if the criteria evaluates to true, false otherwise
53
         */
54
        public boolean evaluateCriteria(String criteria, Obs obs) {
55
                if (obs == null) {
1✔
56
                        throw new IllegalArgumentException("Failed to evaluate criteria with reason: Obs is null");
1✔
57
                }
58
                
59
                if (obs.getPerson() == null) {
1✔
60
                        throw new IllegalArgumentException("Failed to evaluate criteria with reason: patient is null");
×
61
                }
62
                
63
                if (StringUtils.isBlank(criteria)) {
1✔
64
                        throw new IllegalArgumentException("Failed to evaluate criteria with reason: criteria is empty");
1✔
65
                }
66
                
67
                VelocityContext velocityContext = new VelocityContext();
1✔
68
                velocityContext.put("fn", this);
1✔
69
                velocityContext.put("obs", obs);
1✔
70
                
71
                velocityContext.put("patient", obs.getPerson());
1✔
72
                
73
                VelocityEngine velocityEngine = new VelocityEngine();
1✔
74
                try {
75
                        Properties props = new Properties();
1✔
76
                        props.put("runtime.log.logsystem.class", Log4JLogChute.class.getName());
1✔
77
                        props.put("runtime.log.logsystem.log4j.category", "velocity");
1✔
78
                        props.put("runtime.log.logsystem.log4j.logger", "velocity");
1✔
79
                        velocityEngine.init(props);
1✔
80
                }
81
                catch (Exception e) {
×
82
                        throw new APIException("Failed to create the velocity engine: " + e.getMessage(), e);
×
83
                }
1✔
84
                
85
                StringWriter writer = new StringWriter();
1✔
86
                String wrappedCriteria = "#set( $criteria = " + criteria + " )$criteria";
1✔
87
                
88
                try {
89
                        velocityEngine.evaluate(velocityContext, writer, ConceptReferenceRangeUtility.class.getName(), wrappedCriteria);
1✔
90
                        return Boolean.parseBoolean(writer.toString());
1✔
91
                }
92
                catch (ParseErrorException e) {
1✔
93
                        throw new APIException("An error occurred while evaluating criteria. Invalid criteria: " + criteria, e);
1✔
94
                }
95
                catch (Exception e) {
×
96
                        throw new APIException("An error occurred while evaluating criteria: ", e);
×
97
                }
98
        }
99
        
100
        /**
101
         * Gets the latest Obs by concept.
102
         *
103
         * @param conceptRef can be either concept uuid or conceptMap's code and sourceName 
104
         *                   e.g "bac25fd5-c143-4e43-bffe-4eb1e7efb6ce" or "CIEL:1434"
105
         * @param person person to get obs for
106
         *                   
107
         * @return Obs latest Obs
108
         */
109
        public Obs getLatestObs(String conceptRef, Person person) {
110
                Concept concept = Context.getConceptService().getConceptByReference(conceptRef);
1✔
111

112
                if (concept != null) {
1✔
113
                        List<Obs> observations = Context.getObsService().getObservations(
1✔
114
                                Collections.singletonList(person), 
1✔
115
                                null, 
116
                                Collections.singletonList(concept), 
1✔
117
                                null, 
118
                                null, 
119
                                null,
120
                                Collections.singletonList("dateCreated"), 
1✔
121
                                1, 
1✔
122
                                null,
123
                                null, 
124
                                null, 
125
                                false
126
                        );
127

128
                        return observations.isEmpty() ? null : observations.get(0);
1✔
129
                }
130

131
                return null;
1✔
132
        }
133
        
134
        /**
135
         * Gets the time of the day in hours.
136
         *
137
         * @return the hour of the day in 24hr format (e.g. 14 to mean 2pm)
138
         */
139
        public int getCurrentHour() {
140
                return LocalTime.now().getHourOfDay();
1✔
141
        }
142
        
143
        /**
144
         * Retrieves the most relevant Obs for the given current Obs and conceptRef. If the current Obs contains a valid value 
145
         * (coded, numeric, date, text e.t.c) and the concept in Obs is the same as the supplied concept,
146
         * the method returns the current Obs. Otherwise, it fetches the latest Obs for the supplied concept and patient.
147
         *
148
         * @param currentObs the current Obs being evaluated
149
         * @return the most relevant Obs based on the current Obs, or the latest Obs if the current one has no valid value
150
         */
151
        public Obs getCurrentObs(String conceptRef, Obs currentObs) {
152
                Concept concept = Context.getConceptService().getConceptByReference(conceptRef);
1✔
153
                
154
                if (currentObs.getValueAsString(Locale.ENGLISH).isEmpty() && (concept != null && concept == currentObs.getConcept())) {
1✔
155
                        return currentObs;
×
156
                } else {
157
                        return getLatestObs(conceptRef, currentObs.getPerson());
1✔
158
                }
159
        }
160
        
161
        /**
162
         * Gets the person's latest observation date for a given concept
163
         * 
164
         * @param conceptRef can be either concept uuid or conceptMap's code and sourceName 
165
         *                   e.g "bac25fd5-c143-4e43-bffe-4eb1e7efb6ce" or "CIEL:1434"
166
         * @param person the person
167
         * 
168
         * @return the observation date
169
         * 
170
         * @since 2.7.8
171
         */
172
        public Date getLatestObsDate(String conceptRef, Person person) {
173
                Obs obs = getLatestObs(conceptRef, person);
1✔
174
                if (obs == null) {
1✔
NEW
175
                        return null;
×
176
                }
177
                
178
                Date date = obs.getValueDate();
1✔
179
                if (date == null) {
1✔
180
                        date = obs.getValueDatetime();
1✔
181
                }
182
                
183
                return date;
1✔
184
        }
185
        
186
        /**
187
         * Gets the number of days from the person's latest observation date value for a given concept to the current date
188
         * 
189
         * @param conceptRef can be either concept uuid or conceptMap's code and sourceName 
190
         *                   e.g "bac25fd5-c143-4e43-bffe-4eb1e7efb6ce" or "CIEL:1434"
191
         * @param person the person
192
         * 
193
         * @return the number of days
194
         * 
195
         * @since 2.7.8
196
         */
197
        public long getObsDays(String conceptRef, Person person) {
198
                Date date = getLatestObsDate(conceptRef, person);
1✔
199
                if (date == null) {
1✔
NEW
200
                        return NULL_DATE_RETURN_VALUE;
×
201
                }
202
                return this.getDays(date);
1✔
203
        }
204
        
205
        /**
206
         * Gets the number of weeks from the person's latest observation date value for a given concept to the current date
207
         * 
208
         * @param conceptRef can be either concept uuid or conceptMap's code and sourceName 
209
         *                   e.g "bac25fd5-c143-4e43-bffe-4eb1e7efb6ce" or "CIEL:1434"
210
         * @param person the person
211
         * 
212
         * @return the number of weeks
213
         * 
214
         * @since 2.7.8
215
         */
216
        public long getObsWeeks(String conceptRef, Person person) {
217
                Date date = getLatestObsDate(conceptRef, person);
1✔
218
                if (date == null) {
1✔
219
                        return NULL_DATE_RETURN_VALUE;
1✔
220
                }
221
                return this.getWeeks(date);
1✔
222
        }
223
        
224
        /**
225
         * Gets the number of months from the person's latest observation date value for a given concept to the current date
226
         * 
227
         * @param conceptRef can be either concept uuid or conceptMap's code and sourceName 
228
         *                   e.g "bac25fd5-c143-4e43-bffe-4eb1e7efb6ce" or "CIEL:1434"
229
         * @param person the person
230
         * 
231
         * @return the number of months
232
         * 
233
         * @since 2.7.8
234
         */
235
        public long getObsMonths(String conceptRef, Person person) {
236
                Date date = getLatestObsDate(conceptRef, person);
1✔
237
                if (date == null) {
1✔
NEW
238
                        return NULL_DATE_RETURN_VALUE;
×
239
                }
240
                return this.getMonths(date);
1✔
241
        }
242
        
243
        /**
244
         * Gets the number of years from the person's latest observation date value for a given concept to the current date
245
         * 
246
         * @param conceptRef can be either concept uuid or conceptMap's code and sourceName 
247
         *                   e.g "bac25fd5-c143-4e43-bffe-4eb1e7efb6ce" or "CIEL:1434"
248
         * @param person the person
249
         * 
250
         * @return the number of years
251
         * 
252
         * @since 2.7.8
253
         */
254
        public long getObsYears(String conceptRef, Person person) {
255
                Date date = getLatestObsDate(conceptRef, person);
1✔
256
                if (date == null) {
1✔
NEW
257
                        return NULL_DATE_RETURN_VALUE;
×
258
                }
259
                return this.getYears(date);
1✔
260
        }
261
        
262
        /**
263
         * Gets the number of days between two given dates
264
         * 
265
         * @param fromDate the date from which to start counting
266
         * @param dateDate the date up to which to stop counting
267
         * 
268
         * @return the number of days between
269
         * 
270
         * @since 2.7.8
271
         */
272
        public long getDaysBetween(Date fromDate, Date toDate) {
273
                if (fromDate == null || toDate == null) {
1✔
NEW
274
                        return NULL_DATE_RETURN_VALUE;
×
275
                }
276
                return ChronoUnit.DAYS.between(toLocalDate(fromDate), toLocalDate(toDate));
1✔
277
        }
278
        
279
        /**
280
         * Gets the number of weeks between two given dates
281
         * 
282
         * @param fromDate the date from which to start counting
283
         * @param dateDate the date up to which to stop counting
284
         * 
285
         * @return the number of weeks between
286
         * 
287
         * @since 2.7.8
288
         */
289
        public long getWeeksBetween(Date fromDate, Date toDate) {
290
                if (fromDate == null || toDate == null) {
1✔
NEW
291
                        return NULL_DATE_RETURN_VALUE;
×
292
                }
293
                return ChronoUnit.WEEKS.between(toLocalDate(fromDate), toLocalDate(toDate));
1✔
294
        }
295
        
296
        /**
297
         * Gets the number of months between two given dates
298
         * 
299
         * @param fromDate the date from which to start counting
300
         * @param dateDate the date up to which to stop counting
301
         * 
302
         * @return the number of months between
303
         * 
304
         * @since 2.7.8
305
         */
306
        public long getMonthsBetween(Date fromDate, Date toDate) {
307
                if (fromDate == null || toDate == null) {
1✔
NEW
308
                        return NULL_DATE_RETURN_VALUE;
×
309
                }
310
                return ChronoUnit.MONTHS.between(toLocalDate(fromDate), toLocalDate(toDate));
1✔
311
        }
312
        
313
        /**
314
         * Gets the number of years between two given dates
315
         * 
316
         * @param fromDate the date from which to start counting
317
         * @param dateDate the date up to which to stop counting
318
         * 
319
         * @return the number of years between
320
         * 
321
         * @since 2.7.8
322
         */
323
        public long getYearsBetween(Date fromDate, Date toDate) {
324
                if (fromDate == null || toDate == null) {
1✔
NEW
325
                        return NULL_DATE_RETURN_VALUE;
×
326
                }
327
                return ChronoUnit.YEARS.between(toLocalDate(fromDate), toLocalDate(toDate));
1✔
328
        }
329
        
330
        /**
331
         * Gets the number of days from a given date up to the current date.
332
         * 
333
         * @param fromDate the date from which to start counting
334
         * @return the number of days
335
         * 
336
         * @since 2.7.8
337
         */
338
        public long getDays(Date fromDate) {
339
                return getDaysBetween(fromDate, new Date());
1✔
340
        }
341
        
342
        /**
343
         * Gets the number of weeks from a given date up to the current date.
344
         * 
345
         * @param fromDate the date from which to start counting
346
         * @return the number of weeks
347
         * 
348
         * @since 2.7.8
349
         */
350
        public long getWeeks(Date fromDate) {
351
                return getWeeksBetween(fromDate, new Date());
1✔
352
        }
353
        
354
        /**
355
         * Gets the number of months from a given date up to the current date.
356
         * 
357
         * @param fromDate the date from which to start counting
358
         * @return the number of months
359
         * 
360
         * @since 2.7.8
361
         */
362
        public long getMonths(Date fromDate) {
363
                return getMonthsBetween(fromDate, new Date());
1✔
364
        }
365
        
366
        /**
367
         * Gets the number of years from a given date up to the current date.
368
         * 
369
         * @param fromDate the date from which to start counting
370
         * @return the number of years
371
         * 
372
         * @since 2.7.8
373
         */
374
        public long getYears(Date fromDate) {
375
                return getYearsBetween(fromDate, new Date());
1✔
376
        }
377
        
378
        /**
379
         * Converts a java.util.Date to java.time.LocalDate
380
         * 
381
         * @param date the java.util.Date
382
         * @return the java.time.LocalDate
383
         */
384
        private LocalDate toLocalDate(Date date) {
385
                return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
1✔
386
        }
387
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc