diff --git a/api-2.2/pom.xml b/api-2.2/pom.xml new file mode 100644 index 000000000..088837fa2 --- /dev/null +++ b/api-2.2/pom.xml @@ -0,0 +1,145 @@ + + 4.0.0 + + + org.openmrs.module + htmlformentry + 3.12.0-SNAPSHOT + + + htmlformentry-api-2.2 + jar + HTML Form Entry API 2.2 + 2.2 api project for htmlformentry + + + 2.2.0 + + + + + ${project.parent.groupId} + ${project.parent.artifactId}-api + ${project.parent.version} + tests + + + + ${project.parent.groupId} + ${project.parent.artifactId}-api + ${project.parent.version} + + + + org.openmrs.module + htmlformentry-api-tests + ${project.parent.version} + + + + org.openmrs.api + openmrs-api + ${openMRSVersion} + provided + + + + org.openmrs.api + openmrs-api + ${openMRSVersion} + tests + + + + org.openmrs.test + openmrs-test + pom + ${openMRSVersion} + + + + org.openmrs.web + openmrs-web + ${openMRSVersion} + + + + + org.codehaus.groovy + groovy + + + cglib + cglib + + + xalan + xalan + + + joda-time + joda-time + + + org.joda + joda-convert + + + org.codehaus.jackson + jackson-mapper-asl + + + org.codehaus.jackson + jackson-core-asl + + + + javax.servlet + javax.servlet-api + 3.0.1 + test + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 1.8 + 1.8 + + + + + + + src/main/resources + true + + + + + + src/test/resources + + **/*.properties + **/*.xml + + true + + + src/test/resources + + **/*.properties + **/*.xml + + false + + + + + diff --git a/api-2.2/src/main/java/org/openmrs/module/htmlformentry/ConditionElement.java b/api-2.2/src/main/java/org/openmrs/module/htmlformentry/ConditionElement.java index 0f005e1ba..deda2b8bf 100644 --- a/api-2.2/src/main/java/org/openmrs/module/htmlformentry/ConditionElement.java +++ b/api-2.2/src/main/java/org/openmrs/module/htmlformentry/ConditionElement.java @@ -268,5 +268,4 @@ public void setConditionStatusesWidget(RadioButtonsWidget conditionStatusesWidge public void setRequired(boolean required) { this.required = required; } - } diff --git a/api-2.2/src/main/java/org/openmrs/module/htmlformentry/EnrollInProgramElement2_2.java b/api-2.2/src/main/java/org/openmrs/module/htmlformentry/EnrollInProgramElement2_2.java new file mode 100644 index 000000000..7d61498c6 --- /dev/null +++ b/api-2.2/src/main/java/org/openmrs/module/htmlformentry/EnrollInProgramElement2_2.java @@ -0,0 +1,236 @@ +package org.openmrs.module.htmlformentry; + +import java.util.ArrayList; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Set; +import java.util.Stack; +import java.util.Vector; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.commons.lang.ObjectUtils; +import org.apache.commons.lang.StringUtils; +import org.openmrs.Encounter; +import org.openmrs.Patient; +import org.openmrs.PatientProgram; +import org.openmrs.PatientProgramAttribute; +import org.openmrs.PatientState; +import org.openmrs.Program; +import org.openmrs.ProgramWorkflowState; +import org.openmrs.module.htmlformentry.FormEntryContext.Mode; +import org.openmrs.module.htmlformentry.element.EnrollInProgramElement; +import org.openmrs.module.htmlformentry.widget.CheckboxWidget; +import org.openmrs.module.htmlformentry.widget.DateWidget; +import org.openmrs.module.htmlformentry.widget.ErrorWidget; +import org.openmrs.util.OpenmrsUtil; + +public class EnrollInProgramElement2_2 extends EnrollInProgramElement { + + private List patientProgramAttributes; + + private List states; + + private Program program; + + private CheckboxWidget checkToEnrollWidget; + + private ErrorWidget checkToEnrollErrorWidget; + + private DateWidget dateWidget; + + private ErrorWidget dateErrorWidget; + + private Stack stack = new Stack(); + + private List patientProgramsToUpdate = new Vector(); + + private List patientProgramsToCreate = new Vector(); + + public EnrollInProgramElement2_2(FormEntryContext context, Map parameters) { + super(context, parameters); + + try { + program = HtmlFormEntryUtil.getProgram(parameters.get("programId")); + if (program == null) + throw new FormEntryException(""); + } + catch (Exception ex) { + throw new IllegalArgumentException("Couldn't find program in: " + parameters); + } + + if ("true".equalsIgnoreCase(parameters.get("showDate"))) { + dateWidget = new DateWidget(); + dateErrorWidget = new ErrorWidget(); + context.registerWidget(dateWidget); + context.registerErrorWidget(dateWidget, dateErrorWidget); + } + + if ("true".equalsIgnoreCase(parameters.get("showCheckbox"))) { + checkToEnrollWidget = new CheckboxWidget(); + { // If patient is already enrolled, check and disable the checkbox + Patient patient = context.getExistingPatient(); + Date encounterDate = (Date) ObjectUtils.defaultIfNull(context.getPreviousEncounterDate(), + ObjectUtils.defaultIfNull(context.getDefaultEncounterDate(), new Date())); + if (HtmlFormEntryUtil.isEnrolledInProgramOnDate(patient, program, encounterDate)) { + checkToEnrollWidget.setInitialValue("true"); + checkToEnrollWidget.setDisabled(true); + } + } + context.registerWidget(checkToEnrollWidget); + checkToEnrollErrorWidget = new ErrorWidget(); + context.registerErrorWidget(checkToEnrollWidget, checkToEnrollErrorWidget); + } + + String patientProgramAttributeIds = parameters.get("patientProgramAttributes"); + if (StringUtils.isNotBlank(patientProgramAttributeIds)) { + patientProgramAttributes = new ArrayList(); + String[] patientProgramAttributesIdsUuidsOrNames = patientProgramAttributeIds.split(","); + new HashSet(); + + for (String value : patientProgramAttributesIdsUuidsOrNames) { + value = value.trim(); + PatientProgramAttribute programAttribute = HtmlFormEntryUtil2_2.getPatientProgramAttribute(value, program); + if (programAttribute == null) { + String errorMsgPart = "with an id or uuid"; + if (value.indexOf(":") > -1) + throw new FormEntryException( + "Cannot find a program attribute " + errorMsgPart + " that matches '" + value + "'"); + } + + if (!patientProgramAttributes.contains(programAttribute)) + patientProgramAttributes.add(programAttribute); + } + } + + String stateIdsStr = parameters.get("stateIds"); + if (StringUtils.isNotBlank(stateIdsStr)) { + states = new ArrayList(); + String[] stateIdsUuidsOrPrefNames = stateIdsStr.split(","); + // set to store unique work flow state combinations so as to determine multiple + // states in same work flow + Set workflowsAndStates = new HashSet(); + for (String value : stateIdsUuidsOrPrefNames) { + value = value.trim(); + ProgramWorkflowState state = HtmlFormEntryUtil.getState(value, program); + if (state == null) { + String errorMsgPart = "with an id or uuid"; + if (value.indexOf(":") > -1) + errorMsgPart = "associated to a concept with a concept mapping"; + throw new FormEntryException( + "Cannot find a program work flow state " + errorMsgPart + " that matches '" + value + "'"); + } else if (!state.getInitial()) { + throw new FormEntryException( + "The program work flow state that matches '" + value + "' is not marked as initial"); + } else if (!workflowsAndStates.add(state.getProgramWorkflow().getUuid())) { + throw new FormEntryException("A patient cannot be in multiple states in the same workflow"); + } + if (!states.contains(state)) + states.add(state); + } + + } + } + + /** + * @see org.openmrs.module.htmlformentry.action.FormSubmissionControllerAction#handleSubmission(org.openmrs.module.htmlformentry.FormEntrySession, + * javax.servlet.http.HttpServletRequest) + */ + @Override + public void handleSubmission(FormEntrySession session, HttpServletRequest submission) { + // Only enroll if we are not in view mode and either the checkbox is checked or + // it doesn't exist + if (session.getContext().getMode() != Mode.VIEW && (checkToEnrollWidget == null + || "true".equals(checkToEnrollWidget.getValue(session.getContext(), submission)))) { + Date selectedDate = null; + if (dateWidget != null) { + selectedDate = dateWidget.getValue(session.getContext(), submission); + } + enrollInProgram(program, selectedDate, states, patientProgramAttributes); + } + } + + private void enrollInProgram(Program program, Date enrollmentDate, List states, + List patientProgramAttributes) { + if (program == null) + throw new IllegalArgumentException("Cannot enroll in a blank program"); + + Patient patient = highestOnStack(Patient.class); + if (patient == null) + throw new IllegalArgumentException("Cannot enroll in a program outside of a Patient"); + Encounter encounter = highestOnStack(Encounter.class); + + // if an enrollment date has not been specified, enrollment date is the + // encounter date + enrollmentDate = (enrollmentDate != null) ? enrollmentDate + : (encounter != null) ? encounter.getEncounterDatetime() : null; + + if (enrollmentDate == null) + throw new IllegalArgumentException( + "Cannot enroll in a program without specifying an Encounter Date or Enrollment Date"); + + // only need to do some if the patient is not enrolled in the specified program + // on the specified date + if (!HtmlFormEntryUtil.isEnrolledInProgramOnDate(patient, program, enrollmentDate)) { + + // see if the patient is enrolled in this program in the future + PatientProgram pp = HtmlFormEntryUtil.getClosestFutureProgramEnrollment(patient, program, enrollmentDate); + + if (pp != null) { + // set the start dates of all states with a start date equal to the enrollment + // date to the selected date + for (PatientState patientState : pp.getStates()) { + if (OpenmrsUtil.nullSafeEquals(patientState.getStartDate(), pp.getDateEnrolled())) { + patientState.setStartDate(enrollmentDate); + } + } + + // set the program enrollment date to the newly selected date + pp.setDateEnrolled(enrollmentDate); + + patientProgramsToUpdate.add(pp); + } + // otherwise, create the new program + else { + pp = new PatientProgram(); + pp.setPatient(patient); + pp.setProgram(program); + if (enrollmentDate != null) + pp.setDateEnrolled(enrollmentDate); + + if (states != null) { + for (ProgramWorkflowState programWorkflowState : states) { + pp.transitionToState(programWorkflowState, enrollmentDate); + } + } + + if (patientProgramAttributes != null) { + for (PatientProgramAttribute pogramattribute : patientProgramAttributes) { + pp.setAttribute(pogramattribute); + } + } + + patientProgramsToCreate.add(pp); + } + + } + } + + /** + * Utility method that returns the object of a specified class that was most recently added to the + * stack + */ + @SuppressWarnings("unchecked") + private T highestOnStack(Class clazz) { + for (ListIterator iter = stack.listIterator(stack.size()); iter.hasPrevious();) { + Object o = iter.previous(); + if (clazz.isAssignableFrom(o.getClass())) + return (T) o; + } + return null; + } + +} diff --git a/api-2.2/src/main/java/org/openmrs/module/htmlformentry/HtmlFormEntryUtil2_2.java b/api-2.2/src/main/java/org/openmrs/module/htmlformentry/HtmlFormEntryUtil2_2.java new file mode 100644 index 000000000..91cef6356 --- /dev/null +++ b/api-2.2/src/main/java/org/openmrs/module/htmlformentry/HtmlFormEntryUtil2_2.java @@ -0,0 +1,63 @@ +package org.openmrs.module.htmlformentry; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.openmrs.PatientProgramAttribute; +import org.openmrs.Program; +import org.openmrs.api.context.Context; + +public class HtmlFormEntryUtil2_2 extends HtmlFormEntryUtil { + + public static Log log = LogFactory.getLog(HtmlFormEntryUtil2_2.class); + + public static PatientProgramAttribute getProgramAttribute(String identifier) { + PatientProgramAttribute pa = null; + if (identifier != null) { + try { + identifier = identifier.trim(); + + Integer.valueOf(identifier); + pa = getProgramAttribute(identifier); + + if (pa != null) { + return pa; + } + } + catch (NumberFormatException e) {} + + if (isValidUuidFormat(identifier)) { + pa = Context.getProgramWorkflowService().getPatientProgramAttributeByUuid(identifier); + + if (pa != null) { + return pa; + } + } + } + return null; + + } + + /** + * Looks up a {@link PatientProgramAttribute} from the specified program by patientprogramId,uuid + * + * @param identifier the programWorkflowStateId, uuid or the concept name to match against + * @param program + * @return Should return the patient program attribute with the matching id + * Should return the state with the matching uuid Should + */ + public static PatientProgramAttribute getPatientProgramAttribute(String identifier, Program program) { + + if (identifier == null) { + return null; + } + + // try to fetch by id or uuid + PatientProgramAttribute progrmAttribute = getProgramAttribute(identifier); + + if (progrmAttribute != null) { + return progrmAttribute; + } + return null; + } + +} diff --git a/api-2.2/src/main/java/org/openmrs/module/htmlformentry/handler/EnrollInProgramHandler2_2.java b/api-2.2/src/main/java/org/openmrs/module/htmlformentry/handler/EnrollInProgramHandler2_2.java new file mode 100644 index 000000000..85529f03e --- /dev/null +++ b/api-2.2/src/main/java/org/openmrs/module/htmlformentry/handler/EnrollInProgramHandler2_2.java @@ -0,0 +1,36 @@ +package org.openmrs.module.htmlformentry.handler; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.openmrs.Program; +import org.openmrs.ProgramWorkflowState; +import org.openmrs.annotation.OpenmrsProfile; +import org.openmrs.module.htmlformentry.EnrollInProgramElement2_2; +import org.openmrs.module.htmlformentry.FormEntrySession; +import org.openmrs.module.htmlformentry.FormSubmissionController; + +@OpenmrsProfile(openmrsPlatformVersion = "2.2.0") +public class EnrollInProgramHandler2_2 extends EnrollInProgramHandler { + + @Override + protected List createAttributeDescriptors() { + List attributeDescriptors = new ArrayList(); + attributeDescriptors.add(new AttributeDescriptor("programId", Program.class)); + attributeDescriptors.add(new AttributeDescriptor("stateIds", ProgramWorkflowState.class)); + attributeDescriptors.add(new AttributeDescriptor("patientProgramAttributes", ProgramWorkflowState.class)); + return Collections.unmodifiableList(attributeDescriptors); + } + + @Override + protected String getSubstitution(FormEntrySession session, FormSubmissionController controllerActions, + Map parameters) { + EnrollInProgramElement2_2 element = new EnrollInProgramElement2_2(session.getContext(), parameters); + session.getSubmissionController().addAction(element); + + return element.generateHtml(session.getContext()); + } + +} diff --git a/api-2.2/src/test/java/org/openmrs/htmlformentry/element/EnrollInProgramElement2_2Test.java b/api-2.2/src/test/java/org/openmrs/htmlformentry/element/EnrollInProgramElement2_2Test.java new file mode 100644 index 000000000..ea93c8e87 --- /dev/null +++ b/api-2.2/src/test/java/org/openmrs/htmlformentry/element/EnrollInProgramElement2_2Test.java @@ -0,0 +1,118 @@ +package org.openmrs.htmlformentry.element; + +import static org.junit.Assert.assertEquals; + +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.log4j.Level; +import org.apache.log4j.Logger; +import org.junit.Before; +import org.junit.Test; +import org.openmrs.Patient; +import org.openmrs.PatientProgram; +import org.openmrs.PatientProgramAttribute; +import org.openmrs.Program; +import org.openmrs.ProgramWorkflow; +import org.openmrs.api.PatientService; +import org.openmrs.api.ProgramWorkflowService; +import org.openmrs.api.context.Context; +import org.openmrs.api.db.hibernate.AuditableInterceptor; +import org.openmrs.module.htmlformentry.RegressionTestHelper; +import org.openmrs.test.BaseModuleContextSensitiveTest; +import org.springframework.mock.web.MockHttpServletRequest; + +import junit.framework.Assert; + +public class EnrollInProgramElement2_2Test extends BaseModuleContextSensitiveTest { + + protected static final String XML_REGRESSION_TEST_DATASET = "org/openmrs/module/htmlformentry/include/RegressionTest-data-openmrs-2.2.xml"; + + PatientService ps; + + ProgramWorkflowService pws; + + @Before + public void loadConcepts() throws Exception { + ps = Context.getPatientService(); + pws = Context.getProgramWorkflowService(); + executeDataSet(XML_REGRESSION_TEST_DATASET); + } + + @Test + public void enrollInProgram_shouldEnrollAPatietntWhenPatientProgramAttributeIsSetByUuid() throws Exception { + + // This is a temporary workaround for TRUNK-5491. + Logger.getLogger(AuditableInterceptor.class).setLevel(Level.INFO); + + final Integer patientId = 2; + final Integer programId = 10; + final Patient patient = Context.getPatientService().getPatient(patientId); + final PatientProgramAttribute ppa = Context.getProgramWorkflowService() + .getPatientProgramAttributeByUuid("9de7ed10-97ad-11e1-8cb6-00248150a7eb"); + // sanity check + assertEquals(0, + pws.getPatientPrograms(ps.getPatient(patientId), pws.getProgram(programId), null, null, null, null, false) + .size()); + final Date encounterDate = new Date(); + + // enroll the patient in a test program. + PatientProgram pp = new PatientProgram(); + pp.setPatient(patient); + pp.setAttribute(ppa); + Program program = pws.getProgram(programId); + pp.setProgram(pws.getProgram(programId)); + final ProgramWorkflow wf = program.getWorkflow(100); + final Date initialEnrollmentDate = new Date(); + pp.setDateEnrolled(initialEnrollmentDate); + pp.transitionToState(wf.getState(200), initialEnrollmentDate); + pws.savePatientProgram(pp); + pp.getCurrentState(wf).getState(); + pp.getId(); + pp.getDateCompleted(); + + new RegressionTestHelper() { + + @Override + public String getFormName() { + return "enrollPatientInProgramSimpleFormWithPatientProgramAttribute"; + } + + @Override + public Patient getPatient() { + return ps.getPatient(patientId); + } + + @Override + public String[] widgetLabels() { + return new String[] { "Encounter Date:", "Encounter Location:", "Encounter Provider:" }; + } + + @Override + public void setupRequest(MockHttpServletRequest request, Map widgets) { + request.setParameter(widgets.get("Encounter Date:"), dateAsString(encounterDate)); + request.setParameter(widgets.get("Encounter Location:"), "2"); + request.setParameter(widgets.get("Encounter Provider:"), "502"); + } + + @Override + public void testResults(SubmissionResults results) { + results.assertNoErrors(); + results.assertEncounterCreated(); + List pps = pws.getPatientPrograms(ps.getPatient(patientId), pws.getProgram(programId), null, + null, null, null, false); + assertEquals(1, pps.size()); + + // make sure the patient program attribute has been set + Set attributes = pps.get(0).getAttributes(); + Assert.assertEquals(1, attributes.size()); + Assert.assertEquals(attributes.toArray()[0], Context.getProgramWorkflowService() + .getPatientProgramAttributeByUuid("9de7ed10-97ad-11e1-8cb6-00248150a7eb")); + + } + }.run(); + } + +} diff --git a/api-2.3/src/test/java/org/openmrs/htmlformentry/element/ConditionTagTest.java b/api-2.3/src/test/java/org/openmrs/htmlformentry/element/ConditionTagTest.java index 01aa64868..b20932331 100644 --- a/api-2.3/src/test/java/org/openmrs/htmlformentry/element/ConditionTagTest.java +++ b/api-2.3/src/test/java/org/openmrs/htmlformentry/element/ConditionTagTest.java @@ -79,7 +79,7 @@ public void testResults(SubmissionResults results) { results.assertNoErrors(); Assert.assertEquals(2, conditions.length); - + Condition currentCondition = conditions[0]; Assert.assertEquals(ConditionClinicalStatus.ACTIVE, currentCondition.getClinicalStatus()); Assert.assertEquals(expectedCondition, currentCondition.getCondition().getCoded()); diff --git a/api/src/test/resources/org/openmrs/module/htmlformentry/include/RegressionTest-data-openmrs-2.2.xml b/api/src/test/resources/org/openmrs/module/htmlformentry/include/RegressionTest-data-openmrs-2.2.xml new file mode 100644 index 000000000..e016f41d3 --- /dev/null +++ b/api/src/test/resources/org/openmrs/module/htmlformentry/include/RegressionTest-data-openmrs-2.2.xml @@ -0,0 +1,316 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/api/src/test/resources/org/openmrs/module/htmlformentry/include/enrollPatientInProgramSimpleFormWithPatientProgramAttribute.xml b/api/src/test/resources/org/openmrs/module/htmlformentry/include/enrollPatientInProgramSimpleFormWithPatientProgramAttribute.xml new file mode 100644 index 000000000..1ba78379c --- /dev/null +++ b/api/src/test/resources/org/openmrs/module/htmlformentry/include/enrollPatientInProgramSimpleFormWithPatientProgramAttribute.xml @@ -0,0 +1,10 @@ + + + Encounter Date: + Encounter Location: + Encounter Provider: + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index e61fc9c50..a814fc593 100644 --- a/pom.xml +++ b/pom.xml @@ -203,6 +203,7 @@ api-1.9 api-1.10 api-2.0 + api-2.2 api-2.3 api-tests omod