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

Source Code for Module Gnumed.wxpython.gmMeasurementWidgets

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