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