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

Source Code for Module Gnumed.wxpython.gmVaccWidgets

   1  """GNUmed immunisation/vaccination widgets. 
   2   
   3  Modelled after Richard Terry's design document. 
   4   
   5  copyright: authors 
   6  """ 
   7  #====================================================================== 
   8  __author__ = "R.Terry, S.J.Tan, K.Hilbert" 
   9  __license__ = "GPL v2 or later (details at http://www.gnu.org)" 
  10   
  11  import sys, time, logging 
  12   
  13   
  14  import wx 
  15   
  16   
  17  if __name__ == '__main__': 
  18          sys.path.insert(0, '../../') 
  19  from Gnumed.pycommon import gmDispatcher 
  20  from Gnumed.pycommon import gmMatchProvider 
  21  from Gnumed.pycommon import gmTools 
  22  from Gnumed.pycommon import gmI18N 
  23  from Gnumed.pycommon import gmCfg 
  24  from Gnumed.pycommon import gmDateTime 
  25  from Gnumed.pycommon import gmNetworkTools 
  26  from Gnumed.pycommon import gmPrinting 
  27   
  28  from Gnumed.business import gmPerson 
  29  from Gnumed.business import gmVaccination 
  30  from Gnumed.business import gmSurgery 
  31   
  32  from Gnumed.wxpython import gmPhraseWheel 
  33  from Gnumed.wxpython import gmTerryGuiParts 
  34  from Gnumed.wxpython import gmRegetMixin 
  35  from Gnumed.wxpython import gmGuiHelpers 
  36  from Gnumed.wxpython import gmEditArea 
  37  from Gnumed.wxpython import gmListWidgets 
  38  from Gnumed.wxpython import gmFormWidgets 
  39  from Gnumed.wxpython import gmMacro 
  40   
  41   
  42  _log = logging.getLogger('gm.vaccination') 
  43   
  44  #====================================================================== 
  45  # vaccination indication related widgets 
  46  #---------------------------------------------------------------------- 
47 -def manage_vaccination_indications(parent=None):
48 49 if parent is None: 50 parent = wx.GetApp().GetTopWindow() 51 #------------------------------------------------------------ 52 def refresh(lctrl): 53 inds = gmVaccination.get_indications(order_by = 'l10n_description') 54 55 items = [ [ 56 i['l10n_description'], 57 gmTools.coalesce ( 58 i['atcs_single_indication'], 59 u'', 60 u'%s' 61 ), 62 gmTools.coalesce ( 63 i['atcs_combi_indication'], 64 u'', 65 u'%s' 66 ), 67 u'%s' % i['id'] 68 ] for i in inds ] 69 70 lctrl.set_string_items(items) 71 lctrl.set_data(inds)
72 #------------------------------------------------------------ 73 gmListWidgets.get_choices_from_list ( 74 parent = parent, 75 msg = _('\nConditions preventable by vaccination as currently known to GNUmed.\n'), 76 caption = _('Showing vaccination preventable conditions.'), 77 columns = [ _('Condition'), _('ATCs: single-condition vaccines'), _('ATCs: multi-condition vaccines'), u'#' ], 78 single_selection = True, 79 refresh_callback = refresh 80 ) 81 #----------------------------------------------------------------------
82 -def pick_indications(parent=None, msg=None, right_column=None, picks=None):
83 84 if parent is None: 85 parent = wx.GetApp().GetTopWindow() 86 87 if msg is None: 88 msg = _('Pick the relevant indications.') 89 90 if right_column is None: 91 right_columns = ['This vaccine'] 92 else: 93 right_columns = [right_column] 94 95 picker = gmListWidgets.cItemPickerDlg(parent, -1, msg = msg) 96 picker.set_columns(columns = [_('Known indications')], columns_right = right_columns) 97 inds = gmVaccination.get_indications(order_by = 'l10n_description') 98 picker.set_choices ( 99 choices = [ i['l10n_description'] for i in inds ], 100 data = inds 101 ) 102 picker.set_picks ( 103 picks = [ p['l10n_description'] for p in picks ], 104 data = picks 105 ) 106 result = picker.ShowModal() 107 108 if result == wx.ID_CANCEL: 109 picker.Destroy() 110 return None 111 112 picks = picker.picks 113 picker.Destroy() 114 return picks
115 116 #====================================================================== 117 # vaccines related widgets 118 #----------------------------------------------------------------------
119 -def edit_vaccine(parent=None, vaccine=None, single_entry=True):
120 ea = cVaccineEAPnl(parent = parent, id = -1) 121 ea.data = vaccine 122 ea.mode = gmTools.coalesce(vaccine, 'new', 'edit') 123 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = single_entry) 124 dlg.SetTitle(gmTools.coalesce(vaccine, _('Adding new vaccine'), _('Editing vaccine'))) 125 if dlg.ShowModal() == wx.ID_OK: 126 dlg.Destroy() 127 return True 128 dlg.Destroy() 129 return False
130 #----------------------------------------------------------------------
131 -def manage_vaccines(parent=None):
132 133 if parent is None: 134 parent = wx.GetApp().GetTopWindow() 135 #------------------------------------------------------------ 136 def delete(vaccine=None): 137 deleted = gmVaccination.delete_vaccine(vaccine = vaccine['pk_vaccine']) 138 if deleted: 139 return True 140 141 gmGuiHelpers.gm_show_info ( 142 _( 143 'Cannot delete vaccine\n' 144 '\n' 145 ' %s - %s (#%s)\n' 146 '\n' 147 'It is probably documented in a vaccination.' 148 ) % ( 149 vaccine['vaccine'], 150 vaccine['preparation'], 151 vaccine['pk_vaccine'] 152 ), 153 _('Deleting vaccine') 154 ) 155 156 return False
157 #------------------------------------------------------------ 158 def edit(vaccine=None): 159 return edit_vaccine(parent = parent, vaccine = vaccine, single_entry = True) 160 #------------------------------------------------------------ 161 def refresh(lctrl): 162 vaccines = gmVaccination.get_vaccines(order_by = 'vaccine') 163 164 items = [ [ 165 u'%s' % v['pk_brand'], 166 u'%s%s' % ( 167 v['vaccine'], 168 gmTools.bool2subst ( 169 v['is_fake_vaccine'], 170 u' (%s)' % _('fake'), 171 u'' 172 ) 173 ), 174 v['preparation'], 175 #u'%s (%s)' % (v['route_abbreviation'], v['route_description']), 176 #gmTools.bool2subst(v['is_live'], gmTools.u_checkmark_thin, u'', u'?'), 177 gmTools.coalesce(v['atc_code'], u''), 178 u'%s%s' % ( 179 gmTools.coalesce(v['min_age'], u'?'), 180 gmTools.coalesce(v['max_age'], u'?', u' - %s'), 181 ), 182 gmTools.coalesce(v['comment'], u'') 183 ] for v in vaccines ] 184 lctrl.set_string_items(items) 185 lctrl.set_data(vaccines) 186 #------------------------------------------------------------ 187 gmListWidgets.get_choices_from_list ( 188 parent = parent, 189 msg = _('\nThe vaccines currently known to GNUmed.\n'), 190 caption = _('Showing vaccines.'), 191 #columns = [ u'#', _('Brand'), _('Preparation'), _(u'Route'), _('Live'), _('ATC'), _('Age range'), _('Comment') ], 192 columns = [ u'#', _('Brand'), _('Preparation'), _('ATC'), _('Age range'), _('Comment') ], 193 single_selection = True, 194 refresh_callback = refresh, 195 edit_callback = edit, 196 new_callback = edit, 197 delete_callback = delete 198 ) 199 #----------------------------------------------------------------------
200 -class cBatchNoPhraseWheel(gmPhraseWheel.cPhraseWheel):
201
202 - def __init__(self, *args, **kwargs):
203 204 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 205 206 context = { 207 u'ctxt_vaccine': { 208 u'where_part': u'AND pk_vaccine = %(pk_vaccine)s', 209 u'placeholder': u'pk_vaccine' 210 } 211 } 212 213 query = u""" 214 SELECT data, field_label, list_label FROM ( 215 216 SELECT distinct on (field_label) 217 data, 218 field_label, 219 list_label, 220 rank 221 FROM (( 222 -- batch_no by vaccine 223 SELECT 224 batch_no AS data, 225 batch_no AS field_label, 226 batch_no || ' (' || vaccine || ')' AS list_label, 227 1 as rank 228 FROM 229 clin.v_pat_vaccinations 230 WHERE 231 batch_no %(fragment_condition)s 232 %(ctxt_vaccine)s 233 ) UNION ALL ( 234 -- batch_no for any vaccine 235 SELECT 236 batch_no AS data, 237 batch_no AS field_label, 238 batch_no || ' (' || vaccine || ')' AS list_label, 239 2 AS rank 240 FROM 241 clin.v_pat_vaccinations 242 WHERE 243 batch_no %(fragment_condition)s 244 ) 245 246 ) AS matching_batch_nos 247 248 ) as unique_matches 249 250 ORDER BY rank, list_label 251 LIMIT 25 252 """ 253 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query, context = context) 254 mp.setThresholds(1, 2, 3) 255 self.matcher = mp 256 257 self.unset_context(context = u'pk_vaccine') 258 self.SetToolTipString(_('Enter or select the batch/lot number of the vaccine used.')) 259 self.selection_only = False
260 #----------------------------------------------------------------------
261 -class cVaccinePhraseWheel(gmPhraseWheel.cPhraseWheel):
262
263 - def __init__(self, *args, **kwargs):
264 265 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 266 267 # consider ATCs in ref.branded_drug and vacc_indication 268 query = u""" 269 SELECT data, list_label, field_label FROM ( 270 271 SELECT DISTINCT ON (data) 272 data, 273 list_label, 274 field_label 275 FROM (( 276 -- fragment -> vaccine 277 SELECT 278 pk_vaccine AS data, 279 vaccine || ' (' || array_to_string(l10n_indications, ', ') || ')' AS list_label, 280 vaccine AS field_label 281 FROM 282 clin.v_vaccines 283 WHERE 284 vaccine %(fragment_condition)s 285 286 ) union all ( 287 288 -- fragment -> localized indication -> vaccines 289 SELECT 290 pk_vaccine AS data, 291 vaccine || ' (' || array_to_string(l10n_indications, ', ') || ')' AS list_label, 292 vaccine AS field_label 293 FROM 294 clin.v_indications4vaccine 295 WHERE 296 l10n_indication %(fragment_condition)s 297 298 ) union all ( 299 300 -- fragment -> indication -> vaccines 301 SELECT 302 pk_vaccine AS data, 303 vaccine || ' (' || array_to_string(indications, ', ') || ')' AS list_label, 304 vaccine AS field_label 305 FROM 306 clin.v_indications4vaccine 307 WHERE 308 indication %(fragment_condition)s 309 ) 310 ) AS distinct_total 311 312 ) AS total 313 314 ORDER by list_label 315 LIMIT 25 316 """ 317 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query) 318 mp.setThresholds(1, 2, 3) 319 self.matcher = mp 320 321 self.selection_only = True
322 #------------------------------------------------------------------
323 - def _data2instance(self):
324 return gmVaccination.cVaccine(aPK_obj = self.GetData())
325 #---------------------------------------------------------------------- 326 from Gnumed.wxGladeWidgets import wxgVaccineEAPnl 327
328 -class cVaccineEAPnl(wxgVaccineEAPnl.wxgVaccineEAPnl, gmEditArea.cGenericEditAreaMixin):
329
330 - def __init__(self, *args, **kwargs):
331 try: 332 data = kwargs['vaccine'] 333 del kwargs['vaccine'] 334 except KeyError: 335 data = None 336 337 wxgVaccineEAPnl.wxgVaccineEAPnl.__init__(self, *args, **kwargs) 338 gmEditArea.cGenericEditAreaMixin.__init__(self) 339 340 self.mode = 'new' 341 self.data = data 342 if data is not None: 343 self.mode = 'edit'
344 #----------------------------------------------------------------
345 - def __refresh_indications(self):
346 self._TCTRL_indications.SetValue(u'') 347 if len(self.__indications) == 0: 348 return 349 self._TCTRL_indications.SetValue(u'- ' + u'\n- '.join([ i['l10n_description'] for i in self.__indications ]))
350 #---------------------------------------------------------------- 351 # generic Edit Area mixin API 352 #----------------------------------------------------------------
353 - def _valid_for_save(self):
354 355 has_errors = False 356 357 if self._PRW_brand.GetValue().strip() == u'': 358 has_errors = True 359 self._PRW_brand.display_as_valid(False) 360 else: 361 self._PRW_brand.display_as_valid(True) 362 363 if self._PRW_atc.GetValue().strip() in [u'', u'J07']: 364 self._PRW_atc.display_as_valid(True) 365 else: 366 if self._PRW_atc.GetData() is None: 367 self._PRW_atc.display_as_valid(True) 368 else: 369 has_errors = True 370 self._PRW_atc.display_as_valid(False) 371 372 val = self._PRW_age_min.GetValue().strip() 373 if val == u'': 374 self._PRW_age_min.display_as_valid(True) 375 else: 376 if gmDateTime.str2interval(val) is None: 377 has_errors = True 378 self._PRW_age_min.display_as_valid(False) 379 else: 380 self._PRW_age_min.display_as_valid(True) 381 382 val = self._PRW_age_max.GetValue().strip() 383 if val == u'': 384 self._PRW_age_max.display_as_valid(True) 385 else: 386 if gmDateTime.str2interval(val) is None: 387 has_errors = True 388 self._PRW_age_max.display_as_valid(False) 389 else: 390 self._PRW_age_max.display_as_valid(True) 391 392 # are we editing ? 393 ask_user = (self.mode == 'edit') 394 # is this vaccine in use ? 395 ask_user = (ask_user and self.data.is_in_use) 396 # a change ... 397 ask_user = ask_user and ( 398 # ... of brand ... 399 (self.data['pk_brand'] != self._PRW_route.GetData()) 400 or 401 # ... or indications ? 402 (set(self.data['pk_indications']) != set([ i['id'] for i in self.__indications ])) 403 ) 404 405 if ask_user: 406 do_it = gmGuiHelpers.gm_show_question ( 407 aTitle = _('Saving vaccine'), 408 aMessage = _( 409 u'This vaccine is already in use:\n' 410 u'\n' 411 u' "%s"\n' 412 u' (%s)\n' 413 u'\n' 414 u'Are you absolutely positively sure that\n' 415 u'you really want to edit this vaccine ?\n' 416 '\n' 417 u'This will change the vaccine name and/or target\n' 418 u'conditions in each patient this vaccine was\n' 419 u'used in to document a vaccination with.\n' 420 ) % ( 421 self._PRW_brand.GetValue().strip(), 422 u', '.join(self.data['l10n_indications']) 423 ) 424 ) 425 if not do_it: 426 has_errors = True 427 428 return (has_errors is False)
429 #----------------------------------------------------------------
430 - def _save_as_new(self):
431 432 if len(self.__indications) == 0: 433 gmGuiHelpers.gm_show_info ( 434 aTitle = _('Saving vaccine'), 435 aMessage = _('You must select at least one indication.') 436 ) 437 return False 438 439 # save the data as a new instance 440 data = gmVaccination.create_vaccine ( 441 pk_brand = self._PRW_brand.GetData(), 442 brand_name = self._PRW_brand.GetValue(), 443 pk_indications = [ i['id'] for i in self.__indications ] 444 ) 445 446 # data['is_live'] = self._CHBOX_live.GetValue() 447 val = self._PRW_age_min.GetValue().strip() 448 if val != u'': 449 data['min_age'] = gmDateTime.str2interval(val) 450 val = self._PRW_age_max.GetValue().strip() 451 if val != u'': 452 data['max_age'] = gmDateTime.str2interval(val) 453 val = self._TCTRL_comment.GetValue().strip() 454 if val != u'': 455 data['comment'] = val 456 457 data.save() 458 459 drug = data.brand 460 drug['is_fake_brand'] = self._CHBOX_fake.GetValue() 461 val = self._PRW_atc.GetData() 462 if val is not None: 463 if val != u'J07': 464 drug['atc'] = val.strip() 465 drug.save() 466 467 # must be done very late or else the property access 468 # will refresh the display such that later field 469 # access will return empty values 470 self.data = data 471 472 return True
473 #----------------------------------------------------------------
474 - def _save_as_update(self):
475 476 if len(self.__indications) == 0: 477 gmGuiHelpers.gm_show_info ( 478 aTitle = _('Saving vaccine'), 479 aMessage = _('You must select at least one indication.') 480 ) 481 return False 482 483 drug = self.data.brand 484 drug['brand'] = self._PRW_brand.GetValue().strip() 485 drug['is_fake_brand'] = self._CHBOX_fake.GetValue() 486 val = self._PRW_atc.GetData() 487 if val is not None: 488 if val != u'J07': 489 drug['atc'] = val.strip() 490 drug.save() 491 492 # the validator already asked for changes so just do it 493 self.data.set_indications(pk_indications = [ i['id'] for i in self.__indications ]) 494 495 # self.data['is_live'] = self._CHBOX_live.GetValue() 496 val = self._PRW_age_min.GetValue().strip() 497 if val != u'': 498 self.data['min_age'] = gmDateTime.str2interval(val) 499 if val != u'': 500 self.data['max_age'] = gmDateTime.str2interval(val) 501 val = self._TCTRL_comment.GetValue().strip() 502 if val != u'': 503 self.data['comment'] = val 504 505 self.data.save() 506 return True
507 #----------------------------------------------------------------
508 - def _refresh_as_new(self):
509 self._PRW_brand.SetText(value = u'', data = None, suppress_smarts = True) 510 # self._CHBOX_live.SetValue(True) 511 self._CHBOX_fake.SetValue(False) 512 self._PRW_atc.SetText(value = u'', data = None, suppress_smarts = True) 513 self._PRW_age_min.SetText(value = u'', data = None, suppress_smarts = True) 514 self._PRW_age_max.SetText(value = u'', data = None, suppress_smarts = True) 515 self._TCTRL_comment.SetValue(u'') 516 517 self.__indications = [] 518 self.__refresh_indications() 519 520 self._PRW_brand.SetFocus()
521 #----------------------------------------------------------------
522 - def _refresh_from_existing(self):
523 self._PRW_brand.SetText(value = self.data['vaccine'], data = self.data['pk_brand']) 524 # self._CHBOX_live.SetValue(self.data['is_live']) 525 self._CHBOX_fake.SetValue(self.data['is_fake_vaccine']) 526 self._PRW_atc.SetText(value = self.data['atc_code'], data = self.data['atc_code']) 527 if self.data['min_age'] is None: 528 self._PRW_age_min.SetText(value = u'', data = None, suppress_smarts = True) 529 else: 530 self._PRW_age_min.SetText ( 531 value = gmDateTime.format_interval(self.data['min_age'], gmDateTime.acc_years), 532 data = self.data['min_age'] 533 ) 534 if self.data['max_age'] is None: 535 self._PRW_age_max.SetText(value = u'', data = None, suppress_smarts = True) 536 else: 537 self._PRW_age_max.SetText ( 538 value = gmDateTime.format_interval(self.data['max_age'], gmDateTime.acc_years), 539 data = self.data['max_age'] 540 ) 541 self._TCTRL_comment.SetValue(gmTools.coalesce(self.data['comment'], u'')) 542 543 self.__indications = self.data.indications 544 self.__refresh_indications() 545 546 self._PRW_brand.SetFocus()
547 #----------------------------------------------------------------
549 self._refresh_as_new()
550 #---------------------------------------------------------------- 551 #----------------------------------------------------------------
553 event.Skip() 554 555 picks = pick_indications ( 556 parent = self, 557 msg = _('Pick the diseases this vaccine protects against.'), 558 right_column = _('This vaccine'), 559 picks = self.__indications 560 ) 561 if picks is None: 562 return 563 564 self.__indications = picks 565 self.__refresh_indications()
566 567 #====================================================================== 568 # vaccination related widgets 569 #---------------------------------------------------------------------- 627 628 #----------------------------------------------------------------------
629 -def edit_vaccination(parent=None, vaccination=None, single_entry=True):
630 ea = cVaccinationEAPnl(parent = parent, id = -1) 631 ea.data = vaccination 632 ea.mode = gmTools.coalesce(vaccination, 'new', 'edit') 633 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = single_entry) 634 dlg.SetTitle(gmTools.coalesce(vaccination, _('Adding new vaccinations'), _('Editing vaccination'))) 635 if dlg.ShowModal() == wx.ID_OK: 636 dlg.Destroy() 637 return True 638 dlg.Destroy() 639 if not single_entry: 640 return True 641 return False
642 643 #----------------------------------------------------------------------
644 -def manage_vaccinations(parent=None):
645 646 pat = gmPerson.gmCurrentPatient() 647 emr = pat.get_emr() 648 649 if parent is None: 650 parent = wx.GetApp().GetTopWindow() 651 #------------------------------------------------------------ 652 def browse2schedules(vaccination=None): 653 dbcfg = gmCfg.cCfgSQL() 654 url = dbcfg.get2 ( 655 option = 'external.urls.vaccination_plans', 656 workplace = gmSurgery.gmCurrentPractice().active_workplace, 657 bias = 'user', 658 default = u'http://www.bundesaerztekammer.de/downloads/STIKOEmpf2011.pdf' 659 ) 660 661 gmNetworkTools.open_url_in_browser(url = url) 662 return False
663 #------------------------------------------------------------ 664 def print_vaccs(vaccination=None): 665 print_vaccinations(parent = parent) 666 return False 667 #------------------------------------------------------------ 668 def edit(vaccination=None): 669 return edit_vaccination(parent = parent, vaccination = vaccination, single_entry = (vaccination is not None)) 670 #------------------------------------------------------------ 671 def delete(vaccination=None): 672 gmVaccination.delete_vaccination(vaccination = vaccination['pk_vaccination']) 673 return True 674 #------------------------------------------------------------ 675 def refresh(lctrl): 676 677 vaccs = emr.get_vaccinations(order_by = 'date_given DESC, pk_vaccination') 678 679 items = [ [ 680 v['date_given'].strftime('%Y %B %d').decode(gmI18N.get_encoding()), 681 v['vaccine'], 682 u', '.join(v['l10n_indications']), 683 v['batch_no'], 684 gmTools.coalesce(v['site'], u''), 685 gmTools.coalesce(v['reaction'], u''), 686 gmTools.coalesce(v['comment'], u'') 687 ] for v in vaccs ] 688 689 lctrl.set_string_items(items) 690 lctrl.set_data(vaccs) 691 #------------------------------------------------------------ 692 gmListWidgets.get_choices_from_list ( 693 parent = parent, 694 msg = _('\nComplete vaccination history for this patient.\n'), 695 caption = _('Showing vaccinations.'), 696 columns = [ _('Date'), _('Vaccine'), _(u'Intended to protect from'), _('Batch'), _('Site'), _('Reaction'), _('Comment') ], 697 single_selection = True, 698 refresh_callback = refresh, 699 new_callback = edit, 700 edit_callback = edit, 701 delete_callback = delete, 702 left_extra_button = (_('Print'), _('Print vaccinations using a template.'), print_vaccs), 703 right_extra_button = (_('Vaccination Plans'), _('Open a browser showing vaccination schedules.'), browse2schedules) 704 ) 705 #---------------------------------------------------------------------- 706 from Gnumed.wxGladeWidgets import wxgVaccinationEAPnl 707
708 -class cVaccinationEAPnl(wxgVaccinationEAPnl.wxgVaccinationEAPnl, gmEditArea.cGenericEditAreaMixin):
709 """ 710 - warn on apparent duplicates 711 - ask if "missing" (= previous, non-recorded) vaccinations 712 should be estimated and saved (add note "auto-generated") 713 714 Batch No (http://www.fao.org/docrep/003/v9952E12.htm) 715 """
716 - def __init__(self, *args, **kwargs):
717 718 try: 719 data = kwargs['vaccination'] 720 del kwargs['vaccination'] 721 except KeyError: 722 data = None 723 724 wxgVaccinationEAPnl.wxgVaccinationEAPnl.__init__(self, *args, **kwargs) 725 gmEditArea.cGenericEditAreaMixin.__init__(self) 726 727 self.mode = 'new' 728 self.data = data 729 if data is not None: 730 self.mode = 'edit' 731 732 self.__init_ui()
733 #----------------------------------------------------------------
734 - def __init_ui(self):
735 # adjust phrasewheels etc 736 self._PRW_vaccine.add_callback_on_lose_focus(self._on_PRW_vaccine_lost_focus) 737 self._PRW_provider.selection_only = False 738 self._PRW_reaction.add_callback_on_lose_focus(self._on_PRW_reaction_lost_focus) 739 if self.mode == 'edit': 740 self._BTN_select_indications.Disable()
741 #----------------------------------------------------------------
742 - def _on_PRW_vaccine_lost_focus(self):
743 744 vaccine = self._PRW_vaccine.GetData(as_instance=True) 745 746 # if we are editing we do not allow using indications rather than a vaccine 747 if self.mode == u'edit': 748 if vaccine is None: 749 self._PRW_batch.unset_context(context = 'pk_vaccine') 750 self.__indications = [] 751 else: 752 self._PRW_batch.set_context(context = 'pk_vaccine', val = vaccine['pk_vaccine']) 753 self.__indications = vaccine.indications 754 # we are entering a new vaccination 755 else: 756 if vaccine is None: 757 self._PRW_batch.unset_context(context = 'pk_vaccine') 758 self.__indications = [] 759 self._BTN_select_indications.Enable() 760 else: 761 self._PRW_batch.set_context(context = 'pk_vaccine', val = vaccine['pk_vaccine']) 762 self.__indications = vaccine.indications 763 self._BTN_select_indications.Disable() 764 765 self.__refresh_indications()
766 #----------------------------------------------------------------
768 if self._PRW_reaction.GetValue().strip() == u'': 769 self._BTN_report.Enable(False) 770 else: 771 self._BTN_report.Enable(True)
772 #----------------------------------------------------------------
773 - def __refresh_indications(self):
774 self._TCTRL_indications.SetValue(u'') 775 if len(self.__indications) == 0: 776 return 777 self._TCTRL_indications.SetValue(u'- ' + u'\n- '.join([ i['l10n_description'] for i in self.__indications ]))
778 #---------------------------------------------------------------- 779 # generic Edit Area mixin API 780 #----------------------------------------------------------------
781 - def _valid_for_save(self):
782 783 has_errors = False 784 785 if not self._PRW_date_given.is_valid_timestamp(allow_empty = False): 786 has_errors = True 787 788 vaccine = self._PRW_vaccine.GetData(as_instance = True) 789 790 # we are editing, require vaccine rather than indications 791 if self.mode == u'edit': 792 if vaccine is None: 793 has_errors = True 794 self._PRW_vaccine.display_as_valid(False) 795 else: 796 self._PRW_vaccine.display_as_valid(True) 797 # we are creating, allow either vaccine or indications 798 else: 799 if vaccine is None: 800 if len(self.__indications) == 0: 801 self._PRW_vaccine.display_as_valid(False) 802 has_errors = True 803 else: 804 self._PRW_vaccine.display_as_valid(True) 805 else: 806 self._PRW_vaccine.display_as_valid(True) 807 808 if self._PRW_batch.GetValue().strip() == u'': 809 has_errors = True 810 self._PRW_batch.display_as_valid(False) 811 else: 812 self._PRW_batch.display_as_valid(True) 813 814 if self._PRW_episode.GetValue().strip() == u'': 815 self._PRW_episode.SetText(value = _('prevention')) 816 817 return (has_errors is False)
818 #----------------------------------------------------------------
819 - def _save_as_new(self):
820 821 vaccine = self._PRW_vaccine.GetData() 822 if vaccine is None: 823 data = self.__save_new_from_indications() 824 else: 825 data = self.__save_new_from_vaccine(vaccine = vaccine) 826 827 # must be done very late or else the property access 828 # will refresh the display such that later field 829 # access will return empty values 830 self.data = data 831 832 return True
833 #----------------------------------------------------------------
835 836 if len(self.__indications) == 0: 837 gmGuiHelpers.gm_show_info ( 838 aTitle = _('Saving vaccination'), 839 aMessage = _('You must select at least one indication.') 840 ) 841 return False 842 843 vaccine = gmVaccination.map_indications2generic_vaccine(indications = [ i['description'] for i in self.__indications ]) 844 845 if vaccine is None: 846 for ind in self.__indications: 847 vaccine = gmVaccination.map_indications2generic_vaccine(indications = [ind['description']]) 848 data = self.__save_new_from_vaccine(vaccine = vaccine['pk_vaccine']) 849 else: 850 data = self.__save_new_from_vaccine(vaccine = vaccine['pk_vaccine']) 851 852 return data
853 #----------------------------------------------------------------
854 - def __save_new_from_vaccine(self, vaccine=None):
855 856 emr = gmPerson.gmCurrentPatient().get_emr() 857 858 data = emr.add_vaccination ( 859 episode = self._PRW_episode.GetData(can_create = True, is_open = False), 860 vaccine = vaccine, 861 batch_no = self._PRW_batch.GetValue().strip() 862 ) 863 864 if self._CHBOX_anamnestic.GetValue() is True: 865 data['soap_cat'] = u's' 866 else: 867 data['soap_cat'] = u'p' 868 869 data['date_given'] = self._PRW_date_given.GetData() 870 data['site'] = self._PRW_site.GetValue().strip() 871 data['pk_provider'] = self._PRW_provider.GetData() 872 data['reaction'] = self._PRW_reaction.GetValue().strip() 873 data['comment'] = self._TCTRL_comment.GetValue().strip() 874 875 data.save() 876 877 return data
878 #----------------------------------------------------------------
879 - def _save_as_update(self):
880 881 if self._CHBOX_anamnestic.GetValue() is True: 882 self.data['soap_cat'] = u's' 883 else: 884 self.data['soap_cat'] = u'p' 885 886 self.data['date_given'] = self._PRW_date_given.GetData() 887 self.data['pk_vaccine'] = self._PRW_vaccine.GetData() 888 self.data['batch_no'] = self._PRW_batch.GetValue().strip() 889 self.data['pk_episode'] = self._PRW_episode.GetData(can_create = True, is_open = False) 890 self.data['site'] = self._PRW_site.GetValue().strip() 891 self.data['pk_provider'] = self._PRW_provider.GetData() 892 self.data['reaction'] = self._PRW_reaction.GetValue().strip() 893 self.data['comment'] = self._TCTRL_comment.GetValue().strip() 894 895 self.data.save() 896 897 return True
898 #----------------------------------------------------------------
899 - def _refresh_as_new(self):
900 self._PRW_date_given.SetText(data = gmDateTime.pydt_now_here()) 901 self._CHBOX_anamnestic.SetValue(False) 902 self._PRW_vaccine.SetText(value = u'', data = None, suppress_smarts = True) 903 self._PRW_batch.unset_context(context = 'pk_vaccine') 904 self._PRW_batch.SetValue(u'') 905 self._PRW_episode.SetText(value = u'', data = None, suppress_smarts = True) 906 self._PRW_site.SetValue(u'') 907 self._PRW_provider.SetData(data = None) 908 self._PRW_reaction.SetText(value = u'', data = None, suppress_smarts = True) 909 self._BTN_report.Enable(False) 910 self._TCTRL_comment.SetValue(u'') 911 912 self.__indications = [] 913 self.__refresh_indications() 914 self._BTN_select_indications.Enable() 915 916 self._PRW_date_given.SetFocus()
917 #----------------------------------------------------------------
918 - def _refresh_from_existing(self):
919 self._PRW_date_given.SetText(data = self.data['date_given']) 920 if self.data['soap_cat'] == u's': 921 self._CHBOX_anamnestic.SetValue(True) 922 else: 923 self._CHBOX_anamnestic.SetValue(False) 924 self._PRW_vaccine.SetText(value = self.data['vaccine'], data = self.data['pk_vaccine']) 925 926 self._PRW_batch.SetValue(self.data['batch_no']) 927 self._PRW_episode.SetData(data = self.data['pk_episode']) 928 self._PRW_site.SetValue(gmTools.coalesce(self.data['site'], u'')) 929 self._PRW_provider.SetData(self.data['pk_provider']) 930 self._PRW_reaction.SetValue(gmTools.coalesce(self.data['reaction'], u'')) 931 if self.data['reaction'] is None: 932 self._BTN_report.Enable(False) 933 else: 934 self._BTN_report.Enable(True) 935 self._TCTRL_comment.SetValue(gmTools.coalesce(self.data['comment'], u'')) 936 937 self.__indications = self.data.vaccine.indications 938 self.__refresh_indications() 939 self._BTN_select_indications.Disable() 940 941 self._PRW_date_given.SetFocus()
942 #----------------------------------------------------------------
944 self._PRW_date_given.SetText(data = self.data['date_given']) 945 #self._CHBOX_anamnestic.SetValue(False) 946 self._PRW_vaccine.SetText(value = self.data['vaccine'], data = self.data['pk_vaccine']) 947 948 self._PRW_batch.set_context(context = 'pk_vaccine', val = self.data['pk_vaccine']) 949 self._PRW_batch.SetValue(u'') 950 951 self._PRW_episode.SetData(data = self.data['pk_episode']) 952 self._PRW_site.SetValue(gmTools.coalesce(self.data['site'], u'')) 953 self._PRW_provider.SetData(self.data['pk_provider']) 954 self._PRW_reaction.SetValue(u'') 955 self._BTN_report.Enable(False) 956 self._TCTRL_comment.SetValue(u'') 957 958 self.__indications = self.data.vaccine.indications 959 self.__refresh_indications() 960 self._BTN_select_indications.Enable() 961 962 self._PRW_date_given.SetFocus()
963 #---------------------------------------------------------------- 964 # event handlers 965 #----------------------------------------------------------------
966 - def _on_report_button_pressed(self, event):
967 event.Skip() 968 dbcfg = gmCfg.cCfgSQL() 969 url = dbcfg.get2 ( 970 option = u'external.urls.report_vaccine_ADR', 971 workplace = gmSurgery.gmCurrentPractice().active_workplace, 972 bias = u'user', 973 default = u'http://www.pei.de/cln_042/SharedDocs/Downloads/fachkreise/uaw/meldeboegen/b-ifsg-meldebogen,templateId=raw,property=publicationFile.pdf/b-ifsg-meldebogen.pdf' 974 ) 975 976 if url.strip() == u'': 977 url = dbcfg.get2 ( 978 option = u'external.urls.report_ADR', 979 workplace = gmSurgery.gmCurrentPractice().active_workplace, 980 bias = u'user' 981 ) 982 gmNetworkTools.open_url_in_browser(url = url)
983 #----------------------------------------------------------------
984 - def _on_add_vaccine_button_pressed(self, event):
985 edit_vaccine(parent = self, vaccine = None, single_entry = False)
986 # FIXME: could set newly generated vaccine here 987 #----------------------------------------------------------------
989 event.Skip() 990 991 picks = pick_indications ( 992 parent = self, 993 msg = _('Pick the diseases this vaccination was given against.'), 994 right_column = _('This vaccine'), 995 picks = self.__indications 996 ) 997 if picks is None: 998 return 999 1000 self.__indications = picks 1001 self.__refresh_indications()
1002 1003 #====================================================================== 1004 #====================================================================== 1005 #====================================================================== 1006 #======================================================================
1007 -class cImmunisationsPanel(wx.Panel, gmRegetMixin.cRegetOnPaintMixin):
1008
1009 - def __init__(self, parent, id):
1010 wx.Panel.__init__(self, parent, id, wx.DefaultPosition, wx.DefaultSize, wx.RAISED_BORDER) 1011 gmRegetMixin.cRegetOnPaintMixin.__init__(self) 1012 self.__pat = gmPerson.gmCurrentPatient() 1013 # do this here so "import cImmunisationsPanel from gmVaccWidgets" works 1014 self.ID_VaccinatedIndicationsList = wx.NewId() 1015 self.ID_VaccinationsPerRegimeList = wx.NewId() 1016 self.ID_MissingShots = wx.NewId() 1017 self.ID_ActiveSchedules = wx.NewId() 1018 self.__do_layout() 1019 self.__register_interests() 1020 self.__reset_ui_content()
1021 #----------------------------------------------------
1022 - def __do_layout(self):
1023 #----------------------------------------------- 1024 # top part 1025 #----------------------------------------------- 1026 pnl_UpperCaption = gmTerryGuiParts.cHeadingCaption(self, -1, _(" IMMUNISATIONS ")) 1027 self.editarea = cVaccinationEditArea(self, -1, wx.DefaultPosition, wx.DefaultSize, wx.NO_BORDER) 1028 1029 #----------------------------------------------- 1030 # middle part 1031 #----------------------------------------------- 1032 # divider headings below editing area 1033 indications_heading = gmTerryGuiParts.cDividerCaption(self, -1, _("Indications")) 1034 vaccinations_heading = gmTerryGuiParts.cDividerCaption(self, -1, _("Vaccinations")) 1035 schedules_heading = gmTerryGuiParts.cDividerCaption(self, -1, _("Active Schedules")) 1036 szr_MiddleCap = wx.BoxSizer(wx.HORIZONTAL) 1037 szr_MiddleCap.Add(indications_heading, 4, wx.EXPAND) 1038 szr_MiddleCap.Add(vaccinations_heading, 6, wx.EXPAND) 1039 szr_MiddleCap.Add(schedules_heading, 10, wx.EXPAND) 1040 1041 # left list: indications for which vaccinations have been given 1042 self.LBOX_vaccinated_indications = wx.ListBox( 1043 parent = self, 1044 id = self.ID_VaccinatedIndicationsList, 1045 choices = [], 1046 style = wx.LB_HSCROLL | wx.LB_NEEDED_SB | wx.SUNKEN_BORDER 1047 ) 1048 self.LBOX_vaccinated_indications.SetFont(wx.Font(12,wx.SWISS, wx.NORMAL, wx.NORMAL, False, '')) 1049 1050 # right list: when an indication has been selected on the left 1051 # display the corresponding vaccinations on the right 1052 self.LBOX_given_shots = wx.ListBox( 1053 parent = self, 1054 id = self.ID_VaccinationsPerRegimeList, 1055 choices = [], 1056 style = wx.LB_HSCROLL | wx.LB_NEEDED_SB | wx.SUNKEN_BORDER 1057 ) 1058 self.LBOX_given_shots.SetFont(wx.Font(12,wx.SWISS, wx.NORMAL, wx.NORMAL, False, '')) 1059 1060 self.LBOX_active_schedules = wx.ListBox ( 1061 parent = self, 1062 id = self.ID_ActiveSchedules, 1063 choices = [], 1064 style = wx.LB_HSCROLL | wx.LB_NEEDED_SB | wx.SUNKEN_BORDER 1065 ) 1066 self.LBOX_active_schedules.SetFont(wx.Font(12, wx.SWISS, wx.NORMAL, wx.NORMAL, False, '')) 1067 1068 szr_MiddleLists = wx.BoxSizer(wx.HORIZONTAL) 1069 szr_MiddleLists.Add(self.LBOX_vaccinated_indications, 4, wx.EXPAND) 1070 szr_MiddleLists.Add(self.LBOX_given_shots, 6, wx.EXPAND) 1071 szr_MiddleLists.Add(self.LBOX_active_schedules, 10, wx.EXPAND) 1072 1073 #--------------------------------------------- 1074 # bottom part 1075 #--------------------------------------------- 1076 missing_heading = gmTerryGuiParts.cDividerCaption(self, -1, _("Missing Immunisations")) 1077 szr_BottomCap = wx.BoxSizer(wx.HORIZONTAL) 1078 szr_BottomCap.Add(missing_heading, 1, wx.EXPAND) 1079 1080 self.LBOX_missing_shots = wx.ListBox ( 1081 parent = self, 1082 id = self.ID_MissingShots, 1083 choices = [], 1084 style = wx.LB_HSCROLL | wx.LB_NEEDED_SB | wx.SUNKEN_BORDER 1085 ) 1086 self.LBOX_missing_shots.SetFont(wx.Font(12, wx.SWISS, wx.NORMAL, wx.NORMAL, False, '')) 1087 1088 szr_BottomLists = wx.BoxSizer(wx.HORIZONTAL) 1089 szr_BottomLists.Add(self.LBOX_missing_shots, 1, wx.EXPAND) 1090 1091 # alert caption 1092 pnl_AlertCaption = gmTerryGuiParts.cAlertCaption(self, -1, _(' Alerts ')) 1093 1094 #--------------------------------------------- 1095 # add all elements to the main background sizer 1096 #--------------------------------------------- 1097 self.mainsizer = wx.BoxSizer(wx.VERTICAL) 1098 self.mainsizer.Add(pnl_UpperCaption, 0, wx.EXPAND) 1099 self.mainsizer.Add(self.editarea, 6, wx.EXPAND) 1100 self.mainsizer.Add(szr_MiddleCap, 0, wx.EXPAND) 1101 self.mainsizer.Add(szr_MiddleLists, 4, wx.EXPAND) 1102 self.mainsizer.Add(szr_BottomCap, 0, wx.EXPAND) 1103 self.mainsizer.Add(szr_BottomLists, 4, wx.EXPAND) 1104 self.mainsizer.Add(pnl_AlertCaption, 0, wx.EXPAND) 1105 1106 self.SetAutoLayout(True) 1107 self.SetSizer(self.mainsizer) 1108 self.mainsizer.Fit(self)
1109 #----------------------------------------------------
1110 - def __register_interests(self):
1111 # wxPython events 1112 wx.EVT_SIZE(self, self.OnSize) 1113 wx.EVT_LISTBOX(self, self.ID_VaccinatedIndicationsList, self._on_vaccinated_indication_selected) 1114 wx.EVT_LISTBOX_DCLICK(self, self.ID_VaccinationsPerRegimeList, self._on_given_shot_selected) 1115 wx.EVT_LISTBOX_DCLICK(self, self.ID_MissingShots, self._on_missing_shot_selected) 1116 # wx.EVT_RIGHT_UP(self.lb1, self.EvtRightButton) 1117 1118 # client internal signals 1119 gmDispatcher.connect(signal= u'post_patient_selection', receiver=self._schedule_data_reget) 1120 gmDispatcher.connect(signal= u'vaccinations_updated', receiver=self._schedule_data_reget)
1121 #---------------------------------------------------- 1122 # event handlers 1123 #----------------------------------------------------
1124 - def OnSize (self, event):
1125 w, h = event.GetSize() 1126 self.mainsizer.SetDimension (0, 0, w, h)
1127 #----------------------------------------------------
1128 - def _on_given_shot_selected(self, event):
1129 """Paste previously given shot into edit area. 1130 """ 1131 self.editarea.set_data(aVacc=event.GetClientData())
1132 #----------------------------------------------------
1133 - def _on_missing_shot_selected(self, event):
1134 self.editarea.set_data(aVacc = event.GetClientData())
1135 #----------------------------------------------------
1136 - def _on_vaccinated_indication_selected(self, event):
1137 """Update right hand middle list to show vaccinations given for selected indication.""" 1138 ind_list = event.GetEventObject() 1139 selected_item = ind_list.GetSelection() 1140 ind = ind_list.GetClientData(selected_item) 1141 # clear list 1142 self.LBOX_given_shots.Set([]) 1143 emr = self.__pat.get_emr() 1144 shots = emr.get_vaccinations(indications = [ind]) 1145 # FIXME: use Set() for entire array (but problem with client_data) 1146 for shot in shots: 1147 if shot['is_booster']: 1148 marker = 'B' 1149 else: 1150 marker = '#%s' % shot['seq_no'] 1151 label = '%s - %s: %s' % (marker, shot['date'].strftime('%m/%Y'), shot['vaccine']) 1152 self.LBOX_given_shots.Append(label, shot)
1153 #----------------------------------------------------
1154 - def __reset_ui_content(self):
1155 # clear edit area 1156 self.editarea.set_data() 1157 # clear lists 1158 self.LBOX_vaccinated_indications.Clear() 1159 self.LBOX_given_shots.Clear() 1160 self.LBOX_active_schedules.Clear() 1161 self.LBOX_missing_shots.Clear()
1162 #----------------------------------------------------
1163 - def _populate_with_data(self):
1164 # clear lists 1165 self.LBOX_vaccinated_indications.Clear() 1166 self.LBOX_given_shots.Clear() 1167 self.LBOX_active_schedules.Clear() 1168 self.LBOX_missing_shots.Clear() 1169 1170 emr = self.__pat.get_emr() 1171 1172 t1 = time.time() 1173 # populate vaccinated-indications list 1174 # FIXME: consider adding virtual indication "most recent" to 1175 # FIXME: display most recent of all indications as suggested by Syan 1176 status, indications = emr.get_vaccinated_indications() 1177 # FIXME: would be faster to use Set() but can't 1178 # use Set(labels, client_data), and have to know 1179 # line position in SetClientData :-( 1180 for indication in indications: 1181 self.LBOX_vaccinated_indications.Append(indication[1], indication[0]) 1182 # self.LBOX_vaccinated_indications.Set(lines) 1183 # self.LBOX_vaccinated_indications.SetClientData(data) 1184 print "vaccinated indications took", time.time()-t1, "seconds" 1185 1186 t1 = time.time() 1187 # populate active schedules list 1188 scheds = emr.get_scheduled_vaccination_regimes() 1189 if scheds is None: 1190 label = _('ERROR: cannot retrieve active vaccination schedules') 1191 self.LBOX_active_schedules.Append(label) 1192 elif len(scheds) == 0: 1193 label = _('no active vaccination schedules') 1194 self.LBOX_active_schedules.Append(label) 1195 else: 1196 for sched in scheds: 1197 label = _('%s for %s (%s shots): %s') % (sched['regime'], sched['l10n_indication'], sched['shots'], sched['comment']) 1198 self.LBOX_active_schedules.Append(label) 1199 print "active schedules took", time.time()-t1, "seconds" 1200 1201 t1 = time.time() 1202 # populate missing-shots list 1203 missing_shots = emr.get_missing_vaccinations() 1204 print "getting missing shots took", time.time()-t1, "seconds" 1205 if missing_shots is None: 1206 label = _('ERROR: cannot retrieve due/overdue vaccinations') 1207 self.LBOX_missing_shots.Append(label, None) 1208 return True 1209 # due 1210 due_template = _('%.0d weeks left: shot %s for %s in %s, due %s (%s)') 1211 overdue_template = _('overdue %.0dyrs %.0dwks: shot %s for %s in schedule "%s" (%s)') 1212 for shot in missing_shots['due']: 1213 if shot['overdue']: 1214 years, days_left = divmod(shot['amount_overdue'].days, 364.25) 1215 weeks = days_left / 7 1216 # amount_overdue, seq_no, indication, regime, vacc_comment 1217 label = overdue_template % ( 1218 years, 1219 weeks, 1220 shot['seq_no'], 1221 shot['l10n_indication'], 1222 shot['regime'], 1223 shot['vacc_comment'] 1224 ) 1225 self.LBOX_missing_shots.Append(label, shot) 1226 else: 1227 # time_left, seq_no, regime, latest_due, vacc_comment 1228 label = due_template % ( 1229 shot['time_left'].days / 7, 1230 shot['seq_no'], 1231 shot['indication'], 1232 shot['regime'], 1233 shot['latest_due'].strftime('%m/%Y'), 1234 shot['vacc_comment'] 1235 ) 1236 self.LBOX_missing_shots.Append(label, shot) 1237 # booster 1238 lbl_template = _('due now: booster for %s in schedule "%s" (%s)') 1239 for shot in missing_shots['boosters']: 1240 # indication, regime, vacc_comment 1241 label = lbl_template % ( 1242 shot['l10n_indication'], 1243 shot['regime'], 1244 shot['vacc_comment'] 1245 ) 1246 self.LBOX_missing_shots.Append(label, shot) 1247 print "displaying missing shots took", time.time()-t1, "seconds" 1248 1249 return True
1250 #----------------------------------------------------
1251 - def _on_post_patient_selection(self, **kwargs):
1252 return 1
1253 # FIXME: 1254 # if has_focus: 1255 # wxCallAfter(self.__reset_ui_content) 1256 # else: 1257 # return 1 1258 #----------------------------------------------------
1259 - def _on_vaccinations_updated(self, **kwargs):
1260 return 1
1261 # FIXME: 1262 # if has_focus: 1263 # wxCallAfter(self.__reset_ui_content) 1264 # else: 1265 # is_stale == True 1266 # return 1 1267 #====================================================================== 1268 # main 1269 #---------------------------------------------------------------------- 1270 if __name__ == "__main__": 1271 1272 if len(sys.argv) < 2: 1273 sys.exit() 1274 1275 if sys.argv[1] != u'test': 1276 sys.exit() 1277 1278 app = wx.PyWidgetTester(size = (600, 600)) 1279 app.SetWidget(cATCPhraseWheel, -1) 1280 app.MainLoop() 1281