Skip to content

Commit

Permalink
HTML-842: Add Appointments Tag
Browse files Browse the repository at this point in the history
  • Loading branch information
mogoodrich committed May 21, 2024
1 parent 6184b0f commit f0840c8
Show file tree
Hide file tree
Showing 9 changed files with 111 additions and 65 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,10 @@ public String[] widgetLabels() {

@Override
public void setupRequest(MockHttpServletRequest request, Map<String, String> widgets) {
request.addParameter(widgets.get("Date:"), dateAsString(date));
request.addParameter(widgets.get("Location:"), "2");
request.addParameter(widgets.get("Provider:"), "502");
request.addParameter(widgets.get("Appointments:"), "05f2ad92-1cc8-4cec-bf54-9cac0200746d");
request.setParameter(widgets.get("Date:"), dateAsString(date));
request.setParameter(widgets.get("Location:"), "2");
request.setParameter(widgets.get("Provider:"), "502");
request.setParameter(widgets.get("Appointments:"), "05f2ad92-1cc8-4cec-bf54-9cac0200746d");
}

@Override
Expand Down Expand Up @@ -103,11 +103,11 @@ public String[] widgetLabels() {

@Override
public void setupRequest(MockHttpServletRequest request, Map<String, String> widgets) {
request.addParameter(widgets.get("Date:"), dateAsString(date));
request.addParameter(widgets.get("Location:"), "2");
request.addParameter(widgets.get("Provider:"), "502");
request.addParameter(widgets.get("Appointments:"), "05f2ad92-1cc8-4cec-bf54-9cac0200746d");
request.addParameter(widgets.get("Appointments:").replace("_1", "_2"),
request.setParameter(widgets.get("Date:"), dateAsString(date));
request.setParameter(widgets.get("Location:"), "2");
request.setParameter(widgets.get("Provider:"), "502");
request.setParameter(widgets.get("Appointments:"), "05f2ad92-1cc8-4cec-bf54-9cac0200746d");
request.setParameter(widgets.get("Appointments:").replace("_1", "_2"),
"75504r42-3ca8-11e3-bf2b-0800271c1111");
}

Expand Down Expand Up @@ -149,10 +149,10 @@ public String[] widgetLabels() {

@Override
public void setupRequest(MockHttpServletRequest request, Map<String, String> widgets) {
request.addParameter(widgets.get("Date:"), dateAsString(date));
request.addParameter(widgets.get("Location:"), "2");
request.addParameter(widgets.get("Provider:"), "502");
request.addParameter(widgets.get("Appointments:"), "05f2ad92-1cc8-4cec-bf54-9cac0200746d");
request.setParameter(widgets.get("Date:"), dateAsString(date));
request.setParameter(widgets.get("Location:"), "2");
request.setParameter(widgets.get("Provider:"), "502");
request.setParameter(widgets.get("Appointments:"), "05f2ad92-1cc8-4cec-bf54-9cac0200746d");
}

@Override
Expand All @@ -166,8 +166,8 @@ public String[] widgetLabelsForEdit() {
}

public void setupEditRequest(MockHttpServletRequest request, Map<String, String> widgets) {
request.addParameter(widgets.get("Appointments:"), "");
request.addParameter(widgets.get("Appointments:").replace("_1", "_2"),
request.setParameter(widgets.get("Appointments:"), "");
request.setParameter(widgets.get("Appointments:").replace("_1", "_2"),
"75504r42-3ca8-11e3-bf2b-0800271c1111");
}

Expand Down Expand Up @@ -203,7 +203,6 @@ public void testEditedResults(SubmissionResults results) {
Appointment appointment1 = Context.getService(AppointmentsService.class)
.getAppointmentByUuid("05f2ad92-1cc8-4cec-bf54-9cac0200746d");
Assert.assertEquals(AppointmentStatus.CheckedIn, appointment1.getStatus()); // Note that we are NOT changing the status back to Scheduled
// TODO debug after testing IRL
Assert.assertEquals(0, appointment1.getFulfillingEncounters().size()); // but encounter should be removed

Appointment appointment2 = Context.getService(AppointmentsService.class)
Expand Down Expand Up @@ -232,10 +231,10 @@ public String[] widgetLabels() {

@Override
public void setupRequest(MockHttpServletRequest request, Map<String, String> widgets) {
request.addParameter(widgets.get("Date:"), dateAsString(date));
request.addParameter(widgets.get("Location:"), "2");
request.addParameter(widgets.get("Provider:"), "502");
request.addParameter(widgets.get("Appointments:"), "05f2ad92-1cc8-4cec-bf54-9cac0200746d");
request.setParameter(widgets.get("Date:"), dateAsString(date));
request.setParameter(widgets.get("Location:"), "2");
request.setParameter(widgets.get("Provider:"), "502");
request.setParameter(widgets.get("Appointments:"), "05f2ad92-1cc8-4cec-bf54-9cac0200746d");
}

@Override
Expand All @@ -250,7 +249,7 @@ public String[] widgetLabelsForEdit() {

public void setupEditRequest(MockHttpServletRequest request, Map<String, String> widgets) {
// upon edit keep the same checked
request.addParameter(widgets.get("Appointments:"), "05f2ad92-1cc8-4cec-bf54-9cac0200746d");
request.setParameter(widgets.get("Appointments:"), "05f2ad92-1cc8-4cec-bf54-9cac0200746d");
}

@Override
Expand Down Expand Up @@ -280,8 +279,3 @@ public void testEditedResults(SubmissionResults results) {
}.run();
}
}

// TODOS: make sure test to remove works
// TODOs: review specs
// TODOs: add to check in form for SL and format/test IRL!
// TODOs ticket separate functionality to pass in appointment to automatically mark as checked in
Original file line number Diff line number Diff line change
Expand Up @@ -146,12 +146,14 @@ public Mode getMode() {
private Integer sequenceNextVal = 1;

/**
* Registers a widget within the Context
* Registers a widget within the Context If a field id is passed in, register with that field id,
* otherwise generate a unique field id using a sequence number and appending 'w' to the front
* (Generally you do not want to pass in a field id, but rather defer to this method to generate one
* for you)
*
* @param widget the widget to register
* @return the field id used to identify this widget in the HTML Form
*/
// TODO update documentation
public String registerWidget(Widget widget, String fieldName) {
if (fieldNames.containsKey(widget))
throw new IllegalArgumentException("This widget is already registered");
Expand All @@ -171,7 +173,9 @@ public String registerWidget(Widget widget, String fieldName) {
return fieldName;
}

// TODO document
/**
* Registeres a widget within the Context, generating a unique field id
*/
public String registerWidget(Widget widget) {
return registerWidget(widget, null);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -652,15 +652,15 @@ public void applyActions() throws BadFormDesignException {
} else {
appointment.setFulfillingEncounters(Collections.singleton(encounter));
}
Context.getService(AppointmentsService.class).validateAndSave(appointment);
Context.getService(AppointmentsService.class).validateAndSave(() -> appointment);
}
}

if (submissionActions.getAppointmentsToDisassociateFromEncounter() != null) {
for (Appointment appointment : submissionActions.getAppointmentsToDisassociateFromEncounter()) {
if (appointment.getFulfillingEncounters() != null) {
appointment.getFulfillingEncounters().remove(encounter);
Context.getService(AppointmentsService.class).validateAndSave(appointment);
Context.getService(AppointmentsService.class).validateAndSave(() -> appointment);
}
}
}
Expand Down Expand Up @@ -1015,7 +1015,7 @@ public void applyActions() throws BadFormDesignException {
exitFromCareProperty.getReasonExitConcept());
}
}

// handle any custom actions (for an example of a custom action, see: https://github.com/PIH/openmrs-module-appointmentschedulingui/commit/e2cda8de1caa8a45d319ae4fbf7714c90c9adb8b)
if (submissionActions.getCustomFormSubmissionActions() != null) {
for (CustomFormSubmissionAction customFormSubmissionAction : submissionActions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2313,6 +2313,19 @@ public static Date clearTimeComponent(Date date) {
return cal.getTime();
}

/**
* @return a SimpleDateFormat object for the current locale, using the global property
*/
public static SimpleDateFormat getDateTimeFormat() {
String df = Context.getAdministrationService().getGlobalProperty(HtmlFormEntryConstants.GP_FORMATTER_DATETIME,
"yyyy-MM-dd, HH:mm:ss");
if (StringUtils.isNotBlank(df)) {
return new SimpleDateFormat(df, Context.getLocale());
} else {
return Context.getDateTimeFormat();
}
}

/**
* @param date the date to check
* @return true if the given date is not at midnight
Expand Down Expand Up @@ -2696,6 +2709,22 @@ public static Provider getProvider(String id) {
return provider;
}

/**
* Given a provider, returns the provider name by first looking at the name of any associated person
* and, if none, falling back to provider.getName()
*
* @param provider
* @return
*/
public static String getProviderName(Provider provider) {
if (provider != null) {
return provider.getPerson() != null
? HtmlFormEntryUtil.getFullNameWithFamilyNameFirst(provider.getPerson().getPersonName())
: provider.getName();
}
return "";
}

/**
* Convenience method to get all the names of this PersonName and concatonating them together with
* family name compoenents first, separated by a comma from given and middle names. If any part of
Expand Down Expand Up @@ -2858,4 +2887,5 @@ private static List<String> getProviderFieldsToSearch(Provider provider) {
}
return ret;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Map;

import org.joda.time.DateTime;
import org.openmrs.Patient;
Expand All @@ -26,26 +27,36 @@ public class AppointmentsElement implements HtmlGeneratorElement, FormSubmission

private List<Appointment> appointments = new ArrayList<>();

public AppointmentsElement(FormEntryContext context) {
private String clazz;

public AppointmentsElement(FormEntryContext context, Map<String, String> parameters) {

if (parameters.get("class") != null) {
clazz = parameters.get("class");
}

Patient patient = context.getExistingPatient();
if (patient != null) {

// first, get all scheduled appointments for this patient
AppointmentSearchRequest request = new AppointmentSearchRequest();
request.setPatientUuid(patient.getUuid());
request.setStartDate(new DateTime().minusYears(1000).toDate()); // TODO hack, we want all appts for patient regardless of start date, but start date is required, this will start to fail in a thousand years
request.setStartDate(new DateTime().minusYears(1000).toDate()); // TODO hack, we want all appts for patient regardless of start date, but the search method always returns null if start date is null; this will start to fail in a thousand years
appointments = Context.getService(AppointmentsService.class).search(request);

appointments.sort(Comparator.comparing(Appointment::getStartDateTime).reversed());
appointments.removeIf(appointment -> appointment.getStatus() != AppointmentStatus.Scheduled

// in VIEW mode, only show appointments linked to encounter; in EDIT mode show those linked to encounter and all scheduled appts
appointments.removeIf(appointment -> (context.getMode() == FormEntryContext.Mode.VIEW
|| appointment.getStatus() != AppointmentStatus.Scheduled)
&& (appointment.getFulfillingEncounters() == null
&& !appointment.getFulfillingEncounters().contains(context.getExistingEncounter())));
|| !appointment.getFulfillingEncounters().contains(context.getExistingEncounter())));
}
}

@Override
public String generateHtml(FormEntryContext context) {
appointmentsWidget = new AppointmentsWidget(appointments, context);
appointmentsWidget = new AppointmentsWidget(appointments, context, clazz);
return appointmentsWidget.generateHtml(context);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ protected List<AttributeDescriptor> createAttributeDescriptors() {
@Override
protected String getSubstitution(FormEntrySession session, FormSubmissionController controllerActions,
Map<String, String> parameters) {
AppointmentsElement element = new AppointmentsElement(session.getContext());
AppointmentsElement element = new AppointmentsElement(session.getContext(), parameters);
session.getSubmissionController().addAction(element);
return element.generateHtml(session.getContext());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.joda.time.DateTime;
import org.openmrs.api.context.Context;
import org.openmrs.module.appointments.model.Appointment;
import org.openmrs.module.htmlformentry.FormEntryContext;
Expand All @@ -20,17 +23,34 @@ public class AppointmentsWidget implements Widget {

private List<CheckboxWidget> checkboxWidgets = new ArrayList<CheckboxWidget>();

public AppointmentsWidget(List<Appointment> appointments, FormEntryContext context) {
private String clazz = null;

public AppointmentsWidget(List<Appointment> appointments, FormEntryContext context, String clazz) {

this.clazz = clazz;

this.appointments = appointments;
String fieldName = context.registerWidget(this);

// TODO: document why we need the special registration for form consistency
// note that we are relying on the register widget to generate a single unique field name,
// and then we are appending _1, _2, _3, etc to that field name to create unique field names for each checkbox
// this is to ensure that this widet consistently increments the field name sequential value once,
// for consistency among page reloads; otherws, if the number of matches appointments changed between, for example,
// when the form was opened and the form was saved, widget names might be inconsistent, wreaking havoc on the form
int i = 1;
for (Appointment appointment : appointments) {

// compare dates so that we can highlight any that match the encounter darw
boolean appointmentDateMatchesEncounterDate = appointment.getStartDateTime() != null
&& new DateTime(appointment.getStartDateTime()).withTimeAtStartOfDay()
.equals(new DateTime(context.getBestApproximationOfEncounterDate()).withTimeAtStartOfDay());

CheckboxWidget checkboxWidget = new CheckboxWidget();
checkboxWidget.setLabel(
dateTimeFormat().format(appointment.getStartDateTime()) + " - " + renderProviderNames(appointment) + " - "
+ (appointment.getLocation() != null ? appointment.getLocation().getName() : ""));
checkboxWidget.setLabel((appointmentDateMatchesEncounterDate ? "<strong>" : "")
+ HtmlFormEntryUtil.getDateTimeFormat().format(appointment.getStartDateTime()) + " - "
+ renderProviderNames(appointment) + " - "
+ (appointment.getLocation() != null ? appointment.getLocation().getName() : "")
+ (appointmentDateMatchesEncounterDate ? "</strong>" : ""));
checkboxWidget.setValue(appointment.getUuid());
if (appointment.getFulfillingEncounters() != null && context.getExistingEncounter() != null
&& appointment.getFulfillingEncounters().contains(context.getExistingEncounter())) {
Expand All @@ -44,18 +64,19 @@ public AppointmentsWidget(List<Appointment> appointments, FormEntryContext conte

@Override
public void setInitialValue(Object initialValue) {
// TODO?
// the constructor takes care of setting the initial value for each checkbox
}

@Override
public String generateHtml(FormEntryContext context) {
if (appointments == null || appointments.isEmpty()) {
return "No appointments found"; // TODO translate, style
return Context.getMessageSourceService().getMessage("appointmentsui.noAppointmentsFound");
}

return checkboxWidgets.stream().map(checkboxWidget -> {
return checkboxWidget.generateHtml(context);
}).collect(Collectors.joining());
}).map((html) -> "<div" + (clazz != null ? " class=\"" + clazz + "\" " : "") + "/>" + html + "</div>")
.collect(Collectors.joining());
}

@Override
Expand All @@ -70,25 +91,12 @@ public Object getValue(FormEntryContext context, HttpServletRequest request) {
return selectedAppointmentUuids;
}

// TODO move to util method?
private SimpleDateFormat dateTimeFormat() {
String df = Context.getAdministrationService().getGlobalProperty(HtmlFormEntryConstants.GP_FORMATTER_DATETIME,
"yyyy-MM-dd, HH:mm:ss");
if (StringUtils.hasText(df)) {
return new SimpleDateFormat(df, Context.getLocale());
} else {
return Context.getDateTimeFormat();
}
}

private String renderProviderNames(Appointment appointment) {
if (appointment.getProviders() != null) {
return appointment.getProviders().stream().map(provider -> {
return provider.getProvider().getPerson() != null ? HtmlFormEntryUtil.getFullNameWithFamilyNameFirst(
provider.getProvider().getPerson().getPersonName()) : provider.getProvider().getName();
return HtmlFormEntryUtil.getProviderName(provider.getProvider());
}).collect(Collectors.joining("; "));
}
return "";

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,7 @@ public String generateHtml(FormEntryContext context) {
sb.append("\n<option ");
if (initialValue != null && initialValue.equals(provider))
sb.append("selected=\"true\" ");
sb.append("value=\"" + provider.getId() + "\">")
.append(provider.getPerson() != null
? HtmlFormEntryUtil.getFullNameWithFamilyNameFirst(provider.getPerson().getPersonName())
: provider.getName())
sb.append("value=\"" + provider.getId() + "\">").append(HtmlFormEntryUtil.getProviderName(provider))
.append("</option>");
}
}
Expand Down
2 changes: 2 additions & 0 deletions api/src/main/resources/messages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -179,3 +179,5 @@ htmlformentry.conditionui.condition.label=Condition
htmlformentry.conditionui.active.label=Active
htmlformentry.conditionui.inactive.label=Inactive
htmlformentry.conditionui.additionalDetail.label=Additional Detail

htmlformentry.appointments.noAppointmentsFound=No appointments found

0 comments on commit f0840c8

Please sign in to comment.