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

Source Code for Module Gnumed.wxpython.gmMeasurementWidgets

   1  """GNUmed measurement widgets.""" 
   2  #================================================================ 
   3  __author__ = "Karsten Hilbert <Karsten.Hilbert@gmx.net>" 
   4  __license__ = "GPL" 
   5   
   6   
   7  import sys 
   8  import logging 
   9  import datetime as pyDT 
  10  import decimal 
  11  import os 
  12  import subprocess 
  13  import io 
  14  import os.path 
  15   
  16   
  17  import wx 
  18  import wx.grid 
  19  import wx.adv as wxh 
  20   
  21   
  22  if __name__ == '__main__': 
  23          sys.path.insert(0, '../../') 
  24  from Gnumed.pycommon import gmTools 
  25  from Gnumed.pycommon import gmNetworkTools 
  26  from Gnumed.pycommon import gmI18N 
  27  from Gnumed.pycommon import gmShellAPI 
  28  from Gnumed.pycommon import gmCfg 
  29  from Gnumed.pycommon import gmDateTime 
  30  from Gnumed.pycommon import gmMatchProvider 
  31  from Gnumed.pycommon import gmDispatcher 
  32  from Gnumed.pycommon import gmMimeLib 
  33   
  34  from Gnumed.business import gmPerson 
  35  from Gnumed.business import gmStaff 
  36  from Gnumed.business import gmPathLab 
  37  from Gnumed.business import gmPraxis 
  38  from Gnumed.business import gmLOINC 
  39  from Gnumed.business import gmForms 
  40  from Gnumed.business import gmPersonSearch 
  41  from Gnumed.business import gmOrganization 
  42  from Gnumed.business import gmHL7 
  43  from Gnumed.business import gmIncomingData 
  44  from Gnumed.business import gmDocuments 
  45   
  46  from Gnumed.wxpython import gmRegetMixin 
  47  from Gnumed.wxpython import gmPlugin 
  48  from Gnumed.wxpython import gmEditArea 
  49  from Gnumed.wxpython import gmPhraseWheel 
  50  from Gnumed.wxpython import gmListWidgets 
  51  from Gnumed.wxpython import gmGuiHelpers 
  52  from Gnumed.wxpython import gmAuthWidgets 
  53  from Gnumed.wxpython import gmOrganizationWidgets 
  54  from Gnumed.wxpython import gmEMRStructWidgets 
  55  from Gnumed.wxpython import gmCfgWidgets 
  56  from Gnumed.wxpython import gmDocumentWidgets 
  57   
  58   
  59  _log = logging.getLogger('gm.ui') 
  60   
  61  #================================================================ 
  62  # HL7 related widgets 
  63  #================================================================ 
64 -def show_hl7_file(parent=None):
65 66 if parent is None: 67 parent = wx.GetApp().GetTopWindow() 68 69 # select file 70 paths = gmTools.gmPaths() 71 dlg = wx.FileDialog ( 72 parent = parent, 73 message = _('Show HL7 file:'), 74 # make configurable: 75 defaultDir = os.path.join(paths.home_dir, 'gnumed'), 76 wildcard = "hl7 files|*.hl7|HL7 files|*.HL7|all files|*", 77 style = wx.FD_OPEN | wx.FD_FILE_MUST_EXIST 78 ) 79 choice = dlg.ShowModal() 80 hl7_name = dlg.GetPath() 81 dlg.Destroy() 82 if choice != wx.ID_OK: 83 return False 84 85 formatted_name = gmHL7.format_hl7_file ( 86 hl7_name, 87 skip_empty_fields = True, 88 return_filename = True, 89 fix_hl7 = True 90 ) 91 gmMimeLib.call_viewer_on_file(aFile = formatted_name, block = False) 92 return True
93 94 #================================================================
95 -def unwrap_HL7_from_XML(parent=None):
96 97 if parent is None: 98 parent = wx.GetApp().GetTopWindow() 99 100 # select file 101 paths = gmTools.gmPaths() 102 dlg = wx.FileDialog ( 103 parent = parent, 104 message = _('Extract HL7 from XML file:'), 105 # make configurable: 106 defaultDir = os.path.join(paths.home_dir, 'gnumed'), 107 wildcard = "xml files|*.xml|XML files|*.XML|all files|*", 108 style = wx.FD_OPEN | wx.FD_FILE_MUST_EXIST 109 ) 110 choice = dlg.ShowModal() 111 xml_name = dlg.GetPath() 112 dlg.Destroy() 113 if choice != wx.ID_OK: 114 return False 115 116 target_dir = os.path.split(xml_name)[0] 117 xml_path = './/Message' 118 hl7_name = gmHL7.extract_HL7_from_XML_CDATA(xml_name, xml_path, target_dir = target_dir) 119 if hl7_name is None: 120 gmGuiHelpers.gm_show_error ( 121 title = _('Extracting HL7 from XML file'), 122 error = ( 123 'Cannot unwrap HL7 data from XML file\n' 124 '\n' 125 ' [%s]\n' 126 '\n' 127 '(CDATA of [%s] nodes)' 128 ) % ( 129 xml_name, 130 xml_path 131 ) 132 ) 133 return False 134 135 gmDispatcher.send(signal = 'statustext', msg = _('Unwrapped HL7 into [%s] from [%s].') % (hl7_name, xml_name), beep = False) 136 return True
137 138 #================================================================
139 -def stage_hl7_file(parent=None):
140 141 if parent is None: 142 parent = wx.GetApp().GetTopWindow() 143 144 paths = gmTools.gmPaths() 145 dlg = wx.FileDialog ( 146 parent = parent, 147 message = _('Select HL7 file for staging:'), 148 # make configurable: 149 defaultDir = os.path.join(paths.home_dir, 'gnumed'), 150 wildcard = ".hl7 files|*.hl7|.HL7 files|*.HL7|all files|*", 151 style = wx.FD_OPEN | wx.FD_FILE_MUST_EXIST 152 ) 153 choice = dlg.ShowModal() 154 hl7_name = dlg.GetPath() 155 dlg.Destroy() 156 if choice != wx.ID_OK: 157 return False 158 159 target_dir = os.path.join(paths.home_dir, '.gnumed', 'hl7') 160 success, PID_names = gmHL7.split_hl7_file(hl7_name, target_dir = target_dir, encoding = 'utf8') 161 if not success: 162 gmGuiHelpers.gm_show_error ( 163 title = _('Staging HL7 file'), 164 error = _( 165 'There was a problem with splitting the HL7 file\n' 166 '\n' 167 ' %s' 168 ) % hl7_name 169 ) 170 return False 171 172 failed_files = [] 173 for PID_name in PID_names: 174 if not gmHL7.stage_single_PID_hl7_file(PID_name, source = _('generic'), encoding = 'utf8'): 175 failed_files.append(PID_name) 176 if len(failed_files) > 0: 177 gmGuiHelpers.gm_show_error ( 178 title = _('Staging HL7 file'), 179 error = _( 180 'There was a problem with staging the following files\n' 181 '\n' 182 ' %s' 183 ) % '\n '.join(failed_files) 184 ) 185 return False 186 187 gmDispatcher.send(signal = 'statustext', msg = _('Staged HL7 from [%s].') % hl7_name, beep = False) 188 return True
189 190 #================================================================
191 -def browse_incoming_unmatched(parent=None):
192 193 if parent is None: 194 parent = wx.GetApp().GetTopWindow() 195 #------------------------------------------------------------ 196 def show_hl7(staged_item): 197 if staged_item is None: 198 return False 199 if 'HL7' not in staged_item['data_type']: 200 return False 201 filename = staged_item.save_to_file() 202 if filename is None: 203 filename = gmTools.get_unique_filename() 204 tmp_file = io.open(filename, mode = 'at', encoding = 'utf8') 205 tmp_file.write('\n') 206 tmp_file.write('-' * 80) 207 tmp_file.write('\n') 208 tmp_file.write(gmTools.coalesce(staged_item['comment'], '')) 209 tmp_file.close() 210 gmMimeLib.call_viewer_on_file(aFile = filename, block = False) 211 return False
212 #------------------------------------------------------------ 213 def import_hl7(staged_item): 214 if staged_item is None: 215 return False 216 if 'HL7' not in staged_item['data_type']: 217 return False 218 unset_identity_on_error = False 219 if staged_item['pk_identity_disambiguated'] is None: 220 pat = gmPerson.gmCurrentPatient() 221 if pat.connected: 222 answer = gmGuiHelpers.gm_show_question ( 223 title = _('Importing HL7 data'), 224 question = _( 225 'There has not been a patient explicitely associated\n' 226 'with this chunk of HL7 data. However, the data file\n' 227 'contains the following patient identification information:\n' 228 '\n' 229 ' %s\n' 230 '\n' 231 'Do you want to import the HL7 under the current patient ?\n' 232 '\n' 233 ' %s\n' 234 '\n' 235 'Selecting [NO] makes GNUmed try to find a patient matching the HL7 data.\n' 236 ) % ( 237 staged_item.patient_identification, 238 pat['description_gender'] 239 ), 240 cancel_button = True 241 ) 242 if answer is None: 243 return False 244 if answer is True: 245 unset_identity_on_error = True 246 staged_item['pk_identity_disambiguated'] = pat.ID 247 248 success, log_name = gmHL7.process_staged_single_PID_hl7_file(staged_item) 249 if success: 250 return True 251 252 if unset_identity_on_error: 253 staged_item['pk_identity_disambiguated'] = None 254 staged_item.save() 255 256 gmGuiHelpers.gm_show_error ( 257 error = _('Error processing HL7 data.'), 258 title = _('Processing staged HL7 data.') 259 ) 260 return False 261 262 #------------------------------------------------------------ 263 def delete(staged_item): 264 if staged_item is None: 265 return False 266 do_delete = gmGuiHelpers.gm_show_question ( 267 title = _('Deleting incoming data'), 268 question = _( 269 'Do you really want to delete the incoming data ?\n' 270 '\n' 271 'Note that deletion is not reversible.' 272 ) 273 ) 274 if not do_delete: 275 return False 276 return gmIncomingData.delete_incoming_data(pk_incoming_data = staged_item['pk_incoming_data_unmatched']) 277 #------------------------------------------------------------ 278 def refresh(lctrl): 279 incoming = gmIncomingData.get_incoming_data() 280 items = [ [ 281 gmTools.coalesce(i['data_type'], ''), 282 '%s, %s (%s) %s' % ( 283 gmTools.coalesce(i['lastnames'], ''), 284 gmTools.coalesce(i['firstnames'], ''), 285 gmDateTime.pydt_strftime(dt = i['dob'], format = '%Y %b %d', accuracy = gmDateTime.acc_days, none_str = _('unknown DOB')), 286 gmTools.coalesce(i['gender'], '') 287 ), 288 gmTools.coalesce(i['external_data_id'], ''), 289 i['pk_incoming_data_unmatched'] 290 ] for i in incoming ] 291 lctrl.set_string_items(items) 292 lctrl.set_data(incoming) 293 #------------------------------------------------------------ 294 gmListWidgets.get_choices_from_list ( 295 parent = parent, 296 msg = None, 297 caption = _('Showing unmatched incoming data'), 298 columns = [ _('Type'), _('Identification'), _('Reference'), '#' ], 299 single_selection = True, 300 can_return_empty = False, 301 ignore_OK_button = True, 302 refresh_callback = refresh, 303 # edit_callback=None, 304 # new_callback=None, 305 delete_callback = delete, 306 left_extra_button = [_('Show'), _('Show formatted HL7'), show_hl7], 307 middle_extra_button = [_('Import'), _('Import HL7 data into patient chart'), import_hl7] 308 # right_extra_button=None 309 ) 310 311 #================================================================ 312 # convenience functions 313 #================================================================
314 -def call_browser_on_measurement_type(measurement_type=None):
315 316 dbcfg = gmCfg.cCfgSQL() 317 318 url = dbcfg.get2 ( 319 option = 'external.urls.measurements_search', 320 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace, 321 bias = 'user', 322 default = gmPathLab.URL_test_result_information_search 323 ) 324 325 base_url = dbcfg.get2 ( 326 option = 'external.urls.measurements_encyclopedia', 327 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace, 328 bias = 'user', 329 default = gmPathLab.URL_test_result_information 330 ) 331 332 if measurement_type is None: 333 url = base_url 334 335 measurement_type = measurement_type.strip() 336 337 if measurement_type == '': 338 url = base_url 339 340 url = url % {'search_term': measurement_type} 341 342 gmNetworkTools.open_url_in_browser(url = url)
343 344 #----------------------------------------------------------------
345 -def edit_measurement(parent=None, measurement=None, single_entry=False, fields=None):
346 ea = cMeasurementEditAreaPnl(parent, -1) 347 ea.data = measurement 348 ea.mode = gmTools.coalesce(measurement, 'new', 'edit') 349 dlg = gmEditArea.cGenericEditAreaDlg2(parent, -1, edit_area = ea, single_entry = single_entry) 350 dlg.SetTitle(gmTools.coalesce(measurement, _('Adding new measurement'), _('Editing measurement'))) 351 if fields is not None: 352 ea.set_fields(fields) 353 if dlg.ShowModal() == wx.ID_OK: 354 dlg.Destroy() 355 return True 356 dlg.Destroy() 357 return False
358 359 #----------------------------------------------------------------
360 -def manage_measurements(parent=None, single_selection=False, emr=None):
361 362 if parent is None: 363 parent = wx.GetApp().GetTopWindow() 364 365 if emr is None: 366 emr = gmPerson.gmCurrentPatient().emr 367 368 #------------------------------------------------------------ 369 def edit(measurement=None): 370 return edit_measurement(parent = parent, measurement = measurement, single_entry = True)
371 #------------------------------------------------------------ 372 def delete(measurement): 373 gmPathLab.delete_test_result(result = measurement) 374 return True 375 #------------------------------------------------------------ 376 def do_review(lctrl): 377 data = lctrl.get_selected_item_data() 378 if len(data) == 0: 379 return 380 return review_tests(parent = parent, tests = data) 381 #------------------------------------------------------------ 382 def do_plot(lctrl): 383 data = lctrl.get_selected_item_data() 384 if len(data) == 0: 385 return 386 return plot_measurements(parent = parent, tests = data) 387 #------------------------------------------------------------ 388 def get_tooltip(measurement): 389 return measurement.format(with_review=True, with_evaluation=True, with_ranges=True) 390 #------------------------------------------------------------ 391 def refresh(lctrl): 392 results = emr.get_test_results(order_by = 'clin_when DESC, unified_abbrev, unified_name') 393 items = [ [ 394 gmDateTime.pydt_strftime ( 395 r['clin_when'], 396 '%Y %b %d %H:%M', 397 accuracy = gmDateTime.acc_minutes 398 ), 399 r['unified_abbrev'], 400 '%s%s%s%s' % ( 401 gmTools.bool2subst ( 402 boolean = (not r['reviewed'] or (not r['review_by_you'] and r['you_are_responsible'])), 403 true_return = 'u' + gmTools.u_writing_hand, 404 false_return = '' 405 ), 406 r['unified_val'], 407 gmTools.coalesce(r['val_unit'], '', ' %s'), 408 gmTools.coalesce(r['abnormality_indicator'], '', ' %s') 409 ), 410 r['unified_name'], 411 # u'%s%s' % ( 412 # gmTools.bool2subst ( 413 # boolean = not r['reviewed'], 414 # true_return = _('no review at all'), 415 # false_return = gmTools.bool2subst ( 416 # boolean = (r['you_are_responsible'] and not r['review_by_you']), 417 # true_return = _('no review by you (you are responsible)'), 418 # false_return = _('reviewed') 419 # ) 420 # ), 421 # gmTools.coalesce(r['comment'], u'', u' / %s') 422 # ), 423 gmTools.coalesce(r['comment'], ''), 424 r['pk_test_result'] 425 ] for r in results ] 426 lctrl.set_string_items(items) 427 lctrl.set_data(results) 428 429 #------------------------------------------------------------ 430 msg = _('Test results (ordered reverse-chronologically)') 431 432 return gmListWidgets.get_choices_from_list ( 433 parent = parent, 434 msg = msg, 435 caption = _('Showing test results.'), 436 columns = [ _('When'), _('Abbrev'), _('Value'), _('Name'), _('Comment'), '#' ], 437 single_selection = single_selection, 438 can_return_empty = False, 439 refresh_callback = refresh, 440 edit_callback = edit, 441 new_callback = edit, 442 delete_callback = delete, 443 list_tooltip_callback = get_tooltip, 444 left_extra_button = (_('Review'), _('Review current selection'), do_review, True), 445 middle_extra_button = (_('Plot'), _('Plot current selection'), do_plot, True) 446 ) 447 448 #================================================================
449 -def configure_default_top_lab_panel(parent=None):
450 451 if parent is None: 452 parent = wx.GetApp().GetTopWindow() 453 454 panels = gmPathLab.get_test_panels(order_by = 'description') 455 gmCfgWidgets.configure_string_from_list_option ( 456 parent = parent, 457 message = _('Select the measurements panel to show in the top pane for continuous monitoring.'), 458 option = 'horstspace.top_panel.lab_panel', 459 bias = 'user', 460 default_value = None, 461 choices = [ '%s%s' % (p['description'], gmTools.coalesce(p['comment'], '', ' (%s)')) for p in panels ], 462 columns = [_('Lab panel')], 463 data = [ p['pk_test_panel'] for p in panels ], 464 caption = _('Configuring continuous monitoring measurements panel') 465 )
466 467 #================================================================
468 -def configure_default_gnuplot_template(parent=None):
469 470 from Gnumed.wxpython import gmFormWidgets 471 472 if parent is None: 473 parent = wx.GetApp().GetTopWindow() 474 475 template = gmFormWidgets.manage_form_templates ( 476 parent = parent, 477 active_only = True, 478 template_types = ['gnuplot script'] 479 ) 480 481 option = 'form_templates.default_gnuplot_template' 482 483 if template is None: 484 gmDispatcher.send(signal = 'statustext', msg = _('No default Gnuplot script template selected.'), beep = True) 485 return None 486 487 if template['engine'] != 'G': 488 gmDispatcher.send(signal = 'statustext', msg = _('No default Gnuplot script template selected.'), beep = True) 489 return None 490 491 dbcfg = gmCfg.cCfgSQL() 492 dbcfg.set ( 493 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace, 494 option = option, 495 value = '%s - %s' % (template['name_long'], template['external_version']) 496 ) 497 return template
498 499 #============================================================
500 -def get_default_gnuplot_template(parent = None):
501 502 option = 'form_templates.default_gnuplot_template' 503 504 dbcfg = gmCfg.cCfgSQL() 505 506 # load from option 507 default_template_name = dbcfg.get2 ( 508 option = option, 509 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace, 510 bias = 'user' 511 ) 512 513 # not configured -> try to configure 514 if default_template_name is None: 515 gmDispatcher.send('statustext', msg = _('No default Gnuplot template configured.'), beep = False) 516 default_template = configure_default_gnuplot_template(parent = parent) 517 # still not configured -> return 518 if default_template is None: 519 gmGuiHelpers.gm_show_error ( 520 aMessage = _('There is no default Gnuplot one-type script template configured.'), 521 aTitle = _('Plotting test results') 522 ) 523 return None 524 return default_template 525 526 # now it MUST be configured (either newly or previously) 527 # but also *validly* ? 528 try: 529 name, ver = default_template_name.split(' - ') 530 except: 531 # not valid 532 _log.exception('problem splitting Gnuplot script template name [%s]', default_template_name) 533 gmDispatcher.send(signal = 'statustext', msg = _('Problem loading Gnuplot script template.'), beep = True) 534 return None 535 536 default_template = gmForms.get_form_template(name_long = name, external_version = ver) 537 if default_template is None: 538 default_template = configure_default_gnuplot_template(parent = parent) 539 # still not configured -> return 540 if default_template is None: 541 gmGuiHelpers.gm_show_error ( 542 aMessage = _('Cannot load default Gnuplot script template [%s - %s]') % (name, ver), 543 aTitle = _('Plotting test results') 544 ) 545 return None 546 547 return default_template
548 549 #----------------------------------------------------------------
550 -def plot_measurements(parent=None, tests=None, format=None, show_year = True, use_default_template=False):
551 552 from Gnumed.wxpython import gmFormWidgets 553 554 # only valid for one-type plotting 555 if use_default_template: 556 template = get_default_gnuplot_template() 557 else: 558 template = gmFormWidgets.manage_form_templates ( 559 parent = parent, 560 active_only = True, 561 template_types = ['gnuplot script'] 562 ) 563 564 if template is None: 565 gmGuiHelpers.gm_show_error ( 566 aMessage = _('Cannot plot without a plot script.'), 567 aTitle = _('Plotting test results') 568 ) 569 return False 570 571 fname_data = gmPathLab.export_results_for_gnuplot(results = tests, show_year = show_year) 572 573 script = template.instantiate() 574 script.data_filename = fname_data 575 script.generate_output(format = format) # Gnuplot output terminal, wxt = wxWidgets window
576 577 #----------------------------------------------------------------
578 -def plot_adjacent_measurements(parent=None, test=None, format=None, show_year=True, plot_singular_result=True, use_default_template=False):
579 580 earlier, later = test.get_adjacent_results(desired_earlier_results = 2, desired_later_results = 2) 581 results2plot = [] 582 if earlier is not None: 583 results2plot.extend(earlier) 584 results2plot.append(test) 585 if later is not None: 586 results2plot.extend(later) 587 if len(results2plot) == 1: 588 if not plot_singular_result: 589 return 590 plot_measurements ( 591 parent = parent, 592 tests = results2plot, 593 format = format, 594 show_year = show_year, 595 use_default_template = use_default_template 596 )
597 #================================================================ 598 #from Gnumed.wxGladeWidgets import wxgPrimaryCareVitalsInputPnl 599 600 # Taillenumfang: Mitte zwischen unterster Rippe und 601 # hoechstem Teil des Beckenkamms 602 # Maenner: maessig: 94-102, deutlich: > 102 .. erhoeht 603 # Frauen: maessig: 80-88, deutlich: > 88 .. erhoeht 604 605 #================================================================ 606 # display widgets 607 #================================================================ 608 from Gnumed.wxGladeWidgets import wxgMeasurementsAsListPnl 609
610 -class cMeasurementsAsListPnl(wxgMeasurementsAsListPnl.wxgMeasurementsAsListPnl, gmRegetMixin.cRegetOnPaintMixin):
611 """A class for displaying all measurement results as a simple list. 612 613 - operates on a cPatient instance handed to it and NOT on the currently active patient 614 """
615 - def __init__(self, *args, **kwargs):
616 wxgMeasurementsAsListPnl.wxgMeasurementsAsListPnl.__init__(self, *args, **kwargs) 617 618 gmRegetMixin.cRegetOnPaintMixin.__init__(self) 619 620 self.__patient = None 621 622 self.__init_ui() 623 self.__register_events()
624 625 #------------------------------------------------------------ 626 # internal helpers 627 #------------------------------------------------------------
628 - def __init_ui(self):
629 self._LCTRL_results.set_columns([_('When'), _('Test'), _('Result'), _('Reference')]) 630 self._LCTRL_results.edit_callback = self._on_edit
631 632 #------------------------------------------------------------
633 - def __register_events(self):
634 gmDispatcher.connect(signal = 'gm_table_mod', receiver = self._on_database_signal)
635 636 #------------------------------------------------------------
637 - def __repopulate_ui(self):
638 if self.__patient is None: 639 self._TCTRL_measurements.SetValue('') 640 return 641 642 results = self.__patient.emr.get_test_results(order_by = 'clin_when DESC, unified_abbrev, unified_name') 643 items = [] 644 data = [] 645 for r in results: 646 range_info = gmTools.coalesce ( 647 r.formatted_clinical_range, 648 r.formatted_normal_range 649 ) 650 review = gmTools.bool2subst ( 651 r['reviewed'], 652 '', 653 ' ' + gmTools.u_writing_hand, 654 ' ' + gmTools.u_writing_hand 655 ) 656 items.append ([ 657 gmDateTime.pydt_strftime(r['clin_when'], '%Y %b %d %H:%M', accuracy = gmDateTime.acc_minutes), 658 r['abbrev_tt'], 659 '%s%s%s%s' % ( 660 gmTools.strip_empty_lines(text = r['unified_val'])[0], 661 gmTools.coalesce(r['val_unit'], '', ' %s'), 662 gmTools.coalesce(r['abnormality_indicator'], '', ' %s'), 663 review 664 ), 665 gmTools.coalesce(range_info, '') 666 ]) 667 data.append({'data': r, 'formatted': r.format(with_source_data = True)}) 668 669 self._LCTRL_results.set_string_items(items) 670 self._LCTRL_results.set_column_widths([wx.LIST_AUTOSIZE, wx.LIST_AUTOSIZE, wx.LIST_AUTOSIZE, wx.LIST_AUTOSIZE]) 671 self._LCTRL_results.set_data(data) 672 if len(items) > 0: 673 self._LCTRL_results.Select(idx = 0, on = 1) 674 self._TCTRL_measurements.SetValue(self._LCTRL_results.get_item_data(item_idx = 0)['formatted']) 675 676 self._LCTRL_results.SetFocus()
677 678 #------------------------------------------------------------
679 - def _on_edit(self):
680 item_data = self._LCTRL_results.get_selected_item_data(only_one = True) 681 if item_data is None: 682 return 683 if edit_measurement(parent = self, measurement = item_data['data'], single_entry = True): 684 self.__repopulate_ui()
685 686 #------------------------------------------------------------ 687 # event handlers 688 #------------------------------------------------------------
689 - def _on_database_signal(self, **kwds):
690 if self.__patient is None: 691 return True 692 693 if kwds['pk_identity'] is not None: # review table doesn't have pk_identity yet 694 if kwds['pk_identity'] != self.__patient.ID: 695 return True 696 697 if kwds['table'] not in ['clin.test_result', 'clin.reviewed_test_results']: 698 return True 699 700 self._schedule_data_reget() 701 return True
702 703 #------------------------------------------------------------
704 - def _on_result_selected(self, event):
705 event.Skip() 706 item_data = self._LCTRL_results.get_item_data(item_idx = event.Index) 707 self._TCTRL_measurements.SetValue(item_data['formatted'])
708 709 #------------------------------------------------------------ 710 # reget mixin API 711 #------------------------------------------------------------
712 - def _populate_with_data(self):
713 self.__repopulate_ui() 714 return True
715 716 #------------------------------------------------------------ 717 # properties 718 #------------------------------------------------------------
719 - def _get_patient(self):
720 return self.__patient
721
722 - def _set_patient(self, patient):
723 if (self.__patient is None) and (patient is None): 724 return 725 if (self.__patient is None) or (patient is None): 726 self.__patient = patient 727 self._schedule_data_reget() 728 return 729 if self.__patient.ID == patient.ID: 730 return 731 self.__patient = patient 732 self._schedule_data_reget()
733 734 patient = property(_get_patient, _set_patient)
735 736 #================================================================ 737 from Gnumed.wxGladeWidgets import wxgMeasurementsByDayPnl 738
739 -class cMeasurementsByDayPnl(wxgMeasurementsByDayPnl.wxgMeasurementsByDayPnl, gmRegetMixin.cRegetOnPaintMixin):
740 """A class for displaying measurement results as a list partitioned by day. 741 742 - operates on a cPatient instance handed to it and NOT on the currently active patient 743 """
744 - def __init__(self, *args, **kwargs):
745 wxgMeasurementsByDayPnl.wxgMeasurementsByDayPnl.__init__(self, *args, **kwargs) 746 747 gmRegetMixin.cRegetOnPaintMixin.__init__(self) 748 749 self.__patient = None 750 self.__date_format = str('%Y %b %d') 751 self.__pk_curr_episode = None 752 753 self.__init_ui() 754 self.__register_events()
755 756 #------------------------------------------------------------ 757 # internal helpers 758 #------------------------------------------------------------
759 - def __init_ui(self):
760 self._LCTRL_days.set_columns([_('Day')]) 761 self._LCTRL_results.set_columns([_('Time'), _('Test'), _('Result'), _('Reference')]) 762 self._LCTRL_results.edit_callback = self._on_edit 763 self._LBL_no_of_docs.SetLabel(_('no related documents found')) 764 dbcfg = gmCfg.cCfgSQL() 765 lab_doc_types = dbcfg.get2 ( 766 option = 'horstspace.lab_doc_types', 767 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace, 768 bias = 'user' 769 ) 770 if lab_doc_types is None: 771 txt = _('No document types declared to contain lab results.') 772 elif len(lab_doc_types) == 0: 773 txt = _('No document types declared to contain lab results.') 774 else: 775 txt = _('Document types declared to contain lab results:') 776 txt += '\n ' 777 txt += '\n '.join(lab_doc_types) 778 self._LBL_no_of_docs.SetToolTip(txt)
779 780 #------------------------------------------------------------
781 - def __register_events(self):
782 gmDispatcher.connect(signal = 'gm_table_mod', receiver = self._on_database_signal)
783 784 #------------------------------------------------------------
785 - def __clear(self):
786 self._LCTRL_days.set_string_items() 787 self._LCTRL_results.set_string_items() 788 self._TCTRL_measurements.SetValue('')
789 790 #------------------------------------------------------------
791 - def __repopulate_ui(self):
792 if self.__patient is None: 793 self.__clear() 794 return 795 796 dates = self.__patient.emr.get_dates_for_results(reverse_chronological = True) 797 items = [ ['%s%s' % ( 798 gmDateTime.pydt_strftime(d['clin_when_day'], self.__date_format), 799 gmTools.bool2subst(d['is_reviewed'], '', gmTools.u_writing_hand, gmTools.u_writing_hand) 800 )] 801 for d in dates 802 ] 803 804 self._LCTRL_days.set_string_items(items) 805 self._LCTRL_days.set_data(dates) 806 if len(items) > 0: 807 self._LCTRL_days.Select(idx = 0, on = 1) 808 self._LCTRL_days.SetFocus() 809 810 self.__pk_curr_episode = None
811 812 #------------------------------------------------------------
813 - def _on_edit(self):
814 item_data = self._LCTRL_results.get_selected_item_data(only_one = True) 815 if item_data is None: 816 return 817 if edit_measurement(parent = self, measurement = item_data['data'], single_entry = True): 818 self.__repopulate_ui()
819 820 #------------------------------------------------------------ 821 # event handlers 822 #------------------------------------------------------------
823 - def _on_database_signal(self, **kwds):
824 if self.__patient is None: 825 return True 826 827 if kwds['pk_identity'] is not None: # review table doesn't have pk_identity yet 828 if kwds['pk_identity'] != self.__patient.ID: 829 return True 830 831 if kwds['table'] not in ['clin.test_result', 'clin.reviewed_test_results']: 832 return True 833 834 self._schedule_data_reget() 835 return True
836 837 #------------------------------------------------------------
838 - def _on_day_selected(self, event):
839 event.Skip() 840 841 day = self._LCTRL_days.get_item_data(item_idx = event.Index)['clin_when_day'] 842 results = self.__patient.emr.get_results_for_day(timestamp = day) 843 items = [] 844 data = [] 845 for r in results: 846 range_info = gmTools.coalesce ( 847 r.formatted_clinical_range, 848 r.formatted_normal_range 849 ) 850 review = gmTools.bool2subst ( 851 r['reviewed'], 852 '', 853 ' ' + gmTools.u_writing_hand, 854 ' ' + gmTools.u_writing_hand 855 ) 856 items.append ([ 857 gmDateTime.pydt_strftime(r['clin_when'], '%H:%M'), 858 r['abbrev_tt'], 859 '%s%s%s%s' % ( 860 gmTools.strip_empty_lines(text = r['unified_val'])[0], 861 gmTools.coalesce(r['val_unit'], '', ' %s'), 862 gmTools.coalesce(r['abnormality_indicator'], '', ' %s'), 863 review 864 ), 865 gmTools.coalesce(range_info, '') 866 ]) 867 data.append({'data': r, 'formatted': r.format(with_source_data = True)}) 868 869 self._LCTRL_results.set_string_items(items) 870 self._LCTRL_results.set_column_widths([wx.LIST_AUTOSIZE_USEHEADER, wx.LIST_AUTOSIZE, wx.LIST_AUTOSIZE, wx.LIST_AUTOSIZE]) 871 self._LCTRL_results.set_data(data) 872 self._LCTRL_results.Select(idx = 0, on = 1)
873 874 #------------------------------------------------------------
875 - def _on_result_selected(self, event):
876 event.Skip() 877 item_data = self._LCTRL_results.get_item_data(item_idx = event.Index) 878 self._TCTRL_measurements.SetValue(item_data['formatted']) 879 pk_episode = item_data['data']['pk_episode'] 880 if pk_episode == self.__pk_curr_episode: 881 return 882 self.__pk_curr_episode = pk_episode 883 self._LBL_no_of_docs.SetLabel(_('no related documents found')) 884 self._BTN_list_docs.Disable() 885 dbcfg = gmCfg.cCfgSQL() 886 lab_doc_types = dbcfg.get2 ( 887 option = 'horstspace.lab_doc_types', 888 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace, 889 bias = 'user' 890 ) 891 if lab_doc_types is None: 892 return 893 d_types = gmDocuments.map_types2pk(lab_doc_types) 894 if len(d_types) is None: 895 return 896 docs = gmDocuments.search_for_documents ( 897 pk_episode = pk_episode, 898 pk_types = [ dt['pk_doc_type'] for dt in d_types ] 899 ) 900 if len(docs) == 0: 901 return 902 self._LBL_no_of_docs.SetLabel(_("Related documents: %s") % len(docs)) 903 self._LBL_no_of_docs.Refresh() 904 self._BTN_list_docs.Enable()
905 906 #------------------------------------------------------------
907 - def _on_list_docs_button_pressed(self, event):
908 event.Skip() 909 dbcfg = gmCfg.cCfgSQL() 910 lab_doc_types = dbcfg.get2 ( 911 option = 'horstspace.lab_doc_types', 912 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace, 913 bias = 'user' 914 ) 915 if lab_doc_types is None: 916 return 917 d_types = gmDocuments.map_types2pk(lab_doc_types) 918 if len(d_types) is None: 919 return 920 gmDocumentWidgets.manage_documents ( 921 parent = self, 922 msg = _('Documents possibly related to this episode'), 923 pk_types = [ dt['pk_doc_type'] for dt in d_types ], 924 pk_episodes = [ self.__pk_curr_episode ] 925 )
926 927 #------------------------------------------------------------
928 - def _on_select_lab_doc_types_pressed(self, event):
929 event.Skip() 930 doc_types = gmDocuments.get_document_types() 931 gmCfgWidgets.configure_list_from_list_option ( 932 parent = self, 933 message = _('Select the document types which are expected to contain lab results.'), 934 option = 'horstspace.lab_doc_types', 935 bias = 'user', 936 choices = [ dt['l10n_type'] for dt in doc_types ], 937 columns = [_('Document types')]#, 938 #data = None, 939 #caption = None, 940 #picks = None 941 ) 942 dbcfg = gmCfg.cCfgSQL() 943 lab_doc_types = dbcfg.get2 ( 944 option = 'horstspace.lab_doc_types', 945 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace, 946 bias = 'user' 947 ) 948 if lab_doc_types is None: 949 txt = _('No document types declared to contain lab results.') 950 elif len(lab_doc_types) == 0: 951 txt = _('No document types declared to contain lab results.') 952 else: 953 txt = _('Document types declared to contain lab results:') 954 txt += '\n ' 955 txt += '\n '.join(lab_doc_types) 956 self._LBL_no_of_docs.SetToolTip(txt)
957 958 #------------------------------------------------------------ 959 # reget mixin API 960 #------------------------------------------------------------
961 - def _populate_with_data(self):
962 self.__repopulate_ui() 963 return True
964 965 #------------------------------------------------------------ 966 # properties 967 #------------------------------------------------------------
968 - def _get_patient(self):
969 return self.__patient
970
971 - def _set_patient(self, patient):
972 if (self.__patient is None) and (patient is None): 973 return 974 if patient is None: 975 self.__patient = None 976 self.__clear() 977 return 978 if self.__patient is None: 979 self.__patient = patient 980 self._schedule_data_reget() 981 return 982 if self.__patient.ID == patient.ID: 983 return 984 self.__patient = patient 985 self._schedule_data_reget()
986 987 patient = property(_get_patient, _set_patient)
988 989 #================================================================ 990 from Gnumed.wxGladeWidgets import wxgMeasurementsByIssuePnl 991
992 -class cMeasurementsByIssuePnl(wxgMeasurementsByIssuePnl.wxgMeasurementsByIssuePnl, gmRegetMixin.cRegetOnPaintMixin):
993 """A class for displaying measurement results as a list partitioned by issue/episode. 994 995 - operates on a cPatient instance handed to it and NOT on the currently active patient 996 """
997 - def __init__(self, *args, **kwargs):
998 wxgMeasurementsByIssuePnl.wxgMeasurementsByIssuePnl.__init__(self, *args, **kwargs) 999 1000 gmRegetMixin.cRegetOnPaintMixin.__init__(self) 1001 1002 self.__patient = None 1003 1004 self.__init_ui() 1005 self.__register_events()
1006 1007 #------------------------------------------------------------ 1008 # internal helpers 1009 #------------------------------------------------------------
1010 - def __init_ui(self):
1011 self._LCTRL_issues.set_columns([_('Problem')]) 1012 self._LCTRL_issues.select_callback = self._on_problem_selected 1013 self._LCTRL_results.set_columns([_('When'), _('Test'), _('Result'), _('Reference')]) 1014 self._LCTRL_results.edit_callback = self._on_edit 1015 self._LCTRL_results.select_callback = self._on_result_selected
1016 1017 #------------------------------------------------------------
1018 - def __register_events(self):
1019 gmDispatcher.connect(signal = 'gm_table_mod', receiver = self._on_database_signal)
1020 1021 #------------------------------------------------------------
1022 - def __clear(self):
1023 self._LCTRL_issues.set_string_items() 1024 self._LCTRL_results.set_string_items() 1025 self._TCTRL_measurements.SetValue('')
1026 1027 #------------------------------------------------------------
1028 - def __repopulate_ui(self):
1029 if self.__patient is None: 1030 self.__clear() 1031 return 1032 1033 probs = self.__patient.emr.get_issues_or_episodes_for_results() 1034 items = [ ['%s%s' % ( 1035 gmTools.coalesce(p['pk_health_issue'], gmTools.u_diameter + ':', ''), 1036 gmTools.shorten_words_in_line(text = p['problem'], min_word_length = 5, max_length = 30) 1037 )] for p in probs ] 1038 self._LCTRL_issues.set_string_items(items) 1039 self._LCTRL_issues.set_data([ {'pk_issue': p['pk_health_issue'], 'pk_episode': p['pk_episode']} for p in probs ]) 1040 if len(items) > 0: 1041 self._LCTRL_issues.Select(idx = 0, on = 1) 1042 self._LCTRL_issues.SetFocus()
1043 1044 #------------------------------------------------------------
1045 - def _on_edit(self):
1046 item_data = self._LCTRL_results.get_selected_item_data(only_one = True) 1047 if item_data is None: 1048 return 1049 if edit_measurement(parent = self, measurement = item_data['data'], single_entry = True): 1050 self.__repopulate_ui()
1051 1052 #------------------------------------------------------------ 1053 # event handlers 1054 #------------------------------------------------------------
1055 - def _on_database_signal(self, **kwds):
1056 if self.__patient is None: 1057 return True 1058 1059 if kwds['pk_identity'] is not None: # review table doesn't have pk_identity yet 1060 if kwds['pk_identity'] != self.__patient.ID: 1061 return True 1062 1063 if kwds['table'] not in ['clin.test_result', 'clin.reviewed_test_results']: 1064 return True 1065 1066 self._schedule_data_reget() 1067 return True
1068 1069 #------------------------------------------------------------
1070 - def _on_problem_selected(self, event):
1071 event.Skip() 1072 1073 pk_issue = self._LCTRL_issues.get_item_data(item_idx = event.Index)['pk_issue'] 1074 if pk_issue is None: 1075 pk_episode = self._LCTRL_issues.get_item_data(item_idx = event.Index)['pk_episode'] 1076 results = self.__patient.emr.get_results_for_episode(pk_episode = pk_episode) 1077 else: 1078 results = self.__patient.emr.get_results_for_issue(pk_health_issue = pk_issue) 1079 items = [] 1080 data = [] 1081 for r in results: 1082 range_info = gmTools.coalesce ( 1083 r.formatted_clinical_range, 1084 r.formatted_normal_range 1085 ) 1086 review = gmTools.bool2subst ( 1087 r['reviewed'], 1088 '', 1089 ' ' + gmTools.u_writing_hand, 1090 ' ' + gmTools.u_writing_hand 1091 ) 1092 items.append ([ 1093 gmDateTime.pydt_strftime(r['clin_when'], '%Y %b %d %H:%M'), 1094 r['abbrev_tt'], 1095 '%s%s%s%s' % ( 1096 gmTools.strip_empty_lines(text = r['unified_val'])[0], 1097 gmTools.coalesce(r['val_unit'], '', ' %s'), 1098 gmTools.coalesce(r['abnormality_indicator'], '', ' %s'), 1099 review 1100 ), 1101 gmTools.coalesce(range_info, '') 1102 ]) 1103 data.append({'data': r, 'formatted': r.format(with_source_data = True)}) 1104 1105 self._LCTRL_results.set_string_items(items) 1106 self._LCTRL_results.set_column_widths([wx.LIST_AUTOSIZE_USEHEADER, wx.LIST_AUTOSIZE, wx.LIST_AUTOSIZE, wx.LIST_AUTOSIZE]) 1107 self._LCTRL_results.set_data(data) 1108 self._LCTRL_results.Select(idx = 0, on = 1) 1109 self._TCTRL_measurements.SetValue(self._LCTRL_results.get_item_data(item_idx = 0)['formatted'])
1110 1111 #------------------------------------------------------------
1112 - def _on_result_selected(self, event):
1113 event.Skip() 1114 item_data = self._LCTRL_results.get_item_data(item_idx = event.Index) 1115 self._TCTRL_measurements.SetValue(item_data['formatted'])
1116 1117 #------------------------------------------------------------ 1118 # reget mixin API 1119 #------------------------------------------------------------
1120 - def _populate_with_data(self):
1121 self.__repopulate_ui() 1122 return True
1123 1124 #------------------------------------------------------------ 1125 # properties 1126 #------------------------------------------------------------
1127 - def _get_patient(self):
1128 return self.__patient
1129
1130 - def _set_patient(self, patient):
1131 if (self.__patient is None) and (patient is None): 1132 return 1133 if patient is None: 1134 self.__patient = None 1135 self.__clear() 1136 return 1137 if self.__patient is None: 1138 self.__patient = patient 1139 self._schedule_data_reget() 1140 return 1141 if self.__patient.ID == patient.ID: 1142 return 1143 self.__patient = patient 1144 self._schedule_data_reget()
1145 1146 patient = property(_get_patient, _set_patient)
1147 1148 #================================================================ 1149 from Gnumed.wxGladeWidgets import wxgMeasurementsByBatteryPnl 1150
1151 -class cMeasurementsByBatteryPnl(wxgMeasurementsByBatteryPnl.wxgMeasurementsByBatteryPnl, gmRegetMixin.cRegetOnPaintMixin):
1152 """A grid class for displaying measurement results filtered by battery/panel. 1153 1154 - operates on a cPatient instance handed to it and NOT on the currently active patient 1155 """
1156 - def __init__(self, *args, **kwargs):
1157 wxgMeasurementsByBatteryPnl.wxgMeasurementsByBatteryPnl.__init__(self, *args, **kwargs) 1158 1159 gmRegetMixin.cRegetOnPaintMixin.__init__(self) 1160 1161 self.__patient = None 1162 1163 self.__init_ui() 1164 self.__register_events()
1165 1166 #------------------------------------------------------------ 1167 # internal helpers 1168 #------------------------------------------------------------
1169 - def __init_ui(self):
1170 self._GRID_results_battery.show_by_panel = True
1171 1172 #------------------------------------------------------------
1173 - def __register_events(self):
1174 gmDispatcher.connect(signal = 'gm_table_mod', receiver = self._on_database_signal) 1175 1176 self._PRW_panel.add_callback_on_selection(callback = self._on_panel_selected) 1177 self._PRW_panel.add_callback_on_modified(callback = self._on_panel_selection_modified)
1178 1179 #------------------------------------------------------------
1180 - def __repopulate_ui(self):
1181 self._GRID_results_battery.patient = self.__patient 1182 return True
1183 1184 #--------------------------------------------------------
1185 - def __on_panel_selected(self, panel):
1186 if panel is None: 1187 self._TCTRL_panel_comment.SetValue('') 1188 self._GRID_results_battery.panel_to_show = None 1189 else: 1190 pnl = self._PRW_panel.GetData(as_instance = True) 1191 self._TCTRL_panel_comment.SetValue(gmTools.coalesce ( 1192 pnl['comment'], 1193 '' 1194 )) 1195 self._GRID_results_battery.panel_to_show = pnl
1196 # self.Layout() 1197 1198 #--------------------------------------------------------
1200 self._TCTRL_panel_comment.SetValue('') 1201 if self._PRW_panel.GetValue().strip() == '': 1202 self._GRID_results_battery.panel_to_show = None
1203 # self.Layout() 1204 1205 #------------------------------------------------------------ 1206 # event handlers 1207 #------------------------------------------------------------
1208 - def _on_database_signal(self, **kwds):
1209 if self.__patient is None: 1210 return True 1211 1212 if kwds['pk_identity'] is not None: # review table doesn't have pk_identity yet 1213 if kwds['pk_identity'] != self.__patient.ID: 1214 return True 1215 1216 if kwds['table'] not in ['clin.test_result', 'clin.reviewed_test_results']: 1217 return True 1218 1219 self._schedule_data_reget() 1220 return True
1221 1222 #------------------------------------------------------------
1223 - def _on_manage_panels_button_pressed(self, event):
1225 1226 #--------------------------------------------------------
1227 - def _on_panel_selected(self, panel):
1228 wx.CallAfter(self.__on_panel_selected, panel=panel)
1229 1230 #--------------------------------------------------------
1232 wx.CallAfter(self.__on_panel_selection_modified)
1233 1234 #------------------------------------------------------------ 1235 # reget mixin API 1236 #------------------------------------------------------------
1237 - def _populate_with_data(self):
1238 self.__repopulate_ui() 1239 return True
1240 1241 #------------------------------------------------------------ 1242 # properties 1243 #------------------------------------------------------------
1244 - def _get_patient(self):
1245 return self.__patient
1246
1247 - def _set_patient(self, patient):
1248 if (self.__patient is None) and (patient is None): 1249 return 1250 if (self.__patient is None) or (patient is None): 1251 self.__patient = patient 1252 self._schedule_data_reget() 1253 return 1254 if self.__patient.ID == patient.ID: 1255 return 1256 self.__patient = patient 1257 self._schedule_data_reget()
1258 1259 patient = property(_get_patient, _set_patient)
1260 1261 #================================================================ 1262 from Gnumed.wxGladeWidgets import wxgMeasurementsAsTablePnl 1263
1264 -class cMeasurementsAsTablePnl(wxgMeasurementsAsTablePnl.wxgMeasurementsAsTablePnl, gmRegetMixin.cRegetOnPaintMixin):
1265 """A panel for holding a grid displaying all measurement results. 1266 1267 - operates on a cPatient instance handed to it and NOT on the currently active patient 1268 """
1269 - def __init__(self, *args, **kwargs):
1270 wxgMeasurementsAsTablePnl.wxgMeasurementsAsTablePnl.__init__(self, *args, **kwargs) 1271 1272 gmRegetMixin.cRegetOnPaintMixin.__init__(self) 1273 1274 self.__patient = None 1275 1276 self.__init_ui() 1277 self.__register_events()
1278 1279 #------------------------------------------------------------ 1280 # internal helpers 1281 #------------------------------------------------------------
1282 - def __init_ui(self):
1283 self.__action_button_popup = wx.Menu(title = _('Perform on selected results:')) 1284 1285 item = self.__action_button_popup.Append(-1, _('Review and &sign')) 1286 self.Bind(wx.EVT_MENU, self.__on_sign_current_selection, item) 1287 1288 item = self.__action_button_popup.Append(-1, _('Plot')) 1289 self.Bind(wx.EVT_MENU, self.__on_plot_current_selection, item) 1290 1291 #item = self.__action_button_popup.Append(-1, _('Export to &file')) 1292 #self.Bind(wx.EVT_MENU, self._GRID_results_all.current_selection_to_file, item) 1293 #self.__action_button_popup.Enable(id = item.Id, enable = False) 1294 1295 #item = self.__action_button_popup.Append(-1, _('Export to &clipboard')) 1296 #self.Bind(wx.EVT_MENU, self._GRID_results_all.current_selection_to_clipboard, item) 1297 #self.__action_button_popup.Enable(id = item.Id, enable = False) 1298 1299 item = self.__action_button_popup.Append(-1, _('&Delete')) 1300 self.Bind(wx.EVT_MENU, self.__on_delete_current_selection, item) 1301 1302 # FIXME: create inbox message to staff to phone patient to come in 1303 # FIXME: generate and let edit a SOAP narrative and include the values 1304 1305 self._GRID_results_all.show_by_panel = False
1306 1307 #------------------------------------------------------------
1308 - def __register_events(self):
1309 gmDispatcher.connect(signal = 'gm_table_mod', receiver = self._on_database_signal)
1310 1311 #------------------------------------------------------------
1312 - def __repopulate_ui(self):
1313 self._GRID_results_all.patient = self.__patient 1314 #self._GRID_results_battery.Fit() 1315 self.Layout() 1316 return True
1317 1318 #------------------------------------------------------------
1319 - def __on_sign_current_selection(self, evt):
1320 self._GRID_results_all.sign_current_selection()
1321 1322 #------------------------------------------------------------
1323 - def __on_plot_current_selection(self, evt):
1324 self._GRID_results_all.plot_current_selection()
1325 1326 #------------------------------------------------------------
1327 - def __on_delete_current_selection(self, evt):
1328 self._GRID_results_all.delete_current_selection()
1329 1330 #------------------------------------------------------------ 1331 # event handlers 1332 #------------------------------------------------------------
1333 - def _on_database_signal(self, **kwds):
1334 if self.__patient is None: 1335 return True 1336 1337 if kwds['pk_identity'] is not None: # review table doesn't have pk_identity yet 1338 if kwds['pk_identity'] != self.__patient.ID: 1339 return True 1340 1341 if kwds['table'] not in ['clin.test_result', 'clin.reviewed_test_results']: 1342 return True 1343 1344 self._schedule_data_reget() 1345 return True
1346 1347 #--------------------------------------------------------
1348 - def _on_add_button_pressed(self, event):
1349 edit_measurement(parent = self, measurement = None)
1350 1351 #--------------------------------------------------------
1352 - def _on_manage_types_button_pressed(self, event):
1353 event.Skip() 1354 manage_measurement_types(parent = self)
1355 1356 #--------------------------------------------------------
1357 - def _on_review_button_pressed(self, evt):
1358 self.PopupMenu(self.__action_button_popup)
1359 1360 #--------------------------------------------------------
1361 - def _on_select_button_pressed(self, evt):
1362 if self._RBTN_my_unsigned.GetValue() is True: 1363 self._GRID_results_all.select_cells(unsigned_only = True, accountables_only = True, keep_preselections = False) 1364 elif self._RBTN_all_unsigned.GetValue() is True: 1365 self._GRID_results_all.select_cells(unsigned_only = True, accountables_only = False, keep_preselections = False)
1366 1367 #------------------------------------------------------------ 1368 # reget mixin API 1369 #------------------------------------------------------------
1370 - def _populate_with_data(self):
1371 self.__repopulate_ui() 1372 return True
1373 1374 #------------------------------------------------------------ 1375 # properties 1376 #------------------------------------------------------------
1377 - def _get_patient(self):
1378 return self.__patient
1379
1380 - def _set_patient(self, patient):
1381 if (self.__patient is None) and (patient is None): 1382 return 1383 if (self.__patient is None) or (patient is None): 1384 self.__patient = patient 1385 self._schedule_data_reget() 1386 return 1387 if self.__patient.ID == patient.ID: 1388 return 1389 self.__patient = patient 1390 self._schedule_data_reget()
1391 1392 patient = property(_get_patient, _set_patient)
1393 1394 #================================================================ 1395 # notebook based measurements plugin 1396 #================================================================
1397 -class cMeasurementsNb(wx.Notebook, gmPlugin.cPatientChange_PluginMixin):
1398 """Notebook displaying measurements pages: 1399 1400 - by test battery 1401 - by day 1402 - by issue/episode 1403 - full grid 1404 - full list 1405 1406 Used as a main notebook plugin page. 1407 1408 Operates on the active patient. 1409 """ 1410 #--------------------------------------------------------
1411 - def __init__(self, parent, id):
1412 1413 wx.Notebook.__init__ ( 1414 self, 1415 parent = parent, 1416 id = id, 1417 style = wx.NB_TOP | wx.NB_MULTILINE | wx.NO_BORDER, 1418 name = self.__class__.__name__ 1419 ) 1420 _log.debug('created wx.Notebook: %s with ID %s', self.__class__.__name__, self.Id) 1421 gmPlugin.cPatientChange_PluginMixin.__init__(self) 1422 self.__patient = gmPerson.gmCurrentPatient() 1423 self.__init_ui() 1424 self.SetSelection(0)
1425 1426 #-------------------------------------------------------- 1427 # patient change plugin API 1428 #--------------------------------------------------------
1429 - def _on_current_patient_unset(self, **kwds):
1430 for page_idx in range(self.GetPageCount()): 1431 page = self.GetPage(page_idx) 1432 page.patient = None
1433 1434 #--------------------------------------------------------
1435 - def _post_patient_selection(self, **kwds):
1436 for page_idx in range(self.GetPageCount()): 1437 page = self.GetPage(page_idx) 1438 page.patient = self.__patient.patient
1439 1440 #-------------------------------------------------------- 1441 # notebook plugin API 1442 #--------------------------------------------------------
1443 - def repopulate_ui(self):
1444 if self.__patient.connected: 1445 pat = self.__patient.patient 1446 else: 1447 pat = None 1448 for page_idx in range(self.GetPageCount()): 1449 page = self.GetPage(page_idx) 1450 page.patient = pat 1451 1452 return True
1453 1454 #-------------------------------------------------------- 1455 # internal API 1456 #--------------------------------------------------------
1457 - def __init_ui(self):
1458 1459 # by day 1460 new_page = cMeasurementsByDayPnl(self, -1) 1461 new_page.patient = None 1462 self.AddPage ( 1463 page = new_page, 1464 text = _('Days'), 1465 select = True 1466 ) 1467 1468 # by issue 1469 new_page = cMeasurementsByIssuePnl(self, -1) 1470 new_page.patient = None 1471 self.AddPage ( 1472 page = new_page, 1473 text = _('Problems'), 1474 select = False 1475 ) 1476 1477 # by test panel 1478 new_page = cMeasurementsByBatteryPnl(self, -1) 1479 new_page.patient = None 1480 self.AddPage ( 1481 page = new_page, 1482 text = _('Panels'), 1483 select = False 1484 ) 1485 1486 # full grid 1487 new_page = cMeasurementsAsTablePnl(self, -1) 1488 new_page.patient = None 1489 self.AddPage ( 1490 page = new_page, 1491 text = _('Table'), 1492 select = False 1493 ) 1494 1495 # full list 1496 new_page = cMeasurementsAsListPnl(self, -1) 1497 new_page.patient = None 1498 self.AddPage ( 1499 page = new_page, 1500 text = _('List'), 1501 select = False 1502 )
1503 1504 #-------------------------------------------------------- 1505 # properties 1506 #--------------------------------------------------------
1507 - def _get_patient(self):
1508 return self.__patient
1509
1510 - def _set_patient(self, patient):
1511 self.__patient = patient 1512 if self.__patient.connected: 1513 pat = self.__patient.patient 1514 else: 1515 pat = None 1516 for page_idx in range(self.GetPageCount()): 1517 page = self.GetPage(page_idx) 1518 page.patient = pat
1519 1520 patient = property(_get_patient, _set_patient)
1521 1522 #================================================================
1523 -class cMeasurementsGrid(wx.grid.Grid):
1524 """A grid class for displaying measurement results. 1525 1526 - operates on a cPatient instance handed to it 1527 - does NOT listen to the currently active patient 1528 - thereby it can display any patient at any time 1529 """ 1530 # FIXME: sort-by-battery 1531 # FIXME: filter out empty 1532 # FIXME: filter by tests of a selected date 1533 # FIXME: dates DESC/ASC by cfg 1534 # FIXME: mouse over column header: display date info
1535 - def __init__(self, *args, **kwargs):
1536 1537 wx.grid.Grid.__init__(self, *args, **kwargs) 1538 1539 self.__patient = None 1540 self.__panel_to_show = None 1541 self.__show_by_panel = False 1542 self.__cell_data = {} 1543 self.__row_label_data = [] 1544 self.__col_label_data = [] 1545 1546 self.__prev_row = None 1547 self.__prev_col = None 1548 self.__prev_label_row = None 1549 self.__date_format = str((_('lab_grid_date_format::%Y\n%b %d')).lstrip('lab_grid_date_format::')) 1550 1551 self.__init_ui() 1552 self.__register_events()
1553 1554 #------------------------------------------------------------ 1555 # external API 1556 #------------------------------------------------------------
1557 - def delete_current_selection(self):
1558 if not self.IsSelection(): 1559 gmDispatcher.send(signal = 'statustext', msg = _('No results selected for deletion.')) 1560 return True 1561 1562 selected_cells = self.get_selected_cells() 1563 if len(selected_cells) > 20: 1564 results = None 1565 msg = _( 1566 'There are %s results marked for deletion.\n' 1567 '\n' 1568 'Are you sure you want to delete these results ?' 1569 ) % len(selected_cells) 1570 else: 1571 results = self.__cells_to_data(cells = selected_cells, exclude_multi_cells = False) 1572 txt = '\n'.join([ '%s %s (%s): %s %s%s' % ( 1573 r['clin_when'].strftime('%x %H:%M'), 1574 r['unified_abbrev'], 1575 r['unified_name'], 1576 r['unified_val'], 1577 r['val_unit'], 1578 gmTools.coalesce(r['abnormality_indicator'], '', ' (%s)') 1579 ) for r in results 1580 ]) 1581 msg = _( 1582 'The following results are marked for deletion:\n' 1583 '\n' 1584 '%s\n' 1585 '\n' 1586 'Are you sure you want to delete these results ?' 1587 ) % txt 1588 1589 dlg = gmGuiHelpers.c2ButtonQuestionDlg ( 1590 self, 1591 -1, 1592 caption = _('Deleting test results'), 1593 question = msg, 1594 button_defs = [ 1595 {'label': _('Delete'), 'tooltip': _('Yes, delete all the results.'), 'default': False}, 1596 {'label': _('Cancel'), 'tooltip': _('No, do NOT delete any results.'), 'default': True} 1597 ] 1598 ) 1599 decision = dlg.ShowModal() 1600 1601 if decision == wx.ID_YES: 1602 if results is None: 1603 results = self.__cells_to_data(cells = selected_cells, exclude_multi_cells = False) 1604 for result in results: 1605 gmPathLab.delete_test_result(result)
1606 1607 #------------------------------------------------------------
1608 - def sign_current_selection(self):
1609 if not self.IsSelection(): 1610 gmDispatcher.send(signal = 'statustext', msg = _('Cannot sign results. No results selected.')) 1611 return True 1612 1613 selected_cells = self.get_selected_cells() 1614 tests = self.__cells_to_data(cells = selected_cells, exclude_multi_cells = False) 1615 1616 return review_tests(parent = self, tests = tests)
1617 1618 #------------------------------------------------------------
1619 - def plot_current_selection(self):
1620 1621 if not self.IsSelection(): 1622 gmDispatcher.send(signal = 'statustext', msg = _('Cannot plot results. No results selected.')) 1623 return True 1624 1625 tests = self.__cells_to_data ( 1626 cells = self.get_selected_cells(), 1627 exclude_multi_cells = False, 1628 auto_include_multi_cells = True 1629 ) 1630 1631 plot_measurements(parent = self, tests = tests)
1632 #------------------------------------------------------------
1633 - def get_selected_cells(self):
1634 1635 sel_block_top_left = self.GetSelectionBlockTopLeft() 1636 sel_block_bottom_right = self.GetSelectionBlockBottomRight() 1637 sel_cols = self.GetSelectedCols() 1638 sel_rows = self.GetSelectedRows() 1639 1640 selected_cells = [] 1641 1642 # individually selected cells (ctrl-click) 1643 selected_cells += self.GetSelectedCells() 1644 1645 # selected rows 1646 selected_cells += list ( 1647 (row, col) 1648 for row in sel_rows 1649 for col in range(self.GetNumberCols()) 1650 ) 1651 1652 # selected columns 1653 selected_cells += list ( 1654 (row, col) 1655 for row in range(self.GetNumberRows()) 1656 for col in sel_cols 1657 ) 1658 1659 # selection blocks 1660 for top_left, bottom_right in zip(self.GetSelectionBlockTopLeft(), self.GetSelectionBlockBottomRight()): 1661 selected_cells += [ 1662 (row, col) 1663 for row in range(top_left[0], bottom_right[0] + 1) 1664 for col in range(top_left[1], bottom_right[1] + 1) 1665 ] 1666 1667 return set(selected_cells)
1668 #------------------------------------------------------------
1669 - def select_cells(self, unsigned_only=False, accountables_only=False, keep_preselections=False):
1670 """Select a range of cells according to criteria. 1671 1672 unsigned_only: include only those which are not signed at all yet 1673 accountable_only: include only those for which the current user is responsible 1674 keep_preselections: broaden (rather than replace) the range of selected cells 1675 1676 Combinations are powerful ! 1677 """ 1678 wx.BeginBusyCursor() 1679 self.BeginBatch() 1680 1681 if not keep_preselections: 1682 self.ClearSelection() 1683 1684 for col_idx in self.__cell_data.keys(): 1685 for row_idx in self.__cell_data[col_idx].keys(): 1686 # loop over results in cell and only include 1687 # those multi-value cells that are not ambiguous 1688 do_not_include = False 1689 for result in self.__cell_data[col_idx][row_idx]: 1690 if unsigned_only: 1691 if result['reviewed']: 1692 do_not_include = True 1693 break 1694 if accountables_only: 1695 if not result['you_are_responsible']: 1696 do_not_include = True 1697 break 1698 if do_not_include: 1699 continue 1700 1701 self.SelectBlock(row_idx, col_idx, row_idx, col_idx, addToSelected = True) 1702 1703 self.EndBatch() 1704 wx.EndBusyCursor()
1705 1706 #------------------------------------------------------------
1707 - def repopulate_grid(self):
1708 self.empty_grid() 1709 if self.__patient is None: 1710 return 1711 1712 if self.__show_by_panel: 1713 if self.__panel_to_show is None: 1714 return 1715 tests = self.__panel_to_show.get_test_types_for_results ( 1716 self.__patient.ID, 1717 order_by = 'unified_abbrev', 1718 unique_meta_types = True 1719 ) 1720 self.__repopulate_grid ( 1721 tests4rows = tests, 1722 test_pks2show = [ tt['pk_test_type'] for tt in self.__panel_to_show['test_types'] ] 1723 ) 1724 return 1725 1726 emr = self.__patient.emr 1727 tests = emr.get_test_types_for_results(order_by = 'unified_abbrev', unique_meta_types = True) 1728 self.__repopulate_grid(tests4rows = tests)
1729 1730 #------------------------------------------------------------
1731 - def __repopulate_grid(self, tests4rows=None, test_pks2show=None):
1732 1733 if len(tests4rows) == 0: 1734 return 1735 1736 emr = self.__patient.emr 1737 1738 self.__row_label_data = tests4rows 1739 row_labels = [ '%s%s' % ( 1740 gmTools.bool2subst(test_type['is_fake_meta_type'], '', gmTools.u_sum, ''), 1741 test_type['unified_abbrev'] 1742 ) for test_type in self.__row_label_data 1743 ] 1744 1745 self.__col_label_data = [ d['clin_when_day'] for d in emr.get_dates_for_results ( 1746 tests = test_pks2show, 1747 reverse_chronological = True 1748 )] 1749 col_labels = [ gmDateTime.pydt_strftime(date, self.__date_format, accuracy = gmDateTime.acc_days) for date in self.__col_label_data ] 1750 1751 results = emr.get_test_results_by_date ( 1752 tests = test_pks2show, 1753 reverse_chronological = True 1754 ) 1755 1756 self.BeginBatch() 1757 1758 # rows 1759 self.AppendRows(numRows = len(row_labels)) 1760 for row_idx in range(len(row_labels)): 1761 self.SetRowLabelValue(row_idx, row_labels[row_idx]) 1762 1763 # columns 1764 self.AppendCols(numCols = len(col_labels)) 1765 for col_idx in range(len(col_labels)): 1766 self.SetColLabelValue(col_idx, col_labels[col_idx]) 1767 1768 # cell values (list of test results) 1769 for result in results: 1770 row_idx = row_labels.index('%s%s' % ( 1771 gmTools.bool2subst(result['is_fake_meta_type'], '', gmTools.u_sum, ''), 1772 result['unified_abbrev'] 1773 )) 1774 col_idx = col_labels.index(gmDateTime.pydt_strftime(result['clin_when'], self.__date_format, accuracy = gmDateTime.acc_days)) 1775 1776 try: 1777 self.__cell_data[col_idx] 1778 except KeyError: 1779 self.__cell_data[col_idx] = {} 1780 1781 # the tooltip always shows the youngest sub result details 1782 if row_idx in self.__cell_data[col_idx]: 1783 self.__cell_data[col_idx][row_idx].append(result) 1784 self.__cell_data[col_idx][row_idx].sort(key = lambda x: x['clin_when'], reverse = True) 1785 else: 1786 self.__cell_data[col_idx][row_idx] = [result] 1787 1788 # rebuild cell display string 1789 vals2display = [] 1790 cell_has_out_of_bounds_value = False 1791 for sub_result in self.__cell_data[col_idx][row_idx]: 1792 1793 if sub_result.is_considered_abnormal: 1794 cell_has_out_of_bounds_value = True 1795 1796 abnormality_indicator = sub_result.formatted_abnormality_indicator 1797 if abnormality_indicator is None: 1798 abnormality_indicator = '' 1799 if abnormality_indicator != '': 1800 abnormality_indicator = ' (%s)' % abnormality_indicator[:3] 1801 1802 missing_review = False 1803 # warn on missing review if 1804 # a) no review at all exists or 1805 if not sub_result['reviewed']: 1806 missing_review = True 1807 # b) there is a review but 1808 else: 1809 # current user is reviewer and hasn't reviewed 1810 if sub_result['you_are_responsible'] and not sub_result['review_by_you']: 1811 missing_review = True 1812 1813 needs_superscript = False 1814 1815 # can we display the full sub_result length ? 1816 if sub_result.is_long_text: 1817 lines = gmTools.strip_empty_lines ( 1818 text = sub_result['unified_val'], 1819 eol = '\n', 1820 return_list = True 1821 ) 1822 needs_superscript = True 1823 tmp = lines[0][:7] 1824 else: 1825 val = gmTools.strip_empty_lines ( 1826 text = sub_result['unified_val'], 1827 eol = '\n', 1828 return_list = False 1829 ).replace('\n', '//') 1830 if len(val) > 8: 1831 needs_superscript = True 1832 tmp = val[:7] 1833 else: 1834 tmp = '%.8s' % val[:8] 1835 1836 # abnormal ? 1837 tmp = '%s%.6s' % (tmp, abnormality_indicator) 1838 1839 # is there a comment ? 1840 has_sub_result_comment = gmTools.coalesce ( 1841 gmTools.coalesce(sub_result['note_test_org'], sub_result['comment']), 1842 '' 1843 ).strip() != '' 1844 if has_sub_result_comment: 1845 needs_superscript = True 1846 1847 if needs_superscript: 1848 tmp = '%s%s' % (tmp, gmTools.u_superscript_one) 1849 1850 # lacking a review ? 1851 if missing_review: 1852 tmp = '%s %s' % (tmp, gmTools.u_writing_hand) 1853 else: 1854 if sub_result['is_clinically_relevant']: 1855 tmp += ' !' 1856 1857 # part of a multi-result cell ? 1858 if len(self.__cell_data[col_idx][row_idx]) > 1: 1859 tmp = '%s %s' % (sub_result['clin_when'].strftime('%H:%M'), tmp) 1860 1861 vals2display.append(tmp) 1862 1863 self.SetCellValue(row_idx, col_idx, '\n'.join(vals2display)) 1864 self.SetCellAlignment(row_idx, col_idx, horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE) 1865 # We used to color text in cells holding abnormals 1866 # in firebrick red but that would color ALL text (including 1867 # normals) and not only the abnormals within that 1868 # cell. Shading, however, only says that *something* 1869 # inside that cell is worthy of attention. 1870 #if sub_result_relevant: 1871 # font = self.GetCellFont(row_idx, col_idx) 1872 # self.SetCellTextColour(row_idx, col_idx, 'firebrick') 1873 # font.SetWeight(wx.FONTWEIGHT_BOLD) 1874 # self.SetCellFont(row_idx, col_idx, font) 1875 if cell_has_out_of_bounds_value: 1876 #self.SetCellBackgroundColour(row_idx, col_idx, 'cornflower blue') 1877 self.SetCellBackgroundColour(row_idx, col_idx, 'PALE TURQUOISE') 1878 1879 self.EndBatch() 1880 1881 self.AutoSize() 1882 self.AdjustScrollbars() 1883 self.ForceRefresh() 1884 1885 #self.Fit() 1886 1887 return
1888 1889 #------------------------------------------------------------
1890 - def empty_grid(self):
1891 self.BeginBatch() 1892 self.ClearGrid() 1893 # Windows cannot do nothing, it rather decides to assert() 1894 # on thinking it is supposed to do nothing 1895 if self.GetNumberRows() > 0: 1896 self.DeleteRows(pos = 0, numRows = self.GetNumberRows()) 1897 if self.GetNumberCols() > 0: 1898 self.DeleteCols(pos = 0, numCols = self.GetNumberCols()) 1899 self.EndBatch() 1900 self.__cell_data = {} 1901 self.__row_label_data = [] 1902 self.__col_label_data = []
1903 1904 #------------------------------------------------------------
1905 - def get_row_tooltip(self, row=None):
1906 # include details about test types included ? 1907 1908 # sometimes, for some reason, there is no row and 1909 # wxPython still tries to find a tooltip for it 1910 try: 1911 tt = self.__row_label_data[row] 1912 except IndexError: 1913 return ' ' 1914 1915 if tt['is_fake_meta_type']: 1916 return tt.format(patient = self.__patient.ID) 1917 1918 meta_tt = tt.meta_test_type 1919 txt = meta_tt.format(with_tests = True, patient = self.__patient.ID) 1920 1921 return txt
1922 1923 #------------------------------------------------------------
1924 - def get_cell_tooltip(self, col=None, row=None):
1925 try: 1926 cell_results = self.__cell_data[col][row] 1927 except KeyError: 1928 # FIXME: maybe display the most recent or when the most recent was ? 1929 cell_results = None 1930 1931 if cell_results is None: 1932 return ' ' 1933 1934 is_multi_cell = False 1935 if len(cell_results) > 1: 1936 is_multi_cell = True 1937 result = cell_results[0] 1938 1939 tt = '' 1940 # header 1941 if is_multi_cell: 1942 tt += _('Details of most recent (topmost) result ! \n') 1943 if result.is_long_text: 1944 tt += gmTools.strip_empty_lines(text = result['val_alpha'], eol = '\n', return_list = False) 1945 return tt 1946 1947 tt += result.format(with_review = True, with_evaluation = True, with_ranges = True) 1948 return tt
1949 1950 #------------------------------------------------------------ 1951 # internal helpers 1952 #------------------------------------------------------------
1953 - def __init_ui(self):
1954 #self.SetMinSize(wx.DefaultSize) 1955 self.SetMinSize((10, 10)) 1956 1957 self.CreateGrid(0, 1) 1958 self.EnableEditing(0) 1959 self.EnableDragGridSize(1) 1960 1961 # column labels 1962 # setting this screws up the labels: they are cut off and displaced 1963 #self.SetColLabelAlignment(wx.ALIGN_CENTER, wx.ALIGN_BOTTOM) 1964 1965 # row labels 1966 self.SetRowLabelSize(wx.grid.GRID_AUTOSIZE) # starting with 2.8.8 1967 #self.SetRowLabelSize(150) 1968 self.SetRowLabelAlignment(horiz = wx.ALIGN_LEFT, vert = wx.ALIGN_CENTRE) 1969 font = self.GetLabelFont() 1970 font.SetWeight(wx.FONTWEIGHT_LIGHT) 1971 self.SetLabelFont(font) 1972 1973 # add link to left upper corner 1974 dbcfg = gmCfg.cCfgSQL() 1975 url = dbcfg.get2 ( 1976 option = 'external.urls.measurements_encyclopedia', 1977 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace, 1978 bias = 'user', 1979 default = gmPathLab.URL_test_result_information 1980 ) 1981 1982 self.__WIN_corner = self.GetGridCornerLabelWindow() # a wx.Window instance 1983 1984 LNK_lab = wxh.HyperlinkCtrl ( 1985 self.__WIN_corner, 1986 -1, 1987 label = _('Tests'), 1988 style = wxh.HL_DEFAULT_STYLE # wx.TE_READONLY|wx.TE_CENTRE| wx.NO_BORDER | 1989 ) 1990 LNK_lab.SetURL(url) 1991 LNK_lab.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BACKGROUND)) 1992 LNK_lab.SetToolTip(_( 1993 'Navigate to an encyclopedia of measurements\n' 1994 'and test methods on the web.\n' 1995 '\n' 1996 ' <%s>' 1997 ) % url) 1998 1999 SZR_inner = wx.BoxSizer(wx.HORIZONTAL) 2000 SZR_inner.Add((20, 20), 1, wx.EXPAND, 0) # spacer 2001 SZR_inner.Add(LNK_lab, 0, wx.ALIGN_CENTER_VERTICAL, 0) #wx.ALIGN_CENTER wx.EXPAND 2002 SZR_inner.Add((20, 20), 1, wx.EXPAND, 0) # spacer 2003 2004 SZR_corner = wx.BoxSizer(wx.VERTICAL) 2005 SZR_corner.Add((20, 20), 1, wx.EXPAND, 0) # spacer 2006 SZR_corner.Add(SZR_inner, 0, wx.EXPAND) # inner sizer with centered hyperlink 2007 SZR_corner.Add((20, 20), 1, wx.EXPAND, 0) # spacer 2008 2009 self.__WIN_corner.SetSizer(SZR_corner) 2010 SZR_corner.Fit(self.__WIN_corner)
2011 2012 #------------------------------------------------------------
2013 - def __resize_corner_window(self, evt):
2014 self.__WIN_corner.Layout()
2015 2016 #------------------------------------------------------------
2017 - def __cells_to_data(self, cells=None, exclude_multi_cells=False, auto_include_multi_cells=False):
2018 """List of <cells> must be in row / col order.""" 2019 data = [] 2020 for row, col in cells: 2021 try: 2022 # cell data is stored col / row 2023 data_list = self.__cell_data[col][row] 2024 except KeyError: 2025 continue 2026 2027 if len(data_list) == 1: 2028 data.append(data_list[0]) 2029 continue 2030 2031 if exclude_multi_cells: 2032 gmDispatcher.send(signal = 'statustext', msg = _('Excluding multi-result field from further processing.')) 2033 continue 2034 2035 if auto_include_multi_cells: 2036 data.extend(data_list) 2037 continue 2038 2039 data_to_include = self.__get_choices_from_multi_cell(cell_data = data_list) 2040 if data_to_include is None: 2041 continue 2042 data.extend(data_to_include) 2043 2044 return data
2045 2046 #------------------------------------------------------------
2047 - def __get_choices_from_multi_cell(self, cell_data=None, single_selection=False):
2048 data = gmListWidgets.get_choices_from_list ( 2049 parent = self, 2050 msg = _( 2051 'Your selection includes a field with multiple results.\n' 2052 '\n' 2053 'Please select the individual results you want to work on:' 2054 ), 2055 caption = _('Selecting test results'), 2056 choices = [ [d['clin_when'], '%s: %s' % (d['abbrev_tt'], d['name_tt']), d['unified_val']] for d in cell_data ], 2057 columns = [ _('Date / Time'), _('Test'), _('Result') ], 2058 data = cell_data, 2059 single_selection = single_selection 2060 ) 2061 return data
2062 2063 #------------------------------------------------------------ 2064 # event handling 2065 #------------------------------------------------------------
2066 - def __register_events(self):
2067 # dynamic tooltips: GridWindow, GridRowLabelWindow, GridColLabelWindow, GridCornerLabelWindow 2068 self.GetGridWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_cells) 2069 self.GetGridRowLabelWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_row_labels) 2070 #self.GetGridColLabelWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_col_labels) 2071 2072 # sizing left upper corner window 2073 self.Bind(wx.EVT_SIZE, self.__resize_corner_window) 2074 2075 # editing cells 2076 self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.__on_cell_left_dclicked)
2077 2078 #------------------------------------------------------------
2079 - def __on_cell_left_dclicked(self, evt):
2080 col = evt.GetCol() 2081 row = evt.GetRow() 2082 2083 try: 2084 self.__cell_data[col][row] 2085 except KeyError: # empty cell 2086 fields = {} 2087 col_date = self.__col_label_data[col] 2088 fields['clin_when'] = {'data': col_date} 2089 test_type = self.__row_label_data[row] 2090 temporally_closest_result_of_row_type = test_type.meta_test_type.get_temporally_closest_result(col_date, self.__patient.ID) 2091 if temporally_closest_result_of_row_type is not None: 2092 fields['pk_test_type'] = {'data': temporally_closest_result_of_row_type['pk_test_type']} 2093 same_day_results = gmPathLab.get_results_for_day ( 2094 timestamp = col_date, 2095 patient = self.__patient.ID, 2096 order_by = None 2097 ) 2098 if len(same_day_results) > 0: 2099 fields['pk_episode'] = {'data': same_day_results[0]['pk_episode']} 2100 # maybe ['comment'] as in "medical context" ? - not thought through yet 2101 # no need to set because because setting pk_test_type will do so: 2102 # fields['val_unit'] 2103 # fields['val_normal_min'] 2104 # fields['val_normal_max'] 2105 # fields['val_normal_range'] 2106 # fields['val_target_min'] 2107 # fields['val_target_max'] 2108 # fields['val_target_range'] 2109 edit_measurement ( 2110 parent = self, 2111 measurement = None, 2112 single_entry = True, 2113 fields = fields 2114 ) 2115 return 2116 2117 if len(self.__cell_data[col][row]) > 1: 2118 data = self.__get_choices_from_multi_cell(cell_data = self.__cell_data[col][row], single_selection = True) 2119 else: 2120 data = self.__cell_data[col][row][0] 2121 2122 if data is None: 2123 return 2124 2125 edit_measurement(parent = self, measurement = data, single_entry = True)
2126 2127 #------------------------------------------------------------ 2128 # def OnMouseMotionRowLabel(self, evt): 2129 # x, y = self.CalcUnscrolledPosition(evt.GetPosition()) 2130 # row = self.YToRow(y) 2131 # label = self.table().GetRowHelpValue(row) 2132 # self.GetGridRowLabelWindow().SetToolTip(label or "") 2133 # evt.Skip()
2134 - def __on_mouse_over_row_labels(self, evt):
2135 2136 # Use CalcUnscrolledPosition() to get the mouse position within the 2137 # entire grid including what's offscreen 2138 x, y = self.CalcUnscrolledPosition(evt.GetX(), evt.GetY()) 2139 2140 row = self.YToRow(y) 2141 2142 if self.__prev_label_row == row: 2143 return 2144 2145 self.__prev_label_row == row 2146 2147 evt.GetEventObject().SetToolTip(self.get_row_tooltip(row = row))
2148 #------------------------------------------------------------ 2149 # def OnMouseMotionColLabel(self, evt): 2150 # x, y = self.CalcUnscrolledPosition(evt.GetPosition()) 2151 # col = self.XToCol(x) 2152 # label = self.table().GetColHelpValue(col) 2153 # self.GetGridColLabelWindow().SetToolTip(label or "") 2154 # evt.Skip() 2155 #------------------------------------------------------------
2156 - def __on_mouse_over_cells(self, evt):
2157 """Calculate where the mouse is and set the tooltip dynamically.""" 2158 2159 # Use CalcUnscrolledPosition() to get the mouse position within the 2160 # entire grid including what's offscreen 2161 x, y = self.CalcUnscrolledPosition(evt.GetX(), evt.GetY()) 2162 2163 # use this logic to prevent tooltips outside the actual cells 2164 # apply to GetRowSize, too 2165 # tot = 0 2166 # for col in range(self.NumberCols): 2167 # tot += self.GetColSize(col) 2168 # if xpos <= tot: 2169 # self.tool_tip.Tip = 'Tool tip for Column %s' % ( 2170 # self.GetColLabelValue(col)) 2171 # break 2172 # else: # mouse is in label area beyond the right-most column 2173 # self.tool_tip.Tip = '' 2174 2175 row, col = self.XYToCell(x, y) 2176 2177 if (row == self.__prev_row) and (col == self.__prev_col): 2178 return 2179 2180 self.__prev_row = row 2181 self.__prev_col = col 2182 2183 evt.GetEventObject().SetToolTip(self.get_cell_tooltip(col=col, row=row))
2184 2185 #------------------------------------------------------------ 2186 # properties 2187 #------------------------------------------------------------
2188 - def _get_patient(self):
2189 return self.__patient
2190
2191 - def _set_patient(self, patient):
2192 self.__patient = patient 2193 self.repopulate_grid()
2194 2195 patient = property(_get_patient, _set_patient) 2196 #------------------------------------------------------------
2197 - def _set_panel_to_show(self, panel):
2198 self.__panel_to_show = panel 2199 self.repopulate_grid()
2200 2201 panel_to_show = property(lambda x:x, _set_panel_to_show) 2202 #------------------------------------------------------------
2203 - def _set_show_by_panel(self, show_by_panel):
2204 self.__show_by_panel = show_by_panel 2205 self.repopulate_grid()
2206 2207 show_by_panel = property(lambda x:x, _set_show_by_panel)
2208 2209 #================================================================ 2210 # integrated measurements plugin 2211 #================================================================ 2212 from Gnumed.wxGladeWidgets import wxgMeasurementsPnl 2213
2214 -class cMeasurementsPnl(wxgMeasurementsPnl.wxgMeasurementsPnl, gmRegetMixin.cRegetOnPaintMixin):
2215 """Panel holding a grid with lab data. Used as notebook page.""" 2216
2217 - def __init__(self, *args, **kwargs):
2218 2219 wxgMeasurementsPnl.wxgMeasurementsPnl.__init__(self, *args, **kwargs) 2220 gmRegetMixin.cRegetOnPaintMixin.__init__(self) 2221 self.__display_mode = 'grid' 2222 self.__init_ui() 2223 self.__register_interests()
2224 #-------------------------------------------------------- 2225 # event handling 2226 #--------------------------------------------------------
2227 - def __register_interests(self):
2228 gmDispatcher.connect(signal = 'pre_patient_unselection', receiver = self._on_pre_patient_unselection) 2229 gmDispatcher.connect(signal = 'post_patient_selection', receiver = self._on_post_patient_selection) 2230 gmDispatcher.connect(signal = 'clin.test_result_mod_db', receiver = self._schedule_data_reget) 2231 gmDispatcher.connect(signal = 'clin.reviewed_test_results_mod_db', receiver = self._schedule_data_reget)
2232 #--------------------------------------------------------
2233 - def _on_post_patient_selection(self):
2234 self._schedule_data_reget()
2235 #--------------------------------------------------------
2237 self._GRID_results_all.patient = None 2238 self._GRID_results_battery.patient = None
2239 #--------------------------------------------------------
2240 - def _on_add_button_pressed(self, event):
2241 edit_measurement(parent = self, measurement = None)
2242 #--------------------------------------------------------
2243 - def _on_manage_types_button_pressed(self, event):
2244 event.Skip() 2245 manage_measurement_types(parent = self)
2246 #--------------------------------------------------------
2247 - def _on_list_button_pressed(self, event):
2248 event.Skip() 2249 manage_measurements(parent = self, single_selection = True)#, emr = pat.emr)
2250 #--------------------------------------------------------
2251 - def _on_review_button_pressed(self, evt):
2252 self.PopupMenu(self.__action_button_popup)
2253 #--------------------------------------------------------
2254 - def _on_select_button_pressed(self, evt):
2255 if self._RBTN_my_unsigned.GetValue() is True: 2256 self._GRID_results_all.select_cells(unsigned_only = True, accountables_only = True, keep_preselections = False) 2257 elif self._RBTN_all_unsigned.GetValue() is True: 2258 self._GRID_results_all.select_cells(unsigned_only = True, accountables_only = False, keep_preselections = False)
2259 #--------------------------------------------------------
2260 - def _on_manage_panels_button_pressed(self, event):
2262 #--------------------------------------------------------
2263 - def _on_display_mode_button_pressed(self, event):
2264 event.Skip() 2265 if self.__display_mode == 'grid': 2266 self._BTN_display_mode.SetLabel(_('All: as &Grid')) 2267 self.__display_mode = 'day' 2268 #self._GRID_results_all.Hide() 2269 self._PNL_results_all_grid.Hide() 2270 if self._PNL_results_all_listed.patient is None: 2271 self._PNL_results_all_listed.patient = self._GRID_results_all.patient 2272 self._PNL_results_all_listed.Show() 2273 else: 2274 self._BTN_display_mode.SetLabel(_('All: by &Day')) 2275 self.__display_mode = 'grid' 2276 self._PNL_results_all_listed.Hide() 2277 if self._GRID_results_all.patient is None: 2278 self._GRID_results_all.patient = self._PNL_results_all_listed.patient 2279 #self._GRID_results_all.Show() 2280 self._PNL_results_all_grid.Show() 2281 self.Layout()
2282 #--------------------------------------------------------
2283 - def __on_sign_current_selection(self, evt):
2284 self._GRID_results_all.sign_current_selection()
2285 #--------------------------------------------------------
2286 - def __on_plot_current_selection(self, evt):
2287 self._GRID_results_all.plot_current_selection()
2288 #--------------------------------------------------------
2289 - def __on_delete_current_selection(self, evt):
2290 self._GRID_results_all.delete_current_selection()
2291 #--------------------------------------------------------
2292 - def _on_panel_selected(self, panel):
2293 wx.CallAfter(self.__on_panel_selected, panel=panel)
2294 #--------------------------------------------------------
2295 - def __on_panel_selected(self, panel):
2296 if panel is None: 2297 self._TCTRL_panel_comment.SetValue('') 2298 self._GRID_results_battery.panel_to_show = None 2299 #self._GRID_results_battery.Hide() 2300 self._PNL_results_battery_grid.Hide() 2301 else: 2302 pnl = self._PRW_panel.GetData(as_instance = True) 2303 self._TCTRL_panel_comment.SetValue(gmTools.coalesce ( 2304 pnl['comment'], 2305 '' 2306 )) 2307 self._GRID_results_battery.panel_to_show = pnl 2308 #self._GRID_results_battery.Show() 2309 self._PNL_results_battery_grid.Show() 2310 self._GRID_results_battery.Fit() 2311 self._GRID_results_all.Fit() 2312 self.Layout()
2313 #--------------------------------------------------------
2315 wx.CallAfter(self.__on_panel_selection_modified)
2316 #--------------------------------------------------------
2318 self._TCTRL_panel_comment.SetValue('') 2319 if self._PRW_panel.GetValue().strip() == '': 2320 self._GRID_results_battery.panel_to_show = None 2321 #self._GRID_results_battery.Hide() 2322 self._PNL_results_battery_grid.Hide() 2323 self.Layout()
2324 #-------------------------------------------------------- 2325 # internal API 2326 #--------------------------------------------------------
2327 - def __init_ui(self):
2328 self.SetMinSize((10, 10)) 2329 2330 self.__action_button_popup = wx.Menu(title = _('Perform on selected results:')) 2331 2332 item = self.__action_button_popup.Append(-1, _('Review and &sign')) 2333 self.Bind(wx.EVT_MENU, self.__on_sign_current_selection, item) 2334 2335 item = self.__action_button_popup.Append(-1, _('Plot')) 2336 self.Bind(wx.EVT_MENU, self.__on_plot_current_selection, item) 2337 2338 item = self.__action_button_popup.Append(-1, _('Export to &file')) 2339 self.Bind(wx.EVT_MENU, self._GRID_results_all.current_selection_to_file, item) 2340 self.__action_button_popup.Enable(id = menu_id, enable = False) 2341 2342 item = self.__action_button_popup.Append(-1, _('Export to &clipboard')) 2343 self.Bind(wx.EVT_MENU, self._GRID_results_all.current_selection_to_clipboard, item) 2344 self.__action_button_popup.Enable(id = menu_id, enable = False) 2345 2346 item = self.__action_button_popup.Append(-1, _('&Delete')) 2347 self.Bind(wx.EVT_MENU, self.__on_delete_current_selection, item) 2348 2349 # FIXME: create inbox message to staff to phone patient to come in 2350 # FIXME: generate and let edit a SOAP narrative and include the values 2351 2352 self._PRW_panel.add_callback_on_selection(callback = self._on_panel_selected) 2353 self._PRW_panel.add_callback_on_modified(callback = self._on_panel_selection_modified) 2354 2355 self._GRID_results_battery.show_by_panel = True 2356 self._GRID_results_battery.panel_to_show = None 2357 #self._GRID_results_battery.Hide() 2358 self._PNL_results_battery_grid.Hide() 2359 self._BTN_display_mode.SetLabel(_('All: by &Day')) 2360 #self._GRID_results_all.Show() 2361 self._PNL_results_all_grid.Show() 2362 self._PNL_results_all_listed.Hide() 2363 self.Layout() 2364 2365 self._PRW_panel.SetFocus()
2366 #-------------------------------------------------------- 2367 # reget mixin API 2368 #--------------------------------------------------------
2369 - def _populate_with_data(self):
2370 pat = gmPerson.gmCurrentPatient() 2371 if pat.connected: 2372 self._GRID_results_battery.patient = pat 2373 if self.__display_mode == 'grid': 2374 self._GRID_results_all.patient = pat 2375 self._PNL_results_all_listed.patient = None 2376 else: 2377 self._GRID_results_all.patient = None 2378 self._PNL_results_all_listed.patient = pat 2379 else: 2380 self._GRID_results_battery.patient = None 2381 self._GRID_results_all.patient = None 2382 self._PNL_results_all_listed.patient = None 2383 return True
2384 2385 #================================================================ 2386 # editing widgets 2387 #================================================================
2388 -def review_tests(parent=None, tests=None):
2389 2390 if tests is None: 2391 return True 2392 2393 if len(tests) == 0: 2394 return True 2395 2396 if parent is None: 2397 parent = wx.GetApp().GetTopWindow() 2398 2399 if len(tests) > 10: 2400 test_count = len(tests) 2401 tests2show = None 2402 else: 2403 test_count = None 2404 tests2show = tests 2405 if len(tests) == 0: 2406 return True 2407 2408 dlg = cMeasurementsReviewDlg(parent, -1, tests = tests, test_count = test_count) 2409 decision = dlg.ShowModal() 2410 if decision != wx.ID_APPLY: 2411 return True 2412 2413 wx.BeginBusyCursor() 2414 if dlg._RBTN_confirm_abnormal.GetValue(): 2415 abnormal = None 2416 elif dlg._RBTN_results_normal.GetValue(): 2417 abnormal = False 2418 else: 2419 abnormal = True 2420 2421 if dlg._RBTN_confirm_relevance.GetValue(): 2422 relevant = None 2423 elif dlg._RBTN_results_not_relevant.GetValue(): 2424 relevant = False 2425 else: 2426 relevant = True 2427 2428 comment = None 2429 if len(tests) == 1: 2430 comment = dlg._TCTRL_comment.GetValue() 2431 2432 make_responsible = dlg._CHBOX_responsible.IsChecked() 2433 dlg.Destroy() 2434 2435 for test in tests: 2436 test.set_review ( 2437 technically_abnormal = abnormal, 2438 clinically_relevant = relevant, 2439 comment = comment, 2440 make_me_responsible = make_responsible 2441 ) 2442 wx.EndBusyCursor() 2443 2444 return True
2445 2446 #---------------------------------------------------------------- 2447 from Gnumed.wxGladeWidgets import wxgMeasurementsReviewDlg 2448
2449 -class cMeasurementsReviewDlg(wxgMeasurementsReviewDlg.wxgMeasurementsReviewDlg):
2450
2451 - def __init__(self, *args, **kwargs):
2452 2453 try: 2454 tests = kwargs['tests'] 2455 del kwargs['tests'] 2456 test_count = len(tests) 2457 try: del kwargs['test_count'] 2458 except KeyError: pass 2459 except KeyError: 2460 tests = None 2461 test_count = kwargs['test_count'] 2462 del kwargs['test_count'] 2463 2464 wxgMeasurementsReviewDlg.wxgMeasurementsReviewDlg.__init__(self, *args, **kwargs) 2465 2466 if tests is None: 2467 msg = _('%s results selected. Too many to list individually.') % test_count 2468 else: 2469 msg = '\n'.join ( 2470 [ '%s: %s %s (%s)' % ( 2471 t['unified_abbrev'], 2472 t['unified_val'], 2473 t['val_unit'], 2474 gmDateTime.pydt_strftime(t['clin_when'], '%Y %b %d') 2475 ) for t in tests 2476 ] 2477 ) 2478 2479 self._LBL_tests.SetLabel(msg) 2480 2481 if test_count == 1: 2482 self._TCTRL_comment.Enable(True) 2483 self._TCTRL_comment.SetValue(gmTools.coalesce(tests[0]['review_comment'], '')) 2484 if tests[0]['you_are_responsible']: 2485 self._CHBOX_responsible.Enable(False) 2486 2487 self.Fit()
2488 #-------------------------------------------------------- 2489 # event handling 2490 #--------------------------------------------------------
2491 - def _on_signoff_button_pressed(self, evt):
2492 if self.IsModal(): 2493 self.EndModal(wx.ID_APPLY) 2494 else: 2495 self.Close()
2496 2497 #================================================================ 2498 from Gnumed.wxGladeWidgets import wxgMeasurementEditAreaPnl 2499
2500 -class cMeasurementEditAreaPnl(wxgMeasurementEditAreaPnl.wxgMeasurementEditAreaPnl, gmEditArea.cGenericEditAreaMixin):
2501 """This edit area saves *new* measurements into the active patient only.""" 2502
2503 - def __init__(self, *args, **kwargs):
2504 2505 try: 2506 self.__default_date = kwargs['date'] 2507 del kwargs['date'] 2508 except KeyError: 2509 self.__default_date = None 2510 2511 wxgMeasurementEditAreaPnl.wxgMeasurementEditAreaPnl.__init__(self, *args, **kwargs) 2512 gmEditArea.cGenericEditAreaMixin.__init__(self) 2513 2514 self.__register_interests() 2515 2516 self.successful_save_msg = _('Successfully saved measurement.') 2517 2518 self._DPRW_evaluated.display_accuracy = gmDateTime.acc_minutes
2519 2520 #-------------------------------------------------------- 2521 # generic edit area mixin API 2522 #----------------------------------------------------------------
2523 - def set_fields(self, fields):
2524 try: 2525 self._PRW_test.SetData(data = fields['pk_test_type']['data']) 2526 except KeyError: 2527 pass 2528 try: 2529 self._DPRW_evaluated.SetData(data = fields['clin_when']['data']) 2530 except KeyError: 2531 pass 2532 try: 2533 self._PRW_problem.SetData(data = fields['pk_episode']['data']) 2534 except KeyError: 2535 pass 2536 try: 2537 self._PRW_units.SetText(fields['val_unit']['data'], fields['val_unit']['data'], True) 2538 except KeyError: 2539 pass 2540 try: 2541 self._TCTRL_normal_min.SetValue(fields['val_normal_min']['data']) 2542 except KeyError: 2543 pass 2544 try: 2545 self._TCTRL_normal_max.SetValue(fields['val_normal_max']['data']) 2546 except KeyError: 2547 pass 2548 try: 2549 self._TCTRL_normal_range.SetValue(fields['val_normal_range']['data']) 2550 except KeyError: 2551 pass 2552 try: 2553 self._TCTRL_target_min.SetValue(fields['val_target_min']['data']) 2554 except KeyError: 2555 pass 2556 try: 2557 self._TCTRL_target_max.SetValue(fields['val_target_max']['data']) 2558 except KeyError: 2559 pass 2560 try: 2561 self._TCTRL_target_range.SetValue(fields['val_target_range']['data']) 2562 except KeyError: 2563 pass 2564 2565 self._TCTRL_result.SetFocus()
2566 2567 #--------------------------------------------------------
2568 - def _refresh_as_new(self):
2569 self._PRW_test.SetText('', None, True) 2570 self.__refresh_loinc_info() 2571 self.__refresh_previous_value() 2572 self.__update_units_context() 2573 self._TCTRL_result.SetValue('') 2574 self._PRW_units.SetText('', None, True) 2575 self._PRW_abnormality_indicator.SetText('', None, True) 2576 if self.__default_date is None: 2577 self._DPRW_evaluated.SetData(data = pyDT.datetime.now(tz = gmDateTime.gmCurrentLocalTimezone)) 2578 else: 2579 self._DPRW_evaluated.SetData(data = None) 2580 self._TCTRL_note_test_org.SetValue('') 2581 self._PRW_intended_reviewer.SetData(gmStaff.gmCurrentProvider()['pk_staff']) 2582 self._PRW_problem.SetData() 2583 self._TCTRL_narrative.SetValue('') 2584 self._CHBOX_review.SetValue(False) 2585 self._CHBOX_abnormal.SetValue(False) 2586 self._CHBOX_relevant.SetValue(False) 2587 self._CHBOX_abnormal.Enable(False) 2588 self._CHBOX_relevant.Enable(False) 2589 self._TCTRL_review_comment.SetValue('') 2590 self._TCTRL_normal_min.SetValue('') 2591 self._TCTRL_normal_max.SetValue('') 2592 self._TCTRL_normal_range.SetValue('') 2593 self._TCTRL_target_min.SetValue('') 2594 self._TCTRL_target_max.SetValue('') 2595 self._TCTRL_target_range.SetValue('') 2596 self._TCTRL_norm_ref_group.SetValue('') 2597 2598 self._PRW_test.SetFocus()
2599 #--------------------------------------------------------
2600 - def _refresh_from_existing(self):
2601 self._PRW_test.SetData(data = self.data['pk_test_type']) 2602 self.__refresh_loinc_info() 2603 self.__refresh_previous_value() 2604 self.__update_units_context() 2605 self._TCTRL_result.SetValue(self.data['unified_val']) 2606 self._PRW_units.SetText(self.data['val_unit'], self.data['val_unit'], True) 2607 self._PRW_abnormality_indicator.SetText ( 2608 gmTools.coalesce(self.data['abnormality_indicator'], ''), 2609 gmTools.coalesce(self.data['abnormality_indicator'], ''), 2610 True 2611 ) 2612 self._DPRW_evaluated.SetData(data = self.data['clin_when']) 2613 self._TCTRL_note_test_org.SetValue(gmTools.coalesce(self.data['note_test_org'], '')) 2614 self._PRW_intended_reviewer.SetData(self.data['pk_intended_reviewer']) 2615 self._PRW_problem.SetData(self.data['pk_episode']) 2616 self._TCTRL_narrative.SetValue(gmTools.coalesce(self.data['comment'], '')) 2617 self._CHBOX_review.SetValue(False) 2618 self._CHBOX_abnormal.SetValue(gmTools.coalesce(self.data['is_technically_abnormal'], False)) 2619 self._CHBOX_relevant.SetValue(gmTools.coalesce(self.data['is_clinically_relevant'], False)) 2620 self._CHBOX_abnormal.Enable(False) 2621 self._CHBOX_relevant.Enable(False) 2622 self._TCTRL_review_comment.SetValue(gmTools.coalesce(self.data['review_comment'], '')) 2623 self._TCTRL_normal_min.SetValue(str(gmTools.coalesce(self.data['val_normal_min'], ''))) 2624 self._TCTRL_normal_max.SetValue(str(gmTools.coalesce(self.data['val_normal_max'], ''))) 2625 self._TCTRL_normal_range.SetValue(gmTools.coalesce(self.data['val_normal_range'], '')) 2626 self._TCTRL_target_min.SetValue(str(gmTools.coalesce(self.data['val_target_min'], ''))) 2627 self._TCTRL_target_max.SetValue(str(gmTools.coalesce(self.data['val_target_max'], ''))) 2628 self._TCTRL_target_range.SetValue(gmTools.coalesce(self.data['val_target_range'], '')) 2629 self._TCTRL_norm_ref_group.SetValue(gmTools.coalesce(self.data['norm_ref_group'], '')) 2630 2631 self._TCTRL_result.SetFocus()
2632 #--------------------------------------------------------
2634 self._PRW_test.SetText('', None, True) 2635 self.__refresh_loinc_info() 2636 self.__refresh_previous_value() 2637 self.__update_units_context() 2638 self._TCTRL_result.SetValue('') 2639 self._PRW_units.SetText('', None, True) 2640 self._PRW_abnormality_indicator.SetText('', None, True) 2641 self._DPRW_evaluated.SetData(data = self.data['clin_when']) 2642 self._TCTRL_note_test_org.SetValue('') 2643 self._PRW_intended_reviewer.SetData(self.data['pk_intended_reviewer']) 2644 self._PRW_problem.SetData(self.data['pk_episode']) 2645 self._TCTRL_narrative.SetValue('') 2646 self._CHBOX_review.SetValue(False) 2647 self._CHBOX_abnormal.SetValue(False) 2648 self._CHBOX_relevant.SetValue(False) 2649 self._CHBOX_abnormal.Enable(False) 2650 self._CHBOX_relevant.Enable(False) 2651 self._TCTRL_review_comment.SetValue('') 2652 self._TCTRL_normal_min.SetValue('') 2653 self._TCTRL_normal_max.SetValue('') 2654 self._TCTRL_normal_range.SetValue('') 2655 self._TCTRL_target_min.SetValue('') 2656 self._TCTRL_target_max.SetValue('') 2657 self._TCTRL_target_range.SetValue('') 2658 self._TCTRL_norm_ref_group.SetValue('') 2659 2660 self._PRW_test.SetFocus()
2661 #--------------------------------------------------------
2662 - def _valid_for_save(self):
2663 2664 validity = True 2665 2666 if not self._DPRW_evaluated.is_valid_timestamp(): 2667 self._DPRW_evaluated.display_as_valid(False) 2668 validity = False 2669 else: 2670 self._DPRW_evaluated.display_as_valid(True) 2671 2672 val = self._TCTRL_result.GetValue().strip() 2673 if val == '': 2674 validity = False 2675 self.display_ctrl_as_valid(self._TCTRL_result, False) 2676 else: 2677 self.display_ctrl_as_valid(self._TCTRL_result, True) 2678 numeric, val = gmTools.input2decimal(val) 2679 if numeric: 2680 if self._PRW_units.GetValue().strip() == '': 2681 self._PRW_units.display_as_valid(False) 2682 validity = False 2683 else: 2684 self._PRW_units.display_as_valid(True) 2685 else: 2686 self._PRW_units.display_as_valid(True) 2687 2688 if self._PRW_problem.GetValue().strip() == '': 2689 self._PRW_problem.display_as_valid(False) 2690 validity = False 2691 else: 2692 self._PRW_problem.display_as_valid(True) 2693 2694 if self._PRW_test.GetValue().strip() == '': 2695 self._PRW_test.display_as_valid(False) 2696 validity = False 2697 else: 2698 self._PRW_test.display_as_valid(True) 2699 2700 if self._PRW_intended_reviewer.GetData() is None: 2701 self._PRW_intended_reviewer.display_as_valid(False) 2702 validity = False 2703 else: 2704 self._PRW_intended_reviewer.display_as_valid(True) 2705 2706 ctrls = [self._TCTRL_normal_min, self._TCTRL_normal_max, self._TCTRL_target_min, self._TCTRL_target_max] 2707 for widget in ctrls: 2708 val = widget.GetValue().strip() 2709 if val == '': 2710 continue 2711 try: 2712 decimal.Decimal(val.replace(',', '.', 1)) 2713 self.display_ctrl_as_valid(widget, True) 2714 except: 2715 validity = False 2716 self.display_ctrl_as_valid(widget, False) 2717 2718 if validity is False: 2719 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save result. Invalid or missing essential input.')) 2720 2721 return validity
2722 #--------------------------------------------------------
2723 - def _save_as_new(self):
2724 2725 emr = gmPerson.gmCurrentPatient().emr 2726 2727 success, result = gmTools.input2decimal(self._TCTRL_result.GetValue()) 2728 if success: 2729 v_num = result 2730 v_al = None 2731 else: 2732 v_al = self._TCTRL_result.GetValue().strip() 2733 v_num = None 2734 2735 pk_type = self._PRW_test.GetData() 2736 if pk_type is None: 2737 abbrev = self._PRW_test.GetValue().strip() 2738 name = self._PRW_test.GetValue().strip() 2739 unit = gmTools.coalesce(self._PRW_units.GetData(), self._PRW_units.GetValue()).strip() 2740 lab = manage_measurement_orgs ( 2741 parent = self, 2742 msg = _('Please select (or create) a lab for the new test type [%s in %s]') % (name, unit) 2743 ) 2744 if lab is not None: 2745 lab = lab['pk_test_org'] 2746 tt = gmPathLab.create_measurement_type ( 2747 lab = lab, 2748 abbrev = abbrev, 2749 name = name, 2750 unit = unit 2751 ) 2752 pk_type = tt['pk_test_type'] 2753 2754 tr = emr.add_test_result ( 2755 episode = self._PRW_problem.GetData(can_create=True, is_open=False), 2756 type = pk_type, 2757 intended_reviewer = self._PRW_intended_reviewer.GetData(), 2758 val_num = v_num, 2759 val_alpha = v_al, 2760 unit = self._PRW_units.GetValue() 2761 ) 2762 2763 tr['clin_when'] = self._DPRW_evaluated.GetData().get_pydt() 2764 2765 ctrls = [ 2766 ('abnormality_indicator', self._PRW_abnormality_indicator), 2767 ('note_test_org', self._TCTRL_note_test_org), 2768 ('comment', self._TCTRL_narrative), 2769 ('val_normal_range', self._TCTRL_normal_range), 2770 ('val_target_range', self._TCTRL_target_range), 2771 ('norm_ref_group', self._TCTRL_norm_ref_group) 2772 ] 2773 for field, widget in ctrls: 2774 tr[field] = widget.GetValue().strip() 2775 2776 ctrls = [ 2777 ('val_normal_min', self._TCTRL_normal_min), 2778 ('val_normal_max', self._TCTRL_normal_max), 2779 ('val_target_min', self._TCTRL_target_min), 2780 ('val_target_max', self._TCTRL_target_max) 2781 ] 2782 for field, widget in ctrls: 2783 val = widget.GetValue().strip() 2784 if val == '': 2785 tr[field] = None 2786 else: 2787 tr[field] = decimal.Decimal(val.replace(',', '.', 1)) 2788 2789 tr.save_payload() 2790 2791 if self._CHBOX_review.GetValue() is True: 2792 tr.set_review ( 2793 technically_abnormal = self._CHBOX_abnormal.GetValue(), 2794 clinically_relevant = self._CHBOX_relevant.GetValue(), 2795 comment = gmTools.none_if(self._TCTRL_review_comment.GetValue().strip(), ''), 2796 make_me_responsible = False 2797 ) 2798 2799 self.data = tr 2800 2801 # wx.CallAfter ( 2802 # plot_adjacent_measurements, 2803 # test = self.data, 2804 # plot_singular_result = False, 2805 # use_default_template = True 2806 # ) 2807 2808 return True
2809 #--------------------------------------------------------
2810 - def _save_as_update(self):
2811 2812 success, result = gmTools.input2decimal(self._TCTRL_result.GetValue()) 2813 if success: 2814 v_num = result 2815 v_al = None 2816 else: 2817 v_num = None 2818 v_al = self._TCTRL_result.GetValue().strip() 2819 2820 pk_type = self._PRW_test.GetData() 2821 if pk_type is None: 2822 abbrev = self._PRW_test.GetValue().strip() 2823 name = self._PRW_test.GetValue().strip() 2824 unit = gmTools.coalesce(self._PRW_units.GetData(), self._PRW_units.GetValue()).strip() 2825 lab = manage_measurement_orgs ( 2826 parent = self, 2827 msg = _('Please select (or create) a lab for the new test type [%s in %s]') % (name, unit) 2828 ) 2829 if lab is not None: 2830 lab = lab['pk_test_org'] 2831 tt = gmPathLab.create_measurement_type ( 2832 lab = None, 2833 abbrev = abbrev, 2834 name = name, 2835 unit = unit 2836 ) 2837 pk_type = tt['pk_test_type'] 2838 2839 tr = self.data 2840 2841 tr['pk_episode'] = self._PRW_problem.GetData(can_create=True, is_open=False) 2842 tr['pk_test_type'] = pk_type 2843 tr['pk_intended_reviewer'] = self._PRW_intended_reviewer.GetData() 2844 tr['val_num'] = v_num 2845 tr['val_alpha'] = v_al 2846 tr['val_unit'] = gmTools.coalesce(self._PRW_units.GetData(), self._PRW_units.GetValue()).strip() 2847 tr['clin_when'] = self._DPRW_evaluated.GetData().get_pydt() 2848 2849 ctrls = [ 2850 ('abnormality_indicator', self._PRW_abnormality_indicator), 2851 ('note_test_org', self._TCTRL_note_test_org), 2852 ('comment', self._TCTRL_narrative), 2853 ('val_normal_range', self._TCTRL_normal_range), 2854 ('val_target_range', self._TCTRL_target_range), 2855 ('norm_ref_group', self._TCTRL_norm_ref_group) 2856 ] 2857 for field, widget in ctrls: 2858 tr[field] = widget.GetValue().strip() 2859 2860 ctrls = [ 2861 ('val_normal_min', self._TCTRL_normal_min), 2862 ('val_normal_max', self._TCTRL_normal_max), 2863 ('val_target_min', self._TCTRL_target_min), 2864 ('val_target_max', self._TCTRL_target_max) 2865 ] 2866 for field, widget in ctrls: 2867 val = widget.GetValue().strip() 2868 if val == '': 2869 tr[field] = None 2870 else: 2871 tr[field] = decimal.Decimal(val.replace(',', '.', 1)) 2872 2873 tr.save_payload() 2874 2875 if self._CHBOX_review.GetValue() is True: 2876 tr.set_review ( 2877 technically_abnormal = self._CHBOX_abnormal.GetValue(), 2878 clinically_relevant = self._CHBOX_relevant.GetValue(), 2879 comment = gmTools.none_if(self._TCTRL_review_comment.GetValue().strip(), ''), 2880 make_me_responsible = False 2881 ) 2882 2883 # wx.CallAfter ( 2884 # plot_adjacent_measurements, 2885 # test = self.data, 2886 # plot_singular_result = False, 2887 # use_default_template = True 2888 # ) 2889 2890 return True
2891 #-------------------------------------------------------- 2892 # event handling 2893 #--------------------------------------------------------
2894 - def __register_interests(self):
2895 self._PRW_test.add_callback_on_lose_focus(self._on_leave_test_prw) 2896 self._PRW_abnormality_indicator.add_callback_on_lose_focus(self._on_leave_indicator_prw) 2897 self._PRW_units.add_callback_on_lose_focus(self._on_leave_unit_prw)
2898 #--------------------------------------------------------
2899 - def _on_leave_test_prw(self):
2900 self.__refresh_loinc_info() 2901 self.__refresh_previous_value() 2902 self.__update_units_context() 2903 # only works if we've got a unit set 2904 self.__update_normal_range() 2905 self.__update_clinical_range()
2906 #--------------------------------------------------------
2907 - def _on_leave_unit_prw(self):
2908 # maybe we've got a unit now ? 2909 self.__update_normal_range() 2910 self.__update_clinical_range()
2911 #--------------------------------------------------------
2912 - def _on_leave_indicator_prw(self):
2913 # if the user hasn't explicitly enabled reviewing 2914 if not self._CHBOX_review.GetValue(): 2915 self._CHBOX_abnormal.SetValue(self._PRW_abnormality_indicator.GetValue().strip() != '')
2916 #--------------------------------------------------------
2917 - def _on_review_box_checked(self, evt):
2918 self._CHBOX_abnormal.Enable(self._CHBOX_review.GetValue()) 2919 self._CHBOX_relevant.Enable(self._CHBOX_review.GetValue()) 2920 self._TCTRL_review_comment.Enable(self._CHBOX_review.GetValue())
2921 #--------------------------------------------------------
2922 - def _on_test_info_button_pressed(self, event):
2923 pk = self._PRW_test.GetData() 2924 if pk is not None: 2925 tt = gmPathLab.cMeasurementType(aPK_obj = pk) 2926 search_term = '%s %s %s' % ( 2927 tt['name'], 2928 tt['abbrev'], 2929 gmTools.coalesce(tt['loinc'], '') 2930 ) 2931 else: 2932 search_term = self._PRW_test.GetValue() 2933 2934 search_term = search_term.replace(' ', '+') 2935 2936 call_browser_on_measurement_type(measurement_type = search_term)
2937 #--------------------------------------------------------
2938 - def _on_manage_episodes_button_pressed(self, event):
2941 #-------------------------------------------------------- 2942 # internal helpers 2943 #--------------------------------------------------------
2944 - def __update_units_context(self):
2945 2946 if self._PRW_test.GetData() is None: 2947 self._PRW_units.unset_context(context = 'pk_type') 2948 self._PRW_units.unset_context(context = 'loinc') 2949 if self._PRW_test.GetValue().strip() == '': 2950 self._PRW_units.unset_context(context = 'test_name') 2951 else: 2952 self._PRW_units.set_context(context = 'test_name', val = self._PRW_test.GetValue().strip()) 2953 return 2954 2955 tt = self._PRW_test.GetData(as_instance = True) 2956 2957 self._PRW_units.set_context(context = 'pk_type', val = tt['pk_test_type']) 2958 self._PRW_units.set_context(context = 'test_name', val = tt['name']) 2959 2960 if tt['loinc'] is not None: 2961 self._PRW_units.set_context(context = 'loinc', val = tt['loinc']) 2962 2963 # closest unit 2964 if self._PRW_units.GetValue().strip() == '': 2965 clin_when = self._DPRW_evaluated.GetData() 2966 if clin_when is None: 2967 unit = tt.temporally_closest_unit 2968 else: 2969 clin_when = clin_when.get_pydt() 2970 unit = tt.get_temporally_closest_unit(timestamp = clin_when) 2971 if unit is None: 2972 self._PRW_units.SetText('', unit, True) 2973 else: 2974 self._PRW_units.SetText(unit, unit, True)
2975 2976 #--------------------------------------------------------
2977 - def __update_normal_range(self):
2978 unit = self._PRW_units.GetValue().strip() 2979 if unit == '': 2980 return 2981 if self._PRW_test.GetData() is None: 2982 return 2983 for ctrl in [self._TCTRL_normal_min, self._TCTRL_normal_max, self._TCTRL_normal_range, self._TCTRL_norm_ref_group]: 2984 if ctrl.GetValue().strip() != '': 2985 return 2986 tt = self._PRW_test.GetData(as_instance = True) 2987 test_w_range = tt.get_temporally_closest_normal_range ( 2988 unit, 2989 timestamp = self._DPRW_evaluated.GetData().get_pydt() 2990 ) 2991 if test_w_range is None: 2992 return 2993 self._TCTRL_normal_min.SetValue(str(gmTools.coalesce(test_w_range['val_normal_min'], ''))) 2994 self._TCTRL_normal_max.SetValue(str(gmTools.coalesce(test_w_range['val_normal_max'], ''))) 2995 self._TCTRL_normal_range.SetValue(gmTools.coalesce(test_w_range['val_normal_range'], '')) 2996 self._TCTRL_norm_ref_group.SetValue(gmTools.coalesce(test_w_range['norm_ref_group'], ''))
2997 2998 #--------------------------------------------------------
2999 - def __update_clinical_range(self):
3000 unit = self._PRW_units.GetValue().strip() 3001 if unit == '': 3002 return 3003 if self._PRW_test.GetData() is None: 3004 return 3005 for ctrl in [self._TCTRL_target_min, self._TCTRL_target_max, self._TCTRL_target_range]: 3006 if ctrl.GetValue().strip() != '': 3007 return 3008 tt = self._PRW_test.GetData(as_instance = True) 3009 test_w_range = tt.get_temporally_closest_target_range ( 3010 unit, 3011 gmPerson.gmCurrentPatient().ID, 3012 timestamp = self._DPRW_evaluated.GetData().get_pydt() 3013 ) 3014 if test_w_range is None: 3015 return 3016 self._TCTRL_target_min.SetValue(str(gmTools.coalesce(test_w_range['val_target_min'], ''))) 3017 self._TCTRL_target_max.SetValue(str(gmTools.coalesce(test_w_range['val_target_max'], ''))) 3018 self._TCTRL_target_range.SetValue(gmTools.coalesce(test_w_range['val_target_range'], ''))
3019 3020 #--------------------------------------------------------
3021 - def __refresh_loinc_info(self):
3022 3023 self._TCTRL_loinc.SetValue('') 3024 3025 if self._PRW_test.GetData() is None: 3026 return 3027 3028 tt = self._PRW_test.GetData(as_instance = True) 3029 3030 if tt['loinc'] is None: 3031 return 3032 3033 info = gmLOINC.loinc2term(loinc = tt['loinc']) 3034 if len(info) == 0: 3035 self._TCTRL_loinc.SetValue('') 3036 return 3037 3038 self._TCTRL_loinc.SetValue('%s: %s' % (tt['loinc'], info[0]))
3039 #--------------------------------------------------------
3040 - def __refresh_previous_value(self):
3041 self._TCTRL_previous_value.SetValue('') 3042 # it doesn't make much sense to show the most 3043 # recent value when editing an existing one 3044 if self.data is not None: 3045 return 3046 if self._PRW_test.GetData() is None: 3047 return 3048 tt = self._PRW_test.GetData(as_instance = True) 3049 most_recent = tt.get_most_recent_results ( 3050 no_of_results = 1, 3051 patient = gmPerson.gmCurrentPatient().ID 3052 ) 3053 if most_recent is None: 3054 return 3055 self._TCTRL_previous_value.SetValue(_('%s ago: %s%s%s - %s%s') % ( 3056 gmDateTime.format_interval_medically(gmDateTime.pydt_now_here() - most_recent['clin_when']), 3057 most_recent['unified_val'], 3058 most_recent['val_unit'], 3059 gmTools.coalesce(most_recent['abnormality_indicator'], '', ' (%s)'), 3060 most_recent['abbrev_tt'], 3061 gmTools.coalesce(most_recent.formatted_range, '', ' [%s]') 3062 )) 3063 self._TCTRL_previous_value.SetToolTip(most_recent.format ( 3064 with_review = True, 3065 with_evaluation = False, 3066 with_ranges = True, 3067 with_episode = True, 3068 with_type_details=True 3069 ))
3070 3071 #================================================================ 3072 # measurement type handling 3073 #================================================================
3074 -def pick_measurement_types(parent=None, msg=None, right_column=None, picks=None):
3075 3076 if parent is None: 3077 parent = wx.GetApp().GetTopWindow() 3078 3079 if msg is None: 3080 msg = _('Pick the relevant measurement types.') 3081 3082 if right_column is None: 3083 right_columns = [_('Picked')] 3084 else: 3085 right_columns = [right_column] 3086 3087 picker = gmListWidgets.cItemPickerDlg(parent, -1, msg = msg) 3088 picker.set_columns(columns = [_('Known measurement types')], columns_right = right_columns) 3089 types = gmPathLab.get_measurement_types(order_by = 'unified_abbrev') 3090 picker.set_choices ( 3091 choices = [ 3092 '%s: %s%s' % ( 3093 t['unified_abbrev'], 3094 t['unified_name'], 3095 gmTools.coalesce(t['name_org'], '', ' (%s)') 3096 ) 3097 for t in types 3098 ], 3099 data = types 3100 ) 3101 if picks is not None: 3102 picker.set_picks ( 3103 picks = [ 3104 '%s: %s%s' % ( 3105 p['unified_abbrev'], 3106 p['unified_name'], 3107 gmTools.coalesce(p['name_org'], '', ' (%s)') 3108 ) 3109 for p in picks 3110 ], 3111 data = picks 3112 ) 3113 result = picker.ShowModal() 3114 3115 if result == wx.ID_CANCEL: 3116 picker.Destroy() 3117 return None 3118 3119 picks = picker.picks 3120 picker.Destroy() 3121 return picks
3122 3123 #----------------------------------------------------------------
3124 -def manage_measurement_types(parent=None):
3125 3126 if parent is None: 3127 parent = wx.GetApp().GetTopWindow() 3128 3129 #------------------------------------------------------------ 3130 def edit(test_type=None): 3131 ea = cMeasurementTypeEAPnl(parent, -1, type = test_type) 3132 dlg = gmEditArea.cGenericEditAreaDlg2 ( 3133 parent = parent, 3134 id = -1, 3135 edit_area = ea, 3136 single_entry = gmTools.bool2subst((test_type is None), False, True) 3137 ) 3138 dlg.SetTitle(gmTools.coalesce(test_type, _('Adding measurement type'), _('Editing measurement type'))) 3139 3140 if dlg.ShowModal() == wx.ID_OK: 3141 dlg.Destroy() 3142 return True 3143 3144 dlg.Destroy() 3145 return False
3146 #------------------------------------------------------------ 3147 def delete(measurement_type): 3148 if measurement_type.in_use: 3149 gmDispatcher.send ( 3150 signal = 'statustext', 3151 beep = True, 3152 msg = _('Cannot delete measurement type [%s (%s)] because it is in use.') % (measurement_type['name'], measurement_type['abbrev']) 3153 ) 3154 return False 3155 gmPathLab.delete_measurement_type(measurement_type = measurement_type['pk_test_type']) 3156 return True 3157 #------------------------------------------------------------ 3158 def get_tooltip(test_type): 3159 return test_type.format() 3160 #------------------------------------------------------------ 3161 def manage_aggregates(test_type): 3162 manage_meta_test_types(parent = parent) 3163 return False 3164 #------------------------------------------------------------ 3165 def refresh(lctrl): 3166 mtypes = gmPathLab.get_measurement_types(order_by = 'name, abbrev') 3167 items = [ [ 3168 m['abbrev'], 3169 m['name'], 3170 gmTools.coalesce(m['reference_unit'], ''), 3171 gmTools.coalesce(m['loinc'], ''), 3172 gmTools.coalesce(m['comment_type'], ''), 3173 gmTools.coalesce(m['name_org'], '?'), 3174 gmTools.coalesce(m['comment_org'], ''), 3175 m['pk_test_type'] 3176 ] for m in mtypes ] 3177 lctrl.set_string_items(items) 3178 lctrl.set_data(mtypes) 3179 #------------------------------------------------------------ 3180 msg = _( 3181 '\n' 3182 'These are the measurement types currently defined in GNUmed.\n' 3183 '\n' 3184 ) 3185 3186 gmListWidgets.get_choices_from_list ( 3187 parent = parent, 3188 msg = msg, 3189 caption = _('Showing measurement types.'), 3190 columns = [ _('Abbrev'), _('Name'), _('Unit'), _('LOINC'), _('Comment'), _('Org'), _('Comment'), '#' ], 3191 single_selection = True, 3192 refresh_callback = refresh, 3193 edit_callback = edit, 3194 new_callback = edit, 3195 delete_callback = delete, 3196 list_tooltip_callback = get_tooltip, 3197 left_extra_button = (_('%s &Aggregate') % gmTools.u_sum, _('Manage aggregations (%s) of tests into groups.') % gmTools.u_sum, manage_aggregates) 3198 ) 3199 3200 #----------------------------------------------------------------
3201 -class cMeasurementTypePhraseWheel(gmPhraseWheel.cPhraseWheel):
3202
3203 - def __init__(self, *args, **kwargs):
3204 3205 query = """ 3206 SELECT DISTINCT ON (field_label) 3207 pk_test_type AS data, 3208 name 3209 || ' (' 3210 || coalesce ( 3211 (SELECT unit || ' @ ' || organization FROM clin.v_test_orgs c_vto WHERE c_vto.pk_test_org = c_vtt.pk_test_org), 3212 '%(in_house)s' 3213 ) 3214 || ')' 3215 AS field_label, 3216 name 3217 || ' (' 3218 || abbrev || ', ' 3219 || coalesce(abbrev_meta || ': ' || name_meta || ', ', '') 3220 || coalesce ( 3221 (SELECT unit || ' @ ' || organization FROM clin.v_test_orgs c_vto WHERE c_vto.pk_test_org = c_vtt.pk_test_org), 3222 '%(in_house)s' 3223 ) 3224 || ')' 3225 AS list_label 3226 FROM 3227 clin.v_test_types c_vtt 3228 WHERE 3229 abbrev_meta %%(fragment_condition)s 3230 OR 3231 name_meta %%(fragment_condition)s 3232 OR 3233 abbrev %%(fragment_condition)s 3234 OR 3235 name %%(fragment_condition)s 3236 ORDER BY field_label 3237 LIMIT 50""" % {'in_house': _('generic / in house lab')} 3238 3239 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query) 3240 mp.setThresholds(1, 2, 4) 3241 mp.word_separators = '[ \t:@]+' 3242 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 3243 self.matcher = mp 3244 self.SetToolTip(_('Select the type of measurement.')) 3245 self.selection_only = False
3246 3247 #------------------------------------------------------------
3248 - def _data2instance(self):
3249 if self.GetData() is None: 3250 return None 3251 3252 return gmPathLab.cMeasurementType(aPK_obj = self.GetData())
3253 3254 #---------------------------------------------------------------- 3255 from Gnumed.wxGladeWidgets import wxgMeasurementTypeEAPnl 3256
3257 -class cMeasurementTypeEAPnl(wxgMeasurementTypeEAPnl.wxgMeasurementTypeEAPnl, gmEditArea.cGenericEditAreaMixin):
3258
3259 - def __init__(self, *args, **kwargs):
3260 3261 try: 3262 data = kwargs['type'] 3263 del kwargs['type'] 3264 except KeyError: 3265 data = None 3266 3267 wxgMeasurementTypeEAPnl.wxgMeasurementTypeEAPnl.__init__(self, *args, **kwargs) 3268 gmEditArea.cGenericEditAreaMixin.__init__(self) 3269 self.mode = 'new' 3270 self.data = data 3271 if data is not None: 3272 self.mode = 'edit' 3273 3274 self.__init_ui()
3275 3276 #----------------------------------------------------------------
3277 - def __init_ui(self):
3278 3279 # name phraseweel 3280 query = """ 3281 select distinct on (name) 3282 pk, 3283 name 3284 from clin.test_type 3285 where 3286 name %(fragment_condition)s 3287 order by name 3288 limit 50""" 3289 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query) 3290 mp.setThresholds(1, 2, 4) 3291 self._PRW_name.matcher = mp 3292 self._PRW_name.selection_only = False 3293 self._PRW_name.add_callback_on_lose_focus(callback = self._on_name_lost_focus) 3294 3295 # abbreviation 3296 query = """ 3297 select distinct on (abbrev) 3298 pk, 3299 abbrev 3300 from clin.test_type 3301 where 3302 abbrev %(fragment_condition)s 3303 order by abbrev 3304 limit 50""" 3305 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query) 3306 mp.setThresholds(1, 2, 3) 3307 self._PRW_abbrev.matcher = mp 3308 self._PRW_abbrev.selection_only = False 3309 3310 # unit 3311 self._PRW_reference_unit.selection_only = False 3312 3313 # loinc 3314 mp = gmLOINC.cLOINCMatchProvider() 3315 mp.setThresholds(1, 2, 4) 3316 #mp.print_queries = True 3317 #mp.word_separators = '[ \t:@]+' 3318 self._PRW_loinc.matcher = mp 3319 self._PRW_loinc.selection_only = False 3320 self._PRW_loinc.add_callback_on_lose_focus(callback = self._on_loinc_lost_focus)
3321 3322 #----------------------------------------------------------------
3323 - def _on_name_lost_focus(self):
3324 3325 test = self._PRW_name.GetValue().strip() 3326 3327 if test == '': 3328 self._PRW_reference_unit.unset_context(context = 'test_name') 3329 return 3330 3331 self._PRW_reference_unit.set_context(context = 'test_name', val = test)
3332 #----------------------------------------------------------------
3333 - def _on_loinc_lost_focus(self):
3334 loinc = self._PRW_loinc.GetData() 3335 3336 if loinc is None: 3337 self._TCTRL_loinc_info.SetValue('') 3338 self._PRW_reference_unit.unset_context(context = 'loinc') 3339 return 3340 3341 self._PRW_reference_unit.set_context(context = 'loinc', val = loinc) 3342 3343 info = gmLOINC.loinc2term(loinc = loinc) 3344 if len(info) == 0: 3345 self._TCTRL_loinc_info.SetValue('') 3346 return 3347 3348 self._TCTRL_loinc_info.SetValue(info[0])
3349 #---------------------------------------------------------------- 3350 # generic Edit Area mixin API 3351 #----------------------------------------------------------------
3352 - def _valid_for_save(self):
3353 3354 has_errors = False 3355 for field in [self._PRW_name, self._PRW_abbrev, self._PRW_reference_unit]: 3356 if field.GetValue().strip() in ['', None]: 3357 has_errors = True 3358 field.display_as_valid(valid = False) 3359 else: 3360 field.display_as_valid(valid = True) 3361 field.Refresh() 3362 3363 return (not has_errors)
3364 #----------------------------------------------------------------
3365 - def _save_as_new(self):
3366 3367 pk_org = self._PRW_test_org.GetData() 3368 if pk_org is None: 3369 pk_org = gmPathLab.create_test_org ( 3370 name = gmTools.none_if(self._PRW_test_org.GetValue().strip(), '') 3371 )['pk_test_org'] 3372 3373 tt = gmPathLab.create_measurement_type ( 3374 lab = pk_org, 3375 abbrev = self._PRW_abbrev.GetValue().strip(), 3376 name = self._PRW_name.GetValue().strip(), 3377 unit = gmTools.coalesce ( 3378 self._PRW_reference_unit.GetData(), 3379 self._PRW_reference_unit.GetValue() 3380 ).strip() 3381 ) 3382 if self._PRW_loinc.GetData() is not None: 3383 tt['loinc'] = gmTools.none_if(self._PRW_loinc.GetData().strip(), '') 3384 else: 3385 tt['loinc'] = gmTools.none_if(self._PRW_loinc.GetValue().strip(), '') 3386 tt['comment_type'] = gmTools.none_if(self._TCTRL_comment_type.GetValue().strip(), '') 3387 tt['pk_meta_test_type'] = self._PRW_meta_type.GetData() 3388 3389 tt.save() 3390 3391 self.data = tt 3392 3393 return True
3394 #----------------------------------------------------------------
3395 - def _save_as_update(self):
3396 3397 pk_org = self._PRW_test_org.GetData() 3398 if pk_org is None: 3399 pk_org = gmPathLab.create_test_org ( 3400 name = gmTools.none_if(self._PRW_test_org.GetValue().strip(), '') 3401 )['pk_test_org'] 3402 3403 self.data['pk_test_org'] = pk_org 3404 self.data['abbrev'] = self._PRW_abbrev.GetValue().strip() 3405 self.data['name'] = self._PRW_name.GetValue().strip() 3406 self.data['reference_unit'] = gmTools.coalesce ( 3407 self._PRW_reference_unit.GetData(), 3408 self._PRW_reference_unit.GetValue() 3409 ).strip() 3410 if self._PRW_loinc.GetData() is not None: 3411 self.data['loinc'] = gmTools.none_if(self._PRW_loinc.GetData().strip(), '') 3412 if self._PRW_loinc.GetData() is not None: 3413 self.data['loinc'] = gmTools.none_if(self._PRW_loinc.GetData().strip(), '') 3414 else: 3415 self.data['loinc'] = gmTools.none_if(self._PRW_loinc.GetValue().strip(), '') 3416 self.data['comment_type'] = gmTools.none_if(self._TCTRL_comment_type.GetValue().strip(), '') 3417 self.data['pk_meta_test_type'] = self._PRW_meta_type.GetData() 3418 self.data.save() 3419 3420 return True
3421 #----------------------------------------------------------------
3422 - def _refresh_as_new(self):
3423 self._PRW_name.SetText('', None, True) 3424 self._on_name_lost_focus() 3425 self._PRW_abbrev.SetText('', None, True) 3426 self._PRW_reference_unit.SetText('', None, True) 3427 self._PRW_loinc.SetText('', None, True) 3428 self._on_loinc_lost_focus() 3429 self._TCTRL_comment_type.SetValue('') 3430 self._PRW_test_org.SetText('', None, True) 3431 self._PRW_meta_type.SetText('', None, True) 3432 3433 self._PRW_name.SetFocus()
3434 #----------------------------------------------------------------
3435 - def _refresh_from_existing(self):
3436 self._PRW_name.SetText(self.data['name'], self.data['name'], True) 3437 self._on_name_lost_focus() 3438 self._PRW_abbrev.SetText(self.data['abbrev'], self.data['abbrev'], True) 3439 self._PRW_reference_unit.SetText ( 3440 gmTools.coalesce(self.data['reference_unit'], ''), 3441 self.data['reference_unit'], 3442 True 3443 ) 3444 self._PRW_loinc.SetText ( 3445 gmTools.coalesce(self.data['loinc'], ''), 3446 self.data['loinc'], 3447 True 3448 ) 3449 self._on_loinc_lost_focus() 3450 self._TCTRL_comment_type.SetValue(gmTools.coalesce(self.data['comment_type'], '')) 3451 self._PRW_test_org.SetText ( 3452 gmTools.coalesce(self.data['pk_test_org'], '', self.data['name_org']), 3453 self.data['pk_test_org'], 3454 True 3455 ) 3456 if self.data['pk_meta_test_type'] is None: 3457 self._PRW_meta_type.SetText('', None, True) 3458 else: 3459 self._PRW_meta_type.SetText('%s: %s' % (self.data['abbrev_meta'], self.data['name_meta']), self.data['pk_meta_test_type'], True) 3460 3461 self._PRW_name.SetFocus()
3462 #----------------------------------------------------------------
3464 self._refresh_as_new() 3465 self._PRW_test_org.SetText ( 3466 gmTools.coalesce(self.data['pk_test_org'], '', self.data['name_org']), 3467 self.data['pk_test_org'], 3468 True 3469 ) 3470 self._PRW_name.SetFocus()
3471 3472 #================================================================ 3473 _SQL_units_from_test_results = """ 3474 -- via clin.v_test_results.pk_type (for types already used in results) 3475 SELECT 3476 val_unit AS data, 3477 val_unit AS field_label, 3478 val_unit || ' (' || name_tt || ')' AS list_label, 3479 1 AS rank 3480 FROM 3481 clin.v_test_results 3482 WHERE 3483 ( 3484 val_unit %(fragment_condition)s 3485 OR 3486 reference_unit %(fragment_condition)s 3487 ) 3488 %(ctxt_type_pk)s 3489 %(ctxt_test_name)s 3490 """ 3491 3492 _SQL_units_from_test_types = """ 3493 -- via clin.test_type (for types not yet used in results) 3494 SELECT 3495 reference_unit AS data, 3496 reference_unit AS field_label, 3497 reference_unit || ' (' || name || ')' AS list_label, 3498 2 AS rank 3499 FROM 3500 clin.test_type 3501 WHERE 3502 reference_unit %(fragment_condition)s 3503 %(ctxt_ctt)s 3504 """ 3505 3506 _SQL_units_from_loinc_ipcc = """ 3507 -- via ref.loinc.ipcc_units 3508 SELECT 3509 ipcc_units AS data, 3510 ipcc_units AS field_label, 3511 ipcc_units || ' (LOINC.ipcc: ' || term || ')' AS list_label, 3512 3 AS rank 3513 FROM 3514 ref.loinc 3515 WHERE 3516 ipcc_units %(fragment_condition)s 3517 %(ctxt_loinc)s 3518 %(ctxt_loinc_term)s 3519 """ 3520 3521 _SQL_units_from_loinc_submitted = """ 3522 -- via ref.loinc.submitted_units 3523 SELECT 3524 submitted_units AS data, 3525 submitted_units AS field_label, 3526 submitted_units || ' (LOINC.submitted:' || term || ')' AS list_label, 3527 3 AS rank 3528 FROM 3529 ref.loinc 3530 WHERE 3531 submitted_units %(fragment_condition)s 3532 %(ctxt_loinc)s 3533 %(ctxt_loinc_term)s 3534 """ 3535 3536 _SQL_units_from_loinc_example = """ 3537 -- via ref.loinc.example_units 3538 SELECT 3539 example_units AS data, 3540 example_units AS field_label, 3541 example_units || ' (LOINC.example: ' || term || ')' AS list_label, 3542 3 AS rank 3543 FROM 3544 ref.loinc 3545 WHERE 3546 example_units %(fragment_condition)s 3547 %(ctxt_loinc)s 3548 %(ctxt_loinc_term)s 3549 """ 3550 3551 _SQL_units_from_substance_doses = """ 3552 -- via ref.v_substance_doses.unit 3553 SELECT 3554 unit AS data, 3555 unit AS field_label, 3556 unit || ' (' || substance || ')' AS list_label, 3557 2 AS rank 3558 FROM 3559 ref.v_substance_doses 3560 WHERE 3561 unit %(fragment_condition)s 3562 %(ctxt_substance)s 3563 """ 3564 3565 _SQL_units_from_substance_doses2 = """ 3566 -- via ref.v_substance_doses.dose_unit 3567 SELECT 3568 dose_unit AS data, 3569 dose_unit AS field_label, 3570 dose_unit || ' (' || substance || ')' AS list_label, 3571 2 AS rank 3572 FROM 3573 ref.v_substance_doses 3574 WHERE 3575 dose_unit %(fragment_condition)s 3576 %(ctxt_substance)s 3577 """ 3578 3579 #----------------------------------------------------------------
3580 -class cUnitPhraseWheel(gmPhraseWheel.cPhraseWheel):
3581
3582 - def __init__(self, *args, **kwargs):
3583 3584 query = """ 3585 SELECT DISTINCT ON (data) 3586 data, 3587 field_label, 3588 list_label 3589 FROM ( 3590 3591 SELECT 3592 data, 3593 field_label, 3594 list_label, 3595 rank 3596 FROM ( 3597 (%s) UNION ALL 3598 (%s) UNION ALL 3599 (%s) UNION ALL 3600 (%s) UNION ALL 3601 (%s) UNION ALL 3602 (%s) UNION ALL 3603 (%s) 3604 ) AS all_matching_units 3605 WHERE data IS NOT NULL 3606 ORDER BY rank, list_label 3607 3608 ) AS ranked_matching_units 3609 LIMIT 50""" % ( 3610 _SQL_units_from_test_results, 3611 _SQL_units_from_test_types, 3612 _SQL_units_from_loinc_ipcc, 3613 _SQL_units_from_loinc_submitted, 3614 _SQL_units_from_loinc_example, 3615 _SQL_units_from_substance_doses, 3616 _SQL_units_from_substance_doses2 3617 ) 3618 3619 ctxt = { 3620 'ctxt_type_pk': { 3621 'where_part': 'AND pk_test_type = %(pk_type)s', 3622 'placeholder': 'pk_type' 3623 }, 3624 'ctxt_test_name': { 3625 'where_part': 'AND %(test_name)s IN (name_tt, name_meta, abbrev_meta)', 3626 'placeholder': 'test_name' 3627 }, 3628 'ctxt_ctt': { 3629 'where_part': 'AND %(test_name)s IN (name, abbrev)', 3630 'placeholder': 'test_name' 3631 }, 3632 'ctxt_loinc': { 3633 'where_part': 'AND code = %(loinc)s', 3634 'placeholder': 'loinc' 3635 }, 3636 'ctxt_loinc_term': { 3637 'where_part': 'AND term ~* %(test_name)s', 3638 'placeholder': 'test_name' 3639 }, 3640 'ctxt_substance': { 3641 'where_part': 'AND description ~* %(substance)s', 3642 'placeholder': 'substance' 3643 } 3644 } 3645 3646 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query, context = ctxt) 3647 mp.setThresholds(1, 2, 4) 3648 #mp.print_queries = True 3649 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 3650 self.matcher = mp 3651 self.SetToolTip(_('Select the desired unit for the amount or measurement.')) 3652 self.selection_only = False 3653 self.phrase_separators = '[;|]+'
3654 3655 #================================================================ 3656 3657 #================================================================
3658 -class cTestResultIndicatorPhraseWheel(gmPhraseWheel.cPhraseWheel):
3659
3660 - def __init__(self, *args, **kwargs):
3661 3662 query = """ 3663 select distinct abnormality_indicator, 3664 abnormality_indicator, abnormality_indicator 3665 from clin.v_test_results 3666 where 3667 abnormality_indicator %(fragment_condition)s 3668 order by abnormality_indicator 3669 limit 25""" 3670 3671 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query) 3672 mp.setThresholds(1, 1, 2) 3673 mp.ignored_chars = "[.'\\\[\]#$%_]+" + '"' 3674 mp.word_separators = '[ \t&:]+' 3675 gmPhraseWheel.cPhraseWheel.__init__ ( 3676 self, 3677 *args, 3678 **kwargs 3679 ) 3680 self.matcher = mp 3681 self.SetToolTip(_('Select an indicator for the level of abnormality.')) 3682 self.selection_only = False
3683 3684 #================================================================ 3685 # measurement org widgets / functions 3686 #----------------------------------------------------------------
3687 -def edit_measurement_org(parent=None, org=None):
3688 ea = cMeasurementOrgEAPnl(parent, -1) 3689 ea.data = org 3690 ea.mode = gmTools.coalesce(org, 'new', 'edit') 3691 dlg = gmEditArea.cGenericEditAreaDlg2(parent, -1, edit_area = ea) 3692 dlg.SetTitle(gmTools.coalesce(org, _('Adding new diagnostic org'), _('Editing diagnostic org'))) 3693 if dlg.ShowModal() == wx.ID_OK: 3694 dlg.Destroy() 3695 return True 3696 dlg.Destroy() 3697 return False
3698 #----------------------------------------------------------------
3699 -def manage_measurement_orgs(parent=None, msg=None):
3700 3701 if parent is None: 3702 parent = wx.GetApp().GetTopWindow() 3703 3704 #------------------------------------------------------------ 3705 def edit(org=None): 3706 return edit_measurement_org(parent = parent, org = org)
3707 #------------------------------------------------------------ 3708 def refresh(lctrl): 3709 orgs = gmPathLab.get_test_orgs() 3710 lctrl.set_string_items ([ 3711 (o['unit'], o['organization'], gmTools.coalesce(o['test_org_contact'], ''), gmTools.coalesce(o['comment'], ''), o['pk_test_org']) 3712 for o in orgs 3713 ]) 3714 lctrl.set_data(orgs) 3715 #------------------------------------------------------------ 3716 def delete(test_org): 3717 gmPathLab.delete_test_org(test_org = test_org['pk_test_org']) 3718 return True 3719 #------------------------------------------------------------ 3720 if msg is None: 3721 msg = _('\nThese are the diagnostic orgs (path labs etc) currently defined in GNUmed.\n\n') 3722 3723 return gmListWidgets.get_choices_from_list ( 3724 parent = parent, 3725 msg = msg, 3726 caption = _('Showing diagnostic orgs.'), 3727 columns = [_('Name'), _('Organization'), _('Contact'), _('Comment'), '#'], 3728 single_selection = True, 3729 refresh_callback = refresh, 3730 edit_callback = edit, 3731 new_callback = edit, 3732 delete_callback = delete 3733 ) 3734 3735 #---------------------------------------------------------------- 3736 from Gnumed.wxGladeWidgets import wxgMeasurementOrgEAPnl 3737
3738 -class cMeasurementOrgEAPnl(wxgMeasurementOrgEAPnl.wxgMeasurementOrgEAPnl, gmEditArea.cGenericEditAreaMixin):
3739
3740 - def __init__(self, *args, **kwargs):
3741 3742 try: 3743 data = kwargs['org'] 3744 del kwargs['org'] 3745 except KeyError: 3746 data = None 3747 3748 wxgMeasurementOrgEAPnl.wxgMeasurementOrgEAPnl.__init__(self, *args, **kwargs) 3749 gmEditArea.cGenericEditAreaMixin.__init__(self) 3750 3751 self.mode = 'new' 3752 self.data = data 3753 if data is not None: 3754 self.mode = 'edit'
3755 3756 #self.__init_ui() 3757 #---------------------------------------------------------------- 3758 # def __init_ui(self): 3759 # # adjust phrasewheels etc 3760 #---------------------------------------------------------------- 3761 # generic Edit Area mixin API 3762 #----------------------------------------------------------------
3763 - def _valid_for_save(self):
3764 has_errors = False 3765 if self._PRW_org_unit.GetData() is None: 3766 if self._PRW_org_unit.GetValue().strip() == '': 3767 has_errors = True 3768 self._PRW_org_unit.display_as_valid(valid = False) 3769 else: 3770 self._PRW_org_unit.display_as_valid(valid = True) 3771 else: 3772 self._PRW_org_unit.display_as_valid(valid = True) 3773 3774 return (not has_errors)
3775 #----------------------------------------------------------------
3776 - def _save_as_new(self):
3777 data = gmPathLab.create_test_org ( 3778 name = self._PRW_org_unit.GetValue().strip(), 3779 comment = self._TCTRL_comment.GetValue().strip(), 3780 pk_org_unit = self._PRW_org_unit.GetData() 3781 ) 3782 data['test_org_contact'] = self._TCTRL_contact.GetValue().strip() 3783 data.save() 3784 self.data = data 3785 return True
3786 #----------------------------------------------------------------
3787 - def _save_as_update(self):
3788 # get or create the org unit 3789 name = self._PRW_org_unit.GetValue().strip() 3790 org = gmOrganization.org_exists(organization = name) 3791 if org is None: 3792 org = gmOrganization.create_org ( 3793 organization = name, 3794 category = 'Laboratory' 3795 ) 3796 org_unit = gmOrganization.create_org_unit ( 3797 pk_organization = org['pk_org'], 3798 unit = name 3799 ) 3800 # update test_org fields 3801 self.data['pk_org_unit'] = org_unit['pk_org_unit'] 3802 self.data['test_org_contact'] = self._TCTRL_contact.GetValue().strip() 3803 self.data['comment'] = self._TCTRL_comment.GetValue().strip() 3804 self.data.save() 3805 return True
3806 #----------------------------------------------------------------
3807 - def _refresh_as_new(self):
3808 self._PRW_org_unit.SetText(value = '', data = None) 3809 self._TCTRL_contact.SetValue('') 3810 self._TCTRL_comment.SetValue('')
3811 #----------------------------------------------------------------
3812 - def _refresh_from_existing(self):
3813 self._PRW_org_unit.SetText(value = self.data['unit'], data = self.data['pk_org_unit']) 3814 self._TCTRL_contact.SetValue(gmTools.coalesce(self.data['test_org_contact'], '')) 3815 self._TCTRL_comment.SetValue(gmTools.coalesce(self.data['comment'], ''))
3816 #----------------------------------------------------------------
3818 self._refresh_as_new()
3819 #----------------------------------------------------------------
3820 - def _on_manage_orgs_button_pressed(self, event):
3822 3823 #----------------------------------------------------------------
3824 -class cMeasurementOrgPhraseWheel(gmPhraseWheel.cPhraseWheel):
3825
3826 - def __init__(self, *args, **kwargs):
3827 3828 query = """ 3829 SELECT DISTINCT ON (list_label) 3830 pk_test_org AS data, 3831 unit || ' (' || organization || ')' AS field_label, 3832 unit || ' @ ' || organization AS list_label 3833 FROM clin.v_test_orgs 3834 WHERE 3835 unit %(fragment_condition)s 3836 OR 3837 organization %(fragment_condition)s 3838 ORDER BY list_label 3839 LIMIT 50""" 3840 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query) 3841 mp.setThresholds(1, 2, 4) 3842 #mp.word_separators = '[ \t:@]+' 3843 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 3844 self.matcher = mp 3845 self.SetToolTip(_('The name of the path lab/diagnostic organisation.')) 3846 self.selection_only = False
3847 #------------------------------------------------------------
3848 - def _create_data(self):
3849 if self.GetData() is not None: 3850 _log.debug('data already set, not creating') 3851 return 3852 3853 if self.GetValue().strip() == '': 3854 _log.debug('cannot create new lab, missing name') 3855 return 3856 3857 lab = gmPathLab.create_test_org(name = self.GetValue().strip()) 3858 self.SetText(value = lab['unit'], data = lab['pk_test_org']) 3859 return
3860 #------------------------------------------------------------
3861 - def _data2instance(self):
3862 return gmPathLab.cTestOrg(aPK_obj = self.GetData())
3863 3864 #================================================================ 3865 # Meta test type widgets 3866 #----------------------------------------------------------------
3867 -def edit_meta_test_type(parent=None, meta_test_type=None):
3868 ea = cMetaTestTypeEAPnl(parent, -1) 3869 ea.data = meta_test_type 3870 ea.mode = gmTools.coalesce(meta_test_type, 'new', 'edit') 3871 dlg = gmEditArea.cGenericEditAreaDlg2 ( 3872 parent = parent, 3873 id = -1, 3874 edit_area = ea, 3875 single_entry = gmTools.bool2subst((meta_test_type is None), False, True) 3876 ) 3877 dlg.SetTitle(gmTools.coalesce(meta_test_type, _('Adding new meta test type'), _('Editing meta test type'))) 3878 if dlg.ShowModal() == wx.ID_OK: 3879 dlg.Destroy() 3880 return True 3881 dlg.Destroy() 3882 return False
3883 3884 #----------------------------------------------------------------
3885 -def manage_meta_test_types(parent=None):
3886 3887 if parent is None: 3888 parent = wx.GetApp().GetTopWindow() 3889 3890 #------------------------------------------------------------ 3891 def edit(meta_test_type=None): 3892 return edit_meta_test_type(parent = parent, meta_test_type = meta_test_type)
3893 #------------------------------------------------------------ 3894 def delete(meta_test_type): 3895 gmPathLab.delete_meta_type(meta_type = meta_test_type['pk']) 3896 return True 3897 #---------------------------------------- 3898 def get_tooltip(data): 3899 if data is None: 3900 return None 3901 return data.format(with_tests = True) 3902 #------------------------------------------------------------ 3903 def refresh(lctrl): 3904 mtts = gmPathLab.get_meta_test_types() 3905 items = [ [ 3906 m['abbrev'], 3907 m['name'], 3908 gmTools.coalesce(m['loinc'], ''), 3909 gmTools.coalesce(m['comment'], ''), 3910 m['pk'] 3911 ] for m in mtts ] 3912 lctrl.set_string_items(items) 3913 lctrl.set_data(mtts) 3914 #---------------------------------------- 3915 3916 msg = _( 3917 '\n' 3918 'These are the meta test types currently defined in GNUmed.\n' 3919 '\n' 3920 'Meta test types allow you to aggregate several actual test types used\n' 3921 'by pathology labs into one logical type.\n' 3922 '\n' 3923 'This is useful for grouping together results of tests which come under\n' 3924 'different names but really are the same thing. This often happens when\n' 3925 'you switch labs or the lab starts using another test method.\n' 3926 ) 3927 3928 gmListWidgets.get_choices_from_list ( 3929 parent = parent, 3930 msg = msg, 3931 caption = _('Showing meta test types.'), 3932 columns = [_('Abbrev'), _('Name'), _('LOINC'), _('Comment'), '#'], 3933 single_selection = True, 3934 list_tooltip_callback = get_tooltip, 3935 edit_callback = edit, 3936 new_callback = edit, 3937 delete_callback = delete, 3938 refresh_callback = refresh 3939 ) 3940 3941 #----------------------------------------------------------------
3942 -class cMetaTestTypePRW(gmPhraseWheel.cPhraseWheel):
3943
3944 - def __init__(self, *args, **kwargs):
3945 3946 query = """ 3947 SELECT DISTINCT ON (field_label) 3948 c_mtt.pk 3949 AS data, 3950 c_mtt.abbrev || ': ' || name 3951 AS field_label, 3952 c_mtt.abbrev || ': ' || name 3953 || coalesce ( 3954 ' (' || c_mtt.comment || ')', 3955 '' 3956 ) 3957 || coalesce ( 3958 ', LOINC: ' || c_mtt.loinc, 3959 '' 3960 ) 3961 AS list_label 3962 FROM 3963 clin.meta_test_type c_mtt 3964 WHERE 3965 abbrev %(fragment_condition)s 3966 OR 3967 name %(fragment_condition)s 3968 OR 3969 loinc %(fragment_condition)s 3970 ORDER BY field_label 3971 LIMIT 50""" 3972 3973 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query) 3974 mp.setThresholds(1, 2, 4) 3975 mp.word_separators = '[ \t:@]+' 3976 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 3977 self.matcher = mp 3978 self.SetToolTip(_('Select the meta test type.')) 3979 self.selection_only = True
3980 #------------------------------------------------------------
3981 - def _data2instance(self):
3982 if self.GetData() is None: 3983 return None 3984 3985 return gmPathLab.cMetaTestType(aPK_obj = self.GetData())
3986 3987 #---------------------------------------------------------------- 3988 from Gnumed.wxGladeWidgets import wxgMetaTestTypeEAPnl 3989
3990 -class cMetaTestTypeEAPnl(wxgMetaTestTypeEAPnl.wxgMetaTestTypeEAPnl, gmEditArea.cGenericEditAreaMixin):
3991
3992 - def __init__(self, *args, **kwargs):
3993 3994 try: 3995 data = kwargs['meta_test_type'] 3996 del kwargs['meta_test_type'] 3997 except KeyError: 3998 data = None 3999 4000 wxgMetaTestTypeEAPnl.wxgMetaTestTypeEAPnl.__init__(self, *args, **kwargs) 4001 gmEditArea.cGenericEditAreaMixin.__init__(self) 4002 4003 # Code using this mixin should set mode and data 4004 # after instantiating the class: 4005 self.mode = 'new' 4006 self.data = data 4007 if data is not None: 4008 self.mode = 'edit' 4009 4010 self.__init_ui()
4011 #----------------------------------------------------------------
4012 - def __init_ui(self):
4013 # loinc 4014 mp = gmLOINC.cLOINCMatchProvider() 4015 mp.setThresholds(1, 2, 4) 4016 #mp.print_queries = True 4017 #mp.word_separators = '[ \t:@]+' 4018 self._PRW_loinc.matcher = mp 4019 self._PRW_loinc.selection_only = False 4020 self._PRW_loinc.add_callback_on_lose_focus(callback = self._on_loinc_lost_focus)
4021 4022 #---------------------------------------------------------------- 4023 # generic Edit Area mixin API 4024 #----------------------------------------------------------------
4025 - def _valid_for_save(self):
4026 4027 validity = True 4028 4029 if self._PRW_abbreviation.GetValue().strip() == '': 4030 validity = False 4031 self._PRW_abbreviation.display_as_valid(False) 4032 self.status_message = _('Missing abbreviation for meta test type.') 4033 self._PRW_abbreviation.SetFocus() 4034 else: 4035 self._PRW_abbreviation.display_as_valid(True) 4036 4037 if self._PRW_name.GetValue().strip() == '': 4038 validity = False 4039 self._PRW_name.display_as_valid(False) 4040 self.status_message = _('Missing name for meta test type.') 4041 self._PRW_name.SetFocus() 4042 else: 4043 self._PRW_name.display_as_valid(True) 4044 4045 return validity
4046 #----------------------------------------------------------------
4047 - def _save_as_new(self):
4048 4049 # save the data as a new instance 4050 data = gmPathLab.create_meta_type ( 4051 name = self._PRW_name.GetValue().strip(), 4052 abbreviation = self._PRW_abbreviation.GetValue().strip(), 4053 return_existing = False 4054 ) 4055 if data is None: 4056 self.status_message = _('This meta test type already exists.') 4057 return False 4058 data['loinc'] = self._PRW_loinc.GetData() 4059 data['comment'] = self._TCTRL_comment.GetValue().strip() 4060 data.save() 4061 self.data = data 4062 return True
4063 #----------------------------------------------------------------
4064 - def _save_as_update(self):
4065 self.data['name'] = self._PRW_name.GetValue().strip() 4066 self.data['abbrev'] = self._PRW_abbreviation.GetValue().strip() 4067 self.data['loinc'] = self._PRW_loinc.GetData() 4068 self.data['comment'] = self._TCTRL_comment.GetValue().strip() 4069 self.data.save() 4070 return True
4071 #----------------------------------------------------------------
4072 - def _refresh_as_new(self):
4073 self._PRW_name.SetText('', None) 4074 self._PRW_abbreviation.SetText('', None) 4075 self._PRW_loinc.SetText('', None) 4076 self._TCTRL_loinc_info.SetValue('') 4077 self._TCTRL_comment.SetValue('') 4078 self._LBL_member_detail.SetLabel('') 4079 4080 self._PRW_name.SetFocus()
4081 #----------------------------------------------------------------
4083 self._refresh_as_new()
4084 #----------------------------------------------------------------
4085 - def _refresh_from_existing(self):
4086 self._PRW_name.SetText(self.data['name'], self.data['pk']) 4087 self._PRW_abbreviation.SetText(self.data['abbrev'], self.data['abbrev']) 4088 self._PRW_loinc.SetText(gmTools.coalesce(self.data['loinc'], ''), self.data['loinc']) 4089 self.__refresh_loinc_info() 4090 self._TCTRL_comment.SetValue(gmTools.coalesce(self.data['comment'], '')) 4091 self.__refresh_members() 4092 4093 self._PRW_name.SetFocus()
4094 #---------------------------------------------------------------- 4095 # event handlers 4096 #----------------------------------------------------------------
4097 - def _on_loinc_lost_focus(self):
4098 self.__refresh_loinc_info()
4099 #---------------------------------------------------------------- 4100 # internal helpers 4101 #----------------------------------------------------------------
4102 - def __refresh_loinc_info(self):
4103 loinc = self._PRW_loinc.GetData() 4104 4105 if loinc is None: 4106 self._TCTRL_loinc_info.SetValue('') 4107 return 4108 4109 info = gmLOINC.loinc2term(loinc = loinc) 4110 if len(info) == 0: 4111 self._TCTRL_loinc_info.SetValue('') 4112 return 4113 4114 self._TCTRL_loinc_info.SetValue(info[0])
4115 #----------------------------------------------------------------
4116 - def __refresh_members(self):
4117 if self.data is None: 4118 self._LBL_member_detail.SetLabel('') 4119 return 4120 4121 types = self.data.included_test_types 4122 if len(types) == 0: 4123 self._LBL_member_detail.SetLabel('') 4124 return 4125 4126 lines = [] 4127 for tt in types: 4128 lines.append('%s (%s%s) [#%s] @ %s' % ( 4129 tt['name'], 4130 tt['abbrev'], 4131 gmTools.coalesce(tt['loinc'], '', ', LOINC: %s'), 4132 tt['pk_test_type'], 4133 tt['name_org'] 4134 )) 4135 self._LBL_member_detail.SetLabel('\n'.join(lines))
4136 4137 #================================================================ 4138 # test panel handling 4139 #================================================================
4140 -def edit_test_panel(parent=None, test_panel=None):
4141 ea = cTestPanelEAPnl(parent, -1) 4142 ea.data = test_panel 4143 ea.mode = gmTools.coalesce(test_panel, 'new', 'edit') 4144 dlg = gmEditArea.cGenericEditAreaDlg2 ( 4145 parent = parent, 4146 id = -1, 4147 edit_area = ea, 4148 single_entry = gmTools.bool2subst((test_panel is None), False, True) 4149 ) 4150 dlg.SetTitle(gmTools.coalesce(test_panel, _('Adding new test panel'), _('Editing test panel'))) 4151 if dlg.ShowModal() == wx.ID_OK: 4152 dlg.Destroy() 4153 return True 4154 dlg.Destroy() 4155 return False
4156 4157 #----------------------------------------------------------------
4158 -def manage_test_panels(parent=None):
4159 4160 if parent is None: 4161 parent = wx.GetApp().GetTopWindow() 4162 4163 #------------------------------------------------------------ 4164 def edit(test_panel=None): 4165 return edit_test_panel(parent = parent, test_panel = test_panel)
4166 #------------------------------------------------------------ 4167 def delete(test_panel): 4168 gmPathLab.delete_test_panel(pk = test_panel['pk_test_panel']) 4169 return True 4170 #------------------------------------------------------------ 4171 def get_tooltip(test_panel): 4172 return test_panel.format() 4173 #------------------------------------------------------------ 4174 def refresh(lctrl): 4175 panels = gmPathLab.get_test_panels(order_by = 'description') 4176 items = [ [ 4177 p['description'], 4178 gmTools.coalesce(p['comment'], ''), 4179 p['pk_test_panel'] 4180 ] for p in panels ] 4181 lctrl.set_string_items(items) 4182 lctrl.set_data(panels) 4183 #------------------------------------------------------------ 4184 gmListWidgets.get_choices_from_list ( 4185 parent = parent, 4186 caption = 'GNUmed: ' + _('Test panels list'), 4187 columns = [ _('Name'), _('Comment'), '#' ], 4188 single_selection = True, 4189 refresh_callback = refresh, 4190 edit_callback = edit, 4191 new_callback = edit, 4192 delete_callback = delete, 4193 list_tooltip_callback = get_tooltip 4194 ) 4195 4196 #----------------------------------------------------------------
4197 -class cTestPanelPRW(gmPhraseWheel.cPhraseWheel):
4198
4199 - def __init__(self, *args, **kwargs):
4200 query = """ 4201 SELECT 4202 pk_test_panel 4203 AS data, 4204 description 4205 AS field_label, 4206 description 4207 AS list_label 4208 FROM 4209 clin.v_test_panels 4210 WHERE 4211 description %(fragment_condition)s 4212 ORDER BY field_label 4213 LIMIT 30""" 4214 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query) 4215 mp.setThresholds(1, 2, 4) 4216 #mp.word_separators = '[ \t:@]+' 4217 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs) 4218 self.matcher = mp 4219 self.SetToolTip(_('Select a test panel.')) 4220 self.selection_only = True
4221 #------------------------------------------------------------
4222 - def _data2instance(self):
4223 if self.GetData() is None: 4224 return None 4225 return gmPathLab.cTestPanel(aPK_obj = self.GetData())
4226 #------------------------------------------------------------
4227 - def _get_data_tooltip(self):
4228 if self.GetData() is None: 4229 return None 4230 return gmPathLab.cTestPanel(aPK_obj = self.GetData()).format()
4231 4232 #==================================================================== 4233 from Gnumed.wxGladeWidgets import wxgTestPanelEAPnl 4234
4235 -class cTestPanelEAPnl(wxgTestPanelEAPnl.wxgTestPanelEAPnl, gmEditArea.cGenericEditAreaMixin):
4236
4237 - def __init__(self, *args, **kwargs):
4238 4239 try: 4240 data = kwargs['panel'] 4241 del kwargs['panel'] 4242 except KeyError: 4243 data = None 4244 4245 wxgTestPanelEAPnl.wxgTestPanelEAPnl.__init__(self, *args, **kwargs) 4246 gmEditArea.cGenericEditAreaMixin.__init__(self) 4247 4248 self.__loincs = None 4249 4250 self.mode = 'new' 4251 self.data = data 4252 if data is not None: 4253 self.mode = 'edit' 4254 4255 self.__init_ui()
4256 4257 #----------------------------------------------------------------
4258 - def __init_ui(self):
4259 self._LCTRL_loincs.set_columns([_('LOINC'), _('Term'), _('Units')]) 4260 self._LCTRL_loincs.set_column_widths(widths = [wx.LIST_AUTOSIZE, wx.LIST_AUTOSIZE, wx.LIST_AUTOSIZE]) 4261 #self._LCTRL_loincs.set_resize_column(column = 2) 4262 self._LCTRL_loincs.delete_callback = self._remove_loincs_from_list 4263 self.__refresh_loinc_list() 4264 4265 self._PRW_loinc.final_regex = r'.*' 4266 self._PRW_loinc.add_callback_on_selection(callback = self._on_loinc_selected)
4267 4268 #----------------------------------------------------------------
4269 - def __refresh_loinc_list(self):
4270 self._LCTRL_loincs.remove_items_safely() 4271 if self.__loincs is None: 4272 if self.data is None: 4273 return 4274 self.__loincs = self.data['loincs'] 4275 4276 items = [] 4277 for loinc in self.__loincs: 4278 loinc_detail = gmLOINC.loinc2data(loinc = loinc) 4279 if len(loinc_detail) == 0: 4280 # check for test type with this pseudo loinc 4281 ttypes = gmPathLab.get_measurement_types(loincs = [loinc]) 4282 if len(ttypes) == 0: 4283 items.append([loinc, _('LOINC not found'), '']) 4284 else: 4285 for tt in ttypes: 4286 items.append([loinc, _('not a LOINC') + u'; %(name)s @ %(name_org)s [#%(pk_test_type)s]' % tt, '']) 4287 continue 4288 items.append ([ 4289 loinc, 4290 loinc_detail['term'], 4291 gmTools.coalesce(loinc_detail['example_units'], '', '%s') 4292 ]) 4293 4294 self._LCTRL_loincs.set_string_items(items) 4295 self._LCTRL_loincs.set_column_widths()
4296 4297 #---------------------------------------------------------------- 4298 # generic Edit Area mixin API 4299 #----------------------------------------------------------------
4300 - def _valid_for_save(self):
4301 validity = True 4302 4303 if self.__loincs is None: 4304 if self.data is not None: 4305 self.__loincs = self.data['loincs'] 4306 4307 if self.__loincs is None: 4308 # not fatal despite panel being useless 4309 self.status_message = _('No LOINC codes selected.') 4310 self._PRW_loinc.SetFocus() 4311 4312 if self._TCTRL_description.GetValue().strip() == '': 4313 validity = False 4314 self.display_tctrl_as_valid(tctrl = self._TCTRL_description, valid = False) 4315 self._TCTRL_description.SetFocus() 4316 else: 4317 self.display_tctrl_as_valid(tctrl = self._TCTRL_description, valid = True) 4318 4319 return validity
4320 4321 #----------------------------------------------------------------
4322 - def _save_as_new(self):
4323 data = gmPathLab.create_test_panel(description = self._TCTRL_description.GetValue().strip()) 4324 data['comment'] = self._TCTRL_comment.GetValue().strip() 4325 data.save() 4326 if self.__loincs is not None: 4327 data.included_loincs = self.__loincs 4328 self.data = data 4329 return True
4330 4331 #----------------------------------------------------------------
4332 - def _save_as_update(self):
4333 self.data['description'] = self._TCTRL_description.GetValue().strip() 4334 self.data['comment'] = self._TCTRL_comment.GetValue().strip() 4335 self.data.save() 4336 if self.__loincs is not None: 4337 self.data.included_loincs = self.__loincs 4338 return True
4339 4340 #----------------------------------------------------------------
4341 - def _refresh_as_new(self):
4342 self._TCTRL_description.SetValue('') 4343 self._TCTRL_comment.SetValue('') 4344 self._PRW_loinc.SetText('', None) 4345 self._LBL_loinc.SetLabel('') 4346 self.__loincs = None 4347 self.__refresh_loinc_list() 4348 4349 self._TCTRL_description.SetFocus()
4350 4351 #----------------------------------------------------------------
4353 self._refresh_as_new()
4354 4355 #----------------------------------------------------------------
4356 - def _refresh_from_existing(self):
4357 self._TCTRL_description.SetValue(self.data['description']) 4358 self._TCTRL_comment.SetValue(gmTools.coalesce(self.data['comment'], '')) 4359 self._PRW_loinc.SetText('', None) 4360 self._LBL_loinc.SetLabel('') 4361 self.__loincs = self.data['loincs'] 4362 self.__refresh_loinc_list() 4363 4364 self._PRW_loinc.SetFocus()
4365 4366 #---------------------------------------------------------------- 4367 # event handlers 4368 #----------------------------------------------------------------
4369 - def _on_loinc_selected(self, loinc):
4370 loinc = self._PRW_loinc.GetData() 4371 if loinc is None: 4372 self._LBL_loinc.SetLabel('') 4373 return 4374 loinc_detail = gmLOINC.loinc2data(loinc = loinc) 4375 if len(loinc_detail) == 0: 4376 loinc_str = _('no LOINC details found') 4377 else: 4378 loinc_str = '%s: %s%s' % ( 4379 loinc, 4380 loinc_detail['term'], 4381 gmTools.coalesce(loinc_detail['example_units'], '', ' (%s)') 4382 ) 4383 self._LBL_loinc.SetLabel(loinc_str)
4384 4385 #----------------------------------------------------------------
4386 - def _on_add_loinc_button_pressed(self, event):
4387 event.Skip() 4388 4389 loinc = self._PRW_loinc.GetData() 4390 if loinc is None: 4391 loinc = self._PRW_loinc.GetValue().strip() 4392 if loinc.strip() == '': 4393 return 4394 4395 if self.__loincs is None: 4396 self.__loincs = [loinc] 4397 else: 4398 if loinc in self.__loincs: 4399 return 4400 self.__loincs.append(loinc) 4401 4402 self.__refresh_loinc_list() 4403 self._PRW_loinc.SetText('', None) 4404 self._LBL_loinc.SetLabel('') 4405 4406 self._PRW_loinc.SetFocus()
4407 4408 #----------------------------------------------------------------
4409 - def _on_remove_loinc_button_pressed(self, event):
4410 event.Skip() 4411 self._remove_loincs_from_list()
4412 4413 #----------------------------------------------------------------
4414 - def _remove_loincs_from_list(self):
4415 loincs2remove = self._LCTRL_loincs.selected_item_data 4416 if loincs2remove is None: 4417 return 4418 for loinc in loincs2remove: 4419 try: 4420 while True: 4421 self.__loincs.remove(loinc[0]) 4422 except ValueError: 4423 pass 4424 self.__refresh_loinc_list()
4425 4426 #================================================================ 4427 # main 4428 #---------------------------------------------------------------- 4429 if __name__ == '__main__': 4430 4431 from Gnumed.pycommon import gmLog2 4432 from Gnumed.wxpython import gmPatSearchWidgets 4433 4434 gmI18N.activate_locale() 4435 gmI18N.install_domain() 4436 gmDateTime.init() 4437 4438 #------------------------------------------------------------
4439 - def test_grid():
4440 pat = gmPersonSearch.ask_for_patient() 4441 app = wx.PyWidgetTester(size = (500, 300)) 4442 lab_grid = cMeasurementsGrid(app.frame, -1) 4443 lab_grid.patient = pat 4444 app.frame.Show() 4445 app.MainLoop()
4446 #------------------------------------------------------------
4447 - def test_test_ea_pnl():
4448 pat = gmPersonSearch.ask_for_patient() 4449 gmPatSearchWidgets.set_active_patient(patient=pat) 4450 app = wx.PyWidgetTester(size = (500, 300)) 4451 ea = cMeasurementEditAreaPnl(app.frame, -1) 4452 app.frame.Show() 4453 app.MainLoop()
4454 #------------------------------------------------------------ 4455 # def test_primary_care_vitals_pnl(): 4456 # app = wx.PyWidgetTester(size = (500, 300)) 4457 # pnl = wxgPrimaryCareVitalsInputPnl.wxgPrimaryCareVitalsInputPnl(app.frame, -1) 4458 # app.frame.Show() 4459 # app.MainLoop() 4460 #------------------------------------------------------------ 4461 if (len(sys.argv) > 1) and (sys.argv[1] == 'test'): 4462 #test_grid() 4463 test_test_ea_pnl() 4464 #test_primary_care_vitals_pnl() 4465 4466 #================================================================ 4467