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