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.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
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 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
760 tmp = u'%s%.6s' % (tmp, abnormality_indicator)
761
762
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
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
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
786
787
788
789
790
791
792
793
794
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
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
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
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
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
850 vals2display = []
851 cell_has_out_of_bounds_value = False
852 for sub_result in self.__cell_data[col][row]:
853
854
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
865
866 sub_result_relevant = sub_result['is_clinically_relevant']
867 if sub_result_relevant is None:
868
869 sub_result_relevant = False
870
871 missing_review = False
872
873
874 if not sub_result['reviewed']:
875 missing_review = True
876
877 else:
878
879 if sub_result['you_are_responsible'] and not sub_result['review_by_you']:
880 missing_review = True
881
882
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
889 tmp = u'%s%.6s' % (tmp, abnormality_indicator)
890
891
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
900 if missing_review:
901 tmp = u'%s %s' % (tmp, gmTools.u_writing_hand)
902
903
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
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
928
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
954
976
977
978
980 self.CreateGrid(0, 1)
981 self.EnableEditing(0)
982 self.EnableDragGridSize(1)
983 self.SetMinSize(wx.DefaultSize)
984
985
986
987
988
989
990 self.SetRowLabelSize(wx.grid.GRID_AUTOSIZE)
991
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
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()
1007
1008 LNK_lab = wx.lib.hyperlink.HyperLinkCtrl (
1009 self.__WIN_corner,
1010 -1,
1011 label = _('Tests'),
1012 style = wx.HL_DEFAULT_STYLE
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)
1025 SZR_inner.Add(LNK_lab, 0, wx.ALIGN_CENTER_VERTICAL, 0)
1026 SZR_inner.Add((20, 20), 1, wx.EXPAND, 0)
1027
1028 SZR_corner = wx.BoxSizer(wx.VERTICAL)
1029 SZR_corner.Add((20, 20), 1, wx.EXPAND, 0)
1030 SZR_corner.AddWindow(SZR_inner, 0, wx.EXPAND)
1031 SZR_corner.Add((20, 20), 1, wx.EXPAND, 0)
1032
1033 self.__WIN_corner.SetSizer(SZR_corner)
1034 SZR_corner.Fit(self.__WIN_corner)
1035
1037 self.__WIN_corner.Layout()
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
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
1085
1087
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
1091
1092
1093 self.Bind(wx.EVT_SIZE, self.__resize_corner_window)
1094
1095
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
1103 try:
1104 self.__cell_data[col][row]
1105 except KeyError:
1106
1107
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
1121
1122
1123
1124
1125
1127
1128
1129
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
1142
1143
1144
1145
1146
1147
1149 """Calculate where the mouse is and set the tooltip dynamically."""
1150
1151
1152
1153 x, y = self.CalcUnscrolledPosition(evt.GetX(), evt.GetY())
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
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
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
1208
1209
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
1218 wx.CallAfter(self.__on_post_patient_selection)
1219
1221 self._schedule_data_reget()
1222
1224 wx.CallAfter(self.__on_pre_patient_selection)
1225
1227 self.data_grid.patient = None
1228 self.panel_data_grid.patient = None
1229
1232
1236
1239
1245
1248
1251
1254
1257
1259 wx.CallAfter(self.__on_panel_selected, panel=panel)
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
1276
1278 wx.CallAfter(self.__on_panel_selection_modified)
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
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
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
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
1315
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
1328
1339
1340
1341
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
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
1472
1473
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
1782
1787
1789 self.__refresh_loinc_info()
1790 self.__refresh_previous_value()
1791 self.__update_units_context()
1792
1793 self.__update_normal_range()
1794 self.__update_clinical_range()
1795
1797
1798 self.__update_normal_range()
1799 self.__update_clinical_range()
1800
1802
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
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
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
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
1928
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
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
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
2132
2133 from Gnumed.wxGladeWidgets import wxgMeasurementTypeEAPnl
2134
2135 -class cMeasurementTypeEAPnl(wxgMeasurementTypeEAPnl.wxgMeasurementTypeEAPnl, gmEditArea.cGenericEditAreaMixin):
2136
2153
2154
2156
2157
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
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
2189 self._PRW_conversion_unit.selection_only = False
2190
2191
2192 mp = gmLOINC.cLOINCMatchProvider()
2193 mp.setThresholds(1, 2, 4)
2194
2195
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
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
2271
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
2341 self._refresh_as_new()
2342 self._PRW_test_org.SetText (
2343 gmTools.coalesce(self.data['pk_test_org'], u'', self.data['name_org']),
2344 self.data['pk_test_org'],
2345 True
2346 )
2347 self._PRW_name.SetFocus()
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
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
2563
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
2629
2630
2631
2632
2633
2634
2635
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
2660
2680
2685
2690
2692 self._refresh_as_new()
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
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
2734
2737
2738
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
2781
2782
2783
2784 )
2785
2830
2831
2832
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
2917 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
2918 self.matcher = mp
2919 self.SetToolTipString(_('Select a test panel.'))
2920 self.selection_only = True
2921
2926
2931
2932
2933 from Gnumed.wxGladeWidgets import wxgTestPanelEAPnl
2934
2935 -class cTestPanelEAPnl(wxgTestPanelEAPnl.wxgTestPanelEAPnl, gmEditArea.cGenericEditAreaMixin):
2936
2954
2955
2956
2957
2958
2959
2960
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
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
3031
3047
3048
3049
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
3068
3076
3077
3078
3079
3080
3081
3082
3083 if (len(sys.argv) > 1) and (sys.argv[1] == 'test'):
3084
3085 test_test_ea_pnl()
3086
3087
3088
3089