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

Source Code for Module Gnumed.wxpython.gmPersonContactWidgets

   1  """Widgets dealing with address/contact information.""" 
   2  #============================================================ 
   3  __version__ = "$Revision: 1.175 $" 
   4  __author__ = "R.Terry, SJ Tan, I Haywood, Carlos Moro <cfmoro1976@yahoo.es>" 
   5  __license__ = 'GPL v2 or later (details at http://www.gnu.org)' 
   6   
   7  # standard library 
   8  import sys, logging 
   9   
  10   
  11  import wx 
  12   
  13   
  14  # GNUmed specific 
  15  if __name__ == '__main__': 
  16          sys.path.insert(0, '../../') 
  17  from Gnumed.pycommon import gmDispatcher, gmMatchProvider, gmPG2, gmTools 
  18  from Gnumed.business import gmDemographicRecord 
  19  from Gnumed.wxpython import gmPhraseWheel, gmGuiHelpers, gmCfgWidgets 
  20  from Gnumed.wxpython import gmListWidgets, gmEditArea 
  21   
  22   
  23  # constant defs 
  24  _log = logging.getLogger('gm.ui') 
  25   
  26   
  27  try: 
  28          _('dummy-no-need-to-translate-but-make-epydoc-happy') 
  29  except NameError: 
  30          _ = lambda x:x 
  31   
  32  #============================================================ 
  33  # country related widgets / functions 
  34  #============================================================ 
35 -def configure_default_country(parent=None):
36 37 if parent is None: 38 parent = wx.GetApp().GetTopWindow() 39 40 countries = gmDemographicRecord.get_countries() 41 42 gmCfgWidgets.configure_string_from_list_option ( 43 parent = parent, 44 message = _('Select the default country for new persons.\n'), 45 option = 'person.create.default_country', 46 bias = 'user', 47 choices = [ (c['l10n_country'], c['code']) for c in countries ], 48 columns = [_('Country'), _('Code')], 49 data = [ c['name'] for c in countries ] 50 )
51 #============================================================
52 -class cCountryPhraseWheel(gmPhraseWheel.cPhraseWheel):
53
54 - def __init__(self, *args, **kwargs):
55 56 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 57 58 context = { 59 u'ctxt_zip': { 60 u'where_part': u'and zip ilike %(zip)s', 61 u'placeholder': u'zip' 62 } 63 } 64 query = u""" 65 select code, name from ( 66 select distinct on (code, name) code, (name || ' (' || code || ')') as name, rank from ( 67 68 -- localized to user 69 70 select 71 code_country as code, l10n_country as name, 1 as rank 72 from dem.v_zip2data 73 where 74 l10n_country %(fragment_condition)s 75 %(ctxt_zip)s 76 77 union all 78 79 select 80 code as code, _(name) as name, 2 as rank 81 from dem.country 82 where 83 _(name) %(fragment_condition)s 84 85 union all 86 87 -- non-localized 88 89 select 90 code_country as code, country as name, 3 as rank 91 from dem.v_zip2data 92 where 93 country %(fragment_condition)s 94 %(ctxt_zip)s 95 96 union all 97 98 select 99 code as code, name as name, 4 as rank 100 from dem.country 101 where 102 name %(fragment_condition)s 103 104 union all 105 106 -- abbreviation 107 108 select 109 code as code, name as name, 5 as rank 110 from dem.country 111 where 112 code %(fragment_condition)s 113 114 ) as q2 115 ) as q1 order by rank, name limit 25""" 116 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query, context=context) 117 mp.setThresholds(2, 5, 9) 118 self.matcher = mp 119 120 self.unset_context(context = u'zip') 121 self.SetToolTipString(_('Type or select a country.')) 122 self.capitalisation_mode = gmTools.CAPS_FIRST 123 self.selection_only = True
124 125 #============================================================ 126 # province/state related widgets / functions 127 #============================================================
128 -def configure_default_region(parent=None):
129 130 if parent is None: 131 parent = wx.GetApp().GetTopWindow() 132 133 provs = gmDemographicRecord.get_provinces() 134 135 gmCfgWidgets.configure_string_from_list_option ( 136 parent = parent, 137 message = _('Select the default region/province/state/territory for new persons.\n'), 138 option = 'person.create.default_region', 139 bias = 'user', 140 choices = [ (p['l10n_country'], p['l10n_state'], p['code_state']) for p in provs ], 141 columns = [_('Country'), _('Region'), _('Code')], 142 data = [ p['state'] for p in provs ] 143 )
144 #============================================================
145 -def edit_province(parent=None, province=None):
146 ea = cProvinceEAPnl(parent = parent, id = -1, province = province) 147 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = (province is not None)) 148 dlg.SetTitle(gmTools.coalesce(province, _('Adding province'), _('Editing province'))) 149 result = dlg.ShowModal() 150 dlg.Destroy() 151 return (result == wx.ID_OK)
152 #============================================================
153 -def delete_province(parent=None, province=None):
154 155 msg = _( 156 'Are you sure you want to delete this province ?\n' 157 '\n' 158 'Deletion will only work if this province is not\n' 159 'yet in use in any patient addresses.' 160 ) 161 162 tt = _( 163 'Also delete any towns/cities/villages known\n' 164 'to be situated in this state as long as\n' 165 'no patients are recorded to live there.' 166 ) 167 168 dlg = gmGuiHelpers.c2ButtonQuestionDlg ( 169 parent, 170 -1, 171 caption = _('Deleting province'), 172 question = msg, 173 show_checkbox = True, 174 checkbox_msg = _('delete related townships'), 175 checkbox_tooltip = tt, 176 button_defs = [ 177 {'label': _('Yes, delete'), 'tooltip': _('Delete province and possibly related townships.'), 'default': False}, 178 {'label': _('No'), 'tooltip': _('No, do NOT delete anything.'), 'default': True} 179 ] 180 ) 181 182 decision = dlg.ShowModal() 183 if decision != wx.ID_YES: 184 dlg.Destroy() 185 return False 186 187 include_urbs = dlg.checkbox_is_checked() 188 dlg.Destroy() 189 190 return gmDemographicRecord.delete_province(province = province, delete_urbs = include_urbs)
191 #============================================================
192 -def manage_provinces(parent=None):
193 194 if parent is None: 195 parent = wx.GetApp().GetTopWindow() 196 197 #------------------------------------------------------------ 198 def delete(province=None): 199 return delete_province(parent = parent, province = province['pk_state'])
200 #------------------------------------------------------------ 201 def edit(province=None): 202 return edit_province(parent = parent, province = province) 203 #------------------------------------------------------------ 204 def refresh(lctrl): 205 wx.BeginBusyCursor() 206 provinces = gmDemographicRecord.get_provinces() 207 lctrl.set_string_items([ (p['l10n_country'], p['l10n_state']) for p in provinces ]) 208 lctrl.set_data(provinces) 209 wx.EndBusyCursor() 210 #------------------------------------------------------------ 211 msg = _( 212 '\n' 213 'This list shows the provinces known to GNUmed.\n' 214 '\n' 215 'In your jurisdiction "province" may correspond to either of "state",\n' 216 '"county", "region", "territory", or some such term.\n' 217 '\n' 218 'Select the province you want to edit !\n' 219 ) 220 221 gmListWidgets.get_choices_from_list ( 222 parent = parent, 223 msg = msg, 224 caption = _('Editing provinces ...'), 225 columns = [_('Country'), _('Province')], 226 single_selection = True, 227 new_callback = edit, 228 #edit_callback = edit, 229 delete_callback = delete, 230 refresh_callback = refresh 231 ) 232 #============================================================
233 -class cStateSelectionPhraseWheel(gmPhraseWheel.cPhraseWheel):
234
235 - def __init__(self, *args, **kwargs):
236 237 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 238 239 context = { 240 u'ctxt_country_name': { 241 u'where_part': u'and l10n_country ilike %(country_name)s or country ilike %(country_name)s', 242 u'placeholder': u'country_name' 243 }, 244 u'ctxt_zip': { 245 u'where_part': u'and zip ilike %(zip)s', 246 u'placeholder': u'zip' 247 }, 248 u'ctxt_country_code': { 249 u'where_part': u'and country in (select code from dem.country where _(name) ilike %(country_name)s or name ilike %(country_name)s)', 250 u'placeholder': u'country_name' 251 } 252 } 253 254 query = u""" 255 select code, name from ( 256 select distinct on (name) code, name, rank from ( 257 -- 1: find states based on name, context: zip and country name 258 select 259 code_state as code, state as name, 1 as rank 260 from dem.v_zip2data 261 where 262 state %(fragment_condition)s 263 %(ctxt_country_name)s 264 %(ctxt_zip)s 265 266 union all 267 268 -- 2: find states based on code, context: zip and country name 269 select 270 code_state as code, state as name, 2 as rank 271 from dem.v_zip2data 272 where 273 code_state %(fragment_condition)s 274 %(ctxt_country_name)s 275 %(ctxt_zip)s 276 277 union all 278 279 -- 3: find states based on name, context: country 280 select 281 code as code, name as name, 3 as rank 282 from dem.state 283 where 284 name %(fragment_condition)s 285 %(ctxt_country_code)s 286 287 union all 288 289 -- 4: find states based on code, context: country 290 select 291 code as code, name as name, 3 as rank 292 from dem.state 293 where 294 code %(fragment_condition)s 295 %(ctxt_country_code)s 296 297 ) as q2 298 ) as q1 order by rank, name limit 50""" 299 300 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query, context=context) 301 mp.setThresholds(2, 5, 6) 302 mp.word_separators = u'[ \t]+' 303 self.matcher = mp 304 305 self.unset_context(context = u'zip') 306 self.unset_context(context = u'country_name') 307 self.SetToolTipString(_('Type or select a state/region/province/territory.')) 308 self.capitalisation_mode = gmTools.CAPS_FIRST 309 self.selection_only = True
310 #==================================================================== 311 from Gnumed.wxGladeWidgets import wxgProvinceEAPnl 312
313 -class cProvinceEAPnl(wxgProvinceEAPnl.wxgProvinceEAPnl, gmEditArea.cGenericEditAreaMixin):
314
315 - def __init__(self, *args, **kwargs):
316 317 try: 318 data = kwargs['province'] 319 del kwargs['province'] 320 except KeyError: 321 data = None 322 323 wxgProvinceEAPnl.wxgProvinceEAPnl.__init__(self, *args, **kwargs) 324 gmEditArea.cGenericEditAreaMixin.__init__(self) 325 326 self.mode = 'new' 327 self.data = data 328 if data is not None: 329 self.mode = 'edit' 330 331 self.__init_ui()
332 #----------------------------------------------------------------
333 - def __init_ui(self):
334 self._PRW_province.selection_only = False
335 #---------------------------------------------------------------- 336 # generic Edit Area mixin API 337 #----------------------------------------------------------------
338 - def _valid_for_save(self):
339 340 validity = True 341 342 if self._PRW_province.GetData() is None: 343 if self._PRW_province.GetValue().strip() == u'': 344 validity = False 345 self._PRW_province.display_as_valid(False) 346 else: 347 self._PRW_province.display_as_valid(True) 348 else: 349 self._PRW_province.display_as_valid(True) 350 351 if self._PRW_province.GetData() is None: 352 if self._TCTRL_code.GetValue().strip() == u'': 353 validity = False 354 self._TCTRL_code.SetBackgroundColour(gmPhraseWheel.color_prw_invalid) 355 else: 356 self._TCTRL_code.SetBackgroundColour(gmPhraseWheel.color_prw_valid) 357 358 if self._PRW_country.GetData() is None: 359 validity = False 360 self._PRW_country.display_as_valid(False) 361 else: 362 self._PRW_country.display_as_valid(True) 363 364 return validity
365 #----------------------------------------------------------------
366 - def _save_as_new(self):
367 gmDemographicRecord.create_province ( 368 name = self._PRW_province.GetValue().strip(), 369 code = self._TCTRL_code.GetValue().strip(), 370 country = self._PRW_country.GetData() 371 ) 372 373 # EA is refreshed automatically after save, so need this ... 374 self.data = { 375 'l10n_state' : self._PRW_province.GetValue().strip(), 376 'code_state' : self._TCTRL_code.GetValue().strip(), 377 'l10n_country' : self._PRW_country.GetValue().strip() 378 } 379 380 return True
381 #----------------------------------------------------------------
382 - def _save_as_update(self):
383 # update self.data and save the changes 384 #self.data[''] = 385 #self.data[''] = 386 #self.data[''] = 387 #self.data.save() 388 389 # do nothing for now (IOW, don't support updates) 390 return True
391 #----------------------------------------------------------------
392 - def _refresh_as_new(self):
393 self._PRW_province.SetText() 394 self._TCTRL_code.SetValue(u'') 395 self._PRW_country.SetText() 396 397 self._PRW_province.SetFocus()
398 #----------------------------------------------------------------
399 - def _refresh_from_existing(self):
400 self._PRW_province.SetText(self.data['l10n_state'], self.data['code_state']) 401 self._TCTRL_code.SetValue(self.data['code_state']) 402 self._PRW_country.SetText(self.data['l10n_country'], self.data['code_country']) 403 404 self._PRW_province.SetFocus()
405 #----------------------------------------------------------------
407 self._PRW_province.SetText() 408 self._TCTRL_code.SetValue(u'') 409 self._PRW_country.SetText(self.data['l10n_country'], self.data['code_country']) 410 411 self._PRW_province.SetFocus()
412 413 #============================================================ 414 # address phrasewheels and widgets 415 #============================================================
416 -def manage_addresses(parent=None):
417 418 if parent is None: 419 parent = wx.GetApp().GetTopWindow() 420 #------------------------------------------------------------ 421 def refresh(lctrl): 422 adrs = gmDemographicRecord.get_addresses(order_by = u'l10n_country, urb, street, number, subunit') 423 items = [ [ 424 a['street'], 425 gmTools.coalesce(a['notes_street'], u''), 426 a['number'], 427 gmTools.coalesce(a['subunit'], u''), 428 a['postcode'], 429 a['urb'], 430 gmTools.coalesce(a['suburb'], u''), 431 a['l10n_state'], 432 a['l10n_country'], 433 gmTools.coalesce(a['notes_subunit'], u'') 434 ] for a in adrs 435 ] 436 lctrl.set_string_items(items) 437 lctrl.set_data(adrs)
438 439 #------------------------------------------------------------ 440 cols = [ 441 _('Street'), 442 _('Street info'), 443 _('Number'), 444 _('Subunit'), 445 _('Postal code'), 446 _('Place'), 447 _('Suburb'), 448 _('Region'), 449 _('Country'), 450 _('Comment') 451 ] 452 gmListWidgets.get_choices_from_list ( 453 parent = parent, 454 caption = _('Showing addresses registered in GNUmed.'), 455 columns = cols, 456 single_selection = True, 457 refresh_callback = refresh 458 ) 459 #============================================================
460 -class cPersonAddressesManagerPnl(gmListWidgets.cGenericListManagerPnl):
461 """A list for managing a person's addresses. 462 463 Does NOT act on/listen to the current patient. 464 """
465 - def __init__(self, *args, **kwargs):
466 467 try: 468 self.__identity = kwargs['identity'] 469 del kwargs['identity'] 470 except KeyError: 471 self.__identity = None 472 473 gmListWidgets.cGenericListManagerPnl.__init__(self, *args, **kwargs) 474 475 self.new_callback = self._add_address 476 self.edit_callback = self._edit_address 477 self.delete_callback = self._del_address 478 self.refresh_callback = self.refresh 479 480 self.__init_ui() 481 self.refresh()
482 #-------------------------------------------------------- 483 # external API 484 #--------------------------------------------------------
485 - def refresh(self, *args, **kwargs):
486 if self.__identity is None: 487 self._LCTRL_items.set_string_items() 488 return 489 490 adrs = self.__identity.get_addresses() 491 self._LCTRL_items.set_string_items ( 492 items = [ [ 493 a['l10n_address_type'], 494 a['street'], 495 gmTools.coalesce(a['notes_street'], u''), 496 a['number'], 497 gmTools.coalesce(a['subunit'], u''), 498 a['postcode'], 499 a['urb'], 500 gmTools.coalesce(a['suburb'], u''), 501 a['l10n_state'], 502 a['l10n_country'], 503 gmTools.coalesce(a['notes_subunit'], u'') 504 ] for a in adrs 505 ] 506 ) 507 self._LCTRL_items.set_column_widths() 508 self._LCTRL_items.set_data(data = adrs)
509 #-------------------------------------------------------- 510 # internal helpers 511 #--------------------------------------------------------
512 - def __init_ui(self):
513 self._LCTRL_items.SetToolTipString(_('List of known addresses.')) 514 self._LCTRL_items.set_columns(columns = [ 515 _('Type'), 516 _('Street'), 517 _('Street info'), 518 _('Number'), 519 _('Subunit'), 520 _('Postal code'), 521 _('Place'), 522 _('Suburb'), 523 _('Region'), 524 _('Country'), 525 _('Comment') 526 ])
527 #--------------------------------------------------------
528 - def _add_address(self):
529 ea = cAddressEditAreaPnl(self, -1) 530 ea.identity = self.__identity 531 dlg = gmEditArea.cGenericEditAreaDlg(self, -1, edit_area = ea) 532 dlg.SetTitle(_('Adding new address')) 533 if dlg.ShowModal() == wx.ID_OK: 534 return True 535 return False
536 #--------------------------------------------------------
537 - def _edit_address(self, address):
538 ea = cAddressEditAreaPnl(self, -1, address = address) 539 ea.identity = self.__identity 540 dlg = gmEditArea.cGenericEditAreaDlg(self, -1, edit_area = ea) 541 dlg.SetTitle(_('Editing address')) 542 if dlg.ShowModal() == wx.ID_OK: 543 # did we add an entirely new address ? 544 # if so then unlink the old one as implied by "edit" 545 if ea.address['pk_address'] != address['pk_address']: 546 self.__identity.unlink_address(address = address) 547 return True 548 return False
549 #--------------------------------------------------------
550 - def _del_address(self, address):
551 go_ahead = gmGuiHelpers.gm_show_question ( 552 _( 'Are you sure you want to remove this\n' 553 "address from the patient's addresses ?\n" 554 '\n' 555 'The address itself will not be deleted\n' 556 'but it will no longer be associated with\n' 557 'this patient.' 558 ), 559 _('Removing address') 560 ) 561 if not go_ahead: 562 return False 563 self.__identity.unlink_address(address = address) 564 return True
565 #-------------------------------------------------------- 566 # properties 567 #--------------------------------------------------------
568 - def _get_identity(self):
569 return self.__identity
570
571 - def _set_identity(self, identity):
572 self.__identity = identity 573 self.refresh()
574 575 identity = property(_get_identity, _set_identity)
576 #============================================================ 577 from Gnumed.wxGladeWidgets import wxgGenericAddressEditAreaPnl 578
579 -class cAddressEditAreaPnl(wxgGenericAddressEditAreaPnl.wxgGenericAddressEditAreaPnl):
580 """An edit area for editing/creating an address. 581 582 Does NOT act on/listen to the current patient. 583 """
584 - def __init__(self, *args, **kwargs):
585 try: 586 self.address = kwargs['address'] 587 del kwargs['address'] 588 except KeyError: 589 self.address = None 590 591 wxgGenericAddressEditAreaPnl.wxgGenericAddressEditAreaPnl.__init__(self, *args, **kwargs) 592 593 self.identity = None 594 595 self.__register_interests() 596 self.refresh()
597 #-------------------------------------------------------- 598 # external API 599 #--------------------------------------------------------
600 - def refresh(self, address = None):
601 if address is not None: 602 self.address = address 603 604 if self.address is not None: 605 self._PRW_type.SetText(self.address['l10n_address_type']) 606 self._PRW_zip.SetText(self.address['postcode']) 607 self._PRW_street.SetText(self.address['street'], data = self.address['street']) 608 self._TCTRL_notes_street.SetValue(gmTools.coalesce(self.address['notes_street'], '')) 609 self._TCTRL_number.SetValue(self.address['number']) 610 self._TCTRL_subunit.SetValue(gmTools.coalesce(self.address['subunit'], '')) 611 self._PRW_suburb.SetText(gmTools.coalesce(self.address['suburb'], '')) 612 self._PRW_urb.SetText(self.address['urb'], data = self.address['urb']) 613 self._PRW_state.SetText(self.address['l10n_state'], data = self.address['code_state']) 614 self._PRW_country.SetText(self.address['l10n_country'], data = self.address['code_country']) 615 self._TCTRL_notes_subunit.SetValue(gmTools.coalesce(self.address['notes_subunit'], ''))
616 # FIXME: clear fields 617 # else: 618 # pass 619 #--------------------------------------------------------
620 - def save(self):
621 """Links address to patient, creating new address if necessary""" 622 623 if not self.__valid_for_save(): 624 return False 625 626 # link address to patient 627 try: 628 adr = self.identity.link_address ( 629 number = self._TCTRL_number.GetValue().strip(), 630 street = self._PRW_street.GetValue().strip(), 631 postcode = self._PRW_zip.GetValue().strip(), 632 urb = self._PRW_urb.GetValue().strip(), 633 state = self._PRW_state.GetData(), 634 country = self._PRW_country.GetData(), 635 subunit = gmTools.none_if(self._TCTRL_subunit.GetValue().strip(), u''), 636 suburb = gmTools.none_if(self._PRW_suburb.GetValue().strip(), u''), 637 id_type = self._PRW_type.GetData() 638 ) 639 except: 640 _log.exception('cannot save address') 641 gmGuiHelpers.gm_show_error ( 642 _('Cannot save address.\n\n' 643 'Does the state [%s]\n' 644 'exist in country [%s] ?' 645 ) % ( 646 self._PRW_state.GetValue().strip(), 647 self._PRW_country.GetValue().strip() 648 ), 649 _('Saving address') 650 ) 651 return False 652 653 notes = self._TCTRL_notes_street.GetValue().strip() 654 if notes != u'': 655 adr['notes_street'] = notes 656 notes = self._TCTRL_notes_subunit.GetValue().strip() 657 if notes != u'': 658 adr['notes_subunit'] = notes 659 adr.save_payload() 660 661 self.address = adr 662 663 return True
664 #-------------------------------------------------------- 665 # event handling 666 #--------------------------------------------------------
667 - def __register_interests(self):
668 self._PRW_zip.add_callback_on_lose_focus(self._on_zip_set) 669 self._PRW_country.add_callback_on_lose_focus(self._on_country_set)
670 #--------------------------------------------------------
671 - def _on_zip_set(self):
672 """Set the street, town, state and country according to entered zip code.""" 673 zip_code = self._PRW_zip.GetValue() 674 if zip_code.strip() == u'': 675 self._PRW_street.unset_context(context = u'zip') 676 self._PRW_urb.unset_context(context = u'zip') 677 self._PRW_state.unset_context(context = u'zip') 678 self._PRW_country.unset_context(context = u'zip') 679 else: 680 self._PRW_street.set_context(context = u'zip', val = zip_code) 681 self._PRW_urb.set_context(context = u'zip', val = zip_code) 682 self._PRW_state.set_context(context = u'zip', val = zip_code) 683 self._PRW_country.set_context(context = u'zip', val = zip_code)
684 #--------------------------------------------------------
685 - def _on_country_set(self):
686 """Set the states according to entered country.""" 687 country = self._PRW_country.GetData() 688 if country is None: 689 self._PRW_state.unset_context(context = 'country') 690 else: 691 self._PRW_state.set_context(context = 'country', val = country)
692 #-------------------------------------------------------- 693 # internal helpers 694 #--------------------------------------------------------
695 - def __valid_for_save(self):
696 697 # validate required fields 698 is_any_field_filled = False 699 700 required_fields = ( 701 self._PRW_type, 702 self._PRW_zip, 703 self._PRW_street, 704 self._TCTRL_number, 705 self._PRW_urb 706 ) 707 for field in required_fields: 708 if len(field.GetValue().strip()) == 0: 709 if is_any_field_filled: 710 field.SetBackgroundColour('pink') 711 field.SetFocus() 712 field.Refresh() 713 gmGuiHelpers.gm_show_error ( 714 _('Address details must be filled in completely or not at all.'), 715 _('Saving contact data') 716 ) 717 return False 718 else: 719 is_any_field_filled = True 720 field.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 721 field.Refresh() 722 723 required_fields = ( 724 self._PRW_state, 725 self._PRW_country 726 ) 727 for field in required_fields: 728 if field.GetData() is None: 729 if is_any_field_filled: 730 field.SetBackgroundColour('pink') 731 field.SetFocus() 732 field.Refresh() 733 gmGuiHelpers.gm_show_error ( 734 _('Address details must be filled in completely or not at all.'), 735 _('Saving contact data') 736 ) 737 return False 738 else: 739 is_any_field_filled = True 740 field.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 741 field.Refresh() 742 743 return True
744 #============================================================
745 -class cAddressMatchProvider(gmMatchProvider.cMatchProvider_SQL2):
746
747 - def __init__(self):
748 749 query = u""" 750 select * from ( 751 (select 752 pk_address, 753 (street || ' ' || number || coalesce(' (' || subunit || ')', '') || ', ' 754 || urb || coalesce(' (' || suburb || ')', '') || ', ' 755 || postcode 756 || coalesce(', ' || notes_street, '') 757 || coalesce(', ' || notes_subunit, '') 758 ) as address 759 from 760 dem.v_address 761 where 762 street %(fragment_condition)s 763 764 ) union ( 765 766 select 767 pk_address, 768 (street || ' ' || number || coalesce(' (' || subunit || ')', '') || ', ' 769 || urb || coalesce(' (' || suburb || ')', '') || ', ' 770 || postcode 771 || coalesce(', ' || notes_street, '') 772 || coalesce(', ' || notes_subunit, '') 773 ) as address 774 from 775 dem.v_address 776 where 777 postcode_street %(fragment_condition)s 778 779 ) union ( 780 781 select 782 pk_address, 783 (street || ' ' || number || coalesce(' (' || subunit || ')', '') || ', ' 784 || urb || coalesce(' (' || suburb || ')', '') || ', ' 785 || postcode 786 || coalesce(', ' || notes_street, '') 787 || coalesce(', ' || notes_subunit, '') 788 ) as address 789 from 790 dem.v_address 791 where 792 postcode_urb %(fragment_condition)s 793 ) 794 ) as union_result 795 order by union_result.address limit 50""" 796 797 gmMatchProvider.cMatchProvider_SQL2.__init__(self, queries = query) 798 799 self.setThresholds(2, 4, 6)
800 # self.word_separators = u'[ \t]+' 801 802 #============================================================
803 -class cAddressPhraseWheel(gmPhraseWheel.cPhraseWheel):
804
805 - def __init__(self, *args, **kwargs):
806 807 mp = cAddressMatchProvider() 808 gmPhraseWheel.cPhraseWheel.__init__ ( 809 self, 810 *args, 811 **kwargs 812 ) 813 self.matcher = cAddressMatchProvider() 814 self.SetToolTipString(_('Select an address by postcode or street name.')) 815 self.selection_only = True 816 self.__address = None 817 self.__old_pk = None
818 #--------------------------------------------------------
819 - def get_address(self):
820 821 pk = self.GetData() 822 823 if pk is None: 824 self.__address = None 825 return None 826 827 if self.__address is None: 828 self.__old_pk = pk 829 self.__address = gmDemographicRecord.cAddress(aPK_obj = pk) 830 else: 831 if pk != self.__old_pk: 832 self.__old_pk = pk 833 self.__address = gmDemographicRecord.cAddress(aPK_obj = pk) 834 835 return self.__address
836 #============================================================
837 -class cAddressTypePhraseWheel(gmPhraseWheel.cPhraseWheel):
838
839 - def __init__(self, *args, **kwargs):
840 841 query = u""" 842 select id, type from (( 843 select id, _(name) as type, 1 as rank 844 from dem.address_type 845 where _(name) %(fragment_condition)s 846 ) union ( 847 select id, name as type, 2 as rank 848 from dem.address_type 849 where name %(fragment_condition)s 850 )) as ur 851 order by 852 ur.rank, ur.type 853 """ 854 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query) 855 mp.setThresholds(1, 2, 4) 856 mp.word_separators = u'[ \t]+' 857 gmPhraseWheel.cPhraseWheel.__init__ ( 858 self, 859 *args, 860 **kwargs 861 ) 862 self.matcher = mp 863 self.SetToolTipString(_('Select the type of address.')) 864 # self.capitalisation_mode = gmTools.CAPS_FIRST 865 self.selection_only = True
866 #-------------------------------------------------------- 867 # def GetData(self, can_create=False): 868 # if self.data is None: 869 # if can_create: 870 # self.data = gmDocuments.create_document_type(self.GetValue().strip())['pk_doc_type'] # FIXME: error handling 871 # return self.data 872 #============================================================
873 -class cZipcodePhraseWheel(gmPhraseWheel.cPhraseWheel):
874
875 - def __init__(self, *args, **kwargs):
876 # FIXME: add possible context 877 query = u""" 878 (select distinct postcode, postcode from dem.street where postcode %(fragment_condition)s limit 20) 879 union 880 (select distinct postcode, postcode from dem.urb where postcode %(fragment_condition)s limit 20)""" 881 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query) 882 mp.setThresholds(2, 3, 15) 883 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 884 self.SetToolTipString(_("Type or select a zip code (postcode).")) 885 self.matcher = mp
886 #============================================================
887 -class cStreetPhraseWheel(gmPhraseWheel.cPhraseWheel):
888
889 - def __init__(self, *args, **kwargs):
890 context = { 891 u'ctxt_zip': { 892 u'where_part': u'and zip ilike %(zip)s', 893 u'placeholder': u'zip' 894 } 895 } 896 query = u""" 897 SELECT 898 data, 899 field_label, 900 list_label 901 FROM ( 902 903 SELECT DISTINCT ON (data) 904 street AS data, 905 street AS field_label, 906 street || ' (' || zip || ', ' || urb || coalesce(', ' || suburb, '') || ', ' || l10n_country || ')' AS list_label, 907 1 AS rank 908 FROM dem.v_zip2data 909 WHERE 910 street %(fragment_condition)s 911 %(ctxt_zip)s 912 913 UNION ALL 914 915 SELECT DISTINCT ON (data) 916 name AS data, 917 name AS field_label, 918 name || ' (' || postcode || coalesce(', ' || suburb, '') || ')' AS list_label, 919 2 AS rank 920 FROM dem.street 921 WHERE 922 name %(fragment_condition)s 923 924 ) AS matching_streets 925 ORDER BY rank, field_label 926 LIMIT 50""" 927 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query, context=context) 928 mp.setThresholds(3, 5, 8) 929 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 930 self.unset_context(context = u'zip') 931 932 self.SetToolTipString(_('Type or select a street.')) 933 self.capitalisation_mode = gmTools.CAPS_FIRST 934 self.matcher = mp
935 #============================================================
936 -class cSuburbPhraseWheel(gmPhraseWheel.cPhraseWheel):
937
938 - def __init__(self, *args, **kwargs):
939 940 query = """ 941 select distinct on (suburb) suburb, suburb 942 from dem.street 943 where suburb %(fragment_condition)s 944 order by suburb 945 limit 50 946 """ 947 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query) 948 mp.setThresholds(2, 3, 6) 949 gmPhraseWheel.cPhraseWheel.__init__ ( 950 self, 951 *args, 952 **kwargs 953 ) 954 955 self.SetToolTipString(_('Type or select the suburb.')) 956 self.capitalisation_mode = gmTools.CAPS_FIRST 957 self.matcher = mp
958 #============================================================
959 -class cUrbPhraseWheel(gmPhraseWheel.cPhraseWheel):
960
961 - def __init__(self, *args, **kwargs):
962 context = { 963 u'ctxt_zip': { 964 u'where_part': u'and zip ilike %(zip)s', 965 u'placeholder': u'zip' 966 } 967 } 968 query = u""" 969 SELECT DISTINCT ON (rank, data) 970 data, 971 field_label, 972 list_label 973 FROM ( 974 975 SELECT 976 urb AS data, 977 urb AS field_label, 978 urb || ' (' || zip || ', ' || state || ', ' || l10n_country || ')' AS list_label, 979 1 AS rank 980 FROM dem.v_zip2data 981 WHERE 982 urb %(fragment_condition)s 983 %(ctxt_zip)s 984 985 UNION ALL 986 987 SELECT 988 name AS data, 989 name AS field_label, 990 name || ' (' || postcode ||')' AS list_label, 991 2 AS rank 992 FROM dem.urb 993 WHERE 994 name %(fragment_condition)s 995 996 ) AS matching_urbs 997 ORDER BY rank, data 998 LIMIT 50""" 999 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query, context=context) 1000 mp.setThresholds(3, 5, 7) 1001 gmPhraseWheel.cPhraseWheel.__init__ ( 1002 self, 1003 *args, 1004 **kwargs 1005 ) 1006 self.unset_context(context = u'zip') 1007 1008 self.SetToolTipString(_('Type or select a city/town/village/dwelling.')) 1009 self.capitalisation_mode = gmTools.CAPS_FIRST 1010 self.matcher = mp
1011 1012 #============================================================ 1013 # communication channels related widgets 1014 #============================================================
1015 -def manage_comm_channel_types(parent=None):
1016 1017 if parent is None: 1018 parent = wx.GetApp().GetTopWindow() 1019 1020 #------------------------------------------------------------ 1021 def delete(channel=None): 1022 return gmDemographicRecord.delete_comm_channel_type(pk_channel_type = channel['pk'])
1023 #------------------------------------------------------------ 1024 def refresh(lctrl): 1025 wx.BeginBusyCursor() 1026 channel_types = gmDemographicRecord.get_comm_channel_types() 1027 lctrl.set_string_items([ (ct['l10n_description'], ct['description'], ct['pk']) for ct in channel_types ]) 1028 lctrl.set_data(channel_types) 1029 wx.EndBusyCursor() 1030 #------------------------------------------------------------ 1031 msg = _('\nThis lists the communication channel types known to GNUmed.\n') 1032 1033 gmListWidgets.get_choices_from_list ( 1034 parent = parent, 1035 msg = msg, 1036 caption = _('Managing communication types ...'), 1037 columns = [_('Channel'), _('System type'), '#'], 1038 single_selection = True, 1039 #new_callback = edit, 1040 #edit_callback = edit, 1041 delete_callback = delete, 1042 refresh_callback = refresh 1043 ) 1044 1045 #------------------------------------------------------------
1046 -class cCommChannelTypePhraseWheel(gmPhraseWheel.cPhraseWheel):
1047
1048 - def __init__(self, *args, **kwargs):
1049 1050 query = u""" 1051 SELECT 1052 data, 1053 field_label, 1054 list_label 1055 FROM ( 1056 SELECT DISTINCT ON (field_label) 1057 pk 1058 AS data, 1059 _(description) 1060 AS field_label, 1061 (_(description) || ' (' || description || ')') 1062 AS list_label 1063 FROM dem.enum_comm_types 1064 WHERE 1065 _(description) %(fragment_condition)s 1066 OR 1067 description %(fragment_condition)s 1068 ) AS ur 1069 ORDER BY 1070 ur.list_label 1071 """ 1072 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query) 1073 mp.setThresholds(1, 2, 4) 1074 mp.word_separators = u'[ \t]+' 1075 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 1076 self.matcher = mp 1077 self.SetToolTipString(_('Select the type of communications channel.')) 1078 self.selection_only = True
1079 1080 #------------------------------------------------------------ 1081 from Gnumed.wxGladeWidgets import wxgCommChannelEditAreaPnl 1082
1083 -class cCommChannelEditAreaPnl(wxgCommChannelEditAreaPnl.wxgCommChannelEditAreaPnl):
1084 """An edit area for editing/creating a comms channel. 1085 1086 Does NOT act on/listen to the current patient. 1087 """
1088 - def __init__(self, *args, **kwargs):
1089 try: 1090 self.channel = kwargs['comm_channel'] 1091 del kwargs['comm_channel'] 1092 except KeyError: 1093 self.channel = None 1094 1095 wxgCommChannelEditAreaPnl.wxgCommChannelEditAreaPnl.__init__(self, *args, **kwargs) 1096 1097 self.identity = None 1098 1099 self.refresh()
1100 #-------------------------------------------------------- 1101 # external API 1102 #--------------------------------------------------------
1103 - def refresh(self, comm_channel = None):
1104 if comm_channel is not None: 1105 self.channel = comm_channel 1106 1107 if self.channel is None: 1108 self._PRW_type.SetText(u'') 1109 self._TCTRL_url.SetValue(u'') 1110 # self._PRW_address.SetText(value = u'', data = None) 1111 self._CHBOX_confidential.SetValue(False) 1112 else: 1113 self._PRW_type.SetText(self.channel['l10n_comm_type']) 1114 self._TCTRL_url.SetValue(self.channel['url']) 1115 # self._PRW_address.SetData(data = self.channel['pk_address']) 1116 self._CHBOX_confidential.SetValue(self.channel['is_confidential']) 1117 1118 self._PRW_address.Disable()
1119 #--------------------------------------------------------
1120 - def save(self):
1121 """Links comm channel to patient.""" 1122 if self.channel is None: 1123 return self.__save_new() 1124 return self.__save_udpate()
1125 # self.channel['pk_address'] = self._PRW_address.GetData() 1126 #-------------------------------------------------------- 1127 # internal helpers 1128 #--------------------------------------------------------
1129 - def __save_new(self):
1130 if not self.__valid_for_save(): 1131 return False 1132 try: 1133 self.channel = self.identity.link_comm_channel ( 1134 comm_medium = self._PRW_type.GetValue().strip(), 1135 pk_channel_type = self._PRW_type.GetData(), 1136 url = self._TCTRL_url.GetValue().strip(), 1137 is_confidential = self._CHBOX_confidential.GetValue(), 1138 ) 1139 except gmPG2.dbapi.IntegrityError: 1140 _log.exception('error saving comm channel') 1141 gmDispatcher.send(signal = u'statustext', msg = _('Cannot save communications channel.'), beep = True) 1142 return False 1143 return True
1144 #--------------------------------------------------------
1145 - def __save_update(self):
1146 comm_type = self._PRW_type.GetValue().strip() 1147 if comm_type != u'': 1148 self.channel['comm_type'] = comm_type 1149 url = self._TCTRL_url.GetValue().strip() 1150 if url != u'': 1151 self.channel['url'] = url 1152 self.channel['is_confidential'] = self._CHBOX_confidential.GetValue() 1153 self.channel.save_payload() 1154 1155 return True
1156 #--------------------------------------------------------
1157 - def __valid_for_save(self):
1158 1159 no_errors = True 1160 1161 # if self._PRW_type.GetData() is None: 1162 if self._PRW_type.GetValue().strip() == u'': 1163 no_errors = False 1164 self._PRW_type.display_as_valid(False) 1165 self._PRW_type.SetFocus() 1166 else: 1167 self._PRW_type.display_as_valid(True) 1168 1169 if self._TCTRL_url.GetValue().strip() == u'': 1170 self._TCTRL_url.SetBackgroundColour('pink') 1171 self._TCTRL_url.SetFocus() 1172 self._TCTRL_url.Refresh() 1173 no_errors = False 1174 else: 1175 self._TCTRL_url.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 1176 self._TCTRL_url.Refresh() 1177 1178 return no_errors
1179 1180 #------------------------------------------------------------
1181 -class cPersonCommsManagerPnl(gmListWidgets.cGenericListManagerPnl):
1182 """A list for managing a person's comm channels. 1183 1184 Does NOT act on/listen to the current patient. 1185 """
1186 - def __init__(self, *args, **kwargs):
1187 1188 try: 1189 self.__identity = kwargs['identity'] 1190 del kwargs['identity'] 1191 except KeyError: 1192 self.__identity = None 1193 1194 gmListWidgets.cGenericListManagerPnl.__init__(self, *args, **kwargs) 1195 1196 self.new_callback = self._add_comm 1197 self.edit_callback = self._edit_comm 1198 self.delete_callback = self._del_comm 1199 self.refresh_callback = self.refresh 1200 1201 self.__init_ui() 1202 self.refresh()
1203 #-------------------------------------------------------- 1204 # external API 1205 #--------------------------------------------------------
1206 - def refresh(self, *args, **kwargs):
1207 if self.__identity is None: 1208 self._LCTRL_items.set_string_items() 1209 return 1210 1211 comms = self.__identity.get_comm_channels() 1212 self._LCTRL_items.set_string_items ( 1213 items = [ [ gmTools.bool2str(c['is_confidential'], u'X', u''), c['l10n_comm_type'], c['url'] ] for c in comms ] 1214 ) 1215 self._LCTRL_items.set_column_widths() 1216 self._LCTRL_items.set_data(data = comms)
1217 #-------------------------------------------------------- 1218 # internal helpers 1219 #--------------------------------------------------------
1220 - def __init_ui(self):
1221 self._LCTRL_items.SetToolTipString(_('List of known communication channels.')) 1222 self._LCTRL_items.set_columns(columns = [ 1223 _('confidential'), 1224 _('Type'), 1225 _('Value') 1226 ])
1227 #--------------------------------------------------------
1228 - def _add_comm(self):
1229 ea = cCommChannelEditAreaPnl(self, -1) 1230 ea.identity = self.__identity 1231 dlg = gmEditArea.cGenericEditAreaDlg(self, -1, edit_area = ea) 1232 dlg.SetTitle(_('Adding new communications channel')) 1233 if dlg.ShowModal() == wx.ID_OK: 1234 return True 1235 return False
1236 #--------------------------------------------------------
1237 - def _edit_comm(self, comm_channel):
1238 ea = cCommChannelEditAreaPnl(self, -1, comm_channel = comm_channel) 1239 ea.identity = self.__identity 1240 dlg = gmEditArea.cGenericEditAreaDlg(self, -1, edit_area = ea) 1241 dlg.SetTitle(_('Editing communications channel')) 1242 if dlg.ShowModal() == wx.ID_OK: 1243 return True 1244 return False
1245 #--------------------------------------------------------
1246 - def _del_comm(self, comm):
1247 go_ahead = gmGuiHelpers.gm_show_question ( 1248 _( 'Are you sure this patient can no longer\n' 1249 "be contacted via this channel ?" 1250 ), 1251 _('Removing communication channel') 1252 ) 1253 if not go_ahead: 1254 return False 1255 self.__identity.unlink_comm_channel(comm_channel = comm) 1256 return True
1257 #-------------------------------------------------------- 1258 # properties 1259 #--------------------------------------------------------
1260 - def _get_identity(self):
1261 return self.__identity
1262
1263 - def _set_identity(self, identity):
1264 self.__identity = identity 1265 self.refresh()
1266 1267 identity = property(_get_identity, _set_identity)
1268 1269 #------------------------------------------------------------ 1270 from Gnumed.wxGladeWidgets import wxgPersonContactsManagerPnl 1271
1272 -class cPersonContactsManagerPnl(wxgPersonContactsManagerPnl.wxgPersonContactsManagerPnl):
1273 """A panel for editing contact data for a person. 1274 1275 - provides access to: 1276 - addresses 1277 - communication paths 1278 1279 Does NOT act on/listen to the current patient. 1280 """
1281 - def __init__(self, *args, **kwargs):
1282 1283 wxgPersonContactsManagerPnl.wxgPersonContactsManagerPnl.__init__(self, *args, **kwargs) 1284 1285 self.__identity = None 1286 self.refresh()
1287 #-------------------------------------------------------- 1288 # external API 1289 #--------------------------------------------------------
1290 - def refresh(self):
1291 self._PNL_addresses.identity = self.__identity 1292 self._PNL_comms.identity = self.__identity
1293 #-------------------------------------------------------- 1294 # properties 1295 #--------------------------------------------------------
1296 - def _get_identity(self):
1297 return self.__identity
1298
1299 - def _set_identity(self, identity):
1300 self.__identity = identity 1301 self.refresh()
1302 1303 identity = property(_get_identity, _set_identity)
1304 1305 #============================================================ 1306 if __name__ == "__main__": 1307 1308 #--------------------------------------------------------
1309 - def test_state_prw():
1310 app = wx.PyWidgetTester(size = (200, 50)) 1311 pw = cStateSelectionPhraseWheel(app.frame, -1) 1312 # pw.set_context(context = u'zip', val = u'04318') 1313 # pw.set_context(context = u'country', val = u'Deutschland') 1314 app.frame.Show(True) 1315 app.MainLoop()
1316 #--------------------------------------------------------
1317 - def test_person_adrs_pnl():
1318 app = wx.PyWidgetTester(size = (600, 400)) 1319 widget = cPersonAddressesManagerPnl(app.frame, -1) 1320 widget.identity = activate_patient() 1321 app.frame.Show(True) 1322 app.MainLoop()
1323 #--------------------------------------------------------
1324 - def test_address_ea_pnl():
1325 app = wx.PyWidgetTester(size = (600, 400)) 1326 app.SetWidget(cAddressEditAreaPnl, address = gmDemographicRecord.cAddress(aPK_obj = 1)) 1327 app.MainLoop()
1328 #--------------------------------------------------------
1329 - def test_address_prw():
1330 app = wx.PyWidgetTester(size = (200, 50)) 1331 pw = cAddressPhraseWheel(app.frame, -1) 1332 app.frame.Show(True) 1333 app.MainLoop()
1334 #--------------------------------------------------------
1335 - def test_address_type_prw():
1336 app = wx.PyWidgetTester(size = (200, 50)) 1337 pw = cAddressTypePhraseWheel(app.frame, -1) 1338 app.frame.Show(True) 1339 app.MainLoop()
1340 #--------------------------------------------------------
1341 - def test_zipcode_prw():
1342 app = wx.PyWidgetTester(size = (200, 50)) 1343 pw = cZipcodePhraseWheel(app.frame, -1) 1344 app.frame.Show(True) 1345 app.MainLoop()
1346 #--------------------------------------------------------
1347 - def test_street_prw():
1348 app = wx.PyWidgetTester(size = (200, 50)) 1349 pw = cStreetPhraseWheel(app.frame, -1) 1350 # pw.set_context(context = u'zip', val = u'04318') 1351 app.frame.Show(True) 1352 app.MainLoop()
1353 #--------------------------------------------------------
1354 - def test_suburb_prw():
1355 app = wx.PyWidgetTester(size = (200, 50)) 1356 pw = cSuburbPhraseWheel(app.frame, -1) 1357 app.frame.Show(True) 1358 app.MainLoop()
1359 #--------------------------------------------------------
1360 - def test_urb_prw():
1361 app = wx.PyWidgetTester(size = (200, 50)) 1362 pw = cUrbPhraseWheel(app.frame, -1) 1363 app.frame.Show(True) 1364 pw.set_context(context = u'zip', val = u'04317') 1365 app.MainLoop()
1366 #--------------------------------------------------------
1367 - def test_person_comms_pnl():
1368 app = wx.PyWidgetTester(size = (600, 400)) 1369 widget = cPersonCommsManagerPnl(app.frame, -1) 1370 widget.identity = activate_patient() 1371 app.frame.Show(True) 1372 app.MainLoop()
1373 1374 #============================================================ 1375