package femr.ui.controllers; import com.google.inject.Inject; 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.controllers.helpers.FieldHelper; import femr.ui.helpers.security.AllowedRoles; import femr.ui.helpers.security.FEMRAuthenticated; import femr.ui.models.medical.*; import femr.ui.views.html.medical.index; import femr.ui.views.html.medical.edit; import femr.ui.views.html.medical.newVitals; import femr.ui.views.html.medical.listVitals; import femr.ui.views.html.partials.medical.tabs.prescriptionRow; import femr.util.DataStructure.Mapping.TabFieldMultiMap; import femr.util.DataStructure.Mapping.VitalMultiMap; import femr.util.stringhelpers.StringUtils; import play.data.Form; import play.data.FormFactory; import play.mvc.Controller; import play.mvc.Http; import play.mvc.Result; import play.mvc.Security; import java.io.File; import java.util.*; import java.util.stream.Collectors; @Security.Authenticated(FEMRAuthenticated.class) @AllowedRoles({Roles.PHYSICIAN, Roles.PHARMACIST, Roles.NURSE}) public class MedicalController extends Controller { private final FormFactory formFactory; private final ITabService tabService; private final IEncounterService encounterService; private final IMedicationService medicationService; private final IPhotoService photoService; private final ISessionService sessionService; private final ISearchService searchService; private final IVitalService vitalService; private final FieldHelper fieldHelper; @Inject public MedicalController(FormFactory formFactory, ITabService tabService, IEncounterService encounterService, IMedicationService medicationService, IPhotoService photoService, ISessionService sessionService, ISearchService searchService, IVitalService vitalService) { this.formFactory = formFactory; this.tabService = tabService; this.encounterService = encounterService; this.sessionService = sessionService; this.searchService = searchService; this.medicationService = medicationService; this.photoService = photoService; this.vitalService = vitalService; this.fieldHelper = new FieldHelper(); } public Result indexGet() { CurrentUser currentUserSession = sessionService.retrieveCurrentUserSession(); return ok(index.render(currentUserSession, null, 0)); } public Result indexPost() { CurrentUser currentUserSession = sessionService.retrieveCurrentUserSession(); String queryString_id = request().body().asFormUrlEncoded().get("id")[0]; ServiceResponse<Integer> idQueryStringResponse = searchService.parseIdFromQueryString(queryString_id); if (idQueryStringResponse.hasErrors()) { return ok(index.render(currentUserSession, idQueryStringResponse.getErrors().get(""), 0)); } Integer patientId = idQueryStringResponse.getResponseObject(); //get the patient's encounter ServiceResponse<PatientEncounterItem> patientEncounterItemServiceResponse = searchService.retrieveRecentPatientEncounterItemByPatientId(patientId); if (patientEncounterItemServiceResponse.hasErrors()) { return ok(index.render(currentUserSession, patientEncounterItemServiceResponse.getErrors().get(""), 0)); } PatientEncounterItem patientEncounterItem = patientEncounterItemServiceResponse.getResponseObject(); //check for encounter closed if (patientEncounterItem.getIsClosed()) { return ok(index.render(currentUserSession, "That patient's encounter has been closed.", 0)); } //check if the doc has already seen the patient today ServiceResponse<UserItem> userItemServiceResponse = encounterService.retrievePhysicianThatCheckedInPatientToMedical(patientEncounterItem.getId()); if (userItemServiceResponse.hasErrors()) { throw new RuntimeException(); } else { if (userItemServiceResponse.getResponseObject() != null) { return ok(index.render(currentUserSession, "That patient has already been seen today. Would you like to edit their encounter?", patientId)); } } return redirect(routes.MedicalController.editGet(patientId)); } public Result editGet(int patientId) { CurrentUser currentUserSession = sessionService.retrieveCurrentUserSession(); EditViewModelGet viewModelGet = new EditViewModelGet(); //Get Patient Encounter PatientEncounterItem patientEncounter; ServiceResponse<PatientEncounterItem> patientEncounterItemServiceResponse = searchService.retrieveRecentPatientEncounterItemByPatientId(patientId); if (patientEncounterItemServiceResponse.hasErrors()) { throw new RuntimeException(); } patientEncounter = patientEncounterItemServiceResponse.getResponseObject(); viewModelGet.setPatientEncounterItem(patientEncounter); //verify encounter is still open if (patientEncounter.getIsClosed()) { return ok(index.render(currentUserSession, "That patient's encounter has been closed.", 0)); } //get patient ServiceResponse<PatientItem> patientItemServiceResponse = searchService.retrievePatientItemByPatientId(patientId); if (patientItemServiceResponse.hasErrors()) { throw new RuntimeException(); } viewModelGet.setPatientItem(patientItemServiceResponse.getResponseObject()); //get prescriptions ServiceResponse<List<PrescriptionItem>> prescriptionItemServiceResponse = searchService.retrieveUnreplacedPrescriptionItems(patientEncounter.getId(), currentUserSession.getTripId()); if (prescriptionItemServiceResponse.hasErrors()) { throw new RuntimeException(); } viewModelGet.setPrescriptionItems(prescriptionItemServiceResponse.getResponseObject()); //get MedicationAdministrationItems ServiceResponse<List<MedicationAdministrationItem>> medicationAdministrationItemServiceResponse = medicationService.retrieveAvailableMedicationAdministrations(); if (medicationAdministrationItemServiceResponse.hasErrors()) { throw new RuntimeException(); } viewModelGet.setMedicationAdministrationItems(medicationAdministrationItemServiceResponse.getResponseObject()); //get problems ServiceResponse<List<ProblemItem>> problemItemServiceResponse = encounterService.retrieveProblemItems(patientEncounter.getId()); if (problemItemServiceResponse.hasErrors()) { throw new RuntimeException(); } viewModelGet.setProblemItems(problemItemServiceResponse.getResponseObject()); //get vitals ServiceResponse<VitalMultiMap> vitalMapResponse = vitalService.retrieveVitalMultiMap(patientEncounter.getId()); if (vitalMapResponse.hasErrors()) { throw new RuntimeException(); } //get all fields and their values ServiceResponse<TabFieldMultiMap> tabFieldMultiMapResponse = tabService.retrieveTabFieldMultiMap(patientEncounter.getId()); if (tabFieldMultiMapResponse.hasErrors()) { throw new RuntimeException(); } TabFieldMultiMap tabFieldMultiMap = tabFieldMultiMapResponse.getResponseObject(); ServiceResponse<List<TabItem>> tabItemServiceResponse = tabService.retrieveAvailableTabs(false); if (tabItemServiceResponse.hasErrors()) { throw new RuntimeException(); } ServiceResponse<Map<String, List<String>>> tabFieldToTabMappingServiceResponse = tabService.retrieveTabFieldToTabMapping(false, false); if (tabFieldToTabMappingServiceResponse.hasErrors()){ throw new RuntimeException(); } Map<String, List<String>> tabFieldToTabMapping = tabFieldToTabMappingServiceResponse.getResponseObject(); List<TabItem> tabItems = tabItemServiceResponse.getResponseObject(); //match the fields to their respective tabs for (TabItem tabItem : tabItems) { switch (tabItem.getName().toLowerCase()) { case "hpi": tabItem.setFields(FieldHelper.structureHPIFieldsForView(tabFieldMultiMap, tabFieldToTabMapping.get("hpi"))); break; case "pmh": tabItem.setFields(FieldHelper.structurePMHFieldsForView(tabFieldMultiMap, tabFieldToTabMapping.get("pmh"))); break; case "treatment": tabItem.setFields(FieldHelper.structureTreatmentFieldsForView(tabFieldMultiMap, tabFieldToTabMapping.get("treatment"))); break; case "photos": break; default: tabItem.setFields(fieldHelper.structureDynamicFieldsForView(tabFieldMultiMap, tabFieldToTabMapping.get(tabItem.getName().toLowerCase()))); break; } } tabItems = FieldHelper.applyIndicesToFieldsForView(tabItems); viewModelGet.setTabItems(tabItems); viewModelGet.setChiefComplaints(tabFieldMultiMap.getChiefComplaintList()); ServiceResponse<List<PhotoItem>> photoListResponse = photoService.retrieveEncounterPhotos(patientEncounter.getId()); if (photoListResponse.hasErrors()) { throw new RuntimeException(); } else { viewModelGet.setPhotos(photoListResponse.getResponseObject()); } ServiceResponse<SettingItem> response = searchService.retrieveSystemSettings(); viewModelGet.setSettings(response.getResponseObject()); //Alaa Serhan VitalMultiMap vitalMultiMap = vitalMapResponse.getResponseObject(); return ok(edit.render(currentUserSession, vitalMultiMap, viewModelGet)); } /** * Get the populated partial view that represents 1 row of new prescription fields * - meant to be an AJAX call * * @param index * @return */ public Result prescriptionRowGet( int index ) { //get MedicationAdministrationItems ServiceResponse<List<MedicationAdministrationItem>> medicationAdministrationItemServiceResponse = medicationService.retrieveAvailableMedicationAdministrations(); if (medicationAdministrationItemServiceResponse.hasErrors()) { throw new RuntimeException(); } List<MedicationAdministrationItem> items = medicationAdministrationItemServiceResponse.getResponseObject(); return ok( prescriptionRow.render( items, index, null ) ); } public Result editPost(int patientId) { CurrentUser currentUserSession = sessionService.retrieveCurrentUserSession(); final Form<EditViewModelPost> createViewModelPostForm = formFactory.form(EditViewModelPost.class); EditViewModelPost viewModelPost = createViewModelPostForm.bindFromRequest().get(); //get current patient ServiceResponse<PatientItem> patientItemServiceResponse = searchService.retrievePatientItemByPatientId(patientId); if (patientItemServiceResponse.hasErrors()) { throw new RuntimeException(); } PatientItem patientItem = patientItemServiceResponse.getResponseObject(); //get current encounter ServiceResponse<PatientEncounterItem> patientEncounterServiceResponse = searchService.retrieveRecentPatientEncounterItemByPatientId(patientId); if (patientEncounterServiceResponse.hasErrors()) { throw new RuntimeException(); } PatientEncounterItem patientEncounterItem = patientEncounterServiceResponse.getResponseObject(); patientEncounterItem = encounterService.checkPatientInToMedical(patientEncounterItem.getId(), currentUserSession.getId()).getResponseObject(); //get and save problems List<String> problemList = new ArrayList<>(); for (ProblemItem pi : viewModelPost.getProblems()) { if (StringUtils.isNotNullOrWhiteSpace(pi.getName())) { problemList.add(pi.getName()); } } if (problemList.size() > 0) { encounterService.createProblems(problemList, patientEncounterItem.getId(), currentUserSession.getId()); } //get tab fields that do not have a related chief complaint and put them into a nice map Map<String, String> tabFieldItemsWithNoRelatedChiefComplaint = new HashMap<>(); Map<String, Map<String, String>> tabFieldItemsWithChiefComplaint = new HashMap<>(); //get tab fields other than problems for (TabFieldItem tfi : viewModelPost.getTabFieldItems()) { if (StringUtils.isNotNullOrWhiteSpace(tfi.getValue()) && StringUtils.isNullOrWhiteSpace(tfi.getChiefComplaint())) { tabFieldItemsWithNoRelatedChiefComplaint.put(tfi.getName(), tfi.getValue()); }else if (StringUtils.isNotNullOrWhiteSpace(tfi.getValue()) && StringUtils.isNotNullOrWhiteSpace(tfi.getChiefComplaint())) { // Get the tabField Map for chief complaint Map<String, String> tabFieldMap = tabFieldItemsWithChiefComplaint.get(tfi.getChiefComplaint()); if (tabFieldMap == null){ // if it does not exist, create it tabFieldMap = new HashMap<>(); } // create and add a tabFieldMap to the Map of Maps for the chief complaint, ummm yea... tabFieldMap.put(tfi.getName(), tfi.getValue()); tabFieldItemsWithChiefComplaint.put(tfi.getChiefComplaint(), tabFieldMap); } } //save the tab fields that do not have a related chief complaint ServiceResponse<List<TabFieldItem>> createPatientEncounterTabFieldsServiceResponse; if (tabFieldItemsWithNoRelatedChiefComplaint.size() > 0) { createPatientEncounterTabFieldsServiceResponse = encounterService.createPatientEncounterTabFields(tabFieldItemsWithNoRelatedChiefComplaint, patientEncounterItem.getId(), currentUserSession.getId()); if (createPatientEncounterTabFieldsServiceResponse.hasErrors()) { throw new RuntimeException(); } } //save the tab fields that do have related chief complaint(s) ServiceResponse<List<TabFieldItem>> createPatientEncounterTabFieldsWithChiefComplaintsServiceResponse; if (tabFieldItemsWithChiefComplaint.size() > 0){ //call the service once for each existing chief complaint for (Map.Entry<String, Map<String,String>> entry : tabFieldItemsWithChiefComplaint.entrySet()){ createPatientEncounterTabFieldsWithChiefComplaintsServiceResponse = encounterService.createPatientEncounterTabFields(entry.getValue(), patientEncounterItem.getId(), currentUserSession.getId(), entry.getKey()); if (createPatientEncounterTabFieldsWithChiefComplaintsServiceResponse.hasErrors()){ throw new RuntimeException(); } } } //create patient encounter photos Http.MultipartFormData<File> body = request().body().asMultipartFormData(); List<Http.MultipartFormData.FilePart<File>> files = body.getFiles(); photoService.createEncounterPhotos(files, patientEncounterItem, viewModelPost); //get the prescriptions that have an ID (e.g. prescriptions that exist in the dictionary). List<PrescriptionItem> prescriptionItemsWithID = viewModelPost.getPrescriptions() .stream() .filter(prescription -> prescription.getMedicationID() != null) .collect(Collectors.toList()); //create the prescriptions that already have an ID ServiceResponse<PrescriptionItem> createPrescriptionServiceResponse; for (PrescriptionItem prescriptionItem : prescriptionItemsWithID){ //The POST data sends -1 if an administration ID is not set. Null is more appropriate for the //service layer if (prescriptionItem.getAdministrationID() == -1) prescriptionItem.setAdministrationID(null); createPrescriptionServiceResponse = medicationService.createPrescription( prescriptionItem.getMedicationID(), prescriptionItem.getAdministrationID(), patientEncounterItem.getId(), currentUserSession.getId(), prescriptionItem.getAmountWithNull(), null); if (createPrescriptionServiceResponse.hasErrors()){ throw new RuntimeException(); } } // get the prescriptions that DO NOT have an ID (e.g. prescriptions that DO NOT exist in the dictionary). // also ignore new new prescriptions that do not have a name List<PrescriptionItem> prescriptionItemsWithoutID = viewModelPost.getPrescriptions() .stream() .filter( prescription -> prescription.getMedicationID() == null ) .filter( prescription -> StringUtils.isNotNullOrWhiteSpace( prescription.getMedicationName() ) ) .collect(Collectors.toList()); for (PrescriptionItem prescriptionItem : prescriptionItemsWithoutID){ //The POST data sends -1 if an administration ID is not set. Null is more appropriate for the //service layer if (prescriptionItem.getAdministrationID() == -1) prescriptionItem.setAdministrationID(null); createPrescriptionServiceResponse = medicationService.createPrescriptionWithNewMedication( prescriptionItem.getMedicationName(), prescriptionItem.getAdministrationID(), patientEncounterItem.getId(), currentUserSession.getId(), prescriptionItem.getAmountWithNull(), null); if (createPrescriptionServiceResponse.hasErrors()){ throw new RuntimeException(); } } String message = "Patient information for " + patientItem.getFirstName() + " " + patientItem.getLastName() + " (id: " + patientItem.getId() + ") was saved successfully."; return ok(index.render(currentUserSession, message, 0)); } public Result updateVitalsPost(int id) { CurrentUser currentUser = sessionService.retrieveCurrentUserSession(); // Alaa Serhan // Add View Model to Get the Settings to see if METRIC SYSTEM are set or not EditViewModelGet viewModelGet = new EditViewModelGet(); ServiceResponse<SettingItem> response = searchService.retrieveSystemSettings(); viewModelGet.setSettings(response.getResponseObject()); // Get patient Encounter ServiceResponse<PatientEncounterItem> currentEncounterByPatientId = searchService.retrieveRecentPatientEncounterItemByPatientId(id); if (currentEncounterByPatientId.hasErrors()) { throw new RuntimeException(); } PatientEncounterItem patientEncounter = currentEncounterByPatientId.getResponseObject(); //update date_of_medical_visit when a vital is updated encounterService.checkPatientInToMedical(currentEncounterByPatientId.getResponseObject().getId(), currentUser.getId()); final Form<UpdateVitalsModel> updateVitalsModelForm = formFactory.form(UpdateVitalsModel.class); UpdateVitalsModel updateVitalsModel = updateVitalsModelForm.bindFromRequest().get(); Map<String, Float> patientEncounterVitals = getPatientEncounterVitals(updateVitalsModel); ServiceResponse<List<VitalItem>> patientEncounterVitalsServiceResponse = vitalService.createPatientEncounterVitals(patientEncounterVitals, currentUser.getId(), patientEncounter.getId()); if (patientEncounterVitalsServiceResponse.hasErrors()) { throw new RuntimeException(); } return ok("true"); } //Called via AJAX: (/medical/deleteProblem/:patientId/:problem) to deleted a problem public Result deleteExistingProblem(int patientId, String problem){ //get current patient ServiceResponse<PatientItem> patientItemServiceResponse = searchService.retrievePatientItemByPatientId(patientId); if (patientItemServiceResponse.hasErrors()) { throw new RuntimeException(); } PatientItem patientItem = patientItemServiceResponse.getResponseObject(); //get current encounter ServiceResponse<PatientEncounterItem> patientEncounterServiceResponse = searchService.retrieveRecentPatientEncounterItemByPatientId(patientId); if (patientEncounterServiceResponse.hasErrors()) { throw new RuntimeException(); } PatientEncounterItem patientEncounterItem = patientEncounterServiceResponse.getResponseObject(); ServiceResponse<Boolean> deleteProblemServiceResponse = encounterService.deleteExistingProblem(patientEncounterItem.getId(), problem, sessionService.retrieveCurrentUserSession().getId()); return ok(deleteProblemServiceResponse.getResponseObject().toString()); } //partials public Result newVitalsGet() { // Alaa Serhan - Add View Model to Get the Settings to see if METRIC SYSTEM are set or not EditViewModelGet viewModelGet = new EditViewModelGet(); ServiceResponse<SettingItem> response = searchService.retrieveSystemSettings(); viewModelGet.setSettings(response.getResponseObject()); return ok(newVitals.render(viewModelGet)); } public Result listVitalsGet(Integer id) { // Alaa Serhan - Add View Model to Get the Settings to see if METRIC SYSTEM are set or not EditViewModelGet viewModelGet = new EditViewModelGet(); ServiceResponse<SettingItem> response = searchService.retrieveSystemSettings(); viewModelGet.setSettings(response.getResponseObject()); ServiceResponse<PatientEncounterItem> patientEncounterServiceResponse = searchService.retrieveRecentPatientEncounterItemByPatientId(id); if (patientEncounterServiceResponse.hasErrors()) { throw new RuntimeException(); } ServiceResponse<VitalMultiMap> vitalMultiMapServiceResponse = vitalService.retrieveVitalMultiMap(patientEncounterServiceResponse.getResponseObject().getId()); if (vitalMultiMapServiceResponse.hasErrors()) { throw new RuntimeException(); } //Alaa Serhan VitalMultiMap vitalMap = vitalMultiMapServiceResponse.getResponseObject(); return ok(listVitals.render(vitalMap, viewModelGet)); } /** * Maps vitals from view model to a Map structure * * @param viewModel the view model with POST data * @return Mapped vital value to vital name */ private Map<String, Float> getPatientEncounterVitals(UpdateVitalsModel viewModel) { EditViewModelGet viewModelGet = new EditViewModelGet(); ServiceResponse<SettingItem> response = searchService.retrieveSystemSettings(); viewModelGet.setSettings(response.getResponseObject()); Map<String, Float> newVitals = new HashMap<>(); if (viewModel.getRespiratoryRate() != null) { newVitals.put("respiratoryRate", viewModel.getRespiratoryRate()); } if (viewModel.getHeartRate() != null) { newVitals.put("heartRate", viewModel.getHeartRate()); } //Alaa Serhan if (viewModel.getTemperature() != null) { Float temperature = viewModel.getTemperature(); newVitals.put("temperature", temperature); } if (viewModel.getOxygenSaturation() != null) { newVitals.put("oxygenSaturation", viewModel.getOxygenSaturation()); } //Alaa Serhan if (viewModel.getHeightFeet() != null && viewModel.getHeightInches() != null) { Float heightFeet = viewModel.getHeightFeet().floatValue(); Float heightInches = viewModel.getHeightInches().floatValue(); newVitals.put("heightFeet", heightFeet); newVitals.put("heightInches", heightInches); } //Alaa Serhan if (viewModel.getWeight() != null) { Float weight = viewModel.getWeight(); newVitals.put("weight", weight); } if (viewModel.getBloodPressureSystolic() != null) { newVitals.put("bloodPressureSystolic", viewModel.getBloodPressureSystolic()); } if (viewModel.getBloodPressureDiastolic() != null) { newVitals.put("bloodPressureDiastolic", viewModel.getBloodPressureDiastolic()); } if (viewModel.getGlucose() != null) { newVitals.put("glucose", viewModel.getGlucose()); } if (viewModel.getWeeksPregnant() != null) { newVitals.put("weeksPregnant", viewModel.getWeeksPregnant()); } return newVitals; } /* private List<TabFieldItem> mapHpiFieldItemsFromJSON(String JSON) { List<TabFieldItem> tabFieldItems = new ArrayList<>(); Gson gson = new Gson(); //get values from JSON, assign list of values to chief complaint Map<String, List<JCustomField>> hpiTabInformation = gson.fromJson(JSON, new TypeToken<Map<String, List<JCustomField>>>() { }.getType()); for (Map.Entry<String, List<JCustomField>> entry : hpiTabInformation.entrySet()) { List<JCustomField> fields = entry.getValue(); for (JCustomField jcf : fields) { TabFieldItem tabFieldItem = new TabFieldItem(); tabFieldItem = jcf.getName()); tabFieldItem.setChiefComplaint(entry.getKey().trim()); tabFieldItem.setIsCustom(false); tabFieldItem.setValue(jcf.getValue()); tabFieldItems.add(tabFieldItem); } } return tabFieldItems; } */ }