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