// Copyright 2015 The Project Buendia Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy
// of the License at: http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software distrib-
// uted under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
// OR CONDITIONS OF ANY KIND, either express or implied. See the License for
// specific language governing permissions and limitations under the License.
package org.projectbuendia.client.ui.chart;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Point;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import com.google.common.base.Optional;
import org.joda.time.DateTime;
import org.joda.time.Interval;
import org.joda.time.LocalDate;
import org.odk.collect.android.model.Preset;
import org.projectbuendia.client.App;
import org.projectbuendia.client.R;
import org.projectbuendia.client.events.CrudEventBus;
import org.projectbuendia.client.events.FetchXformFailedEvent;
import org.projectbuendia.client.events.FetchXformSucceededEvent;
import org.projectbuendia.client.events.SubmitXformFailedEvent;
import org.projectbuendia.client.events.SubmitXformSucceededEvent;
import org.projectbuendia.client.events.actions.OrderDeleteRequestedEvent;
import org.projectbuendia.client.events.actions.OrderExecutionSaveRequestedEvent;
import org.projectbuendia.client.events.actions.OrderSaveRequestedEvent;
import org.projectbuendia.client.events.actions.VoidObservationsRequestEvent;
import org.projectbuendia.client.events.data.AppLocationTreeFetchedEvent;
import org.projectbuendia.client.events.data.EncounterAddFailedEvent;
import org.projectbuendia.client.events.data.ItemDeletedEvent;
import org.projectbuendia.client.events.data.ItemFetchedEvent;
import org.projectbuendia.client.events.data.PatientUpdateFailedEvent;
import org.projectbuendia.client.events.sync.SyncSucceededEvent;
import org.projectbuendia.client.json.JsonUser;
import org.projectbuendia.client.models.AppModel;
import org.projectbuendia.client.models.Chart;
import org.projectbuendia.client.models.ConceptUuids;
import org.projectbuendia.client.models.Encounter;
import org.projectbuendia.client.models.Encounter.Observation;
import org.projectbuendia.client.models.LocationTree;
import org.projectbuendia.client.models.Obs;
import org.projectbuendia.client.models.ObsRow;
import org.projectbuendia.client.models.Order;
import org.projectbuendia.client.models.Patient;
import org.projectbuendia.client.models.PatientDelta;
import org.projectbuendia.client.models.VoidObs;
import org.projectbuendia.client.sync.ChartDataHelper;
import org.projectbuendia.client.sync.SyncManager;
import org.projectbuendia.client.ui.dialogs.AssignLocationDialog;
import org.projectbuendia.client.utils.EventBusRegistrationInterface;
import org.projectbuendia.client.utils.LocaleSelector;
import org.projectbuendia.client.utils.Logger;
import org.projectbuendia.client.utils.Utils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
/** Controller for {@link PatientChartActivity}. */
final class PatientChartController implements ChartRenderer.GridJsInterface {
private static final Logger LOG = Logger.create();
private static final boolean DEBUG = true;
private static final String KEY_PENDING_UUIDS = "pendingUuids";
// Form UUIDs specific to Ebola deployments.
static final String OBSERVATION_FORM_UUID = "buendia-form-clinical_observation";
static final String EBOLA_LAB_TEST_FORM_UUID = "buendia-form-ebola_lab_test";
/**
* Period between observation syncs while the chart view is active. It would be nice for
* this to be even shorter (20 s? 10 s?) but currently the table scroll position resets on
* each sync, if any data has changed.
* TODO: Try reducing this period to improve responsiveness, but be wary of the table scrolling
* whenever data is refreshed.
*/
private static final int OBSERVATION_SYNC_PERIOD_MILLIS = 60000;
// TODO: Get rid of mPatientUuids, mNextIndex etc. now that we have mFormRequests.
/** Maximum concurrent ODK forms assigned request codes. */
private static final int MAX_ODK_REQUESTS = 10;
// The ODK code for filling in a form has no way of attaching metadata to it.
// This means we can't pass which patient is currently being edited. Instead, we keep an array
// of up to MAX_ODK_REQUESTS patientUuids. The array is persisted through activity restart in
// the savedInstanceState.
// TODO: Use a map for this instead of an array.
private final String[] mPatientUuids;
private int mNextIndex = 0;
private Patient mPatient = Patient.builder().build();
private LocationTree mLocationTree;
private String mPatientUuid = "";
private Map<String, Order> mOrdersByUuid;
private List<Obs> mObservations;
// This value is incremented whenever the controller is activated or suspended.
// A "phase" is a period of time between such transition points.
private int mCurrentPhaseId = 0;
private final EventBusRegistrationInterface mDefaultEventBus;
private final CrudEventBus mCrudEventBus;
private final OdkResultSender mOdkResultSender;
private final Ui mUi;
private final ChartDataHelper mChartHelper;
private final AppModel mAppModel;
private final EventSubscriber mEventBusSubscriber = new EventSubscriber();
private final SyncManager mSyncManager;
private final MinimalHandler mMainThreadHandler;
private AssignLocationDialog mAssignLocationDialog;
private AssignGeneralConditionDialog mAssignGeneralConditionDialog;
private List<Chart> mCharts;
private int lastChartIndex = 0;
// Every form request made by this controller is kept in this list until
// the form is closed.
List<FormRequest> mFormRequests = new ArrayList<>();
// Store chart's last scroll position
private Point mLastScrollPosition;
public Point getLastScrollPosition() {
return mLastScrollPosition;
}
public interface Ui {
/** Sets the activity title. */
void setTitle(String title);
/** Updates the UI showing the admission date and first symptoms date for this patient. */
void updateAdmissionDateAndFirstSymptomsDateUi(
LocalDate admissionDate,
LocalDate firstSymptomsDate);
/** Updates the UI showing Ebola PCR lab test results for this patient. */
void updateEbolaPcrTestResultUi(Map<String, Obs> observations);
/** Updates the UI showing the pregnancy status and IV status for this patient. */
void updatePregnancyAndIvStatusUi(Map<String, Obs> observations);
/** Updates the general condition UI with the patient's current condition. */
void updatePatientConditionUi(String generalConditionUuid);
/** Updates the UI with the patient's location. */
void updatePatientLocationUi(LocationTree locationTree, Patient patient);
/** Updates the UI showing the history of observations and orders for this patient. */
void updateTilesAndGrid(
Chart chart,
Map<String, Obs> latestObservations,
List<Obs> observations,
List<Order> orders,
LocalDate admissionDate,
LocalDate firstSymptomsDate);
/** Updates the UI with the patient's personal details (name, gender, etc.). */
void updatePatientDetailsUi(Patient patient);
/** Displays an error message with the given resource id. */
void showError(int errorMessageResource);
/** Displays an error with the given resource and optional substitution args. */
void showError(int errorResource, Object... args);
/** Starts a new form activity to collect observations from the user. */
void fetchAndShowXform(
int requestCode, String formUuid, org.odk.collect.android.model.Patient patient,
Preset preset);
void reEnableFetch();
void showFormLoadingDialog(boolean show);
void showFormSubmissionDialog(boolean show);
void showOrderDialog(String patientUuid, Order order);
void showOrderExecutionDialog(Order order, Interval
interval, List<DateTime> executionTimes);
void showEditPatientDialog(Patient patient);
void showObservationsDialog(ArrayList<ObsRow> obs);
}
/** Sends ODK form data. */
public interface OdkResultSender {
void sendOdkResultToServer(
@Nullable String patientUuid,
int resultCode,
Intent data);
}
public interface MinimalHandler {
void post(Runnable runnable);
}
public PatientChartController(
AppModel appModel,
EventBusRegistrationInterface defaultEventBus,
CrudEventBus crudEventBus,
Ui ui,
String patientUuid,
OdkResultSender odkResultSender,
ChartDataHelper chartHelper,
@Nullable Bundle savedState,
SyncManager syncManager,
MinimalHandler mainThreadHandler) {
mAppModel = appModel;
mDefaultEventBus = defaultEventBus;
mCrudEventBus = crudEventBus;
mUi = ui;
mPatientUuid = patientUuid;
mOdkResultSender = odkResultSender;
mChartHelper = chartHelper;
if (savedState != null) {
mPatientUuids = savedState.getStringArray(KEY_PENDING_UUIDS);
} else {
mPatientUuids = new String[MAX_ODK_REQUESTS];
}
mSyncManager = syncManager;
mMainThreadHandler = mainThreadHandler;
mLastScrollPosition = new Point(Integer.MAX_VALUE, 0);
mCharts = mChartHelper.getCharts(AppModel.CHART_UUID);
}
/**
* Returns the state of the controller. This should be saved to preserve it over activity
* restarts.
*/
public Bundle getState() {
Bundle bundle = new Bundle();
bundle.putStringArray(KEY_PENDING_UUIDS, mPatientUuids);
return bundle;
}
/**
* Initializes the controller, setting async operations going to collect data required by the
* UI.
*/
public void init() {
mCurrentPhaseId++; // phase ID changes on every init() or suspend()
mDefaultEventBus.register(mEventBusSubscriber);
mCrudEventBus.register(mEventBusSubscriber);
mAppModel.fetchSinglePatient(mCrudEventBus, mPatientUuid);
mAppModel.fetchLocationTree(mCrudEventBus, LocaleSelector.getCurrentLocale().toString());
startObservationSync();
}
/** Starts syncing observations more frequently while the user is viewing the chart. */
private void startObservationSync() {
final Handler handler = new Handler(Looper.getMainLooper());
final int phaseId = mCurrentPhaseId;
Runnable runnable = new Runnable() {
@Override public void run() {
// This runnable triggers itself in a cycle, each run calling postDelayed()
// to schedule the next run. Each such cycle belongs to a phase, identified
// by phaseId; once the current phase is exited the cycle stops. Thus, when the
// controller is suspended the cycle stops; and also since mCurrentPhaseId can
// only have one value, only one such cycle can be active at any given time.
if (mCurrentPhaseId == phaseId) {
mSyncManager.startObservationsAndOrdersSync();
handler.postDelayed(this, OBSERVATION_SYNC_PERIOD_MILLIS);
}
}
};
handler.postDelayed(runnable, 0);
}
/** Releases any resources used by the controller. */
public void suspend() {
mCurrentPhaseId++; // phase ID changes on every init() or suspend()
mCrudEventBus.unregister(mEventBusSubscriber);
mDefaultEventBus.unregister(mEventBusSubscriber);
if (mLocationTree != null) {
mLocationTree.close();
}
}
public void onXFormResult(int requestCode, int resultCode, Intent data) {
FormRequest request = popFormRequest(requestCode);
if (request == null) {
LOG.e("Unknown form request code: " + requestCode);
return;
}
boolean shouldShowSubmissionDialog = (resultCode != Activity.RESULT_CANCELED);
String action = (resultCode == Activity.RESULT_CANCELED)
? "form_discard_pressed" : "form_save_pressed";
Utils.logUserAction(action,
"form", request.formUuid,
"patient_uuid", request.patientUuid);
mOdkResultSender.sendOdkResultToServer(request.patientUuid, resultCode, data);
mUi.showFormSubmissionDialog(shouldShowSubmissionDialog);
}
FormRequest popFormRequest(int requestIndex) {
FormRequest request = mFormRequests.get(requestIndex);
mFormRequests.set(requestIndex, null);
return request;
}
/** Call when the user has indicated they want to add observation data. */
public void onAddObservationPressed() {
onAddObservationPressed(null);
}
/**
* Call when the user has indicated they want to add observation data.
* @param targetGroup the description of the corresponding group in the XForm. This corresponds
* with the "description" field in OpenMRS.
*/
public void onAddObservationPressed(String targetGroup) {
// Don't acknowledge this action if a dialog is showing
if (dialogShowing()) return;
Preset preset = new Preset();
preset.locationName = "Triage"; // TODO/i18n: Several occurrences of "Triage" in this file.
JsonUser user = App.getUserManager().getActiveUser();
Utils.logUserAction("form_opener_pressed",
"form", "round",
"group", targetGroup);
if (user != null) {
preset.clinicianName = user.fullName;
}
Map<String, Obs> observations =
mChartHelper.getLatestObservations(mPatientUuid);
if (observations.containsKey(ConceptUuids.PREGNANCY_UUID)
&& ConceptUuids.YES_UUID.equals(observations.get(ConceptUuids.PREGNANCY_UUID).value)) {
preset.pregnant = Preset.YES;
}
if (observations.containsKey(ConceptUuids.IV_UUID)
&& ConceptUuids.YES_UUID.equals(observations.get(ConceptUuids.IV_UUID).value)) {
preset.ivFitted = Preset.YES;
}
preset.targetGroup = targetGroup;
mUi.showFormLoadingDialog(true);
FormRequest request = newFormRequest(OBSERVATION_FORM_UUID, mPatientUuid);
mUi.fetchAndShowXform(
request.requestIndex, request.formUuid,
mPatient.toOdkPatient(), preset);
}
public void onEditPatientPressed() {
Utils.logUserAction("edit_patient_pressed", "uuid", mPatientUuid);
mUi.showEditPatientDialog(mPatient);
}
private boolean dialogShowing() {
return (mAssignGeneralConditionDialog != null && mAssignGeneralConditionDialog.isShowing())
|| (mAssignLocationDialog != null && mAssignLocationDialog.isShowing());
}
FormRequest newFormRequest(String formUuid, String patientUuid) {
// Find an empty slot in the array of all existing form requests.
int requestIndex = 0;
while (requestIndex < mFormRequests.size() && mFormRequests.get(requestIndex) != null) {
requestIndex++;
}
if (requestIndex >= mFormRequests.size()) {
mFormRequests.add(null);
}
FormRequest request = new FormRequest(formUuid, patientUuid, requestIndex);
mFormRequests.set(requestIndex, request);
return request;
}
public void onAddTestResultsPressed() {
Preset preset = new Preset();
preset.locationName = "Triage";
JsonUser user = App.getUserManager().getActiveUser();
Utils.logUserAction("form_opener_pressed", "form", "lab_test");
if (user != null) {
preset.clinicianName = user.fullName;
}
mUi.showFormLoadingDialog(true);
FormRequest request = newFormRequest(EBOLA_LAB_TEST_FORM_UUID, mPatientUuid);
mUi.fetchAndShowXform(
request.requestIndex, request.formUuid,
mPatient.toOdkPatient(), preset);
}
public void onOpenFormPressed(String formUuid) {
Preset preset = new Preset();
preset.locationName = "Triage";
JsonUser user = App.getUserManager().getActiveUser();
if (user != null) {
preset.clinicianName = user.fullName;
}
Utils.logUserAction("form_opener_pressed", "form", formUuid);
mUi.showFormLoadingDialog(true);
FormRequest request = newFormRequest(formUuid, mPatientUuid);
mUi.fetchAndShowXform(
request.requestIndex, request.formUuid,
mPatient.toOdkPatient(), preset);
}
@android.webkit.JavascriptInterface
public void onObsDialog(String conceptUuid, String startMillis, String stopMillis) {
ArrayList<ObsRow> observations = null;
if (!conceptUuid.isEmpty()){
if (!startMillis.isEmpty()){
observations = mChartHelper.getPatientObservationsByConceptMillis(mPatientUuid, conceptUuid, startMillis, stopMillis);
}
else{
observations = mChartHelper.getPatientObservationsByConcept(mPatientUuid, conceptUuid);
}
}
else if (!startMillis.isEmpty()){
observations = mChartHelper.getPatientObservationsByMillis(mPatientUuid, startMillis, stopMillis);
}
if ((observations != null) && (!observations.isEmpty())){
mUi.showObservationsDialog(observations);
}
}
@android.webkit.JavascriptInterface
public void onNewOrderPressed() {
mUi.showOrderDialog(mPatientUuid, null);
}
@android.webkit.JavascriptInterface
public void onOrderHeadingPressed(String orderUuid) {
mUi.showOrderDialog(mPatientUuid, mOrdersByUuid.get(orderUuid));
}
@android.webkit.JavascriptInterface
public void onOrderCellPressed(String orderUuid, long startMillis) {
Order order = mOrdersByUuid.get(orderUuid);
DateTime start = new DateTime(startMillis);
Interval interval = new Interval(start, start.plusDays(1));
List<DateTime> executionTimes = new ArrayList<>();
for (Obs obs : mObservations) {
if (AppModel.ORDER_EXECUTED_CONCEPT_UUID.equals(obs.conceptUuid) &&
order.uuid.equals(obs.value)) {
executionTimes.add(obs.time);
}
}
mUi.showOrderExecutionDialog(order, interval, executionTimes);
}
@android.webkit.JavascriptInterface
public void onPageUnload(int scrollX, int scrollY) {
mLastScrollPosition.set(scrollX, scrollY);
}
public void showAssignGeneralConditionDialog(
Context context, final String generalConditionUuid) {
AssignGeneralConditionDialog.ConditionSelectedCallback callback =
new AssignGeneralConditionDialog.ConditionSelectedCallback() {
@Override public boolean onNewConditionSelected(String newConditionUuid) {
setCondition(newConditionUuid);
Utils.logUserAction("condition_assigned");
return false;
}
};
mAssignGeneralConditionDialog = new AssignGeneralConditionDialog(
context, generalConditionUuid, callback);
mAssignGeneralConditionDialog.show();
}
public void setCondition(String newConditionUuid) {
LOG.v("Assigning general condition: %s", newConditionUuid);
Encounter encounter = new Encounter(
mPatientUuid,
null, // encounter UUID, which the server will generate
DateTime.now(),
new Observation[] {
new Observation(
ConceptUuids.GENERAL_CONDITION_UUID,
newConditionUuid,
Observation.Type.NON_DATE)
}, null);
mAppModel.addEncounter(mCrudEventBus, mPatient, encounter);
}
public void showAssignLocationDialog(Context context) {
if (mAssignLocationDialog != null) return;
AssignLocationDialog.LocationSelectedCallback callback =
new AssignLocationDialog.LocationSelectedCallback() {
@Override public boolean onLocationSelected(String locationUuid) {
PatientDelta delta = new PatientDelta();
delta.assignedLocationUuid = Optional.of(locationUuid);
mAppModel.updatePatient(mCrudEventBus, mPatient.uuid, delta);
Utils.logUserAction("location_assigned");
return false;
}
};
Runnable onDismiss = new Runnable() {
@Override public void run() {
mAssignLocationDialog = null;
}
};
mAssignLocationDialog = new AssignLocationDialog(
context,
mAppModel,
LocaleSelector.getCurrentLocale().getLanguage(),
onDismiss,
mCrudEventBus,
Optional.of(mPatient.locationUuid),
callback);
mAssignLocationDialog.show();
}
/** Gets the latest observation values and displays them on the UI. */
public synchronized void updatePatientObsUi(int chartNum) {
// Get the observations and orders
// TODO: Background thread this, or make this call async-like.
mObservations = mChartHelper.getObservations(mPatientUuid);
Map<String, Obs> latestObservations =
new HashMap<>(mChartHelper.getLatestObservations(mPatientUuid));
List<Order> orders = mChartHelper.getOrders(mPatientUuid);
mOrdersByUuid = new HashMap<>();
for (Order order : orders) {
mOrdersByUuid.put(order.uuid, order);
}
LOG.d("Showing " + mObservations.size() + " observations and "
+ orders.size() + " orders");
LocalDate admissionDate = getObservedDate(
latestObservations, ConceptUuids.ADMISSION_DATE_UUID);
LocalDate firstSymptomsDate = getObservedDate(
latestObservations, ConceptUuids.FIRST_SYMPTOM_DATE_UUID);
mUi.updateAdmissionDateAndFirstSymptomsDateUi(admissionDate, firstSymptomsDate);
mUi.updateEbolaPcrTestResultUi(latestObservations);
mUi.updatePregnancyAndIvStatusUi(latestObservations);
lastChartIndex = chartNum;
mUi.updateTilesAndGrid(
mCharts.get(chartNum),
latestObservations, mObservations, orders,
admissionDate, firstSymptomsDate);
}
public List<Chart> getCharts(){
return mCharts;
}
/** Retrieves the value of a date observation as a LocalDate. */
private LocalDate getObservedDate(
Map<String, Obs> observations, String conceptUuid) {
Obs obs = observations.get(conceptUuid);
return obs == null ? null : Utils.toLocalDate(obs.valueName);
}
private synchronized void updatePatientLocationUi() {
if (mLocationTree != null && mPatient != null && mPatient.locationUuid != null) {
mUi.updatePatientLocationUi(mLocationTree, mPatient);
}
}
/** Represents an instance of a form being opened by the user. */
class FormRequest {
public final String formUuid;
public final String patientUuid;
public final int requestIndex;
public FormRequest(String formUuid, String patientUuid, int index) {
this.formUuid = formUuid;
this.patientUuid = patientUuid;
this.requestIndex = index;
}
}
@SuppressWarnings("unused") // Called by reflection from EventBus.
private final class EventSubscriber {
public void onEventMainThread(AppLocationTreeFetchedEvent event) {
if (mLocationTree != null) {
mLocationTree.close();
}
mLocationTree = event.tree;
updatePatientLocationUi();
}
public void onEventMainThread(SyncSucceededEvent event) {
updatePatientObsUi(lastChartIndex);
}
public void onEventMainThread(EncounterAddFailedEvent event) {
if (mAssignGeneralConditionDialog != null) {
mAssignGeneralConditionDialog.dismiss();
mAssignGeneralConditionDialog = null;
}
int messageResource;
String exceptionMessage = event.exception.getMessage();
switch (event.reason) {
case FAILED_TO_AUTHENTICATE:
messageResource = R.string.encounter_add_failed_to_authenticate;
break;
case FAILED_TO_FETCH_SAVED_OBSERVATION:
messageResource = R.string.encounter_add_failed_to_fetch_saved;
break;
case FAILED_TO_SAVE_ON_SERVER:
messageResource = R.string.encounter_add_failed_to_saved_on_server;
break;
case FAILED_TO_VALIDATE:
messageResource = R.string.encounter_add_failed_invalid_encounter;
// Validation reason typically starts after the message below.
exceptionMessage = exceptionMessage.replaceFirst(
".*failed to validate with reason: .*: ", "");
break;
case INTERRUPTED:
messageResource = R.string.encounter_add_failed_interrupted;
break;
case INVALID_NUMBER_OF_OBSERVATIONS_SAVED: // Hard to communicate to the user.
case UNKNOWN_SERVER_ERROR:
messageResource = R.string.encounter_add_failed_unknown_server_error;
break;
case UNKNOWN:
default:
messageResource = R.string.encounter_add_failed_unknown_reason;
}
mUi.showError(messageResource, exceptionMessage);
}
// We get a ItemFetchedEvent when the initial patient data is loaded
// from SQLite or after an edit has been successfully posted to the server.
public void onEventMainThread(ItemFetchedEvent event) {
if (event.item instanceof Patient) {
// When the patient's location is changed, the location dialog stays
// open while we wait for the patient edit to be posted to the server.
// Now that the patient has been posted, close the dialog.
if (mAssignLocationDialog != null) {
mAssignLocationDialog.dismiss();
mAssignLocationDialog = null;
}
// Update the parts of the UI that use data in the Patient.
mPatient = (Patient) event.item;
mUi.updatePatientDetailsUi(mPatient);
updatePatientLocationUi();
} else if (event.item instanceof Encounter) {
// When the patient's condition is changed, the condition dialog stays
// open while we wait for the observation to be posted to the server.
// Now that the encounter has been posted, close the dialog.
if (mAssignGeneralConditionDialog != null) {
mAssignGeneralConditionDialog.dismiss();
mAssignGeneralConditionDialog = null;
}
// We don't need to update the UI here because updatePatientObsUi()
// below updates all the parts of the UI that use observation data.
}
// TODO: Displaying the observations part of the UI takes a lot of main-thread time.
// This delays rendering of the rest of UI. To allow the rest of the UI to be displayed
// before we attempt to populate the observations, we delay the observation update
// slightly. We need this hack because we load observations on the main thread. We
// should change this to use a background thread. Either an async task or using
// CrudEventBus events.
mMainThreadHandler.post(new Runnable() {
@Override
public void run() {
updatePatientObsUi(lastChartIndex);
}
});
}
public void onEventMainThread(ItemDeletedEvent event) {
mMainThreadHandler.post(new Runnable() {
@Override
public void run() {
updatePatientObsUi(lastChartIndex);
}
});
}
public void onEventMainThread(PatientUpdateFailedEvent event) {
mAssignLocationDialog.onPatientUpdateFailed(event.reason);
LOG.e(event.exception, "Patient update failed.");
}
public void onEventMainThread(SubmitXformSucceededEvent event) {
mMainThreadHandler.post(new Runnable() {
@Override
public void run() {
updatePatientObsUi(lastChartIndex);
mUi.showFormSubmissionDialog(false);
}
});
}
public void onEventMainThread(SubmitXformFailedEvent event) {
mUi.showFormSubmissionDialog(false);
int errorMessageResource;
switch (event.reason) {
case SERVER_AUTH:
errorMessageResource = R.string.submit_xform_failed_server_auth;
break;
case SERVER_TIMEOUT:
errorMessageResource = R.string.submit_xform_failed_server_timeout;
break;
default:
errorMessageResource = R.string.submit_xform_failed_unknown_reason;
}
mUi.showError(errorMessageResource);
}
public void onEventMainThread(FetchXformSucceededEvent event) {
mUi.showFormLoadingDialog(false);
mUi.reEnableFetch();
}
public void onEventMainThread(FetchXformFailedEvent event) {
int errorMessageResource = R.string.fetch_xform_failed_unknown_reason;
switch (event.reason) {
case NO_FORMS_FOUND:
errorMessageResource = R.string.fetch_xform_failed_no_forms_found;
break;
case SERVER_AUTH:
errorMessageResource = R.string.fetch_xform_failed_server_auth;
break;
case SERVER_BAD_ENDPOINT:
errorMessageResource = R.string.fetch_xform_failed_server_bad_endpoint;
break;
case SERVER_FAILED_TO_FETCH:
errorMessageResource = R.string.fetch_xform_failed_server_failed_to_fetch;
break;
case SERVER_UNKNOWN:
errorMessageResource = R.string.fetch_xform_failed_server_unknown;
break;
case UNKNOWN:
default:
// Intentionally blank.
}
mUi.showError(errorMessageResource);
mUi.showFormLoadingDialog(false);
mUi.reEnableFetch();
}
public void onEventMainThread(OrderSaveRequestedEvent event) {
DateTime start = event.start;
DateTime stop = null;
if (event.durationDays != null) {
LocalDate stopDate = start.toLocalDate().plusDays(event.durationDays);
// In OpenMRS, OrderServiceImpl.saveOrderInternal() forces the
// order expiry (auuughhh!) to 23:59:59.999 on its specified date.
// We have to shift it back a bit to prevent it from being
// advanced almost an entire day, and even then this only works if
// the client's time zone matches the server's time zone, because
// the server's fidelity is time-zone-dependent (auggghh!!!)
stop = stopDate.toDateTimeAtStartOfDay().minusSeconds(1);
}
mAppModel.saveOrder(mCrudEventBus, new Order(
event.orderUuid, event.patientUuid, event.instructions, start, stop));
}
public void onEventMainThread(OrderDeleteRequestedEvent event) {
mAppModel.deleteOrder(mCrudEventBus, event.orderUuid);
}
public void onEventMainThread(VoidObservationsRequestEvent event) {
for (String uuid : event.Uuids) {
mAppModel.VoidObservation(mCrudEventBus, new VoidObs(uuid));
}
updatePatientObsUi(lastChartIndex);
}
public void onEventMainThread(OrderExecutionSaveRequestedEvent event) {
Order order = mOrdersByUuid.get(event.orderUuid);
if (order != null) {
mAppModel.addOrderExecutedEncounter(mCrudEventBus, mPatient, order.uuid);
}
}
}
}