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