1 """GNUmed measurement widgets."""
2
3 __author__ = "Karsten Hilbert <Karsten.Hilbert@gmx.net>"
4 __license__ = "GPL"
5
6
7 import sys, logging, datetime as pyDT, decimal, os, subprocess, codecs
8 import os.path
9
10
11 import wx, wx.grid, wx.lib.hyperlink
12
13
14 if __name__ == '__main__':
15 sys.path.insert(0, '../../')
16 from Gnumed.business import gmPerson
17 from Gnumed.business import gmStaff
18 from Gnumed.business import gmPathLab
19 from Gnumed.business import gmSurgery
20 from Gnumed.business import gmLOINC
21 from Gnumed.business import gmForms
22 from Gnumed.business import gmPersonSearch
23 from Gnumed.business import gmOrganization
24 from Gnumed.business import gmHL7
25
26 from Gnumed.pycommon import gmTools
27 from Gnumed.pycommon import gmNetworkTools
28 from Gnumed.pycommon import gmI18N
29 from Gnumed.pycommon import gmShellAPI
30 from Gnumed.pycommon import gmCfg
31 from Gnumed.pycommon import gmDateTime
32 from Gnumed.pycommon import gmMatchProvider
33 from Gnumed.pycommon import gmDispatcher
34
35 from Gnumed.wxpython import gmRegetMixin
36 from Gnumed.wxpython import gmEditArea
37 from Gnumed.wxpython import gmPhraseWheel
38 from Gnumed.wxpython import gmListWidgets
39 from Gnumed.wxpython import gmGuiHelpers
40 from Gnumed.wxpython import gmAuthWidgets
41 from Gnumed.wxpython import gmOrganizationWidgets
42
43
44 _log = logging.getLogger('gm.ui')
45
46
47
48
50
51 if parent is None:
52 parent = wx.GetApp().GetTopWindow()
53
54
55 dlg = wx.FileDialog (
56 parent = parent,
57 message = 'Import Excelleris HL7 from XML file:',
58
59
60 wildcard = "xml files|*.xml|XML files|*.XML|all files|*",
61 style = wx.OPEN | wx.FILE_MUST_EXIST
62 )
63 choice = dlg.ShowModal()
64 xml_name = dlg.GetPath()
65 dlg.Destroy()
66 if choice != wx.ID_OK:
67 return False
68
69 hl7 = gmHL7.extract_HL7_from_CDATA(xml_name, u'.//Message')
70 if hl7 is None:
71 gmGuiHelpers.gm_show_info (
72 u'File [%s]\ndoes not seem to contain HL7 wrapped in XML.' % xml_name,
73 u'Extracting HL7 from XML'
74 )
75 return False
76 fixed_hl7 = gmHL7.fix_HL7_stupidities(hl7)
77 PID_names = gmHL7.split_HL7_by_PID(fixed_hl7)
78 for name in PID_names:
79 gmHL7.stage_MSH_as_incoming_data(name, source = u'Excelleris')
80
81
83
84 if parent is None:
85 parent = wx.GetApp().GetTopWindow()
86
87
88 dlg = wx.FileDialog (
89 parent = parent,
90 message = 'Import HL7 from file:',
91
92
93 wildcard = "*.hl7|*.hl7|*.HL7|*.HL7|all files|*",
94 style = wx.OPEN | wx.FILE_MUST_EXIST
95 )
96 choice = dlg.ShowModal()
97 hl7_name = dlg.GetPath()
98 dlg.Destroy()
99 if choice != wx.ID_OK:
100 return False
101
102 fixed_hl7 = gmHL7.fix_HL7_stupidities(hl7_name)
103 PID_names = gmHL7.split_HL7_by_PID(fixed_hl7)
104 for name in PID_names:
105 gmHL7.stage_MSH_as_incoming_data(name, source = u'generic')
106
107
109
110 if parent is None:
111 parent = wx.GetApp().GetTopWindow()
112
113 def refresh(lctrl):
114 incoming = gmHL7.get_incoming_data()
115 items = [ [
116 i['data_type'],
117 u'%s, %s (%s) %s' % (
118 i['lastnames'],
119 i['firstnames'],
120 i['dob'],
121 i['gender']
122 ),
123 i['external_data_id'],
124 i['pk_incoming_data_unmatched']
125 ] for i in incoming ]
126 lctrl.set_string_items(items)
127 lctrl.set_data(incoming)
128
129 gmListWidgets.get_choices_from_list (
130 parent = parent,
131 msg = None,
132 caption = _('Showing unmatched incoming data'),
133 columns = [ _('Type'), _('Patient'), _('Data ID'), '#' ],
134 single_selection = True,
135 can_return_empty = False,
136 ignore_OK_button = True,
137 refresh_callback = refresh
138
139
140
141
142
143
144 )
145
146
147
148
150
151 wx.BeginBusyCursor()
152
153 gmDispatcher.send(signal = 'statustext', msg = _('Updating LOINC data can take quite a while...'), beep = True)
154
155
156 loinc_zip = gmNetworkTools.download_file(url = 'http://www.gnumed.de/downloads/data/loinc/loinctab.zip', suffix = '.zip')
157 if loinc_zip is None:
158 wx.EndBusyCursor()
159 gmGuiHelpers.gm_show_warning (
160 aTitle = _('Downloading LOINC'),
161 aMessage = _('Error downloading the latest LOINC data.\n')
162 )
163 return False
164
165 _log.debug('downloaded zipped LOINC data into [%s]', loinc_zip)
166
167 loinc_dir = gmNetworkTools.unzip_data_pack(filename = loinc_zip)
168
169
170 data_fname, license_fname = gmLOINC.split_LOINCDBTXT(input_fname = os.path.join(loinc_dir, 'LOINCDB.TXT'))
171
172 wx.EndBusyCursor()
173
174 conn = gmAuthWidgets.get_dbowner_connection(procedure = _('importing LOINC reference data'))
175 if conn is None:
176 return False
177
178 wx.BeginBusyCursor()
179
180
181 if gmLOINC.loinc_import(data_fname = data_fname, license_fname = license_fname, conn = conn):
182 gmDispatcher.send(signal = 'statustext', msg = _('Successfully imported LOINC reference data.'))
183 else:
184 gmDispatcher.send(signal = 'statustext', msg = _('Importing LOINC reference data failed.'), beep = True)
185
186 wx.EndBusyCursor()
187 return True
188
189
190
192
193 dbcfg = gmCfg.cCfgSQL()
194
195 url = dbcfg.get (
196 option = u'external.urls.measurements_search',
197 workplace = gmSurgery.gmCurrentPractice().active_workplace,
198 bias = 'user',
199 default = u"http://www.google.de/search?as_oq=%(search_term)s&num=10&as_sitesearch=laborlexikon.de"
200 )
201
202 base_url = dbcfg.get2 (
203 option = u'external.urls.measurements_encyclopedia',
204 workplace = gmSurgery.gmCurrentPractice().active_workplace,
205 bias = 'user',
206 default = u'http://www.laborlexikon.de'
207 )
208
209 if measurement_type is None:
210 url = base_url
211
212 measurement_type = measurement_type.strip()
213
214 if measurement_type == u'':
215 url = base_url
216
217 url = url % {'search_term': measurement_type}
218
219 gmNetworkTools.open_url_in_browser(url = url)
220
221
233
234
236
237 if parent is None:
238 parent = wx.GetApp().GetTopWindow()
239
240 if emr is None:
241 emr = gmPerson.gmCurrentPatient().emr
242
243
244 def edit(measurement=None):
245 return edit_measurement(parent = parent, measurement = measurement, single_entry = True)
246
247 def delete(measurement):
248 gmPathLab.delete_test_result(result = measurement)
249 return True
250
251 def get_tooltip(measurement):
252 return measurement.format(with_review=True, with_evaluation=True, with_ranges=True)
253
254 def refresh(lctrl):
255 results = emr.get_test_results(order_by = 'clin_when DESC, unified_abbrev, unified_name')
256 items = [ [
257 gmDateTime.pydt_strftime (
258 r['clin_when'],
259 '%Y %b %d %H:%M',
260 accuracy = gmDateTime.acc_minutes
261 ),
262 r['unified_abbrev'],
263 u'%s%s%s' % (
264 r['unified_val'],
265 gmTools.coalesce(r['val_unit'], u'', u' %s'),
266 gmTools.coalesce(r['abnormality_indicator'], u'', u' %s')
267 ),
268 r['unified_name'],
269 gmTools.coalesce(r['comment'], u''),
270 r['pk_test_result']
271 ] for r in results ]
272 lctrl.set_string_items(items)
273 lctrl.set_data(results)
274
275 msg = _('Test results (ordered reverse-chronologically)')
276
277 return gmListWidgets.get_choices_from_list (
278 parent = parent,
279 msg = msg,
280 caption = _('Showing test results.'),
281 columns = [ _('When'), _('Abbrev'), _('Value'), _('Name'), _('Comment'), u'#' ],
282 single_selection = single_selection,
283 can_return_empty = False,
284 refresh_callback = refresh,
285 edit_callback = edit,
286 new_callback = edit,
287 delete_callback = delete,
288 list_tooltip_callback = get_tooltip
289 )
290
291
322
323
325
326 option = u'form_templates.default_gnuplot_template'
327
328 dbcfg = gmCfg.cCfgSQL()
329
330
331 default_template_name = dbcfg.get2 (
332 option = option,
333 workplace = gmSurgery.gmCurrentPractice().active_workplace,
334 bias = 'user'
335 )
336
337
338 if default_template_name is None:
339 gmDispatcher.send('statustext', msg = _('No default Gnuplot template configured.'), beep = False)
340 default_template = configure_default_gnuplot_template(parent = parent)
341
342 if default_template is None:
343 gmGuiHelpers.gm_show_error (
344 aMessage = _('There is no default Gnuplot one-type script template configured.'),
345 aTitle = _('Plotting test results')
346 )
347 return None
348 return default_template
349
350
351
352 try:
353 name, ver = default_template_name.split(u' - ')
354 except:
355
356 _log.exception('problem splitting Gnuplot script template name [%s]', default_template_name)
357 gmDispatcher.send(signal = 'statustext', msg = _('Problem loading Gnuplot script template.'), beep = True)
358 return None
359
360 default_template = gmForms.get_form_template(name_long = name, external_version = ver)
361 if default_template is None:
362 default_template = configure_default_gnuplot_template(parent = parent)
363
364 if default_template is None:
365 gmGuiHelpers.gm_show_error (
366 aMessage = _('Cannot load default Gnuplot script template [%s - %s]') % (name, ver),
367 aTitle = _('Plotting test results')
368 )
369 return None
370
371 return default_template
372
373
374 -def plot_measurements(parent=None, tests=None, format=None, show_year = True, use_default_template=False):
400
401
402 -def plot_adjacent_measurements(parent=None, test=None, format=None, show_year=True, plot_singular_result=True, use_default_template=False):
403
404 earlier, later = test.get_adjacent_results(desired_earlier_results = 2, desired_later_results = 2)
405 results2plot = []
406 if earlier is not None:
407 results2plot.extend(earlier)
408 results2plot.append(test)
409 if later is not None:
410 results2plot.extend(later)
411 if len(results2plot) == 1:
412 if not plot_singular_result:
413 return
414 plot_measurements (
415 parent = parent,
416 tests = results2plot,
417 format = format,
418 show_year = show_year,
419 use_default_template = use_default_template
420 )
421
422
423
424
425
426
427
428
429
430
431
433 """A grid class for displaying measurment results.
434
435 - does NOT listen to the currently active patient
436 - thereby it can display any patient at any time
437 """
438
439
440
441
442
443
445
446 wx.grid.Grid.__init__(self, *args, **kwargs)
447
448 self.__patient = None
449 self.__panel_to_show = None
450 self.__show_by_panel = False
451 self.__cell_data = {}
452 self.__row_label_data = []
453
454 self.__prev_row = None
455 self.__prev_col = None
456 self.__prev_label_row = None
457 self.__date_format = str((_('lab_grid_date_format::%Y\n%b %d')).lstrip('lab_grid_date_format::'))
458
459 self.__init_ui()
460 self.__register_events()
461
462
463
465 if not self.IsSelection():
466 gmDispatcher.send(signal = u'statustext', msg = _('No results selected for deletion.'))
467 return True
468
469 selected_cells = self.get_selected_cells()
470 if len(selected_cells) > 20:
471 results = None
472 msg = _(
473 'There are %s results marked for deletion.\n'
474 '\n'
475 'Are you sure you want to delete these results ?'
476 ) % len(selected_cells)
477 else:
478 results = self.__cells_to_data(cells = selected_cells, exclude_multi_cells = False)
479 txt = u'\n'.join([ u'%s %s (%s): %s %s%s' % (
480 r['clin_when'].strftime('%x %H:%M').decode(gmI18N.get_encoding()),
481 r['unified_abbrev'],
482 r['unified_name'],
483 r['unified_val'],
484 r['val_unit'],
485 gmTools.coalesce(r['abnormality_indicator'], u'', u' (%s)')
486 ) for r in results
487 ])
488 msg = _(
489 'The following results are marked for deletion:\n'
490 '\n'
491 '%s\n'
492 '\n'
493 'Are you sure you want to delete these results ?'
494 ) % txt
495
496 dlg = gmGuiHelpers.c2ButtonQuestionDlg (
497 self,
498 -1,
499 caption = _('Deleting test results'),
500 question = msg,
501 button_defs = [
502 {'label': _('Delete'), 'tooltip': _('Yes, delete all the results.'), 'default': False},
503 {'label': _('Cancel'), 'tooltip': _('No, do NOT delete any results.'), 'default': True}
504 ]
505 )
506 decision = dlg.ShowModal()
507
508 if decision == wx.ID_YES:
509 if results is None:
510 results = self.__cells_to_data(cells = selected_cells, exclude_multi_cells = False)
511 for result in results:
512 gmPathLab.delete_test_result(result)
513
515 if not self.IsSelection():
516 gmDispatcher.send(signal = u'statustext', msg = _('Cannot sign results. No results selected.'))
517 return True
518
519 selected_cells = self.get_selected_cells()
520 if len(selected_cells) > 10:
521 test_count = len(selected_cells)
522 tests = None
523 else:
524 test_count = None
525 tests = self.__cells_to_data(cells = selected_cells, exclude_multi_cells = False)
526 if len(tests) == 0:
527 return True
528
529 dlg = cMeasurementsReviewDlg (
530 self,
531 -1,
532 tests = tests,
533 test_count = test_count
534 )
535 decision = dlg.ShowModal()
536
537 if decision == wx.ID_APPLY:
538 wx.BeginBusyCursor()
539
540 if dlg._RBTN_confirm_abnormal.GetValue():
541 abnormal = None
542 elif dlg._RBTN_results_normal.GetValue():
543 abnormal = False
544 else:
545 abnormal = True
546
547 if dlg._RBTN_confirm_relevance.GetValue():
548 relevant = None
549 elif dlg._RBTN_results_not_relevant.GetValue():
550 relevant = False
551 else:
552 relevant = True
553
554 if tests is None:
555 tests = self.__cells_to_data(cells = selected_cells, exclude_multi_cells = False)
556
557 comment = None
558 if len(tests) == 1:
559 comment = dlg._TCTRL_comment.GetValue()
560
561 for test in tests:
562 test.set_review (
563 technically_abnormal = abnormal,
564 clinically_relevant = relevant,
565 comment = comment,
566 make_me_responsible = dlg._CHBOX_responsible.IsChecked()
567 )
568
569 wx.EndBusyCursor()
570
571 dlg.Destroy()
572
574
575 if not self.IsSelection():
576 gmDispatcher.send(signal = u'statustext', msg = _('Cannot plot results. No results selected.'))
577 return True
578
579 tests = self.__cells_to_data (
580 cells = self.get_selected_cells(),
581 exclude_multi_cells = False,
582 auto_include_multi_cells = True
583 )
584
585 plot_measurements(parent = self, tests = tests)
586
588
589 sel_block_top_left = self.GetSelectionBlockTopLeft()
590 sel_block_bottom_right = self.GetSelectionBlockBottomRight()
591 sel_cols = self.GetSelectedCols()
592 sel_rows = self.GetSelectedRows()
593
594 selected_cells = []
595
596
597 selected_cells += self.GetSelectedCells()
598
599
600 selected_cells += list (
601 (row, col)
602 for row in sel_rows
603 for col in xrange(self.GetNumberCols())
604 )
605
606
607 selected_cells += list (
608 (row, col)
609 for row in xrange(self.GetNumberRows())
610 for col in sel_cols
611 )
612
613
614 for top_left, bottom_right in zip(self.GetSelectionBlockTopLeft(), self.GetSelectionBlockBottomRight()):
615 selected_cells += [
616 (row, col)
617 for row in xrange(top_left[0], bottom_right[0] + 1)
618 for col in xrange(top_left[1], bottom_right[1] + 1)
619 ]
620
621 return set(selected_cells)
622
623 - def select_cells(self, unsigned_only=False, accountables_only=False, keep_preselections=False):
624 """Select a range of cells according to criteria.
625
626 unsigned_only: include only those which are not signed at all yet
627 accountable_only: include only those for which the current user is responsible
628 keep_preselections: broaden (rather than replace) the range of selected cells
629
630 Combinations are powerful !
631 """
632 wx.BeginBusyCursor()
633 self.BeginBatch()
634
635 if not keep_preselections:
636 self.ClearSelection()
637
638 for col_idx in self.__cell_data.keys():
639 for row_idx in self.__cell_data[col_idx].keys():
640
641
642 do_not_include = False
643 for result in self.__cell_data[col_idx][row_idx]:
644 if unsigned_only:
645 if result['reviewed']:
646 do_not_include = True
647 break
648 if accountables_only:
649 if not result['you_are_responsible']:
650 do_not_include = True
651 break
652 if do_not_include:
653 continue
654
655 self.SelectBlock(row_idx, col_idx, row_idx, col_idx, addToSelected = True)
656
657 self.EndBatch()
658 wx.EndBusyCursor()
659
661 self.empty_grid()
662 if self.__patient is None:
663 return
664
665 if self.__show_by_panel:
666 self.__repopulate_grid_by_panel()
667 return
668
669 self.__repopulate_grid_all_results()
670
672
673 if self.__panel_to_show is None:
674 return
675
676 emr = self.__patient.get_emr()
677
678
679 self.__row_label_data = self.__panel_to_show.test_types
680 row_labels = [ u'%s%s' % (
681 gmTools.bool2subst(test['is_fake_meta_type'], u'', gmTools.u_sum, u''),
682 test['unified_abbrev']
683 ) for test in self.__row_label_data
684 ]
685 if len(row_labels) == 0:
686 return
687
688
689 column_labels = [
690 date[0].strftime(self.__date_format) for date in emr.get_dates_for_results (
691 tests = self.__panel_to_show['pk_test_types'],
692
693 reverse_chronological = True
694 )
695 ]
696 results = emr.get_test_results_by_date (
697 tests = self.__panel_to_show['pk_test_types'],
698
699 reverse_chronological = True
700 )
701
702 self.BeginBatch()
703
704
705 self.AppendRows(numRows = len(row_labels))
706 for row_idx in range(len(row_labels)):
707 self.SetRowLabelValue(row_idx, row_labels[row_idx])
708
709
710 self.AppendCols(numCols = len(column_labels))
711 for date_idx in range(len(column_labels)):
712 self.SetColLabelValue(date_idx, column_labels[date_idx])
713
714
715 for result in results:
716 row = row_labels.index(u'%s%s' % (
717 gmTools.bool2subst(result['is_fake_meta_type'], u'', gmTools.u_sum, u''),
718 result['unified_abbrev']
719 ))
720 col = column_labels.index(result['clin_when'].strftime(self.__date_format))
721
722 try:
723 self.__cell_data[col]
724 except KeyError:
725 self.__cell_data[col] = {}
726
727
728 if self.__cell_data[col].has_key(row):
729 self.__cell_data[col][row].append(result)
730 self.__cell_data[col][row].sort(key = lambda x: x['clin_when'], reverse = True)
731 else:
732 self.__cell_data[col][row] = [result]
733
734
735 vals2display = []
736 for sub_result in self.__cell_data[col][row]:
737
738
739 ind = gmTools.coalesce(sub_result['abnormality_indicator'], u'').strip()
740 if ind != u'':
741 lab_abnormality_indicator = u' (%s)' % ind[:3]
742 else:
743 lab_abnormality_indicator = u''
744
745 if sub_result['is_technically_abnormal'] is None:
746 abnormality_indicator = lab_abnormality_indicator
747
748 elif sub_result['is_technically_abnormal'] is False:
749 abnormality_indicator = u''
750
751 else:
752
753 if lab_abnormality_indicator == u'':
754
755 abnormality_indicator = u' (%s)' % gmTools.u_plus_minus
756
757 else:
758 abnormality_indicator = lab_abnormality_indicator
759
760
761
762 sub_result_relevant = sub_result['is_clinically_relevant']
763 if sub_result_relevant is None:
764
765 sub_result_relevant = False
766
767 missing_review = False
768
769
770 if not sub_result['reviewed']:
771 missing_review = True
772
773 else:
774
775 if sub_result['you_are_responsible'] and not sub_result['review_by_you']:
776 missing_review = True
777
778
779 if len(sub_result['unified_val']) > 8:
780 tmp = u'%.7s%s' % (sub_result['unified_val'][:7], gmTools.u_ellipsis)
781 else:
782 tmp = u'%.8s' % sub_result['unified_val'][:8]
783
784
785 tmp = u'%s%.6s' % (tmp, abnormality_indicator)
786
787
788 has_sub_result_comment = gmTools.coalesce (
789 gmTools.coalesce(sub_result['note_test_org'], sub_result['comment']),
790 u''
791 ).strip() != u''
792 if has_sub_result_comment:
793 tmp = u'%s %s' % (tmp, gmTools.u_ellipsis)
794
795
796 if missing_review:
797 tmp = u'%s %s' % (tmp, gmTools.u_writing_hand)
798
799
800 if len(self.__cell_data[col][row]) > 1:
801 tmp = u'%s %s' % (sub_result['clin_when'].strftime('%H:%M'), tmp)
802
803 vals2display.append(tmp)
804
805 self.SetCellValue(row, col, u'\n'.join(vals2display))
806 self.SetCellAlignment(row, col, horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE)
807
808
809
810
811 if sub_result_relevant:
812 font = self.GetCellFont(row, col)
813 self.SetCellTextColour(row, col, 'firebrick')
814 font.SetWeight(wx.FONTWEIGHT_BOLD)
815 self.SetCellFont(row, col, font)
816
817
818 self.AutoSize()
819 self.EndBatch()
820 return
821
823 emr = self.__patient.get_emr()
824
825 self.__row_label_data = emr.get_test_types_for_results()
826 test_type_labels = [ u'%s%s' % (
827 gmTools.bool2subst(test['is_fake_meta_type'], u'', gmTools.u_sum, u''),
828 test['unified_abbrev']
829 ) for test in self.__row_label_data
830 ]
831 if len(test_type_labels) == 0:
832 return
833
834 test_date_labels = [ date[0].strftime(self.__date_format) for date in emr.get_dates_for_results() ]
835 results = emr.get_test_results_by_date()
836
837 self.BeginBatch()
838
839
840 self.AppendRows(numRows = len(test_type_labels))
841 for row_idx in range(len(test_type_labels)):
842 self.SetRowLabelValue(row_idx, test_type_labels[row_idx])
843
844
845 self.AppendCols(numCols = len(test_date_labels))
846 for date_idx in range(len(test_date_labels)):
847 self.SetColLabelValue(date_idx, test_date_labels[date_idx])
848
849
850 for result in results:
851 row = test_type_labels.index(u'%s%s' % (
852 gmTools.bool2subst(result['is_fake_meta_type'], u'', gmTools.u_sum, u''),
853 result['unified_abbrev']
854 ))
855 col = test_date_labels.index(result['clin_when'].strftime(self.__date_format))
856
857 try:
858 self.__cell_data[col]
859 except KeyError:
860 self.__cell_data[col] = {}
861
862
863 if self.__cell_data[col].has_key(row):
864 self.__cell_data[col][row].append(result)
865 self.__cell_data[col][row].sort(key = lambda x: x['clin_when'], reverse = True)
866 else:
867 self.__cell_data[col][row] = [result]
868
869
870 vals2display = []
871 for sub_result in self.__cell_data[col][row]:
872
873
874 ind = gmTools.coalesce(sub_result['abnormality_indicator'], u'').strip()
875 if ind != u'':
876 lab_abnormality_indicator = u' (%s)' % ind[:3]
877 else:
878 lab_abnormality_indicator = u''
879
880 if sub_result['is_technically_abnormal'] is None:
881 abnormality_indicator = lab_abnormality_indicator
882
883 elif sub_result['is_technically_abnormal'] is False:
884 abnormality_indicator = u''
885
886 else:
887
888 if lab_abnormality_indicator == u'':
889
890 abnormality_indicator = u' (%s)' % gmTools.u_plus_minus
891
892 else:
893 abnormality_indicator = lab_abnormality_indicator
894
895
896
897 sub_result_relevant = sub_result['is_clinically_relevant']
898 if sub_result_relevant is None:
899
900 sub_result_relevant = False
901
902 missing_review = False
903
904
905 if not sub_result['reviewed']:
906 missing_review = True
907
908 else:
909
910 if sub_result['you_are_responsible'] and not sub_result['review_by_you']:
911 missing_review = True
912
913
914 if len(sub_result['unified_val']) > 8:
915 tmp = u'%.7s%s' % (sub_result['unified_val'][:7], gmTools.u_ellipsis)
916 else:
917 tmp = u'%.8s' % sub_result['unified_val'][:8]
918
919
920 tmp = u'%s%.6s' % (tmp, abnormality_indicator)
921
922
923 has_sub_result_comment = gmTools.coalesce (
924 gmTools.coalesce(sub_result['note_test_org'], sub_result['comment']),
925 u''
926 ).strip() != u''
927 if has_sub_result_comment:
928 tmp = u'%s %s' % (tmp, gmTools.u_ellipsis)
929
930
931 if missing_review:
932 tmp = u'%s %s' % (tmp, gmTools.u_writing_hand)
933
934
935 if len(self.__cell_data[col][row]) > 1:
936 tmp = u'%s %s' % (sub_result['clin_when'].strftime('%H:%M'), tmp)
937
938 vals2display.append(tmp)
939
940 self.SetCellValue(row, col, u'\n'.join(vals2display))
941 self.SetCellAlignment(row, col, horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE)
942
943
944
945
946 if sub_result_relevant:
947 font = self.GetCellFont(row, col)
948 self.SetCellTextColour(row, col, 'firebrick')
949 font.SetWeight(wx.FONTWEIGHT_BOLD)
950 self.SetCellFont(row, col, font)
951
952
953 self.AutoSize()
954 self.EndBatch()
955 return
956
958 self.BeginBatch()
959 self.ClearGrid()
960
961
962 if self.GetNumberRows() > 0:
963 self.DeleteRows(pos = 0, numRows = self.GetNumberRows())
964 if self.GetNumberCols() > 0:
965 self.DeleteCols(pos = 0, numCols = self.GetNumberCols())
966 self.EndBatch()
967 self.__cell_data = {}
968 self.__row_label_data = []
969
982
1004
1005
1006
1008 self.CreateGrid(0, 1)
1009 self.EnableEditing(0)
1010 self.EnableDragGridSize(1)
1011 self.SetMinSize(wx.DefaultSize)
1012
1013
1014
1015
1016 self.SetRowLabelSize(wx.grid.GRID_AUTOSIZE)
1017
1018 self.SetRowLabelAlignment(horiz = wx.ALIGN_LEFT, vert = wx.ALIGN_CENTRE)
1019
1020
1021 dbcfg = gmCfg.cCfgSQL()
1022 url = dbcfg.get2 (
1023 option = u'external.urls.measurements_encyclopedia',
1024 workplace = gmSurgery.gmCurrentPractice().active_workplace,
1025 bias = 'user',
1026 default = u'http://www.laborlexikon.de'
1027 )
1028
1029 self.__WIN_corner = self.GetGridCornerLabelWindow()
1030
1031 LNK_lab = wx.lib.hyperlink.HyperLinkCtrl (
1032 self.__WIN_corner,
1033 -1,
1034 label = _('Tests'),
1035 style = wx.HL_DEFAULT_STYLE
1036 )
1037 LNK_lab.SetURL(url)
1038 LNK_lab.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BACKGROUND))
1039 LNK_lab.SetToolTipString(_(
1040 'Navigate to an encyclopedia of measurements\n'
1041 'and test methods on the web.\n'
1042 '\n'
1043 ' <%s>'
1044 ) % url)
1045
1046 SZR_inner = wx.BoxSizer(wx.HORIZONTAL)
1047 SZR_inner.Add((20, 20), 1, wx.EXPAND, 0)
1048 SZR_inner.Add(LNK_lab, 0, wx.ALIGN_CENTER_VERTICAL, 0)
1049 SZR_inner.Add((20, 20), 1, wx.EXPAND, 0)
1050
1051 SZR_corner = wx.BoxSizer(wx.VERTICAL)
1052 SZR_corner.Add((20, 20), 1, wx.EXPAND, 0)
1053 SZR_corner.AddWindow(SZR_inner, 0, wx.EXPAND)
1054 SZR_corner.Add((20, 20), 1, wx.EXPAND, 0)
1055
1056 self.__WIN_corner.SetSizer(SZR_corner)
1057 SZR_corner.Fit(self.__WIN_corner)
1058
1060 self.__WIN_corner.Layout()
1061
1062 - def __cells_to_data(self, cells=None, exclude_multi_cells=False, auto_include_multi_cells=False):
1063 """List of <cells> must be in row / col order."""
1064 data = []
1065 for row, col in cells:
1066 try:
1067
1068 data_list = self.__cell_data[col][row]
1069 except KeyError:
1070 continue
1071
1072 if len(data_list) == 1:
1073 data.append(data_list[0])
1074 continue
1075
1076 if exclude_multi_cells:
1077 gmDispatcher.send(signal = u'statustext', msg = _('Excluding multi-result field from further processing.'))
1078 continue
1079
1080 if auto_include_multi_cells:
1081 data.extend(data_list)
1082 continue
1083
1084 data_to_include = self.__get_choices_from_multi_cell(cell_data = data_list)
1085 if data_to_include is None:
1086 continue
1087 data.extend(data_to_include)
1088
1089 return data
1090
1092 data = gmListWidgets.get_choices_from_list (
1093 parent = self,
1094 msg = _(
1095 'Your selection includes a field with multiple results.\n'
1096 '\n'
1097 'Please select the individual results you want to work on:'
1098 ),
1099 caption = _('Selecting test results'),
1100 choices = [ [d['clin_when'], u'%s: %s' % (d['abbrev_tt'], d['name_tt']), d['unified_val']] for d in cell_data ],
1101 columns = [ _('Date / Time'), _('Test'), _('Result') ],
1102 data = cell_data,
1103 single_selection = single_selection
1104 )
1105 return data
1106
1107
1108
1110
1111 self.GetGridWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_cells)
1112 self.GetGridRowLabelWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_row_labels)
1113
1114
1115
1116 self.Bind(wx.EVT_SIZE, self.__resize_corner_window)
1117
1118
1119 self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.__on_cell_left_dclicked)
1120
1122 col = evt.GetCol()
1123 row = evt.GetRow()
1124
1125
1126 try:
1127 self.__cell_data[col][row]
1128 except KeyError:
1129
1130
1131 return
1132
1133 if len(self.__cell_data[col][row]) > 1:
1134 data = self.__get_choices_from_multi_cell(cell_data = self.__cell_data[col][row], single_selection = True)
1135 else:
1136 data = self.__cell_data[col][row][0]
1137
1138 if data is None:
1139 return
1140
1141 edit_measurement(parent = self, measurement = data, single_entry = True)
1142
1143
1144
1145
1146
1147
1148
1150
1151
1152
1153 x, y = self.CalcUnscrolledPosition(evt.GetX(), evt.GetY())
1154
1155 row = self.YToRow(y)
1156
1157 if self.__prev_label_row == row:
1158 return
1159
1160 self.__prev_label_row == row
1161
1162 evt.GetEventObject().SetToolTipString(self.get_row_tooltip(row = row))
1163
1164
1165
1166
1167
1168
1169
1170
1172 """Calculate where the mouse is and set the tooltip dynamically."""
1173
1174
1175
1176 x, y = self.CalcUnscrolledPosition(evt.GetX(), evt.GetY())
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190 row, col = self.XYToCell(x, y)
1191
1192 if (row == self.__prev_row) and (col == self.__prev_col):
1193 return
1194
1195 self.__prev_row = row
1196 self.__prev_col = col
1197
1198 evt.GetEventObject().SetToolTipString(self.get_cell_tooltip(col=col, row=row))
1199
1200
1201
1205
1206 patient = property(lambda x:x, _set_patient)
1207
1211
1212 panel_to_show = property(lambda x:x, _set_panel_to_show)
1213
1217
1218 show_by_panel = property(lambda x:x, _set_show_by_panel)
1219
1220 from Gnumed.wxGladeWidgets import wxgMeasurementsPnl
1221
1222 -class cMeasurementsPnl(wxgMeasurementsPnl.wxgMeasurementsPnl, gmRegetMixin.cRegetOnPaintMixin):
1223 """Panel holding a grid with lab data. Used as notebook page."""
1224
1231
1232
1233
1235 gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._on_pre_patient_selection)
1236 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._on_post_patient_selection)
1237 gmDispatcher.connect(signal = u'test_result_mod_db', receiver = self._schedule_data_reget)
1238 gmDispatcher.connect(signal = u'reviewed_test_results_mod_db', receiver = self._schedule_data_reget)
1239
1241 wx.CallAfter(self.__on_post_patient_selection)
1242
1244 self._schedule_data_reget()
1245
1247 wx.CallAfter(self.__on_pre_patient_selection)
1248
1250 self.data_grid.patient = None
1251 self.panel_data_grid.patient = None
1252
1255
1258
1264
1267
1270
1273
1276
1278 wx.CallAfter(self.__on_panel_selected, panel=panel)
1279
1281 if panel is None:
1282 self._TCTRL_panel_comment.SetValue(u'')
1283 self.panel_data_grid.panel_to_show = None
1284 self.panel_data_grid.Hide()
1285 else:
1286 pnl = self._PRW_panel.GetData(as_instance = True)
1287 self._TCTRL_panel_comment.SetValue(gmTools.coalesce (
1288 pnl['comment'],
1289 u''
1290 ))
1291 self.panel_data_grid.panel_to_show = pnl
1292 self.panel_data_grid.Show()
1293 self.Layout()
1294
1295
1297 wx.CallAfter(self.__on_panel_selection_modified)
1298
1300 self._TCTRL_panel_comment.SetValue(u'')
1301 if self._PRW_panel.GetValue().strip() == u'':
1302 self.panel_data_grid.panel_to_show = None
1303 self.panel_data_grid.Hide()
1304 self.Layout()
1305
1306
1307
1309 self.__action_button_popup = wx.Menu(title = _('Perform on selected results:'))
1310
1311 menu_id = wx.NewId()
1312 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('Review and &sign')))
1313 wx.EVT_MENU(self.__action_button_popup, menu_id, self.__on_sign_current_selection)
1314
1315 menu_id = wx.NewId()
1316 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('Plot')))
1317 wx.EVT_MENU(self.__action_button_popup, menu_id, self.__on_plot_current_selection)
1318
1319 menu_id = wx.NewId()
1320 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('Export to &file')))
1321
1322 self.__action_button_popup.Enable(id = menu_id, enable = False)
1323
1324 menu_id = wx.NewId()
1325 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('Export to &clipboard')))
1326
1327 self.__action_button_popup.Enable(id = menu_id, enable = False)
1328
1329 menu_id = wx.NewId()
1330 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('&Delete')))
1331 wx.EVT_MENU(self.__action_button_popup, menu_id, self.__on_delete_current_selection)
1332
1333
1334
1335
1336 self._PRW_panel.add_callback_on_selection(callback = self._on_panel_selected)
1337 self._PRW_panel.add_callback_on_modified(callback = self._on_panel_selection_modified)
1338
1339 self.panel_data_grid.show_by_panel = True
1340 self.panel_data_grid.panel_to_show = None
1341 self.panel_data_grid.Hide()
1342 self.Layout()
1343
1344 self._PRW_panel.SetFocus()
1345
1346
1347
1358
1359
1360
1361
1362 from Gnumed.wxGladeWidgets import wxgMeasurementsReviewDlg
1363
1365
1367
1368 try:
1369 tests = kwargs['tests']
1370 del kwargs['tests']
1371 test_count = len(tests)
1372 try: del kwargs['test_count']
1373 except KeyError: pass
1374 except KeyError:
1375 tests = None
1376 test_count = kwargs['test_count']
1377 del kwargs['test_count']
1378
1379 wxgMeasurementsReviewDlg.wxgMeasurementsReviewDlg.__init__(self, *args, **kwargs)
1380
1381 if tests is None:
1382 msg = _('%s results selected. Too many to list individually.') % test_count
1383 else:
1384 msg = ' // '.join (
1385 [ u'%s: %s %s (%s)' % (
1386 t['unified_abbrev'],
1387 t['unified_val'],
1388 t['val_unit'],
1389 t['clin_when'].strftime('%Y %b %d').decode(gmI18N.get_encoding())
1390 ) for t in tests
1391 ]
1392 )
1393
1394 self._LBL_tests.SetLabel(msg)
1395
1396 if test_count == 1:
1397 self._TCTRL_comment.Enable(True)
1398 self._TCTRL_comment.SetValue(gmTools.coalesce(tests[0]['review_comment'], u''))
1399 if tests[0]['you_are_responsible']:
1400 self._CHBOX_responsible.Enable(False)
1401
1402 self.Fit()
1403
1404
1405
1411
1412 from Gnumed.wxGladeWidgets import wxgMeasurementEditAreaPnl
1413
1414 -class cMeasurementEditAreaPnl(wxgMeasurementEditAreaPnl.wxgMeasurementEditAreaPnl, gmEditArea.cGenericEditAreaMixin):
1415 """This edit area saves *new* measurements into the active patient only."""
1416
1433
1434
1435
1437 self._PRW_test.SetText(u'', None, True)
1438 self.__refresh_loinc_info()
1439 self.__refresh_previous_value()
1440 self.__update_units_context()
1441 self._TCTRL_result.SetValue(u'')
1442 self._PRW_units.SetText(u'', None, True)
1443 self._PRW_abnormality_indicator.SetText(u'', None, True)
1444 if self.__default_date is None:
1445 self._DPRW_evaluated.SetData(data = pyDT.datetime.now(tz = gmDateTime.gmCurrentLocalTimezone))
1446 else:
1447 self._DPRW_evaluated.SetData(data = None)
1448 self._TCTRL_note_test_org.SetValue(u'')
1449 self._PRW_intended_reviewer.SetData(gmStaff.gmCurrentProvider()['pk_staff'])
1450 self._PRW_problem.SetData()
1451 self._TCTRL_narrative.SetValue(u'')
1452 self._CHBOX_review.SetValue(False)
1453 self._CHBOX_abnormal.SetValue(False)
1454 self._CHBOX_relevant.SetValue(False)
1455 self._CHBOX_abnormal.Enable(False)
1456 self._CHBOX_relevant.Enable(False)
1457 self._TCTRL_review_comment.SetValue(u'')
1458 self._TCTRL_normal_min.SetValue(u'')
1459 self._TCTRL_normal_max.SetValue(u'')
1460 self._TCTRL_normal_range.SetValue(u'')
1461 self._TCTRL_target_min.SetValue(u'')
1462 self._TCTRL_target_max.SetValue(u'')
1463 self._TCTRL_target_range.SetValue(u'')
1464 self._TCTRL_norm_ref_group.SetValue(u'')
1465
1466 self._PRW_test.SetFocus()
1467
1469 self._PRW_test.SetData(data = self.data['pk_test_type'])
1470 self.__refresh_loinc_info()
1471 self.__refresh_previous_value()
1472 self.__update_units_context()
1473 self._TCTRL_result.SetValue(self.data['unified_val'])
1474 self._PRW_units.SetText(self.data['val_unit'], self.data['val_unit'], True)
1475 self._PRW_abnormality_indicator.SetText (
1476 gmTools.coalesce(self.data['abnormality_indicator'], u''),
1477 gmTools.coalesce(self.data['abnormality_indicator'], u''),
1478 True
1479 )
1480 self._DPRW_evaluated.SetData(data = self.data['clin_when'])
1481 self._TCTRL_note_test_org.SetValue(gmTools.coalesce(self.data['note_test_org'], u''))
1482 self._PRW_intended_reviewer.SetData(self.data['pk_intended_reviewer'])
1483 self._PRW_problem.SetData(self.data['pk_episode'])
1484 self._TCTRL_narrative.SetValue(gmTools.coalesce(self.data['comment'], u''))
1485 self._CHBOX_review.SetValue(False)
1486 self._CHBOX_abnormal.SetValue(gmTools.coalesce(self.data['is_technically_abnormal'], False))
1487 self._CHBOX_relevant.SetValue(gmTools.coalesce(self.data['is_clinically_relevant'], False))
1488 self._CHBOX_abnormal.Enable(False)
1489 self._CHBOX_relevant.Enable(False)
1490 self._TCTRL_review_comment.SetValue(gmTools.coalesce(self.data['review_comment'], u''))
1491 self._TCTRL_normal_min.SetValue(unicode(gmTools.coalesce(self.data['val_normal_min'], u'')))
1492 self._TCTRL_normal_max.SetValue(unicode(gmTools.coalesce(self.data['val_normal_max'], u'')))
1493 self._TCTRL_normal_range.SetValue(gmTools.coalesce(self.data['val_normal_range'], u''))
1494 self._TCTRL_target_min.SetValue(unicode(gmTools.coalesce(self.data['val_target_min'], u'')))
1495 self._TCTRL_target_max.SetValue(unicode(gmTools.coalesce(self.data['val_target_max'], u'')))
1496 self._TCTRL_target_range.SetValue(gmTools.coalesce(self.data['val_target_range'], u''))
1497 self._TCTRL_norm_ref_group.SetValue(gmTools.coalesce(self.data['norm_ref_group'], u''))
1498
1499 self._TCTRL_result.SetFocus()
1500
1502 self._refresh_from_existing()
1503
1504 self._PRW_test.SetText(u'', None, True)
1505 self.__refresh_loinc_info()
1506 self.__refresh_previous_value()
1507 self.__update_units_context()
1508 self._TCTRL_result.SetValue(u'')
1509 self._PRW_units.SetText(u'', None, True)
1510 self._PRW_abnormality_indicator.SetText(u'', None, True)
1511
1512 self._TCTRL_note_test_org.SetValue(u'')
1513 self._TCTRL_narrative.SetValue(u'')
1514 self._CHBOX_review.SetValue(False)
1515 self._CHBOX_abnormal.SetValue(False)
1516 self._CHBOX_relevant.SetValue(False)
1517 self._CHBOX_abnormal.Enable(False)
1518 self._CHBOX_relevant.Enable(False)
1519 self._TCTRL_review_comment.SetValue(u'')
1520 self._TCTRL_normal_min.SetValue(u'')
1521 self._TCTRL_normal_max.SetValue(u'')
1522 self._TCTRL_normal_range.SetValue(u'')
1523 self._TCTRL_target_min.SetValue(u'')
1524 self._TCTRL_target_max.SetValue(u'')
1525 self._TCTRL_target_range.SetValue(u'')
1526 self._TCTRL_norm_ref_group.SetValue(u'')
1527
1528 self._PRW_test.SetFocus()
1529
1531
1532 validity = True
1533
1534 if not self._DPRW_evaluated.is_valid_timestamp():
1535 self._DPRW_evaluated.display_as_valid(False)
1536 validity = False
1537 else:
1538 self._DPRW_evaluated.display_as_valid(True)
1539
1540 if self._TCTRL_result.GetValue().strip() == u'':
1541 validity = False
1542 self.display_ctrl_as_valid(self._TCTRL_result, False)
1543 else:
1544 self.display_ctrl_as_valid(self._TCTRL_result, True)
1545
1546 if self._PRW_problem.GetValue().strip() == u'':
1547 self._PRW_problem.display_as_valid(False)
1548 validity = False
1549 else:
1550 self._PRW_problem.display_as_valid(True)
1551
1552 if self._PRW_test.GetValue().strip() == u'':
1553 self._PRW_test.display_as_valid(False)
1554 validity = False
1555 else:
1556 self._PRW_test.display_as_valid(True)
1557
1558 if self._PRW_intended_reviewer.GetData() is None:
1559 self._PRW_intended_reviewer.display_as_valid(False)
1560 validity = False
1561 else:
1562 self._PRW_intended_reviewer.display_as_valid(True)
1563
1564 if self._PRW_units.GetValue().strip() == u'':
1565 self._PRW_units.display_as_valid(False)
1566 validity = False
1567 else:
1568 self._PRW_units.display_as_valid(True)
1569
1570 ctrls = [self._TCTRL_normal_min, self._TCTRL_normal_max, self._TCTRL_target_min, self._TCTRL_target_max]
1571 for widget in ctrls:
1572 val = widget.GetValue().strip()
1573 if val == u'':
1574 continue
1575 try:
1576 decimal.Decimal(val.replace(',', u'.', 1))
1577 self.display_ctrl_as_valid(widget, True)
1578 except:
1579 validity = False
1580 self.display_ctrl_as_valid(widget, False)
1581
1582 if validity is False:
1583 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save result. Invalid or missing essential input.'))
1584
1585 return validity
1586
1588
1589 emr = gmPerson.gmCurrentPatient().get_emr()
1590
1591 success, result = gmTools.input2decimal(self._TCTRL_result.GetValue())
1592 if success:
1593 v_num = result
1594 v_al = None
1595 else:
1596 v_al = self._TCTRL_result.GetValue().strip()
1597 v_num = None
1598
1599 pk_type = self._PRW_test.GetData()
1600 if pk_type is None:
1601 tt = gmPathLab.create_measurement_type (
1602 lab = None,
1603 abbrev = self._PRW_test.GetValue().strip(),
1604 name = self._PRW_test.GetValue().strip(),
1605 unit = gmTools.coalesce(self._PRW_units.GetData(), self._PRW_units.GetValue()).strip()
1606 )
1607 pk_type = tt['pk_test_type']
1608
1609 tr = emr.add_test_result (
1610 episode = self._PRW_problem.GetData(can_create=True, is_open=False),
1611 type = pk_type,
1612 intended_reviewer = self._PRW_intended_reviewer.GetData(),
1613 val_num = v_num,
1614 val_alpha = v_al,
1615 unit = self._PRW_units.GetValue()
1616 )
1617
1618 tr['clin_when'] = self._DPRW_evaluated.GetData().get_pydt()
1619
1620 ctrls = [
1621 ('abnormality_indicator', self._PRW_abnormality_indicator),
1622 ('note_test_org', self._TCTRL_note_test_org),
1623 ('comment', self._TCTRL_narrative),
1624 ('val_normal_range', self._TCTRL_normal_range),
1625 ('val_target_range', self._TCTRL_target_range),
1626 ('norm_ref_group', self._TCTRL_norm_ref_group)
1627 ]
1628 for field, widget in ctrls:
1629 tr[field] = widget.GetValue().strip()
1630
1631 ctrls = [
1632 ('val_normal_min', self._TCTRL_normal_min),
1633 ('val_normal_max', self._TCTRL_normal_max),
1634 ('val_target_min', self._TCTRL_target_min),
1635 ('val_target_max', self._TCTRL_target_max)
1636 ]
1637 for field, widget in ctrls:
1638 val = widget.GetValue().strip()
1639 if val == u'':
1640 tr[field] = None
1641 else:
1642 tr[field] = decimal.Decimal(val.replace(',', u'.', 1))
1643
1644 tr.save_payload()
1645
1646 if self._CHBOX_review.GetValue() is True:
1647 tr.set_review (
1648 technically_abnormal = self._CHBOX_abnormal.GetValue(),
1649 clinically_relevant = self._CHBOX_relevant.GetValue(),
1650 comment = gmTools.none_if(self._TCTRL_review_comment.GetValue().strip(), u''),
1651 make_me_responsible = False
1652 )
1653
1654 self.data = tr
1655
1656 wx.CallAfter (
1657 plot_adjacent_measurements,
1658 test = self.data,
1659 plot_singular_result = False,
1660 use_default_template = True
1661 )
1662
1663 return True
1664
1666
1667 success, result = gmTools.input2decimal(self._TCTRL_result.GetValue())
1668 if success:
1669 v_num = result
1670 v_al = None
1671 else:
1672 v_num = None
1673 v_al = self._TCTRL_result.GetValue().strip()
1674
1675 pk_type = self._PRW_test.GetData()
1676 if pk_type is None:
1677 tt = gmPathLab.create_measurement_type (
1678 lab = None,
1679 abbrev = self._PRW_test.GetValue().strip(),
1680 name = self._PRW_test.GetValue().strip(),
1681 unit = gmTools.none_if(self._PRW_units.GetValue().strip(), u'')
1682 )
1683 pk_type = tt['pk_test_type']
1684
1685 tr = self.data
1686
1687 tr['pk_episode'] = self._PRW_problem.GetData(can_create=True, is_open=False)
1688 tr['pk_test_type'] = pk_type
1689 tr['pk_intended_reviewer'] = self._PRW_intended_reviewer.GetData()
1690 tr['val_num'] = v_num
1691 tr['val_alpha'] = v_al
1692 tr['val_unit'] = gmTools.coalesce(self._PRW_units.GetData(), self._PRW_units.GetValue()).strip()
1693 tr['clin_when'] = self._DPRW_evaluated.GetData().get_pydt()
1694
1695 ctrls = [
1696 ('abnormality_indicator', self._PRW_abnormality_indicator),
1697 ('note_test_org', self._TCTRL_note_test_org),
1698 ('comment', self._TCTRL_narrative),
1699 ('val_normal_range', self._TCTRL_normal_range),
1700 ('val_target_range', self._TCTRL_target_range),
1701 ('norm_ref_group', self._TCTRL_norm_ref_group)
1702 ]
1703 for field, widget in ctrls:
1704 tr[field] = widget.GetValue().strip()
1705
1706 ctrls = [
1707 ('val_normal_min', self._TCTRL_normal_min),
1708 ('val_normal_max', self._TCTRL_normal_max),
1709 ('val_target_min', self._TCTRL_target_min),
1710 ('val_target_max', self._TCTRL_target_max)
1711 ]
1712 for field, widget in ctrls:
1713 val = widget.GetValue().strip()
1714 if val == u'':
1715 tr[field] = None
1716 else:
1717 tr[field] = decimal.Decimal(val.replace(',', u'.', 1))
1718
1719 tr.save_payload()
1720
1721 if self._CHBOX_review.GetValue() is True:
1722 tr.set_review (
1723 technically_abnormal = self._CHBOX_abnormal.GetValue(),
1724 clinically_relevant = self._CHBOX_relevant.GetValue(),
1725 comment = gmTools.none_if(self._TCTRL_review_comment.GetValue().strip(), u''),
1726 make_me_responsible = False
1727 )
1728
1729 wx.CallAfter (
1730 plot_adjacent_measurements,
1731 test = self.data,
1732 plot_singular_result = False,
1733 use_default_template = True
1734 )
1735
1736 return True
1737
1738
1739
1743
1745 self.__refresh_loinc_info()
1746 self.__refresh_previous_value()
1747 self.__update_units_context()
1748
1750
1751 if not self._CHBOX_review.GetValue():
1752 self._CHBOX_abnormal.SetValue(self._PRW_abnormality_indicator.GetValue().strip() != u'')
1753
1755 self._CHBOX_abnormal.Enable(self._CHBOX_review.GetValue())
1756 self._CHBOX_relevant.Enable(self._CHBOX_review.GetValue())
1757 self._TCTRL_review_comment.Enable(self._CHBOX_review.GetValue())
1758
1775
1776
1777
1779
1780 self._PRW_units.unset_context(context = u'loinc')
1781
1782 tt = self._PRW_test.GetData(as_instance = True)
1783
1784 if tt is None:
1785 self._PRW_units.unset_context(context = u'pk_type')
1786 if self._PRW_test.GetValue().strip() == u'':
1787 self._PRW_units.unset_context(context = u'test_name')
1788 else:
1789 self._PRW_units.set_context(context = u'test_name', val = self._PRW_test.GetValue().strip())
1790 return
1791
1792 self._PRW_units.set_context(context = u'pk_type', val = tt['pk_test_type'])
1793 self._PRW_units.set_context(context = u'test_name', val = tt['name'])
1794
1795 if tt['loinc'] is None:
1796 return
1797
1798 self._PRW_units.set_context(context = u'loinc', val = tt['loinc'])
1799
1801
1802 self._TCTRL_loinc.SetValue(u'')
1803
1804 if self._PRW_test.GetData() is None:
1805 return
1806
1807 tt = self._PRW_test.GetData(as_instance = True)
1808
1809 if tt['loinc'] is None:
1810 return
1811
1812 info = gmLOINC.loinc2term(loinc = tt['loinc'])
1813 if len(info) == 0:
1814 self._TCTRL_loinc.SetValue(u'')
1815 return
1816
1817 self._TCTRL_loinc.SetValue(u'%s: %s' % (tt['loinc'], info[0]))
1818
1841
1842
1843
1844
1846
1847 if parent is None:
1848 parent = wx.GetApp().GetTopWindow()
1849
1850 if msg is None:
1851 msg = _('Pick the relevant measurement types.')
1852
1853 if right_column is None:
1854 right_columns = [_('Picked')]
1855 else:
1856 right_columns = [right_column]
1857
1858 picker = gmListWidgets.cItemPickerDlg(parent, -1, msg = msg)
1859 picker.set_columns(columns = [_('Known measurement types')], columns_right = right_columns)
1860 types = gmPathLab.get_measurement_types(order_by = 'unified_abbrev')
1861 picker.set_choices (
1862 choices = [
1863 u'%s: %s%s' % (
1864 t['unified_abbrev'],
1865 t['unified_name'],
1866 gmTools.coalesce(t['name_org'], u'', u' (%s)')
1867 )
1868 for t in types
1869 ],
1870 data = types
1871 )
1872 if picks is not None:
1873 picker.set_picks (
1874 picks = [
1875 u'%s: %s%s' % (
1876 p['unified_abbrev'],
1877 p['unified_name'],
1878 gmTools.coalesce(p['name_org'], u'', u' (%s)')
1879 )
1880 for p in picks
1881 ],
1882 data = picks
1883 )
1884 result = picker.ShowModal()
1885
1886 if result == wx.ID_CANCEL:
1887 picker.Destroy()
1888 return None
1889
1890 picks = picker.picks
1891 picker.Destroy()
1892 return picks
1893
1894
1917
1918 def delete(measurement_type):
1919 if measurement_type.in_use:
1920 gmDispatcher.send (
1921 signal = 'statustext',
1922 beep = True,
1923 msg = _('Cannot delete measurement type [%s (%s)] because it is in use.') % (measurement_type['name'], measurement_type['abbrev'])
1924 )
1925 return False
1926 gmPathLab.delete_measurement_type(measurement_type = measurement_type['pk_test_type'])
1927 return True
1928
1929 def get_tooltip(test_type):
1930 return test_type.format()
1931
1932 def refresh(lctrl):
1933 mtypes = gmPathLab.get_measurement_types(order_by = 'name, abbrev')
1934 items = [ [
1935 m['abbrev'],
1936 m['name'],
1937 gmTools.coalesce(m['conversion_unit'], u''),
1938 gmTools.coalesce(m['loinc'], u''),
1939 gmTools.coalesce(m['comment_type'], u''),
1940 gmTools.coalesce(m['name_org'], u'?'),
1941 gmTools.coalesce(m['comment_org'], u''),
1942 m['pk_test_type']
1943 ] for m in mtypes ]
1944 lctrl.set_string_items(items)
1945 lctrl.set_data(mtypes)
1946
1947 msg = _(
1948 '\n'
1949 'These are the measurement types currently defined in GNUmed.\n'
1950 '\n'
1951 )
1952
1953 gmListWidgets.get_choices_from_list (
1954 parent = parent,
1955 msg = msg,
1956 caption = _('Showing measurement types.'),
1957 columns = [ _('Abbrev'), _('Name'), _('Unit'), _('LOINC'), _('Comment'), _('Org'), _('Comment'), u'#' ],
1958 single_selection = True,
1959 refresh_callback = refresh,
1960 edit_callback = edit,
1961 new_callback = edit,
1962 delete_callback = delete,
1963 list_tooltip_callback = get_tooltip
1964 )
1965
1967
1969
1970 query = u"""
1971 SELECT DISTINCT ON (field_label)
1972 pk_test_type AS data,
1973 name
1974 || ' ('
1975 || coalesce (
1976 (SELECT unit || ' @ ' || organization FROM clin.v_test_orgs c_vto WHERE c_vto.pk_test_org = c_vtt.pk_test_org),
1977 '%(in_house)s'
1978 )
1979 || ')'
1980 AS field_label,
1981 name
1982 || ' ('
1983 || abbrev || ', '
1984 || coalesce(abbrev_meta || ': ' || name_meta || ', ', '')
1985 || coalesce (
1986 (SELECT unit || ' @ ' || organization FROM clin.v_test_orgs c_vto WHERE c_vto.pk_test_org = c_vtt.pk_test_org),
1987 '%(in_house)s'
1988 )
1989 || ')'
1990 AS list_label
1991 FROM
1992 clin.v_test_types c_vtt
1993 WHERE
1994 abbrev_meta %%(fragment_condition)s
1995 OR
1996 name_meta %%(fragment_condition)s
1997 OR
1998 abbrev %%(fragment_condition)s
1999 OR
2000 name %%(fragment_condition)s
2001 ORDER BY field_label
2002 LIMIT 50""" % {'in_house': _('generic / in house lab')}
2003
2004 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
2005 mp.setThresholds(1, 2, 4)
2006 mp.word_separators = '[ \t:@]+'
2007 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
2008 self.matcher = mp
2009 self.SetToolTipString(_('Select the type of measurement.'))
2010 self.selection_only = False
2011
2017
2018 from Gnumed.wxGladeWidgets import wxgMeasurementTypeEAPnl
2019
2020 -class cMeasurementTypeEAPnl(wxgMeasurementTypeEAPnl.wxgMeasurementTypeEAPnl, gmEditArea.cGenericEditAreaMixin):
2021
2038
2039
2041
2042
2043 query = u"""
2044 select distinct on (name)
2045 pk,
2046 name
2047 from clin.test_type
2048 where
2049 name %(fragment_condition)s
2050 order by name
2051 limit 50"""
2052 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
2053 mp.setThresholds(1, 2, 4)
2054 self._PRW_name.matcher = mp
2055 self._PRW_name.selection_only = False
2056 self._PRW_name.add_callback_on_lose_focus(callback = self._on_name_lost_focus)
2057
2058
2059 query = u"""
2060 select distinct on (abbrev)
2061 pk,
2062 abbrev
2063 from clin.test_type
2064 where
2065 abbrev %(fragment_condition)s
2066 order by abbrev
2067 limit 50"""
2068 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
2069 mp.setThresholds(1, 2, 3)
2070 self._PRW_abbrev.matcher = mp
2071 self._PRW_abbrev.selection_only = False
2072
2073
2074 self._PRW_conversion_unit.selection_only = False
2075
2076
2077 query = u"""
2078 SELECT DISTINCT ON (list_label)
2079 data,
2080 field_label,
2081 list_label
2082 FROM ((
2083
2084 SELECT
2085 loinc AS data,
2086 loinc AS field_label,
2087 (loinc || ': ' || abbrev || ' (' || name || ')') AS list_label
2088 FROM clin.test_type
2089 WHERE loinc %(fragment_condition)s
2090 LIMIT 50
2091
2092 ) UNION ALL (
2093
2094 SELECT
2095 code AS data,
2096 code AS field_label,
2097 (code || ': ' || term) AS list_label
2098 FROM ref.v_coded_terms
2099 WHERE
2100 coding_system = 'LOINC'
2101 AND
2102 lang = i18n.get_curr_lang()
2103 AND
2104 (code %(fragment_condition)s
2105 OR
2106 term %(fragment_condition)s)
2107 LIMIT 50
2108
2109 ) UNION ALL (
2110
2111 SELECT
2112 code AS data,
2113 code AS field_label,
2114 (code || ': ' || term) AS list_label
2115 FROM ref.v_coded_terms
2116 WHERE
2117 coding_system = 'LOINC'
2118 AND
2119 lang = 'en_EN'
2120 AND
2121 (code %(fragment_condition)s
2122 OR
2123 term %(fragment_condition)s)
2124 LIMIT 50
2125
2126 ) UNION ALL (
2127
2128 SELECT
2129 code AS data,
2130 code AS field_label,
2131 (code || ': ' || term) AS list_label
2132 FROM ref.v_coded_terms
2133 WHERE
2134 coding_system = 'LOINC'
2135 AND
2136 (code %(fragment_condition)s
2137 OR
2138 term %(fragment_condition)s)
2139 LIMIT 50
2140 )
2141 ) AS all_known_loinc
2142
2143 ORDER BY list_label
2144 LIMIT 50"""
2145 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
2146 mp.setThresholds(1, 2, 4)
2147 self._PRW_loinc.matcher = mp
2148 self._PRW_loinc.selection_only = False
2149 self._PRW_loinc.add_callback_on_lose_focus(callback = self._on_loinc_lost_focus)
2150
2152
2153 test = self._PRW_name.GetValue().strip()
2154
2155 if test == u'':
2156 self._PRW_conversion_unit.unset_context(context = u'test_name')
2157 return
2158
2159 self._PRW_conversion_unit.set_context(context = u'test_name', val = test)
2160
2162 loinc = self._PRW_loinc.GetData()
2163
2164 if loinc is None:
2165 self._TCTRL_loinc_info.SetValue(u'')
2166 self._PRW_conversion_unit.unset_context(context = u'loinc')
2167 return
2168
2169 self._PRW_conversion_unit.set_context(context = u'loinc', val = loinc)
2170
2171 info = gmLOINC.loinc2term(loinc = loinc)
2172 if len(info) == 0:
2173 self._TCTRL_loinc_info.SetValue(u'')
2174 return
2175
2176 self._TCTRL_loinc_info.SetValue(info[0])
2177
2178
2179
2181
2182 has_errors = False
2183 for field in [self._PRW_name, self._PRW_abbrev, self._PRW_conversion_unit]:
2184 if field.GetValue().strip() in [u'', None]:
2185 has_errors = True
2186 field.display_as_valid(valid = False)
2187 else:
2188 field.display_as_valid(valid = True)
2189 field.Refresh()
2190
2191 return (not has_errors)
2192
2194
2195 pk_org = self._PRW_test_org.GetData()
2196 if pk_org is None:
2197 pk_org = gmPathLab.create_test_org (
2198 name = gmTools.none_if(self._PRW_test_org.GetValue().strip(), u''),
2199 comment = gmTools.none_if(self._TCTRL_comment_org.GetValue().strip(), u'')
2200 )['pk_test_org']
2201
2202 tt = gmPathLab.create_measurement_type (
2203 lab = pk_org,
2204 abbrev = self._PRW_abbrev.GetValue().strip(),
2205 name = self._PRW_name.GetValue().strip(),
2206 unit = gmTools.coalesce (
2207 self._PRW_conversion_unit.GetData(),
2208 self._PRW_conversion_unit.GetValue()
2209 ).strip()
2210 )
2211 if self._PRW_loinc.GetData() is not None:
2212 tt['loinc'] = gmTools.none_if(self._PRW_loinc.GetData().strip(), u'')
2213 else:
2214 tt['loinc'] = gmTools.none_if(self._PRW_loinc.GetValue().strip(), u'')
2215 tt['comment_type'] = gmTools.none_if(self._TCTRL_comment_type.GetValue().strip(), u'')
2216 tt['pk_meta_test_type'] = self._PRW_meta_type.GetData()
2217
2218 tt.save()
2219
2220 self.data = tt
2221
2222 return True
2223
2251
2253 self._PRW_name.SetText(u'', None, True)
2254 self._on_name_lost_focus()
2255 self._PRW_abbrev.SetText(u'', None, True)
2256 self._PRW_conversion_unit.SetText(u'', None, True)
2257 self._PRW_loinc.SetText(u'', None, True)
2258 self._on_loinc_lost_focus()
2259 self._TCTRL_comment_type.SetValue(u'')
2260 self._PRW_test_org.SetText(u'', None, True)
2261 self._TCTRL_comment_org.SetValue(u'')
2262 self._PRW_meta_type.SetText(u'', None, True)
2263
2264 self._PRW_name.SetFocus()
2265
2267 self._PRW_name.SetText(self.data['name'], self.data['name'], True)
2268 self._on_name_lost_focus()
2269 self._PRW_abbrev.SetText(self.data['abbrev'], self.data['abbrev'], True)
2270 self._PRW_conversion_unit.SetText (
2271 gmTools.coalesce(self.data['conversion_unit'], u''),
2272 self.data['conversion_unit'],
2273 True
2274 )
2275 self._PRW_loinc.SetText (
2276 gmTools.coalesce(self.data['loinc'], u''),
2277 self.data['loinc'],
2278 True
2279 )
2280 self._on_loinc_lost_focus()
2281 self._TCTRL_comment_type.SetValue(gmTools.coalesce(self.data['comment_type'], u''))
2282 self._PRW_test_org.SetText (
2283 gmTools.coalesce(self.data['pk_test_org'], u'', self.data['name_org']),
2284 self.data['pk_test_org'],
2285 True
2286 )
2287 self._TCTRL_comment_org.SetValue(gmTools.coalesce(self.data['comment_org'], u''))
2288 if self.data['pk_meta_test_type'] is None:
2289 self._PRW_meta_type.SetText(u'', None, True)
2290 else:
2291 self._PRW_meta_type.SetText(u'%s: %s' % (self.data['abbrev_meta'], self.data['name_meta']), self.data['pk_meta_test_type'], True)
2292
2293 self._PRW_name.SetFocus()
2294
2305
2306
2307 _SQL_units_from_test_results = u"""
2308 -- via clin.v_test_results.pk_type (for types already used in results)
2309 SELECT
2310 val_unit AS data,
2311 val_unit AS field_label,
2312 val_unit || ' (' || name_tt || ')' AS list_label,
2313 1 AS rank
2314 FROM
2315 clin.v_test_results
2316 WHERE
2317 (
2318 val_unit %(fragment_condition)s
2319 OR
2320 conversion_unit %(fragment_condition)s
2321 )
2322 %(ctxt_type_pk)s
2323 %(ctxt_test_name)s
2324 """
2325
2326 _SQL_units_from_test_types = u"""
2327 -- via clin.test_type (for types not yet used in results)
2328 SELECT
2329 conversion_unit AS data,
2330 conversion_unit AS field_label,
2331 conversion_unit || ' (' || name || ')' AS list_label,
2332 2 AS rank
2333 FROM
2334 clin.test_type
2335 WHERE
2336 conversion_unit %(fragment_condition)s
2337 %(ctxt_ctt)s
2338 """
2339
2340 _SQL_units_from_loinc_ipcc = u"""
2341 -- via ref.loinc.ipcc_units
2342 SELECT
2343 ipcc_units AS data,
2344 ipcc_units AS field_label,
2345 ipcc_units || ' (LOINC.ipcc: ' || term || ')' AS list_label,
2346 3 AS rank
2347 FROM
2348 ref.loinc
2349 WHERE
2350 ipcc_units %(fragment_condition)s
2351 %(ctxt_loinc)s
2352 %(ctxt_loinc_term)s
2353 """
2354
2355 _SQL_units_from_loinc_submitted = u"""
2356 -- via ref.loinc.submitted_units
2357 SELECT
2358 submitted_units AS data,
2359 submitted_units AS field_label,
2360 submitted_units || ' (LOINC.submitted:' || term || ')' AS list_label,
2361 3 AS rank
2362 FROM
2363 ref.loinc
2364 WHERE
2365 submitted_units %(fragment_condition)s
2366 %(ctxt_loinc)s
2367 %(ctxt_loinc_term)s
2368 """
2369
2370 _SQL_units_from_loinc_example = u"""
2371 -- via ref.loinc.example_units
2372 SELECT
2373 example_units AS data,
2374 example_units AS field_label,
2375 example_units || ' (LOINC.example: ' || term || ')' AS list_label,
2376 3 AS rank
2377 FROM
2378 ref.loinc
2379 WHERE
2380 example_units %(fragment_condition)s
2381 %(ctxt_loinc)s
2382 %(ctxt_loinc_term)s
2383 """
2384
2385 _SQL_units_from_atc = u"""
2386 -- via ref.atc.unit
2387 SELECT
2388 unit AS data,
2389 unit AS field_label,
2390 unit || ' (ATC: ' || term || ')' AS list_label,
2391 2 AS rank
2392 FROM
2393 ref.atc
2394 WHERE
2395 unit IS NOT NULL
2396 AND
2397 unit %(fragment_condition)s
2398 """
2399
2400 _SQL_units_from_consumable_substance = u"""
2401 -- via ref.consumable_substance.unit
2402 SELECT
2403 unit AS data,
2404 unit AS field_label,
2405 unit || ' (' || description || ')' AS list_label,
2406 2 AS rank
2407 FROM
2408 ref.consumable_substance
2409 WHERE
2410 unit %(fragment_condition)s
2411 %(ctxt_substance)s
2412 """
2413
2414
2416
2418
2419 query = u"""
2420 SELECT DISTINCT ON (data)
2421 data,
2422 field_label,
2423 list_label
2424 FROM (
2425
2426 SELECT
2427 data,
2428 field_label,
2429 list_label,
2430 rank
2431 FROM (
2432 (%s) UNION ALL
2433 (%s) UNION ALL
2434 (%s) UNION ALL
2435 (%s) UNION ALL
2436 (%s) UNION ALL
2437 (%s) UNION ALL
2438 (%s)
2439 ) AS all_matching_units
2440 WHERE data IS NOT NULL
2441 ORDER BY rank
2442
2443 ) AS ranked_matching_units
2444 LIMIT 50""" % (
2445 _SQL_units_from_test_results,
2446 _SQL_units_from_test_types,
2447 _SQL_units_from_loinc_ipcc,
2448 _SQL_units_from_loinc_submitted,
2449 _SQL_units_from_loinc_example,
2450 _SQL_units_from_atc,
2451 _SQL_units_from_consumable_substance
2452 )
2453
2454 ctxt = {
2455 'ctxt_type_pk': {
2456 'where_part': u'AND pk_test_type = %(pk_type)s',
2457 'placeholder': u'pk_type'
2458 },
2459 'ctxt_test_name': {
2460 'where_part': u'AND %(test_name)s IN (name_tt, name_meta, abbrev_meta)',
2461 'placeholder': u'test_name'
2462 },
2463 'ctxt_ctt': {
2464 'where_part': u'AND %(test_name)s IN (name, abbrev)',
2465 'placeholder': u'test_name'
2466 },
2467 'ctxt_loinc': {
2468 'where_part': u'AND code = %(loinc)s',
2469 'placeholder': u'loinc'
2470 },
2471 'ctxt_loinc_term': {
2472 'where_part': u'AND term ~* %(test_name)s',
2473 'placeholder': u'test_name'
2474 },
2475 'ctxt_substance': {
2476 'where_part': u'AND description ~* %(substance)s',
2477 'placeholder': u'substance'
2478 }
2479 }
2480
2481 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query, context = ctxt)
2482 mp.setThresholds(1, 2, 4)
2483
2484 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
2485 self.matcher = mp
2486 self.SetToolTipString(_('Select the desired unit for the amount or measurement.'))
2487 self.selection_only = False
2488 self.phrase_separators = u'[;|]+'
2489
2490
2491
2493
2495
2496 query = u"""
2497 select distinct abnormality_indicator,
2498 abnormality_indicator, abnormality_indicator
2499 from clin.v_test_results
2500 where
2501 abnormality_indicator %(fragment_condition)s
2502 order by abnormality_indicator
2503 limit 25"""
2504
2505 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
2506 mp.setThresholds(1, 1, 2)
2507 mp.ignored_chars = "[.'\\\[\]#$%_]+" + '"'
2508 mp.word_separators = '[ \t&:]+'
2509 gmPhraseWheel.cPhraseWheel.__init__ (
2510 self,
2511 *args,
2512 **kwargs
2513 )
2514 self.matcher = mp
2515 self.SetToolTipString(_('Select an indicator for the level of abnormality.'))
2516 self.selection_only = False
2517
2518
2519
2520
2532
2534
2535 if parent is None:
2536 parent = wx.GetApp().GetTopWindow()
2537
2538
2539 def edit(org=None):
2540 return edit_measurement_org(parent = parent, org = org)
2541
2542 def refresh(lctrl):
2543 orgs = gmPathLab.get_test_orgs()
2544 lctrl.set_string_items ([
2545 (o['unit'], o['organization'], gmTools.coalesce(o['test_org_contact'], u''), gmTools.coalesce(o['comment'], u''), o['pk_test_org'])
2546 for o in orgs
2547 ])
2548 lctrl.set_data(orgs)
2549
2550 def delete(test_org):
2551 gmPathLab.delete_test_org(test_org = test_org['pk_test_org'])
2552 return True
2553
2554 gmListWidgets.get_choices_from_list (
2555 parent = parent,
2556 msg = _('\nThese are the diagnostic orgs (path labs etc) currently defined in GNUmed.\n\n'),
2557 caption = _('Showing diagnostic orgs.'),
2558 columns = [_('Name'), _('Organization'), _('Contact'), _('Comment'), u'#'],
2559 single_selection = True,
2560 refresh_callback = refresh,
2561 edit_callback = edit,
2562 new_callback = edit,
2563 delete_callback = delete
2564 )
2565
2566
2567 from Gnumed.wxGladeWidgets import wxgMeasurementOrgEAPnl
2568
2569 -class cMeasurementOrgEAPnl(wxgMeasurementOrgEAPnl.wxgMeasurementOrgEAPnl, gmEditArea.cGenericEditAreaMixin):
2570
2586
2587
2588
2589
2590
2591
2592
2593
2595 has_errors = False
2596 if self._PRW_org_unit.GetData() is None:
2597 if self._PRW_org_unit.GetValue().strip() == u'':
2598 has_errors = True
2599 self._PRW_org_unit.display_as_valid(valid = False)
2600 else:
2601 self._PRW_org_unit.display_as_valid(valid = True)
2602 else:
2603 self._PRW_org_unit.display_as_valid(valid = True)
2604
2605 return (not has_errors)
2606
2617
2637
2642
2647
2649 self._refresh_as_new()
2650
2653
2654
2656
2658
2659 query = u"""
2660 SELECT DISTINCT ON (list_label)
2661 pk AS data,
2662 unit || ' (' || organization || ')' AS field_label,
2663 unit || ' @ ' || organization AS list_label
2664 FROM clin.v_test_orgs
2665 WHERE
2666 unit %(fragment_condition)s
2667 OR
2668 organization %(fragment_condition)s
2669 ORDER BY list_label
2670 LIMIT 50"""
2671 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
2672 mp.setThresholds(1, 2, 4)
2673
2674 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
2675 self.matcher = mp
2676 self.SetToolTipString(_('The name of the path lab/diagnostic organisation.'))
2677 self.selection_only = False
2678
2691
2694
2695
2734
2779
2780
2781
2782
2784 ea = cTestPanelEAPnl(parent = parent, id = -1)
2785 ea.data = test_panel
2786 ea.mode = gmTools.coalesce(test_panel, 'new', 'edit')
2787 dlg = gmEditArea.cGenericEditAreaDlg2 (
2788 parent = parent,
2789 id = -1,
2790 edit_area = ea,
2791 single_entry = gmTools.bool2subst((test_panel is None), False, True)
2792 )
2793 dlg.SetTitle(gmTools.coalesce(test_panel, _('Adding new test panel'), _('Editing test panel')))
2794 if dlg.ShowModal() == wx.ID_OK:
2795 dlg.Destroy()
2796 return True
2797 dlg.Destroy()
2798 return False
2799
2800
2802
2803 if parent is None:
2804 parent = wx.GetApp().GetTopWindow()
2805
2806
2807 def edit(test_panel=None):
2808 return edit_test_panel(parent = parent, test_panel = test_panel)
2809
2810 def delete(test_panel):
2811 gmPathLab.delete_test_panel(pk = test_panel['pk_test_panel'])
2812 return True
2813
2814 def get_tooltip(test_panel):
2815 return test_panel.format()
2816
2817 def refresh(lctrl):
2818 panels = gmPathLab.get_test_panels(order_by = 'description')
2819 items = [ [
2820 p['description'],
2821 gmTools.coalesce(p['comment'], u''),
2822 p['pk_test_panel']
2823 ] for p in panels ]
2824 lctrl.set_string_items(items)
2825 lctrl.set_data(panels)
2826
2827 msg = _(
2828 '\n'
2829 'Test panels as defined in GNUmed.\n'
2830 )
2831
2832 gmListWidgets.get_choices_from_list (
2833 parent = parent,
2834 msg = msg,
2835 caption = _('Showing test panels.'),
2836 columns = [ _('Name'), _('Comment'), u'#' ],
2837 single_selection = True,
2838 refresh_callback = refresh,
2839 edit_callback = edit,
2840 new_callback = edit,
2841 delete_callback = delete,
2842 list_tooltip_callback = get_tooltip
2843 )
2844
2845
2847
2849 query = u"""
2850 SELECT
2851 pk_test_panel
2852 AS data,
2853 description
2854 AS field_label,
2855 description
2856 AS list_label
2857 FROM
2858 clin.v_test_panels
2859 WHERE
2860 description %(fragment_condition)s
2861 ORDER BY field_label
2862 LIMIT 30"""
2863 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
2864 mp.setThresholds(1, 2, 4)
2865
2866 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
2867 self.matcher = mp
2868 self.SetToolTipString(_('Select a test panel.'))
2869 self.selection_only = True
2870
2875
2880
2881
2882 from Gnumed.wxGladeWidgets import wxgTestPanelEAPnl
2883
2884 -class cTestPanelEAPnl(wxgTestPanelEAPnl.wxgTestPanelEAPnl, gmEditArea.cGenericEditAreaMixin):
2885
2903
2904
2905
2906
2907
2908
2909
2910
2912 validity = True
2913
2914 if self._test_types is None:
2915 validity = False
2916 gmDispatcher.send(signal = 'statustext', msg = _('No test types selected.'))
2917 self._BTN_select_tests.SetFocus()
2918
2919 if self._TCTRL_description.GetValue().strip() == u'':
2920 validity = False
2921 self.display_tctrl_as_valid(tctrl = self._TCTRL_description, valid = False)
2922 self._TCTRL_description.SetFocus()
2923 else:
2924 self.display_tctrl_as_valid(tctrl = self._TCTRL_description, valid = True)
2925
2926 return validity
2927
2936
2938 self.data['description'] = self._TCTRL_description.GetValue().strip()
2939 self.data['comment'] = self._TCTRL_comment.GetValue().strip()
2940 self.data['pk_test_types'] = [ tt['pk_test_type'] for tt in self._test_types ]
2941 self.data.save()
2942 self.data.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ]
2943 return True
2944
2946 self._TCTRL_tests.SetValue(u'')
2947 self._test_types = test_types
2948 if self._test_types is None:
2949 return
2950 tmp = u';\n'.join ([
2951 u'%s: %s%s' % (
2952 t['unified_abbrev'],
2953 t['unified_name'],
2954 gmTools.coalesce(t['name_org'], u'', u' (%s)')
2955 )
2956 for t in self._test_types
2957 ])
2958 self._TCTRL_tests.SetValue(tmp)
2959
2961 self._TCTRL_description.SetValue(u'')
2962 self._TCTRL_comment.SetValue(u'')
2963 self.__refresh_test_types_field()
2964 self._PRW_codes.SetText()
2965
2966 self._TCTRL_description.SetFocus()
2967
2971
2980
2996
2997
2998
2999
3000 if __name__ == '__main__':
3001
3002 from Gnumed.pycommon import gmLog2
3003 from Gnumed.wxpython import gmPatSearchWidgets
3004
3005 gmI18N.activate_locale()
3006 gmI18N.install_domain()
3007 gmDateTime.init()
3008
3009
3017
3025
3026
3027
3028
3029
3030
3031
3032 if (len(sys.argv) > 1) and (sys.argv[1] == 'test'):
3033
3034 test_test_ea_pnl()
3035
3036
3037
3038