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