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
56
58
59 if parent is None:
60 parent = wx.GetApp().GetTopWindow()
61
62
63 dlg = wx.FileDialog (
64 parent = parent,
65 message = 'Import Excelleris HL7 from XML file:',
66
67
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
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
99 dlg = wx.FileDialog (
100 parent = parent,
101 message = 'Import HL7 from file:',
102
103
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
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
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
167
168
169 left_extra_button = [_('Show'), _('Show formatted HL7'), show_hl7]
170
171
172 )
173
174
175
176
178
179 wx.BeginBusyCursor()
180
181 gmDispatcher.send(signal = 'statustext', msg = _('Updating LOINC data can take quite a while...'), beep = True)
182
183
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
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
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
219
221
222 dbcfg = gmCfg.cCfgSQL()
223
224 url = dbcfg.get2 (
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
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
365
366
368
369 option = u'form_templates.default_gnuplot_template'
370
371 dbcfg = gmCfg.cCfgSQL()
372
373
374 default_template_name = dbcfg.get2 (
375 option = option,
376 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
377 bias = 'user'
378 )
379
380
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
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
394
395 try:
396 name, ver = default_template_name.split(u' - ')
397 except:
398
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
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):
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
466
467
468
469
470
471
472
473
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
482
483
484
485
486
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
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
591 selected_cells += self.GetSelectedCells()
592
593
594 selected_cells += list (
595 (row, col)
596 for row in sel_rows
597 for col in xrange(self.GetNumberCols())
598 )
599
600
601 selected_cells += list (
602 (row, col)
603 for row in xrange(self.GetNumberRows())
604 for col in sel_cols
605 )
606
607
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
635
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
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
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
687 reverse_chronological = True
688 )
689 ]
690 results = emr.get_test_results_by_date (
691 tests = self.__panel_to_show['pk_test_types'],
692
693 reverse_chronological = True
694 )
695
696 self.BeginBatch()
697
698
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
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
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
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
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
744
745 if not sub_result['reviewed']:
746 missing_review = True
747
748 else:
749
750 if sub_result['you_are_responsible'] and not sub_result['review_by_you']:
751 missing_review = True
752
753
754 if sub_result.is_long_text:
755 lines = gmTools.strip_empty_lines (
756 text = sub_result['unified_val'],
757 eol = u'\n',
758 return_list = True
759 )
760 tmp = u'%.7s%s' % (lines[0][:7], gmTools.u_ellipsis)
761 else:
762 val = gmTools.strip_empty_lines (
763 text = sub_result['unified_val'],
764 eol = u'\n',
765 return_list = False
766 ).replace(u'\n', u'//')
767 if len(val) > 8:
768 tmp = u'%.7s%s' % (val[:7], gmTools.u_ellipsis)
769 else:
770 tmp = u'%.8s' % val[:8]
771
772
773 tmp = u'%s%.6s' % (tmp, abnormality_indicator)
774
775
776 has_sub_result_comment = gmTools.coalesce (
777 gmTools.coalesce(sub_result['note_test_org'], sub_result['comment']),
778 u''
779 ).strip() != u''
780 if has_sub_result_comment:
781 tmp = u'%s %s' % (tmp, gmTools.u_ellipsis)
782
783
784 if missing_review:
785 tmp = u'%s %s' % (tmp, gmTools.u_writing_hand)
786 else:
787 if sub_result['is_clinically_relevant']:
788 tmp += u' !'
789
790
791 if len(self.__cell_data[col][row]) > 1:
792 tmp = u'%s %s' % (sub_result['clin_when'].strftime('%H:%M'), tmp)
793
794 vals2display.append(tmp)
795
796 self.SetCellValue(row, col, u'\n'.join(vals2display))
797 self.SetCellAlignment(row, col, horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE)
798
799
800
801
802
803
804
805
806
807
808 if cell_has_out_of_bounds_value:
809 self.SetCellBackgroundColour(row, col, 'cornflower blue')
810
811 self.AutoSize()
812 self.EndBatch()
813 return
814
816 emr = self.__patient.get_emr()
817
818 self.__row_label_data = emr.get_test_types_for_results()
819 test_type_labels = [ u'%s%s' % (
820 gmTools.bool2subst(test['is_fake_meta_type'], u'', gmTools.u_sum, u''),
821 test['unified_abbrev']
822 ) for test in self.__row_label_data
823 ]
824 if len(test_type_labels) == 0:
825 return
826
827 test_date_labels = [ date[0].strftime(self.__date_format) for date in emr.get_dates_for_results() ]
828 results = emr.get_test_results_by_date()
829
830 self.BeginBatch()
831
832
833 self.AppendRows(numRows = len(test_type_labels))
834 for row_idx in range(len(test_type_labels)):
835 self.SetRowLabelValue(row_idx, test_type_labels[row_idx])
836
837
838 self.AppendCols(numCols = len(test_date_labels))
839 for date_idx in range(len(test_date_labels)):
840 self.SetColLabelValue(date_idx, test_date_labels[date_idx])
841
842
843 for result in results:
844 row = test_type_labels.index(u'%s%s' % (
845 gmTools.bool2subst(result['is_fake_meta_type'], u'', gmTools.u_sum, u''),
846 result['unified_abbrev']
847 ))
848 col = test_date_labels.index(result['clin_when'].strftime(self.__date_format))
849
850 try:
851 self.__cell_data[col]
852 except KeyError:
853 self.__cell_data[col] = {}
854
855
856 if self.__cell_data[col].has_key(row):
857 self.__cell_data[col][row].append(result)
858 self.__cell_data[col][row].sort(key = lambda x: x['clin_when'], reverse = True)
859 else:
860 self.__cell_data[col][row] = [result]
861
862
863 vals2display = []
864 cell_has_out_of_bounds_value = False
865 for sub_result in self.__cell_data[col][row]:
866
867
868 if sub_result.is_considered_abnormal:
869 cell_has_out_of_bounds_value = True
870
871 abnormality_indicator = sub_result.formatted_abnormality_indicator
872 if abnormality_indicator is None:
873 abnormality_indicator = u''
874 if abnormality_indicator != u'':
875 abnormality_indicator = u' (%s)' % abnormality_indicator[:3]
876
877
878
879 sub_result_relevant = sub_result['is_clinically_relevant']
880 if sub_result_relevant is None:
881
882 sub_result_relevant = False
883
884 missing_review = False
885
886
887 if not sub_result['reviewed']:
888 missing_review = True
889
890 else:
891
892 if sub_result['you_are_responsible'] and not sub_result['review_by_you']:
893 missing_review = True
894
895
896 if sub_result.is_long_text:
897 lines = gmTools.strip_empty_lines (
898 text = sub_result['unified_val'],
899 eol = u'\n',
900 return_list = True
901 )
902 tmp = u'%.7s%s' % (lines[0][:7], gmTools.u_ellipsis)
903 else:
904 val = gmTools.strip_empty_lines (
905 text = sub_result['unified_val'],
906 eol = u'\n',
907 return_list = False
908 ).replace(u'\n', u'//')
909 if len(val) > 8:
910 tmp = u'%.7s%s' % (val[:7], gmTools.u_ellipsis)
911 else:
912 tmp = u'%.8s' % val[:8]
913
914
915 tmp = u'%s%.6s' % (tmp, abnormality_indicator)
916
917
918 has_sub_result_comment = gmTools.coalesce (
919 gmTools.coalesce(sub_result['note_test_org'], sub_result['comment']),
920 u''
921 ).strip() != u''
922 if has_sub_result_comment:
923 tmp = u'%s %s' % (tmp, gmTools.u_ellipsis)
924
925
926 if missing_review:
927 tmp = u'%s %s' % (tmp, gmTools.u_writing_hand)
928
929
930 if len(self.__cell_data[col][row]) > 1:
931 tmp = u'%s %s' % (sub_result['clin_when'].strftime('%H:%M'), tmp)
932
933 vals2display.append(tmp)
934
935 self.SetCellValue(row, col, u'\n'.join(vals2display))
936 self.SetCellAlignment(row, col, horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE)
937
938 if sub_result_relevant:
939 font = self.GetCellFont(row, col)
940 self.SetCellTextColour(row, col, 'firebrick')
941 font.SetWeight(wx.FONTWEIGHT_BOLD)
942 self.SetCellFont(row, col, font)
943 if cell_has_out_of_bounds_value:
944 self.SetCellBackgroundColour(row, col, 'cornflower blue')
945
946 self.AutoSize()
947 self.EndBatch()
948 return
949
951 self.BeginBatch()
952 self.ClearGrid()
953
954
955 if self.GetNumberRows() > 0:
956 self.DeleteRows(pos = 0, numRows = self.GetNumberRows())
957 if self.GetNumberCols() > 0:
958 self.DeleteCols(pos = 0, numCols = self.GetNumberCols())
959 self.EndBatch()
960 self.__cell_data = {}
961 self.__row_label_data = []
962
980
1006
1007
1008
1010 self.CreateGrid(0, 1)
1011 self.EnableEditing(0)
1012 self.EnableDragGridSize(1)
1013 self.SetMinSize(wx.DefaultSize)
1014
1015
1016
1017
1018
1019
1020 self.SetRowLabelSize(wx.grid.GRID_AUTOSIZE)
1021
1022 self.SetRowLabelAlignment(horiz = wx.ALIGN_LEFT, vert = wx.ALIGN_CENTRE)
1023 font = self.GetLabelFont()
1024 font.SetWeight(wx.FONTWEIGHT_LIGHT)
1025 self.SetLabelFont(font)
1026
1027
1028 dbcfg = gmCfg.cCfgSQL()
1029 url = dbcfg.get2 (
1030 option = u'external.urls.measurements_encyclopedia',
1031 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
1032 bias = 'user',
1033 default = u'http://www.laborlexikon.de'
1034 )
1035
1036 self.__WIN_corner = self.GetGridCornerLabelWindow()
1037
1038 LNK_lab = wx.lib.hyperlink.HyperLinkCtrl (
1039 self.__WIN_corner,
1040 -1,
1041 label = _('Tests'),
1042 style = wx.HL_DEFAULT_STYLE
1043 )
1044 LNK_lab.SetURL(url)
1045 LNK_lab.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BACKGROUND))
1046 LNK_lab.SetToolTipString(_(
1047 'Navigate to an encyclopedia of measurements\n'
1048 'and test methods on the web.\n'
1049 '\n'
1050 ' <%s>'
1051 ) % url)
1052
1053 SZR_inner = wx.BoxSizer(wx.HORIZONTAL)
1054 SZR_inner.Add((20, 20), 1, wx.EXPAND, 0)
1055 SZR_inner.Add(LNK_lab, 0, wx.ALIGN_CENTER_VERTICAL, 0)
1056 SZR_inner.Add((20, 20), 1, wx.EXPAND, 0)
1057
1058 SZR_corner = wx.BoxSizer(wx.VERTICAL)
1059 SZR_corner.Add((20, 20), 1, wx.EXPAND, 0)
1060 SZR_corner.AddWindow(SZR_inner, 0, wx.EXPAND)
1061 SZR_corner.Add((20, 20), 1, wx.EXPAND, 0)
1062
1063 self.__WIN_corner.SetSizer(SZR_corner)
1064 SZR_corner.Fit(self.__WIN_corner)
1065
1067 self.__WIN_corner.Layout()
1068
1069 - def __cells_to_data(self, cells=None, exclude_multi_cells=False, auto_include_multi_cells=False):
1070 """List of <cells> must be in row / col order."""
1071 data = []
1072 for row, col in cells:
1073 try:
1074
1075 data_list = self.__cell_data[col][row]
1076 except KeyError:
1077 continue
1078
1079 if len(data_list) == 1:
1080 data.append(data_list[0])
1081 continue
1082
1083 if exclude_multi_cells:
1084 gmDispatcher.send(signal = u'statustext', msg = _('Excluding multi-result field from further processing.'))
1085 continue
1086
1087 if auto_include_multi_cells:
1088 data.extend(data_list)
1089 continue
1090
1091 data_to_include = self.__get_choices_from_multi_cell(cell_data = data_list)
1092 if data_to_include is None:
1093 continue
1094 data.extend(data_to_include)
1095
1096 return data
1097
1099 data = gmListWidgets.get_choices_from_list (
1100 parent = self,
1101 msg = _(
1102 'Your selection includes a field with multiple results.\n'
1103 '\n'
1104 'Please select the individual results you want to work on:'
1105 ),
1106 caption = _('Selecting test results'),
1107 choices = [ [d['clin_when'], u'%s: %s' % (d['abbrev_tt'], d['name_tt']), d['unified_val']] for d in cell_data ],
1108 columns = [ _('Date / Time'), _('Test'), _('Result') ],
1109 data = cell_data,
1110 single_selection = single_selection
1111 )
1112 return data
1113
1114
1115
1117
1118 self.GetGridWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_cells)
1119 self.GetGridRowLabelWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_row_labels)
1120
1121
1122
1123 self.Bind(wx.EVT_SIZE, self.__resize_corner_window)
1124
1125
1126 self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.__on_cell_left_dclicked)
1127
1129 col = evt.GetCol()
1130 row = evt.GetRow()
1131
1132
1133 try:
1134 self.__cell_data[col][row]
1135 except KeyError:
1136
1137
1138 return
1139
1140 if len(self.__cell_data[col][row]) > 1:
1141 data = self.__get_choices_from_multi_cell(cell_data = self.__cell_data[col][row], single_selection = True)
1142 else:
1143 data = self.__cell_data[col][row][0]
1144
1145 if data is None:
1146 return
1147
1148 edit_measurement(parent = self, measurement = data, single_entry = True)
1149
1150
1151
1152
1153
1154
1155
1157
1158
1159
1160 x, y = self.CalcUnscrolledPosition(evt.GetX(), evt.GetY())
1161
1162 row = self.YToRow(y)
1163
1164 if self.__prev_label_row == row:
1165 return
1166
1167 self.__prev_label_row == row
1168
1169 evt.GetEventObject().SetToolTipString(self.get_row_tooltip(row = row))
1170
1171
1172
1173
1174
1175
1176
1177
1179 """Calculate where the mouse is and set the tooltip dynamically."""
1180
1181
1182
1183 x, y = self.CalcUnscrolledPosition(evt.GetX(), evt.GetY())
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197 row, col = self.XYToCell(x, y)
1198
1199 if (row == self.__prev_row) and (col == self.__prev_col):
1200 return
1201
1202 self.__prev_row = row
1203 self.__prev_col = col
1204
1205 evt.GetEventObject().SetToolTipString(self.get_cell_tooltip(col=col, row=row))
1206
1207
1208
1212
1213 patient = property(lambda x:x, _set_patient)
1214
1218
1219 panel_to_show = property(lambda x:x, _set_panel_to_show)
1220
1224
1225 show_by_panel = property(lambda x:x, _set_show_by_panel)
1226
1227 from Gnumed.wxGladeWidgets import wxgMeasurementsPnl
1228
1229 -class cMeasurementsPnl(wxgMeasurementsPnl.wxgMeasurementsPnl, gmRegetMixin.cRegetOnPaintMixin):
1230 """Panel holding a grid with lab data. Used as notebook page."""
1231
1238
1239
1240
1242 gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._on_pre_patient_selection)
1243 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._on_post_patient_selection)
1244 gmDispatcher.connect(signal = u'clin.test_result_mod_db', receiver = self._schedule_data_reget)
1245 gmDispatcher.connect(signal = u'clin.reviewed_test_results_mod_db', receiver = self._schedule_data_reget)
1246
1248 wx.CallAfter(self.__on_post_patient_selection)
1249
1251 self._schedule_data_reget()
1252
1254 wx.CallAfter(self.__on_pre_patient_selection)
1255
1257 self.data_grid.patient = None
1258 self.panel_data_grid.patient = None
1259
1262
1266
1269
1275
1278
1281
1284
1287
1289 wx.CallAfter(self.__on_panel_selected, panel=panel)
1290
1292 if panel is None:
1293 self._TCTRL_panel_comment.SetValue(u'')
1294 self.panel_data_grid.panel_to_show = None
1295 self.panel_data_grid.Hide()
1296 else:
1297 pnl = self._PRW_panel.GetData(as_instance = True)
1298 self._TCTRL_panel_comment.SetValue(gmTools.coalesce (
1299 pnl['comment'],
1300 u''
1301 ))
1302 self.panel_data_grid.panel_to_show = pnl
1303 self.panel_data_grid.Show()
1304 self.Layout()
1305
1306
1308 wx.CallAfter(self.__on_panel_selection_modified)
1309
1311 self._TCTRL_panel_comment.SetValue(u'')
1312 if self._PRW_panel.GetValue().strip() == u'':
1313 self.panel_data_grid.panel_to_show = None
1314 self.panel_data_grid.Hide()
1315 self.Layout()
1316
1317
1318
1320 self.__action_button_popup = wx.Menu(title = _('Perform on selected results:'))
1321
1322 menu_id = wx.NewId()
1323 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('Review and &sign')))
1324 wx.EVT_MENU(self.__action_button_popup, menu_id, self.__on_sign_current_selection)
1325
1326 menu_id = wx.NewId()
1327 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('Plot')))
1328 wx.EVT_MENU(self.__action_button_popup, menu_id, self.__on_plot_current_selection)
1329
1330 menu_id = wx.NewId()
1331 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('Export to &file')))
1332
1333 self.__action_button_popup.Enable(id = menu_id, enable = False)
1334
1335 menu_id = wx.NewId()
1336 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('Export to &clipboard')))
1337
1338 self.__action_button_popup.Enable(id = menu_id, enable = False)
1339
1340 menu_id = wx.NewId()
1341 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('&Delete')))
1342 wx.EVT_MENU(self.__action_button_popup, menu_id, self.__on_delete_current_selection)
1343
1344
1345
1346
1347 self._PRW_panel.add_callback_on_selection(callback = self._on_panel_selected)
1348 self._PRW_panel.add_callback_on_modified(callback = self._on_panel_selection_modified)
1349
1350 self.panel_data_grid.show_by_panel = True
1351 self.panel_data_grid.panel_to_show = None
1352 self.panel_data_grid.Hide()
1353 self.Layout()
1354
1355 self._PRW_panel.SetFocus()
1356
1357
1358
1369
1370
1371
1372
1374
1375 if tests is None:
1376 return True
1377
1378 if len(tests) == 0:
1379 return True
1380
1381 if parent is None:
1382 parent = wx.GetApp().GetTopWindow()
1383
1384 if len(tests) > 10:
1385 test_count = len(tests)
1386 tests2show = None
1387 else:
1388 test_count = None
1389 tests2show = tests
1390 if len(tests) == 0:
1391 return True
1392
1393 dlg = cMeasurementsReviewDlg(parent, -1, tests = tests, test_count = test_count)
1394 decision = dlg.ShowModal()
1395 if decision != wx.ID_APPLY:
1396 return True
1397
1398 wx.BeginBusyCursor()
1399 if dlg._RBTN_confirm_abnormal.GetValue():
1400 abnormal = None
1401 elif dlg._RBTN_results_normal.GetValue():
1402 abnormal = False
1403 else:
1404 abnormal = True
1405
1406 if dlg._RBTN_confirm_relevance.GetValue():
1407 relevant = None
1408 elif dlg._RBTN_results_not_relevant.GetValue():
1409 relevant = False
1410 else:
1411 relevant = True
1412
1413 comment = None
1414 if len(tests) == 1:
1415 comment = dlg._TCTRL_comment.GetValue()
1416
1417 make_responsible = dlg._CHBOX_responsible.IsChecked()
1418 dlg.Destroy()
1419
1420 for test in tests:
1421 test.set_review (
1422 technically_abnormal = abnormal,
1423 clinically_relevant = relevant,
1424 comment = comment,
1425 make_me_responsible = make_responsible
1426 )
1427 wx.EndBusyCursor()
1428
1429 return True
1430
1431 from Gnumed.wxGladeWidgets import wxgMeasurementsReviewDlg
1432
1434
1436
1437 try:
1438 tests = kwargs['tests']
1439 del kwargs['tests']
1440 test_count = len(tests)
1441 try: del kwargs['test_count']
1442 except KeyError: pass
1443 except KeyError:
1444 tests = None
1445 test_count = kwargs['test_count']
1446 del kwargs['test_count']
1447
1448 wxgMeasurementsReviewDlg.wxgMeasurementsReviewDlg.__init__(self, *args, **kwargs)
1449
1450 if tests is None:
1451 msg = _('%s results selected. Too many to list individually.') % test_count
1452 else:
1453 msg = '\n'.join (
1454 [ u'%s: %s %s (%s)' % (
1455 t['unified_abbrev'],
1456 t['unified_val'],
1457 t['val_unit'],
1458 gmDateTime.pydt_strftime(t['clin_when'], '%Y %b %d')
1459 ) for t in tests
1460 ]
1461 )
1462
1463 self._LBL_tests.SetLabel(msg)
1464
1465 if test_count == 1:
1466 self._TCTRL_comment.Enable(True)
1467 self._TCTRL_comment.SetValue(gmTools.coalesce(tests[0]['review_comment'], u''))
1468 if tests[0]['you_are_responsible']:
1469 self._CHBOX_responsible.Enable(False)
1470
1471 self.Fit()
1472
1473
1474
1480
1481 from Gnumed.wxGladeWidgets import wxgMeasurementEditAreaPnl
1482
1483 -class cMeasurementEditAreaPnl(wxgMeasurementEditAreaPnl.wxgMeasurementEditAreaPnl, gmEditArea.cGenericEditAreaMixin):
1484 """This edit area saves *new* measurements into the active patient only."""
1485
1502
1503
1504
1506 self._PRW_test.SetText(u'', None, True)
1507 self.__refresh_loinc_info()
1508 self.__refresh_previous_value()
1509 self.__update_units_context()
1510 self._TCTRL_result.SetValue(u'')
1511 self._PRW_units.SetText(u'', None, True)
1512 self._PRW_abnormality_indicator.SetText(u'', None, True)
1513 if self.__default_date is None:
1514 self._DPRW_evaluated.SetData(data = pyDT.datetime.now(tz = gmDateTime.gmCurrentLocalTimezone))
1515 else:
1516 self._DPRW_evaluated.SetData(data = None)
1517 self._TCTRL_note_test_org.SetValue(u'')
1518 self._PRW_intended_reviewer.SetData(gmStaff.gmCurrentProvider()['pk_staff'])
1519 self._PRW_problem.SetData()
1520 self._TCTRL_narrative.SetValue(u'')
1521 self._CHBOX_review.SetValue(False)
1522 self._CHBOX_abnormal.SetValue(False)
1523 self._CHBOX_relevant.SetValue(False)
1524 self._CHBOX_abnormal.Enable(False)
1525 self._CHBOX_relevant.Enable(False)
1526 self._TCTRL_review_comment.SetValue(u'')
1527 self._TCTRL_normal_min.SetValue(u'')
1528 self._TCTRL_normal_max.SetValue(u'')
1529 self._TCTRL_normal_range.SetValue(u'')
1530 self._TCTRL_target_min.SetValue(u'')
1531 self._TCTRL_target_max.SetValue(u'')
1532 self._TCTRL_target_range.SetValue(u'')
1533 self._TCTRL_norm_ref_group.SetValue(u'')
1534
1535 self._PRW_test.SetFocus()
1536
1538 self._PRW_test.SetData(data = self.data['pk_test_type'])
1539 self.__refresh_loinc_info()
1540 self.__refresh_previous_value()
1541 self.__update_units_context()
1542 self._TCTRL_result.SetValue(self.data['unified_val'])
1543 self._PRW_units.SetText(self.data['val_unit'], self.data['val_unit'], True)
1544 self._PRW_abnormality_indicator.SetText (
1545 gmTools.coalesce(self.data['abnormality_indicator'], u''),
1546 gmTools.coalesce(self.data['abnormality_indicator'], u''),
1547 True
1548 )
1549 self._DPRW_evaluated.SetData(data = self.data['clin_when'])
1550 self._TCTRL_note_test_org.SetValue(gmTools.coalesce(self.data['note_test_org'], u''))
1551 self._PRW_intended_reviewer.SetData(self.data['pk_intended_reviewer'])
1552 self._PRW_problem.SetData(self.data['pk_episode'])
1553 self._TCTRL_narrative.SetValue(gmTools.coalesce(self.data['comment'], u''))
1554 self._CHBOX_review.SetValue(False)
1555 self._CHBOX_abnormal.SetValue(gmTools.coalesce(self.data['is_technically_abnormal'], False))
1556 self._CHBOX_relevant.SetValue(gmTools.coalesce(self.data['is_clinically_relevant'], False))
1557 self._CHBOX_abnormal.Enable(False)
1558 self._CHBOX_relevant.Enable(False)
1559 self._TCTRL_review_comment.SetValue(gmTools.coalesce(self.data['review_comment'], u''))
1560 self._TCTRL_normal_min.SetValue(unicode(gmTools.coalesce(self.data['val_normal_min'], u'')))
1561 self._TCTRL_normal_max.SetValue(unicode(gmTools.coalesce(self.data['val_normal_max'], u'')))
1562 self._TCTRL_normal_range.SetValue(gmTools.coalesce(self.data['val_normal_range'], u''))
1563 self._TCTRL_target_min.SetValue(unicode(gmTools.coalesce(self.data['val_target_min'], u'')))
1564 self._TCTRL_target_max.SetValue(unicode(gmTools.coalesce(self.data['val_target_max'], u'')))
1565 self._TCTRL_target_range.SetValue(gmTools.coalesce(self.data['val_target_range'], u''))
1566 self._TCTRL_norm_ref_group.SetValue(gmTools.coalesce(self.data['norm_ref_group'], u''))
1567
1568 self._TCTRL_result.SetFocus()
1569
1571 self._PRW_test.SetText(u'', None, True)
1572 self.__refresh_loinc_info()
1573 self.__refresh_previous_value()
1574 self.__update_units_context()
1575 self._TCTRL_result.SetValue(u'')
1576 self._PRW_units.SetText(u'', None, True)
1577 self._PRW_abnormality_indicator.SetText(u'', None, True)
1578 self._DPRW_evaluated.SetData(data = self.data['clin_when'])
1579 self._TCTRL_note_test_org.SetValue(u'')
1580 self._PRW_intended_reviewer.SetData(self.data['pk_intended_reviewer'])
1581 self._PRW_problem.SetData(self.data['pk_episode'])
1582 self._TCTRL_narrative.SetValue(u'')
1583 self._CHBOX_review.SetValue(False)
1584 self._CHBOX_abnormal.SetValue(False)
1585 self._CHBOX_relevant.SetValue(False)
1586 self._CHBOX_abnormal.Enable(False)
1587 self._CHBOX_relevant.Enable(False)
1588 self._TCTRL_review_comment.SetValue(u'')
1589 self._TCTRL_normal_min.SetValue(u'')
1590 self._TCTRL_normal_max.SetValue(u'')
1591 self._TCTRL_normal_range.SetValue(u'')
1592 self._TCTRL_target_min.SetValue(u'')
1593 self._TCTRL_target_max.SetValue(u'')
1594 self._TCTRL_target_range.SetValue(u'')
1595 self._TCTRL_norm_ref_group.SetValue(u'')
1596
1597 self._PRW_test.SetFocus()
1598
1600
1601 validity = True
1602
1603 if not self._DPRW_evaluated.is_valid_timestamp():
1604 self._DPRW_evaluated.display_as_valid(False)
1605 validity = False
1606 else:
1607 self._DPRW_evaluated.display_as_valid(True)
1608
1609 val = self._TCTRL_result.GetValue().strip()
1610 if val == u'':
1611 validity = False
1612 self.display_ctrl_as_valid(self._TCTRL_result, False)
1613 else:
1614 self.display_ctrl_as_valid(self._TCTRL_result, True)
1615 numeric, val = gmTools.input2decimal(val)
1616 if numeric:
1617 if self._PRW_units.GetValue().strip() == u'':
1618 self._PRW_units.display_as_valid(False)
1619 validity = False
1620 else:
1621 self._PRW_units.display_as_valid(True)
1622 else:
1623 self._PRW_units.display_as_valid(True)
1624
1625 if self._PRW_problem.GetValue().strip() == u'':
1626 self._PRW_problem.display_as_valid(False)
1627 validity = False
1628 else:
1629 self._PRW_problem.display_as_valid(True)
1630
1631 if self._PRW_test.GetValue().strip() == u'':
1632 self._PRW_test.display_as_valid(False)
1633 validity = False
1634 else:
1635 self._PRW_test.display_as_valid(True)
1636
1637 if self._PRW_intended_reviewer.GetData() is None:
1638 self._PRW_intended_reviewer.display_as_valid(False)
1639 validity = False
1640 else:
1641 self._PRW_intended_reviewer.display_as_valid(True)
1642
1643 ctrls = [self._TCTRL_normal_min, self._TCTRL_normal_max, self._TCTRL_target_min, self._TCTRL_target_max]
1644 for widget in ctrls:
1645 val = widget.GetValue().strip()
1646 if val == u'':
1647 continue
1648 try:
1649 decimal.Decimal(val.replace(',', u'.', 1))
1650 self.display_ctrl_as_valid(widget, True)
1651 except:
1652 validity = False
1653 self.display_ctrl_as_valid(widget, False)
1654
1655 if validity is False:
1656 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save result. Invalid or missing essential input.'))
1657
1658 return validity
1659
1661
1662 emr = gmPerson.gmCurrentPatient().get_emr()
1663
1664 success, result = gmTools.input2decimal(self._TCTRL_result.GetValue())
1665 if success:
1666 v_num = result
1667 v_al = None
1668 else:
1669 v_al = self._TCTRL_result.GetValue().strip()
1670 v_num = None
1671
1672 pk_type = self._PRW_test.GetData()
1673 if pk_type is None:
1674 tt = gmPathLab.create_measurement_type (
1675 lab = None,
1676 abbrev = self._PRW_test.GetValue().strip(),
1677 name = self._PRW_test.GetValue().strip(),
1678 unit = gmTools.coalesce(self._PRW_units.GetData(), self._PRW_units.GetValue()).strip()
1679 )
1680 pk_type = tt['pk_test_type']
1681
1682 tr = emr.add_test_result (
1683 episode = self._PRW_problem.GetData(can_create=True, is_open=False),
1684 type = pk_type,
1685 intended_reviewer = self._PRW_intended_reviewer.GetData(),
1686 val_num = v_num,
1687 val_alpha = v_al,
1688 unit = self._PRW_units.GetValue()
1689 )
1690
1691 tr['clin_when'] = self._DPRW_evaluated.GetData().get_pydt()
1692
1693 ctrls = [
1694 ('abnormality_indicator', self._PRW_abnormality_indicator),
1695 ('note_test_org', self._TCTRL_note_test_org),
1696 ('comment', self._TCTRL_narrative),
1697 ('val_normal_range', self._TCTRL_normal_range),
1698 ('val_target_range', self._TCTRL_target_range),
1699 ('norm_ref_group', self._TCTRL_norm_ref_group)
1700 ]
1701 for field, widget in ctrls:
1702 tr[field] = widget.GetValue().strip()
1703
1704 ctrls = [
1705 ('val_normal_min', self._TCTRL_normal_min),
1706 ('val_normal_max', self._TCTRL_normal_max),
1707 ('val_target_min', self._TCTRL_target_min),
1708 ('val_target_max', self._TCTRL_target_max)
1709 ]
1710 for field, widget in ctrls:
1711 val = widget.GetValue().strip()
1712 if val == u'':
1713 tr[field] = None
1714 else:
1715 tr[field] = decimal.Decimal(val.replace(',', u'.', 1))
1716
1717 tr.save_payload()
1718
1719 if self._CHBOX_review.GetValue() is True:
1720 tr.set_review (
1721 technically_abnormal = self._CHBOX_abnormal.GetValue(),
1722 clinically_relevant = self._CHBOX_relevant.GetValue(),
1723 comment = gmTools.none_if(self._TCTRL_review_comment.GetValue().strip(), u''),
1724 make_me_responsible = False
1725 )
1726
1727 self.data = tr
1728
1729 wx.CallAfter (
1730 plot_adjacent_measurements,
1731 test = self.data,
1732 plot_singular_result = False,
1733 use_default_template = True
1734 )
1735
1736 return True
1737
1739
1740 success, result = gmTools.input2decimal(self._TCTRL_result.GetValue())
1741 if success:
1742 v_num = result
1743 v_al = None
1744 else:
1745 v_num = None
1746 v_al = self._TCTRL_result.GetValue().strip()
1747
1748 pk_type = self._PRW_test.GetData()
1749 if pk_type is None:
1750 tt = gmPathLab.create_measurement_type (
1751 lab = None,
1752 abbrev = self._PRW_test.GetValue().strip(),
1753 name = self._PRW_test.GetValue().strip(),
1754 unit = gmTools.none_if(self._PRW_units.GetValue().strip(), u'')
1755 )
1756 pk_type = tt['pk_test_type']
1757
1758 tr = self.data
1759
1760 tr['pk_episode'] = self._PRW_problem.GetData(can_create=True, is_open=False)
1761 tr['pk_test_type'] = pk_type
1762 tr['pk_intended_reviewer'] = self._PRW_intended_reviewer.GetData()
1763 tr['val_num'] = v_num
1764 tr['val_alpha'] = v_al
1765 tr['val_unit'] = gmTools.coalesce(self._PRW_units.GetData(), self._PRW_units.GetValue()).strip()
1766 tr['clin_when'] = self._DPRW_evaluated.GetData().get_pydt()
1767
1768 ctrls = [
1769 ('abnormality_indicator', self._PRW_abnormality_indicator),
1770 ('note_test_org', self._TCTRL_note_test_org),
1771 ('comment', self._TCTRL_narrative),
1772 ('val_normal_range', self._TCTRL_normal_range),
1773 ('val_target_range', self._TCTRL_target_range),
1774 ('norm_ref_group', self._TCTRL_norm_ref_group)
1775 ]
1776 for field, widget in ctrls:
1777 tr[field] = widget.GetValue().strip()
1778
1779 ctrls = [
1780 ('val_normal_min', self._TCTRL_normal_min),
1781 ('val_normal_max', self._TCTRL_normal_max),
1782 ('val_target_min', self._TCTRL_target_min),
1783 ('val_target_max', self._TCTRL_target_max)
1784 ]
1785 for field, widget in ctrls:
1786 val = widget.GetValue().strip()
1787 if val == u'':
1788 tr[field] = None
1789 else:
1790 tr[field] = decimal.Decimal(val.replace(',', u'.', 1))
1791
1792 tr.save_payload()
1793
1794 if self._CHBOX_review.GetValue() is True:
1795 tr.set_review (
1796 technically_abnormal = self._CHBOX_abnormal.GetValue(),
1797 clinically_relevant = self._CHBOX_relevant.GetValue(),
1798 comment = gmTools.none_if(self._TCTRL_review_comment.GetValue().strip(), u''),
1799 make_me_responsible = False
1800 )
1801
1802 wx.CallAfter (
1803 plot_adjacent_measurements,
1804 test = self.data,
1805 plot_singular_result = False,
1806 use_default_template = True
1807 )
1808
1809 return True
1810
1811
1812
1817
1819 self.__refresh_loinc_info()
1820 self.__refresh_previous_value()
1821 self.__update_units_context()
1822
1823 self.__update_normal_range()
1824 self.__update_clinical_range()
1825
1827
1828 self.__update_normal_range()
1829 self.__update_clinical_range()
1830
1832
1833 if not self._CHBOX_review.GetValue():
1834 self._CHBOX_abnormal.SetValue(self._PRW_abnormality_indicator.GetValue().strip() != u'')
1835
1837 self._CHBOX_abnormal.Enable(self._CHBOX_review.GetValue())
1838 self._CHBOX_relevant.Enable(self._CHBOX_review.GetValue())
1839 self._TCTRL_review_comment.Enable(self._CHBOX_review.GetValue())
1840
1857
1858
1859
1861
1862 if self._PRW_test.GetData() is None:
1863 self._PRW_units.unset_context(context = u'pk_type')
1864 self._PRW_units.unset_context(context = u'loinc')
1865 if self._PRW_test.GetValue().strip() == u'':
1866 self._PRW_units.unset_context(context = u'test_name')
1867 else:
1868 self._PRW_units.set_context(context = u'test_name', val = self._PRW_test.GetValue().strip())
1869 return
1870
1871 tt = self._PRW_test.GetData(as_instance = True)
1872
1873 self._PRW_units.set_context(context = u'pk_type', val = tt['pk_test_type'])
1874 self._PRW_units.set_context(context = u'test_name', val = tt['name'])
1875
1876 if tt['loinc'] is not None:
1877 self._PRW_units.set_context(context = u'loinc', val = tt['loinc'])
1878
1879
1880 if self._PRW_units.GetValue().strip() == u'':
1881 clin_when = self._DPRW_evaluated.GetData().get_pydt()
1882 if clin_when is None:
1883 unit = tt.temporally_closest_unit
1884 else:
1885 unit = tt.get_temporally_closest_unit(timestamp = clin_when)
1886 if unit is None:
1887 self._PRW_units.SetText(u'', unit, True)
1888 else:
1889 self._PRW_units.SetText(unit, unit, True)
1890
1891
1893 unit = self._PRW_units.GetValue().strip()
1894 if unit == u'':
1895 return
1896 if self._PRW_test.GetData() is None:
1897 return
1898 for ctrl in [self._TCTRL_normal_min, self._TCTRL_normal_max, self._TCTRL_normal_range, self._TCTRL_norm_ref_group]:
1899 if ctrl.GetValue().strip() != u'':
1900 return
1901 tt = self._PRW_test.GetData(as_instance = True)
1902 test_w_range = tt.get_temporally_closest_normal_range (
1903 unit,
1904 timestamp = self._DPRW_evaluated.GetData().get_pydt()
1905 )
1906 if test_w_range is None:
1907 return
1908 self._TCTRL_normal_min.SetValue(unicode(gmTools.coalesce(test_w_range['val_normal_min'], u'')))
1909 self._TCTRL_normal_max.SetValue(unicode(gmTools.coalesce(test_w_range['val_normal_max'], u'')))
1910 self._TCTRL_normal_range.SetValue(gmTools.coalesce(test_w_range['val_normal_range'], u''))
1911 self._TCTRL_norm_ref_group.SetValue(gmTools.coalesce(test_w_range['norm_ref_group'], u''))
1912
1913
1934
1935
1937
1938 self._TCTRL_loinc.SetValue(u'')
1939
1940 if self._PRW_test.GetData() is None:
1941 return
1942
1943 tt = self._PRW_test.GetData(as_instance = True)
1944
1945 if tt['loinc'] is None:
1946 return
1947
1948 info = gmLOINC.loinc2term(loinc = tt['loinc'])
1949 if len(info) == 0:
1950 self._TCTRL_loinc.SetValue(u'')
1951 return
1952
1953 self._TCTRL_loinc.SetValue(u'%s: %s' % (tt['loinc'], info[0]))
1954
1956 self._TCTRL_previous_value.SetValue(u'')
1957
1958
1959 if self.data is not None:
1960 return
1961 if self._PRW_test.GetData() is None:
1962 return
1963 tt = self._PRW_test.GetData(as_instance = True)
1964 most_recent = tt.get_most_recent_results (
1965 no_of_results = 1,
1966 patient = gmPerson.gmCurrentPatient().ID
1967 )
1968 if most_recent is None:
1969 return
1970 self._TCTRL_previous_value.SetValue(_('%s ago: %s%s%s - %s%s') % (
1971 gmDateTime.format_interval_medically(gmDateTime.pydt_now_here() - most_recent['clin_when']),
1972 most_recent['unified_val'],
1973 most_recent['val_unit'],
1974 gmTools.coalesce(most_recent['abnormality_indicator'], u'', u' (%s)'),
1975 most_recent['abbrev_tt'],
1976 gmTools.coalesce(most_recent.formatted_range, u'', u' [%s]')
1977 ))
1978 self._TCTRL_previous_value.SetToolTipString(most_recent.format (
1979 with_review = True,
1980 with_evaluation = False,
1981 with_ranges = True,
1982 with_episode = True,
1983 with_type_details=True
1984 ))
1985
1986
1987
1988
1990
1991 if parent is None:
1992 parent = wx.GetApp().GetTopWindow()
1993
1994 if msg is None:
1995 msg = _('Pick the relevant measurement types.')
1996
1997 if right_column is None:
1998 right_columns = [_('Picked')]
1999 else:
2000 right_columns = [right_column]
2001
2002 picker = gmListWidgets.cItemPickerDlg(parent, -1, msg = msg)
2003 picker.set_columns(columns = [_('Known measurement types')], columns_right = right_columns)
2004 types = gmPathLab.get_measurement_types(order_by = 'unified_abbrev')
2005 picker.set_choices (
2006 choices = [
2007 u'%s: %s%s' % (
2008 t['unified_abbrev'],
2009 t['unified_name'],
2010 gmTools.coalesce(t['name_org'], u'', u' (%s)')
2011 )
2012 for t in types
2013 ],
2014 data = types
2015 )
2016 if picks is not None:
2017 picker.set_picks (
2018 picks = [
2019 u'%s: %s%s' % (
2020 p['unified_abbrev'],
2021 p['unified_name'],
2022 gmTools.coalesce(p['name_org'], u'', u' (%s)')
2023 )
2024 for p in picks
2025 ],
2026 data = picks
2027 )
2028 result = picker.ShowModal()
2029
2030 if result == wx.ID_CANCEL:
2031 picker.Destroy()
2032 return None
2033
2034 picks = picker.picks
2035 picker.Destroy()
2036 return picks
2037
2038
2061
2062 def delete(measurement_type):
2063 if measurement_type.in_use:
2064 gmDispatcher.send (
2065 signal = 'statustext',
2066 beep = True,
2067 msg = _('Cannot delete measurement type [%s (%s)] because it is in use.') % (measurement_type['name'], measurement_type['abbrev'])
2068 )
2069 return False
2070 gmPathLab.delete_measurement_type(measurement_type = measurement_type['pk_test_type'])
2071 return True
2072
2073 def get_tooltip(test_type):
2074 return test_type.format()
2075
2076 def refresh(lctrl):
2077 mtypes = gmPathLab.get_measurement_types(order_by = 'name, abbrev')
2078 items = [ [
2079 m['abbrev'],
2080 m['name'],
2081 gmTools.coalesce(m['conversion_unit'], u''),
2082 gmTools.coalesce(m['loinc'], u''),
2083 gmTools.coalesce(m['comment_type'], u''),
2084 gmTools.coalesce(m['name_org'], u'?'),
2085 gmTools.coalesce(m['comment_org'], u''),
2086 m['pk_test_type']
2087 ] for m in mtypes ]
2088 lctrl.set_string_items(items)
2089 lctrl.set_data(mtypes)
2090
2091 msg = _(
2092 '\n'
2093 'These are the measurement types currently defined in GNUmed.\n'
2094 '\n'
2095 )
2096
2097 gmListWidgets.get_choices_from_list (
2098 parent = parent,
2099 msg = msg,
2100 caption = _('Showing measurement types.'),
2101 columns = [ _('Abbrev'), _('Name'), _('Unit'), _('LOINC'), _('Comment'), _('Org'), _('Comment'), u'#' ],
2102 single_selection = True,
2103 refresh_callback = refresh,
2104 edit_callback = edit,
2105 new_callback = edit,
2106 delete_callback = delete,
2107 list_tooltip_callback = get_tooltip
2108 )
2109
2110
2112
2114
2115 query = u"""
2116 SELECT DISTINCT ON (field_label)
2117 pk_test_type AS data,
2118 name
2119 || ' ('
2120 || coalesce (
2121 (SELECT unit || ' @ ' || organization FROM clin.v_test_orgs c_vto WHERE c_vto.pk_test_org = c_vtt.pk_test_org),
2122 '%(in_house)s'
2123 )
2124 || ')'
2125 AS field_label,
2126 name
2127 || ' ('
2128 || abbrev || ', '
2129 || coalesce(abbrev_meta || ': ' || name_meta || ', ', '')
2130 || coalesce (
2131 (SELECT unit || ' @ ' || organization FROM clin.v_test_orgs c_vto WHERE c_vto.pk_test_org = c_vtt.pk_test_org),
2132 '%(in_house)s'
2133 )
2134 || ')'
2135 AS list_label
2136 FROM
2137 clin.v_test_types c_vtt
2138 WHERE
2139 abbrev_meta %%(fragment_condition)s
2140 OR
2141 name_meta %%(fragment_condition)s
2142 OR
2143 abbrev %%(fragment_condition)s
2144 OR
2145 name %%(fragment_condition)s
2146 ORDER BY field_label
2147 LIMIT 50""" % {'in_house': _('generic / in house lab')}
2148
2149 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
2150 mp.setThresholds(1, 2, 4)
2151 mp.word_separators = '[ \t:@]+'
2152 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
2153 self.matcher = mp
2154 self.SetToolTipString(_('Select the type of measurement.'))
2155 self.selection_only = False
2156
2162
2163 from Gnumed.wxGladeWidgets import wxgMeasurementTypeEAPnl
2164
2165 -class cMeasurementTypeEAPnl(wxgMeasurementTypeEAPnl.wxgMeasurementTypeEAPnl, gmEditArea.cGenericEditAreaMixin):
2166
2183
2184
2186
2187
2188 query = u"""
2189 select distinct on (name)
2190 pk,
2191 name
2192 from clin.test_type
2193 where
2194 name %(fragment_condition)s
2195 order by name
2196 limit 50"""
2197 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
2198 mp.setThresholds(1, 2, 4)
2199 self._PRW_name.matcher = mp
2200 self._PRW_name.selection_only = False
2201 self._PRW_name.add_callback_on_lose_focus(callback = self._on_name_lost_focus)
2202
2203
2204 query = u"""
2205 select distinct on (abbrev)
2206 pk,
2207 abbrev
2208 from clin.test_type
2209 where
2210 abbrev %(fragment_condition)s
2211 order by abbrev
2212 limit 50"""
2213 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
2214 mp.setThresholds(1, 2, 3)
2215 self._PRW_abbrev.matcher = mp
2216 self._PRW_abbrev.selection_only = False
2217
2218
2219 self._PRW_conversion_unit.selection_only = False
2220
2221
2222 mp = gmLOINC.cLOINCMatchProvider()
2223 mp.setThresholds(1, 2, 4)
2224
2225
2226 self._PRW_loinc.matcher = mp
2227 self._PRW_loinc.selection_only = False
2228 self._PRW_loinc.add_callback_on_lose_focus(callback = self._on_loinc_lost_focus)
2229
2231
2232 test = self._PRW_name.GetValue().strip()
2233
2234 if test == u'':
2235 self._PRW_conversion_unit.unset_context(context = u'test_name')
2236 return
2237
2238 self._PRW_conversion_unit.set_context(context = u'test_name', val = test)
2239
2241 loinc = self._PRW_loinc.GetData()
2242
2243 if loinc is None:
2244 self._TCTRL_loinc_info.SetValue(u'')
2245 self._PRW_conversion_unit.unset_context(context = u'loinc')
2246 return
2247
2248 self._PRW_conversion_unit.set_context(context = u'loinc', val = loinc)
2249
2250 info = gmLOINC.loinc2term(loinc = loinc)
2251 if len(info) == 0:
2252 self._TCTRL_loinc_info.SetValue(u'')
2253 return
2254
2255 self._TCTRL_loinc_info.SetValue(info[0])
2256
2257
2258
2260
2261 has_errors = False
2262 for field in [self._PRW_name, self._PRW_abbrev, self._PRW_conversion_unit]:
2263 if field.GetValue().strip() in [u'', None]:
2264 has_errors = True
2265 field.display_as_valid(valid = False)
2266 else:
2267 field.display_as_valid(valid = True)
2268 field.Refresh()
2269
2270 return (not has_errors)
2271
2301
2328
2330 self._PRW_name.SetText(u'', None, True)
2331 self._on_name_lost_focus()
2332 self._PRW_abbrev.SetText(u'', None, True)
2333 self._PRW_conversion_unit.SetText(u'', None, True)
2334 self._PRW_loinc.SetText(u'', None, True)
2335 self._on_loinc_lost_focus()
2336 self._TCTRL_comment_type.SetValue(u'')
2337 self._PRW_test_org.SetText(u'', None, True)
2338 self._PRW_meta_type.SetText(u'', None, True)
2339
2340 self._PRW_name.SetFocus()
2341
2343 self._PRW_name.SetText(self.data['name'], self.data['name'], True)
2344 self._on_name_lost_focus()
2345 self._PRW_abbrev.SetText(self.data['abbrev'], self.data['abbrev'], True)
2346 self._PRW_conversion_unit.SetText (
2347 gmTools.coalesce(self.data['conversion_unit'], u''),
2348 self.data['conversion_unit'],
2349 True
2350 )
2351 self._PRW_loinc.SetText (
2352 gmTools.coalesce(self.data['loinc'], u''),
2353 self.data['loinc'],
2354 True
2355 )
2356 self._on_loinc_lost_focus()
2357 self._TCTRL_comment_type.SetValue(gmTools.coalesce(self.data['comment_type'], u''))
2358 self._PRW_test_org.SetText (
2359 gmTools.coalesce(self.data['pk_test_org'], u'', self.data['name_org']),
2360 self.data['pk_test_org'],
2361 True
2362 )
2363 if self.data['pk_meta_test_type'] is None:
2364 self._PRW_meta_type.SetText(u'', None, True)
2365 else:
2366 self._PRW_meta_type.SetText(u'%s: %s' % (self.data['abbrev_meta'], self.data['name_meta']), self.data['pk_meta_test_type'], True)
2367
2368 self._PRW_name.SetFocus()
2369
2371 self._refresh_as_new()
2372 self._PRW_test_org.SetText (
2373 gmTools.coalesce(self.data['pk_test_org'], u'', self.data['name_org']),
2374 self.data['pk_test_org'],
2375 True
2376 )
2377 self._PRW_name.SetFocus()
2378
2379
2380 _SQL_units_from_test_results = u"""
2381 -- via clin.v_test_results.pk_type (for types already used in results)
2382 SELECT
2383 val_unit AS data,
2384 val_unit AS field_label,
2385 val_unit || ' (' || name_tt || ')' AS list_label,
2386 1 AS rank
2387 FROM
2388 clin.v_test_results
2389 WHERE
2390 (
2391 val_unit %(fragment_condition)s
2392 OR
2393 conversion_unit %(fragment_condition)s
2394 )
2395 %(ctxt_type_pk)s
2396 %(ctxt_test_name)s
2397 """
2398
2399 _SQL_units_from_test_types = u"""
2400 -- via clin.test_type (for types not yet used in results)
2401 SELECT
2402 conversion_unit AS data,
2403 conversion_unit AS field_label,
2404 conversion_unit || ' (' || name || ')' AS list_label,
2405 2 AS rank
2406 FROM
2407 clin.test_type
2408 WHERE
2409 conversion_unit %(fragment_condition)s
2410 %(ctxt_ctt)s
2411 """
2412
2413 _SQL_units_from_loinc_ipcc = u"""
2414 -- via ref.loinc.ipcc_units
2415 SELECT
2416 ipcc_units AS data,
2417 ipcc_units AS field_label,
2418 ipcc_units || ' (LOINC.ipcc: ' || term || ')' AS list_label,
2419 3 AS rank
2420 FROM
2421 ref.loinc
2422 WHERE
2423 ipcc_units %(fragment_condition)s
2424 %(ctxt_loinc)s
2425 %(ctxt_loinc_term)s
2426 """
2427
2428 _SQL_units_from_loinc_submitted = u"""
2429 -- via ref.loinc.submitted_units
2430 SELECT
2431 submitted_units AS data,
2432 submitted_units AS field_label,
2433 submitted_units || ' (LOINC.submitted:' || term || ')' AS list_label,
2434 3 AS rank
2435 FROM
2436 ref.loinc
2437 WHERE
2438 submitted_units %(fragment_condition)s
2439 %(ctxt_loinc)s
2440 %(ctxt_loinc_term)s
2441 """
2442
2443 _SQL_units_from_loinc_example = u"""
2444 -- via ref.loinc.example_units
2445 SELECT
2446 example_units AS data,
2447 example_units AS field_label,
2448 example_units || ' (LOINC.example: ' || term || ')' AS list_label,
2449 3 AS rank
2450 FROM
2451 ref.loinc
2452 WHERE
2453 example_units %(fragment_condition)s
2454 %(ctxt_loinc)s
2455 %(ctxt_loinc_term)s
2456 """
2457
2458 _SQL_units_from_atc = u"""
2459 -- via ref.atc.unit
2460 SELECT
2461 unit AS data,
2462 unit AS field_label,
2463 unit || ' (ATC: ' || term || ')' AS list_label,
2464 2 AS rank
2465 FROM
2466 ref.atc
2467 WHERE
2468 unit IS NOT NULL
2469 AND
2470 unit %(fragment_condition)s
2471 """
2472
2473 _SQL_units_from_consumable_substance = u"""
2474 -- via ref.consumable_substance.unit
2475 SELECT
2476 unit AS data,
2477 unit AS field_label,
2478 unit || ' (' || description || ')' AS list_label,
2479 2 AS rank
2480 FROM
2481 ref.consumable_substance
2482 WHERE
2483 unit %(fragment_condition)s
2484 %(ctxt_substance)s
2485 """
2486
2487
2489
2491
2492 query = u"""
2493 SELECT DISTINCT ON (data)
2494 data,
2495 field_label,
2496 list_label
2497 FROM (
2498
2499 SELECT
2500 data,
2501 field_label,
2502 list_label,
2503 rank
2504 FROM (
2505 (%s) UNION ALL
2506 (%s) UNION ALL
2507 (%s) UNION ALL
2508 (%s) UNION ALL
2509 (%s) UNION ALL
2510 (%s) UNION ALL
2511 (%s)
2512 ) AS all_matching_units
2513 WHERE data IS NOT NULL
2514 ORDER BY rank, list_label
2515
2516 ) AS ranked_matching_units
2517 LIMIT 50""" % (
2518 _SQL_units_from_test_results,
2519 _SQL_units_from_test_types,
2520 _SQL_units_from_loinc_ipcc,
2521 _SQL_units_from_loinc_submitted,
2522 _SQL_units_from_loinc_example,
2523 _SQL_units_from_atc,
2524 _SQL_units_from_consumable_substance
2525 )
2526
2527 ctxt = {
2528 'ctxt_type_pk': {
2529 'where_part': u'AND pk_test_type = %(pk_type)s',
2530 'placeholder': u'pk_type'
2531 },
2532 'ctxt_test_name': {
2533 'where_part': u'AND %(test_name)s IN (name_tt, name_meta, abbrev_meta)',
2534 'placeholder': u'test_name'
2535 },
2536 'ctxt_ctt': {
2537 'where_part': u'AND %(test_name)s IN (name, abbrev)',
2538 'placeholder': u'test_name'
2539 },
2540 'ctxt_loinc': {
2541 'where_part': u'AND code = %(loinc)s',
2542 'placeholder': u'loinc'
2543 },
2544 'ctxt_loinc_term': {
2545 'where_part': u'AND term ~* %(test_name)s',
2546 'placeholder': u'test_name'
2547 },
2548 'ctxt_substance': {
2549 'where_part': u'AND description ~* %(substance)s',
2550 'placeholder': u'substance'
2551 }
2552 }
2553
2554 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query, context = ctxt)
2555 mp.setThresholds(1, 2, 4)
2556
2557 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
2558 self.matcher = mp
2559 self.SetToolTipString(_('Select the desired unit for the amount or measurement.'))
2560 self.selection_only = False
2561 self.phrase_separators = u'[;|]+'
2562
2563
2564
2566
2568
2569 query = u"""
2570 select distinct abnormality_indicator,
2571 abnormality_indicator, abnormality_indicator
2572 from clin.v_test_results
2573 where
2574 abnormality_indicator %(fragment_condition)s
2575 order by abnormality_indicator
2576 limit 25"""
2577
2578 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
2579 mp.setThresholds(1, 1, 2)
2580 mp.ignored_chars = "[.'\\\[\]#$%_]+" + '"'
2581 mp.word_separators = '[ \t&:]+'
2582 gmPhraseWheel.cPhraseWheel.__init__ (
2583 self,
2584 *args,
2585 **kwargs
2586 )
2587 self.matcher = mp
2588 self.SetToolTipString(_('Select an indicator for the level of abnormality.'))
2589 self.selection_only = False
2590
2591
2592
2593
2605
2607
2608 if parent is None:
2609 parent = wx.GetApp().GetTopWindow()
2610
2611
2612 def edit(org=None):
2613 return edit_measurement_org(parent = parent, org = org)
2614
2615 def refresh(lctrl):
2616 orgs = gmPathLab.get_test_orgs()
2617 lctrl.set_string_items ([
2618 (o['unit'], o['organization'], gmTools.coalesce(o['test_org_contact'], u''), gmTools.coalesce(o['comment'], u''), o['pk_test_org'])
2619 for o in orgs
2620 ])
2621 lctrl.set_data(orgs)
2622
2623 def delete(test_org):
2624 gmPathLab.delete_test_org(test_org = test_org['pk_test_org'])
2625 return True
2626
2627 gmListWidgets.get_choices_from_list (
2628 parent = parent,
2629 msg = _('\nThese are the diagnostic orgs (path labs etc) currently defined in GNUmed.\n\n'),
2630 caption = _('Showing diagnostic orgs.'),
2631 columns = [_('Name'), _('Organization'), _('Contact'), _('Comment'), u'#'],
2632 single_selection = True,
2633 refresh_callback = refresh,
2634 edit_callback = edit,
2635 new_callback = edit,
2636 delete_callback = delete
2637 )
2638
2639
2640 from Gnumed.wxGladeWidgets import wxgMeasurementOrgEAPnl
2641
2642 -class cMeasurementOrgEAPnl(wxgMeasurementOrgEAPnl.wxgMeasurementOrgEAPnl, gmEditArea.cGenericEditAreaMixin):
2643
2659
2660
2661
2662
2663
2664
2665
2666
2668 has_errors = False
2669 if self._PRW_org_unit.GetData() is None:
2670 if self._PRW_org_unit.GetValue().strip() == u'':
2671 has_errors = True
2672 self._PRW_org_unit.display_as_valid(valid = False)
2673 else:
2674 self._PRW_org_unit.display_as_valid(valid = True)
2675 else:
2676 self._PRW_org_unit.display_as_valid(valid = True)
2677
2678 return (not has_errors)
2679
2690
2710
2715
2720
2722 self._refresh_as_new()
2723
2726
2727
2729
2731
2732 query = u"""
2733 SELECT DISTINCT ON (list_label)
2734 pk_test_org AS data,
2735 unit || ' (' || organization || ')' AS field_label,
2736 unit || ' @ ' || organization AS list_label
2737 FROM clin.v_test_orgs
2738 WHERE
2739 unit %(fragment_condition)s
2740 OR
2741 organization %(fragment_condition)s
2742 ORDER BY list_label
2743 LIMIT 50"""
2744 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
2745 mp.setThresholds(1, 2, 4)
2746
2747 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
2748 self.matcher = mp
2749 self.SetToolTipString(_('The name of the path lab/diagnostic organisation.'))
2750 self.selection_only = False
2751
2764
2767
2768
2769
2770
2787
2788
2797
2798 def delete(meta_test_type):
2799 gmPathLab.delete_meta_type(meta_type = meta_test_type['pk'])
2800 return True
2801
2802 def get_tooltip(data):
2803 if data is None:
2804 return None
2805 return data.format(with_tests = True)
2806
2807 def refresh(lctrl):
2808 mtts = gmPathLab.get_meta_test_types()
2809 items = [ [
2810 m['abbrev'],
2811 m['name'],
2812 gmTools.coalesce(m['loinc'], u''),
2813 gmTools.coalesce(m['comment'], u''),
2814 m['pk']
2815 ] for m in mtts ]
2816 lctrl.set_string_items(items)
2817 lctrl.set_data(mtts)
2818
2819
2820 msg = _(
2821 '\n'
2822 'These are the meta test types currently defined in GNUmed.\n'
2823 '\n'
2824 'Meta test types allow you to aggregate several actual test types used\n'
2825 'by pathology labs into one logical type.\n'
2826 '\n'
2827 'This is useful for grouping together results of tests which come under\n'
2828 'different names but really are the same thing. This often happens when\n'
2829 'you switch labs or the lab starts using another test method.\n'
2830 )
2831
2832 gmListWidgets.get_choices_from_list (
2833 parent = parent,
2834 msg = msg,
2835 caption = _('Showing meta test types.'),
2836 columns = [_('Abbrev'), _('Name'), _('LOINC'), _('Comment'), u'#'],
2837 single_selection = True,
2838 list_tooltip_callback = get_tooltip,
2839 edit_callback = edit,
2840 new_callback = edit,
2841 delete_callback = delete,
2842 refresh_callback = refresh
2843 )
2844
2845
2890
2891
2892 from Gnumed.wxGladeWidgets import wxgMetaTestTypeEAPnl
2893
3039
3040
3041
3042
3044 ea = cTestPanelEAPnl(parent = parent, id = -1)
3045 ea.data = test_panel
3046 ea.mode = gmTools.coalesce(test_panel, 'new', 'edit')
3047 dlg = gmEditArea.cGenericEditAreaDlg2 (
3048 parent = parent,
3049 id = -1,
3050 edit_area = ea,
3051 single_entry = gmTools.bool2subst((test_panel is None), False, True)
3052 )
3053 dlg.SetTitle(gmTools.coalesce(test_panel, _('Adding new test panel'), _('Editing test panel')))
3054 if dlg.ShowModal() == wx.ID_OK:
3055 dlg.Destroy()
3056 return True
3057 dlg.Destroy()
3058 return False
3059
3060
3062
3063 if parent is None:
3064 parent = wx.GetApp().GetTopWindow()
3065
3066
3067 def edit(test_panel=None):
3068 return edit_test_panel(parent = parent, test_panel = test_panel)
3069
3070 def delete(test_panel):
3071 gmPathLab.delete_test_panel(pk = test_panel['pk_test_panel'])
3072 return True
3073
3074 def get_tooltip(test_panel):
3075 return test_panel.format()
3076
3077 def refresh(lctrl):
3078 panels = gmPathLab.get_test_panels(order_by = 'description')
3079 items = [ [
3080 p['description'],
3081 gmTools.coalesce(p['comment'], u''),
3082 p['pk_test_panel']
3083 ] for p in panels ]
3084 lctrl.set_string_items(items)
3085 lctrl.set_data(panels)
3086
3087 msg = _(
3088 '\n'
3089 'Test panels as defined in GNUmed.\n'
3090 )
3091
3092 gmListWidgets.get_choices_from_list (
3093 parent = parent,
3094 msg = msg,
3095 caption = _('Showing test panels.'),
3096 columns = [ _('Name'), _('Comment'), u'#' ],
3097 single_selection = True,
3098 refresh_callback = refresh,
3099 edit_callback = edit,
3100 new_callback = edit,
3101 delete_callback = delete,
3102 list_tooltip_callback = get_tooltip
3103 )
3104
3105
3107
3109 query = u"""
3110 SELECT
3111 pk_test_panel
3112 AS data,
3113 description
3114 AS field_label,
3115 description
3116 AS list_label
3117 FROM
3118 clin.v_test_panels
3119 WHERE
3120 description %(fragment_condition)s
3121 ORDER BY field_label
3122 LIMIT 30"""
3123 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
3124 mp.setThresholds(1, 2, 4)
3125
3126 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
3127 self.matcher = mp
3128 self.SetToolTipString(_('Select a test panel.'))
3129 self.selection_only = True
3130
3135
3140
3141
3142 from Gnumed.wxGladeWidgets import wxgTestPanelEAPnl
3143
3144 -class cTestPanelEAPnl(wxgTestPanelEAPnl.wxgTestPanelEAPnl, gmEditArea.cGenericEditAreaMixin):
3145
3163
3164
3165
3166
3167
3168
3169
3170
3172 validity = True
3173
3174 if self._test_types is None:
3175 validity = False
3176 gmDispatcher.send(signal = 'statustext', msg = _('No test types selected.'))
3177 self._BTN_select_tests.SetFocus()
3178
3179 if self._TCTRL_description.GetValue().strip() == u'':
3180 validity = False
3181 self.display_tctrl_as_valid(tctrl = self._TCTRL_description, valid = False)
3182 self._TCTRL_description.SetFocus()
3183 else:
3184 self.display_tctrl_as_valid(tctrl = self._TCTRL_description, valid = True)
3185
3186 return validity
3187
3196
3198 self.data['description'] = self._TCTRL_description.GetValue().strip()
3199 self.data['comment'] = self._TCTRL_comment.GetValue().strip()
3200 self.data['pk_test_types'] = [ tt['pk_test_type'] for tt in self._test_types ]
3201 self.data.save()
3202 self.data.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ]
3203 return True
3204
3206 self._TCTRL_tests.SetValue(u'')
3207 self._test_types = test_types
3208 if self._test_types is None:
3209 return
3210 tmp = u';\n'.join ([
3211 u'%s: %s%s' % (
3212 t['unified_abbrev'],
3213 t['unified_name'],
3214 gmTools.coalesce(t['name_org'], u'', u' (%s)')
3215 )
3216 for t in self._test_types
3217 ])
3218 self._TCTRL_tests.SetValue(tmp)
3219
3221 self._TCTRL_description.SetValue(u'')
3222 self._TCTRL_comment.SetValue(u'')
3223 self.__refresh_test_types_field()
3224 self._PRW_codes.SetText()
3225
3226 self._TCTRL_description.SetFocus()
3227
3231
3240
3256
3257
3258
3259
3260 if __name__ == '__main__':
3261
3262 from Gnumed.pycommon import gmLog2
3263 from Gnumed.wxpython import gmPatSearchWidgets
3264
3265 gmI18N.activate_locale()
3266 gmI18N.install_domain()
3267 gmDateTime.init()
3268
3269
3277
3285
3286
3287
3288
3289
3290
3291
3292 if (len(sys.argv) > 1) and (sys.argv[1] == 'test'):
3293
3294 test_test_ea_pnl()
3295
3296
3297
3298