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, webbrowser, 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 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
25 from Gnumed.pycommon import gmTools
26 from Gnumed.pycommon import gmNetworkTools
27 from Gnumed.pycommon import gmI18N
28 from Gnumed.pycommon import gmShellAPI
29 from Gnumed.pycommon import gmCfg
30 from Gnumed.pycommon import gmDateTime
31 from Gnumed.pycommon import gmMatchProvider
32 from Gnumed.pycommon import gmDispatcher
33
34 from Gnumed.wxpython import gmRegetMixin
35 from Gnumed.wxpython import gmPhraseWheel
36 from Gnumed.wxpython import gmEditArea
37 from Gnumed.wxpython import gmGuiHelpers
38 from Gnumed.wxpython import gmListWidgets
39 from Gnumed.wxpython import gmAuthWidgets
40 from Gnumed.wxpython import gmFormWidgets
41 from Gnumed.wxpython import gmPatSearchWidgets
42 from Gnumed.wxpython import gmOrganizationWidgets
43
44
45 _log = logging.getLogger('gm.ui')
46 _log.info(__version__)
47
48
49
50
52
53 wx.BeginBusyCursor()
54
55 gmDispatcher.send(signal = 'statustext', msg = _('Updating LOINC data can take quite a while...'), beep = True)
56
57
58 downloaded, loinc_dir = gmNetworkTools.download_data_pack(url = 'http://www.gnumed.de/downloads/data/loinc/loinctab.zip')
59 if not downloaded:
60 wx.EndBusyCursor()
61 gmGuiHelpers.gm_show_warning (
62 aTitle = _('Downloading LOINC'),
63 aMessage = _('Error downloading the latest LOINC data.\n')
64 )
65 return False
66
67
68 data_fname, license_fname = gmLOINC.split_LOINCDBTXT(input_fname = os.path.join(loinc_dir, 'LOINCDB.TXT'))
69
70 wx.EndBusyCursor()
71
72 conn = gmAuthWidgets.get_dbowner_connection(procedure = _('importing LOINC reference data'))
73 if conn is None:
74 return False
75
76 wx.BeginBusyCursor()
77
78
79 if gmLOINC.loinc_import(data_fname = data_fname, license_fname = license_fname, conn = conn):
80 gmDispatcher.send(signal = 'statustext', msg = _('Successfully imported LOINC reference data.'))
81 else:
82 gmDispatcher.send(signal = 'statustext', msg = _('Importing LOINC reference data failed.'), beep = True)
83
84 wx.EndBusyCursor()
85 return True
86
87
88
90
91 dbcfg = gmCfg.cCfgSQL()
92
93 url = dbcfg.get (
94 option = u'external.urls.measurements_search',
95 workplace = gmSurgery.gmCurrentPractice().active_workplace,
96 bias = 'user',
97 default = u"http://www.google.de/search?as_oq=%(search_term)s&num=10&as_sitesearch=laborlexikon.de"
98 )
99
100 base_url = dbcfg.get2 (
101 option = u'external.urls.measurements_encyclopedia',
102 workplace = gmSurgery.gmCurrentPractice().active_workplace,
103 bias = 'user',
104 default = u'http://www.laborlexikon.de'
105 )
106
107 if measurement_type is None:
108 url = base_url
109
110 measurement_type = measurement_type.strip()
111
112 if measurement_type == u'':
113 url = base_url
114
115 url = url % {'search_term': measurement_type}
116
117 webbrowser.open (
118 url = url,
119 new = False,
120 autoraise = True
121 )
122
134
155
156
157
158
159
160
161
162
163
164
165
166
168 """A grid class for displaying measurment results.
169
170 - does NOT listen to the currently active patient
171 - thereby it can display any patient at any time
172 """
173
174
175
176
177
178
180
181 wx.grid.Grid.__init__(self, *args, **kwargs)
182
183 self.__patient = None
184 self.__cell_data = {}
185 self.__row_label_data = []
186
187 self.__prev_row = None
188 self.__prev_col = None
189 self.__prev_label_row = None
190 self.__date_format = str((_('lab_grid_date_format::%Y\n%b %d')).lstrip('lab_grid_date_format::'))
191
192 self.__init_ui()
193 self.__register_events()
194
195
196
198 if not self.IsSelection():
199 gmDispatcher.send(signal = u'statustext', msg = _('No results selected for deletion.'))
200 return True
201
202 selected_cells = self.get_selected_cells()
203 if len(selected_cells) > 20:
204 results = None
205 msg = _(
206 'There are %s results marked for deletion.\n'
207 '\n'
208 'Are you sure you want to delete these results ?'
209 ) % len(selected_cells)
210 else:
211 results = self.__cells_to_data(cells = selected_cells, exclude_multi_cells = False)
212 txt = u'\n'.join([ u'%s %s (%s): %s %s%s' % (
213 r['clin_when'].strftime('%x %H:%M').decode(gmI18N.get_encoding()),
214 r['unified_abbrev'],
215 r['unified_name'],
216 r['unified_val'],
217 r['val_unit'],
218 gmTools.coalesce(r['abnormality_indicator'], u'', u' (%s)')
219 ) for r in results
220 ])
221 msg = _(
222 'The following results are marked for deletion:\n'
223 '\n'
224 '%s\n'
225 '\n'
226 'Are you sure you want to delete these results ?'
227 ) % txt
228
229 dlg = gmGuiHelpers.c2ButtonQuestionDlg (
230 self,
231 -1,
232 caption = _('Deleting test results'),
233 question = msg,
234 button_defs = [
235 {'label': _('Delete'), 'tooltip': _('Yes, delete all the results.'), 'default': False},
236 {'label': _('Cancel'), 'tooltip': _('No, do NOT delete any results.'), 'default': True}
237 ]
238 )
239 decision = dlg.ShowModal()
240
241 if decision == wx.ID_YES:
242 if results is None:
243 results = self.__cells_to_data(cells = selected_cells, exclude_multi_cells = False)
244 for result in results:
245 gmPathLab.delete_test_result(result)
246
248 if not self.IsSelection():
249 gmDispatcher.send(signal = u'statustext', msg = _('Cannot sign results. No results selected.'))
250 return True
251
252 selected_cells = self.get_selected_cells()
253 if len(selected_cells) > 10:
254 test_count = len(selected_cells)
255 tests = None
256 else:
257 test_count = None
258 tests = self.__cells_to_data(cells = selected_cells, exclude_multi_cells = False)
259 if len(tests) == 0:
260 return True
261
262 dlg = cMeasurementsReviewDlg (
263 self,
264 -1,
265 tests = tests,
266 test_count = test_count
267 )
268 decision = dlg.ShowModal()
269
270 if decision == wx.ID_APPLY:
271 wx.BeginBusyCursor()
272
273 if dlg._RBTN_confirm_abnormal.GetValue():
274 abnormal = None
275 elif dlg._RBTN_results_normal.GetValue():
276 abnormal = False
277 else:
278 abnormal = True
279
280 if dlg._RBTN_confirm_relevance.GetValue():
281 relevant = None
282 elif dlg._RBTN_results_not_relevant.GetValue():
283 relevant = False
284 else:
285 relevant = True
286
287 if tests is None:
288 tests = self.__cells_to_data(cells = selected_cells, exclude_multi_cells = False)
289
290 comment = None
291 if len(tests) == 1:
292 comment = dlg._TCTRL_comment.GetValue()
293
294 for test in tests:
295 test.set_review (
296 technically_abnormal = abnormal,
297 clinically_relevant = relevant,
298 comment = comment,
299 make_me_responsible = dlg._CHBOX_responsible.IsChecked()
300 )
301
302 wx.EndBusyCursor()
303
304 dlg.Destroy()
305
307
308 if not self.IsSelection():
309 gmDispatcher.send(signal = u'statustext', msg = _('Cannot plot results. No results selected.'))
310 return True
311
312 tests = self.__cells_to_data (
313 cells = self.get_selected_cells(),
314 exclude_multi_cells = False,
315 auto_include_multi_cells = True
316 )
317
318 plot_measurements(parent = self, tests = tests)
319
321
322 sel_block_top_left = self.GetSelectionBlockTopLeft()
323 sel_block_bottom_right = self.GetSelectionBlockBottomRight()
324 sel_cols = self.GetSelectedCols()
325 sel_rows = self.GetSelectedRows()
326
327 selected_cells = []
328
329
330 selected_cells += self.GetSelectedCells()
331
332
333 selected_cells += list (
334 (row, col)
335 for row in sel_rows
336 for col in xrange(self.GetNumberCols())
337 )
338
339
340 selected_cells += list (
341 (row, col)
342 for row in xrange(self.GetNumberRows())
343 for col in sel_cols
344 )
345
346
347 for top_left, bottom_right in zip(self.GetSelectionBlockTopLeft(), self.GetSelectionBlockBottomRight()):
348 selected_cells += [
349 (row, col)
350 for row in xrange(top_left[0], bottom_right[0] + 1)
351 for col in xrange(top_left[1], bottom_right[1] + 1)
352 ]
353
354 return set(selected_cells)
355
356 - def select_cells(self, unsigned_only=False, accountables_only=False, keep_preselections=False):
357 """Select a range of cells according to criteria.
358
359 unsigned_only: include only those which are not signed at all yet
360 accountable_only: include only those for which the current user is responsible
361 keep_preselections: broaden (rather than replace) the range of selected cells
362
363 Combinations are powerful !
364 """
365 wx.BeginBusyCursor()
366 self.BeginBatch()
367
368 if not keep_preselections:
369 self.ClearSelection()
370
371 for col_idx in self.__cell_data.keys():
372 for row_idx in self.__cell_data[col_idx].keys():
373
374
375 do_not_include = False
376 for result in self.__cell_data[col_idx][row_idx]:
377 if unsigned_only:
378 if result['reviewed']:
379 do_not_include = True
380 break
381 if accountables_only:
382 if not result['you_are_responsible']:
383 do_not_include = True
384 break
385 if do_not_include:
386 continue
387
388 self.SelectBlock(row_idx, col_idx, row_idx, col_idx, addToSelected = True)
389
390 self.EndBatch()
391 wx.EndBusyCursor()
392
394
395 self.empty_grid()
396 if self.__patient is None:
397 return
398
399 emr = self.__patient.get_emr()
400
401 self.__row_label_data = emr.get_test_types_for_results()
402 test_type_labels = [ u'%s (%s)' % (test['unified_abbrev'], test['unified_name']) for test in self.__row_label_data ]
403 if len(test_type_labels) == 0:
404 return
405
406 test_date_labels = [ date[0].strftime(self.__date_format) for date in emr.get_dates_for_results() ]
407 results = emr.get_test_results_by_date()
408
409 self.BeginBatch()
410
411
412 self.AppendRows(numRows = len(test_type_labels))
413 for row_idx in range(len(test_type_labels)):
414 self.SetRowLabelValue(row_idx, test_type_labels[row_idx])
415
416
417 self.AppendCols(numCols = len(test_date_labels))
418 for date_idx in range(len(test_date_labels)):
419 self.SetColLabelValue(date_idx, test_date_labels[date_idx])
420
421
422 for result in results:
423 row = test_type_labels.index(u'%s (%s)' % (result['unified_abbrev'], result['unified_name']))
424 col = test_date_labels.index(result['clin_when'].strftime(self.__date_format))
425
426 try:
427 self.__cell_data[col]
428 except KeyError:
429 self.__cell_data[col] = {}
430
431
432 if self.__cell_data[col].has_key(row):
433 self.__cell_data[col][row].append(result)
434 self.__cell_data[col][row].sort(key = lambda x: x['clin_when'], reverse = True)
435 else:
436 self.__cell_data[col][row] = [result]
437
438
439 vals2display = []
440 for sub_result in self.__cell_data[col][row]:
441
442
443 ind = gmTools.coalesce(sub_result['abnormality_indicator'], u'').strip()
444 if ind != u'':
445 lab_abnormality_indicator = u' (%s)' % ind[:3]
446 else:
447 lab_abnormality_indicator = u''
448
449 if sub_result['is_technically_abnormal'] is None:
450 abnormality_indicator = lab_abnormality_indicator
451
452 elif sub_result['is_technically_abnormal'] is False:
453 abnormality_indicator = u''
454
455 else:
456
457 if lab_abnormality_indicator == u'':
458
459 abnormality_indicator = u' (%s)' % gmTools.u_plus_minus
460
461 else:
462 abnormality_indicator = lab_abnormality_indicator
463
464
465
466 sub_result_relevant = sub_result['is_clinically_relevant']
467 if sub_result_relevant is None:
468
469 sub_result_relevant = False
470
471 missing_review = False
472
473
474 if not sub_result['reviewed']:
475 missing_review = True
476
477 else:
478
479 if sub_result['you_are_responsible'] and not sub_result['review_by_you']:
480 missing_review = True
481
482
483 if len(sub_result['unified_val']) > 8:
484 tmp = u'%.7s%s' % (sub_result['unified_val'][:7], gmTools.u_ellipsis)
485 else:
486 tmp = u'%.8s' % sub_result['unified_val'][:8]
487
488
489 tmp = u'%s%.6s' % (tmp, abnormality_indicator)
490
491
492 has_sub_result_comment = gmTools.coalesce (
493 gmTools.coalesce(sub_result['note_test_org'], sub_result['comment']),
494 u''
495 ).strip() != u''
496 if has_sub_result_comment:
497 tmp = u'%s %s' % (tmp, gmTools.u_ellipsis)
498
499
500 if missing_review:
501 tmp = u'%s %s' % (tmp, gmTools.u_writing_hand)
502
503
504 if len(self.__cell_data[col][row]) > 1:
505 tmp = u'%s %s' % (sub_result['clin_when'].strftime('%H:%M'), tmp)
506
507 vals2display.append(tmp)
508
509 self.SetCellValue(row, col, u'\n'.join(vals2display))
510 self.SetCellAlignment(row, col, horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE)
511
512
513
514
515 if sub_result_relevant:
516 font = self.GetCellFont(row, col)
517 self.SetCellTextColour(row, col, 'firebrick')
518 font.SetWeight(wx.FONTWEIGHT_BOLD)
519 self.SetCellFont(row, col, font)
520
521
522 self.AutoSize()
523 self.EndBatch()
524 return
525
527 self.BeginBatch()
528 self.ClearGrid()
529
530
531 if self.GetNumberRows() > 0:
532 self.DeleteRows(pos = 0, numRows = self.GetNumberRows())
533 if self.GetNumberCols() > 0:
534 self.DeleteCols(pos = 0, numCols = self.GetNumberCols())
535 self.EndBatch()
536 self.__cell_data = {}
537 self.__row_label_data = []
538
577
825
826
827
829 self.CreateGrid(0, 1)
830 self.EnableEditing(0)
831 self.EnableDragGridSize(1)
832
833
834
835
836
837 self.SetRowLabelSize(150)
838 self.SetRowLabelAlignment(horiz = wx.ALIGN_LEFT, vert = wx.ALIGN_CENTRE)
839
840
841 dbcfg = gmCfg.cCfgSQL()
842 url = dbcfg.get2 (
843 option = u'external.urls.measurements_encyclopedia',
844 workplace = gmSurgery.gmCurrentPractice().active_workplace,
845 bias = 'user',
846 default = u'http://www.laborlexikon.de'
847 )
848
849 self.__WIN_corner = self.GetGridCornerLabelWindow()
850
851 LNK_lab = wx.lib.hyperlink.HyperLinkCtrl (
852 self.__WIN_corner,
853 -1,
854 label = _('Reference'),
855 style = wx.HL_DEFAULT_STYLE
856 )
857 LNK_lab.SetURL(url)
858 LNK_lab.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BACKGROUND))
859 LNK_lab.SetToolTipString(_(
860 'Navigate to an encyclopedia of measurements\n'
861 'and test methods on the web.\n'
862 '\n'
863 ' <%s>'
864 ) % url)
865
866 SZR_inner = wx.BoxSizer(wx.HORIZONTAL)
867 SZR_inner.Add((20, 20), 1, wx.EXPAND, 0)
868 SZR_inner.Add(LNK_lab, 0, wx.ALIGN_CENTER_VERTICAL, 0)
869 SZR_inner.Add((20, 20), 1, wx.EXPAND, 0)
870
871 SZR_corner = wx.BoxSizer(wx.VERTICAL)
872 SZR_corner.Add((20, 20), 1, wx.EXPAND, 0)
873 SZR_corner.AddWindow(SZR_inner, 0, wx.EXPAND)
874 SZR_corner.Add((20, 20), 1, wx.EXPAND, 0)
875
876 self.__WIN_corner.SetSizer(SZR_corner)
877 SZR_corner.Fit(self.__WIN_corner)
878
880 self.__WIN_corner.Layout()
881
882 - def __cells_to_data(self, cells=None, exclude_multi_cells=False, auto_include_multi_cells=False):
883 """List of <cells> must be in row / col order."""
884 data = []
885 for row, col in cells:
886 try:
887
888 data_list = self.__cell_data[col][row]
889 except KeyError:
890 continue
891
892 if len(data_list) == 1:
893 data.append(data_list[0])
894 continue
895
896 if exclude_multi_cells:
897 gmDispatcher.send(signal = u'statustext', msg = _('Excluding multi-result field from further processing.'))
898 continue
899
900 if auto_include_multi_cells:
901 data.extend(data_list)
902 continue
903
904 data_to_include = self.__get_choices_from_multi_cell(cell_data = data_list)
905 if data_to_include is None:
906 continue
907 data.extend(data_to_include)
908
909 return data
910
912 data = gmListWidgets.get_choices_from_list (
913 parent = self,
914 msg = _(
915 'Your selection includes a field with multiple results.\n'
916 '\n'
917 'Please select the individual results you want to work on:'
918 ),
919 caption = _('Selecting test results'),
920 choices = [ [d['clin_when'], d['unified_abbrev'], d['unified_name'], d['unified_val']] for d in cell_data ],
921 columns = [_('Date / Time'), _('Code'), _('Test'), _('Result')],
922 data = cell_data,
923 single_selection = single_selection
924 )
925 return data
926
927
928
930
931 self.GetGridWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_cells)
932 self.GetGridRowLabelWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_row_labels)
933
934
935
936 self.Bind(wx.EVT_SIZE, self.__resize_corner_window)
937
938
939 self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.__on_cell_left_dclicked)
940
942 col = evt.GetCol()
943 row = evt.GetRow()
944
945
946 try:
947 self.__cell_data[col][row]
948 except KeyError:
949
950
951 return
952
953 if len(self.__cell_data[col][row]) > 1:
954 data = self.__get_choices_from_multi_cell(cell_data = self.__cell_data[col][row], single_selection = True)
955 else:
956 data = self.__cell_data[col][row][0]
957
958 if data is None:
959 return
960
961 edit_measurement(parent = self, measurement = data, single_entry = True)
962
963
964
965
966
967
968
970
971
972
973 x, y = self.CalcUnscrolledPosition(evt.GetX(), evt.GetY())
974
975 row = self.YToRow(y)
976
977 if self.__prev_label_row == row:
978 return
979
980 self.__prev_label_row == row
981
982 evt.GetEventObject().SetToolTipString(self.get_row_tooltip(row = row))
983
984
985
986
987
988
989
990
992 """Calculate where the mouse is and set the tooltip dynamically."""
993
994
995
996 x, y = self.CalcUnscrolledPosition(evt.GetX(), evt.GetY())
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010 row, col = self.XYToCell(x, y)
1011
1012 if (row == self.__prev_row) and (col == self.__prev_col):
1013 return
1014
1015 self.__prev_row = row
1016 self.__prev_col = col
1017
1018 evt.GetEventObject().SetToolTipString(self.get_cell_tooltip(col=col, row=row))
1019
1020
1021
1025
1026 patient = property(lambda x:x, _set_patient)
1027
1028 from Gnumed.wxGladeWidgets import wxgMeasurementsPnl
1029
1030 -class cMeasurementsPnl(wxgMeasurementsPnl.wxgMeasurementsPnl, gmRegetMixin.cRegetOnPaintMixin):
1031 """Panel holding a grid with lab data. Used as notebook page."""
1032
1039
1040
1041
1043 gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._on_pre_patient_selection)
1044 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._on_post_patient_selection)
1045 gmDispatcher.connect(signal = u'test_result_mod_db', receiver = self._schedule_data_reget)
1046 gmDispatcher.connect(signal = u'reviewed_test_results_mod_db', receiver = self._schedule_data_reget)
1047
1049 wx.CallAfter(self.__on_post_patient_selection)
1050
1052 self._schedule_data_reget()
1053
1055 wx.CallAfter(self.__on_pre_patient_selection)
1056
1059
1062
1065
1071
1074
1077
1080
1081
1082
1084 self.__action_button_popup = wx.Menu(title = _('Perform on selected results:'))
1085
1086 menu_id = wx.NewId()
1087 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('Review and &sign')))
1088 wx.EVT_MENU(self.__action_button_popup, menu_id, self.__on_sign_current_selection)
1089
1090 menu_id = wx.NewId()
1091 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('Plot')))
1092 wx.EVT_MENU(self.__action_button_popup, menu_id, self.__on_plot_current_selection)
1093
1094 menu_id = wx.NewId()
1095 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('Export to &file')))
1096
1097 self.__action_button_popup.Enable(id = menu_id, enable = False)
1098
1099 menu_id = wx.NewId()
1100 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('Export to &clipboard')))
1101
1102 self.__action_button_popup.Enable(id = menu_id, enable = False)
1103
1104 menu_id = wx.NewId()
1105 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('&Delete')))
1106 wx.EVT_MENU(self.__action_button_popup, menu_id, self.__on_delete_current_selection)
1107
1108
1109
1110
1111
1112
1113
1122
1123
1124
1125 from Gnumed.wxGladeWidgets import wxgMeasurementsReviewDlg
1126
1128
1130
1131 try:
1132 tests = kwargs['tests']
1133 del kwargs['tests']
1134 test_count = len(tests)
1135 try: del kwargs['test_count']
1136 except KeyError: pass
1137 except KeyError:
1138 tests = None
1139 test_count = kwargs['test_count']
1140 del kwargs['test_count']
1141
1142 wxgMeasurementsReviewDlg.wxgMeasurementsReviewDlg.__init__(self, *args, **kwargs)
1143
1144 if tests is None:
1145 msg = _('%s results selected. Too many to list individually.') % test_count
1146 else:
1147 msg = ' // '.join (
1148 [ u'%s: %s %s (%s)' % (
1149 t['unified_abbrev'],
1150 t['unified_val'],
1151 t['val_unit'],
1152 t['clin_when'].strftime('%x').decode(gmI18N.get_encoding())
1153 ) for t in tests
1154 ]
1155 )
1156
1157 self._LBL_tests.SetLabel(msg)
1158
1159 if test_count == 1:
1160 self._TCTRL_comment.Enable(True)
1161 self._TCTRL_comment.SetValue(gmTools.coalesce(tests[0]['review_comment'], u''))
1162 if tests[0]['you_are_responsible']:
1163 self._CHBOX_responsible.Enable(False)
1164
1165 self.Fit()
1166
1167
1168
1174
1175 from Gnumed.wxGladeWidgets import wxgMeasurementEditAreaPnl
1176
1177 -class cMeasurementEditAreaPnl(wxgMeasurementEditAreaPnl.wxgMeasurementEditAreaPnl, gmEditArea.cGenericEditAreaMixin):
1178 """This edit area saves *new* measurements into the active patient only."""
1179
1196
1197
1198
1200 self._PRW_test.SetText(u'', None, True)
1201 self.__refresh_loinc_info()
1202 self.__update_units_context()
1203 self._TCTRL_result.SetValue(u'')
1204 self._PRW_units.SetText(u'', None, True)
1205 self._PRW_abnormality_indicator.SetText(u'', None, True)
1206 if self.__default_date is None:
1207 self._DPRW_evaluated.SetData(data = pyDT.datetime.now(tz = gmDateTime.gmCurrentLocalTimezone))
1208 else:
1209 self._DPRW_evaluated.SetData(data = None)
1210 self._TCTRL_note_test_org.SetValue(u'')
1211 self._PRW_intended_reviewer.SetData(gmPerson.gmCurrentProvider()['pk_staff'])
1212 self._PRW_problem.SetData()
1213 self._TCTRL_narrative.SetValue(u'')
1214 self._CHBOX_review.SetValue(False)
1215 self._CHBOX_abnormal.SetValue(False)
1216 self._CHBOX_relevant.SetValue(False)
1217 self._CHBOX_abnormal.Enable(False)
1218 self._CHBOX_relevant.Enable(False)
1219 self._TCTRL_review_comment.SetValue(u'')
1220 self._TCTRL_normal_min.SetValue(u'')
1221 self._TCTRL_normal_max.SetValue(u'')
1222 self._TCTRL_normal_range.SetValue(u'')
1223 self._TCTRL_target_min.SetValue(u'')
1224 self._TCTRL_target_max.SetValue(u'')
1225 self._TCTRL_target_range.SetValue(u'')
1226 self._TCTRL_norm_ref_group.SetValue(u'')
1227
1228 self._PRW_test.SetFocus()
1229
1231 self._PRW_test.SetData(data = self.data['pk_test_type'])
1232 self.__refresh_loinc_info()
1233 self.__update_units_context()
1234 self._TCTRL_result.SetValue(self.data['unified_val'])
1235 self._PRW_units.SetText(self.data['val_unit'], self.data['val_unit'], True)
1236 self._PRW_abnormality_indicator.SetText (
1237 gmTools.coalesce(self.data['abnormality_indicator'], u''),
1238 gmTools.coalesce(self.data['abnormality_indicator'], u''),
1239 True
1240 )
1241 self._DPRW_evaluated.SetData(data = self.data['clin_when'])
1242 self._TCTRL_note_test_org.SetValue(gmTools.coalesce(self.data['note_test_org'], u''))
1243 self._PRW_intended_reviewer.SetData(self.data['pk_intended_reviewer'])
1244 self._PRW_problem.SetData(self.data['pk_episode'])
1245 self._TCTRL_narrative.SetValue(gmTools.coalesce(self.data['comment'], u''))
1246 self._CHBOX_review.SetValue(False)
1247 self._CHBOX_abnormal.SetValue(gmTools.coalesce(self.data['is_technically_abnormal'], False))
1248 self._CHBOX_relevant.SetValue(gmTools.coalesce(self.data['is_clinically_relevant'], False))
1249 self._CHBOX_abnormal.Enable(False)
1250 self._CHBOX_relevant.Enable(False)
1251 self._TCTRL_review_comment.SetValue(gmTools.coalesce(self.data['review_comment'], u''))
1252 self._TCTRL_normal_min.SetValue(unicode(gmTools.coalesce(self.data['val_normal_min'], u'')))
1253 self._TCTRL_normal_max.SetValue(unicode(gmTools.coalesce(self.data['val_normal_max'], u'')))
1254 self._TCTRL_normal_range.SetValue(gmTools.coalesce(self.data['val_normal_range'], u''))
1255 self._TCTRL_target_min.SetValue(unicode(gmTools.coalesce(self.data['val_target_min'], u'')))
1256 self._TCTRL_target_max.SetValue(unicode(gmTools.coalesce(self.data['val_target_max'], u'')))
1257 self._TCTRL_target_range.SetValue(gmTools.coalesce(self.data['val_target_range'], u''))
1258 self._TCTRL_norm_ref_group.SetValue(gmTools.coalesce(self.data['norm_ref_group'], u''))
1259
1260 self._TCTRL_result.SetFocus()
1261
1263 self._refresh_from_existing()
1264
1265 self._PRW_test.SetText(u'', None, True)
1266 self.__refresh_loinc_info()
1267 self.__update_units_context()
1268 self._TCTRL_result.SetValue(u'')
1269 self._PRW_units.SetText(u'', None, True)
1270 self._PRW_abnormality_indicator.SetText(u'', None, True)
1271
1272 self._TCTRL_note_test_org.SetValue(u'')
1273 self._TCTRL_narrative.SetValue(u'')
1274 self._CHBOX_review.SetValue(False)
1275 self._CHBOX_abnormal.SetValue(False)
1276 self._CHBOX_relevant.SetValue(False)
1277 self._CHBOX_abnormal.Enable(False)
1278 self._CHBOX_relevant.Enable(False)
1279 self._TCTRL_review_comment.SetValue(u'')
1280 self._TCTRL_normal_min.SetValue(u'')
1281 self._TCTRL_normal_max.SetValue(u'')
1282 self._TCTRL_normal_range.SetValue(u'')
1283 self._TCTRL_target_min.SetValue(u'')
1284 self._TCTRL_target_max.SetValue(u'')
1285 self._TCTRL_target_range.SetValue(u'')
1286 self._TCTRL_norm_ref_group.SetValue(u'')
1287
1288 self._PRW_test.SetFocus()
1289
1291
1292 validity = True
1293
1294 if not self._DPRW_evaluated.is_valid_timestamp():
1295 self._DPRW_evaluated.display_as_valid(False)
1296 validity = False
1297 else:
1298 self._DPRW_evaluated.display_as_valid(True)
1299
1300 if self._TCTRL_result.GetValue().strip() == u'':
1301 validity = False
1302 self.display_ctrl_as_valid(self._TCTRL_result, False)
1303 else:
1304 self.display_ctrl_as_valid(self._TCTRL_result, True)
1305
1306 if self._PRW_problem.GetValue().strip() == u'':
1307 self._PRW_problem.display_as_valid(False)
1308 validity = False
1309 else:
1310 self._PRW_problem.display_as_valid(True)
1311
1312 if self._PRW_test.GetValue().strip() == u'':
1313 self._PRW_test.display_as_valid(False)
1314 validity = False
1315 else:
1316 self._PRW_test.display_as_valid(True)
1317
1318 if self._PRW_intended_reviewer.GetData() is None:
1319 self._PRW_intended_reviewer.display_as_valid(False)
1320 validity = False
1321 else:
1322 self._PRW_intended_reviewer.display_as_valid(True)
1323
1324 if self._PRW_units.GetValue().strip() == u'':
1325 self._PRW_units.display_as_valid(False)
1326 validity = False
1327 else:
1328 self._PRW_units.display_as_valid(True)
1329
1330 ctrls = [self._TCTRL_normal_min, self._TCTRL_normal_max, self._TCTRL_target_min, self._TCTRL_target_max]
1331 for widget in ctrls:
1332 val = widget.GetValue().strip()
1333 if val == u'':
1334 continue
1335 try:
1336 decimal.Decimal(val.replace(',', u'.', 1))
1337 self.display_ctrl_as_valid(widget, True)
1338 except:
1339 validity = False
1340 self.display_ctrl_as_valid(widget, False)
1341
1342 if validity is False:
1343 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save result. Invalid or missing essential input.'))
1344
1345 return validity
1346
1348
1349 emr = gmPerson.gmCurrentPatient().get_emr()
1350
1351 success, result = gmTools.input2decimal(self._TCTRL_result.GetValue())
1352 if success:
1353 v_num = result
1354 v_al = None
1355 else:
1356 v_al = self._TCTRL_result.GetValue().strip()
1357 v_num = None
1358
1359 pk_type = self._PRW_test.GetData()
1360 if pk_type is None:
1361 tt = gmPathLab.create_measurement_type (
1362 lab = None,
1363 abbrev = self._PRW_test.GetValue().strip(),
1364 name = self._PRW_test.GetValue().strip(),
1365 unit = gmTools.coalesce(self._PRW_units.GetData(), self._PRW_units.GetValue()).strip()
1366 )
1367 pk_type = tt['pk_test_type']
1368
1369 tr = emr.add_test_result (
1370 episode = self._PRW_problem.GetData(can_create=True, is_open=False),
1371 type = pk_type,
1372 intended_reviewer = self._PRW_intended_reviewer.GetData(),
1373 val_num = v_num,
1374 val_alpha = v_al,
1375 unit = self._PRW_units.GetValue()
1376 )
1377
1378 tr['clin_when'] = self._DPRW_evaluated.GetData().get_pydt()
1379
1380 ctrls = [
1381 ('abnormality_indicator', self._PRW_abnormality_indicator),
1382 ('note_test_org', self._TCTRL_note_test_org),
1383 ('comment', self._TCTRL_narrative),
1384 ('val_normal_range', self._TCTRL_normal_range),
1385 ('val_target_range', self._TCTRL_target_range),
1386 ('norm_ref_group', self._TCTRL_norm_ref_group)
1387 ]
1388 for field, widget in ctrls:
1389 tr[field] = widget.GetValue().strip()
1390
1391 ctrls = [
1392 ('val_normal_min', self._TCTRL_normal_min),
1393 ('val_normal_max', self._TCTRL_normal_max),
1394 ('val_target_min', self._TCTRL_target_min),
1395 ('val_target_max', self._TCTRL_target_max)
1396 ]
1397 for field, widget in ctrls:
1398 val = widget.GetValue().strip()
1399 if val == u'':
1400 tr[field] = None
1401 else:
1402 tr[field] = decimal.Decimal(val.replace(',', u'.', 1))
1403
1404 tr.save_payload()
1405
1406 if self._CHBOX_review.GetValue() is True:
1407 tr.set_review (
1408 technically_abnormal = self._CHBOX_abnormal.GetValue(),
1409 clinically_relevant = self._CHBOX_relevant.GetValue(),
1410 comment = gmTools.none_if(self._TCTRL_review_comment.GetValue().strip(), u''),
1411 make_me_responsible = False
1412 )
1413
1414 self.data = tr
1415
1416 return True
1417
1419
1420 success, result = gmTools.input2decimal(self._TCTRL_result.GetValue())
1421 if success:
1422 v_num = result
1423 v_al = None
1424 else:
1425 v_num = None
1426 v_al = self._TCTRL_result.GetValue().strip()
1427
1428 pk_type = self._PRW_test.GetData()
1429 if pk_type is None:
1430 tt = gmPathLab.create_measurement_type (
1431 lab = None,
1432 abbrev = self._PRW_test.GetValue().strip(),
1433 name = self._PRW_test.GetValue().strip(),
1434 unit = gmTools.none_if(self._PRW_units.GetValue().strip(), u'')
1435 )
1436 pk_type = tt['pk_test_type']
1437
1438 tr = self.data
1439
1440 tr['pk_episode'] = self._PRW_problem.GetData(can_create=True, is_open=False)
1441 tr['pk_test_type'] = pk_type
1442 tr['pk_intended_reviewer'] = self._PRW_intended_reviewer.GetData()
1443 tr['val_num'] = v_num
1444 tr['val_alpha'] = v_al
1445 tr['val_unit'] = gmTools.coalesce(self._PRW_units.GetData(), self._PRW_units.GetValue()).strip()
1446 tr['clin_when'] = self._DPRW_evaluated.GetData().get_pydt()
1447
1448 ctrls = [
1449 ('abnormality_indicator', self._PRW_abnormality_indicator),
1450 ('note_test_org', self._TCTRL_note_test_org),
1451 ('comment', self._TCTRL_narrative),
1452 ('val_normal_range', self._TCTRL_normal_range),
1453 ('val_target_range', self._TCTRL_target_range),
1454 ('norm_ref_group', self._TCTRL_norm_ref_group)
1455 ]
1456 for field, widget in ctrls:
1457 tr[field] = widget.GetValue().strip()
1458
1459 ctrls = [
1460 ('val_normal_min', self._TCTRL_normal_min),
1461 ('val_normal_max', self._TCTRL_normal_max),
1462 ('val_target_min', self._TCTRL_target_min),
1463 ('val_target_max', self._TCTRL_target_max)
1464 ]
1465 for field, widget in ctrls:
1466 val = widget.GetValue().strip()
1467 if val == u'':
1468 tr[field] = None
1469 else:
1470 tr[field] = decimal.Decimal(val.replace(',', u'.', 1))
1471
1472 tr.save_payload()
1473
1474 if self._CHBOX_review.GetValue() is True:
1475 tr.set_review (
1476 technically_abnormal = self._CHBOX_abnormal.GetValue(),
1477 clinically_relevant = self._CHBOX_relevant.GetValue(),
1478 comment = gmTools.none_if(self._TCTRL_review_comment.GetValue().strip(), u''),
1479 make_me_responsible = False
1480 )
1481
1482 return True
1483
1484
1485
1489
1491 self.__refresh_loinc_info()
1492 self.__update_units_context()
1493
1495
1496 if not self._CHBOX_review.GetValue():
1497 self._CHBOX_abnormal.SetValue(self._PRW_abnormality_indicator.GetValue().strip() != u'')
1498
1500 self._CHBOX_abnormal.Enable(self._CHBOX_review.GetValue())
1501 self._CHBOX_relevant.Enable(self._CHBOX_review.GetValue())
1502 self._TCTRL_review_comment.Enable(self._CHBOX_review.GetValue())
1503
1520
1521
1522
1524
1525 self._PRW_units.unset_context(context = u'loinc')
1526
1527 tt = self._PRW_test.GetData(as_instance = True)
1528
1529 if tt is None:
1530 self._PRW_units.unset_context(context = u'pk_type')
1531 if self._PRW_test.GetValue().strip() == u'':
1532 self._PRW_units.unset_context(context = u'test_name')
1533 else:
1534 self._PRW_units.set_context(context = u'test_name', val = self._PRW_test.GetValue().strip())
1535 return
1536
1537 self._PRW_units.set_context(context = u'pk_type', val = tt['pk_test_type'])
1538 self._PRW_units.set_context(context = u'test_name', val = tt['name'])
1539
1540 if tt['loinc'] is None:
1541 return
1542
1543 self._PRW_units.set_context(context = u'loinc', val = tt['loinc'])
1544
1546
1547 self._TCTRL_loinc.SetValue(u'')
1548
1549 if self._PRW_test.GetData() is None:
1550 return
1551
1552 tt = self._PRW_test.GetData(as_instance = True)
1553
1554 if tt['loinc'] is None:
1555 return
1556
1557 info = gmLOINC.loinc2term(loinc = tt['loinc'])
1558 if len(info) == 0:
1559 self._TCTRL_loinc.SetValue(u'')
1560 return
1561
1562 self._TCTRL_loinc.SetValue(u'%s: %s' % (tt['loinc'], info[0]))
1563
1564
1565
1567
1568 if parent is None:
1569 parent = wx.GetApp().GetTopWindow()
1570
1571
1572 def edit(test_type=None):
1573 ea = cMeasurementTypeEAPnl(parent = parent, id = -1, type = test_type)
1574 dlg = gmEditArea.cGenericEditAreaDlg2 (
1575 parent = parent,
1576 id = -1,
1577 edit_area = ea,
1578 single_entry = gmTools.bool2subst((test_type is None), False, True)
1579 )
1580 dlg.SetTitle(gmTools.coalesce(test_type, _('Adding measurement type'), _('Editing measurement type')))
1581
1582 if dlg.ShowModal() == wx.ID_OK:
1583 dlg.Destroy()
1584 return True
1585
1586 dlg.Destroy()
1587 return False
1588
1589 def refresh(lctrl):
1590 mtypes = gmPathLab.get_measurement_types(order_by = 'name, abbrev')
1591 items = [ [
1592 m['abbrev'],
1593 m['name'],
1594 gmTools.coalesce(m['loinc'], u''),
1595 gmTools.coalesce(m['conversion_unit'], u''),
1596 gmTools.coalesce(m['comment_type'], u''),
1597 gmTools.coalesce(m['name_org'], u'?'),
1598 gmTools.coalesce(m['comment_org'], u''),
1599 m['pk_test_type']
1600 ] for m in mtypes ]
1601 lctrl.set_string_items(items)
1602 lctrl.set_data(mtypes)
1603
1604 def delete(measurement_type):
1605 if measurement_type.in_use:
1606 gmDispatcher.send (
1607 signal = 'statustext',
1608 beep = True,
1609 msg = _('Cannot delete measurement type [%s (%s)] because it is in use.') % (measurement_type['name'], measurement_type['abbrev'])
1610 )
1611 return False
1612 gmPathLab.delete_measurement_type(measurement_type = measurement_type['pk_test_type'])
1613 return True
1614
1615 msg = _(
1616 '\n'
1617 'These are the measurement types currently defined in GNUmed.\n'
1618 '\n'
1619 )
1620
1621 gmListWidgets.get_choices_from_list (
1622 parent = parent,
1623 msg = msg,
1624 caption = _('Showing measurement types.'),
1625 columns = [_('Abbrev'), _('Name'), _('LOINC'), _('Base unit'), _('Comment'), _('Org'), _('Comment'), u'#'],
1626 single_selection = True,
1627 refresh_callback = refresh,
1628 edit_callback = edit,
1629 new_callback = edit,
1630 delete_callback = delete
1631 )
1632
1634
1636
1637 query = u"""
1638 SELECT DISTINCT ON (field_label)
1639 pk_test_type AS data,
1640 name_tt
1641 || ' ('
1642 || coalesce (
1643 (SELECT unit || ' @ ' || organization FROM clin.v_test_orgs c_vto WHERE c_vto.pk_test_org = vcutt.pk_test_org),
1644 '%(in_house)s'
1645 )
1646 || ')'
1647 AS field_label,
1648 name_tt
1649 || ' ('
1650 || code_tt || ', '
1651 || abbrev_tt || ', '
1652 || coalesce(abbrev_meta || ': ' || name_meta || ', ', '')
1653 || coalesce (
1654 (SELECT unit || ' @ ' || organization FROM clin.v_test_orgs c_vto WHERE c_vto.pk_test_org = vcutt.pk_test_org),
1655 '%(in_house)s'
1656 )
1657 || ')'
1658 AS list_label
1659 FROM
1660 clin.v_unified_test_types vcutt
1661 WHERE
1662 abbrev_meta %%(fragment_condition)s
1663 OR
1664 name_meta %%(fragment_condition)s
1665 OR
1666 abbrev_tt %%(fragment_condition)s
1667 OR
1668 name_tt %%(fragment_condition)s
1669 OR
1670 code_tt %%(fragment_condition)s
1671 ORDER BY field_label
1672 LIMIT 50""" % {'in_house': _('generic / in house lab')}
1673
1674 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
1675 mp.setThresholds(1, 2, 4)
1676 mp.word_separators = '[ \t:@]+'
1677 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
1678 self.matcher = mp
1679 self.SetToolTipString(_('Select the type of measurement.'))
1680 self.selection_only = False
1681
1687
1688 from Gnumed.wxGladeWidgets import wxgMeasurementTypeEAPnl
1689
1690 -class cMeasurementTypeEAPnl(wxgMeasurementTypeEAPnl.wxgMeasurementTypeEAPnl, gmEditArea.cGenericEditAreaMixin):
1691
1708
1709
1711
1712
1713 query = u"""
1714 select distinct on (name)
1715 pk,
1716 name
1717 from clin.test_type
1718 where
1719 name %(fragment_condition)s
1720 order by name
1721 limit 50"""
1722 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
1723 mp.setThresholds(1, 2, 4)
1724 self._PRW_name.matcher = mp
1725 self._PRW_name.selection_only = False
1726 self._PRW_name.add_callback_on_lose_focus(callback = self._on_name_lost_focus)
1727
1728
1729 query = u"""
1730 select distinct on (abbrev)
1731 pk,
1732 abbrev
1733 from clin.test_type
1734 where
1735 abbrev %(fragment_condition)s
1736 order by abbrev
1737 limit 50"""
1738 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
1739 mp.setThresholds(1, 2, 3)
1740 self._PRW_abbrev.matcher = mp
1741 self._PRW_abbrev.selection_only = False
1742
1743
1744 self._PRW_conversion_unit.selection_only = False
1745
1746
1747 query = u"""
1748 SELECT DISTINCT ON (list_label)
1749 data,
1750 field_label,
1751 list_label
1752 FROM ((
1753
1754 SELECT
1755 loinc AS data,
1756 loinc AS field_label,
1757 (loinc || ': ' || abbrev || ' (' || name || ')') AS list_label
1758 FROM clin.test_type
1759 WHERE loinc %(fragment_condition)s
1760 LIMIT 50
1761
1762 ) UNION ALL (
1763
1764 SELECT
1765 code AS data,
1766 code AS field_label,
1767 (code || ': ' || term) AS list_label
1768 FROM ref.v_coded_terms
1769 WHERE
1770 coding_system = 'LOINC'
1771 AND
1772 lang = i18n.get_curr_lang()
1773 AND
1774 (code %(fragment_condition)s
1775 OR
1776 term %(fragment_condition)s)
1777 LIMIT 50
1778
1779 ) UNION ALL (
1780
1781 SELECT
1782 code AS data,
1783 code AS field_label,
1784 (code || ': ' || term) AS list_label
1785 FROM ref.v_coded_terms
1786 WHERE
1787 coding_system = 'LOINC'
1788 AND
1789 lang = 'en_EN'
1790 AND
1791 (code %(fragment_condition)s
1792 OR
1793 term %(fragment_condition)s)
1794 LIMIT 50
1795
1796 ) UNION ALL (
1797
1798 SELECT
1799 code AS data,
1800 code AS field_label,
1801 (code || ': ' || term) AS list_label
1802 FROM ref.v_coded_terms
1803 WHERE
1804 coding_system = 'LOINC'
1805 AND
1806 (code %(fragment_condition)s
1807 OR
1808 term %(fragment_condition)s)
1809 LIMIT 50
1810 )
1811 ) AS all_known_loinc
1812
1813 ORDER BY list_label
1814 LIMIT 50"""
1815 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
1816 mp.setThresholds(1, 2, 4)
1817 self._PRW_loinc.matcher = mp
1818 self._PRW_loinc.selection_only = False
1819 self._PRW_loinc.add_callback_on_lose_focus(callback = self._on_loinc_lost_focus)
1820
1822
1823 test = self._PRW_name.GetValue().strip()
1824
1825 if test == u'':
1826 self._PRW_conversion_unit.unset_context(context = u'test_name')
1827 return
1828
1829 self._PRW_conversion_unit.set_context(context = u'test_name', val = test)
1830
1832 loinc = self._PRW_loinc.GetData()
1833
1834 if loinc is None:
1835 self._TCTRL_loinc_info.SetValue(u'')
1836 self._PRW_conversion_unit.unset_context(context = u'loinc')
1837 return
1838
1839 self._PRW_conversion_unit.set_context(context = u'loinc', val = loinc)
1840
1841 info = gmLOINC.loinc2term(loinc = loinc)
1842 if len(info) == 0:
1843 self._TCTRL_loinc_info.SetValue(u'')
1844 return
1845
1846 self._TCTRL_loinc_info.SetValue(info[0])
1847
1848
1849
1851
1852 has_errors = False
1853 for field in [self._PRW_name, self._PRW_abbrev, self._PRW_conversion_unit]:
1854 if field.GetValue().strip() in [u'', None]:
1855 has_errors = True
1856 field.display_as_valid(valid = False)
1857 else:
1858 field.display_as_valid(valid = True)
1859 field.Refresh()
1860
1861 return (not has_errors)
1862
1864
1865 pk_org = self._PRW_test_org.GetData()
1866 if pk_org is None:
1867 pk_org = gmPathLab.create_test_org (
1868 name = gmTools.none_if(self._PRW_test_org.GetValue().strip(), u''),
1869 comment = gmTools.none_if(self._TCTRL_comment_org.GetValue().strip(), u'')
1870 )['pk_test_org']
1871
1872 tt = gmPathLab.create_measurement_type (
1873 lab = pk_org,
1874 abbrev = self._PRW_abbrev.GetValue().strip(),
1875 name = self._PRW_name.GetValue().strip(),
1876 unit = gmTools.coalesce (
1877 self._PRW_conversion_unit.GetData(),
1878 self._PRW_conversion_unit.GetValue()
1879 ).strip()
1880 )
1881 if self._PRW_loinc.GetData() is not None:
1882 tt['loinc'] = gmTools.none_if(self._PRW_loinc.GetData().strip(), u'')
1883 else:
1884 tt['loinc'] = gmTools.none_if(self._PRW_loinc.GetValue().strip(), u'')
1885 tt['comment_type'] = gmTools.none_if(self._TCTRL_comment_type.GetValue().strip(), u'')
1886 tt.save()
1887
1888 self.data = tt
1889
1890 return True
1891
1918
1920 self._PRW_name.SetText(u'', None, True)
1921 self._on_name_lost_focus()
1922 self._PRW_abbrev.SetText(u'', None, True)
1923 self._PRW_conversion_unit.SetText(u'', None, True)
1924 self._PRW_loinc.SetText(u'', None, True)
1925 self._on_loinc_lost_focus()
1926 self._TCTRL_comment_type.SetValue(u'')
1927 self._PRW_test_org.SetText(u'', None, True)
1928 self._TCTRL_comment_org.SetValue(u'')
1929
1930 self._PRW_name.SetFocus()
1931
1933 self._PRW_name.SetText(self.data['name'], self.data['name'], True)
1934 self._on_name_lost_focus()
1935 self._PRW_abbrev.SetText(self.data['abbrev'], self.data['abbrev'], True)
1936 self._PRW_conversion_unit.SetText (
1937 gmTools.coalesce(self.data['conversion_unit'], u''),
1938 self.data['conversion_unit'],
1939 True
1940 )
1941 self._PRW_loinc.SetText (
1942 gmTools.coalesce(self.data['loinc'], u''),
1943 self.data['loinc'],
1944 True
1945 )
1946 self._on_loinc_lost_focus()
1947 self._TCTRL_comment_type.SetValue(gmTools.coalesce(self.data['comment_type'], u''))
1948 self._PRW_test_org.SetText (
1949 gmTools.coalesce(self.data['pk_test_org'], u'', self.data['name_org']),
1950 self.data['pk_test_org'],
1951 True
1952 )
1953 self._TCTRL_comment_org.SetValue(gmTools.coalesce(self.data['comment_org'], u''))
1954
1955 self._PRW_name.SetFocus()
1956
1967
1968 _SQL_units_from_test_results = u"""
1969 -- via clin.v_test_results.pk_type (for types already used in results)
1970 SELECT
1971 val_unit AS data,
1972 val_unit AS field_label,
1973 val_unit || ' (' || name_tt || ')' AS list_label,
1974 1 AS rank
1975 FROM
1976 clin.v_test_results
1977 WHERE
1978 (
1979 val_unit %(fragment_condition)s
1980 OR
1981 conversion_unit %(fragment_condition)s
1982 )
1983 %(ctxt_type_pk)s
1984 %(ctxt_test_name)s
1985 """
1986
1987 _SQL_units_from_test_types = u"""
1988 -- via clin.test_type (for types not yet used in results)
1989 SELECT
1990 conversion_unit AS data,
1991 conversion_unit AS field_label,
1992 conversion_unit || ' (' || name || ')' AS list_label,
1993 2 AS rank
1994 FROM
1995 clin.test_type
1996 WHERE
1997 conversion_unit %(fragment_condition)s
1998 %(ctxt_ctt)s
1999 """
2000
2001 _SQL_units_from_loinc_ipcc = u"""
2002 -- via ref.loinc.ipcc_units
2003 SELECT
2004 ipcc_units AS data,
2005 ipcc_units AS field_label,
2006 ipcc_units || ' (LOINC.ipcc: ' || term || ')' AS list_label,
2007 3 AS rank
2008 FROM
2009 ref.loinc
2010 WHERE
2011 ipcc_units %(fragment_condition)s
2012 %(ctxt_loinc)s
2013 %(ctxt_loinc_term)s
2014 """
2015
2016 _SQL_units_from_loinc_submitted = u"""
2017 -- via ref.loinc.submitted_units
2018 SELECT
2019 submitted_units AS data,
2020 submitted_units AS field_label,
2021 submitted_units || ' (LOINC.submitted:' || term || ')' AS list_label,
2022 3 AS rank
2023 FROM
2024 ref.loinc
2025 WHERE
2026 submitted_units %(fragment_condition)s
2027 %(ctxt_loinc)s
2028 %(ctxt_loinc_term)s
2029 """
2030
2031 _SQL_units_from_loinc_example = u"""
2032 -- via ref.loinc.example_units
2033 SELECT
2034 example_units AS data,
2035 example_units AS field_label,
2036 example_units || ' (LOINC.example: ' || term || ')' AS list_label,
2037 3 AS rank
2038 FROM
2039 ref.loinc
2040 WHERE
2041 example_units %(fragment_condition)s
2042 %(ctxt_loinc)s
2043 %(ctxt_loinc_term)s
2044 """
2045
2046 _SQL_units_from_atc = u"""
2047 -- via rev.atc.unit
2048 SELECT
2049 unit AS data,
2050 unit AS field_label,
2051 unit || ' (ATC: ' || term || ')' AS list_label,
2052 2 AS rank
2053 FROM
2054 ref.atc
2055 WHERE
2056 unit IS NOT NULL
2057 AND
2058 unit %(fragment_condition)s
2059 """
2060
2061 _SQL_units_from_consumable_substance = u"""
2062 -- via ref.consumable_substance.unit
2063 SELECT
2064 unit AS data,
2065 unit AS field_label,
2066 unit || ' (' || description || ')' AS list_label,
2067 2 AS rank
2068 FROM
2069 ref.consumable_substance
2070 WHERE
2071 unit %(fragment_condition)s
2072 %(ctxt_substance)s
2073 """
2074
2076
2078
2079 query = u"""
2080 SELECT DISTINCT ON (data)
2081 data,
2082 field_label,
2083 list_label
2084 FROM (
2085
2086 SELECT
2087 data,
2088 field_label,
2089 list_label,
2090 rank
2091 FROM (
2092 (%s) UNION ALL
2093 (%s) UNION ALL
2094 (%s) UNION ALL
2095 (%s) UNION ALL
2096 (%s) UNION ALL
2097 (%s) UNION ALL
2098 (%s)
2099 ) AS all_matching_units
2100 WHERE data IS NOT NULL
2101 ORDER BY rank
2102
2103 ) AS ranked_matching_units
2104 LIMIT 50""" % (
2105 _SQL_units_from_test_results,
2106 _SQL_units_from_test_types,
2107 _SQL_units_from_loinc_ipcc,
2108 _SQL_units_from_loinc_submitted,
2109 _SQL_units_from_loinc_example,
2110 _SQL_units_from_atc,
2111 _SQL_units_from_consumable_substance
2112 )
2113
2114 ctxt = {
2115 'ctxt_type_pk': {
2116 'where_part': u'AND pk_test_type = %(pk_type)s',
2117 'placeholder': u'pk_type'
2118 },
2119 'ctxt_test_name': {
2120 'where_part': u'AND %(test_name)s IN (name_tt, name_meta, code_tt, abbrev_meta)',
2121 'placeholder': u'test_name'
2122 },
2123 'ctxt_ctt': {
2124 'where_part': u'AND %(test_name)s IN (name, code, abbrev)',
2125 'placeholder': u'test_name'
2126 },
2127 'ctxt_loinc': {
2128 'where_part': u'AND code = %(loinc)s',
2129 'placeholder': u'loinc'
2130 },
2131 'ctxt_loinc_term': {
2132 'where_part': u'AND term ~* %(test_name)s',
2133 'placeholder': u'test_name'
2134 },
2135 'ctxt_substance': {
2136 'where_part': u'AND description ~* %(substance)s',
2137 'placeholder': u'substance'
2138 }
2139 }
2140
2141 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query, context = ctxt)
2142 mp.setThresholds(1, 2, 4)
2143
2144 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
2145 self.matcher = mp
2146 self.SetToolTipString(_('Select the desired unit for the amount or measurement.'))
2147 self.selection_only = False
2148 self.phrase_separators = u'[;|]+'
2149
2150
2151
2153
2155
2156 query = u"""
2157 select distinct abnormality_indicator,
2158 abnormality_indicator, abnormality_indicator
2159 from clin.v_test_results
2160 where
2161 abnormality_indicator %(fragment_condition)s
2162 order by abnormality_indicator
2163 limit 25"""
2164
2165 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
2166 mp.setThresholds(1, 1, 2)
2167 mp.ignored_chars = "[.'\\\[\]#$%_]+" + '"'
2168 mp.word_separators = '[ \t&:]+'
2169 gmPhraseWheel.cPhraseWheel.__init__ (
2170 self,
2171 *args,
2172 **kwargs
2173 )
2174 self.matcher = mp
2175 self.SetToolTipString(_('Select an indicator for the level of abnormality.'))
2176 self.selection_only = False
2177
2178
2179
2191
2193
2194 if parent is None:
2195 parent = wx.GetApp().GetTopWindow()
2196
2197
2198 def edit(org=None):
2199 return edit_measurement_org(parent = parent, org = org)
2200
2201 def refresh(lctrl):
2202 orgs = gmPathLab.get_test_orgs()
2203 lctrl.set_string_items ([
2204 (o['unit'], o['organization'], gmTools.coalesce(o['test_org_contact'], u''), gmTools.coalesce(o['comment'], u''), o['pk_test_org'])
2205 for o in orgs
2206 ])
2207 lctrl.set_data(orgs)
2208
2209 def delete(test_org):
2210 gmPathLab.delete_test_org(test_org = test_org['pk_test_org'])
2211 return True
2212
2213 gmListWidgets.get_choices_from_list (
2214 parent = parent,
2215 msg = _('\nThese are the diagnostic orgs (path labs etc) currently defined in GNUmed.\n\n'),
2216 caption = _('Showing diagnostic orgs.'),
2217 columns = [_('Name'), _('Organization'), _('Contact'), _('Comment'), u'#'],
2218 single_selection = True,
2219 refresh_callback = refresh,
2220 edit_callback = edit,
2221 new_callback = edit,
2222 delete_callback = delete
2223 )
2224
2225
2226 from Gnumed.wxGladeWidgets import wxgMeasurementOrgEAPnl
2227
2228 -class cMeasurementOrgEAPnl(wxgMeasurementOrgEAPnl.wxgMeasurementOrgEAPnl, gmEditArea.cGenericEditAreaMixin):
2229
2245
2246
2247
2248
2249
2250
2251
2252
2254 has_errors = False
2255 if self._PRW_org_unit.GetData() is None:
2256 if self._PRW_org_unit.GetValue().strip() == u'':
2257 has_errors = True
2258 self._PRW_org_unit.display_as_valid(valid = False)
2259 else:
2260 self._PRW_org_unit.display_as_valid(valid = True)
2261 else:
2262 self._PRW_org_unit.display_as_valid(valid = True)
2263
2264 return (not has_errors)
2265
2276
2296
2301
2306
2308 self._refresh_as_new()
2309
2312
2314
2316
2317 query = u"""
2318 SELECT DISTINCT ON (list_label)
2319 pk AS data,
2320 unit || ' (' || organization || ')' AS field_label,
2321 unit || ' @ ' || organization AS list_label
2322 FROM clin.v_test_orgs
2323 WHERE
2324 unit %(fragment_condition)s
2325 OR
2326 organization %(fragment_condition)s
2327 ORDER BY list_label
2328 LIMIT 50"""
2329 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
2330 mp.setThresholds(1, 2, 4)
2331
2332 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
2333 self.matcher = mp
2334 self.SetToolTipString(_('The name of the path lab/diagnostic organisation.'))
2335 self.selection_only = False
2336
2349
2352
2391
2392
2393
2394 if __name__ == '__main__':
2395
2396 from Gnumed.pycommon import gmLog2
2397
2398 gmI18N.activate_locale()
2399 gmI18N.install_domain()
2400 gmDateTime.init()
2401
2402
2410
2418
2419
2420
2421
2422
2423
2424
2425 if (len(sys.argv) > 1) and (sys.argv[1] == 'test'):
2426
2427 test_test_ea_pnl()
2428
2429
2430
2431