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