/*
fEMR - fast Electronic Medical Records
Copyright (C) 2014 Team fEMR
fEMR is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
fEMR is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with fEMR. If not, see <http://www.gnu.org/licenses/>. If
you have any questions, contact <info@teamfemr.org>.
*/
package femr.ui.controllers;
import com.google.inject.Inject;
import femr.ui.helpers.security.AllowedRoles;
import femr.business.services.core.*;
import femr.common.dtos.CurrentUser;
import femr.common.dtos.ServiceResponse;
import femr.common.models.*;
import femr.data.models.mysql.Roles;
import femr.ui.helpers.security.FEMRAuthenticated;
import femr.ui.models.history.*;
import femr.ui.views.html.history.indexEncounter;
import femr.ui.views.html.history.indexPatient;
import femr.ui.views.html.partials.history.listTabFieldHistory;
import femr.util.DataStructure.Mapping.TabFieldMultiMap;
import femr.util.DataStructure.Mapping.VitalMultiMap;
import femr.util.stringhelpers.StringUtils;
import play.data.Form;
import java.util.ArrayList;
import java.util.HashMap;
import play.data.FormFactory;
import play.mvc.Security;
import play.mvc.Controller;
import play.mvc.Result;
import java.util.List;
import java.util.Map;
@Security.Authenticated(FEMRAuthenticated.class)
@AllowedRoles({Roles.PHYSICIAN, Roles.PHARMACIST, Roles.NURSE})
public class HistoryController extends Controller {
private final FormFactory formFactory;
private final IEncounterService encounterService;
private final ISessionService sessionService;
private final ISearchService searchService;
private final ITabService tabService;
private final IPhotoService photoService;
private final IVitalService vitalService;
@Inject
public HistoryController(FormFactory formFactory,
IEncounterService encounterService,
ISessionService sessionService,
ISearchService searchService,
ITabService tabService,
IPhotoService photoService,
IVitalService vitalService) {
this.formFactory = formFactory;
this.encounterService = encounterService;
this.sessionService = sessionService;
this.searchService = searchService;
this.tabService = tabService;
this.photoService = photoService;
this.vitalService = vitalService;
}
/**
* Render the page for viewing a patient (a patient can have multiple encounters)
*
* @param query this is a query string representing what the user searched (patient first/last name).
*/
public Result indexPatientGet(String query) {
CurrentUser currentUser = sessionService.retrieveCurrentUserSession();
boolean error = false;
IndexPatientViewModelGet viewModel = new IndexPatientViewModelGet();
//how do we show more than one?
query = query.replace("-", " ");
ServiceResponse<List<PatientItem>> patientResponse = searchService.retrievePatientsFromQueryString(query);
if (patientResponse.hasErrors()) {
throw new RuntimeException();
}
List<PatientItem> patientItems = patientResponse.getResponseObject();
if (patientItems == null || patientItems.size() < 1) {
// return ok(showError.render(currentUser));
//return an error near the search box
}
//too much logic - move patient photo finding up to the service layer
for (PatientItem patientItem : patientItems)
patientItem.setPathToPhoto(routes.PhotoController.GetPatientPhoto(patientItem.getId(), true).toString());
viewModel.setPatientItems(patientItems);
viewModel.setPatientItem(patientItems.get(0));
ServiceResponse<List<PatientEncounterItem>> patientEncountersServiceResponse = searchService.retrievePatientEncounterItemsByPatientId(patientItems.get(0).getId());
if (patientEncountersServiceResponse.hasErrors()) {
throw new RuntimeException();
}
List<PatientEncounterItem> patientEncounterItems = patientEncountersServiceResponse.getResponseObject();
if (patientEncounterItems == null || patientEncounterItems.size() < 1) {
//return ok(showError.render(currentUser));
//return an error near the search box
}
viewModel.setPatientEncounterItems(patientEncounterItems);
return ok(indexPatient.render(currentUser, error, viewModel, patientEncounterItems));
}
/**
* Render the page for viewing an encounter.
*
* @param encounterId the id of the encounter
*/
public Result indexEncounterGet(int encounterId) {
CurrentUser currentUser = sessionService.retrieveCurrentUserSession();
IndexEncounterViewModel indexEncounterViewModel = new IndexEncounterViewModel();
IndexEncounterMedicalViewModel indexEncounterMedicalViewModel = new IndexEncounterMedicalViewModel();
IndexEncounterPharmacyViewModel indexEncounterPharmacyViewModel = new IndexEncounterPharmacyViewModel();
/* Alaa Serhan */
ServiceResponse<SettingItem> response = searchService.retrieveSystemSettings();
indexEncounterMedicalViewModel.setSettings(response.getResponseObject());
ServiceResponse<PatientItem> patientItemServiceResponse = searchService.retrievePatientItemByEncounterId(encounterId);
if (patientItemServiceResponse.hasErrors()) {
throw new RuntimeException();
}
PatientItem patientItem = patientItemServiceResponse.getResponseObject();
patientItem.setPathToPhoto(routes.PhotoController.GetPatientPhoto(patientItem.getId(), true).toString());
indexEncounterViewModel.setPatientItem(patientItem);
ServiceResponse<PatientEncounterItem> patientEncounterItemServiceResponse = searchService.retrievePatientEncounterItemByEncounterId(encounterId);
if (patientEncounterItemServiceResponse.hasErrors()) {
throw new RuntimeException();
}
indexEncounterViewModel.setPatientEncounterItem(patientEncounterItemServiceResponse.getResponseObject());
//get vitals
ServiceResponse<VitalMultiMap> patientEncounterVitalMapResponse = vitalService.retrieveVitalMultiMap(encounterId);
if (patientEncounterVitalMapResponse.hasErrors()) {
throw new RuntimeException();
}
/* Alaa Serhan */
// Get patient vitals
VitalMultiMap vitalMultiMap = patientEncounterVitalMapResponse.getResponseObject();
indexEncounterMedicalViewModel.setVitalList(vitalMultiMap);
//get photos
ServiceResponse<List<PhotoItem>> photoListResponse = photoService.retrieveEncounterPhotos(encounterId);
if (photoListResponse.hasErrors()) {
throw new RuntimeException();
}
indexEncounterMedicalViewModel.setPhotos(photoListResponse.getResponseObject());
// Get patient encounter tab field multimap
ServiceResponse<TabFieldMultiMap> patientEncounterTabFieldResponse = tabService.retrieveTabFieldMultiMap(encounterId);
if (patientEncounterTabFieldResponse.hasErrors()) {
throw new RuntimeException();
}
TabFieldMultiMap tabFieldMultiMap = patientEncounterTabFieldResponse.getResponseObject();
//Get a mapping of tabs to tab fields in string form
ServiceResponse<Map<String, List<String>>> tabFieldToTabMappingServiceResponse = tabService.retrieveTabFieldToTabMapping(false, false);
if (tabFieldToTabMappingServiceResponse.hasErrors()){
throw new RuntimeException();
}
Map<String, List<String>> tabFieldToTabMapping = tabFieldToTabMappingServiceResponse.getResponseObject();
//extract the most recent treatment fields
Map<String, TabFieldItem> treatmentFields = new HashMap<>();
for (String field : tabFieldToTabMapping.get("treatment")){
treatmentFields.put(field, tabFieldMultiMap.getMostRecentOrEmpty(field, null));
}
indexEncounterMedicalViewModel.setTreatmentFields(treatmentFields);
//extract the most recent pmh fields
Map<String, TabFieldItem> pmhFields = new HashMap<>();
for (String field : tabFieldToTabMapping.get("pmh")){
pmhFields.put(field, tabFieldMultiMap.getMostRecentOrEmpty(field, null));
}
indexEncounterMedicalViewModel.setPmhFields(pmhFields);
//extract the most recent hpi fields
if (patientEncounterItemServiceResponse.getResponseObject().getChiefComplaints().size() > 1) {
//extract the HPI fields while the user has entered multiple chief complaints
indexEncounterMedicalViewModel.setHpiFieldsWithMultipleChiefComplaints(extractHpiFieldsWithMultipleChiefComplaints(tabFieldMultiMap, patientEncounterItemServiceResponse.getResponseObject().getChiefComplaints()));
indexEncounterMedicalViewModel.setIsMultipleChiefComplaints(true);
} else {
String chiefComplaint = null;
if (patientEncounterItemServiceResponse.getResponseObject().getChiefComplaints().size() == 1)
chiefComplaint = patientEncounterItemServiceResponse.getResponseObject().getChiefComplaints().get(0);
Map<String, TabFieldItem> hpiFields = new HashMap<>();
for (String field : tabFieldToTabMapping.get("hpi")){
hpiFields.put(field, tabFieldMultiMap.getMostRecentOrEmpty(field, chiefComplaint));
}
indexEncounterMedicalViewModel.setHpiFieldsWithoutMultipleChiefComplaints(hpiFields);
indexEncounterMedicalViewModel.setIsMultipleChiefComplaints(false);
}
//extract the most recent custom fields
indexEncounterMedicalViewModel.setCustomFields(extractCustomFields(tabFieldMultiMap));
//get problems
List<String> problems = new ArrayList<>();
ServiceResponse<List<ProblemItem>> problemItemServiceResponse = encounterService.retrieveProblemItems(encounterId);
if (problemItemServiceResponse.hasErrors()) {
throw new RuntimeException();
}
for (ProblemItem pi : problemItemServiceResponse.getResponseObject()) {
problems.add(pi.getName());
}
indexEncounterPharmacyViewModel.setProblems(problems);
//get prescriptions
List<String> prescriptions = new ArrayList<>();
ServiceResponse<List<PrescriptionItem>> prescriptionItemServiceResponse = searchService.retrieveDispensedPrescriptionItems(encounterId);
if (prescriptionItemServiceResponse.hasErrors()) {
throw new RuntimeException();
}
indexEncounterPharmacyViewModel.setPrescriptions(prescriptionItemServiceResponse.getResponseObject());
return ok(indexEncounter.render(currentUser, indexEncounterViewModel, indexEncounterMedicalViewModel, indexEncounterPharmacyViewModel));
}
/**
* Updates a field from an encounter. Called from AJAX.
*/
public Result updateEncounterPost(int encounterId) {
CurrentUser currentUser = sessionService.retrieveCurrentUserSession();
//get POST data
final Form<fieldValueViewModel> fieldValueViewModelForm = formFactory.form(fieldValueViewModel.class);
fieldValueViewModel fields = fieldValueViewModelForm.bindFromRequest().get();
//get the patient encounter from the service layer
ServiceResponse<PatientEncounterItem> patientEncounterByEncounterId = searchService.retrievePatientEncounterItemByEncounterId(encounterId);
if (patientEncounterByEncounterId.hasErrors()) {
throw new RuntimeException();
}
//extract the patient encounter item from the service response
PatientEncounterItem patientEncounter = patientEncounterByEncounterId.getResponseObject();
//Create a mapping of the only tabfield from its name to its value for saving
Map<String, String> tabFieldNameValues = new HashMap<>();
tabFieldNameValues.put(fields.getFieldName(), fields.getFieldValue());
//save the MFer
ServiceResponse<List<TabFieldItem>> patientEncounterTabFieldsServiceResponse = new ServiceResponse<>();
if (StringUtils.isNullOrWhiteSpace(fields.getChiefComplaintName()))
encounterService.createPatientEncounterTabFields(tabFieldNameValues, patientEncounter.getId(), currentUser.getId());
else
encounterService.createPatientEncounterTabFields(tabFieldNameValues, patientEncounter.getId(), currentUser.getId(), fields.getChiefComplaintName());
if (patientEncounterTabFieldsServiceResponse.hasErrors())
throw new RuntimeException();
return ok("true");
}
/**
* Gets the partial view that shows the history of tab field items. Called from AJAX.
*/
public Result listTabFieldHistoryGet(int encounterID) {
//Populate model with request data that was changed
final Form<fieldValueViewModel> fieldValueViewModelForm = formFactory.form(fieldValueViewModel.class);
fieldValueViewModel fields = fieldValueViewModelForm.bindFromRequest().get();
//get the recorded tab field values using the id from the previous
ServiceResponse<TabFieldMultiMap> tabFieldsResponseObject = tabService.findTabFieldMultiMap(
encounterID,
fields.getFieldName(),
fields.getChiefComplaintName()
);
if (tabFieldsResponseObject.hasErrors()) {
throw new RuntimeException();
}
return ok(listTabFieldHistory.render(fields.getFieldName(), tabFieldsResponseObject.getResponseObject()));
}
/**
* A helper that extracts the most recent custom fields from the tabfieldmultimap. This method
* is not aware of which tab the fields are under, nor does it care.
*/
private Map<String, TabFieldItem> extractCustomFields(TabFieldMultiMap tabFieldMultiMap) {
Map<String, TabFieldItem> customFields = new HashMap<>();
List<String> customFieldNames = tabFieldMultiMap.getCustomFieldNameList();
for (String customField : customFieldNames) {
customFields.put(customField, tabFieldMultiMap.getMostRecentOrEmpty(customField, null));
}
return customFields;
}
/**
* A helper that extracts the most recent HPI fields from the tabfieldmultimap. Takes into consideration all chief complaints.
*/
private Map<String, Map<String, TabFieldItem>> extractHpiFieldsWithMultipleChiefComplaints(TabFieldMultiMap tabFieldMultiMap, List<String> chiefComplaints) {
Map<String, Map<String, TabFieldItem>> hpiFields = new HashMap<>();
for (String cc : chiefComplaints) {
Map<String, TabFieldItem> hpiFieldsUnderChiefComplaint = new HashMap<>();
hpiFieldsUnderChiefComplaint.put("onset", tabFieldMultiMap.getMostRecentOrEmpty("onset", cc.trim()));
hpiFieldsUnderChiefComplaint.put("quality", tabFieldMultiMap.getMostRecentOrEmpty("quality", cc.trim()));
hpiFieldsUnderChiefComplaint.put("radiation", tabFieldMultiMap.getMostRecentOrEmpty("radiation", cc.trim()));
hpiFieldsUnderChiefComplaint.put("severity", tabFieldMultiMap.getMostRecentOrEmpty("severity", cc.trim()));
hpiFieldsUnderChiefComplaint.put("provokes", tabFieldMultiMap.getMostRecentOrEmpty("provokes", cc.trim()));
hpiFieldsUnderChiefComplaint.put("palliates", tabFieldMultiMap.getMostRecentOrEmpty("palliates", cc.trim()));
hpiFieldsUnderChiefComplaint.put("timeOfDay", tabFieldMultiMap.getMostRecentOrEmpty("timeOfDay", cc.trim()));
hpiFieldsUnderChiefComplaint.put("narrative", tabFieldMultiMap.getMostRecentOrEmpty("narrative", cc.trim()));
hpiFieldsUnderChiefComplaint.put("physicalExamination", tabFieldMultiMap.getMostRecentOrEmpty("physicalExamination", cc.trim()));
hpiFields.put(cc.trim(), hpiFieldsUnderChiefComplaint);
}
return hpiFields;
}
}