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
1060
1063
1066
1069
1070
1071
1073 self.__action_button_popup = wx.Menu(title = _('Perform on selected results:'))
1074
1075 menu_id = wx.NewId()
1076 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('Review and &sign')))
1077 wx.EVT_MENU(self.__action_button_popup, menu_id, self.__on_sign_current_selection)
1078
1079 menu_id = wx.NewId()
1080 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('Plot')))
1081 wx.EVT_MENU(self.__action_button_popup, menu_id, self.__on_plot_current_selection)
1082
1083 menu_id = wx.NewId()
1084 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('Export to &file')))
1085
1086 self.__action_button_popup.Enable(id = menu_id, enable = False)
1087
1088 menu_id = wx.NewId()
1089 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('Export to &clipboard')))
1090
1091 self.__action_button_popup.Enable(id = menu_id, enable = False)
1092
1093 menu_id = wx.NewId()
1094 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('&Delete')))
1095 wx.EVT_MENU(self.__action_button_popup, menu_id, self.__on_delete_current_selection)
1096
1097
1098
1099
1100
1101
1102
1111
1112
1113
1114 from Gnumed.wxGladeWidgets import wxgMeasurementsReviewDlg
1115
1117
1119
1120 try:
1121 tests = kwargs['tests']
1122 del kwargs['tests']
1123 test_count = len(tests)
1124 try: del kwargs['test_count']
1125 except KeyError: pass
1126 except KeyError:
1127 tests = None
1128 test_count = kwargs['test_count']
1129 del kwargs['test_count']
1130
1131 wxgMeasurementsReviewDlg.wxgMeasurementsReviewDlg.__init__(self, *args, **kwargs)
1132
1133 if tests is None:
1134 msg = _('%s results selected. Too many to list individually.') % test_count
1135 else:
1136 msg = ' // '.join (
1137 [ u'%s: %s %s (%s)' % (
1138 t['unified_abbrev'],
1139 t['unified_val'],
1140 t['val_unit'],
1141 t['clin_when'].strftime('%x').decode(gmI18N.get_encoding())
1142 ) for t in tests
1143 ]
1144 )
1145
1146 self._LBL_tests.SetLabel(msg)
1147
1148 if test_count == 1:
1149 self._TCTRL_comment.Enable(True)
1150 self._TCTRL_comment.SetValue(gmTools.coalesce(tests[0]['review_comment'], u''))
1151 if tests[0]['you_are_responsible']:
1152 self._CHBOX_responsible.Enable(False)
1153
1154 self.Fit()
1155
1156
1157
1163
1164 from Gnumed.wxGladeWidgets import wxgMeasurementEditAreaPnl
1165
1166 -class cMeasurementEditAreaPnl(wxgMeasurementEditAreaPnl.wxgMeasurementEditAreaPnl, gmEditArea.cGenericEditAreaMixin):
1167 """This edit area saves *new* measurements into the active patient only."""
1168
1183
1184
1185
1187 self._PRW_test.SetText(u'', None, True)
1188 self.__refresh_loinc_info()
1189 self.__update_units_context()
1190 self._TCTRL_result.SetValue(u'')
1191 self._PRW_units.SetText(u'', None, True)
1192 self._PRW_abnormality_indicator.SetText(u'', None, True)
1193 if self.__default_date is None:
1194 self._DPRW_evaluated.SetData(data = pyDT.datetime.now(tz = gmDateTime.gmCurrentLocalTimezone))
1195 else:
1196 self._DPRW_evaluated.SetData(data = None)
1197 self._TCTRL_note_test_org.SetValue(u'')
1198 self._PRW_intended_reviewer.SetData(gmPerson.gmCurrentProvider()['pk_staff'])
1199 self._PRW_problem.SetData()
1200 self._TCTRL_narrative.SetValue(u'')
1201 self._CHBOX_review.SetValue(False)
1202 self._CHBOX_abnormal.SetValue(False)
1203 self._CHBOX_relevant.SetValue(False)
1204 self._CHBOX_abnormal.Enable(False)
1205 self._CHBOX_relevant.Enable(False)
1206 self._TCTRL_review_comment.SetValue(u'')
1207 self._TCTRL_normal_min.SetValue(u'')
1208 self._TCTRL_normal_max.SetValue(u'')
1209 self._TCTRL_normal_range.SetValue(u'')
1210 self._TCTRL_target_min.SetValue(u'')
1211 self._TCTRL_target_max.SetValue(u'')
1212 self._TCTRL_target_range.SetValue(u'')
1213 self._TCTRL_norm_ref_group.SetValue(u'')
1214
1215 self._PRW_test.SetFocus()
1216
1218 self._PRW_test.SetData(data = self.data['pk_test_type'])
1219 self.__refresh_loinc_info()
1220 self.__update_units_context()
1221 self._TCTRL_result.SetValue(self.data['unified_val'])
1222 self._PRW_units.SetText(self.data['val_unit'], self.data['val_unit'], True)
1223 self._PRW_abnormality_indicator.SetText (
1224 gmTools.coalesce(self.data['abnormality_indicator'], u''),
1225 gmTools.coalesce(self.data['abnormality_indicator'], u''),
1226 True
1227 )
1228 self._DPRW_evaluated.SetData(data = self.data['clin_when'])
1229 self._TCTRL_note_test_org.SetValue(gmTools.coalesce(self.data['note_test_org'], u''))
1230 self._PRW_intended_reviewer.SetData(self.data['pk_intended_reviewer'])
1231 self._PRW_problem.SetData(self.data['pk_episode'])
1232 self._TCTRL_narrative.SetValue(gmTools.coalesce(self.data['comment'], u''))
1233 self._CHBOX_review.SetValue(False)
1234 self._CHBOX_abnormal.SetValue(gmTools.coalesce(self.data['is_technically_abnormal'], False))
1235 self._CHBOX_relevant.SetValue(gmTools.coalesce(self.data['is_clinically_relevant'], False))
1236 self._CHBOX_abnormal.Enable(False)
1237 self._CHBOX_relevant.Enable(False)
1238 self._TCTRL_review_comment.SetValue(gmTools.coalesce(self.data['review_comment'], u''))
1239 self._TCTRL_normal_min.SetValue(unicode(gmTools.coalesce(self.data['val_normal_min'], u'')))
1240 self._TCTRL_normal_max.SetValue(unicode(gmTools.coalesce(self.data['val_normal_max'], u'')))
1241 self._TCTRL_normal_range.SetValue(gmTools.coalesce(self.data['val_normal_range'], u''))
1242 self._TCTRL_target_min.SetValue(unicode(gmTools.coalesce(self.data['val_target_min'], u'')))
1243 self._TCTRL_target_max.SetValue(unicode(gmTools.coalesce(self.data['val_target_max'], u'')))
1244 self._TCTRL_target_range.SetValue(gmTools.coalesce(self.data['val_target_range'], u''))
1245 self._TCTRL_norm_ref_group.SetValue(gmTools.coalesce(self.data['norm_ref_group'], u''))
1246
1247 self._TCTRL_result.SetFocus()
1248
1250 self._refresh_from_existing()
1251
1252 self._PRW_test.SetText(u'', None, True)
1253 self.__refresh_loinc_info()
1254 self.__update_units_context()
1255 self._TCTRL_result.SetValue(u'')
1256 self._PRW_units.SetText(u'', None, True)
1257 self._PRW_abnormality_indicator.SetText(u'', None, True)
1258
1259 self._TCTRL_note_test_org.SetValue(u'')
1260 self._TCTRL_narrative.SetValue(u'')
1261 self._CHBOX_review.SetValue(False)
1262 self._CHBOX_abnormal.SetValue(False)
1263 self._CHBOX_relevant.SetValue(False)
1264 self._CHBOX_abnormal.Enable(False)
1265 self._CHBOX_relevant.Enable(False)
1266 self._TCTRL_review_comment.SetValue(u'')
1267 self._TCTRL_normal_min.SetValue(u'')
1268 self._TCTRL_normal_max.SetValue(u'')
1269 self._TCTRL_normal_range.SetValue(u'')
1270 self._TCTRL_target_min.SetValue(u'')
1271 self._TCTRL_target_max.SetValue(u'')
1272 self._TCTRL_target_range.SetValue(u'')
1273 self._TCTRL_norm_ref_group.SetValue(u'')
1274
1275 self._PRW_test.SetFocus()
1276
1278
1279 validity = True
1280
1281 if not self._DPRW_evaluated.is_valid_timestamp():
1282 self._DPRW_evaluated.display_as_valid(False)
1283 validity = False
1284 else:
1285 self._DPRW_evaluated.display_as_valid(True)
1286
1287 if self._TCTRL_result.GetValue().strip() == u'':
1288 validity = False
1289 self.display_ctrl_as_valid(self._TCTRL_result, False)
1290 else:
1291 self.display_ctrl_as_valid(self._TCTRL_result, True)
1292
1293 if self._PRW_problem.GetValue().strip() == u'':
1294 self._PRW_problem.display_as_valid(False)
1295 validity = False
1296 else:
1297 self._PRW_problem.display_as_valid(True)
1298
1299 if self._PRW_test.GetValue().strip() == u'':
1300 self._PRW_test.display_as_valid(False)
1301 validity = False
1302 else:
1303 self._PRW_test.display_as_valid(True)
1304
1305 if self._PRW_intended_reviewer.GetData() is None:
1306 self._PRW_intended_reviewer.display_as_valid(False)
1307 validity = False
1308 else:
1309 self._PRW_intended_reviewer.display_as_valid(True)
1310
1311 if self._PRW_units.GetValue().strip() == u'':
1312 self._PRW_units.display_as_valid(False)
1313 validity = False
1314 else:
1315 self._PRW_units.display_as_valid(True)
1316
1317 ctrls = [self._TCTRL_normal_min, self._TCTRL_normal_max, self._TCTRL_target_min, self._TCTRL_target_max]
1318 for widget in ctrls:
1319 val = widget.GetValue().strip()
1320 if val == u'':
1321 continue
1322 try:
1323 decimal.Decimal(val.replace(',', u'.', 1))
1324 self.display_ctrl_as_valid(widget, True)
1325 except:
1326 validity = False
1327 self.display_ctrl_as_valid(widget, False)
1328
1329 if validity is False:
1330 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save result. Invalid or missing essential input.'))
1331
1332 return validity
1333
1335
1336 emr = gmPerson.gmCurrentPatient().get_emr()
1337
1338 try:
1339 v_num = decimal.Decimal(self._TCTRL_result.GetValue().strip().replace(',', '.', 1))
1340 v_al = None
1341 except:
1342 v_num = None
1343 v_al = self._TCTRL_result.GetValue().strip()
1344
1345 pk_type = self._PRW_test.GetData()
1346 if pk_type is None:
1347 tt = gmPathLab.create_measurement_type (
1348 lab = None,
1349 abbrev = self._PRW_test.GetValue().strip(),
1350 name = self._PRW_test.GetValue().strip(),
1351 unit = gmTools.coalesce(self._PRW_units.GetData(), self._PRW_units.GetValue()).strip()
1352 )
1353 pk_type = tt['pk_test_type']
1354
1355 tr = emr.add_test_result (
1356 episode = self._PRW_problem.GetData(can_create=True, is_open=False),
1357 type = pk_type,
1358 intended_reviewer = self._PRW_intended_reviewer.GetData(),
1359 val_num = v_num,
1360 val_alpha = v_al,
1361 unit = self._PRW_units.GetValue()
1362 )
1363
1364 tr['clin_when'] = self._DPRW_evaluated.GetData().get_pydt()
1365
1366 ctrls = [
1367 ('abnormality_indicator', self._PRW_abnormality_indicator),
1368 ('note_test_org', self._TCTRL_note_test_org),
1369 ('comment', self._TCTRL_narrative),
1370 ('val_normal_range', self._TCTRL_normal_range),
1371 ('val_target_range', self._TCTRL_target_range),
1372 ('norm_ref_group', self._TCTRL_norm_ref_group)
1373 ]
1374 for field, widget in ctrls:
1375 tr[field] = widget.GetValue().strip()
1376
1377 ctrls = [
1378 ('val_normal_min', self._TCTRL_normal_min),
1379 ('val_normal_max', self._TCTRL_normal_max),
1380 ('val_target_min', self._TCTRL_target_min),
1381 ('val_target_max', self._TCTRL_target_max)
1382 ]
1383 for field, widget in ctrls:
1384 val = widget.GetValue().strip()
1385 if val == u'':
1386 tr[field] = None
1387 else:
1388 tr[field] = decimal.Decimal(val.replace(',', u'.', 1))
1389
1390 tr.save_payload()
1391
1392 if self._CHBOX_review.GetValue() is True:
1393 tr.set_review (
1394 technically_abnormal = self._CHBOX_abnormal.GetValue(),
1395 clinically_relevant = self._CHBOX_relevant.GetValue(),
1396 comment = gmTools.none_if(self._TCTRL_review_comment.GetValue().strip(), u''),
1397 make_me_responsible = False
1398 )
1399
1400 self.data = tr
1401
1402 return True
1403
1405
1406 success, result = gmTools.input2decimal(self._TCTRL_result.GetValue())
1407 if success:
1408 v_num = result
1409 v_al = None
1410 else:
1411 v_num = None
1412 v_al = self._TCTRL_result.GetValue().strip()
1413
1414 pk_type = self._PRW_test.GetData()
1415 if pk_type is None:
1416 tt = gmPathLab.create_measurement_type (
1417 lab = None,
1418 abbrev = self._PRW_test.GetValue().strip(),
1419 name = self._PRW_test.GetValue().strip(),
1420 unit = gmTools.none_if(self._PRW_units.GetValue().strip(), u'')
1421 )
1422 pk_type = tt['pk_test_type']
1423
1424 tr = self.data
1425
1426 tr['pk_episode'] = self._PRW_problem.GetData(can_create=True, is_open=False)
1427 tr['pk_test_type'] = pk_type
1428 tr['pk_intended_reviewer'] = self._PRW_intended_reviewer.GetData()
1429 tr['val_num'] = v_num
1430 tr['val_alpha'] = v_al
1431 tr['val_unit'] = gmTools.coalesce(self._PRW_units.GetData(), self._PRW_units.GetValue()).strip()
1432 tr['clin_when'] = self._DPRW_evaluated.GetData().get_pydt()
1433
1434 ctrls = [
1435 ('abnormality_indicator', self._PRW_abnormality_indicator),
1436 ('note_test_org', self._TCTRL_note_test_org),
1437 ('comment', self._TCTRL_narrative),
1438 ('val_normal_range', self._TCTRL_normal_range),
1439 ('val_target_range', self._TCTRL_target_range),
1440 ('norm_ref_group', self._TCTRL_norm_ref_group)
1441 ]
1442 for field, widget in ctrls:
1443 tr[field] = widget.GetValue().strip()
1444
1445 ctrls = [
1446 ('val_normal_min', self._TCTRL_normal_min),
1447 ('val_normal_max', self._TCTRL_normal_max),
1448 ('val_target_min', self._TCTRL_target_min),
1449 ('val_target_max', self._TCTRL_target_max)
1450 ]
1451 for field, widget in ctrls:
1452 val = widget.GetValue().strip()
1453 if val == u'':
1454 tr[field] = None
1455 else:
1456 tr[field] = decimal.Decimal(val.replace(',', u'.', 1))
1457
1458 tr.save_payload()
1459
1460 if self._CHBOX_review.GetValue() is True:
1461 tr.set_review (
1462 technically_abnormal = self._CHBOX_abnormal.GetValue(),
1463 clinically_relevant = self._CHBOX_relevant.GetValue(),
1464 comment = gmTools.none_if(self._TCTRL_review_comment.GetValue().strip(), u''),
1465 make_me_responsible = False
1466 )
1467
1468 return True
1469
1470
1471
1475
1477 self.__refresh_loinc_info()
1478 self.__update_units_context()
1479
1481
1482 if not self._CHBOX_review.GetValue():
1483 self._CHBOX_abnormal.SetValue(self._PRW_abnormality_indicator.GetValue().strip() != u'')
1484
1486 self._CHBOX_abnormal.Enable(self._CHBOX_review.GetValue())
1487 self._CHBOX_relevant.Enable(self._CHBOX_review.GetValue())
1488 self._TCTRL_review_comment.Enable(self._CHBOX_review.GetValue())
1489
1506
1507
1508
1510
1511 self._PRW_units.unset_context(context = u'loinc')
1512
1513 tt = self._PRW_test.GetData(as_instance = True)
1514
1515 if tt is None:
1516 self._PRW_units.unset_context(context = u'pk_type')
1517 if self._PRW_test.GetValue().strip() == u'':
1518 self._PRW_units.unset_context(context = u'test_name')
1519 else:
1520 self._PRW_units.set_context(context = u'test_name', val = self._PRW_test.GetValue().strip())
1521 return
1522
1523 self._PRW_units.set_context(context = u'pk_type', val = tt['pk_test_type'])
1524 self._PRW_units.set_context(context = u'test_name', val = tt['name'])
1525
1526 if tt['loinc'] is None:
1527 return
1528
1529 self._PRW_units.set_context(context = u'loinc', val = tt['loinc'])
1530
1532
1533 self._TCTRL_loinc.SetValue(u'')
1534
1535 if self._PRW_test.GetData() is None:
1536 return
1537
1538 tt = self._PRW_test.GetData(as_instance = True)
1539
1540 if tt['loinc'] is None:
1541 return
1542
1543 info = gmLOINC.loinc2info(loinc = tt['loinc'])
1544 if len(info) == 0:
1545 return
1546
1547 self._TCTRL_loinc.SetValue(u'%s: %s' % (tt['loinc'], info[0]))
1548
1549
1550
1552
1553 if parent is None:
1554 parent = wx.GetApp().GetTopWindow()
1555
1556
1557 def edit(test_type=None):
1558 ea = cMeasurementTypeEAPnl(parent = parent, id = -1, type = test_type)
1559 dlg = gmEditArea.cGenericEditAreaDlg2 (
1560 parent = parent,
1561 id = -1,
1562 edit_area = ea,
1563 single_entry = gmTools.bool2subst((test_type is None), False, True)
1564 )
1565 dlg.SetTitle(gmTools.coalesce(test_type, _('Adding measurement type'), _('Editing measurement type')))
1566
1567 if dlg.ShowModal() == wx.ID_OK:
1568 dlg.Destroy()
1569 return True
1570
1571 dlg.Destroy()
1572 return False
1573
1574 def refresh(lctrl):
1575 mtypes = gmPathLab.get_measurement_types(order_by = 'name, abbrev')
1576 items = [ [
1577 m['abbrev'],
1578 m['name'],
1579 gmTools.coalesce(m['loinc'], u''),
1580 gmTools.coalesce(m['conversion_unit'], u''),
1581 gmTools.coalesce(m['comment_type'], u''),
1582 gmTools.coalesce(m['internal_name_org'], _('in-house')),
1583 gmTools.coalesce(m['comment_org'], u''),
1584 m['pk_test_type']
1585 ] for m in mtypes ]
1586 lctrl.set_string_items(items)
1587 lctrl.set_data(mtypes)
1588
1589 def delete(measurement_type):
1590 if measurement_type.in_use:
1591 gmDispatcher.send (
1592 signal = 'statustext',
1593 beep = True,
1594 msg = _('Cannot delete measurement type [%s (%s)] because it is in use.') % (measurement_type['name'], measurement_type['abbrev'])
1595 )
1596 return False
1597 gmPathLab.delete_measurement_type(measurement_type = measurement_type['pk_test_type'])
1598 return True
1599
1600 msg = _(
1601 '\n'
1602 'These are the measurement types currently defined in GNUmed.\n'
1603 '\n'
1604 )
1605
1606 gmListWidgets.get_choices_from_list (
1607 parent = parent,
1608 msg = msg,
1609 caption = _('Showing measurement types.'),
1610 columns = [_('Abbrev'), _('Name'), _('LOINC'), _('Base unit'), _('Comment'), _('Org'), _('Comment'), u'#'],
1611 single_selection = True,
1612 refresh_callback = refresh,
1613 edit_callback = edit,
1614 new_callback = edit,
1615 delete_callback = delete
1616 )
1617
1619
1621
1622 query = u"""
1623 SELECT DISTINCT ON (field_label)
1624 pk_test_type AS data,
1625 name_tt
1626 || ' ('
1627 || coalesce (
1628 (SELECT internal_name FROM clin.test_org cto WHERE cto.pk = vcutt.pk_test_org),
1629 '%(in_house)s'
1630 )
1631 || ')'
1632 AS field_label,
1633 name_tt
1634 || ' ('
1635 || code_tt || ', '
1636 || abbrev_tt || ', '
1637 || coalesce(abbrev_meta || ': ' || name_meta || ', ', '')
1638 || coalesce (
1639 (SELECT internal_name FROM clin.test_org cto WHERE cto.pk = vcutt.pk_test_org),
1640 '%(in_house)s'
1641 )
1642 || ')'
1643 AS list_label
1644 FROM
1645 clin.v_unified_test_types vcutt
1646 WHERE
1647 abbrev_meta %%(fragment_condition)s
1648 OR
1649 name_meta %%(fragment_condition)s
1650 OR
1651 abbrev_tt %%(fragment_condition)s
1652 OR
1653 name_tt %%(fragment_condition)s
1654 OR
1655 code_tt %%(fragment_condition)s
1656 ORDER BY field_label
1657 LIMIT 50""" % {'in_house': _('generic / in house lab')}
1658
1659 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
1660 mp.setThresholds(1, 2, 4)
1661 mp.word_separators = '[ \t:@]+'
1662 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
1663 self.matcher = mp
1664 self.SetToolTipString(_('Select the type of measurement.'))
1665 self.selection_only = False
1666
1672
1674
1676
1677 query = u"""
1678 select distinct on (internal_name)
1679 pk,
1680 internal_name
1681 from clin.test_org
1682 where
1683 internal_name %(fragment_condition)s
1684 order by internal_name
1685 limit 50"""
1686 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
1687 mp.setThresholds(1, 2, 4)
1688
1689 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
1690 self.matcher = mp
1691 self.SetToolTipString(_('The name of the path lab/diagnostic organisation.'))
1692 self.selection_only = False
1693
1706
1709
1710 from Gnumed.wxGladeWidgets import wxgMeasurementTypeEAPnl
1711
1712 -class cMeasurementTypeEAPnl(wxgMeasurementTypeEAPnl.wxgMeasurementTypeEAPnl, gmEditArea.cGenericEditAreaMixin):
1713
1730
1731
1733
1734
1735 query = u"""
1736 select distinct on (name)
1737 pk,
1738 name
1739 from clin.test_type
1740 where
1741 name %(fragment_condition)s
1742 order by name
1743 limit 50"""
1744 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
1745 mp.setThresholds(1, 2, 4)
1746 self._PRW_name.matcher = mp
1747 self._PRW_name.selection_only = False
1748 self._PRW_name.add_callback_on_lose_focus(callback = self._on_name_lost_focus)
1749
1750
1751 query = u"""
1752 select distinct on (abbrev)
1753 pk,
1754 abbrev
1755 from clin.test_type
1756 where
1757 abbrev %(fragment_condition)s
1758 order by abbrev
1759 limit 50"""
1760 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
1761 mp.setThresholds(1, 2, 3)
1762 self._PRW_abbrev.matcher = mp
1763 self._PRW_abbrev.selection_only = False
1764
1765
1766 self._PRW_conversion_unit.selection_only = False
1767
1768
1769 query = u"""
1770 SELECT DISTINCT ON (list_label)
1771 data,
1772 field_label,
1773 list_label
1774 FROM ((
1775
1776 SELECT
1777 loinc AS data,
1778 loinc AS field_label,
1779 (loinc || ': ' || abbrev || ' (' || name || ')') AS list_label
1780 FROM clin.test_type
1781 WHERE loinc %(fragment_condition)s
1782 LIMIT 50
1783
1784 ) UNION ALL (
1785
1786 SELECT
1787 code AS data,
1788 code AS field_label,
1789 (code || ': ' || term) AS list_label
1790 FROM ref.v_coded_terms
1791 WHERE
1792 coding_system = 'LOINC'
1793 AND
1794 lang = i18n.get_curr_lang()
1795 AND
1796 (code %(fragment_condition)s
1797 OR
1798 term %(fragment_condition)s)
1799 LIMIT 50
1800
1801 ) UNION ALL (
1802
1803 SELECT
1804 code AS data,
1805 code AS field_label,
1806 (code || ': ' || term) AS list_label
1807 FROM ref.v_coded_terms
1808 WHERE
1809 coding_system = 'LOINC'
1810 AND
1811 lang = 'en_EN'
1812 AND
1813 (code %(fragment_condition)s
1814 OR
1815 term %(fragment_condition)s)
1816 LIMIT 50
1817
1818 ) UNION ALL (
1819
1820 SELECT
1821 code AS data,
1822 code AS field_label,
1823 (code || ': ' || term) AS list_label
1824 FROM ref.v_coded_terms
1825 WHERE
1826 coding_system = 'LOINC'
1827 AND
1828 (code %(fragment_condition)s
1829 OR
1830 term %(fragment_condition)s)
1831 LIMIT 50
1832 )
1833 ) AS all_known_loinc
1834
1835 ORDER BY list_label
1836 LIMIT 50"""
1837 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
1838 mp.setThresholds(1, 2, 4)
1839 self._PRW_loinc.matcher = mp
1840 self._PRW_loinc.selection_only = False
1841 self._PRW_loinc.add_callback_on_lose_focus(callback = self._on_loinc_lost_focus)
1842
1844
1845 test = self._PRW_name.GetValue().strip()
1846
1847 if test == u'':
1848 self._PRW_conversion_unit.unset_context(context = u'test_name')
1849 return
1850
1851 self._PRW_conversion_unit.set_context(context = u'test_name', val = test)
1852
1854 loinc = self._PRW_loinc.GetData()
1855
1856 if loinc is None:
1857 self._TCTRL_loinc_info.SetValue(u'')
1858 self._PRW_conversion_unit.unset_context(context = u'loinc')
1859 return
1860
1861 self._PRW_conversion_unit.set_context(context = u'loinc', val = loinc)
1862
1863 info = gmLOINC.loinc2info(loinc = loinc)
1864 if len(info) == 0:
1865 self._TCTRL_loinc_info.SetValue(u'')
1866 return
1867
1868 self._TCTRL_loinc_info.SetValue(info[0])
1869
1870
1871
1873
1874 has_errors = False
1875 for field in [self._PRW_name, self._PRW_abbrev, self._PRW_conversion_unit]:
1876 if field.GetValue().strip() in [u'', None]:
1877 has_errors = True
1878 field.display_as_valid(valid = False)
1879 else:
1880 field.display_as_valid(valid = True)
1881 field.Refresh()
1882
1883 return (not has_errors)
1884
1911
1934
1936 self._PRW_name.SetText(u'', None, True)
1937 self._on_name_lost_focus()
1938 self._PRW_abbrev.SetText(u'', None, True)
1939 self._PRW_conversion_unit.SetText(u'', None, True)
1940 self._PRW_loinc.SetText(u'', None, True)
1941 self._on_loinc_lost_focus()
1942 self._TCTRL_comment_type.SetValue(u'')
1943 self._PRW_test_org.SetText(u'', None, True)
1944 self._TCTRL_comment_org.SetValue(u'')
1945
1946 self._PRW_name.SetFocus()
1947
1949 self._PRW_name.SetText(self.data['name'], self.data['name'], True)
1950 self._on_name_lost_focus()
1951 self._PRW_abbrev.SetText(self.data['abbrev'], self.data['abbrev'], True)
1952 self._PRW_conversion_unit.SetText (
1953 gmTools.coalesce(self.data['conversion_unit'], u''),
1954 self.data['conversion_unit'],
1955 True
1956 )
1957 self._PRW_loinc.SetText (
1958 gmTools.coalesce(self.data['loinc'], u''),
1959 self.data['loinc'],
1960 True
1961 )
1962 self._on_loinc_lost_focus()
1963 self._TCTRL_comment_type.SetValue(gmTools.coalesce(self.data['comment_type'], u''))
1964 self._PRW_test_org.SetText (
1965 gmTools.coalesce(self.data['pk_test_org'], u'', self.data['internal_name_org']),
1966 self.data['pk_test_org'],
1967 True
1968 )
1969 self._TCTRL_comment_org.SetValue(gmTools.coalesce(self.data['comment_org'], u''))
1970
1971 self._PRW_name.SetFocus()
1972
1983
1984 _SQL_units_from_test_results = u"""
1985 -- via clin.v_test_results.pk_type (for types already used in results)
1986 SELECT
1987 val_unit AS data,
1988 val_unit AS field_label,
1989 val_unit || ' (' || name_tt || ')' AS list_label,
1990 1 AS rank
1991 FROM
1992 clin.v_test_results
1993 WHERE
1994 (
1995 val_unit %(fragment_condition)s
1996 OR
1997 conversion_unit %(fragment_condition)s
1998 )
1999 %(ctxt_type_pk)s
2000 %(ctxt_test_name)s
2001 """
2002
2003 _SQL_units_from_test_types = u"""
2004 -- via clin.test_type (for types not yet used in results)
2005 SELECT
2006 conversion_unit AS data,
2007 conversion_unit AS field_label,
2008 conversion_unit || ' (' || name || ')' AS list_label,
2009 2 AS rank
2010 FROM
2011 clin.test_type
2012 WHERE
2013 conversion_unit %(fragment_condition)s
2014 %(ctxt_ctt)s
2015 """
2016
2017 _SQL_units_from_loinc_ipcc = u"""
2018 -- via ref.loinc.ipcc_units
2019 SELECT
2020 ipcc_units AS data,
2021 ipcc_units AS field_label,
2022 ipcc_units || ' (' || term || ')' AS list_label,
2023 3 AS rank
2024 FROM
2025 ref.loinc
2026 WHERE
2027 ipcc_units %(fragment_condition)s
2028 %(ctxt_loinc)s
2029 %(ctxt_loinc_term)s
2030 """
2031
2032 _SQL_units_from_loinc_submitted = u"""
2033 -- via ref.loinc.submitted_units
2034 SELECT
2035 submitted_units AS data,
2036 submitted_units AS field_label,
2037 submitted_units || ' (' || term || ')' AS list_label,
2038 3 AS rank
2039 FROM
2040 ref.loinc
2041 WHERE
2042 submitted_units %(fragment_condition)s
2043 %(ctxt_loinc)s
2044 %(ctxt_loinc_term)s
2045 """
2046
2047 _SQL_units_from_loinc_example = u"""
2048 -- via ref.loinc.example_units
2049 SELECT
2050 example_units AS data,
2051 example_units AS field_label,
2052 example_units || ' (' || term || ')' AS list_label,
2053 3 AS rank
2054 FROM
2055 ref.loinc
2056 WHERE
2057 example_units %(fragment_condition)s
2058 %(ctxt_loinc)s
2059 %(ctxt_loinc_term)s
2060 """
2061
2062 _SQL_units_from_atc = u"""
2063 -- via rev.atc.unit
2064 SELECT
2065 unit AS data,
2066 unit AS field_label,
2067 unit AS list_label,
2068 1 AS rank
2069 FROM
2070 ref.atc
2071 WHERE
2072 unit IS NOT NULL
2073 AND
2074 unit %(fragment_condition)s
2075 """
2076
2077 _SQL_units_from_consumable_substance = u"""
2078 -- via ref.consumable_substance.unit
2079 SELECT
2080 unit AS data,
2081 unit AS field_label,
2082 unit AS list_label,
2083 1 AS rank
2084 FROM
2085 ref.consumable_substance
2086 WHERE
2087 unit %(fragment_condition)s
2088 %(ctxt_substance)s
2089 """
2090
2092
2094
2095 query = u"""
2096 SELECT DISTINCT ON (data)
2097 data,
2098 field_label,
2099 list_label
2100 FROM (
2101
2102 SELECT
2103 data,
2104 field_label,
2105 list_label,
2106 rank
2107 FROM (
2108 (%s) UNION ALL
2109 (%s) UNION ALL
2110 (%s) UNION ALL
2111 (%s) UNION ALL
2112 (%s) UNION ALL
2113 (%s) UNION ALL
2114 (%s)
2115 ) AS all_matching_units
2116 WHERE data IS NOT NULL
2117 ORDER BY rank
2118
2119 ) AS ranked_matching_units
2120 LIMIT 50""" % (
2121 _SQL_units_from_test_results,
2122 _SQL_units_from_test_types,
2123 _SQL_units_from_loinc_ipcc,
2124 _SQL_units_from_loinc_submitted,
2125 _SQL_units_from_loinc_example,
2126 _SQL_units_from_atc,
2127 _SQL_units_from_consumable_substance
2128 )
2129
2130 ctxt = {
2131 'ctxt_type_pk': {
2132 'where_part': u'AND pk_test_type = %(pk_type)s',
2133 'placeholder': u'pk_type'
2134 },
2135 'ctxt_test_name': {
2136 'where_part': u'AND %(test_name)s IN (name_tt, name_meta, code_tt, abbrev_meta)',
2137 'placeholder': u'test_name'
2138 },
2139 'ctxt_ctt': {
2140 'where_part': u'AND %(test_name)s IN (name, code, abbrev)',
2141 'placeholder': u'test_name'
2142 },
2143 'ctxt_loinc': {
2144 'where_part': u'AND code = %(loinc)s',
2145 'placeholder': u'loinc'
2146 },
2147 'ctxt_loinc_term': {
2148 'where_part': u'AND term ~* %(test_name)s',
2149 'placeholder': u'test_name'
2150 },
2151 'ctxt_substance': {
2152 'where_part': u'AND description ~* %(substance)s',
2153 'placeholder': u'substance'
2154 }
2155 }
2156
2157 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query, context = ctxt)
2158 mp.setThresholds(1, 2, 4)
2159
2160 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
2161 self.matcher = mp
2162 self.SetToolTipString(_('Select the desired unit for the amount or measurement.'))
2163 self.selection_only = False
2164 self.phrase_separators = u'[;|]+'
2165
2166
2167
2169
2171
2172 query = u"""
2173 select distinct abnormality_indicator,
2174 abnormality_indicator, abnormality_indicator
2175 from clin.v_test_results
2176 where
2177 abnormality_indicator %(fragment_condition)s
2178 order by abnormality_indicator
2179 limit 25"""
2180
2181 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
2182 mp.setThresholds(1, 1, 2)
2183 mp.ignored_chars = "[.'\\\[\]#$%_]+" + '"'
2184 mp.word_separators = '[ \t&:]+'
2185 gmPhraseWheel.cPhraseWheel.__init__ (
2186 self,
2187 *args,
2188 **kwargs
2189 )
2190 self.matcher = mp
2191 self.SetToolTipString(_('Select an indicator for the level of abnormality.'))
2192 self.selection_only = False
2193
2194
2195
2207
2209
2210 if parent is None:
2211 parent = wx.GetApp().GetTopWindow()
2212
2213
2214 def edit(org=None):
2215 return edit_measurement_org(parent = parent, org = org)
2216
2217 def refresh(lctrl):
2218 orgs = gmPathLab.get_test_orgs()
2219 lctrl.set_string_items ([
2220 (o['internal_name'], gmTools.coalesce(o['contact'], u''), gmTools.coalesce(o['comment']), o['pk'])
2221 for o in orgs
2222 ])
2223 lctrl.set_data(orgs)
2224
2225 def delete(measurement_type):
2226 if measurement_type.in_use:
2227 gmDispatcher.send (
2228 signal = 'statustext',
2229 beep = True,
2230 msg = _('Cannot delete measurement type [%s (%s)] because it is in use.') % (measurement_type['name'], measurement_type['abbrev'])
2231 )
2232 return False
2233 gmPathLab.delete_measurement_type(measurement_type = measurement_type['pk_test_type'])
2234 return True
2235
2236 gmListWidgets.get_choices_from_list (
2237 parent = parent,
2238 msg = _('\nThese are the diagnostic orgs (path labs etc) currently defined in GNUmed.\n\n'),
2239 caption = _('Showing diagnostic orgs.'),
2240 columns = [_('Name'), _('Contact'), _('Comment'), u'#'],
2241 single_selection = True,
2242 refresh_callback = refresh,
2243 edit_callback = edit,
2244 new_callback = edit
2245
2246 )
2247
2248
2249
2250 from Gnumed.wxGladeWidgets import wxgMeasurementOrgEAPnl
2251
2252 -class cMeasurementOrgEAPnl(wxgMeasurementOrgEAPnl.wxgMeasurementOrgEAPnl, gmEditArea.cGenericEditAreaMixin):
2253
2271
2272
2273
2274
2275
2276
2277
2278
2280 has_errors = False
2281 if self._PRW_name.GetValue().strip() == u'':
2282 has_errors = True
2283 self._PRW_name.display_as_valid(valid = False)
2284 else:
2285 self._PRW_name.display_as_valid(valid = True)
2286
2287 return (not has_errors)
2288
2290
2291 data = self._PRW_name.GetData(can_create = True, as_instance = True)
2292
2293 data['contact'] = self._TCTRL_contact.GetValue().strip()
2294 data['comment'] = self._TCTRL_comment.GetValue().strip()
2295 data.save()
2296
2297
2298
2299
2300 self.data = data
2301
2302 return True
2303
2305 self.data['internal_name'] = self._PRW_name.GetValue().strip()
2306 self.data['contact'] = self._TCTRL_contact.GetValue().strip()
2307 self.data['comment'] = self._TCTRL_comment.GetValue().strip()
2308 self.data.save()
2309 return True
2310
2315
2320
2322 self._refresh_as_new()
2323
2362
2363
2364
2365 if __name__ == '__main__':
2366
2367 from Gnumed.pycommon import gmLog2
2368
2369 gmI18N.activate_locale()
2370 gmI18N.install_domain()
2371 gmDateTime.init()
2372
2373
2381
2389
2390
2391
2392
2393
2394
2395
2396 if (len(sys.argv) > 1) and (sys.argv[1] == 'test'):
2397
2398 test_test_ea_pnl()
2399
2400
2401
2402