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