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