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 for child in self._SZR_bitmaps.GetChildren(): 296 self._SZR_bitmaps.Detach(child) 297 # while self._SZR_bitmaps.Detach(0): 298 # pass 299 for bmp in self.__bitmaps: 300 bmp.Destroy() 301 self.__bitmaps = []
302 #-------------------------------------------------------- 303 # internal helpers 304 #--------------------------------------------------------
305 - def __remove_tag(self, evt):
306 if self.__current_tag is None: 307 return 308 pat = gmPerson.gmCurrentPatient() 309 if not pat.connected: 310 return 311 pat.remove_tag(tag = self.__current_tag['pk_identity_tag'])
312 #--------------------------------------------------------
313 - def __edit_tag(self, evt):
314 if self.__current_tag is None: 315 return 316 317 msg = _('Edit the comment on tag [%s]') % self.__current_tag['l10n_description'] 318 comment = wx.GetTextFromUser ( 319 message = msg, 320 caption = _('Editing tag comment'), 321 default_value = gmTools.coalesce(self.__current_tag['comment'], u''), 322 parent = self 323 ) 324 325 if comment == u'': 326 return 327 328 if comment.strip() == self.__current_tag['comment']: 329 return 330 331 if comment == u' ': 332 self.__current_tag['comment'] = None 333 else: 334 self.__current_tag['comment'] = comment.strip() 335 336 self.__current_tag.save()
337 #-------------------------------------------------------- 338 # event handlers 339 #--------------------------------------------------------
340 - def _on_bitmap_rightclicked(self, evt):
341 self.__current_tag = evt.GetEventObject().tag 342 self.PopupMenu(self.__context_popup, pos = wx.DefaultPosition) 343 self.__current_tag = None
344 #============================================================ 345 #============================================================
346 -class cKOrganizerSchedulePnl(gmDataMiningWidgets.cPatientListingPnl):
347
348 - def __init__(self, *args, **kwargs):
349 350 kwargs['message'] = _("Today's KOrganizer appointments ...") 351 kwargs['button_defs'] = [ 352 {'label': _('Reload'), 'tooltip': _('Reload appointments from KOrganizer')}, 353 {'label': u''}, 354 {'label': u''}, 355 {'label': u''}, 356 {'label': u'KOrganizer', 'tooltip': _('Launch KOrganizer')} 357 ] 358 gmDataMiningWidgets.cPatientListingPnl.__init__(self, *args, **kwargs) 359 360 self.fname = os.path.expanduser(os.path.join('~', '.gnumed', 'tmp', 'korganizer2gnumed.csv')) 361 self.reload_cmd = 'konsolekalendar --view --export-type csv --export-file %s' % self.fname
362 363 #--------------------------------------------------------
364 - def _on_BTN_1_pressed(self, event):
365 """Reload appointments from KOrganizer.""" 366 self.reload_appointments()
367 #--------------------------------------------------------
368 - def _on_BTN_5_pressed(self, event):
369 """Reload appointments from KOrganizer.""" 370 found, cmd = gmShellAPI.detect_external_binary(binary = 'korganizer') 371 372 if not found: 373 gmDispatcher.send(signal = 'statustext', msg = _('KOrganizer is not installed.'), beep = True) 374 return 375 376 gmShellAPI.run_command_in_shell(command = cmd, blocking = False)
377 #--------------------------------------------------------
378 - def reload_appointments(self):
379 try: os.remove(self.fname) 380 except OSError: pass 381 gmShellAPI.run_command_in_shell(command=self.reload_cmd, blocking=True) 382 try: 383 csv_file = codecs.open(self.fname , mode = 'rU', encoding = 'utf8', errors = 'replace') 384 except IOError: 385 gmDispatcher.send(signal = u'statustext', msg = _('Cannot access KOrganizer transfer file [%s]') % self.fname, beep = True) 386 return 387 388 csv_lines = gmTools.unicode_csv_reader ( 389 csv_file, 390 delimiter = ',' 391 ) 392 # start_date, start_time, end_date, end_time, title (patient), ort, comment, UID 393 self._LCTRL_items.set_columns ([ 394 _('Place'), 395 _('Start'), 396 u'', 397 u'', 398 _('Patient'), 399 _('Comment') 400 ]) 401 items = [] 402 data = [] 403 for line in csv_lines: 404 items.append([line[5], line[0], line[1], line[3], line[4], line[6]]) 405 data.append([line[4], line[7]]) 406 407 self._LCTRL_items.set_string_items(items = items) 408 self._LCTRL_items.set_column_widths() 409 self._LCTRL_items.set_data(data = data) 410 self._LCTRL_items.patient_key = 0
411 #-------------------------------------------------------- 412 # notebook plugins API 413 #--------------------------------------------------------
414 - def repopulate_ui(self):
415 self.reload_appointments()
416 #============================================================ 417 # occupation related widgets / functions 418 #============================================================
419 -def edit_occupation():
420 421 pat = gmPerson.gmCurrentPatient() 422 curr_jobs = pat.get_occupations() 423 if len(curr_jobs) > 0: 424 old_job = curr_jobs[0]['l10n_occupation'] 425 update = curr_jobs[0]['modified_when'].strftime('%m/%Y') 426 else: 427 old_job = u'' 428 update = u'' 429 430 msg = _( 431 'Please enter the primary occupation of the patient.\n' 432 '\n' 433 'Currently recorded:\n' 434 '\n' 435 ' %s (last updated %s)' 436 ) % (old_job, update) 437 438 new_job = wx.GetTextFromUser ( 439 message = msg, 440 caption = _('Editing primary occupation'), 441 default_value = old_job, 442 parent = None 443 ) 444 if new_job.strip() == u'': 445 return 446 447 for job in curr_jobs: 448 # unlink all but the new job 449 if job['l10n_occupation'] != new_job: 450 pat.unlink_occupation(occupation = job['l10n_occupation']) 451 # and link the new one 452 pat.link_occupation(occupation = new_job)
453 454 #------------------------------------------------------------
455 -class cOccupationPhraseWheel(gmPhraseWheel.cPhraseWheel):
456
457 - def __init__(self, *args, **kwargs):
458 query = u"select distinct name, _(name) from dem.occupation where _(name) %(fragment_condition)s" 459 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query) 460 mp.setThresholds(1, 3, 5) 461 gmPhraseWheel.cPhraseWheel.__init__ ( 462 self, 463 *args, 464 **kwargs 465 ) 466 self.SetToolTipString(_("Type or select an occupation.")) 467 self.capitalisation_mode = gmTools.CAPS_FIRST 468 self.matcher = mp
469 470 #============================================================ 471 # identity widgets / functions 472 #============================================================
473 -def disable_identity(identity=None):
474 # ask user for assurance 475 go_ahead = gmGuiHelpers.gm_show_question ( 476 _('Are you sure you really, positively want\n' 477 'to disable the following person ?\n' 478 '\n' 479 ' %s %s %s\n' 480 ' born %s\n' 481 '\n' 482 '%s\n' 483 ) % ( 484 identity['firstnames'], 485 identity['lastnames'], 486 identity['gender'], 487 identity['dob'], 488 gmTools.bool2subst ( 489 identity.is_patient, 490 _('This patient DID receive care.'), 491 _('This person did NOT receive care.') 492 ) 493 ), 494 _('Disabling person') 495 ) 496 if not go_ahead: 497 return True 498 499 # get admin connection 500 conn = gmAuthWidgets.get_dbowner_connection ( 501 procedure = _('Disabling patient') 502 ) 503 # - user cancelled 504 if conn is False: 505 return True 506 # - error 507 if conn is None: 508 return False 509 510 # now disable patient 511 gmPG2.run_rw_queries(queries = [{'cmd': u"update dem.identity set deleted=True where pk=%s", 'args': [identity['pk_identity']]}]) 512 513 return True
514 515 #------------------------------------------------------------ 516 # phrasewheels 517 #------------------------------------------------------------
518 -class cLastnamePhraseWheel(gmPhraseWheel.cPhraseWheel):
519
520 - def __init__(self, *args, **kwargs):
521 query = u"select distinct lastnames, lastnames from dem.names where lastnames %(fragment_condition)s order by lastnames limit 25" 522 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query) 523 mp.setThresholds(3, 5, 9) 524 gmPhraseWheel.cPhraseWheel.__init__ ( 525 self, 526 *args, 527 **kwargs 528 ) 529 self.SetToolTipString(_("Type or select a last name (family name/surname).")) 530 self.capitalisation_mode = gmTools.CAPS_NAMES 531 self.matcher = mp
532 #------------------------------------------------------------
533 -class cFirstnamePhraseWheel(gmPhraseWheel.cPhraseWheel):
534
535 - def __init__(self, *args, **kwargs):
536 query = u""" 537 (select distinct firstnames, firstnames from dem.names where firstnames %(fragment_condition)s order by firstnames limit 20) 538 union 539 (select distinct name, name from dem.name_gender_map where name %(fragment_condition)s order by name limit 20)""" 540 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query) 541 mp.setThresholds(3, 5, 9) 542 gmPhraseWheel.cPhraseWheel.__init__ ( 543 self, 544 *args, 545 **kwargs 546 ) 547 self.SetToolTipString(_("Type or select a first name (forename/Christian name/given name).")) 548 self.capitalisation_mode = gmTools.CAPS_NAMES 549 self.matcher = mp
550 #------------------------------------------------------------
551 -class cNicknamePhraseWheel(gmPhraseWheel.cPhraseWheel):
552
553 - def __init__(self, *args, **kwargs):
554 query = u""" 555 (select distinct preferred, preferred from dem.names where preferred %(fragment_condition)s order by preferred limit 20) 556 union 557 (select distinct firstnames, firstnames from dem.names where firstnames %(fragment_condition)s order by firstnames limit 20) 558 union 559 (select distinct name, name from dem.name_gender_map where name %(fragment_condition)s order by name limit 20)""" 560 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query) 561 mp.setThresholds(3, 5, 9) 562 gmPhraseWheel.cPhraseWheel.__init__ ( 563 self, 564 *args, 565 **kwargs 566 ) 567 self.SetToolTipString(_("Type or select an alias (nick name, preferred name, call name, warrior name, artist name).")) 568 # nicknames CAN start with lower case ! 569 #self.capitalisation_mode = gmTools.CAPS_NAMES 570 self.matcher = mp
571 #------------------------------------------------------------
572 -class cTitlePhraseWheel(gmPhraseWheel.cPhraseWheel):
573
574 - def __init__(self, *args, **kwargs):
575 query = u"select distinct title, title from dem.identity where title %(fragment_condition)s" 576 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query) 577 mp.setThresholds(1, 3, 9) 578 gmPhraseWheel.cPhraseWheel.__init__ ( 579 self, 580 *args, 581 **kwargs 582 ) 583 self.SetToolTipString(_("Type or select a title. Note that the title applies to the person, not to a particular name !")) 584 self.matcher = mp
585 #------------------------------------------------------------
586 -class cGenderSelectionPhraseWheel(gmPhraseWheel.cPhraseWheel):
587 """Let user select a gender.""" 588 589 _gender_map = None 590
591 - def __init__(self, *args, **kwargs):
592 593 if cGenderSelectionPhraseWheel._gender_map is None: 594 cmd = u""" 595 select tag, l10n_label, sort_weight 596 from dem.v_gender_labels 597 order by sort_weight desc""" 598 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx=True) 599 cGenderSelectionPhraseWheel._gender_map = {} 600 for gender in rows: 601 cGenderSelectionPhraseWheel._gender_map[gender[idx['tag']]] = { 602 'data': gender[idx['tag']], 603 'label': gender[idx['l10n_label']], 604 'weight': gender[idx['sort_weight']] 605 } 606 607 mp = gmMatchProvider.cMatchProvider_FixedList(aSeq = cGenderSelectionPhraseWheel._gender_map.values()) 608 mp.setThresholds(1, 1, 3) 609 610 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 611 self.selection_only = True 612 self.matcher = mp 613 self.picklist_delay = 50
614 #------------------------------------------------------------
615 -class cExternalIDTypePhraseWheel(gmPhraseWheel.cPhraseWheel):
616
617 - def __init__(self, *args, **kwargs):
618 query = u""" 619 select distinct pk, (name || coalesce(' (%s ' || issuer || ')', '')) as label 620 from dem.enum_ext_id_types 621 where name %%(fragment_condition)s 622 order by label limit 25""" % _('issued by') 623 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query) 624 mp.setThresholds(1, 3, 5) 625 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 626 self.SetToolTipString(_("Enter or select a type for the external ID.")) 627 self.matcher = mp
628 #------------------------------------------------------------
629 -class cExternalIDIssuerPhraseWheel(gmPhraseWheel.cPhraseWheel):
630
631 - def __init__(self, *args, **kwargs):
632 query = u""" 633 select distinct issuer, issuer 634 from dem.enum_ext_id_types 635 where issuer %(fragment_condition)s 636 order by issuer limit 25""" 637 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query) 638 mp.setThresholds(1, 3, 5) 639 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 640 self.SetToolTipString(_("Type or select an ID issuer.")) 641 self.capitalisation_mode = gmTools.CAPS_FIRST 642 self.matcher = mp
643 #------------------------------------------------------------ 644 # edit areas 645 #------------------------------------------------------------ 646 from Gnumed.wxGladeWidgets import wxgExternalIDEditAreaPnl 647
648 -class cExternalIDEditAreaPnl(wxgExternalIDEditAreaPnl.wxgExternalIDEditAreaPnl):
649 """An edit area for editing/creating external IDs. 650 651 Does NOT act on/listen to the current patient. 652 """
653 - def __init__(self, *args, **kwargs):
654 655 try: 656 self.ext_id = kwargs['external_id'] 657 del kwargs['external_id'] 658 except: 659 self.ext_id = None 660 661 wxgExternalIDEditAreaPnl.wxgExternalIDEditAreaPnl.__init__(self, *args, **kwargs) 662 663 self.identity = None 664 665 self.__register_events() 666 667 self.refresh()
668 #-------------------------------------------------------- 669 # external API 670 #--------------------------------------------------------
671 - def refresh(self, ext_id=None):
672 if ext_id is not None: 673 self.ext_id = ext_id 674 675 if self.ext_id is not None: 676 self._PRW_type.SetText(value = self.ext_id['name'], data = self.ext_id['pk_type']) 677 self._TCTRL_value.SetValue(self.ext_id['value']) 678 self._PRW_issuer.SetText(self.ext_id['issuer']) 679 self._TCTRL_comment.SetValue(gmTools.coalesce(self.ext_id['comment'], u''))
680 # FIXME: clear fields 681 # else: 682 # pass 683 #--------------------------------------------------------
684 - def save(self):
685 686 if not self.__valid_for_save(): 687 return False 688 689 # strip out " (issued by ...)" added by phrasewheel 690 type = regex.split(' \(%s .+\)$' % _('issued by'), self._PRW_type.GetValue().strip(), 1)[0] 691 692 # add new external ID 693 if self.ext_id is None: 694 self.identity.add_external_id ( 695 type_name = type, 696 value = self._TCTRL_value.GetValue().strip(), 697 issuer = gmTools.none_if(self._PRW_issuer.GetValue().strip(), u''), 698 comment = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), u'') 699 ) 700 # edit old external ID 701 else: 702 self.identity.update_external_id ( 703 pk_id = self.ext_id['pk_id'], 704 type = type, 705 value = self._TCTRL_value.GetValue().strip(), 706 issuer = gmTools.none_if(self._PRW_issuer.GetValue().strip(), u''), 707 comment = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), u'') 708 ) 709 710 return True
711 #-------------------------------------------------------- 712 # internal helpers 713 #--------------------------------------------------------
714 - def __register_events(self):
715 self._PRW_type.add_callback_on_lose_focus(self._on_type_set)
716 #--------------------------------------------------------
717 - def _on_type_set(self):
718 """Set the issuer according to the selected type. 719 720 Matches are fetched from existing records in backend. 721 """ 722 pk_curr_type = self._PRW_type.GetData() 723 if pk_curr_type is None: 724 return True 725 rows, idx = gmPG2.run_ro_queries(queries = [{ 726 'cmd': u"select issuer from dem.enum_ext_id_types where pk = %s", 727 'args': [pk_curr_type] 728 }]) 729 if len(rows) == 0: 730 return True 731 wx.CallAfter(self._PRW_issuer.SetText, rows[0][0]) 732 return True
733 #--------------------------------------------------------
734 - def __valid_for_save(self):
735 736 no_errors = True 737 738 # do not test .GetData() because adding external IDs 739 # will create types if necessary 740 # if self._PRW_type.GetData() is None: 741 if self._PRW_type.GetValue().strip() == u'': 742 self._PRW_type.SetBackgroundColour('pink') 743 self._PRW_type.SetFocus() 744 self._PRW_type.Refresh() 745 else: 746 self._PRW_type.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 747 self._PRW_type.Refresh() 748 749 if self._TCTRL_value.GetValue().strip() == u'': 750 self._TCTRL_value.SetBackgroundColour('pink') 751 self._TCTRL_value.SetFocus() 752 self._TCTRL_value.Refresh() 753 no_errors = False 754 else: 755 self._TCTRL_value.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 756 self._TCTRL_value.Refresh() 757 758 return no_errors
759 #------------------------------------------------------------ 760 from Gnumed.wxGladeWidgets import wxgIdentityEAPnl 761
762 -class cIdentityEAPnl(wxgIdentityEAPnl.wxgIdentityEAPnl, gmEditArea.cGenericEditAreaMixin):
763 """An edit area for editing/creating title/gender/dob/dod etc.""" 764
765 - def __init__(self, *args, **kwargs):
766 767 try: 768 data = kwargs['identity'] 769 del kwargs['identity'] 770 except KeyError: 771 data = None 772 773 wxgIdentityEAPnl.wxgIdentityEAPnl.__init__(self, *args, **kwargs) 774 gmEditArea.cGenericEditAreaMixin.__init__(self) 775 776 self.mode = 'new' 777 self.data = data 778 if data is not None: 779 self.mode = 'edit'
780 781 # self.__init_ui() 782 #---------------------------------------------------------------- 783 # def __init_ui(self): 784 # # adjust phrasewheels etc 785 #---------------------------------------------------------------- 786 # generic Edit Area mixin API 787 #----------------------------------------------------------------
788 - def _valid_for_save(self):
789 790 has_error = False 791 792 if self._PRW_gender.GetData() is None: 793 self._PRW_gender.SetFocus() 794 has_error = True 795 796 if not self._PRW_dob.is_valid_timestamp(): 797 val = self._PRW_dob.GetValue().strip() 798 gmDispatcher.send(signal = u'statustext', msg = _('Cannot parse <%s> into proper timestamp.') % val) 799 self._PRW_dob.SetBackgroundColour('pink') 800 self._PRW_dob.Refresh() 801 self._PRW_dob.SetFocus() 802 has_error = True 803 else: 804 self._PRW_dob.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 805 self._PRW_dob.Refresh() 806 807 if not self._PRW_dod.is_valid_timestamp(allow_empty = True): 808 gmDispatcher.send(signal = u'statustext', msg = _('Invalid date of death.')) 809 self._PRW_dod.SetFocus() 810 has_error = True 811 812 return (has_error is False)
813 #----------------------------------------------------------------
814 - def _save_as_new(self):
815 # not intended to be used 816 return False
817 #----------------------------------------------------------------
818 - def _save_as_update(self):
819 820 self.data['gender'] = self._PRW_gender.GetData() 821 822 if self._PRW_dob.GetValue().strip() == u'': 823 self.data['dob'] = None 824 else: 825 self.data['dob'] = self._PRW_dob.GetData().get_pydt() 826 827 self.data['title'] = gmTools.none_if(self._PRW_title.GetValue().strip(), u'') 828 self.data['deceased'] = self._PRW_dod.GetData() 829 self.data['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), u'') 830 831 self.data.save() 832 return True
833 #----------------------------------------------------------------
834 - def _refresh_as_new(self):
835 pass
836 #----------------------------------------------------------------
837 - def _refresh_from_existing(self):
838 839 self._LBL_info.SetLabel(u'ID: #%s' % ( 840 self.data.ID 841 # FIXME: add 'deleted' status 842 )) 843 self._PRW_dob.SetText ( 844 value = self.data.get_formatted_dob(format = '%Y-%m-%d %H:%M', encoding = gmI18N.get_encoding()), 845 data = self.data['dob'] 846 ) 847 self._PRW_dod.SetText(value = self.data['deceased']) 848 self._PRW_gender.SetData(self.data['gender']) 849 #self._PRW_ethnicity.SetValue() 850 self._PRW_title.SetText(gmTools.coalesce(self.data['title'], u'')) 851 self._TCTRL_comment.SetValue(gmTools.coalesce(self.data['comment'], u''))
852 #----------------------------------------------------------------
854 pass
855 #------------------------------------------------------------ 856 from Gnumed.wxGladeWidgets import wxgNameGenderDOBEditAreaPnl 857
858 -class cNameGenderDOBEditAreaPnl(wxgNameGenderDOBEditAreaPnl.wxgNameGenderDOBEditAreaPnl):
859 """An edit area for editing/creating name/gender/dob. 860 861 Does NOT act on/listen to the current patient. 862 """
863 - def __init__(self, *args, **kwargs):
864 865 self.__name = kwargs['name'] 866 del kwargs['name'] 867 self.__identity = gmPerson.cIdentity(aPK_obj = self.__name['pk_identity']) 868 869 wxgNameGenderDOBEditAreaPnl.wxgNameGenderDOBEditAreaPnl.__init__(self, *args, **kwargs) 870 871 self.__register_interests() 872 self.refresh()
873 #-------------------------------------------------------- 874 # external API 875 #--------------------------------------------------------
876 - def refresh(self):
877 if self.__name is None: 878 return 879 880 self._PRW_title.SetText(gmTools.coalesce(self.__name['title'], u'')) 881 self._PRW_firstname.SetText(self.__name['firstnames']) 882 self._PRW_lastname.SetText(self.__name['lastnames']) 883 self._PRW_nick.SetText(gmTools.coalesce(self.__name['preferred'], u'')) 884 self._PRW_dob.SetText ( 885 value = self.__identity.get_formatted_dob(format = '%Y-%m-%d %H:%M', encoding = gmI18N.get_encoding()), 886 data = self.__identity['dob'] 887 ) 888 self._PRW_gender.SetData(self.__name['gender']) 889 self._CHBOX_active.SetValue(self.__name['active_name']) 890 self._PRW_dod.SetText(data = self.__identity['deceased']) 891 self._TCTRL_comment.SetValue(gmTools.coalesce(self.__name['comment'], u''))
892 # FIXME: clear fields 893 # else: 894 # pass 895 #--------------------------------------------------------
896 - def save(self):
897 898 if not self.__valid_for_save(): 899 return False 900 901 self.__identity['gender'] = self._PRW_gender.GetData() 902 if self._PRW_dob.GetValue().strip() == u'': 903 self.__identity['dob'] = None 904 else: 905 self.__identity['dob'] = self._PRW_dob.GetData().get_pydt() 906 self.__identity['title'] = gmTools.none_if(self._PRW_title.GetValue().strip(), u'') 907 self.__identity['deceased'] = self._PRW_dod.GetData() 908 self.__identity.save_payload() 909 910 active = self._CHBOX_active.GetValue() 911 first = self._PRW_firstname.GetValue().strip() 912 last = self._PRW_lastname.GetValue().strip() 913 old_nick = self.__name['preferred'] 914 915 # is it a new name ? 916 old_name = self.__name['firstnames'] + self.__name['lastnames'] 917 if (first + last) != old_name: 918 self.__name = self.__identity.add_name(first, last, active) 919 920 self.__name['active_name'] = active 921 self.__name['preferred'] = gmTools.none_if(self._PRW_nick.GetValue().strip(), u'') 922 self.__name['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), u'') 923 924 self.__name.save_payload() 925 926 return True
927 #-------------------------------------------------------- 928 # event handling 929 #--------------------------------------------------------
930 - def __register_interests(self):
931 self._PRW_firstname.add_callback_on_lose_focus(self._on_name_set)
932 #--------------------------------------------------------
933 - def _on_name_set(self):
934 """Set the gender according to entered firstname. 935 936 Matches are fetched from existing records in backend. 937 """ 938 firstname = self._PRW_firstname.GetValue().strip() 939 if firstname == u'': 940 return True 941 rows, idx = gmPG2.run_ro_queries(queries = [{ 942 'cmd': u"select gender from dem.name_gender_map where name ilike %s", 943 'args': [firstname] 944 }]) 945 if len(rows) == 0: 946 return True 947 wx.CallAfter(self._PRW_gender.SetData, rows[0][0]) 948 return True
949 #-------------------------------------------------------- 950 # internal helpers 951 #--------------------------------------------------------
952 - def __valid_for_save(self):
953 954 has_error = False 955 956 if self._PRW_gender.GetData() is None: 957 self._PRW_gender.display_as_valid(False) 958 self._PRW_gender.SetFocus() 959 has_error = True 960 else: 961 self._PRW_gender.display_as_valid(True) 962 963 if not self._PRW_dob.is_valid_timestamp(): 964 val = self._PRW_dob.GetValue().strip() 965 gmDispatcher.send(signal = u'statustext', msg = _('Cannot parse <%s> into proper timestamp.') % val) 966 self._PRW_dob.display_as_valid(False) 967 self._PRW_dob.SetFocus() 968 has_error = True 969 else: 970 self._PRW_dob.display_as_valid(True) 971 972 if not self._PRW_dod.is_valid_timestamp(): 973 gmDispatcher.send(signal = u'statustext', msg = _('Invalid date of death.')) 974 self._PRW_dod.display_as_valid(False) 975 self._PRW_dod.SetFocus() 976 has_error = True 977 else: 978 self._PRW_dod.display_as_valid(True) 979 980 if self._PRW_lastname.GetValue().strip() == u'': 981 self._PRW_lastname.display_as_valid(False) 982 self._PRW_lastname.SetFocus() 983 has_error = True 984 else: 985 self._PRW_lastname.display_as_valid(True) 986 987 if self._PRW_firstname.GetValue().strip() == u'': 988 self._PRW_firstname.display_as_valid(False) 989 self._PRW_firstname.SetFocus() 990 has_error = True 991 else: 992 self._PRW_firstname.display_as_valid(True) 993 994 return (has_error is False)
995 #------------------------------------------------------------ 996 # list manager 997 #------------------------------------------------------------
998 -class cPersonNamesManagerPnl(gmListWidgets.cGenericListManagerPnl):
999 """A list for managing a person's names. 1000 1001 Does NOT act on/listen to the current patient. 1002 """
1003 - def __init__(self, *args, **kwargs):
1004 1005 try: 1006 self.__identity = kwargs['identity'] 1007 del kwargs['identity'] 1008 except KeyError: 1009 self.__identity = None 1010 1011 gmListWidgets.cGenericListManagerPnl.__init__(self, *args, **kwargs) 1012 1013 self.new_callback = self._add_name 1014 self.edit_callback = self._edit_name 1015 self.delete_callback = self._del_name 1016 self.refresh_callback = self.refresh 1017 1018 self.__init_ui() 1019 self.refresh()
1020 #-------------------------------------------------------- 1021 # external API 1022 #--------------------------------------------------------
1023 - def refresh(self, *args, **kwargs):
1024 if self.__identity is None: 1025 self._LCTRL_items.set_string_items() 1026 return 1027 1028 names = self.__identity.get_names() 1029 self._LCTRL_items.set_string_items ( 1030 items = [ [ 1031 gmTools.bool2str(n['active_name'], 'X', ''), 1032 n['lastnames'], 1033 n['firstnames'], 1034 gmTools.coalesce(n['preferred'], u''), 1035 gmTools.coalesce(n['comment'], u'') 1036 ] for n in names ] 1037 ) 1038 self._LCTRL_items.set_column_widths() 1039 self._LCTRL_items.set_data(data = names)
1040 #-------------------------------------------------------- 1041 # internal helpers 1042 #--------------------------------------------------------
1043 - def __init_ui(self):
1044 self._LCTRL_items.set_columns(columns = [ 1045 _('Active'), 1046 _('Lastname'), 1047 _('Firstname(s)'), 1048 _('Preferred Name'), 1049 _('Comment') 1050 ]) 1051 self._BTN_edit.SetLabel(_('Clone and &edit'))
1052 #--------------------------------------------------------
1053 - def _add_name(self):
1054 ea = cNameGenderDOBEditAreaPnl(self, -1, name = self.__identity.get_active_name()) 1055 dlg = gmEditArea.cGenericEditAreaDlg(self, -1, edit_area = ea) 1056 dlg.SetTitle(_('Adding new name')) 1057 if dlg.ShowModal() == wx.ID_OK: 1058 dlg.Destroy() 1059 return True 1060 dlg.Destroy() 1061 return False
1062 #--------------------------------------------------------
1063 - def _edit_name(self, name):
1064 ea = cNameGenderDOBEditAreaPnl(self, -1, name = name) 1065 dlg = gmEditArea.cGenericEditAreaDlg(self, -1, edit_area = ea) 1066 dlg.SetTitle(_('Cloning name')) 1067 if dlg.ShowModal() == wx.ID_OK: 1068 dlg.Destroy() 1069 return True 1070 dlg.Destroy() 1071 return False
1072 #--------------------------------------------------------
1073 - def _del_name(self, name):
1074 1075 if len(self.__identity.get_names()) == 1: 1076 gmDispatcher.send(signal = u'statustext', msg = _('Cannot delete the only name of a person.'), beep = True) 1077 return False 1078 1079 go_ahead = gmGuiHelpers.gm_show_question ( 1080 _( 'It is often advisable to keep old names around and\n' 1081 'just create a new "currently active" name.\n' 1082 '\n' 1083 'This allows finding the patient by both the old\n' 1084 'and the new name (think before/after marriage).\n' 1085 '\n' 1086 'Do you still want to really delete\n' 1087 "this name from the patient ?" 1088 ), 1089 _('Deleting name') 1090 ) 1091 if not go_ahead: 1092 return False 1093 1094 self.__identity.delete_name(name = name) 1095 return True
1096 #-------------------------------------------------------- 1097 # properties 1098 #--------------------------------------------------------
1099 - def _get_identity(self):
1100 return self.__identity
1101
1102 - def _set_identity(self, identity):
1103 self.__identity = identity 1104 self.refresh()
1105 1106 identity = property(_get_identity, _set_identity)
1107 #------------------------------------------------------------
1108 -class cPersonIDsManagerPnl(gmListWidgets.cGenericListManagerPnl):
1109 """A list for managing a person's external IDs. 1110 1111 Does NOT act on/listen to the current patient. 1112 """
1113 - def __init__(self, *args, **kwargs):
1114 1115 try: 1116 self.__identity = kwargs['identity'] 1117 del kwargs['identity'] 1118 except KeyError: 1119 self.__identity = None 1120 1121 gmListWidgets.cGenericListManagerPnl.__init__(self, *args, **kwargs) 1122 1123 self.new_callback = self._add_id 1124 self.edit_callback = self._edit_id 1125 self.delete_callback = self._del_id 1126 self.refresh_callback = self.refresh 1127 1128 self.__init_ui() 1129 self.refresh()
1130 #-------------------------------------------------------- 1131 # external API 1132 #--------------------------------------------------------
1133 - def refresh(self, *args, **kwargs):
1134 if self.__identity is None: 1135 self._LCTRL_items.set_string_items() 1136 return 1137 1138 ids = self.__identity.get_external_ids() 1139 self._LCTRL_items.set_string_items ( 1140 items = [ [ 1141 i['name'], 1142 i['value'], 1143 gmTools.coalesce(i['issuer'], u''), 1144 gmTools.coalesce(i['comment'], u'') 1145 ] for i in ids 1146 ] 1147 ) 1148 self._LCTRL_items.set_column_widths() 1149 self._LCTRL_items.set_data(data = ids)
1150 #-------------------------------------------------------- 1151 # internal helpers 1152 #--------------------------------------------------------
1153 - def __init_ui(self):
1154 self._LCTRL_items.set_columns(columns = [ 1155 _('ID type'), 1156 _('Value'), 1157 _('Issuer'), 1158 _('Comment') 1159 ])
1160 #--------------------------------------------------------
1161 - def _add_id(self):
1162 ea = cExternalIDEditAreaPnl(self, -1) 1163 ea.identity = self.__identity 1164 dlg = gmEditArea.cGenericEditAreaDlg(self, -1, edit_area = ea) 1165 dlg.SetTitle(_('Adding new external ID')) 1166 if dlg.ShowModal() == wx.ID_OK: 1167 dlg.Destroy() 1168 return True 1169 dlg.Destroy() 1170 return False
1171 #--------------------------------------------------------
1172 - def _edit_id(self, ext_id):
1173 ea = cExternalIDEditAreaPnl(self, -1, external_id = ext_id) 1174 ea.identity = self.__identity 1175 dlg = gmEditArea.cGenericEditAreaDlg(self, -1, edit_area = ea) 1176 dlg.SetTitle(_('Editing external ID')) 1177 if dlg.ShowModal() == wx.ID_OK: 1178 dlg.Destroy() 1179 return True 1180 dlg.Destroy() 1181 return False
1182 #--------------------------------------------------------
1183 - def _del_id(self, ext_id):
1184 go_ahead = gmGuiHelpers.gm_show_question ( 1185 _( 'Do you really want to delete this\n' 1186 'external ID from the patient ?'), 1187 _('Deleting external ID') 1188 ) 1189 if not go_ahead: 1190 return False 1191 self.__identity.delete_external_id(pk_ext_id = ext_id['pk_id']) 1192 return True
1193 #-------------------------------------------------------- 1194 # properties 1195 #--------------------------------------------------------
1196 - def _get_identity(self):
1197 return self.__identity
1198
1199 - def _set_identity(self, identity):
1200 self.__identity = identity 1201 self.refresh()
1202 1203 identity = property(_get_identity, _set_identity)
1204 #------------------------------------------------------------ 1205 # integrated panels 1206 #------------------------------------------------------------ 1207 from Gnumed.wxGladeWidgets import wxgPersonIdentityManagerPnl 1208
1209 -class cPersonIdentityManagerPnl(wxgPersonIdentityManagerPnl.wxgPersonIdentityManagerPnl):
1210 """A panel for editing identity data for a person. 1211 1212 - provides access to: 1213 - name 1214 - external IDs 1215 1216 Does NOT act on/listen to the current patient. 1217 """
1218 - def __init__(self, *args, **kwargs):
1219 1220 wxgPersonIdentityManagerPnl.wxgPersonIdentityManagerPnl.__init__(self, *args, **kwargs) 1221 1222 self.__identity = None 1223 self.refresh()
1224 #-------------------------------------------------------- 1225 # external API 1226 #--------------------------------------------------------
1227 - def refresh(self):
1228 self._PNL_names.identity = self.__identity 1229 self._PNL_ids.identity = self.__identity 1230 # this is an Edit Area: 1231 self._PNL_identity.mode = 'new' 1232 self._PNL_identity.data = self.__identity 1233 if self.__identity is not None: 1234 self._PNL_identity.mode = 'edit'
1235 #-------------------------------------------------------- 1236 # properties 1237 #--------------------------------------------------------
1238 - def _get_identity(self):
1239 return self.__identity
1240
1241 - def _set_identity(self, identity):
1242 self.__identity = identity 1243 self.refresh()
1244 1245 identity = property(_get_identity, _set_identity) 1246 #-------------------------------------------------------- 1247 # event handlers 1248 #--------------------------------------------------------
1250 if not self._PNL_identity.save(): 1251 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save identity. Incomplete information.'), beep = True)
1252 #--------------------------------------------------------
1253 - def _on_reload_identity_button_pressed(self, event):
1254 self._PNL_identity.refresh()
1255 1256 #============================================================ 1257 from Gnumed.wxGladeWidgets import wxgPersonSocialNetworkManagerPnl 1258
1259 -class cPersonSocialNetworkManagerPnl(wxgPersonSocialNetworkManagerPnl.wxgPersonSocialNetworkManagerPnl):
1260 - def __init__(self, *args, **kwargs):
1261 1262 wxgPersonSocialNetworkManagerPnl.wxgPersonSocialNetworkManagerPnl.__init__(self, *args, **kwargs) 1263 1264 self.__identity = None 1265 self._PRW_provider.selection_only = False 1266 self.refresh()
1267 #-------------------------------------------------------- 1268 # external API 1269 #--------------------------------------------------------
1270 - def refresh(self):
1271 1272 tt = _("Link another person in this database as the emergency contact.") 1273 1274 if self.__identity is None: 1275 self._TCTRL_er_contact.SetValue(u'') 1276 self._TCTRL_person.person = None 1277 self._TCTRL_person.SetToolTipString(tt) 1278 1279 self._PRW_provider.SetText(value = u'', data = None) 1280 return 1281 1282 self._TCTRL_er_contact.SetValue(gmTools.coalesce(self.__identity['emergency_contact'], u'')) 1283 if self.__identity['pk_emergency_contact'] is not None: 1284 ident = gmPerson.cIdentity(aPK_obj = self.__identity['pk_emergency_contact']) 1285 self._TCTRL_person.person = ident 1286 tt = u'%s\n\n%s\n\n%s' % ( 1287 tt, 1288 ident['description_gender'], 1289 u'\n'.join([ 1290 u'%s: %s%s' % ( 1291 c['l10n_comm_type'], 1292 c['url'], 1293 gmTools.bool2subst(c['is_confidential'], _(' (confidential !)'), u'', u'') 1294 ) 1295 for c in ident.get_comm_channels() 1296 ]) 1297 ) 1298 else: 1299 self._TCTRL_person.person = None 1300 1301 self._TCTRL_person.SetToolTipString(tt) 1302 1303 if self.__identity['pk_primary_provider'] is None: 1304 self._PRW_provider.SetText(value = u'', data = None) 1305 else: 1306 self._PRW_provider.SetData(data = self.__identity['pk_primary_provider'])
1307 #-------------------------------------------------------- 1308 # properties 1309 #--------------------------------------------------------
1310 - def _get_identity(self):
1311 return self.__identity
1312
1313 - def _set_identity(self, identity):
1314 self.__identity = identity 1315 self.refresh()
1316 1317 identity = property(_get_identity, _set_identity) 1318 #-------------------------------------------------------- 1319 # event handlers 1320 #--------------------------------------------------------
1321 - def _on_save_button_pressed(self, event):
1322 if self.__identity is not None: 1323 self.__identity['emergency_contact'] = self._TCTRL_er_contact.GetValue().strip() 1324 if self._TCTRL_person.person is not None: 1325 self.__identity['pk_emergency_contact'] = self._TCTRL_person.person.ID 1326 if self._PRW_provider.GetValue().strip == u'': 1327 self.__identity['pk_primary_provider'] = None 1328 else: 1329 self.__identity['pk_primary_provider'] = self._PRW_provider.GetData() 1330 1331 self.__identity.save() 1332 1333 event.Skip()
1334 #--------------------------------------------------------
1335 - def _on_remove_contact_button_pressed(self, event):
1336 event.Skip() 1337 1338 if self.__identity is None: 1339 return 1340 1341 self._TCTRL_person.person = None 1342 1343 self.__identity['pk_emergency_contact'] = None 1344 self.__identity.save()
1345 #--------------------------------------------------------
1346 - def _on_button_activate_contact_pressed(self, event):
1347 ident = self._TCTRL_person.person 1348 if ident is not None: 1349 from Gnumed.wxpython import gmPatSearchWidgets 1350 gmPatSearchWidgets.set_active_patient(patient = ident, forced_reload = False) 1351 1352 event.Skip()
1353 #============================================================ 1354 # new-patient widgets 1355 #============================================================
1356 -def create_new_person(parent=None, activate=False):
1357 1358 dbcfg = gmCfg.cCfgSQL() 1359 1360 def_region = dbcfg.get2 ( 1361 option = u'person.create.default_region', 1362 workplace = gmSurgery.gmCurrentPractice().active_workplace, 1363 bias = u'user' 1364 ) 1365 def_country = None 1366 1367 if def_region is None: 1368 def_country = dbcfg.get2 ( 1369 option = u'person.create.default_country', 1370 workplace = gmSurgery.gmCurrentPractice().active_workplace, 1371 bias = u'user' 1372 ) 1373 else: 1374 countries = gmDemographicRecord.get_country_for_region(region = def_region) 1375 if len(countries) == 1: 1376 def_country = countries[0]['l10n_country'] 1377 1378 if parent is None: 1379 parent = wx.GetApp().GetTopWindow() 1380 1381 ea = cNewPatientEAPnl(parent = parent, id = -1, country = def_country, region = def_region) 1382 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = True) 1383 dlg.SetTitle(_('Adding new person')) 1384 ea._PRW_lastname.SetFocus() 1385 result = dlg.ShowModal() 1386 pat = ea.data 1387 dlg.Destroy() 1388 1389 if result != wx.ID_OK: 1390 return False 1391 1392 _log.debug('created new person [%s]', pat.ID) 1393 1394 if activate: 1395 from Gnumed.wxpython import gmPatSearchWidgets 1396 gmPatSearchWidgets.set_active_patient(patient = pat) 1397 1398 gmDispatcher.send(signal = 'display_widget', name = 'gmNotebookedPatientEditionPlugin') 1399 1400 return True
1401 #============================================================ 1402 from Gnumed.wxGladeWidgets import wxgNewPatientEAPnl 1403
1404 -class cNewPatientEAPnl(wxgNewPatientEAPnl.wxgNewPatientEAPnl, gmEditArea.cGenericEditAreaMixin):
1405
1406 - def __init__(self, *args, **kwargs):
1407 1408 try: 1409 self.default_region = kwargs['region'] 1410 del kwargs['region'] 1411 except KeyError: 1412 self.default_region = None 1413 1414 try: 1415 self.default_country = kwargs['country'] 1416 del kwargs['country'] 1417 except KeyError: 1418 self.default_country = None 1419 1420 wxgNewPatientEAPnl.wxgNewPatientEAPnl.__init__(self, *args, **kwargs) 1421 gmEditArea.cGenericEditAreaMixin.__init__(self) 1422 1423 self.mode = 'new' 1424 self.data = None 1425 self._address = None 1426 1427 self.__init_ui() 1428 self.__register_interests()
1429 #---------------------------------------------------------------- 1430 # internal helpers 1431 #----------------------------------------------------------------
1432 - def __init_ui(self):
1433 self._PRW_lastname.final_regex = '.+' 1434 self._PRW_firstnames.final_regex = '.+' 1435 self._PRW_address_searcher.selection_only = False 1436 1437 # only if we would support None on selection_only's: 1438 # self._PRW_external_id_type.selection_only = True 1439 1440 if self.default_country is not None: 1441 self._PRW_country.SetText(value = self.default_country) 1442 1443 if self.default_region is not None: 1444 self._PRW_region.SetText(value = self.default_region)
1445 #----------------------------------------------------------------
1446 - def __perhaps_invalidate_address_searcher(self, ctrl=None, field=None):
1447 1448 adr = self._PRW_address_searcher.get_address() 1449 if adr is None: 1450 return True 1451 1452 if ctrl.GetValue().strip() != adr[field]: 1453 wx.CallAfter(self._PRW_address_searcher.SetText, value = u'', data = None) 1454 return True 1455 1456 return False
1457 #----------------------------------------------------------------
1459 adr = self._PRW_address_searcher.get_address() 1460 if adr is None: 1461 return True 1462 1463 self._PRW_zip.SetText(value = adr['postcode'], data = adr['postcode']) 1464 1465 self._PRW_street.SetText(value = adr['street'], data = adr['street']) 1466 self._PRW_street.set_context(context = u'zip', val = adr['postcode']) 1467 1468 self._TCTRL_number.SetValue(adr['number']) 1469 1470 self._PRW_urb.SetText(value = adr['urb'], data = adr['urb']) 1471 self._PRW_urb.set_context(context = u'zip', val = adr['postcode']) 1472 1473 self._PRW_region.SetText(value = adr['l10n_state'], data = adr['code_state']) 1474 self._PRW_region.set_context(context = u'zip', val = adr['postcode']) 1475 1476 self._PRW_country.SetText(value = adr['l10n_country'], data = adr['code_country']) 1477 self._PRW_country.set_context(context = u'zip', val = adr['postcode'])
1478 #----------------------------------------------------------------
1479 - def __identity_valid_for_save(self):
1480 error = False 1481 1482 # name fields 1483 if self._PRW_lastname.GetValue().strip() == u'': 1484 error = True 1485 gmDispatcher.send(signal = 'statustext', msg = _('Must enter lastname.')) 1486 self._PRW_lastname.display_as_valid(False) 1487 else: 1488 self._PRW_lastname.display_as_valid(True) 1489 1490 if self._PRW_firstnames.GetValue().strip() == '': 1491 error = True 1492 gmDispatcher.send(signal = 'statustext', msg = _('Must enter first name.')) 1493 self._PRW_firstnames.display_as_valid(False) 1494 else: 1495 self._PRW_firstnames.display_as_valid(True) 1496 1497 # gender 1498 if self._PRW_gender.GetData() is None: 1499 error = True 1500 gmDispatcher.send(signal = 'statustext', msg = _('Must select gender.')) 1501 self._PRW_gender.display_as_valid(False) 1502 else: 1503 self._PRW_gender.display_as_valid(True) 1504 1505 # dob validation 1506 # test this last so we can check empty field as last barrier against save 1507 # 1) valid timestamp ? 1508 if self._PRW_dob.is_valid_timestamp(allow_empty = False): # properly colors the field 1509 dob = self._PRW_dob.date 1510 # but year also usable ? 1511 msg = None 1512 if dob.year < 1900: 1513 msg = _( 1514 'DOB: %s\n' 1515 '\n' 1516 'While this is a valid point in time Python does\n' 1517 'not know how to deal with it.\n' 1518 '\n' 1519 'We suggest using January 1st 1901 instead and adding\n' 1520 'the true date of birth to the patient comment.\n' 1521 '\n' 1522 'Sorry for the inconvenience %s' 1523 ) % (dob, gmTools.u_frowning_face) 1524 elif dob > gmDateTime.pydt_now_here(): 1525 msg = _( 1526 'DOB: %s\n' 1527 '\n' 1528 'Date of birth in the future !' 1529 ) % dob 1530 1531 if msg is not None: 1532 error = True 1533 gmGuiHelpers.gm_show_error ( 1534 msg, 1535 _('Registering new person') 1536 ) 1537 self._PRW_dob.display_as_valid(False) 1538 self._PRW_dob.SetFocus() 1539 # 2) invalid timestamp ? 1540 else: 1541 # is this the only error ? 1542 if error is False: 1543 # is it empty rather than invalid ? 1544 if self._PRW_dob.GetValue().strip() == u'': 1545 # maybe even allow empty DOB ? 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._PRW_dob.display_as_valid(True) 1559 else: 1560 error = True 1561 self._PRW_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._PRW_dob.GetData(), 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