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