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