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

Source Code for Module Gnumed.wxpython.gmMeasurementWidgets

   1  """GNUmed measurement widgets.""" 
   2  #================================================================ 
   3  __author__ = "Karsten Hilbert <Karsten.Hilbert@gmx.net>" 
   4  __license__ = "GPL" 
   5   
   6   
   7  import sys 
   8  import logging 
   9  import datetime as pyDT 
  10  import decimal 
  11  import os 
  12  import subprocess 
  13  import codecs 
  14  import os.path 
  15   
  16   
  17  import wx 
  18  import wx.grid 
  19  import wx.lib.hyperlink 
  20   
  21   
  22  if __name__ == '__main__': 
  23          sys.path.insert(0, '../../') 
  24  from Gnumed.pycommon import gmTools 
  25  from Gnumed.pycommon import gmNetworkTools 
  26  from Gnumed.pycommon import gmI18N 
  27  from Gnumed.pycommon import gmShellAPI 
  28  from Gnumed.pycommon import gmCfg 
  29  from Gnumed.pycommon import gmDateTime 
  30  from Gnumed.pycommon import gmMatchProvider 
  31  from Gnumed.pycommon import gmDispatcher 
  32  from Gnumed.pycommon import gmMimeLib 
  33   
  34  from Gnumed.business import gmPerson 
  35  from Gnumed.business import gmStaff 
  36  from Gnumed.business import gmPathLab 
  37  from Gnumed.business import gmPraxis 
  38  from Gnumed.business import gmLOINC 
  39  from Gnumed.business import gmForms 
  40  from Gnumed.business import gmPersonSearch 
  41  from Gnumed.business import gmOrganization 
  42   
  43  from Gnumed.wxpython import gmRegetMixin 
  44  from Gnumed.wxpython import gmEditArea 
  45  from Gnumed.wxpython import gmPhraseWheel 
  46  from Gnumed.wxpython import gmListWidgets 
  47  from Gnumed.wxpython import gmGuiHelpers 
  48  from Gnumed.wxpython import gmAuthWidgets 
  49  from Gnumed.wxpython import gmOrganizationWidgets 
  50   
  51   
  52  _log = logging.getLogger('gm.ui') 
  53   
  54  #================================================================ 
  55  # HL7 related widgets 
  56  #================================================================ 
57 -def import_Excelleris_HL7(parent=None):
58 59 if parent is None: 60 parent = wx.GetApp().GetTopWindow() 61 62 # select file 63 dlg = wx.FileDialog ( 64 parent = parent, 65 message = 'Import Excelleris HL7 from XML file:', 66 # defaultDir = aDefDir, 67 # defaultFile = fname, 68 wildcard = "xml files|*.xml|XML files|*.XML|all files|*", 69 style = wx.OPEN | wx.FILE_MUST_EXIST 70 ) 71 choice = dlg.ShowModal() 72 xml_name = dlg.GetPath() 73 dlg.Destroy() 74 if choice != wx.ID_OK: 75 return False 76 77 # for now, localize gmHL7 import 78 from Gnumed.business import gmHL7 79 80 hl7 = gmHL7.extract_HL7_from_CDATA(xml_name, u'.//Message') 81 if hl7 is None: 82 gmGuiHelpers.gm_show_info ( 83 u'File [%s]\ndoes not seem to contain HL7 wrapped in XML.' % xml_name, 84 u'Extracting HL7 from XML' 85 ) 86 return False 87 fixed_hl7 = gmHL7.fix_HL7_stupidities(hl7) 88 PID_names = gmHL7.split_HL7_by_PID(fixed_hl7) 89 for name in PID_names: 90 gmHL7.stage_MSH_as_incoming_data(name, source = u'Excelleris')
91 92 #================================================================
93 -def import_HL7(parent=None):
94 95 if parent is None: 96 parent = wx.GetApp().GetTopWindow() 97 98 # select file 99 dlg = wx.FileDialog ( 100 parent = parent, 101 message = 'Import HL7 from file:', 102 # defaultDir = aDefDir, 103 # defaultFile = fname, 104 wildcard = "*.hl7|*.hl7|*.HL7|*.HL7|all files|*", 105 style = wx.OPEN | wx.FILE_MUST_EXIST 106 ) 107 choice = dlg.ShowModal() 108 hl7_name = dlg.GetPath() 109 dlg.Destroy() 110 if choice != wx.ID_OK: 111 return False 112 113 # for now, localize gmHL7 import 114 from Gnumed.business import gmHL7 115 116 fixed_hl7 = gmHL7.fix_HL7_stupidities(hl7_name) 117 PID_names = gmHL7.split_HL7_by_PID(fixed_hl7) 118 for name in PID_names: 119 gmHL7.stage_MSH_as_incoming_data(name, source = u'generic')
120 121 #================================================================
122 -def browse_incoming_unmatched(parent=None):
123 124 # for now, localize gmHL7 import 125 from Gnumed.business import gmHL7 126 127 if parent is None: 128 parent = wx.GetApp().GetTopWindow() 129 #------------------------------------------------------------ 130 def show_hl7(data=None): 131 if data is None: 132 return False 133 filename = data.export_to_file() 134 if filename is None: 135 return False 136 formatted_hl7 = gmHL7.format_hl7_file(filename, return_filename = True) 137 gmMimeLib.call_viewer_on_file(aFile = formatted_hl7, block = False) 138 139 return False
140 #------------------------------------------------------------ 141 def refresh(lctrl): 142 incoming = gmHL7.get_incoming_data() 143 items = [ [ 144 i['data_type'], 145 u'%s, %s (%s) %s' % ( 146 i['lastnames'], 147 i['firstnames'], 148 i['dob'], 149 i['gender'] 150 ), 151 i['external_data_id'], 152 i['pk_incoming_data_unmatched'] 153 ] for i in incoming ] 154 lctrl.set_string_items(items) 155 lctrl.set_data(incoming) 156 #------------------------------------------------------------ 157 gmListWidgets.get_choices_from_list ( 158 parent = parent, 159 msg = None, 160 caption = _('Showing unmatched incoming data'), 161 columns = [ _('Type'), _('Patient'), _('Data ID'), '#' ], 162 single_selection = True, 163 can_return_empty = False, 164 ignore_OK_button = True, 165 refresh_callback = refresh, 166 # edit_callback=None, 167 # new_callback=None, 168 # delete_callback=None, 169 left_extra_button = [_('Show'), _('Show formatted HL7'), show_hl7] 170 # middle_extra_button=None, 171 # right_extra_button=None 172 ) 173 174 #================================================================ 175 # LOINC related widgets 176 #================================================================
177 -def update_loinc_reference_data():
178 179 wx.BeginBusyCursor() 180 181 gmDispatcher.send(signal = 'statustext', msg = _('Updating LOINC data can take quite a while...'), beep = True) 182 183 # download 184 loinc_zip = gmNetworkTools.download_file(url = 'http://www.gnumed.de/downloads/data/loinc/loinctab.zip', suffix = '.zip') 185 if loinc_zip is None: 186 wx.EndBusyCursor() 187 gmGuiHelpers.gm_show_warning ( 188 aTitle = _('Downloading LOINC'), 189 aMessage = _('Error downloading the latest LOINC data.\n') 190 ) 191 return False 192 193 _log.debug('downloaded zipped LOINC data into [%s]', loinc_zip) 194 195 loinc_dir = gmNetworkTools.unzip_data_pack(filename = loinc_zip) 196 197 # split master data file 198 data_fname, license_fname = gmLOINC.split_LOINCDBTXT(input_fname = os.path.join(loinc_dir, 'LOINCDB.TXT')) 199 200 wx.EndBusyCursor() 201 202 conn = gmAuthWidgets.get_dbowner_connection(procedure = _('importing LOINC reference data')) 203 if conn is None: 204 return False 205 206 wx.BeginBusyCursor() 207 208 # import data 209 if gmLOINC.loinc_import(data_fname = data_fname, license_fname = license_fname, conn = conn): 210 gmDispatcher.send(signal = 'statustext', msg = _('Successfully imported LOINC reference data.')) 211 else: 212 gmDispatcher.send(signal = 'statustext', msg = _('Importing LOINC reference data failed.'), beep = True) 213 214 wx.EndBusyCursor() 215 return True
216 217 #================================================================ 218 # convenience functions 219 #================================================================
220 -def call_browser_on_measurement_type(measurement_type=None):
221 222 dbcfg = gmCfg.cCfgSQL() 223 224 url = dbcfg.get ( 225 option = u'external.urls.measurements_search', 226 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace, 227 bias = 'user', 228 default = u"http://www.google.de/search?as_oq=%(search_term)s&num=10&as_sitesearch=laborlexikon.de" 229 ) 230 231 base_url = dbcfg.get2 ( 232 option = u'external.urls.measurements_encyclopedia', 233 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace, 234 bias = 'user', 235 default = u'http://www.laborlexikon.de' 236 ) 237 238 if measurement_type is None: 239 url = base_url 240 241 measurement_type = measurement_type.strip() 242 243 if measurement_type == u'': 244 url = base_url 245 246 url = url % {'search_term': measurement_type} 247 248 gmNetworkTools.open_url_in_browser(url = url)
249 250 #----------------------------------------------------------------
251 -def edit_measurement(parent=None, measurement=None, single_entry=False):
252 ea = cMeasurementEditAreaPnl(parent = parent, id = -1) 253 ea.data = measurement 254 ea.mode = gmTools.coalesce(measurement, 'new', 'edit') 255 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = single_entry) 256 dlg.SetTitle(gmTools.coalesce(measurement, _('Adding new measurement'), _('Editing measurement'))) 257 if dlg.ShowModal() == wx.ID_OK: 258 dlg.Destroy() 259 return True 260 dlg.Destroy() 261 return False
262 263 #----------------------------------------------------------------
264 -def manage_measurements(parent=None, single_selection=False, emr=None):
265 266 if parent is None: 267 parent = wx.GetApp().GetTopWindow() 268 269 if emr is None: 270 emr = gmPerson.gmCurrentPatient().emr 271 272 #------------------------------------------------------------ 273 def edit(measurement=None): 274 return edit_measurement(parent = parent, measurement = measurement, single_entry = True)
275 #------------------------------------------------------------ 276 def delete(measurement): 277 gmPathLab.delete_test_result(result = measurement) 278 return True 279 #------------------------------------------------------------ 280 def get_tooltip(measurement): 281 return measurement.format(with_review=True, with_evaluation=True, with_ranges=True) 282 #------------------------------------------------------------ 283 def refresh(lctrl): 284 results = emr.get_test_results(order_by = 'clin_when DESC, unified_abbrev, unified_name') 285 items = [ [ 286 gmDateTime.pydt_strftime ( 287 r['clin_when'], 288 '%Y %b %d %H:%M', 289 accuracy = gmDateTime.acc_minutes 290 ), 291 r['unified_abbrev'], 292 u'%s%s%s' % ( 293 r['unified_val'], 294 gmTools.coalesce(r['val_unit'], u'', u' %s'), 295 gmTools.coalesce(r['abnormality_indicator'], u'', u' %s') 296 ), 297 r['unified_name'], 298 gmTools.coalesce(r['comment'], u''), 299 r['pk_test_result'] 300 ] for r in results ] 301 lctrl.set_string_items(items) 302 lctrl.set_data(results) 303 #------------------------------------------------------------ 304 msg = _('Test results (ordered reverse-chronologically)') 305 306 return gmListWidgets.get_choices_from_list ( 307 parent = parent, 308 msg = msg, 309 caption = _('Showing test results.'), 310 columns = [ _('When'), _('Abbrev'), _('Value'), _('Name'), _('Comment'), u'#' ], 311 single_selection = single_selection, 312 can_return_empty = False, 313 refresh_callback = refresh, 314 edit_callback = edit, 315 new_callback = edit, 316 delete_callback = delete, 317 list_tooltip_callback = get_tooltip 318 ) 319 320 #================================================================
321 -def configure_default_gnuplot_template(parent=None):
322 323 from Gnumed.wxpython import gmFormWidgets 324 325 if parent is None: 326 parent = wx.GetApp().GetTopWindow() 327 328 template = gmFormWidgets.manage_form_templates ( 329 parent = parent, 330 active_only = True, 331 template_types = [u'gnuplot script'] 332 ) 333 334 option = u'form_templates.default_gnuplot_template' 335 336 if template is None: 337 gmDispatcher.send(signal = 'statustext', msg = _('No default Gnuplot script template selected.'), beep = True) 338 return None 339 340 if template['engine'] != u'G': 341 gmDispatcher.send(signal = 'statustext', msg = _('No default Gnuplot script template selected.'), beep = True) 342 return None 343 344 dbcfg = gmCfg.cCfgSQL() 345 dbcfg.set ( 346 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace, 347 option = option, 348 value = u'%s - %s' % (template['name_long'], template['external_version']) 349 ) 350 return template
351 352 #============================================================
353 -def get_default_gnuplot_template(parent = None):
354 355 option = u'form_templates.default_gnuplot_template' 356 357 dbcfg = gmCfg.cCfgSQL() 358 359 # load from option 360 default_template_name = dbcfg.get2 ( 361 option = option, 362 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace, 363 bias = 'user' 364 ) 365 366 # not configured -> try to configure 367 if default_template_name is None: 368 gmDispatcher.send('statustext', msg = _('No default Gnuplot template configured.'), beep = False) 369 default_template = configure_default_gnuplot_template(parent = parent) 370 # still not configured -> return 371 if default_template is None: 372 gmGuiHelpers.gm_show_error ( 373 aMessage = _('There is no default Gnuplot one-type script template configured.'), 374 aTitle = _('Plotting test results') 375 ) 376 return None 377 return default_template 378 379 # now it MUST be configured (either newly or previously) 380 # but also *validly* ? 381 try: 382 name, ver = default_template_name.split(u' - ') 383 except: 384 # not valid 385 _log.exception('problem splitting Gnuplot script template name [%s]', default_template_name) 386 gmDispatcher.send(signal = 'statustext', msg = _('Problem loading Gnuplot script template.'), beep = True) 387 return None 388 389 default_template = gmForms.get_form_template(name_long = name, external_version = ver) 390 if default_template is None: 391 default_template = configure_default_gnuplot_template(parent = parent) 392 # still not configured -> return 393 if default_template is None: 394 gmGuiHelpers.gm_show_error ( 395 aMessage = _('Cannot load default Gnuplot script template [%s - %s]') % (name, ver), 396 aTitle = _('Plotting test results') 397 ) 398 return None 399 400 return default_template
401 402 #----------------------------------------------------------------
403 -def plot_measurements(parent=None, tests=None, format=None, show_year = True, use_default_template=False):
404 405 from Gnumed.wxpython import gmFormWidgets 406 407 # only valid for one-type plotting 408 if use_default_template: 409 template = get_default_gnuplot_template() 410 else: 411 template = gmFormWidgets.manage_form_templates ( 412 parent = parent, 413 active_only = True, 414 template_types = [u'gnuplot script'] 415 ) 416 417 if template is None: 418 gmGuiHelpers.gm_show_error ( 419 aMessage = _('Cannot plot without a plot script.'), 420 aTitle = _('Plotting test results') 421 ) 422 return False 423 424 fname_data = gmPathLab.export_results_for_gnuplot(results = tests, show_year = show_year) 425 426 script = template.instantiate() 427 script.data_filename = fname_data 428 script.generate_output(format = format) # Gnuplot output terminal, wxt = wxWidgets window
429 430 #----------------------------------------------------------------
431 -def plot_adjacent_measurements(parent=None, test=None, format=None, show_year=True, plot_singular_result=True, use_default_template=False):
432 433 earlier, later = test.get_adjacent_results(desired_earlier_results = 2, desired_later_results = 2) 434 results2plot = [] 435 if earlier is not None: 436 results2plot.extend(earlier) 437 results2plot.append(test) 438 if later is not None: 439 results2plot.extend(later) 440 if len(results2plot) == 1: 441 if not plot_singular_result: 442 return 443 plot_measurements ( 444 parent = parent, 445 tests = results2plot, 446 format = format, 447 show_year = show_year, 448 use_default_template = use_default_template 449 )
450 #================================================================ 451 #from Gnumed.wxGladeWidgets import wxgPrimaryCareVitalsInputPnl 452 453 # Taillenumfang: Mitte zwischen unterster Rippe und 454 # hoechstem Teil des Beckenkamms 455 # Maenner: maessig: 94-102, deutlich: > 102 .. erhoeht 456 # Frauen: maessig: 80-88, deutlich: > 88 .. erhoeht 457 458 #================================================================ 459 # display widgets 460 #================================================================
461 -class cMeasurementsGrid(wx.grid.Grid):
462 """A grid class for displaying measurment results. 463 464 - does NOT listen to the currently active patient 465 - thereby it can display any patient at any time 466 """ 467 # FIXME: sort-by-battery 468 # FIXME: filter-by-battery 469 # FIXME: filter out empty 470 # FIXME: filter by tests of a selected date 471 # FIXME: dates DESC/ASC by cfg 472 # FIXME: mouse over column header: display date info
473 - def __init__(self, *args, **kwargs):
474 475 wx.grid.Grid.__init__(self, *args, **kwargs) 476 477 self.__patient = None 478 self.__panel_to_show = None 479 self.__show_by_panel = False 480 self.__cell_data = {} 481 self.__row_label_data = [] 482 483 self.__prev_row = None 484 self.__prev_col = None 485 self.__prev_label_row = None 486 self.__date_format = str((_('lab_grid_date_format::%Y\n%b %d')).lstrip('lab_grid_date_format::')) 487 488 self.__init_ui() 489 self.__register_events()
490 #------------------------------------------------------------ 491 # external API 492 #------------------------------------------------------------
493 - def delete_current_selection(self):
494 if not self.IsSelection(): 495 gmDispatcher.send(signal = u'statustext', msg = _('No results selected for deletion.')) 496 return True 497 498 selected_cells = self.get_selected_cells() 499 if len(selected_cells) > 20: 500 results = None 501 msg = _( 502 'There are %s results marked for deletion.\n' 503 '\n' 504 'Are you sure you want to delete these results ?' 505 ) % len(selected_cells) 506 else: 507 results = self.__cells_to_data(cells = selected_cells, exclude_multi_cells = False) 508 txt = u'\n'.join([ u'%s %s (%s): %s %s%s' % ( 509 r['clin_when'].strftime('%x %H:%M').decode(gmI18N.get_encoding()), 510 r['unified_abbrev'], 511 r['unified_name'], 512 r['unified_val'], 513 r['val_unit'], 514 gmTools.coalesce(r['abnormality_indicator'], u'', u' (%s)') 515 ) for r in results 516 ]) 517 msg = _( 518 'The following results are marked for deletion:\n' 519 '\n' 520 '%s\n' 521 '\n' 522 'Are you sure you want to delete these results ?' 523 ) % txt 524 525 dlg = gmGuiHelpers.c2ButtonQuestionDlg ( 526 self, 527 -1, 528 caption = _('Deleting test results'), 529 question = msg, 530 button_defs = [ 531 {'label': _('Delete'), 'tooltip': _('Yes, delete all the results.'), 'default': False}, 532 {'label': _('Cancel'), 'tooltip': _('No, do NOT delete any results.'), 'default': True} 533 ] 534 ) 535 decision = dlg.ShowModal() 536 537 if decision == wx.ID_YES: 538 if results is None: 539 results = self.__cells_to_data(cells = selected_cells, exclude_multi_cells = False) 540 for result in results: 541 gmPathLab.delete_test_result(result)
542 #------------------------------------------------------------
543 - def sign_current_selection(self):
544 if not self.IsSelection(): 545 gmDispatcher.send(signal = u'statustext', msg = _('Cannot sign results. No results selected.')) 546 return True 547 548 selected_cells = self.get_selected_cells() 549 if len(selected_cells) > 10: 550 test_count = len(selected_cells) 551 tests = None 552 else: 553 test_count = None 554 tests = self.__cells_to_data(cells = selected_cells, exclude_multi_cells = False) 555 if len(tests) == 0: 556 return True 557 558 dlg = cMeasurementsReviewDlg ( 559 self, 560 -1, 561 tests = tests, 562 test_count = test_count 563 ) 564 decision = dlg.ShowModal() 565 566 if decision == wx.ID_APPLY: 567 wx.BeginBusyCursor() 568 569 if dlg._RBTN_confirm_abnormal.GetValue(): 570 abnormal = None 571 elif dlg._RBTN_results_normal.GetValue(): 572 abnormal = False 573 else: 574 abnormal = True 575 576 if dlg._RBTN_confirm_relevance.GetValue(): 577 relevant = None 578 elif dlg._RBTN_results_not_relevant.GetValue(): 579 relevant = False 580 else: 581 relevant = True 582 583 if tests is None: 584 tests = self.__cells_to_data(cells = selected_cells, exclude_multi_cells = False) 585 586 comment = None 587 if len(tests) == 1: 588 comment = dlg._TCTRL_comment.GetValue() 589 590 for test in tests: 591 test.set_review ( 592 technically_abnormal = abnormal, 593 clinically_relevant = relevant, 594 comment = comment, 595 make_me_responsible = dlg._CHBOX_responsible.IsChecked() 596 ) 597 598 wx.EndBusyCursor() 599 600 dlg.Destroy()
601 #------------------------------------------------------------
602 - def plot_current_selection(self):
603 604 if not self.IsSelection(): 605 gmDispatcher.send(signal = u'statustext', msg = _('Cannot plot results. No results selected.')) 606 return True 607 608 tests = self.__cells_to_data ( 609 cells = self.get_selected_cells(), 610 exclude_multi_cells = False, 611 auto_include_multi_cells = True 612 ) 613 614 plot_measurements(parent = self, tests = tests)
615 #------------------------------------------------------------
616 - def get_selected_cells(self):
617 618 sel_block_top_left = self.GetSelectionBlockTopLeft() 619 sel_block_bottom_right = self.GetSelectionBlockBottomRight() 620 sel_cols = self.GetSelectedCols() 621 sel_rows = self.GetSelectedRows() 622 623 selected_cells = [] 624 625 # individually selected cells (ctrl-click) 626 selected_cells += self.GetSelectedCells() 627 628 # selected rows 629 selected_cells += list ( 630 (row, col) 631 for row in sel_rows 632 for col in xrange(self.GetNumberCols()) 633 ) 634 635 # selected columns 636 selected_cells += list ( 637 (row, col) 638 for row in xrange(self.GetNumberRows()) 639 for col in sel_cols 640 ) 641 642 # selection blocks 643 for top_left, bottom_right in zip(self.GetSelectionBlockTopLeft(), self.GetSelectionBlockBottomRight()): 644 selected_cells += [ 645 (row, col) 646 for row in xrange(top_left[0], bottom_right[0] + 1) 647 for col in xrange(top_left[1], bottom_right[1] + 1) 648 ] 649 650 return set(selected_cells)
651 #------------------------------------------------------------
652 - def select_cells(self, unsigned_only=False, accountables_only=False, keep_preselections=False):
653 """Select a range of cells according to criteria. 654 655 unsigned_only: include only those which are not signed at all yet 656 accountable_only: include only those for which the current user is responsible 657 keep_preselections: broaden (rather than replace) the range of selected cells 658 659 Combinations are powerful ! 660 """ 661 wx.BeginBusyCursor() 662 self.BeginBatch() 663 664 if not keep_preselections: 665 self.ClearSelection() 666 667 for col_idx in self.__cell_data.keys(): 668 for row_idx in self.__cell_data[col_idx].keys(): 669 # loop over results in cell and only include 670 # those multi-value cells that are not ambiguous 671 do_not_include = False 672 for result in self.__cell_data[col_idx][row_idx]: 673 if unsigned_only: 674 if result['reviewed']: 675 do_not_include = True 676 break 677 if accountables_only: 678 if not result['you_are_responsible']: 679 do_not_include = True 680 break 681 if do_not_include: 682 continue 683 684 self.SelectBlock(row_idx, col_idx, row_idx, col_idx, addToSelected = True) 685 686 self.EndBatch() 687 wx.EndBusyCursor()
688 #------------------------------------------------------------
689 - def repopulate_grid(self):
690 self.empty_grid() 691 if self.__patient is None: 692 return 693 694 if self.__show_by_panel: 695 self.__repopulate_grid_by_panel() 696 return 697 698 self.__repopulate_grid_all_results()
699 #------------------------------------------------------------
700 - def __repopulate_grid_by_panel(self):
701 702 if self.__panel_to_show is None: 703 return 704 705 emr = self.__patient.get_emr() 706 707 # rows 708 self.__row_label_data = self.__panel_to_show.test_types 709 row_labels = [ u'%s%s' % ( 710 gmTools.bool2subst(test['is_fake_meta_type'], u'', gmTools.u_sum, u''), 711 test['unified_abbrev'] 712 ) for test in self.__row_label_data 713 ] 714 if len(row_labels) == 0: 715 return 716 717 # columns 718 column_labels = [ 719 date[0].strftime(self.__date_format) for date in emr.get_dates_for_results ( 720 tests = self.__panel_to_show['pk_test_types'], 721 # FIXME: make configurable 722 reverse_chronological = True 723 ) 724 ] 725 results = emr.get_test_results_by_date ( 726 tests = self.__panel_to_show['pk_test_types'], 727 # FIXME: make configurable 728 reverse_chronological = True 729 ) 730 731 self.BeginBatch() 732 733 # rows 734 self.AppendRows(numRows = len(row_labels)) 735 for row_idx in range(len(row_labels)): 736 self.SetRowLabelValue(row_idx, row_labels[row_idx]) 737 738 # columns 739 self.AppendCols(numCols = len(column_labels)) 740 for date_idx in range(len(column_labels)): 741 self.SetColLabelValue(date_idx, column_labels[date_idx]) 742 743 # cell values (list of test results) 744 for result in results: 745 row = row_labels.index(u'%s%s' % ( 746 gmTools.bool2subst(result['is_fake_meta_type'], u'', gmTools.u_sum, u''), 747 result['unified_abbrev'] 748 )) 749 col = column_labels.index(result['clin_when'].strftime(self.__date_format)) 750 751 try: 752 self.__cell_data[col] 753 except KeyError: 754 self.__cell_data[col] = {} 755 756 # the tooltip always shows the youngest sub result details 757 if self.__cell_data[col].has_key(row): 758 self.__cell_data[col][row].append(result) 759 self.__cell_data[col][row].sort(key = lambda x: x['clin_when'], reverse = True) 760 else: 761 self.__cell_data[col][row] = [result] 762 763 # rebuild cell display string 764 vals2display = [] 765 for sub_result in self.__cell_data[col][row]: 766 767 # is the sub_result technically abnormal ? 768 ind = gmTools.coalesce(sub_result['abnormality_indicator'], u'').strip() 769 if ind != u'': 770 lab_abnormality_indicator = u' (%s)' % ind[:3] 771 else: 772 lab_abnormality_indicator = u'' 773 # - if noone reviewed - use what the lab thinks 774 if sub_result['is_technically_abnormal'] is None: 775 abnormality_indicator = lab_abnormality_indicator 776 # - if someone reviewed and decreed normality - use that 777 elif sub_result['is_technically_abnormal'] is False: 778 abnormality_indicator = u'' 779 # - if someone reviewed and decreed abnormality ... 780 else: 781 # ... invent indicator if the lab did't use one 782 if lab_abnormality_indicator == u'': 783 # FIXME: calculate from min/max/range 784 abnormality_indicator = u' (%s)' % gmTools.u_plus_minus 785 # ... else use indicator the lab used 786 else: 787 abnormality_indicator = lab_abnormality_indicator 788 789 # is the sub_result relevant clinically ? 790 # FIXME: take into account primary_GP once we support that 791 sub_result_relevant = sub_result['is_clinically_relevant'] 792 if sub_result_relevant is None: 793 # FIXME: calculate from clinical range 794 sub_result_relevant = False 795 796 missing_review = False 797 # warn on missing review if 798 # a) no review at all exists or 799 if not sub_result['reviewed']: 800 missing_review = True 801 # b) there is a review but 802 else: 803 # current user is reviewer and hasn't reviewed 804 if sub_result['you_are_responsible'] and not sub_result['review_by_you']: 805 missing_review = True 806 807 # can we display the full sub_result length ? 808 if len(sub_result['unified_val']) > 8: 809 tmp = u'%.7s%s' % (sub_result['unified_val'][:7], gmTools.u_ellipsis) 810 else: 811 tmp = u'%.8s' % sub_result['unified_val'][:8] 812 813 # abnormal ? 814 tmp = u'%s%.6s' % (tmp, abnormality_indicator) 815 816 # is there a comment ? 817 has_sub_result_comment = gmTools.coalesce ( 818 gmTools.coalesce(sub_result['note_test_org'], sub_result['comment']), 819 u'' 820 ).strip() != u'' 821 if has_sub_result_comment: 822 tmp = u'%s %s' % (tmp, gmTools.u_ellipsis) 823 824 # lacking a review ? 825 if missing_review: 826 tmp = u'%s %s' % (tmp, gmTools.u_writing_hand) 827 828 # part of a multi-result cell ? 829 if len(self.__cell_data[col][row]) > 1: 830 tmp = u'%s %s' % (sub_result['clin_when'].strftime('%H:%M'), tmp) 831 832 vals2display.append(tmp) 833 834 self.SetCellValue(row, col, u'\n'.join(vals2display)) 835 self.SetCellAlignment(row, col, horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE) 836 # font = self.GetCellFont(row, col) 837 # if not font.IsFixedWidth(): 838 # font.SetFamily(family = wx.FONTFAMILY_MODERN) 839 # FIXME: what about partial sub results being relevant ?? 840 if sub_result_relevant: 841 font = self.GetCellFont(row, col) 842 self.SetCellTextColour(row, col, 'firebrick') 843 font.SetWeight(wx.FONTWEIGHT_BOLD) 844 self.SetCellFont(row, col, font) 845 # self.SetCellFont(row, col, font) 846 847 self.AutoSize() 848 self.EndBatch() 849 return
850 #------------------------------------------------------------
852 emr = self.__patient.get_emr() 853 854 self.__row_label_data = emr.get_test_types_for_results() 855 test_type_labels = [ u'%s%s' % ( 856 gmTools.bool2subst(test['is_fake_meta_type'], u'', gmTools.u_sum, u''), 857 test['unified_abbrev'] 858 ) for test in self.__row_label_data 859 ] 860 if len(test_type_labels) == 0: 861 return 862 863 test_date_labels = [ date[0].strftime(self.__date_format) for date in emr.get_dates_for_results() ] 864 results = emr.get_test_results_by_date() 865 866 self.BeginBatch() 867 868 # rows 869 self.AppendRows(numRows = len(test_type_labels)) 870 for row_idx in range(len(test_type_labels)): 871 self.SetRowLabelValue(row_idx, test_type_labels[row_idx]) 872 873 # columns 874 self.AppendCols(numCols = len(test_date_labels)) 875 for date_idx in range(len(test_date_labels)): 876 self.SetColLabelValue(date_idx, test_date_labels[date_idx]) 877 878 # cell values (list of test results) 879 for result in results: 880 row = test_type_labels.index(u'%s%s' % ( 881 gmTools.bool2subst(result['is_fake_meta_type'], u'', gmTools.u_sum, u''), 882 result['unified_abbrev'] 883 )) 884 col = test_date_labels.index(result['clin_when'].strftime(self.__date_format)) 885 886 try: 887 self.__cell_data[col] 888 except KeyError: 889 self.__cell_data[col] = {} 890 891 # the tooltip always shows the youngest sub result details 892 if self.__cell_data[col].has_key(row): 893 self.__cell_data[col][row].append(result) 894 self.__cell_data[col][row].sort(key = lambda x: x['clin_when'], reverse = True) 895 else: 896 self.__cell_data[col][row] = [result] 897 898 # rebuild cell display string 899 vals2display = [] 900 for sub_result in self.__cell_data[col][row]: 901 902 # is the sub_result technically abnormal ? 903 ind = gmTools.coalesce(sub_result['abnormality_indicator'], u'').strip() 904 if ind != u'': 905 lab_abnormality_indicator = u' (%s)' % ind[:3] 906 else: 907 lab_abnormality_indicator = u'' 908 # - if noone reviewed - use what the lab thinks 909 if sub_result['is_technically_abnormal'] is None: 910 abnormality_indicator = lab_abnormality_indicator 911 # - if someone reviewed and decreed normality - use that 912 elif sub_result['is_technically_abnormal'] is False: 913 abnormality_indicator = u'' 914 # - if someone reviewed and decreed abnormality ... 915 else: 916 # ... invent indicator if the lab did't use one 917 if lab_abnormality_indicator == u'': 918 # FIXME: calculate from min/max/range 919 abnormality_indicator = u' (%s)' % gmTools.u_plus_minus 920 # ... else use indicator the lab used 921 else: 922 abnormality_indicator = lab_abnormality_indicator 923 924 # is the sub_result relevant clinically ? 925 # FIXME: take into account primary_GP once we support that 926 sub_result_relevant = sub_result['is_clinically_relevant'] 927 if sub_result_relevant is None: 928 # FIXME: calculate from clinical range 929 sub_result_relevant = False 930 931 missing_review = False 932 # warn on missing review if 933 # a) no review at all exists or 934 if not sub_result['reviewed']: 935 missing_review = True 936 # b) there is a review but 937 else: 938 # current user is reviewer and hasn't reviewed 939 if sub_result['you_are_responsible'] and not sub_result['review_by_you']: 940 missing_review = True 941 942 # can we display the full sub_result length ? 943 if len(sub_result['unified_val']) > 8: 944 tmp = u'%.7s%s' % (sub_result['unified_val'][:7], gmTools.u_ellipsis) 945 else: 946 tmp = u'%.8s' % sub_result['unified_val'][:8] 947 948 # abnormal ? 949 tmp = u'%s%.6s' % (tmp, abnormality_indicator) 950 951 # is there a comment ? 952 has_sub_result_comment = gmTools.coalesce ( 953 gmTools.coalesce(sub_result['note_test_org'], sub_result['comment']), 954 u'' 955 ).strip() != u'' 956 if has_sub_result_comment: 957 tmp = u'%s %s' % (tmp, gmTools.u_ellipsis) 958 959 # lacking a review ? 960 if missing_review: 961 tmp = u'%s %s' % (tmp, gmTools.u_writing_hand) 962 963 # part of a multi-result cell ? 964 if len(self.__cell_data[col][row]) > 1: 965 tmp = u'%s %s' % (sub_result['clin_when'].strftime('%H:%M'), tmp) 966 967 vals2display.append(tmp) 968 969 self.SetCellValue(row, col, u'\n'.join(vals2display)) 970 self.SetCellAlignment(row, col, horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE) 971 # font = self.GetCellFont(row, col) 972 # if not font.IsFixedWidth(): 973 # font.SetFamily(family = wx.FONTFAMILY_MODERN) 974 # FIXME: what about partial sub results being relevant ?? 975 if sub_result_relevant: 976 font = self.GetCellFont(row, col) 977 self.SetCellTextColour(row, col, 'firebrick') 978 font.SetWeight(wx.FONTWEIGHT_BOLD) 979 self.SetCellFont(row, col, font) 980 # self.SetCellFont(row, col, font) 981 982 self.AutoSize() 983 self.EndBatch() 984 return
985 #------------------------------------------------------------
986 - def empty_grid(self):
987 self.BeginBatch() 988 self.ClearGrid() 989 # Windows cannot do nothing, it rather decides to assert() 990 # on thinking it is supposed to do nothing 991 if self.GetNumberRows() > 0: 992 self.DeleteRows(pos = 0, numRows = self.GetNumberRows()) 993 if self.GetNumberCols() > 0: 994 self.DeleteCols(pos = 0, numCols = self.GetNumberCols()) 995 self.EndBatch() 996 self.__cell_data = {} 997 self.__row_label_data = []
998 #------------------------------------------------------------
999 - def get_row_tooltip(self, row=None):
1000 # display test info (unified, which tests are grouped, which panels 1001 # they belong to include details about test types included, 1002 1003 # sometimes, for some reason, there is no row and 1004 # wxPython still tries to find a tooltip for it 1005 try: 1006 tt = self.__row_label_data[row] 1007 except IndexError: 1008 return u' ' 1009 1010 meta_tt = tt.meta_test_type 1011 if meta_tt is None: 1012 return tt.format(patient = self.__patient.ID) 1013 1014 txt = meta_tt.format(with_tests = True) 1015 txt += u'\n' 1016 most_recent = tt.get_most_recent_results(patient = self.__patient.ID, no_of_results = 2) 1017 if most_recent is not None: 1018 txt += _('Most recent results:') 1019 for result in most_recent: 1020 txt += _('\n %s: %s%s%s') % ( 1021 result['clin_when'].strftime('%Y %b %d'), 1022 result['unified_val'], 1023 gmTools.coalesce(result['val_unit'], u'', u' %s'), 1024 gmTools.coalesce(result['abnormality_indicator'], u'', u' (%s)') 1025 ) 1026 1027 return txt
1028 #------------------------------------------------------------
1029 - def get_cell_tooltip(self, col=None, row=None):
1030 try: 1031 d = self.__cell_data[col][row] 1032 except KeyError: 1033 # FIXME: maybe display the most recent or when the most recent was ? 1034 d = None 1035 1036 if d is None: 1037 return u' ' 1038 1039 is_multi_cell = False 1040 if len(d) > 1: 1041 is_multi_cell = True 1042 d = d[0] 1043 1044 tt = u'' 1045 # header 1046 if is_multi_cell: 1047 tt += _(u'Details of most recent (topmost) result ! \n') 1048 tt += d.format(with_review = True, with_evaluation = True, with_ranges = True) 1049 return tt
1050 #------------------------------------------------------------ 1051 # internal helpers 1052 #------------------------------------------------------------
1053 - def __init_ui(self):
1054 self.CreateGrid(0, 1) 1055 self.EnableEditing(0) 1056 self.EnableDragGridSize(1) 1057 self.SetMinSize(wx.DefaultSize) 1058 1059 # setting this screws up the labels: they are cut off and displaced 1060 #self.SetColLabelAlignment(wx.ALIGN_CENTER, wx.ALIGN_BOTTOM) 1061 1062 self.SetRowLabelSize(wx.grid.GRID_AUTOSIZE) # starting with 2.8.8 1063 #self.SetRowLabelSize(150) 1064 self.SetRowLabelAlignment(horiz = wx.ALIGN_LEFT, vert = wx.ALIGN_CENTRE) 1065 1066 # add link to left upper corner 1067 dbcfg = gmCfg.cCfgSQL() 1068 url = dbcfg.get2 ( 1069 option = u'external.urls.measurements_encyclopedia', 1070 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace, 1071 bias = 'user', 1072 default = u'http://www.laborlexikon.de' 1073 ) 1074 1075 self.__WIN_corner = self.GetGridCornerLabelWindow() # a wx.Window instance 1076 1077 LNK_lab = wx.lib.hyperlink.HyperLinkCtrl ( 1078 self.__WIN_corner, 1079 -1, 1080 label = _('Tests'), 1081 style = wx.HL_DEFAULT_STYLE # wx.TE_READONLY|wx.TE_CENTRE| wx.NO_BORDER | 1082 ) 1083 LNK_lab.SetURL(url) 1084 LNK_lab.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BACKGROUND)) 1085 LNK_lab.SetToolTipString(_( 1086 'Navigate to an encyclopedia of measurements\n' 1087 'and test methods on the web.\n' 1088 '\n' 1089 ' <%s>' 1090 ) % url) 1091 1092 SZR_inner = wx.BoxSizer(wx.HORIZONTAL) 1093 SZR_inner.Add((20, 20), 1, wx.EXPAND, 0) # spacer 1094 SZR_inner.Add(LNK_lab, 0, wx.ALIGN_CENTER_VERTICAL, 0) #wx.ALIGN_CENTER wx.EXPAND 1095 SZR_inner.Add((20, 20), 1, wx.EXPAND, 0) # spacer 1096 1097 SZR_corner = wx.BoxSizer(wx.VERTICAL) 1098 SZR_corner.Add((20, 20), 1, wx.EXPAND, 0) # spacer 1099 SZR_corner.AddWindow(SZR_inner, 0, wx.EXPAND) # inner sizer with centered hyperlink 1100 SZR_corner.Add((20, 20), 1, wx.EXPAND, 0) # spacer 1101 1102 self.__WIN_corner.SetSizer(SZR_corner) 1103 SZR_corner.Fit(self.__WIN_corner)
1104 #------------------------------------------------------------
1105 - def __resize_corner_window(self, evt):
1106 self.__WIN_corner.Layout()
1107 #------------------------------------------------------------
1108 - def __cells_to_data(self, cells=None, exclude_multi_cells=False, auto_include_multi_cells=False):
1109 """List of <cells> must be in row / col order.""" 1110 data = [] 1111 for row, col in cells: 1112 try: 1113 # cell data is stored col / row 1114 data_list = self.__cell_data[col][row] 1115 except KeyError: 1116 continue 1117 1118 if len(data_list) == 1: 1119 data.append(data_list[0]) 1120 continue 1121 1122 if exclude_multi_cells: 1123 gmDispatcher.send(signal = u'statustext', msg = _('Excluding multi-result field from further processing.')) 1124 continue 1125 1126 if auto_include_multi_cells: 1127 data.extend(data_list) 1128 continue 1129 1130 data_to_include = self.__get_choices_from_multi_cell(cell_data = data_list) 1131 if data_to_include is None: 1132 continue 1133 data.extend(data_to_include) 1134 1135 return data
1136 #------------------------------------------------------------
1137 - def __get_choices_from_multi_cell(self, cell_data=None, single_selection=False):
1138 data = gmListWidgets.get_choices_from_list ( 1139 parent = self, 1140 msg = _( 1141 'Your selection includes a field with multiple results.\n' 1142 '\n' 1143 'Please select the individual results you want to work on:' 1144 ), 1145 caption = _('Selecting test results'), 1146 choices = [ [d['clin_when'], u'%s: %s' % (d['abbrev_tt'], d['name_tt']), d['unified_val']] for d in cell_data ], 1147 columns = [ _('Date / Time'), _('Test'), _('Result') ], 1148 data = cell_data, 1149 single_selection = single_selection 1150 ) 1151 return data
1152 #------------------------------------------------------------ 1153 # event handling 1154 #------------------------------------------------------------
1155 - def __register_events(self):
1156 # dynamic tooltips: GridWindow, GridRowLabelWindow, GridColLabelWindow, GridCornerLabelWindow 1157 self.GetGridWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_cells) 1158 self.GetGridRowLabelWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_row_labels) 1159 #self.GetGridColLabelWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_col_labels) 1160 1161 # sizing left upper corner window 1162 self.Bind(wx.EVT_SIZE, self.__resize_corner_window) 1163 1164 # editing cells 1165 self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.__on_cell_left_dclicked)
1166 #------------------------------------------------------------
1167 - def __on_cell_left_dclicked(self, evt):
1168 col = evt.GetCol() 1169 row = evt.GetRow() 1170 1171 # empty cell, perhaps ? 1172 try: 1173 self.__cell_data[col][row] 1174 except KeyError: 1175 # FIXME: invoke editor for adding value for day of that column 1176 # FIMXE: and test of that row 1177 return 1178 1179 if len(self.__cell_data[col][row]) > 1: 1180 data = self.__get_choices_from_multi_cell(cell_data = self.__cell_data[col][row], single_selection = True) 1181 else: 1182 data = self.__cell_data[col][row][0] 1183 1184 if data is None: 1185 return 1186 1187 edit_measurement(parent = self, measurement = data, single_entry = True)
1188 #------------------------------------------------------------ 1189 # def OnMouseMotionRowLabel(self, evt): 1190 # x, y = self.CalcUnscrolledPosition(evt.GetPosition()) 1191 # row = self.YToRow(y) 1192 # label = self.table().GetRowHelpValue(row) 1193 # self.GetGridRowLabelWindow().SetToolTipString(label or "") 1194 # evt.Skip()
1195 - def __on_mouse_over_row_labels(self, evt):
1196 1197 # Use CalcUnscrolledPosition() to get the mouse position within the 1198 # entire grid including what's offscreen 1199 x, y = self.CalcUnscrolledPosition(evt.GetX(), evt.GetY()) 1200 1201 row = self.YToRow(y) 1202 1203 if self.__prev_label_row == row: 1204 return 1205 1206 self.__prev_label_row == row 1207 1208 evt.GetEventObject().SetToolTipString(self.get_row_tooltip(row = row))
1209 #------------------------------------------------------------ 1210 # def OnMouseMotionColLabel(self, evt): 1211 # x, y = self.CalcUnscrolledPosition(evt.GetPosition()) 1212 # col = self.XToCol(x) 1213 # label = self.table().GetColHelpValue(col) 1214 # self.GetGridColLabelWindow().SetToolTipString(label or "") 1215 # evt.Skip() 1216 #------------------------------------------------------------
1217 - def __on_mouse_over_cells(self, evt):
1218 """Calculate where the mouse is and set the tooltip dynamically.""" 1219 1220 # Use CalcUnscrolledPosition() to get the mouse position within the 1221 # entire grid including what's offscreen 1222 x, y = self.CalcUnscrolledPosition(evt.GetX(), evt.GetY()) 1223 1224 # use this logic to prevent tooltips outside the actual cells 1225 # apply to GetRowSize, too 1226 # tot = 0 1227 # for col in xrange(self.NumberCols): 1228 # tot += self.GetColSize(col) 1229 # if xpos <= tot: 1230 # self.tool_tip.Tip = 'Tool tip for Column %s' % ( 1231 # self.GetColLabelValue(col)) 1232 # break 1233 # else: # mouse is in label area beyond the right-most column 1234 # self.tool_tip.Tip = '' 1235 1236 row, col = self.XYToCell(x, y) 1237 1238 if (row == self.__prev_row) and (col == self.__prev_col): 1239 return 1240 1241 self.__prev_row = row 1242 self.__prev_col = col 1243 1244 evt.GetEventObject().SetToolTipString(self.get_cell_tooltip(col=col, row=row))
1245 #------------------------------------------------------------ 1246 # properties 1247 #------------------------------------------------------------
1248 - def _set_patient(self, patient):
1249 self.__patient = patient 1250 self.repopulate_grid()
1251 1252 patient = property(lambda x:x, _set_patient) 1253 #------------------------------------------------------------
1254 - def _set_panel_to_show(self, panel):
1255 self.__panel_to_show = panel 1256 self.repopulate_grid()
1257 1258 panel_to_show = property(lambda x:x, _set_panel_to_show) 1259 #------------------------------------------------------------
1260 - def _set_show_by_panel(self, show_by_panel):
1261 self.__show_by_panel = show_by_panel 1262 self.repopulate_grid()
1263 1264 show_by_panel = property(lambda x:x, _set_show_by_panel)
1265 #================================================================ 1266 from Gnumed.wxGladeWidgets import wxgMeasurementsPnl 1267
1268 -class cMeasurementsPnl(wxgMeasurementsPnl.wxgMeasurementsPnl, gmRegetMixin.cRegetOnPaintMixin):
1269 """Panel holding a grid with lab data. Used as notebook page.""" 1270
1271 - def __init__(self, *args, **kwargs):
1272 1273 wxgMeasurementsPnl.wxgMeasurementsPnl.__init__(self, *args, **kwargs) 1274 gmRegetMixin.cRegetOnPaintMixin.__init__(self) 1275 self.__init_ui() 1276 self.__register_interests()
1277 #-------------------------------------------------------- 1278 # event handling 1279 #--------------------------------------------------------
1280 - def __register_interests(self):
1281 gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._on_pre_patient_selection) 1282 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._on_post_patient_selection) 1283 gmDispatcher.connect(signal = u'test_result_mod_db', receiver = self._schedule_data_reget) 1284 gmDispatcher.connect(signal = u'reviewed_test_results_mod_db', receiver = self._schedule_data_reget)
1285 #--------------------------------------------------------
1286 - def _on_post_patient_selection(self):
1287 wx.CallAfter(self.__on_post_patient_selection)
1288 #--------------------------------------------------------
1289 - def __on_post_patient_selection(self):
1290 self._schedule_data_reget()
1291 #--------------------------------------------------------
1292 - def _on_pre_patient_selection(self):
1293 wx.CallAfter(self.__on_pre_patient_selection)
1294 #--------------------------------------------------------
1295 - def __on_pre_patient_selection(self):
1296 self.data_grid.patient = None 1297 self.panel_data_grid.patient = None
1298 #--------------------------------------------------------
1299 - def _on_add_button_pressed(self, event):
1300 edit_measurement(parent = self, measurement = None)
1301 #--------------------------------------------------------
1302 - def _on_review_button_pressed(self, evt):
1303 self.PopupMenu(self.__action_button_popup)
1304 #--------------------------------------------------------
1305 - def _on_select_button_pressed(self, evt):
1306 if self._RBTN_my_unsigned.GetValue() is True: 1307 self.data_grid.select_cells(unsigned_only = True, accountables_only = True, keep_preselections = False) 1308 elif self._RBTN_all_unsigned.GetValue() is True: 1309 self.data_grid.select_cells(unsigned_only = True, accountables_only = False, keep_preselections = False)
1310 #--------------------------------------------------------
1311 - def _on_manage_panels_button_pressed(self, event):
1312 manage_test_panels(parent = self)
1313 #--------------------------------------------------------
1314 - def __on_sign_current_selection(self, evt):
1315 self.data_grid.sign_current_selection()
1316 #--------------------------------------------------------
1317 - def __on_plot_current_selection(self, evt):
1318 self.data_grid.plot_current_selection()
1319 #--------------------------------------------------------
1320 - def __on_delete_current_selection(self, evt):
1321 self.data_grid.delete_current_selection()
1322 #--------------------------------------------------------
1323 - def _on_panel_selected(self, panel):
1324 wx.CallAfter(self.__on_panel_selected, panel=panel)
1325 #--------------------------------------------------------
1326 - def __on_panel_selected(self, panel):
1327 if panel is None: 1328 self._TCTRL_panel_comment.SetValue(u'') 1329 self.panel_data_grid.panel_to_show = None 1330 self.panel_data_grid.Hide() 1331 else: 1332 pnl = self._PRW_panel.GetData(as_instance = True) 1333 self._TCTRL_panel_comment.SetValue(gmTools.coalesce ( 1334 pnl['comment'], 1335 u'' 1336 )) 1337 self.panel_data_grid.panel_to_show = pnl 1338 self.panel_data_grid.Show() 1339 self.Layout()
1340 #self.Refresh() 1341 #--------------------------------------------------------
1343 wx.CallAfter(self.__on_panel_selection_modified)
1344 #--------------------------------------------------------
1346 self._TCTRL_panel_comment.SetValue(u'') 1347 if self._PRW_panel.GetValue().strip() == u'': 1348 self.panel_data_grid.panel_to_show = None 1349 self.panel_data_grid.Hide() 1350 self.Layout()
1351 #-------------------------------------------------------- 1352 # internal API 1353 #--------------------------------------------------------
1354 - def __init_ui(self):
1355 self.__action_button_popup = wx.Menu(title = _('Perform on selected results:')) 1356 1357 menu_id = wx.NewId() 1358 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('Review and &sign'))) 1359 wx.EVT_MENU(self.__action_button_popup, menu_id, self.__on_sign_current_selection) 1360 1361 menu_id = wx.NewId() 1362 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('Plot'))) 1363 wx.EVT_MENU(self.__action_button_popup, menu_id, self.__on_plot_current_selection) 1364 1365 menu_id = wx.NewId() 1366 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('Export to &file'))) 1367 #wx.EVT_MENU(self.__action_button_popup, menu_id, self.data_grid.current_selection_to_file) 1368 self.__action_button_popup.Enable(id = menu_id, enable = False) 1369 1370 menu_id = wx.NewId() 1371 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('Export to &clipboard'))) 1372 #wx.EVT_MENU(self.__action_button_popup, menu_id, self.data_grid.current_selection_to_clipboard) 1373 self.__action_button_popup.Enable(id = menu_id, enable = False) 1374 1375 menu_id = wx.NewId() 1376 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('&Delete'))) 1377 wx.EVT_MENU(self.__action_button_popup, menu_id, self.__on_delete_current_selection) 1378 1379 # FIXME: create inbox message to staff to phone patient to come in 1380 # FIXME: generate and let edit a SOAP narrative and include the values 1381 1382 self._PRW_panel.add_callback_on_selection(callback = self._on_panel_selected) 1383 self._PRW_panel.add_callback_on_modified(callback = self._on_panel_selection_modified) 1384 1385 self.panel_data_grid.show_by_panel = True 1386 self.panel_data_grid.panel_to_show = None 1387 self.panel_data_grid.Hide() 1388 self.Layout() 1389 1390 self._PRW_panel.SetFocus()
1391 #-------------------------------------------------------- 1392 # reget mixin API 1393 #--------------------------------------------------------
1394 - def _populate_with_data(self):
1395 """Populate fields in pages with data from model.""" 1396 pat = gmPerson.gmCurrentPatient() 1397 if pat.connected: 1398 self.data_grid.patient = pat 1399 self.panel_data_grid.patient = pat 1400 else: 1401 self.data_grid.patient = None 1402 self.panel_data_grid.patient = None 1403 return True
1404 1405 #================================================================ 1406 # editing widgets 1407 #================================================================ 1408 from Gnumed.wxGladeWidgets import wxgMeasurementsReviewDlg 1409
1410 -class cMeasurementsReviewDlg(wxgMeasurementsReviewDlg.wxgMeasurementsReviewDlg):
1411
1412 - def __init__(self, *args, **kwargs):
1413 1414 try: 1415 tests = kwargs['tests'] 1416 del kwargs['tests'] 1417 test_count = len(tests) 1418 try: del kwargs['test_count'] 1419 except KeyError: pass 1420 except KeyError: 1421 tests = None 1422 test_count = kwargs['test_count'] 1423 del kwargs['test_count'] 1424 1425 wxgMeasurementsReviewDlg.wxgMeasurementsReviewDlg.__init__(self, *args, **kwargs) 1426 1427 if tests is None: 1428 msg = _('%s results selected. Too many to list individually.') % test_count 1429 else: 1430 msg = ' // '.join ( 1431 [ u'%s: %s %s (%s)' % ( 1432 t['unified_abbrev'], 1433 t['unified_val'], 1434 t['val_unit'], 1435 gmDateTime.pydt_strftime(t['clin_when'], '%Y %b %d') 1436 ) for t in tests 1437 ] 1438 ) 1439 1440 self._LBL_tests.SetLabel(msg) 1441 1442 if test_count == 1: 1443 self._TCTRL_comment.Enable(True) 1444 self._TCTRL_comment.SetValue(gmTools.coalesce(tests[0]['review_comment'], u'')) 1445 if tests[0]['you_are_responsible']: 1446 self._CHBOX_responsible.Enable(False) 1447 1448 self.Fit()
1449 #-------------------------------------------------------- 1450 # event handling 1451 #--------------------------------------------------------
1452 - def _on_signoff_button_pressed(self, evt):
1453 if self.IsModal(): 1454 self.EndModal(wx.ID_APPLY) 1455 else: 1456 self.Close()
1457 #================================================================ 1458 from Gnumed.wxGladeWidgets import wxgMeasurementEditAreaPnl 1459
1460 -class cMeasurementEditAreaPnl(wxgMeasurementEditAreaPnl.wxgMeasurementEditAreaPnl, gmEditArea.cGenericEditAreaMixin):
1461 """This edit area saves *new* measurements into the active patient only.""" 1462
1463 - def __init__(self, *args, **kwargs):
1464 1465 try: 1466 self.__default_date = kwargs['date'] 1467 del kwargs['date'] 1468 except KeyError: 1469 self.__default_date = None 1470 1471 wxgMeasurementEditAreaPnl.wxgMeasurementEditAreaPnl.__init__(self, *args, **kwargs) 1472 gmEditArea.cGenericEditAreaMixin.__init__(self) 1473 1474 self.__register_interests() 1475 1476 self.successful_save_msg = _('Successfully saved measurement.') 1477 1478 self._DPRW_evaluated.display_accuracy = gmDateTime.acc_minutes
1479 #-------------------------------------------------------- 1480 # generic edit area mixin API 1481 #--------------------------------------------------------
1482 - def _refresh_as_new(self):
1483 self._PRW_test.SetText(u'', None, True) 1484 self.__refresh_loinc_info() 1485 self.__refresh_previous_value() 1486 self.__update_units_context() 1487 self._TCTRL_result.SetValue(u'') 1488 self._PRW_units.SetText(u'', None, True) 1489 self._PRW_abnormality_indicator.SetText(u'', None, True) 1490 if self.__default_date is None: 1491 self._DPRW_evaluated.SetData(data = pyDT.datetime.now(tz = gmDateTime.gmCurrentLocalTimezone)) 1492 else: 1493 self._DPRW_evaluated.SetData(data = None) 1494 self._TCTRL_note_test_org.SetValue(u'') 1495 self._PRW_intended_reviewer.SetData(gmStaff.gmCurrentProvider()['pk_staff']) 1496 self._PRW_problem.SetData() 1497 self._TCTRL_narrative.SetValue(u'') 1498 self._CHBOX_review.SetValue(False) 1499 self._CHBOX_abnormal.SetValue(False) 1500 self._CHBOX_relevant.SetValue(False) 1501 self._CHBOX_abnormal.Enable(False) 1502 self._CHBOX_relevant.Enable(False) 1503 self._TCTRL_review_comment.SetValue(u'') 1504 self._TCTRL_normal_min.SetValue(u'') 1505 self._TCTRL_normal_max.SetValue(u'') 1506 self._TCTRL_normal_range.SetValue(u'') 1507 self._TCTRL_target_min.SetValue(u'') 1508 self._TCTRL_target_max.SetValue(u'') 1509 self._TCTRL_target_range.SetValue(u'') 1510 self._TCTRL_norm_ref_group.SetValue(u'') 1511 1512 self._PRW_test.SetFocus()
1513 #--------------------------------------------------------
1514 - def _refresh_from_existing(self):
1515 self._PRW_test.SetData(data = self.data['pk_test_type']) 1516 self.__refresh_loinc_info() 1517 self.__refresh_previous_value() 1518 self.__update_units_context() 1519 self._TCTRL_result.SetValue(self.data['unified_val']) 1520 self._PRW_units.SetText(self.data['val_unit'], self.data['val_unit'], True) 1521 self._PRW_abnormality_indicator.SetText ( 1522 gmTools.coalesce(self.data['abnormality_indicator'], u''), 1523 gmTools.coalesce(self.data['abnormality_indicator'], u''), 1524 True 1525 ) 1526 self._DPRW_evaluated.SetData(data = self.data['clin_when']) 1527 self._TCTRL_note_test_org.SetValue(gmTools.coalesce(self.data['note_test_org'], u'')) 1528 self._PRW_intended_reviewer.SetData(self.data['pk_intended_reviewer']) 1529 self._PRW_problem.SetData(self.data['pk_episode']) 1530 self._TCTRL_narrative.SetValue(gmTools.coalesce(self.data['comment'], u'')) 1531 self._CHBOX_review.SetValue(False) 1532 self._CHBOX_abnormal.SetValue(gmTools.coalesce(self.data['is_technically_abnormal'], False)) 1533 self._CHBOX_relevant.SetValue(gmTools.coalesce(self.data['is_clinically_relevant'], False)) 1534 self._CHBOX_abnormal.Enable(False) 1535 self._CHBOX_relevant.Enable(False) 1536 self._TCTRL_review_comment.SetValue(gmTools.coalesce(self.data['review_comment'], u'')) 1537 self._TCTRL_normal_min.SetValue(unicode(gmTools.coalesce(self.data['val_normal_min'], u''))) 1538 self._TCTRL_normal_max.SetValue(unicode(gmTools.coalesce(self.data['val_normal_max'], u''))) 1539 self._TCTRL_normal_range.SetValue(gmTools.coalesce(self.data['val_normal_range'], u'')) 1540 self._TCTRL_target_min.SetValue(unicode(gmTools.coalesce(self.data['val_target_min'], u''))) 1541 self._TCTRL_target_max.SetValue(unicode(gmTools.coalesce(self.data['val_target_max'], u''))) 1542 self._TCTRL_target_range.SetValue(gmTools.coalesce(self.data['val_target_range'], u'')) 1543 self._TCTRL_norm_ref_group.SetValue(gmTools.coalesce(self.data['norm_ref_group'], u'')) 1544 1545 self._TCTRL_result.SetFocus()
1546 #--------------------------------------------------------
1548 self._refresh_from_existing() 1549 1550 self._PRW_test.SetText(u'', None, True) 1551 self.__refresh_loinc_info() 1552 self.__refresh_previous_value() 1553 self.__update_units_context() 1554 self._TCTRL_result.SetValue(u'') 1555 self._PRW_units.SetText(u'', None, True) 1556 self._PRW_abnormality_indicator.SetText(u'', None, True) 1557 # self._DPRW_evaluated 1558 self._TCTRL_note_test_org.SetValue(u'') 1559 self._TCTRL_narrative.SetValue(u'') 1560 self._CHBOX_review.SetValue(False) 1561 self._CHBOX_abnormal.SetValue(False) 1562 self._CHBOX_relevant.SetValue(False) 1563 self._CHBOX_abnormal.Enable(False) 1564 self._CHBOX_relevant.Enable(False) 1565 self._TCTRL_review_comment.SetValue(u'') 1566 self._TCTRL_normal_min.SetValue(u'') 1567 self._TCTRL_normal_max.SetValue(u'') 1568 self._TCTRL_normal_range.SetValue(u'') 1569 self._TCTRL_target_min.SetValue(u'') 1570 self._TCTRL_target_max.SetValue(u'') 1571 self._TCTRL_target_range.SetValue(u'') 1572 self._TCTRL_norm_ref_group.SetValue(u'') 1573 1574 self._PRW_test.SetFocus()
1575 #--------------------------------------------------------
1576 - def _valid_for_save(self):
1577 1578 validity = True 1579 1580 if not self._DPRW_evaluated.is_valid_timestamp(): 1581 self._DPRW_evaluated.display_as_valid(False) 1582 validity = False 1583 else: 1584 self._DPRW_evaluated.display_as_valid(True) 1585 1586 val = self._TCTRL_result.GetValue().strip() 1587 if val == u'': 1588 validity = False 1589 self.display_ctrl_as_valid(self._TCTRL_result, False) 1590 else: 1591 self.display_ctrl_as_valid(self._TCTRL_result, True) 1592 numeric, val = gmTools.input2decimal(val) 1593 if numeric: 1594 if self._PRW_units.GetValue().strip() == u'': 1595 self._PRW_units.display_as_valid(False) 1596 validity = False 1597 else: 1598 self._PRW_units.display_as_valid(True) 1599 else: 1600 self._PRW_units.display_as_valid(True) 1601 1602 if self._PRW_problem.GetValue().strip() == u'': 1603 self._PRW_problem.display_as_valid(False) 1604 validity = False 1605 else: 1606 self._PRW_problem.display_as_valid(True) 1607 1608 if self._PRW_test.GetValue().strip() == u'': 1609 self._PRW_test.display_as_valid(False) 1610 validity = False 1611 else: 1612 self._PRW_test.display_as_valid(True) 1613 1614 if self._PRW_intended_reviewer.GetData() is None: 1615 self._PRW_intended_reviewer.display_as_valid(False) 1616 validity = False 1617 else: 1618 self._PRW_intended_reviewer.display_as_valid(True) 1619 1620 ctrls = [self._TCTRL_normal_min, self._TCTRL_normal_max, self._TCTRL_target_min, self._TCTRL_target_max] 1621 for widget in ctrls: 1622 val = widget.GetValue().strip() 1623 if val == u'': 1624 continue 1625 try: 1626 decimal.Decimal(val.replace(',', u'.', 1)) 1627 self.display_ctrl_as_valid(widget, True) 1628 except: 1629 validity = False 1630 self.display_ctrl_as_valid(widget, False) 1631 1632 if validity is False: 1633 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save result. Invalid or missing essential input.')) 1634 1635 return validity
1636 #--------------------------------------------------------
1637 - def _save_as_new(self):
1638 1639 emr = gmPerson.gmCurrentPatient().get_emr() 1640 1641 success, result = gmTools.input2decimal(self._TCTRL_result.GetValue()) 1642 if success: 1643 v_num = result 1644 v_al = None 1645 else: 1646 v_al = self._TCTRL_result.GetValue().strip() 1647 v_num = None 1648 1649 pk_type = self._PRW_test.GetData() 1650 if pk_type is None: 1651 tt = gmPathLab.create_measurement_type ( 1652 lab = None, 1653 abbrev = self._PRW_test.GetValue().strip(), 1654 name = self._PRW_test.GetValue().strip(), 1655 unit = gmTools.coalesce(self._PRW_units.GetData(), self._PRW_units.GetValue()).strip() 1656 ) 1657 pk_type = tt['pk_test_type'] 1658 1659 tr = emr.add_test_result ( 1660 episode = self._PRW_problem.GetData(can_create=True, is_open=False), 1661 type = pk_type, 1662 intended_reviewer = self._PRW_intended_reviewer.GetData(), 1663 val_num = v_num, 1664 val_alpha = v_al, 1665 unit = self._PRW_units.GetValue() 1666 ) 1667 1668 tr['clin_when'] = self._DPRW_evaluated.GetData().get_pydt() 1669 1670 ctrls = [ 1671 ('abnormality_indicator', self._PRW_abnormality_indicator), 1672 ('note_test_org', self._TCTRL_note_test_org), 1673 ('comment', self._TCTRL_narrative), 1674 ('val_normal_range', self._TCTRL_normal_range), 1675 ('val_target_range', self._TCTRL_target_range), 1676 ('norm_ref_group', self._TCTRL_norm_ref_group) 1677 ] 1678 for field, widget in ctrls: 1679 tr[field] = widget.GetValue().strip() 1680 1681 ctrls = [ 1682 ('val_normal_min', self._TCTRL_normal_min), 1683 ('val_normal_max', self._TCTRL_normal_max), 1684 ('val_target_min', self._TCTRL_target_min), 1685 ('val_target_max', self._TCTRL_target_max) 1686 ] 1687 for field, widget in ctrls: 1688 val = widget.GetValue().strip() 1689 if val == u'': 1690 tr[field] = None 1691 else: 1692 tr[field] = decimal.Decimal(val.replace(',', u'.', 1)) 1693 1694 tr.save_payload() 1695 1696 if self._CHBOX_review.GetValue() is True: 1697 tr.set_review ( 1698 technically_abnormal = self._CHBOX_abnormal.GetValue(), 1699 clinically_relevant = self._CHBOX_relevant.GetValue(), 1700 comment = gmTools.none_if(self._TCTRL_review_comment.GetValue().strip(), u''), 1701 make_me_responsible = False 1702 ) 1703 1704 self.data = tr 1705 1706 wx.CallAfter ( 1707 plot_adjacent_measurements, 1708 test = self.data, 1709 plot_singular_result = False, 1710 use_default_template = True 1711 ) 1712 1713 return True
1714 #--------------------------------------------------------
1715 - def _save_as_update(self):
1716 1717 success, result = gmTools.input2decimal(self._TCTRL_result.GetValue()) 1718 if success: 1719 v_num = result 1720 v_al = None 1721 else: 1722 v_num = None 1723 v_al = self._TCTRL_result.GetValue().strip() 1724 1725 pk_type = self._PRW_test.GetData() 1726 if pk_type is None: 1727 tt = gmPathLab.create_measurement_type ( 1728 lab = None, 1729 abbrev = self._PRW_test.GetValue().strip(), 1730 name = self._PRW_test.GetValue().strip(), 1731 unit = gmTools.none_if(self._PRW_units.GetValue().strip(), u'') 1732 ) 1733 pk_type = tt['pk_test_type'] 1734 1735 tr = self.data 1736 1737 tr['pk_episode'] = self._PRW_problem.GetData(can_create=True, is_open=False) 1738 tr['pk_test_type'] = pk_type 1739 tr['pk_intended_reviewer'] = self._PRW_intended_reviewer.GetData() 1740 tr['val_num'] = v_num 1741 tr['val_alpha'] = v_al 1742 tr['val_unit'] = gmTools.coalesce(self._PRW_units.GetData(), self._PRW_units.GetValue()).strip() 1743 tr['clin_when'] = self._DPRW_evaluated.GetData().get_pydt() 1744 1745 ctrls = [ 1746 ('abnormality_indicator', self._PRW_abnormality_indicator), 1747 ('note_test_org', self._TCTRL_note_test_org), 1748 ('comment', self._TCTRL_narrative), 1749 ('val_normal_range', self._TCTRL_normal_range), 1750 ('val_target_range', self._TCTRL_target_range), 1751 ('norm_ref_group', self._TCTRL_norm_ref_group) 1752 ] 1753 for field, widget in ctrls: 1754 tr[field] = widget.GetValue().strip() 1755 1756 ctrls = [ 1757 ('val_normal_min', self._TCTRL_normal_min), 1758 ('val_normal_max', self._TCTRL_normal_max), 1759 ('val_target_min', self._TCTRL_target_min), 1760 ('val_target_max', self._TCTRL_target_max) 1761 ] 1762 for field, widget in ctrls: 1763 val = widget.GetValue().strip() 1764 if val == u'': 1765 tr[field] = None 1766 else: 1767 tr[field] = decimal.Decimal(val.replace(',', u'.', 1)) 1768 1769 tr.save_payload() 1770 1771 if self._CHBOX_review.GetValue() is True: 1772 tr.set_review ( 1773 technically_abnormal = self._CHBOX_abnormal.GetValue(), 1774 clinically_relevant = self._CHBOX_relevant.GetValue(), 1775 comment = gmTools.none_if(self._TCTRL_review_comment.GetValue().strip(), u''), 1776 make_me_responsible = False 1777 ) 1778 1779 wx.CallAfter ( 1780 plot_adjacent_measurements, 1781 test = self.data, 1782 plot_singular_result = False, 1783 use_default_template = True 1784 ) 1785 1786 return True
1787 #-------------------------------------------------------- 1788 # event handling 1789 #--------------------------------------------------------
1790 - def __register_interests(self):
1791 self._PRW_test.add_callback_on_lose_focus(self._on_leave_test_prw) 1792 self._PRW_abnormality_indicator.add_callback_on_lose_focus(self._on_leave_indicator_prw)
1793 #--------------------------------------------------------
1794 - def _on_leave_test_prw(self):
1795 self.__refresh_loinc_info() 1796 self.__refresh_previous_value() 1797 self.__update_units_context()
1798 #--------------------------------------------------------
1799 - def _on_leave_indicator_prw(self):
1800 # if the user hasn't explicitly enabled reviewing 1801 if not self._CHBOX_review.GetValue(): 1802 self._CHBOX_abnormal.SetValue(self._PRW_abnormality_indicator.GetValue().strip() != u'')
1803 #--------------------------------------------------------
1804 - def _on_review_box_checked(self, evt):
1805 self._CHBOX_abnormal.Enable(self._CHBOX_review.GetValue()) 1806 self._CHBOX_relevant.Enable(self._CHBOX_review.GetValue()) 1807 self._TCTRL_review_comment.Enable(self._CHBOX_review.GetValue())
1808 #--------------------------------------------------------
1809 - def _on_test_info_button_pressed(self, event):
1810 1811 pk = self._PRW_test.GetData() 1812 if pk is not None: 1813 tt = gmPathLab.cMeasurementType(aPK_obj = pk) 1814 search_term = u'%s %s %s' % ( 1815 tt['name'], 1816 tt['abbrev'], 1817 gmTools.coalesce(tt['loinc'], u'') 1818 ) 1819 else: 1820 search_term = self._PRW_test.GetValue() 1821 1822 search_term = search_term.replace(' ', u'+') 1823 1824 call_browser_on_measurement_type(measurement_type = search_term)
1825 #-------------------------------------------------------- 1826 # internal helpers 1827 #--------------------------------------------------------
1828 - def __update_units_context(self):
1829 1830 self._PRW_units.unset_context(context = u'loinc') 1831 1832 tt = self._PRW_test.GetData(as_instance = True) 1833 1834 if tt is None: 1835 self._PRW_units.unset_context(context = u'pk_type') 1836 if self._PRW_test.GetValue().strip() == u'': 1837 self._PRW_units.unset_context(context = u'test_name') 1838 else: 1839 self._PRW_units.set_context(context = u'test_name', val = self._PRW_test.GetValue().strip()) 1840 return 1841 1842 self._PRW_units.set_context(context = u'pk_type', val = tt['pk_test_type']) 1843 self._PRW_units.set_context(context = u'test_name', val = tt['name']) 1844 1845 if tt['loinc'] is None: 1846 return 1847 1848 self._PRW_units.set_context(context = u'loinc', val = tt['loinc'])
1849 #--------------------------------------------------------
1850 - def __refresh_loinc_info(self):
1851 1852 self._TCTRL_loinc.SetValue(u'') 1853 1854 if self._PRW_test.GetData() is None: 1855 return 1856 1857 tt = self._PRW_test.GetData(as_instance = True) 1858 1859 if tt['loinc'] is None: 1860 return 1861 1862 info = gmLOINC.loinc2term(loinc = tt['loinc']) 1863 if len(info) == 0: 1864 self._TCTRL_loinc.SetValue(u'') 1865 return 1866 1867 self._TCTRL_loinc.SetValue(u'%s: %s' % (tt['loinc'], info[0]))
1868 #--------------------------------------------------------
1869 - def __refresh_previous_value(self):
1870 self._TCTRL_previous_value.SetValue(u'') 1871 # it doesn't make much sense to show the most 1872 # recent value when editing an existing one 1873 if self.data is not None: 1874 return 1875 if self._PRW_test.GetData() is None: 1876 return 1877 tt = self._PRW_test.GetData(as_instance = True) 1878 most_recent = tt.get_most_recent_results ( 1879 no_of_results = 1, 1880 patient = gmPerson.gmCurrentPatient().ID 1881 ) 1882 if most_recent is None: 1883 return 1884 self._TCTRL_previous_value.SetValue(_('%s ago: %s%s%s - %s') % ( 1885 gmDateTime.format_interval_medically(gmDateTime.pydt_now_here() - most_recent['clin_when']), 1886 most_recent['unified_val'], 1887 most_recent['val_unit'], 1888 gmTools.coalesce(most_recent['abnormality_indicator'], u'', u' (%s)'), 1889 most_recent['name_tt'] 1890 ))
1891 1892 #================================================================ 1893 # measurement type handling 1894 #================================================================
1895 -def pick_measurement_types(parent=None, msg=None, right_column=None, picks=None):
1896 1897 if parent is None: 1898 parent = wx.GetApp().GetTopWindow() 1899 1900 if msg is None: 1901 msg = _('Pick the relevant measurement types.') 1902 1903 if right_column is None: 1904 right_columns = [_('Picked')] 1905 else: 1906 right_columns = [right_column] 1907 1908 picker = gmListWidgets.cItemPickerDlg(parent, -1, msg = msg) 1909 picker.set_columns(columns = [_('Known measurement types')], columns_right = right_columns) 1910 types = gmPathLab.get_measurement_types(order_by = 'unified_abbrev') 1911 picker.set_choices ( 1912 choices = [ 1913 u'%s: %s%s' % ( 1914 t['unified_abbrev'], 1915 t['unified_name'], 1916 gmTools.coalesce(t['name_org'], u'', u' (%s)') 1917 ) 1918 for t in types 1919 ], 1920 data = types 1921 ) 1922 if picks is not None: 1923 picker.set_picks ( 1924 picks = [ 1925 u'%s: %s%s' % ( 1926 p['unified_abbrev'], 1927 p['unified_name'], 1928 gmTools.coalesce(p['name_org'], u'', u' (%s)') 1929 ) 1930 for p in picks 1931 ], 1932 data = picks 1933 ) 1934 result = picker.ShowModal() 1935 1936 if result == wx.ID_CANCEL: 1937 picker.Destroy() 1938 return None 1939 1940 picks = picker.picks 1941 picker.Destroy() 1942 return picks
1943 1944 #----------------------------------------------------------------
1945 -def manage_measurement_types(parent=None):
1946 1947 if parent is None: 1948 parent = wx.GetApp().GetTopWindow() 1949 1950 #------------------------------------------------------------ 1951 def edit(test_type=None): 1952 ea = cMeasurementTypeEAPnl(parent = parent, id = -1, type = test_type) 1953 dlg = gmEditArea.cGenericEditAreaDlg2 ( 1954 parent = parent, 1955 id = -1, 1956 edit_area = ea, 1957 single_entry = gmTools.bool2subst((test_type is None), False, True) 1958 ) 1959 dlg.SetTitle(gmTools.coalesce(test_type, _('Adding measurement type'), _('Editing measurement type'))) 1960 1961 if dlg.ShowModal() == wx.ID_OK: 1962 dlg.Destroy() 1963 return True 1964 1965 dlg.Destroy() 1966 return False
1967 #------------------------------------------------------------ 1968 def delete(measurement_type): 1969 if measurement_type.in_use: 1970 gmDispatcher.send ( 1971 signal = 'statustext', 1972 beep = True, 1973 msg = _('Cannot delete measurement type [%s (%s)] because it is in use.') % (measurement_type['name'], measurement_type['abbrev']) 1974 ) 1975 return False 1976 gmPathLab.delete_measurement_type(measurement_type = measurement_type['pk_test_type']) 1977 return True 1978 #------------------------------------------------------------ 1979 def get_tooltip(test_type): 1980 return test_type.format() 1981 #------------------------------------------------------------ 1982 def refresh(lctrl): 1983 mtypes = gmPathLab.get_measurement_types(order_by = 'name, abbrev') 1984 items = [ [ 1985 m['abbrev'], 1986 m['name'], 1987 gmTools.coalesce(m['conversion_unit'], u''), 1988 gmTools.coalesce(m['loinc'], u''), 1989 gmTools.coalesce(m['comment_type'], u''), 1990 gmTools.coalesce(m['name_org'], u'?'), 1991 gmTools.coalesce(m['comment_org'], u''), 1992 m['pk_test_type'] 1993 ] for m in mtypes ] 1994 lctrl.set_string_items(items) 1995 lctrl.set_data(mtypes) 1996 #------------------------------------------------------------ 1997 msg = _( 1998 '\n' 1999 'These are the measurement types currently defined in GNUmed.\n' 2000 '\n' 2001 ) 2002 2003 gmListWidgets.get_choices_from_list ( 2004 parent = parent, 2005 msg = msg, 2006 caption = _('Showing measurement types.'), 2007 columns = [ _('Abbrev'), _('Name'), _('Unit'), _('LOINC'), _('Comment'), _('Org'), _('Comment'), u'#' ], 2008 single_selection = True, 2009 refresh_callback = refresh, 2010 edit_callback = edit, 2011 new_callback = edit, 2012 delete_callback = delete, 2013 list_tooltip_callback = get_tooltip 2014 ) 2015 2016 #----------------------------------------------------------------
2017 -class cMeasurementTypePhraseWheel(gmPhraseWheel.cPhraseWheel):
2018
2019 - def __init__(self, *args, **kwargs):
2020 2021 query = u""" 2022 SELECT DISTINCT ON (field_label) 2023 pk_test_type AS data, 2024 name 2025 || ' (' 2026 || coalesce ( 2027 (SELECT unit || ' @ ' || organization FROM clin.v_test_orgs c_vto WHERE c_vto.pk_test_org = c_vtt.pk_test_org), 2028 '%(in_house)s' 2029 ) 2030 || ')' 2031 AS field_label, 2032 name 2033 || ' (' 2034 || abbrev || ', ' 2035 || coalesce(abbrev_meta || ': ' || name_meta || ', ', '') 2036 || coalesce ( 2037 (SELECT unit || ' @ ' || organization FROM clin.v_test_orgs c_vto WHERE c_vto.pk_test_org = c_vtt.pk_test_org), 2038 '%(in_house)s' 2039 ) 2040 || ')' 2041 AS list_label 2042 FROM 2043 clin.v_test_types c_vtt 2044 WHERE 2045 abbrev_meta %%(fragment_condition)s 2046 OR 2047 name_meta %%(fragment_condition)s 2048 OR 2049 abbrev %%(fragment_condition)s 2050 OR 2051 name %%(fragment_condition)s 2052 ORDER BY field_label 2053 LIMIT 50""" % {'in_house': _('generic / in house lab')} 2054 2055 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query) 2056 mp.setThresholds(1, 2, 4) 2057 mp.word_separators = '[ \t:@]+' 2058 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 2059 self.matcher = mp 2060 self.SetToolTipString(_('Select the type of measurement.')) 2061 self.selection_only = False
2062 #------------------------------------------------------------
2063 - def _data2instance(self):
2064 if self.GetData() is None: 2065 return None 2066 2067 return gmPathLab.cMeasurementType(aPK_obj = self.GetData())
2068 #---------------------------------------------------------------- 2069 from Gnumed.wxGladeWidgets import wxgMeasurementTypeEAPnl 2070
2071 -class cMeasurementTypeEAPnl(wxgMeasurementTypeEAPnl.wxgMeasurementTypeEAPnl, gmEditArea.cGenericEditAreaMixin):
2072
2073 - def __init__(self, *args, **kwargs):
2074 2075 try: 2076 data = kwargs['type'] 2077 del kwargs['type'] 2078 except KeyError: 2079 data = None 2080 2081 wxgMeasurementTypeEAPnl.wxgMeasurementTypeEAPnl.__init__(self, *args, **kwargs) 2082 gmEditArea.cGenericEditAreaMixin.__init__(self) 2083 self.mode = 'new' 2084 self.data = data 2085 if data is not None: 2086 self.mode = 'edit' 2087 2088 self.__init_ui()
2089 2090 #----------------------------------------------------------------
2091 - def __init_ui(self):
2092 2093 # name phraseweel 2094 query = u""" 2095 select distinct on (name) 2096 pk, 2097 name 2098 from clin.test_type 2099 where 2100 name %(fragment_condition)s 2101 order by name 2102 limit 50""" 2103 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query) 2104 mp.setThresholds(1, 2, 4) 2105 self._PRW_name.matcher = mp 2106 self._PRW_name.selection_only = False 2107 self._PRW_name.add_callback_on_lose_focus(callback = self._on_name_lost_focus) 2108 2109 # abbreviation 2110 query = u""" 2111 select distinct on (abbrev) 2112 pk, 2113 abbrev 2114 from clin.test_type 2115 where 2116 abbrev %(fragment_condition)s 2117 order by abbrev 2118 limit 50""" 2119 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query) 2120 mp.setThresholds(1, 2, 3) 2121 self._PRW_abbrev.matcher = mp 2122 self._PRW_abbrev.selection_only = False 2123 2124 # unit 2125 self._PRW_conversion_unit.selection_only = False 2126 2127 # loinc 2128 #mp = gmMatchProvider.cMatchProvider_SQL2(queries = query) 2129 mp = gmLOINC.cLOINCMatchProvider() 2130 mp.setThresholds(1, 2, 4) 2131 #mp.print_queries = True 2132 #mp.word_separators = '[ \t:@]+' 2133 self._PRW_loinc.matcher = mp 2134 self._PRW_loinc.selection_only = False 2135 self._PRW_loinc.add_callback_on_lose_focus(callback = self._on_loinc_lost_focus)
2136 #----------------------------------------------------------------
2137 - def _on_name_lost_focus(self):
2138 2139 test = self._PRW_name.GetValue().strip() 2140 2141 if test == u'': 2142 self._PRW_conversion_unit.unset_context(context = u'test_name') 2143 return 2144 2145 self._PRW_conversion_unit.set_context(context = u'test_name', val = test)
2146 #----------------------------------------------------------------
2147 - def _on_loinc_lost_focus(self):
2148 loinc = self._PRW_loinc.GetData() 2149 2150 if loinc is None: 2151 self._TCTRL_loinc_info.SetValue(u'') 2152 self._PRW_conversion_unit.unset_context(context = u'loinc') 2153 return 2154 2155 self._PRW_conversion_unit.set_context(context = u'loinc', val = loinc) 2156 2157 info = gmLOINC.loinc2term(loinc = loinc) 2158 if len(info) == 0: 2159 self._TCTRL_loinc_info.SetValue(u'') 2160 return 2161 2162 self._TCTRL_loinc_info.SetValue(info[0])
2163 #---------------------------------------------------------------- 2164 # generic Edit Area mixin API 2165 #----------------------------------------------------------------
2166 - def _valid_for_save(self):
2167 2168 has_errors = False 2169 for field in [self._PRW_name, self._PRW_abbrev, self._PRW_conversion_unit]: 2170 if field.GetValue().strip() in [u'', None]: 2171 has_errors = True 2172 field.display_as_valid(valid = False) 2173 else: 2174 field.display_as_valid(valid = True) 2175 field.Refresh() 2176 2177 return (not has_errors)
2178 #----------------------------------------------------------------
2179 - def _save_as_new(self):
2180 2181 pk_org = self._PRW_test_org.GetData() 2182 if pk_org is None: 2183 pk_org = gmPathLab.create_test_org ( 2184 name = gmTools.none_if(self._PRW_test_org.GetValue().strip(), u''), 2185 comment = gmTools.none_if(self._TCTRL_comment_org.GetValue().strip(), u'') 2186 )['pk_test_org'] 2187 2188 tt = gmPathLab.create_measurement_type ( 2189 lab = pk_org, 2190 abbrev = self._PRW_abbrev.GetValue().strip(), 2191 name = self._PRW_name.GetValue().strip(), 2192 unit = gmTools.coalesce ( 2193 self._PRW_conversion_unit.GetData(), 2194 self._PRW_conversion_unit.GetValue() 2195 ).strip() 2196 ) 2197 if self._PRW_loinc.GetData() is not None: 2198 tt['loinc'] = gmTools.none_if(self._PRW_loinc.GetData().strip(), u'') 2199 else: 2200 tt['loinc'] = gmTools.none_if(self._PRW_loinc.GetValue().strip(), u'') 2201 tt['comment_type'] = gmTools.none_if(self._TCTRL_comment_type.GetValue().strip(), u'') 2202 tt['pk_meta_test_type'] = self._PRW_meta_type.GetData() 2203 2204 tt.save() 2205 2206 self.data = tt 2207 2208 return True
2209 #----------------------------------------------------------------
2210 - def _save_as_update(self):
2211 2212 pk_org = self._PRW_test_org.GetData() 2213 if pk_org is None: 2214 pk_org = gmPathLab.create_test_org ( 2215 name = gmTools.none_if(self._PRW_test_org.GetValue().strip(), u''), 2216 comment = gmTools.none_if(self._TCTRL_comment_org.GetValue().strip(), u'') 2217 )['pk_test_org'] 2218 2219 self.data['pk_test_org'] = pk_org 2220 self.data['abbrev'] = self._PRW_abbrev.GetValue().strip() 2221 self.data['name'] = self._PRW_name.GetValue().strip() 2222 self.data['conversion_unit'] = gmTools.coalesce ( 2223 self._PRW_conversion_unit.GetData(), 2224 self._PRW_conversion_unit.GetValue() 2225 ).strip() 2226 if self._PRW_loinc.GetData() is not None: 2227 self.data['loinc'] = gmTools.none_if(self._PRW_loinc.GetData().strip(), u'') 2228 if self._PRW_loinc.GetData() is not None: 2229 self.data['loinc'] = gmTools.none_if(self._PRW_loinc.GetData().strip(), u'') 2230 else: 2231 self.data['loinc'] = gmTools.none_if(self._PRW_loinc.GetValue().strip(), u'') 2232 self.data['comment_type'] = gmTools.none_if(self._TCTRL_comment_type.GetValue().strip(), u'') 2233 self.data['pk_meta_test_type'] = self._PRW_meta_type.GetData() 2234 self.data.save() 2235 2236 return True
2237 #----------------------------------------------------------------
2238 - def _refresh_as_new(self):
2239 self._PRW_name.SetText(u'', None, True) 2240 self._on_name_lost_focus() 2241 self._PRW_abbrev.SetText(u'', None, True) 2242 self._PRW_conversion_unit.SetText(u'', None, True) 2243 self._PRW_loinc.SetText(u'', None, True) 2244 self._on_loinc_lost_focus() 2245 self._TCTRL_comment_type.SetValue(u'') 2246 self._PRW_test_org.SetText(u'', None, True) 2247 self._TCTRL_comment_org.SetValue(u'') 2248 self._PRW_meta_type.SetText(u'', None, True) 2249 2250 self._PRW_name.SetFocus()
2251 #----------------------------------------------------------------
2252 - def _refresh_from_existing(self):
2253 self._PRW_name.SetText(self.data['name'], self.data['name'], True) 2254 self._on_name_lost_focus() 2255 self._PRW_abbrev.SetText(self.data['abbrev'], self.data['abbrev'], True) 2256 self._PRW_conversion_unit.SetText ( 2257 gmTools.coalesce(self.data['conversion_unit'], u''), 2258 self.data['conversion_unit'], 2259 True 2260 ) 2261 self._PRW_loinc.SetText ( 2262 gmTools.coalesce(self.data['loinc'], u''), 2263 self.data['loinc'], 2264 True 2265 ) 2266 self._on_loinc_lost_focus() 2267 self._TCTRL_comment_type.SetValue(gmTools.coalesce(self.data['comment_type'], u'')) 2268 self._PRW_test_org.SetText ( 2269 gmTools.coalesce(self.data['pk_test_org'], u'', self.data['name_org']), 2270 self.data['pk_test_org'], 2271 True 2272 ) 2273 self._TCTRL_comment_org.SetValue(gmTools.coalesce(self.data['comment_org'], u'')) 2274 if self.data['pk_meta_test_type'] is None: 2275 self._PRW_meta_type.SetText(u'', None, True) 2276 else: 2277 self._PRW_meta_type.SetText(u'%s: %s' % (self.data['abbrev_meta'], self.data['name_meta']), self.data['pk_meta_test_type'], True) 2278 2279 self._PRW_name.SetFocus()
2280 #----------------------------------------------------------------
2282 self._refresh_as_new() 2283 self._PRW_test_org.SetText ( 2284 gmTools.coalesce(self.data['pk_test_org'], u'', self.data['name_org']), 2285 self.data['pk_test_org'], 2286 True 2287 ) 2288 self._TCTRL_comment_org.SetValue(gmTools.coalesce(self.data['comment_org'], u'')) 2289 2290 self._PRW_name.SetFocus()
2291 2292 #================================================================ 2293 _SQL_units_from_test_results = u""" 2294 -- via clin.v_test_results.pk_type (for types already used in results) 2295 SELECT 2296 val_unit AS data, 2297 val_unit AS field_label, 2298 val_unit || ' (' || name_tt || ')' AS list_label, 2299 1 AS rank 2300 FROM 2301 clin.v_test_results 2302 WHERE 2303 ( 2304 val_unit %(fragment_condition)s 2305 OR 2306 conversion_unit %(fragment_condition)s 2307 ) 2308 %(ctxt_type_pk)s 2309 %(ctxt_test_name)s 2310 """ 2311 2312 _SQL_units_from_test_types = u""" 2313 -- via clin.test_type (for types not yet used in results) 2314 SELECT 2315 conversion_unit AS data, 2316 conversion_unit AS field_label, 2317 conversion_unit || ' (' || name || ')' AS list_label, 2318 2 AS rank 2319 FROM 2320 clin.test_type 2321 WHERE 2322 conversion_unit %(fragment_condition)s 2323 %(ctxt_ctt)s 2324 """ 2325 2326 _SQL_units_from_loinc_ipcc = u""" 2327 -- via ref.loinc.ipcc_units 2328 SELECT 2329 ipcc_units AS data, 2330 ipcc_units AS field_label, 2331 ipcc_units || ' (LOINC.ipcc: ' || term || ')' AS list_label, 2332 3 AS rank 2333 FROM 2334 ref.loinc 2335 WHERE 2336 ipcc_units %(fragment_condition)s 2337 %(ctxt_loinc)s 2338 %(ctxt_loinc_term)s 2339 """ 2340 2341 _SQL_units_from_loinc_submitted = u""" 2342 -- via ref.loinc.submitted_units 2343 SELECT 2344 submitted_units AS data, 2345 submitted_units AS field_label, 2346 submitted_units || ' (LOINC.submitted:' || term || ')' AS list_label, 2347 3 AS rank 2348 FROM 2349 ref.loinc 2350 WHERE 2351 submitted_units %(fragment_condition)s 2352 %(ctxt_loinc)s 2353 %(ctxt_loinc_term)s 2354 """ 2355 2356 _SQL_units_from_loinc_example = u""" 2357 -- via ref.loinc.example_units 2358 SELECT 2359 example_units AS data, 2360 example_units AS field_label, 2361 example_units || ' (LOINC.example: ' || term || ')' AS list_label, 2362 3 AS rank 2363 FROM 2364 ref.loinc 2365 WHERE 2366 example_units %(fragment_condition)s 2367 %(ctxt_loinc)s 2368 %(ctxt_loinc_term)s 2369 """ 2370 2371 _SQL_units_from_atc = u""" 2372 -- via ref.atc.unit 2373 SELECT 2374 unit AS data, 2375 unit AS field_label, 2376 unit || ' (ATC: ' || term || ')' AS list_label, 2377 2 AS rank 2378 FROM 2379 ref.atc 2380 WHERE 2381 unit IS NOT NULL 2382 AND 2383 unit %(fragment_condition)s 2384 """ 2385 2386 _SQL_units_from_consumable_substance = u""" 2387 -- via ref.consumable_substance.unit 2388 SELECT 2389 unit AS data, 2390 unit AS field_label, 2391 unit || ' (' || description || ')' AS list_label, 2392 2 AS rank 2393 FROM 2394 ref.consumable_substance 2395 WHERE 2396 unit %(fragment_condition)s 2397 %(ctxt_substance)s 2398 """ 2399 2400 #----------------------------------------------------------------
2401 -class cUnitPhraseWheel(gmPhraseWheel.cPhraseWheel):
2402
2403 - def __init__(self, *args, **kwargs):
2404 2405 query = u""" 2406 SELECT DISTINCT ON (data) 2407 data, 2408 field_label, 2409 list_label 2410 FROM ( 2411 2412 SELECT 2413 data, 2414 field_label, 2415 list_label, 2416 rank 2417 FROM ( 2418 (%s) UNION ALL 2419 (%s) UNION ALL 2420 (%s) UNION ALL 2421 (%s) UNION ALL 2422 (%s) UNION ALL 2423 (%s) UNION ALL 2424 (%s) 2425 ) AS all_matching_units 2426 WHERE data IS NOT NULL 2427 ORDER BY rank, list_label 2428 2429 ) AS ranked_matching_units 2430 LIMIT 50""" % ( 2431 _SQL_units_from_test_results, 2432 _SQL_units_from_test_types, 2433 _SQL_units_from_loinc_ipcc, 2434 _SQL_units_from_loinc_submitted, 2435 _SQL_units_from_loinc_example, 2436 _SQL_units_from_atc, 2437 _SQL_units_from_consumable_substance 2438 ) 2439 2440 ctxt = { 2441 'ctxt_type_pk': { 2442 'where_part': u'AND pk_test_type = %(pk_type)s', 2443 'placeholder': u'pk_type' 2444 }, 2445 'ctxt_test_name': { 2446 'where_part': u'AND %(test_name)s IN (name_tt, name_meta, abbrev_meta)', 2447 'placeholder': u'test_name' 2448 }, 2449 'ctxt_ctt': { 2450 'where_part': u'AND %(test_name)s IN (name, abbrev)', 2451 'placeholder': u'test_name' 2452 }, 2453 'ctxt_loinc': { 2454 'where_part': u'AND code = %(loinc)s', 2455 'placeholder': u'loinc' 2456 }, 2457 'ctxt_loinc_term': { 2458 'where_part': u'AND term ~* %(test_name)s', 2459 'placeholder': u'test_name' 2460 }, 2461 'ctxt_substance': { 2462 'where_part': u'AND description ~* %(substance)s', 2463 'placeholder': u'substance' 2464 } 2465 } 2466 2467 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query, context = ctxt) 2468 mp.setThresholds(1, 2, 4) 2469 #mp.print_queries = True 2470 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 2471 self.matcher = mp 2472 self.SetToolTipString(_('Select the desired unit for the amount or measurement.')) 2473 self.selection_only = False 2474 self.phrase_separators = u'[;|]+'
2475 #================================================================ 2476 2477 #================================================================
2478 -class cTestResultIndicatorPhraseWheel(gmPhraseWheel.cPhraseWheel):
2479
2480 - def __init__(self, *args, **kwargs):
2481 2482 query = u""" 2483 select distinct abnormality_indicator, 2484 abnormality_indicator, abnormality_indicator 2485 from clin.v_test_results 2486 where 2487 abnormality_indicator %(fragment_condition)s 2488 order by abnormality_indicator 2489 limit 25""" 2490 2491 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query) 2492 mp.setThresholds(1, 1, 2) 2493 mp.ignored_chars = "[.'\\\[\]#$%_]+" + '"' 2494 mp.word_separators = '[ \t&:]+' 2495 gmPhraseWheel.cPhraseWheel.__init__ ( 2496 self, 2497 *args, 2498 **kwargs 2499 ) 2500 self.matcher = mp 2501 self.SetToolTipString(_('Select an indicator for the level of abnormality.')) 2502 self.selection_only = False
2503 2504 #================================================================ 2505 # measurement org widgets / functions 2506 #----------------------------------------------------------------
2507 -def edit_measurement_org(parent=None, org=None):
2508 ea = cMeasurementOrgEAPnl(parent = parent, id = -1) 2509 ea.data = org 2510 ea.mode = gmTools.coalesce(org, 'new', 'edit') 2511 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea) 2512 dlg.SetTitle(gmTools.coalesce(org, _('Adding new diagnostic org'), _('Editing diagnostic org'))) 2513 if dlg.ShowModal() == wx.ID_OK: 2514 dlg.Destroy() 2515 return True 2516 dlg.Destroy() 2517 return False
2518 #----------------------------------------------------------------
2519 -def manage_measurement_orgs(parent=None):
2520 2521 if parent is None: 2522 parent = wx.GetApp().GetTopWindow() 2523 2524 #------------------------------------------------------------ 2525 def edit(org=None): 2526 return edit_measurement_org(parent = parent, org = org)
2527 #------------------------------------------------------------ 2528 def refresh(lctrl): 2529 orgs = gmPathLab.get_test_orgs() 2530 lctrl.set_string_items ([ 2531 (o['unit'], o['organization'], gmTools.coalesce(o['test_org_contact'], u''), gmTools.coalesce(o['comment'], u''), o['pk_test_org']) 2532 for o in orgs 2533 ]) 2534 lctrl.set_data(orgs) 2535 #------------------------------------------------------------ 2536 def delete(test_org): 2537 gmPathLab.delete_test_org(test_org = test_org['pk_test_org']) 2538 return True 2539 #------------------------------------------------------------ 2540 gmListWidgets.get_choices_from_list ( 2541 parent = parent, 2542 msg = _('\nThese are the diagnostic orgs (path labs etc) currently defined in GNUmed.\n\n'), 2543 caption = _('Showing diagnostic orgs.'), 2544 columns = [_('Name'), _('Organization'), _('Contact'), _('Comment'), u'#'], 2545 single_selection = True, 2546 refresh_callback = refresh, 2547 edit_callback = edit, 2548 new_callback = edit, 2549 delete_callback = delete 2550 ) 2551 2552 #---------------------------------------------------------------- 2553 from Gnumed.wxGladeWidgets import wxgMeasurementOrgEAPnl 2554
2555 -class cMeasurementOrgEAPnl(wxgMeasurementOrgEAPnl.wxgMeasurementOrgEAPnl, gmEditArea.cGenericEditAreaMixin):
2556
2557 - def __init__(self, *args, **kwargs):
2558 2559 try: 2560 data = kwargs['org'] 2561 del kwargs['org'] 2562 except KeyError: 2563 data = None 2564 2565 wxgMeasurementOrgEAPnl.wxgMeasurementOrgEAPnl.__init__(self, *args, **kwargs) 2566 gmEditArea.cGenericEditAreaMixin.__init__(self) 2567 2568 self.mode = 'new' 2569 self.data = data 2570 if data is not None: 2571 self.mode = 'edit'
2572 2573 #self.__init_ui() 2574 #---------------------------------------------------------------- 2575 # def __init_ui(self): 2576 # # adjust phrasewheels etc 2577 #---------------------------------------------------------------- 2578 # generic Edit Area mixin API 2579 #----------------------------------------------------------------
2580 - def _valid_for_save(self):
2581 has_errors = False 2582 if self._PRW_org_unit.GetData() is None: 2583 if self._PRW_org_unit.GetValue().strip() == u'': 2584 has_errors = True 2585 self._PRW_org_unit.display_as_valid(valid = False) 2586 else: 2587 self._PRW_org_unit.display_as_valid(valid = True) 2588 else: 2589 self._PRW_org_unit.display_as_valid(valid = True) 2590 2591 return (not has_errors)
2592 #----------------------------------------------------------------
2593 - def _save_as_new(self):
2594 data = gmPathLab.create_test_org ( 2595 name = self._PRW_org_unit.GetValue().strip(), 2596 comment = self._TCTRL_comment.GetValue().strip(), 2597 pk_org_unit = self._PRW_org_unit.GetData() 2598 ) 2599 data['test_org_contact'] = self._TCTRL_contact.GetValue().strip() 2600 data.save() 2601 self.data = data 2602 return True
2603 #----------------------------------------------------------------
2604 - def _save_as_update(self):
2605 # get or create the org unit 2606 name = self._PRW_org_unit.GetValue().strip() 2607 org = gmOrganization.org_exists(organization = name) 2608 if org is None: 2609 org = gmOrganization.create_org ( 2610 organization = name, 2611 category = u'Laboratory' 2612 ) 2613 org_unit = gmOrganization.create_org_unit ( 2614 pk_organization = org['pk_org'], 2615 unit = name 2616 ) 2617 # update test_org fields 2618 self.data['pk_org_unit'] = org_unit['pk_org_unit'] 2619 self.data['test_org_contact'] = self._TCTRL_contact.GetValue().strip() 2620 self.data['comment'] = self._TCTRL_comment.GetValue().strip() 2621 self.data.save() 2622 return True
2623 #----------------------------------------------------------------
2624 - def _refresh_as_new(self):
2625 self._PRW_org_unit.SetText(value = u'', data = None) 2626 self._TCTRL_contact.SetValue(u'') 2627 self._TCTRL_comment.SetValue(u'')
2628 #----------------------------------------------------------------
2629 - def _refresh_from_existing(self):
2630 self._PRW_org_unit.SetText(value = self.data['unit'], data = self.data['pk_org_unit']) 2631 self._TCTRL_contact.SetValue(gmTools.coalesce(self.data['test_org_contact'], u'')) 2632 self._TCTRL_comment.SetValue(gmTools.coalesce(self.data['comment'], u''))
2633 #----------------------------------------------------------------
2635 self._refresh_as_new()
2636 #----------------------------------------------------------------
2637 - def _on_manage_orgs_button_pressed(self, event):
2638 gmOrganizationWidgets.manage_orgs(parent = self)
2639 2640 #----------------------------------------------------------------
2641 -class cMeasurementOrgPhraseWheel(gmPhraseWheel.cPhraseWheel):
2642
2643 - def __init__(self, *args, **kwargs):
2644 2645 query = u""" 2646 SELECT DISTINCT ON (list_label) 2647 pk_test_org AS data, 2648 unit || ' (' || organization || ')' AS field_label, 2649 unit || ' @ ' || organization AS list_label 2650 FROM clin.v_test_orgs 2651 WHERE 2652 unit %(fragment_condition)s 2653 OR 2654 organization %(fragment_condition)s 2655 ORDER BY list_label 2656 LIMIT 50""" 2657 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query) 2658 mp.setThresholds(1, 2, 4) 2659 #mp.word_separators = '[ \t:@]+' 2660 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 2661 self.matcher = mp 2662 self.SetToolTipString(_('The name of the path lab/diagnostic organisation.')) 2663 self.selection_only = False
2664 #------------------------------------------------------------
2665 - def _create_data(self):
2666 if self.GetData() is not None: 2667 _log.debug('data already set, not creating') 2668 return 2669 2670 if self.GetValue().strip() == u'': 2671 _log.debug('cannot create new lab, missing name') 2672 return 2673 2674 lab = gmPathLab.create_test_org(name = self.GetValue().strip()) 2675 self.SetText(value = lab['unit'], data = lab['pk_test_org']) 2676 return
2677 #------------------------------------------------------------
2678 - def _data2instance(self):
2679 return gmPathLab.cTestOrg(aPK_obj = self.GetData())
2680 2681 #================================================================
2682 -def manage_meta_test_types(parent=None):
2683 2684 if parent is None: 2685 parent = wx.GetApp().GetTopWindow() 2686 2687 #---------------------------------------- 2688 def get_tooltip(data): 2689 if data is None: 2690 return None 2691 return data.format(with_tests = True)
2692 #---------------------------------------- 2693 2694 msg = _( 2695 '\n' 2696 'These are the meta test types currently defined in GNUmed.\n' 2697 '\n' 2698 'Meta test types allow you to aggregate several actual test types used\n' 2699 'by pathology labs into one logical type.\n' 2700 '\n' 2701 'This is useful for grouping together results of tests which come under\n' 2702 'different names but really are the same thing. This often happens when\n' 2703 'you switch labs or the lab starts using another test method.\n' 2704 ) 2705 2706 mtts = gmPathLab.get_meta_test_types() 2707 2708 gmListWidgets.get_choices_from_list ( 2709 parent = parent, 2710 msg = msg, 2711 caption = _('Showing meta test types.'), 2712 columns = [_('Abbrev'), _('Name'), _('LOINC'), _('Comment'), u'#'], 2713 choices = [ [ 2714 m['abbrev'], 2715 m['name'], 2716 gmTools.coalesce(m['loinc'], u''), 2717 gmTools.coalesce(m['comment'], u''), 2718 m['pk'] 2719 ] for m in mtts ], 2720 data = mtts, 2721 single_selection = True, 2722 list_tooltip_callback = get_tooltip 2723 #edit_callback = edit, 2724 #new_callback = edit, 2725 #delete_callback = delete, 2726 #refresh_callback = refresh 2727 ) 2728 #----------------------------------------------------------------
2729 -class cMetaTestTypePRW(gmPhraseWheel.cPhraseWheel):
2730
2731 - def __init__(self, *args, **kwargs):
2732 2733 query = u""" 2734 SELECT DISTINCT ON (field_label) 2735 c_mtt.pk 2736 AS data, 2737 c_mtt.abbrev || ': ' || name 2738 AS field_label, 2739 c_mtt.abbrev || ': ' || name 2740 || coalesce ( 2741 ' (' || c_mtt.comment || ')', 2742 '' 2743 ) 2744 || coalesce ( 2745 ', LOINC: ' || c_mtt.loinc, 2746 '' 2747 ) 2748 AS list_label 2749 FROM 2750 clin.meta_test_type c_mtt 2751 WHERE 2752 abbrev %(fragment_condition)s 2753 OR 2754 name %(fragment_condition)s 2755 OR 2756 loinc %(fragment_condition)s 2757 ORDER BY field_label 2758 LIMIT 50""" 2759 2760 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query) 2761 mp.setThresholds(1, 2, 4) 2762 mp.word_separators = '[ \t:@]+' 2763 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 2764 self.matcher = mp 2765 self.SetToolTipString(_('Select the meta test type.')) 2766 self.selection_only = True
2767 #------------------------------------------------------------
2768 - def _data2instance(self):
2769 if self.GetData() is None: 2770 return None 2771 2772 return gmPathLab.cMetaTestType(aPK_obj = self.GetData())
2773 2774 #================================================================ 2775 # test panel handling 2776 #================================================================
2777 -def edit_test_panel(parent=None, test_panel=None):
2778 ea = cTestPanelEAPnl(parent = parent, id = -1) 2779 ea.data = test_panel 2780 ea.mode = gmTools.coalesce(test_panel, 'new', 'edit') 2781 dlg = gmEditArea.cGenericEditAreaDlg2 ( 2782 parent = parent, 2783 id = -1, 2784 edit_area = ea, 2785 single_entry = gmTools.bool2subst((test_panel is None), False, True) 2786 ) 2787 dlg.SetTitle(gmTools.coalesce(test_panel, _('Adding new test panel'), _('Editing test panel'))) 2788 if dlg.ShowModal() == wx.ID_OK: 2789 dlg.Destroy() 2790 return True 2791 dlg.Destroy() 2792 return False
2793 2794 #----------------------------------------------------------------
2795 -def manage_test_panels(parent=None):
2796 2797 if parent is None: 2798 parent = wx.GetApp().GetTopWindow() 2799 2800 #------------------------------------------------------------ 2801 def edit(test_panel=None): 2802 return edit_test_panel(parent = parent, test_panel = test_panel)
2803 #------------------------------------------------------------ 2804 def delete(test_panel): 2805 gmPathLab.delete_test_panel(pk = test_panel['pk_test_panel']) 2806 return True 2807 #------------------------------------------------------------ 2808 def get_tooltip(test_panel): 2809 return test_panel.format() 2810 #------------------------------------------------------------ 2811 def refresh(lctrl): 2812 panels = gmPathLab.get_test_panels(order_by = 'description') 2813 items = [ [ 2814 p['description'], 2815 gmTools.coalesce(p['comment'], u''), 2816 p['pk_test_panel'] 2817 ] for p in panels ] 2818 lctrl.set_string_items(items) 2819 lctrl.set_data(panels) 2820 #------------------------------------------------------------ 2821 msg = _( 2822 '\n' 2823 'Test panels as defined in GNUmed.\n' 2824 ) 2825 2826 gmListWidgets.get_choices_from_list ( 2827 parent = parent, 2828 msg = msg, 2829 caption = _('Showing test panels.'), 2830 columns = [ _('Name'), _('Comment'), u'#' ], 2831 single_selection = True, 2832 refresh_callback = refresh, 2833 edit_callback = edit, 2834 new_callback = edit, 2835 delete_callback = delete, 2836 list_tooltip_callback = get_tooltip 2837 ) 2838 2839 #----------------------------------------------------------------
2840 -class cTestPanelPRW(gmPhraseWheel.cPhraseWheel):
2841
2842 - def __init__(self, *args, **kwargs):
2843 query = u""" 2844 SELECT 2845 pk_test_panel 2846 AS data, 2847 description 2848 AS field_label, 2849 description 2850 AS list_label 2851 FROM 2852 clin.v_test_panels 2853 WHERE 2854 description %(fragment_condition)s 2855 ORDER BY field_label 2856 LIMIT 30""" 2857 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query) 2858 mp.setThresholds(1, 2, 4) 2859 #mp.word_separators = '[ \t:@]+' 2860 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 2861 self.matcher = mp 2862 self.SetToolTipString(_('Select a test panel.')) 2863 self.selection_only = True
2864 #------------------------------------------------------------
2865 - def _data2instance(self):
2866 if self.GetData() is None: 2867 return None 2868 return gmPathLab.cTestPanel(aPK_obj = self.GetData())
2869 #------------------------------------------------------------
2870 - def _get_data_tooltip(self):
2871 if self.GetData() is None: 2872 return None 2873 return gmPathLab.cTestPanel(aPK_obj = self.GetData()).format()
2874 2875 #==================================================================== 2876 from Gnumed.wxGladeWidgets import wxgTestPanelEAPnl 2877
2878 -class cTestPanelEAPnl(wxgTestPanelEAPnl.wxgTestPanelEAPnl, gmEditArea.cGenericEditAreaMixin):
2879
2880 - def __init__(self, *args, **kwargs):
2881 2882 try: 2883 data = kwargs['panel'] 2884 del kwargs['panel'] 2885 except KeyError: 2886 data = None 2887 2888 wxgTestPanelEAPnl.wxgTestPanelEAPnl.__init__(self, *args, **kwargs) 2889 gmEditArea.cGenericEditAreaMixin.__init__(self) 2890 2891 self._test_types = None 2892 2893 self.mode = 'new' 2894 self.data = data 2895 if data is not None: 2896 self.mode = 'edit'
2897 2898 #self.__init_ui() 2899 #---------------------------------------------------------------- 2900 # def __init_ui(self): 2901 # # adjust phrasewheels etc 2902 #---------------------------------------------------------------- 2903 # generic Edit Area mixin API 2904 #----------------------------------------------------------------
2905 - def _valid_for_save(self):
2906 validity = True 2907 2908 if self._test_types is None: 2909 validity = False 2910 gmDispatcher.send(signal = 'statustext', msg = _('No test types selected.')) 2911 self._BTN_select_tests.SetFocus() 2912 2913 if self._TCTRL_description.GetValue().strip() == u'': 2914 validity = False 2915 self.display_tctrl_as_valid(tctrl = self._TCTRL_description, valid = False) 2916 self._TCTRL_description.SetFocus() 2917 else: 2918 self.display_tctrl_as_valid(tctrl = self._TCTRL_description, valid = True) 2919 2920 return validity
2921 #----------------------------------------------------------------
2922 - def _save_as_new(self):
2923 data = gmPathLab.create_test_panel(description = self._TCTRL_description.GetValue().strip()) 2924 data['comment'] = self._TCTRL_comment.GetValue().strip() 2925 data['pk_test_types'] = [ tt['pk_test_type'] for tt in self._test_types ] 2926 data.save() 2927 data.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ] 2928 self.data = data 2929 return True
2930 #----------------------------------------------------------------
2931 - def _save_as_update(self):
2932 self.data['description'] = self._TCTRL_description.GetValue().strip() 2933 self.data['comment'] = self._TCTRL_comment.GetValue().strip() 2934 self.data['pk_test_types'] = [ tt['pk_test_type'] for tt in self._test_types ] 2935 self.data.save() 2936 self.data.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ] 2937 return True
2938 #----------------------------------------------------------------
2939 - def __refresh_test_types_field(self, test_types=None):
2940 self._TCTRL_tests.SetValue(u'') 2941 self._test_types = test_types 2942 if self._test_types is None: 2943 return 2944 tmp = u';\n'.join ([ 2945 u'%s: %s%s' % ( 2946 t['unified_abbrev'], 2947 t['unified_name'], 2948 gmTools.coalesce(t['name_org'], u'', u' (%s)') 2949 ) 2950 for t in self._test_types 2951 ]) 2952 self._TCTRL_tests.SetValue(tmp)
2953 #----------------------------------------------------------------
2954 - def _refresh_as_new(self):
2955 self._TCTRL_description.SetValue(u'') 2956 self._TCTRL_comment.SetValue(u'') 2957 self.__refresh_test_types_field() 2958 self._PRW_codes.SetText() 2959 2960 self._TCTRL_description.SetFocus()
2961 #----------------------------------------------------------------
2963 self._refresh_as_new() 2964 self.__refresh_test_types_field(test_types = self.data.test_types)
2965 #----------------------------------------------------------------
2966 - def _refresh_from_existing(self):
2967 self._TCTRL_description.SetValue(self.data['description']) 2968 self._TCTRL_comment.SetValue(gmTools.coalesce(self.data['comment'], u'')) 2969 self.__refresh_test_types_field(test_types = self.data.test_types) 2970 val, data = self._PRW_codes.generic_linked_codes2item_dict(self.data.generic_codes) 2971 self._PRW_codes.SetText(val, data) 2972 2973 self._BTN_select_tests.SetFocus()
2974 #----------------------------------------------------------------
2975 - def _on_select_tests_button_pressed(self, event):
2976 desc = self._TCTRL_description.GetValue().strip() 2977 if desc == u'': 2978 desc = None 2979 picked = pick_measurement_types ( 2980 parent = self, 2981 msg = _('Pick the measurement types for this panel.'), 2982 right_column = desc, 2983 picks = self._test_types 2984 ) 2985 if picked is None: 2986 return 2987 if len(picked) == 0: 2988 picked = None 2989 self.__refresh_test_types_field(test_types = picked)
2990 2991 #================================================================ 2992 # main 2993 #---------------------------------------------------------------- 2994 if __name__ == '__main__': 2995 2996 from Gnumed.pycommon import gmLog2 2997 from Gnumed.wxpython import gmPatSearchWidgets 2998 2999 gmI18N.activate_locale() 3000 gmI18N.install_domain() 3001 gmDateTime.init() 3002 3003 #------------------------------------------------------------
3004 - def test_grid():
3005 pat = gmPersonSearch.ask_for_patient() 3006 app = wx.PyWidgetTester(size = (500, 300)) 3007 lab_grid = cMeasurementsGrid(parent = app.frame, id = -1) 3008 lab_grid.patient = pat 3009 app.frame.Show() 3010 app.MainLoop()
3011 #------------------------------------------------------------
3012 - def test_test_ea_pnl():
3013 pat = gmPersonSearch.ask_for_patient() 3014 gmPatSearchWidgets.set_active_patient(patient=pat) 3015 app = wx.PyWidgetTester(size = (500, 300)) 3016 ea = cMeasurementEditAreaPnl(parent = app.frame, id = -1) 3017 app.frame.Show() 3018 app.MainLoop()
3019 #------------------------------------------------------------ 3020 # def test_primary_care_vitals_pnl(): 3021 # app = wx.PyWidgetTester(size = (500, 300)) 3022 # pnl = wxgPrimaryCareVitalsInputPnl.wxgPrimaryCareVitalsInputPnl(parent = app.frame, id = -1) 3023 # app.frame.Show() 3024 # app.MainLoop() 3025 #------------------------------------------------------------ 3026 if (len(sys.argv) > 1) and (sys.argv[1] == 'test'): 3027 #test_grid() 3028 test_test_ea_pnl() 3029 #test_primary_care_vitals_pnl() 3030 3031 #================================================================ 3032