Package Gnumed :: Package wxpython :: Module gmDemographicsWidgets
[frames] | no frames]

Source Code for Module Gnumed.wxpython.gmDemographicsWidgets

   1  """Widgets dealing with patient demographics.""" 
   2  #============================================================ 
   3  __version__ = "$Revision: 1.175 $" 
   4  __author__ = "R.Terry, SJ Tan, I Haywood, Carlos Moro <cfmoro1976@yahoo.es>" 
   5  __license__ = 'GPL v2 or later (details at http://www.gnu.org)' 
   6   
   7  # standard library 
   8  import sys 
   9  import sys 
  10  import codecs 
  11  import re as regex 
  12  import logging 
  13  import webbrowser 
  14  import os 
  15   
  16   
  17  import wx 
  18  import wx.wizard 
  19  import wx.lib.imagebrowser as wx_imagebrowser 
  20  import wx.lib.statbmp as wx_genstatbmp 
  21   
  22   
  23  # GNUmed specific 
  24  if __name__ == '__main__': 
  25          sys.path.insert(0, '../../') 
  26  from Gnumed.pycommon import gmDispatcher 
  27  from Gnumed.pycommon import gmI18N 
  28  from Gnumed.pycommon import gmMatchProvider 
  29  from Gnumed.pycommon import gmPG2 
  30  from Gnumed.pycommon import gmTools 
  31  from Gnumed.pycommon import gmCfg 
  32  from Gnumed.pycommon import gmDateTime 
  33  from Gnumed.pycommon import gmShellAPI 
  34   
  35  from Gnumed.business import gmDemographicRecord 
  36  from Gnumed.business import gmPersonSearch 
  37  from Gnumed.business import gmSurgery 
  38  from Gnumed.business import gmPerson 
  39   
  40  from Gnumed.wxpython import gmPhraseWheel 
  41  from Gnumed.wxpython import gmRegetMixin 
  42  from Gnumed.wxpython import gmAuthWidgets 
  43  from Gnumed.wxpython import gmPersonContactWidgets 
  44  from Gnumed.wxpython import gmEditArea 
  45  from Gnumed.wxpython import gmListWidgets 
  46  from Gnumed.wxpython import gmDateTimeInput 
  47  from Gnumed.wxpython import gmDataMiningWidgets 
  48  from Gnumed.wxpython import gmGuiHelpers 
  49   
  50   
  51  # constant defs 
  52  _log = logging.getLogger('gm.ui') 
  53   
  54   
  55  try: 
  56          _('dummy-no-need-to-translate-but-make-epydoc-happy') 
  57  except NameError: 
  58          _ = lambda x:x 
  59   
  60  #============================================================ 
  61  # image tags related widgets 
  62  #------------------------------------------------------------ 
63 -def edit_tag_image(parent=None, tag_image=None, single_entry=False):
64 if tag_image is not None: 65 if tag_image['is_in_use']: 66 gmGuiHelpers.gm_show_info ( 67 aTitle = _('Editing tag'), 68 aMessage = _( 69 'Cannot edit the image tag\n' 70 '\n' 71 ' "%s"\n' 72 '\n' 73 'because it is currently in use.\n' 74 ) % tag_image['l10n_description'] 75 ) 76 return False 77 78 ea = cTagImageEAPnl(parent = parent, id = -1) 79 ea.data = tag_image 80 ea.mode = gmTools.coalesce(tag_image, 'new', 'edit') 81 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = single_entry) 82 dlg.SetTitle(gmTools.coalesce(tag_image, _('Adding new tag'), _('Editing tag'))) 83 if dlg.ShowModal() == wx.ID_OK: 84 dlg.Destroy() 85 return True 86 dlg.Destroy() 87 return False
88 #------------------------------------------------------------
89 -def manage_tag_images(parent=None):
90 91 if parent is None: 92 parent = wx.GetApp().GetTopWindow() 93 #------------------------------------------------------------ 94 def go_to_openclipart_org(tag_image): 95 webbrowser.open ( 96 url = u'http://www.openclipart.org', 97 new = False, 98 autoraise = True 99 ) 100 webbrowser.open ( 101 url = u'http://www.google.com', 102 new = False, 103 autoraise = True 104 ) 105 return True
106 #------------------------------------------------------------ 107 def edit(tag_image=None): 108 return edit_tag_image(parent = parent, tag_image = tag_image, single_entry = (tag_image is not None)) 109 #------------------------------------------------------------ 110 def delete(tag): 111 if tag['is_in_use']: 112 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete this tag. It is in use.'), beep = True) 113 return False 114 115 return gmDemographicRecord.delete_tag_image(tag_image = tag['pk_tag_image']) 116 #------------------------------------------------------------ 117 def refresh(lctrl): 118 tags = gmDemographicRecord.get_tag_images(order_by = u'l10n_description') 119 items = [ [ 120 t['l10n_description'], 121 gmTools.bool2subst(t['is_in_use'], u'X', u''), 122 u'%s' % t['size'], 123 t['pk_tag_image'] 124 ] for t in tags ] 125 lctrl.set_string_items(items) 126 lctrl.set_column_widths(widths = [wx.LIST_AUTOSIZE, wx.LIST_AUTOSIZE_USEHEADER, wx.LIST_AUTOSIZE_USEHEADER, wx.LIST_AUTOSIZE]) 127 lctrl.set_data(tags) 128 #------------------------------------------------------------ 129 msg = _('\nTags with images registered with GNUmed.\n') 130 131 tag = gmListWidgets.get_choices_from_list ( 132 parent = parent, 133 msg = msg, 134 caption = _('Showing tags with images.'), 135 columns = [_('Tag name'), _('In use'), _('Image size'), u'#'], 136 single_selection = True, 137 new_callback = edit, 138 edit_callback = edit, 139 delete_callback = delete, 140 refresh_callback = refresh, 141 left_extra_button = (_('WWW'), _('Go to www.openclipart.org for images.'), go_to_openclipart_org) 142 ) 143 144 return tag 145 #------------------------------------------------------------ 146 from Gnumed.wxGladeWidgets import wxgTagImageEAPnl 147
148 -class cTagImageEAPnl(wxgTagImageEAPnl.wxgTagImageEAPnl, gmEditArea.cGenericEditAreaMixin):
149
150 - def __init__(self, *args, **kwargs):
151 152 try: 153 data = kwargs['tag_image'] 154 del kwargs['tag_image'] 155 except KeyError: 156 data = None 157 158 wxgTagImageEAPnl.wxgTagImageEAPnl.__init__(self, *args, **kwargs) 159 gmEditArea.cGenericEditAreaMixin.__init__(self) 160 161 self.mode = 'new' 162 self.data = data 163 if data is not None: 164 self.mode = 'edit' 165 166 self.__selected_image_file = None
167 #---------------------------------------------------------------- 168 # generic Edit Area mixin API 169 #----------------------------------------------------------------
170 - def _valid_for_save(self):
171 172 valid = True 173 174 if self.mode == u'new': 175 if self.__selected_image_file is None: 176 valid = False 177 gmDispatcher.send(signal = 'statustext', msg = _('Must pick an image file for a new tag.'), beep = True) 178 self._BTN_pick_image.SetFocus() 179 180 if self.__selected_image_file is not None: 181 try: 182 open(self.__selected_image_file).close() 183 except StandardError: 184 valid = False 185 self.__selected_image_file = None 186 gmDispatcher.send(signal = 'statustext', msg = _('Cannot open the image file [%s].') % self.__selected_image_file, beep = True) 187 self._BTN_pick_image.SetFocus() 188 189 if self._TCTRL_description.GetValue().strip() == u'': 190 valid = False 191 self.display_tctrl_as_valid(self._TCTRL_description, False) 192 self._TCTRL_description.SetFocus() 193 else: 194 self.display_tctrl_as_valid(self._TCTRL_description, True) 195 196 return (valid is True)
197 #----------------------------------------------------------------
198 - def _save_as_new(self):
199 200 dbo_conn = gmAuthWidgets.get_dbowner_connection(procedure = _('Creating tag with image')) 201 if dbo_conn is None: 202 return False 203 204 data = gmDemographicRecord.create_tag_image(description = self._TCTRL_description.GetValue().strip(), link_obj = dbo_conn) 205 dbo_conn.close() 206 207 data['filename'] = self._TCTRL_filename.GetValue().strip() 208 data.save() 209 data.update_image_from_file(filename = self.__selected_image_file) 210 211 # must be done very late or else the property access 212 # will refresh the display such that later field 213 # access will return empty values 214 self.data = data 215 return True
216 #----------------------------------------------------------------
217 - def _save_as_update(self):
218 219 # this is somewhat fake as it never actually uses the gm-dbo conn 220 # (although it does verify it) 221 dbo_conn = gmAuthWidgets.get_dbowner_connection(procedure = _('Updating tag with image')) 222 if dbo_conn is None: 223 return False 224 dbo_conn.close() 225 226 self.data['description'] = self._TCTRL_description.GetValue().strip() 227 self.data['filename'] = self._TCTRL_filename.GetValue().strip() 228 self.data.save() 229 230 if self.__selected_image_file is not None: 231 open(self.__selected_image_file).close() 232 self.data.update_image_from_file(filename = self.__selected_image_file) 233 self.__selected_image_file = None 234 235 return True
236 #----------------------------------------------------------------
237 - def _refresh_as_new(self):
238 self._TCTRL_description.SetValue(u'') 239 self._TCTRL_filename.SetValue(u'') 240 self._BMP_image.SetBitmap(bitmap = wx.EmptyBitmap(100, 100)) 241 242 self.__selected_image_file = None 243 244 self._TCTRL_description.SetFocus()
245 #----------------------------------------------------------------
247 self._refresh_as_new()
248 #----------------------------------------------------------------
249 - def _refresh_from_existing(self):
250 self._TCTRL_description.SetValue(self.data['l10n_description']) 251 self._TCTRL_filename.SetValue(gmTools.coalesce(self.data['filename'], u'')) 252 fname = self.data.export_image2file() 253 if fname is None: 254 self._BMP_image.SetBitmap(bitmap = wx.EmptyBitmap(100, 100)) 255 else: 256 self._BMP_image.SetBitmap(bitmap = gmGuiHelpers.file2scaled_image(filename = fname, height = 100)) 257 258 self.__selected_image_file = None 259 260 self._TCTRL_description.SetFocus()
261 #---------------------------------------------------------------- 262 # event handlers 263 #----------------------------------------------------------------
264 - def _on_pick_image_button_pressed(self, event):
265 paths = gmTools.gmPaths() 266 img_dlg = wx_imagebrowser.ImageDialog(parent = self, set_dir = paths.home_dir) 267 img_dlg.Centre() 268 if img_dlg.ShowModal() != wx.ID_OK: 269 return 270 271 self.__selected_image_file = img_dlg.GetFile() 272 self._BMP_image.SetBitmap(bitmap = gmGuiHelpers.file2scaled_image(filename = self.__selected_image_file, height = 100)) 273 fdir, fname = os.path.split(self.__selected_image_file) 274 self._TCTRL_filename.SetValue(fname)
275 276 #============================================================ 277 from Gnumed.wxGladeWidgets import wxgVisualSoapPresenterPnl 278
279 -class cImageTagPresenterPnl(wxgVisualSoapPresenterPnl.wxgVisualSoapPresenterPnl):
280
281 - def __init__(self, *args, **kwargs):
282 wxgVisualSoapPresenterPnl.wxgVisualSoapPresenterPnl.__init__(self, *args, **kwargs) 283 self._SZR_bitmaps = self.GetSizer() 284 self.__bitmaps = [] 285 286 self.__context_popup = wx.Menu() 287 288 item = self.__context_popup.Append(-1, _('&Edit comment')) 289 self.Bind(wx.EVT_MENU, self.__edit_tag, item) 290 291 item = self.__context_popup.Append(-1, _('&Remove tag')) 292 self.Bind(wx.EVT_MENU, self.__remove_tag, item)
293 #-------------------------------------------------------- 294 # external API 295 #--------------------------------------------------------
296 - def refresh(self, patient):
297 298 self.clear() 299 300 for tag in patient.get_tags(order_by = u'l10n_description'): 301 fname = tag.export_image2file() 302 if fname is None: 303 _log.warning('cannot export image data of tag [%s]', tag['l10n_description']) 304 continue 305 img = gmGuiHelpers.file2scaled_image(filename = fname, height = 20) 306 bmp = wx_genstatbmp.GenStaticBitmap(self, -1, img, style = wx.NO_BORDER) 307 bmp.SetToolTipString(u'%s%s' % ( 308 tag['l10n_description'], 309 gmTools.coalesce(tag['comment'], u'', u'\n\n%s') 310 )) 311 bmp.tag = tag 312 bmp.Bind(wx.EVT_RIGHT_UP, self._on_bitmap_rightclicked) 313 # FIXME: add context menu for Delete/Clone/Add/Configure 314 self._SZR_bitmaps.Add(bmp, 0, wx.LEFT | wx.RIGHT | wx.TOP | wx.BOTTOM, 1) # | wx.EXPAND 315 self.__bitmaps.append(bmp) 316 317 self.GetParent().Layout()
318 #--------------------------------------------------------
319 - def clear(self):
320 for child_idx in range(len(self._SZR_bitmaps.GetChildren())): 321 self._SZR_bitmaps.Detach(child_idx) 322 for bmp in self.__bitmaps: 323 bmp.Destroy() 324 self.__bitmaps = []
325 #-------------------------------------------------------- 326 # internal helpers 327 #--------------------------------------------------------
328 - def __remove_tag(self, evt):
329 if self.__current_tag is None: 330 return 331 pat = gmPerson.gmCurrentPatient() 332 if not pat.connected: 333 return 334 pat.remove_tag(tag = self.__current_tag['pk_identity_tag'])
335 #--------------------------------------------------------
336 - def __edit_tag(self, evt):
337 if self.__current_tag is None: 338 return 339 340 msg = _('Edit the comment on tag [%s]') % self.__current_tag['l10n_description'] 341 comment = wx.GetTextFromUser ( 342 message = msg, 343 caption = _('Editing tag comment'), 344 default_value = gmTools.coalesce(self.__current_tag['comment'], u''), 345 parent = self 346 ) 347 348 if comment == u'': 349 return 350 351 if comment.strip() == self.__current_tag['comment']: 352 return 353 354 if comment == u' ': 355 self.__current_tag['comment'] = None 356 else: 357 self.__current_tag['comment'] = comment.strip() 358 359 self.__current_tag.save()
360 #-------------------------------------------------------- 361 # event handlers 362 #--------------------------------------------------------
363 - def _on_bitmap_rightclicked(self, evt):
364 self.__current_tag = evt.GetEventObject().tag 365 self.PopupMenu(self.__context_popup, pos = wx.DefaultPosition) 366 self.__current_tag = None
367 #============================================================ 368 #============================================================
369 -class cKOrganizerSchedulePnl(gmDataMiningWidgets.cPatientListingPnl):
370
371 - def __init__(self, *args, **kwargs):
372 373 kwargs['message'] = _("Today's KOrganizer appointments ...") 374 kwargs['button_defs'] = [ 375 {'label': _('Reload'), 'tooltip': _('Reload appointments from KOrganizer')}, 376 {'label': u''}, 377 {'label': u''}, 378 {'label': u''}, 379 {'label': u'KOrganizer', 'tooltip': _('Launch KOrganizer')} 380 ] 381 gmDataMiningWidgets.cPatientListingPnl.__init__(self, *args, **kwargs) 382 383 self.fname = os.path.expanduser(os.path.join('~', '.gnumed', 'tmp', 'korganizer2gnumed.csv')) 384 self.reload_cmd = 'konsolekalendar --view --export-type csv --export-file %s' % self.fname
385 386 #--------------------------------------------------------
387 - def _on_BTN_1_pressed(self, event):
388 """Reload appointments from KOrganizer.""" 389 self.reload_appointments()
390 #--------------------------------------------------------
391 - def _on_BTN_5_pressed(self, event):
392 """Reload appointments from KOrganizer.""" 393 found, cmd = gmShellAPI.detect_external_binary(binary = 'korganizer') 394 395 if not found: 396 gmDispatcher.send(signal = 'statustext', msg = _('KOrganizer is not installed.'), beep = True) 397 return 398 399 gmShellAPI.run_command_in_shell(command = cmd, blocking = False)
400 #--------------------------------------------------------
401 - def reload_appointments(self):
402 try: os.remove(self.fname) 403 except OSError: pass 404 gmShellAPI.run_command_in_shell(command=self.reload_cmd, blocking=True) 405 try: 406 csv_file = codecs.open(self.fname , mode = 'rU', encoding = 'utf8', errors = 'replace') 407 except IOError: 408 gmDispatcher.send(signal = u'statustext', msg = _('Cannot access KOrganizer transfer file [%s]') % self.fname, beep = True) 409 return 410 411 csv_lines = gmTools.unicode_csv_reader ( 412 csv_file, 413 delimiter = ',' 414 ) 415 # start_date, start_time, end_date, end_time, title (patient), ort, comment, UID 416 self._LCTRL_items.set_columns ([ 417 _('Place'), 418 _('Start'), 419 u'', 420 u'', 421 _('Patient'), 422 _('Comment') 423 ]) 424 items = [] 425 data = [] 426 for line in csv_lines: 427 items.append([line[5], line[0], line[1], line[3], line[4], line[6]]) 428 data.append([line[4], line[7]]) 429 430 self._LCTRL_items.set_string_items(items = items) 431 self._LCTRL_items.set_column_widths() 432 self._LCTRL_items.set_data(data = data) 433 self._LCTRL_items.patient_key = 0
434 #-------------------------------------------------------- 435 # notebook plugins API 436 #--------------------------------------------------------
437 - def repopulate_ui(self):
438 self.reload_appointments()
439 #============================================================ 440 # occupation related widgets / functions 441 #============================================================
442 -def edit_occupation():
443 444 pat = gmPerson.gmCurrentPatient() 445 curr_jobs = pat.get_occupations() 446 if len(curr_jobs) > 0: 447 old_job = curr_jobs[0]['l10n_occupation'] 448 update = curr_jobs[0]['modified_when'].strftime('%m/%Y') 449 else: 450 old_job = u'' 451 update = u'' 452 453 msg = _( 454 'Please enter the primary occupation of the patient.\n' 455 '\n' 456 'Currently recorded:\n' 457 '\n' 458 ' %s (last updated %s)' 459 ) % (old_job, update) 460 461 new_job = wx.GetTextFromUser ( 462 message = msg, 463 caption = _('Editing primary occupation'), 464 default_value = old_job, 465 parent = None 466 ) 467 if new_job.strip() == u'': 468 return 469 470 for job in curr_jobs: 471 # unlink all but the new job 472 if job['l10n_occupation'] != new_job: 473 pat.unlink_occupation(occupation = job['l10n_occupation']) 474 # and link the new one 475 pat.link_occupation(occupation = new_job)
476 477 #------------------------------------------------------------
478 -class cOccupationPhraseWheel(gmPhraseWheel.cPhraseWheel):
479
480 - def __init__(self, *args, **kwargs):
481 query = u"select distinct name, _(name) from dem.occupation where _(name) %(fragment_condition)s" 482 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query) 483 mp.setThresholds(1, 3, 5) 484 gmPhraseWheel.cPhraseWheel.__init__ ( 485 self, 486 *args, 487 **kwargs 488 ) 489 self.SetToolTipString(_("Type or select an occupation.")) 490 self.capitalisation_mode = gmTools.CAPS_FIRST 491 self.matcher = mp
492 493 #============================================================ 494 # identity widgets / functions 495 #============================================================
496 -def disable_identity(identity=None):
497 # ask user for assurance 498 go_ahead = gmGuiHelpers.gm_show_question ( 499 _('Are you sure you really, positively want\n' 500 'to disable the following person ?\n' 501 '\n' 502 ' %s %s %s\n' 503 ' born %s\n' 504 '\n' 505 '%s\n' 506 ) % ( 507 identity['firstnames'], 508 identity['lastnames'], 509 identity['gender'], 510 identity['dob'], 511 gmTools.bool2subst ( 512 identity.is_patient, 513 _('This patient DID receive care.'), 514 _('This person did NOT receive care.') 515 ) 516 ), 517 _('Disabling person') 518 ) 519 if not go_ahead: 520 return True 521 522 # get admin connection 523 conn = gmAuthWidgets.get_dbowner_connection ( 524 procedure = _('Disabling patient') 525 ) 526 # - user cancelled 527 if conn is False: 528 return True 529 # - error 530 if conn is None: 531 return False 532 533 # now disable patient 534 gmPG2.run_rw_queries(queries = [{'cmd': u"update dem.identity set deleted=True where pk=%s", 'args': [identity['pk_identity']]}]) 535 536 return True
537 538 #------------------------------------------------------------ 539 # phrasewheels 540 #------------------------------------------------------------
541 -class cLastnamePhraseWheel(gmPhraseWheel.cPhraseWheel):
542
543 - def __init__(self, *args, **kwargs):
544 query = u"select distinct lastnames, lastnames from dem.names where lastnames %(fragment_condition)s order by lastnames limit 25" 545 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query) 546 mp.setThresholds(3, 5, 9) 547 gmPhraseWheel.cPhraseWheel.__init__ ( 548 self, 549 *args, 550 **kwargs 551 ) 552 self.SetToolTipString(_("Type or select a last name (family name/surname).")) 553 self.capitalisation_mode = gmTools.CAPS_NAMES 554 self.matcher = mp
555 #------------------------------------------------------------
556 -class cFirstnamePhraseWheel(gmPhraseWheel.cPhraseWheel):
557
558 - def __init__(self, *args, **kwargs):
559 query = u""" 560 (select distinct firstnames, firstnames from dem.names where firstnames %(fragment_condition)s order by firstnames limit 20) 561 union 562 (select distinct name, name from dem.name_gender_map where name %(fragment_condition)s order by name limit 20)""" 563 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query) 564 mp.setThresholds(3, 5, 9) 565 gmPhraseWheel.cPhraseWheel.__init__ ( 566 self, 567 *args, 568 **kwargs 569 ) 570 self.SetToolTipString(_("Type or select a first name (forename/Christian name/given name).")) 571 self.capitalisation_mode = gmTools.CAPS_NAMES 572 self.matcher = mp
573 #------------------------------------------------------------
574 -class cNicknamePhraseWheel(gmPhraseWheel.cPhraseWheel):
575
576 - def __init__(self, *args, **kwargs):
577 query = u""" 578 (select distinct preferred, preferred from dem.names where preferred %(fragment_condition)s order by preferred limit 20) 579 union 580 (select distinct firstnames, firstnames from dem.names where firstnames %(fragment_condition)s order by firstnames limit 20) 581 union 582 (select distinct name, name from dem.name_gender_map where name %(fragment_condition)s order by name limit 20)""" 583 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query) 584 mp.setThresholds(3, 5, 9) 585 gmPhraseWheel.cPhraseWheel.__init__ ( 586 self, 587 *args, 588 **kwargs 589 ) 590 self.SetToolTipString(_("Type or select an alias (nick name, preferred name, call name, warrior name, artist name).")) 591 # nicknames CAN start with lower case ! 592 #self.capitalisation_mode = gmTools.CAPS_NAMES 593 self.matcher = mp
594 #------------------------------------------------------------
595 -class cTitlePhraseWheel(gmPhraseWheel.cPhraseWheel):
596
597 - def __init__(self, *args, **kwargs):
598 query = u"select distinct title, title from dem.identity where title %(fragment_condition)s" 599 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query) 600 mp.setThresholds(1, 3, 9) 601 gmPhraseWheel.cPhraseWheel.__init__ ( 602 self, 603 *args, 604 **kwargs 605 ) 606 self.SetToolTipString(_("Type or select a title. Note that the title applies to the person, not to a particular name !")) 607 self.matcher = mp
608 #------------------------------------------------------------
609 -class cGenderSelectionPhraseWheel(gmPhraseWheel.cPhraseWheel):
610 """Let user select a gender.""" 611 612 _gender_map = None 613
614 - def __init__(self, *args, **kwargs):
615 616 if cGenderSelectionPhraseWheel._gender_map is None: 617 cmd = u""" 618 select tag, l10n_label, sort_weight 619 from dem.v_gender_labels 620 order by sort_weight desc""" 621 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx=True) 622 cGenderSelectionPhraseWheel._gender_map = {} 623 for gender in rows: 624 cGenderSelectionPhraseWheel._gender_map[gender[idx['tag']]] = { 625 'data': gender[idx['tag']], 626 'field_label': gender[idx['l10n_label']], 627 'list_label': gender[idx['l10n_label']], 628 'weight': gender[idx['sort_weight']] 629 } 630 631 mp = gmMatchProvider.cMatchProvider_FixedList(aSeq = cGenderSelectionPhraseWheel._gender_map.values()) 632 mp.setThresholds(1, 1, 3) 633 634 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 635 self.selection_only = True 636 self.matcher = mp 637 self.picklist_delay = 50
638 #------------------------------------------------------------
639 -class cExternalIDTypePhraseWheel(gmPhraseWheel.cPhraseWheel):
640
641 - def __init__(self, *args, **kwargs):
642 query = u""" 643 select distinct pk, (name || coalesce(' (%s ' || issuer || ')', '')) as label 644 from dem.enum_ext_id_types 645 where name %%(fragment_condition)s 646 order by label limit 25""" % _('issued by') 647 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query) 648 mp.setThresholds(1, 3, 5) 649 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 650 self.SetToolTipString(_("Enter or select a type for the external ID.")) 651 self.matcher = mp
652 #------------------------------------------------------------
653 -class cExternalIDIssuerPhraseWheel(gmPhraseWheel.cPhraseWheel):
654
655 - def __init__(self, *args, **kwargs):
656 query = u""" 657 select distinct issuer, issuer 658 from dem.enum_ext_id_types 659 where issuer %(fragment_condition)s 660 order by issuer limit 25""" 661 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query) 662 mp.setThresholds(1, 3, 5) 663 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 664 self.SetToolTipString(_("Type or select an ID issuer.")) 665 self.capitalisation_mode = gmTools.CAPS_FIRST 666 self.matcher = mp
667 #------------------------------------------------------------ 668 # edit areas 669 #------------------------------------------------------------ 670 from Gnumed.wxGladeWidgets import wxgExternalIDEditAreaPnl 671
672 -class cExternalIDEditAreaPnl(wxgExternalIDEditAreaPnl.wxgExternalIDEditAreaPnl):
673 """An edit area for editing/creating external IDs. 674 675 Does NOT act on/listen to the current patient. 676 """
677 - def __init__(self, *args, **kwargs):
678 679 try: 680 self.ext_id = kwargs['external_id'] 681 del kwargs['external_id'] 682 except: 683 self.ext_id = None 684 685 wxgExternalIDEditAreaPnl.wxgExternalIDEditAreaPnl.__init__(self, *args, **kwargs) 686 687 self.identity = None 688 689 self.__register_events() 690 691 self.refresh()
692 #-------------------------------------------------------- 693 # external API 694 #--------------------------------------------------------
695 - def refresh(self, ext_id=None):
696 if ext_id is not None: 697 self.ext_id = ext_id 698 699 if self.ext_id is not None: 700 self._PRW_type.SetText(value = self.ext_id['name'], data = self.ext_id['pk_type']) 701 self._TCTRL_value.SetValue(self.ext_id['value']) 702 self._PRW_issuer.SetText(self.ext_id['issuer']) 703 self._TCTRL_comment.SetValue(gmTools.coalesce(self.ext_id['comment'], u''))
704 # FIXME: clear fields 705 # else: 706 # pass 707 #--------------------------------------------------------
708 - def save(self):
709 710 if not self.__valid_for_save(): 711 return False 712 713 # strip out " (issued by ...)" added by phrasewheel 714 type = regex.split(' \(%s .+\)$' % _('issued by'), self._PRW_type.GetValue().strip(), 1)[0] 715 716 # add new external ID 717 if self.ext_id is None: 718 self.identity.add_external_id ( 719 type_name = type, 720 value = self._TCTRL_value.GetValue().strip(), 721 issuer = gmTools.none_if(self._PRW_issuer.GetValue().strip(), u''), 722 comment = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), u'') 723 ) 724 # edit old external ID 725 else: 726 self.identity.update_external_id ( 727 pk_id = self.ext_id['pk_id'], 728 type = type, 729 value = self._TCTRL_value.GetValue().strip(), 730 issuer = gmTools.none_if(self._PRW_issuer.GetValue().strip(), u''), 731 comment = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), u'') 732 ) 733 734 return True
735 #-------------------------------------------------------- 736 # internal helpers 737 #--------------------------------------------------------
738 - def __register_events(self):
739 self._PRW_type.add_callback_on_lose_focus(self._on_type_set)
740 #--------------------------------------------------------
741 - def _on_type_set(self):
742 """Set the issuer according to the selected type. 743 744 Matches are fetched from existing records in backend. 745 """ 746 pk_curr_type = self._PRW_type.GetData() 747 if pk_curr_type is None: 748 return True 749 rows, idx = gmPG2.run_ro_queries(queries = [{ 750 'cmd': u"select issuer from dem.enum_ext_id_types where pk = %s", 751 'args': [pk_curr_type] 752 }]) 753 if len(rows) == 0: 754 return True 755 wx.CallAfter(self._PRW_issuer.SetText, rows[0][0]) 756 return True
757 #--------------------------------------------------------
758 - def __valid_for_save(self):
759 760 no_errors = True 761 762 # do not test .GetData() because adding external IDs 763 # will create types if necessary 764 # if self._PRW_type.GetData() is None: 765 if self._PRW_type.GetValue().strip() == u'': 766 self._PRW_type.SetBackgroundColour('pink') 767 self._PRW_type.SetFocus() 768 self._PRW_type.Refresh() 769 else: 770 self._PRW_type.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 771 self._PRW_type.Refresh() 772 773 if self._TCTRL_value.GetValue().strip() == u'': 774 self._TCTRL_value.SetBackgroundColour('pink') 775 self._TCTRL_value.SetFocus() 776 self._TCTRL_value.Refresh() 777 no_errors = False 778 else: 779 self._TCTRL_value.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 780 self._TCTRL_value.Refresh() 781 782 return no_errors
783 #------------------------------------------------------------ 784 from Gnumed.wxGladeWidgets import wxgIdentityEAPnl 785
786 -class cIdentityEAPnl(wxgIdentityEAPnl.wxgIdentityEAPnl, gmEditArea.cGenericEditAreaMixin):
787 """An edit area for editing/creating title/gender/dob/dod etc.""" 788
789 - def __init__(self, *args, **kwargs):
790 791 try: 792 data = kwargs['identity'] 793 del kwargs['identity'] 794 except KeyError: 795 data = None 796 797 wxgIdentityEAPnl.wxgIdentityEAPnl.__init__(self, *args, **kwargs) 798 gmEditArea.cGenericEditAreaMixin.__init__(self) 799 800 self.mode = 'new' 801 self.data = data 802 if data is not None: 803 self.mode = 'edit'
804 805 # self.__init_ui() 806 #---------------------------------------------------------------- 807 # def __init_ui(self): 808 # # adjust phrasewheels etc 809 #---------------------------------------------------------------- 810 # generic Edit Area mixin API 811 #----------------------------------------------------------------
812 - def _valid_for_save(self):
813 814 has_error = False 815 816 if self._PRW_gender.GetData() is None: 817 self._PRW_gender.SetFocus() 818 has_error = True 819 820 if not self._PRW_dob.is_valid_timestamp(): 821 val = self._PRW_dob.GetValue().strip() 822 gmDispatcher.send(signal = u'statustext', msg = _('Cannot parse <%s> into proper timestamp.') % val) 823 self._PRW_dob.SetBackgroundColour('pink') 824 self._PRW_dob.Refresh() 825 self._PRW_dob.SetFocus() 826 has_error = True 827 else: 828 self._PRW_dob.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 829 self._PRW_dob.Refresh() 830 831 if not self._PRW_dod.is_valid_timestamp(allow_empty = True): 832 gmDispatcher.send(signal = u'statustext', msg = _('Invalid date of death.')) 833 self._PRW_dod.SetFocus() 834 has_error = True 835 836 return (has_error is False)
837 #----------------------------------------------------------------
838 - def _save_as_new(self):
839 # not intended to be used 840 return False
841 #----------------------------------------------------------------
842 - def _save_as_update(self):
843 844 self.data['gender'] = self._PRW_gender.GetData() 845 846 if self._PRW_dob.GetValue().strip() == u'': 847 self.data['dob'] = None 848 else: 849 self.data['dob'] = self._PRW_dob.GetData().get_pydt() 850 851 self.data['title'] = gmTools.none_if(self._PRW_title.GetValue().strip(), u'') 852 self.data['deceased'] = self._PRW_dod.GetData() 853 self.data['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), u'') 854 855 self.data.save() 856 return True
857 #----------------------------------------------------------------
858 - def _refresh_as_new(self):
859 pass
860 #----------------------------------------------------------------
861 - def _refresh_from_existing(self):
862 863 self._LBL_info.SetLabel(u'ID: #%s' % ( 864 self.data.ID 865 # FIXME: add 'deleted' status 866 )) 867 self._PRW_dob.SetText ( 868 value = self.data.get_formatted_dob(format = '%Y-%m-%d %H:%M', encoding = gmI18N.get_encoding()), 869 data = self.data['dob'] 870 ) 871 if self.data['deceased'] is None: 872 val = u'' 873 else: 874 val = gmDateTime.pydt_strftime ( 875 self.data['deceased'], 876 format = '%Y-%m-%d %H:%M', 877 accuracy = gmDateTime.acc_minutes 878 ) 879 self._PRW_dod.SetText(value = val, data = self.data['deceased']) 880 self._PRW_gender.SetData(self.data['gender']) 881 #self._PRW_ethnicity.SetValue() 882 self._PRW_title.SetText(gmTools.coalesce(self.data['title'], u'')) 883 self._TCTRL_comment.SetValue(gmTools.coalesce(self.data['comment'], u''))
884 #----------------------------------------------------------------
886 pass
887 #------------------------------------------------------------ 888 from Gnumed.wxGladeWidgets import wxgNameGenderDOBEditAreaPnl 889
890 -class cNameGenderDOBEditAreaPnl(wxgNameGenderDOBEditAreaPnl.wxgNameGenderDOBEditAreaPnl):
891 """An edit area for editing/creating name/gender/dob. 892 893 Does NOT act on/listen to the current patient. 894 """
895 - def __init__(self, *args, **kwargs):
896 897 self.__name = kwargs['name'] 898 del kwargs['name'] 899 self.__identity = gmPerson.cIdentity(aPK_obj = self.__name['pk_identity']) 900 901 wxgNameGenderDOBEditAreaPnl.wxgNameGenderDOBEditAreaPnl.__init__(self, *args, **kwargs) 902 903 self.__register_interests() 904 self.refresh()
905 #-------------------------------------------------------- 906 # external API 907 #--------------------------------------------------------
908 - def refresh(self):
909 if self.__name is None: 910 return 911 912 self._PRW_title.SetText(gmTools.coalesce(self.__name['title'], u'')) 913 self._PRW_firstname.SetText(self.__name['firstnames']) 914 self._PRW_lastname.SetText(self.__name['lastnames']) 915 self._PRW_nick.SetText(gmTools.coalesce(self.__name['preferred'], u'')) 916 self._PRW_dob.SetText ( 917 value = self.__identity.get_formatted_dob(format = '%Y-%m-%d %H:%M', encoding = gmI18N.get_encoding()), 918 data = self.__identity['dob'] 919 ) 920 self._PRW_gender.SetData(self.__name['gender']) 921 self._CHBOX_active.SetValue(self.__name['active_name']) 922 if self.__identity['deceased'] is None: 923 val = u'' 924 else: 925 val = gmDateTime.pydt_strftime ( 926 self.__identity['deceased'], 927 format = '%Y-%m-%d %H:%M', 928 accuracy = gmDateTime.acc_minutes 929 ) 930 self._PRW_dod.SetText(value = val, data = self.__identity['deceased']) 931 self._TCTRL_comment.SetValue(gmTools.coalesce(self.__name['comment'], u''))
932 # FIXME: clear fields 933 # else: 934 # pass 935 #--------------------------------------------------------
936 - def save(self):
937 938 if not self.__valid_for_save(): 939 return False 940 941 self.__identity['gender'] = self._PRW_gender.GetData() 942 if self._PRW_dob.GetValue().strip() == u'': 943 self.__identity['dob'] = None 944 else: 945 self.__identity['dob'] = self._PRW_dob.GetData().get_pydt() 946 self.__identity['title'] = gmTools.none_if(self._PRW_title.GetValue().strip(), u'') 947 self.__identity['deceased'] = self._PRW_dod.GetData() 948 self.__identity.save_payload() 949 950 active = self._CHBOX_active.GetValue() 951 first = self._PRW_firstname.GetValue().strip() 952 last = self._PRW_lastname.GetValue().strip() 953 old_nick = self.__name['preferred'] 954 955 # is it a new name ? 956 old_name = self.__name['firstnames'] + self.__name['lastnames'] 957 if (first + last) != old_name: 958 self.__name = self.__identity.add_name(first, last, active) 959 960 self.__name['active_name'] = active 961 self.__name['preferred'] = gmTools.none_if(self._PRW_nick.GetValue().strip(), u'') 962 self.__name['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), u'') 963 964 self.__name.save_payload() 965 966 return True
967 #-------------------------------------------------------- 968 # event handling 969 #--------------------------------------------------------
970 - def __register_interests(self):
971 self._PRW_firstname.add_callback_on_lose_focus(self._on_name_set)
972 #--------------------------------------------------------
973 - def _on_name_set(self):
974 """Set the gender according to entered firstname. 975 976 Matches are fetched from existing records in backend. 977 """ 978 firstname = self._PRW_firstname.GetValue().strip() 979 if firstname == u'': 980 return True 981 rows, idx = gmPG2.run_ro_queries(queries = [{ 982 'cmd': u"select gender from dem.name_gender_map where name ilike %s", 983 'args': [firstname] 984 }]) 985 if len(rows) == 0: 986 return True 987 wx.CallAfter(self._PRW_gender.SetData, rows[0][0]) 988 return True
989 #-------------------------------------------------------- 990 # internal helpers 991 #--------------------------------------------------------
992 - def __valid_for_save(self):
993 994 has_error = False 995 996 if self._PRW_gender.GetData() is None: 997 self._PRW_gender.display_as_valid(False) 998 self._PRW_gender.SetFocus() 999 has_error = True 1000 else: 1001 self._PRW_gender.display_as_valid(True) 1002 1003 if not self._PRW_dob.is_valid_timestamp(): 1004 val = self._PRW_dob.GetValue().strip() 1005 gmDispatcher.send(signal = u'statustext', msg = _('Cannot parse <%s> into proper timestamp.') % val) 1006 self._PRW_dob.display_as_valid(False) 1007 self._PRW_dob.SetFocus() 1008 has_error = True 1009 else: 1010 self._PRW_dob.display_as_valid(True) 1011 1012 if not self._PRW_dod.is_valid_timestamp(): 1013 gmDispatcher.send(signal = u'statustext', msg = _('Invalid date of death.')) 1014 self._PRW_dod.display_as_valid(False) 1015 self._PRW_dod.SetFocus() 1016 has_error = True 1017 else: 1018 self._PRW_dod.display_as_valid(True) 1019 1020 if self._PRW_lastname.GetValue().strip() == u'': 1021 self._PRW_lastname.display_as_valid(False) 1022 self._PRW_lastname.SetFocus() 1023 has_error = True 1024 else: 1025 self._PRW_lastname.display_as_valid(True) 1026 1027 if self._PRW_firstname.GetValue().strip() == u'': 1028 self._PRW_firstname.display_as_valid(False) 1029 self._PRW_firstname.SetFocus() 1030 has_error = True 1031 else: 1032 self._PRW_firstname.display_as_valid(True) 1033 1034 return (has_error is False)
1035 #------------------------------------------------------------ 1036 # list manager 1037 #------------------------------------------------------------
1038 -class cPersonNamesManagerPnl(gmListWidgets.cGenericListManagerPnl):
1039 """A list for managing a person's names. 1040 1041 Does NOT act on/listen to the current patient. 1042 """
1043 - def __init__(self, *args, **kwargs):
1044 1045 try: 1046 self.__identity = kwargs['identity'] 1047 del kwargs['identity'] 1048 except KeyError: 1049 self.__identity = None 1050 1051 gmListWidgets.cGenericListManagerPnl.__init__(self, *args, **kwargs) 1052 1053 self.new_callback = self._add_name 1054 self.edit_callback = self._edit_name 1055 self.delete_callback = self._del_name 1056 self.refresh_callback = self.refresh 1057 1058 self.__init_ui() 1059 self.refresh()
1060 #-------------------------------------------------------- 1061 # external API 1062 #--------------------------------------------------------
1063 - def refresh(self, *args, **kwargs):
1064 if self.__identity is None: 1065 self._LCTRL_items.set_string_items() 1066 return 1067 1068 names = self.__identity.get_names() 1069 self._LCTRL_items.set_string_items ( 1070 items = [ [ 1071 gmTools.bool2str(n['active_name'], 'X', ''), 1072 n['lastnames'], 1073 n['firstnames'], 1074 gmTools.coalesce(n['preferred'], u''), 1075 gmTools.coalesce(n['comment'], u'') 1076 ] for n in names ] 1077 ) 1078 self._LCTRL_items.set_column_widths() 1079 self._LCTRL_items.set_data(data = names)
1080 #-------------------------------------------------------- 1081 # internal helpers 1082 #--------------------------------------------------------
1083 - def __init_ui(self):
1084 self._LCTRL_items.set_columns(columns = [ 1085 _('Active'), 1086 _('Lastname'), 1087 _('Firstname(s)'), 1088 _('Preferred Name'), 1089 _('Comment') 1090 ]) 1091 self._BTN_edit.SetLabel(_('Clone and &edit'))
1092 #--------------------------------------------------------
1093 - def _add_name(self):
1094 ea = cNameGenderDOBEditAreaPnl(self, -1, name = self.__identity.get_active_name()) 1095 dlg = gmEditArea.cGenericEditAreaDlg(self, -1, edit_area = ea) 1096 dlg.SetTitle(_('Adding new name')) 1097 if dlg.ShowModal() == wx.ID_OK: 1098 dlg.Destroy() 1099 return True 1100 dlg.Destroy() 1101 return False
1102 #--------------------------------------------------------
1103 - def _edit_name(self, name):
1104 ea = cNameGenderDOBEditAreaPnl(self, -1, name = name) 1105 dlg = gmEditArea.cGenericEditAreaDlg(self, -1, edit_area = ea) 1106 dlg.SetTitle(_('Cloning name')) 1107 if dlg.ShowModal() == wx.ID_OK: 1108 dlg.Destroy() 1109 return True 1110 dlg.Destroy() 1111 return False
1112 #--------------------------------------------------------
1113 - def _del_name(self, name):
1114 1115 if len(self.__identity.get_names()) == 1: 1116 gmDispatcher.send(signal = u'statustext', msg = _('Cannot delete the only name of a person.'), beep = True) 1117 return False 1118 1119 go_ahead = gmGuiHelpers.gm_show_question ( 1120 _( 'It is often advisable to keep old names around and\n' 1121 'just create a new "currently active" name.\n' 1122 '\n' 1123 'This allows finding the patient by both the old\n' 1124 'and the new name (think before/after marriage).\n' 1125 '\n' 1126 'Do you still want to really delete\n' 1127 "this name from the patient ?" 1128 ), 1129 _('Deleting name') 1130 ) 1131 if not go_ahead: 1132 return False 1133 1134 self.__identity.delete_name(name = name) 1135 return True
1136 #-------------------------------------------------------- 1137 # properties 1138 #--------------------------------------------------------
1139 - def _get_identity(self):
1140 return self.__identity
1141
1142 - def _set_identity(self, identity):
1143 self.__identity = identity 1144 self.refresh()
1145 1146 identity = property(_get_identity, _set_identity)
1147 #------------------------------------------------------------
1148 -class cPersonIDsManagerPnl(gmListWidgets.cGenericListManagerPnl):
1149 """A list for managing a person's external IDs. 1150 1151 Does NOT act on/listen to the current patient. 1152 """
1153 - def __init__(self, *args, **kwargs):
1154 1155 try: 1156 self.__identity = kwargs['identity'] 1157 del kwargs['identity'] 1158 except KeyError: 1159 self.__identity = None 1160 1161 gmListWidgets.cGenericListManagerPnl.__init__(self, *args, **kwargs) 1162 1163 self.new_callback = self._add_id 1164 self.edit_callback = self._edit_id 1165 self.delete_callback = self._del_id 1166 self.refresh_callback = self.refresh 1167 1168 self.__init_ui() 1169 self.refresh()
1170 #-------------------------------------------------------- 1171 # external API 1172 #--------------------------------------------------------
1173 - def refresh(self, *args, **kwargs):
1174 if self.__identity is None: 1175 self._LCTRL_items.set_string_items() 1176 return 1177 1178 ids = self.__identity.get_external_ids() 1179 self._LCTRL_items.set_string_items ( 1180 items = [ [ 1181 i['name'], 1182 i['value'], 1183 gmTools.coalesce(i['issuer'], u''), 1184 gmTools.coalesce(i['comment'], u'') 1185 ] for i in ids 1186 ] 1187 ) 1188 self._LCTRL_items.set_column_widths() 1189 self._LCTRL_items.set_data(data = ids)
1190 #-------------------------------------------------------- 1191 # internal helpers 1192 #--------------------------------------------------------
1193 - def __init_ui(self):
1194 self._LCTRL_items.set_columns(columns = [ 1195 _('ID type'), 1196 _('Value'), 1197 _('Issuer'), 1198 _('Comment') 1199 ])
1200 #--------------------------------------------------------
1201 - def _add_id(self):
1202 ea = cExternalIDEditAreaPnl(self, -1) 1203 ea.identity = self.__identity 1204 dlg = gmEditArea.cGenericEditAreaDlg(self, -1, edit_area = ea) 1205 dlg.SetTitle(_('Adding new external ID')) 1206 if dlg.ShowModal() == wx.ID_OK: 1207 dlg.Destroy() 1208 return True 1209 dlg.Destroy() 1210 return False
1211 #--------------------------------------------------------
1212 - def _edit_id(self, ext_id):
1213 ea = cExternalIDEditAreaPnl(self, -1, external_id = ext_id) 1214 ea.identity = self.__identity 1215 dlg = gmEditArea.cGenericEditAreaDlg(self, -1, edit_area = ea) 1216 dlg.SetTitle(_('Editing external ID')) 1217 if dlg.ShowModal() == wx.ID_OK: 1218 dlg.Destroy() 1219 return True 1220 dlg.Destroy() 1221 return False
1222 #--------------------------------------------------------
1223 - def _del_id(self, ext_id):
1224 go_ahead = gmGuiHelpers.gm_show_question ( 1225 _( 'Do you really want to delete this\n' 1226 'external ID from the patient ?'), 1227 _('Deleting external ID') 1228 ) 1229 if not go_ahead: 1230 return False 1231 self.__identity.delete_external_id(pk_ext_id = ext_id['pk_id']) 1232 return True
1233 #-------------------------------------------------------- 1234 # properties 1235 #--------------------------------------------------------
1236 - def _get_identity(self):
1237 return self.__identity
1238
1239 - def _set_identity(self, identity):
1240 self.__identity = identity 1241 self.refresh()
1242 1243 identity = property(_get_identity, _set_identity)
1244 #------------------------------------------------------------ 1245 # integrated panels 1246 #------------------------------------------------------------ 1247 from Gnumed.wxGladeWidgets import wxgPersonIdentityManagerPnl 1248
1249 -class cPersonIdentityManagerPnl(wxgPersonIdentityManagerPnl.wxgPersonIdentityManagerPnl):
1250 """A panel for editing identity data for a person. 1251 1252 - provides access to: 1253 - name 1254 - external IDs 1255 1256 Does NOT act on/listen to the current patient. 1257 """
1258 - def __init__(self, *args, **kwargs):
1259 1260 wxgPersonIdentityManagerPnl.wxgPersonIdentityManagerPnl.__init__(self, *args, **kwargs) 1261 1262 self.__identity = None 1263 self.refresh()
1264 #-------------------------------------------------------- 1265 # external API 1266 #--------------------------------------------------------
1267 - def refresh(self):
1268 self._PNL_names.identity = self.__identity 1269 self._PNL_ids.identity = self.__identity 1270 # this is an Edit Area: 1271 self._PNL_identity.mode = 'new' 1272 self._PNL_identity.data = self.__identity 1273 if self.__identity is not None: 1274 self._PNL_identity.mode = 'edit'
1275 #-------------------------------------------------------- 1276 # properties 1277 #--------------------------------------------------------
1278 - def _get_identity(self):
1279 return self.__identity
1280
1281 - def _set_identity(self, identity):
1282 self.__identity = identity 1283 self.refresh()
1284 1285 identity = property(_get_identity, _set_identity) 1286 #-------------------------------------------------------- 1287 # event handlers 1288 #--------------------------------------------------------
1290 if not self._PNL_identity.save(): 1291 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save identity. Incomplete information.'), beep = True)
1292 #--------------------------------------------------------
1293 - def _on_reload_identity_button_pressed(self, event):
1294 self._PNL_identity.refresh()
1295 1296 #============================================================ 1297 from Gnumed.wxGladeWidgets import wxgPersonSocialNetworkManagerPnl 1298
1299 -class cPersonSocialNetworkManagerPnl(wxgPersonSocialNetworkManagerPnl.wxgPersonSocialNetworkManagerPnl):
1300 - def __init__(self, *args, **kwargs):
1301 1302 wxgPersonSocialNetworkManagerPnl.wxgPersonSocialNetworkManagerPnl.__init__(self, *args, **kwargs) 1303 1304 self.__identity = None 1305 self._PRW_provider.selection_only = False 1306 self.refresh()
1307 #-------------------------------------------------------- 1308 # external API 1309 #--------------------------------------------------------
1310 - def refresh(self):
1311 1312 tt = _('Link another person in this database as the emergency contact:\n\nEnter person name part or identifier and hit <enter>.') 1313 1314 if self.__identity is None: 1315 self._TCTRL_er_contact.SetValue(u'') 1316 self._TCTRL_person.person = None 1317 self._TCTRL_person.SetToolTipString(tt) 1318 1319 self._PRW_provider.SetText(value = u'', data = None) 1320 return 1321 1322 self._TCTRL_er_contact.SetValue(gmTools.coalesce(self.__identity['emergency_contact'], u'')) 1323 if self.__identity['pk_emergency_contact'] is not None: 1324 ident = gmPerson.cIdentity(aPK_obj = self.__identity['pk_emergency_contact']) 1325 self._TCTRL_person.person = ident 1326 tt = u'%s\n\n%s\n\n%s' % ( 1327 tt, 1328 ident['description_gender'], 1329 u'\n'.join([ 1330 u'%s: %s%s' % ( 1331 c['l10n_comm_type'], 1332 c['url'], 1333 gmTools.bool2subst(c['is_confidential'], _(' (confidential !)'), u'', u'') 1334 ) 1335 for c in ident.get_comm_channels() 1336 ]) 1337 ) 1338 else: 1339 self._TCTRL_person.person = None 1340 1341 self._TCTRL_person.SetToolTipString(tt) 1342 1343 if self.__identity['pk_primary_provider'] is None: 1344 self._PRW_provider.SetText(value = u'', data = None) 1345 else: 1346 self._PRW_provider.SetData(data = self.__identity['pk_primary_provider'])
1347 #-------------------------------------------------------- 1348 # properties 1349 #--------------------------------------------------------
1350 - def _get_identity(self):
1351 return self.__identity
1352
1353 - def _set_identity(self, identity):
1354 self.__identity = identity 1355 self.refresh()
1356 1357 identity = property(_get_identity, _set_identity) 1358 #-------------------------------------------------------- 1359 # event handlers 1360 #--------------------------------------------------------
1361 - def _on_save_button_pressed(self, event):
1362 if self.__identity is not None: 1363 self.__identity['emergency_contact'] = self._TCTRL_er_contact.GetValue().strip() 1364 if self._TCTRL_person.person is not None: 1365 self.__identity['pk_emergency_contact'] = self._TCTRL_person.person.ID 1366 if self._PRW_provider.GetValue().strip == u'': 1367 self.__identity['pk_primary_provider'] = None 1368 else: 1369 self.__identity['pk_primary_provider'] = self._PRW_provider.GetData() 1370 1371 self.__identity.save() 1372 1373 event.Skip()
1374 #--------------------------------------------------------
1375 - def _on_remove_contact_button_pressed(self, event):
1376 event.Skip() 1377 1378 if self.__identity is None: 1379 return 1380 1381 self._TCTRL_person.person = None 1382 1383 self.__identity['pk_emergency_contact'] = None 1384 self.__identity.save()
1385 #--------------------------------------------------------
1386 - def _on_button_activate_contact_pressed(self, event):
1387 ident = self._TCTRL_person.person 1388 if ident is not None: 1389 from Gnumed.wxpython import gmPatSearchWidgets 1390 gmPatSearchWidgets.set_active_patient(patient = ident, forced_reload = False) 1391 1392 event.Skip()
1393 #============================================================ 1394 # new-patient widgets 1395 #============================================================
1396 -def create_new_person(parent=None, activate=False):
1397 1398 dbcfg = gmCfg.cCfgSQL() 1399 1400 def_region = dbcfg.get2 ( 1401 option = u'person.create.default_region', 1402 workplace = gmSurgery.gmCurrentPractice().active_workplace, 1403 bias = u'user' 1404 ) 1405 def_country = None 1406 1407 if def_region is None: 1408 def_country = dbcfg.get2 ( 1409 option = u'person.create.default_country', 1410 workplace = gmSurgery.gmCurrentPractice().active_workplace, 1411 bias = u'user' 1412 ) 1413 else: 1414 countries = gmDemographicRecord.get_country_for_region(region = def_region) 1415 if len(countries) == 1: 1416 def_country = countries[0]['l10n_country'] 1417 1418 if parent is None: 1419 parent = wx.GetApp().GetTopWindow() 1420 1421 ea = cNewPatientEAPnl(parent = parent, id = -1, country = def_country, region = def_region) 1422 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = True) 1423 dlg.SetTitle(_('Adding new person')) 1424 ea._PRW_lastname.SetFocus() 1425 result = dlg.ShowModal() 1426 pat = ea.data 1427 dlg.Destroy() 1428 1429 if result != wx.ID_OK: 1430 return False 1431 1432 _log.debug('created new person [%s]', pat.ID) 1433 1434 if activate: 1435 from Gnumed.wxpython import gmPatSearchWidgets 1436 gmPatSearchWidgets.set_active_patient(patient = pat) 1437 1438 gmDispatcher.send(signal = 'display_widget', name = 'gmNotebookedPatientEditionPlugin') 1439 1440 return True
1441 #============================================================ 1442 from Gnumed.wxGladeWidgets import wxgNewPatientEAPnl 1443
1444 -class cNewPatientEAPnl(wxgNewPatientEAPnl.wxgNewPatientEAPnl, gmEditArea.cGenericEditAreaMixin):
1445
1446 - def __init__(self, *args, **kwargs):
1447 1448 try: 1449 self.default_region = kwargs['region'] 1450 del kwargs['region'] 1451 except KeyError: 1452 self.default_region = None 1453 1454 try: 1455 self.default_country = kwargs['country'] 1456 del kwargs['country'] 1457 except KeyError: 1458 self.default_country = None 1459 1460 wxgNewPatientEAPnl.wxgNewPatientEAPnl.__init__(self, *args, **kwargs) 1461 gmEditArea.cGenericEditAreaMixin.__init__(self) 1462 1463 self.mode = 'new' 1464 self.data = None 1465 self._address = None 1466 1467 self.__init_ui() 1468 self.__register_interests()
1469 #---------------------------------------------------------------- 1470 # internal helpers 1471 #----------------------------------------------------------------
1472 - def __init_ui(self):
1473 self._PRW_lastname.final_regex = '.+' 1474 self._PRW_firstnames.final_regex = '.+' 1475 self._PRW_address_searcher.selection_only = False 1476 1477 # only if we would support None on selection_only's: 1478 # self._PRW_external_id_type.selection_only = True 1479 1480 if self.default_country is not None: 1481 self._PRW_country.SetText(value = self.default_country) 1482 1483 if self.default_region is not None: 1484 self._PRW_region.SetText(value = self.default_region)
1485 #----------------------------------------------------------------
1486 - def __perhaps_invalidate_address_searcher(self, ctrl=None, field=None):
1487 1488 adr = self._PRW_address_searcher.get_address() 1489 if adr is None: 1490 return True 1491 1492 if ctrl.GetValue().strip() != adr[field]: 1493 wx.CallAfter(self._PRW_address_searcher.SetText, value = u'', data = None) 1494 return True 1495 1496 return False
1497 #----------------------------------------------------------------
1499 adr = self._PRW_address_searcher.get_address() 1500 if adr is None: 1501 return True 1502 1503 self._PRW_zip.SetText(value = adr['postcode'], data = adr['postcode']) 1504 1505 self._PRW_street.SetText(value = adr['street'], data = adr['street']) 1506 self._PRW_street.set_context(context = u'zip', val = adr['postcode']) 1507 1508 self._TCTRL_number.SetValue(adr['number']) 1509 1510 self._PRW_urb.SetText(value = adr['urb'], data = adr['urb']) 1511 self._PRW_urb.set_context(context = u'zip', val = adr['postcode']) 1512 1513 self._PRW_region.SetText(value = adr['l10n_state'], data = adr['code_state']) 1514 self._PRW_region.set_context(context = u'zip', val = adr['postcode']) 1515 1516 self._PRW_country.SetText(value = adr['l10n_country'], data = adr['code_country']) 1517 self._PRW_country.set_context(context = u'zip', val = adr['postcode'])
1518 #----------------------------------------------------------------
1519 - def __identity_valid_for_save(self):
1520 error = False 1521 1522 # name fields 1523 if self._PRW_lastname.GetValue().strip() == u'': 1524 error = True 1525 gmDispatcher.send(signal = 'statustext', msg = _('Must enter lastname.')) 1526 self._PRW_lastname.display_as_valid(False) 1527 else: 1528 self._PRW_lastname.display_as_valid(True) 1529 1530 if self._PRW_firstnames.GetValue().strip() == '': 1531 error = True 1532 gmDispatcher.send(signal = 'statustext', msg = _('Must enter first name.')) 1533 self._PRW_firstnames.display_as_valid(False) 1534 else: 1535 self._PRW_firstnames.display_as_valid(True) 1536 1537 # gender 1538 if self._PRW_gender.GetData() is None: 1539 error = True 1540 gmDispatcher.send(signal = 'statustext', msg = _('Must select gender.')) 1541 self._PRW_gender.display_as_valid(False) 1542 else: 1543 self._PRW_gender.display_as_valid(True) 1544 1545 # dob validation 1546 # test this last so we can check empty field as last barrier against save 1547 # 1) valid timestamp ? 1548 if self._PRW_dob.is_valid_timestamp(allow_empty = False): # properly colors the field 1549 dob = self._PRW_dob.date 1550 # but year also usable ? 1551 msg = None 1552 if dob.year < 1900: 1553 msg = _( 1554 'DOB: %s\n' 1555 '\n' 1556 'While this is a valid point in time Python does\n' 1557 'not know how to deal with it.\n' 1558 '\n' 1559 'We suggest using January 1st 1901 instead and adding\n' 1560 'the true date of birth to the patient comment.\n' 1561 '\n' 1562 'Sorry for the inconvenience %s' 1563 ) % (dob, gmTools.u_frowning_face) 1564 elif dob > gmDateTime.pydt_now_here(): 1565 msg = _( 1566 'DOB: %s\n' 1567 '\n' 1568 'Date of birth in the future !' 1569 ) % dob 1570 1571 if msg is not None: 1572 error = True 1573 gmGuiHelpers.gm_show_error ( 1574 msg, 1575 _('Registering new person') 1576 ) 1577 self._PRW_dob.display_as_valid(False) 1578 self._PRW_dob.SetFocus() 1579 # 2) invalid timestamp ? 1580 else: 1581 # is this the only error ? 1582 if error is False: 1583 # is it empty rather than invalid ? 1584 if self._PRW_dob.GetValue().strip() == u'': 1585 # maybe even allow empty DOB ? 1586 allow_empty_dob = gmGuiHelpers.gm_show_question ( 1587 _( 1588 'Are you sure you want to register this person\n' 1589 'without a valid date of birth ?\n' 1590 '\n' 1591 'This can be useful for temporary staff members\n' 1592 'but will provoke nag screens if this person\n' 1593 'becomes a patient.\n' 1594 ), 1595 _('Registering new person') 1596 ) 1597 if allow_empty_dob: 1598 self._PRW_dob.display_as_valid(True) 1599 else: 1600 error = True 1601 self._PRW_dob.SetFocus() 1602 1603 # TOB validation if non-empty 1604 # if self._TCTRL_tob.GetValue().strip() != u'': 1605 1606 return (not error)
1607 #----------------------------------------------------------------
1608 - def __address_valid_for_save(self, empty_address_is_valid=False):
1609 1610 # existing address ? if so set other fields 1611 if self._PRW_address_searcher.GetData() is not None: 1612 wx.CallAfter(self.__set_fields_from_address_searcher) 1613 return True 1614 1615 # must either all contain something or none of them 1616 fields_to_fill = ( 1617 self._TCTRL_number, 1618 self._PRW_zip, 1619 self._PRW_street, 1620 self._PRW_urb, 1621 self._PRW_region, 1622 self._PRW_country 1623 ) 1624 no_of_filled_fields = 0 1625 1626 for field in fields_to_fill: 1627 if field.GetValue().strip() != u'': 1628 no_of_filled_fields += 1 1629 field.SetBackgroundColour(gmPhraseWheel.color_prw_valid) 1630 field.Refresh() 1631 1632 # empty address ? 1633 if no_of_filled_fields == 0: 1634 if empty_address_is_valid: 1635 return True 1636 else: 1637 return None 1638 1639 # incompletely filled address ? 1640 if no_of_filled_fields != len(fields_to_fill): 1641 for field in fields_to_fill: 1642 if field.GetValue().strip() == u'': 1643 field.SetBackgroundColour(gmPhraseWheel.color_prw_invalid) 1644 field.SetFocus() 1645 field.Refresh() 1646 msg = _('To properly create an address, all the related fields must be filled in.') 1647 gmGuiHelpers.gm_show_error(msg, _('Required fields')) 1648 return False 1649 1650 # fields which must contain a selected item 1651 # FIXME: they must also contain an *acceptable combination* which 1652 # FIXME: can only be tested against the database itself ... 1653 strict_fields = ( 1654 self._PRW_region, 1655 self._PRW_country 1656 ) 1657 error = False 1658 for field in strict_fields: 1659 if field.GetData() is None: 1660 error = True 1661 field.SetBackgroundColour(gmPhraseWheel.color_prw_invalid) 1662 field.SetFocus() 1663 else: 1664 field.SetBackgroundColour(gmPhraseWheel.color_prw_valid) 1665 field.Refresh() 1666 1667 if error: 1668 msg = _('This field must contain an item selected from the dropdown list.') 1669 gmGuiHelpers.gm_show_error(msg, _('Required fields')) 1670 return False 1671 1672 return True
1673 #----------------------------------------------------------------
1674 - def __register_interests(self):
1675 1676 # identity 1677 self._PRW_firstnames.add_callback_on_lose_focus(self._on_leaving_firstname) 1678 1679 # address 1680 self._PRW_address_searcher.add_callback_on_lose_focus(self._on_leaving_adress_searcher) 1681 1682 # invalidate address searcher when any field edited 1683 self._PRW_street.add_callback_on_lose_focus(self._invalidate_address_searcher) 1684 wx.EVT_KILL_FOCUS(self._TCTRL_number, self._invalidate_address_searcher) 1685 self._PRW_urb.add_callback_on_lose_focus(self._invalidate_address_searcher) 1686 self._PRW_region.add_callback_on_lose_focus(self._invalidate_address_searcher) 1687 1688 self._PRW_zip.add_callback_on_lose_focus(self._on_leaving_zip) 1689 self._PRW_country.add_callback_on_lose_focus(self._on_leaving_country)
1690 #---------------------------------------------------------------- 1691 # event handlers 1692 #----------------------------------------------------------------
1693 - def _on_leaving_firstname(self):
1694 """Set the gender according to entered firstname. 1695 1696 Matches are fetched from existing records in backend. 1697 """ 1698 # only set if not already set so as to not 1699 # overwrite a change by the user 1700 if self._PRW_gender.GetData() is not None: 1701 return True 1702 1703 firstname = self._PRW_firstnames.GetValue().strip() 1704 if firstname == u'': 1705 return True 1706 1707 gender = gmPerson.map_firstnames2gender(firstnames = firstname) 1708 if gender is None: 1709 return True 1710 1711 wx.CallAfter(self._PRW_gender.SetData, gender) 1712 return True
1713 #----------------------------------------------------------------
1714 - def _on_leaving_zip(self):
1715 self.__perhaps_invalidate_address_searcher(self._PRW_zip, 'postcode') 1716 1717 zip_code = gmTools.none_if(self._PRW_zip.GetValue().strip(), u'') 1718 self._PRW_street.set_context(context = u'zip', val = zip_code) 1719 self._PRW_urb.set_context(context = u'zip', val = zip_code) 1720 self._PRW_region.set_context(context = u'zip', val = zip_code) 1721 self._PRW_country.set_context(context = u'zip', val = zip_code) 1722 1723 return True
1724 #----------------------------------------------------------------
1725 - def _on_leaving_country(self):
1726 self.__perhaps_invalidate_address_searcher(self._PRW_country, 'l10n_country') 1727 1728 country = gmTools.none_if(self._PRW_country.GetValue().strip(), u'') 1729 self._PRW_region.set_context(context = u'country', val = country) 1730 1731 return True
1732 #----------------------------------------------------------------
1733 - def _invalidate_address_searcher(self, *args, **kwargs):
1734 mapping = [ 1735 (self._PRW_street, 'street'), 1736 (self._TCTRL_number, 'number'), 1737 (self._PRW_urb, 'urb'), 1738 (self._PRW_region, 'l10n_state') 1739 ] 1740 1741 # loop through fields and invalidate address searcher if different 1742 for ctrl, field in mapping: 1743 if self.__perhaps_invalidate_address_searcher(ctrl, field): 1744 return True 1745 1746 return True
1747 #----------------------------------------------------------------
1749 adr = self._PRW_address_searcher.get_address() 1750 if adr is None: 1751 return True 1752 1753 wx.CallAfter(self.__set_fields_from_address_searcher) 1754 return True
1755 #---------------------------------------------------------------- 1756 # generic Edit Area mixin API 1757 #----------------------------------------------------------------
1758 - def _valid_for_save(self):
1759 return (self.__identity_valid_for_save() and self.__address_valid_for_save(empty_address_is_valid = True))
1760 #----------------------------------------------------------------
1761 - def _save_as_new(self):
1762 1763 # identity 1764 new_identity = gmPerson.create_identity ( 1765 gender = self._PRW_gender.GetData(), 1766 dob = self._PRW_dob.GetData(), 1767 lastnames = self._PRW_lastname.GetValue().strip(), 1768 firstnames = self._PRW_firstnames.GetValue().strip() 1769 ) 1770 _log.debug('identity created: %s' % new_identity) 1771 1772 new_identity['title'] = gmTools.none_if(self._PRW_title.GetValue().strip()) 1773 new_identity.set_nickname(nickname = gmTools.none_if(self._PRW_nickname.GetValue().strip(), u'')) 1774 #TOB 1775 new_identity.save() 1776 1777 name = new_identity.get_active_name() 1778 name['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), u'') 1779 name.save() 1780 1781 # address 1782 is_valid = self.__address_valid_for_save(empty_address_is_valid = False) 1783 if is_valid is True: 1784 # because we currently only check for non-emptiness 1785 # we must still deal with database errors 1786 try: 1787 new_identity.link_address ( 1788 number = self._TCTRL_number.GetValue().strip(), 1789 street = self._PRW_street.GetValue().strip(), 1790 postcode = self._PRW_zip.GetValue().strip(), 1791 urb = self._PRW_urb.GetValue().strip(), 1792 state = self._PRW_region.GetData(), 1793 country = self._PRW_country.GetData() 1794 ) 1795 except gmPG2.dbapi.InternalError: 1796 _log.debug('number: >>%s<<', self._TCTRL_number.GetValue().strip()) 1797 _log.debug('street: >>%s<<', self._PRW_street.GetValue().strip()) 1798 _log.debug('postcode: >>%s<<', self._PRW_zip.GetValue().strip()) 1799 _log.debug('urb: >>%s<<', self._PRW_urb.GetValue().strip()) 1800 _log.debug('state: >>%s<<', self._PRW_region.GetData().strip()) 1801 _log.debug('country: >>%s<<', self._PRW_country.GetData().strip()) 1802 _log.exception('cannot link address') 1803 gmGuiHelpers.gm_show_error ( 1804 aTitle = _('Saving address'), 1805 aMessage = _( 1806 'Cannot save this address.\n' 1807 '\n' 1808 'You will have to add it via the Demographics plugin.\n' 1809 ) 1810 ) 1811 elif is_valid is False: 1812 gmGuiHelpers.gm_show_error ( 1813 aTitle = _('Saving address'), 1814 aMessage = _( 1815 'Address not saved.\n' 1816 '\n' 1817 'You will have to add it via the Demographics plugin.\n' 1818 ) 1819 ) 1820 # else it is None which means empty address which we ignore 1821 1822 # phone 1823 channel_name = self._PRW_channel_type.GetValue().strip() 1824 pk_channel_type = self._PRW_channel_type.GetData() 1825 if pk_channel_type is None: 1826 if channel_name == u'': 1827 channel_name = u'homephone' 1828 new_identity.link_comm_channel ( 1829 comm_medium = channel_name, 1830 pk_channel_type = pk_channel_type, 1831 url = gmTools.none_if(self._TCTRL_phone.GetValue().strip(), u''), 1832 is_confidential = False 1833 ) 1834 1835 # external ID 1836 pk_type = self._PRW_external_id_type.GetData() 1837 id_value = self._TCTRL_external_id_value.GetValue().strip() 1838 if (pk_type is not None) and (id_value != u''): 1839 new_identity.add_external_id(value = id_value, pk_type = pk_type) 1840 1841 # occupation 1842 new_identity.link_occupation ( 1843 occupation = gmTools.none_if(self._PRW_occupation.GetValue().strip(), u'') 1844 ) 1845 1846 self.data = new_identity 1847 return True
1848 #----------------------------------------------------------------
1849 - def _save_as_update(self):
1850 raise NotImplementedError('[%s]: not expected to be used' % self.__class__.__name__)
1851 #----------------------------------------------------------------
1852 - def _refresh_as_new(self):
1853 # FIXME: button "empty out" 1854 return
1855 #----------------------------------------------------------------
1856 - def _refresh_from_existing(self):
1857 return # there is no forward button so nothing to do here
1858 #----------------------------------------------------------------
1860 raise NotImplementedError('[%s]: not expected to be used' % self.__class__.__name__)
1861 1862 #============================================================ 1863 # patient demographics editing classes 1864 #============================================================
1865 -class cPersonDemographicsEditorNb(wx.Notebook):
1866 """Notebook displaying demographics editing pages: 1867 1868 - Identity 1869 - Contacts (addresses, phone numbers, etc) 1870 - Social network (significant others, GP, etc) 1871 1872 Does NOT act on/listen to the current patient. 1873 """ 1874 #--------------------------------------------------------
1875 - def __init__(self, parent, id):
1876 1877 wx.Notebook.__init__ ( 1878 self, 1879 parent = parent, 1880 id = id, 1881 style = wx.NB_TOP | wx.NB_MULTILINE | wx.NO_BORDER, 1882 name = self.__class__.__name__ 1883 ) 1884 1885 self.__identity = None 1886 self.__do_layout() 1887 self.SetSelection(0)
1888 #-------------------------------------------------------- 1889 # public API 1890 #--------------------------------------------------------
1891 - def refresh(self):
1892 """Populate fields in pages with data from model.""" 1893 for page_idx in range(self.GetPageCount()): 1894 page = self.GetPage(page_idx) 1895 page.identity = self.__identity 1896 1897 return True
1898 #-------------------------------------------------------- 1899 # internal API 1900 #--------------------------------------------------------
1901 - def __do_layout(self):
1902 """Build patient edition notebook pages.""" 1903 1904 # contacts page 1905 new_page = gmPersonContactWidgets.cPersonContactsManagerPnl(self, -1) 1906 new_page.identity = self.__identity 1907 self.AddPage ( 1908 page = new_page, 1909 text = _('Contacts'), 1910 select = True 1911 ) 1912 1913 # identity page 1914 new_page = cPersonIdentityManagerPnl(self, -1) 1915 new_page.identity = self.__identity 1916 self.AddPage ( 1917 page = new_page, 1918 text = _('Identity'), 1919 select = False 1920 ) 1921 1922 # social network page 1923 new_page = cPersonSocialNetworkManagerPnl(self, -1) 1924 new_page.identity = self.__identity 1925 self.AddPage ( 1926 page = new_page, 1927 text = _('Social network'), 1928 select = False 1929 )
1930 #-------------------------------------------------------- 1931 # properties 1932 #--------------------------------------------------------
1933 - def _get_identity(self):
1934 return self.__identity
1935
1936 - def _set_identity(self, identity):
1937 self.__identity = identity
1938 1939 identity = property(_get_identity, _set_identity)
1940 #============================================================ 1941 # old occupation widgets 1942 #============================================================ 1943 # FIXME: support multiple occupations 1944 # FIXME: redo with wxGlade 1945
1946 -class cPatOccupationsPanel(wx.Panel):
1947 """Page containing patient occupations edition fields. 1948 """
1949 - def __init__(self, parent, id, ident=None):
1950 """ 1951 Creates a new instance of BasicPatDetailsPage 1952 @param parent - The parent widget 1953 @type parent - A wx.Window instance 1954 @param id - The widget id 1955 @type id - An integer 1956 """ 1957 wx.Panel.__init__(self, parent, id) 1958 self.__ident = ident 1959 self.__do_layout()
1960 #--------------------------------------------------------
1961 - def __do_layout(self):
1962 PNL_form = wx.Panel(self, -1) 1963 # occupation 1964 STT_occupation = wx.StaticText(PNL_form, -1, _('Occupation')) 1965 self.PRW_occupation = cOccupationPhraseWheel(parent = PNL_form, id = -1) 1966 self.PRW_occupation.SetToolTipString(_("primary occupation of the patient")) 1967 # known since 1968 STT_occupation_updated = wx.StaticText(PNL_form, -1, _('Last updated')) 1969 self.TTC_occupation_updated = wx.TextCtrl(PNL_form, -1, style = wx.TE_READONLY) 1970 1971 # layout input widgets 1972 SZR_input = wx.FlexGridSizer(cols = 2, rows = 5, vgap = 4, hgap = 4) 1973 SZR_input.AddGrowableCol(1) 1974 SZR_input.Add(STT_occupation, 0, wx.SHAPED) 1975 SZR_input.Add(self.PRW_occupation, 1, wx.EXPAND) 1976 SZR_input.Add(STT_occupation_updated, 0, wx.SHAPED) 1977 SZR_input.Add(self.TTC_occupation_updated, 1, wx.EXPAND) 1978 PNL_form.SetSizerAndFit(SZR_input) 1979 1980 # layout page 1981 SZR_main = wx.BoxSizer(wx.VERTICAL) 1982 SZR_main.Add(PNL_form, 1, wx.EXPAND) 1983 self.SetSizer(SZR_main)
1984 #--------------------------------------------------------
1985 - def set_identity(self, identity):
1986 return self.refresh(identity=identity)
1987 #--------------------------------------------------------
1988 - def refresh(self, identity=None):
1989 if identity is not None: 1990 self.__ident = identity 1991 jobs = self.__ident.get_occupations() 1992 if len(jobs) > 0: 1993 self.PRW_occupation.SetText(jobs[0]['l10n_occupation']) 1994 self.TTC_occupation_updated.SetValue(jobs[0]['modified_when'].strftime('%m/%Y')) 1995 return True
1996 #--------------------------------------------------------
1997 - def save(self):
1998 if self.PRW_occupation.IsModified(): 1999 new_job = self.PRW_occupation.GetValue().strip() 2000 jobs = self.__ident.get_occupations() 2001 for job in jobs: 2002 if job['l10n_occupation'] == new_job: 2003 continue 2004 self.__ident.unlink_occupation(occupation = job['l10n_occupation']) 2005 self.__ident.link_occupation(occupation = new_job) 2006 return True
2007 #============================================================
2008 -class cNotebookedPatEditionPanel(wx.Panel, gmRegetMixin.cRegetOnPaintMixin):
2009 """Patient demographics plugin for main notebook. 2010 2011 Hosts another notebook with pages for Identity, Contacts, etc. 2012 2013 Acts on/listens to the currently active patient. 2014 """ 2015 #--------------------------------------------------------
2016 - def __init__(self, parent, id):
2017 wx.Panel.__init__ (self, parent = parent, id = id, style = wx.NO_BORDER) 2018 gmRegetMixin.cRegetOnPaintMixin.__init__(self) 2019 self.__do_layout() 2020 self.__register_interests()
2021 #-------------------------------------------------------- 2022 # public API 2023 #-------------------------------------------------------- 2024 #-------------------------------------------------------- 2025 # internal helpers 2026 #--------------------------------------------------------
2027 - def __do_layout(self):
2028 """Arrange widgets.""" 2029 self.__patient_notebook = cPersonDemographicsEditorNb(self, -1) 2030 2031 szr_main = wx.BoxSizer(wx.VERTICAL) 2032 szr_main.Add(self.__patient_notebook, 1, wx.EXPAND) 2033 self.SetSizerAndFit(szr_main)
2034 #-------------------------------------------------------- 2035 # event handling 2036 #--------------------------------------------------------
2037 - def __register_interests(self):
2038 gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._on_pre_patient_selection) 2039 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._on_post_patient_selection)
2040 #--------------------------------------------------------
2041 - def _on_pre_patient_selection(self):
2042 self._schedule_data_reget()
2043 #--------------------------------------------------------
2044 - def _on_post_patient_selection(self):
2045 self._schedule_data_reget()
2046 #-------------------------------------------------------- 2047 # reget mixin API 2048 #--------------------------------------------------------
2049 - def _populate_with_data(self):
2050 """Populate fields in pages with data from model.""" 2051 pat = gmPerson.gmCurrentPatient() 2052 if pat.connected: 2053 self.__patient_notebook.identity = pat 2054 else: 2055 self.__patient_notebook.identity = None 2056 self.__patient_notebook.refresh() 2057 return True
2058 2059 2060 #============================================================ 2061 #============================================================ 2062 #============================================================ 2063 #============================================================ 2064 # outdated, delete soon: 2065 # new-patient wizard classes 2066 #============================================================
2067 -class cBasicPatDetailsPage(wx.wizard.WizardPageSimple):
2068 """ 2069 Wizard page for entering patient's basic demographic information 2070 """ 2071 2072 form_fields = ( 2073 'firstnames', 'lastnames', 'nick', 'dob', 'gender', 'title', 'occupation', 2074 'address_number', 'zip_code', 'street', 'town', 'state', 'country', 'phone', 'comment' 2075 ) 2076
2077 - def __init__(self, parent, title):
2078 """ 2079 Creates a new instance of BasicPatDetailsPage 2080 @param parent - The parent widget 2081 @type parent - A wx.Window instance 2082 @param tile - The title of the page 2083 @type title - A StringType instance 2084 """ 2085 wx.wizard.WizardPageSimple.__init__(self, parent) #, bitmap = gmGuiHelpers.gm_icon(_('oneperson')) 2086 self.__title = title 2087 self.__do_layout() 2088 self.__register_interests()
2089 #--------------------------------------------------------
2090 - def __do_layout(self):
2091 PNL_form = wx.Panel(self, -1) 2092 2093 # last name 2094 STT_lastname = wx.StaticText(PNL_form, -1, _('Last name')) 2095 STT_lastname.SetForegroundColour('red') 2096 self.PRW_lastname = cLastnamePhraseWheel(parent = PNL_form, id = -1) 2097 self.PRW_lastname.SetToolTipString(_('Required: lastname (family name)')) 2098 2099 # first name 2100 STT_firstname = wx.StaticText(PNL_form, -1, _('First name(s)')) 2101 STT_firstname.SetForegroundColour('red') 2102 self.PRW_firstname = cFirstnamePhraseWheel(parent = PNL_form, id = -1) 2103 self.PRW_firstname.SetToolTipString(_('Required: surname/given name/first name')) 2104 2105 # nickname 2106 STT_nick = wx.StaticText(PNL_form, -1, _('Nick name')) 2107 self.PRW_nick = cNicknamePhraseWheel(parent = PNL_form, id = -1) 2108 2109 # DOB 2110 STT_dob = wx.StaticText(PNL_form, -1, _('Date of birth')) 2111 STT_dob.SetForegroundColour('red') 2112 self.PRW_dob = gmDateTimeInput.cFuzzyTimestampInput(parent = PNL_form, id = -1) 2113 self.PRW_dob.SetToolTipString(_("Required: date of birth, if unknown or aliasing wanted then invent one")) 2114 2115 # gender 2116 STT_gender = wx.StaticText(PNL_form, -1, _('Gender')) 2117 STT_gender.SetForegroundColour('red') 2118 self.PRW_gender = cGenderSelectionPhraseWheel(parent = PNL_form, id=-1) 2119 self.PRW_gender.SetToolTipString(_("Required: gender of patient")) 2120 2121 # title 2122 STT_title = wx.StaticText(PNL_form, -1, _('Title')) 2123 self.PRW_title = cTitlePhraseWheel(parent = PNL_form, id = -1) 2124 2125 # zip code 2126 STT_zip_code = wx.StaticText(PNL_form, -1, _('Postal code')) 2127 STT_zip_code.SetForegroundColour('orange') 2128 self.PRW_zip_code = gmPersonContactWidgets.cZipcodePhraseWheel(parent = PNL_form, id = -1) 2129 self.PRW_zip_code.SetToolTipString(_("primary/home address: zip/postal code")) 2130 2131 # street 2132 STT_street = wx.StaticText(PNL_form, -1, _('Street')) 2133 STT_street.SetForegroundColour('orange') 2134 self.PRW_street = gmPersonContactWidgets.cStreetPhraseWheel(parent = PNL_form, id = -1) 2135 self.PRW_street.SetToolTipString(_("primary/home address: name of street")) 2136 2137 # address number 2138 STT_address_number = wx.StaticText(PNL_form, -1, _('Number')) 2139 STT_address_number.SetForegroundColour('orange') 2140 self.TTC_address_number = wx.TextCtrl(PNL_form, -1) 2141 self.TTC_address_number.SetToolTipString(_("primary/home address: address number")) 2142 2143 # town 2144 STT_town = wx.StaticText(PNL_form, -1, _('Place')) 2145 STT_town.SetForegroundColour('orange') 2146 self.PRW_town = gmPersonContactWidgets.cUrbPhraseWheel(parent = PNL_form, id = -1) 2147 self.PRW_town.SetToolTipString(_("primary/home address: city/town/village/dwelling/...")) 2148 2149 # state 2150 STT_state = wx.StaticText(PNL_form, -1, _('Region')) 2151 STT_state.SetForegroundColour('orange') 2152 self.PRW_state = gmPersonContactWidgets.cStateSelectionPhraseWheel(parent=PNL_form, id=-1) 2153 self.PRW_state.SetToolTipString(_("primary/home address: state/province/county/...")) 2154 2155 # country 2156 STT_country = wx.StaticText(PNL_form, -1, _('Country')) 2157 STT_country.SetForegroundColour('orange') 2158 self.PRW_country = gmPersonContactWidgets.cCountryPhraseWheel(parent = PNL_form, id = -1) 2159 self.PRW_country.SetToolTipString(_("primary/home address: country")) 2160 2161 # phone 2162 STT_phone = wx.StaticText(PNL_form, -1, _('Phone')) 2163 self.TTC_phone = wx.TextCtrl(PNL_form, -1) 2164 self.TTC_phone.SetToolTipString(_("phone number at home")) 2165 2166 # occupation 2167 STT_occupation = wx.StaticText(PNL_form, -1, _('Occupation')) 2168 self.PRW_occupation = cOccupationPhraseWheel(parent = PNL_form, id = -1) 2169 2170 # comment 2171 STT_comment = wx.StaticText(PNL_form, -1, _('Comment')) 2172 self.TCTRL_comment = wx.TextCtrl(PNL_form, -1) 2173 self.TCTRL_comment.SetToolTipString(_('A comment on this patient.')) 2174 2175 # form main validator 2176 self.form_DTD = cFormDTD(fields = self.__class__.form_fields) 2177 PNL_form.SetValidator(cBasicPatDetailsPageValidator(dtd = self.form_DTD)) 2178 2179 # layout input widgets 2180 SZR_input = wx.FlexGridSizer(cols = 2, rows = 16, vgap = 4, hgap = 4) 2181 SZR_input.AddGrowableCol(1) 2182 SZR_input.Add(STT_lastname, 0, wx.SHAPED) 2183 SZR_input.Add(self.PRW_lastname, 1, wx.EXPAND) 2184 SZR_input.Add(STT_firstname, 0, wx.SHAPED) 2185 SZR_input.Add(self.PRW_firstname, 1, wx.EXPAND) 2186 SZR_input.Add(STT_nick, 0, wx.SHAPED) 2187 SZR_input.Add(self.PRW_nick, 1, wx.EXPAND) 2188 SZR_input.Add(STT_dob, 0, wx.SHAPED) 2189 SZR_input.Add(self.PRW_dob, 1, wx.EXPAND) 2190 SZR_input.Add(STT_gender, 0, wx.SHAPED) 2191 SZR_input.Add(self.PRW_gender, 1, wx.EXPAND) 2192 SZR_input.Add(STT_title, 0, wx.SHAPED) 2193 SZR_input.Add(self.PRW_title, 1, wx.EXPAND) 2194 SZR_input.Add(STT_zip_code, 0, wx.SHAPED) 2195 SZR_input.Add(self.PRW_zip_code, 1, wx.EXPAND) 2196 SZR_input.Add(STT_street, 0, wx.SHAPED) 2197 SZR_input.Add(self.PRW_street, 1, wx.EXPAND) 2198 SZR_input.Add(STT_address_number, 0, wx.SHAPED) 2199 SZR_input.Add(self.TTC_address_number, 1, wx.EXPAND) 2200 SZR_input.Add(STT_town, 0, wx.SHAPED) 2201 SZR_input.Add(self.PRW_town, 1, wx.EXPAND) 2202 SZR_input.Add(STT_state, 0, wx.SHAPED) 2203 SZR_input.Add(self.PRW_state, 1, wx.EXPAND) 2204 SZR_input.Add(STT_country, 0, wx.SHAPED) 2205 SZR_input.Add(self.PRW_country, 1, wx.EXPAND) 2206 SZR_input.Add(STT_phone, 0, wx.SHAPED) 2207 SZR_input.Add(self.TTC_phone, 1, wx.EXPAND) 2208 SZR_input.Add(STT_occupation, 0, wx.SHAPED) 2209 SZR_input.Add(self.PRW_occupation, 1, wx.EXPAND) 2210 SZR_input.Add(STT_comment, 0, wx.SHAPED) 2211 SZR_input.Add(self.TCTRL_comment, 1, wx.EXPAND) 2212 2213 PNL_form.SetSizerAndFit(SZR_input) 2214 2215 # layout page 2216 SZR_main = makePageTitle(self, self.__title) 2217 SZR_main.Add(PNL_form, 1, wx.EXPAND)
2218 #-------------------------------------------------------- 2219 # event handling 2220 #--------------------------------------------------------
2221 - def __register_interests(self):
2222 self.PRW_firstname.add_callback_on_lose_focus(self.on_name_set) 2223 self.PRW_country.add_callback_on_selection(self.on_country_selected) 2224 self.PRW_zip_code.add_callback_on_lose_focus(self.on_zip_set)
2225 #--------------------------------------------------------
2226 - def on_country_selected(self, data):
2227 """Set the states according to entered country.""" 2228 self.PRW_state.set_context(context=u'country', val=data) 2229 return True
2230 #--------------------------------------------------------
2231 - def on_name_set(self):
2232 """Set the gender according to entered firstname. 2233 2234 Matches are fetched from existing records in backend. 2235 """ 2236 firstname = self.PRW_firstname.GetValue().strip() 2237 rows, idx = gmPG2.run_ro_queries(queries = [{ 2238 'cmd': u"select gender from dem.name_gender_map where name ilike %s", 2239 'args': [firstname] 2240 }]) 2241 if len(rows) == 0: 2242 return True 2243 wx.CallAfter(self.PRW_gender.SetData, rows[0][0]) 2244 return True
2245 #--------------------------------------------------------
2246 - def on_zip_set(self):
2247 """Set the street, town, state and country according to entered zip code.""" 2248 zip_code = self.PRW_zip_code.GetValue().strip() 2249 self.PRW_street.set_context(context=u'zip', val=zip_code) 2250 self.PRW_town.set_context(context=u'zip', val=zip_code) 2251 self.PRW_state.set_context(context=u'zip', val=zip_code) 2252 self.PRW_country.set_context(context=u'zip', val=zip_code) 2253 return True
2254 #============================================================
2255 -def makePageTitle(wizPg, title):
2256 """ 2257 Utility function to create the main sizer of a wizard's page. 2258 2259 @param wizPg The wizard page widget 2260 @type wizPg A wx.WizardPageSimple instance 2261 @param title The wizard page's descriptive title 2262 @type title A StringType instance 2263 """ 2264 sizer = wx.BoxSizer(wx.VERTICAL) 2265 wizPg.SetSizer(sizer) 2266 title = wx.StaticText(wizPg, -1, title) 2267 title.SetFont(wx.Font(10, wx.SWISS, wx.NORMAL, wx.BOLD)) 2268 sizer.Add(title, 0, wx.ALIGN_CENTRE|wx.ALL, 2) 2269 sizer.Add(wx.StaticLine(wizPg, -1), 0, wx.EXPAND|wx.ALL, 2) 2270 return sizer
2271 #============================================================
2272 -class cNewPatientWizard(wx.wizard.Wizard):
2273 """ 2274 Wizard to create a new patient. 2275 2276 TODO: 2277 - write pages for different "themes" of patient creation 2278 - make it configurable which pages are loaded 2279 - make available sets of pages that apply to a country 2280 - make loading of some pages depend upon values in earlier pages, eg 2281 when the patient is female and older than 13 include a page about 2282 "female" data (number of kids etc) 2283 2284 FIXME: use: wizard.FindWindowById(wx.ID_FORWARD).Disable() 2285 """ 2286 #--------------------------------------------------------
2287 - def __init__(self, parent, title = _('Register new person'), subtitle = _('Basic demographic details') ):
2288 """ 2289 Creates a new instance of NewPatientWizard 2290 @param parent - The parent widget 2291 @type parent - A wx.Window instance 2292 """ 2293 id_wiz = wx.NewId() 2294 wx.wizard.Wizard.__init__(self, parent, id_wiz, title) #images.getWizTest1Bitmap() 2295 self.SetExtraStyle(wx.WS_EX_VALIDATE_RECURSIVELY) 2296 self.__subtitle = subtitle 2297 self.__do_layout()
2298 #--------------------------------------------------------
2299 - def RunWizard(self, activate=False):
2300 """Create new patient. 2301 2302 activate, too, if told to do so (and patient successfully created) 2303 """ 2304 while True: 2305 2306 if not wx.wizard.Wizard.RunWizard(self, self.basic_pat_details): 2307 return False 2308 2309 try: 2310 # retrieve DTD and create patient 2311 ident = create_identity_from_dtd(dtd = self.basic_pat_details.form_DTD) 2312 except: 2313 _log.exception('cannot add new patient - missing identity fields') 2314 gmGuiHelpers.gm_show_error ( 2315 _('Cannot create new patient.\n' 2316 'Missing parts of the identity.' 2317 ), 2318 _('Adding new patient') 2319 ) 2320 continue 2321 2322 update_identity_from_dtd(identity = ident, dtd = self.basic_pat_details.form_DTD) 2323 2324 try: 2325 link_contacts_from_dtd(identity = ident, dtd = self.basic_pat_details.form_DTD) 2326 except: 2327 _log.exception('cannot finalize new patient - missing address fields') 2328 gmGuiHelpers.gm_show_error ( 2329 _('Cannot add address for the new patient.\n' 2330 'You must either enter all of the address fields or\n' 2331 'none at all. The relevant fields are marked in yellow.\n' 2332 '\n' 2333 'You will need to add the address details in the\n' 2334 'demographics module.' 2335 ), 2336 _('Adding new patient') 2337 ) 2338 break 2339 2340 link_occupation_from_dtd(identity = ident, dtd = self.basic_pat_details.form_DTD) 2341 2342 break 2343 2344 if activate: 2345 from Gnumed.wxpython import gmPatSearchWidgets 2346 gmPatSearchWidgets.set_active_patient(patient = ident) 2347 2348 return ident
2349 #-------------------------------------------------------- 2350 # internal helpers 2351 #--------------------------------------------------------
2352 - def __do_layout(self):
2353 """Arrange widgets. 2354 """ 2355 # Create the wizard pages 2356 self.basic_pat_details = cBasicPatDetailsPage(self, self.__subtitle ) 2357 self.FitToPage(self.basic_pat_details)
2358 #============================================================ 2359 #============================================================
2360 -class cBasicPatDetailsPageValidator(wx.PyValidator):
2361 """ 2362 This validator is used to ensure that the user has entered all 2363 the required conditional values in the page (eg., to properly 2364 create an address, all the related fields must be filled). 2365 """ 2366 #--------------------------------------------------------
2367 - def __init__(self, dtd):
2368 """ 2369 Validator initialization. 2370 @param dtd The object containing the data model. 2371 @type dtd A cFormDTD instance 2372 """ 2373 # initialize parent class 2374 wx.PyValidator.__init__(self) 2375 # validator's storage object 2376 self.form_DTD = dtd
2377 #--------------------------------------------------------
2378 - def Clone(self):
2379 """ 2380 Standard cloner. 2381 Note that every validator must implement the Clone() method. 2382 """ 2383 return cBasicPatDetailsPageValidator(dtd = self.form_DTD) # FIXME: probably need new instance of DTD ?
2384 #--------------------------------------------------------
2385 - def Validate(self, parent = None):
2386 """ 2387 Validate the contents of the given text control. 2388 """ 2389 _pnl_form = self.GetWindow().GetParent() 2390 2391 error = False 2392 2393 # name fields 2394 if _pnl_form.PRW_lastname.GetValue().strip() == '': 2395 error = True 2396 gmDispatcher.send(signal = 'statustext', msg = _('Must enter lastname.')) 2397 _pnl_form.PRW_lastname.SetBackgroundColour('pink') 2398 _pnl_form.PRW_lastname.Refresh() 2399 else: 2400 _pnl_form.PRW_lastname.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 2401 _pnl_form.PRW_lastname.Refresh() 2402 2403 if _pnl_form.PRW_firstname.GetValue().strip() == '': 2404 error = True 2405 gmDispatcher.send(signal = 'statustext', msg = _('Must enter first name.')) 2406 _pnl_form.PRW_firstname.SetBackgroundColour('pink') 2407 _pnl_form.PRW_firstname.Refresh() 2408 else: 2409 _pnl_form.PRW_firstname.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 2410 _pnl_form.PRW_firstname.Refresh() 2411 2412 # gender 2413 if _pnl_form.PRW_gender.GetData() is None: 2414 error = True 2415 gmDispatcher.send(signal = 'statustext', msg = _('Must select gender.')) 2416 _pnl_form.PRW_gender.SetBackgroundColour('pink') 2417 _pnl_form.PRW_gender.Refresh() 2418 else: 2419 _pnl_form.PRW_gender.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 2420 _pnl_form.PRW_gender.Refresh() 2421 2422 # dob validation 2423 if ( 2424 (_pnl_form.PRW_dob.GetValue().strip() == u'') 2425 or (not _pnl_form.PRW_dob.is_valid_timestamp()) 2426 or (_pnl_form.PRW_dob.GetData().timestamp.year < 1900) 2427 ): 2428 error = True 2429 msg = _('Cannot parse <%s> into proper timestamp.') % _pnl_form.PRW_dob.GetValue() 2430 gmDispatcher.send(signal = 'statustext', msg = msg) 2431 _pnl_form.PRW_dob.SetBackgroundColour('pink') 2432 _pnl_form.PRW_dob.Refresh() 2433 else: 2434 _pnl_form.PRW_dob.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 2435 _pnl_form.PRW_dob.Refresh() 2436 2437 # address 2438 is_any_field_filled = False 2439 address_fields = ( 2440 _pnl_form.TTC_address_number, 2441 _pnl_form.PRW_zip_code, 2442 _pnl_form.PRW_street, 2443 _pnl_form.PRW_town 2444 ) 2445 for field in address_fields: 2446 if field.GetValue().strip() == u'': 2447 if is_any_field_filled: 2448 error = True 2449 msg = _('To properly create an address, all the related fields must be filled in.') 2450 gmGuiHelpers.gm_show_error(msg, _('Required fields')) 2451 field.SetBackgroundColour('pink') 2452 field.SetFocus() 2453 field.Refresh() 2454 else: 2455 is_any_field_filled = True 2456 field.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 2457 field.Refresh() 2458 2459 address_fields = ( 2460 _pnl_form.PRW_state, 2461 _pnl_form.PRW_country 2462 ) 2463 for field in address_fields: 2464 if field.GetData() is None: 2465 if is_any_field_filled: 2466 error = True 2467 msg = _('To properly create an address, all the related fields must be filled in.') 2468 gmGuiHelpers.gm_show_error(msg, _('Required fields')) 2469 field.SetBackgroundColour('pink') 2470 field.SetFocus() 2471 field.Refresh() 2472 else: 2473 is_any_field_filled = True 2474 field.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 2475 field.Refresh() 2476 2477 return (not error)
2478 #--------------------------------------------------------
2479 - def TransferToWindow(self):
2480 """ 2481 Transfer data from validator to window. 2482 The default implementation returns False, indicating that an error 2483 occurred. We simply return True, as we don't do any data transfer. 2484 """ 2485 _pnl_form = self.GetWindow().GetParent() 2486 # fill in controls with values from self.form_DTD 2487 _pnl_form.PRW_gender.SetData(self.form_DTD['gender']) 2488 _pnl_form.PRW_dob.SetText(self.form_DTD['dob']) 2489 _pnl_form.PRW_lastname.SetText(self.form_DTD['lastnames']) 2490 _pnl_form.PRW_firstname.SetText(self.form_DTD['firstnames']) 2491 _pnl_form.PRW_title.SetText(self.form_DTD['title']) 2492 _pnl_form.PRW_nick.SetText(self.form_DTD['nick']) 2493 _pnl_form.PRW_occupation.SetText(self.form_DTD['occupation']) 2494 _pnl_form.TTC_address_number.SetValue(self.form_DTD['address_number']) 2495 _pnl_form.PRW_street.SetText(self.form_DTD['street']) 2496 _pnl_form.PRW_zip_code.SetText(self.form_DTD['zip_code']) 2497 _pnl_form.PRW_town.SetText(self.form_DTD['town']) 2498 _pnl_form.PRW_state.SetData(self.form_DTD['state']) 2499 _pnl_form.PRW_country.SetData(self.form_DTD['country']) 2500 _pnl_form.TTC_phone.SetValue(self.form_DTD['phone']) 2501 _pnl_form.TCTRL_comment.SetValue(self.form_DTD['comment']) 2502 return True # Prevent wxDialog from complaining
2503 #--------------------------------------------------------
2504 - def TransferFromWindow(self):
2505 """ 2506 Transfer data from window to validator. 2507 The default implementation returns False, indicating that an error 2508 occurred. We simply return True, as we don't do any data transfer. 2509 """ 2510 # FIXME: should be called automatically 2511 if not self.GetWindow().GetParent().Validate(): 2512 return False 2513 try: 2514 _pnl_form = self.GetWindow().GetParent() 2515 # fill in self.form_DTD with values from controls 2516 self.form_DTD['gender'] = _pnl_form.PRW_gender.GetData() 2517 self.form_DTD['dob'] = _pnl_form.PRW_dob.GetData() 2518 2519 self.form_DTD['lastnames'] = _pnl_form.PRW_lastname.GetValue() 2520 self.form_DTD['firstnames'] = _pnl_form.PRW_firstname.GetValue() 2521 self.form_DTD['title'] = _pnl_form.PRW_title.GetValue() 2522 self.form_DTD['nick'] = _pnl_form.PRW_nick.GetValue() 2523 2524 self.form_DTD['occupation'] = _pnl_form.PRW_occupation.GetValue() 2525 2526 self.form_DTD['address_number'] = _pnl_form.TTC_address_number.GetValue() 2527 self.form_DTD['street'] = _pnl_form.PRW_street.GetValue() 2528 self.form_DTD['zip_code'] = _pnl_form.PRW_zip_code.GetValue() 2529 self.form_DTD['town'] = _pnl_form.PRW_town.GetValue() 2530 self.form_DTD['state'] = _pnl_form.PRW_state.GetData() 2531 self.form_DTD['country'] = _pnl_form.PRW_country.GetData() 2532 2533 self.form_DTD['phone'] = _pnl_form.TTC_phone.GetValue() 2534 2535 self.form_DTD['comment'] = _pnl_form.TCTRL_comment.GetValue() 2536 except: 2537 return False 2538 return True
2539 #============================================================
2540 -class TestWizardPanel(wx.Panel):
2541 """ 2542 Utility class to test the new patient wizard. 2543 """ 2544 #--------------------------------------------------------
2545 - def __init__(self, parent, id):
2546 """ 2547 Create a new instance of TestPanel. 2548 @param parent The parent widget 2549 @type parent A wx.Window instance 2550 """ 2551 wx.Panel.__init__(self, parent, id) 2552 wizard = cNewPatientWizard(self) 2553 print wizard.RunWizard()
2554 #============================================================ 2555 if __name__ == "__main__": 2556 2557 #--------------------------------------------------------
2558 - def test_organizer_pnl():
2559 app = wx.PyWidgetTester(size = (600, 400)) 2560 app.SetWidget(cKOrganizerSchedulePnl) 2561 app.MainLoop()
2562 #--------------------------------------------------------
2563 - def test_person_names_pnl():
2564 app = wx.PyWidgetTester(size = (600, 400)) 2565 widget = cPersonNamesManagerPnl(app.frame, -1) 2566 widget.identity = activate_patient() 2567 app.frame.Show(True) 2568 app.MainLoop()
2569 #--------------------------------------------------------
2570 - def test_person_ids_pnl():
2571 app = wx.PyWidgetTester(size = (600, 400)) 2572 widget = cPersonIDsManagerPnl(app.frame, -1) 2573 widget.identity = activate_patient() 2574 app.frame.Show(True) 2575 app.MainLoop()
2576 #--------------------------------------------------------
2577 - def test_pat_ids_pnl():
2578 app = wx.PyWidgetTester(size = (600, 400)) 2579 widget = cPersonIdentityManagerPnl(app.frame, -1) 2580 widget.identity = activate_patient() 2581 app.frame.Show(True) 2582 app.MainLoop()
2583 #--------------------------------------------------------
2584 - def test_name_ea_pnl():
2585 app = wx.PyWidgetTester(size = (600, 400)) 2586 app.SetWidget(cNameGenderDOBEditAreaPnl, name = activate_patient().get_active_name()) 2587 app.MainLoop()
2588 #--------------------------------------------------------
2589 - def test_pat_contacts_pnl():
2590 app = wx.PyWidgetTester(size = (600, 400)) 2591 widget = cPersonContactsManagerPnl(app.frame, -1) 2592 widget.identity = activate_patient() 2593 app.frame.Show(True) 2594 app.MainLoop()
2595 #--------------------------------------------------------
2596 - def test_cPersonDemographicsEditorNb():
2597 app = wx.PyWidgetTester(size = (600, 400)) 2598 widget = cPersonDemographicsEditorNb(app.frame, -1) 2599 widget.identity = activate_patient() 2600 widget.refresh() 2601 app.frame.Show(True) 2602 app.MainLoop()
2603 #--------------------------------------------------------
2604 - def activate_patient():
2605 patient = gmPersonSearch.ask_for_patient() 2606 if patient is None: 2607 print "No patient. Exiting gracefully..." 2608 sys.exit(0) 2609 from Gnumed.wxpython import gmPatSearchWidgets 2610 gmPatSearchWidgets.set_active_patient(patient=patient) 2611 return patient
2612 #-------------------------------------------------------- 2613 if len(sys.argv) > 1 and sys.argv[1] == 'test': 2614 2615 gmI18N.activate_locale() 2616 gmI18N.install_domain(domain='gnumed') 2617 gmPG2.get_connection() 2618 2619 # a = cFormDTD(fields = cBasicPatDetailsPage.form_fields) 2620 2621 # app = wx.PyWidgetTester(size = (400, 300)) 2622 # app.SetWidget(cNotebookedPatEditionPanel, -1) 2623 # app.SetWidget(TestWizardPanel, -1) 2624 # app.frame.Show(True) 2625 # app.MainLoop() 2626 2627 # phrasewheels 2628 # test_zipcode_prw() 2629 # test_state_prw() 2630 # test_street_prw() 2631 # test_organizer_pnl() 2632 #test_address_type_prw() 2633 #test_suburb_prw() 2634 test_urb_prw() 2635 #test_address_prw() 2636 2637 # contacts related widgets 2638 #test_address_ea_pnl() 2639 #test_person_adrs_pnl() 2640 #test_person_comms_pnl() 2641 #test_pat_contacts_pnl() 2642 2643 # identity related widgets 2644 #test_person_names_pnl() 2645 #test_person_ids_pnl() 2646 #test_pat_ids_pnl() 2647 #test_name_ea_pnl() 2648 2649 #test_cPersonDemographicsEditorNb() 2650 2651 #============================================================ 2652