Skip to content

Commit 646c762

Browse files
committed
HTML-715:EnrollInProgramTag to support program attributes from 2.2
1 parent 4425e26 commit 646c762

8 files changed

Lines changed: 539 additions & 0 deletions

File tree

api-2.2/pom.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,14 @@
2222
<artifactId>${project.parent.artifactId}-api</artifactId>
2323
<version>${project.parent.version}</version>
2424
</dependency>
25+
26+
<dependency>
27+
<groupId>org.openmrs.module</groupId>
28+
<artifactId>htmlformentry-api</artifactId>
29+
<version>${project.parent.version}</version>
30+
<classifier>tests</classifier>
31+
<scope>test</scope>
32+
</dependency>
2533

2634
<dependency>
2735
<groupId>org.openmrs.api</groupId>
@@ -35,11 +43,13 @@
3543
<artifactId>openmrs-test</artifactId>
3644
<type>pom</type>
3745
<version>${openMRSVersion}</version>
46+
<scope>test-jar</scope>
3847
</dependency>
3948

4049
<dependency>
4150
<groupId>org.openmrs.web</groupId>
4251
<artifactId>openmrs-web</artifactId>
52+
<version>${openMRSVersion}</version>
4353
</dependency>
4454

4555
<dependency>
Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
package org.openmrs.module.htmlformentry;
2+
3+
import java.util.ArrayList;
4+
import java.util.Date;
5+
import java.util.HashSet;
6+
import java.util.List;
7+
import java.util.ListIterator;
8+
import java.util.Map;
9+
import java.util.Set;
10+
import java.util.Stack;
11+
import java.util.Vector;
12+
13+
import javax.servlet.http.HttpServletRequest;
14+
15+
import org.apache.commons.lang.ObjectUtils;
16+
import org.apache.commons.lang.StringUtils;
17+
import org.openmrs.Encounter;
18+
import org.openmrs.Patient;
19+
import org.openmrs.PatientProgram;
20+
import org.openmrs.PatientProgramAttribute;
21+
import org.openmrs.PatientState;
22+
import org.openmrs.Program;
23+
import org.openmrs.ProgramWorkflowState;
24+
import org.openmrs.module.htmlformentry.FormEntryContext.Mode;
25+
import org.openmrs.module.htmlformentry.element.EnrollInProgramElement;
26+
import org.openmrs.module.htmlformentry.widget.CheckboxWidget;
27+
import org.openmrs.module.htmlformentry.widget.DateWidget;
28+
import org.openmrs.module.htmlformentry.widget.ErrorWidget;
29+
import org.openmrs.util.OpenmrsUtil;
30+
31+
public class EnrollInProgramElement2_2 extends EnrollInProgramElement {
32+
33+
private List<PatientProgramAttribute> patientProgramAttributes;
34+
35+
private List<ProgramWorkflowState> states;
36+
37+
private Program program;
38+
39+
private CheckboxWidget checkToEnrollWidget;
40+
41+
private ErrorWidget checkToEnrollErrorWidget;
42+
43+
private DateWidget dateWidget;
44+
45+
private ErrorWidget dateErrorWidget;
46+
47+
private Stack<Object> stack = new Stack<Object>();
48+
49+
private List<PatientProgram> patientProgramsToUpdate = new Vector<PatientProgram>();
50+
51+
private List<PatientProgram> patientProgramsToCreate = new Vector<PatientProgram>();
52+
53+
public EnrollInProgramElement2_2(FormEntryContext context, Map<String, String> parameters) {
54+
super(context, parameters);
55+
56+
try {
57+
program = HtmlFormEntryUtil.getProgram(parameters.get("programId"));
58+
if (program == null)
59+
throw new FormEntryException("");
60+
} catch (Exception ex) {
61+
throw new IllegalArgumentException("Couldn't find program in: " + parameters);
62+
}
63+
64+
if ("true".equalsIgnoreCase(parameters.get("showDate"))) {
65+
dateWidget = new DateWidget();
66+
dateErrorWidget = new ErrorWidget();
67+
context.registerWidget(dateWidget);
68+
context.registerErrorWidget(dateWidget, dateErrorWidget);
69+
}
70+
71+
if ("true".equalsIgnoreCase(parameters.get("showCheckbox"))) {
72+
checkToEnrollWidget = new CheckboxWidget();
73+
{ // If patient is already enrolled, check and disable the checkbox
74+
Patient patient = context.getExistingPatient();
75+
Date encounterDate = (Date) ObjectUtils.defaultIfNull(context.getPreviousEncounterDate(),
76+
ObjectUtils.defaultIfNull(context.getDefaultEncounterDate(), new Date()));
77+
if (HtmlFormEntryUtil.isEnrolledInProgramOnDate(patient, program, encounterDate)) {
78+
checkToEnrollWidget.setInitialValue("true");
79+
checkToEnrollWidget.setDisabled(true);
80+
}
81+
}
82+
context.registerWidget(checkToEnrollWidget);
83+
checkToEnrollErrorWidget = new ErrorWidget();
84+
context.registerErrorWidget(checkToEnrollWidget, checkToEnrollErrorWidget);
85+
}
86+
87+
String patientProgramAttributeIds = parameters.get("patientProgramAttributes");
88+
if (StringUtils.isNotBlank(patientProgramAttributeIds)) {
89+
patientProgramAttributes = new ArrayList<PatientProgramAttribute>();
90+
String[] patientProgramAttributesIdsUuidsOrNames = patientProgramAttributeIds.split(",");
91+
new HashSet<String>();
92+
93+
for (String value : patientProgramAttributesIdsUuidsOrNames) {
94+
value = value.trim();
95+
PatientProgramAttribute programAttribute = HtmlFormEntryUtil2_2.getPatientProgramAttribute(value,
96+
program);
97+
if (programAttribute == null) {
98+
String errorMsgPart = "with an id or uuid";
99+
if (value.indexOf(":") > -1)
100+
throw new FormEntryException(
101+
"Cannot find a program attribute " + errorMsgPart + " that matches '" + value + "'");
102+
}
103+
104+
if (!patientProgramAttributes.contains(programAttribute))
105+
patientProgramAttributes.add(programAttribute);
106+
}
107+
}
108+
109+
String stateIdsStr = parameters.get("stateIds");
110+
if (StringUtils.isNotBlank(stateIdsStr)) {
111+
states = new ArrayList<ProgramWorkflowState>();
112+
String[] stateIdsUuidsOrPrefNames = stateIdsStr.split(",");
113+
// set to store unique work flow state combinations so as to determine multiple
114+
// states in same work flow
115+
Set<String> workflowsAndStates = new HashSet<String>();
116+
for (String value : stateIdsUuidsOrPrefNames) {
117+
value = value.trim();
118+
ProgramWorkflowState state = HtmlFormEntryUtil.getState(value, program);
119+
if (state == null) {
120+
String errorMsgPart = "with an id or uuid";
121+
if (value.indexOf(":") > -1)
122+
errorMsgPart = "associated to a concept with a concept mapping";
123+
throw new FormEntryException(
124+
"Cannot find a program work flow state " + errorMsgPart + " that matches '" + value + "'");
125+
} else if (!state.getInitial()) {
126+
throw new FormEntryException(
127+
"The program work flow state that matches '" + value + "' is not marked as initial");
128+
} else if (!workflowsAndStates.add(state.getProgramWorkflow().getUuid())) {
129+
throw new FormEntryException("A patient cannot be in multiple states in the same workflow");
130+
}
131+
if (!states.contains(state))
132+
states.add(state);
133+
}
134+
135+
}
136+
}
137+
138+
/**
139+
* @see org.openmrs.module.htmlformentry.action.FormSubmissionControllerAction#handleSubmission(org.openmrs.module.htmlformentry.FormEntrySession,
140+
* javax.servlet.http.HttpServletRequest)
141+
*/
142+
@Override
143+
public void handleSubmission(FormEntrySession session, HttpServletRequest submission) {
144+
// Only enroll if we are not in view mode and either the checkbox is checked or
145+
// it doesn't exist
146+
if (session.getContext().getMode() != Mode.VIEW && (checkToEnrollWidget == null
147+
|| "true".equals(checkToEnrollWidget.getValue(session.getContext(), submission)))) {
148+
Date selectedDate = null;
149+
if (dateWidget != null) {
150+
selectedDate = dateWidget.getValue(session.getContext(), submission);
151+
}
152+
enrollInProgram(program, selectedDate, states, patientProgramAttributes);
153+
}
154+
}
155+
156+
private void enrollInProgram(Program program, Date enrollmentDate, List<ProgramWorkflowState> states,
157+
List<PatientProgramAttribute> patientProgramAttributes) {
158+
if (program == null)
159+
throw new IllegalArgumentException("Cannot enroll in a blank program");
160+
161+
Patient patient = highestOnStack(Patient.class);
162+
if (patient == null)
163+
throw new IllegalArgumentException("Cannot enroll in a program outside of a Patient");
164+
Encounter encounter = highestOnStack(Encounter.class);
165+
166+
// if an enrollment date has not been specified, enrollment date is the
167+
// encounter date
168+
enrollmentDate = (enrollmentDate != null) ? enrollmentDate
169+
: (encounter != null) ? encounter.getEncounterDatetime() : null;
170+
171+
if (enrollmentDate == null)
172+
throw new IllegalArgumentException(
173+
"Cannot enroll in a program without specifying an Encounter Date or Enrollment Date");
174+
175+
// only need to do some if the patient is not enrolled in the specified program
176+
// on the specified date
177+
if (!HtmlFormEntryUtil.isEnrolledInProgramOnDate(patient, program, enrollmentDate)) {
178+
179+
// see if the patient is enrolled in this program in the future
180+
PatientProgram pp = HtmlFormEntryUtil.getClosestFutureProgramEnrollment(patient, program, enrollmentDate);
181+
182+
if (pp != null) {
183+
// set the start dates of all states with a start date equal to the enrollment
184+
// date to the selected date
185+
for (PatientState patientState : pp.getStates()) {
186+
if (OpenmrsUtil.nullSafeEquals(patientState.getStartDate(), pp.getDateEnrolled())) {
187+
patientState.setStartDate(enrollmentDate);
188+
}
189+
}
190+
191+
// set the program enrollment date to the newly selected date
192+
pp.setDateEnrolled(enrollmentDate);
193+
194+
patientProgramsToUpdate.add(pp);
195+
}
196+
// otherwise, create the new program
197+
else {
198+
pp = new PatientProgram();
199+
pp.setPatient(patient);
200+
pp.setProgram(program);
201+
if (enrollmentDate != null)
202+
pp.setDateEnrolled(enrollmentDate);
203+
204+
if (states != null) {
205+
for (ProgramWorkflowState programWorkflowState : states) {
206+
pp.transitionToState(programWorkflowState, enrollmentDate);
207+
}
208+
}
209+
210+
if (patientProgramAttributes != null) {
211+
for (PatientProgramAttribute pogramattribute : patientProgramAttributes) {
212+
pp.setAttribute(pogramattribute);
213+
}
214+
}
215+
216+
patientProgramsToCreate.add(pp);
217+
}
218+
219+
}
220+
}
221+
222+
/**
223+
* Utility method that returns the object of a specified class that was most
224+
* recently added to the stack
225+
*/
226+
@SuppressWarnings("unchecked")
227+
private <T> T highestOnStack(Class<T> clazz) {
228+
for (ListIterator<Object> iter = stack.listIterator(stack.size()); iter.hasPrevious();) {
229+
Object o = iter.previous();
230+
if (clazz.isAssignableFrom(o.getClass()))
231+
return (T) o;
232+
}
233+
return null;
234+
}
235+
236+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package org.openmrs.module.htmlformentry;
2+
3+
import org.apache.commons.logging.Log;
4+
import org.apache.commons.logging.LogFactory;
5+
import org.openmrs.PatientProgramAttribute;
6+
import org.openmrs.Program;
7+
import org.openmrs.api.context.Context;
8+
9+
public class HtmlFormEntryUtil2_2 extends HtmlFormEntryUtil{
10+
public static Log log = LogFactory.getLog(HtmlFormEntryUtil2_2.class);
11+
12+
public static PatientProgramAttribute getProgramAttribute(String identifier) {
13+
PatientProgramAttribute pa = null;
14+
if (identifier != null) {
15+
try {
16+
identifier = identifier.trim();
17+
18+
Integer.valueOf(identifier);
19+
pa = getProgramAttribute(identifier);
20+
21+
if (pa != null) {
22+
return pa;
23+
}
24+
} catch (NumberFormatException e) {
25+
}
26+
27+
if (isValidUuidFormat(identifier)) {
28+
pa = Context.getProgramWorkflowService().getPatientProgramAttributeByUuid(identifier);
29+
30+
if (pa != null) {
31+
return pa;
32+
}
33+
}
34+
}
35+
return null;
36+
37+
}
38+
39+
/**
40+
* Looks up a {@link PatientProgramAttribute} from the specified program by
41+
* patientprogramId,uuid
42+
*
43+
* @param identifier the programWorkflowStateId, uuid or the concept name to
44+
* match against
45+
* @param program
46+
* @return <strong>Should</strong> return the patient program attribute with the
47+
* matching id <strong>Should</strong> return the state with the
48+
* matching uuid <strong>Should</strong>
49+
*/
50+
public static PatientProgramAttribute getPatientProgramAttribute(String identifier, Program program) {
51+
52+
if (identifier == null) {
53+
return null;
54+
}
55+
56+
// try to fetch by id or uuid
57+
PatientProgramAttribute progrmAttribute = getProgramAttribute(identifier);
58+
59+
if (progrmAttribute != null) {
60+
return progrmAttribute;
61+
}
62+
return null;
63+
}
64+
65+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package org.openmrs.module.htmlformentry.handler;
2+
3+
4+
import java.util.ArrayList;
5+
import java.util.Collections;
6+
import java.util.List;
7+
import java.util.Map;
8+
9+
import org.openmrs.Program;
10+
import org.openmrs.ProgramWorkflowState;
11+
import org.openmrs.annotation.OpenmrsProfile;
12+
import org.openmrs.module.htmlformentry.EnrollInProgramElement2_2;
13+
import org.openmrs.module.htmlformentry.FormEntrySession;
14+
import org.openmrs.module.htmlformentry.FormSubmissionController;
15+
16+
@OpenmrsProfile(openmrsPlatformVersion = "2.2.*")
17+
public class EnrollInProgramHandler2_2 extends EnrollInProgramHandler{
18+
19+
@Override
20+
protected List<AttributeDescriptor> createAttributeDescriptors() {
21+
List<AttributeDescriptor> attributeDescriptors = new ArrayList<AttributeDescriptor>();
22+
attributeDescriptors.add(new AttributeDescriptor("programId", Program.class));
23+
attributeDescriptors.add(new AttributeDescriptor("stateIds", ProgramWorkflowState.class));
24+
attributeDescriptors.add(new AttributeDescriptor("patientProgramAttributes", ProgramWorkflowState.class));
25+
return Collections.unmodifiableList(attributeDescriptors);
26+
}
27+
28+
@Override
29+
protected String getSubstitution(FormEntrySession session, FormSubmissionController controllerActions,
30+
Map<String, String> parameters) {
31+
EnrollInProgramElement2_2 element = new EnrollInProgramElement2_2(session.getContext(), parameters);
32+
session.getSubmissionController().addAction(element);
33+
34+
return element.generateHtml(session.getContext());
35+
}
36+
37+
}

0 commit comments

Comments
 (0)