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 'label': gender[idx['l10n_label']], 627 'weight': gender[idx['sort_weight']] 628 } 629 630 mp = gmMatchProvider.cMatchProvider_FixedList(aSeq = cGenderSelectionPhraseWheel._gender_map.values()) 631 mp.setThresholds(1, 1, 3) 632 633 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 634 self.selection_only = True 635 self.matcher = mp 636 self.picklist_delay = 50
637 #------------------------------------------------------------
638 -class cExternalIDTypePhraseWheel(gmPhraseWheel.cPhraseWheel):
639
640 - def __init__(self, *args, **kwargs):
641 query = u""" 642 select distinct pk, (name || coalesce(' (%s ' || issuer || ')', '')) as label 643 from dem.enum_ext_id_types 644 where name %%(fragment_condition)s 645 order by label limit 25""" % _('issued by') 646 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query) 647 mp.setThresholds(1, 3, 5) 648 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 649 self.SetToolTipString(_("Enter or select a type for the external ID.")) 650 self.matcher = mp
651 #------------------------------------------------------------
652 -class cExternalIDIssuerPhraseWheel(gmPhraseWheel.cPhraseWheel):
653
654 - def __init__(self, *args, **kwargs):
655 query = u""" 656 select distinct issuer, issuer 657 from dem.enum_ext_id_types 658 where issuer %(fragment_condition)s 659 order by issuer limit 25""" 660 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query) 661 mp.setThresholds(1, 3, 5) 662 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 663 self.SetToolTipString(_("Type or select an ID issuer.")) 664 self.capitalisation_mode = gmTools.CAPS_FIRST 665 self.matcher = mp
666 #------------------------------------------------------------ 667 # edit areas 668 #------------------------------------------------------------ 669 from Gnumed.wxGladeWidgets import wxgExternalIDEditAreaPnl 670
671 -class cExternalIDEditAreaPnl(wxgExternalIDEditAreaPnl.wxgExternalIDEditAreaPnl):
672 """An edit area for editing/creating external IDs. 673 674 Does NOT act on/listen to the current patient. 675 """
676 - def __init__(self, *args, **kwargs):
677 678 try: 679 self.ext_id = kwargs['external_id'] 680 del kwargs['external_id'] 681 except: 682 self.ext_id = None 683 684 wxgExternalIDEditAreaPnl.wxgExternalIDEditAreaPnl.__init__(self, *args, **kwargs) 685 686 self.identity = None 687 688 self.__register_events() 689 690 self.refresh()
691 #-------------------------------------------------------- 692 # external API 693 #--------------------------------------------------------
694 - def refresh(self, ext_id=None):
695 if ext_id is not None: 696 self.ext_id = ext_id 697 698 if self.ext_id is not None: 699 self._PRW_type.SetText(value = self.ext_id['name'], data = self.ext_id['pk_type']) 700 self._TCTRL_value.SetValue(self.ext_id['value']) 701 self._PRW_issuer.SetText(self.ext_id['issuer']) 702 self._TCTRL_comment.SetValue(gmTools.coalesce(self.ext_id['comment'], u''))
703 # FIXME: clear fields 704 # else: 705 # pass 706 #--------------------------------------------------------
707 - def save(self):
708 709 if not self.__valid_for_save(): 710 return False 711 712 # strip out " (issued by ...)" added by phrasewheel 713 type = regex.split(' \(%s .+\)$' % _('issued by'), self._PRW_type.GetValue().strip(), 1)[0] 714 715 # add new external ID 716 if self.ext_id is None: 717 self.identity.add_external_id ( 718 type_name = type, 719 value = self._TCTRL_value.GetValue().strip(), 720 issuer = gmTools.none_if(self._PRW_issuer.GetValue().strip(), u''), 721 comment = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), u'') 722 ) 723 # edit old external ID 724 else: 725 self.identity.update_external_id ( 726 pk_id = self.ext_id['pk_id'], 727 type = type, 728 value = self._TCTRL_value.GetValue().strip(), 729 issuer = gmTools.none_if(self._PRW_issuer.GetValue().strip(), u''), 730 comment = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), u'') 731 ) 732 733 return True
734 #-------------------------------------------------------- 735 # internal helpers 736 #--------------------------------------------------------
737 - def __register_events(self):
738 self._PRW_type.add_callback_on_lose_focus(self._on_type_set)
739 #--------------------------------------------------------
740 - def _on_type_set(self):
741 """Set the issuer according to the selected type. 742 743 Matches are fetched from existing records in backend. 744 """ 745 pk_curr_type = self._PRW_type.GetData() 746 if pk_curr_type is None: 747 return True 748 rows, idx = gmPG2.run_ro_queries(queries = [{ 749 'cmd': u"select issuer from dem.enum_ext_id_types where pk = %s", 750 'args': [pk_curr_type] 751 }]) 752 if len(rows) == 0: 753 return True 754 wx.CallAfter(self._PRW_issuer.SetText, rows[0][0]) 755 return True
756 #--------------------------------------------------------
757 - def __valid_for_save(self):
758 759 no_errors = True 760 761 # do not test .GetData() because adding external IDs 762 # will create types if necessary 763 # if self._PRW_type.GetData() is None: 764 if self._PRW_type.GetValue().strip() == u'': 765 self._PRW_type.SetBackgroundColour('pink') 766 self._PRW_type.SetFocus() 767 self._PRW_type.Refresh() 768 else: 769 self._PRW_type.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 770 self._PRW_type.Refresh() 771 772 if self._TCTRL_value.GetValue().strip() == u'': 773 self._TCTRL_value.SetBackgroundColour('pink') 774 self._TCTRL_value.SetFocus() 775 self._TCTRL_value.Refresh() 776 no_errors = False 777 else: 778 self._TCTRL_value.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 779 self._TCTRL_value.Refresh() 780 781 return no_errors
782 #------------------------------------------------------------ 783 from Gnumed.wxGladeWidgets import wxgIdentityEAPnl 784
785 -class cIdentityEAPnl(wxgIdentityEAPnl.wxgIdentityEAPnl, gmEditArea.cGenericEditAreaMixin):
786 """An edit area for editing/creating title/gender/dob/dod etc.""" 787
788 - def __init__(self, *args, **kwargs):
789 790 try: 791 data = kwargs['identity'] 792 del kwargs['identity'] 793 except KeyError: 794 data = None 795 796 wxgIdentityEAPnl.wxgIdentityEAPnl.__init__(self, *args, **kwargs) 797 gmEditArea.cGenericEditAreaMixin.__init__(self) 798 799 self.mode = 'new' 800 self.data = data 801 if data is not None: 802 self.mode = 'edit'
803 804 # self.__init_ui() 805 #---------------------------------------------------------------- 806 # def __init_ui(self): 807 # # adjust phrasewheels etc 808 #---------------------------------------------------------------- 809 # generic Edit Area mixin API 810 #----------------------------------------------------------------
811 - def _valid_for_save(self):
812 813 has_error = False 814 815 if self._PRW_gender.GetData() is None: 816 self._PRW_gender.SetFocus() 817 has_error = True 818 819 if not self._PRW_dob.is_valid_timestamp(): 820 val = self._PRW_dob.GetValue().strip() 821 gmDispatcher.send(signal = u'statustext', msg = _('Cannot parse <%s> into proper timestamp.') % val) 822 self._PRW_dob.SetBackgroundColour('pink') 823 self._PRW_dob.Refresh() 824 self._PRW_dob.SetFocus() 825 has_error = True 826 else: 827 self._PRW_dob.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 828 self._PRW_dob.Refresh() 829 830 if not self._PRW_dod.is_valid_timestamp(allow_empty = True): 831 gmDispatcher.send(signal = u'statustext', msg = _('Invalid date of death.')) 832 self._PRW_dod.SetFocus() 833 has_error = True 834 835 return (has_error is False)
836 #----------------------------------------------------------------
837 - def _save_as_new(self):
838 # not intended to be used 839 return False
840 #----------------------------------------------------------------
841 - def _save_as_update(self):
842 843 self.data['gender'] = self._PRW_gender.GetData() 844 845 if self._PRW_dob.GetValue().strip() == u'': 846 self.data['dob'] = None 847 else: 848 self.data['dob'] = self._PRW_dob.GetData().get_pydt() 849 850 self.data['title'] = gmTools.none_if(self._PRW_title.GetValue().strip(), u'') 851 self.data['deceased'] = self._PRW_dod.GetData() 852 self.data['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), u'') 853 854 self.data.save() 855 return True
856 #----------------------------------------------------------------
857 - def _refresh_as_new(self):
858 pass
859 #----------------------------------------------------------------
860 - def _refresh_from_existing(self):
861 862 self._LBL_info.SetLabel(u'ID: #%s' % ( 863 self.data.ID 864 # FIXME: add 'deleted' status 865 )) 866 self._PRW_dob.SetText ( 867 value = self.data.get_formatted_dob(format = '%Y-%m-%d %H:%M', encoding = gmI18N.get_encoding()), 868 data = self.data['dob'] 869 ) 870 if self.data['deceased'] is None: 871 val = u'' 872 else: 873 val = gmDateTime.pydt_strftime ( 874 self.data['deceased'], 875 format = '%Y-%m-%d %H:%M', 876 accuracy = gmDateTime.acc_minutes 877 ) 878 self._PRW_dod.SetText(value = val, data = self.data['deceased']) 879 self._PRW_gender.SetData(self.data['gender']) 880 #self._PRW_ethnicity.SetValue() 881 self._PRW_title.SetText(gmTools.coalesce(self.data['title'], u'')) 882 self._TCTRL_comment.SetValue(gmTools.coalesce(self.data['comment'], u''))
883 #----------------------------------------------------------------
885 pass
886 #------------------------------------------------------------ 887 from Gnumed.wxGladeWidgets import wxgNameGenderDOBEditAreaPnl 888
889 -class cNameGenderDOBEditAreaPnl(wxgNameGenderDOBEditAreaPnl.wxgNameGenderDOBEditAreaPnl):
890 """An edit area for editing/creating name/gender/dob. 891 892 Does NOT act on/listen to the current patient. 893 """
894 - def __init__(self, *args, **kwargs):
895 896 self.__name = kwargs['name'] 897 del kwargs['name'] 898 self.__identity = gmPerson.cIdentity(aPK_obj = self.__name['pk_identity']) 899 900 wxgNameGenderDOBEditAreaPnl.wxgNameGenderDOBEditAreaPnl.__init__(self, *args, **kwargs) 901 902 self.__register_interests() 903 self.refresh()
904 #-------------------------------------------------------- 905 # external API 906 #--------------------------------------------------------
907 - def refresh(self):
908 if self.__name is None: 909 return 910 911 self._PRW_title.SetText(gmTools.coalesce(self.__name['title'], u'')) 912 self._PRW_firstname.SetText(self.__name['firstnames']) 913 self._PRW_lastname.SetText(self.__name['lastnames']) 914 self._PRW_nick.SetText(gmTools.coalesce(self.__name['preferred'], u'')) 915 self._PRW_dob.SetText ( 916 value = self.__identity.get_formatted_dob(format = '%Y-%m-%d %H:%M', encoding = gmI18N.get_encoding()), 917 data = self.__identity['dob'] 918 ) 919 self._PRW_gender.SetData(self.__name['gender']) 920 self._CHBOX_active.SetValue(self.__name['active_name']) 921 if self.__identity['deceased'] is None: 922 val = u'' 923 else: 924 val = gmDateTime.pydt_strftime ( 925 self.__identity['deceased'], 926 format = '%Y-%m-%d %H:%M', 927 accuracy = gmDateTime.acc_minutes 928 ) 929 self._PRW_dod.SetText(value = val, data = self.__identity['deceased']) 930 self._TCTRL_comment.SetValue(gmTools.coalesce(self.__name['comment'], u''))
931 # FIXME: clear fields 932 # else: 933 # pass 934 #--------------------------------------------------------
935 - def save(self):
936 937 if not self.__valid_for_save(): 938 return False 939 940 self.__identity['gender'] = self._PRW_gender.GetData() 941 if self._PRW_dob.GetValue().strip() == u'': 942 self.__identity['dob'] = None 943 else: 944 self.__identity['dob'] = self._PRW_dob.GetData().get_pydt() 945 self.__identity['title'] = gmTools.none_if(self._PRW_title.GetValue().strip(), u'') 946 self.__identity['deceased'] = self._PRW_dod.GetData() 947 self.__identity.save_payload() 948 949 active = self._CHBOX_active.GetValue() 950 first = self._PRW_firstname.GetValue().strip() 951 last = self._PRW_lastname.GetValue().strip() 952 old_nick = self.__name['preferred'] 953 954 # is it a new name ? 955 old_name = self.__name['firstnames'] + self.__name['lastnames'] 956 if (first + last) != old_name: 957 self.__name = self.__identity.add_name(first, last, active) 958 959 self.__name['active_name'] = active 960 self.__name['preferred'] = gmTools.none_if(self._PRW_nick.GetValue().strip(), u'') 961 self.__name['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), u'') 962 963 self.__name.save_payload() 964 965 return True
966 #-------------------------------------------------------- 967 # event handling 968 #--------------------------------------------------------
969 - def __register_interests(self):
970 self._PRW_firstname.add_callback_on_lose_focus(self._on_name_set)
971 #--------------------------------------------------------
972 - def _on_name_set(self):
973 """Set the gender according to entered firstname. 974 975 Matches are fetched from existing records in backend. 976 """ 977 firstname = self._PRW_firstname.GetValue().strip() 978 if firstname == u'': 979 return True 980 rows, idx = gmPG2.run_ro_queries(queries = [{ 981 'cmd': u"select gender from dem.name_gender_map where name ilike %s", 982 'args': [firstname] 983 }]) 984 if len(rows) == 0: 985 return True 986 wx.CallAfter(self._PRW_gender.SetData, rows[0][0]) 987 return True
988 #-------------------------------------------------------- 989 # internal helpers 990 #--------------------------------------------------------
991 - def __valid_for_save(self):
992 993 has_error = False 994 995 if self._PRW_gender.GetData() is None: 996 self._PRW_gender.display_as_valid(False) 997 self._PRW_gender.SetFocus() 998 has_error = True 999 else: 1000 self._PRW_gender.display_as_valid(True) 1001 1002 if not self._PRW_dob.is_valid_timestamp(): 1003 val = self._PRW_dob.GetValue().strip() 1004 gmDispatcher.send(signal = u'statustext', msg = _('Cannot parse <%s> into proper timestamp.') % val) 1005 self._PRW_dob.display_as_valid(False) 1006 self._PRW_dob.SetFocus() 1007 has_error = True 1008 else: 1009 self._PRW_dob.display_as_valid(True) 1010 1011 if not self._PRW_dod.is_valid_timestamp(): 1012 gmDispatcher.send(signal = u'statustext', msg = _('Invalid date of death.')) 1013 self._PRW_dod.display_as_valid(False) 1014 self._PRW_dod.SetFocus() 1015 has_error = True 1016 else: 1017 self._PRW_dod.display_as_valid(True) 1018 1019 if self._PRW_lastname.GetValue().strip() == u'': 1020 self._PRW_lastname.display_as_valid(False) 1021 self._PRW_lastname.SetFocus() 1022 has_error = True 1023 else: 1024 self._PRW_lastname.display_as_valid(True) 1025 1026 if self._PRW_firstname.GetValue().strip() == u'': 1027 self._PRW_firstname.display_as_valid(False) 1028 self._PRW_firstname.SetFocus() 1029 has_error = True 1030 else: 1031 self._PRW_firstname.display_as_valid(True) 1032 1033 return (has_error is False)
1034 #------------------------------------------------------------ 1035 # list manager 1036 #------------------------------------------------------------
1037 -class cPersonNamesManagerPnl(gmListWidgets.cGenericListManagerPnl):
1038 """A list for managing a person's names. 1039 1040 Does NOT act on/listen to the current patient. 1041 """
1042 - def __init__(self, *args, **kwargs):
1043 1044 try: 1045 self.__identity = kwargs['identity'] 1046 del kwargs['identity'] 1047 except KeyError: 1048 self.__identity = None 1049 1050 gmListWidgets.cGenericListManagerPnl.__init__(self, *args, **kwargs) 1051 1052 self.new_callback = self._add_name 1053 self.edit_callback = self._edit_name 1054 self.delete_callback = self._del_name 1055 self.refresh_callback = self.refresh 1056 1057 self.__init_ui() 1058 self.refresh()
1059 #-------------------------------------------------------- 1060 # external API 1061 #--------------------------------------------------------
1062 - def refresh(self, *args, **kwargs):
1063 if self.__identity is None: 1064 self._LCTRL_items.set_string_items() 1065 return 1066 1067 names = self.__identity.get_names() 1068 self._LCTRL_items.set_string_items ( 1069 items = [ [ 1070 gmTools.bool2str(n['active_name'], 'X', ''), 1071 n['lastnames'], 1072 n['firstnames'], 1073 gmTools.coalesce(n['preferred'], u''), 1074 gmTools.coalesce(n['comment'], u'') 1075 ] for n in names ] 1076 ) 1077 self._LCTRL_items.set_column_widths() 1078 self._LCTRL_items.set_data(data = names)
1079 #-------------------------------------------------------- 1080 # internal helpers 1081 #--------------------------------------------------------
1082 - def __init_ui(self):
1083 self._LCTRL_items.set_columns(columns = [ 1084 _('Active'), 1085 _('Lastname'), 1086 _('Firstname(s)'), 1087 _('Preferred Name'), 1088 _('Comment') 1089 ]) 1090 self._BTN_edit.SetLabel(_('Clone and &edit'))
1091 #--------------------------------------------------------
1092 - def _add_name(self):
1093 ea = cNameGenderDOBEditAreaPnl(self, -1, name = self.__identity.get_active_name()) 1094 dlg = gmEditArea.cGenericEditAreaDlg(self, -1, edit_area = ea) 1095 dlg.SetTitle(_('Adding new name')) 1096 if dlg.ShowModal() == wx.ID_OK: 1097 dlg.Destroy() 1098 return True 1099 dlg.Destroy() 1100 return False
1101 #--------------------------------------------------------
1102 - def _edit_name(self, name):
1103 ea = cNameGenderDOBEditAreaPnl(self, -1, name = name) 1104 dlg = gmEditArea.cGenericEditAreaDlg(self, -1, edit_area = ea) 1105 dlg.SetTitle(_('Cloning name')) 1106 if dlg.ShowModal() == wx.ID_OK: 1107 dlg.Destroy() 1108 return True 1109 dlg.Destroy() 1110 return False
1111 #--------------------------------------------------------
1112 - def _del_name(self, name):
1113 1114 if len(self.__identity.get_names()) == 1: 1115 gmDispatcher.send(signal = u'statustext', msg = _('Cannot delete the only name of a person.'), beep = True) 1116 return False 1117 1118 go_ahead = gmGuiHelpers.gm_show_question ( 1119 _( 'It is often advisable to keep old names around and\n' 1120 'just create a new "currently active" name.\n' 1121 '\n' 1122 'This allows finding the patient by both the old\n' 1123 'and the new name (think before/after marriage).\n' 1124 '\n' 1125 'Do you still want to really delete\n' 1126 "this name from the patient ?" 1127 ), 1128 _('Deleting name') 1129 ) 1130 if not go_ahead: 1131 return False 1132 1133 self.__identity.delete_name(name = name) 1134 return True
1135 #-------------------------------------------------------- 1136 # properties 1137 #--------------------------------------------------------
1138 - def _get_identity(self):
1139 return self.__identity
1140
1141 - def _set_identity(self, identity):
1142 self.__identity = identity 1143 self.refresh()
1144 1145 identity = property(_get_identity, _set_identity)
1146 #------------------------------------------------------------
1147 -class cPersonIDsManagerPnl(gmListWidgets.cGenericListManagerPnl):
1148 """A list for managing a person's external IDs. 1149 1150 Does NOT act on/listen to the current patient. 1151 """
1152 - def __init__(self, *args, **kwargs):
1153 1154 try: 1155 self.__identity = kwargs['identity'] 1156 del kwargs['identity'] 1157 except KeyError: 1158 self.__identity = None 1159 1160 gmListWidgets.cGenericListManagerPnl.__init__(self, *args, **kwargs) 1161 1162 self.new_callback = self._add_id 1163 self.edit_callback = self._edit_id 1164 self.delete_callback = self._del_id 1165 self.refresh_callback = self.refresh 1166 1167 self.__init_ui() 1168 self.refresh()
1169 #-------------------------------------------------------- 1170 # external API 1171 #--------------------------------------------------------
1172 - def refresh(self, *args, **kwargs):
1173 if self.__identity is None: 1174 self._LCTRL_items.set_string_items() 1175 return 1176 1177 ids = self.__identity.get_external_ids() 1178 self._LCTRL_items.set_string_items ( 1179 items = [ [ 1180 i['name'], 1181 i['value'], 1182 gmTools.coalesce(i['issuer'], u''), 1183 gmTools.coalesce(i['comment'], u'') 1184 ] for i in ids 1185 ] 1186 ) 1187 self._LCTRL_items.set_column_widths() 1188 self._LCTRL_items.set_data(data = ids)
1189 #-------------------------------------------------------- 1190 # internal helpers 1191 #--------------------------------------------------------
1192 - def __init_ui(self):
1193 self._LCTRL_items.set_columns(columns = [ 1194 _('ID type'), 1195 _('Value'), 1196 _('Issuer'), 1197 _('Comment') 1198 ])
1199 #--------------------------------------------------------
1200 - def _add_id(self):
1201 ea = cExternalIDEditAreaPnl(self, -1) 1202 ea.identity = self.__identity 1203 dlg = gmEditArea.cGenericEditAreaDlg(self, -1, edit_area = ea) 1204 dlg.SetTitle(_('Adding new external ID')) 1205 if dlg.ShowModal() == wx.ID_OK: 1206 dlg.Destroy() 1207 return True 1208 dlg.Destroy() 1209 return False
1210 #--------------------------------------------------------
1211 - def _edit_id(self, ext_id):
1212 ea = cExternalIDEditAreaPnl(self, -1, external_id = ext_id) 1213 ea.identity = self.__identity 1214 dlg = gmEditArea.cGenericEditAreaDlg(self, -1, edit_area = ea) 1215 dlg.SetTitle(_('Editing external ID')) 1216 if dlg.ShowModal() == wx.ID_OK: 1217 dlg.Destroy() 1218 return True 1219 dlg.Destroy() 1220 return False
1221 #--------------------------------------------------------
1222 - def _del_id(self, ext_id):
1223 go_ahead = gmGuiHelpers.gm_show_question ( 1224 _( 'Do you really want to delete this\n' 1225 'external ID from the patient ?'), 1226 _('Deleting external ID') 1227 ) 1228 if not go_ahead: 1229 return False 1230 self.__identity.delete_external_id(pk_ext_id = ext_id['pk_id']) 1231 return True
1232 #-------------------------------------------------------- 1233 # properties 1234 #--------------------------------------------------------
1235 - def _get_identity(self):
1236 return self.__identity
1237
1238 - def _set_identity(self, identity):
1239 self.__identity = identity 1240 self.refresh()
1241 1242 identity = property(_get_identity, _set_identity)
1243 #------------------------------------------------------------ 1244 # integrated panels 1245 #------------------------------------------------------------ 1246 from Gnumed.wxGladeWidgets import wxgPersonIdentityManagerPnl 1247
1248 -class cPersonIdentityManagerPnl(wxgPersonIdentityManagerPnl.wxgPersonIdentityManagerPnl):
1249 """A panel for editing identity data for a person. 1250 1251 - provides access to: 1252 - name 1253 - external IDs 1254 1255 Does NOT act on/listen to the current patient. 1256 """
1257 - def __init__(self, *args, **kwargs):
1258 1259 wxgPersonIdentityManagerPnl.wxgPersonIdentityManagerPnl.__init__(self, *args, **kwargs) 1260 1261 self.__identity = None 1262 self.refresh()
1263 #-------------------------------------------------------- 1264 # external API 1265 #--------------------------------------------------------
1266 - def refresh(self):
1267 self._PNL_names.identity = self.__identity 1268 self._PNL_ids.identity = self.__identity 1269 # this is an Edit Area: 1270 self._PNL_identity.mode = 'new' 1271 self._PNL_identity.data = self.__identity 1272 if self.__identity is not None: 1273 self._PNL_identity.mode = 'edit'
1274 #-------------------------------------------------------- 1275 # properties 1276 #--------------------------------------------------------
1277 - def _get_identity(self):
1278 return self.__identity
1279
1280 - def _set_identity(self, identity):
1281 self.__identity = identity 1282 self.refresh()
1283 1284 identity = property(_get_identity, _set_identity) 1285 #-------------------------------------------------------- 1286 # event handlers 1287 #--------------------------------------------------------
1289 if not self._PNL_identity.save(): 1290 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save identity. Incomplete information.'), beep = True)
1291 #--------------------------------------------------------
1292 - def _on_reload_identity_button_pressed(self, event):
1293 self._PNL_identity.refresh()
1294 1295 #============================================================ 1296 from Gnumed.wxGladeWidgets import wxgPersonSocialNetworkManagerPnl 1297
1298 -class cPersonSocialNetworkManagerPnl(wxgPersonSocialNetworkManagerPnl.wxgPersonSocialNetworkManagerPnl):
1299 - def __init__(self, *args, **kwargs):
1300 1301 wxgPersonSocialNetworkManagerPnl.wxgPersonSocialNetworkManagerPnl.__init__(self, *args, **kwargs) 1302 1303 self.__identity = None 1304 self._PRW_provider.selection_only = False 1305 self.refresh()
1306 #-------------------------------------------------------- 1307 # external API 1308 #--------------------------------------------------------
1309 - def refresh(self):
1310 1311 tt = _("Link another person in this database as the emergency contact.") 1312 1313 if self.__identity is None: 1314 self._TCTRL_er_contact.SetValue(u'') 1315 self._TCTRL_person.person = None 1316 self._TCTRL_person.SetToolTipString(tt) 1317 1318 self._PRW_provider.SetText(value = u'', data = None) 1319 return 1320 1321 self._TCTRL_er_contact.SetValue(gmTools.coalesce(self.__identity['emergency_contact'], u'')) 1322 if self.__identity['pk_emergency_contact'] is not None: 1323 ident = gmPerson.cIdentity(aPK_obj = self.__identity['pk_emergency_contact']) 1324 self._TCTRL_person.person = ident 1325 tt = u'%s\n\n%s\n\n%s' % ( 1326 tt, 1327 ident['description_gender'], 1328 u'\n'.join([ 1329 u'%s: %s%s' % ( 1330 c['l10n_comm_type'], 1331 c['url'], 1332 gmTools.bool2subst(c['is_confidential'], _(' (confidential !)'), u'', u'') 1333 ) 1334 for c in ident.get_comm_channels() 1335 ]) 1336 ) 1337 else: 1338 self._TCTRL_person.person = None 1339 1340 self._TCTRL_person.SetToolTipString(tt) 1341 1342 if self.__identity['pk_primary_provider'] is None: 1343 self._PRW_provider.SetText(value = u'', data = None) 1344 else: 1345 self._PRW_provider.SetData(data = self.__identity['pk_primary_provider'])
1346 #-------------------------------------------------------- 1347 # properties 1348 #--------------------------------------------------------
1349 - def _get_identity(self):
1350 return self.__identity
1351
1352 - def _set_identity(self, identity):
1353 self.__identity = identity 1354 self.refresh()
1355 1356 identity = property(_get_identity, _set_identity) 1357 #-------------------------------------------------------- 1358 # event handlers 1359 #--------------------------------------------------------
1360 - def _on_save_button_pressed(self, event):
1361 if self.__identity is not None: 1362 self.__identity['emergency_contact'] = self._TCTRL_er_contact.GetValue().strip() 1363 if self._TCTRL_person.person is not None: 1364 self.__identity['pk_emergency_contact'] = self._TCTRL_person.person.ID 1365 if self._PRW_provider.GetValue().strip == u'': 1366 self.__identity['pk_primary_provider'] = None 1367 else: 1368 self.__identity['pk_primary_provider'] = self._PRW_provider.GetData() 1369 1370 self.__identity.save() 1371 1372 event.Skip()
1373 #--------------------------------------------------------
1374 - def _on_remove_contact_button_pressed(self, event):
1375 event.Skip() 1376 1377 if self.__identity is None: 1378 return 1379 1380 self._TCTRL_person.person = None 1381 1382 self.__identity['pk_emergency_contact'] = None 1383 self.__identity.save()
1384 #--------------------------------------------------------
1385 - def _on_button_activate_contact_pressed(self, event):
1386 ident = self._TCTRL_person.person 1387 if ident is not None: 1388 from Gnumed.wxpython import gmPatSearchWidgets 1389 gmPatSearchWidgets.set_active_patient(patient = ident, forced_reload = False) 1390 1391 event.Skip()
1392 #============================================================ 1393 # new-patient widgets 1394 #============================================================
1395 -def create_new_person(parent=None, activate=False):
1396 1397 dbcfg = gmCfg.cCfgSQL() 1398 1399 def_region = dbcfg.get2 ( 1400 option = u'person.create.default_region', 1401 workplace = gmSurgery.gmCurrentPractice().active_workplace, 1402 bias = u'user' 1403 ) 1404 def_country = None 1405 1406 if def_region is None: 1407 def_country = dbcfg.get2 ( 1408 option = u'person.create.default_country', 1409 workplace = gmSurgery.gmCurrentPractice().active_workplace, 1410 bias = u'user' 1411 ) 1412 else: 1413 countries = gmDemographicRecord.get_country_for_region(region = def_region) 1414 if len(countries) == 1: 1415 def_country = countries[0]['l10n_country'] 1416 1417 if parent is None: 1418 parent = wx.GetApp().GetTopWindow() 1419 1420 ea = cNewPatientEAPnl(parent = parent, id = -1, country = def_country, region = def_region) 1421 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = True) 1422 dlg.SetTitle(_('Adding new person')) 1423 ea._PRW_lastname.SetFocus() 1424 result = dlg.ShowModal() 1425 pat = ea.data 1426 dlg.Destroy() 1427 1428 if result != wx.ID_OK: 1429 return False 1430 1431 _log.debug('created new person [%s]', pat.ID) 1432 1433 if activate: 1434 from Gnumed.wxpython import gmPatSearchWidgets 1435 gmPatSearchWidgets.set_active_patient(patient = pat) 1436 1437 gmDispatcher.send(signal = 'display_widget', name = 'gmNotebookedPatientEditionPlugin') 1438 1439 return True
1440 #============================================================ 1441 from Gnumed.wxGladeWidgets import wxgNewPatientEAPnl 1442
1443 -class cNewPatientEAPnl(wxgNewPatientEAPnl.wxgNewPatientEAPnl, gmEditArea.cGenericEditAreaMixin):
1444
1445 - def __init__(self, *args, **kwargs):
1446 1447 try: 1448 self.default_region = kwargs['region'] 1449 del kwargs['region'] 1450 except KeyError: 1451 self.default_region = None 1452 1453 try: 1454 self.default_country = kwargs['country'] 1455 del kwargs['country'] 1456 except KeyError: 1457 self.default_country = None 1458 1459 wxgNewPatientEAPnl.wxgNewPatientEAPnl.__init__(self, *args, **kwargs) 1460 gmEditArea.cGenericEditAreaMixin.__init__(self) 1461 1462 self.mode = 'new' 1463 self.data = None 1464 self._address = None 1465 1466 self.__init_ui() 1467 self.__register_interests()
1468 #---------------------------------------------------------------- 1469 # internal helpers 1470 #----------------------------------------------------------------
1471 - def __init_ui(self):
1472 self._PRW_lastname.final_regex = '.+' 1473 self._PRW_firstnames.final_regex = '.+' 1474 self._PRW_address_searcher.selection_only = False 1475 1476 # only if we would support None on selection_only's: 1477 # self._PRW_external_id_type.selection_only = True 1478 1479 if self.default_country is not None: 1480 self._PRW_country.SetText(value = self.default_country) 1481 1482 if self.default_region is not None: 1483 self._PRW_region.SetText(value = self.default_region)
1484 #----------------------------------------------------------------
1485 - def __perhaps_invalidate_address_searcher(self, ctrl=None, field=None):
1486 1487 adr = self._PRW_address_searcher.get_address() 1488 if adr is None: 1489 return True 1490 1491 if ctrl.GetValue().strip() != adr[field]: 1492 wx.CallAfter(self._PRW_address_searcher.SetText, value = u'', data = None) 1493 return True 1494 1495 return False
1496 #----------------------------------------------------------------
1498 adr = self._PRW_address_searcher.get_address() 1499 if adr is None: 1500 return True 1501 1502 self._PRW_zip.SetText(value = adr['postcode'], data = adr['postcode']) 1503 1504 self._PRW_street.SetText(value = adr['street'], data = adr['street']) 1505 self._PRW_street.set_context(context = u'zip', val = adr['postcode']) 1506 1507 self._TCTRL_number.SetValue(adr['number']) 1508 1509 self._PRW_urb.SetText(value = adr['urb'], data = adr['urb']) 1510 self._PRW_urb.set_context(context = u'zip', val = adr['postcode']) 1511 1512 self._PRW_region.SetText(value = adr['l10n_state'], data = adr['code_state']) 1513 self._PRW_region.set_context(context = u'zip', val = adr['postcode']) 1514 1515 self._PRW_country.SetText(value = adr['l10n_country'], data = adr['code_country']) 1516 self._PRW_country.set_context(context = u'zip', val = adr['postcode'])
1517 #----------------------------------------------------------------
1518 - def __identity_valid_for_save(self):
1519 error = False 1520 1521 # name fields 1522 if self._PRW_lastname.GetValue().strip() == u'': 1523 error = True 1524 gmDispatcher.send(signal = 'statustext', msg = _('Must enter lastname.')) 1525 self._PRW_lastname.display_as_valid(False) 1526 else: 1527 self._PRW_lastname.display_as_valid(True) 1528 1529 if self._PRW_firstnames.GetValue().strip() == '': 1530 error = True 1531 gmDispatcher.send(signal = 'statustext', msg = _('Must enter first name.')) 1532 self._PRW_firstnames.display_as_valid(False) 1533 else: 1534 self._PRW_firstnames.display_as_valid(True) 1535 1536 # gender 1537 if self._PRW_gender.GetData() is None: 1538 error = True 1539 gmDispatcher.send(signal = 'statustext', msg = _('Must select gender.')) 1540 self._PRW_gender.display_as_valid(False) 1541 else: 1542 self._PRW_gender.display_as_valid(True) 1543 1544 # dob validation 1545 # test this last so we can check empty field as last barrier against save 1546 # 1) valid timestamp ? 1547 if self._PRW_dob.is_valid_timestamp(allow_empty = False): # properly colors the field 1548 dob = self._PRW_dob.date 1549 # but year also usable ? 1550 msg = None 1551 if dob.year < 1900: 1552 msg = _( 1553 'DOB: %s\n' 1554 '\n' 1555 'While this is a valid point in time Python does\n' 1556 'not know how to deal with it.\n' 1557 '\n' 1558 'We suggest using January 1st 1901 instead and adding\n' 1559 'the true date of birth to the patient comment.\n' 1560 '\n' 1561 'Sorry for the inconvenience %s' 1562 ) % (dob, gmTools.u_frowning_face) 1563 elif dob > gmDateTime.pydt_now_here(): 1564 msg = _( 1565 'DOB: %s\n' 1566 '\n' 1567 'Date of birth in the future !' 1568 ) % dob 1569 1570 if msg is not None: 1571 error = True 1572 gmGuiHelpers.gm_show_error ( 1573 msg, 1574 _('Registering new person') 1575 ) 1576 self._PRW_dob.display_as_valid(False) 1577 self._PRW_dob.SetFocus() 1578 # 2) invalid timestamp ? 1579 else: 1580 # is this the only error ? 1581 if error is False: 1582 # is it empty rather than invalid ? 1583 if self._PRW_dob.GetValue().strip() == u'': 1584 # maybe even allow empty DOB ? 1585 allow_empty_dob = gmGuiHelpers.gm_show_question ( 1586 _( 1587 'Are you sure you want to register this person\n' 1588 'without a valid date of birth ?\n' 1589 '\n' 1590 'This can be useful for temporary staff members\n' 1591 'but will provoke nag screens if this person\n' 1592 'becomes a patient.\n' 1593 ), 1594 _('Registering new person') 1595 ) 1596 if allow_empty_dob: 1597 self._PRW_dob.display_as_valid(True) 1598 else: 1599 error = True 1600 self._PRW_dob.SetFocus() 1601 1602 # TOB validation if non-empty 1603 # if self._TCTRL_tob.GetValue().strip() != u'': 1604 1605 return (not error)
1606 #----------------------------------------------------------------
1607 - def __address_valid_for_save(self, empty_address_is_valid=False):
1608 1609 # existing address ? if so set other fields 1610 if self._PRW_address_searcher.GetData() is not None: 1611 wx.CallAfter(self.__set_fields_from_address_searcher) 1612 return True 1613 1614 # must either all contain something or none of them 1615 fields_to_fill = ( 1616 self._TCTRL_number, 1617 self._PRW_zip, 1618 self._PRW_street, 1619 self._PRW_urb, 1620 self._PRW_region, 1621 self._PRW_country 1622 ) 1623 no_of_filled_fields = 0 1624 1625 for field in fields_to_fill: 1626 if field.GetValue().strip() != u'': 1627 no_of_filled_fields += 1 1628 field.SetBackgroundColour(gmPhraseWheel.color_prw_valid) 1629 field.Refresh() 1630 1631 # empty address ? 1632 if no_of_filled_fields == 0: 1633 if empty_address_is_valid: 1634 return True 1635 else: 1636 return None 1637 1638 # incompletely filled address ? 1639 if no_of_filled_fields != len(fields_to_fill): 1640 for field in fields_to_fill: 1641 if field.GetValue().strip() == u'': 1642 field.SetBackgroundColour(gmPhraseWheel.color_prw_invalid) 1643 field.SetFocus() 1644 field.Refresh() 1645 msg = _('To properly create an address, all the related fields must be filled in.') 1646 gmGuiHelpers.gm_show_error(msg, _('Required fields')) 1647 return False 1648 1649 # fields which must contain a selected item 1650 # FIXME: they must also contain an *acceptable combination* which 1651 # FIXME: can only be tested against the database itself ... 1652 strict_fields = ( 1653 self._PRW_region, 1654 self._PRW_country 1655 ) 1656 error = False 1657 for field in strict_fields: 1658 if field.GetData() is None: 1659 error = True 1660 field.SetBackgroundColour(gmPhraseWheel.color_prw_invalid) 1661 field.SetFocus() 1662 else: 1663 field.SetBackgroundColour(gmPhraseWheel.color_prw_valid) 1664 field.Refresh() 1665 1666 if error: 1667 msg = _('This field must contain an item selected from the dropdown list.') 1668 gmGuiHelpers.gm_show_error(msg, _('Required fields')) 1669 return False 1670 1671 return True
1672 #----------------------------------------------------------------
1673 - def __register_interests(self):
1674 1675 # identity 1676 self._PRW_firstnames.add_callback_on_lose_focus(self._on_leaving_firstname) 1677 1678 # address 1679 self._PRW_address_searcher.add_callback_on_lose_focus(self._on_leaving_adress_searcher) 1680 1681 # invalidate address searcher when any field edited 1682 self._PRW_street.add_callback_on_lose_focus(self._invalidate_address_searcher) 1683 wx.EVT_KILL_FOCUS(self._TCTRL_number, self._invalidate_address_searcher) 1684 self._PRW_urb.add_callback_on_lose_focus(self._invalidate_address_searcher) 1685 self._PRW_region.add_callback_on_lose_focus(self._invalidate_address_searcher) 1686 1687 self._PRW_zip.add_callback_on_lose_focus(self._on_leaving_zip) 1688 self._PRW_country.add_callback_on_lose_focus(self._on_leaving_country)
1689 #---------------------------------------------------------------- 1690 # event handlers 1691 #----------------------------------------------------------------
1692 - def _on_leaving_firstname(self):
1693 """Set the gender according to entered firstname. 1694 1695 Matches are fetched from existing records in backend. 1696 """ 1697 # only set if not already set so as to not 1698 # overwrite a change by the user 1699 if self._PRW_gender.GetData() is not None: 1700 return True 1701 1702 firstname = self._PRW_firstnames.GetValue().strip() 1703 if firstname == u'': 1704 return True 1705 1706 gender = gmPerson.map_firstnames2gender(firstnames = firstname) 1707 if gender is None: 1708 return True 1709 1710 wx.CallAfter(self._PRW_gender.SetData, gender) 1711 return True
1712 #----------------------------------------------------------------
1713 - def _on_leaving_zip(self):
1714 self.__perhaps_invalidate_address_searcher(self._PRW_zip, 'postcode') 1715 1716 zip_code = gmTools.none_if(self._PRW_zip.GetValue().strip(), u'') 1717 self._PRW_street.set_context(context = u'zip', val = zip_code) 1718 self._PRW_urb.set_context(context = u'zip', val = zip_code) 1719 self._PRW_region.set_context(context = u'zip', val = zip_code) 1720 self._PRW_country.set_context(context = u'zip', val = zip_code) 1721 1722 return True
1723 #----------------------------------------------------------------
1724 - def _on_leaving_country(self):
1725 self.__perhaps_invalidate_address_searcher(self._PRW_country, 'l10n_country') 1726 1727 country = gmTools.none_if(self._PRW_country.GetValue().strip(), u'') 1728 self._PRW_region.set_context(context = u'country', val = country) 1729 1730 return True
1731 #----------------------------------------------------------------
1732 - def _invalidate_address_searcher(self, *args, **kwargs):
1733 mapping = [ 1734 (self._PRW_street, 'street'), 1735 (self._TCTRL_number, 'number'), 1736 (self._PRW_urb, 'urb'), 1737 (self._PRW_region, 'l10n_state') 1738 ] 1739 1740 # loop through fields and invalidate address searcher if different 1741 for ctrl, field in mapping: 1742 if self.__perhaps_invalidate_address_searcher(ctrl, field): 1743 return True 1744 1745 return True
1746 #----------------------------------------------------------------
1748 adr = self._PRW_address_searcher.get_address() 1749 if adr is None: 1750 return True 1751 1752 wx.CallAfter(self.__set_fields_from_address_searcher) 1753 return True
1754 #---------------------------------------------------------------- 1755 # generic Edit Area mixin API 1756 #----------------------------------------------------------------
1757 - def _valid_for_save(self):
1758 return (self.__identity_valid_for_save() and self.__address_valid_for_save(empty_address_is_valid = True))
1759 #----------------------------------------------------------------
1760 - def _save_as_new(self):
1761 1762 # identity 1763 new_identity = gmPerson.create_identity ( 1764 gender = self._PRW_gender.GetData(), 1765 dob = self._PRW_dob.GetData(), 1766 lastnames = self._PRW_lastname.GetValue().strip(), 1767 firstnames = self._PRW_firstnames.GetValue().strip() 1768 ) 1769 _log.debug('identity created: %s' % new_identity) 1770 1771 new_identity['title'] = gmTools.none_if(self._PRW_title.GetValue().strip()) 1772 new_identity.set_nickname(nickname = gmTools.none_if(self._PRW_nickname.GetValue().strip(), u'')) 1773 #TOB 1774 new_identity.save() 1775 1776 name = new_identity.get_active_name() 1777 name['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), u'') 1778 name.save() 1779 1780 # address 1781 is_valid = self.__address_valid_for_save(empty_address_is_valid = False) 1782 if is_valid is True: 1783 # because we currently only check for non-emptiness 1784 # we must still deal with database errors 1785 try: 1786 new_identity.link_address ( 1787 number = self._TCTRL_number.GetValue().strip(), 1788 street = self._PRW_street.GetValue().strip(), 1789 postcode = self._PRW_zip.GetValue().strip(), 1790 urb = self._PRW_urb.GetValue().strip(), 1791 state = self._PRW_region.GetData(), 1792 country = self._PRW_country.GetData() 1793 ) 1794 except gmPG2.dbapi.InternalError: 1795 _log.debug('number: >>%s<<', self._TCTRL_number.GetValue().strip()) 1796 _log.debug('street: >>%s<<', self._PRW_street.GetValue().strip()) 1797 _log.debug('postcode: >>%s<<', self._PRW_zip.GetValue().strip()) 1798 _log.debug('urb: >>%s<<', self._PRW_urb.GetValue().strip()) 1799 _log.debug('state: >>%s<<', self._PRW_region.GetData().strip()) 1800 _log.debug('country: >>%s<<', self._PRW_country.GetData().strip()) 1801 _log.exception('cannot link address') 1802 gmGuiHelpers.gm_show_error ( 1803 aTitle = _('Saving address'), 1804 aMessage = _( 1805 'Cannot save this address.\n' 1806 '\n' 1807 'You will have to add it via the Demographics plugin.\n' 1808 ) 1809 ) 1810 elif is_valid is False: 1811 gmGuiHelpers.gm_show_error ( 1812 aTitle = _('Saving address'), 1813 aMessage = _( 1814 'Address not saved.\n' 1815 '\n' 1816 'You will have to add it via the Demographics plugin.\n' 1817 ) 1818 ) 1819 # else it is None which means empty address which we ignore 1820 1821 # phone 1822 new_identity.link_comm_channel ( 1823 comm_medium = u'homephone', 1824 url = gmTools.none_if(self._TCTRL_phone.GetValue().strip(), u''), 1825 is_confidential = False 1826 ) 1827 1828 # external ID 1829 pk_type = self._PRW_external_id_type.GetData() 1830 id_value = self._TCTRL_external_id_value.GetValue().strip() 1831 if (pk_type is not None) and (id_value != u''): 1832 new_identity.add_external_id(value = id_value, pk_type = pk_type) 1833 1834 # occupation 1835 new_identity.link_occupation ( 1836 occupation = gmTools.none_if(self._PRW_occupation.GetValue().strip(), u'') 1837 ) 1838 1839 self.data = new_identity 1840 return True
1841 #----------------------------------------------------------------
1842 - def _save_as_update(self):
1843 raise NotImplementedError('[%s]: not expected to be used' % self.__class__.__name__)
1844 #----------------------------------------------------------------
1845 - def _refresh_as_new(self):
1846 # FIXME: button "empty out" 1847 return
1848 #----------------------------------------------------------------
1849 - def _refresh_from_existing(self):
1850 return # there is no forward button so nothing to do here
1851 #----------------------------------------------------------------
1853 raise NotImplementedError('[%s]: not expected to be used' % self.__class__.__name__)
1854 1855 #============================================================ 1856 # patient demographics editing classes 1857 #============================================================
1858 -class cPersonDemographicsEditorNb(wx.Notebook):
1859 """Notebook displaying demographics editing pages: 1860 1861 - Identity 1862 - Contacts (addresses, phone numbers, etc) 1863 - Social network (significant others, GP, etc) 1864 1865 Does NOT act on/listen to the current patient. 1866 """ 1867 #--------------------------------------------------------
1868 - def __init__(self, parent, id):
1869 1870 wx.Notebook.__init__ ( 1871 self, 1872 parent = parent, 1873 id = id, 1874 style = wx.NB_TOP | wx.NB_MULTILINE | wx.NO_BORDER, 1875 name = self.__class__.__name__ 1876 ) 1877 1878 self.__identity = None 1879 self.__do_layout() 1880 self.SetSelection(0)
1881 #-------------------------------------------------------- 1882 # public API 1883 #--------------------------------------------------------
1884 - def refresh(self):
1885 """Populate fields in pages with data from model.""" 1886 for page_idx in range(self.GetPageCount()): 1887 page = self.GetPage(page_idx) 1888 page.identity = self.__identity 1889 1890 return True
1891 #-------------------------------------------------------- 1892 # internal API 1893 #--------------------------------------------------------
1894 - def __do_layout(self):
1895 """Build patient edition notebook pages.""" 1896 1897 # contacts page 1898 new_page = gmPersonContactWidgets.cPersonContactsManagerPnl(self, -1) 1899 new_page.identity = self.__identity 1900 self.AddPage ( 1901 page = new_page, 1902 text = _('Contacts'), 1903 select = True 1904 ) 1905 1906 # identity page 1907 new_page = cPersonIdentityManagerPnl(self, -1) 1908 new_page.identity = self.__identity 1909 self.AddPage ( 1910 page = new_page, 1911 text = _('Identity'), 1912 select = False 1913 ) 1914 1915 # social network page 1916 new_page = cPersonSocialNetworkManagerPnl(self, -1) 1917 new_page.identity = self.__identity 1918 self.AddPage ( 1919 page = new_page, 1920 text = _('Social network'), 1921 select = False 1922 )
1923 #-------------------------------------------------------- 1924 # properties 1925 #--------------------------------------------------------
1926 - def _get_identity(self):
1927 return self.__identity
1928
1929 - def _set_identity(self, identity):
1930 self.__identity = identity
1931 1932 identity = property(_get_identity, _set_identity)
1933 #============================================================ 1934 # old occupation widgets 1935 #============================================================ 1936 # FIXME: support multiple occupations 1937 # FIXME: redo with wxGlade 1938
1939 -class cPatOccupationsPanel(wx.Panel):
1940 """Page containing patient occupations edition fields. 1941 """
1942 - def __init__(self, parent, id, ident=None):
1943 """ 1944 Creates a new instance of BasicPatDetailsPage 1945 @param parent - The parent widget 1946 @type parent - A wx.Window instance 1947 @param id - The widget id 1948 @type id - An integer 1949 """ 1950 wx.Panel.__init__(self, parent, id) 1951 self.__ident = ident 1952 self.__do_layout()
1953 #--------------------------------------------------------
1954 - def __do_layout(self):
1955 PNL_form = wx.Panel(self, -1) 1956 # occupation 1957 STT_occupation = wx.StaticText(PNL_form, -1, _('Occupation')) 1958 self.PRW_occupation = cOccupationPhraseWheel(parent = PNL_form, id = -1) 1959 self.PRW_occupation.SetToolTipString(_("primary occupation of the patient")) 1960 # known since 1961 STT_occupation_updated = wx.StaticText(PNL_form, -1, _('Last updated')) 1962 self.TTC_occupation_updated = wx.TextCtrl(PNL_form, -1, style = wx.TE_READONLY) 1963 1964 # layout input widgets 1965 SZR_input = wx.FlexGridSizer(cols = 2, rows = 5, vgap = 4, hgap = 4) 1966 SZR_input.AddGrowableCol(1) 1967 SZR_input.Add(STT_occupation, 0, wx.SHAPED) 1968 SZR_input.Add(self.PRW_occupation, 1, wx.EXPAND) 1969 SZR_input.Add(STT_occupation_updated, 0, wx.SHAPED) 1970 SZR_input.Add(self.TTC_occupation_updated, 1, wx.EXPAND) 1971 PNL_form.SetSizerAndFit(SZR_input) 1972 1973 # layout page 1974 SZR_main = wx.BoxSizer(wx.VERTICAL) 1975 SZR_main.Add(PNL_form, 1, wx.EXPAND) 1976 self.SetSizer(SZR_main)
1977 #--------------------------------------------------------
1978 - def set_identity(self, identity):
1979 return self.refresh(identity=identity)
1980 #--------------------------------------------------------
1981 - def refresh(self, identity=None):
1982 if identity is not None: 1983 self.__ident = identity 1984 jobs = self.__ident.get_occupations() 1985 if len(jobs) > 0: 1986 self.PRW_occupation.SetText(jobs[0]['l10n_occupation']) 1987 self.TTC_occupation_updated.SetValue(jobs[0]['modified_when'].strftime('%m/%Y')) 1988 return True
1989 #--------------------------------------------------------
1990 - def save(self):
1991 if self.PRW_occupation.IsModified(): 1992 new_job = self.PRW_occupation.GetValue().strip() 1993 jobs = self.__ident.get_occupations() 1994 for job in jobs: 1995 if job['l10n_occupation'] == new_job: 1996 continue 1997 self.__ident.unlink_occupation(occupation = job['l10n_occupation']) 1998 self.__ident.link_occupation(occupation = new_job) 1999 return True
2000 #============================================================
2001 -class cNotebookedPatEditionPanel(wx.Panel, gmRegetMixin.cRegetOnPaintMixin):
2002 """Patient demographics plugin for main notebook. 2003 2004 Hosts another notebook with pages for Identity, Contacts, etc. 2005 2006 Acts on/listens to the currently active patient. 2007 """ 2008 #--------------------------------------------------------
2009 - def __init__(self, parent, id):
2010 wx.Panel.__init__ (self, parent = parent, id = id, style = wx.NO_BORDER) 2011 gmRegetMixin.cRegetOnPaintMixin.__init__(self) 2012 self.__do_layout() 2013 self.__register_interests()
2014 #-------------------------------------------------------- 2015 # public API 2016 #-------------------------------------------------------- 2017 #-------------------------------------------------------- 2018 # internal helpers 2019 #--------------------------------------------------------
2020 - def __do_layout(self):
2021 """Arrange widgets.""" 2022 self.__patient_notebook = cPersonDemographicsEditorNb(self, -1) 2023 2024 szr_main = wx.BoxSizer(wx.VERTICAL) 2025 szr_main.Add(self.__patient_notebook, 1, wx.EXPAND) 2026 self.SetSizerAndFit(szr_main)
2027 #-------------------------------------------------------- 2028 # event handling 2029 #--------------------------------------------------------
2030 - def __register_interests(self):
2031 gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._on_pre_patient_selection) 2032 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._on_post_patient_selection)
2033 #--------------------------------------------------------
2034 - def _on_pre_patient_selection(self):
2035 self._schedule_data_reget()
2036 #--------------------------------------------------------
2037 - def _on_post_patient_selection(self):
2038 self._schedule_data_reget()
2039 #-------------------------------------------------------- 2040 # reget mixin API 2041 #--------------------------------------------------------
2042 - def _populate_with_data(self):
2043 """Populate fields in pages with data from model.""" 2044 pat = gmPerson.gmCurrentPatient() 2045 if pat.connected: 2046 self.__patient_notebook.identity = pat 2047 else: 2048 self.__patient_notebook.identity = None 2049 self.__patient_notebook.refresh() 2050 return True
2051 2052 2053 #============================================================ 2054 #============================================================ 2055 #============================================================ 2056 #============================================================ 2057 # outdated, delete soon: 2058 # new-patient wizard classes 2059 #============================================================
2060 -class cBasicPatDetailsPage(wx.wizard.WizardPageSimple):
2061 """ 2062 Wizard page for entering patient's basic demographic information 2063 """ 2064 2065 form_fields = ( 2066 'firstnames', 'lastnames', 'nick', 'dob', 'gender', 'title', 'occupation', 2067 'address_number', 'zip_code', 'street', 'town', 'state', 'country', 'phone', 'comment' 2068 ) 2069
2070 - def __init__(self, parent, title):
2071 """ 2072 Creates a new instance of BasicPatDetailsPage 2073 @param parent - The parent widget 2074 @type parent - A wx.Window instance 2075 @param tile - The title of the page 2076 @type title - A StringType instance 2077 """ 2078 wx.wizard.WizardPageSimple.__init__(self, parent) #, bitmap = gmGuiHelpers.gm_icon(_('oneperson')) 2079 self.__title = title 2080 self.__do_layout() 2081 self.__register_interests()
2082 #--------------------------------------------------------
2083 - def __do_layout(self):
2084 PNL_form = wx.Panel(self, -1) 2085 2086 # last name 2087 STT_lastname = wx.StaticText(PNL_form, -1, _('Last name')) 2088 STT_lastname.SetForegroundColour('red') 2089 self.PRW_lastname = cLastnamePhraseWheel(parent = PNL_form, id = -1) 2090 self.PRW_lastname.SetToolTipString(_('Required: lastname (family name)')) 2091 2092 # first name 2093 STT_firstname = wx.StaticText(PNL_form, -1, _('First name(s)')) 2094 STT_firstname.SetForegroundColour('red') 2095 self.PRW_firstname = cFirstnamePhraseWheel(parent = PNL_form, id = -1) 2096 self.PRW_firstname.SetToolTipString(_('Required: surname/given name/first name')) 2097 2098 # nickname 2099 STT_nick = wx.StaticText(PNL_form, -1, _('Nick name')) 2100 self.PRW_nick = cNicknamePhraseWheel(parent = PNL_form, id = -1) 2101 2102 # DOB 2103 STT_dob = wx.StaticText(PNL_form, -1, _('Date of birth')) 2104 STT_dob.SetForegroundColour('red') 2105 self.PRW_dob = gmDateTimeInput.cFuzzyTimestampInput(parent = PNL_form, id = -1) 2106 self.PRW_dob.SetToolTipString(_("Required: date of birth, if unknown or aliasing wanted then invent one")) 2107 2108 # gender 2109 STT_gender = wx.StaticText(PNL_form, -1, _('Gender')) 2110 STT_gender.SetForegroundColour('red') 2111 self.PRW_gender = cGenderSelectionPhraseWheel(parent = PNL_form, id=-1) 2112 self.PRW_gender.SetToolTipString(_("Required: gender of patient")) 2113 2114 # title 2115 STT_title = wx.StaticText(PNL_form, -1, _('Title')) 2116 self.PRW_title = cTitlePhraseWheel(parent = PNL_form, id = -1) 2117 2118 # zip code 2119 STT_zip_code = wx.StaticText(PNL_form, -1, _('Postal code')) 2120 STT_zip_code.SetForegroundColour('orange') 2121 self.PRW_zip_code = gmPersonContactWidgets.cZipcodePhraseWheel(parent = PNL_form, id = -1) 2122 self.PRW_zip_code.SetToolTipString(_("primary/home address: zip/postal code")) 2123 2124 # street 2125 STT_street = wx.StaticText(PNL_form, -1, _('Street')) 2126 STT_street.SetForegroundColour('orange') 2127 self.PRW_street = gmPersonContactWidgets.cStreetPhraseWheel(parent = PNL_form, id = -1) 2128 self.PRW_street.SetToolTipString(_("primary/home address: name of street")) 2129 2130 # address number 2131 STT_address_number = wx.StaticText(PNL_form, -1, _('Number')) 2132 STT_address_number.SetForegroundColour('orange') 2133 self.TTC_address_number = wx.TextCtrl(PNL_form, -1) 2134 self.TTC_address_number.SetToolTipString(_("primary/home address: address number")) 2135 2136 # town 2137 STT_town = wx.StaticText(PNL_form, -1, _('Place')) 2138 STT_town.SetForegroundColour('orange') 2139 self.PRW_town = gmPersonContactWidgets.cUrbPhraseWheel(parent = PNL_form, id = -1) 2140 self.PRW_town.SetToolTipString(_("primary/home address: city/town/village/dwelling/...")) 2141 2142 # state 2143 STT_state = wx.StaticText(PNL_form, -1, _('Region')) 2144 STT_state.SetForegroundColour('orange') 2145 self.PRW_state = gmPersonContactWidgets.cStateSelectionPhraseWheel(parent=PNL_form, id=-1) 2146 self.PRW_state.SetToolTipString(_("primary/home address: state/province/county/...")) 2147 2148 # country 2149 STT_country = wx.StaticText(PNL_form, -1, _('Country')) 2150 STT_country.SetForegroundColour('orange') 2151 self.PRW_country = gmPersonContactWidgets.cCountryPhraseWheel(parent = PNL_form, id = -1) 2152 self.PRW_country.SetToolTipString(_("primary/home address: country")) 2153 2154 # phone 2155 STT_phone = wx.StaticText(PNL_form, -1, _('Phone')) 2156 self.TTC_phone = wx.TextCtrl(PNL_form, -1) 2157 self.TTC_phone.SetToolTipString(_("phone number at home")) 2158 2159 # occupation 2160 STT_occupation = wx.StaticText(PNL_form, -1, _('Occupation')) 2161 self.PRW_occupation = cOccupationPhraseWheel(parent = PNL_form, id = -1) 2162 2163 # comment 2164 STT_comment = wx.StaticText(PNL_form, -1, _('Comment')) 2165 self.TCTRL_comment = wx.TextCtrl(PNL_form, -1) 2166 self.TCTRL_comment.SetToolTipString(_('A comment on this patient.')) 2167 2168 # form main validator 2169 self.form_DTD = cFormDTD(fields = self.__class__.form_fields) 2170 PNL_form.SetValidator(cBasicPatDetailsPageValidator(dtd = self.form_DTD)) 2171 2172 # layout input widgets 2173 SZR_input = wx.FlexGridSizer(cols = 2, rows = 16, vgap = 4, hgap = 4) 2174 SZR_input.AddGrowableCol(1) 2175 SZR_input.Add(STT_lastname, 0, wx.SHAPED) 2176 SZR_input.Add(self.PRW_lastname, 1, wx.EXPAND) 2177 SZR_input.Add(STT_firstname, 0, wx.SHAPED) 2178 SZR_input.Add(self.PRW_firstname, 1, wx.EXPAND) 2179 SZR_input.Add(STT_nick, 0, wx.SHAPED) 2180 SZR_input.Add(self.PRW_nick, 1, wx.EXPAND) 2181 SZR_input.Add(STT_dob, 0, wx.SHAPED) 2182 SZR_input.Add(self.PRW_dob, 1, wx.EXPAND) 2183 SZR_input.Add(STT_gender, 0, wx.SHAPED) 2184 SZR_input.Add(self.PRW_gender, 1, wx.EXPAND) 2185 SZR_input.Add(STT_title, 0, wx.SHAPED) 2186 SZR_input.Add(self.PRW_title, 1, wx.EXPAND) 2187 SZR_input.Add(STT_zip_code, 0, wx.SHAPED) 2188 SZR_input.Add(self.PRW_zip_code, 1, wx.EXPAND) 2189 SZR_input.Add(STT_street, 0, wx.SHAPED) 2190 SZR_input.Add(self.PRW_street, 1, wx.EXPAND) 2191 SZR_input.Add(STT_address_number, 0, wx.SHAPED) 2192 SZR_input.Add(self.TTC_address_number, 1, wx.EXPAND) 2193 SZR_input.Add(STT_town, 0, wx.SHAPED) 2194 SZR_input.Add(self.PRW_town, 1, wx.EXPAND) 2195 SZR_input.Add(STT_state, 0, wx.SHAPED) 2196 SZR_input.Add(self.PRW_state, 1, wx.EXPAND) 2197 SZR_input.Add(STT_country, 0, wx.SHAPED) 2198 SZR_input.Add(self.PRW_country, 1, wx.EXPAND) 2199 SZR_input.Add(STT_phone, 0, wx.SHAPED) 2200 SZR_input.Add(self.TTC_phone, 1, wx.EXPAND) 2201 SZR_input.Add(STT_occupation, 0, wx.SHAPED) 2202 SZR_input.Add(self.PRW_occupation, 1, wx.EXPAND) 2203 SZR_input.Add(STT_comment, 0, wx.SHAPED) 2204 SZR_input.Add(self.TCTRL_comment, 1, wx.EXPAND) 2205 2206 PNL_form.SetSizerAndFit(SZR_input) 2207 2208 # layout page 2209 SZR_main = makePageTitle(self, self.__title) 2210 SZR_main.Add(PNL_form, 1, wx.EXPAND)
2211 #-------------------------------------------------------- 2212 # event handling 2213 #--------------------------------------------------------
2214 - def __register_interests(self):
2215 self.PRW_firstname.add_callback_on_lose_focus(self.on_name_set) 2216 self.PRW_country.add_callback_on_selection(self.on_country_selected) 2217 self.PRW_zip_code.add_callback_on_lose_focus(self.on_zip_set)
2218 #--------------------------------------------------------
2219 - def on_country_selected(self, data):
2220 """Set the states according to entered country.""" 2221 self.PRW_state.set_context(context=u'country', val=data) 2222 return True
2223 #--------------------------------------------------------
2224 - def on_name_set(self):
2225 """Set the gender according to entered firstname. 2226 2227 Matches are fetched from existing records in backend. 2228 """ 2229 firstname = self.PRW_firstname.GetValue().strip() 2230 rows, idx = gmPG2.run_ro_queries(queries = [{ 2231 'cmd': u"select gender from dem.name_gender_map where name ilike %s", 2232 'args': [firstname] 2233 }]) 2234 if len(rows) == 0: 2235 return True 2236 wx.CallAfter(self.PRW_gender.SetData, rows[0][0]) 2237 return True
2238 #--------------------------------------------------------
2239 - def on_zip_set(self):
2240 """Set the street, town, state and country according to entered zip code.""" 2241 zip_code = self.PRW_zip_code.GetValue().strip() 2242 self.PRW_street.set_context(context=u'zip', val=zip_code) 2243 self.PRW_town.set_context(context=u'zip', val=zip_code) 2244 self.PRW_state.set_context(context=u'zip', val=zip_code) 2245 self.PRW_country.set_context(context=u'zip', val=zip_code) 2246 return True
2247 #============================================================
2248 -def makePageTitle(wizPg, title):
2249 """ 2250 Utility function to create the main sizer of a wizard's page. 2251 2252 @param wizPg The wizard page widget 2253 @type wizPg A wx.WizardPageSimple instance 2254 @param title The wizard page's descriptive title 2255 @type title A StringType instance 2256 """ 2257 sizer = wx.BoxSizer(wx.VERTICAL) 2258 wizPg.SetSizer(sizer) 2259 title = wx.StaticText(wizPg, -1, title) 2260 title.SetFont(wx.Font(10, wx.SWISS, wx.NORMAL, wx.BOLD)) 2261 sizer.Add(title, 0, wx.ALIGN_CENTRE|wx.ALL, 2) 2262 sizer.Add(wx.StaticLine(wizPg, -1), 0, wx.EXPAND|wx.ALL, 2) 2263 return sizer
2264 #============================================================
2265 -class cNewPatientWizard(wx.wizard.Wizard):
2266 """ 2267 Wizard to create a new patient. 2268 2269 TODO: 2270 - write pages for different "themes" of patient creation 2271 - make it configurable which pages are loaded 2272 - make available sets of pages that apply to a country 2273 - make loading of some pages depend upon values in earlier pages, eg 2274 when the patient is female and older than 13 include a page about 2275 "female" data (number of kids etc) 2276 2277 FIXME: use: wizard.FindWindowById(wx.ID_FORWARD).Disable() 2278 """ 2279 #--------------------------------------------------------
2280 - def __init__(self, parent, title = _('Register new person'), subtitle = _('Basic demographic details') ):
2281 """ 2282 Creates a new instance of NewPatientWizard 2283 @param parent - The parent widget 2284 @type parent - A wx.Window instance 2285 """ 2286 id_wiz = wx.NewId() 2287 wx.wizard.Wizard.__init__(self, parent, id_wiz, title) #images.getWizTest1Bitmap() 2288 self.SetExtraStyle(wx.WS_EX_VALIDATE_RECURSIVELY) 2289 self.__subtitle = subtitle 2290 self.__do_layout()
2291 #--------------------------------------------------------
2292 - def RunWizard(self, activate=False):
2293 """Create new patient. 2294 2295 activate, too, if told to do so (and patient successfully created) 2296 """ 2297 while True: 2298 2299 if not wx.wizard.Wizard.RunWizard(self, self.basic_pat_details): 2300 return False 2301 2302 try: 2303 # retrieve DTD and create patient 2304 ident = create_identity_from_dtd(dtd = self.basic_pat_details.form_DTD) 2305 except: 2306 _log.exception('cannot add new patient - missing identity fields') 2307 gmGuiHelpers.gm_show_error ( 2308 _('Cannot create new patient.\n' 2309 'Missing parts of the identity.' 2310 ), 2311 _('Adding new patient') 2312 ) 2313 continue 2314 2315 update_identity_from_dtd(identity = ident, dtd = self.basic_pat_details.form_DTD) 2316 2317 try: 2318 link_contacts_from_dtd(identity = ident, dtd = self.basic_pat_details.form_DTD) 2319 except: 2320 _log.exception('cannot finalize new patient - missing address fields') 2321 gmGuiHelpers.gm_show_error ( 2322 _('Cannot add address for the new patient.\n' 2323 'You must either enter all of the address fields or\n' 2324 'none at all. The relevant fields are marked in yellow.\n' 2325 '\n' 2326 'You will need to add the address details in the\n' 2327 'demographics module.' 2328 ), 2329 _('Adding new patient') 2330 ) 2331 break 2332 2333 link_occupation_from_dtd(identity = ident, dtd = self.basic_pat_details.form_DTD) 2334 2335 break 2336 2337 if activate: 2338 from Gnumed.wxpython import gmPatSearchWidgets 2339 gmPatSearchWidgets.set_active_patient(patient = ident) 2340 2341 return ident
2342 #-------------------------------------------------------- 2343 # internal helpers 2344 #--------------------------------------------------------
2345 - def __do_layout(self):
2346 """Arrange widgets. 2347 """ 2348 # Create the wizard pages 2349 self.basic_pat_details = cBasicPatDetailsPage(self, self.__subtitle ) 2350 self.FitToPage(self.basic_pat_details)
2351 #============================================================ 2352 #============================================================
2353 -class cBasicPatDetailsPageValidator(wx.PyValidator):
2354 """ 2355 This validator is used to ensure that the user has entered all 2356 the required conditional values in the page (eg., to properly 2357 create an address, all the related fields must be filled). 2358 """ 2359 #--------------------------------------------------------
2360 - def __init__(self, dtd):
2361 """ 2362 Validator initialization. 2363 @param dtd The object containing the data model. 2364 @type dtd A cFormDTD instance 2365 """ 2366 # initialize parent class 2367 wx.PyValidator.__init__(self) 2368 # validator's storage object 2369 self.form_DTD = dtd
2370 #--------------------------------------------------------
2371 - def Clone(self):
2372 """ 2373 Standard cloner. 2374 Note that every validator must implement the Clone() method. 2375 """ 2376 return cBasicPatDetailsPageValidator(dtd = self.form_DTD) # FIXME: probably need new instance of DTD ?
2377 #--------------------------------------------------------
2378 - def Validate(self, parent = None):
2379 """ 2380 Validate the contents of the given text control. 2381 """ 2382 _pnl_form = self.GetWindow().GetParent() 2383 2384 error = False 2385 2386 # name fields 2387 if _pnl_form.PRW_lastname.GetValue().strip() == '': 2388 error = True 2389 gmDispatcher.send(signal = 'statustext', msg = _('Must enter lastname.')) 2390 _pnl_form.PRW_lastname.SetBackgroundColour('pink') 2391 _pnl_form.PRW_lastname.Refresh() 2392 else: 2393 _pnl_form.PRW_lastname.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 2394 _pnl_form.PRW_lastname.Refresh() 2395 2396 if _pnl_form.PRW_firstname.GetValue().strip() == '': 2397 error = True 2398 gmDispatcher.send(signal = 'statustext', msg = _('Must enter first name.')) 2399 _pnl_form.PRW_firstname.SetBackgroundColour('pink') 2400 _pnl_form.PRW_firstname.Refresh() 2401 else: 2402 _pnl_form.PRW_firstname.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 2403 _pnl_form.PRW_firstname.Refresh() 2404 2405 # gender 2406 if _pnl_form.PRW_gender.GetData() is None: 2407 error = True 2408 gmDispatcher.send(signal = 'statustext', msg = _('Must select gender.')) 2409 _pnl_form.PRW_gender.SetBackgroundColour('pink') 2410 _pnl_form.PRW_gender.Refresh() 2411 else: 2412 _pnl_form.PRW_gender.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 2413 _pnl_form.PRW_gender.Refresh() 2414 2415 # dob validation 2416 if ( 2417 (_pnl_form.PRW_dob.GetValue().strip() == u'') 2418 or (not _pnl_form.PRW_dob.is_valid_timestamp()) 2419 or (_pnl_form.PRW_dob.GetData().timestamp.year < 1900) 2420 ): 2421 error = True 2422 msg = _('Cannot parse <%s> into proper timestamp.') % _pnl_form.PRW_dob.GetValue() 2423 gmDispatcher.send(signal = 'statustext', msg = msg) 2424 _pnl_form.PRW_dob.SetBackgroundColour('pink') 2425 _pnl_form.PRW_dob.Refresh() 2426 else: 2427 _pnl_form.PRW_dob.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 2428 _pnl_form.PRW_dob.Refresh() 2429 2430 # address 2431 is_any_field_filled = False 2432 address_fields = ( 2433 _pnl_form.TTC_address_number, 2434 _pnl_form.PRW_zip_code, 2435 _pnl_form.PRW_street, 2436 _pnl_form.PRW_town 2437 ) 2438 for field in address_fields: 2439 if field.GetValue().strip() == u'': 2440 if is_any_field_filled: 2441 error = True 2442 msg = _('To properly create an address, all the related fields must be filled in.') 2443 gmGuiHelpers.gm_show_error(msg, _('Required fields')) 2444 field.SetBackgroundColour('pink') 2445 field.SetFocus() 2446 field.Refresh() 2447 else: 2448 is_any_field_filled = True 2449 field.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 2450 field.Refresh() 2451 2452 address_fields = ( 2453 _pnl_form.PRW_state, 2454 _pnl_form.PRW_country 2455 ) 2456 for field in address_fields: 2457 if field.GetData() is None: 2458 if is_any_field_filled: 2459 error = True 2460 msg = _('To properly create an address, all the related fields must be filled in.') 2461 gmGuiHelpers.gm_show_error(msg, _('Required fields')) 2462 field.SetBackgroundColour('pink') 2463 field.SetFocus() 2464 field.Refresh() 2465 else: 2466 is_any_field_filled = True 2467 field.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 2468 field.Refresh() 2469 2470 return (not error)
2471 #--------------------------------------------------------
2472 - def TransferToWindow(self):
2473 """ 2474 Transfer data from validator to window. 2475 The default implementation returns False, indicating that an error 2476 occurred. We simply return True, as we don't do any data transfer. 2477 """ 2478 _pnl_form = self.GetWindow().GetParent() 2479 # fill in controls with values from self.form_DTD 2480 _pnl_form.PRW_gender.SetData(self.form_DTD['gender']) 2481 _pnl_form.PRW_dob.SetText(self.form_DTD['dob']) 2482 _pnl_form.PRW_lastname.SetText(self.form_DTD['lastnames']) 2483 _pnl_form.PRW_firstname.SetText(self.form_DTD['firstnames']) 2484 _pnl_form.PRW_title.SetText(self.form_DTD['title']) 2485 _pnl_form.PRW_nick.SetText(self.form_DTD['nick']) 2486 _pnl_form.PRW_occupation.SetText(self.form_DTD['occupation']) 2487 _pnl_form.TTC_address_number.SetValue(self.form_DTD['address_number']) 2488 _pnl_form.PRW_street.SetText(self.form_DTD['street']) 2489 _pnl_form.PRW_zip_code.SetText(self.form_DTD['zip_code']) 2490 _pnl_form.PRW_town.SetText(self.form_DTD['town']) 2491 _pnl_form.PRW_state.SetData(self.form_DTD['state']) 2492 _pnl_form.PRW_country.SetData(self.form_DTD['country']) 2493 _pnl_form.TTC_phone.SetValue(self.form_DTD['phone']) 2494 _pnl_form.TCTRL_comment.SetValue(self.form_DTD['comment']) 2495 return True # Prevent wxDialog from complaining
2496 #--------------------------------------------------------
2497 - def TransferFromWindow(self):
2498 """ 2499 Transfer data from window to validator. 2500 The default implementation returns False, indicating that an error 2501 occurred. We simply return True, as we don't do any data transfer. 2502 """ 2503 # FIXME: should be called automatically 2504 if not self.GetWindow().GetParent().Validate(): 2505 return False 2506 try: 2507 _pnl_form = self.GetWindow().GetParent() 2508 # fill in self.form_DTD with values from controls 2509 self.form_DTD['gender'] = _pnl_form.PRW_gender.GetData() 2510 self.form_DTD['dob'] = _pnl_form.PRW_dob.GetData() 2511 2512 self.form_DTD['lastnames'] = _pnl_form.PRW_lastname.GetValue() 2513 self.form_DTD['firstnames'] = _pnl_form.PRW_firstname.GetValue() 2514 self.form_DTD['title'] = _pnl_form.PRW_title.GetValue() 2515 self.form_DTD['nick'] = _pnl_form.PRW_nick.GetValue() 2516 2517 self.form_DTD['occupation'] = _pnl_form.PRW_occupation.GetValue() 2518 2519 self.form_DTD['address_number'] = _pnl_form.TTC_address_number.GetValue() 2520 self.form_DTD['street'] = _pnl_form.PRW_street.GetValue() 2521 self.form_DTD['zip_code'] = _pnl_form.PRW_zip_code.GetValue() 2522 self.form_DTD['town'] = _pnl_form.PRW_town.GetValue() 2523 self.form_DTD['state'] = _pnl_form.PRW_state.GetData() 2524 self.form_DTD['country'] = _pnl_form.PRW_country.GetData() 2525 2526 self.form_DTD['phone'] = _pnl_form.TTC_phone.GetValue() 2527 2528 self.form_DTD['comment'] = _pnl_form.TCTRL_comment.GetValue() 2529 except: 2530 return False 2531 return True
2532 #============================================================
2533 -class TestWizardPanel(wx.Panel):
2534 """ 2535 Utility class to test the new patient wizard. 2536 """ 2537 #--------------------------------------------------------
2538 - def __init__(self, parent, id):
2539 """ 2540 Create a new instance of TestPanel. 2541 @param parent The parent widget 2542 @type parent A wx.Window instance 2543 """ 2544 wx.Panel.__init__(self, parent, id) 2545 wizard = cNewPatientWizard(self) 2546 print wizard.RunWizard()
2547 #============================================================ 2548 if __name__ == "__main__": 2549 2550 #--------------------------------------------------------
2551 - def test_organizer_pnl():
2552 app = wx.PyWidgetTester(size = (600, 400)) 2553 app.SetWidget(cKOrganizerSchedulePnl) 2554 app.MainLoop()
2555 #--------------------------------------------------------
2556 - def test_person_names_pnl():
2557 app = wx.PyWidgetTester(size = (600, 400)) 2558 widget = cPersonNamesManagerPnl(app.frame, -1) 2559 widget.identity = activate_patient() 2560 app.frame.Show(True) 2561 app.MainLoop()
2562 #--------------------------------------------------------
2563 - def test_person_ids_pnl():
2564 app = wx.PyWidgetTester(size = (600, 400)) 2565 widget = cPersonIDsManagerPnl(app.frame, -1) 2566 widget.identity = activate_patient() 2567 app.frame.Show(True) 2568 app.MainLoop()
2569 #--------------------------------------------------------
2570 - def test_pat_ids_pnl():
2571 app = wx.PyWidgetTester(size = (600, 400)) 2572 widget = cPersonIdentityManagerPnl(app.frame, -1) 2573 widget.identity = activate_patient() 2574 app.frame.Show(True) 2575 app.MainLoop()
2576 #--------------------------------------------------------
2577 - def test_name_ea_pnl():
2578 app = wx.PyWidgetTester(size = (600, 400)) 2579 app.SetWidget(cNameGenderDOBEditAreaPnl, name = activate_patient().get_active_name()) 2580 app.MainLoop()
2581 #--------------------------------------------------------
2582 - def test_pat_contacts_pnl():
2583 app = wx.PyWidgetTester(size = (600, 400)) 2584 widget = cPersonContactsManagerPnl(app.frame, -1) 2585 widget.identity = activate_patient() 2586 app.frame.Show(True) 2587 app.MainLoop()
2588 #--------------------------------------------------------
2589 - def test_cPersonDemographicsEditorNb():
2590 app = wx.PyWidgetTester(size = (600, 400)) 2591 widget = cPersonDemographicsEditorNb(app.frame, -1) 2592 widget.identity = activate_patient() 2593 widget.refresh() 2594 app.frame.Show(True) 2595 app.MainLoop()
2596 #--------------------------------------------------------
2597 - def activate_patient():
2598 patient = gmPersonSearch.ask_for_patient() 2599 if patient is None: 2600 print "No patient. Exiting gracefully..." 2601 sys.exit(0) 2602 from Gnumed.wxpython import gmPatSearchWidgets 2603 gmPatSearchWidgets.set_active_patient(patient=patient) 2604 return patient
2605 #-------------------------------------------------------- 2606 if len(sys.argv) > 1 and sys.argv[1] == 'test': 2607 2608 gmI18N.activate_locale() 2609 gmI18N.install_domain(domain='gnumed') 2610 gmPG2.get_connection() 2611 2612 # a = cFormDTD(fields = cBasicPatDetailsPage.form_fields) 2613 2614 # app = wx.PyWidgetTester(size = (400, 300)) 2615 # app.SetWidget(cNotebookedPatEditionPanel, -1) 2616 # app.SetWidget(TestWizardPanel, -1) 2617 # app.frame.Show(True) 2618 # app.MainLoop() 2619 2620 # phrasewheels 2621 # test_zipcode_prw() 2622 # test_state_prw() 2623 # test_street_prw() 2624 # test_organizer_pnl() 2625 #test_address_type_prw() 2626 #test_suburb_prw() 2627 test_urb_prw() 2628 #test_address_prw() 2629 2630 # contacts related widgets 2631 #test_address_ea_pnl() 2632 #test_person_adrs_pnl() 2633 #test_person_comms_pnl() 2634 #test_pat_contacts_pnl() 2635 2636 # identity related widgets 2637 #test_person_names_pnl() 2638 #test_person_ids_pnl() 2639 #test_pat_ids_pnl() 2640 #test_name_ea_pnl() 2641 2642 #test_cPersonDemographicsEditorNb() 2643 2644 #============================================================ 2645