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