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

Source Code for Module Gnumed.wxpython.gmAddressWidgets

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