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

Source Code for Module Gnumed.wxpython.gmMeasurementWidgets

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