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