| Home | Trees | Indices | Help |
|
|---|
|
|
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 codecs
14 import os.path
15
16
17 import wx
18 import wx.grid
19 import wx.lib.hyperlink
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
43 from Gnumed.wxpython import gmRegetMixin
44 from Gnumed.wxpython import gmEditArea
45 from Gnumed.wxpython import gmPhraseWheel
46 from Gnumed.wxpython import gmListWidgets
47 from Gnumed.wxpython import gmGuiHelpers
48 from Gnumed.wxpython import gmAuthWidgets
49 from Gnumed.wxpython import gmOrganizationWidgets
50
51
52 _log = logging.getLogger('gm.ui')
53
54 #================================================================
55 # HL7 related widgets
56 #================================================================
58
59 if parent is None:
60 parent = wx.GetApp().GetTopWindow()
61
62 # select file
63 dlg = wx.FileDialog (
64 parent = parent,
65 message = 'Import Excelleris HL7 from XML file:',
66 # defaultDir = aDefDir,
67 # defaultFile = fname,
68 wildcard = "xml files|*.xml|XML files|*.XML|all files|*",
69 style = wx.OPEN | wx.FILE_MUST_EXIST
70 )
71 choice = dlg.ShowModal()
72 xml_name = dlg.GetPath()
73 dlg.Destroy()
74 if choice != wx.ID_OK:
75 return False
76
77 # for now, localize gmHL7 import
78 from Gnumed.business import gmHL7
79
80 hl7 = gmHL7.extract_HL7_from_CDATA(xml_name, u'.//Message')
81 if hl7 is None:
82 gmGuiHelpers.gm_show_info (
83 u'File [%s]\ndoes not seem to contain HL7 wrapped in XML.' % xml_name,
84 u'Extracting HL7 from XML'
85 )
86 return False
87 fixed_hl7 = gmHL7.fix_HL7_stupidities(hl7)
88 PID_names = gmHL7.split_HL7_by_PID(fixed_hl7)
89 for name in PID_names:
90 gmHL7.stage_MSH_as_incoming_data(name, source = u'Excelleris')
91
92 #================================================================
94
95 if parent is None:
96 parent = wx.GetApp().GetTopWindow()
97
98 # select file
99 dlg = wx.FileDialog (
100 parent = parent,
101 message = 'Import HL7 from file:',
102 # defaultDir = aDefDir,
103 # defaultFile = fname,
104 wildcard = "*.hl7|*.hl7|*.HL7|*.HL7|all files|*",
105 style = wx.OPEN | wx.FILE_MUST_EXIST
106 )
107 choice = dlg.ShowModal()
108 hl7_name = dlg.GetPath()
109 dlg.Destroy()
110 if choice != wx.ID_OK:
111 return False
112
113 # for now, localize gmHL7 import
114 from Gnumed.business import gmHL7
115
116 fixed_hl7 = gmHL7.fix_HL7_stupidities(hl7_name)
117 PID_names = gmHL7.split_HL7_by_PID(fixed_hl7)
118 for name in PID_names:
119 gmHL7.stage_MSH_as_incoming_data(name, source = u'generic')
120
121 #================================================================
123
124 # for now, localize gmHL7 import
125 from Gnumed.business import gmHL7
126
127 if parent is None:
128 parent = wx.GetApp().GetTopWindow()
129 #------------------------------------------------------------
130 def show_hl7(data=None):
131 if data is None:
132 return False
133 filename = data.export_to_file()
134 if filename is None:
135 return False
136 formatted_hl7 = gmHL7.format_hl7_file(filename, return_filename = True)
137 gmMimeLib.call_viewer_on_file(aFile = formatted_hl7, block = False)
138
139 return False
140 #------------------------------------------------------------
141 def refresh(lctrl):
142 incoming = gmHL7.get_incoming_data()
143 items = [ [
144 i['data_type'],
145 u'%s, %s (%s) %s' % (
146 i['lastnames'],
147 i['firstnames'],
148 i['dob'],
149 i['gender']
150 ),
151 i['external_data_id'],
152 i['pk_incoming_data_unmatched']
153 ] for i in incoming ]
154 lctrl.set_string_items(items)
155 lctrl.set_data(incoming)
156 #------------------------------------------------------------
157 gmListWidgets.get_choices_from_list (
158 parent = parent,
159 msg = None,
160 caption = _('Showing unmatched incoming data'),
161 columns = [ _('Type'), _('Patient'), _('Data ID'), '#' ],
162 single_selection = True,
163 can_return_empty = False,
164 ignore_OK_button = True,
165 refresh_callback = refresh,
166 # edit_callback=None,
167 # new_callback=None,
168 # delete_callback=None,
169 left_extra_button = [_('Show'), _('Show formatted HL7'), show_hl7]
170 # middle_extra_button=None,
171 # right_extra_button=None
172 )
173
174 #================================================================
175 # LOINC related widgets
176 #================================================================
178
179 wx.BeginBusyCursor()
180
181 gmDispatcher.send(signal = 'statustext', msg = _('Updating LOINC data can take quite a while...'), beep = True)
182
183 # download
184 loinc_zip = gmNetworkTools.download_file(url = 'http://www.gnumed.de/downloads/data/loinc/loinctab.zip', suffix = '.zip')
185 if loinc_zip is None:
186 wx.EndBusyCursor()
187 gmGuiHelpers.gm_show_warning (
188 aTitle = _('Downloading LOINC'),
189 aMessage = _('Error downloading the latest LOINC data.\n')
190 )
191 return False
192
193 _log.debug('downloaded zipped LOINC data into [%s]', loinc_zip)
194
195 loinc_dir = gmNetworkTools.unzip_data_pack(filename = loinc_zip)
196
197 # split master data file
198 data_fname, license_fname = gmLOINC.split_LOINCDBTXT(input_fname = os.path.join(loinc_dir, 'LOINCDB.TXT'))
199
200 wx.EndBusyCursor()
201
202 conn = gmAuthWidgets.get_dbowner_connection(procedure = _('importing LOINC reference data'))
203 if conn is None:
204 return False
205
206 wx.BeginBusyCursor()
207
208 # import data
209 if gmLOINC.loinc_import(data_fname = data_fname, license_fname = license_fname, conn = conn):
210 gmDispatcher.send(signal = 'statustext', msg = _('Successfully imported LOINC reference data.'))
211 else:
212 gmDispatcher.send(signal = 'statustext', msg = _('Importing LOINC reference data failed.'), beep = True)
213
214 wx.EndBusyCursor()
215 return True
216
217 #================================================================
218 # convenience functions
219 #================================================================
221
222 dbcfg = gmCfg.cCfgSQL()
223
224 url = dbcfg.get (
225 option = u'external.urls.measurements_search',
226 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
227 bias = 'user',
228 default = u"http://www.google.de/search?as_oq=%(search_term)s&num=10&as_sitesearch=laborlexikon.de"
229 )
230
231 base_url = dbcfg.get2 (
232 option = u'external.urls.measurements_encyclopedia',
233 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
234 bias = 'user',
235 default = u'http://www.laborlexikon.de'
236 )
237
238 if measurement_type is None:
239 url = base_url
240
241 measurement_type = measurement_type.strip()
242
243 if measurement_type == u'':
244 url = base_url
245
246 url = url % {'search_term': measurement_type}
247
248 gmNetworkTools.open_url_in_browser(url = url)
249
250 #----------------------------------------------------------------
252 ea = cMeasurementEditAreaPnl(parent = parent, id = -1)
253 ea.data = measurement
254 ea.mode = gmTools.coalesce(measurement, 'new', 'edit')
255 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = single_entry)
256 dlg.SetTitle(gmTools.coalesce(measurement, _('Adding new measurement'), _('Editing measurement')))
257 if dlg.ShowModal() == wx.ID_OK:
258 dlg.Destroy()
259 return True
260 dlg.Destroy()
261 return False
262
263 #----------------------------------------------------------------
265
266 if parent is None:
267 parent = wx.GetApp().GetTopWindow()
268
269 if emr is None:
270 emr = gmPerson.gmCurrentPatient().emr
271
272 #------------------------------------------------------------
273 def edit(measurement=None):
274 return edit_measurement(parent = parent, measurement = measurement, single_entry = True)
275 #------------------------------------------------------------
276 def delete(measurement):
277 gmPathLab.delete_test_result(result = measurement)
278 return True
279 #------------------------------------------------------------
280 def do_review(lctrl):
281 data = lctrl.get_selected_item_data()
282 if len(data) == 0:
283 return
284 return review_tests(parent = parent, tests = data)
285 #------------------------------------------------------------
286 def do_plot(lctrl):
287 data = lctrl.get_selected_item_data()
288 if len(data) == 0:
289 return
290 return plot_measurements(parent = parent, tests = data)
291 #------------------------------------------------------------
292 def get_tooltip(measurement):
293 return measurement.format(with_review=True, with_evaluation=True, with_ranges=True)
294 #------------------------------------------------------------
295 def refresh(lctrl):
296 results = emr.get_test_results(order_by = 'clin_when DESC, unified_abbrev, unified_name')
297 items = [ [
298 gmDateTime.pydt_strftime (
299 r['clin_when'],
300 '%Y %b %d %H:%M',
301 accuracy = gmDateTime.acc_minutes
302 ),
303 r['unified_abbrev'],
304 u'%s%s%s' % (
305 r['unified_val'],
306 gmTools.coalesce(r['val_unit'], u'', u' %s'),
307 gmTools.coalesce(r['abnormality_indicator'], u'', u' %s')
308 ),
309 r['unified_name'],
310 gmTools.coalesce(r['comment'], u''),
311 r['pk_test_result']
312 ] for r in results ]
313 lctrl.set_string_items(items)
314 lctrl.set_data(results)
315 #------------------------------------------------------------
316 msg = _('Test results (ordered reverse-chronologically)')
317
318 return gmListWidgets.get_choices_from_list (
319 parent = parent,
320 msg = msg,
321 caption = _('Showing test results.'),
322 columns = [ _('When'), _('Abbrev'), _('Value'), _('Name'), _('Comment'), u'#' ],
323 single_selection = single_selection,
324 can_return_empty = False,
325 refresh_callback = refresh,
326 edit_callback = edit,
327 new_callback = edit,
328 delete_callback = delete,
329 list_tooltip_callback = get_tooltip,
330 left_extra_button = (_('Review'), _('Review current selection'), do_review, True),
331 middle_extra_button = (_('Plot'), _('Plot current selection'), do_plot, True)
332 )
333
334 #================================================================
336
337 from Gnumed.wxpython import gmFormWidgets
338
339 if parent is None:
340 parent = wx.GetApp().GetTopWindow()
341
342 template = gmFormWidgets.manage_form_templates (
343 parent = parent,
344 active_only = True,
345 template_types = [u'gnuplot script']
346 )
347
348 option = u'form_templates.default_gnuplot_template'
349
350 if template is None:
351 gmDispatcher.send(signal = 'statustext', msg = _('No default Gnuplot script template selected.'), beep = True)
352 return None
353
354 if template['engine'] != u'G':
355 gmDispatcher.send(signal = 'statustext', msg = _('No default Gnuplot script template selected.'), beep = True)
356 return None
357
358 dbcfg = gmCfg.cCfgSQL()
359 dbcfg.set (
360 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
361 option = option,
362 value = u'%s - %s' % (template['name_long'], template['external_version'])
363 )
364 return template
365
366 #============================================================
368
369 option = u'form_templates.default_gnuplot_template'
370
371 dbcfg = gmCfg.cCfgSQL()
372
373 # load from option
374 default_template_name = dbcfg.get2 (
375 option = option,
376 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
377 bias = 'user'
378 )
379
380 # not configured -> try to configure
381 if default_template_name is None:
382 gmDispatcher.send('statustext', msg = _('No default Gnuplot template configured.'), beep = False)
383 default_template = configure_default_gnuplot_template(parent = parent)
384 # still not configured -> return
385 if default_template is None:
386 gmGuiHelpers.gm_show_error (
387 aMessage = _('There is no default Gnuplot one-type script template configured.'),
388 aTitle = _('Plotting test results')
389 )
390 return None
391 return default_template
392
393 # now it MUST be configured (either newly or previously)
394 # but also *validly* ?
395 try:
396 name, ver = default_template_name.split(u' - ')
397 except:
398 # not valid
399 _log.exception('problem splitting Gnuplot script template name [%s]', default_template_name)
400 gmDispatcher.send(signal = 'statustext', msg = _('Problem loading Gnuplot script template.'), beep = True)
401 return None
402
403 default_template = gmForms.get_form_template(name_long = name, external_version = ver)
404 if default_template is None:
405 default_template = configure_default_gnuplot_template(parent = parent)
406 # still not configured -> return
407 if default_template is None:
408 gmGuiHelpers.gm_show_error (
409 aMessage = _('Cannot load default Gnuplot script template [%s - %s]') % (name, ver),
410 aTitle = _('Plotting test results')
411 )
412 return None
413
414 return default_template
415
416 #----------------------------------------------------------------
417 -def plot_measurements(parent=None, tests=None, format=None, show_year = True, use_default_template=False):
418
419 from Gnumed.wxpython import gmFormWidgets
420
421 # only valid for one-type plotting
422 if use_default_template:
423 template = get_default_gnuplot_template()
424 else:
425 template = gmFormWidgets.manage_form_templates (
426 parent = parent,
427 active_only = True,
428 template_types = [u'gnuplot script']
429 )
430
431 if template is None:
432 gmGuiHelpers.gm_show_error (
433 aMessage = _('Cannot plot without a plot script.'),
434 aTitle = _('Plotting test results')
435 )
436 return False
437
438 fname_data = gmPathLab.export_results_for_gnuplot(results = tests, show_year = show_year)
439
440 script = template.instantiate()
441 script.data_filename = fname_data
442 script.generate_output(format = format) # Gnuplot output terminal, wxt = wxWidgets window
443
444 #----------------------------------------------------------------
445 -def plot_adjacent_measurements(parent=None, test=None, format=None, show_year=True, plot_singular_result=True, use_default_template=False):
446
447 earlier, later = test.get_adjacent_results(desired_earlier_results = 2, desired_later_results = 2)
448 results2plot = []
449 if earlier is not None:
450 results2plot.extend(earlier)
451 results2plot.append(test)
452 if later is not None:
453 results2plot.extend(later)
454 if len(results2plot) == 1:
455 if not plot_singular_result:
456 return
457 plot_measurements (
458 parent = parent,
459 tests = results2plot,
460 format = format,
461 show_year = show_year,
462 use_default_template = use_default_template
463 )
464 #================================================================
465 #from Gnumed.wxGladeWidgets import wxgPrimaryCareVitalsInputPnl
466
467 # Taillenumfang: Mitte zwischen unterster Rippe und
468 # hoechstem Teil des Beckenkamms
469 # Maenner: maessig: 94-102, deutlich: > 102 .. erhoeht
470 # Frauen: maessig: 80-88, deutlich: > 88 .. erhoeht
471
472 #================================================================
473 # display widgets
474 #================================================================
476 """A grid class for displaying measurment results.
477
478 - does NOT listen to the currently active patient
479 - thereby it can display any patient at any time
480 """
481 # FIXME: sort-by-battery
482 # FIXME: filter-by-battery
483 # FIXME: filter out empty
484 # FIXME: filter by tests of a selected date
485 # FIXME: dates DESC/ASC by cfg
486 # FIXME: mouse over column header: display date info
488
489 wx.grid.Grid.__init__(self, *args, **kwargs)
490
491 self.__patient = None
492 self.__panel_to_show = None
493 self.__show_by_panel = False
494 self.__cell_data = {}
495 self.__row_label_data = []
496
497 self.__prev_row = None
498 self.__prev_col = None
499 self.__prev_label_row = None
500 self.__date_format = str((_('lab_grid_date_format::%Y\n%b %d')).lstrip('lab_grid_date_format::'))
501
502 self.__init_ui()
503 self.__register_events()
504 #------------------------------------------------------------
505 # external API
506 #------------------------------------------------------------
508 if not self.IsSelection():
509 gmDispatcher.send(signal = u'statustext', msg = _('No results selected for deletion.'))
510 return True
511
512 selected_cells = self.get_selected_cells()
513 if len(selected_cells) > 20:
514 results = None
515 msg = _(
516 'There are %s results marked for deletion.\n'
517 '\n'
518 'Are you sure you want to delete these results ?'
519 ) % len(selected_cells)
520 else:
521 results = self.__cells_to_data(cells = selected_cells, exclude_multi_cells = False)
522 txt = u'\n'.join([ u'%s %s (%s): %s %s%s' % (
523 r['clin_when'].strftime('%x %H:%M').decode(gmI18N.get_encoding()),
524 r['unified_abbrev'],
525 r['unified_name'],
526 r['unified_val'],
527 r['val_unit'],
528 gmTools.coalesce(r['abnormality_indicator'], u'', u' (%s)')
529 ) for r in results
530 ])
531 msg = _(
532 'The following results are marked for deletion:\n'
533 '\n'
534 '%s\n'
535 '\n'
536 'Are you sure you want to delete these results ?'
537 ) % txt
538
539 dlg = gmGuiHelpers.c2ButtonQuestionDlg (
540 self,
541 -1,
542 caption = _('Deleting test results'),
543 question = msg,
544 button_defs = [
545 {'label': _('Delete'), 'tooltip': _('Yes, delete all the results.'), 'default': False},
546 {'label': _('Cancel'), 'tooltip': _('No, do NOT delete any results.'), 'default': True}
547 ]
548 )
549 decision = dlg.ShowModal()
550
551 if decision == wx.ID_YES:
552 if results is None:
553 results = self.__cells_to_data(cells = selected_cells, exclude_multi_cells = False)
554 for result in results:
555 gmPathLab.delete_test_result(result)
556 #------------------------------------------------------------
558 if not self.IsSelection():
559 gmDispatcher.send(signal = u'statustext', msg = _('Cannot sign results. No results selected.'))
560 return True
561
562 selected_cells = self.get_selected_cells()
563 tests = self.__cells_to_data(cells = selected_cells, exclude_multi_cells = False)
564
565 return review_tests(parent = self, tests = tests)
566 #------------------------------------------------------------
568
569 if not self.IsSelection():
570 gmDispatcher.send(signal = u'statustext', msg = _('Cannot plot results. No results selected.'))
571 return True
572
573 tests = self.__cells_to_data (
574 cells = self.get_selected_cells(),
575 exclude_multi_cells = False,
576 auto_include_multi_cells = True
577 )
578
579 plot_measurements(parent = self, tests = tests)
580 #------------------------------------------------------------
582
583 sel_block_top_left = self.GetSelectionBlockTopLeft()
584 sel_block_bottom_right = self.GetSelectionBlockBottomRight()
585 sel_cols = self.GetSelectedCols()
586 sel_rows = self.GetSelectedRows()
587
588 selected_cells = []
589
590 # individually selected cells (ctrl-click)
591 selected_cells += self.GetSelectedCells()
592
593 # selected rows
594 selected_cells += list (
595 (row, col)
596 for row in sel_rows
597 for col in xrange(self.GetNumberCols())
598 )
599
600 # selected columns
601 selected_cells += list (
602 (row, col)
603 for row in xrange(self.GetNumberRows())
604 for col in sel_cols
605 )
606
607 # selection blocks
608 for top_left, bottom_right in zip(self.GetSelectionBlockTopLeft(), self.GetSelectionBlockBottomRight()):
609 selected_cells += [
610 (row, col)
611 for row in xrange(top_left[0], bottom_right[0] + 1)
612 for col in xrange(top_left[1], bottom_right[1] + 1)
613 ]
614
615 return set(selected_cells)
616 #------------------------------------------------------------
617 - def select_cells(self, unsigned_only=False, accountables_only=False, keep_preselections=False):
618 """Select a range of cells according to criteria.
619
620 unsigned_only: include only those which are not signed at all yet
621 accountable_only: include only those for which the current user is responsible
622 keep_preselections: broaden (rather than replace) the range of selected cells
623
624 Combinations are powerful !
625 """
626 wx.BeginBusyCursor()
627 self.BeginBatch()
628
629 if not keep_preselections:
630 self.ClearSelection()
631
632 for col_idx in self.__cell_data.keys():
633 for row_idx in self.__cell_data[col_idx].keys():
634 # loop over results in cell and only include
635 # those multi-value cells that are not ambiguous
636 do_not_include = False
637 for result in self.__cell_data[col_idx][row_idx]:
638 if unsigned_only:
639 if result['reviewed']:
640 do_not_include = True
641 break
642 if accountables_only:
643 if not result['you_are_responsible']:
644 do_not_include = True
645 break
646 if do_not_include:
647 continue
648
649 self.SelectBlock(row_idx, col_idx, row_idx, col_idx, addToSelected = True)
650
651 self.EndBatch()
652 wx.EndBusyCursor()
653 #------------------------------------------------------------
655 self.empty_grid()
656 if self.__patient is None:
657 return
658
659 if self.__show_by_panel:
660 self.__repopulate_grid_by_panel()
661 return
662
663 self.__repopulate_grid_all_results()
664 #------------------------------------------------------------
666
667 if self.__panel_to_show is None:
668 return
669
670 emr = self.__patient.get_emr()
671
672 # rows
673 self.__row_label_data = self.__panel_to_show.test_types
674 row_labels = [ u'%s%s' % (
675 gmTools.bool2subst(test['is_fake_meta_type'], u'', gmTools.u_sum, u''),
676 test['unified_abbrev']
677 ) for test in self.__row_label_data
678 ]
679 if len(row_labels) == 0:
680 return
681
682 # columns
683 column_labels = [
684 date[0].strftime(self.__date_format) for date in emr.get_dates_for_results (
685 tests = self.__panel_to_show['pk_test_types'],
686 # FIXME: make configurable
687 reverse_chronological = True
688 )
689 ]
690 results = emr.get_test_results_by_date (
691 tests = self.__panel_to_show['pk_test_types'],
692 # FIXME: make configurable
693 reverse_chronological = True
694 )
695
696 self.BeginBatch()
697
698 # rows
699 self.AppendRows(numRows = len(row_labels))
700 for row_idx in range(len(row_labels)):
701 self.SetRowLabelValue(row_idx, row_labels[row_idx])
702
703 # columns
704 self.AppendCols(numCols = len(column_labels))
705 for date_idx in range(len(column_labels)):
706 self.SetColLabelValue(date_idx, column_labels[date_idx])
707
708 # cell values (list of test results)
709 for result in results:
710 row = row_labels.index(u'%s%s' % (
711 gmTools.bool2subst(result['is_fake_meta_type'], u'', gmTools.u_sum, u''),
712 result['unified_abbrev']
713 ))
714 col = column_labels.index(result['clin_when'].strftime(self.__date_format))
715
716 try:
717 self.__cell_data[col]
718 except KeyError:
719 self.__cell_data[col] = {}
720
721 # the tooltip always shows the youngest sub result details
722 if self.__cell_data[col].has_key(row):
723 self.__cell_data[col][row].append(result)
724 self.__cell_data[col][row].sort(key = lambda x: x['clin_when'], reverse = True)
725 else:
726 self.__cell_data[col][row] = [result]
727
728 # rebuild cell display string
729 vals2display = []
730 cell_has_out_of_bounds_value = False
731 for sub_result in self.__cell_data[col][row]:
732
733 if sub_result.is_considered_abnormal:
734 cell_has_out_of_bounds_value = True
735
736 abnormality_indicator = sub_result.formatted_abnormality_indicator
737 if abnormality_indicator is None:
738 abnormality_indicator = u''
739 if abnormality_indicator != u'':
740 abnormality_indicator = u' (%s)' % abnormality_indicator[:3]
741
742 missing_review = False
743 # warn on missing review if
744 # a) no review at all exists or
745 if not sub_result['reviewed']:
746 missing_review = True
747 # b) there is a review but
748 else:
749 # current user is reviewer and hasn't reviewed
750 if sub_result['you_are_responsible'] and not sub_result['review_by_you']:
751 missing_review = True
752
753 # can we display the full sub_result length ?
754 if len(sub_result['unified_val']) > 8:
755 tmp = u'%.7s%s' % (sub_result['unified_val'][:7], gmTools.u_ellipsis)
756 else:
757 tmp = u'%.8s' % sub_result['unified_val'][:8]
758
759 # abnormal ?
760 tmp = u'%s%.6s' % (tmp, abnormality_indicator)
761
762 # is there a comment ?
763 has_sub_result_comment = gmTools.coalesce (
764 gmTools.coalesce(sub_result['note_test_org'], sub_result['comment']),
765 u''
766 ).strip() != u''
767 if has_sub_result_comment:
768 tmp = u'%s %s' % (tmp, gmTools.u_ellipsis)
769
770 # lacking a review ?
771 if missing_review:
772 tmp = u'%s %s' % (tmp, gmTools.u_writing_hand)
773 else:
774 if sub_result['is_clinically_relevant']:
775 tmp += u' !'
776
777 # part of a multi-result cell ?
778 if len(self.__cell_data[col][row]) > 1:
779 tmp = u'%s %s' % (sub_result['clin_when'].strftime('%H:%M'), tmp)
780
781 vals2display.append(tmp)
782
783 self.SetCellValue(row, col, u'\n'.join(vals2display))
784 self.SetCellAlignment(row, col, horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE)
785 # We used to color text in cells holding abnormals
786 # in firebrick red but that would color ALL text (including
787 # normals) and not only the abnormals within that
788 # cell. Shading, however, only says that *something*
789 # inside that cell is worthy of attention.
790 #if sub_result_relevant:
791 # font = self.GetCellFont(row, col)
792 # self.SetCellTextColour(row, col, 'firebrick')
793 # font.SetWeight(wx.FONTWEIGHT_BOLD)
794 # self.SetCellFont(row, col, font)
795 if cell_has_out_of_bounds_value:
796 self.SetCellBackgroundColour(row, col, 'cornflower blue')
797
798 self.AutoSize()
799 self.EndBatch()
800 return
801 #------------------------------------------------------------
803 emr = self.__patient.get_emr()
804
805 self.__row_label_data = emr.get_test_types_for_results()
806 test_type_labels = [ u'%s%s' % (
807 gmTools.bool2subst(test['is_fake_meta_type'], u'', gmTools.u_sum, u''),
808 test['unified_abbrev']
809 ) for test in self.__row_label_data
810 ]
811 if len(test_type_labels) == 0:
812 return
813
814 test_date_labels = [ date[0].strftime(self.__date_format) for date in emr.get_dates_for_results() ]
815 results = emr.get_test_results_by_date()
816
817 self.BeginBatch()
818
819 # rows
820 self.AppendRows(numRows = len(test_type_labels))
821 for row_idx in range(len(test_type_labels)):
822 self.SetRowLabelValue(row_idx, test_type_labels[row_idx])
823
824 # columns
825 self.AppendCols(numCols = len(test_date_labels))
826 for date_idx in range(len(test_date_labels)):
827 self.SetColLabelValue(date_idx, test_date_labels[date_idx])
828
829 # cell values (list of test results)
830 for result in results:
831 row = test_type_labels.index(u'%s%s' % (
832 gmTools.bool2subst(result['is_fake_meta_type'], u'', gmTools.u_sum, u''),
833 result['unified_abbrev']
834 ))
835 col = test_date_labels.index(result['clin_when'].strftime(self.__date_format))
836
837 try:
838 self.__cell_data[col]
839 except KeyError:
840 self.__cell_data[col] = {}
841
842 # the tooltip always shows the youngest sub result details
843 if self.__cell_data[col].has_key(row):
844 self.__cell_data[col][row].append(result)
845 self.__cell_data[col][row].sort(key = lambda x: x['clin_when'], reverse = True)
846 else:
847 self.__cell_data[col][row] = [result]
848
849 # rebuild cell display string
850 vals2display = []
851 cell_has_out_of_bounds_value = False
852 for sub_result in self.__cell_data[col][row]:
853
854 #if (sub_result.is_considered_lowered is True) or (sub_result.is_considered_elevated is True):
855 if sub_result.is_considered_abnormal:
856 cell_has_out_of_bounds_value = True
857
858 abnormality_indicator = sub_result.formatted_abnormality_indicator
859 if abnormality_indicator is None:
860 abnormality_indicator = u''
861 if abnormality_indicator != u'':
862 abnormality_indicator = u' (%s)' % abnormality_indicator[:3]
863
864 # is the sub_result relevant clinically ?
865 # FIXME: take into account primary_GP once we support that
866 sub_result_relevant = sub_result['is_clinically_relevant']
867 if sub_result_relevant is None:
868 # FIXME: calculate from clinical range
869 sub_result_relevant = False
870
871 missing_review = False
872 # warn on missing review if
873 # a) no review at all exists or
874 if not sub_result['reviewed']:
875 missing_review = True
876 # b) there is a review but
877 else:
878 # current user is reviewer and hasn't reviewed
879 if sub_result['you_are_responsible'] and not sub_result['review_by_you']:
880 missing_review = True
881
882 # can we display the full sub_result length ?
883 if len(sub_result['unified_val']) > 8:
884 tmp = u'%.7s%s' % (sub_result['unified_val'][:7], gmTools.u_ellipsis)
885 else:
886 tmp = u'%.8s' % sub_result['unified_val'][:8]
887
888 # abnormal ?
889 tmp = u'%s%.6s' % (tmp, abnormality_indicator)
890
891 # is there a comment ?
892 has_sub_result_comment = gmTools.coalesce (
893 gmTools.coalesce(sub_result['note_test_org'], sub_result['comment']),
894 u''
895 ).strip() != u''
896 if has_sub_result_comment:
897 tmp = u'%s %s' % (tmp, gmTools.u_ellipsis)
898
899 # lacking a review ?
900 if missing_review:
901 tmp = u'%s %s' % (tmp, gmTools.u_writing_hand)
902
903 # part of a multi-result cell ?
904 if len(self.__cell_data[col][row]) > 1:
905 tmp = u'%s %s' % (sub_result['clin_when'].strftime('%H:%M'), tmp)
906
907 vals2display.append(tmp)
908
909 self.SetCellValue(row, col, u'\n'.join(vals2display))
910 self.SetCellAlignment(row, col, horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE)
911 # FIXME: what about partial sub results being relevant ??
912 if sub_result_relevant:
913 font = self.GetCellFont(row, col)
914 self.SetCellTextColour(row, col, 'firebrick')
915 font.SetWeight(wx.FONTWEIGHT_BOLD)
916 self.SetCellFont(row, col, font)
917 if cell_has_out_of_bounds_value:
918 self.SetCellBackgroundColour(row, col, 'cornflower blue')
919
920 self.AutoSize()
921 self.EndBatch()
922 return
923 #------------------------------------------------------------
925 self.BeginBatch()
926 self.ClearGrid()
927 # Windows cannot do nothing, it rather decides to assert()
928 # on thinking it is supposed to do nothing
929 if self.GetNumberRows() > 0:
930 self.DeleteRows(pos = 0, numRows = self.GetNumberRows())
931 if self.GetNumberCols() > 0:
932 self.DeleteCols(pos = 0, numCols = self.GetNumberCols())
933 self.EndBatch()
934 self.__cell_data = {}
935 self.__row_label_data = []
936 #------------------------------------------------------------
938 # include details about test types included ?
939
940 # sometimes, for some reason, there is no row and
941 # wxPython still tries to find a tooltip for it
942 try:
943 tt = self.__row_label_data[row]
944 except IndexError:
945 return u' '
946
947 if tt['is_fake_meta_type']:
948 return tt.format(patient = self.__patient.ID)
949
950 meta_tt = tt.meta_test_type
951 txt = meta_tt.format(with_tests = True, patient = self.__patient.ID)
952
953 return txt
954 #------------------------------------------------------------
956 try:
957 d = self.__cell_data[col][row]
958 except KeyError:
959 # FIXME: maybe display the most recent or when the most recent was ?
960 d = None
961
962 if d is None:
963 return u' '
964
965 is_multi_cell = False
966 if len(d) > 1:
967 is_multi_cell = True
968 d = d[0]
969
970 tt = u''
971 # header
972 if is_multi_cell:
973 tt += _(u'Details of most recent (topmost) result ! \n')
974 tt += d.format(with_review = True, with_evaluation = True, with_ranges = True)
975 return tt
976 #------------------------------------------------------------
977 # internal helpers
978 #------------------------------------------------------------
980 self.CreateGrid(0, 1)
981 self.EnableEditing(0)
982 self.EnableDragGridSize(1)
983 self.SetMinSize(wx.DefaultSize)
984
985 # column labels
986 # setting this screws up the labels: they are cut off and displaced
987 #self.SetColLabelAlignment(wx.ALIGN_CENTER, wx.ALIGN_BOTTOM)
988
989 # row labels
990 self.SetRowLabelSize(wx.grid.GRID_AUTOSIZE) # starting with 2.8.8
991 #self.SetRowLabelSize(150)
992 self.SetRowLabelAlignment(horiz = wx.ALIGN_LEFT, vert = wx.ALIGN_CENTRE)
993 font = self.GetLabelFont()
994 font.SetWeight(wx.FONTWEIGHT_LIGHT)
995 self.SetLabelFont(font)
996
997 # add link to left upper corner
998 dbcfg = gmCfg.cCfgSQL()
999 url = dbcfg.get2 (
1000 option = u'external.urls.measurements_encyclopedia',
1001 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
1002 bias = 'user',
1003 default = u'http://www.laborlexikon.de'
1004 )
1005
1006 self.__WIN_corner = self.GetGridCornerLabelWindow() # a wx.Window instance
1007
1008 LNK_lab = wx.lib.hyperlink.HyperLinkCtrl (
1009 self.__WIN_corner,
1010 -1,
1011 label = _('Tests'),
1012 style = wx.HL_DEFAULT_STYLE # wx.TE_READONLY|wx.TE_CENTRE| wx.NO_BORDER |
1013 )
1014 LNK_lab.SetURL(url)
1015 LNK_lab.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BACKGROUND))
1016 LNK_lab.SetToolTipString(_(
1017 'Navigate to an encyclopedia of measurements\n'
1018 'and test methods on the web.\n'
1019 '\n'
1020 ' <%s>'
1021 ) % url)
1022
1023 SZR_inner = wx.BoxSizer(wx.HORIZONTAL)
1024 SZR_inner.Add((20, 20), 1, wx.EXPAND, 0) # spacer
1025 SZR_inner.Add(LNK_lab, 0, wx.ALIGN_CENTER_VERTICAL, 0) #wx.ALIGN_CENTER wx.EXPAND
1026 SZR_inner.Add((20, 20), 1, wx.EXPAND, 0) # spacer
1027
1028 SZR_corner = wx.BoxSizer(wx.VERTICAL)
1029 SZR_corner.Add((20, 20), 1, wx.EXPAND, 0) # spacer
1030 SZR_corner.AddWindow(SZR_inner, 0, wx.EXPAND) # inner sizer with centered hyperlink
1031 SZR_corner.Add((20, 20), 1, wx.EXPAND, 0) # spacer
1032
1033 self.__WIN_corner.SetSizer(SZR_corner)
1034 SZR_corner.Fit(self.__WIN_corner)
1035 #------------------------------------------------------------
1038 #------------------------------------------------------------
1039 - def __cells_to_data(self, cells=None, exclude_multi_cells=False, auto_include_multi_cells=False):
1040 """List of <cells> must be in row / col order."""
1041 data = []
1042 for row, col in cells:
1043 try:
1044 # cell data is stored col / row
1045 data_list = self.__cell_data[col][row]
1046 except KeyError:
1047 continue
1048
1049 if len(data_list) == 1:
1050 data.append(data_list[0])
1051 continue
1052
1053 if exclude_multi_cells:
1054 gmDispatcher.send(signal = u'statustext', msg = _('Excluding multi-result field from further processing.'))
1055 continue
1056
1057 if auto_include_multi_cells:
1058 data.extend(data_list)
1059 continue
1060
1061 data_to_include = self.__get_choices_from_multi_cell(cell_data = data_list)
1062 if data_to_include is None:
1063 continue
1064 data.extend(data_to_include)
1065
1066 return data
1067 #------------------------------------------------------------
1069 data = gmListWidgets.get_choices_from_list (
1070 parent = self,
1071 msg = _(
1072 'Your selection includes a field with multiple results.\n'
1073 '\n'
1074 'Please select the individual results you want to work on:'
1075 ),
1076 caption = _('Selecting test results'),
1077 choices = [ [d['clin_when'], u'%s: %s' % (d['abbrev_tt'], d['name_tt']), d['unified_val']] for d in cell_data ],
1078 columns = [ _('Date / Time'), _('Test'), _('Result') ],
1079 data = cell_data,
1080 single_selection = single_selection
1081 )
1082 return data
1083 #------------------------------------------------------------
1084 # event handling
1085 #------------------------------------------------------------
1087 # dynamic tooltips: GridWindow, GridRowLabelWindow, GridColLabelWindow, GridCornerLabelWindow
1088 self.GetGridWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_cells)
1089 self.GetGridRowLabelWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_row_labels)
1090 #self.GetGridColLabelWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_col_labels)
1091
1092 # sizing left upper corner window
1093 self.Bind(wx.EVT_SIZE, self.__resize_corner_window)
1094
1095 # editing cells
1096 self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.__on_cell_left_dclicked)
1097 #------------------------------------------------------------
1099 col = evt.GetCol()
1100 row = evt.GetRow()
1101
1102 # empty cell, perhaps ?
1103 try:
1104 self.__cell_data[col][row]
1105 except KeyError:
1106 # FIXME: invoke editor for adding value for day of that column
1107 # FIMXE: and test of that row
1108 return
1109
1110 if len(self.__cell_data[col][row]) > 1:
1111 data = self.__get_choices_from_multi_cell(cell_data = self.__cell_data[col][row], single_selection = True)
1112 else:
1113 data = self.__cell_data[col][row][0]
1114
1115 if data is None:
1116 return
1117
1118 edit_measurement(parent = self, measurement = data, single_entry = True)
1119 #------------------------------------------------------------
1120 # def OnMouseMotionRowLabel(self, evt):
1121 # x, y = self.CalcUnscrolledPosition(evt.GetPosition())
1122 # row = self.YToRow(y)
1123 # label = self.table().GetRowHelpValue(row)
1124 # self.GetGridRowLabelWindow().SetToolTipString(label or "")
1125 # evt.Skip()
1127
1128 # Use CalcUnscrolledPosition() to get the mouse position within the
1129 # entire grid including what's offscreen
1130 x, y = self.CalcUnscrolledPosition(evt.GetX(), evt.GetY())
1131
1132 row = self.YToRow(y)
1133
1134 if self.__prev_label_row == row:
1135 return
1136
1137 self.__prev_label_row == row
1138
1139 evt.GetEventObject().SetToolTipString(self.get_row_tooltip(row = row))
1140 #------------------------------------------------------------
1141 # def OnMouseMotionColLabel(self, evt):
1142 # x, y = self.CalcUnscrolledPosition(evt.GetPosition())
1143 # col = self.XToCol(x)
1144 # label = self.table().GetColHelpValue(col)
1145 # self.GetGridColLabelWindow().SetToolTipString(label or "")
1146 # evt.Skip()
1147 #------------------------------------------------------------
1149 """Calculate where the mouse is and set the tooltip dynamically."""
1150
1151 # Use CalcUnscrolledPosition() to get the mouse position within the
1152 # entire grid including what's offscreen
1153 x, y = self.CalcUnscrolledPosition(evt.GetX(), evt.GetY())
1154
1155 # use this logic to prevent tooltips outside the actual cells
1156 # apply to GetRowSize, too
1157 # tot = 0
1158 # for col in xrange(self.NumberCols):
1159 # tot += self.GetColSize(col)
1160 # if xpos <= tot:
1161 # self.tool_tip.Tip = 'Tool tip for Column %s' % (
1162 # self.GetColLabelValue(col))
1163 # break
1164 # else: # mouse is in label area beyond the right-most column
1165 # self.tool_tip.Tip = ''
1166
1167 row, col = self.XYToCell(x, y)
1168
1169 if (row == self.__prev_row) and (col == self.__prev_col):
1170 return
1171
1172 self.__prev_row = row
1173 self.__prev_col = col
1174
1175 evt.GetEventObject().SetToolTipString(self.get_cell_tooltip(col=col, row=row))
1176 #------------------------------------------------------------
1177 # properties
1178 #------------------------------------------------------------
1182
1183 patient = property(lambda x:x, _set_patient)
1184 #------------------------------------------------------------
1188
1189 panel_to_show = property(lambda x:x, _set_panel_to_show)
1190 #------------------------------------------------------------
1194
1195 show_by_panel = property(lambda x:x, _set_show_by_panel)
1196 #================================================================
1197 from Gnumed.wxGladeWidgets import wxgMeasurementsPnl
1198
1199 -class cMeasurementsPnl(wxgMeasurementsPnl.wxgMeasurementsPnl, gmRegetMixin.cRegetOnPaintMixin):
1200 """Panel holding a grid with lab data. Used as notebook page."""
1201
1203
1204 wxgMeasurementsPnl.wxgMeasurementsPnl.__init__(self, *args, **kwargs)
1205 gmRegetMixin.cRegetOnPaintMixin.__init__(self)
1206 self.__init_ui()
1207 self.__register_interests()
1208 #--------------------------------------------------------
1209 # event handling
1210 #--------------------------------------------------------
1212 gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._on_pre_patient_selection)
1213 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._on_post_patient_selection)
1214 gmDispatcher.connect(signal = u'clin.test_result_mod_db', receiver = self._schedule_data_reget)
1215 gmDispatcher.connect(signal = u'clin.reviewed_test_results_mod_db', receiver = self._schedule_data_reget)
1216 #--------------------------------------------------------
1219 #--------------------------------------------------------
1222 #--------------------------------------------------------
1225 #--------------------------------------------------------
1229 #--------------------------------------------------------
1232 #--------------------------------------------------------
1236 #--------------------------------------------------------
1239 #--------------------------------------------------------
1245 #--------------------------------------------------------
1248 #--------------------------------------------------------
1250 self.data_grid.sign_current_selection()
1251 #--------------------------------------------------------
1253 self.data_grid.plot_current_selection()
1254 #--------------------------------------------------------
1256 self.data_grid.delete_current_selection()
1257 #--------------------------------------------------------
1260 #--------------------------------------------------------
1262 if panel is None:
1263 self._TCTRL_panel_comment.SetValue(u'')
1264 self.panel_data_grid.panel_to_show = None
1265 self.panel_data_grid.Hide()
1266 else:
1267 pnl = self._PRW_panel.GetData(as_instance = True)
1268 self._TCTRL_panel_comment.SetValue(gmTools.coalesce (
1269 pnl['comment'],
1270 u''
1271 ))
1272 self.panel_data_grid.panel_to_show = pnl
1273 self.panel_data_grid.Show()
1274 self.Layout()
1275 #self.Refresh()
1276 #--------------------------------------------------------
1279 #--------------------------------------------------------
1281 self._TCTRL_panel_comment.SetValue(u'')
1282 if self._PRW_panel.GetValue().strip() == u'':
1283 self.panel_data_grid.panel_to_show = None
1284 self.panel_data_grid.Hide()
1285 self.Layout()
1286 #--------------------------------------------------------
1287 # internal API
1288 #--------------------------------------------------------
1290 self.__action_button_popup = wx.Menu(title = _('Perform on selected results:'))
1291
1292 menu_id = wx.NewId()
1293 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('Review and &sign')))
1294 wx.EVT_MENU(self.__action_button_popup, menu_id, self.__on_sign_current_selection)
1295
1296 menu_id = wx.NewId()
1297 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('Plot')))
1298 wx.EVT_MENU(self.__action_button_popup, menu_id, self.__on_plot_current_selection)
1299
1300 menu_id = wx.NewId()
1301 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('Export to &file')))
1302 #wx.EVT_MENU(self.__action_button_popup, menu_id, self.data_grid.current_selection_to_file)
1303 self.__action_button_popup.Enable(id = menu_id, enable = False)
1304
1305 menu_id = wx.NewId()
1306 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('Export to &clipboard')))
1307 #wx.EVT_MENU(self.__action_button_popup, menu_id, self.data_grid.current_selection_to_clipboard)
1308 self.__action_button_popup.Enable(id = menu_id, enable = False)
1309
1310 menu_id = wx.NewId()
1311 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('&Delete')))
1312 wx.EVT_MENU(self.__action_button_popup, menu_id, self.__on_delete_current_selection)
1313
1314 # FIXME: create inbox message to staff to phone patient to come in
1315 # FIXME: generate and let edit a SOAP narrative and include the values
1316
1317 self._PRW_panel.add_callback_on_selection(callback = self._on_panel_selected)
1318 self._PRW_panel.add_callback_on_modified(callback = self._on_panel_selection_modified)
1319
1320 self.panel_data_grid.show_by_panel = True
1321 self.panel_data_grid.panel_to_show = None
1322 self.panel_data_grid.Hide()
1323 self.Layout()
1324
1325 self._PRW_panel.SetFocus()
1326 #--------------------------------------------------------
1327 # reget mixin API
1328 #--------------------------------------------------------
1339
1340 #================================================================
1341 # editing widgets
1342 #================================================================
1344
1345 if tests is None:
1346 return True
1347
1348 if len(tests) == 0:
1349 return True
1350
1351 if parent is None:
1352 parent = wx.GetApp().GetTopWindow()
1353
1354 if len(tests) > 10:
1355 test_count = len(tests)
1356 tests2show = None
1357 else:
1358 test_count = None
1359 tests2show = tests
1360 if len(tests) == 0:
1361 return True
1362
1363 dlg = cMeasurementsReviewDlg(parent, -1, tests = tests, test_count = test_count)
1364 decision = dlg.ShowModal()
1365 if decision != wx.ID_APPLY:
1366 return True
1367
1368 wx.BeginBusyCursor()
1369 if dlg._RBTN_confirm_abnormal.GetValue():
1370 abnormal = None
1371 elif dlg._RBTN_results_normal.GetValue():
1372 abnormal = False
1373 else:
1374 abnormal = True
1375
1376 if dlg._RBTN_confirm_relevance.GetValue():
1377 relevant = None
1378 elif dlg._RBTN_results_not_relevant.GetValue():
1379 relevant = False
1380 else:
1381 relevant = True
1382
1383 comment = None
1384 if len(tests) == 1:
1385 comment = dlg._TCTRL_comment.GetValue()
1386
1387 make_responsible = dlg._CHBOX_responsible.IsChecked()
1388 dlg.Destroy()
1389
1390 for test in tests:
1391 test.set_review (
1392 technically_abnormal = abnormal,
1393 clinically_relevant = relevant,
1394 comment = comment,
1395 make_me_responsible = make_responsible
1396 )
1397 wx.EndBusyCursor()
1398
1399 return True
1400 #----------------------------------------------------------------
1401 from Gnumed.wxGladeWidgets import wxgMeasurementsReviewDlg
1402
1404
1406
1407 try:
1408 tests = kwargs['tests']
1409 del kwargs['tests']
1410 test_count = len(tests)
1411 try: del kwargs['test_count']
1412 except KeyError: pass
1413 except KeyError:
1414 tests = None
1415 test_count = kwargs['test_count']
1416 del kwargs['test_count']
1417
1418 wxgMeasurementsReviewDlg.wxgMeasurementsReviewDlg.__init__(self, *args, **kwargs)
1419
1420 if tests is None:
1421 msg = _('%s results selected. Too many to list individually.') % test_count
1422 else:
1423 msg = '\n'.join (
1424 [ u'%s: %s %s (%s)' % (
1425 t['unified_abbrev'],
1426 t['unified_val'],
1427 t['val_unit'],
1428 gmDateTime.pydt_strftime(t['clin_when'], '%Y %b %d')
1429 ) for t in tests
1430 ]
1431 )
1432
1433 self._LBL_tests.SetLabel(msg)
1434
1435 if test_count == 1:
1436 self._TCTRL_comment.Enable(True)
1437 self._TCTRL_comment.SetValue(gmTools.coalesce(tests[0]['review_comment'], u''))
1438 if tests[0]['you_are_responsible']:
1439 self._CHBOX_responsible.Enable(False)
1440
1441 self.Fit()
1442 #--------------------------------------------------------
1443 # event handling
1444 #--------------------------------------------------------
1450 #================================================================
1451 from Gnumed.wxGladeWidgets import wxgMeasurementEditAreaPnl
1452
1453 -class cMeasurementEditAreaPnl(wxgMeasurementEditAreaPnl.wxgMeasurementEditAreaPnl, gmEditArea.cGenericEditAreaMixin):
1454 """This edit area saves *new* measurements into the active patient only."""
1455
1457
1458 try:
1459 self.__default_date = kwargs['date']
1460 del kwargs['date']
1461 except KeyError:
1462 self.__default_date = None
1463
1464 wxgMeasurementEditAreaPnl.wxgMeasurementEditAreaPnl.__init__(self, *args, **kwargs)
1465 gmEditArea.cGenericEditAreaMixin.__init__(self)
1466
1467 self.__register_interests()
1468
1469 self.successful_save_msg = _('Successfully saved measurement.')
1470
1471 self._DPRW_evaluated.display_accuracy = gmDateTime.acc_minutes
1472 #--------------------------------------------------------
1473 # generic edit area mixin API
1474 #--------------------------------------------------------
1476 self._PRW_test.SetText(u'', None, True)
1477 self.__refresh_loinc_info()
1478 self.__refresh_previous_value()
1479 self.__update_units_context()
1480 self._TCTRL_result.SetValue(u'')
1481 self._PRW_units.SetText(u'', None, True)
1482 self._PRW_abnormality_indicator.SetText(u'', None, True)
1483 if self.__default_date is None:
1484 self._DPRW_evaluated.SetData(data = pyDT.datetime.now(tz = gmDateTime.gmCurrentLocalTimezone))
1485 else:
1486 self._DPRW_evaluated.SetData(data = None)
1487 self._TCTRL_note_test_org.SetValue(u'')
1488 self._PRW_intended_reviewer.SetData(gmStaff.gmCurrentProvider()['pk_staff'])
1489 self._PRW_problem.SetData()
1490 self._TCTRL_narrative.SetValue(u'')
1491 self._CHBOX_review.SetValue(False)
1492 self._CHBOX_abnormal.SetValue(False)
1493 self._CHBOX_relevant.SetValue(False)
1494 self._CHBOX_abnormal.Enable(False)
1495 self._CHBOX_relevant.Enable(False)
1496 self._TCTRL_review_comment.SetValue(u'')
1497 self._TCTRL_normal_min.SetValue(u'')
1498 self._TCTRL_normal_max.SetValue(u'')
1499 self._TCTRL_normal_range.SetValue(u'')
1500 self._TCTRL_target_min.SetValue(u'')
1501 self._TCTRL_target_max.SetValue(u'')
1502 self._TCTRL_target_range.SetValue(u'')
1503 self._TCTRL_norm_ref_group.SetValue(u'')
1504
1505 self._PRW_test.SetFocus()
1506 #--------------------------------------------------------
1508 self._PRW_test.SetData(data = self.data['pk_test_type'])
1509 self.__refresh_loinc_info()
1510 self.__refresh_previous_value()
1511 self.__update_units_context()
1512 self._TCTRL_result.SetValue(self.data['unified_val'])
1513 self._PRW_units.SetText(self.data['val_unit'], self.data['val_unit'], True)
1514 self._PRW_abnormality_indicator.SetText (
1515 gmTools.coalesce(self.data['abnormality_indicator'], u''),
1516 gmTools.coalesce(self.data['abnormality_indicator'], u''),
1517 True
1518 )
1519 self._DPRW_evaluated.SetData(data = self.data['clin_when'])
1520 self._TCTRL_note_test_org.SetValue(gmTools.coalesce(self.data['note_test_org'], u''))
1521 self._PRW_intended_reviewer.SetData(self.data['pk_intended_reviewer'])
1522 self._PRW_problem.SetData(self.data['pk_episode'])
1523 self._TCTRL_narrative.SetValue(gmTools.coalesce(self.data['comment'], u''))
1524 self._CHBOX_review.SetValue(False)
1525 self._CHBOX_abnormal.SetValue(gmTools.coalesce(self.data['is_technically_abnormal'], False))
1526 self._CHBOX_relevant.SetValue(gmTools.coalesce(self.data['is_clinically_relevant'], False))
1527 self._CHBOX_abnormal.Enable(False)
1528 self._CHBOX_relevant.Enable(False)
1529 self._TCTRL_review_comment.SetValue(gmTools.coalesce(self.data['review_comment'], u''))
1530 self._TCTRL_normal_min.SetValue(unicode(gmTools.coalesce(self.data['val_normal_min'], u'')))
1531 self._TCTRL_normal_max.SetValue(unicode(gmTools.coalesce(self.data['val_normal_max'], u'')))
1532 self._TCTRL_normal_range.SetValue(gmTools.coalesce(self.data['val_normal_range'], u''))
1533 self._TCTRL_target_min.SetValue(unicode(gmTools.coalesce(self.data['val_target_min'], u'')))
1534 self._TCTRL_target_max.SetValue(unicode(gmTools.coalesce(self.data['val_target_max'], u'')))
1535 self._TCTRL_target_range.SetValue(gmTools.coalesce(self.data['val_target_range'], u''))
1536 self._TCTRL_norm_ref_group.SetValue(gmTools.coalesce(self.data['norm_ref_group'], u''))
1537
1538 self._TCTRL_result.SetFocus()
1539 #--------------------------------------------------------
1541 self._PRW_test.SetText(u'', None, True)
1542 self.__refresh_loinc_info()
1543 self.__refresh_previous_value()
1544 self.__update_units_context()
1545 self._TCTRL_result.SetValue(u'')
1546 self._PRW_units.SetText(u'', None, True)
1547 self._PRW_abnormality_indicator.SetText(u'', None, True)
1548 self._DPRW_evaluated.SetData(data = self.data['clin_when'])
1549 self._TCTRL_note_test_org.SetValue(u'')
1550 self._PRW_intended_reviewer.SetData(self.data['pk_intended_reviewer'])
1551 self._PRW_problem.SetData(self.data['pk_episode'])
1552 self._TCTRL_narrative.SetValue(u'')
1553 self._CHBOX_review.SetValue(False)
1554 self._CHBOX_abnormal.SetValue(False)
1555 self._CHBOX_relevant.SetValue(False)
1556 self._CHBOX_abnormal.Enable(False)
1557 self._CHBOX_relevant.Enable(False)
1558 self._TCTRL_review_comment.SetValue(u'')
1559 self._TCTRL_normal_min.SetValue(u'')
1560 self._TCTRL_normal_max.SetValue(u'')
1561 self._TCTRL_normal_range.SetValue(u'')
1562 self._TCTRL_target_min.SetValue(u'')
1563 self._TCTRL_target_max.SetValue(u'')
1564 self._TCTRL_target_range.SetValue(u'')
1565 self._TCTRL_norm_ref_group.SetValue(u'')
1566
1567 self._PRW_test.SetFocus()
1568 #--------------------------------------------------------
1570
1571 validity = True
1572
1573 if not self._DPRW_evaluated.is_valid_timestamp():
1574 self._DPRW_evaluated.display_as_valid(False)
1575 validity = False
1576 else:
1577 self._DPRW_evaluated.display_as_valid(True)
1578
1579 val = self._TCTRL_result.GetValue().strip()
1580 if val == u'':
1581 validity = False
1582 self.display_ctrl_as_valid(self._TCTRL_result, False)
1583 else:
1584 self.display_ctrl_as_valid(self._TCTRL_result, True)
1585 numeric, val = gmTools.input2decimal(val)
1586 if numeric:
1587 if self._PRW_units.GetValue().strip() == u'':
1588 self._PRW_units.display_as_valid(False)
1589 validity = False
1590 else:
1591 self._PRW_units.display_as_valid(True)
1592 else:
1593 self._PRW_units.display_as_valid(True)
1594
1595 if self._PRW_problem.GetValue().strip() == u'':
1596 self._PRW_problem.display_as_valid(False)
1597 validity = False
1598 else:
1599 self._PRW_problem.display_as_valid(True)
1600
1601 if self._PRW_test.GetValue().strip() == u'':
1602 self._PRW_test.display_as_valid(False)
1603 validity = False
1604 else:
1605 self._PRW_test.display_as_valid(True)
1606
1607 if self._PRW_intended_reviewer.GetData() is None:
1608 self._PRW_intended_reviewer.display_as_valid(False)
1609 validity = False
1610 else:
1611 self._PRW_intended_reviewer.display_as_valid(True)
1612
1613 ctrls = [self._TCTRL_normal_min, self._TCTRL_normal_max, self._TCTRL_target_min, self._TCTRL_target_max]
1614 for widget in ctrls:
1615 val = widget.GetValue().strip()
1616 if val == u'':
1617 continue
1618 try:
1619 decimal.Decimal(val.replace(',', u'.', 1))
1620 self.display_ctrl_as_valid(widget, True)
1621 except:
1622 validity = False
1623 self.display_ctrl_as_valid(widget, False)
1624
1625 if validity is False:
1626 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save result. Invalid or missing essential input.'))
1627
1628 return validity
1629 #--------------------------------------------------------
1631
1632 emr = gmPerson.gmCurrentPatient().get_emr()
1633
1634 success, result = gmTools.input2decimal(self._TCTRL_result.GetValue())
1635 if success:
1636 v_num = result
1637 v_al = None
1638 else:
1639 v_al = self._TCTRL_result.GetValue().strip()
1640 v_num = None
1641
1642 pk_type = self._PRW_test.GetData()
1643 if pk_type is None:
1644 tt = gmPathLab.create_measurement_type (
1645 lab = None,
1646 abbrev = self._PRW_test.GetValue().strip(),
1647 name = self._PRW_test.GetValue().strip(),
1648 unit = gmTools.coalesce(self._PRW_units.GetData(), self._PRW_units.GetValue()).strip()
1649 )
1650 pk_type = tt['pk_test_type']
1651
1652 tr = emr.add_test_result (
1653 episode = self._PRW_problem.GetData(can_create=True, is_open=False),
1654 type = pk_type,
1655 intended_reviewer = self._PRW_intended_reviewer.GetData(),
1656 val_num = v_num,
1657 val_alpha = v_al,
1658 unit = self._PRW_units.GetValue()
1659 )
1660
1661 tr['clin_when'] = self._DPRW_evaluated.GetData().get_pydt()
1662
1663 ctrls = [
1664 ('abnormality_indicator', self._PRW_abnormality_indicator),
1665 ('note_test_org', self._TCTRL_note_test_org),
1666 ('comment', self._TCTRL_narrative),
1667 ('val_normal_range', self._TCTRL_normal_range),
1668 ('val_target_range', self._TCTRL_target_range),
1669 ('norm_ref_group', self._TCTRL_norm_ref_group)
1670 ]
1671 for field, widget in ctrls:
1672 tr[field] = widget.GetValue().strip()
1673
1674 ctrls = [
1675 ('val_normal_min', self._TCTRL_normal_min),
1676 ('val_normal_max', self._TCTRL_normal_max),
1677 ('val_target_min', self._TCTRL_target_min),
1678 ('val_target_max', self._TCTRL_target_max)
1679 ]
1680 for field, widget in ctrls:
1681 val = widget.GetValue().strip()
1682 if val == u'':
1683 tr[field] = None
1684 else:
1685 tr[field] = decimal.Decimal(val.replace(',', u'.', 1))
1686
1687 tr.save_payload()
1688
1689 if self._CHBOX_review.GetValue() is True:
1690 tr.set_review (
1691 technically_abnormal = self._CHBOX_abnormal.GetValue(),
1692 clinically_relevant = self._CHBOX_relevant.GetValue(),
1693 comment = gmTools.none_if(self._TCTRL_review_comment.GetValue().strip(), u''),
1694 make_me_responsible = False
1695 )
1696
1697 self.data = tr
1698
1699 wx.CallAfter (
1700 plot_adjacent_measurements,
1701 test = self.data,
1702 plot_singular_result = False,
1703 use_default_template = True
1704 )
1705
1706 return True
1707 #--------------------------------------------------------
1709
1710 success, result = gmTools.input2decimal(self._TCTRL_result.GetValue())
1711 if success:
1712 v_num = result
1713 v_al = None
1714 else:
1715 v_num = None
1716 v_al = self._TCTRL_result.GetValue().strip()
1717
1718 pk_type = self._PRW_test.GetData()
1719 if pk_type is None:
1720 tt = gmPathLab.create_measurement_type (
1721 lab = None,
1722 abbrev = self._PRW_test.GetValue().strip(),
1723 name = self._PRW_test.GetValue().strip(),
1724 unit = gmTools.none_if(self._PRW_units.GetValue().strip(), u'')
1725 )
1726 pk_type = tt['pk_test_type']
1727
1728 tr = self.data
1729
1730 tr['pk_episode'] = self._PRW_problem.GetData(can_create=True, is_open=False)
1731 tr['pk_test_type'] = pk_type
1732 tr['pk_intended_reviewer'] = self._PRW_intended_reviewer.GetData()
1733 tr['val_num'] = v_num
1734 tr['val_alpha'] = v_al
1735 tr['val_unit'] = gmTools.coalesce(self._PRW_units.GetData(), self._PRW_units.GetValue()).strip()
1736 tr['clin_when'] = self._DPRW_evaluated.GetData().get_pydt()
1737
1738 ctrls = [
1739 ('abnormality_indicator', self._PRW_abnormality_indicator),
1740 ('note_test_org', self._TCTRL_note_test_org),
1741 ('comment', self._TCTRL_narrative),
1742 ('val_normal_range', self._TCTRL_normal_range),
1743 ('val_target_range', self._TCTRL_target_range),
1744 ('norm_ref_group', self._TCTRL_norm_ref_group)
1745 ]
1746 for field, widget in ctrls:
1747 tr[field] = widget.GetValue().strip()
1748
1749 ctrls = [
1750 ('val_normal_min', self._TCTRL_normal_min),
1751 ('val_normal_max', self._TCTRL_normal_max),
1752 ('val_target_min', self._TCTRL_target_min),
1753 ('val_target_max', self._TCTRL_target_max)
1754 ]
1755 for field, widget in ctrls:
1756 val = widget.GetValue().strip()
1757 if val == u'':
1758 tr[field] = None
1759 else:
1760 tr[field] = decimal.Decimal(val.replace(',', u'.', 1))
1761
1762 tr.save_payload()
1763
1764 if self._CHBOX_review.GetValue() is True:
1765 tr.set_review (
1766 technically_abnormal = self._CHBOX_abnormal.GetValue(),
1767 clinically_relevant = self._CHBOX_relevant.GetValue(),
1768 comment = gmTools.none_if(self._TCTRL_review_comment.GetValue().strip(), u''),
1769 make_me_responsible = False
1770 )
1771
1772 wx.CallAfter (
1773 plot_adjacent_measurements,
1774 test = self.data,
1775 plot_singular_result = False,
1776 use_default_template = True
1777 )
1778
1779 return True
1780 #--------------------------------------------------------
1781 # event handling
1782 #--------------------------------------------------------
1784 self._PRW_test.add_callback_on_lose_focus(self._on_leave_test_prw)
1785 self._PRW_abnormality_indicator.add_callback_on_lose_focus(self._on_leave_indicator_prw)
1786 self._PRW_units.add_callback_on_lose_focus(self._on_leave_unit_prw)
1787 #--------------------------------------------------------
1789 self.__refresh_loinc_info()
1790 self.__refresh_previous_value()
1791 self.__update_units_context()
1792 # only works if we've got a unit set
1793 self.__update_normal_range()
1794 self.__update_clinical_range()
1795 #--------------------------------------------------------
1797 # maybe we've got a unit now ?
1798 self.__update_normal_range()
1799 self.__update_clinical_range()
1800 #--------------------------------------------------------
1802 # if the user hasn't explicitly enabled reviewing
1803 if not self._CHBOX_review.GetValue():
1804 self._CHBOX_abnormal.SetValue(self._PRW_abnormality_indicator.GetValue().strip() != u'')
1805 #--------------------------------------------------------
1807 self._CHBOX_abnormal.Enable(self._CHBOX_review.GetValue())
1808 self._CHBOX_relevant.Enable(self._CHBOX_review.GetValue())
1809 self._TCTRL_review_comment.Enable(self._CHBOX_review.GetValue())
1810 #--------------------------------------------------------
1827 #--------------------------------------------------------
1828 # internal helpers
1829 #--------------------------------------------------------
1831
1832 if self._PRW_test.GetData() is None:
1833 self._PRW_units.unset_context(context = u'pk_type')
1834 self._PRW_units.unset_context(context = u'loinc')
1835 if self._PRW_test.GetValue().strip() == u'':
1836 self._PRW_units.unset_context(context = u'test_name')
1837 else:
1838 self._PRW_units.set_context(context = u'test_name', val = self._PRW_test.GetValue().strip())
1839 return
1840
1841 tt = self._PRW_test.GetData(as_instance = True)
1842
1843 self._PRW_units.set_context(context = u'pk_type', val = tt['pk_test_type'])
1844 self._PRW_units.set_context(context = u'test_name', val = tt['name'])
1845
1846 if tt['loinc'] is not None:
1847 self._PRW_units.set_context(context = u'loinc', val = tt['loinc'])
1848
1849 # closest unit
1850 if self._PRW_units.GetValue().strip() == u'':
1851 clin_when = self._DPRW_evaluated.GetData().get_pydt()
1852 if clin_when is None:
1853 unit = tt.temporally_closest_unit
1854 else:
1855 unit = tt.get_temporally_closest_unit(timestamp = clin_when)
1856 if unit is None:
1857 self._PRW_units.SetText(u'', unit, True)
1858 else:
1859 self._PRW_units.SetText(unit, unit, True)
1860
1861 #--------------------------------------------------------
1863 unit = self._PRW_units.GetValue().strip()
1864 if unit == u'':
1865 return
1866 if self._PRW_test.GetData() is None:
1867 return
1868 for ctrl in [self._TCTRL_normal_min, self._TCTRL_normal_max, self._TCTRL_normal_range, self._TCTRL_norm_ref_group]:
1869 if ctrl.GetValue().strip() != u'':
1870 return
1871 tt = self._PRW_test.GetData(as_instance = True)
1872 test_w_range = tt.get_temporally_closest_normal_range (
1873 unit,
1874 timestamp = self._DPRW_evaluated.GetData().get_pydt()
1875 )
1876 if test_w_range is None:
1877 return
1878 self._TCTRL_normal_min.SetValue(unicode(gmTools.coalesce(test_w_range['val_normal_min'], u'')))
1879 self._TCTRL_normal_max.SetValue(unicode(gmTools.coalesce(test_w_range['val_normal_max'], u'')))
1880 self._TCTRL_normal_range.SetValue(gmTools.coalesce(test_w_range['val_normal_range'], u''))
1881 self._TCTRL_norm_ref_group.SetValue(gmTools.coalesce(test_w_range['norm_ref_group'], u''))
1882
1883 #--------------------------------------------------------
1885 unit = self._PRW_units.GetValue().strip()
1886 if unit == u'':
1887 return
1888 if self._PRW_test.GetData() is None:
1889 return
1890 for ctrl in [self._TCTRL_target_min, self._TCTRL_target_max, self._TCTRL_target_range]:
1891 if ctrl.GetValue().strip() != u'':
1892 return
1893 tt = self._PRW_test.GetData(as_instance = True)
1894 test_w_range = tt.get_temporally_closest_target_range (
1895 unit,
1896 gmPerson.gmCurrentPatient().ID,
1897 timestamp = self._DPRW_evaluated.GetData().get_pydt()
1898 )
1899 if test_w_range is None:
1900 return
1901 self._TCTRL_target_min.SetValue(unicode(gmTools.coalesce(test_w_range['val_target_min'], u'')))
1902 self._TCTRL_target_max.SetValue(unicode(gmTools.coalesce(test_w_range['val_target_max'], u'')))
1903 self._TCTRL_target_range.SetValue(gmTools.coalesce(test_w_range['val_target_range'], u''))
1904
1905 #--------------------------------------------------------
1907
1908 self._TCTRL_loinc.SetValue(u'')
1909
1910 if self._PRW_test.GetData() is None:
1911 return
1912
1913 tt = self._PRW_test.GetData(as_instance = True)
1914
1915 if tt['loinc'] is None:
1916 return
1917
1918 info = gmLOINC.loinc2term(loinc = tt['loinc'])
1919 if len(info) == 0:
1920 self._TCTRL_loinc.SetValue(u'')
1921 return
1922
1923 self._TCTRL_loinc.SetValue(u'%s: %s' % (tt['loinc'], info[0]))
1924 #--------------------------------------------------------
1926 self._TCTRL_previous_value.SetValue(u'')
1927 # it doesn't make much sense to show the most
1928 # recent value when editing an existing one
1929 if self.data is not None:
1930 return
1931 if self._PRW_test.GetData() is None:
1932 return
1933 tt = self._PRW_test.GetData(as_instance = True)
1934 most_recent = tt.get_most_recent_results (
1935 no_of_results = 1,
1936 patient = gmPerson.gmCurrentPatient().ID
1937 )
1938 if most_recent is None:
1939 return
1940 self._TCTRL_previous_value.SetValue(_('%s ago: %s%s%s - %s%s') % (
1941 gmDateTime.format_interval_medically(gmDateTime.pydt_now_here() - most_recent['clin_when']),
1942 most_recent['unified_val'],
1943 most_recent['val_unit'],
1944 gmTools.coalesce(most_recent['abnormality_indicator'], u'', u' (%s)'),
1945 most_recent['abbrev_tt'],
1946 gmTools.coalesce(most_recent.formatted_range, u'', u' [%s]')
1947 ))
1948 self._TCTRL_previous_value.SetToolTipString(most_recent.format (
1949 with_review = True,
1950 with_evaluation = False,
1951 with_ranges = True,
1952 with_episode = True,
1953 with_type_details=True
1954 ))
1955
1956 #================================================================
1957 # measurement type handling
1958 #================================================================
1960
1961 if parent is None:
1962 parent = wx.GetApp().GetTopWindow()
1963
1964 if msg is None:
1965 msg = _('Pick the relevant measurement types.')
1966
1967 if right_column is None:
1968 right_columns = [_('Picked')]
1969 else:
1970 right_columns = [right_column]
1971
1972 picker = gmListWidgets.cItemPickerDlg(parent, -1, msg = msg)
1973 picker.set_columns(columns = [_('Known measurement types')], columns_right = right_columns)
1974 types = gmPathLab.get_measurement_types(order_by = 'unified_abbrev')
1975 picker.set_choices (
1976 choices = [
1977 u'%s: %s%s' % (
1978 t['unified_abbrev'],
1979 t['unified_name'],
1980 gmTools.coalesce(t['name_org'], u'', u' (%s)')
1981 )
1982 for t in types
1983 ],
1984 data = types
1985 )
1986 if picks is not None:
1987 picker.set_picks (
1988 picks = [
1989 u'%s: %s%s' % (
1990 p['unified_abbrev'],
1991 p['unified_name'],
1992 gmTools.coalesce(p['name_org'], u'', u' (%s)')
1993 )
1994 for p in picks
1995 ],
1996 data = picks
1997 )
1998 result = picker.ShowModal()
1999
2000 if result == wx.ID_CANCEL:
2001 picker.Destroy()
2002 return None
2003
2004 picks = picker.picks
2005 picker.Destroy()
2006 return picks
2007
2008 #----------------------------------------------------------------
2010
2011 if parent is None:
2012 parent = wx.GetApp().GetTopWindow()
2013
2014 #------------------------------------------------------------
2015 def edit(test_type=None):
2016 ea = cMeasurementTypeEAPnl(parent = parent, id = -1, type = test_type)
2017 dlg = gmEditArea.cGenericEditAreaDlg2 (
2018 parent = parent,
2019 id = -1,
2020 edit_area = ea,
2021 single_entry = gmTools.bool2subst((test_type is None), False, True)
2022 )
2023 dlg.SetTitle(gmTools.coalesce(test_type, _('Adding measurement type'), _('Editing measurement type')))
2024
2025 if dlg.ShowModal() == wx.ID_OK:
2026 dlg.Destroy()
2027 return True
2028
2029 dlg.Destroy()
2030 return False
2031 #------------------------------------------------------------
2032 def delete(measurement_type):
2033 if measurement_type.in_use:
2034 gmDispatcher.send (
2035 signal = 'statustext',
2036 beep = True,
2037 msg = _('Cannot delete measurement type [%s (%s)] because it is in use.') % (measurement_type['name'], measurement_type['abbrev'])
2038 )
2039 return False
2040 gmPathLab.delete_measurement_type(measurement_type = measurement_type['pk_test_type'])
2041 return True
2042 #------------------------------------------------------------
2043 def get_tooltip(test_type):
2044 return test_type.format()
2045 #------------------------------------------------------------
2046 def refresh(lctrl):
2047 mtypes = gmPathLab.get_measurement_types(order_by = 'name, abbrev')
2048 items = [ [
2049 m['abbrev'],
2050 m['name'],
2051 gmTools.coalesce(m['conversion_unit'], u''),
2052 gmTools.coalesce(m['loinc'], u''),
2053 gmTools.coalesce(m['comment_type'], u''),
2054 gmTools.coalesce(m['name_org'], u'?'),
2055 gmTools.coalesce(m['comment_org'], u''),
2056 m['pk_test_type']
2057 ] for m in mtypes ]
2058 lctrl.set_string_items(items)
2059 lctrl.set_data(mtypes)
2060 #------------------------------------------------------------
2061 msg = _(
2062 '\n'
2063 'These are the measurement types currently defined in GNUmed.\n'
2064 '\n'
2065 )
2066
2067 gmListWidgets.get_choices_from_list (
2068 parent = parent,
2069 msg = msg,
2070 caption = _('Showing measurement types.'),
2071 columns = [ _('Abbrev'), _('Name'), _('Unit'), _('LOINC'), _('Comment'), _('Org'), _('Comment'), u'#' ],
2072 single_selection = True,
2073 refresh_callback = refresh,
2074 edit_callback = edit,
2075 new_callback = edit,
2076 delete_callback = delete,
2077 list_tooltip_callback = get_tooltip
2078 )
2079
2080 #----------------------------------------------------------------
2082
2084
2085 query = u"""
2086 SELECT DISTINCT ON (field_label)
2087 pk_test_type AS data,
2088 name
2089 || ' ('
2090 || coalesce (
2091 (SELECT unit || ' @ ' || organization FROM clin.v_test_orgs c_vto WHERE c_vto.pk_test_org = c_vtt.pk_test_org),
2092 '%(in_house)s'
2093 )
2094 || ')'
2095 AS field_label,
2096 name
2097 || ' ('
2098 || abbrev || ', '
2099 || coalesce(abbrev_meta || ': ' || name_meta || ', ', '')
2100 || coalesce (
2101 (SELECT unit || ' @ ' || organization FROM clin.v_test_orgs c_vto WHERE c_vto.pk_test_org = c_vtt.pk_test_org),
2102 '%(in_house)s'
2103 )
2104 || ')'
2105 AS list_label
2106 FROM
2107 clin.v_test_types c_vtt
2108 WHERE
2109 abbrev_meta %%(fragment_condition)s
2110 OR
2111 name_meta %%(fragment_condition)s
2112 OR
2113 abbrev %%(fragment_condition)s
2114 OR
2115 name %%(fragment_condition)s
2116 ORDER BY field_label
2117 LIMIT 50""" % {'in_house': _('generic / in house lab')}
2118
2119 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
2120 mp.setThresholds(1, 2, 4)
2121 mp.word_separators = '[ \t:@]+'
2122 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
2123 self.matcher = mp
2124 self.SetToolTipString(_('Select the type of measurement.'))
2125 self.selection_only = False
2126 #------------------------------------------------------------
2128 if self.GetData() is None:
2129 return None
2130
2131 return gmPathLab.cMeasurementType(aPK_obj = self.GetData())
2132 #----------------------------------------------------------------
2133 from Gnumed.wxGladeWidgets import wxgMeasurementTypeEAPnl
2134
2135 -class cMeasurementTypeEAPnl(wxgMeasurementTypeEAPnl.wxgMeasurementTypeEAPnl, gmEditArea.cGenericEditAreaMixin):
2136
2138
2139 try:
2140 data = kwargs['type']
2141 del kwargs['type']
2142 except KeyError:
2143 data = None
2144
2145 wxgMeasurementTypeEAPnl.wxgMeasurementTypeEAPnl.__init__(self, *args, **kwargs)
2146 gmEditArea.cGenericEditAreaMixin.__init__(self)
2147 self.mode = 'new'
2148 self.data = data
2149 if data is not None:
2150 self.mode = 'edit'
2151
2152 self.__init_ui()
2153
2154 #----------------------------------------------------------------
2156
2157 # name phraseweel
2158 query = u"""
2159 select distinct on (name)
2160 pk,
2161 name
2162 from clin.test_type
2163 where
2164 name %(fragment_condition)s
2165 order by name
2166 limit 50"""
2167 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
2168 mp.setThresholds(1, 2, 4)
2169 self._PRW_name.matcher = mp
2170 self._PRW_name.selection_only = False
2171 self._PRW_name.add_callback_on_lose_focus(callback = self._on_name_lost_focus)
2172
2173 # abbreviation
2174 query = u"""
2175 select distinct on (abbrev)
2176 pk,
2177 abbrev
2178 from clin.test_type
2179 where
2180 abbrev %(fragment_condition)s
2181 order by abbrev
2182 limit 50"""
2183 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
2184 mp.setThresholds(1, 2, 3)
2185 self._PRW_abbrev.matcher = mp
2186 self._PRW_abbrev.selection_only = False
2187
2188 # unit
2189 self._PRW_conversion_unit.selection_only = False
2190
2191 # loinc
2192 mp = gmLOINC.cLOINCMatchProvider()
2193 mp.setThresholds(1, 2, 4)
2194 #mp.print_queries = True
2195 #mp.word_separators = '[ \t:@]+'
2196 self._PRW_loinc.matcher = mp
2197 self._PRW_loinc.selection_only = False
2198 self._PRW_loinc.add_callback_on_lose_focus(callback = self._on_loinc_lost_focus)
2199 #----------------------------------------------------------------
2201
2202 test = self._PRW_name.GetValue().strip()
2203
2204 if test == u'':
2205 self._PRW_conversion_unit.unset_context(context = u'test_name')
2206 return
2207
2208 self._PRW_conversion_unit.set_context(context = u'test_name', val = test)
2209 #----------------------------------------------------------------
2211 loinc = self._PRW_loinc.GetData()
2212
2213 if loinc is None:
2214 self._TCTRL_loinc_info.SetValue(u'')
2215 self._PRW_conversion_unit.unset_context(context = u'loinc')
2216 return
2217
2218 self._PRW_conversion_unit.set_context(context = u'loinc', val = loinc)
2219
2220 info = gmLOINC.loinc2term(loinc = loinc)
2221 if len(info) == 0:
2222 self._TCTRL_loinc_info.SetValue(u'')
2223 return
2224
2225 self._TCTRL_loinc_info.SetValue(info[0])
2226 #----------------------------------------------------------------
2227 # generic Edit Area mixin API
2228 #----------------------------------------------------------------
2230
2231 has_errors = False
2232 for field in [self._PRW_name, self._PRW_abbrev, self._PRW_conversion_unit]:
2233 if field.GetValue().strip() in [u'', None]:
2234 has_errors = True
2235 field.display_as_valid(valid = False)
2236 else:
2237 field.display_as_valid(valid = True)
2238 field.Refresh()
2239
2240 return (not has_errors)
2241 #----------------------------------------------------------------
2243
2244 pk_org = self._PRW_test_org.GetData()
2245 if pk_org is None:
2246 pk_org = gmPathLab.create_test_org (
2247 name = gmTools.none_if(self._PRW_test_org.GetValue().strip(), u'')
2248 )['pk_test_org']
2249
2250 tt = gmPathLab.create_measurement_type (
2251 lab = pk_org,
2252 abbrev = self._PRW_abbrev.GetValue().strip(),
2253 name = self._PRW_name.GetValue().strip(),
2254 unit = gmTools.coalesce (
2255 self._PRW_conversion_unit.GetData(),
2256 self._PRW_conversion_unit.GetValue()
2257 ).strip()
2258 )
2259 if self._PRW_loinc.GetData() is not None:
2260 tt['loinc'] = gmTools.none_if(self._PRW_loinc.GetData().strip(), u'')
2261 else:
2262 tt['loinc'] = gmTools.none_if(self._PRW_loinc.GetValue().strip(), u'')
2263 tt['comment_type'] = gmTools.none_if(self._TCTRL_comment_type.GetValue().strip(), u'')
2264 tt['pk_meta_test_type'] = self._PRW_meta_type.GetData()
2265
2266 tt.save()
2267
2268 self.data = tt
2269
2270 return True
2271 #----------------------------------------------------------------
2273
2274 pk_org = self._PRW_test_org.GetData()
2275 if pk_org is None:
2276 pk_org = gmPathLab.create_test_org (
2277 name = gmTools.none_if(self._PRW_test_org.GetValue().strip(), u'')
2278 )['pk_test_org']
2279
2280 self.data['pk_test_org'] = pk_org
2281 self.data['abbrev'] = self._PRW_abbrev.GetValue().strip()
2282 self.data['name'] = self._PRW_name.GetValue().strip()
2283 self.data['conversion_unit'] = gmTools.coalesce (
2284 self._PRW_conversion_unit.GetData(),
2285 self._PRW_conversion_unit.GetValue()
2286 ).strip()
2287 if self._PRW_loinc.GetData() is not None:
2288 self.data['loinc'] = gmTools.none_if(self._PRW_loinc.GetData().strip(), u'')
2289 if self._PRW_loinc.GetData() is not None:
2290 self.data['loinc'] = gmTools.none_if(self._PRW_loinc.GetData().strip(), u'')
2291 else:
2292 self.data['loinc'] = gmTools.none_if(self._PRW_loinc.GetValue().strip(), u'')
2293 self.data['comment_type'] = gmTools.none_if(self._TCTRL_comment_type.GetValue().strip(), u'')
2294 self.data['pk_meta_test_type'] = self._PRW_meta_type.GetData()
2295 self.data.save()
2296
2297 return True
2298 #----------------------------------------------------------------
2300 self._PRW_name.SetText(u'', None, True)
2301 self._on_name_lost_focus()
2302 self._PRW_abbrev.SetText(u'', None, True)
2303 self._PRW_conversion_unit.SetText(u'', None, True)
2304 self._PRW_loinc.SetText(u'', None, True)
2305 self._on_loinc_lost_focus()
2306 self._TCTRL_comment_type.SetValue(u'')
2307 self._PRW_test_org.SetText(u'', None, True)
2308 self._PRW_meta_type.SetText(u'', None, True)
2309
2310 self._PRW_name.SetFocus()
2311 #----------------------------------------------------------------
2313 self._PRW_name.SetText(self.data['name'], self.data['name'], True)
2314 self._on_name_lost_focus()
2315 self._PRW_abbrev.SetText(self.data['abbrev'], self.data['abbrev'], True)
2316 self._PRW_conversion_unit.SetText (
2317 gmTools.coalesce(self.data['conversion_unit'], u''),
2318 self.data['conversion_unit'],
2319 True
2320 )
2321 self._PRW_loinc.SetText (
2322 gmTools.coalesce(self.data['loinc'], u''),
2323 self.data['loinc'],
2324 True
2325 )
2326 self._on_loinc_lost_focus()
2327 self._TCTRL_comment_type.SetValue(gmTools.coalesce(self.data['comment_type'], u''))
2328 self._PRW_test_org.SetText (
2329 gmTools.coalesce(self.data['pk_test_org'], u'', self.data['name_org']),
2330 self.data['pk_test_org'],
2331 True
2332 )
2333 if self.data['pk_meta_test_type'] is None:
2334 self._PRW_meta_type.SetText(u'', None, True)
2335 else:
2336 self._PRW_meta_type.SetText(u'%s: %s' % (self.data['abbrev_meta'], self.data['name_meta']), self.data['pk_meta_test_type'], True)
2337
2338 self._PRW_name.SetFocus()
2339 #----------------------------------------------------------------
2348
2349 #================================================================
2350 _SQL_units_from_test_results = u"""
2351 -- via clin.v_test_results.pk_type (for types already used in results)
2352 SELECT
2353 val_unit AS data,
2354 val_unit AS field_label,
2355 val_unit || ' (' || name_tt || ')' AS list_label,
2356 1 AS rank
2357 FROM
2358 clin.v_test_results
2359 WHERE
2360 (
2361 val_unit %(fragment_condition)s
2362 OR
2363 conversion_unit %(fragment_condition)s
2364 )
2365 %(ctxt_type_pk)s
2366 %(ctxt_test_name)s
2367 """
2368
2369 _SQL_units_from_test_types = u"""
2370 -- via clin.test_type (for types not yet used in results)
2371 SELECT
2372 conversion_unit AS data,
2373 conversion_unit AS field_label,
2374 conversion_unit || ' (' || name || ')' AS list_label,
2375 2 AS rank
2376 FROM
2377 clin.test_type
2378 WHERE
2379 conversion_unit %(fragment_condition)s
2380 %(ctxt_ctt)s
2381 """
2382
2383 _SQL_units_from_loinc_ipcc = u"""
2384 -- via ref.loinc.ipcc_units
2385 SELECT
2386 ipcc_units AS data,
2387 ipcc_units AS field_label,
2388 ipcc_units || ' (LOINC.ipcc: ' || term || ')' AS list_label,
2389 3 AS rank
2390 FROM
2391 ref.loinc
2392 WHERE
2393 ipcc_units %(fragment_condition)s
2394 %(ctxt_loinc)s
2395 %(ctxt_loinc_term)s
2396 """
2397
2398 _SQL_units_from_loinc_submitted = u"""
2399 -- via ref.loinc.submitted_units
2400 SELECT
2401 submitted_units AS data,
2402 submitted_units AS field_label,
2403 submitted_units || ' (LOINC.submitted:' || term || ')' AS list_label,
2404 3 AS rank
2405 FROM
2406 ref.loinc
2407 WHERE
2408 submitted_units %(fragment_condition)s
2409 %(ctxt_loinc)s
2410 %(ctxt_loinc_term)s
2411 """
2412
2413 _SQL_units_from_loinc_example = u"""
2414 -- via ref.loinc.example_units
2415 SELECT
2416 example_units AS data,
2417 example_units AS field_label,
2418 example_units || ' (LOINC.example: ' || term || ')' AS list_label,
2419 3 AS rank
2420 FROM
2421 ref.loinc
2422 WHERE
2423 example_units %(fragment_condition)s
2424 %(ctxt_loinc)s
2425 %(ctxt_loinc_term)s
2426 """
2427
2428 _SQL_units_from_atc = u"""
2429 -- via ref.atc.unit
2430 SELECT
2431 unit AS data,
2432 unit AS field_label,
2433 unit || ' (ATC: ' || term || ')' AS list_label,
2434 2 AS rank
2435 FROM
2436 ref.atc
2437 WHERE
2438 unit IS NOT NULL
2439 AND
2440 unit %(fragment_condition)s
2441 """
2442
2443 _SQL_units_from_consumable_substance = u"""
2444 -- via ref.consumable_substance.unit
2445 SELECT
2446 unit AS data,
2447 unit AS field_label,
2448 unit || ' (' || description || ')' AS list_label,
2449 2 AS rank
2450 FROM
2451 ref.consumable_substance
2452 WHERE
2453 unit %(fragment_condition)s
2454 %(ctxt_substance)s
2455 """
2456
2457 #----------------------------------------------------------------
2459
2461
2462 query = u"""
2463 SELECT DISTINCT ON (data)
2464 data,
2465 field_label,
2466 list_label
2467 FROM (
2468
2469 SELECT
2470 data,
2471 field_label,
2472 list_label,
2473 rank
2474 FROM (
2475 (%s) UNION ALL
2476 (%s) UNION ALL
2477 (%s) UNION ALL
2478 (%s) UNION ALL
2479 (%s) UNION ALL
2480 (%s) UNION ALL
2481 (%s)
2482 ) AS all_matching_units
2483 WHERE data IS NOT NULL
2484 ORDER BY rank, list_label
2485
2486 ) AS ranked_matching_units
2487 LIMIT 50""" % (
2488 _SQL_units_from_test_results,
2489 _SQL_units_from_test_types,
2490 _SQL_units_from_loinc_ipcc,
2491 _SQL_units_from_loinc_submitted,
2492 _SQL_units_from_loinc_example,
2493 _SQL_units_from_atc,
2494 _SQL_units_from_consumable_substance
2495 )
2496
2497 ctxt = {
2498 'ctxt_type_pk': {
2499 'where_part': u'AND pk_test_type = %(pk_type)s',
2500 'placeholder': u'pk_type'
2501 },
2502 'ctxt_test_name': {
2503 'where_part': u'AND %(test_name)s IN (name_tt, name_meta, abbrev_meta)',
2504 'placeholder': u'test_name'
2505 },
2506 'ctxt_ctt': {
2507 'where_part': u'AND %(test_name)s IN (name, abbrev)',
2508 'placeholder': u'test_name'
2509 },
2510 'ctxt_loinc': {
2511 'where_part': u'AND code = %(loinc)s',
2512 'placeholder': u'loinc'
2513 },
2514 'ctxt_loinc_term': {
2515 'where_part': u'AND term ~* %(test_name)s',
2516 'placeholder': u'test_name'
2517 },
2518 'ctxt_substance': {
2519 'where_part': u'AND description ~* %(substance)s',
2520 'placeholder': u'substance'
2521 }
2522 }
2523
2524 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query, context = ctxt)
2525 mp.setThresholds(1, 2, 4)
2526 #mp.print_queries = True
2527 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
2528 self.matcher = mp
2529 self.SetToolTipString(_('Select the desired unit for the amount or measurement.'))
2530 self.selection_only = False
2531 self.phrase_separators = u'[;|]+'
2532 #================================================================
2533
2534 #================================================================
2536
2538
2539 query = u"""
2540 select distinct abnormality_indicator,
2541 abnormality_indicator, abnormality_indicator
2542 from clin.v_test_results
2543 where
2544 abnormality_indicator %(fragment_condition)s
2545 order by abnormality_indicator
2546 limit 25"""
2547
2548 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
2549 mp.setThresholds(1, 1, 2)
2550 mp.ignored_chars = "[.'\\\[\]#$%_]+" + '"'
2551 mp.word_separators = '[ \t&:]+'
2552 gmPhraseWheel.cPhraseWheel.__init__ (
2553 self,
2554 *args,
2555 **kwargs
2556 )
2557 self.matcher = mp
2558 self.SetToolTipString(_('Select an indicator for the level of abnormality.'))
2559 self.selection_only = False
2560
2561 #================================================================
2562 # measurement org widgets / functions
2563 #----------------------------------------------------------------
2565 ea = cMeasurementOrgEAPnl(parent = parent, id = -1)
2566 ea.data = org
2567 ea.mode = gmTools.coalesce(org, 'new', 'edit')
2568 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea)
2569 dlg.SetTitle(gmTools.coalesce(org, _('Adding new diagnostic org'), _('Editing diagnostic org')))
2570 if dlg.ShowModal() == wx.ID_OK:
2571 dlg.Destroy()
2572 return True
2573 dlg.Destroy()
2574 return False
2575 #----------------------------------------------------------------
2577
2578 if parent is None:
2579 parent = wx.GetApp().GetTopWindow()
2580
2581 #------------------------------------------------------------
2582 def edit(org=None):
2583 return edit_measurement_org(parent = parent, org = org)
2584 #------------------------------------------------------------
2585 def refresh(lctrl):
2586 orgs = gmPathLab.get_test_orgs()
2587 lctrl.set_string_items ([
2588 (o['unit'], o['organization'], gmTools.coalesce(o['test_org_contact'], u''), gmTools.coalesce(o['comment'], u''), o['pk_test_org'])
2589 for o in orgs
2590 ])
2591 lctrl.set_data(orgs)
2592 #------------------------------------------------------------
2593 def delete(test_org):
2594 gmPathLab.delete_test_org(test_org = test_org['pk_test_org'])
2595 return True
2596 #------------------------------------------------------------
2597 gmListWidgets.get_choices_from_list (
2598 parent = parent,
2599 msg = _('\nThese are the diagnostic orgs (path labs etc) currently defined in GNUmed.\n\n'),
2600 caption = _('Showing diagnostic orgs.'),
2601 columns = [_('Name'), _('Organization'), _('Contact'), _('Comment'), u'#'],
2602 single_selection = True,
2603 refresh_callback = refresh,
2604 edit_callback = edit,
2605 new_callback = edit,
2606 delete_callback = delete
2607 )
2608
2609 #----------------------------------------------------------------
2610 from Gnumed.wxGladeWidgets import wxgMeasurementOrgEAPnl
2611
2612 -class cMeasurementOrgEAPnl(wxgMeasurementOrgEAPnl.wxgMeasurementOrgEAPnl, gmEditArea.cGenericEditAreaMixin):
2613
2615
2616 try:
2617 data = kwargs['org']
2618 del kwargs['org']
2619 except KeyError:
2620 data = None
2621
2622 wxgMeasurementOrgEAPnl.wxgMeasurementOrgEAPnl.__init__(self, *args, **kwargs)
2623 gmEditArea.cGenericEditAreaMixin.__init__(self)
2624
2625 self.mode = 'new'
2626 self.data = data
2627 if data is not None:
2628 self.mode = 'edit'
2629
2630 #self.__init_ui()
2631 #----------------------------------------------------------------
2632 # def __init_ui(self):
2633 # # adjust phrasewheels etc
2634 #----------------------------------------------------------------
2635 # generic Edit Area mixin API
2636 #----------------------------------------------------------------
2638 has_errors = False
2639 if self._PRW_org_unit.GetData() is None:
2640 if self._PRW_org_unit.GetValue().strip() == u'':
2641 has_errors = True
2642 self._PRW_org_unit.display_as_valid(valid = False)
2643 else:
2644 self._PRW_org_unit.display_as_valid(valid = True)
2645 else:
2646 self._PRW_org_unit.display_as_valid(valid = True)
2647
2648 return (not has_errors)
2649 #----------------------------------------------------------------
2651 data = gmPathLab.create_test_org (
2652 name = self._PRW_org_unit.GetValue().strip(),
2653 comment = self._TCTRL_comment.GetValue().strip(),
2654 pk_org_unit = self._PRW_org_unit.GetData()
2655 )
2656 data['test_org_contact'] = self._TCTRL_contact.GetValue().strip()
2657 data.save()
2658 self.data = data
2659 return True
2660 #----------------------------------------------------------------
2662 # get or create the org unit
2663 name = self._PRW_org_unit.GetValue().strip()
2664 org = gmOrganization.org_exists(organization = name)
2665 if org is None:
2666 org = gmOrganization.create_org (
2667 organization = name,
2668 category = u'Laboratory'
2669 )
2670 org_unit = gmOrganization.create_org_unit (
2671 pk_organization = org['pk_org'],
2672 unit = name
2673 )
2674 # update test_org fields
2675 self.data['pk_org_unit'] = org_unit['pk_org_unit']
2676 self.data['test_org_contact'] = self._TCTRL_contact.GetValue().strip()
2677 self.data['comment'] = self._TCTRL_comment.GetValue().strip()
2678 self.data.save()
2679 return True
2680 #----------------------------------------------------------------
2682 self._PRW_org_unit.SetText(value = u'', data = None)
2683 self._TCTRL_contact.SetValue(u'')
2684 self._TCTRL_comment.SetValue(u'')
2685 #----------------------------------------------------------------
2687 self._PRW_org_unit.SetText(value = self.data['unit'], data = self.data['pk_org_unit'])
2688 self._TCTRL_contact.SetValue(gmTools.coalesce(self.data['test_org_contact'], u''))
2689 self._TCTRL_comment.SetValue(gmTools.coalesce(self.data['comment'], u''))
2690 #----------------------------------------------------------------
2693 #----------------------------------------------------------------
2696
2697 #----------------------------------------------------------------
2699
2701
2702 query = u"""
2703 SELECT DISTINCT ON (list_label)
2704 pk_test_org AS data,
2705 unit || ' (' || organization || ')' AS field_label,
2706 unit || ' @ ' || organization AS list_label
2707 FROM clin.v_test_orgs
2708 WHERE
2709 unit %(fragment_condition)s
2710 OR
2711 organization %(fragment_condition)s
2712 ORDER BY list_label
2713 LIMIT 50"""
2714 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
2715 mp.setThresholds(1, 2, 4)
2716 #mp.word_separators = '[ \t:@]+'
2717 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
2718 self.matcher = mp
2719 self.SetToolTipString(_('The name of the path lab/diagnostic organisation.'))
2720 self.selection_only = False
2721 #------------------------------------------------------------
2723 if self.GetData() is not None:
2724 _log.debug('data already set, not creating')
2725 return
2726
2727 if self.GetValue().strip() == u'':
2728 _log.debug('cannot create new lab, missing name')
2729 return
2730
2731 lab = gmPathLab.create_test_org(name = self.GetValue().strip())
2732 self.SetText(value = lab['unit'], data = lab['pk_test_org'])
2733 return
2734 #------------------------------------------------------------
2737
2738 #================================================================
2740
2741 if parent is None:
2742 parent = wx.GetApp().GetTopWindow()
2743
2744 #----------------------------------------
2745 def get_tooltip(data):
2746 if data is None:
2747 return None
2748 return data.format(with_tests = True)
2749 #----------------------------------------
2750
2751 msg = _(
2752 '\n'
2753 'These are the meta test types currently defined in GNUmed.\n'
2754 '\n'
2755 'Meta test types allow you to aggregate several actual test types used\n'
2756 'by pathology labs into one logical type.\n'
2757 '\n'
2758 'This is useful for grouping together results of tests which come under\n'
2759 'different names but really are the same thing. This often happens when\n'
2760 'you switch labs or the lab starts using another test method.\n'
2761 )
2762
2763 mtts = gmPathLab.get_meta_test_types()
2764
2765 gmListWidgets.get_choices_from_list (
2766 parent = parent,
2767 msg = msg,
2768 caption = _('Showing meta test types.'),
2769 columns = [_('Abbrev'), _('Name'), _('LOINC'), _('Comment'), u'#'],
2770 choices = [ [
2771 m['abbrev'],
2772 m['name'],
2773 gmTools.coalesce(m['loinc'], u''),
2774 gmTools.coalesce(m['comment'], u''),
2775 m['pk']
2776 ] for m in mtts ],
2777 data = mtts,
2778 single_selection = True,
2779 list_tooltip_callback = get_tooltip
2780 #edit_callback = edit,
2781 #new_callback = edit,
2782 #delete_callback = delete,
2783 #refresh_callback = refresh
2784 )
2785 #----------------------------------------------------------------
2787
2789
2790 query = u"""
2791 SELECT DISTINCT ON (field_label)
2792 c_mtt.pk
2793 AS data,
2794 c_mtt.abbrev || ': ' || name
2795 AS field_label,
2796 c_mtt.abbrev || ': ' || name
2797 || coalesce (
2798 ' (' || c_mtt.comment || ')',
2799 ''
2800 )
2801 || coalesce (
2802 ', LOINC: ' || c_mtt.loinc,
2803 ''
2804 )
2805 AS list_label
2806 FROM
2807 clin.meta_test_type c_mtt
2808 WHERE
2809 abbrev %(fragment_condition)s
2810 OR
2811 name %(fragment_condition)s
2812 OR
2813 loinc %(fragment_condition)s
2814 ORDER BY field_label
2815 LIMIT 50"""
2816
2817 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
2818 mp.setThresholds(1, 2, 4)
2819 mp.word_separators = '[ \t:@]+'
2820 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
2821 self.matcher = mp
2822 self.SetToolTipString(_('Select the meta test type.'))
2823 self.selection_only = True
2824 #------------------------------------------------------------
2826 if self.GetData() is None:
2827 return None
2828
2829 return gmPathLab.cMetaTestType(aPK_obj = self.GetData())
2830
2831 #================================================================
2832 # test panel handling
2833 #================================================================
2835 ea = cTestPanelEAPnl(parent = parent, id = -1)
2836 ea.data = test_panel
2837 ea.mode = gmTools.coalesce(test_panel, 'new', 'edit')
2838 dlg = gmEditArea.cGenericEditAreaDlg2 (
2839 parent = parent,
2840 id = -1,
2841 edit_area = ea,
2842 single_entry = gmTools.bool2subst((test_panel is None), False, True)
2843 )
2844 dlg.SetTitle(gmTools.coalesce(test_panel, _('Adding new test panel'), _('Editing test panel')))
2845 if dlg.ShowModal() == wx.ID_OK:
2846 dlg.Destroy()
2847 return True
2848 dlg.Destroy()
2849 return False
2850
2851 #----------------------------------------------------------------
2853
2854 if parent is None:
2855 parent = wx.GetApp().GetTopWindow()
2856
2857 #------------------------------------------------------------
2858 def edit(test_panel=None):
2859 return edit_test_panel(parent = parent, test_panel = test_panel)
2860 #------------------------------------------------------------
2861 def delete(test_panel):
2862 gmPathLab.delete_test_panel(pk = test_panel['pk_test_panel'])
2863 return True
2864 #------------------------------------------------------------
2865 def get_tooltip(test_panel):
2866 return test_panel.format()
2867 #------------------------------------------------------------
2868 def refresh(lctrl):
2869 panels = gmPathLab.get_test_panels(order_by = 'description')
2870 items = [ [
2871 p['description'],
2872 gmTools.coalesce(p['comment'], u''),
2873 p['pk_test_panel']
2874 ] for p in panels ]
2875 lctrl.set_string_items(items)
2876 lctrl.set_data(panels)
2877 #------------------------------------------------------------
2878 msg = _(
2879 '\n'
2880 'Test panels as defined in GNUmed.\n'
2881 )
2882
2883 gmListWidgets.get_choices_from_list (
2884 parent = parent,
2885 msg = msg,
2886 caption = _('Showing test panels.'),
2887 columns = [ _('Name'), _('Comment'), u'#' ],
2888 single_selection = True,
2889 refresh_callback = refresh,
2890 edit_callback = edit,
2891 new_callback = edit,
2892 delete_callback = delete,
2893 list_tooltip_callback = get_tooltip
2894 )
2895
2896 #----------------------------------------------------------------
2898
2900 query = u"""
2901 SELECT
2902 pk_test_panel
2903 AS data,
2904 description
2905 AS field_label,
2906 description
2907 AS list_label
2908 FROM
2909 clin.v_test_panels
2910 WHERE
2911 description %(fragment_condition)s
2912 ORDER BY field_label
2913 LIMIT 30"""
2914 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
2915 mp.setThresholds(1, 2, 4)
2916 #mp.word_separators = '[ \t:@]+'
2917 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
2918 self.matcher = mp
2919 self.SetToolTipString(_('Select a test panel.'))
2920 self.selection_only = True
2921 #------------------------------------------------------------
2923 if self.GetData() is None:
2924 return None
2925 return gmPathLab.cTestPanel(aPK_obj = self.GetData())
2926 #------------------------------------------------------------
2928 if self.GetData() is None:
2929 return None
2930 return gmPathLab.cTestPanel(aPK_obj = self.GetData()).format()
2931
2932 #====================================================================
2933 from Gnumed.wxGladeWidgets import wxgTestPanelEAPnl
2934
2936
2938
2939 try:
2940 data = kwargs['panel']
2941 del kwargs['panel']
2942 except KeyError:
2943 data = None
2944
2945 wxgTestPanelEAPnl.wxgTestPanelEAPnl.__init__(self, *args, **kwargs)
2946 gmEditArea.cGenericEditAreaMixin.__init__(self)
2947
2948 self._test_types = None
2949
2950 self.mode = 'new'
2951 self.data = data
2952 if data is not None:
2953 self.mode = 'edit'
2954
2955 #self.__init_ui()
2956 #----------------------------------------------------------------
2957 # def __init_ui(self):
2958 # # adjust phrasewheels etc
2959 #----------------------------------------------------------------
2960 # generic Edit Area mixin API
2961 #----------------------------------------------------------------
2963 validity = True
2964
2965 if self._test_types is None:
2966 validity = False
2967 gmDispatcher.send(signal = 'statustext', msg = _('No test types selected.'))
2968 self._BTN_select_tests.SetFocus()
2969
2970 if self._TCTRL_description.GetValue().strip() == u'':
2971 validity = False
2972 self.display_tctrl_as_valid(tctrl = self._TCTRL_description, valid = False)
2973 self._TCTRL_description.SetFocus()
2974 else:
2975 self.display_tctrl_as_valid(tctrl = self._TCTRL_description, valid = True)
2976
2977 return validity
2978 #----------------------------------------------------------------
2980 data = gmPathLab.create_test_panel(description = self._TCTRL_description.GetValue().strip())
2981 data['comment'] = self._TCTRL_comment.GetValue().strip()
2982 data['pk_test_types'] = [ tt['pk_test_type'] for tt in self._test_types ]
2983 data.save()
2984 data.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ]
2985 self.data = data
2986 return True
2987 #----------------------------------------------------------------
2989 self.data['description'] = self._TCTRL_description.GetValue().strip()
2990 self.data['comment'] = self._TCTRL_comment.GetValue().strip()
2991 self.data['pk_test_types'] = [ tt['pk_test_type'] for tt in self._test_types ]
2992 self.data.save()
2993 self.data.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ]
2994 return True
2995 #----------------------------------------------------------------
2997 self._TCTRL_tests.SetValue(u'')
2998 self._test_types = test_types
2999 if self._test_types is None:
3000 return
3001 tmp = u';\n'.join ([
3002 u'%s: %s%s' % (
3003 t['unified_abbrev'],
3004 t['unified_name'],
3005 gmTools.coalesce(t['name_org'], u'', u' (%s)')
3006 )
3007 for t in self._test_types
3008 ])
3009 self._TCTRL_tests.SetValue(tmp)
3010 #----------------------------------------------------------------
3012 self._TCTRL_description.SetValue(u'')
3013 self._TCTRL_comment.SetValue(u'')
3014 self.__refresh_test_types_field()
3015 self._PRW_codes.SetText()
3016
3017 self._TCTRL_description.SetFocus()
3018 #----------------------------------------------------------------
3022 #----------------------------------------------------------------
3024 self._TCTRL_description.SetValue(self.data['description'])
3025 self._TCTRL_comment.SetValue(gmTools.coalesce(self.data['comment'], u''))
3026 self.__refresh_test_types_field(test_types = self.data.test_types)
3027 val, data = self._PRW_codes.generic_linked_codes2item_dict(self.data.generic_codes)
3028 self._PRW_codes.SetText(val, data)
3029
3030 self._BTN_select_tests.SetFocus()
3031 #----------------------------------------------------------------
3047
3048 #================================================================
3049 # main
3050 #----------------------------------------------------------------
3051 if __name__ == '__main__':
3052
3053 from Gnumed.pycommon import gmLog2
3054 from Gnumed.wxpython import gmPatSearchWidgets
3055
3056 gmI18N.activate_locale()
3057 gmI18N.install_domain()
3058 gmDateTime.init()
3059
3060 #------------------------------------------------------------
3062 pat = gmPersonSearch.ask_for_patient()
3063 app = wx.PyWidgetTester(size = (500, 300))
3064 lab_grid = cMeasurementsGrid(parent = app.frame, id = -1)
3065 lab_grid.patient = pat
3066 app.frame.Show()
3067 app.MainLoop()
3068 #------------------------------------------------------------
3070 pat = gmPersonSearch.ask_for_patient()
3071 gmPatSearchWidgets.set_active_patient(patient=pat)
3072 app = wx.PyWidgetTester(size = (500, 300))
3073 ea = cMeasurementEditAreaPnl(parent = app.frame, id = -1)
3074 app.frame.Show()
3075 app.MainLoop()
3076 #------------------------------------------------------------
3077 # def test_primary_care_vitals_pnl():
3078 # app = wx.PyWidgetTester(size = (500, 300))
3079 # pnl = wxgPrimaryCareVitalsInputPnl.wxgPrimaryCareVitalsInputPnl(parent = app.frame, id = -1)
3080 # app.frame.Show()
3081 # app.MainLoop()
3082 #------------------------------------------------------------
3083 if (len(sys.argv) > 1) and (sys.argv[1] == 'test'):
3084 #test_grid()
3085 test_test_ea_pnl()
3086 #test_primary_care_vitals_pnl()
3087
3088 #================================================================
3089
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Sat Aug 3 03:56:18 2013 | http://epydoc.sourceforge.net |