1 """GNUmed measurement widgets."""
2
3 __version__ = "$Revision: 1.66 $"
4 __author__ = "Karsten Hilbert <Karsten.Hilbert@gmx.net>"
5 __license__ = "GPL"
6
7
8 import sys, logging, datetime as pyDT, decimal, os, subprocess, codecs
9 import os.path
10
11
12 import wx, wx.grid, wx.lib.hyperlink
13
14
15 if __name__ == '__main__':
16 sys.path.insert(0, '../../')
17 from Gnumed.business import gmPerson
18 from Gnumed.business import gmStaff
19 from Gnumed.business import gmPathLab
20 from Gnumed.business import gmSurgery
21 from Gnumed.business import gmLOINC
22 from Gnumed.business import gmForms
23 from Gnumed.business import gmPersonSearch
24 from Gnumed.business import gmOrganization
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 gmPhraseWheel
37 from Gnumed.wxpython import gmEditArea
38 from Gnumed.wxpython import gmGuiHelpers
39 from Gnumed.wxpython import gmListWidgets
40 from Gnumed.wxpython import gmAuthWidgets
41 from Gnumed.wxpython import gmFormWidgets
42 from Gnumed.wxpython import gmPatSearchWidgets
43 from Gnumed.wxpython import gmOrganizationWidgets
44
45
46 _log = logging.getLogger('gm.ui')
47 _log.info(__version__)
48
49
50
51
53
54 wx.BeginBusyCursor()
55
56 gmDispatcher.send(signal = 'statustext', msg = _('Updating LOINC data can take quite a while...'), beep = True)
57
58
59 loinc_zip = gmNetworkTools.download_file(url = 'http://www.gnumed.de/downloads/data/loinc/loinctab.zip', suffix = '.zip')
60 if loinc_zip is None:
61 wx.EndBusyCursor()
62 gmGuiHelpers.gm_show_warning (
63 aTitle = _('Downloading LOINC'),
64 aMessage = _('Error downloading the latest LOINC data.\n')
65 )
66 return False
67
68 _log.debug('downloaded zipped LOINC data into [%s]', loinc_zip)
69
70 loinc_dir = gmNetworkTools.unzip_data_pack(filename = loinc_zip)
71
72
73 data_fname, license_fname = gmLOINC.split_LOINCDBTXT(input_fname = os.path.join(loinc_dir, 'LOINCDB.TXT'))
74
75 wx.EndBusyCursor()
76
77 conn = gmAuthWidgets.get_dbowner_connection(procedure = _('importing LOINC reference data'))
78 if conn is None:
79 return False
80
81 wx.BeginBusyCursor()
82
83
84 if gmLOINC.loinc_import(data_fname = data_fname, license_fname = license_fname, conn = conn):
85 gmDispatcher.send(signal = 'statustext', msg = _('Successfully imported LOINC reference data.'))
86 else:
87 gmDispatcher.send(signal = 'statustext', msg = _('Importing LOINC reference data failed.'), beep = True)
88
89 wx.EndBusyCursor()
90 return True
91
92
93
95
96 dbcfg = gmCfg.cCfgSQL()
97
98 url = dbcfg.get (
99 option = u'external.urls.measurements_search',
100 workplace = gmSurgery.gmCurrentPractice().active_workplace,
101 bias = 'user',
102 default = u"http://www.google.de/search?as_oq=%(search_term)s&num=10&as_sitesearch=laborlexikon.de"
103 )
104
105 base_url = dbcfg.get2 (
106 option = u'external.urls.measurements_encyclopedia',
107 workplace = gmSurgery.gmCurrentPractice().active_workplace,
108 bias = 'user',
109 default = u'http://www.laborlexikon.de'
110 )
111
112 if measurement_type is None:
113 url = base_url
114
115 measurement_type = measurement_type.strip()
116
117 if measurement_type == u'':
118 url = base_url
119
120 url = url % {'search_term': measurement_type}
121
122 gmNetworkTools.open_url_in_browser(url = url)
123
135
136
165
166
168
169 option = u'form_templates.default_gnuplot_template'
170
171 dbcfg = gmCfg.cCfgSQL()
172
173
174 default_template_name = dbcfg.get2 (
175 option = option,
176 workplace = gmSurgery.gmCurrentPractice().active_workplace,
177 bias = 'user'
178 )
179
180
181 if default_template_name is None:
182 gmDispatcher.send('statustext', msg = _('No default Gnuplot template configured.'), beep = False)
183 default_template = configure_default_gnuplot_template(parent = parent)
184
185 if default_template is None:
186 gmGuiHelpers.gm_show_error (
187 aMessage = _('There is no default Gnuplot one-type script template configured.'),
188 aTitle = _('Plotting test results')
189 )
190 return None
191 return default_template
192
193
194
195 try:
196 name, ver = default_template_name.split(u' - ')
197 except:
198
199 _log.exception('problem splitting Gnuplot script template name [%s]', default_template_name)
200 gmDispatcher.send(signal = 'statustext', msg = _('Problem loading Gnuplot script template.'), beep = True)
201 return None
202
203 default_template = gmForms.get_form_template(name_long = name, external_version = ver)
204 if default_template is None:
205 default_template = configure_default_gnuplot_template(parent = parent)
206
207 if default_template is None:
208 gmGuiHelpers.gm_show_error (
209 aMessage = _('Cannot load default Gnuplot script template [%s - %s]') % (name, ver),
210 aTitle = _('Plotting test results')
211 )
212 return None
213
214 return default_template
215
216
217 -def plot_measurements(parent=None, tests=None, format=None, show_year = True, use_default_template=False):
241
242
243 -def plot_adjacent_measurements(parent=None, test=None, format=None, show_year=True, plot_singular_result=True, use_default_template=False):
244
245 earlier, later = test.get_adjacent_results(desired_earlier_results = 2, desired_later_results = 2)
246 results2plot = []
247 if earlier is not None:
248 results2plot.extend(earlier)
249 results2plot.append(test)
250 if later is not None:
251 results2plot.extend(later)
252 if len(results2plot) == 1:
253 if not plot_singular_result:
254 return
255 plot_measurements (
256 parent = parent,
257 tests = results2plot,
258 format = format,
259 show_year = show_year,
260 use_default_template = use_default_template
261 )
262
263
264
265
266
267
268
269
270
271
272
274 """A grid class for displaying measurment results.
275
276 - does NOT listen to the currently active patient
277 - thereby it can display any patient at any time
278 """
279
280
281
282
283
284
286
287 wx.grid.Grid.__init__(self, *args, **kwargs)
288
289 self.__patient = None
290 self.__cell_data = {}
291 self.__row_label_data = []
292
293 self.__prev_row = None
294 self.__prev_col = None
295 self.__prev_label_row = None
296 self.__date_format = str((_('lab_grid_date_format::%Y\n%b %d')).lstrip('lab_grid_date_format::'))
297
298 self.__init_ui()
299 self.__register_events()
300
301
302
304 if not self.IsSelection():
305 gmDispatcher.send(signal = u'statustext', msg = _('No results selected for deletion.'))
306 return True
307
308 selected_cells = self.get_selected_cells()
309 if len(selected_cells) > 20:
310 results = None
311 msg = _(
312 'There are %s results marked for deletion.\n'
313 '\n'
314 'Are you sure you want to delete these results ?'
315 ) % len(selected_cells)
316 else:
317 results = self.__cells_to_data(cells = selected_cells, exclude_multi_cells = False)
318 txt = u'\n'.join([ u'%s %s (%s): %s %s%s' % (
319 r['clin_when'].strftime('%x %H:%M').decode(gmI18N.get_encoding()),
320 r['unified_abbrev'],
321 r['unified_name'],
322 r['unified_val'],
323 r['val_unit'],
324 gmTools.coalesce(r['abnormality_indicator'], u'', u' (%s)')
325 ) for r in results
326 ])
327 msg = _(
328 'The following results are marked for deletion:\n'
329 '\n'
330 '%s\n'
331 '\n'
332 'Are you sure you want to delete these results ?'
333 ) % txt
334
335 dlg = gmGuiHelpers.c2ButtonQuestionDlg (
336 self,
337 -1,
338 caption = _('Deleting test results'),
339 question = msg,
340 button_defs = [
341 {'label': _('Delete'), 'tooltip': _('Yes, delete all the results.'), 'default': False},
342 {'label': _('Cancel'), 'tooltip': _('No, do NOT delete any results.'), 'default': True}
343 ]
344 )
345 decision = dlg.ShowModal()
346
347 if decision == wx.ID_YES:
348 if results is None:
349 results = self.__cells_to_data(cells = selected_cells, exclude_multi_cells = False)
350 for result in results:
351 gmPathLab.delete_test_result(result)
352
354 if not self.IsSelection():
355 gmDispatcher.send(signal = u'statustext', msg = _('Cannot sign results. No results selected.'))
356 return True
357
358 selected_cells = self.get_selected_cells()
359 if len(selected_cells) > 10:
360 test_count = len(selected_cells)
361 tests = None
362 else:
363 test_count = None
364 tests = self.__cells_to_data(cells = selected_cells, exclude_multi_cells = False)
365 if len(tests) == 0:
366 return True
367
368 dlg = cMeasurementsReviewDlg (
369 self,
370 -1,
371 tests = tests,
372 test_count = test_count
373 )
374 decision = dlg.ShowModal()
375
376 if decision == wx.ID_APPLY:
377 wx.BeginBusyCursor()
378
379 if dlg._RBTN_confirm_abnormal.GetValue():
380 abnormal = None
381 elif dlg._RBTN_results_normal.GetValue():
382 abnormal = False
383 else:
384 abnormal = True
385
386 if dlg._RBTN_confirm_relevance.GetValue():
387 relevant = None
388 elif dlg._RBTN_results_not_relevant.GetValue():
389 relevant = False
390 else:
391 relevant = True
392
393 if tests is None:
394 tests = self.__cells_to_data(cells = selected_cells, exclude_multi_cells = False)
395
396 comment = None
397 if len(tests) == 1:
398 comment = dlg._TCTRL_comment.GetValue()
399
400 for test in tests:
401 test.set_review (
402 technically_abnormal = abnormal,
403 clinically_relevant = relevant,
404 comment = comment,
405 make_me_responsible = dlg._CHBOX_responsible.IsChecked()
406 )
407
408 wx.EndBusyCursor()
409
410 dlg.Destroy()
411
413
414 if not self.IsSelection():
415 gmDispatcher.send(signal = u'statustext', msg = _('Cannot plot results. No results selected.'))
416 return True
417
418 tests = self.__cells_to_data (
419 cells = self.get_selected_cells(),
420 exclude_multi_cells = False,
421 auto_include_multi_cells = True
422 )
423
424 plot_measurements(parent = self, tests = tests)
425
427
428 sel_block_top_left = self.GetSelectionBlockTopLeft()
429 sel_block_bottom_right = self.GetSelectionBlockBottomRight()
430 sel_cols = self.GetSelectedCols()
431 sel_rows = self.GetSelectedRows()
432
433 selected_cells = []
434
435
436 selected_cells += self.GetSelectedCells()
437
438
439 selected_cells += list (
440 (row, col)
441 for row in sel_rows
442 for col in xrange(self.GetNumberCols())
443 )
444
445
446 selected_cells += list (
447 (row, col)
448 for row in xrange(self.GetNumberRows())
449 for col in sel_cols
450 )
451
452
453 for top_left, bottom_right in zip(self.GetSelectionBlockTopLeft(), self.GetSelectionBlockBottomRight()):
454 selected_cells += [
455 (row, col)
456 for row in xrange(top_left[0], bottom_right[0] + 1)
457 for col in xrange(top_left[1], bottom_right[1] + 1)
458 ]
459
460 return set(selected_cells)
461
462 - def select_cells(self, unsigned_only=False, accountables_only=False, keep_preselections=False):
463 """Select a range of cells according to criteria.
464
465 unsigned_only: include only those which are not signed at all yet
466 accountable_only: include only those for which the current user is responsible
467 keep_preselections: broaden (rather than replace) the range of selected cells
468
469 Combinations are powerful !
470 """
471 wx.BeginBusyCursor()
472 self.BeginBatch()
473
474 if not keep_preselections:
475 self.ClearSelection()
476
477 for col_idx in self.__cell_data.keys():
478 for row_idx in self.__cell_data[col_idx].keys():
479
480
481 do_not_include = False
482 for result in self.__cell_data[col_idx][row_idx]:
483 if unsigned_only:
484 if result['reviewed']:
485 do_not_include = True
486 break
487 if accountables_only:
488 if not result['you_are_responsible']:
489 do_not_include = True
490 break
491 if do_not_include:
492 continue
493
494 self.SelectBlock(row_idx, col_idx, row_idx, col_idx, addToSelected = True)
495
496 self.EndBatch()
497 wx.EndBusyCursor()
498
500
501 self.empty_grid()
502 if self.__patient is None:
503 return
504
505 emr = self.__patient.get_emr()
506
507 self.__row_label_data = emr.get_test_types_for_results()
508 test_type_labels = [ u'%s (%s)' % (test['unified_abbrev'], test['unified_name']) for test in self.__row_label_data ]
509 if len(test_type_labels) == 0:
510 return
511
512 test_date_labels = [ date[0].strftime(self.__date_format) for date in emr.get_dates_for_results() ]
513 results = emr.get_test_results_by_date()
514
515 self.BeginBatch()
516
517
518 self.AppendRows(numRows = len(test_type_labels))
519 for row_idx in range(len(test_type_labels)):
520 self.SetRowLabelValue(row_idx, test_type_labels[row_idx])
521
522
523 self.AppendCols(numCols = len(test_date_labels))
524 for date_idx in range(len(test_date_labels)):
525 self.SetColLabelValue(date_idx, test_date_labels[date_idx])
526
527
528 for result in results:
529 row = test_type_labels.index(u'%s (%s)' % (result['unified_abbrev'], result['unified_name']))
530 col = test_date_labels.index(result['clin_when'].strftime(self.__date_format))
531
532 try:
533 self.__cell_data[col]
534 except KeyError:
535 self.__cell_data[col] = {}
536
537
538 if self.__cell_data[col].has_key(row):
539 self.__cell_data[col][row].append(result)
540 self.__cell_data[col][row].sort(key = lambda x: x['clin_when'], reverse = True)
541 else:
542 self.__cell_data[col][row] = [result]
543
544
545 vals2display = []
546 for sub_result in self.__cell_data[col][row]:
547
548
549 ind = gmTools.coalesce(sub_result['abnormality_indicator'], u'').strip()
550 if ind != u'':
551 lab_abnormality_indicator = u' (%s)' % ind[:3]
552 else:
553 lab_abnormality_indicator = u''
554
555 if sub_result['is_technically_abnormal'] is None:
556 abnormality_indicator = lab_abnormality_indicator
557
558 elif sub_result['is_technically_abnormal'] is False:
559 abnormality_indicator = u''
560
561 else:
562
563 if lab_abnormality_indicator == u'':
564
565 abnormality_indicator = u' (%s)' % gmTools.u_plus_minus
566
567 else:
568 abnormality_indicator = lab_abnormality_indicator
569
570
571
572 sub_result_relevant = sub_result['is_clinically_relevant']
573 if sub_result_relevant is None:
574
575 sub_result_relevant = False
576
577 missing_review = False
578
579
580 if not sub_result['reviewed']:
581 missing_review = True
582
583 else:
584
585 if sub_result['you_are_responsible'] and not sub_result['review_by_you']:
586 missing_review = True
587
588
589 if len(sub_result['unified_val']) > 8:
590 tmp = u'%.7s%s' % (sub_result['unified_val'][:7], gmTools.u_ellipsis)
591 else:
592 tmp = u'%.8s' % sub_result['unified_val'][:8]
593
594
595 tmp = u'%s%.6s' % (tmp, abnormality_indicator)
596
597
598 has_sub_result_comment = gmTools.coalesce (
599 gmTools.coalesce(sub_result['note_test_org'], sub_result['comment']),
600 u''
601 ).strip() != u''
602 if has_sub_result_comment:
603 tmp = u'%s %s' % (tmp, gmTools.u_ellipsis)
604
605
606 if missing_review:
607 tmp = u'%s %s' % (tmp, gmTools.u_writing_hand)
608
609
610 if len(self.__cell_data[col][row]) > 1:
611 tmp = u'%s %s' % (sub_result['clin_when'].strftime('%H:%M'), tmp)
612
613 vals2display.append(tmp)
614
615 self.SetCellValue(row, col, u'\n'.join(vals2display))
616 self.SetCellAlignment(row, col, horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE)
617
618
619
620
621 if sub_result_relevant:
622 font = self.GetCellFont(row, col)
623 self.SetCellTextColour(row, col, 'firebrick')
624 font.SetWeight(wx.FONTWEIGHT_BOLD)
625 self.SetCellFont(row, col, font)
626
627
628 self.AutoSize()
629 self.EndBatch()
630 return
631
633 self.BeginBatch()
634 self.ClearGrid()
635
636
637 if self.GetNumberRows() > 0:
638 self.DeleteRows(pos = 0, numRows = self.GetNumberRows())
639 if self.GetNumberCols() > 0:
640 self.DeleteCols(pos = 0, numCols = self.GetNumberCols())
641 self.EndBatch()
642 self.__cell_data = {}
643 self.__row_label_data = []
644
683
931
932
933
935 self.CreateGrid(0, 1)
936 self.EnableEditing(0)
937 self.EnableDragGridSize(1)
938
939
940
941
942
943 self.SetRowLabelSize(150)
944 self.SetRowLabelAlignment(horiz = wx.ALIGN_LEFT, vert = wx.ALIGN_CENTRE)
945
946
947 dbcfg = gmCfg.cCfgSQL()
948 url = dbcfg.get2 (
949 option = u'external.urls.measurements_encyclopedia',
950 workplace = gmSurgery.gmCurrentPractice().active_workplace,
951 bias = 'user',
952 default = u'http://www.laborlexikon.de'
953 )
954
955 self.__WIN_corner = self.GetGridCornerLabelWindow()
956
957 LNK_lab = wx.lib.hyperlink.HyperLinkCtrl (
958 self.__WIN_corner,
959 -1,
960 label = _('Reference'),
961 style = wx.HL_DEFAULT_STYLE
962 )
963 LNK_lab.SetURL(url)
964 LNK_lab.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BACKGROUND))
965 LNK_lab.SetToolTipString(_(
966 'Navigate to an encyclopedia of measurements\n'
967 'and test methods on the web.\n'
968 '\n'
969 ' <%s>'
970 ) % url)
971
972 SZR_inner = wx.BoxSizer(wx.HORIZONTAL)
973 SZR_inner.Add((20, 20), 1, wx.EXPAND, 0)
974 SZR_inner.Add(LNK_lab, 0, wx.ALIGN_CENTER_VERTICAL, 0)
975 SZR_inner.Add((20, 20), 1, wx.EXPAND, 0)
976
977 SZR_corner = wx.BoxSizer(wx.VERTICAL)
978 SZR_corner.Add((20, 20), 1, wx.EXPAND, 0)
979 SZR_corner.AddWindow(SZR_inner, 0, wx.EXPAND)
980 SZR_corner.Add((20, 20), 1, wx.EXPAND, 0)
981
982 self.__WIN_corner.SetSizer(SZR_corner)
983 SZR_corner.Fit(self.__WIN_corner)
984
986 self.__WIN_corner.Layout()
987
988 - def __cells_to_data(self, cells=None, exclude_multi_cells=False, auto_include_multi_cells=False):
989 """List of <cells> must be in row / col order."""
990 data = []
991 for row, col in cells:
992 try:
993
994 data_list = self.__cell_data[col][row]
995 except KeyError:
996 continue
997
998 if len(data_list) == 1:
999 data.append(data_list[0])
1000 continue
1001
1002 if exclude_multi_cells:
1003 gmDispatcher.send(signal = u'statustext', msg = _('Excluding multi-result field from further processing.'))
1004 continue
1005
1006 if auto_include_multi_cells:
1007 data.extend(data_list)
1008 continue
1009
1010 data_to_include = self.__get_choices_from_multi_cell(cell_data = data_list)
1011 if data_to_include is None:
1012 continue
1013 data.extend(data_to_include)
1014
1015 return data
1016
1018 data = gmListWidgets.get_choices_from_list (
1019 parent = self,
1020 msg = _(
1021 'Your selection includes a field with multiple results.\n'
1022 '\n'
1023 'Please select the individual results you want to work on:'
1024 ),
1025 caption = _('Selecting test results'),
1026 choices = [ [d['clin_when'], d['unified_abbrev'], d['unified_name'], d['unified_val']] for d in cell_data ],
1027 columns = [_('Date / Time'), _('Code'), _('Test'), _('Result')],
1028 data = cell_data,
1029 single_selection = single_selection
1030 )
1031 return data
1032
1033
1034
1036
1037 self.GetGridWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_cells)
1038 self.GetGridRowLabelWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_row_labels)
1039
1040
1041
1042 self.Bind(wx.EVT_SIZE, self.__resize_corner_window)
1043
1044
1045 self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.__on_cell_left_dclicked)
1046
1048 col = evt.GetCol()
1049 row = evt.GetRow()
1050
1051
1052 try:
1053 self.__cell_data[col][row]
1054 except KeyError:
1055
1056
1057 return
1058
1059 if len(self.__cell_data[col][row]) > 1:
1060 data = self.__get_choices_from_multi_cell(cell_data = self.__cell_data[col][row], single_selection = True)
1061 else:
1062 data = self.__cell_data[col][row][0]
1063
1064 if data is None:
1065 return
1066
1067 edit_measurement(parent = self, measurement = data, single_entry = True)
1068
1069
1070
1071
1072
1073
1074
1076
1077
1078
1079 x, y = self.CalcUnscrolledPosition(evt.GetX(), evt.GetY())
1080
1081 row = self.YToRow(y)
1082
1083 if self.__prev_label_row == row:
1084 return
1085
1086 self.__prev_label_row == row
1087
1088 evt.GetEventObject().SetToolTipString(self.get_row_tooltip(row = row))
1089
1090
1091
1092
1093
1094
1095
1096
1098 """Calculate where the mouse is and set the tooltip dynamically."""
1099
1100
1101
1102 x, y = self.CalcUnscrolledPosition(evt.GetX(), evt.GetY())
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116 row, col = self.XYToCell(x, y)
1117
1118 if (row == self.__prev_row) and (col == self.__prev_col):
1119 return
1120
1121 self.__prev_row = row
1122 self.__prev_col = col
1123
1124 evt.GetEventObject().SetToolTipString(self.get_cell_tooltip(col=col, row=row))
1125
1126
1127
1131
1132 patient = property(lambda x:x, _set_patient)
1133
1134 from Gnumed.wxGladeWidgets import wxgMeasurementsPnl
1135
1136 -class cMeasurementsPnl(wxgMeasurementsPnl.wxgMeasurementsPnl, gmRegetMixin.cRegetOnPaintMixin):
1137 """Panel holding a grid with lab data. Used as notebook page."""
1138
1145
1146
1147
1149 gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._on_pre_patient_selection)
1150 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._on_post_patient_selection)
1151 gmDispatcher.connect(signal = u'test_result_mod_db', receiver = self._schedule_data_reget)
1152 gmDispatcher.connect(signal = u'reviewed_test_results_mod_db', receiver = self._schedule_data_reget)
1153
1155 wx.CallAfter(self.__on_post_patient_selection)
1156
1158 self._schedule_data_reget()
1159
1161 wx.CallAfter(self.__on_pre_patient_selection)
1162
1165
1168
1171
1177
1180
1183
1186
1187
1188
1190 self.__action_button_popup = wx.Menu(title = _('Perform on selected results:'))
1191
1192 menu_id = wx.NewId()
1193 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('Review and &sign')))
1194 wx.EVT_MENU(self.__action_button_popup, menu_id, self.__on_sign_current_selection)
1195
1196 menu_id = wx.NewId()
1197 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('Plot')))
1198 wx.EVT_MENU(self.__action_button_popup, menu_id, self.__on_plot_current_selection)
1199
1200 menu_id = wx.NewId()
1201 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('Export to &file')))
1202
1203 self.__action_button_popup.Enable(id = menu_id, enable = False)
1204
1205 menu_id = wx.NewId()
1206 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('Export to &clipboard')))
1207
1208 self.__action_button_popup.Enable(id = menu_id, enable = False)
1209
1210 menu_id = wx.NewId()
1211 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('&Delete')))
1212 wx.EVT_MENU(self.__action_button_popup, menu_id, self.__on_delete_current_selection)
1213
1214
1215
1216
1217
1218
1219
1228
1229
1230
1231 from Gnumed.wxGladeWidgets import wxgMeasurementsReviewDlg
1232
1234
1236
1237 try:
1238 tests = kwargs['tests']
1239 del kwargs['tests']
1240 test_count = len(tests)
1241 try: del kwargs['test_count']
1242 except KeyError: pass
1243 except KeyError:
1244 tests = None
1245 test_count = kwargs['test_count']
1246 del kwargs['test_count']
1247
1248 wxgMeasurementsReviewDlg.wxgMeasurementsReviewDlg.__init__(self, *args, **kwargs)
1249
1250 if tests is None:
1251 msg = _('%s results selected. Too many to list individually.') % test_count
1252 else:
1253 msg = ' // '.join (
1254 [ u'%s: %s %s (%s)' % (
1255 t['unified_abbrev'],
1256 t['unified_val'],
1257 t['val_unit'],
1258 t['clin_when'].strftime('%x').decode(gmI18N.get_encoding())
1259 ) for t in tests
1260 ]
1261 )
1262
1263 self._LBL_tests.SetLabel(msg)
1264
1265 if test_count == 1:
1266 self._TCTRL_comment.Enable(True)
1267 self._TCTRL_comment.SetValue(gmTools.coalesce(tests[0]['review_comment'], u''))
1268 if tests[0]['you_are_responsible']:
1269 self._CHBOX_responsible.Enable(False)
1270
1271 self.Fit()
1272
1273
1274
1280
1281 from Gnumed.wxGladeWidgets import wxgMeasurementEditAreaPnl
1282
1283 -class cMeasurementEditAreaPnl(wxgMeasurementEditAreaPnl.wxgMeasurementEditAreaPnl, gmEditArea.cGenericEditAreaMixin):
1284 """This edit area saves *new* measurements into the active patient only."""
1285
1302
1303
1304
1306 self._PRW_test.SetText(u'', None, True)
1307 self.__refresh_loinc_info()
1308 self.__refresh_previous_value()
1309 self.__update_units_context()
1310 self._TCTRL_result.SetValue(u'')
1311 self._PRW_units.SetText(u'', None, True)
1312 self._PRW_abnormality_indicator.SetText(u'', None, True)
1313 if self.__default_date is None:
1314 self._DPRW_evaluated.SetData(data = pyDT.datetime.now(tz = gmDateTime.gmCurrentLocalTimezone))
1315 else:
1316 self._DPRW_evaluated.SetData(data = None)
1317 self._TCTRL_note_test_org.SetValue(u'')
1318 self._PRW_intended_reviewer.SetData(gmStaff.gmCurrentProvider()['pk_staff'])
1319 self._PRW_problem.SetData()
1320 self._TCTRL_narrative.SetValue(u'')
1321 self._CHBOX_review.SetValue(False)
1322 self._CHBOX_abnormal.SetValue(False)
1323 self._CHBOX_relevant.SetValue(False)
1324 self._CHBOX_abnormal.Enable(False)
1325 self._CHBOX_relevant.Enable(False)
1326 self._TCTRL_review_comment.SetValue(u'')
1327 self._TCTRL_normal_min.SetValue(u'')
1328 self._TCTRL_normal_max.SetValue(u'')
1329 self._TCTRL_normal_range.SetValue(u'')
1330 self._TCTRL_target_min.SetValue(u'')
1331 self._TCTRL_target_max.SetValue(u'')
1332 self._TCTRL_target_range.SetValue(u'')
1333 self._TCTRL_norm_ref_group.SetValue(u'')
1334
1335 self._PRW_test.SetFocus()
1336
1338 self._PRW_test.SetData(data = self.data['pk_test_type'])
1339 self.__refresh_loinc_info()
1340 self.__refresh_previous_value()
1341 self.__update_units_context()
1342 self._TCTRL_result.SetValue(self.data['unified_val'])
1343 self._PRW_units.SetText(self.data['val_unit'], self.data['val_unit'], True)
1344 self._PRW_abnormality_indicator.SetText (
1345 gmTools.coalesce(self.data['abnormality_indicator'], u''),
1346 gmTools.coalesce(self.data['abnormality_indicator'], u''),
1347 True
1348 )
1349 self._DPRW_evaluated.SetData(data = self.data['clin_when'])
1350 self._TCTRL_note_test_org.SetValue(gmTools.coalesce(self.data['note_test_org'], u''))
1351 self._PRW_intended_reviewer.SetData(self.data['pk_intended_reviewer'])
1352 self._PRW_problem.SetData(self.data['pk_episode'])
1353 self._TCTRL_narrative.SetValue(gmTools.coalesce(self.data['comment'], u''))
1354 self._CHBOX_review.SetValue(False)
1355 self._CHBOX_abnormal.SetValue(gmTools.coalesce(self.data['is_technically_abnormal'], False))
1356 self._CHBOX_relevant.SetValue(gmTools.coalesce(self.data['is_clinically_relevant'], False))
1357 self._CHBOX_abnormal.Enable(False)
1358 self._CHBOX_relevant.Enable(False)
1359 self._TCTRL_review_comment.SetValue(gmTools.coalesce(self.data['review_comment'], u''))
1360 self._TCTRL_normal_min.SetValue(unicode(gmTools.coalesce(self.data['val_normal_min'], u'')))
1361 self._TCTRL_normal_max.SetValue(unicode(gmTools.coalesce(self.data['val_normal_max'], u'')))
1362 self._TCTRL_normal_range.SetValue(gmTools.coalesce(self.data['val_normal_range'], u''))
1363 self._TCTRL_target_min.SetValue(unicode(gmTools.coalesce(self.data['val_target_min'], u'')))
1364 self._TCTRL_target_max.SetValue(unicode(gmTools.coalesce(self.data['val_target_max'], u'')))
1365 self._TCTRL_target_range.SetValue(gmTools.coalesce(self.data['val_target_range'], u''))
1366 self._TCTRL_norm_ref_group.SetValue(gmTools.coalesce(self.data['norm_ref_group'], u''))
1367
1368 self._TCTRL_result.SetFocus()
1369
1371 self._refresh_from_existing()
1372
1373 self._PRW_test.SetText(u'', None, True)
1374 self.__refresh_loinc_info()
1375 self.__refresh_previous_value()
1376 self.__update_units_context()
1377 self._TCTRL_result.SetValue(u'')
1378 self._PRW_units.SetText(u'', None, True)
1379 self._PRW_abnormality_indicator.SetText(u'', None, True)
1380
1381 self._TCTRL_note_test_org.SetValue(u'')
1382 self._TCTRL_narrative.SetValue(u'')
1383 self._CHBOX_review.SetValue(False)
1384 self._CHBOX_abnormal.SetValue(False)
1385 self._CHBOX_relevant.SetValue(False)
1386 self._CHBOX_abnormal.Enable(False)
1387 self._CHBOX_relevant.Enable(False)
1388 self._TCTRL_review_comment.SetValue(u'')
1389 self._TCTRL_normal_min.SetValue(u'')
1390 self._TCTRL_normal_max.SetValue(u'')
1391 self._TCTRL_normal_range.SetValue(u'')
1392 self._TCTRL_target_min.SetValue(u'')
1393 self._TCTRL_target_max.SetValue(u'')
1394 self._TCTRL_target_range.SetValue(u'')
1395 self._TCTRL_norm_ref_group.SetValue(u'')
1396
1397 self._PRW_test.SetFocus()
1398
1400
1401 validity = True
1402
1403 if not self._DPRW_evaluated.is_valid_timestamp():
1404 self._DPRW_evaluated.display_as_valid(False)
1405 validity = False
1406 else:
1407 self._DPRW_evaluated.display_as_valid(True)
1408
1409 if self._TCTRL_result.GetValue().strip() == u'':
1410 validity = False
1411 self.display_ctrl_as_valid(self._TCTRL_result, False)
1412 else:
1413 self.display_ctrl_as_valid(self._TCTRL_result, True)
1414
1415 if self._PRW_problem.GetValue().strip() == u'':
1416 self._PRW_problem.display_as_valid(False)
1417 validity = False
1418 else:
1419 self._PRW_problem.display_as_valid(True)
1420
1421 if self._PRW_test.GetValue().strip() == u'':
1422 self._PRW_test.display_as_valid(False)
1423 validity = False
1424 else:
1425 self._PRW_test.display_as_valid(True)
1426
1427 if self._PRW_intended_reviewer.GetData() is None:
1428 self._PRW_intended_reviewer.display_as_valid(False)
1429 validity = False
1430 else:
1431 self._PRW_intended_reviewer.display_as_valid(True)
1432
1433 if self._PRW_units.GetValue().strip() == u'':
1434 self._PRW_units.display_as_valid(False)
1435 validity = False
1436 else:
1437 self._PRW_units.display_as_valid(True)
1438
1439 ctrls = [self._TCTRL_normal_min, self._TCTRL_normal_max, self._TCTRL_target_min, self._TCTRL_target_max]
1440 for widget in ctrls:
1441 val = widget.GetValue().strip()
1442 if val == u'':
1443 continue
1444 try:
1445 decimal.Decimal(val.replace(',', u'.', 1))
1446 self.display_ctrl_as_valid(widget, True)
1447 except:
1448 validity = False
1449 self.display_ctrl_as_valid(widget, False)
1450
1451 if validity is False:
1452 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save result. Invalid or missing essential input.'))
1453
1454 return validity
1455
1457
1458 emr = gmPerson.gmCurrentPatient().get_emr()
1459
1460 success, result = gmTools.input2decimal(self._TCTRL_result.GetValue())
1461 if success:
1462 v_num = result
1463 v_al = None
1464 else:
1465 v_al = self._TCTRL_result.GetValue().strip()
1466 v_num = None
1467
1468 pk_type = self._PRW_test.GetData()
1469 if pk_type is None:
1470 tt = gmPathLab.create_measurement_type (
1471 lab = None,
1472 abbrev = self._PRW_test.GetValue().strip(),
1473 name = self._PRW_test.GetValue().strip(),
1474 unit = gmTools.coalesce(self._PRW_units.GetData(), self._PRW_units.GetValue()).strip()
1475 )
1476 pk_type = tt['pk_test_type']
1477
1478 tr = emr.add_test_result (
1479 episode = self._PRW_problem.GetData(can_create=True, is_open=False),
1480 type = pk_type,
1481 intended_reviewer = self._PRW_intended_reviewer.GetData(),
1482 val_num = v_num,
1483 val_alpha = v_al,
1484 unit = self._PRW_units.GetValue()
1485 )
1486
1487 tr['clin_when'] = self._DPRW_evaluated.GetData().get_pydt()
1488
1489 ctrls = [
1490 ('abnormality_indicator', self._PRW_abnormality_indicator),
1491 ('note_test_org', self._TCTRL_note_test_org),
1492 ('comment', self._TCTRL_narrative),
1493 ('val_normal_range', self._TCTRL_normal_range),
1494 ('val_target_range', self._TCTRL_target_range),
1495 ('norm_ref_group', self._TCTRL_norm_ref_group)
1496 ]
1497 for field, widget in ctrls:
1498 tr[field] = widget.GetValue().strip()
1499
1500 ctrls = [
1501 ('val_normal_min', self._TCTRL_normal_min),
1502 ('val_normal_max', self._TCTRL_normal_max),
1503 ('val_target_min', self._TCTRL_target_min),
1504 ('val_target_max', self._TCTRL_target_max)
1505 ]
1506 for field, widget in ctrls:
1507 val = widget.GetValue().strip()
1508 if val == u'':
1509 tr[field] = None
1510 else:
1511 tr[field] = decimal.Decimal(val.replace(',', u'.', 1))
1512
1513 tr.save_payload()
1514
1515 if self._CHBOX_review.GetValue() is True:
1516 tr.set_review (
1517 technically_abnormal = self._CHBOX_abnormal.GetValue(),
1518 clinically_relevant = self._CHBOX_relevant.GetValue(),
1519 comment = gmTools.none_if(self._TCTRL_review_comment.GetValue().strip(), u''),
1520 make_me_responsible = False
1521 )
1522
1523 self.data = tr
1524
1525 wx.CallAfter (
1526 plot_adjacent_measurements,
1527 test = self.data,
1528 plot_singular_result = False,
1529 use_default_template = True
1530 )
1531
1532 return True
1533
1535
1536 success, result = gmTools.input2decimal(self._TCTRL_result.GetValue())
1537 if success:
1538 v_num = result
1539 v_al = None
1540 else:
1541 v_num = None
1542 v_al = self._TCTRL_result.GetValue().strip()
1543
1544 pk_type = self._PRW_test.GetData()
1545 if pk_type is None:
1546 tt = gmPathLab.create_measurement_type (
1547 lab = None,
1548 abbrev = self._PRW_test.GetValue().strip(),
1549 name = self._PRW_test.GetValue().strip(),
1550 unit = gmTools.none_if(self._PRW_units.GetValue().strip(), u'')
1551 )
1552 pk_type = tt['pk_test_type']
1553
1554 tr = self.data
1555
1556 tr['pk_episode'] = self._PRW_problem.GetData(can_create=True, is_open=False)
1557 tr['pk_test_type'] = pk_type
1558 tr['pk_intended_reviewer'] = self._PRW_intended_reviewer.GetData()
1559 tr['val_num'] = v_num
1560 tr['val_alpha'] = v_al
1561 tr['val_unit'] = gmTools.coalesce(self._PRW_units.GetData(), self._PRW_units.GetValue()).strip()
1562 tr['clin_when'] = self._DPRW_evaluated.GetData().get_pydt()
1563
1564 ctrls = [
1565 ('abnormality_indicator', self._PRW_abnormality_indicator),
1566 ('note_test_org', self._TCTRL_note_test_org),
1567 ('comment', self._TCTRL_narrative),
1568 ('val_normal_range', self._TCTRL_normal_range),
1569 ('val_target_range', self._TCTRL_target_range),
1570 ('norm_ref_group', self._TCTRL_norm_ref_group)
1571 ]
1572 for field, widget in ctrls:
1573 tr[field] = widget.GetValue().strip()
1574
1575 ctrls = [
1576 ('val_normal_min', self._TCTRL_normal_min),
1577 ('val_normal_max', self._TCTRL_normal_max),
1578 ('val_target_min', self._TCTRL_target_min),
1579 ('val_target_max', self._TCTRL_target_max)
1580 ]
1581 for field, widget in ctrls:
1582 val = widget.GetValue().strip()
1583 if val == u'':
1584 tr[field] = None
1585 else:
1586 tr[field] = decimal.Decimal(val.replace(',', u'.', 1))
1587
1588 tr.save_payload()
1589
1590 if self._CHBOX_review.GetValue() is True:
1591 tr.set_review (
1592 technically_abnormal = self._CHBOX_abnormal.GetValue(),
1593 clinically_relevant = self._CHBOX_relevant.GetValue(),
1594 comment = gmTools.none_if(self._TCTRL_review_comment.GetValue().strip(), u''),
1595 make_me_responsible = False
1596 )
1597
1598 wx.CallAfter (
1599 plot_adjacent_measurements,
1600 test = self.data,
1601 plot_singular_result = False,
1602 use_default_template = True
1603 )
1604
1605 return True
1606
1607
1608
1612
1614 self.__refresh_loinc_info()
1615 self.__refresh_previous_value()
1616 self.__update_units_context()
1617
1619
1620 if not self._CHBOX_review.GetValue():
1621 self._CHBOX_abnormal.SetValue(self._PRW_abnormality_indicator.GetValue().strip() != u'')
1622
1624 self._CHBOX_abnormal.Enable(self._CHBOX_review.GetValue())
1625 self._CHBOX_relevant.Enable(self._CHBOX_review.GetValue())
1626 self._TCTRL_review_comment.Enable(self._CHBOX_review.GetValue())
1627
1644
1645
1646
1648
1649 self._PRW_units.unset_context(context = u'loinc')
1650
1651 tt = self._PRW_test.GetData(as_instance = True)
1652
1653 if tt is None:
1654 self._PRW_units.unset_context(context = u'pk_type')
1655 if self._PRW_test.GetValue().strip() == u'':
1656 self._PRW_units.unset_context(context = u'test_name')
1657 else:
1658 self._PRW_units.set_context(context = u'test_name', val = self._PRW_test.GetValue().strip())
1659 return
1660
1661 self._PRW_units.set_context(context = u'pk_type', val = tt['pk_test_type'])
1662 self._PRW_units.set_context(context = u'test_name', val = tt['name'])
1663
1664 if tt['loinc'] is None:
1665 return
1666
1667 self._PRW_units.set_context(context = u'loinc', val = tt['loinc'])
1668
1670
1671 self._TCTRL_loinc.SetValue(u'')
1672
1673 if self._PRW_test.GetData() is None:
1674 return
1675
1676 tt = self._PRW_test.GetData(as_instance = True)
1677
1678 if tt['loinc'] is None:
1679 return
1680
1681 info = gmLOINC.loinc2term(loinc = tt['loinc'])
1682 if len(info) == 0:
1683 self._TCTRL_loinc.SetValue(u'')
1684 return
1685
1686 self._TCTRL_loinc.SetValue(u'%s: %s' % (tt['loinc'], info[0]))
1687
1710
1711
1712
1735
1736 def refresh(lctrl):
1737 mtypes = gmPathLab.get_measurement_types(order_by = 'name, abbrev')
1738 items = [ [
1739 m['abbrev'],
1740 m['name'],
1741 gmTools.coalesce(m['loinc'], u''),
1742 gmTools.coalesce(m['conversion_unit'], u''),
1743 gmTools.coalesce(m['comment_type'], u''),
1744 gmTools.coalesce(m['name_org'], u'?'),
1745 gmTools.coalesce(m['comment_org'], u''),
1746 m['pk_test_type']
1747 ] for m in mtypes ]
1748 lctrl.set_string_items(items)
1749 lctrl.set_data(mtypes)
1750
1751 def delete(measurement_type):
1752 if measurement_type.in_use:
1753 gmDispatcher.send (
1754 signal = 'statustext',
1755 beep = True,
1756 msg = _('Cannot delete measurement type [%s (%s)] because it is in use.') % (measurement_type['name'], measurement_type['abbrev'])
1757 )
1758 return False
1759 gmPathLab.delete_measurement_type(measurement_type = measurement_type['pk_test_type'])
1760 return True
1761
1762 msg = _(
1763 '\n'
1764 'These are the measurement types currently defined in GNUmed.\n'
1765 '\n'
1766 )
1767
1768 gmListWidgets.get_choices_from_list (
1769 parent = parent,
1770 msg = msg,
1771 caption = _('Showing measurement types.'),
1772 columns = [_('Abbrev'), _('Name'), _('LOINC'), _('Base unit'), _('Comment'), _('Org'), _('Comment'), u'#'],
1773 single_selection = True,
1774 refresh_callback = refresh,
1775 edit_callback = edit,
1776 new_callback = edit,
1777 delete_callback = delete
1778 )
1779
1781
1783
1784 query = u"""
1785 SELECT DISTINCT ON (field_label)
1786 pk_test_type AS data,
1787 name_tt
1788 || ' ('
1789 || coalesce (
1790 (SELECT unit || ' @ ' || organization FROM clin.v_test_orgs c_vto WHERE c_vto.pk_test_org = vcutt.pk_test_org),
1791 '%(in_house)s'
1792 )
1793 || ')'
1794 AS field_label,
1795 name_tt
1796 || ' ('
1797 || coalesce(code_tt || ', ', '')
1798 || abbrev_tt || ', '
1799 || coalesce(abbrev_meta || ': ' || name_meta || ', ', '')
1800 || coalesce (
1801 (SELECT unit || ' @ ' || organization FROM clin.v_test_orgs c_vto WHERE c_vto.pk_test_org = vcutt.pk_test_org),
1802 '%(in_house)s'
1803 )
1804 || ')'
1805 AS list_label
1806 FROM
1807 clin.v_unified_test_types vcutt
1808 WHERE
1809 abbrev_meta %%(fragment_condition)s
1810 OR
1811 name_meta %%(fragment_condition)s
1812 OR
1813 abbrev_tt %%(fragment_condition)s
1814 OR
1815 name_tt %%(fragment_condition)s
1816 OR
1817 code_tt %%(fragment_condition)s
1818 ORDER BY field_label
1819 LIMIT 50""" % {'in_house': _('generic / in house lab')}
1820
1821 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
1822 mp.setThresholds(1, 2, 4)
1823 mp.word_separators = '[ \t:@]+'
1824 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
1825 self.matcher = mp
1826 self.SetToolTipString(_('Select the type of measurement.'))
1827 self.selection_only = False
1828
1834
1835 from Gnumed.wxGladeWidgets import wxgMeasurementTypeEAPnl
1836
1837 -class cMeasurementTypeEAPnl(wxgMeasurementTypeEAPnl.wxgMeasurementTypeEAPnl, gmEditArea.cGenericEditAreaMixin):
1838
1855
1856
1858
1859
1860 query = u"""
1861 select distinct on (name)
1862 pk,
1863 name
1864 from clin.test_type
1865 where
1866 name %(fragment_condition)s
1867 order by name
1868 limit 50"""
1869 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
1870 mp.setThresholds(1, 2, 4)
1871 self._PRW_name.matcher = mp
1872 self._PRW_name.selection_only = False
1873 self._PRW_name.add_callback_on_lose_focus(callback = self._on_name_lost_focus)
1874
1875
1876 query = u"""
1877 select distinct on (abbrev)
1878 pk,
1879 abbrev
1880 from clin.test_type
1881 where
1882 abbrev %(fragment_condition)s
1883 order by abbrev
1884 limit 50"""
1885 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
1886 mp.setThresholds(1, 2, 3)
1887 self._PRW_abbrev.matcher = mp
1888 self._PRW_abbrev.selection_only = False
1889
1890
1891 self._PRW_conversion_unit.selection_only = False
1892
1893
1894 query = u"""
1895 SELECT DISTINCT ON (list_label)
1896 data,
1897 field_label,
1898 list_label
1899 FROM ((
1900
1901 SELECT
1902 loinc AS data,
1903 loinc AS field_label,
1904 (loinc || ': ' || abbrev || ' (' || name || ')') AS list_label
1905 FROM clin.test_type
1906 WHERE loinc %(fragment_condition)s
1907 LIMIT 50
1908
1909 ) UNION ALL (
1910
1911 SELECT
1912 code AS data,
1913 code AS field_label,
1914 (code || ': ' || term) AS list_label
1915 FROM ref.v_coded_terms
1916 WHERE
1917 coding_system = 'LOINC'
1918 AND
1919 lang = i18n.get_curr_lang()
1920 AND
1921 (code %(fragment_condition)s
1922 OR
1923 term %(fragment_condition)s)
1924 LIMIT 50
1925
1926 ) UNION ALL (
1927
1928 SELECT
1929 code AS data,
1930 code AS field_label,
1931 (code || ': ' || term) AS list_label
1932 FROM ref.v_coded_terms
1933 WHERE
1934 coding_system = 'LOINC'
1935 AND
1936 lang = 'en_EN'
1937 AND
1938 (code %(fragment_condition)s
1939 OR
1940 term %(fragment_condition)s)
1941 LIMIT 50
1942
1943 ) UNION ALL (
1944
1945 SELECT
1946 code AS data,
1947 code AS field_label,
1948 (code || ': ' || term) AS list_label
1949 FROM ref.v_coded_terms
1950 WHERE
1951 coding_system = 'LOINC'
1952 AND
1953 (code %(fragment_condition)s
1954 OR
1955 term %(fragment_condition)s)
1956 LIMIT 50
1957 )
1958 ) AS all_known_loinc
1959
1960 ORDER BY list_label
1961 LIMIT 50"""
1962 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
1963 mp.setThresholds(1, 2, 4)
1964 self._PRW_loinc.matcher = mp
1965 self._PRW_loinc.selection_only = False
1966 self._PRW_loinc.add_callback_on_lose_focus(callback = self._on_loinc_lost_focus)
1967
1969
1970 test = self._PRW_name.GetValue().strip()
1971
1972 if test == u'':
1973 self._PRW_conversion_unit.unset_context(context = u'test_name')
1974 return
1975
1976 self._PRW_conversion_unit.set_context(context = u'test_name', val = test)
1977
1979 loinc = self._PRW_loinc.GetData()
1980
1981 if loinc is None:
1982 self._TCTRL_loinc_info.SetValue(u'')
1983 self._PRW_conversion_unit.unset_context(context = u'loinc')
1984 return
1985
1986 self._PRW_conversion_unit.set_context(context = u'loinc', val = loinc)
1987
1988 info = gmLOINC.loinc2term(loinc = loinc)
1989 if len(info) == 0:
1990 self._TCTRL_loinc_info.SetValue(u'')
1991 return
1992
1993 self._TCTRL_loinc_info.SetValue(info[0])
1994
1995
1996
1998
1999 has_errors = False
2000 for field in [self._PRW_name, self._PRW_abbrev, self._PRW_conversion_unit]:
2001 if field.GetValue().strip() in [u'', None]:
2002 has_errors = True
2003 field.display_as_valid(valid = False)
2004 else:
2005 field.display_as_valid(valid = True)
2006 field.Refresh()
2007
2008 return (not has_errors)
2009
2011
2012 pk_org = self._PRW_test_org.GetData()
2013 if pk_org is None:
2014 pk_org = gmPathLab.create_test_org (
2015 name = gmTools.none_if(self._PRW_test_org.GetValue().strip(), u''),
2016 comment = gmTools.none_if(self._TCTRL_comment_org.GetValue().strip(), u'')
2017 )['pk_test_org']
2018
2019 tt = gmPathLab.create_measurement_type (
2020 lab = pk_org,
2021 abbrev = self._PRW_abbrev.GetValue().strip(),
2022 name = self._PRW_name.GetValue().strip(),
2023 unit = gmTools.coalesce (
2024 self._PRW_conversion_unit.GetData(),
2025 self._PRW_conversion_unit.GetValue()
2026 ).strip()
2027 )
2028 if self._PRW_loinc.GetData() is not None:
2029 tt['loinc'] = gmTools.none_if(self._PRW_loinc.GetData().strip(), u'')
2030 else:
2031 tt['loinc'] = gmTools.none_if(self._PRW_loinc.GetValue().strip(), u'')
2032 tt['comment_type'] = gmTools.none_if(self._TCTRL_comment_type.GetValue().strip(), u'')
2033 tt.save()
2034
2035 self.data = tt
2036
2037 return True
2038
2065
2067 self._PRW_name.SetText(u'', None, True)
2068 self._on_name_lost_focus()
2069 self._PRW_abbrev.SetText(u'', None, True)
2070 self._PRW_conversion_unit.SetText(u'', None, True)
2071 self._PRW_loinc.SetText(u'', None, True)
2072 self._on_loinc_lost_focus()
2073 self._TCTRL_comment_type.SetValue(u'')
2074 self._PRW_test_org.SetText(u'', None, True)
2075 self._TCTRL_comment_org.SetValue(u'')
2076
2077 self._PRW_name.SetFocus()
2078
2080 self._PRW_name.SetText(self.data['name'], self.data['name'], True)
2081 self._on_name_lost_focus()
2082 self._PRW_abbrev.SetText(self.data['abbrev'], self.data['abbrev'], True)
2083 self._PRW_conversion_unit.SetText (
2084 gmTools.coalesce(self.data['conversion_unit'], u''),
2085 self.data['conversion_unit'],
2086 True
2087 )
2088 self._PRW_loinc.SetText (
2089 gmTools.coalesce(self.data['loinc'], u''),
2090 self.data['loinc'],
2091 True
2092 )
2093 self._on_loinc_lost_focus()
2094 self._TCTRL_comment_type.SetValue(gmTools.coalesce(self.data['comment_type'], u''))
2095 self._PRW_test_org.SetText (
2096 gmTools.coalesce(self.data['pk_test_org'], u'', self.data['name_org']),
2097 self.data['pk_test_org'],
2098 True
2099 )
2100 self._TCTRL_comment_org.SetValue(gmTools.coalesce(self.data['comment_org'], u''))
2101
2102 self._PRW_name.SetFocus()
2103
2114
2115 _SQL_units_from_test_results = u"""
2116 -- via clin.v_test_results.pk_type (for types already used in results)
2117 SELECT
2118 val_unit AS data,
2119 val_unit AS field_label,
2120 val_unit || ' (' || name_tt || ')' AS list_label,
2121 1 AS rank
2122 FROM
2123 clin.v_test_results
2124 WHERE
2125 (
2126 val_unit %(fragment_condition)s
2127 OR
2128 conversion_unit %(fragment_condition)s
2129 )
2130 %(ctxt_type_pk)s
2131 %(ctxt_test_name)s
2132 """
2133
2134 _SQL_units_from_test_types = u"""
2135 -- via clin.test_type (for types not yet used in results)
2136 SELECT
2137 conversion_unit AS data,
2138 conversion_unit AS field_label,
2139 conversion_unit || ' (' || name || ')' AS list_label,
2140 2 AS rank
2141 FROM
2142 clin.test_type
2143 WHERE
2144 conversion_unit %(fragment_condition)s
2145 %(ctxt_ctt)s
2146 """
2147
2148 _SQL_units_from_loinc_ipcc = u"""
2149 -- via ref.loinc.ipcc_units
2150 SELECT
2151 ipcc_units AS data,
2152 ipcc_units AS field_label,
2153 ipcc_units || ' (LOINC.ipcc: ' || term || ')' AS list_label,
2154 3 AS rank
2155 FROM
2156 ref.loinc
2157 WHERE
2158 ipcc_units %(fragment_condition)s
2159 %(ctxt_loinc)s
2160 %(ctxt_loinc_term)s
2161 """
2162
2163 _SQL_units_from_loinc_submitted = u"""
2164 -- via ref.loinc.submitted_units
2165 SELECT
2166 submitted_units AS data,
2167 submitted_units AS field_label,
2168 submitted_units || ' (LOINC.submitted:' || term || ')' AS list_label,
2169 3 AS rank
2170 FROM
2171 ref.loinc
2172 WHERE
2173 submitted_units %(fragment_condition)s
2174 %(ctxt_loinc)s
2175 %(ctxt_loinc_term)s
2176 """
2177
2178 _SQL_units_from_loinc_example = u"""
2179 -- via ref.loinc.example_units
2180 SELECT
2181 example_units AS data,
2182 example_units AS field_label,
2183 example_units || ' (LOINC.example: ' || term || ')' AS list_label,
2184 3 AS rank
2185 FROM
2186 ref.loinc
2187 WHERE
2188 example_units %(fragment_condition)s
2189 %(ctxt_loinc)s
2190 %(ctxt_loinc_term)s
2191 """
2192
2193 _SQL_units_from_atc = u"""
2194 -- via rev.atc.unit
2195 SELECT
2196 unit AS data,
2197 unit AS field_label,
2198 unit || ' (ATC: ' || term || ')' AS list_label,
2199 2 AS rank
2200 FROM
2201 ref.atc
2202 WHERE
2203 unit IS NOT NULL
2204 AND
2205 unit %(fragment_condition)s
2206 """
2207
2208 _SQL_units_from_consumable_substance = u"""
2209 -- via ref.consumable_substance.unit
2210 SELECT
2211 unit AS data,
2212 unit AS field_label,
2213 unit || ' (' || description || ')' AS list_label,
2214 2 AS rank
2215 FROM
2216 ref.consumable_substance
2217 WHERE
2218 unit %(fragment_condition)s
2219 %(ctxt_substance)s
2220 """
2221
2223
2225
2226 query = u"""
2227 SELECT DISTINCT ON (data)
2228 data,
2229 field_label,
2230 list_label
2231 FROM (
2232
2233 SELECT
2234 data,
2235 field_label,
2236 list_label,
2237 rank
2238 FROM (
2239 (%s) UNION ALL
2240 (%s) UNION ALL
2241 (%s) UNION ALL
2242 (%s) UNION ALL
2243 (%s) UNION ALL
2244 (%s) UNION ALL
2245 (%s)
2246 ) AS all_matching_units
2247 WHERE data IS NOT NULL
2248 ORDER BY rank
2249
2250 ) AS ranked_matching_units
2251 LIMIT 50""" % (
2252 _SQL_units_from_test_results,
2253 _SQL_units_from_test_types,
2254 _SQL_units_from_loinc_ipcc,
2255 _SQL_units_from_loinc_submitted,
2256 _SQL_units_from_loinc_example,
2257 _SQL_units_from_atc,
2258 _SQL_units_from_consumable_substance
2259 )
2260
2261 ctxt = {
2262 'ctxt_type_pk': {
2263 'where_part': u'AND pk_test_type = %(pk_type)s',
2264 'placeholder': u'pk_type'
2265 },
2266 'ctxt_test_name': {
2267 'where_part': u'AND %(test_name)s IN (name_tt, name_meta, code_tt, abbrev_meta)',
2268 'placeholder': u'test_name'
2269 },
2270 'ctxt_ctt': {
2271 'where_part': u'AND %(test_name)s IN (name, code, abbrev)',
2272 'placeholder': u'test_name'
2273 },
2274 'ctxt_loinc': {
2275 'where_part': u'AND code = %(loinc)s',
2276 'placeholder': u'loinc'
2277 },
2278 'ctxt_loinc_term': {
2279 'where_part': u'AND term ~* %(test_name)s',
2280 'placeholder': u'test_name'
2281 },
2282 'ctxt_substance': {
2283 'where_part': u'AND description ~* %(substance)s',
2284 'placeholder': u'substance'
2285 }
2286 }
2287
2288 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query, context = ctxt)
2289 mp.setThresholds(1, 2, 4)
2290
2291 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
2292 self.matcher = mp
2293 self.SetToolTipString(_('Select the desired unit for the amount or measurement.'))
2294 self.selection_only = False
2295 self.phrase_separators = u'[;|]+'
2296
2297
2298
2300
2302
2303 query = u"""
2304 select distinct abnormality_indicator,
2305 abnormality_indicator, abnormality_indicator
2306 from clin.v_test_results
2307 where
2308 abnormality_indicator %(fragment_condition)s
2309 order by abnormality_indicator
2310 limit 25"""
2311
2312 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
2313 mp.setThresholds(1, 1, 2)
2314 mp.ignored_chars = "[.'\\\[\]#$%_]+" + '"'
2315 mp.word_separators = '[ \t&:]+'
2316 gmPhraseWheel.cPhraseWheel.__init__ (
2317 self,
2318 *args,
2319 **kwargs
2320 )
2321 self.matcher = mp
2322 self.SetToolTipString(_('Select an indicator for the level of abnormality.'))
2323 self.selection_only = False
2324
2325
2326
2338
2340
2341 if parent is None:
2342 parent = wx.GetApp().GetTopWindow()
2343
2344
2345 def edit(org=None):
2346 return edit_measurement_org(parent = parent, org = org)
2347
2348 def refresh(lctrl):
2349 orgs = gmPathLab.get_test_orgs()
2350 lctrl.set_string_items ([
2351 (o['unit'], o['organization'], gmTools.coalesce(o['test_org_contact'], u''), gmTools.coalesce(o['comment'], u''), o['pk_test_org'])
2352 for o in orgs
2353 ])
2354 lctrl.set_data(orgs)
2355
2356 def delete(test_org):
2357 gmPathLab.delete_test_org(test_org = test_org['pk_test_org'])
2358 return True
2359
2360 gmListWidgets.get_choices_from_list (
2361 parent = parent,
2362 msg = _('\nThese are the diagnostic orgs (path labs etc) currently defined in GNUmed.\n\n'),
2363 caption = _('Showing diagnostic orgs.'),
2364 columns = [_('Name'), _('Organization'), _('Contact'), _('Comment'), u'#'],
2365 single_selection = True,
2366 refresh_callback = refresh,
2367 edit_callback = edit,
2368 new_callback = edit,
2369 delete_callback = delete
2370 )
2371
2372
2373 from Gnumed.wxGladeWidgets import wxgMeasurementOrgEAPnl
2374
2375 -class cMeasurementOrgEAPnl(wxgMeasurementOrgEAPnl.wxgMeasurementOrgEAPnl, gmEditArea.cGenericEditAreaMixin):
2376
2392
2393
2394
2395
2396
2397
2398
2399
2401 has_errors = False
2402 if self._PRW_org_unit.GetData() is None:
2403 if self._PRW_org_unit.GetValue().strip() == u'':
2404 has_errors = True
2405 self._PRW_org_unit.display_as_valid(valid = False)
2406 else:
2407 self._PRW_org_unit.display_as_valid(valid = True)
2408 else:
2409 self._PRW_org_unit.display_as_valid(valid = True)
2410
2411 return (not has_errors)
2412
2423
2443
2448
2453
2455 self._refresh_as_new()
2456
2459
2461
2463
2464 query = u"""
2465 SELECT DISTINCT ON (list_label)
2466 pk AS data,
2467 unit || ' (' || organization || ')' AS field_label,
2468 unit || ' @ ' || organization AS list_label
2469 FROM clin.v_test_orgs
2470 WHERE
2471 unit %(fragment_condition)s
2472 OR
2473 organization %(fragment_condition)s
2474 ORDER BY list_label
2475 LIMIT 50"""
2476 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
2477 mp.setThresholds(1, 2, 4)
2478
2479 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
2480 self.matcher = mp
2481 self.SetToolTipString(_('The name of the path lab/diagnostic organisation.'))
2482 self.selection_only = False
2483
2496
2499
2538
2539
2540
2541 if __name__ == '__main__':
2542
2543 from Gnumed.pycommon import gmLog2
2544
2545 gmI18N.activate_locale()
2546 gmI18N.install_domain()
2547 gmDateTime.init()
2548
2549
2557
2565
2566
2567
2568
2569
2570
2571
2572 if (len(sys.argv) > 1) and (sys.argv[1] == 'test'):
2573
2574 test_test_ea_pnl()
2575
2576
2577
2578