Package Gnumed :: Package wxpython :: Module gmPersonCreationWidgets
[frames] | no frames]

Source Code for Module Gnumed.wxpython.gmPersonCreationWidgets

  1  """GNUmed patient creation widgets. 
  2   
  3  copyright: authors 
  4  """ 
  5  #============================================================ 
  6  __author__ = "K.Hilbert" 
  7  __license__ = "GPL v2 or later (details at http://www.gnu.org)" 
  8   
  9  import logging 
 10  import sys 
 11  import datetime as pydt 
 12   
 13   
 14  import wx 
 15   
 16   
 17  if __name__ == '__main__': 
 18          sys.path.insert(0, '../../') 
 19  from Gnumed.pycommon import gmCfg 
 20  from Gnumed.pycommon import gmPG2 
 21  from Gnumed.pycommon import gmTools 
 22  from Gnumed.pycommon import gmDateTime 
 23  from Gnumed.pycommon import gmDispatcher 
 24   
 25  from Gnumed.business import gmPraxis 
 26  from Gnumed.business import gmPerson 
 27  from Gnumed.business import gmStaff 
 28  from Gnumed.business import gmDemographicRecord 
 29   
 30  from Gnumed.wxpython import gmEditArea 
 31  from Gnumed.wxpython import gmGuiHelpers 
 32  from Gnumed.wxpython import gmEncounterWidgets 
 33  from Gnumed.wxpython.gmDemographicsWidgets import _validate_dob_field, _validate_tob_field, _empty_dob_allowed 
 34   
 35   
 36  _log = logging.getLogger('gm.patient') 
 37   
 38  #============================================================ 
39 -def create_new_person(parent=None, activate=False):
40 41 if parent is None: 42 parent = wx.GetApp().GetTopWindow() 43 44 if activate: # meaning we will switch away from the current patient if any 45 msg = _( 46 'Before creating a new person review the encounter details\n' 47 'of the patient you just worked on:\n' 48 ) 49 gmEncounterWidgets.sanity_check_encounter_of_active_patient(parent = parent, msg = msg) 50 51 msg = _('Edit the current encounter of the patient you are ABOUT TO LEAVE:') 52 53 dbcfg = gmCfg.cCfgSQL() 54 55 def_region = dbcfg.get2 ( 56 option = 'person.create.default_region', 57 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace, 58 bias = 'user' 59 ) 60 def_country = None 61 62 if def_region is None: 63 def_country = dbcfg.get2 ( 64 option = 'person.create.default_country', 65 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace, 66 bias = 'user' 67 ) 68 else: 69 countries = gmDemographicRecord.get_country_for_region(region = def_region) 70 if len(countries) == 1: 71 def_country = countries[0]['code_country'] 72 73 ea = cNewPatientEAPnl(parent, -1, country = def_country, region = def_region) 74 dlg = gmEditArea.cGenericEditAreaDlg2(parent, -1, edit_area = ea, single_entry = True) 75 dlg.SetTitle(_('Adding new person')) 76 ea._PRW_lastname.SetFocus() 77 result = dlg.ShowModal() 78 pat = ea.data 79 dlg.Destroy() 80 81 if result != wx.ID_OK: 82 return False 83 84 _log.debug('created new person [%s]', pat.ID) 85 86 if activate: 87 from Gnumed.wxpython import gmPatSearchWidgets 88 gmPatSearchWidgets.set_active_patient(patient = pat) 89 90 gmDispatcher.send(signal = 'display_widget', name = 'gmNotebookedPatientEditionPlugin') 91 92 return True
93 94 #============================================================ 95 from Gnumed.wxGladeWidgets import wxgNewPatientEAPnl 96
97 -class cNewPatientEAPnl(wxgNewPatientEAPnl.wxgNewPatientEAPnl, gmEditArea.cGenericEditAreaMixin):
98
99 - def __init__(self, *args, **kwargs):
100 101 try: 102 self.default_region = kwargs['region'] 103 del kwargs['region'] 104 except KeyError: 105 self.default_region = None 106 107 try: 108 self.default_country = kwargs['country'] 109 del kwargs['country'] 110 except KeyError: 111 self.default_country = None 112 113 wxgNewPatientEAPnl.wxgNewPatientEAPnl.__init__(self, *args, **kwargs) 114 gmEditArea.cGenericEditAreaMixin.__init__(self) 115 116 self.mode = 'new' 117 self.data = None 118 self._address = None 119 120 self.__init_ui() 121 self.__register_interests()
122 #---------------------------------------------------------------- 123 # internal helpers 124 #----------------------------------------------------------------
125 - def __init_ui(self):
126 self._PRW_lastname.final_regex = '.+' 127 self._PRW_firstnames.final_regex = '.+' 128 self._PRW_address_searcher.selection_only = False 129 130 # only if we would support None on selection_only's: 131 # self._PRW_external_id_type.selection_only = True 132 133 if self.default_country is not None: 134 match = self._PRW_country._data2match(data = self.default_country) 135 if match is not None: 136 self._PRW_country.SetText(value = match['field_label'], data = match['data']) 137 138 if self.default_region is not None: 139 self._PRW_region.SetText(value = self.default_region) 140 141 self._PRW_type.SetText(value = 'home') 142 # FIXME: only use this if member of gm-doctors, 143 # FIXME: other than that check fallback_primary_provider 144 self._PRW_primary_provider.SetData(data = gmStaff.gmCurrentProvider()['pk_staff']) 145 146 self._PRW_lastname.SetFocus()
147 #----------------------------------------------------------------
148 - def _refresh_ext_id_warning(self):
149 id_type = self._PRW_external_id_type.GetData() 150 if id_type is None: 151 self._LBL_id_exists.SetLabel('') 152 return 153 val = self._TCTRL_external_id_value.GetValue().strip() 154 if val == '': 155 self._LBL_id_exists.SetLabel('') 156 return 157 if gmPerson.external_id_exists(pk_issuer = id_type, value = val) > 0: 158 self._LBL_id_exists.SetLabel(_('ID exists !')) 159 else: 160 self._LBL_id_exists.SetLabel('')
161 #----------------------------------------------------------------
162 - def _refresh_dupe_warning(self):
163 lname = self._PRW_lastname.GetValue().strip() 164 if lname == '': 165 self._LBL_person_exists.SetLabel('') 166 return 167 168 dob = self._PRW_dob.GetData() 169 if dob is None: 170 self._LBL_person_exists.SetLabel('') 171 return 172 173 fname = gmTools.none_if(self._PRW_firstnames.GetValue().strip()[:1], '') 174 175 no_of_dupes = gmPerson.person_exists(lastnames = lname, firstnames = fname, dob = dob) 176 if no_of_dupes == 0: 177 lbl = '' 178 elif no_of_dupes == 1: 179 lbl = _('One "%s, %s (%s)" already exists !') % ( 180 lname, 181 gmTools.coalesce(fname, '?', '%s %%s. %s' % (gmTools.u_ellipsis, gmTools.u_ellipsis)), 182 gmDateTime.pydt_strftime(dob, '%Y %b %d', 'utf8') 183 ) 184 else: 185 lbl = _('%s "%s, %s (%s)" already exist !') % ( 186 no_of_dupes, 187 lname, 188 gmTools.coalesce(fname, '?', '%s %%s. %s' % (gmTools.u_ellipsis, gmTools.u_ellipsis)), 189 gmDateTime.pydt_strftime(dob, '%Y %b %d', 'utf8') 190 ) 191 192 self._LBL_person_exists.SetLabel(lbl)
193 #----------------------------------------------------------------
194 - def __perhaps_invalidate_address_searcher(self, ctrl=None, field=None):
195 196 adr = self._PRW_address_searcher.address 197 if adr is None: 198 return True 199 200 if ctrl.GetValue().strip() != adr[field]: 201 wx.CallAfter(self._PRW_address_searcher.SetText, value = '', data = None) 202 return True 203 204 return False
205 #----------------------------------------------------------------
207 adr = self._PRW_address_searcher.address 208 if adr is None: 209 return True 210 211 self._PRW_zip.SetText(value = adr['postcode'], data = adr['postcode']) 212 213 self._PRW_street.SetText(value = adr['street'], data = adr['street']) 214 self._PRW_street.set_context(context = 'zip', val = adr['postcode']) 215 216 self._PRW_urb.SetText(value = adr['urb'], data = adr['urb']) 217 self._PRW_urb.set_context(context = 'zip', val = adr['postcode']) 218 219 self._PRW_region.SetText(value = adr['l10n_region'], data = adr['code_region']) 220 self._PRW_region.set_context(context = 'zip', val = adr['postcode']) 221 222 self._PRW_country.SetText(value = adr['l10n_country'], data = adr['code_country']) 223 self._PRW_country.set_context(context = 'zip', val = adr['postcode'])
224 #----------------------------------------------------------------
226 error = False 227 228 # name fields 229 if self._PRW_lastname.GetValue().strip() == '': 230 error = True 231 gmDispatcher.send(signal = 'statustext', msg = _('Must enter lastname.')) 232 self._PRW_lastname.display_as_valid(False) 233 else: 234 self._PRW_lastname.display_as_valid(True) 235 236 if self._PRW_firstnames.GetValue().strip() == '': 237 error = True 238 gmDispatcher.send(signal = 'statustext', msg = _('Must enter first name.')) 239 self._PRW_firstnames.display_as_valid(False) 240 else: 241 self._PRW_firstnames.display_as_valid(True) 242 243 # gender 244 if self._PRW_gender.GetData() is None: 245 error = True 246 gmDispatcher.send(signal = 'statustext', msg = _('Must select gender.')) 247 self._PRW_gender.display_as_valid(False) 248 else: 249 self._PRW_gender.display_as_valid(True) 250 251 # dob validation 252 if not _validate_dob_field(self._PRW_dob): 253 error = True 254 255 # TOB validation 256 if _validate_tob_field(self._TCTRL_tob): 257 self.display_ctrl_as_valid(ctrl = self._TCTRL_tob, valid = True) 258 else: 259 error = True 260 self.display_ctrl_as_valid(ctrl = self._TCTRL_tob, valid = False) 261 262 return (not error)
263 #----------------------------------------------------------------
264 - def __address_valid_for_save(self, empty_address_is_valid=False):
265 266 # existing address ? if so set other fields 267 if self._PRW_address_searcher.GetData() is not None: 268 wx.CallAfter(self.__set_fields_from_address_searcher) 269 return True 270 271 # must either all contain something or none of them 272 fields_to_fill = ( 273 self._TCTRL_number, 274 self._PRW_zip, 275 self._PRW_street, 276 self._PRW_urb, 277 self._PRW_type 278 ) 279 no_of_filled_fields = 0 280 281 for field in fields_to_fill: 282 if field.GetValue().strip() != '': 283 no_of_filled_fields += 1 284 field.display_as_valid(True) 285 286 # empty address ? 287 if no_of_filled_fields == 0: 288 if empty_address_is_valid: 289 return True 290 else: 291 return None 292 293 # incompletely filled address ? 294 if no_of_filled_fields != len(fields_to_fill): 295 for field in fields_to_fill: 296 if field.GetValue().strip() == '': 297 field.display_as_valid(False) 298 field.SetFocus() 299 msg = _('To properly create an address, all the related fields must be filled in.') 300 gmGuiHelpers.gm_show_error(msg, _('Required fields')) 301 return False 302 303 # fields which must contain a selected item 304 # FIXME: they must also contain an *acceptable combination* which 305 # FIXME: can only be tested against the database itself ... 306 strict_fields = ( 307 self._PRW_type, 308 self._PRW_region, 309 self._PRW_country 310 ) 311 error = False 312 for field in strict_fields: 313 if field.GetData() is None: 314 error = True 315 field.display_as_valid(False) 316 field.SetFocus() 317 else: 318 field.display_as_valid(True) 319 320 if error: 321 msg = _('This field must contain an item selected from the dropdown list.') 322 gmGuiHelpers.gm_show_error(msg, _('Required fields')) 323 return False 324 325 return True
326 #----------------------------------------------------------------
327 - def __register_interests(self):
328 329 # identity 330 self._PRW_lastname.add_callback_on_lose_focus(self._on_leaving_lastname) 331 self._PRW_firstnames.add_callback_on_lose_focus(self._on_leaving_firstname) 332 self._PRW_dob.add_callback_on_lose_focus(self._on_leaving_dob) 333 334 # address 335 self._PRW_address_searcher.add_callback_on_lose_focus(self._on_leaving_adress_searcher) 336 337 # invalidate address searcher when any field edited 338 self._PRW_street.add_callback_on_lose_focus(self._invalidate_address_searcher) 339 self._TCTRL_number.Bind(wx.EVT_KILL_FOCUS, self._on_leaving_number) 340 self._TCTRL_unit.Bind(wx.EVT_KILL_FOCUS, self._on_leaving_unit) 341 self._PRW_urb.add_callback_on_lose_focus(self._invalidate_address_searcher) 342 self._PRW_region.add_callback_on_lose_focus(self._invalidate_address_searcher) 343 344 self._PRW_zip.add_callback_on_lose_focus(self._on_leaving_zip) 345 self._PRW_country.add_callback_on_lose_focus(self._on_leaving_country) 346 347 self._PRW_external_id_type.add_callback_on_lose_focus(callback = self._on_leaving_ext_id_type) 348 self._TCTRL_external_id_value.add_callback_on_lose_focus(callback = self._on_leaving_ext_id_val)
349 350 #---------------------------------------------------------------- 351 # event handlers 352 #----------------------------------------------------------------
353 - def _on_leaving_ext_id_type(self):
354 wx.CallAfter(self._refresh_ext_id_warning)
355 #----------------------------------------------------------------
356 - def _on_leaving_ext_id_val(self):
357 wx.CallAfter(self._refresh_ext_id_warning)
358 #----------------------------------------------------------------
359 - def _on_leaving_lastname(self):
360 wx.CallAfter(self._refresh_dupe_warning)
361 #----------------------------------------------------------------
362 - def _on_leaving_firstname(self):
363 """Set the gender according to entered firstname. 364 365 Matches are fetched from existing records in backend. 366 """ 367 wx.CallAfter(self._refresh_dupe_warning) 368 369 # only set if not already set so as to not 370 # overwrite a change by the user 371 if self._PRW_gender.GetData() is not None: 372 return True 373 374 firstname = self._PRW_firstnames.GetValue().strip() 375 if firstname == '': 376 return True 377 378 gender = gmPerson.map_firstnames2gender(firstnames = firstname) 379 if gender is None: 380 return True 381 382 wx.CallAfter(self._PRW_gender.SetData, gender) 383 384 return True
385 #----------------------------------------------------------------
386 - def _on_leaving_dob(self):
387 wx.CallAfter(self._refresh_dupe_warning)
388 #----------------------------------------------------------------
389 - def _on_leaving_zip(self):
390 self.__perhaps_invalidate_address_searcher(self._PRW_zip, 'postcode') 391 392 zip_code = gmTools.none_if(self._PRW_zip.GetValue().strip(), '') 393 self._PRW_street.set_context(context = 'zip', val = zip_code) 394 self._PRW_urb.set_context(context = 'zip', val = zip_code) 395 self._PRW_region.set_context(context = 'zip', val = zip_code) 396 self._PRW_country.set_context(context = 'zip', val = zip_code) 397 398 return True
399 #----------------------------------------------------------------
400 - def _on_leaving_country(self):
401 self.__perhaps_invalidate_address_searcher(self._PRW_country, 'l10n_country') 402 403 country = gmTools.none_if(self._PRW_country.GetValue().strip(), '') 404 self._PRW_region.set_context(context = 'country', val = country) 405 406 return True
407 #----------------------------------------------------------------
408 - def _on_leaving_number(self, evt):
409 if self._TCTRL_number.GetValue().strip() == '': 410 adr = self._PRW_address_searcher.address 411 if adr is None: 412 return True 413 self._TCTRL_number.SetValue(adr['number']) 414 return True 415 416 self.__perhaps_invalidate_address_searcher(self._TCTRL_number, 'number') 417 return True
418 #----------------------------------------------------------------
419 - def _on_leaving_unit(self, evt):
420 if self._TCTRL_unit.GetValue().strip() == '': 421 adr = self._PRW_address_searcher.address 422 if adr is None: 423 return True 424 self._TCTRL_unit.SetValue(gmTools.coalesce(adr['subunit'], '')) 425 return True 426 427 self.__perhaps_invalidate_address_searcher(self._TCTRL_unit, 'subunit') 428 return True
429 #----------------------------------------------------------------
430 - def _invalidate_address_searcher(self, *args, **kwargs):
431 mapping = [ 432 (self._PRW_street, 'street'), 433 (self._PRW_urb, 'urb'), 434 (self._PRW_region, 'l10n_region') 435 ] 436 # loop through fields and invalidate address searcher if different 437 for ctrl, field in mapping: 438 if self.__perhaps_invalidate_address_searcher(ctrl, field): 439 return True 440 441 return True
442 #----------------------------------------------------------------
444 if self._PRW_address_searcher.address is None: 445 return True 446 447 wx.CallAfter(self.__set_fields_from_address_searcher) 448 return True
449 #---------------------------------------------------------------- 450 # generic Edit Area mixin API 451 #----------------------------------------------------------------
452 - def _valid_for_save(self):
453 if self._PRW_primary_provider.GetValue().strip() == '': 454 self._PRW_primary_provider.display_as_valid(True) 455 else: 456 if self._PRW_primary_provider.GetData() is None: 457 self._PRW_primary_provider.display_as_valid(False) 458 else: 459 self._PRW_primary_provider.display_as_valid(True) 460 return (self.__identity_valid_for_save() and self.__address_valid_for_save(empty_address_is_valid = True))
461 #----------------------------------------------------------------
462 - def _save_as_new(self):
463 464 if self._PRW_dob.GetValue().strip() == '': 465 if not _empty_dob_allowed(): 466 self._PRW_dob.display_as_valid(False) 467 self._PRW_dob.SetFocus() 468 return False 469 470 # identity 471 new_identity = gmPerson.create_identity ( 472 gender = self._PRW_gender.GetData(), 473 dob = self._PRW_dob.GetData(), 474 lastnames = self._PRW_lastname.GetValue().strip(), 475 firstnames = self._PRW_firstnames.GetValue().strip() 476 ) 477 _log.info('identity created: %s' % new_identity) 478 479 new_identity['dob_is_estimated'] = self._CHBOX_estimated_dob.GetValue() 480 val = self._TCTRL_tob.GetValue().strip() 481 if val != '': 482 new_identity['tob'] = pydt.time(int(val[:2]), int(val[3:5])) 483 new_identity['title'] = gmTools.none_if(self._PRW_title.GetValue().strip()) 484 485 prov = self._PRW_primary_provider.GetData() 486 if prov is not None: 487 new_identity['pk_primary_provider'] = prov 488 new_identity['comment'] = gmTools.none_if(self._TCTRL_comment.GetValue().strip(), '') 489 new_identity.save() 490 _log.info('new identity updated: %s' % new_identity) 491 492 new_identity.set_nickname(nickname = gmTools.none_if(self._PRW_nickname.GetValue().strip(), '')) 493 _log.info('nickname set on new identity: %s' % new_identity) 494 495 # address 496 # if we reach this the address cannot be completely empty 497 is_valid = self.__address_valid_for_save(empty_address_is_valid = False) 498 if is_valid is True: 499 # because we currently only check for non-emptiness 500 # we must still deal with database errors 501 try: 502 new_identity.link_address ( 503 number = self._TCTRL_number.GetValue().strip(), 504 street = self._PRW_street.GetValue().strip(), 505 postcode = self._PRW_zip.GetValue().strip(), 506 urb = self._PRW_urb.GetValue().strip(), 507 region_code = self._PRW_region.GetData(), 508 country_code = self._PRW_country.GetData(), 509 subunit = gmTools.none_if(self._TCTRL_unit.GetValue().strip(), ''), 510 id_type = self._PRW_type.GetData() 511 ) 512 except gmPG2.dbapi.InternalError: 513 _log.debug('number: >>%s<<', self._TCTRL_number.GetValue().strip()) 514 _log.debug('(sub)unit: >>%s<<', self._TCTRL_unit.GetValue().strip()) 515 _log.debug('street: >>%s<<', self._PRW_street.GetValue().strip()) 516 _log.debug('postcode: >>%s<<', self._PRW_zip.GetValue().strip()) 517 _log.debug('urb: >>%s<<', self._PRW_urb.GetValue().strip()) 518 _log.debug('region: >>%s<<', self._PRW_region.GetData().strip()) 519 _log.debug('country: >>%s<<', self._PRW_country.GetData().strip()) 520 _log.exception('cannot link address') 521 gmGuiHelpers.gm_show_error ( 522 aTitle = _('Saving address'), 523 aMessage = _( 524 'Cannot save this address.\n' 525 '\n' 526 'You will have to add it via the Demographics plugin.\n' 527 ) 528 ) 529 elif is_valid is False: 530 gmGuiHelpers.gm_show_error ( 531 aTitle = _('Saving address'), 532 aMessage = _( 533 'Address not saved.\n' 534 '\n' 535 'You will have to add it via the Demographics plugin.\n' 536 ) 537 ) 538 # else it is None which means empty address which we ignore 539 540 # phone 541 channel_name = self._PRW_channel_type.GetValue().strip() 542 pk_channel_type = self._PRW_channel_type.GetData() 543 if pk_channel_type is None: 544 if channel_name == '': 545 channel_name = 'homephone' 546 new_identity.link_comm_channel ( 547 comm_medium = channel_name, 548 pk_channel_type = pk_channel_type, 549 url = gmTools.none_if(self._TCTRL_phone.GetValue().strip(), ''), 550 is_confidential = False 551 ) 552 553 # external ID 554 pk_type = self._PRW_external_id_type.GetData() 555 id_value = self._TCTRL_external_id_value.GetValue().strip() 556 if (pk_type is not None) and (id_value != ''): 557 new_identity.add_external_id(value = id_value, pk_type = pk_type) 558 559 # occupation 560 new_identity.link_occupation ( 561 occupation = gmTools.none_if(self._PRW_occupation.GetValue().strip(), '') 562 ) 563 564 self.data = new_identity 565 return True
566 #----------------------------------------------------------------
567 - def _save_as_update(self):
568 raise NotImplementedError('[%s]: not expected to be used' % self.__class__.__name__)
569 #----------------------------------------------------------------
570 - def _refresh_as_new(self):
571 # FIXME: button "empty out" 572 return
573 #----------------------------------------------------------------
574 - def _refresh_from_existing(self):
575 return # there is no forward button so nothing to do here
576 #----------------------------------------------------------------
578 raise NotImplementedError('[%s]: not expected to be used' % self.__class__.__name__)
579 580 #============================================================ 581 # main 582 #------------------------------------------------------------ 583 if __name__ == "__main__": 584 585 if len(sys.argv) < 2: 586 sys.exit() 587 588 if sys.argv[1] != 'test': 589 sys.exit() 590 591 # from Gnumed.pycommon import gmPG2 592 # from Gnumed.pycommon import gmI18N 593 # gmI18N.activate_locale() 594 # gmI18N.install_domain() 595 596 #-------------------------------------------------------- 597 #test_org_unit_prw() 598