def _save_as_update(self): self.data['description'] = self._PRW_description.GetValue().strip() self.data['summary'] = self._TCTRL_status.GetValue().strip() self.data['episode_open'] = not self._CHBOX_closed.IsChecked() self.data['diagnostic_certainty_classification'] = self._PRW_certainty.GetData() issue_name = self._PRW_issue.GetValue().strip() if len(issue_name) == 0: self.data['pk_health_issue'] = None else: self.data['pk_health_issue'] = self._PRW_issue.GetData(can_create = True) issue = gmEMRStructItems.cHealthIssue(aPK_obj = self.data['pk_health_issue']) if not move_episode_to_issue(episode = self.data, target_issue = issue, save_to_backend = False): gmDispatcher.send ( signal = 'statustext', msg = _('Cannot attach episode [%s] to health issue [%s] because it already has a running episode.') % ( self.data['description'], issue['description'] ) ) return False self.data.save() self.data.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ] return True
def _save_as_new(self): pat = gmPerson.gmCurrentPatient() emr = pat.emr epi = emr.add_episode(episode_name = self._PRW_description.GetValue().strip()) epi['summary'] = self._TCTRL_status.GetValue().strip() epi['episode_open'] = not self._CHBOX_closed.IsChecked() epi['diagnostic_certainty_classification'] = self._PRW_certainty.GetData() issue_name = self._PRW_issue.GetValue().strip() if len(issue_name) != 0: epi['pk_health_issue'] = self._PRW_issue.GetData(can_create = True) issue = gmEMRStructItems.cHealthIssue(aPK_obj = epi['pk_health_issue']) if not move_episode_to_issue(episode = epi, target_issue = issue, save_to_backend = False): gmDispatcher.send ( signal = 'statustext', msg = _('Cannot attach episode [%s] to health issue [%s] because it already has a running episode.') % ( epi['description'], issue['description'] ) ) gmEMRStructItems.delete_episode(episode = epi) return False epi.save() epi.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ] self.data = epi return True
def _on_add_patient_button_pressed(self, evt): self.__id_most_recently_activated_patient = None curr_pat = gmPerson.gmCurrentPatient() if not curr_pat.connected: gmDispatcher.send(signal = 'statustext', msg = _('Cannot add waiting list entry: No patient selected.'), beep = True) return edit_waiting_list_entry(parent = self, patient = curr_pat)
def _check_birthday(patient=None): if patient['dob'] is None: return dbcfg = gmCfg.cCfgSQL() dob_distance = dbcfg.get2 ( option = 'patient_search.dob_warn_interval', workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace, bias = 'user', default = '1 week' ) if not patient.dob_in_range(dob_distance, dob_distance): return now = gmDateTime.pydt_now_here() enc = gmI18N.get_encoding() msg = _('%(pat)s turns %(age)s on %(month)s %(day)s ! (today is %(month_now)s %(day_now)s)') % { 'pat': patient.get_description_gender(), 'age': patient.get_medical_age().strip('y'), 'month': patient.get_formatted_dob(format = '%B'), 'day': patient.get_formatted_dob(format = '%d'), 'month_now': gmDateTime.pydt_strftime(now, '%B', gmDateTime.acc_months), 'day_now': gmDateTime.pydt_strftime(now, '%d', gmDateTime.acc_days) } gmDispatcher.send(signal = 'statustext', msg = msg)
def _on_identity_item_activated(self, event): data = self._LCTRL_identity.get_selected_item_data(only_one = True) if data is None: gmDispatcher.send(signal = 'display_widget', name = 'gmNotebookedPatientEditionPlugin') # <ctrl> down ? if not wx.GetKeyState(wx.WXK_CONTROL): gmDispatcher.send(signal = 'display_widget', name = 'gmNotebookedPatientEditionPlugin') # <ctrl> down ! if isinstance(data, gmPerson.cPersonName): ea = gmDemographicsWidgets.cPersonNameEAPnl(self, -1, name = data) dlg = gmEditArea.cGenericEditAreaDlg2(self, -1, edit_area = ea, single_entry = True) dlg.SetTitle(_('Cloning name')) dlg.ShowModal() return if isinstance(data, dict): key = list(data.keys())[0] val = data[key] if key == 'id': ea = gmDemographicsWidgets.cExternalIDEditAreaPnl(self, -1, external_id = val) ea.id_holder = gmPerson.gmCurrentPatient() dlg = gmEditArea.cGenericEditAreaDlg2(self, -1, edit_area = ea, single_entry = True) dlg.SetTitle(_('Editing external ID')) dlg.ShowModal() return if key == 'job': gmDemographicsWidgets.edit_occupation() return
def check_for_updates(): dbcfg = gmCfg.cCfgSQL() url = dbcfg.get2 ( option = u'horstspace.update.url', workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace, bias = 'workplace', default = u'http://www.gnumed.de/downloads/gnumed-versions.txt' ) consider_latest_branch = bool(dbcfg.get2 ( option = u'horstspace.update.consider_latest_branch', workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace, bias = 'workplace', default = True )) _cfg = gmCfg2.gmCfgData() found, msg = gmNetworkTools.check_for_update ( url = url, current_branch = _cfg.get(option = 'client_branch'), current_version = _cfg.get(option = 'client_version'), consider_latest_branch = consider_latest_branch ) if found is False: gmDispatcher.send(signal = 'statustext', msg = _('Your client (%s) is up to date.') % _cfg.get(option = 'client_version')) return gmGuiHelpers.gm_show_info ( msg, _('Checking for client updates') )
def run(self, patient=None, debug=False): self.pdf_result = None cfg_file = self.__write_config_file(patient = patient) # FIXME: add user-configured path if not self.__detect_binary(): return False args = ( self.path_to_binary, '--file=%s' % cfg_file, '--show-cli-pane=%s' % gmTools.bool2subst(debug, 'true', 'false') ) try: subprocess.check_call(args = args, close_fds = True) except (OSError, ValueError, subprocess.CalledProcessError): _log.exception('there was a problem executing [%s]', self.path_to_binary) gmDispatcher.send(signal = u'statustext', msg = _('Cannot run [arriba] !'), beep = True) return False try: open(self.pdf_result).close() except: _log.exception('error accessing [%s]', self.pdf_result) gmDispatcher.send(signal = u'statustext', msg = _('No [arriba] result found in [%s].') % self.pdf_result, beep = False) return False return True
def delete(enc=None): if enc is None: return False question = _( 'Really delete encounter [%s] ?\n' '\n' 'Once deletion succeeds it cannot be undone.\n' '\n' 'Note that it will only succeed if there\n' 'is no data attached to the encounter.' ) % enc['pk_encounter'] delete_it = gmGuiHelpers.gm_show_question ( question = question, title = _('Deleting encounter'), cancel_button = False ) if not delete_it: return False if gmEMRStructItems.delete_encounter(pk_encounter = enc['pk_encounter']): return True gmDispatcher.send ( signal = u'statustext', msg = _('Cannot delete encounter [%s]. It is probably in use.') % enc['pk_encounter'], beep = True ) return False
def contribute_translations(item=None): do_it = gmGuiHelpers.gm_show_question ( aTitle = _('Contributing translations'), aMessage = _('Do you want to contribute your translations to the GNUmed project ?') ) if not do_it: return False fname = gmTools.get_unique_filename(prefix = 'gm-db-translations-', suffix = '.sql') gmPG2.export_translations_from_database(filename = fname) msg = ( 'These are database string translations contributed by a GNUmed user.\n' '\n' '\tThe GNUmed "%s" Client' ) % gmI18N.system_locale if not gmNetworkTools.compose_and_send_email ( auth = {'user': gmNetworkTools.default_mail_sender, 'password': '******'}, sender = 'GNUmed Client <*****@*****.**>', receiver = ['*****@*****.**'], subject = '<contribution>: database translation', message = msg, attachments = [[fname, 'text/plain', 'quoted-printable']] ): gmDispatcher.send(signal = 'statustext', msg = _('Unable to send mail. Cannot contribute translations to GNUmed community.') % report, beep = True) return False gmDispatcher.send(signal = 'statustext', msg = _('Thank you for your contribution to the GNUmed community!'), beep = True) return True
def register(self): """Register ourselves with the main notebook widget.""" _log.info("set: [%s] class: [%s] name: [%s]" % (self._set, self.__class__.__name__, self.name())) # create widget nb = self.gb['horstspace.notebook'] widget = self.GetWidget(nb) # add ourselves to the main notebook nb.AddPage(widget, self.name()) # so notebook can find this widget self.gb['horstspace.notebook.%s' % self._set][self.__class__.__name__] = self self.gb['horstspace.notebook.pages'].append(self) # and put ourselves into the menu structure menu_info = self.MenuInfo() if menu_info is None: # register with direct access menu only gmDispatcher.send(signal = 'plugin_loaded', plugin_name = self.name(), class_name = self.__class__.__name__) else: name_of_menu, menu_item_name = menu_info gmDispatcher.send ( signal = 'plugin_loaded', plugin_name = menu_item_name, class_name = self.__class__.__name__, menu_name = name_of_menu, menu_item_name = menu_item_name, # FIXME: this shouldn't be self.name() but rather self.menu_help_string() menu_help_string = self.name() ) return True
def load_person_from_vcard_file(): wildcards = '|'.join ([ '%s (*.vcf)|*.vcf' % _('vcf files'), '%s (*.VCF)|*.VCF' % _('VCF files'), '%s (*)|*' % _('all files'), '%s (*.*)|*.*' % _('all files (Windows)') ]) dlg = wx.FileDialog ( parent = wx.GetApp().GetTopWindow(), message = _('Choose a vCard file:'), defaultDir = os.path.join(gmTools.gmPaths().home_dir, 'gnumed'), wildcard = wildcards, style = wx.FD_OPEN | wx.FD_FILE_MUST_EXIST ) result = dlg.ShowModal() fname = dlg.GetPath() dlg.DestroyLater() if result == wx.ID_CANCEL: return from Gnumed.business import gmVCard dto = gmVCard.parse_vcard2dto(filename = fname) if dto is None: gmDispatcher.send(signal='statustext', msg=_('[%s] does not seem to contain a vCard.') % fname) return idents = dto.get_candidate_identities(can_create = True) if len(idents) == 1: ident = idents[0] if not set_active_patient(patient = ident): gmGuiHelpers.gm_show_info (_( 'Cannot activate patient:\n\n' '%s %s (%s)\n' '%s' ) % ( dto.firstnames, dto.lastnames, dto.gender, gmDateTime.pydt_strftime(dto.dob, '%Y %b %d') ), _('Activating external patient') ) return dlg = cSelectPersonFromListDlg(wx.GetApp().GetTopWindow(), -1) dlg.set_persons(persons = idents) result = dlg.ShowModal() ident = dlg.get_selected_person() dlg.DestroyLater() if result == wx.ID_CANCEL: return if not set_active_patient(patient = ident): gmGuiHelpers.gm_show_info (_( 'Cannot activate patient:\n\n' '%s %s (%s)\n' '%s' ) % ( dto.firstnames, dto.lastnames, dto.gender, gmDateTime.pydt_strftime(dto.dob, '%Y %b %d') ), _('Activating external patient') )
def _on_allergies_dclicked(self, evt): if not self.curr_pat.connected: gmDispatcher.send('statustext', msg = _('Cannot activate Allergy Manager. No active patient.')) return dlg = gmAllergyWidgets.cAllergyManagerDlg(parent=self, id=-1) dlg.ShowModal() return
def _on_delete_problem_button_pressed(self, event): event.Skip() epi = self._LCTRL_problems.get_selected_item_data(only_one = True) if epi is None: return if not gmEMRStructItems.delete_episode(episode = epi): gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete problem. There is still clinical data recorded for it.'))
def delete(item): delete_it = gmGuiHelpers.gm_show_question ( aTitle = _('Deleting option'), aMessage = '%s\n\n%s %s (#%s) %s\n\n%s\n\n%s' % ( tooltip(item), gmTools.u_box_horiz_single * 3, item['option'], item['pk_cfg_item'], gmTools.u_box_horiz_single * 3, _('Do you really want to delete this option ?'), _('(GNUmed will re-create options as needed.)') ) ) if not delete_it: return False from Gnumed.wxpython.gmAuthWidgets import get_dbowner_connection conn = get_dbowner_connection(procedure = _('Deleting option')) if conn is None: gmDispatcher.send(signal = 'statustext', msg = _('Cannot connect as database owner. Unable to delete option.')) return False cfg = gmCfg.cCfgSQL() cfg.delete(conn = conn, pk_option = item['pk_cfg_item']) return True
def _pre_exit_callback(self): """The client is about to (be) shut down. Shutdown will not proceed before this returns. """ if self.__pat is None: return True # if self.__encounter_modified(): # do_save_enc = gmGuiHelpers.gm_show_question ( # aMessage = _( # 'You have modified the details\n' # 'of the current encounter.\n' # '\n' # 'Do you want to save those changes ?' # ), # aTitle = _('Starting new encounter') # ) # if do_save_enc: # if not self.save_encounter(): # gmDispatcher.send(signal = u'statustext', msg = _('Error saving current encounter.'), beep = True) saved = self._NB_soap_editors.save_all_editors ( emr = self.__pat.emr, episode_name_candidates = [ gmTools.none_if(self._TCTRL_aoe.GetValue().strip(), ''), gmTools.none_if(self._TCTRL_rfe.GetValue().strip(), '') ] ) if not saved: gmDispatcher.send(signal = 'statustext', msg = _('Cannot save all editors. Some were kept open.'), beep = True) return True
def _goto_measurements_review(self, pk_context=None, pk_patient=None): msg = _('Supposedly there are unreviewed results\n' 'for patient [%s]. However, I cannot find\n' 'that patient in the GNUmed database.' ) % pk_patient wx.BeginBusyCursor() try: pat = gmPerson.cPerson(aPK_obj = pk_patient) except gmExceptions.ConstructorError: wx.EndBusyCursor() _log.exception('patient [%s] not found', pk_patient) gmGuiHelpers.gm_show_error(msg, _('handling provider inbox item')) return False success = set_active_patient(patient = pat) wx.EndBusyCursor() if not success: gmGuiHelpers.gm_show_error(msg, _('handling provider inbox item')) return False gmDispatcher.send(signal = 'display_widget', name = 'gmMeasurementsGridPlugin') return True
def _on_list_item_activated(self, evt): data = self.get_selected_item_data(only_one = True) pk_pat_col = self.__get_patient_pk_data_key(data = data) if pk_pat_col is None: gmDispatcher.send(signal = 'statustext', msg = _('List not known to be patient-related.')) return pat_data = data[pk_pat_col] try: pat_pk = int(pat_data) pat = gmPerson.cPerson(aPK_obj = pat_pk) except (ValueError, TypeError): searcher = gmPersonSearch.cPatientSearcher_SQL() idents = searcher.get_identities(pat_data) if len(idents) == 0: gmDispatcher.send(signal = 'statustext', msg = _('No matching patient found.')) return if len(idents) == 1: pat = idents[0] else: from Gnumed.wxpython import gmPatSearchWidgets dlg = gmPatSearchWidgets.cSelectPersonFromListDlg(parent=wx.GetTopLevelParent(self), id=-1) dlg.set_persons(persons=idents) result = dlg.ShowModal() if result == wx.ID_CANCEL: dlg.Destroy() return pat = dlg.get_selected_person() dlg.Destroy() from Gnumed.wxpython import gmPatSearchWidgets gmPatSearchWidgets.set_active_patient(patient = pat)
def start_new_encounter(emr=None): emr.start_new_encounter() gmDispatcher.send(signal = 'statustext', msg = _('Started a new encounter for the active patient.'), beep = True) time.sleep(0.5) gmGuiHelpers.gm_show_info ( _('\nA new encounter was started for the active patient.\n'), _('Start of new encounter') )
def generate_form_from_template(parent=None, template_types=None, edit=None, template=None, excluded_template_types=None): """If <edit> is None it will honor the template setting.""" if parent is None: parent = wx.GetApp().GetTopWindow() # 1) get template to use if template is None: template = manage_form_templates ( parent = parent, active_only = True, template_types = template_types, excluded_types = excluded_template_types ) if template is None: gmDispatcher.send(signal = 'statustext', msg = _('No document template selected.'), beep = False) return None if template['engine'] == 'O': return print_doc_from_ooo_template(template = template) wx.BeginBusyCursor() # 2) process template try: form = template.instantiate() except KeyError: _log.exception('cannot instantiate document template [%s]', template) gmGuiHelpers.gm_show_error ( aMessage = _('Invalid document template [%s - %s (%s)]') % (name, ver, template['engine']), aTitle = _('Generating document from template') ) wx.EndBusyCursor() return None ph = gmMacro.gmPlaceholderHandler() #ph.debug = True form.substitute_placeholders(data_source = ph) if edit is None: if form.template['edit_after_substitution']: edit = True else: edit = False if edit: wx.EndBusyCursor() form.edit() wx.BeginBusyCursor() # 3) generate output pdf_name = form.generate_output() wx.EndBusyCursor() if pdf_name is not None: return form gmGuiHelpers.gm_show_error ( aMessage = _('Error generating document printout.'), aTitle = _('Generating document printout') ) return None
def _valid_for_save(self): has_errors = False if not self._DPRW_date.is_valid_timestamp(): self._DPRW_date.display_as_valid(False) has_errors = True else: self._DPRW_date.display_as_valid(True) start = self._DPRW_date.GetData() end = self._DPRW_end.GetData() self._DPRW_end.display_as_valid(True) if end is not None: end = end.get_pydt() if start is not None: start = start.get_pydt() if end < start: has_errors = True self._DPRW_end.display_as_valid(False) if self._CHBOX_ongoing.IsChecked(): now = gmDateTime.pydt_now_here() if end < now: has_errors = True self._DPRW_end.display_as_valid(False) if self._PRW_hospital_stay.GetData() is None: if self._PRW_episode.GetValue().strip() == '': self._PRW_episode.display_as_valid(False) has_errors = True else: self._PRW_episode.display_as_valid(True) else: self._PRW_episode.display_as_valid(True) if (self._PRW_procedure.GetValue() is None) or (self._PRW_procedure.GetValue().strip() == ''): self._PRW_procedure.display_as_valid(False) has_errors = True else: self._PRW_procedure.display_as_valid(True) invalid_location = ( (self._PRW_hospital_stay.GetData() is None) and (self._PRW_location.GetData() is None) or (self._PRW_hospital_stay.GetData() is not None) and (self._PRW_location.GetData() is not None) ) if invalid_location: self._PRW_hospital_stay.display_as_valid(False) self._PRW_location.display_as_valid(False) has_errors = True else: self._PRW_hospital_stay.display_as_valid(True) self._PRW_location.display_as_valid(True) gmDispatcher.send(signal = 'statustext', msg = _('Cannot save procedure.'), beep = True) return (has_errors is False)
def delete(enc_type=None): if gmEMRStructItems.delete_encounter_type(description = enc_type['description']): return True gmDispatcher.send ( signal = u'statustext', msg = _('Cannot delete encounter type [%s]. It is in use.') % enc_type['l10n_description'], beep = True ) return False
def delete(episode=None): if gmEMRStructItems.delete_episode(episode = episode): return True gmDispatcher.send ( signal = 'statustext', msg = _('Cannot delete episode.'), beep = True ) return False
def delete(stay=None): if gmEMRStructItems.delete_hospital_stay(stay = stay['pk_hospital_stay']): return True gmDispatcher.send ( signal = 'statustext', msg = _('Cannot delete hospitalization.'), beep = True ) return False
def _on_post_patient_selection(self, **kwargs): db_cfg = gmCfg.cCfgSQL() default_plugin = db_cfg.get2 ( option = u'patient_search.plugin_to_raise_after_search', workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace, bias = u'user', default = u'gmPatientOverviewPlugin' ) gmDispatcher.send(signal = 'display_widget', name = default_plugin)
def delete(issue=None): if gmEMRStructItems.delete_health_issue(health_issue = issue): return True gmDispatcher.send ( signal = 'statustext', msg = _('Cannot delete health issue.'), beep = True ) return False
def _valid_for_save(self): validity = True self.display_tctrl_as_valid(tctrl = self._PRW_patient, valid = (self._PRW_patient.person is not None)) validity = (self._PRW_patient.person is not None) if validity is False: gmDispatcher.send(signal = 'statustext', msg = _('Cannot add to waiting list. Missing essential input.')) return validity
def delete(external_care_item=None): if gmExternalCare.delete_external_care_item(pk_external_care = external_care_item['pk_external_care']): return True gmDispatcher.send ( signal = u'statustext', msg = _('Cannot delete external care item.'), beep = True ) return False
def _on_result_activated(self, event): # data = self._LCTRL_inbox.get_selected_item_data(only_one = True) # # if data is not None: # # <ctrl> down ? # if wx.GetKeyState(wx.WXK_CONTROL): # if isinstance(data, gmProviderInbox.cInboxMessage): # xxxxxxxxx gmDispatcher.send(signal = 'display_widget', name = 'gmMeasurementsGridPlugin') return
def _pre_exit_callback(self): """The client is about to be shut down. Shutdown will not proceed before this returns. """ if not self.__curr_pat.connected: return if not self.__save_soap(): gmDispatcher.send(signal = 'statustext', msg = _('Cannot save SimpleNotes SOAP note.'), beep = True) return
def delete(procedure=None): if gmEMRStructItems.delete_performed_procedure(procedure = procedure['pk_procedure']): return True gmDispatcher.send ( signal = 'statustext', msg = _('Cannot delete performed procedure.'), beep = True ) return False
def delete(item): delete_it = gmGuiHelpers.gm_show_question( aTitle=_('Deleting option'), aMessage='%s\n\n%s %s (#%s) %s\n\n%s\n\n%s' % (tooltip(item), gmTools.u_box_horiz_single * 3, item['option'], item['pk_cfg_item'], gmTools.u_box_horiz_single * 3, _('Do you really want to delete this option ?'), _('(GNUmed will re-create options as needed.)'))) if not delete_it: return False from Gnumed.wxpython.gmAuthWidgets import get_dbowner_connection conn = get_dbowner_connection(procedure=_('Deleting option')) if conn is None: gmDispatcher.send( signal='statustext', msg=_( 'Cannot connect as database owner. Unable to delete option.' )) return False cfg = gmCfg.cCfgSQL() cfg.delete(conn=conn, pk_option=item['pk_cfg_item']) return True
def __export_as_zip(self, msg_title, encrypt=True, items=None): # get password zip_pwd = None if encrypt: zip_pwd = self.__get_password(msg_title) if zip_pwd is None: _log.debug('user aborted by not providing the same password twice') gmDispatcher.send(signal = 'statustext', msg = _('Password not provided twice. Aborting.')) return None # create archive wx.BeginBusyCursor() zip_file = None try: exp_area = gmPerson.gmCurrentPatient().export_area zip_file = exp_area.export_as_zip(passphrase = zip_pwd, items = items) except Exception: _log.exception('cannot create zip file') wx.EndBusyCursor() if zip_file is None: gmGuiHelpers.gm_show_error ( aMessage = _('Error creating zip file.'), aTitle = msg_title ) return zip_file
def delete(enc=None): if enc is None: return False question = _( 'Really delete encounter [%s] ?\n' '\n' 'Once deletion succeeds it cannot be undone.\n' '\n' 'Note that it will only succeed if there\n' 'is no data attached to the encounter.') % enc['pk_encounter'] delete_it = gmGuiHelpers.gm_show_question( question=question, title=_('Deleting encounter'), cancel_button=False) if not delete_it: return False if gmEMRStructItems.delete_encounter(pk_encounter=enc['pk_encounter']): return True gmDispatcher.send( signal=u'statustext', msg=_('Cannot delete encounter [%s]. It is probably in use.') % enc['pk_encounter'], beep=True) return False
def __identity_valid_for_save(self): validity = True # name fields if self._PRW_lastname.GetValue().strip() == '': validity = False gmDispatcher.send(signal='statustext', msg=_('Must enter lastname.')) self._PRW_lastname.display_as_valid(False) else: self._PRW_lastname.display_as_valid(True) if self._PRW_firstnames.GetValue().strip() == '': validity = False gmDispatcher.send(signal='statustext', msg=_('Must enter first name.')) self._PRW_firstnames.display_as_valid(False) else: self._PRW_firstnames.display_as_valid(True) # gender if self._PRW_gender.GetData() is None: validity = False gmDispatcher.send(signal='statustext', msg=_('Must select gender.')) self._PRW_gender.display_as_valid(False) else: self._PRW_gender.display_as_valid(True) # dob validation if not _validate_dob_field(self._PRW_dob): validity = False # TOB validation if _validate_tob_field(self._TCTRL_tob): self.display_ctrl_as_valid(ctrl=self._TCTRL_tob, valid=True) else: validity = False self.display_ctrl_as_valid(ctrl=self._TCTRL_tob, valid=False) # uniqueness if len( gmPerson.get_person_duplicates( lastnames=self._PRW_lastname.GetValue(), firstnames=self._PRW_firstnames.GetValue(), dob=self._PRW_dob.GetData(), gender=self._PRW_gender.GetData(), comment=self._TCTRL_comment.Value)) > 0: validity = False self.StatusText = _( 'Duplicate person. Modify name and/or DOB or use comment to make unique.' ) return validity
def _on_confirm_button_pressed(self, evt): pat = gmPerson.gmCurrentPatient() emr = pat.emr allergies = emr.get_allergies() state = emr.allergy_state cmt = self._TCTRL_state_comment.GetValue().strip() if self._RBTN_unknown.GetValue(): if len(allergies) > 0: gmDispatcher.send( signal=u'statustext', msg= _('Cannot set allergy state to <unknown> because there are allergies stored for this patient.' ), beep=True) self._RBTN_some.SetValue(True) state['has_allergy'] = 1 return False else: state['has_allergy'] = None elif self._RBTN_none.GetValue(): if len(allergies) > 0: gmDispatcher.send( signal=u'statustext', msg= _('Cannot set allergy state to <None> because there are allergies stored for this patient.' ), beep=True) self._RBTN_some.SetValue(True) state['has_allergy'] = 1 return False else: state['has_allergy'] = 0 elif self._RBTN_some.GetValue(): if (len(allergies) == 0) and (cmt == u''): gmDispatcher.send( signal=u'statustext', msg= _('Cannot set allergy state to <some> because there are neither allergies nor a comment available for this patient.' ), beep=True) return False else: state['has_allergy'] = 1 state['comment'] = cmt state['last_confirmed'] = u'now' state.save_payload() self.__refresh_state_ui()
def _on_delete_focussed_msg(self, evt): if self.__focussed_msg['is_virtual']: gmDispatcher.send( signal='statustext', msg= _('You must deal with the reason for this message to remove it from your inbox.' ), beep=True) return False # message to a certain provider ? if self.__focussed_msg['pk_staff'] is not None: # do not delete messages to *other* providers if self.__focussed_msg['pk_staff'] != gmStaff.gmCurrentProvider( )['pk_staff']: gmDispatcher.send( signal='statustext', msg=_('This message can only be deleted by [%s].') % self.__focussed_msg['provider'], beep=True) return False pk_patient = self.__focussed_msg['pk_patient'] if pk_patient is not None: emr = gmClinicalRecord.cClinicalRecord(aPKey=pk_patient) epi = emr.add_episode(episode_name='administrative', is_open=False) soap_cat = gmTools.bool2subst( (self.__focussed_msg['category'] == 'clinical'), 'u', None) narr = _('Deleted inbox message:\n%s' ) % self.__focussed_msg.format(with_patient=False) emr.add_clin_narrative(note=narr, soap_cat=soap_cat, episode=epi) gmDispatcher.send( signal='statustext', msg=_('Recorded deletion of inbox message in EMR.'), beep=False) if not self.provider.inbox.delete_message( self.__focussed_msg['pk_inbox_message']): gmDispatcher.send(signal='statustext', msg=_('Problem removing message from Inbox.')) return False return True
def __identity_valid_for_save(self): error = False # name fields if self._PRW_lastname.GetValue().strip() == '': error = True gmDispatcher.send(signal='statustext', msg=_('Must enter lastname.')) self._PRW_lastname.display_as_valid(False) else: self._PRW_lastname.display_as_valid(True) if self._PRW_firstnames.GetValue().strip() == '': error = True gmDispatcher.send(signal='statustext', msg=_('Must enter first name.')) self._PRW_firstnames.display_as_valid(False) else: self._PRW_firstnames.display_as_valid(True) # gender if self._PRW_gender.GetData() is None: error = True gmDispatcher.send(signal='statustext', msg=_('Must select gender.')) self._PRW_gender.display_as_valid(False) else: self._PRW_gender.display_as_valid(True) # dob validation if not _validate_dob_field(self._PRW_dob): error = True # TOB validation if _validate_tob_field(self._TCTRL_tob): self.display_ctrl_as_valid(ctrl=self._TCTRL_tob, valid=True) else: error = True self.display_ctrl_as_valid(ctrl=self._TCTRL_tob, valid=False) # uniqueness if gmPerson.this_person_exists( self._PRW_lastname.GetValue().strip(), self._PRW_firstnames.GetValue().strip(), self._PRW_dob.GetData(), gmTools.none_if(self._TCTRL_comment.GetValue().strip(), u'')): error = True self.StatusText = _( 'Duplicate person. Modify name and/or DOB or use comment to make unique.' ) return (not error)
def _on_export_items_button_pressed(self, event): event.Skip() items = self.__get_items_to_work_on(_('Exporting entries')) if items is None: return # export dialog pat = gmPerson.gmCurrentPatient() dlg = cExportAreaExportToMediaDlg(self, -1, patient = pat, item_count = len(items)) choice = dlg.ShowModal() media = dlg._LCTRL_removable_media.get_selected_item_data(only_one = True) use_subdir = dlg._CHBOX_use_subdirectory.IsChecked() encrypt = dlg._CHBOX_encrypt.IsChecked() dlg.DestroyLater() if choice == wx.ID_CANCEL: return # export the files if media['type'] == 'cd': base_dir = gmTools.mk_sandbox_dir(prefix = 'iso-') else: base_dir = media['mountpoint'] if use_subdir: dir2save2 = os.path.join(base_dir, pat.subdir_name) else: dir2save2 = base_dir export_dir = self.__export_as_files ( gmTools.coalesce(encrypt, _('Exporting encrypted entries'), _('Exporting entries')), base_dir = dir2save2, items = items, encrypt = encrypt, with_metadata = True ) if export_dir is None: gmDispatcher.send(signal = 'statustext', msg = _('Cannot export: aborted or error.')) return if media['type'] == 'cd': if not self.__burn_dir_to_disk(base_dir = base_dir): return gmDispatcher.send(signal = 'statustext', msg = _('Entries successfully burned to disk.')) self.__save_soap_note(soap = _('Burned onto CD/DVD:\n - %s') % '\n - '.join([ i['description'] for i in items ])) else: gmDispatcher.send(signal = 'statustext', msg = _('Exported entries into [%s]') % export_dir) self.__save_soap_note(soap = _('Exported onto removable media:\n - %s') % '\n - '.join([ i['description'] for i in items ])) self.__browse_patient_data(dir2save2, encrypted = encrypt, archive = False, has_metadata = True) # remove_entries ? return True
def update_loinc_reference_data(): wx.BeginBusyCursor() gmDispatcher.send(signal='statustext', msg=_('Updating LOINC data can take quite a while...'), beep=True) # download loinc_zip = gmNetworkTools.download_file( url='http://www.gnumed.de/downloads/data/loinc/loinctab.zip', suffix='.zip') if loinc_zip is None: wx.EndBusyCursor() gmGuiHelpers.gm_show_warning( aTitle=_('Downloading LOINC'), aMessage=_('Error downloading the latest LOINC data.\n')) return False _log.debug('downloaded zipped LOINC data into [%s]', loinc_zip) loinc_dir = gmNetworkTools.unzip_data_pack(filename=loinc_zip) # split master data file data_fname, license_fname = gmLOINC.split_LOINCDBTXT( input_fname=os.path.join(loinc_dir, 'LOINCDB.TXT')) wx.EndBusyCursor() conn = gmAuthWidgets.get_dbowner_connection( procedure=_('importing LOINC reference data')) if conn is None: return False wx.BeginBusyCursor() # import data if gmLOINC.loinc_import(data_fname=data_fname, license_fname=license_fname, conn=conn): gmDispatcher.send(signal='statustext', msg=_('Successfully imported LOINC reference data.')) else: gmDispatcher.send(signal='statustext', msg=_('Importing LOINC reference data failed.'), beep=True) wx.EndBusyCursor() return True
def __identity_valid_for_save(self): error = False # name fields if self._PRW_lastname.GetValue().strip() == '': error = True gmDispatcher.send(signal='statustext', msg=_('Must enter lastname.')) self._PRW_lastname.display_as_valid(False) else: self._PRW_lastname.display_as_valid(True) if self._PRW_firstnames.GetValue().strip() == '': error = True gmDispatcher.send(signal='statustext', msg=_('Must enter first name.')) self._PRW_firstnames.display_as_valid(False) else: self._PRW_firstnames.display_as_valid(True) # gender if self._PRW_gender.GetData() is None: error = True gmDispatcher.send(signal='statustext', msg=_('Must select gender.')) self._PRW_gender.display_as_valid(False) else: self._PRW_gender.display_as_valid(True) # dob validation if not _validate_dob_field(self._PRW_dob): error = True # TOB validation if _validate_tob_field(self._TCTRL_tob): self.display_ctrl_as_valid(ctrl=self._TCTRL_tob, valid=True) else: error = True self.display_ctrl_as_valid(ctrl=self._TCTRL_tob, valid=False) return (not error)
def _on_list_item_activated(self, evt): data = self.get_selected_item_data(only_one=True) pk_pat_col = self.__get_patient_pk_data_key(data=data) if pk_pat_col is None: gmDispatcher.send(signal='statustext', msg=_('List not known to be patient-related.')) return pat_data = data[pk_pat_col] try: pat_pk = int(pat_data) pat = gmPerson.cPerson(aPK_obj=pat_pk) except (ValueError, TypeError): searcher = gmPersonSearch.cPatientSearcher_SQL() idents = searcher.get_identities(pat_data) if len(idents) == 0: gmDispatcher.send(signal='statustext', msg=_('No matching patient found.')) return if len(idents) == 1: pat = idents[0] else: from Gnumed.wxpython import gmPatSearchWidgets dlg = gmPatSearchWidgets.cSelectPersonFromListDlg( parent=wx.GetTopLevelParent(self), id=-1) dlg.set_persons(persons=idents) result = dlg.ShowModal() if result == wx.ID_CANCEL: dlg.Destroy() return pat = dlg.get_selected_person() dlg.Destroy() except ConstructorError: gmDispatcher.send(signal='statustext', msg=_('No matching patient found.')) return from Gnumed.wxpython import gmPatSearchWidgets gmPatSearchWidgets.set_active_patient(patient=pat)
def is_valid(value): if value is None: gmDispatcher.send(signal='statustext', msg=_('You need to actually set an editor.'), beep=True) return False, value if value.strip() == u'': gmDispatcher.send(signal='statustext', msg=_('You need to actually set an editor.'), beep=True) return False, value found, binary = gmShellAPI.detect_external_binary(value) if not found: gmDispatcher.send(signal='statustext', msg=_('The command [%s] is not found.') % value, beep=True) return True, value return True, binary
def _on_inbox_item_activated(self, event): data = self._LCTRL_inbox.get_selected_item_data(only_one = True) # if it is a dynamic hint open the URL for that if isinstance(data, gmAutoHints.cDynamicHint): if data['url'] is not None: gmNetworkTools.open_url_in_browser(data['url']) return # holding down <CTRL> when double-clicking an inbox # item indicates the desire to delete it # <ctrl> down ? if wx.GetKeyState(wx.WXK_CONTROL): # better safe than sorry: can only delete real inbox items if data is None: return if not isinstance(data, gmProviderInbox.cInboxMessage): return delete_it = gmGuiHelpers.gm_show_question ( question = _('Do you really want to\ndelete this inbox message ?'), title = _('Deleting inbox message') ) if not delete_it: return gmProviderInbox.delete_inbox_message(inbox_message = data['pk_inbox_message']) return if data is None: gmDispatcher.send(signal = 'display_widget', name = 'gmProviderInboxPlugin') return if not isinstance(data, gmProviderInbox.cInboxMessage): gmDispatcher.send(signal = 'display_widget', name = 'gmProviderInboxPlugin') return gmDispatcher.send(signal = 'display_widget', name = 'gmProviderInboxPlugin', filter_by_active_patient = True) return
def manage_progress_notes(parent=None, encounters=None, episodes=None, patient=None): # sanity checks if patient is None: patient = gmPerson.gmCurrentPatient() if not patient.connected: gmDispatcher.send( signal='statustext', msg=_('Cannot edit progress notes. No active patient.')) return False if parent is None: parent = wx.GetApp().GetTopWindow() emr = patient.emr #-------------------------- def delete(item): if item is None: return False dlg = gmGuiHelpers.c2ButtonQuestionDlg( parent, -1, caption=_('Deleting progress note'), question=_( 'Are you positively sure you want to delete this\n' 'progress note from the medical record ?\n' '\n' 'Note that even if you chose to delete the entry it will\n' 'still be (invisibly) kept in the audit trail to protect\n' 'you from litigation because physical deletion is known\n' 'to be unlawful in some jurisdictions.\n'), button_defs=({ 'label': _('Delete'), 'tooltip': _('Yes, delete the progress note.'), 'default': False }, { 'label': _('Cancel'), 'tooltip': _('No, do NOT delete the progress note.'), 'default': True })) decision = dlg.ShowModal() if decision != wx.ID_YES: return False gmClinNarrative.delete_clin_narrative(narrative=item['pk_narrative']) return True #-------------------------- def edit(item): if item is None: return False dlg = gmGuiHelpers.cMultilineTextEntryDlg( parent, -1, title=_('Editing progress note'), msg=_('This is the original progress note:'), data=item.format(left_margin=' ', fancy=True), text=item['narrative']) decision = dlg.ShowModal() if decision != wx.ID_SAVE: return False val = dlg.value dlg.Destroy() if val.strip() == '': return False item['narrative'] = val item.save_payload() return True #-------------------------- def refresh(lctrl): notes = emr.get_clin_narrative( encounters=encounters, episodes=episodes, providers=[gmStaff.gmCurrentProvider()['short_alias']]) lctrl.set_string_items(items=[[ narr['date'].strftime('%x %H:%M'), gmSoapDefs.soap_cat2l10n[ narr['soap_cat']], narr['narrative'].replace( '\n', '/').replace('\r', '/') ] for narr in notes]) lctrl.set_data(data=notes) #-------------------------- gmListWidgets.get_choices_from_list( parent=parent, caption=_('Managing progress notes'), msg=_('\n' ' This list shows the progress notes by %s.\n' '\n') % gmStaff.gmCurrentProvider()['short_alias'], columns=[_('when'), _('type'), _('entry')], single_selection=True, can_return_empty=False, edit_callback=edit, delete_callback=delete, refresh_callback=refresh)
def print_generic_document(parent=None, jobtype:str=None, episode=None): """Call LibreOffice Writer with a generated (fake) ODT file. Once Writer is closed, the ODT is imported if different from its initial content. Note: The file passed to LO _must_ reside in a directory the parents of which are all R-X by the user or else LO will throw up its arms in despair and fall back to the user's home dir upon saving. So, /tmp/user/$UID/some-file.odt will *not* work (because .../user/... is "drwx--x--x root.root") while /home/$USER/some/dir/some-file.odt does. """ sandbox = os.path.join(gmTools.gmPaths().user_tmp_dir, 'libreoffice') gmTools.mkdir(sandbox) fpath = gmTools.get_unique_filename(suffix = '.txt', tmp_dir = sandbox) doc_file = open(fpath, mode = 'wt') doc_file.write(__ODT_FILE_PREAMBLE) doc_file.write(_('Today: %s') % gmDateTime.pydt_now_here().strftime('%c')) doc_file.write('\n\n') prax = gmPraxis.gmCurrentPraxisBranch() doc_file.write('Praxis:\n') doc_file.write(prax.format()) doc_file.write('\n') doc_file.write('Praxis branch:\n') doc_file.write('\n'.join(prax.org_unit.format ( with_address = True, with_org = True, with_comms = True ))) doc_file.write('\n\n') pat = gmPerson.gmCurrentPatient(gmPerson.cPatient(12)) if pat.connected: doc_file.write('Patient:\n') doc_file.write(pat.get_description_gender()) doc_file.write('\n\n') for adr in pat.get_addresses(): doc_file.write(adr.format(single_line = False, verbose = True, show_type = True)) doc_file.write('\n\n') for chan in pat.get_comm_channels(): doc_file.werite(chan.format()) doc_file.write('\n\n') doc_file.write('Provider:\n') doc_file.write('\n'.join(gmStaff.gmCurrentProvider().get_staff().format())) doc_file.write('\n\n-----------------------------------------------------------------------------\n\n') doc_file.close() # convert txt -> odt success, ret_code, stdout = gmShellAPI.run_process ( cmd_line = [ 'lowriter', '--convert-to', 'odt', '--outdir', os.path.split(fpath)[0], fpath ], verbose = True ) if success: fpath = gmTools.fname_stem_with_path(fpath) + '.odt' else: _log.warning('cannot convert .txt to .odt') md5_before = gmTools.file2md5(fpath) gmShellAPI.run_process(cmd_line = ['lowriter', fpath], verbose = True) md5_after = gmTools.file2md5(fpath) if md5_before == md5_after: gmDispatcher.send(signal = 'statustext', msg = _('Document not modified. Discarding.'), beep = False) return if not pat.connected: shutil.move(fpath, gmTools.gmPaths().user_work_dir) gmDispatcher.send(signal = 'statustext', msg = _('No patient. Moved file into %s') % gmTools.gmPaths().user_work_dir, beep = False) return pat.export_area.add_file ( filename = fpath, hint = _('Generic letter, written at %s') % gmDateTime.pydt_now_here().strftime('%Y %b %d %H:%M') ) gmDispatcher.send(signal = 'display_widget', name = 'gmExportAreaPlugin')
def save_screenshot_to_file(filename=None, widget=None, settle_time=None): """Take screenshot of widget. <settle_time> in milliseconds """ assert (isinstance(widget, wx.Window)), '<widget> must be (sub)class of wx.Window' if filename is None: filename = gmTools.get_unique_filename( prefix='gm-screenshot-%s-' % pyDT.datetime.now().strftime('%Y-%m-%d_%H-%M-%S'), suffix='.png' # for testing: #,tmp_dir = os.path.join(gmTools.gmPaths().home_dir, 'gnumed') ) else: filename = gmTools.fname_sanitize(filename) _log.debug('filename: %s', filename) _log.debug('widget: %s', widget) _log.debug('display size: %s', wx.DisplaySize()) # let it settle a bit for, say, tooltips if settle_time is not None: for wait_slice in range(int(settle_time // 100)): wx.SafeYield() time.sleep(0.1) widget_rect_on_screen = widget.GetScreenRect() client_area_origin_on_screen = widget.ClientToScreen((0, 0)) widget_rect_local = widget.GetRect() widget_rect_client_area = widget.GetClientRect() client_area_origin_local = widget.GetClientAreaOrigin() _log.debug('widget.GetScreenRect(): %s', widget_rect_on_screen) _log.debug('widget.ClientToScreen(0, 0): %s', client_area_origin_on_screen) _log.debug('widget.GetRect(): %s', widget_rect_local) _log.debug('widget.GetClientRect(): %s', widget_rect_client_area) _log.debug('widget.GetClientAreaOrigin(): %s', client_area_origin_local) width2snap = widget_rect_local.width height2snap = widget_rect_local.height border_x = client_area_origin_on_screen.x - widget_rect_local.x x2snap_from = 0 - border_x title_and_menu_height = client_area_origin_on_screen.y - widget_rect_on_screen.y y2snap_from = 0 - title_and_menu_height # those are the correct dimensions but we don't get to # *see* the window decorations on a WindowDC or ClientDC :-( # (and a screendc doesn't work either) _log.debug('left (x) border: %s', border_x) _log.debug('top (y) border: %s', title_and_menu_height) _log.debug('x2snap_from: %s', x2snap_from) _log.debug('y2snap_from: %s', y2snap_from) _log.debug('width2snap: %s', width2snap) _log.debug('height2snap: %s', height2snap) # WindowDC includes decorations, supposedly, but Windows only window_dc = wx.WindowDC(widget) wxbmp = __snapshot_to_bitmap(source_dc=window_dc, x2snap_from=x2snap_from, y2snap_from=y2snap_from, width2snap=width2snap, height2snap=height2snap) window_dc.Destroy() del window_dc wxbmp.SaveFile(filename, wx.BITMAP_TYPE_PNG) del wxbmp x2snap_on_screen = widget_rect_on_screen.x y2snap_on_screen = widget_rect_on_screen.y # adjust for menu/title ? sane_x2snap_on_screen = max(0, x2snap_on_screen) sane_y2snap_on_screen = max(0, y2snap_on_screen) _log.debug('x2snap_on_screen: %s', x2snap_on_screen) _log.debug('y2snap_on_screen: %s', y2snap_on_screen) _log.debug('sane x2snap_on_screen: %s', sane_x2snap_on_screen) _log.debug('sane x2snap_on_screen: %s', sane_y2snap_on_screen) screen_dc = wx.ScreenDC() # not implemented: #wxbmp = screen_dc.GetAsBitmap() # can use subrect=... wxbmp = __snapshot_to_bitmap(source_dc=screen_dc, x2snap_from=sane_x2snap_on_screen, y2snap_from=sane_y2snap_on_screen, width2snap=width2snap, height2snap=height2snap) screen_dc.Destroy() del screen_dc wxbmp.SaveFile(filename + '.screendc.png', wx.BITMAP_TYPE_PNG) del wxbmp # ClientDC does not include decorations, only client area #client_dc = wx.ClientDC(widget) #wxbmp = __snapshot_to_bitmap ( # source_dc = client_dc, # x2snap_from = x2snap_from, # y2snap_from = y2snap_from, # width2snap = width2snap, # height2snap = height2snap #) #client_dc.Destroy() #del client_dc #wxbmp.SaveFile(filename + '.clientdc.png', wx.BITMAP_TYPE_PNG) #del wxbmp # adjust for window decoration on Linux #if sys.platform == 'linux': # If the widget has a menu bar, remove that from the title bar height. #if hasattr(widget, 'GetMenuBar'): # if widget.GetMenuBar(): # title_bar_height /= 2 # print('title bar height:', title_bar_height) #width2snap += (border_width * 2) #height2snap += title_bar_height + border_width gmDispatcher.send(signal='statustext', msg=_('Saved screenshot to file [%s].') % filename) return filename
def select_narrative_from_episodes(parent=None, soap_cats=None): """soap_cats needs to be a list""" pat = gmPerson.gmCurrentPatient() emr = pat.emr if parent is None: parent = wx.GetApp().GetTopWindow() selected_soap = {} selected_issue_pks = [] selected_episode_pks = [] selected_narrative_pks = [] while 1: # 1) select health issues to select episodes from all_issues = emr.get_health_issues() all_issues.insert(0, gmEMRStructItems.get_dummy_health_issue()) dlg = gmEMRStructWidgets.cIssueListSelectorDlg( parent=parent, id=-1, issues=all_issues, msg= _('\n In the list below mark the health issues you want to report on.\n' )) selection_idxs = [] for idx in range(len(all_issues)): if all_issues[idx]['pk_health_issue'] in selected_issue_pks: selection_idxs.append(idx) if len(selection_idxs) != 0: dlg.set_selections(selections=selection_idxs) btn_pressed = dlg.ShowModal() selected_issues = dlg.get_selected_item_data() dlg.Destroy() if btn_pressed == wx.ID_CANCEL: return selected_soap.values() selected_issue_pks = [i['pk_health_issue'] for i in selected_issues] while 1: # 2) select episodes to select items from all_epis = emr.get_episodes(issues=selected_issue_pks) if len(all_epis) == 0: gmDispatcher.send( signal='statustext', msg=_( 'No episodes recorded for the health issues selected.') ) break dlg = gmEMRStructWidgets.cEpisodeListSelectorDlg( parent=parent, id=-1, episodes=all_epis, msg=_( '\n These are the episodes known for the health issues just selected.\n\n' ' Now, mark the the episodes you want to report on.\n')) selection_idxs = [] for idx in range(len(all_epis)): if all_epis[idx]['pk_episode'] in selected_episode_pks: selection_idxs.append(idx) if len(selection_idxs) != 0: dlg.set_selections(selections=selection_idxs) btn_pressed = dlg.ShowModal() selected_epis = dlg.get_selected_item_data() dlg.Destroy() if btn_pressed == wx.ID_CANCEL: break selected_episode_pks = [i['pk_episode'] for i in selected_epis] # 3) select narrative corresponding to the above constraints all_narr = emr.get_clin_narrative(episodes=selected_episode_pks, soap_cats=soap_cats) if len(all_narr) == 0: gmDispatcher.send( signal='statustext', msg=_('No narrative available for selected episodes.')) continue dlg = cNarrativeListSelectorDlg( parent=parent, id=-1, narrative=all_narr, msg= _('\n This is the narrative (type %s) for the chosen episodes.\n\n' ' Now, mark the entries you want to include in your report.\n' ) % '/'.join([ gmSoapDefs.soap_cat2l10n[cat] for cat in gmTools.coalesce(soap_cats, list('soapu')) ])) selection_idxs = [] for idx in range(len(all_narr)): if all_narr[idx]['pk_narrative'] in selected_narrative_pks: selection_idxs.append(idx) if len(selection_idxs) != 0: dlg.set_selections(selections=selection_idxs) btn_pressed = dlg.ShowModal() selected_narr = dlg.get_selected_item_data() dlg.Destroy() if btn_pressed == wx.ID_CANCEL: continue selected_narrative_pks = [i['pk_narrative'] for i in selected_narr] for narr in selected_narr: selected_soap[narr['pk_narrative']] = narr
def import_hook_module(reimport=False): global hook_module if not reimport: if hook_module is not None: return True # hardcoding path and script name allows us to # not need configuration for it, the environment # can always be detected at runtime (workplace etc) script_name = 'hook_script.py' script_path = os.path.expanduser(os.path.join('~', '.gnumed', 'scripts')) full_script = os.path.join(script_path, script_name) if not os.access(full_script, os.F_OK): _log.warning('creating default hook script') f = io.open(full_script, mode='wt', encoding='utf8') f.write(""" # known hooks: # %s def run_script(hook=None): pass """ % '# '.join(known_hooks)) f.close() os.chmod(full_script, 384) if os.path.islink(full_script): gmDispatcher.send(signal='statustext', msg=_('Script must not be a link: [%s].') % full_script) return False if not os.access(full_script, os.R_OK): gmDispatcher.send( signal='statustext', msg=_('Script must be readable by the calling user: [%s].') % full_script) return False script_stat_val = os.stat(full_script) _log.debug('hook script stat(): %s', script_stat_val) script_perms = stat.S_IMODE(script_stat_val.st_mode) _log.debug('hook script mode: %s (oktal: %s)', script_perms, oct(script_perms)) if script_perms != 384: # octal 0600 if os.name in ['nt']: _log.warning( 'this platform does not support os.stat() file permission checking' ) else: gmDispatcher.send( signal='statustext', msg= _('Script must be readable by the calling user only (permissions "0600"): [%s].' ) % full_script) return False try: tmp = gmTools.import_module_from_directory(script_path, script_name) except Exception: _log.exception('cannot import hook script') return False hook_module = tmp # if reimport: # imp.reload(tmp) # this has well-known shortcomings ! _log.info('hook script: %s', full_script) return True
def _on_merge_button_pressed(self, event): if self._TCTRL_patient1.person is None: gmDispatcher.send(signal = 'statustext', msg = _('No patient selected on the left.'), beep = True) return if self._TCTRL_patient2.person is None: gmDispatcher.send(signal = 'statustext', msg = _('No patient selected on the right.'), beep = True) return if self._RBTN_patient1.GetValue(): patient2keep = self._TCTRL_patient1.person patient2merge = self._TCTRL_patient2.person else: patient2keep = self._TCTRL_patient2.person patient2merge = self._TCTRL_patient1.person if patient2merge['lastnames'] == 'Kirk': if _cfg.get(option = 'debug'): gmNetworkTools.open_url_in_browser(url = 'http://en.wikipedia.org/wiki/File:Picard_as_Locutus.jpg') gmGuiHelpers.gm_show_info(_('\n\nYou will be assimilated.\n\n'), _('The Borg')) return else: gmDispatcher.send(signal = 'statustext', msg = _('Cannot merge Kirk into another patient.'), beep = True) return doit = gmGuiHelpers.gm_show_question ( aMessage = _( 'Are you positively sure you want to merge patient\n\n' ' #%s: %s (%s, %s)\n\n' 'into patient\n\n' ' #%s: %s (%s, %s) ?\n\n' 'Note that this action can ONLY be reversed by a laborious\n' 'manual process requiring in-depth knowledge about databases\n' 'and the patients in question !\n' ) % ( patient2merge.ID, patient2merge['description_gender'], patient2merge['gender'], patient2merge.get_formatted_dob(format = '%Y %b %d'), patient2keep.ID, patient2keep['description_gender'], patient2keep['gender'], patient2keep.get_formatted_dob(format = '%Y %b %d') ), aTitle = _('Merging patients: confirmation'), cancel_button = False ) if not doit: return conn = gmAuthWidgets.get_dbowner_connection(procedure = _('Merging patients')) if conn is None: gmDispatcher.send(signal = 'statustext', msg = _('Cannot merge patients without admin access.'), beep = True) return success, msg = patient2keep.assimilate_identity(other_identity = patient2merge, link_obj = conn) conn.close() if not success: gmDispatcher.send(signal = 'statustext', msg = msg, beep = True) return msg = _( 'The patient\n' '\n' ' #%s: %s (%s, %s)\n' '\n' 'has successfully been merged into\n' '\n' ' #%s: %s (%s, %s)' ) % ( patient2merge.ID, patient2merge['description_gender'], patient2merge['gender'], patient2merge.get_formatted_dob(format = '%Y %b %d'), patient2keep.ID, patient2keep['description_gender'], patient2keep['gender'], patient2keep.get_formatted_dob(format = '%Y %b %d') ) title = _('Merging patients: success') curr_pat = gmPerson.gmCurrentPatient() # announce success if (curr_pat.connected) and (patient2keep.ID == curr_pat.ID): gmGuiHelpers.gm_show_info(aMessage = msg, aTitle = title) # and offer to activate kept patient if not active else: msg = msg + ( '\n\n\n' 'Do you want to activate that patient\n' 'now for further modifications ?\n' ) doit = gmGuiHelpers.gm_show_question ( aMessage = msg, aTitle = title, cancel_button = False ) if doit: wx.CallAfter(set_active_patient, patient = patient2keep) if self.IsModal(): self.EndModal(wx.ID_OK) else: self.Close()
def select_visual_progress_note_template(parent=None): if parent is None: parent = wx.GetApp().GetTopWindow() dlg = gmGuiHelpers.c3ButtonQuestionDlg( parent, -1, caption=_('Visual progress note source'), question=_( 'From which source do you want to pick the image template ?'), button_defs=[{ 'label': _('Database'), 'tooltip': _('List of templates in the database.'), 'default': True }, { 'label': _('File'), 'tooltip': _('Files in the filesystem.'), 'default': False }, { 'label': _('Device'), 'tooltip': _('Image capture devices (scanners, cameras, etc)'), 'default': False }]) result = dlg.ShowModal() dlg.Destroy() # 1) select from template if result == wx.ID_YES: _log.debug('visual progress note template from: database template') from Gnumed.wxpython import gmFormWidgets template = gmFormWidgets.manage_form_templates( parent=parent, template_types=[gmDocuments.DOCUMENT_TYPE_VISUAL_PROGRESS_NOTE], active_only=True) if template is None: return (None, None) filename = template.export_to_file() if filename is None: gmDispatcher.send( signal=u'statustext', msg=_('Cannot export visual progress note template for [%s].') % template['name_long']) return (None, None) return (filename, True) # 2) select from disk file if result == wx.ID_NO: _log.debug('visual progress note template from: disk file') fname = select_file_as_visual_progress_note_template(parent=parent) if fname is None: return (None, None) # create a copy of the picked file -- don't modify the original ext = os.path.splitext(fname)[1] tmp_name = gmTools.get_unique_filename(suffix=ext) _log.debug('visual progress note from file: [%s] -> [%s]', fname, tmp_name) shutil.copy2(fname, tmp_name) return (tmp_name, False) # 3) acquire from capture device if result == wx.ID_CANCEL: _log.debug('visual progress note template from: image capture device') fnames = gmDocumentWidgets.acquire_images_from_capture_device( device=None, calling_window=parent) if fnames is None: return (None, None) if len(fnames) == 0: return (None, None) return (fnames[0], False) _log.debug('no visual progress note template source selected') return (None, None)
def edit_visual_progress_note(filename=None, episode=None, discard_unmodified=False, doc_part=None, health_issue=None): """This assumes <filename> contains an image which can be handled by the configured image editor.""" if doc_part is not None: filename = doc_part.export_to_file() if filename is None: gmDispatcher.send( signal=u'statustext', msg=_('Cannot export visual progress note to file.')) return None dbcfg = gmCfg.cCfgSQL() cmd = dbcfg.get2( option=u'external.tools.visual_soap_editor_cmd', workplace=gmPraxis.gmCurrentPraxisBranch().active_workplace, bias='user') if cmd is None: gmDispatcher.send( signal=u'statustext', msg=_('Editor for visual progress note not configured.'), beep=False) cmd = configure_visual_progress_note_editor() if cmd is None: gmDispatcher.send( signal=u'statustext', msg=_('Editor for visual progress note not configured.'), beep=True) return None if u'%(img)s' in cmd: cmd = cmd % {u'img': filename} else: cmd = u'%s %s' % (cmd, filename) if discard_unmodified: original_stat = os.stat(filename) original_md5 = gmTools.file2md5(filename) success = gmShellAPI.run_command_in_shell(cmd, blocking=True) if not success: gmGuiHelpers.gm_show_error( _('There was a problem with running the editor\n' 'for visual progress notes.\n' '\n' ' [%s]\n' '\n') % cmd, _('Editing visual progress note')) return None try: open(filename, 'r').close() except Exception: _log.exception('problem accessing visual progress note file [%s]', filename) gmGuiHelpers.gm_show_error( _('There was a problem reading the visual\n' 'progress note from the file:\n' '\n' ' [%s]\n' '\n') % filename, _('Saving visual progress note')) return None if discard_unmodified: modified_stat = os.stat(filename) # same size ? if original_stat.st_size == modified_stat.st_size: modified_md5 = gmTools.file2md5(filename) # same hash ? if original_md5 == modified_md5: _log.debug('visual progress note (template) not modified') # ask user to decide msg = _( u'You either created a visual progress note from a template\n' u'in the database (rather than from a file on disk) or you\n' u'edited an existing visual progress note.\n' u'\n' u'The template/original was not modified at all, however.\n' u'\n' u'Do you still want to save the unmodified image as a\n' u'visual progress note into the EMR of the patient ?\n') save_unmodified = gmGuiHelpers.gm_show_question( msg, _('Saving visual progress note')) if not save_unmodified: _log.debug('user discarded unmodified note') return if doc_part is not None: _log.debug('updating visual progress note') doc_part.update_data_from_file(fname=filename) doc_part.set_reviewed(technically_abnormal=False, clinically_relevant=True) return None if not isinstance(episode, gmEMRStructItems.cEpisode): if episode is None: episode = _('visual progress notes') pat = gmPerson.gmCurrentPatient() emr = pat.emr episode = emr.add_episode(episode_name=episode.strip(), pk_health_issue=health_issue, is_open=False) doc = gmDocumentWidgets.save_file_as_new_document( filename=filename, document_type=gmDocuments.DOCUMENT_TYPE_VISUAL_PROGRESS_NOTE, episode=episode, unlock_patient=False, pk_org_unit=gmPraxis.gmCurrentPraxisBranch()['pk_org_unit']) doc.set_reviewed(technically_abnormal=False, clinically_relevant=True) return doc
def select_encounters(parent=None, patient=None, single_selection=True, encounters=None, ignore_OK_button=False): if patient is None: patient = gmPerson.gmCurrentPatient() if not patient.connected: gmDispatcher.send(signal='statustext', msg=_('Cannot list encounters. No active patient.')) return False if parent is None: parent = wx.GetApp().GetTopWindow() emr = patient.emr #-------------------- def new(): enc_type = gmCfgDB.get4user( option='encounter.default_type', workplace=gmPraxis.gmCurrentPraxisBranch().active_workplace) if enc_type is None: enc_type = gmEMRStructItems.get_most_commonly_used_encounter_type() if enc_type is None: enc_type = 'in surgery' enc = gmEMRStructItems.create_encounter(fk_patient=patient.ID, enc_type=enc_type) saved = edit_encounter(parent=parent, encounter=enc) if saved: return True gmEMRStructItems.delete_encounter(pk_encounter=enc['pk_encounter']) return False #-------------------- def edit(enc=None): if enc is None: return False return edit_encounter(parent=parent, encounter=enc) #-------------------- def edit_active(enc=None): return edit_encounter(parent=parent, encounter=emr.active_encounter) #-------------------- def start_new(enc=None): start_new_encounter(emr=emr) return True #-------------------- def delete(enc=None): if enc is None: return False question = _( 'Really delete encounter [%s] ?\n' '\n' 'Once deletion succeeds it cannot be undone.\n' '\n' 'Note that it will only succeed if there\n' 'is no data attached to the encounter.') % enc['pk_encounter'] delete_it = gmGuiHelpers.gm_show_question( question=question, title=_('Deleting encounter'), cancel_button=False) if not delete_it: return False if gmEMRStructItems.delete_encounter(pk_encounter=enc['pk_encounter']): return True gmDispatcher.send( signal='statustext', msg=_('Cannot delete encounter [%s]. It is probably in use.') % enc['pk_encounter'], beep=True) return False #-------------------- def get_tooltip(data): if data is None: return None return data.format( patient=patient, with_soap=False, with_docs=False, with_tests=False, with_vaccinations=False, with_rfe_aoe=True, with_family_history=False, by_episode=False, fancy_header=True, ) #-------------------- def refresh(lctrl): if encounters is None: encs = emr.get_encounters() else: encs = encounters items = [[ '%s - %s' % (gmDateTime.pydt_strftime(e['started'], '%Y %b %d %H:%M'), e['last_affirmed'].strftime('%H:%M')), e['l10n_type'], gmTools.coalesce(e['praxis_branch'], ''), gmTools.coalesce(e['reason_for_encounter'], ''), gmTools.coalesce(e['assessment_of_encounter'], ''), gmTools.bool2subst(e.has_clinical_data(), '', gmTools.u_checkmark_thin), e['pk_encounter'] ] for e in encs] lctrl.set_string_items(items=items) lctrl.set_data(data=encs) active_pk = emr.active_encounter['pk_encounter'] for idx in range(len(encs)): e = encs[idx] if e['pk_encounter'] == active_pk: lctrl.SetItemTextColour(idx, wx.Colour('RED')) #-------------------- return gmListWidgets.get_choices_from_list( parent=parent, msg=_("The patient's encounters.\n"), caption=_('Encounters ...'), columns=[ _('When'), _('Type'), _('Where'), _('Reason for Encounter'), _('Assessment of Encounter'), _('Empty'), '#' ], can_return_empty=False, single_selection=single_selection, refresh_callback=refresh, edit_callback=edit, new_callback=new, delete_callback=delete, list_tooltip_callback=get_tooltip, ignore_OK_button=ignore_OK_button, left_extra_button=(_('Edit active'), _('Edit the active encounter'), edit_active), middle_extra_button=( _('Start new'), _('Start new active encounter for the current patient.'), start_new))
def search_narrative_in_emr(parent=None, patient=None): # sanity checks if patient is None: patient = gmPerson.gmCurrentPatient() if not patient.connected: gmDispatcher.send(signal='statustext', msg=_('Cannot search EMR. No active patient.')) return False if parent is None: parent = wx.GetApp().GetTopWindow() search_term_dlg = wx.TextEntryDialog( parent=parent, message=_('Enter search term:'), caption=_('Text search of entire EMR of active patient'), style=wx.OK | wx.CANCEL | wx.CENTRE) result = search_term_dlg.ShowModal() if result != wx.ID_OK: search_term_dlg.Destroy() return False wx.BeginBusyCursor() val = search_term_dlg.GetValue() search_term_dlg.Destroy() emr = patient.emr rows = emr.search_narrative_simple(val) wx.EndBusyCursor() if len(rows) == 0: gmGuiHelpers.gm_show_info( _('Nothing found for search term:\n' ' "%s"') % val, _('Search results')) return True txt = '' for row in rows: txt += '%s: %s\n' % (row['soap_cat'], row['narrative']) txt += ' %s: %s - %s %s\n' % ( _('Encounter'), row['encounter_started'].strftime('%x %H:%M'), row['encounter_ended'].strftime('%H:%M'), row['encounter_type']) txt += ' %s: %s\n' % (_('Episode'), row['episode']) txt += ' %s: %s\n\n' % (_('Health issue'), row['health_issue']) msg = _('Search term was: "%s"\n' '\n' 'Search results:\n\n' '%s\n') % (val, txt) dlg = wx.MessageDialog(parent=parent, message=msg, caption=_('Search results for [%s]') % val, style=wx.OK | wx.STAY_ON_TOP) dlg.ShowModal() dlg.Destroy() return True
def move_progress_notes_to_another_encounter(parent=None, encounters=None, episodes=None, patient=None, move_all=False): # sanity checks if patient is None: patient = gmPerson.gmCurrentPatient() if not patient.connected: gmDispatcher.send( signal='statustext', msg=_('Cannot move progress notes. No active patient.')) return False if parent is None: parent = wx.GetApp().GetTopWindow() emr = patient.emr if encounters is None: all_encs_in_epi = emr.get_encounters(episodes=episodes, skip_empty=True) # nothing to do ? if len(all_encs_in_epi) == 0: return True encounters = gmEncounterWidgets.select_encounters( parent=parent, patient=patient, single_selection=False, encounters=all_encs_in_epi) # cancelled if encounters is None: return True # none selected if len(encounters) == 0: return True notes = emr.get_clin_narrative(encounters=encounters, episodes=episodes) # which narrative if move_all: selected_narr = notes else: selected_narr = gmListWidgets.get_choices_from_list( parent=parent, caption=_('Moving progress notes between encounters ...'), single_selection=False, can_return_empty=True, data=notes, msg=_('\n Select the progress notes to move from the list !\n\n'), columns=[_('when'), _('who'), _('type'), _('entry')], choices=[[ narr['date'].strftime('%x %H:%M'), narr['modified_by'], gmSoapDefs.soap_cat2l10n[narr['soap_cat']], narr['narrative'].replace('\n', '/').replace('\r', '/') ] for narr in notes]) if not selected_narr: return True # which encounter to move to enc2move2 = gmEncounterWidgets.select_encounters(parent=parent, patient=patient, single_selection=True) if not enc2move2: return True for narr in selected_narr: narr['pk_encounter'] = enc2move2['pk_encounter'] narr.save() return True
def export_narrative_for_medistar_import(parent=None, soap_cats='soapu', encounter=None): # sanity checks pat = gmPerson.gmCurrentPatient() if not pat.connected: gmDispatcher.send( signal='statustext', msg=_('Cannot export EMR for Medistar. No active patient.')) return False if encounter is None: encounter = pat.emr.active_encounter if parent is None: parent = wx.GetApp().GetTopWindow() # get file name aWildcard = "%s (*.txt)|*.txt|%s (*)|*" % (_("text files"), _("all files")) # FIXME: make configurable aDefDir = os.path.abspath(os.path.expanduser(os.path.join('~', 'gnumed'))) # FIXME: make configurable fname = '%s-%s-%s-%s-%s.txt' % ( 'Medistar-MD', time.strftime('%Y-%m-%d', time.localtime()), pat['lastnames'].replace(' ', '-'), pat['firstnames'].replace( ' ', '_'), pat.get_formatted_dob(format='%Y-%m-%d')) dlg = wx.FileDialog( parent=parent, message=_("Save EMR extract for MEDISTAR import as..."), defaultDir=aDefDir, defaultFile=fname, wildcard=aWildcard, style=wx.FD_SAVE) choice = dlg.ShowModal() fname = dlg.GetPath() dlg.Destroy() if choice != wx.ID_OK: return False wx.BeginBusyCursor() _log.debug('exporting encounter for medistar import to [%s]', fname) exporter = gmPatientExporter.cMedistarSOAPExporter(patient=pat) successful, fname = exporter.save_to_file(filename=fname, encounter=encounter, soap_cats='soapu', export_to_import_file=True) if not successful: gmGuiHelpers.gm_show_error( _('Error exporting progress notes for MEDISTAR import.'), _('MEDISTAR progress notes export')) wx.EndBusyCursor() return False gmDispatcher.send( signal='statustext', msg= _('Successfully exported progress notes into file [%s] for Medistar import.' ) % fname, beep=False) wx.EndBusyCursor() return True
def generate_form_from_template(parent=None, template_types=None, edit=None, template=None, excluded_template_types=None): """If <edit> is None it will honor the template setting.""" if parent is None: parent = wx.GetApp().GetTopWindow() # 1) get template to use if template is None: template = manage_form_templates( parent=parent, active_only=True, template_types=template_types, excluded_types=excluded_template_types) if template is None: gmDispatcher.send(signal='statustext', msg=_('No document template selected.'), beep=False) return None if template['engine'] == 'O': return print_doc_from_ooo_template(template=template) wx.BeginBusyCursor() # 2) process template try: form = template.instantiate() except KeyError: _log.exception('cannot instantiate document template [%s]', template) gmGuiHelpers.gm_show_error( aMessage=_('Invalid document template [%s - %s (%s)]') % (name, ver, template['engine']), aTitle=_('Generating document from template')) wx.EndBusyCursor() return None ph = gmMacro.gmPlaceholderHandler() #ph.debug = True form.substitute_placeholders(data_source=ph) if edit is None: if form.template['edit_after_substitution']: edit = True else: edit = False if edit: wx.EndBusyCursor() form.edit() wx.BeginBusyCursor() # 3) generate output pdf_name = form.generate_output() wx.EndBusyCursor() if pdf_name is not None: return form gmGuiHelpers.gm_show_error( aMessage=_('Error generating document printout.'), aTitle=_('Generating document printout')) return None
def select_narrative_by_episode(parent=None, soap_cats=None): pat = gmPerson.gmCurrentPatient() emr = pat.emr all_epis = [ epi for epi in emr.get_episodes(order_by='description') if epi.has_narrative ] if len(all_epis) == 0: gmDispatcher.send(signal='statustext', msg=_('No episodes with progress notes found.')) return [] if parent is None: parent = wx.GetApp().GetTopWindow() if soap_cats is None: soap_cats = 'soapu' soap_cats = list(soap_cats) i18n_soap_cats = [ gmSoapDefs.soap_cat2l10n[cat].upper() for cat in soap_cats ] selected_soap = {} #selected_narrative_pks = [] #----------------------------------------------- def get_soap_tooltip(soap): return soap.format(fancy=True, width=60) #----------------------------------------------- def pick_soap_from_episode(episode): if episode is None: return False narr_for_epi = emr.get_clin_narrative(episodes=[episode['pk_episode']], soap_cats=soap_cats) if len(narr_for_epi) == 0: gmDispatcher.send( signal='statustext', msg=_('No narrative available for selected episode.')) return True selected_narr = gmListWidgets.get_choices_from_list( parent=parent, msg=_('Pick the [%s] narrative you want to include in the report.') % '/'.join(i18n_soap_cats), caption=_('Picking [%s] from %s%s%s') % ('/'.join(i18n_soap_cats), gmTools.u_left_double_angle_quote, episode['description'], gmTools.u_right_double_angle_quote), columns=[_('When'), _('Who'), _('Type'), _('Entry')], choices=[[ gmDateTime.pydt_strftime(narr['date'], '%Y %b %d %H:%M', accuracy=gmDateTime.acc_minutes), narr['modified_by'], gmSoapDefs.soap_cat2l10n[narr['soap_cat']], narr['narrative'].replace('\n', '//').replace('\r', '//') ] for narr in narr_for_epi], data=narr_for_epi, #selections=None, #edit_callback=None, single_selection=False, can_return_empty=False, list_tooltip_callback=get_soap_tooltip) if selected_narr is None: return True for narr in selected_narr: selected_soap[narr['pk_narrative']] = narr return True # selection_idxs = [] # for idx in range(len(narr_for_epi)): # if narr_for_epi[idx]['pk_narrative'] in selected_narrative_pks: # selection_idxs.append(idx) # if len(selection_idxs) != 0: # dlg.set_selections(selections = selection_idxs) # selected_narrative_pks = [ i['pk_narrative'] for i in selected_narr ] # for narr in selected_narr: # selected_soap[narr['pk_narrative']] = narr # # print "before returning from picking soap" # # return True # #----------------------------------------------- def edit_episode(episode): return gmEMRStructWidgets.edit_episode(parent=parent, episode=episode) #----------------------------------------------- def refresh_episodes(lctrl): all_epis = [ epi for epi in emr.get_episodes(order_by='description') if epi.has_narrative ] lctrl.set_string_items([[ '%s%s' % (e['description'], gmTools.coalesce(e['health_issue'], '', ' (%s)')), gmTools.bool2subst(e['episode_open'], _('open'), _('closed')) ] for e in all_epis]) lctrl.set_data(all_epis) #----------------------------------------------- def get_episode_tooltip(episode): return episode.format(patient=pat, with_encounters=False, with_documents=False, with_hospital_stays=False, with_procedures=False, with_family_history=False, with_tests=False, with_vaccinations=False) #----------------------------------------------- #selected_episode_pks = [] epis_picked_from = gmListWidgets.get_choices_from_list( parent=parent, msg=_('\n Select the episode you want to report on.'), caption=_('Picking [%s] from episodes') % '/'.join(i18n_soap_cats), columns=[_('Episode'), _('Status')], edit_callback=edit_episode, refresh_callback=refresh_episodes, single_selection=True, can_return_empty=True, ignore_OK_button=False, left_extra_button=(_('&Pick notes'), _('Pick [%s] entries from selected episode') % '/'.join(i18n_soap_cats), pick_soap_from_episode), list_tooltip_callback=get_episode_tooltip) if epis_picked_from is None: return [] return selected_soap.values()
def _on_notebook_page_changing(self, event): """Called before notebook page change is processed.""" _log.debug('just before switching notebook tabs') _log.debug('id: %s', event.Id) _log.debug('event object (= source notebook): %s = %s', event.EventObject.Id, event.EventObject) _log.debug('this notebook (= event receiver): %s = %s', self.Id, self) if event.EventObject.Id != self.Id: _log.error('this event came from another notebook') self.__target_page_already_checked = False self.__id_nb_page_before_switch = self.GetSelection() self.__id_evt_page_before_switch = event.GetOldSelection() __id_evt_page_after_switch = event.GetSelection() _log.debug('source/target page state in EVT_NOTEBOOK_PAGE_CHANGING:') _log.debug( ' #1 - notebook current page: %s (= notebook.GetSelection())', self.__id_nb_page_before_switch) _log.debug( ' #2 - event source page: %s (= page event says it is coming from, event.GetOldSelection())', self.__id_evt_page_before_switch) _log.debug( ' #3 - event target page: %s (= page event wants to go to, event.GetSelection())', __id_evt_page_after_switch) if self.__id_evt_page_before_switch != self.__id_nb_page_before_switch: _log.warning(' problem: #1 and #2 really should match but do not') # can we check the target page ? if __id_evt_page_after_switch == self.__id_evt_page_before_switch: # no, so complain # (the docs say that on Windows GetSelection() returns the # old page ID, eg. the same value GetOldSelection() returns) _log.debug('this system is: sys: [%s] wx: [%s]', sys.platform, wx.Platform) _log.debug( 'it seems to be one of those platforms that have no clue which notebook page they are switching to' ) _log.debug( '(Windows is documented to return the old page from both evt.GetOldSelection() and evt.GetSelection())' ) _log.debug('current notebook page : %s', self.__id_nb_page_before_switch) _log.debug('source page from event: %s', self.__id_evt_page_before_switch) _log.debug('target page from event: %s', __id_evt_page_after_switch) _log.warning( 'cannot check whether notebook page change needs to be vetoed') # but let's do a basic check anyways pat = gmPerson.gmCurrentPatient() if not pat.connected: gmDispatcher.send( signal='statustext', msg=_('Cannot change notebook tabs. No active patient.')) event.Veto() return # that test passed, so let's hope things are fine event.Allow() # redundant ? event.Skip() return # check target page target_page = self.__gb['horstspace.notebook.pages'][ __id_evt_page_after_switch] _log.debug('checking event target page for focussability: %s', target_page) if not target_page.can_receive_focus(): _log.warning('veto()ing page change') event.Veto() return # everything seems fine so switch _log.debug('event target page seems focussable') self.__target_page_already_checked = True event.Allow() # redundant ? event.Skip() return
def add_editor(self, problem=None, allow_same_problem=False): """Add a progress note editor page. The way <allow_same_problem> is currently used in callers it only applies to unassociated episodes. """ problem_to_add = problem # determine label if problem_to_add is None: label = _('new problem') else: # normalize problem type if isinstance(problem_to_add, gmEMRStructItems.cEpisode): problem_to_add = gmEMRStructItems.episode2problem(episode = problem_to_add, allow_closed = True) elif isinstance(problem_to_add, gmEMRStructItems.cHealthIssue): problem_to_add = gmEMRStructItems.health_issue2problem(health_issue = problem_to_add, allow_irrelevant = True) if not isinstance(problem_to_add, gmEMRStructItems.cProblem): raise TypeError('cannot open progress note editor for [%s]' % problem_to_add) label = problem_to_add['problem'] # FIXME: configure maximum length if len(label) > 23: label = label[:21] + gmTools.u_ellipsis # new unassociated problem or dupes allowed if allow_same_problem: new_page = gmProgressNotesEAWidgets.cProgressNotesEAPnl(self, -1, problem = problem_to_add) result = self.AddPage ( page = new_page, text = label, select = True ) return result # real problem, no dupes allowed # - raise existing editor for page_idx in range(self.GetPageCount()): page = self.GetPage(page_idx) if problem_to_add is None: if page.problem is None: self.SetSelection(page_idx) gmDispatcher.send(signal = 'statustext', msg = 'Raising existing editor.', beep = True) return True continue # editor is for unassociated new problem if page.problem is None: continue # editor is for episode if page.problem['type'] == 'episode': if page.problem['pk_episode'] == problem_to_add['pk_episode']: self.SetSelection(page_idx) gmDispatcher.send(signal = 'statustext', msg = 'Raising existing editor.', beep = True) return True continue # editor is for health issue if page.problem['type'] == 'issue': if page.problem['pk_health_issue'] == problem_to_add['pk_health_issue']: self.SetSelection(page_idx) gmDispatcher.send(signal = 'statustext', msg = 'Raising existing editor.', beep = True) return True continue # - or add new editor new_page = gmProgressNotesEAWidgets.cProgressNotesEAPnl(parent = self, problem = problem_to_add) result = self.AddPage ( page = new_page, text = label, select = True ) return result
def get_person_from_external_sources(parent=None, search_immediately=False, activate_immediately=False): """Load patient from external source. - scan external sources for candidates - let user select source - if > 1 available: always - if only 1 available: depending on search_immediately - search for patients matching info from external source - if more than one match: - let user select patient - if no match: - create patient - activate patient """ # get DTOs from interfaces dtos = [] dtos.extend(load_persons_from_xdt()) dtos.extend(load_persons_from_pracsoft_au()) dtos.extend(load_persons_from_kvks()) dtos.extend(load_persons_from_ca_msva()) # no external persons if len(dtos) == 0: gmDispatcher.send(signal='statustext', msg=_('No patients found in external sources.')) return None # one external patient with DOB - already active ? if (len(dtos) == 1) and (dtos[0]['dto'].dob is not None): dto = dtos[0]['dto'] # is it already the current patient ? curr_pat = gmPerson.gmCurrentPatient() if curr_pat.connected: key_dto = dto.firstnames + dto.lastnames + dto.dob.strftime('%Y-%m-%d') + dto.gender names = curr_pat.get_active_name() key_pat = names['firstnames'] + names['lastnames'] + curr_pat.get_formatted_dob(format = '%Y-%m-%d') + curr_pat['gender'] _log.debug('current patient: %s' % key_pat) _log.debug('dto patient : %s' % key_dto) if key_dto == key_pat: gmDispatcher.send(signal='statustext', msg=_('The only external patient is already active in GNUmed.'), beep=False) return None # one external person - look for internal match immediately ? if (len(dtos) == 1) and search_immediately: dto = dtos[0]['dto'] # several external persons else: if parent is None: parent = wx.GetApp().GetTopWindow() dlg = cSelectPersonDTOFromListDlg(parent=parent, id=-1) dlg.set_dtos(dtos=dtos) result = dlg.ShowModal() if result == wx.ID_CANCEL: return None dto = dlg.get_selected_dto()['dto'] dlg.DestroyLater() # search idents = dto.get_candidate_identities(can_create=True) if idents is None: gmGuiHelpers.gm_show_info (_( 'Cannot create new patient:\n\n' ' [%s %s (%s), %s]' ) % ( dto.firstnames, dto.lastnames, dto.gender, gmDateTime.pydt_strftime(dto.dob, '%Y %b %d') ), _('Activating external patient') ) return None if len(idents) == 1: ident = idents[0] if len(idents) > 1: if parent is None: parent = wx.GetApp().GetTopWindow() dlg = cSelectPersonFromListDlg(parent=parent, id=-1) dlg.set_persons(persons=idents) result = dlg.ShowModal() ident = dlg.get_selected_person() dlg.DestroyLater() if result == wx.ID_CANCEL: return None if activate_immediately: if not set_active_patient(patient = ident): gmGuiHelpers.gm_show_info (_( 'Cannot activate patient:\n\n' '%s %s (%s)\n' '%s' ) % ( dto.firstnames, dto.lastnames, dto.gender, gmDateTime.pydt_strftime(dto.dob, '%Y %b %d') ), _('Activating external patient') ) return None dto.import_extra_data(identity = ident) dto.delete_from_source() return ident