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

Source Code for Module Gnumed.wxpython.gmMeasurementWidgets

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