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 (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 -class cPersonAddressesManagerPnl(gmListWidgets.cGenericListManagerPnl):
417 """A list for managing a person's addresses. 418 419 Does NOT act on/listen to the current patient. 420 """
421 - def __init__(self, *args, **kwargs):
422 423 try: 424 self.__identity = kwargs['identity'] 425 del kwargs['identity'] 426 except KeyError: 427 self.__identity = None 428 429 gmListWidgets.cGenericListManagerPnl.__init__(self, *args, **kwargs) 430 431 self.new_callback = self._add_address 432 self.edit_callback = self._edit_address 433 self.delete_callback = self._del_address 434 self.refresh_callback = self.refresh 435 436 self.__init_ui() 437 self.refresh()
438 #-------------------------------------------------------- 439 # external API 440 #--------------------------------------------------------
441 - def refresh(self, *args, **kwargs):
442 if self.__identity is None: 443 self._LCTRL_items.set_string_items() 444 return 445 446 adrs = self.__identity.get_addresses() 447 self._LCTRL_items.set_string_items ( 448 items = [ [ 449 a['l10n_address_type'], 450 a['street'], 451 gmTools.coalesce(a['notes_street'], u''), 452 a['number'], 453 gmTools.coalesce(a['subunit'], u''), 454 a['postcode'], 455 a['urb'], 456 gmTools.coalesce(a['suburb'], u''), 457 a['l10n_state'], 458 a['l10n_country'], 459 gmTools.coalesce(a['notes_subunit'], u'') 460 ] for a in adrs 461 ] 462 ) 463 self._LCTRL_items.set_column_widths() 464 self._LCTRL_items.set_data(data = adrs)
465 #-------------------------------------------------------- 466 # internal helpers 467 #--------------------------------------------------------
468 - def __init_ui(self):
469 self._LCTRL_items.SetToolTipString(_('List of known addresses.')) 470 self._LCTRL_items.set_columns(columns = [ 471 _('Type'), 472 _('Street'), 473 _('Street info'), 474 _('Number'), 475 _('Subunit'), 476 _('Postal code'), 477 _('Place'), 478 _('Suburb'), 479 _('Region'), 480 _('Country'), 481 _('Comment') 482 ])
483 #--------------------------------------------------------
484 - def _add_address(self):
485 ea = cAddressEditAreaPnl(self, -1) 486 ea.identity = self.__identity 487 dlg = gmEditArea.cGenericEditAreaDlg(self, -1, edit_area = ea) 488 dlg.SetTitle(_('Adding new address')) 489 if dlg.ShowModal() == wx.ID_OK: 490 return True 491 return False
492 #--------------------------------------------------------
493 - def _edit_address(self, address):
494 ea = cAddressEditAreaPnl(self, -1, address = address) 495 ea.identity = self.__identity 496 dlg = gmEditArea.cGenericEditAreaDlg(self, -1, edit_area = ea) 497 dlg.SetTitle(_('Editing address')) 498 if dlg.ShowModal() == wx.ID_OK: 499 # did we add an entirely new address ? 500 # if so then unlink the old one as implied by "edit" 501 if ea.address['pk_address'] != address['pk_address']: 502 self.__identity.unlink_address(address = address) 503 return True 504 return False
505 #--------------------------------------------------------
506 - def _del_address(self, address):
507 go_ahead = gmGuiHelpers.gm_show_question ( 508 _( 'Are you sure you want to remove this\n' 509 "address from the patient's addresses ?\n" 510 '\n' 511 'The address itself will not be deleted\n' 512 'but it will no longer be associated with\n' 513 'this patient.' 514 ), 515 _('Removing address') 516 ) 517 if not go_ahead: 518 return False 519 self.__identity.unlink_address(address = address) 520 return True
521 #-------------------------------------------------------- 522 # properties 523 #--------------------------------------------------------
524 - def _get_identity(self):
525 return self.__identity
526
527 - def _set_identity(self, identity):
528 self.__identity = identity 529 self.refresh()
530 531 identity = property(_get_identity, _set_identity)
532 #============================================================ 533 from Gnumed.wxGladeWidgets import wxgGenericAddressEditAreaPnl 534
535 -class cAddressEditAreaPnl(wxgGenericAddressEditAreaPnl.wxgGenericAddressEditAreaPnl):
536 """An edit area for editing/creating an address. 537 538 Does NOT act on/listen to the current patient. 539 """
540 - def __init__(self, *args, **kwargs):
541 try: 542 self.address = kwargs['address'] 543 del kwargs['address'] 544 except KeyError: 545 self.address = None 546 547 wxgGenericAddressEditAreaPnl.wxgGenericAddressEditAreaPnl.__init__(self, *args, **kwargs) 548 549 self.identity = None 550 551 self.__register_interests() 552 self.refresh()
553 #-------------------------------------------------------- 554 # external API 555 #--------------------------------------------------------
556 - def refresh(self, address = None):
557 if address is not None: 558 self.address = address 559 560 if self.address is not None: 561 self._PRW_type.SetText(self.address['l10n_address_type']) 562 self._PRW_zip.SetText(self.address['postcode']) 563 self._PRW_street.SetText(self.address['street'], data = self.address['street']) 564 self._TCTRL_notes_street.SetValue(gmTools.coalesce(self.address['notes_street'], '')) 565 self._TCTRL_number.SetValue(self.address['number']) 566 self._TCTRL_subunit.SetValue(gmTools.coalesce(self.address['subunit'], '')) 567 self._PRW_suburb.SetText(gmTools.coalesce(self.address['suburb'], '')) 568 self._PRW_urb.SetText(self.address['urb'], data = self.address['urb']) 569 self._PRW_state.SetText(self.address['l10n_state'], data = self.address['code_state']) 570 self._PRW_country.SetText(self.address['l10n_country'], data = self.address['code_country']) 571 self._TCTRL_notes_subunit.SetValue(gmTools.coalesce(self.address['notes_subunit'], ''))
572 # FIXME: clear fields 573 # else: 574 # pass 575 #--------------------------------------------------------
576 - def save(self):
577 """Links address to patient, creating new address if necessary""" 578 579 if not self.__valid_for_save(): 580 return False 581 582 # link address to patient 583 try: 584 adr = self.identity.link_address ( 585 number = self._TCTRL_number.GetValue().strip(), 586 street = self._PRW_street.GetValue().strip(), 587 postcode = self._PRW_zip.GetValue().strip(), 588 urb = self._PRW_urb.GetValue().strip(), 589 state = self._PRW_state.GetData(), 590 country = self._PRW_country.GetData(), 591 subunit = gmTools.none_if(self._TCTRL_subunit.GetValue().strip(), u''), 592 suburb = gmTools.none_if(self._PRW_suburb.GetValue().strip(), u''), 593 id_type = self._PRW_type.GetData() 594 ) 595 except: 596 _log.exception('cannot save address') 597 gmGuiHelpers.gm_show_error ( 598 _('Cannot save address.\n\n' 599 'Does the state [%s]\n' 600 'exist in country [%s] ?' 601 ) % ( 602 self._PRW_state.GetValue().strip(), 603 self._PRW_country.GetValue().strip() 604 ), 605 _('Saving address') 606 ) 607 return False 608 609 notes = self._TCTRL_notes_street.GetValue().strip() 610 if notes != u'': 611 adr['notes_street'] = notes 612 notes = self._TCTRL_notes_subunit.GetValue().strip() 613 if notes != u'': 614 adr['notes_subunit'] = notes 615 adr.save_payload() 616 617 self.address = adr 618 619 return True
620 #-------------------------------------------------------- 621 # event handling 622 #--------------------------------------------------------
623 - def __register_interests(self):
624 self._PRW_zip.add_callback_on_lose_focus(self._on_zip_set) 625 self._PRW_country.add_callback_on_lose_focus(self._on_country_set)
626 #--------------------------------------------------------
627 - def _on_zip_set(self):
628 """Set the street, town, state and country according to entered zip code.""" 629 zip_code = self._PRW_zip.GetValue() 630 if zip_code.strip() == u'': 631 self._PRW_street.unset_context(context = u'zip') 632 self._PRW_urb.unset_context(context = u'zip') 633 self._PRW_state.unset_context(context = u'zip') 634 self._PRW_country.unset_context(context = u'zip') 635 else: 636 self._PRW_street.set_context(context = u'zip', val = zip_code) 637 self._PRW_urb.set_context(context = u'zip', val = zip_code) 638 self._PRW_state.set_context(context = u'zip', val = zip_code) 639 self._PRW_country.set_context(context = u'zip', val = zip_code)
640 #--------------------------------------------------------
641 - def _on_country_set(self):
642 """Set the states according to entered country.""" 643 country = self._PRW_country.GetData() 644 if country is None: 645 self._PRW_state.unset_context(context = 'country') 646 else: 647 self._PRW_state.set_context(context = 'country', val = country)
648 #-------------------------------------------------------- 649 # internal helpers 650 #--------------------------------------------------------
651 - def __valid_for_save(self):
652 653 # validate required fields 654 is_any_field_filled = False 655 656 required_fields = ( 657 self._PRW_type, 658 self._PRW_zip, 659 self._PRW_street, 660 self._TCTRL_number, 661 self._PRW_urb 662 ) 663 for field in required_fields: 664 if len(field.GetValue().strip()) == 0: 665 if is_any_field_filled: 666 field.SetBackgroundColour('pink') 667 field.SetFocus() 668 field.Refresh() 669 gmGuiHelpers.gm_show_error ( 670 _('Address details must be filled in completely or not at all.'), 671 _('Saving contact data') 672 ) 673 return False 674 else: 675 is_any_field_filled = True 676 field.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 677 field.Refresh() 678 679 required_fields = ( 680 self._PRW_state, 681 self._PRW_country 682 ) 683 for field in required_fields: 684 if field.GetData() is None: 685 if is_any_field_filled: 686 field.SetBackgroundColour('pink') 687 field.SetFocus() 688 field.Refresh() 689 gmGuiHelpers.gm_show_error ( 690 _('Address details must be filled in completely or not at all.'), 691 _('Saving contact data') 692 ) 693 return False 694 else: 695 is_any_field_filled = True 696 field.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 697 field.Refresh() 698 699 return True
700 #============================================================
701 -class cAddressMatchProvider(gmMatchProvider.cMatchProvider_SQL2):
702
703 - def __init__(self):
704 705 query = u""" 706 select * from ( 707 (select 708 pk_address, 709 (street || ' ' || number || coalesce(' (' || subunit || ')', '') || ', ' 710 || urb || coalesce(' (' || suburb || ')', '') || ', ' 711 || postcode 712 || coalesce(', ' || notes_street, '') 713 || coalesce(', ' || notes_subunit, '') 714 ) as address 715 from 716 dem.v_address 717 where 718 street %(fragment_condition)s 719 720 ) union ( 721 722 select 723 pk_address, 724 (street || ' ' || number || coalesce(' (' || subunit || ')', '') || ', ' 725 || urb || coalesce(' (' || suburb || ')', '') || ', ' 726 || postcode 727 || coalesce(', ' || notes_street, '') 728 || coalesce(', ' || notes_subunit, '') 729 ) as address 730 from 731 dem.v_address 732 where 733 postcode_street %(fragment_condition)s 734 735 ) union ( 736 737 select 738 pk_address, 739 (street || ' ' || number || coalesce(' (' || subunit || ')', '') || ', ' 740 || urb || coalesce(' (' || suburb || ')', '') || ', ' 741 || postcode 742 || coalesce(', ' || notes_street, '') 743 || coalesce(', ' || notes_subunit, '') 744 ) as address 745 from 746 dem.v_address 747 where 748 postcode_urb %(fragment_condition)s 749 ) 750 ) as union_result 751 order by union_result.address limit 50""" 752 753 gmMatchProvider.cMatchProvider_SQL2.__init__(self, queries = query) 754 755 self.setThresholds(2, 4, 6)
756 # self.word_separators = u'[ \t]+' 757 758 #============================================================
759 -class cAddressPhraseWheel(gmPhraseWheel.cPhraseWheel):
760
761 - def __init__(self, *args, **kwargs):
762 763 mp = cAddressMatchProvider() 764 gmPhraseWheel.cPhraseWheel.__init__ ( 765 self, 766 *args, 767 **kwargs 768 ) 769 self.matcher = cAddressMatchProvider() 770 self.SetToolTipString(_('Select an address by postcode or street name.')) 771 self.selection_only = True 772 self.__address = None 773 self.__old_pk = None
774 #--------------------------------------------------------
775 - def get_address(self):
776 777 pk = self.GetData() 778 779 if pk is None: 780 self.__address = None 781 return None 782 783 if self.__address is None: 784 self.__old_pk = pk 785 self.__address = gmDemographicRecord.cAddress(aPK_obj = pk) 786 else: 787 if pk != self.__old_pk: 788 self.__old_pk = pk 789 self.__address = gmDemographicRecord.cAddress(aPK_obj = pk) 790 791 return self.__address
792 #============================================================
793 -class cAddressTypePhraseWheel(gmPhraseWheel.cPhraseWheel):
794
795 - def __init__(self, *args, **kwargs):
796 797 query = u""" 798 select id, type from (( 799 select id, _(name) as type, 1 as rank 800 from dem.address_type 801 where _(name) %(fragment_condition)s 802 ) union ( 803 select id, name as type, 2 as rank 804 from dem.address_type 805 where name %(fragment_condition)s 806 )) as ur 807 order by 808 ur.rank, ur.type 809 """ 810 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query) 811 mp.setThresholds(1, 2, 4) 812 mp.word_separators = u'[ \t]+' 813 gmPhraseWheel.cPhraseWheel.__init__ ( 814 self, 815 *args, 816 **kwargs 817 ) 818 self.matcher = mp 819 self.SetToolTipString(_('Select the type of address.')) 820 # self.capitalisation_mode = gmTools.CAPS_FIRST 821 self.selection_only = True
822 #-------------------------------------------------------- 823 # def GetData(self, can_create=False): 824 # if self.data is None: 825 # if can_create: 826 # self.data = gmDocuments.create_document_type(self.GetValue().strip())['pk_doc_type'] # FIXME: error handling 827 # return self.data 828 #============================================================
829 -class cZipcodePhraseWheel(gmPhraseWheel.cPhraseWheel):
830
831 - def __init__(self, *args, **kwargs):
832 # FIXME: add possible context 833 query = u""" 834 (select distinct postcode, postcode from dem.street where postcode %(fragment_condition)s limit 20) 835 union 836 (select distinct postcode, postcode from dem.urb where postcode %(fragment_condition)s limit 20)""" 837 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query) 838 mp.setThresholds(2, 3, 15) 839 gmPhraseWheel.cPhraseWheel.__init__ ( 840 self, 841 *args, 842 **kwargs 843 ) 844 self.SetToolTipString(_("Type or select a zip code (postcode).")) 845 self.matcher = mp
846 #============================================================
847 -class cStreetPhraseWheel(gmPhraseWheel.cPhraseWheel):
848
849 - def __init__(self, *args, **kwargs):
850 context = { 851 u'ctxt_zip': { 852 u'where_part': u'and zip ilike %(zip)s', 853 u'placeholder': u'zip' 854 } 855 } 856 query = u""" 857 select s1, s2 from ( 858 select s1, s2, rank from ( 859 select distinct on (street) 860 street as s1, street as s2, 1 as rank 861 from dem.v_zip2data 862 where 863 street %(fragment_condition)s 864 %(ctxt_zip)s 865 866 union all 867 868 select distinct on (name) 869 name as s1, name as s2, 2 as rank 870 from dem.street 871 where 872 name %(fragment_condition)s 873 874 ) as q2 875 ) as q1 order by rank, s2 limit 50""" 876 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query, context=context) 877 mp.setThresholds(3, 5, 8) 878 gmPhraseWheel.cPhraseWheel.__init__ ( 879 self, 880 *args, 881 **kwargs 882 ) 883 self.unset_context(context = u'zip') 884 885 self.SetToolTipString(_('Type or select a street.')) 886 self.capitalisation_mode = gmTools.CAPS_FIRST 887 self.matcher = mp
888 #============================================================
889 -class cSuburbPhraseWheel(gmPhraseWheel.cPhraseWheel):
890
891 - def __init__(self, *args, **kwargs):
892 893 query = """ 894 select distinct on (suburb) suburb, suburb 895 from dem.street 896 where suburb %(fragment_condition)s 897 order by suburb 898 limit 50 899 """ 900 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query) 901 mp.setThresholds(2, 3, 6) 902 gmPhraseWheel.cPhraseWheel.__init__ ( 903 self, 904 *args, 905 **kwargs 906 ) 907 908 self.SetToolTipString(_('Type or select the suburb.')) 909 self.capitalisation_mode = gmTools.CAPS_FIRST 910 self.matcher = mp
911 #============================================================
912 -class cUrbPhraseWheel(gmPhraseWheel.cPhraseWheel):
913
914 - def __init__(self, *args, **kwargs):
915 context = { 916 u'ctxt_zip': { 917 u'where_part': u'and zip ilike %(zip)s', 918 u'placeholder': u'zip' 919 } 920 } 921 query = u""" 922 select u1, u2 from ( 923 select distinct on (rank, u1) 924 u1, u2, rank 925 from ( 926 select 927 urb as u1, urb as u2, 1 as rank 928 from dem.v_zip2data 929 where 930 urb %(fragment_condition)s 931 %(ctxt_zip)s 932 933 union all 934 935 select 936 name as u1, name as u2, 2 as rank 937 from dem.urb 938 where 939 name %(fragment_condition)s 940 ) as union_result 941 order by rank, u1 942 ) as distincted_union 943 limit 50 944 """ 945 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query, context=context) 946 mp.setThresholds(3, 5, 7) 947 gmPhraseWheel.cPhraseWheel.__init__ ( 948 self, 949 *args, 950 **kwargs 951 ) 952 self.unset_context(context = u'zip') 953 954 self.SetToolTipString(_('Type or select a city/town/village/dwelling.')) 955 self.capitalisation_mode = gmTools.CAPS_FIRST 956 self.matcher = mp
957 958 #============================================================ 959 # communication channels related widgets 960 #============================================================
961 -class cCommChannelTypePhraseWheel(gmPhraseWheel.cPhraseWheel):
962
963 - def __init__(self, *args, **kwargs):
964 965 query = u""" 966 select pk, type from (( 967 select pk, _(description) as type, 1 as rank 968 from dem.enum_comm_types 969 where _(description) %(fragment_condition)s 970 ) union ( 971 select pk, description as type, 2 as rank 972 from dem.enum_comm_types 973 where description %(fragment_condition)s 974 )) as ur 975 order by 976 ur.rank, ur.type 977 """ 978 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query) 979 mp.setThresholds(1, 2, 4) 980 mp.word_separators = u'[ \t]+' 981 gmPhraseWheel.cPhraseWheel.__init__ ( 982 self, 983 *args, 984 **kwargs 985 ) 986 self.matcher = mp 987 self.SetToolTipString(_('Select the type of communications channel.')) 988 self.selection_only = True
989 990 #------------------------------------------------------------ 991 from Gnumed.wxGladeWidgets import wxgCommChannelEditAreaPnl 992
993 -class cCommChannelEditAreaPnl(wxgCommChannelEditAreaPnl.wxgCommChannelEditAreaPnl):
994 """An edit area for editing/creating a comms channel. 995 996 Does NOT act on/listen to the current patient. 997 """
998 - def __init__(self, *args, **kwargs):
999 try: 1000 self.channel = kwargs['comm_channel'] 1001 del kwargs['comm_channel'] 1002 except KeyError: 1003 self.channel = None 1004 1005 wxgCommChannelEditAreaPnl.wxgCommChannelEditAreaPnl.__init__(self, *args, **kwargs) 1006 1007 self.identity = None 1008 1009 self.refresh()
1010 #-------------------------------------------------------- 1011 # external API 1012 #--------------------------------------------------------
1013 - def refresh(self, comm_channel = None):
1014 if comm_channel is not None: 1015 self.channel = comm_channel 1016 1017 if self.channel is None: 1018 self._PRW_type.SetText(u'') 1019 self._TCTRL_url.SetValue(u'') 1020 self._PRW_address.SetText(value = u'', data = None) 1021 self._CHBOX_confidential.SetValue(False) 1022 else: 1023 self._PRW_type.SetText(self.channel['l10n_comm_type']) 1024 self._TCTRL_url.SetValue(self.channel['url']) 1025 self._PRW_address.SetData(data = self.channel['pk_address']) 1026 self._CHBOX_confidential.SetValue(self.channel['is_confidential'])
1027 #--------------------------------------------------------
1028 - def save(self):
1029 """Links comm channel to patient.""" 1030 if self.channel is None: 1031 if not self.__valid_for_save(): 1032 return False 1033 try: 1034 self.channel = self.identity.link_comm_channel ( 1035 pk_channel_type = self._PRW_type.GetData(), 1036 url = self._TCTRL_url.GetValue().strip(), 1037 is_confidential = self._CHBOX_confidential.GetValue(), 1038 ) 1039 except gmPG2.dbapi.IntegrityError: 1040 _log.exception('error saving comm channel') 1041 gmDispatcher.send(signal = u'statustext', msg = _('Cannot save communications channel.'), beep = True) 1042 return False 1043 else: 1044 comm_type = self._PRW_type.GetValue().strip() 1045 if comm_type != u'': 1046 self.channel['comm_type'] = comm_type 1047 url = self._TCTRL_url.GetValue().strip() 1048 if url != u'': 1049 self.channel['url'] = url 1050 self.channel['is_confidential'] = self._CHBOX_confidential.GetValue() 1051 1052 self.channel['pk_address'] = self._PRW_address.GetData() 1053 self.channel.save_payload() 1054 1055 return True
1056 #-------------------------------------------------------- 1057 # internal helpers 1058 #--------------------------------------------------------
1059 - def __valid_for_save(self):
1060 1061 no_errors = True 1062 1063 if self._PRW_type.GetData() is None: 1064 self._PRW_type.SetBackgroundColour('pink') 1065 self._PRW_type.SetFocus() 1066 self._PRW_type.Refresh() 1067 no_errors = False 1068 else: 1069 self._PRW_type.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 1070 self._PRW_type.Refresh() 1071 1072 if self._TCTRL_url.GetValue().strip() == u'': 1073 self._TCTRL_url.SetBackgroundColour('pink') 1074 self._TCTRL_url.SetFocus() 1075 self._TCTRL_url.Refresh() 1076 no_errors = False 1077 else: 1078 self._TCTRL_url.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW)) 1079 self._TCTRL_url.Refresh() 1080 1081 return no_errors
1082 1083 #------------------------------------------------------------
1084 -class cPersonCommsManagerPnl(gmListWidgets.cGenericListManagerPnl):
1085 """A list for managing a person's comm channels. 1086 1087 Does NOT act on/listen to the current patient. 1088 """
1089 - def __init__(self, *args, **kwargs):
1090 1091 try: 1092 self.__identity = kwargs['identity'] 1093 del kwargs['identity'] 1094 except KeyError: 1095 self.__identity = None 1096 1097 gmListWidgets.cGenericListManagerPnl.__init__(self, *args, **kwargs) 1098 1099 self.new_callback = self._add_comm 1100 self.edit_callback = self._edit_comm 1101 self.delete_callback = self._del_comm 1102 self.refresh_callback = self.refresh 1103 1104 self.__init_ui() 1105 self.refresh()
1106 #-------------------------------------------------------- 1107 # external API 1108 #--------------------------------------------------------
1109 - def refresh(self, *args, **kwargs):
1110 if self.__identity is None: 1111 self._LCTRL_items.set_string_items() 1112 return 1113 1114 comms = self.__identity.get_comm_channels() 1115 self._LCTRL_items.set_string_items ( 1116 items = [ [ gmTools.bool2str(c['is_confidential'], u'X', u''), c['l10n_comm_type'], c['url'] ] for c in comms ] 1117 ) 1118 self._LCTRL_items.set_column_widths() 1119 self._LCTRL_items.set_data(data = comms)
1120 #-------------------------------------------------------- 1121 # internal helpers 1122 #--------------------------------------------------------
1123 - def __init_ui(self):
1124 self._LCTRL_items.SetToolTipString(_('List of known communication channels.')) 1125 self._LCTRL_items.set_columns(columns = [ 1126 _('confidential'), 1127 _('Type'), 1128 _('Value') 1129 ])
1130 #--------------------------------------------------------
1131 - def _add_comm(self):
1132 ea = cCommChannelEditAreaPnl(self, -1) 1133 ea.identity = self.__identity 1134 dlg = gmEditArea.cGenericEditAreaDlg(self, -1, edit_area = ea) 1135 dlg.SetTitle(_('Adding new communications channel')) 1136 if dlg.ShowModal() == wx.ID_OK: 1137 return True 1138 return False
1139 #--------------------------------------------------------
1140 - def _edit_comm(self, comm_channel):
1141 ea = cCommChannelEditAreaPnl(self, -1, comm_channel = comm_channel) 1142 ea.identity = self.__identity 1143 dlg = gmEditArea.cGenericEditAreaDlg(self, -1, edit_area = ea) 1144 dlg.SetTitle(_('Editing communications channel')) 1145 if dlg.ShowModal() == wx.ID_OK: 1146 return True 1147 return False
1148 #--------------------------------------------------------
1149 - def _del_comm(self, comm):
1150 go_ahead = gmGuiHelpers.gm_show_question ( 1151 _( 'Are you sure this patient can no longer\n' 1152 "be contacted via this channel ?" 1153 ), 1154 _('Removing communication channel') 1155 ) 1156 if not go_ahead: 1157 return False 1158 self.__identity.unlink_comm_channel(comm_channel = comm) 1159 return True
1160 #-------------------------------------------------------- 1161 # properties 1162 #--------------------------------------------------------
1163 - def _get_identity(self):
1164 return self.__identity
1165
1166 - def _set_identity(self, identity):
1167 self.__identity = identity 1168 self.refresh()
1169 1170 identity = property(_get_identity, _set_identity)
1171 1172 #------------------------------------------------------------ 1173 from Gnumed.wxGladeWidgets import wxgPersonContactsManagerPnl 1174
1175 -class cPersonContactsManagerPnl(wxgPersonContactsManagerPnl.wxgPersonContactsManagerPnl):
1176 """A panel for editing contact data for a person. 1177 1178 - provides access to: 1179 - addresses 1180 - communication paths 1181 1182 Does NOT act on/listen to the current patient. 1183 """
1184 - def __init__(self, *args, **kwargs):
1185 1186 wxgPersonContactsManagerPnl.wxgPersonContactsManagerPnl.__init__(self, *args, **kwargs) 1187 1188 self.__identity = None 1189 self.refresh()
1190 #-------------------------------------------------------- 1191 # external API 1192 #--------------------------------------------------------
1193 - def refresh(self):
1194 self._PNL_addresses.identity = self.__identity 1195 self._PNL_comms.identity = self.__identity
1196 #-------------------------------------------------------- 1197 # properties 1198 #--------------------------------------------------------
1199 - def _get_identity(self):
1200 return self.__identity
1201
1202 - def _set_identity(self, identity):
1203 self.__identity = identity 1204 self.refresh()
1205 1206 identity = property(_get_identity, _set_identity)
1207 1208 #============================================================ 1209 if __name__ == "__main__": 1210 1211 #--------------------------------------------------------
1212 - def test_state_prw():
1213 app = wx.PyWidgetTester(size = (200, 50)) 1214 pw = cStateSelectionPhraseWheel(app.frame, -1) 1215 # pw.set_context(context = u'zip', val = u'04318') 1216 # pw.set_context(context = u'country', val = u'Deutschland') 1217 app.frame.Show(True) 1218 app.MainLoop()
1219 #--------------------------------------------------------
1220 - def test_person_adrs_pnl():
1221 app = wx.PyWidgetTester(size = (600, 400)) 1222 widget = cPersonAddressesManagerPnl(app.frame, -1) 1223 widget.identity = activate_patient() 1224 app.frame.Show(True) 1225 app.MainLoop()
1226 #--------------------------------------------------------
1227 - def test_address_ea_pnl():
1228 app = wx.PyWidgetTester(size = (600, 400)) 1229 app.SetWidget(cAddressEditAreaPnl, address = gmDemographicRecord.cAddress(aPK_obj = 1)) 1230 app.MainLoop()
1231 #--------------------------------------------------------
1232 - def test_address_prw():
1233 app = wx.PyWidgetTester(size = (200, 50)) 1234 pw = cAddressPhraseWheel(app.frame, -1) 1235 app.frame.Show(True) 1236 app.MainLoop()
1237 #--------------------------------------------------------
1238 - def test_address_type_prw():
1239 app = wx.PyWidgetTester(size = (200, 50)) 1240 pw = cAddressTypePhraseWheel(app.frame, -1) 1241 app.frame.Show(True) 1242 app.MainLoop()
1243 #--------------------------------------------------------
1244 - def test_zipcode_prw():
1245 app = wx.PyWidgetTester(size = (200, 50)) 1246 pw = cZipcodePhraseWheel(app.frame, -1) 1247 app.frame.Show(True) 1248 app.MainLoop()
1249 #--------------------------------------------------------
1250 - def test_street_prw():
1251 app = wx.PyWidgetTester(size = (200, 50)) 1252 pw = cStreetPhraseWheel(app.frame, -1) 1253 # pw.set_context(context = u'zip', val = u'04318') 1254 app.frame.Show(True) 1255 app.MainLoop()
1256 #--------------------------------------------------------
1257 - def test_suburb_prw():
1258 app = wx.PyWidgetTester(size = (200, 50)) 1259 pw = cSuburbPhraseWheel(app.frame, -1) 1260 app.frame.Show(True) 1261 app.MainLoop()
1262 #--------------------------------------------------------
1263 - def test_urb_prw():
1264 app = wx.PyWidgetTester(size = (200, 50)) 1265 pw = cUrbPhraseWheel(app.frame, -1) 1266 app.frame.Show(True) 1267 pw.set_context(context = u'zip', val = u'04317') 1268 app.MainLoop()
1269 #--------------------------------------------------------
1270 - def test_person_comms_pnl():
1271 app = wx.PyWidgetTester(size = (600, 400)) 1272 widget = cPersonCommsManagerPnl(app.frame, -1) 1273 widget.identity = activate_patient() 1274 app.frame.Show(True) 1275 app.MainLoop()
1276 1277 #============================================================ 1278