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 get_tooltip(measurement):
281 return measurement.format(with_review=True, with_evaluation=True, with_ranges=True)
282
283 def refresh(lctrl):
284 results = emr.get_test_results(order_by = 'clin_when DESC, unified_abbrev, unified_name')
285 items = [ [
286 gmDateTime.pydt_strftime (
287 r['clin_when'],
288 '%Y %b %d %H:%M',
289 accuracy = gmDateTime.acc_minutes
290 ),
291 r['unified_abbrev'],
292 u'%s%s%s' % (
293 r['unified_val'],
294 gmTools.coalesce(r['val_unit'], u'', u' %s'),
295 gmTools.coalesce(r['abnormality_indicator'], u'', u' %s')
296 ),
297 r['unified_name'],
298 gmTools.coalesce(r['comment'], u''),
299 r['pk_test_result']
300 ] for r in results ]
301 lctrl.set_string_items(items)
302 lctrl.set_data(results)
303
304 msg = _('Test results (ordered reverse-chronologically)')
305
306 return gmListWidgets.get_choices_from_list (
307 parent = parent,
308 msg = msg,
309 caption = _('Showing test results.'),
310 columns = [ _('When'), _('Abbrev'), _('Value'), _('Name'), _('Comment'), u'#' ],
311 single_selection = single_selection,
312 can_return_empty = False,
313 refresh_callback = refresh,
314 edit_callback = edit,
315 new_callback = edit,
316 delete_callback = delete,
317 list_tooltip_callback = get_tooltip
318 )
319
320
351
352
354
355 option = u'form_templates.default_gnuplot_template'
356
357 dbcfg = gmCfg.cCfgSQL()
358
359
360 default_template_name = dbcfg.get2 (
361 option = option,
362 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
363 bias = 'user'
364 )
365
366
367 if default_template_name is None:
368 gmDispatcher.send('statustext', msg = _('No default Gnuplot template configured.'), beep = False)
369 default_template = configure_default_gnuplot_template(parent = parent)
370
371 if default_template is None:
372 gmGuiHelpers.gm_show_error (
373 aMessage = _('There is no default Gnuplot one-type script template configured.'),
374 aTitle = _('Plotting test results')
375 )
376 return None
377 return default_template
378
379
380
381 try:
382 name, ver = default_template_name.split(u' - ')
383 except:
384
385 _log.exception('problem splitting Gnuplot script template name [%s]', default_template_name)
386 gmDispatcher.send(signal = 'statustext', msg = _('Problem loading Gnuplot script template.'), beep = True)
387 return None
388
389 default_template = gmForms.get_form_template(name_long = name, external_version = ver)
390 if default_template is None:
391 default_template = configure_default_gnuplot_template(parent = parent)
392
393 if default_template is None:
394 gmGuiHelpers.gm_show_error (
395 aMessage = _('Cannot load default Gnuplot script template [%s - %s]') % (name, ver),
396 aTitle = _('Plotting test results')
397 )
398 return None
399
400 return default_template
401
402
403 -def plot_measurements(parent=None, tests=None, format=None, show_year = True, use_default_template=False):
429
430
431 -def plot_adjacent_measurements(parent=None, test=None, format=None, show_year=True, plot_singular_result=True, use_default_template=False):
432
433 earlier, later = test.get_adjacent_results(desired_earlier_results = 2, desired_later_results = 2)
434 results2plot = []
435 if earlier is not None:
436 results2plot.extend(earlier)
437 results2plot.append(test)
438 if later is not None:
439 results2plot.extend(later)
440 if len(results2plot) == 1:
441 if not plot_singular_result:
442 return
443 plot_measurements (
444 parent = parent,
445 tests = results2plot,
446 format = format,
447 show_year = show_year,
448 use_default_template = use_default_template
449 )
450
451
452
453
454
455
456
457
458
459
460
462 """A grid class for displaying measurment results.
463
464 - does NOT listen to the currently active patient
465 - thereby it can display any patient at any time
466 """
467
468
469
470
471
472
474
475 wx.grid.Grid.__init__(self, *args, **kwargs)
476
477 self.__patient = None
478 self.__panel_to_show = None
479 self.__show_by_panel = False
480 self.__cell_data = {}
481 self.__row_label_data = []
482
483 self.__prev_row = None
484 self.__prev_col = None
485 self.__prev_label_row = None
486 self.__date_format = str((_('lab_grid_date_format::%Y\n%b %d')).lstrip('lab_grid_date_format::'))
487
488 self.__init_ui()
489 self.__register_events()
490
491
492
494 if not self.IsSelection():
495 gmDispatcher.send(signal = u'statustext', msg = _('No results selected for deletion.'))
496 return True
497
498 selected_cells = self.get_selected_cells()
499 if len(selected_cells) > 20:
500 results = None
501 msg = _(
502 'There are %s results marked for deletion.\n'
503 '\n'
504 'Are you sure you want to delete these results ?'
505 ) % len(selected_cells)
506 else:
507 results = self.__cells_to_data(cells = selected_cells, exclude_multi_cells = False)
508 txt = u'\n'.join([ u'%s %s (%s): %s %s%s' % (
509 r['clin_when'].strftime('%x %H:%M').decode(gmI18N.get_encoding()),
510 r['unified_abbrev'],
511 r['unified_name'],
512 r['unified_val'],
513 r['val_unit'],
514 gmTools.coalesce(r['abnormality_indicator'], u'', u' (%s)')
515 ) for r in results
516 ])
517 msg = _(
518 'The following results are marked for deletion:\n'
519 '\n'
520 '%s\n'
521 '\n'
522 'Are you sure you want to delete these results ?'
523 ) % txt
524
525 dlg = gmGuiHelpers.c2ButtonQuestionDlg (
526 self,
527 -1,
528 caption = _('Deleting test results'),
529 question = msg,
530 button_defs = [
531 {'label': _('Delete'), 'tooltip': _('Yes, delete all the results.'), 'default': False},
532 {'label': _('Cancel'), 'tooltip': _('No, do NOT delete any results.'), 'default': True}
533 ]
534 )
535 decision = dlg.ShowModal()
536
537 if decision == wx.ID_YES:
538 if results is None:
539 results = self.__cells_to_data(cells = selected_cells, exclude_multi_cells = False)
540 for result in results:
541 gmPathLab.delete_test_result(result)
542
544 if not self.IsSelection():
545 gmDispatcher.send(signal = u'statustext', msg = _('Cannot sign results. No results selected.'))
546 return True
547
548 selected_cells = self.get_selected_cells()
549 if len(selected_cells) > 10:
550 test_count = len(selected_cells)
551 tests = None
552 else:
553 test_count = None
554 tests = self.__cells_to_data(cells = selected_cells, exclude_multi_cells = False)
555 if len(tests) == 0:
556 return True
557
558 dlg = cMeasurementsReviewDlg (
559 self,
560 -1,
561 tests = tests,
562 test_count = test_count
563 )
564 decision = dlg.ShowModal()
565
566 if decision == wx.ID_APPLY:
567 wx.BeginBusyCursor()
568
569 if dlg._RBTN_confirm_abnormal.GetValue():
570 abnormal = None
571 elif dlg._RBTN_results_normal.GetValue():
572 abnormal = False
573 else:
574 abnormal = True
575
576 if dlg._RBTN_confirm_relevance.GetValue():
577 relevant = None
578 elif dlg._RBTN_results_not_relevant.GetValue():
579 relevant = False
580 else:
581 relevant = True
582
583 if tests is None:
584 tests = self.__cells_to_data(cells = selected_cells, exclude_multi_cells = False)
585
586 comment = None
587 if len(tests) == 1:
588 comment = dlg._TCTRL_comment.GetValue()
589
590 for test in tests:
591 test.set_review (
592 technically_abnormal = abnormal,
593 clinically_relevant = relevant,
594 comment = comment,
595 make_me_responsible = dlg._CHBOX_responsible.IsChecked()
596 )
597
598 wx.EndBusyCursor()
599
600 dlg.Destroy()
601
603
604 if not self.IsSelection():
605 gmDispatcher.send(signal = u'statustext', msg = _('Cannot plot results. No results selected.'))
606 return True
607
608 tests = self.__cells_to_data (
609 cells = self.get_selected_cells(),
610 exclude_multi_cells = False,
611 auto_include_multi_cells = True
612 )
613
614 plot_measurements(parent = self, tests = tests)
615
617
618 sel_block_top_left = self.GetSelectionBlockTopLeft()
619 sel_block_bottom_right = self.GetSelectionBlockBottomRight()
620 sel_cols = self.GetSelectedCols()
621 sel_rows = self.GetSelectedRows()
622
623 selected_cells = []
624
625
626 selected_cells += self.GetSelectedCells()
627
628
629 selected_cells += list (
630 (row, col)
631 for row in sel_rows
632 for col in xrange(self.GetNumberCols())
633 )
634
635
636 selected_cells += list (
637 (row, col)
638 for row in xrange(self.GetNumberRows())
639 for col in sel_cols
640 )
641
642
643 for top_left, bottom_right in zip(self.GetSelectionBlockTopLeft(), self.GetSelectionBlockBottomRight()):
644 selected_cells += [
645 (row, col)
646 for row in xrange(top_left[0], bottom_right[0] + 1)
647 for col in xrange(top_left[1], bottom_right[1] + 1)
648 ]
649
650 return set(selected_cells)
651
652 - def select_cells(self, unsigned_only=False, accountables_only=False, keep_preselections=False):
653 """Select a range of cells according to criteria.
654
655 unsigned_only: include only those which are not signed at all yet
656 accountable_only: include only those for which the current user is responsible
657 keep_preselections: broaden (rather than replace) the range of selected cells
658
659 Combinations are powerful !
660 """
661 wx.BeginBusyCursor()
662 self.BeginBatch()
663
664 if not keep_preselections:
665 self.ClearSelection()
666
667 for col_idx in self.__cell_data.keys():
668 for row_idx in self.__cell_data[col_idx].keys():
669
670
671 do_not_include = False
672 for result in self.__cell_data[col_idx][row_idx]:
673 if unsigned_only:
674 if result['reviewed']:
675 do_not_include = True
676 break
677 if accountables_only:
678 if not result['you_are_responsible']:
679 do_not_include = True
680 break
681 if do_not_include:
682 continue
683
684 self.SelectBlock(row_idx, col_idx, row_idx, col_idx, addToSelected = True)
685
686 self.EndBatch()
687 wx.EndBusyCursor()
688
690 self.empty_grid()
691 if self.__patient is None:
692 return
693
694 if self.__show_by_panel:
695 self.__repopulate_grid_by_panel()
696 return
697
698 self.__repopulate_grid_all_results()
699
701
702 if self.__panel_to_show is None:
703 return
704
705 emr = self.__patient.get_emr()
706
707
708 self.__row_label_data = self.__panel_to_show.test_types
709 row_labels = [ u'%s%s' % (
710 gmTools.bool2subst(test['is_fake_meta_type'], u'', gmTools.u_sum, u''),
711 test['unified_abbrev']
712 ) for test in self.__row_label_data
713 ]
714 if len(row_labels) == 0:
715 return
716
717
718 column_labels = [
719 date[0].strftime(self.__date_format) for date in emr.get_dates_for_results (
720 tests = self.__panel_to_show['pk_test_types'],
721
722 reverse_chronological = True
723 )
724 ]
725 results = emr.get_test_results_by_date (
726 tests = self.__panel_to_show['pk_test_types'],
727
728 reverse_chronological = True
729 )
730
731 self.BeginBatch()
732
733
734 self.AppendRows(numRows = len(row_labels))
735 for row_idx in range(len(row_labels)):
736 self.SetRowLabelValue(row_idx, row_labels[row_idx])
737
738
739 self.AppendCols(numCols = len(column_labels))
740 for date_idx in range(len(column_labels)):
741 self.SetColLabelValue(date_idx, column_labels[date_idx])
742
743
744 for result in results:
745 row = row_labels.index(u'%s%s' % (
746 gmTools.bool2subst(result['is_fake_meta_type'], u'', gmTools.u_sum, u''),
747 result['unified_abbrev']
748 ))
749 col = column_labels.index(result['clin_when'].strftime(self.__date_format))
750
751 try:
752 self.__cell_data[col]
753 except KeyError:
754 self.__cell_data[col] = {}
755
756
757 if self.__cell_data[col].has_key(row):
758 self.__cell_data[col][row].append(result)
759 self.__cell_data[col][row].sort(key = lambda x: x['clin_when'], reverse = True)
760 else:
761 self.__cell_data[col][row] = [result]
762
763
764 vals2display = []
765 for sub_result in self.__cell_data[col][row]:
766
767
768 ind = gmTools.coalesce(sub_result['abnormality_indicator'], u'').strip()
769 if ind != u'':
770 lab_abnormality_indicator = u' (%s)' % ind[:3]
771 else:
772 lab_abnormality_indicator = u''
773
774 if sub_result['is_technically_abnormal'] is None:
775 abnormality_indicator = lab_abnormality_indicator
776
777 elif sub_result['is_technically_abnormal'] is False:
778 abnormality_indicator = u''
779
780 else:
781
782 if lab_abnormality_indicator == u'':
783
784 abnormality_indicator = u' (%s)' % gmTools.u_plus_minus
785
786 else:
787 abnormality_indicator = lab_abnormality_indicator
788
789
790
791 sub_result_relevant = sub_result['is_clinically_relevant']
792 if sub_result_relevant is None:
793
794 sub_result_relevant = False
795
796 missing_review = False
797
798
799 if not sub_result['reviewed']:
800 missing_review = True
801
802 else:
803
804 if sub_result['you_are_responsible'] and not sub_result['review_by_you']:
805 missing_review = True
806
807
808 if len(sub_result['unified_val']) > 8:
809 tmp = u'%.7s%s' % (sub_result['unified_val'][:7], gmTools.u_ellipsis)
810 else:
811 tmp = u'%.8s' % sub_result['unified_val'][:8]
812
813
814 tmp = u'%s%.6s' % (tmp, abnormality_indicator)
815
816
817 has_sub_result_comment = gmTools.coalesce (
818 gmTools.coalesce(sub_result['note_test_org'], sub_result['comment']),
819 u''
820 ).strip() != u''
821 if has_sub_result_comment:
822 tmp = u'%s %s' % (tmp, gmTools.u_ellipsis)
823
824
825 if missing_review:
826 tmp = u'%s %s' % (tmp, gmTools.u_writing_hand)
827
828
829 if len(self.__cell_data[col][row]) > 1:
830 tmp = u'%s %s' % (sub_result['clin_when'].strftime('%H:%M'), tmp)
831
832 vals2display.append(tmp)
833
834 self.SetCellValue(row, col, u'\n'.join(vals2display))
835 self.SetCellAlignment(row, col, horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE)
836
837
838
839
840 if sub_result_relevant:
841 font = self.GetCellFont(row, col)
842 self.SetCellTextColour(row, col, 'firebrick')
843 font.SetWeight(wx.FONTWEIGHT_BOLD)
844 self.SetCellFont(row, col, font)
845
846
847 self.AutoSize()
848 self.EndBatch()
849 return
850
852 emr = self.__patient.get_emr()
853
854 self.__row_label_data = emr.get_test_types_for_results()
855 test_type_labels = [ u'%s%s' % (
856 gmTools.bool2subst(test['is_fake_meta_type'], u'', gmTools.u_sum, u''),
857 test['unified_abbrev']
858 ) for test in self.__row_label_data
859 ]
860 if len(test_type_labels) == 0:
861 return
862
863 test_date_labels = [ date[0].strftime(self.__date_format) for date in emr.get_dates_for_results() ]
864 results = emr.get_test_results_by_date()
865
866 self.BeginBatch()
867
868
869 self.AppendRows(numRows = len(test_type_labels))
870 for row_idx in range(len(test_type_labels)):
871 self.SetRowLabelValue(row_idx, test_type_labels[row_idx])
872
873
874 self.AppendCols(numCols = len(test_date_labels))
875 for date_idx in range(len(test_date_labels)):
876 self.SetColLabelValue(date_idx, test_date_labels[date_idx])
877
878
879 for result in results:
880 row = test_type_labels.index(u'%s%s' % (
881 gmTools.bool2subst(result['is_fake_meta_type'], u'', gmTools.u_sum, u''),
882 result['unified_abbrev']
883 ))
884 col = test_date_labels.index(result['clin_when'].strftime(self.__date_format))
885
886 try:
887 self.__cell_data[col]
888 except KeyError:
889 self.__cell_data[col] = {}
890
891
892 if self.__cell_data[col].has_key(row):
893 self.__cell_data[col][row].append(result)
894 self.__cell_data[col][row].sort(key = lambda x: x['clin_when'], reverse = True)
895 else:
896 self.__cell_data[col][row] = [result]
897
898
899 vals2display = []
900 for sub_result in self.__cell_data[col][row]:
901
902
903 ind = gmTools.coalesce(sub_result['abnormality_indicator'], u'').strip()
904 if ind != u'':
905 lab_abnormality_indicator = u' (%s)' % ind[:3]
906 else:
907 lab_abnormality_indicator = u''
908
909 if sub_result['is_technically_abnormal'] is None:
910 abnormality_indicator = lab_abnormality_indicator
911
912 elif sub_result['is_technically_abnormal'] is False:
913 abnormality_indicator = u''
914
915 else:
916
917 if lab_abnormality_indicator == u'':
918
919 abnormality_indicator = u' (%s)' % gmTools.u_plus_minus
920
921 else:
922 abnormality_indicator = lab_abnormality_indicator
923
924
925
926 sub_result_relevant = sub_result['is_clinically_relevant']
927 if sub_result_relevant is None:
928
929 sub_result_relevant = False
930
931 missing_review = False
932
933
934 if not sub_result['reviewed']:
935 missing_review = True
936
937 else:
938
939 if sub_result['you_are_responsible'] and not sub_result['review_by_you']:
940 missing_review = True
941
942
943 if len(sub_result['unified_val']) > 8:
944 tmp = u'%.7s%s' % (sub_result['unified_val'][:7], gmTools.u_ellipsis)
945 else:
946 tmp = u'%.8s' % sub_result['unified_val'][:8]
947
948
949 tmp = u'%s%.6s' % (tmp, abnormality_indicator)
950
951
952 has_sub_result_comment = gmTools.coalesce (
953 gmTools.coalesce(sub_result['note_test_org'], sub_result['comment']),
954 u''
955 ).strip() != u''
956 if has_sub_result_comment:
957 tmp = u'%s %s' % (tmp, gmTools.u_ellipsis)
958
959
960 if missing_review:
961 tmp = u'%s %s' % (tmp, gmTools.u_writing_hand)
962
963
964 if len(self.__cell_data[col][row]) > 1:
965 tmp = u'%s %s' % (sub_result['clin_when'].strftime('%H:%M'), tmp)
966
967 vals2display.append(tmp)
968
969 self.SetCellValue(row, col, u'\n'.join(vals2display))
970 self.SetCellAlignment(row, col, horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE)
971
972
973
974
975 if sub_result_relevant:
976 font = self.GetCellFont(row, col)
977 self.SetCellTextColour(row, col, 'firebrick')
978 font.SetWeight(wx.FONTWEIGHT_BOLD)
979 self.SetCellFont(row, col, font)
980
981
982 self.AutoSize()
983 self.EndBatch()
984 return
985
987 self.BeginBatch()
988 self.ClearGrid()
989
990
991 if self.GetNumberRows() > 0:
992 self.DeleteRows(pos = 0, numRows = self.GetNumberRows())
993 if self.GetNumberCols() > 0:
994 self.DeleteCols(pos = 0, numCols = self.GetNumberCols())
995 self.EndBatch()
996 self.__cell_data = {}
997 self.__row_label_data = []
998
1028
1050
1051
1052
1054 self.CreateGrid(0, 1)
1055 self.EnableEditing(0)
1056 self.EnableDragGridSize(1)
1057 self.SetMinSize(wx.DefaultSize)
1058
1059
1060
1061
1062 self.SetRowLabelSize(wx.grid.GRID_AUTOSIZE)
1063
1064 self.SetRowLabelAlignment(horiz = wx.ALIGN_LEFT, vert = wx.ALIGN_CENTRE)
1065
1066
1067 dbcfg = gmCfg.cCfgSQL()
1068 url = dbcfg.get2 (
1069 option = u'external.urls.measurements_encyclopedia',
1070 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
1071 bias = 'user',
1072 default = u'http://www.laborlexikon.de'
1073 )
1074
1075 self.__WIN_corner = self.GetGridCornerLabelWindow()
1076
1077 LNK_lab = wx.lib.hyperlink.HyperLinkCtrl (
1078 self.__WIN_corner,
1079 -1,
1080 label = _('Tests'),
1081 style = wx.HL_DEFAULT_STYLE
1082 )
1083 LNK_lab.SetURL(url)
1084 LNK_lab.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BACKGROUND))
1085 LNK_lab.SetToolTipString(_(
1086 'Navigate to an encyclopedia of measurements\n'
1087 'and test methods on the web.\n'
1088 '\n'
1089 ' <%s>'
1090 ) % url)
1091
1092 SZR_inner = wx.BoxSizer(wx.HORIZONTAL)
1093 SZR_inner.Add((20, 20), 1, wx.EXPAND, 0)
1094 SZR_inner.Add(LNK_lab, 0, wx.ALIGN_CENTER_VERTICAL, 0)
1095 SZR_inner.Add((20, 20), 1, wx.EXPAND, 0)
1096
1097 SZR_corner = wx.BoxSizer(wx.VERTICAL)
1098 SZR_corner.Add((20, 20), 1, wx.EXPAND, 0)
1099 SZR_corner.AddWindow(SZR_inner, 0, wx.EXPAND)
1100 SZR_corner.Add((20, 20), 1, wx.EXPAND, 0)
1101
1102 self.__WIN_corner.SetSizer(SZR_corner)
1103 SZR_corner.Fit(self.__WIN_corner)
1104
1106 self.__WIN_corner.Layout()
1107
1108 - def __cells_to_data(self, cells=None, exclude_multi_cells=False, auto_include_multi_cells=False):
1109 """List of <cells> must be in row / col order."""
1110 data = []
1111 for row, col in cells:
1112 try:
1113
1114 data_list = self.__cell_data[col][row]
1115 except KeyError:
1116 continue
1117
1118 if len(data_list) == 1:
1119 data.append(data_list[0])
1120 continue
1121
1122 if exclude_multi_cells:
1123 gmDispatcher.send(signal = u'statustext', msg = _('Excluding multi-result field from further processing.'))
1124 continue
1125
1126 if auto_include_multi_cells:
1127 data.extend(data_list)
1128 continue
1129
1130 data_to_include = self.__get_choices_from_multi_cell(cell_data = data_list)
1131 if data_to_include is None:
1132 continue
1133 data.extend(data_to_include)
1134
1135 return data
1136
1138 data = gmListWidgets.get_choices_from_list (
1139 parent = self,
1140 msg = _(
1141 'Your selection includes a field with multiple results.\n'
1142 '\n'
1143 'Please select the individual results you want to work on:'
1144 ),
1145 caption = _('Selecting test results'),
1146 choices = [ [d['clin_when'], u'%s: %s' % (d['abbrev_tt'], d['name_tt']), d['unified_val']] for d in cell_data ],
1147 columns = [ _('Date / Time'), _('Test'), _('Result') ],
1148 data = cell_data,
1149 single_selection = single_selection
1150 )
1151 return data
1152
1153
1154
1156
1157 self.GetGridWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_cells)
1158 self.GetGridRowLabelWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_row_labels)
1159
1160
1161
1162 self.Bind(wx.EVT_SIZE, self.__resize_corner_window)
1163
1164
1165 self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.__on_cell_left_dclicked)
1166
1168 col = evt.GetCol()
1169 row = evt.GetRow()
1170
1171
1172 try:
1173 self.__cell_data[col][row]
1174 except KeyError:
1175
1176
1177 return
1178
1179 if len(self.__cell_data[col][row]) > 1:
1180 data = self.__get_choices_from_multi_cell(cell_data = self.__cell_data[col][row], single_selection = True)
1181 else:
1182 data = self.__cell_data[col][row][0]
1183
1184 if data is None:
1185 return
1186
1187 edit_measurement(parent = self, measurement = data, single_entry = True)
1188
1189
1190
1191
1192
1193
1194
1196
1197
1198
1199 x, y = self.CalcUnscrolledPosition(evt.GetX(), evt.GetY())
1200
1201 row = self.YToRow(y)
1202
1203 if self.__prev_label_row == row:
1204 return
1205
1206 self.__prev_label_row == row
1207
1208 evt.GetEventObject().SetToolTipString(self.get_row_tooltip(row = row))
1209
1210
1211
1212
1213
1214
1215
1216
1218 """Calculate where the mouse is and set the tooltip dynamically."""
1219
1220
1221
1222 x, y = self.CalcUnscrolledPosition(evt.GetX(), evt.GetY())
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236 row, col = self.XYToCell(x, y)
1237
1238 if (row == self.__prev_row) and (col == self.__prev_col):
1239 return
1240
1241 self.__prev_row = row
1242 self.__prev_col = col
1243
1244 evt.GetEventObject().SetToolTipString(self.get_cell_tooltip(col=col, row=row))
1245
1246
1247
1251
1252 patient = property(lambda x:x, _set_patient)
1253
1257
1258 panel_to_show = property(lambda x:x, _set_panel_to_show)
1259
1263
1264 show_by_panel = property(lambda x:x, _set_show_by_panel)
1265
1266 from Gnumed.wxGladeWidgets import wxgMeasurementsPnl
1267
1268 -class cMeasurementsPnl(wxgMeasurementsPnl.wxgMeasurementsPnl, gmRegetMixin.cRegetOnPaintMixin):
1269 """Panel holding a grid with lab data. Used as notebook page."""
1270
1277
1278
1279
1281 gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._on_pre_patient_selection)
1282 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._on_post_patient_selection)
1283 gmDispatcher.connect(signal = u'test_result_mod_db', receiver = self._schedule_data_reget)
1284 gmDispatcher.connect(signal = u'reviewed_test_results_mod_db', receiver = self._schedule_data_reget)
1285
1287 wx.CallAfter(self.__on_post_patient_selection)
1288
1290 self._schedule_data_reget()
1291
1293 wx.CallAfter(self.__on_pre_patient_selection)
1294
1296 self.data_grid.patient = None
1297 self.panel_data_grid.patient = None
1298
1301
1304
1310
1313
1316
1319
1322
1324 wx.CallAfter(self.__on_panel_selected, panel=panel)
1325
1327 if panel is None:
1328 self._TCTRL_panel_comment.SetValue(u'')
1329 self.panel_data_grid.panel_to_show = None
1330 self.panel_data_grid.Hide()
1331 else:
1332 pnl = self._PRW_panel.GetData(as_instance = True)
1333 self._TCTRL_panel_comment.SetValue(gmTools.coalesce (
1334 pnl['comment'],
1335 u''
1336 ))
1337 self.panel_data_grid.panel_to_show = pnl
1338 self.panel_data_grid.Show()
1339 self.Layout()
1340
1341
1343 wx.CallAfter(self.__on_panel_selection_modified)
1344
1346 self._TCTRL_panel_comment.SetValue(u'')
1347 if self._PRW_panel.GetValue().strip() == u'':
1348 self.panel_data_grid.panel_to_show = None
1349 self.panel_data_grid.Hide()
1350 self.Layout()
1351
1352
1353
1355 self.__action_button_popup = wx.Menu(title = _('Perform on selected results:'))
1356
1357 menu_id = wx.NewId()
1358 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('Review and &sign')))
1359 wx.EVT_MENU(self.__action_button_popup, menu_id, self.__on_sign_current_selection)
1360
1361 menu_id = wx.NewId()
1362 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('Plot')))
1363 wx.EVT_MENU(self.__action_button_popup, menu_id, self.__on_plot_current_selection)
1364
1365 menu_id = wx.NewId()
1366 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('Export to &file')))
1367
1368 self.__action_button_popup.Enable(id = menu_id, enable = False)
1369
1370 menu_id = wx.NewId()
1371 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('Export to &clipboard')))
1372
1373 self.__action_button_popup.Enable(id = menu_id, enable = False)
1374
1375 menu_id = wx.NewId()
1376 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('&Delete')))
1377 wx.EVT_MENU(self.__action_button_popup, menu_id, self.__on_delete_current_selection)
1378
1379
1380
1381
1382 self._PRW_panel.add_callback_on_selection(callback = self._on_panel_selected)
1383 self._PRW_panel.add_callback_on_modified(callback = self._on_panel_selection_modified)
1384
1385 self.panel_data_grid.show_by_panel = True
1386 self.panel_data_grid.panel_to_show = None
1387 self.panel_data_grid.Hide()
1388 self.Layout()
1389
1390 self._PRW_panel.SetFocus()
1391
1392
1393
1404
1405
1406
1407
1408 from Gnumed.wxGladeWidgets import wxgMeasurementsReviewDlg
1409
1411
1413
1414 try:
1415 tests = kwargs['tests']
1416 del kwargs['tests']
1417 test_count = len(tests)
1418 try: del kwargs['test_count']
1419 except KeyError: pass
1420 except KeyError:
1421 tests = None
1422 test_count = kwargs['test_count']
1423 del kwargs['test_count']
1424
1425 wxgMeasurementsReviewDlg.wxgMeasurementsReviewDlg.__init__(self, *args, **kwargs)
1426
1427 if tests is None:
1428 msg = _('%s results selected. Too many to list individually.') % test_count
1429 else:
1430 msg = ' // '.join (
1431 [ u'%s: %s %s (%s)' % (
1432 t['unified_abbrev'],
1433 t['unified_val'],
1434 t['val_unit'],
1435 gmDateTime.pydt_strftime(t['clin_when'], '%Y %b %d')
1436 ) for t in tests
1437 ]
1438 )
1439
1440 self._LBL_tests.SetLabel(msg)
1441
1442 if test_count == 1:
1443 self._TCTRL_comment.Enable(True)
1444 self._TCTRL_comment.SetValue(gmTools.coalesce(tests[0]['review_comment'], u''))
1445 if tests[0]['you_are_responsible']:
1446 self._CHBOX_responsible.Enable(False)
1447
1448 self.Fit()
1449
1450
1451
1457
1458 from Gnumed.wxGladeWidgets import wxgMeasurementEditAreaPnl
1459
1460 -class cMeasurementEditAreaPnl(wxgMeasurementEditAreaPnl.wxgMeasurementEditAreaPnl, gmEditArea.cGenericEditAreaMixin):
1461 """This edit area saves *new* measurements into the active patient only."""
1462
1479
1480
1481
1483 self._PRW_test.SetText(u'', None, True)
1484 self.__refresh_loinc_info()
1485 self.__refresh_previous_value()
1486 self.__update_units_context()
1487 self._TCTRL_result.SetValue(u'')
1488 self._PRW_units.SetText(u'', None, True)
1489 self._PRW_abnormality_indicator.SetText(u'', None, True)
1490 if self.__default_date is None:
1491 self._DPRW_evaluated.SetData(data = pyDT.datetime.now(tz = gmDateTime.gmCurrentLocalTimezone))
1492 else:
1493 self._DPRW_evaluated.SetData(data = None)
1494 self._TCTRL_note_test_org.SetValue(u'')
1495 self._PRW_intended_reviewer.SetData(gmStaff.gmCurrentProvider()['pk_staff'])
1496 self._PRW_problem.SetData()
1497 self._TCTRL_narrative.SetValue(u'')
1498 self._CHBOX_review.SetValue(False)
1499 self._CHBOX_abnormal.SetValue(False)
1500 self._CHBOX_relevant.SetValue(False)
1501 self._CHBOX_abnormal.Enable(False)
1502 self._CHBOX_relevant.Enable(False)
1503 self._TCTRL_review_comment.SetValue(u'')
1504 self._TCTRL_normal_min.SetValue(u'')
1505 self._TCTRL_normal_max.SetValue(u'')
1506 self._TCTRL_normal_range.SetValue(u'')
1507 self._TCTRL_target_min.SetValue(u'')
1508 self._TCTRL_target_max.SetValue(u'')
1509 self._TCTRL_target_range.SetValue(u'')
1510 self._TCTRL_norm_ref_group.SetValue(u'')
1511
1512 self._PRW_test.SetFocus()
1513
1515 self._PRW_test.SetData(data = self.data['pk_test_type'])
1516 self.__refresh_loinc_info()
1517 self.__refresh_previous_value()
1518 self.__update_units_context()
1519 self._TCTRL_result.SetValue(self.data['unified_val'])
1520 self._PRW_units.SetText(self.data['val_unit'], self.data['val_unit'], True)
1521 self._PRW_abnormality_indicator.SetText (
1522 gmTools.coalesce(self.data['abnormality_indicator'], u''),
1523 gmTools.coalesce(self.data['abnormality_indicator'], u''),
1524 True
1525 )
1526 self._DPRW_evaluated.SetData(data = self.data['clin_when'])
1527 self._TCTRL_note_test_org.SetValue(gmTools.coalesce(self.data['note_test_org'], u''))
1528 self._PRW_intended_reviewer.SetData(self.data['pk_intended_reviewer'])
1529 self._PRW_problem.SetData(self.data['pk_episode'])
1530 self._TCTRL_narrative.SetValue(gmTools.coalesce(self.data['comment'], u''))
1531 self._CHBOX_review.SetValue(False)
1532 self._CHBOX_abnormal.SetValue(gmTools.coalesce(self.data['is_technically_abnormal'], False))
1533 self._CHBOX_relevant.SetValue(gmTools.coalesce(self.data['is_clinically_relevant'], False))
1534 self._CHBOX_abnormal.Enable(False)
1535 self._CHBOX_relevant.Enable(False)
1536 self._TCTRL_review_comment.SetValue(gmTools.coalesce(self.data['review_comment'], u''))
1537 self._TCTRL_normal_min.SetValue(unicode(gmTools.coalesce(self.data['val_normal_min'], u'')))
1538 self._TCTRL_normal_max.SetValue(unicode(gmTools.coalesce(self.data['val_normal_max'], u'')))
1539 self._TCTRL_normal_range.SetValue(gmTools.coalesce(self.data['val_normal_range'], u''))
1540 self._TCTRL_target_min.SetValue(unicode(gmTools.coalesce(self.data['val_target_min'], u'')))
1541 self._TCTRL_target_max.SetValue(unicode(gmTools.coalesce(self.data['val_target_max'], u'')))
1542 self._TCTRL_target_range.SetValue(gmTools.coalesce(self.data['val_target_range'], u''))
1543 self._TCTRL_norm_ref_group.SetValue(gmTools.coalesce(self.data['norm_ref_group'], u''))
1544
1545 self._TCTRL_result.SetFocus()
1546
1548 self._refresh_from_existing()
1549
1550 self._PRW_test.SetText(u'', None, True)
1551 self.__refresh_loinc_info()
1552 self.__refresh_previous_value()
1553 self.__update_units_context()
1554 self._TCTRL_result.SetValue(u'')
1555 self._PRW_units.SetText(u'', None, True)
1556 self._PRW_abnormality_indicator.SetText(u'', None, True)
1557
1558 self._TCTRL_note_test_org.SetValue(u'')
1559 self._TCTRL_narrative.SetValue(u'')
1560 self._CHBOX_review.SetValue(False)
1561 self._CHBOX_abnormal.SetValue(False)
1562 self._CHBOX_relevant.SetValue(False)
1563 self._CHBOX_abnormal.Enable(False)
1564 self._CHBOX_relevant.Enable(False)
1565 self._TCTRL_review_comment.SetValue(u'')
1566 self._TCTRL_normal_min.SetValue(u'')
1567 self._TCTRL_normal_max.SetValue(u'')
1568 self._TCTRL_normal_range.SetValue(u'')
1569 self._TCTRL_target_min.SetValue(u'')
1570 self._TCTRL_target_max.SetValue(u'')
1571 self._TCTRL_target_range.SetValue(u'')
1572 self._TCTRL_norm_ref_group.SetValue(u'')
1573
1574 self._PRW_test.SetFocus()
1575
1577
1578 validity = True
1579
1580 if not self._DPRW_evaluated.is_valid_timestamp():
1581 self._DPRW_evaluated.display_as_valid(False)
1582 validity = False
1583 else:
1584 self._DPRW_evaluated.display_as_valid(True)
1585
1586 val = self._TCTRL_result.GetValue().strip()
1587 if val == u'':
1588 validity = False
1589 self.display_ctrl_as_valid(self._TCTRL_result, False)
1590 else:
1591 self.display_ctrl_as_valid(self._TCTRL_result, True)
1592 numeric, val = gmTools.input2decimal(val)
1593 if numeric:
1594 if self._PRW_units.GetValue().strip() == u'':
1595 self._PRW_units.display_as_valid(False)
1596 validity = False
1597 else:
1598 self._PRW_units.display_as_valid(True)
1599 else:
1600 self._PRW_units.display_as_valid(True)
1601
1602 if self._PRW_problem.GetValue().strip() == u'':
1603 self._PRW_problem.display_as_valid(False)
1604 validity = False
1605 else:
1606 self._PRW_problem.display_as_valid(True)
1607
1608 if self._PRW_test.GetValue().strip() == u'':
1609 self._PRW_test.display_as_valid(False)
1610 validity = False
1611 else:
1612 self._PRW_test.display_as_valid(True)
1613
1614 if self._PRW_intended_reviewer.GetData() is None:
1615 self._PRW_intended_reviewer.display_as_valid(False)
1616 validity = False
1617 else:
1618 self._PRW_intended_reviewer.display_as_valid(True)
1619
1620 ctrls = [self._TCTRL_normal_min, self._TCTRL_normal_max, self._TCTRL_target_min, self._TCTRL_target_max]
1621 for widget in ctrls:
1622 val = widget.GetValue().strip()
1623 if val == u'':
1624 continue
1625 try:
1626 decimal.Decimal(val.replace(',', u'.', 1))
1627 self.display_ctrl_as_valid(widget, True)
1628 except:
1629 validity = False
1630 self.display_ctrl_as_valid(widget, False)
1631
1632 if validity is False:
1633 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save result. Invalid or missing essential input.'))
1634
1635 return validity
1636
1638
1639 emr = gmPerson.gmCurrentPatient().get_emr()
1640
1641 success, result = gmTools.input2decimal(self._TCTRL_result.GetValue())
1642 if success:
1643 v_num = result
1644 v_al = None
1645 else:
1646 v_al = self._TCTRL_result.GetValue().strip()
1647 v_num = None
1648
1649 pk_type = self._PRW_test.GetData()
1650 if pk_type is None:
1651 tt = gmPathLab.create_measurement_type (
1652 lab = None,
1653 abbrev = self._PRW_test.GetValue().strip(),
1654 name = self._PRW_test.GetValue().strip(),
1655 unit = gmTools.coalesce(self._PRW_units.GetData(), self._PRW_units.GetValue()).strip()
1656 )
1657 pk_type = tt['pk_test_type']
1658
1659 tr = emr.add_test_result (
1660 episode = self._PRW_problem.GetData(can_create=True, is_open=False),
1661 type = pk_type,
1662 intended_reviewer = self._PRW_intended_reviewer.GetData(),
1663 val_num = v_num,
1664 val_alpha = v_al,
1665 unit = self._PRW_units.GetValue()
1666 )
1667
1668 tr['clin_when'] = self._DPRW_evaluated.GetData().get_pydt()
1669
1670 ctrls = [
1671 ('abnormality_indicator', self._PRW_abnormality_indicator),
1672 ('note_test_org', self._TCTRL_note_test_org),
1673 ('comment', self._TCTRL_narrative),
1674 ('val_normal_range', self._TCTRL_normal_range),
1675 ('val_target_range', self._TCTRL_target_range),
1676 ('norm_ref_group', self._TCTRL_norm_ref_group)
1677 ]
1678 for field, widget in ctrls:
1679 tr[field] = widget.GetValue().strip()
1680
1681 ctrls = [
1682 ('val_normal_min', self._TCTRL_normal_min),
1683 ('val_normal_max', self._TCTRL_normal_max),
1684 ('val_target_min', self._TCTRL_target_min),
1685 ('val_target_max', self._TCTRL_target_max)
1686 ]
1687 for field, widget in ctrls:
1688 val = widget.GetValue().strip()
1689 if val == u'':
1690 tr[field] = None
1691 else:
1692 tr[field] = decimal.Decimal(val.replace(',', u'.', 1))
1693
1694 tr.save_payload()
1695
1696 if self._CHBOX_review.GetValue() is True:
1697 tr.set_review (
1698 technically_abnormal = self._CHBOX_abnormal.GetValue(),
1699 clinically_relevant = self._CHBOX_relevant.GetValue(),
1700 comment = gmTools.none_if(self._TCTRL_review_comment.GetValue().strip(), u''),
1701 make_me_responsible = False
1702 )
1703
1704 self.data = tr
1705
1706 wx.CallAfter (
1707 plot_adjacent_measurements,
1708 test = self.data,
1709 plot_singular_result = False,
1710 use_default_template = True
1711 )
1712
1713 return True
1714
1716
1717 success, result = gmTools.input2decimal(self._TCTRL_result.GetValue())
1718 if success:
1719 v_num = result
1720 v_al = None
1721 else:
1722 v_num = None
1723 v_al = self._TCTRL_result.GetValue().strip()
1724
1725 pk_type = self._PRW_test.GetData()
1726 if pk_type is None:
1727 tt = gmPathLab.create_measurement_type (
1728 lab = None,
1729 abbrev = self._PRW_test.GetValue().strip(),
1730 name = self._PRW_test.GetValue().strip(),
1731 unit = gmTools.none_if(self._PRW_units.GetValue().strip(), u'')
1732 )
1733 pk_type = tt['pk_test_type']
1734
1735 tr = self.data
1736
1737 tr['pk_episode'] = self._PRW_problem.GetData(can_create=True, is_open=False)
1738 tr['pk_test_type'] = pk_type
1739 tr['pk_intended_reviewer'] = self._PRW_intended_reviewer.GetData()
1740 tr['val_num'] = v_num
1741 tr['val_alpha'] = v_al
1742 tr['val_unit'] = gmTools.coalesce(self._PRW_units.GetData(), self._PRW_units.GetValue()).strip()
1743 tr['clin_when'] = self._DPRW_evaluated.GetData().get_pydt()
1744
1745 ctrls = [
1746 ('abnormality_indicator', self._PRW_abnormality_indicator),
1747 ('note_test_org', self._TCTRL_note_test_org),
1748 ('comment', self._TCTRL_narrative),
1749 ('val_normal_range', self._TCTRL_normal_range),
1750 ('val_target_range', self._TCTRL_target_range),
1751 ('norm_ref_group', self._TCTRL_norm_ref_group)
1752 ]
1753 for field, widget in ctrls:
1754 tr[field] = widget.GetValue().strip()
1755
1756 ctrls = [
1757 ('val_normal_min', self._TCTRL_normal_min),
1758 ('val_normal_max', self._TCTRL_normal_max),
1759 ('val_target_min', self._TCTRL_target_min),
1760 ('val_target_max', self._TCTRL_target_max)
1761 ]
1762 for field, widget in ctrls:
1763 val = widget.GetValue().strip()
1764 if val == u'':
1765 tr[field] = None
1766 else:
1767 tr[field] = decimal.Decimal(val.replace(',', u'.', 1))
1768
1769 tr.save_payload()
1770
1771 if self._CHBOX_review.GetValue() is True:
1772 tr.set_review (
1773 technically_abnormal = self._CHBOX_abnormal.GetValue(),
1774 clinically_relevant = self._CHBOX_relevant.GetValue(),
1775 comment = gmTools.none_if(self._TCTRL_review_comment.GetValue().strip(), u''),
1776 make_me_responsible = False
1777 )
1778
1779 wx.CallAfter (
1780 plot_adjacent_measurements,
1781 test = self.data,
1782 plot_singular_result = False,
1783 use_default_template = True
1784 )
1785
1786 return True
1787
1788
1789
1793
1795 self.__refresh_loinc_info()
1796 self.__refresh_previous_value()
1797 self.__update_units_context()
1798
1800
1801 if not self._CHBOX_review.GetValue():
1802 self._CHBOX_abnormal.SetValue(self._PRW_abnormality_indicator.GetValue().strip() != u'')
1803
1805 self._CHBOX_abnormal.Enable(self._CHBOX_review.GetValue())
1806 self._CHBOX_relevant.Enable(self._CHBOX_review.GetValue())
1807 self._TCTRL_review_comment.Enable(self._CHBOX_review.GetValue())
1808
1825
1826
1827
1829
1830 self._PRW_units.unset_context(context = u'loinc')
1831
1832 tt = self._PRW_test.GetData(as_instance = True)
1833
1834 if tt is None:
1835 self._PRW_units.unset_context(context = u'pk_type')
1836 if self._PRW_test.GetValue().strip() == u'':
1837 self._PRW_units.unset_context(context = u'test_name')
1838 else:
1839 self._PRW_units.set_context(context = u'test_name', val = self._PRW_test.GetValue().strip())
1840 return
1841
1842 self._PRW_units.set_context(context = u'pk_type', val = tt['pk_test_type'])
1843 self._PRW_units.set_context(context = u'test_name', val = tt['name'])
1844
1845 if tt['loinc'] is None:
1846 return
1847
1848 self._PRW_units.set_context(context = u'loinc', val = tt['loinc'])
1849
1851
1852 self._TCTRL_loinc.SetValue(u'')
1853
1854 if self._PRW_test.GetData() is None:
1855 return
1856
1857 tt = self._PRW_test.GetData(as_instance = True)
1858
1859 if tt['loinc'] is None:
1860 return
1861
1862 info = gmLOINC.loinc2term(loinc = tt['loinc'])
1863 if len(info) == 0:
1864 self._TCTRL_loinc.SetValue(u'')
1865 return
1866
1867 self._TCTRL_loinc.SetValue(u'%s: %s' % (tt['loinc'], info[0]))
1868
1891
1892
1893
1894
1896
1897 if parent is None:
1898 parent = wx.GetApp().GetTopWindow()
1899
1900 if msg is None:
1901 msg = _('Pick the relevant measurement types.')
1902
1903 if right_column is None:
1904 right_columns = [_('Picked')]
1905 else:
1906 right_columns = [right_column]
1907
1908 picker = gmListWidgets.cItemPickerDlg(parent, -1, msg = msg)
1909 picker.set_columns(columns = [_('Known measurement types')], columns_right = right_columns)
1910 types = gmPathLab.get_measurement_types(order_by = 'unified_abbrev')
1911 picker.set_choices (
1912 choices = [
1913 u'%s: %s%s' % (
1914 t['unified_abbrev'],
1915 t['unified_name'],
1916 gmTools.coalesce(t['name_org'], u'', u' (%s)')
1917 )
1918 for t in types
1919 ],
1920 data = types
1921 )
1922 if picks is not None:
1923 picker.set_picks (
1924 picks = [
1925 u'%s: %s%s' % (
1926 p['unified_abbrev'],
1927 p['unified_name'],
1928 gmTools.coalesce(p['name_org'], u'', u' (%s)')
1929 )
1930 for p in picks
1931 ],
1932 data = picks
1933 )
1934 result = picker.ShowModal()
1935
1936 if result == wx.ID_CANCEL:
1937 picker.Destroy()
1938 return None
1939
1940 picks = picker.picks
1941 picker.Destroy()
1942 return picks
1943
1944
1967
1968 def delete(measurement_type):
1969 if measurement_type.in_use:
1970 gmDispatcher.send (
1971 signal = 'statustext',
1972 beep = True,
1973 msg = _('Cannot delete measurement type [%s (%s)] because it is in use.') % (measurement_type['name'], measurement_type['abbrev'])
1974 )
1975 return False
1976 gmPathLab.delete_measurement_type(measurement_type = measurement_type['pk_test_type'])
1977 return True
1978
1979 def get_tooltip(test_type):
1980 return test_type.format()
1981
1982 def refresh(lctrl):
1983 mtypes = gmPathLab.get_measurement_types(order_by = 'name, abbrev')
1984 items = [ [
1985 m['abbrev'],
1986 m['name'],
1987 gmTools.coalesce(m['conversion_unit'], u''),
1988 gmTools.coalesce(m['loinc'], u''),
1989 gmTools.coalesce(m['comment_type'], u''),
1990 gmTools.coalesce(m['name_org'], u'?'),
1991 gmTools.coalesce(m['comment_org'], u''),
1992 m['pk_test_type']
1993 ] for m in mtypes ]
1994 lctrl.set_string_items(items)
1995 lctrl.set_data(mtypes)
1996
1997 msg = _(
1998 '\n'
1999 'These are the measurement types currently defined in GNUmed.\n'
2000 '\n'
2001 )
2002
2003 gmListWidgets.get_choices_from_list (
2004 parent = parent,
2005 msg = msg,
2006 caption = _('Showing measurement types.'),
2007 columns = [ _('Abbrev'), _('Name'), _('Unit'), _('LOINC'), _('Comment'), _('Org'), _('Comment'), u'#' ],
2008 single_selection = True,
2009 refresh_callback = refresh,
2010 edit_callback = edit,
2011 new_callback = edit,
2012 delete_callback = delete,
2013 list_tooltip_callback = get_tooltip
2014 )
2015
2016
2018
2020
2021 query = u"""
2022 SELECT DISTINCT ON (field_label)
2023 pk_test_type AS data,
2024 name
2025 || ' ('
2026 || coalesce (
2027 (SELECT unit || ' @ ' || organization FROM clin.v_test_orgs c_vto WHERE c_vto.pk_test_org = c_vtt.pk_test_org),
2028 '%(in_house)s'
2029 )
2030 || ')'
2031 AS field_label,
2032 name
2033 || ' ('
2034 || abbrev || ', '
2035 || coalesce(abbrev_meta || ': ' || name_meta || ', ', '')
2036 || coalesce (
2037 (SELECT unit || ' @ ' || organization FROM clin.v_test_orgs c_vto WHERE c_vto.pk_test_org = c_vtt.pk_test_org),
2038 '%(in_house)s'
2039 )
2040 || ')'
2041 AS list_label
2042 FROM
2043 clin.v_test_types c_vtt
2044 WHERE
2045 abbrev_meta %%(fragment_condition)s
2046 OR
2047 name_meta %%(fragment_condition)s
2048 OR
2049 abbrev %%(fragment_condition)s
2050 OR
2051 name %%(fragment_condition)s
2052 ORDER BY field_label
2053 LIMIT 50""" % {'in_house': _('generic / in house lab')}
2054
2055 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
2056 mp.setThresholds(1, 2, 4)
2057 mp.word_separators = '[ \t:@]+'
2058 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
2059 self.matcher = mp
2060 self.SetToolTipString(_('Select the type of measurement.'))
2061 self.selection_only = False
2062
2068
2069 from Gnumed.wxGladeWidgets import wxgMeasurementTypeEAPnl
2070
2071 -class cMeasurementTypeEAPnl(wxgMeasurementTypeEAPnl.wxgMeasurementTypeEAPnl, gmEditArea.cGenericEditAreaMixin):
2072
2089
2090
2092
2093
2094 query = u"""
2095 select distinct on (name)
2096 pk,
2097 name
2098 from clin.test_type
2099 where
2100 name %(fragment_condition)s
2101 order by name
2102 limit 50"""
2103 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
2104 mp.setThresholds(1, 2, 4)
2105 self._PRW_name.matcher = mp
2106 self._PRW_name.selection_only = False
2107 self._PRW_name.add_callback_on_lose_focus(callback = self._on_name_lost_focus)
2108
2109
2110 query = u"""
2111 select distinct on (abbrev)
2112 pk,
2113 abbrev
2114 from clin.test_type
2115 where
2116 abbrev %(fragment_condition)s
2117 order by abbrev
2118 limit 50"""
2119 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
2120 mp.setThresholds(1, 2, 3)
2121 self._PRW_abbrev.matcher = mp
2122 self._PRW_abbrev.selection_only = False
2123
2124
2125 self._PRW_conversion_unit.selection_only = False
2126
2127
2128
2129 mp = gmLOINC.cLOINCMatchProvider()
2130 mp.setThresholds(1, 2, 4)
2131
2132
2133 self._PRW_loinc.matcher = mp
2134 self._PRW_loinc.selection_only = False
2135 self._PRW_loinc.add_callback_on_lose_focus(callback = self._on_loinc_lost_focus)
2136
2138
2139 test = self._PRW_name.GetValue().strip()
2140
2141 if test == u'':
2142 self._PRW_conversion_unit.unset_context(context = u'test_name')
2143 return
2144
2145 self._PRW_conversion_unit.set_context(context = u'test_name', val = test)
2146
2148 loinc = self._PRW_loinc.GetData()
2149
2150 if loinc is None:
2151 self._TCTRL_loinc_info.SetValue(u'')
2152 self._PRW_conversion_unit.unset_context(context = u'loinc')
2153 return
2154
2155 self._PRW_conversion_unit.set_context(context = u'loinc', val = loinc)
2156
2157 info = gmLOINC.loinc2term(loinc = loinc)
2158 if len(info) == 0:
2159 self._TCTRL_loinc_info.SetValue(u'')
2160 return
2161
2162 self._TCTRL_loinc_info.SetValue(info[0])
2163
2164
2165
2167
2168 has_errors = False
2169 for field in [self._PRW_name, self._PRW_abbrev, self._PRW_conversion_unit]:
2170 if field.GetValue().strip() in [u'', None]:
2171 has_errors = True
2172 field.display_as_valid(valid = False)
2173 else:
2174 field.display_as_valid(valid = True)
2175 field.Refresh()
2176
2177 return (not has_errors)
2178
2180
2181 pk_org = self._PRW_test_org.GetData()
2182 if pk_org is None:
2183 pk_org = gmPathLab.create_test_org (
2184 name = gmTools.none_if(self._PRW_test_org.GetValue().strip(), u''),
2185 comment = gmTools.none_if(self._TCTRL_comment_org.GetValue().strip(), u'')
2186 )['pk_test_org']
2187
2188 tt = gmPathLab.create_measurement_type (
2189 lab = pk_org,
2190 abbrev = self._PRW_abbrev.GetValue().strip(),
2191 name = self._PRW_name.GetValue().strip(),
2192 unit = gmTools.coalesce (
2193 self._PRW_conversion_unit.GetData(),
2194 self._PRW_conversion_unit.GetValue()
2195 ).strip()
2196 )
2197 if self._PRW_loinc.GetData() is not None:
2198 tt['loinc'] = gmTools.none_if(self._PRW_loinc.GetData().strip(), u'')
2199 else:
2200 tt['loinc'] = gmTools.none_if(self._PRW_loinc.GetValue().strip(), u'')
2201 tt['comment_type'] = gmTools.none_if(self._TCTRL_comment_type.GetValue().strip(), u'')
2202 tt['pk_meta_test_type'] = self._PRW_meta_type.GetData()
2203
2204 tt.save()
2205
2206 self.data = tt
2207
2208 return True
2209
2237
2239 self._PRW_name.SetText(u'', None, True)
2240 self._on_name_lost_focus()
2241 self._PRW_abbrev.SetText(u'', None, True)
2242 self._PRW_conversion_unit.SetText(u'', None, True)
2243 self._PRW_loinc.SetText(u'', None, True)
2244 self._on_loinc_lost_focus()
2245 self._TCTRL_comment_type.SetValue(u'')
2246 self._PRW_test_org.SetText(u'', None, True)
2247 self._TCTRL_comment_org.SetValue(u'')
2248 self._PRW_meta_type.SetText(u'', None, True)
2249
2250 self._PRW_name.SetFocus()
2251
2253 self._PRW_name.SetText(self.data['name'], self.data['name'], True)
2254 self._on_name_lost_focus()
2255 self._PRW_abbrev.SetText(self.data['abbrev'], self.data['abbrev'], True)
2256 self._PRW_conversion_unit.SetText (
2257 gmTools.coalesce(self.data['conversion_unit'], u''),
2258 self.data['conversion_unit'],
2259 True
2260 )
2261 self._PRW_loinc.SetText (
2262 gmTools.coalesce(self.data['loinc'], u''),
2263 self.data['loinc'],
2264 True
2265 )
2266 self._on_loinc_lost_focus()
2267 self._TCTRL_comment_type.SetValue(gmTools.coalesce(self.data['comment_type'], u''))
2268 self._PRW_test_org.SetText (
2269 gmTools.coalesce(self.data['pk_test_org'], u'', self.data['name_org']),
2270 self.data['pk_test_org'],
2271 True
2272 )
2273 self._TCTRL_comment_org.SetValue(gmTools.coalesce(self.data['comment_org'], u''))
2274 if self.data['pk_meta_test_type'] is None:
2275 self._PRW_meta_type.SetText(u'', None, True)
2276 else:
2277 self._PRW_meta_type.SetText(u'%s: %s' % (self.data['abbrev_meta'], self.data['name_meta']), self.data['pk_meta_test_type'], True)
2278
2279 self._PRW_name.SetFocus()
2280
2291
2292
2293 _SQL_units_from_test_results = u"""
2294 -- via clin.v_test_results.pk_type (for types already used in results)
2295 SELECT
2296 val_unit AS data,
2297 val_unit AS field_label,
2298 val_unit || ' (' || name_tt || ')' AS list_label,
2299 1 AS rank
2300 FROM
2301 clin.v_test_results
2302 WHERE
2303 (
2304 val_unit %(fragment_condition)s
2305 OR
2306 conversion_unit %(fragment_condition)s
2307 )
2308 %(ctxt_type_pk)s
2309 %(ctxt_test_name)s
2310 """
2311
2312 _SQL_units_from_test_types = u"""
2313 -- via clin.test_type (for types not yet used in results)
2314 SELECT
2315 conversion_unit AS data,
2316 conversion_unit AS field_label,
2317 conversion_unit || ' (' || name || ')' AS list_label,
2318 2 AS rank
2319 FROM
2320 clin.test_type
2321 WHERE
2322 conversion_unit %(fragment_condition)s
2323 %(ctxt_ctt)s
2324 """
2325
2326 _SQL_units_from_loinc_ipcc = u"""
2327 -- via ref.loinc.ipcc_units
2328 SELECT
2329 ipcc_units AS data,
2330 ipcc_units AS field_label,
2331 ipcc_units || ' (LOINC.ipcc: ' || term || ')' AS list_label,
2332 3 AS rank
2333 FROM
2334 ref.loinc
2335 WHERE
2336 ipcc_units %(fragment_condition)s
2337 %(ctxt_loinc)s
2338 %(ctxt_loinc_term)s
2339 """
2340
2341 _SQL_units_from_loinc_submitted = u"""
2342 -- via ref.loinc.submitted_units
2343 SELECT
2344 submitted_units AS data,
2345 submitted_units AS field_label,
2346 submitted_units || ' (LOINC.submitted:' || term || ')' AS list_label,
2347 3 AS rank
2348 FROM
2349 ref.loinc
2350 WHERE
2351 submitted_units %(fragment_condition)s
2352 %(ctxt_loinc)s
2353 %(ctxt_loinc_term)s
2354 """
2355
2356 _SQL_units_from_loinc_example = u"""
2357 -- via ref.loinc.example_units
2358 SELECT
2359 example_units AS data,
2360 example_units AS field_label,
2361 example_units || ' (LOINC.example: ' || term || ')' AS list_label,
2362 3 AS rank
2363 FROM
2364 ref.loinc
2365 WHERE
2366 example_units %(fragment_condition)s
2367 %(ctxt_loinc)s
2368 %(ctxt_loinc_term)s
2369 """
2370
2371 _SQL_units_from_atc = u"""
2372 -- via ref.atc.unit
2373 SELECT
2374 unit AS data,
2375 unit AS field_label,
2376 unit || ' (ATC: ' || term || ')' AS list_label,
2377 2 AS rank
2378 FROM
2379 ref.atc
2380 WHERE
2381 unit IS NOT NULL
2382 AND
2383 unit %(fragment_condition)s
2384 """
2385
2386 _SQL_units_from_consumable_substance = u"""
2387 -- via ref.consumable_substance.unit
2388 SELECT
2389 unit AS data,
2390 unit AS field_label,
2391 unit || ' (' || description || ')' AS list_label,
2392 2 AS rank
2393 FROM
2394 ref.consumable_substance
2395 WHERE
2396 unit %(fragment_condition)s
2397 %(ctxt_substance)s
2398 """
2399
2400
2402
2404
2405 query = u"""
2406 SELECT DISTINCT ON (data)
2407 data,
2408 field_label,
2409 list_label
2410 FROM (
2411
2412 SELECT
2413 data,
2414 field_label,
2415 list_label,
2416 rank
2417 FROM (
2418 (%s) UNION ALL
2419 (%s) UNION ALL
2420 (%s) UNION ALL
2421 (%s) UNION ALL
2422 (%s) UNION ALL
2423 (%s) UNION ALL
2424 (%s)
2425 ) AS all_matching_units
2426 WHERE data IS NOT NULL
2427 ORDER BY rank, list_label
2428
2429 ) AS ranked_matching_units
2430 LIMIT 50""" % (
2431 _SQL_units_from_test_results,
2432 _SQL_units_from_test_types,
2433 _SQL_units_from_loinc_ipcc,
2434 _SQL_units_from_loinc_submitted,
2435 _SQL_units_from_loinc_example,
2436 _SQL_units_from_atc,
2437 _SQL_units_from_consumable_substance
2438 )
2439
2440 ctxt = {
2441 'ctxt_type_pk': {
2442 'where_part': u'AND pk_test_type = %(pk_type)s',
2443 'placeholder': u'pk_type'
2444 },
2445 'ctxt_test_name': {
2446 'where_part': u'AND %(test_name)s IN (name_tt, name_meta, abbrev_meta)',
2447 'placeholder': u'test_name'
2448 },
2449 'ctxt_ctt': {
2450 'where_part': u'AND %(test_name)s IN (name, abbrev)',
2451 'placeholder': u'test_name'
2452 },
2453 'ctxt_loinc': {
2454 'where_part': u'AND code = %(loinc)s',
2455 'placeholder': u'loinc'
2456 },
2457 'ctxt_loinc_term': {
2458 'where_part': u'AND term ~* %(test_name)s',
2459 'placeholder': u'test_name'
2460 },
2461 'ctxt_substance': {
2462 'where_part': u'AND description ~* %(substance)s',
2463 'placeholder': u'substance'
2464 }
2465 }
2466
2467 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query, context = ctxt)
2468 mp.setThresholds(1, 2, 4)
2469
2470 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
2471 self.matcher = mp
2472 self.SetToolTipString(_('Select the desired unit for the amount or measurement.'))
2473 self.selection_only = False
2474 self.phrase_separators = u'[;|]+'
2475
2476
2477
2479
2481
2482 query = u"""
2483 select distinct abnormality_indicator,
2484 abnormality_indicator, abnormality_indicator
2485 from clin.v_test_results
2486 where
2487 abnormality_indicator %(fragment_condition)s
2488 order by abnormality_indicator
2489 limit 25"""
2490
2491 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
2492 mp.setThresholds(1, 1, 2)
2493 mp.ignored_chars = "[.'\\\[\]#$%_]+" + '"'
2494 mp.word_separators = '[ \t&:]+'
2495 gmPhraseWheel.cPhraseWheel.__init__ (
2496 self,
2497 *args,
2498 **kwargs
2499 )
2500 self.matcher = mp
2501 self.SetToolTipString(_('Select an indicator for the level of abnormality.'))
2502 self.selection_only = False
2503
2504
2505
2506
2518
2520
2521 if parent is None:
2522 parent = wx.GetApp().GetTopWindow()
2523
2524
2525 def edit(org=None):
2526 return edit_measurement_org(parent = parent, org = org)
2527
2528 def refresh(lctrl):
2529 orgs = gmPathLab.get_test_orgs()
2530 lctrl.set_string_items ([
2531 (o['unit'], o['organization'], gmTools.coalesce(o['test_org_contact'], u''), gmTools.coalesce(o['comment'], u''), o['pk_test_org'])
2532 for o in orgs
2533 ])
2534 lctrl.set_data(orgs)
2535
2536 def delete(test_org):
2537 gmPathLab.delete_test_org(test_org = test_org['pk_test_org'])
2538 return True
2539
2540 gmListWidgets.get_choices_from_list (
2541 parent = parent,
2542 msg = _('\nThese are the diagnostic orgs (path labs etc) currently defined in GNUmed.\n\n'),
2543 caption = _('Showing diagnostic orgs.'),
2544 columns = [_('Name'), _('Organization'), _('Contact'), _('Comment'), u'#'],
2545 single_selection = True,
2546 refresh_callback = refresh,
2547 edit_callback = edit,
2548 new_callback = edit,
2549 delete_callback = delete
2550 )
2551
2552
2553 from Gnumed.wxGladeWidgets import wxgMeasurementOrgEAPnl
2554
2555 -class cMeasurementOrgEAPnl(wxgMeasurementOrgEAPnl.wxgMeasurementOrgEAPnl, gmEditArea.cGenericEditAreaMixin):
2556
2572
2573
2574
2575
2576
2577
2578
2579
2581 has_errors = False
2582 if self._PRW_org_unit.GetData() is None:
2583 if self._PRW_org_unit.GetValue().strip() == u'':
2584 has_errors = True
2585 self._PRW_org_unit.display_as_valid(valid = False)
2586 else:
2587 self._PRW_org_unit.display_as_valid(valid = True)
2588 else:
2589 self._PRW_org_unit.display_as_valid(valid = True)
2590
2591 return (not has_errors)
2592
2603
2623
2628
2633
2635 self._refresh_as_new()
2636
2639
2640
2642
2644
2645 query = u"""
2646 SELECT DISTINCT ON (list_label)
2647 pk_test_org AS data,
2648 unit || ' (' || organization || ')' AS field_label,
2649 unit || ' @ ' || organization AS list_label
2650 FROM clin.v_test_orgs
2651 WHERE
2652 unit %(fragment_condition)s
2653 OR
2654 organization %(fragment_condition)s
2655 ORDER BY list_label
2656 LIMIT 50"""
2657 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
2658 mp.setThresholds(1, 2, 4)
2659
2660 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
2661 self.matcher = mp
2662 self.SetToolTipString(_('The name of the path lab/diagnostic organisation.'))
2663 self.selection_only = False
2664
2677
2680
2681
2692
2693
2694 msg = _(
2695 '\n'
2696 'These are the meta test types currently defined in GNUmed.\n'
2697 '\n'
2698 'Meta test types allow you to aggregate several actual test types used\n'
2699 'by pathology labs into one logical type.\n'
2700 '\n'
2701 'This is useful for grouping together results of tests which come under\n'
2702 'different names but really are the same thing. This often happens when\n'
2703 'you switch labs or the lab starts using another test method.\n'
2704 )
2705
2706 mtts = gmPathLab.get_meta_test_types()
2707
2708 gmListWidgets.get_choices_from_list (
2709 parent = parent,
2710 msg = msg,
2711 caption = _('Showing meta test types.'),
2712 columns = [_('Abbrev'), _('Name'), _('LOINC'), _('Comment'), u'#'],
2713 choices = [ [
2714 m['abbrev'],
2715 m['name'],
2716 gmTools.coalesce(m['loinc'], u''),
2717 gmTools.coalesce(m['comment'], u''),
2718 m['pk']
2719 ] for m in mtts ],
2720 data = mtts,
2721 single_selection = True,
2722 list_tooltip_callback = get_tooltip
2723
2724
2725
2726
2727 )
2728
2773
2774
2775
2776
2778 ea = cTestPanelEAPnl(parent = parent, id = -1)
2779 ea.data = test_panel
2780 ea.mode = gmTools.coalesce(test_panel, 'new', 'edit')
2781 dlg = gmEditArea.cGenericEditAreaDlg2 (
2782 parent = parent,
2783 id = -1,
2784 edit_area = ea,
2785 single_entry = gmTools.bool2subst((test_panel is None), False, True)
2786 )
2787 dlg.SetTitle(gmTools.coalesce(test_panel, _('Adding new test panel'), _('Editing test panel')))
2788 if dlg.ShowModal() == wx.ID_OK:
2789 dlg.Destroy()
2790 return True
2791 dlg.Destroy()
2792 return False
2793
2794
2796
2797 if parent is None:
2798 parent = wx.GetApp().GetTopWindow()
2799
2800
2801 def edit(test_panel=None):
2802 return edit_test_panel(parent = parent, test_panel = test_panel)
2803
2804 def delete(test_panel):
2805 gmPathLab.delete_test_panel(pk = test_panel['pk_test_panel'])
2806 return True
2807
2808 def get_tooltip(test_panel):
2809 return test_panel.format()
2810
2811 def refresh(lctrl):
2812 panels = gmPathLab.get_test_panels(order_by = 'description')
2813 items = [ [
2814 p['description'],
2815 gmTools.coalesce(p['comment'], u''),
2816 p['pk_test_panel']
2817 ] for p in panels ]
2818 lctrl.set_string_items(items)
2819 lctrl.set_data(panels)
2820
2821 msg = _(
2822 '\n'
2823 'Test panels as defined in GNUmed.\n'
2824 )
2825
2826 gmListWidgets.get_choices_from_list (
2827 parent = parent,
2828 msg = msg,
2829 caption = _('Showing test panels.'),
2830 columns = [ _('Name'), _('Comment'), u'#' ],
2831 single_selection = True,
2832 refresh_callback = refresh,
2833 edit_callback = edit,
2834 new_callback = edit,
2835 delete_callback = delete,
2836 list_tooltip_callback = get_tooltip
2837 )
2838
2839
2841
2843 query = u"""
2844 SELECT
2845 pk_test_panel
2846 AS data,
2847 description
2848 AS field_label,
2849 description
2850 AS list_label
2851 FROM
2852 clin.v_test_panels
2853 WHERE
2854 description %(fragment_condition)s
2855 ORDER BY field_label
2856 LIMIT 30"""
2857 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
2858 mp.setThresholds(1, 2, 4)
2859
2860 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
2861 self.matcher = mp
2862 self.SetToolTipString(_('Select a test panel.'))
2863 self.selection_only = True
2864
2869
2874
2875
2876 from Gnumed.wxGladeWidgets import wxgTestPanelEAPnl
2877
2878 -class cTestPanelEAPnl(wxgTestPanelEAPnl.wxgTestPanelEAPnl, gmEditArea.cGenericEditAreaMixin):
2879
2897
2898
2899
2900
2901
2902
2903
2904
2906 validity = True
2907
2908 if self._test_types is None:
2909 validity = False
2910 gmDispatcher.send(signal = 'statustext', msg = _('No test types selected.'))
2911 self._BTN_select_tests.SetFocus()
2912
2913 if self._TCTRL_description.GetValue().strip() == u'':
2914 validity = False
2915 self.display_tctrl_as_valid(tctrl = self._TCTRL_description, valid = False)
2916 self._TCTRL_description.SetFocus()
2917 else:
2918 self.display_tctrl_as_valid(tctrl = self._TCTRL_description, valid = True)
2919
2920 return validity
2921
2930
2932 self.data['description'] = self._TCTRL_description.GetValue().strip()
2933 self.data['comment'] = self._TCTRL_comment.GetValue().strip()
2934 self.data['pk_test_types'] = [ tt['pk_test_type'] for tt in self._test_types ]
2935 self.data.save()
2936 self.data.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ]
2937 return True
2938
2940 self._TCTRL_tests.SetValue(u'')
2941 self._test_types = test_types
2942 if self._test_types is None:
2943 return
2944 tmp = u';\n'.join ([
2945 u'%s: %s%s' % (
2946 t['unified_abbrev'],
2947 t['unified_name'],
2948 gmTools.coalesce(t['name_org'], u'', u' (%s)')
2949 )
2950 for t in self._test_types
2951 ])
2952 self._TCTRL_tests.SetValue(tmp)
2953
2955 self._TCTRL_description.SetValue(u'')
2956 self._TCTRL_comment.SetValue(u'')
2957 self.__refresh_test_types_field()
2958 self._PRW_codes.SetText()
2959
2960 self._TCTRL_description.SetFocus()
2961
2965
2974
2990
2991
2992
2993
2994 if __name__ == '__main__':
2995
2996 from Gnumed.pycommon import gmLog2
2997 from Gnumed.wxpython import gmPatSearchWidgets
2998
2999 gmI18N.activate_locale()
3000 gmI18N.install_domain()
3001 gmDateTime.init()
3002
3003
3011
3019
3020
3021
3022
3023
3024
3025
3026 if (len(sys.argv) > 1) and (sys.argv[1] == 'test'):
3027
3028 test_test_ea_pnl()
3029
3030
3031
3032