def __init__(self, parent=None): QtGui.QStackedWidget.__init__(self, parent) self.patient_label = QtGui.QLabel() icon = QtGui.QIcon(":/search.png") self.get_patient_button = QtGui.QPushButton(icon, "") self.get_patient_button.setMaximumWidth(40) self.appt_listView = DraggableList(True, self) self.block_listView = DraggableList(False, self) self.appointment_model = SimpleListModel(self) self.appt_listView.setModel(self.appointment_model) self.appt_listView.setSelectionModel( self.appointment_model.selection_model) self.appt_listView.setSelectionMode(QtGui.QListView.SingleSelection) block_model = BlockListModel(self) self.block_listView.setModel(block_model) icon = QtGui.QIcon(":vcalendar.png") diary_button = QtGui.QPushButton(icon, "") diary_button.setToolTip(_("Open the patient's diary")) icon = QtGui.QIcon(":first.png") self.first_appt_button = QtGui.QPushButton(icon,"") self.first_appt_button.setToolTip(_("Launch the Appointment Wizard")) icon = QtGui.QIcon(":back.png") self.prev_appt_button = QtGui.QPushButton(icon, "") self.prev_appt_button.setToolTip(_("Previous appointment")) icon = QtGui.QIcon(":forward.png") self.next_appt_button = QtGui.QPushButton(icon, "") self.next_appt_button.setToolTip(_("Next available appointment")) self.appt_controls_frame = QtGui.QWidget() layout = QtGui.QGridLayout(self.appt_controls_frame) layout.setMargin(1) layout.addWidget(diary_button,0,0) layout.addWidget(self.first_appt_button,0,1) layout.addWidget(self.prev_appt_button,0,2) layout.addWidget(self.next_appt_button,0,3) self.appt_controls_frame.setSizePolicy( QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Minimum)) # now arrange the stacked widget #page 0 - Browsing mode self.addWidget(QtGui.QLabel("Browsing")) #page 1 -- scheduling mode widg = QtGui.QWidget() layout = QtGui.QGridLayout(widg) layout.setMargin(0) layout.addWidget(self.patient_label,0,0) layout.addWidget(self.get_patient_button,0,1) layout.addWidget(self.appt_listView,2,0,1,2) layout.addWidget(self.appt_controls_frame,3,0,1,2) self.addWidget(widg) #page 2 -- blocking mode widg = QtGui.QWidget() layout = QtGui.QVBoxLayout(widg) layout.addWidget(self.block_listView) self.addWidget(widg) #page 4 -- notes mode self.addWidget(QtGui.QLabel("Notes")) #connect signals self.appointment_model.appointment_selected.connect( self.update_selected_appointment) self.get_patient_button.clicked.connect(self.find_patient) self.first_appt_button.clicked.connect(self.show_first_appt) self.prev_appt_button.clicked.connect(self.show_prev_appt) self.next_appt_button.clicked.connect(self.show_next_appt) diary_button.clicked.connect(self.show_pt_diary) self.appt_listView.pressed.connect(self.appointment_pressed) self.appt_listView.clicked.connect(self.appointment_clicked) self.appt_listView.doubleClicked.connect(self.appointment_2x_clicked)
class DiaryScheduleController(QtGui.QStackedWidget): ''' This widget lives down the left side of the diary widget. It provides a way of switching modes for the diary. ''' BROWSE_MODE = 0 SCHEDULE_MODE = 1 BLOCKING_MODE = 2 NOTES_MODE = 3 NOT_SEARCHING = 0 SEARCHING_FORWARDS = 1 SEARCHING_BACKWARDS = 2 mode = BROWSE_MODE search_mode = NOT_SEARCHING appointment_selected = QtCore.pyqtSignal(object) patient_selected = QtCore.pyqtSignal(object) show_first_appointment = QtCore.pyqtSignal() chosen_slot_changed = QtCore.pyqtSignal() find_appt = QtCore.pyqtSignal(object) start_scheduling = QtCore.pyqtSignal() # from embedded pt diary widget advice_signal = QtCore.pyqtSignal(object, object) pt = None primary_slots = [] secondary_slots = [] _chosen_slot = None finding_joint_appointments = False def __init__(self, parent=None): QtGui.QStackedWidget.__init__(self, parent) self.diary_widget = parent self.patient_label = QtGui.QLabel() icon = QtGui.QIcon(":/search.png") self.get_patient_button = QtGui.QPushButton(icon, "") self.get_patient_button.setMaximumWidth(40) self.appt_listView = DraggableList(self) self.block_listView = DraggableList(self) self.item_delegate = ColouredItemDelegate(self) self.appointment_model = SimpleListModel(self) self.appt_listView.setModel(self.appointment_model) self.appt_listView.setItemDelegate(self.item_delegate) self.appt_listView.setSelectionModel( self.appointment_model.selection_model) self.appt_listView.setSelectionMode( QtGui.QListView.ContiguousSelection) block_model = BlockListModel(self) self.block_listView.setModel(block_model) icon = QtGui.QIcon(":vcalendar.png") diary_button = QtGui.QPushButton(icon, _("Diary")) diary_button.setToolTip(_("Open the patient's diary")) icon = QtGui.QIcon(":settings.png") settings_button = QtGui.QPushButton(icon, _("Options")) settings_button.setToolTip(_("Appointment Settings")) icon = QtGui.QIcon(":back.png") self.prev_appt_button = QtGui.QPushButton(icon, "") self.prev_appt_button.setToolTip(_("Previous appointment")) icon = QtGui.QIcon(":forward.png") self.next_appt_button = QtGui.QPushButton(icon, "") self.next_appt_button.setToolTip(_("Next available appointment")) icon = QtGui.QIcon(":forward.png") self.next_day_button = QtGui.QPushButton(icon, "") self.next_day_button.setToolTip(_("Next Day or Week")) icon = QtGui.QIcon(":back.png") self.prev_day_button = QtGui.QPushButton(icon, "") self.prev_day_button.setToolTip(_("Previous Day or Week")) icon = QtGui.QIcon(":first.png") self.first_appt_button = QtGui.QPushButton(icon, "") self.first_appt_button.setToolTip(_("First available appointment")) self.appt_controls_frame = QtGui.QWidget() layout = QtGui.QGridLayout(self.appt_controls_frame) layout.setMargin(1) layout.setSpacing(2) layout.addWidget(diary_button, 0, 0, 1, 2) layout.addWidget(settings_button, 0, 3, 1, 2) layout.addWidget(self.prev_day_button, 1, 0) layout.addWidget(self.prev_appt_button, 1, 1) layout.addWidget(self.first_appt_button, 1, 2) layout.addWidget(self.next_appt_button, 1, 3) layout.addWidget(self.next_day_button, 1, 4) self.appt_controls_frame.setSizePolicy( QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Minimum)) self.search_criteria_webview = QtWebKit.QWebView(self) self.search_criteria_webview.setMinimumHeight(100) self.search_criteria_webview.setHtml( _("No appointment selected for scheduling")) # now arrange the stacked widget # page 0 - Browsing mode self.browsing_webview = QtWebKit.QWebView() self.reset_browsing_webview() self.addWidget(self.browsing_webview) # page 1 -- scheduling mode widg = QtGui.QWidget() layout = QtGui.QGridLayout(widg) layout.setMargin(0) layout.addWidget(self.patient_label, 0, 0) layout.addWidget(self.get_patient_button, 0, 1) layout.addWidget(self.appt_listView, 2, 0, 1, 2) layout.addWidget(self.appt_controls_frame, 3, 0, 1, 2) layout.addWidget(self.search_criteria_webview, 4, 0, 1, 2) self.addWidget(widg) # page 2 -- blocking mode widg = QtGui.QWidget() layout = QtGui.QVBoxLayout(widg) layout.addWidget(self.block_listView) self.addWidget(widg) # page 4 -- notes mode self.notes_label = QtGui.QLabel(_("Select a patient to edit notes")) self.addWidget(self.notes_label) # connect signals self.get_patient_button.clicked.connect(self.find_patient) settings_button.clicked.connect(self.show_settings_dialog) self.prev_appt_button.clicked.connect(self.show_prev_appt) self.next_appt_button.clicked.connect(self.show_next_appt) self.prev_day_button.clicked.connect(self.show_prev_day) self.next_day_button.clicked.connect(self.show_next_day) self.first_appt_button.clicked.connect(self.find_first_appointment) diary_button.clicked.connect(self.show_pt_diary) self.appt_listView.selectionModel().selectionChanged.connect( self.selection_changed) self.appt_listView.doubleClicked.connect(self.appointment_2x_clicked) def set_mode(self, mode): if self.mode == mode: return if mode == self.SCHEDULE_MODE: self.update_patient_label() self.enable_scheduling_buttons() else: self.clear_slots() if mode == self.BROWSE_MODE: self.reset_browsing_webview() self.mode = mode self.setCurrentIndex(mode) def set_search_mode(self, mode): assert mode in (self.NOT_SEARCHING, self.SEARCHING_FORWARDS, self.SEARCHING_BACKWARDS) self.search_mode = mode def cancel_search_mode(self): self.set_search_mode(self.NOT_SEARCHING) def set_search_future(self): self.set_search_mode(self.SEARCHING_FORWARDS) def set_search_past(self): self.set_search_mode(self.SEARCHING_BACKWARDS) @property def searching_future(self): return self.search_mode == self.SEARCHING_FORWARDS @property def searching_past(self): return self.search_mode == self.SEARCHING_BACKWARDS def set_patient(self, pt): self.clear() self.pt = pt if pt is not None: self.appointment_model.load_from_database(self.pt) self.patient_selected.emit(self.pt) self.enable_scheduling_buttons() def get_data(self): ''' loads the appointment model from database, or clears if patient is None ''' LOGGER.debug("schedule control get-data") if self.pt is None: self.clear() return self.appointment_model.load_from_database(self.pt) @property def patient_text(self): if self.pt: return "%s %s (%s)" % ( self.pt.fname, self.pt.sname, self.pt.serialno) else: return _("No patient Selected") @property def serialno(self): if self.pt: return self.pt.serialno return None def find_patient(self): ''' search and load a patient. ''' dl = FindPatientDialog(self) if dl.exec_(): self.clear() pt = BriefPatient(dl.chosen_sno) self.set_patient(pt) self.diary_widget.set_date(QtCore.QDate.currentDate()) self.update_patient_label() def update_patient_label(self): self.patient_label.setText(self.patient_text) @property def selected_appointment(self): ''' the appointment currently selected by user ''' return self.appointment_model.currentAppt @property def secondary_appointment(self): ''' the appointment jointly selected by user ''' return self.appointment_model.secondaryAppt @property def app1_length(self): try: return self.appointment_model.currentAppt.length except AttributeError: return 0 @property def app1_is_scheduled(self): try: return not self.appointment_model.currentAppt.unscheduled except AttributeError: LOGGER.debug("app1_is_scheduled") return False @property def app2_length(self): try: return self.appointment_model.secondaryAppt.length except AttributeError: return 0 @property def app2_is_scheduled(self): try: return not self.appointment_model.secondaryAppt.unscheduled except AttributeError: LOGGER.debug("app2_is_scheduled") return False @property def ignore_emergency_spaces(self): return ApptSettingsDialog.ignore_emergency_spaces def set_selection_mode(self, mode): assert mode in ( self.CLINICIAN_ANY, self.CLINICIAN_ANY_DENT, self.CLINICIAN_ANY_HYG, self.CLINICIAN_SELECTED), "selection mode misunderstood" self.dent_selection_mode = mode @property def selectedClinicians(self): return self.appointment_model.selectedClinicians def _appt_clinicians(self, appt): ''' use the selected appointment and the chosen settings to see who could perform this treatment. ''' if appt.dent in localsettings.activedent_ixs: if ApptSettingsDialog.dentist_policy == \ ApptSettingsDialog.CLINICIAN_ANY_DENT: return localsettings.activedent_ixs if ApptSettingsDialog.dentist_policy == \ ApptSettingsDialog.CLINICIAN_ANY: return \ localsettings.activedent_ixs + localsettings.activehyg_ixs elif appt.dent in localsettings.activehyg_ixs: if ApptSettingsDialog.hygienist_policy == \ ApptSettingsDialog.CLINICIAN_ANY_HYG: return localsettings.activehyg_ixs return (appt.dent, ) @property def appt1_clinicians(self): ''' what clinicians could provide the treatment in appointment 1? ''' appt = self.appointment_model.currentAppt if appt: return self._appt_clinicians(appt) return () @property def appt2_clinicians(self): ''' what clinicians could provide the treatment in appointment 2? ''' appt = self.appointment_model.secondaryAppt if appt: return self._appt_clinicians(appt) return () @property def involvedClinicians(self): return self.appointment_model.involvedClinicians def sizeHint(self): return QtCore.QSize(180, 400) def update_appt_selection(self, appts): ''' sync with the "patient diary" via signal/slot ''' LOGGER.debug( "updating schedule controller appointments, appts='%s'", appts) for appt in appts: if appt.serialno != self.serialno: return self.appointment_model.set_selected_appointments(appts) def update_selected_appointment(self, appt): self.primary_slots = [] self._chosen_slot = None self.enable_scheduling_buttons() def selection_changed(self, selection): LOGGER.debug("ScheduleControl.selection_changed") self.update_search_criteria_webview() self.enable_scheduling_buttons() try: app = self.appointment_model.data(selection.indexes()[-1], QtCore.Qt.UserRole) except IndexError: app = self.appointment_model.currentAppt self.appointment_selected.emit(app) def appointment_2x_clicked(self, index): LOGGER.debug("ScheduleControl.appointment_clicked") self.show_first_appointment.emit() def clear(self): self.reset_browsing_webview() self.appointment_model.clear() self.reset() def reset(self): self.reset_browsing_webview() self.primary_slots = [] self.secondary_slots = [] self._chosen_slot = None self.cancel_search_mode() self.finding_joint_appointments = False def show_settings_dialog(self): ''' show the settings dialog ''' LOGGER.info("showing the settings dialog") dl = ApptSettingsDialog(self) if dl.exec_(): self.selection_changed( self.appt_listView.selectionModel().selection()) @property def _chosen_slot_no(self): try: return self.primary_slots.index(self._chosen_slot) except ValueError: return 0 def show_next_appt(self): self.set_search_future() try: self._chosen_slot = self.primary_slots[self._chosen_slot_no + 1] self.chosen_slot_changed.emit() except IndexError: self.show_next_day() def show_prev_appt(self): self.set_search_past() try: i = self._chosen_slot_no - 1 if i < 0: raise IndexError self._chosen_slot = self.primary_slots[i] self.chosen_slot_changed.emit() except IndexError: self.show_prev_day() def show_next_day(self): ''' catches the signal to make a big jump forwards. will jump a week if in week view ''' self._chosen_slot = None self.set_search_future() self.diary_widget.step_date(True) def show_prev_day(self): ''' catches the signal to make a big jump backwords. will jump a week if in week view ''' self._chosen_slot = None self.set_search_past() self.diary_widget.step_date(False) @property def all_slots(self): for slot in self.primary_slots: yield slot for slot in self.secondary_slots: yield slot def clear_slots(self): # self.reset_browsing_webview() self._chosen_slot = None self.primary_slots = [] self.secondary_slots = [] def reset_browsing_webview(self): self.browsing_webview.setHtml("") def set_primary_slots(self, slots): self.primary_slots = [] for slot in sorted(slots): if (slot.dent in self.appt1_clinicians and slot.day_no not in self.excluded_days): self.primary_slots.append(slot) def set_secondary_slots(self, slots): LOGGER.debug("filtering secondary slots %s", slots) self.secondary_slots = [] for slot in sorted(slots): slot.is_primary = False if (slot.dent in self.appt2_clinicians and slot.day_no not in self.excluded_days): self.secondary_slots.append(slot) def set_slots_from_day_app_data(self, app_data): self.set_primary_slots([] if self.app1_is_scheduled else app_data.slots(self.app1_length, self.ignore_emergency_spaces)) app2_slots = [] if self.is_searching_for_double_appointments and \ not self.app2_is_scheduled: self.set_secondary_slots( app_data.slots(self.app2_length, self.ignore_emergency_spaces, busy_serialno=self.serialno) ) app1_slots = set([]) if self.app1_is_scheduled: iterset = [self.appointment_model.currentAppt.to_freeslot()] else: iterset = self.primary_slots for app1_slot in iterset: for app2_slot in self.secondary_slots: wait = app1_slot.wait_time(self.app1_length, self.app2_length, app2_slot) if wait is not None and wait <= MAX_WAIT: app2_slots.append(app2_slot) app1_slots.add(app1_slot) if not self.app1_is_scheduled: self.set_primary_slots(app1_slots) self.set_secondary_slots(app2_slots) def get_weekview_slots(self, weekdates): ''' calculate available slots for weekdates (list of QDates) ''' today = QtCore.QDate.currentDate() startday = today if today in weekdates else weekdates[0] # monday sunday = weekdates[6] # sunday # check for suitable apts in the selected WEEK! all_slots = appointments.future_slots( startday.toPyDate(), sunday.toPyDate(), self.appt1_clinicians, busy_serialno=self.serialno, override_emergencies=self.ignore_emergency_spaces) if self.app1_is_scheduled: self.set_primary_slots([]) else: self.set_primary_slots( appointments.getLengthySlots(all_slots, self.app1_length)) app2_slots = [] if self.is_searching_for_double_appointments and \ not self.app2_is_scheduled: all_slots = appointments.future_slots( startday.toPyDate(), sunday.toPyDate(), self.appt2_clinicians, busy_serialno=self.serialno, override_emergencies=self.ignore_emergency_spaces) self.set_secondary_slots( appointments.getLengthySlots(all_slots, self.app2_length) ) app1_slots = set([]) if self.app1_is_scheduled: iterset = [self.appointment_model.currentAppt.to_freeslot()] else: iterset = self.primary_slots for app1_slot in iterset: for app2_slot in self.secondary_slots: wait = app1_slot.wait_time(self.app1_length, self.app2_length, app2_slot) if wait is not None and wait <= MAX_WAIT: app2_slots.append(app2_slot) app1_slots.add(app1_slot) if not self.app1_is_scheduled: self.set_primary_slots(app1_slots) self.set_secondary_slots(app2_slots) @property def chosen_2nd_slots(self): if not (self.appointment_model.currentAppt is None or self.appointment_model.secondaryAppt is None): for app2_slot in self.secondary_slots: wait = self.chosen_slot.wait_time(self.app1_length, self.app2_length, app2_slot) if wait is not None and wait <= MAX_WAIT: yield app2_slot else: LOGGER.debug("no appointments selected") @property def last_appt_date(self): ''' returns the latest date of patient's appointments, or today's date if none found ''' last_d = QtCore.QDate.currentDate().toPyDate() for appt in self.appointment_model.scheduledList: if appt.date > last_d: last_d = appt.date return last_d @property def is_searching(self): ''' are we looking for a slot? ''' app1 = self.appointment_model.currentAppt if app1 is not None and app1.unscheduled: return True app2 = self.appointment_model.secondaryAppt if app2 is not None and app2.unscheduled: return True return False @property def is_searching_for_double_appointments(self): return self.appointment_model.secondaryAppt is not None @property def search_again(self): ''' this determines whether it is worth continuing (on a different date) ''' return (self.is_searching and self.search_mode != self.NOT_SEARCHING and len(self.selectedClinicians) > 0 and len(self.primary_slots) == 0) def find_first_appointment(self): LOGGER.debug("find_first_appointment") self.show_first_appointment.emit() @property def chosen_slot(self): if self.primary_slots == []: if self.app1_is_scheduled: return self.appointment_model.currentAppt.to_freeslot() return None if self._chosen_slot is None: if self.searching_past: self._chosen_slot = self.primary_slots[-1] else: self._chosen_slot = self.primary_slots[0] return self._chosen_slot def set_chosen_slot(self, slot): self._chosen_slot = slot def set_chosen_2nd_slot(self, slot): ''' user has clicked on a secondary slot - we need to switch the appointments over! ''' LOGGER.debug("set_chosen_2nd_slot %s", slot) model = self.appointment_model.selection_model selection = model.selection() selection.swap(0, 1) model.select(selection, model.ClearAndSelect) self.selection_changed( self.appointment_model.selection_model.selection()) self.appointment_model.selection_model.emitSelectionChanged( selection, QtGui.QItemSelection()) slot.is_primary = True self.set_chosen_slot(slot) self.chosen_slot_changed.emit() @property def excluded_days(self): return ApptSettingsDialog.excluded_days def show_pt_diary(self): if self.pt is None: QtGui.QMessageBox.information(self, _("error"), _("No patient selected")) return def _find_appt(appt): dl.accept() self.find_appt.emit(appt) def _start_scheduling(): dl.accept() self.get_data() self.appointment_model.selection_model.reset() appts = pt_diary_widget.selected_appointments self.update_appt_selection(appts) QtCore.QTimer.singleShot(100, self.start_scheduling.emit) pt_diary_widget = PtDiaryWidget() pt_diary_widget.find_appt.connect(_find_appt) pt_diary_widget.start_scheduling.connect(_start_scheduling) pt_diary_widget.set_patient(self.pt) dl = QtGui.QDialog(self) but_box = QtGui.QDialogButtonBox(dl) but = but_box.addButton(_("Close"), but_box.AcceptRole) but.clicked.connect(dl.accept) layout = QtGui.QVBoxLayout(dl) layout.addWidget(pt_diary_widget) layout.addStretch() layout.addWidget(but_box) dl.exec_() self.appointment_model.load_from_database(self.pt) self.enable_scheduling_buttons() def enable_scheduling_buttons(self): enabled = self.is_searching for but in (self.next_appt_button, self.next_day_button, self.prev_appt_button, self.prev_day_button, self.first_appt_button): but.setEnabled(enabled) self.update_search_criteria_webview() def begin_make_appointment(self): ''' this is when an external widget calls for us to start the process of starting an appointment ''' self.set_mode(self.SCHEDULE_MODE) self.enable_scheduling_buttons() LOGGER.debug("checking appointment settings %s", ApptSettingsDialog.is_default_settings()) if not ApptSettingsDialog.is_default_settings(): dl = ApptSettingsResetDialog(self) if dl.exec_(): if dl.show_settings_dialog: self.show_settings_dialog() else: ApptSettingsDialog.reset() def check_schedule_status(self, automatic): ''' this is called by the diary widget when the books have been laid out whilst in schedule mode. Inform the user ''' assert self.mode == self.SCHEDULE_MODE, "not in schedule mode" LOGGER.debug( "check_schedule_status %s", self.diary_widget.selected_date()) if automatic: # this has been called by a timer update to the diary, # and therefore NOT user interaction LOGGER.debug("automatic call to check_schedule_status... ignoring") return try: date_ = self.diary_widget.selected_date().toPyDate() except AttributeError: # self.diary_widget is None? LOGGER.debug("date check error", exc_info=1) date_ = localsettings.currentDay() if not self.appointment_model.selectedAppts: self.advice_signal.emit( _("Please select an appointment to begin scheduling"), 0) elif self.app1_is_scheduled: if self.appointment_model.secondaryAppt is None: self.advice_signal.emit( _("appointment is already scheduled"), 0) elif self.app2_is_scheduled: self.advice_signal.emit(_("Joint appointment Scheduled"), 0) elif not list(self.chosen_2nd_slots): self.advice_signal.emit( _("Joint appointment is not possible with the " "chosen primary appointment"), 1) elif self.search_again: LOGGER.debug("automatic searching") if date_ > localsettings.BOOKEND: self.advice_signal.emit( u'''<b>%s<br />%s</b><hr /><em>(%s)</em> <ul> <li>%s</li><li>%s</li><li>%s</li><li>%s</li> </ul>''' % ( _("This date is beyond the diary limit."), _("Please search again with different criteria."), _("for instance..."), _("no excluded days"), _("ignore emergencies"), _("add or view more clinicians"), _("or you have requested an impossible appointment!")), 1) self.cancel_search_mode() self.diary_widget.set_date(localsettings.currentDay()) elif date_ < localsettings.currentDay(): self.advice_signal.emit( _("You can't schedule an appointment in the past"), 1) self.diary_widget.set_date(localsettings.currentDay()) else: self.diary_widget.step_date(self.searching_future) elif date_ > localsettings.BOOKEND: self.advice_signal.emit( _("This date is beyond the diary limit."), 1) elif date_ < localsettings.currentDay(): self.advice_signal.emit( _("You can't schedule an appointment in the past"), 1) self.diary_widget.set_date(localsettings.currentDay()) elif self.chosen_slot is None and not list(self.chosen_2nd_slots): if self.diary_widget: if self.diary_widget.viewing_week: message = "%s %s" % ( _("in this week"), self.diary_widget.selected_date().weekNumber()) else: message = "%s (%s)" % ( _("on this day"), localsettings.formatDate( self.diary_widget.selected_date().toPyDate()) ) else: message = "" # should only happen if __name__ == "__main__" message = "%s %s" % (_("No Slots Found"), message) self.advice_signal.emit(message, 0) @property def _dentist_message(self): if not self.appointment_model.dentists_involved: return "" if ApptSettingsDialog.dentist_policy == \ ApptSettingsDialog.CLINICIAN_SELECTED: return _("Specified Dentist only") elif ApptSettingsDialog.dentist_policy == \ ApptSettingsDialog.CLINICIAN_ANY_DENT: return _("Any Dentist") else: # CLINICIAN_ANY return _("Any Clinician") @property def _hygienist_message(self): if not self.appointment_model.hygienists_involved: return "" if ApptSettingsDialog.hygienist_policy == \ ApptSettingsDialog.CLINICIAN_SELECTED: return _("Specified Hygienist only for hyg appts") if ApptSettingsDialog.hygienist_policy == \ ApptSettingsDialog.CLINICIAN_ANY_HYG: return _("Any Hygienist for hyg appts") else: # CLINICIAN_ANY return _("Any Clinician for hyg appts") @property def _joint_message(self): if self.is_searching_for_double_appointments: return _("Joint Appointments") return "" @property def _emergency_message(self): if ApptSettingsDialog.ignore_emergency_spaces: return "%s" % _("Overwrite Emergencies") return "" @property def _day_message(self): if self.excluded_days == []: return _("Any Day") days = [] for i, day in enumerate( (_("Mon"), _("Tue"), _("Wed"), _("Thu"), _("Fri"), _("Sat"), _("Sun")), 1): if i not in self.excluded_days: days.append(day) return ", ".join(days) def update_search_criteria_webview(self): if self.appointment_model.currentAppt is None: html = _("No Appointment Selected") else: html = '%s<ul><li>%s</li></ul>' % ( _("Search Settings"), "</li><li>". join( [s for s in ( self._dentist_message, self._hygienist_message, self._joint_message, self._emergency_message, self._day_message) if s != ""]) ) self.search_criteria_webview.setHtml(html) def update_highlighted_appointment(self): ''' the diary widget selected appointment has changed. ''' app = self.diary_widget.highlighted_appointment LOGGER.debug("appointment highlighted %s", app) if app is None: self.reset_browsing_webview() return if self.mode == self.NOTES_MODE: self.notes_label.setText( "<h3>%s</h3>%s<br />%s" % ( _("View/edit today's notes for "), app.name, app.serialno ) ) return self.notes_label.setText("") if self.mode != self.BROWSE_MODE: return feedback = FEEDBACK % ( app.name, app.serialno, localsettings.readableDate( self.diary_widget.selected_date().toPyDate()), "%02d:%02d" % (app.start // 100, app.start % 100), "%02d:%02d" % (app.end // 100, app.end % 100), '</li><li class="trt">'.join( [val for val in (app.trt1, app.trt2, app.trt3) if val != ""]) ) if app.memo != "": feedback += "<hr />%s<br /><i>%s</i>" % (_("Memo"), app.memo) try: datestamp = app.timestamp.date() feedback += \ "<hr />%s<br />%s (%s %s)" % ( _("Made"), localsettings.formatDate(datestamp), _("at"), localsettings.pyTimeToHumantime( app.timestamp)) except AttributeError: pass if app.mh_form_check_date or app.mh_form_required: feedback += "<hr />" if app.mh_form_check_date: feedback += "%s %s<br />" % ( _("last mh form"), localsettings.formatDate( app.mh_form_check_date) ) if app.mh_form_required: feedback += "%s" % _("MH CHECK REQUIRED") feedback = "%s<body></html>" % feedback self.browsing_webview.setHtml(feedback)
class DiaryScheduleController(QtGui.QStackedWidget): BROWSE_MODE = 0 SCHEDULE_MODE = 1 BLOCK_MODE = 2 NOTES_MODE = 3 mode = BROWSE_MODE CLINICIAN_SELECTED = 0 CLINICIAN_ANY_DENT = 1 CLINICIAN_ANY_HYG = 2 CLINICIAN_ANY = 3 selection_mode = CLINICIAN_SELECTED appointment_selected = QtCore.pyqtSignal(object) patient_selected = QtCore.pyqtSignal(object) show_first_appointment = QtCore.pyqtSignal() chosen_slot_changed = QtCore.pyqtSignal() move_on = QtCore.pyqtSignal(object) find_appt = QtCore.pyqtSignal(object) start_scheduling = QtCore.pyqtSignal() pt = None available_slots = [] hygienist_slots = [] _chosen_slot = None excluded_days = [] ignore_emergency_spaces = False finding_joint_appointments = False use_last_slot = False pt_diary_widget = None def __init__(self, parent=None): QtGui.QStackedWidget.__init__(self, parent) self.patient_label = QtGui.QLabel() icon = QtGui.QIcon(":/search.png") self.get_patient_button = QtGui.QPushButton(icon, "") self.get_patient_button.setMaximumWidth(40) self.appt_listView = DraggableList(True, self) self.block_listView = DraggableList(False, self) self.appointment_model = SimpleListModel(self) self.appt_listView.setModel(self.appointment_model) self.appt_listView.setSelectionModel( self.appointment_model.selection_model) self.appt_listView.setSelectionMode(QtGui.QListView.SingleSelection) block_model = BlockListModel(self) self.block_listView.setModel(block_model) icon = QtGui.QIcon(":vcalendar.png") diary_button = QtGui.QPushButton(icon, "") diary_button.setToolTip(_("Open the patient's diary")) icon = QtGui.QIcon(":first.png") self.first_appt_button = QtGui.QPushButton(icon,"") self.first_appt_button.setToolTip(_("Launch the Appointment Wizard")) icon = QtGui.QIcon(":back.png") self.prev_appt_button = QtGui.QPushButton(icon, "") self.prev_appt_button.setToolTip(_("Previous appointment")) icon = QtGui.QIcon(":forward.png") self.next_appt_button = QtGui.QPushButton(icon, "") self.next_appt_button.setToolTip(_("Next available appointment")) self.appt_controls_frame = QtGui.QWidget() layout = QtGui.QGridLayout(self.appt_controls_frame) layout.setMargin(1) layout.addWidget(diary_button,0,0) layout.addWidget(self.first_appt_button,0,1) layout.addWidget(self.prev_appt_button,0,2) layout.addWidget(self.next_appt_button,0,3) self.appt_controls_frame.setSizePolicy( QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Minimum)) # now arrange the stacked widget #page 0 - Browsing mode self.addWidget(QtGui.QLabel("Browsing")) #page 1 -- scheduling mode widg = QtGui.QWidget() layout = QtGui.QGridLayout(widg) layout.setMargin(0) layout.addWidget(self.patient_label,0,0) layout.addWidget(self.get_patient_button,0,1) layout.addWidget(self.appt_listView,2,0,1,2) layout.addWidget(self.appt_controls_frame,3,0,1,2) self.addWidget(widg) #page 2 -- blocking mode widg = QtGui.QWidget() layout = QtGui.QVBoxLayout(widg) layout.addWidget(self.block_listView) self.addWidget(widg) #page 4 -- notes mode self.addWidget(QtGui.QLabel("Notes")) #connect signals self.appointment_model.appointment_selected.connect( self.update_selected_appointment) self.get_patient_button.clicked.connect(self.find_patient) self.first_appt_button.clicked.connect(self.show_first_appt) self.prev_appt_button.clicked.connect(self.show_prev_appt) self.next_appt_button.clicked.connect(self.show_next_appt) diary_button.clicked.connect(self.show_pt_diary) self.appt_listView.pressed.connect(self.appointment_pressed) self.appt_listView.clicked.connect(self.appointment_clicked) self.appt_listView.doubleClicked.connect(self.appointment_2x_clicked) def set_mode(self, mode): if self.mode == mode: return if mode == self.SCHEDULE_MODE: self.update_patient_label() self.enable_scheduling_buttons() self.mode = mode self.setCurrentIndex(mode) def set_patient(self, pt): self.clear() self.pt = pt if pt is not None: self.appointment_model.load_from_database(self.pt) self.patient_selected.emit(self.pt) self.enable_scheduling_buttons() def set_chosen_appointment(self, appointment): self.appointment_model.set_current_appt(appointment) def get_data(self): if self.pt is None: self.clear() return self.appointment_model.load_from_database(self.pt) @property def patient_text(self): if self.pt: return "%s %s (%s)"% ( self.pt.fname, self.pt.sname, self.pt.serialno) else: return _("No patient Selected") def find_patient(self): dl = FindPatientDialog(self) if dl.exec_(): self.clear() pt = BriefPatient(dl.chosen_sno) self.set_patient(pt) self.update_patient_label() def update_patient_label(self): self.patient_label.setText(self.patient_text) @property def min_slot_length(self): msl = 0 if self.mode == self.SCHEDULE_MODE: msl = self.appointment_model.min_slot_length return msl @property def min_hyg_slot_length(self): msl = None if self.mode == self.SCHEDULE_MODE: msl = self.appointment_model.min_unscheduled_hyg_slot_length return msl def set_selection_mode(self, mode): assert mode in ( self.CLINICIAN_ANY, self.CLINICIAN_ANY_DENT, self.CLINICIAN_ANY_HYG, self.CLINICIAN_SELECTED), "selection mode misunderstood" self.selection_mode = mode @property def selectedClinicians(self): if self.selection_mode == self.CLINICIAN_ANY_HYG: return localsettings.activehyg_ixs if self.selection_mode == self.CLINICIAN_ANY_DENT: return localsettings.activedent_ixs if self.selection_mode == self.CLINICIAN_ANY: return localsettings.activedent_ixs + localsettings.activehyg_ixs return self.appointment_model.selectedClinicians @property def involvedClinicians(self): return self.appointment_model.involvedClinicians def set_ignore_emergency_spaces(self, bool_): self.ignore_emergency_spaces = bool_ def sizeHint(self): return QtCore.QSize(150, 200) def update_appt_selection(self, appt): ''' sync with the "patient diary" via signal/slot ''' if appt is None or self.pt is None: return if appt.serialno != self.pt.serialno: return index = self.appointment_model.set_current_appt(appt) self.appt_listView.setCurrentIndex(index) def update_selected_appointment(self, appt): self.available_slots = [] self._chosen_slot = None self.enable_scheduling_buttons() def appointment_clicked(self, index): logging.debug("ScheduleControl.appointment_clicked") def appointment_pressed(self, index): logging.debug("ScheduleControl.appointment_pressed") self.appointment_selected.emit(self.appointment_model.currentAppt) def appointment_2x_clicked(self, index): logging.debug("ScheduleControl.appointment_clicked") self.show_first_appointment.emit() def clear(self): self.appointment_model.clear() self.reset() def reset(self): self.available_slots = [] self.hygienist_slots = [] self._chosen_slot = None self.finding_joint_appointments = False def show_first_appt(self): ''' resets the chosen slot and emits show_first_appointment signal ''' self._chosen_slot = None self.show_first_appointment.emit() @property def _chosen_slot_no(self): try: return self.available_slots.index(self._chosen_slot) except ValueError: return 0 def show_next_appt(self): try: self._chosen_slot = self.available_slots[self._chosen_slot_no + 1] self.chosen_slot_changed.emit() except IndexError: self._chosen_slot = None self.move_on.emit(True) def show_prev_appt(self): try: i = self._chosen_slot_no - 1 if i < 0: raise IndexError self._chosen_slot = self.available_slots[i] self.chosen_slot_changed.emit() except IndexError: self._chosen_slot = None self.move_on.emit(False) def set_available_slots(self, slots): self.available_slots = [] self.hygienist_slots = [] for slot in sorted(slots): if (slot.dent in self.selectedClinicians and slot.day_no not in self.excluded_days) : self.available_slots.append(slot) def set_joint_slots(self, dent_slots, hyg_slots, max_wait=10): logging.debug( "ScheduleControl.set join slots %s %s"% (dent_slots, hyg_slots)) self.available_slots = [] self.hygienist_slots = [] all_dent_slots = [] all_hyg_slots = [] for slot in sorted(dent_slots): if (slot.dent in self.selectedClinicians and slot.day_no not in self.excluded_days) : all_dent_slots.append(slot) for slot in sorted(hyg_slots): if slot.day_no not in self.excluded_days : all_hyg_slots.append(slot) appt = self.appointment_model.currentAppt for slot in all_dent_slots: hyg_slot, wait = slot.best_joint( appt.length, self.min_hyg_slot_length, all_hyg_slots) if wait <= max_wait: self.available_slots.append(slot) self.hygienist_slots.append(hyg_slot) @property def chosen_hyg_slot(self): if self.hygienist_slots == [] or self.chosen_slot is None: return None appt = self.appointment_model.currentAppt best_slot, wait = self.chosen_slot.best_joint( appt.length, self.min_hyg_slot_length, self.hygienist_slots) logging.info("WAIT TIME FOR HYGIENIST = %s minutes"% wait) return best_slot @property def last_appt_date(self): ''' returns the latest date of patient's appointments, or today's date if none found ''' last_d = QtCore.QDate.currentDate().toPyDate() for appt in self.appointment_model.scheduledList: if appt.date > last_d: last_d = appt.date return last_d @property def is_searching(self): appt = self.appointment_model.currentAppt return appt is not None and appt.unscheduled @property def search_again(self): ''' this determines whether it is worth continuing ''' return ( self.is_searching and len(self.selectedClinicians)>0 and len(self.available_slots)==0 ) @property def chosen_slot(self): if self.available_slots == []: return None if self._chosen_slot is None: if self.use_last_slot: i = -1 self.use_last_slot = False else: i = 0 self._chosen_slot = self.available_slots[i] return self._chosen_slot def set_excluded_days(self, days): self.excluded_days = days def show_pt_diary(self): if self.pt is None: QtGui.QMessageBox.information(self, _("error"), _("No patient selected")) return def _find_appt(appt): dl.accept() self.find_appt.emit(appt) def _start_scheduling(): dl.accept() QtCore.QTimer.singleShot(100, self.start_scheduling.emit) pt_diary_widget = PtDiaryWidget() pt_diary_widget.find_appt.connect(_find_appt) pt_diary_widget.start_scheduling.connect(_start_scheduling) pt_diary_widget.appointment_selected.connect( self.appointment_model.set_current_appt) pt_diary_widget.set_patient(self.pt) pt_diary_widget.layout_ptDiary() dl = QtGui.QDialog(self) but_box = QtGui.QDialogButtonBox(dl) but = but_box.addButton(_("Close"), but_box.AcceptRole) but.clicked.connect(dl.accept) layout = QtGui.QVBoxLayout(dl) layout.addWidget(pt_diary_widget) layout.addStretch() layout.addWidget(but_box) dl.exec_() self.appointment_model.load_from_database(self.pt) self.enable_scheduling_buttons() #now force diary relayout self.appointment_selected.emit(self.appointment_model.currentAppt) def enable_scheduling_buttons(self): appt = self.appointment_model.currentAppt enabled = (appt is not None and appt.unscheduled) for but in (self.next_appt_button, self.prev_appt_button, self.first_appt_button): but.setEnabled(enabled)
def __init__(self, parent=None): QtGui.QDialog.__init__(self, parent) self.setWindowTitle("Drag Drop Test") self.pt = duckPt() layout = QtGui.QGridLayout(self) self.model = SimpleListModel() appts = appointments.get_pts_appts(duckPt()) self.model.set_appointments(appts, appts[1]) self.appt_listView = DraggableList(True) self.appt_listView.setModel(self.model) self.block_model = BlockListModel() self.blockView = DraggableList(False) self.blockView.setModel(self.block_model) self.book = appointmentwidget.AppointmentWidget("1000", "1200", self) self.book.setDentist(1) self.book.setStartTime(1015) self.book.setEndTime(1145) for appoint in ( (1, 1030, 1045, 'MCDONALD I', 6155, 'EXAM', '', '', '', 1, 73, 0, 0, datetime.datetime.now()), (1, 1115, 1130, 'EMERGENCY', 0, '', '', '', '', -128, 0, 0, 0, datetime.datetime.now())): self.book.setAppointment(appoint) self.OVbook = AppointmentOverviewWidget("1000", "1200", 15, 2, self) d1 = appointments.DentistDay(1) d1.start = 1015 d1.end = 1145 d1.memo = "hello" d2 = appointments.DentistDay(4) d2.start = 1015 d2.end = 1145 self.OVbook.dents = [d1, d2] self.OVbook.clear() self.OVbook.init_dicts() for d in (d1, d2): self.OVbook.setStartTime(d) self.OVbook.setEndTime(d) self.OVbook.setMemo(d) self.OVbook.setFlags(d) slot = appointments.FreeSlot(datetime.datetime(2009, 2, 2, 10, 45), 1, 20) slot2 = appointments.FreeSlot(datetime.datetime(2009, 2, 2, 11, 0o5), 4, 60) self.OVbook.addSlot(slot) self.OVbook.addSlot(slot2) appt = appointments.WeekViewAppointment() appt.mpm = 10 * 60 + 30 appt.length = 15 appt.dent = 1 self.OVbook.appts[1] = (appt,) emerg = appointments.WeekViewAppointment() emerg.mpm = 11 * 60 + 15 emerg.length = 15 emerg.reason = "emergency" self.OVbook.eTimes[1] = (emerg,) self.OVbook.setMinimumWidth(200) self.tw = QtGui.QTabWidget(self) self.tw.addTab(self.book, "day") self.tw.addTab(self.OVbook, "week") layout.addWidget(self.appt_listView, 0, 0) layout.addWidget(self.blockView, 2, 0) layout.addWidget(self.tw, 0, 1, 3, 1) # self.tw.setCurrentIndex(1) self.model.appointment_selected.connect(slot_catcher)
def __init__(self, parent=None): QtGui.QStackedWidget.__init__(self, parent) self.diary_widget = parent self.patient_label = QtGui.QLabel() icon = QtGui.QIcon(":/search.png") self.get_patient_button = QtGui.QPushButton(icon, "") self.get_patient_button.setMaximumWidth(40) self.appt_listView = DraggableList(self) self.block_listView = DraggableList(self) self.item_delegate = ColouredItemDelegate(self) self.appointment_model = SimpleListModel(self) self.appt_listView.setModel(self.appointment_model) self.appt_listView.setItemDelegate(self.item_delegate) self.appt_listView.setSelectionModel( self.appointment_model.selection_model) self.appt_listView.setSelectionMode( QtGui.QListView.ContiguousSelection) block_model = BlockListModel(self) self.block_listView.setModel(block_model) icon = QtGui.QIcon(":vcalendar.png") diary_button = QtGui.QPushButton(icon, _("Diary")) diary_button.setToolTip(_("Open the patient's diary")) icon = QtGui.QIcon(":settings.png") settings_button = QtGui.QPushButton(icon, _("Options")) settings_button.setToolTip(_("Appointment Settings")) icon = QtGui.QIcon(":back.png") self.prev_appt_button = QtGui.QPushButton(icon, "") self.prev_appt_button.setToolTip(_("Previous appointment")) icon = QtGui.QIcon(":forward.png") self.next_appt_button = QtGui.QPushButton(icon, "") self.next_appt_button.setToolTip(_("Next available appointment")) icon = QtGui.QIcon(":forward.png") self.next_day_button = QtGui.QPushButton(icon, "") self.next_day_button.setToolTip(_("Next Day or Week")) icon = QtGui.QIcon(":back.png") self.prev_day_button = QtGui.QPushButton(icon, "") self.prev_day_button.setToolTip(_("Previous Day or Week")) icon = QtGui.QIcon(":first.png") self.first_appt_button = QtGui.QPushButton(icon, "") self.first_appt_button.setToolTip(_("First available appointment")) self.appt_controls_frame = QtGui.QWidget() layout = QtGui.QGridLayout(self.appt_controls_frame) layout.setMargin(1) layout.setSpacing(2) layout.addWidget(diary_button, 0, 0, 1, 2) layout.addWidget(settings_button, 0, 3, 1, 2) layout.addWidget(self.prev_day_button, 1, 0) layout.addWidget(self.prev_appt_button, 1, 1) layout.addWidget(self.first_appt_button, 1, 2) layout.addWidget(self.next_appt_button, 1, 3) layout.addWidget(self.next_day_button, 1, 4) self.appt_controls_frame.setSizePolicy( QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Minimum)) self.search_criteria_webview = QtWebKit.QWebView(self) self.search_criteria_webview.setMinimumHeight(100) self.search_criteria_webview.setHtml( _("No appointment selected for scheduling")) # now arrange the stacked widget # page 0 - Browsing mode self.browsing_webview = QtWebKit.QWebView() self.reset_browsing_webview() self.addWidget(self.browsing_webview) # page 1 -- scheduling mode widg = QtGui.QWidget() layout = QtGui.QGridLayout(widg) layout.setMargin(0) layout.addWidget(self.patient_label, 0, 0) layout.addWidget(self.get_patient_button, 0, 1) layout.addWidget(self.appt_listView, 2, 0, 1, 2) layout.addWidget(self.appt_controls_frame, 3, 0, 1, 2) layout.addWidget(self.search_criteria_webview, 4, 0, 1, 2) self.addWidget(widg) # page 2 -- blocking mode widg = QtGui.QWidget() layout = QtGui.QVBoxLayout(widg) layout.addWidget(self.block_listView) self.addWidget(widg) # page 4 -- notes mode self.notes_label = QtGui.QLabel(_("Select a patient to edit notes")) self.addWidget(self.notes_label) # connect signals self.get_patient_button.clicked.connect(self.find_patient) settings_button.clicked.connect(self.show_settings_dialog) self.prev_appt_button.clicked.connect(self.show_prev_appt) self.next_appt_button.clicked.connect(self.show_next_appt) self.prev_day_button.clicked.connect(self.show_prev_day) self.next_day_button.clicked.connect(self.show_next_day) self.first_appt_button.clicked.connect(self.find_first_appointment) diary_button.clicked.connect(self.show_pt_diary) self.appt_listView.selectionModel().selectionChanged.connect( self.selection_changed) self.appt_listView.doubleClicked.connect(self.appointment_2x_clicked)
def __init__(self, parent=None): QtGui.QStackedWidget.__init__(self, parent) self.patient_label = QtGui.QLabel() icon = QtGui.QIcon(":/search.png") self.get_patient_button = QtGui.QPushButton(icon, "") self.get_patient_button.setMaximumWidth(40) self.appt_listView = DraggableList(True, self) self.block_listView = DraggableList(False, self) self.appointment_model = SimpleListModel(self) self.appt_listView.setModel(self.appointment_model) self.appt_listView.setSelectionModel( self.appointment_model.selection_model) self.appt_listView.setSelectionMode(QtGui.QListView.SingleSelection) block_model = BlockListModel(self) self.block_listView.setModel(block_model) icon = QtGui.QIcon(":vcalendar.png") diary_button = QtGui.QPushButton(icon, "") diary_button.setToolTip(_("Open the patient's diary")) icon = QtGui.QIcon(":first.png") self.first_appt_button = QtGui.QPushButton(icon, "") self.first_appt_button.setToolTip(_("Launch the Appointment Wizard")) icon = QtGui.QIcon(":back.png") self.prev_appt_button = QtGui.QPushButton(icon, "") self.prev_appt_button.setToolTip(_("Previous appointment")) icon = QtGui.QIcon(":forward.png") self.next_appt_button = QtGui.QPushButton(icon, "") self.next_appt_button.setToolTip(_("Next available appointment")) self.appt_controls_frame = QtGui.QWidget() layout = QtGui.QGridLayout(self.appt_controls_frame) layout.setMargin(1) layout.addWidget(diary_button, 0, 0) layout.addWidget(self.first_appt_button, 0, 1) layout.addWidget(self.prev_appt_button, 0, 2) layout.addWidget(self.next_appt_button, 0, 3) self.appt_controls_frame.setSizePolicy( QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Minimum)) # now arrange the stacked widget # page 0 - Browsing mode self.addWidget(QtGui.QLabel("Browsing")) # page 1 -- scheduling mode widg = QtGui.QWidget() layout = QtGui.QGridLayout(widg) layout.setMargin(0) layout.addWidget(self.patient_label, 0, 0) layout.addWidget(self.get_patient_button, 0, 1) layout.addWidget(self.appt_listView, 2, 0, 1, 2) layout.addWidget(self.appt_controls_frame, 3, 0, 1, 2) self.addWidget(widg) # page 2 -- blocking mode widg = QtGui.QWidget() layout = QtGui.QVBoxLayout(widg) layout.addWidget(self.block_listView) self.addWidget(widg) # page 4 -- notes mode self.addWidget(QtGui.QLabel("Notes")) # connect signals self.appointment_model.appointment_selected.connect( self.update_selected_appointment) self.get_patient_button.clicked.connect(self.find_patient) self.first_appt_button.clicked.connect(self.show_first_appt) self.prev_appt_button.clicked.connect(self.show_prev_appt) self.next_appt_button.clicked.connect(self.show_next_appt) diary_button.clicked.connect(self.show_pt_diary) self.appt_listView.pressed.connect(self.appointment_pressed) self.appt_listView.clicked.connect(self.appointment_clicked) self.appt_listView.doubleClicked.connect(self.appointment_2x_clicked)
class DiaryScheduleController(QtGui.QStackedWidget): BROWSE_MODE = 0 SCHEDULE_MODE = 1 BLOCK_MODE = 2 NOTES_MODE = 3 mode = BROWSE_MODE CLINICIAN_SELECTED = 0 CLINICIAN_ANY_DENT = 1 CLINICIAN_ANY_HYG = 2 CLINICIAN_ANY = 3 selection_mode = CLINICIAN_SELECTED appointment_selected = QtCore.pyqtSignal(object) patient_selected = QtCore.pyqtSignal(object) show_first_appointment = QtCore.pyqtSignal() chosen_slot_changed = QtCore.pyqtSignal() move_on = QtCore.pyqtSignal(object) find_appt = QtCore.pyqtSignal(object) start_scheduling = QtCore.pyqtSignal() pt = None available_slots = [] hygienist_slots = [] _chosen_slot = None excluded_days = [] ignore_emergency_spaces = False finding_joint_appointments = False use_last_slot = False pt_diary_widget = None def __init__(self, parent=None): QtGui.QStackedWidget.__init__(self, parent) self.patient_label = QtGui.QLabel() icon = QtGui.QIcon(":/search.png") self.get_patient_button = QtGui.QPushButton(icon, "") self.get_patient_button.setMaximumWidth(40) self.appt_listView = DraggableList(True, self) self.block_listView = DraggableList(False, self) self.appointment_model = SimpleListModel(self) self.appt_listView.setModel(self.appointment_model) self.appt_listView.setSelectionModel( self.appointment_model.selection_model) self.appt_listView.setSelectionMode(QtGui.QListView.SingleSelection) block_model = BlockListModel(self) self.block_listView.setModel(block_model) icon = QtGui.QIcon(":vcalendar.png") diary_button = QtGui.QPushButton(icon, "") diary_button.setToolTip(_("Open the patient's diary")) icon = QtGui.QIcon(":first.png") self.first_appt_button = QtGui.QPushButton(icon, "") self.first_appt_button.setToolTip(_("Launch the Appointment Wizard")) icon = QtGui.QIcon(":back.png") self.prev_appt_button = QtGui.QPushButton(icon, "") self.prev_appt_button.setToolTip(_("Previous appointment")) icon = QtGui.QIcon(":forward.png") self.next_appt_button = QtGui.QPushButton(icon, "") self.next_appt_button.setToolTip(_("Next available appointment")) self.appt_controls_frame = QtGui.QWidget() layout = QtGui.QGridLayout(self.appt_controls_frame) layout.setMargin(1) layout.addWidget(diary_button, 0, 0) layout.addWidget(self.first_appt_button, 0, 1) layout.addWidget(self.prev_appt_button, 0, 2) layout.addWidget(self.next_appt_button, 0, 3) self.appt_controls_frame.setSizePolicy( QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Minimum)) # now arrange the stacked widget # page 0 - Browsing mode self.addWidget(QtGui.QLabel("Browsing")) # page 1 -- scheduling mode widg = QtGui.QWidget() layout = QtGui.QGridLayout(widg) layout.setMargin(0) layout.addWidget(self.patient_label, 0, 0) layout.addWidget(self.get_patient_button, 0, 1) layout.addWidget(self.appt_listView, 2, 0, 1, 2) layout.addWidget(self.appt_controls_frame, 3, 0, 1, 2) self.addWidget(widg) # page 2 -- blocking mode widg = QtGui.QWidget() layout = QtGui.QVBoxLayout(widg) layout.addWidget(self.block_listView) self.addWidget(widg) # page 4 -- notes mode self.addWidget(QtGui.QLabel("Notes")) # connect signals self.appointment_model.appointment_selected.connect( self.update_selected_appointment) self.get_patient_button.clicked.connect(self.find_patient) self.first_appt_button.clicked.connect(self.show_first_appt) self.prev_appt_button.clicked.connect(self.show_prev_appt) self.next_appt_button.clicked.connect(self.show_next_appt) diary_button.clicked.connect(self.show_pt_diary) self.appt_listView.pressed.connect(self.appointment_pressed) self.appt_listView.clicked.connect(self.appointment_clicked) self.appt_listView.doubleClicked.connect(self.appointment_2x_clicked) def set_mode(self, mode): if self.mode == mode: return if mode == self.SCHEDULE_MODE: self.update_patient_label() self.enable_scheduling_buttons() self.mode = mode self.setCurrentIndex(mode) def set_patient(self, pt): self.clear() self.pt = pt if pt is not None: self.appointment_model.load_from_database(self.pt) self.patient_selected.emit(self.pt) self.enable_scheduling_buttons() def set_chosen_appointment(self, appointment): self.appointment_model.set_current_appt(appointment) def get_data(self): if self.pt is None: self.clear() return self.appointment_model.load_from_database(self.pt) @property def patient_text(self): if self.pt: return "%s %s (%s)" % (self.pt.fname, self.pt.sname, self.pt.serialno) else: return _("No patient Selected") def find_patient(self): dl = FindPatientDialog(self) if dl.exec_(): self.clear() pt = BriefPatient(dl.chosen_sno) self.set_patient(pt) self.update_patient_label() def update_patient_label(self): self.patient_label.setText(self.patient_text) @property def min_slot_length(self): msl = 0 if self.mode == self.SCHEDULE_MODE: msl = self.appointment_model.min_slot_length return msl @property def min_hyg_slot_length(self): msl = None if self.mode == self.SCHEDULE_MODE: msl = self.appointment_model.min_unscheduled_hyg_slot_length return msl def set_selection_mode(self, mode): assert mode in ( self.CLINICIAN_ANY, self.CLINICIAN_ANY_DENT, self.CLINICIAN_ANY_HYG, self.CLINICIAN_SELECTED), "selection mode misunderstood" self.selection_mode = mode @property def selectedClinicians(self): if self.selection_mode == self.CLINICIAN_ANY_HYG: return localsettings.activehyg_ixs if self.selection_mode == self.CLINICIAN_ANY_DENT: return localsettings.activedent_ixs if self.selection_mode == self.CLINICIAN_ANY: return localsettings.activedent_ixs + localsettings.activehyg_ixs return self.appointment_model.selectedClinicians @property def involvedClinicians(self): return self.appointment_model.involvedClinicians def set_ignore_emergency_spaces(self, bool_): self.ignore_emergency_spaces = bool_ def sizeHint(self): return QtCore.QSize(150, 200) def update_appt_selection(self, appt): ''' sync with the "patient diary" via signal/slot ''' if appt is None or self.pt is None: return if appt.serialno != self.pt.serialno: return index = self.appointment_model.set_current_appt(appt) self.appt_listView.setCurrentIndex(index) def update_selected_appointment(self, appt): self.available_slots = [] self._chosen_slot = None self.enable_scheduling_buttons() def appointment_clicked(self, index): LOGGER.debug("ScheduleControl.appointment_clicked") def appointment_pressed(self, index): LOGGER.debug("ScheduleControl.appointment_pressed") self.appointment_selected.emit(self.appointment_model.currentAppt) def appointment_2x_clicked(self, index): LOGGER.debug("ScheduleControl.appointment_clicked") self.show_first_appointment.emit() def clear(self): self.appointment_model.clear() self.reset() def reset(self): self.available_slots = [] self.hygienist_slots = [] self._chosen_slot = None self.finding_joint_appointments = False def show_first_appt(self): ''' resets the chosen slot and emits show_first_appointment signal ''' self._chosen_slot = None self.show_first_appointment.emit() @property def _chosen_slot_no(self): try: return self.available_slots.index(self._chosen_slot) except ValueError: return 0 def show_next_appt(self): try: self._chosen_slot = self.available_slots[self._chosen_slot_no + 1] self.chosen_slot_changed.emit() except IndexError: self._chosen_slot = None self.move_on.emit(True) def show_prev_appt(self): try: i = self._chosen_slot_no - 1 if i < 0: raise IndexError self._chosen_slot = self.available_slots[i] self.chosen_slot_changed.emit() except IndexError: self._chosen_slot = None self.move_on.emit(False) def set_available_slots(self, slots): self.available_slots = [] self.hygienist_slots = [] for slot in sorted(slots): if (slot.dent in self.selectedClinicians and slot.day_no not in self.excluded_days): self.available_slots.append(slot) def set_joint_slots(self, dent_slots, hyg_slots, max_wait=10): LOGGER.debug("ScheduleControl.set join slots %s %s" % (dent_slots, hyg_slots)) self.available_slots = [] self.hygienist_slots = [] all_dent_slots = [] all_hyg_slots = [] for slot in sorted(dent_slots): if (slot.dent in self.selectedClinicians and slot.day_no not in self.excluded_days): all_dent_slots.append(slot) for slot in sorted(hyg_slots): if slot.day_no not in self.excluded_days: all_hyg_slots.append(slot) appt = self.appointment_model.currentAppt for slot in all_dent_slots: hyg_slot, wait = slot.best_joint(appt.length, self.min_hyg_slot_length, all_hyg_slots) if wait <= max_wait: self.available_slots.append(slot) self.hygienist_slots.append(hyg_slot) @property def chosen_hyg_slot(self): if self.hygienist_slots == [] or self.chosen_slot is None: return None appt = self.appointment_model.currentAppt best_slot, wait = self.chosen_slot.best_joint(appt.length, self.min_hyg_slot_length, self.hygienist_slots) logging.info("WAIT TIME FOR HYGIENIST = %s minutes" % wait) return best_slot @property def last_appt_date(self): ''' returns the latest date of patient's appointments, or today's date if none found ''' last_d = QtCore.QDate.currentDate().toPyDate() for appt in self.appointment_model.scheduledList: if appt.date > last_d: last_d = appt.date return last_d @property def is_searching(self): appt = self.appointment_model.currentAppt return appt is not None and appt.unscheduled @property def search_again(self): ''' this determines whether it is worth continuing ''' return (self.is_searching and len(self.selectedClinicians) > 0 and len(self.available_slots) == 0) @property def chosen_slot(self): if self.available_slots == []: return None if self._chosen_slot is None: if self.use_last_slot: i = -1 self.use_last_slot = False else: i = 0 self._chosen_slot = self.available_slots[i] return self._chosen_slot def set_excluded_days(self, days): self.excluded_days = days def show_pt_diary(self): if self.pt is None: QtGui.QMessageBox.information(self, _("error"), _("No patient selected")) return def _find_appt(appt): dl.accept() self.find_appt.emit(appt) def _start_scheduling(): dl.accept() QtCore.QTimer.singleShot(100, self.start_scheduling.emit) pt_diary_widget = PtDiaryWidget() pt_diary_widget.find_appt.connect(_find_appt) pt_diary_widget.start_scheduling.connect(_start_scheduling) pt_diary_widget.appointment_selected.connect( self.appointment_model.set_current_appt) pt_diary_widget.set_patient(self.pt) pt_diary_widget.layout_ptDiary() dl = QtGui.QDialog(self) but_box = QtGui.QDialogButtonBox(dl) but = but_box.addButton(_("Close"), but_box.AcceptRole) but.clicked.connect(dl.accept) layout = QtGui.QVBoxLayout(dl) layout.addWidget(pt_diary_widget) layout.addStretch() layout.addWidget(but_box) dl.exec_() self.appointment_model.load_from_database(self.pt) self.enable_scheduling_buttons() # now force diary relayout self.appointment_selected.emit(self.appointment_model.currentAppt) def enable_scheduling_buttons(self): appt = self.appointment_model.currentAppt enabled = (appt is not None and appt.unscheduled) for but in (self.next_appt_button, self.prev_appt_button, self.first_appt_button): but.setEnabled(enabled)
class DiaryScheduleController(QtGui.QStackedWidget): ''' This widget lives down the left side of the diary widget. It provides a way of switching modes for the diary. ''' BROWSE_MODE = 0 SCHEDULE_MODE = 1 BLOCKING_MODE = 2 NOTES_MODE = 3 NOT_SEARCHING = 0 SEARCHING_FORWARDS = 1 SEARCHING_BACKWARDS = 2 mode = BROWSE_MODE search_mode = NOT_SEARCHING appointment_selected = QtCore.pyqtSignal(object) patient_selected = QtCore.pyqtSignal(object) show_first_appointment = QtCore.pyqtSignal() chosen_slot_changed = QtCore.pyqtSignal() find_appt = QtCore.pyqtSignal(object) start_scheduling = QtCore.pyqtSignal() # from embedded pt diary widget advice_signal = QtCore.pyqtSignal(object, object) pt = None primary_slots = [] secondary_slots = [] _chosen_slot = None finding_joint_appointments = False def __init__(self, parent=None): QtGui.QStackedWidget.__init__(self, parent) self.diary_widget = parent self.patient_label = QtGui.QLabel() icon = QtGui.QIcon(":/search.png") self.get_patient_button = QtGui.QPushButton(icon, "") self.get_patient_button.setMaximumWidth(40) self.appt_listView = DraggableList(self) self.block_listView = DraggableList(self) self.item_delegate = ColouredItemDelegate(self) self.appointment_model = SimpleListModel(self) self.appt_listView.setModel(self.appointment_model) self.appt_listView.setItemDelegate(self.item_delegate) self.appt_listView.setSelectionModel( self.appointment_model.selection_model) self.appt_listView.setSelectionMode( QtGui.QListView.ContiguousSelection) block_model = BlockListModel(self) self.block_listView.setModel(block_model) icon = QtGui.QIcon(":vcalendar.png") diary_button = QtGui.QPushButton(icon, _("Diary")) diary_button.setToolTip(_("Open the patient's diary")) icon = QtGui.QIcon(":settings.png") settings_button = QtGui.QPushButton(icon, _("Options")) settings_button.setToolTip(_("Appointment Settings")) icon = QtGui.QIcon(":back.png") self.prev_appt_button = QtGui.QPushButton(icon, "") self.prev_appt_button.setToolTip(_("Previous appointment")) icon = QtGui.QIcon(":forward.png") self.next_appt_button = QtGui.QPushButton(icon, "") self.next_appt_button.setToolTip(_("Next available appointment")) icon = QtGui.QIcon(":forward.png") self.next_day_button = QtGui.QPushButton(icon, "") self.next_day_button.setToolTip(_("Next Day or Week")) icon = QtGui.QIcon(":back.png") self.prev_day_button = QtGui.QPushButton(icon, "") self.prev_day_button.setToolTip(_("Previous Day or Week")) icon = QtGui.QIcon(":first.png") self.first_appt_button = QtGui.QPushButton(icon, "") self.first_appt_button.setToolTip(_("First available appointment")) self.appt_controls_frame = QtGui.QWidget() layout = QtGui.QGridLayout(self.appt_controls_frame) layout.setMargin(1) layout.setSpacing(2) layout.addWidget(diary_button, 0, 0, 1, 2) layout.addWidget(settings_button, 0, 3, 1, 2) layout.addWidget(self.prev_day_button, 1, 0) layout.addWidget(self.prev_appt_button, 1, 1) layout.addWidget(self.first_appt_button, 1, 2) layout.addWidget(self.next_appt_button, 1, 3) layout.addWidget(self.next_day_button, 1, 4) self.appt_controls_frame.setSizePolicy( QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Minimum)) self.search_criteria_webview = QtWebKit.QWebView(self) self.search_criteria_webview.setMinimumHeight(100) self.search_criteria_webview.setHtml( _("No appointment selected for scheduling")) # now arrange the stacked widget # page 0 - Browsing mode self.browsing_webview = QtWebKit.QWebView() self.reset_browsing_webview() self.addWidget(self.browsing_webview) # page 1 -- scheduling mode widg = QtGui.QWidget() layout = QtGui.QGridLayout(widg) layout.setMargin(0) layout.addWidget(self.patient_label, 0, 0) layout.addWidget(self.get_patient_button, 0, 1) layout.addWidget(self.appt_listView, 2, 0, 1, 2) layout.addWidget(self.appt_controls_frame, 3, 0, 1, 2) layout.addWidget(self.search_criteria_webview, 4, 0, 1, 2) self.addWidget(widg) # page 2 -- blocking mode widg = QtGui.QWidget() layout = QtGui.QVBoxLayout(widg) layout.addWidget(self.block_listView) self.addWidget(widg) # page 4 -- notes mode self.notes_label = QtGui.QLabel(_("Select a patient to edit notes")) self.addWidget(self.notes_label) # connect signals self.get_patient_button.clicked.connect(self.find_patient) settings_button.clicked.connect(self.show_settings_dialog) self.prev_appt_button.clicked.connect(self.show_prev_appt) self.next_appt_button.clicked.connect(self.show_next_appt) self.prev_day_button.clicked.connect(self.show_prev_day) self.next_day_button.clicked.connect(self.show_next_day) self.first_appt_button.clicked.connect(self.find_first_appointment) diary_button.clicked.connect(self.show_pt_diary) self.appt_listView.selectionModel().selectionChanged.connect( self.selection_changed) self.appt_listView.doubleClicked.connect(self.appointment_2x_clicked) def set_mode(self, mode): if self.mode == mode: return if mode == self.SCHEDULE_MODE: self.update_patient_label() self.enable_scheduling_buttons() else: self.clear_slots() if mode == self.BROWSE_MODE: self.reset_browsing_webview() self.mode = mode self.setCurrentIndex(mode) def set_search_mode(self, mode): assert mode in (self.NOT_SEARCHING, self.SEARCHING_FORWARDS, self.SEARCHING_BACKWARDS) self.search_mode = mode def cancel_search_mode(self): self.set_search_mode(self.NOT_SEARCHING) def set_search_future(self): self.set_search_mode(self.SEARCHING_FORWARDS) def set_search_past(self): self.set_search_mode(self.SEARCHING_BACKWARDS) @property def searching_future(self): return self.search_mode == self.SEARCHING_FORWARDS @property def searching_past(self): return self.search_mode == self.SEARCHING_BACKWARDS def set_patient(self, pt): self.clear() self.pt = pt if pt is not None: self.appointment_model.load_from_database(self.pt) self.patient_selected.emit(self.pt) self.enable_scheduling_buttons() def get_data(self): ''' loads the appointment model from database, or clears if patient is None ''' LOGGER.debug("schedule control get-data") if self.pt is None: self.clear() return self.appointment_model.load_from_database(self.pt) @property def patient_text(self): if self.pt: return "%s %s (%s)" % (self.pt.fname, self.pt.sname, self.pt.serialno) else: return _("No patient Selected") @property def serialno(self): if self.pt: return self.pt.serialno return None def find_patient(self): ''' search and load a patient. ''' dl = FindPatientDialog(self) if dl.exec_(): self.clear() pt = BriefPatient(dl.chosen_sno) self.set_patient(pt) self.diary_widget.set_date(QtCore.QDate.currentDate()) self.update_patient_label() def update_patient_label(self): self.patient_label.setText(self.patient_text) @property def selected_appointment(self): ''' the appointment currently selected by user ''' return self.appointment_model.currentAppt @property def secondary_appointment(self): ''' the appointment jointly selected by user ''' return self.appointment_model.secondaryAppt @property def app1_length(self): try: return self.appointment_model.currentAppt.length except AttributeError: return 0 @property def app1_is_scheduled(self): try: return not self.appointment_model.currentAppt.unscheduled except AttributeError: LOGGER.debug("app1_is_scheduled") return False @property def app2_length(self): try: return self.appointment_model.secondaryAppt.length except AttributeError: return 0 @property def app2_is_scheduled(self): try: return not self.appointment_model.secondaryAppt.unscheduled except AttributeError: LOGGER.debug("app2_is_scheduled") return False @property def ignore_emergency_spaces(self): return ApptSettingsDialog.ignore_emergency_spaces def set_selection_mode(self, mode): assert mode in ( self.CLINICIAN_ANY, self.CLINICIAN_ANY_DENT, self.CLINICIAN_ANY_HYG, self.CLINICIAN_SELECTED), "selection mode misunderstood" self.dent_selection_mode = mode @property def selectedClinicians(self): return self.appointment_model.selectedClinicians def _appt_clinicians(self, appt): ''' use the selected appointment and the chosen settings to see who could perform this treatment. ''' if appt.dent in localsettings.activedent_ixs: if ApptSettingsDialog.dentist_policy == \ ApptSettingsDialog.CLINICIAN_ANY_DENT: return localsettings.activedent_ixs if ApptSettingsDialog.dentist_policy == \ ApptSettingsDialog.CLINICIAN_ANY: return \ localsettings.activedent_ixs + localsettings.activehyg_ixs elif appt.dent in localsettings.activehyg_ixs: if ApptSettingsDialog.hygienist_policy == \ ApptSettingsDialog.CLINICIAN_ANY_HYG: return localsettings.activehyg_ixs return (appt.dent, ) @property def appt1_clinicians(self): ''' what clinicians could provide the treatment in appointment 1? ''' appt = self.appointment_model.currentAppt if appt: return self._appt_clinicians(appt) return () @property def appt2_clinicians(self): ''' what clinicians could provide the treatment in appointment 2? ''' appt = self.appointment_model.secondaryAppt if appt: return self._appt_clinicians(appt) return () @property def involvedClinicians(self): return self.appointment_model.involvedClinicians def sizeHint(self): return QtCore.QSize(180, 400) def update_appt_selection(self, appts): ''' sync with the "patient diary" via signal/slot ''' LOGGER.debug("updating schedule controller appointments, appts='%s'", appts) for appt in appts: if appt.serialno != self.serialno: return self.appointment_model.set_selected_appointments(appts) def update_selected_appointment(self, appt): self.primary_slots = [] self._chosen_slot = None self.enable_scheduling_buttons() def selection_changed(self, selection): LOGGER.debug("ScheduleControl.selection_changed") self.update_search_criteria_webview() self.enable_scheduling_buttons() try: app = self.appointment_model.data(selection.indexes()[-1], QtCore.Qt.UserRole) except IndexError: app = self.appointment_model.currentAppt self.appointment_selected.emit(app) def appointment_2x_clicked(self, index): LOGGER.debug("ScheduleControl.appointment_clicked") self.show_first_appointment.emit() def clear(self): self.reset_browsing_webview() self.appointment_model.clear() self.reset() def reset(self): self.reset_browsing_webview() self.primary_slots = [] self.secondary_slots = [] self._chosen_slot = None self.cancel_search_mode() self.finding_joint_appointments = False def show_settings_dialog(self): ''' show the settings dialog ''' LOGGER.info("showing the settings dialog") dl = ApptSettingsDialog(self) if dl.exec_(): self.selection_changed( self.appt_listView.selectionModel().selection()) @property def _chosen_slot_no(self): try: return self.primary_slots.index(self._chosen_slot) except ValueError: return 0 def show_next_appt(self): self.set_search_future() try: self._chosen_slot = self.primary_slots[self._chosen_slot_no + 1] self.chosen_slot_changed.emit() except IndexError: self.show_next_day() def show_prev_appt(self): self.set_search_past() try: i = self._chosen_slot_no - 1 if i < 0: raise IndexError self._chosen_slot = self.primary_slots[i] self.chosen_slot_changed.emit() except IndexError: self.show_prev_day() def show_next_day(self): ''' catches the signal to make a big jump forwards. will jump a week if in week view ''' self._chosen_slot = None self.set_search_future() self.diary_widget.step_date(True) def show_prev_day(self): ''' catches the signal to make a big jump backwords. will jump a week if in week view ''' self._chosen_slot = None self.set_search_past() self.diary_widget.step_date(False) @property def all_slots(self): for slot in self.primary_slots: yield slot for slot in self.secondary_slots: yield slot def clear_slots(self): # self.reset_browsing_webview() self._chosen_slot = None self.primary_slots = [] self.secondary_slots = [] def reset_browsing_webview(self): self.browsing_webview.setHtml("") def set_primary_slots(self, slots): self.primary_slots = [] for slot in sorted(slots): if (slot.dent in self.appt1_clinicians and slot.day_no not in self.excluded_days): self.primary_slots.append(slot) def set_secondary_slots(self, slots): LOGGER.debug("filtering secondary slots %s", slots) self.secondary_slots = [] for slot in sorted(slots): slot.is_primary = False if (slot.dent in self.appt2_clinicians and slot.day_no not in self.excluded_days): self.secondary_slots.append(slot) def set_slots_from_day_app_data(self, app_data): self.set_primary_slots( [] if self.app1_is_scheduled else app_data. slots(self.app1_length, self.ignore_emergency_spaces)) app2_slots = [] if self.is_searching_for_double_appointments and \ not self.app2_is_scheduled: self.set_secondary_slots( app_data.slots(self.app2_length, self.ignore_emergency_spaces, busy_serialno=self.serialno)) app1_slots = set([]) if self.app1_is_scheduled: iterset = [self.appointment_model.currentAppt.to_freeslot()] else: iterset = self.primary_slots for app1_slot in iterset: for app2_slot in self.secondary_slots: wait = app1_slot.wait_time(self.app1_length, self.app2_length, app2_slot) if wait is not None and wait <= MAX_WAIT: app2_slots.append(app2_slot) app1_slots.add(app1_slot) if not self.app1_is_scheduled: self.set_primary_slots(app1_slots) self.set_secondary_slots(app2_slots) def get_weekview_slots(self, weekdates): ''' calculate available slots for weekdates (list of QDates) ''' today = QtCore.QDate.currentDate() startday = today if today in weekdates else weekdates[0] # monday sunday = weekdates[6] # sunday # check for suitable apts in the selected WEEK! all_slots = appointments.future_slots( startday.toPyDate(), sunday.toPyDate(), self.appt1_clinicians, busy_serialno=self.serialno, override_emergencies=self.ignore_emergency_spaces) if self.app1_is_scheduled: self.set_primary_slots([]) else: self.set_primary_slots( appointments.getLengthySlots(all_slots, self.app1_length)) app2_slots = [] if self.is_searching_for_double_appointments and \ not self.app2_is_scheduled: all_slots = appointments.future_slots( startday.toPyDate(), sunday.toPyDate(), self.appt2_clinicians, busy_serialno=self.serialno, override_emergencies=self.ignore_emergency_spaces) self.set_secondary_slots( appointments.getLengthySlots(all_slots, self.app2_length)) app1_slots = set([]) if self.app1_is_scheduled: iterset = [self.appointment_model.currentAppt.to_freeslot()] else: iterset = self.primary_slots for app1_slot in iterset: for app2_slot in self.secondary_slots: wait = app1_slot.wait_time(self.app1_length, self.app2_length, app2_slot) if wait is not None and wait <= MAX_WAIT: app2_slots.append(app2_slot) app1_slots.add(app1_slot) if not self.app1_is_scheduled: self.set_primary_slots(app1_slots) self.set_secondary_slots(app2_slots) @property def chosen_2nd_slots(self): if not (self.appointment_model.currentAppt is None or self.appointment_model.secondaryAppt is None): for app2_slot in self.secondary_slots: wait = self.chosen_slot.wait_time(self.app1_length, self.app2_length, app2_slot) if wait is not None and wait <= MAX_WAIT: yield app2_slot else: LOGGER.debug("no appointments selected") @property def last_appt_date(self): ''' returns the latest date of patient's appointments, or today's date if none found ''' last_d = QtCore.QDate.currentDate().toPyDate() for appt in self.appointment_model.scheduledList: if appt.date > last_d: last_d = appt.date return last_d @property def is_searching(self): ''' are we looking for a slot? ''' app1 = self.appointment_model.currentAppt if app1 is not None and app1.unscheduled: return True app2 = self.appointment_model.secondaryAppt if app2 is not None and app2.unscheduled: return True return False @property def is_searching_for_double_appointments(self): return self.appointment_model.secondaryAppt is not None @property def search_again(self): ''' this determines whether it is worth continuing (on a different date) ''' return (self.is_searching and self.search_mode != self.NOT_SEARCHING and len(self.selectedClinicians) > 0 and len(self.primary_slots) == 0) def find_first_appointment(self): LOGGER.debug("find_first_appointment") self.show_first_appointment.emit() @property def chosen_slot(self): if self.primary_slots == []: if self.app1_is_scheduled: return self.appointment_model.currentAppt.to_freeslot() return None if self._chosen_slot is None: if self.searching_past: self._chosen_slot = self.primary_slots[-1] else: self._chosen_slot = self.primary_slots[0] return self._chosen_slot def set_chosen_slot(self, slot): self._chosen_slot = slot def set_chosen_2nd_slot(self, slot): ''' user has clicked on a secondary slot - we need to switch the appointments over! ''' LOGGER.debug("set_chosen_2nd_slot %s", slot) model = self.appointment_model.selection_model selection = model.selection() selection.swap(0, 1) model.select(selection, model.ClearAndSelect) self.selection_changed( self.appointment_model.selection_model.selection()) self.appointment_model.selection_model.emitSelectionChanged( selection, QtGui.QItemSelection()) slot.is_primary = True self.set_chosen_slot(slot) self.chosen_slot_changed.emit() @property def excluded_days(self): return ApptSettingsDialog.excluded_days def show_pt_diary(self): if self.pt is None: QtGui.QMessageBox.information(self, _("error"), _("No patient selected")) return def _find_appt(appt): dl.accept() self.find_appt.emit(appt) def _start_scheduling(): dl.accept() self.get_data() self.appointment_model.selection_model.reset() appts = pt_diary_widget.selected_appointments self.update_appt_selection(appts) QtCore.QTimer.singleShot(100, self.start_scheduling.emit) pt_diary_widget = PtDiaryWidget() pt_diary_widget.find_appt.connect(_find_appt) pt_diary_widget.start_scheduling.connect(_start_scheduling) pt_diary_widget.set_patient(self.pt) dl = QtGui.QDialog(self) but_box = QtGui.QDialogButtonBox(dl) but = but_box.addButton(_("Close"), but_box.AcceptRole) but.clicked.connect(dl.accept) layout = QtGui.QVBoxLayout(dl) layout.addWidget(pt_diary_widget) layout.addStretch() layout.addWidget(but_box) dl.exec_() self.appointment_model.load_from_database(self.pt) self.enable_scheduling_buttons() def enable_scheduling_buttons(self): enabled = self.is_searching for but in (self.next_appt_button, self.next_day_button, self.prev_appt_button, self.prev_day_button, self.first_appt_button): but.setEnabled(enabled) self.update_search_criteria_webview() def begin_make_appointment(self): ''' this is when an external widget calls for us to start the process of starting an appointment ''' self.set_mode(self.SCHEDULE_MODE) self.enable_scheduling_buttons() LOGGER.debug("checking appointment settings %s", ApptSettingsDialog.is_default_settings()) if not ApptSettingsDialog.is_default_settings(): dl = ApptSettingsResetDialog(self) if dl.exec_(): if dl.show_settings_dialog: self.show_settings_dialog() else: ApptSettingsDialog.reset() def check_schedule_status(self, automatic): ''' this is called by the diary widget when the books have been laid out whilst in schedule mode. Inform the user ''' assert self.mode == self.SCHEDULE_MODE, "not in schedule mode" LOGGER.debug("check_schedule_status %s", self.diary_widget.selected_date()) if automatic: # this has been called by a timer update to the diary, # and therefore NOT user interaction LOGGER.debug("automatic call to check_schedule_status... ignoring") return try: date_ = self.diary_widget.selected_date().toPyDate() except AttributeError: # self.diary_widget is None? LOGGER.debug("date check error", exc_info=1) date_ = localsettings.currentDay() if not self.appointment_model.selectedAppts: self.advice_signal.emit( _("Please select an appointment to begin scheduling"), 0) elif self.app1_is_scheduled: if self.appointment_model.secondaryAppt is None: self.advice_signal.emit(_("appointment is already scheduled"), 0) elif self.app2_is_scheduled: self.advice_signal.emit(_("Joint appointment Scheduled"), 0) elif not list(self.chosen_2nd_slots): self.advice_signal.emit( _("Joint appointment is not possible with the " "chosen primary appointment"), 1) elif self.search_again: LOGGER.debug("automatic searching") if date_ > localsettings.BOOKEND: self.advice_signal.emit( u'''<b>%s<br />%s</b><hr /><em>(%s)</em> <ul> <li>%s</li><li>%s</li><li>%s</li><li>%s</li> </ul>''' % (_("This date is beyond the diary limit."), _("Please search again with different criteria."), _("for instance..."), _("no excluded days"), _("ignore emergencies"), _("add or view more clinicians"), _("or you have requested an impossible appointment!")), 1) self.cancel_search_mode() self.diary_widget.set_date(localsettings.currentDay()) elif date_ < localsettings.currentDay(): self.advice_signal.emit( _("You can't schedule an appointment in the past"), 1) self.diary_widget.set_date(localsettings.currentDay()) else: self.diary_widget.step_date(self.searching_future) elif date_ > localsettings.BOOKEND: self.advice_signal.emit(_("This date is beyond the diary limit."), 1) elif date_ < localsettings.currentDay(): self.advice_signal.emit( _("You can't schedule an appointment in the past"), 1) self.diary_widget.set_date(localsettings.currentDay()) elif self.chosen_slot is None and not list(self.chosen_2nd_slots): if self.diary_widget: if self.diary_widget.viewing_week: message = "%s %s" % ( _("in this week"), self.diary_widget.selected_date().weekNumber()) else: message = "%s (%s)" % ( _("on this day"), localsettings.formatDate( self.diary_widget.selected_date().toPyDate())) else: message = "" # should only happen if __name__ == "__main__" message = "%s %s" % (_("No Slots Found"), message) self.advice_signal.emit(message, 0) @property def _dentist_message(self): if not self.appointment_model.dentists_involved: return "" if ApptSettingsDialog.dentist_policy == \ ApptSettingsDialog.CLINICIAN_SELECTED: return _("Specified Dentist only") elif ApptSettingsDialog.dentist_policy == \ ApptSettingsDialog.CLINICIAN_ANY_DENT: return _("Any Dentist") else: # CLINICIAN_ANY return _("Any Clinician") @property def _hygienist_message(self): if not self.appointment_model.hygienists_involved: return "" if ApptSettingsDialog.hygienist_policy == \ ApptSettingsDialog.CLINICIAN_SELECTED: return _("Specified Hygienist only for hyg appts") if ApptSettingsDialog.hygienist_policy == \ ApptSettingsDialog.CLINICIAN_ANY_HYG: return _("Any Hygienist for hyg appts") else: # CLINICIAN_ANY return _("Any Clinician for hyg appts") @property def _joint_message(self): if self.is_searching_for_double_appointments: return _("Joint Appointments") return "" @property def _emergency_message(self): if ApptSettingsDialog.ignore_emergency_spaces: return "%s" % _("Overwrite Emergencies") return "" @property def _day_message(self): if self.excluded_days == []: return _("Any Day") days = [] for i, day in enumerate((_("Mon"), _("Tue"), _("Wed"), _("Thu"), _("Fri"), _("Sat"), _("Sun")), 1): if i not in self.excluded_days: days.append(day) return ", ".join(days) def update_search_criteria_webview(self): if self.appointment_model.currentAppt is None: html = _("No Appointment Selected") else: html = '%s<ul><li>%s</li></ul>' % ( _("Search Settings"), "</li><li>".join([ s for s in (self._dentist_message, self._hygienist_message, self._joint_message, self._emergency_message, self._day_message) if s != "" ])) self.search_criteria_webview.setHtml(html) def update_highlighted_appointment(self): ''' the diary widget selected appointment has changed. ''' app = self.diary_widget.highlighted_appointment LOGGER.debug("appointment highlighted %s", app) if app is None: self.reset_browsing_webview() return if self.mode == self.NOTES_MODE: self.notes_label.setText( "<h3>%s</h3>%s<br />%s" % (_("View/edit today's notes for "), app.name, app.serialno)) return self.notes_label.setText("") if self.mode != self.BROWSE_MODE: return feedback = FEEDBACK % ( app.name, app.serialno, localsettings.readableDate( self.diary_widget.selected_date().toPyDate()), "%02d:%02d" % (app.start // 100, app.start % 100), "%02d:%02d" % (app.end // 100, app.end % 100), '</li><li class="trt">'.join( [val for val in (app.trt1, app.trt2, app.trt3) if val != ""])) if app.memo != "": feedback += "<hr />%s<br /><i>%s</i>" % (_("Memo"), app.memo) try: datestamp = app.timestamp.date() feedback += \ "<hr />%s<br />%s (%s %s)" % ( _("Made"), localsettings.formatDate(datestamp), _("at"), localsettings.pyTimeToHumantime( app.timestamp)) except AttributeError: pass if app.mh_form_check_date or app.mh_form_required: feedback += "<hr />" if app.mh_form_check_date: feedback += "%s %s<br />" % (_("last mh form"), localsettings.formatDate( app.mh_form_check_date)) if app.mh_form_required: feedback += "%s" % _("MH CHECK REQUIRED") feedback = "%s<body></html>" % feedback self.browsing_webview.setHtml(feedback)
def __init__(self, parent=None): QtGui.QDialog.__init__(self, parent) self.setWindowTitle("Drag Drop Test") self.pt = duckPt() layout = QtGui.QGridLayout(self) self.model = SimpleListModel() appts = appointments.get_pts_appts(duckPt()) self.model.set_appointments(appts, appts[1]) self.appt_listView = DraggableList(True) self.appt_listView.setModel(self.model) self.block_model = BlockListModel() self.blockView = DraggableList(False) self.blockView.setModel(self.block_model) self.book = appointmentwidget.AppointmentWidget("1000", "1200", self) self.book.setDentist(1) self.book.setStartTime(1015) self.book.setEndTime(1145) for appoint in ((1, 1030, 1045, 'MCDONALD I', 6155, 'EXAM', '', '', '', 1, 73, 0, 0, datetime.datetime.now()), (1, 1115, 1130, 'EMERGENCY', 0, '', '', '', '', -128, 0, 0, 0, datetime.datetime.now())): self.book.setAppointment(appoint) self.OVbook = AppointmentOverviewWidget("1000", "1200", 15, 2, self) d1 = appointments.DentistDay(1) d1.start = 1015 d1.end = 1145 d1.memo = "hello" d2 = appointments.DentistDay(4) d2.start = 1015 d2.end = 1145 self.OVbook.dents = [d1, d2] self.OVbook.clear() self.OVbook.init_dicts() for d in (d1, d2): self.OVbook.setStartTime(d) self.OVbook.setEndTime(d) self.OVbook.setMemo(d) self.OVbook.setFlags(d) slot = appointments.FreeSlot(datetime.datetime(2009, 2, 2, 10, 45), 1, 20) slot2 = appointments.FreeSlot(datetime.datetime(2009, 2, 2, 11, 0o5), 4, 60) self.OVbook.addSlot(slot) self.OVbook.addSlot(slot2) appt = appointments.WeekViewAppointment() appt.mpm = 10 * 60 + 30 appt.length = 15 appt.dent = 1 self.OVbook.appts[1] = (appt, ) emerg = appointments.WeekViewAppointment() emerg.mpm = 11 * 60 + 15 emerg.length = 15 emerg.reason = "emergency" self.OVbook.eTimes[1] = (emerg, ) self.OVbook.setMinimumWidth(200) self.tw = QtGui.QTabWidget(self) self.tw.addTab(self.book, "day") self.tw.addTab(self.OVbook, "week") layout.addWidget(self.appt_listView, 0, 0) layout.addWidget(self.blockView, 2, 0) layout.addWidget(self.tw, 0, 1, 3, 1) # self.tw.setCurrentIndex(1) self.model.appointment_selected.connect(slot_catcher)