Skip to content

Commit 2530bbc

Browse files
LAMP-49: Add scheduler to complete programs and fix complete program bug (#5)
1 parent 1241c65 commit 2530bbc

File tree

9 files changed

+338
-23
lines changed

9 files changed

+338
-23
lines changed

api/pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313
<description>API project for Lamp</description>
1414

1515
<dependencies>
16+
<dependency>
17+
<groupId>org.openmrs.api</groupId>
18+
<artifactId>openmrs-api</artifactId>
19+
</dependency>
1620
<dependency>
1721
<groupId>org.powermock</groupId>
1822
<artifactId>powermock-api-mockito2</artifactId>

api/src/main/java/org/openmrs/module/lamp/ChildNutritionProgramStrategy.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import org.openmrs.Concept;
55
import org.openmrs.Encounter;
66
import org.openmrs.PatientProgram;
7+
import org.openmrs.PatientState;
78
import org.openmrs.Program;
89
import org.openmrs.ProgramWorkflow;
910
import org.openmrs.ProgramWorkflowState;
@@ -75,7 +76,13 @@ public void execute(Encounter encounter, User currentUser, Date currentDate, Str
7576
targetState.setTerminal(false);
7677
}
7778

78-
Utils.updateProgram(patientProgram, encounter, currentDate, targetState);
79+
PatientState patientState = patientProgram.getCurrentState(programWorkflow);
80+
if (patientState != null && patientState.getState() != null
81+
&& patientState.getState().getConcept().getUuid().equals(targetState.getConcept().getUuid())) {
82+
return;
83+
}
84+
85+
Utils.updateProgram(patientProgram, encounter, targetState);
7986
patientProgram.setLocation(encounter.getLocation());
8087
programWorkflowService.savePatientProgram(patientProgram);
8188
}

api/src/main/java/org/openmrs/module/lamp/LampConfig.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,8 @@ public class LampConfig {
3333
public static final String PROGRAM_PRENATAL_UUID = "3531501f-bbdf-4e49-be19-6c87220f71ee";
3434

3535
public static final String WORKFLOW_PRENATAL_UUID = "3009b582-1745-46bc-8886-7ea20f4675f2";
36+
37+
public static final String CONCEPT_18_WEEKS_IN_CHILD_NUTRITION_PROGRAM = "74f45a8a-4128-4eb6-b8ca-f4b641c6de3a";
38+
39+
public static final String CONCEPT_10_MONTHS_IN_PRENATAL_PROGRAM = "20cfecf2-d01f-4bd8-b71e-ad112ce0d7ce";
3640
}

api/src/main/java/org/openmrs/module/lamp/PrenatalProgramStrategy.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import org.openmrs.Concept;
55
import org.openmrs.Encounter;
66
import org.openmrs.PatientProgram;
7+
import org.openmrs.PatientState;
78
import org.openmrs.Program;
89
import org.openmrs.ProgramWorkflow;
910
import org.openmrs.ProgramWorkflowState;
@@ -46,7 +47,13 @@ public void execute(Encounter encounter, User currentUser, Date currentDate, Str
4647
return;
4748
}
4849

49-
Utils.updateProgram(patientProgram, encounter, currentDate, targetState);
50+
PatientState patientState = patientProgram.getCurrentState(programWorkflow);
51+
if (patientState != null && patientState.getState() != null
52+
&& patientState.getState().getConcept().getUuid().equals(targetState.getConcept().getUuid())) {
53+
return;
54+
}
55+
56+
Utils.updateProgram(patientProgram, encounter, targetState);
5057
patientProgram.setLocation(encounter.getLocation());
5158
programWorkflowService.savePatientProgram(patientProgram);
5259
}

api/src/main/java/org/openmrs/module/lamp/Utils.java

Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import org.openmrs.Obs;
77
import org.openmrs.Patient;
88
import org.openmrs.PatientProgram;
9+
import org.openmrs.PatientState;
910
import org.openmrs.Program;
1011
import org.openmrs.ProgramWorkflow;
1112
import org.openmrs.ProgramWorkflowState;
@@ -63,34 +64,20 @@ public static ProgramWorkflow getWorkflowByUuid(Program program, String workflow
6364

6465
public static ProgramWorkflowState getStateByConcept(ProgramWorkflow programWorkflow, Concept concept) {
6566
for (ProgramWorkflowState programWorkflowState : programWorkflow.getStates()) {
66-
if (concept.equals(programWorkflowState.getConcept())) {
67+
if (concept.getUuid().equals(programWorkflowState.getConcept().getUuid())) {
6768
return programWorkflowState;
6869
}
6970
}
7071
return null;
7172
}
7273

73-
public static Date getProgramStatusUpdateDate(PatientProgram patientProgram, Encounter encounter, Date currentDate) {
74-
Date programStatusUpdateDate;
75-
Date enrolled = patientProgram.getDateEnrolled();
76-
if (enrolled != null && encounter.getEncounterDatetime() != null
77-
&& encounter.getEncounterDatetime().before(enrolled)) {
78-
programStatusUpdateDate = enrolled;
79-
} else if (encounter.getEncounterDatetime() != null) {
80-
programStatusUpdateDate = encounter.getEncounterDatetime();
81-
} else {
82-
programStatusUpdateDate = currentDate;
74+
public static void updateProgram(PatientProgram patientProgram, Encounter encounter, ProgramWorkflowState targetState) {
75+
for (PatientState ps : patientProgram.getStates()) {
76+
if (ps.getActive() && ps.getState().getProgramWorkflow().equals(targetState.getProgramWorkflow())) {
77+
ps.setEndDate(encounter.getEncounterDatetime());
78+
}
8379
}
84-
return programStatusUpdateDate;
85-
}
86-
87-
public static void updateProgram(PatientProgram patientProgram, Encounter encounter, Date currentDate,
88-
ProgramWorkflowState targetState) {
89-
Date programStatusUpdateDate = getProgramStatusUpdateDate(patientProgram, encounter, currentDate);
9080

91-
if (targetState.getTerminal()) {
92-
patientProgram.setDateCompleted(new Date());
93-
}
94-
patientProgram.transitionToState(targetState, programStatusUpdateDate);
81+
patientProgram.transitionToState(targetState, encounter.getEncounterDatetime());
9582
}
9683
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package org.openmrs.module.lamp.scheduler;
2+
3+
import org.apache.commons.logging.Log;
4+
import org.apache.commons.logging.LogFactory;
5+
import org.openmrs.PatientProgram;
6+
import org.openmrs.PatientState;
7+
import org.openmrs.Program;
8+
import org.openmrs.ProgramWorkflow;
9+
import org.openmrs.ProgramWorkflowState;
10+
import org.openmrs.api.ProgramWorkflowService;
11+
import org.openmrs.api.context.Context;
12+
import org.openmrs.module.lamp.LampConfig;
13+
import org.openmrs.module.lamp.Utils;
14+
import org.openmrs.scheduler.tasks.AbstractTask;
15+
import org.springframework.stereotype.Component;
16+
17+
import java.util.Calendar;
18+
import java.util.Date;
19+
import java.util.List;
20+
21+
@Component
22+
public class CompleteProgramsTask extends AbstractTask {
23+
24+
private static final Log log = LogFactory.getLog(CompleteProgramsTask.class);
25+
26+
@Override
27+
public void execute() {
28+
log.debug("Executing CompletePrograms Task");
29+
ProgramWorkflowService service = Context.getProgramWorkflowService();
30+
31+
completeProgramIfExists(service, LampConfig.PROGRAM_CHILD_NUTRITION_UUID, 18);
32+
completeProgramIfExists(service, LampConfig.PROGRAM_PRENATAL_UUID, 44);
33+
}
34+
35+
@Override
36+
public void shutdown() {
37+
log.debug("Shutting down CompletePrograms Task");
38+
stopExecuting();
39+
}
40+
41+
private void completeProgramIfExists(ProgramWorkflowService service, String programUuid, int weeksThreshold) {
42+
Program program = service.getProgramByUuid(programUuid);
43+
if (program != null) {
44+
completeProgramsStartedBefore(service, program, getThresholdDateWeeksAgo(weeksThreshold));
45+
}
46+
}
47+
48+
private Date getThresholdDateWeeksAgo(int weeks) {
49+
Calendar cal = Calendar.getInstance();
50+
cal.add(Calendar.WEEK_OF_YEAR, -weeks);
51+
return cal.getTime();
52+
}
53+
54+
private void completeProgramsStartedBefore(ProgramWorkflowService service, Program program, Date thresholdDate) {
55+
List<PatientProgram> patientPrograms = service.getPatientPrograms(null, program, null, null, null, null, false);
56+
57+
for (PatientProgram pp : patientPrograms) {
58+
if (pp == null || Boolean.TRUE.equals(pp.getVoided()) || pp.getDateCompleted() != null
59+
|| pp.getDateEnrolled() == null) {
60+
continue;
61+
}
62+
63+
if (pp.getDateEnrolled().before(thresholdDate)) {
64+
completePatientProgram(pp, program);
65+
}
66+
}
67+
}
68+
69+
private void completePatientProgram(PatientProgram pp, Program program) {
70+
String programUuid = pp.getProgram().getUuid();
71+
72+
if (LampConfig.PROGRAM_CHILD_NUTRITION_UUID.equals(programUuid)) {
73+
transitionProgramState(pp, program, LampConfig.WORKFLOW_CHILD_NUTRITION_UUID,
74+
LampConfig.CONCEPT_18_WEEKS_IN_CHILD_NUTRITION_PROGRAM, pp.getProgram().getName());
75+
} else if (LampConfig.PROGRAM_PRENATAL_UUID.equals(programUuid)) {
76+
transitionProgramState(pp, program, LampConfig.WORKFLOW_PRENATAL_UUID,
77+
LampConfig.CONCEPT_10_MONTHS_IN_PRENATAL_PROGRAM, pp.getProgram().getName());
78+
}
79+
}
80+
81+
private void transitionProgramState(PatientProgram pp, Program program, String workflowUuid, String conceptUuid,
82+
String programName) {
83+
ProgramWorkflow workflow = Utils.getWorkflowByUuid(program, workflowUuid);
84+
if (workflow == null) {
85+
return;
86+
}
87+
88+
ProgramWorkflowState programWorkflowState = Utils.getStateByConcept(workflow, Context.getConceptService()
89+
.getConceptByUuid(conceptUuid));
90+
if (programWorkflowState == null) {
91+
return;
92+
}
93+
for (PatientState ps : pp.getStates()) {
94+
if (ps.getActive() && ps.getState().getProgramWorkflow().equals(programWorkflowState.getProgramWorkflow())) {
95+
ps.setEndDate(new Date());
96+
}
97+
}
98+
99+
pp.transitionToState(programWorkflowState, new Date());
100+
101+
log.info("Auto-completed " + programName + " program");
102+
}
103+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
3+
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog/1.9"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog/1.9
6+
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-1.9.xsd">
7+
8+
<!--
9+
See http://wiki.openmrs.org/display/docs/Module+liquibase+File for
10+
documentation on this file.
11+
12+
See http://www.liquibase.org/manual/home#available_database_refactorings
13+
for a list of supported elements and attributes
14+
-->
15+
16+
<changeSet id="create-complete-program-task-2025-11-04" author="siddharth">
17+
<preConditions onFail="MARK_RAN">
18+
<sqlCheck expectedResult="0">
19+
SELECT COUNT(*) FROM scheduler_task_config
20+
WHERE schedulable_class = 'org.openmrs.module.lamp.scheduler.CompleteProgramsTask'
21+
And name = 'Complete LAMP Program Task'
22+
</sqlCheck>
23+
</preConditions>
24+
<comment>Inserting CompletePrograms Task into 'schedule_task_config' table</comment>
25+
<insert tableName="scheduler_task_config">
26+
<column name="name" value="Complete LAMP Program Task" />
27+
<column name="description" value="Completes open Programs in Ozone LAMP" />
28+
<column name="schedulable_class" value="org.openmrs.module.lamp.scheduler.CompleteProgramsTask" />
29+
<column name="start_time_pattern" value="MM/dd/yyyy HH:mm:ss" />
30+
<column name="start_time" valueDate="now()" />
31+
<column name="repeat_interval" value="60" />
32+
<column name="date_created" valueDate="CURRENT_TIMESTAMP" />
33+
<column name="created_by" value="1" />
34+
<column name="start_on_startup" value="1"/>
35+
<column name="started" value="0"/>
36+
<column name="uuid" value="e8ed7c5d-80f2-4f77-beeb-d12c5f0badc6" />
37+
</insert>
38+
</changeSet>
39+
</databaseChangeLog>
40+
41+

0 commit comments

Comments
 (0)