1 """GNUmed measurement widgets."""
2
3 __version__ = "$Revision: 1.66 $"
4 __author__ = "Karsten Hilbert <Karsten.Hilbert@gmx.net>"
5 __license__ = "GPL"
6
7
8 import sys, logging, datetime as pyDT, decimal, os, subprocess, codecs
9 import os.path
10
11
12 import wx, wx.grid, wx.lib.hyperlink
13
14
15 if __name__ == '__main__':
16 sys.path.insert(0, '../../')
17 from Gnumed.business import gmPerson
18 from Gnumed.business import gmStaff
19 from Gnumed.business import gmPathLab
20 from Gnumed.business import gmSurgery
21 from Gnumed.business import gmLOINC
22 from Gnumed.business import gmForms
23 from Gnumed.business import gmPersonSearch
24 from Gnumed.business import gmOrganization
25
26 from Gnumed.pycommon import gmTools
27 from Gnumed.pycommon import gmNetworkTools
28 from Gnumed.pycommon import gmI18N
29 from Gnumed.pycommon import gmShellAPI
30 from Gnumed.pycommon import gmCfg
31 from Gnumed.pycommon import gmDateTime
32 from Gnumed.pycommon import gmMatchProvider
33 from Gnumed.pycommon import gmDispatcher
34
35 from Gnumed.wxpython import gmRegetMixin
36 from Gnumed.wxpython import gmPhraseWheel
37 from Gnumed.wxpython import gmEditArea
38 from Gnumed.wxpython import gmGuiHelpers
39 from Gnumed.wxpython import gmListWidgets
40 from Gnumed.wxpython import gmAuthWidgets
41 from Gnumed.wxpython import gmFormWidgets
42 from Gnumed.wxpython import gmPatSearchWidgets
43 from Gnumed.wxpython import gmOrganizationWidgets
44
45
46 _log = logging.getLogger('gm.ui')
47 _log.info(__version__)
48
49
50
51
53
54 wx.BeginBusyCursor()
55
56 gmDispatcher.send(signal = 'statustext', msg = _('Updating LOINC data can take quite a while...'), beep = True)
57
58
59 loinc_zip = gmNetworkTools.download_file(url = 'http://www.gnumed.de/downloads/data/loinc/loinctab.zip', suffix = '.zip')
60 if loinc_zip is None:
61 wx.EndBusyCursor()
62 gmGuiHelpers.gm_show_warning (
63 aTitle = _('Downloading LOINC'),
64 aMessage = _('Error downloading the latest LOINC data.\n')
65 )
66 return False
67
68 _log.debug('downloaded zipped LOINC data into [%s]', loinc_zip)
69
70 loinc_dir = gmNetworkTools.unzip_data_pack(filename = loinc_zip)
71
72
73 data_fname, license_fname = gmLOINC.split_LOINCDBTXT(input_fname = os.path.join(loinc_dir, 'LOINCDB.TXT'))
74
75 wx.EndBusyCursor()
76
77 conn = gmAuthWidgets.get_dbowner_connection(procedure = _('importing LOINC reference data'))
78 if conn is None:
79 return False
80
81 wx.BeginBusyCursor()
82
83
84 if gmLOINC.loinc_import(data_fname = data_fname, license_fname = license_fname, conn = conn):
85 gmDispatcher.send(signal = 'statustext', msg = _('Successfully imported LOINC reference data.'))
86 else:
87 gmDispatcher.send(signal = 'statustext', msg = _('Importing LOINC reference data failed.'), beep = True)
88
89 wx.EndBusyCursor()
90 return True
91
92
93
95
96 dbcfg = gmCfg.cCfgSQL()
97
98 url = dbcfg.get (
99 option = u'external.urls.measurements_search',
100 workplace = gmSurgery.gmCurrentPractice().active_workplace,
101 bias = 'user',
102 default = u"http://www.google.de/search?as_oq=%(search_term)s&num=10&as_sitesearch=laborlexikon.de"
103 )
104
105 base_url = dbcfg.get2 (
106 option = u'external.urls.measurements_encyclopedia',
107 workplace = gmSurgery.gmCurrentPractice().active_workplace,
108 bias = 'user',
109 default = u'http://www.laborlexikon.de'
110 )
111
112 if measurement_type is None:
113 url = base_url
114
115 measurement_type = measurement_type.strip()
116
117 if measurement_type == u'':
118 url = base_url
119
120 url = url % {'search_term': measurement_type}
121
122 gmNetworkTools.open_url_in_browser(url = url)
123
135
136
165
166
168
169 option = u'form_templates.default_gnuplot_template'
170
171 dbcfg = gmCfg.cCfgSQL()
172
173
174 default_template_name = dbcfg.get2 (
175 option = option,
176 workplace = gmSurgery.gmCurrentPractice().active_workplace,
177 bias = 'user'
178 )
179
180
181 if default_template_name is None:
182 gmDispatcher.send('statustext', msg = _('No default Gnuplot template configured.'), beep = False)
183 default_template = configure_default_gnuplot_template(parent = parent)
184
185 if default_template is None:
186 gmGuiHelpers.gm_show_error (
187 aMessage = _('There is no default Gnuplot one-type script template configured.'),
188 aTitle = _('Plotting test results')
189 )
190 return None
191 return default_template
192
193
194
195 try:
196 name, ver = default_template_name.split(u' - ')
197 except:
198
199 _log.exception('problem splitting Gnuplot script template name [%s]', default_template_name)
200 gmDispatcher.send(signal = 'statustext', msg = _('Problem loading Gnuplot script template.'), beep = True)
201 return None
202
203 default_template = gmForms.get_form_template(name_long = name, external_version = ver)
204 if default_template is None:
205 default_template = configure_default_gnuplot_template(parent = parent)
206
207 if default_template is None:
208 gmGuiHelpers.gm_show_error (
209 aMessage = _('Cannot load default Gnuplot script template [%s - %s]') % (name, ver),
210 aTitle = _('Plotting test results')
211 )
212 return None
213
214 return default_template
215
216
217 -def plot_measurements(parent=None, tests=None, format=None, show_year = True, use_default_template=False):
241
242
243 -def plot_adjacent_measurements(parent=None, test=None, format=None, show_year=True, plot_singular_result=True, use_default_template=False):
244
245 earlier, later = test.get_adjacent_results(desired_earlier_results = 2, desired_later_results = 2)
246 results2plot = []
247 if earlier is not None:
248 results2plot.extend(earlier)
249 results2plot.append(test)
250 if later is not None:
251 results2plot.extend(later)
252 if len(results2plot) == 1:
253 if not plot_singular_result:
254 return
255 plot_measurements (
256 parent = parent,
257 tests = results2plot,
258 format = format,
259 show_year = show_year,
260 use_default_template = use_default_template
261 )
262
263
264
265
266
267
268
269
270
271
272
274 """A grid class for displaying measurment results.
275
276 - does NOT listen to the currently active patient
277 - thereby it can display any patient at any time
278 """
279
280
281
282
283
284
286
287 wx.grid.Grid.__init__(self, *args, **kwargs)
288
289 self.__patient = None
290 self.__cell_data = {}
291 self.__row_label_data = []
292
293 self.__prev_row = None
294 self.__prev_col = None
295 self.__prev_label_row = None
296 self.__date_format = str((_('lab_grid_date_format::%Y\n%b %d')).lstrip('lab_grid_date_format::'))
297
298 self.__init_ui()
299 self.__register_events()
300
301
302
304 if not self.IsSelection():
305 gmDispatcher.send(signal = u'statustext', msg = _('No results selected for deletion.'))
306 return True
307
308 selected_cells = self.get_selected_cells()
309 if len(selected_cells) > 20:
310 results = None
311 msg = _(
312 'There are %s results marked for deletion.\n'
313 '\n'
314 'Are you sure you want to delete these results ?'
315 ) % len(selected_cells)
316 else:
317 results = self.__cells_to_data(cells = selected_cells, exclude_multi_cells = False)
318 txt = u'\n'.join([ u'%s %s (%s): %s %s%s' % (
319 r['clin_when'].strftime('%x %H:%M').decode(gmI18N.get_encoding()),
320 r['unified_abbrev'],
321 r['unified_name'],
322 r['unified_val'],
323 r['val_unit'],
324 gmTools.coalesce(r['abnormality_indicator'], u'', u' (%s)')
325 ) for r in results
326 ])
327 msg = _(
328 'The following results are marked for deletion:\n'
329 '\n'
330 '%s\n'
331 '\n'
332 'Are you sure you want to delete these results ?'
333 ) % txt
334
335 dlg = gmGuiHelpers.c2ButtonQuestionDlg (
336 self,
337 -1,
338 caption = _('Deleting test results'),
339 question = msg,
340 button_defs = [
341 {'label': _('Delete'), 'tooltip': _('Yes, delete all the results.'), 'default': False},
342 {'label': _('Cancel'), 'tooltip': _('No, do NOT delete any results.'), 'default': True}
343 ]
344 )
345 decision = dlg.ShowModal()
346
347 if decision == wx.ID_YES:
348 if results is None:
349 results = self.__cells_to_data(cells = selected_cells, exclude_multi_cells = False)
350 for result in results:
351 gmPathLab.delete_test_result(result)
352
354 if not self.IsSelection():
355 gmDispatcher.send(signal = u'statustext', msg = _('Cannot sign results. No results selected.'))
356 return True
357
358 selected_cells = self.get_selected_cells()
359 if len(selected_cells) > 10:
360 test_count = len(selected_cells)
361 tests = None
362 else:
363 test_count = None
364 tests = self.__cells_to_data(cells = selected_cells, exclude_multi_cells = False)
365 if len(tests) == 0:
366 return True
367
368 dlg = cMeasurementsReviewDlg (
369 self,
370 -1,
371 tests = tests,
372 test_count = test_count
373 )
374 decision = dlg.ShowModal()
375
376 if decision == wx.ID_APPLY:
377 wx.BeginBusyCursor()
378
379 if dlg._RBTN_confirm_abnormal.GetValue():
380 abnormal = None
381 elif dlg._RBTN_results_normal.GetValue():
382 abnormal = False
383 else:
384 abnormal = True
385
386 if dlg._RBTN_confirm_relevance.GetValue():
387 relevant = None
388 elif dlg._RBTN_results_not_relevant.GetValue():
389 relevant = False
390 else:
391 relevant = True
392
393 if tests is None:
394 tests = self.__cells_to_data(cells = selected_cells, exclude_multi_cells = False)
395
396 comment = None
397 if len(tests) == 1:
398 comment = dlg._TCTRL_comment.GetValue()
399
400 for test in tests:
401 test.set_review (
402 technically_abnormal = abnormal,
403 clinically_relevant = relevant,
404 comment = comment,
405 make_me_responsible = dlg._CHBOX_responsible.IsChecked()
406 )
407
408 wx.EndBusyCursor()
409
410 dlg.Destroy()
411
413
414 if not self.IsSelection():
415 gmDispatcher.send(signal = u'statustext', msg = _('Cannot plot results. No results selected.'))
416 return True
417
418 tests = self.__cells_to_data (
419 cells = self.get_selected_cells(),
420 exclude_multi_cells = False,
421 auto_include_multi_cells = True
422 )
423
424 plot_measurements(parent = self, tests = tests)
425
427
428 sel_block_top_left = self.GetSelectionBlockTopLeft()
429 sel_block_bottom_right = self.GetSelectionBlockBottomRight()
430 sel_cols = self.GetSelectedCols()
431 sel_rows = self.GetSelectedRows()
432
433 selected_cells = []
434
435
436 selected_cells += self.GetSelectedCells()
437
438
439 selected_cells += list (
440 (row, col)
441 for row in sel_rows
442 for col in xrange(self.GetNumberCols())
443 )
444
445
446 selected_cells += list (
447 (row, col)
448 for row in xrange(self.GetNumberRows())
449 for col in sel_cols
450 )
451
452
453 for top_left, bottom_right in zip(self.GetSelectionBlockTopLeft(), self.GetSelectionBlockBottomRight()):
454 selected_cells += [
455 (row, col)
456 for row in xrange(top_left[0], bottom_right[0] + 1)
457 for col in xrange(top_left[1], bottom_right[1] + 1)
458 ]
459
460 return set(selected_cells)
461
462 - def select_cells(self, unsigned_only=False, accountables_only=False, keep_preselections=False):
463 """Select a range of cells according to criteria.
464
465 unsigned_only: include only those which are not signed at all yet
466 accountable_only: include only those for which the current user is responsible
467 keep_preselections: broaden (rather than replace) the range of selected cells
468
469 Combinations are powerful !
470 """
471 wx.BeginBusyCursor()
472 self.BeginBatch()
473
474 if not keep_preselections:
475 self.ClearSelection()
476
477 for col_idx in self.__cell_data.keys():
478 for row_idx in self.__cell_data[col_idx].keys():
479
480
481 do_not_include = False
482 for result in self.__cell_data[col_idx][row_idx]:
483 if unsigned_only:
484 if result['reviewed']:
485 do_not_include = True
486 break
487 if accountables_only:
488 if not result['you_are_responsible']:
489 do_not_include = True
490 break
491 if do_not_include:
492 continue
493
494 self.SelectBlock(row_idx, col_idx, row_idx, col_idx, addToSelected = True)
495
496 self.EndBatch()
497 wx.EndBusyCursor()
498
500
501 self.empty_grid()
502 if self.__patient is None:
503 return
504
505 emr = self.__patient.get_emr()
506
507 self.__row_label_data = emr.get_test_types_for_results()
508 test_type_labels = [ u'%s%s' % (
509 gmTools.bool2subst(test['is_fake_meta_type'], u'', gmTools.u_sum, u''),
510 test['unified_abbrev']
511 ) for test in self.__row_label_data
512 ]
513 if len(test_type_labels) == 0:
514 return
515
516 test_date_labels = [ date[0].strftime(self.__date_format) for date in emr.get_dates_for_results() ]
517 results = emr.get_test_results_by_date()
518
519 self.BeginBatch()
520
521
522 self.AppendRows(numRows = len(test_type_labels))
523 for row_idx in range(len(test_type_labels)):
524 self.SetRowLabelValue(row_idx, test_type_labels[row_idx])
525
526
527 self.AppendCols(numCols = len(test_date_labels))
528 for date_idx in range(len(test_date_labels)):
529 self.SetColLabelValue(date_idx, test_date_labels[date_idx])
530
531
532 for result in results:
533 row = test_type_labels.index(u'%s%s' % (
534 gmTools.bool2subst(result['is_fake_meta_type'], u'', gmTools.u_sum, u''),
535 result['unified_abbrev']
536 ))
537 col = test_date_labels.index(result['clin_when'].strftime(self.__date_format))
538
539 try:
540 self.__cell_data[col]
541 except KeyError:
542 self.__cell_data[col] = {}
543
544
545 if self.__cell_data[col].has_key(row):
546 self.__cell_data[col][row].append(result)
547 self.__cell_data[col][row].sort(key = lambda x: x['clin_when'], reverse = True)
548 else:
549 self.__cell_data[col][row] = [result]
550
551
552 vals2display = []
553 for sub_result in self.__cell_data[col][row]:
554
555
556 ind = gmTools.coalesce(sub_result['abnormality_indicator'], u'').strip()
557 if ind != u'':
558 lab_abnormality_indicator = u' (%s)' % ind[:3]
559 else:
560 lab_abnormality_indicator = u''
561
562 if sub_result['is_technically_abnormal'] is None:
563 abnormality_indicator = lab_abnormality_indicator
564
565 elif sub_result['is_technically_abnormal'] is False:
566 abnormality_indicator = u''
567
568 else:
569
570 if lab_abnormality_indicator == u'':
571
572 abnormality_indicator = u' (%s)' % gmTools.u_plus_minus
573
574 else:
575 abnormality_indicator = lab_abnormality_indicator
576
577
578
579 sub_result_relevant = sub_result['is_clinically_relevant']
580 if sub_result_relevant is None:
581
582 sub_result_relevant = False
583
584 missing_review = False
585
586
587 if not sub_result['reviewed']:
588 missing_review = True
589
590 else:
591
592 if sub_result['you_are_responsible'] and not sub_result['review_by_you']:
593 missing_review = True
594
595
596 if len(sub_result['unified_val']) > 8:
597 tmp = u'%.7s%s' % (sub_result['unified_val'][:7], gmTools.u_ellipsis)
598 else:
599 tmp = u'%.8s' % sub_result['unified_val'][:8]
600
601
602 tmp = u'%s%.6s' % (tmp, abnormality_indicator)
603
604
605 has_sub_result_comment = gmTools.coalesce (
606 gmTools.coalesce(sub_result['note_test_org'], sub_result['comment']),
607 u''
608 ).strip() != u''
609 if has_sub_result_comment:
610 tmp = u'%s %s' % (tmp, gmTools.u_ellipsis)
611
612
613 if missing_review:
614 tmp = u'%s %s' % (tmp, gmTools.u_writing_hand)
615
616
617 if len(self.__cell_data[col][row]) > 1:
618 tmp = u'%s %s' % (sub_result['clin_when'].strftime('%H:%M'), tmp)
619
620 vals2display.append(tmp)
621
622 self.SetCellValue(row, col, u'\n'.join(vals2display))
623 self.SetCellAlignment(row, col, horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE)
624
625
626
627
628 if sub_result_relevant:
629 font = self.GetCellFont(row, col)
630 self.SetCellTextColour(row, col, 'firebrick')
631 font.SetWeight(wx.FONTWEIGHT_BOLD)
632 self.SetCellFont(row, col, font)
633
634
635 self.AutoSize()
636 self.EndBatch()
637 return
638
640 self.BeginBatch()
641 self.ClearGrid()
642
643
644 if self.GetNumberRows() > 0:
645 self.DeleteRows(pos = 0, numRows = self.GetNumberRows())
646 if self.GetNumberCols() > 0:
647 self.DeleteCols(pos = 0, numCols = self.GetNumberCols())
648 self.EndBatch()
649 self.__cell_data = {}
650 self.__row_label_data = []
651
664
912
913
914
916 self.CreateGrid(0, 1)
917 self.EnableEditing(0)
918 self.EnableDragGridSize(1)
919
920
921
922
923
924 self.SetRowLabelSize(150)
925 self.SetRowLabelAlignment(horiz = wx.ALIGN_LEFT, vert = wx.ALIGN_CENTRE)
926
927
928 dbcfg = gmCfg.cCfgSQL()
929 url = dbcfg.get2 (
930 option = u'external.urls.measurements_encyclopedia',
931 workplace = gmSurgery.gmCurrentPractice().active_workplace,
932 bias = 'user',
933 default = u'http://www.laborlexikon.de'
934 )
935
936 self.__WIN_corner = self.GetGridCornerLabelWindow()
937
938 LNK_lab = wx.lib.hyperlink.HyperLinkCtrl (
939 self.__WIN_corner,
940 -1,
941 label = _('Measurement type'),
942 style = wx.HL_DEFAULT_STYLE
943 )
944 LNK_lab.SetURL(url)
945 LNK_lab.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BACKGROUND))
946 LNK_lab.SetToolTipString(_(
947 'Navigate to an encyclopedia of measurements\n'
948 'and test methods on the web.\n'
949 '\n'
950 ' <%s>'
951 ) % url)
952
953 SZR_inner = wx.BoxSizer(wx.HORIZONTAL)
954 SZR_inner.Add((20, 20), 1, wx.EXPAND, 0)
955 SZR_inner.Add(LNK_lab, 0, wx.ALIGN_CENTER_VERTICAL, 0)
956 SZR_inner.Add((20, 20), 1, wx.EXPAND, 0)
957
958 SZR_corner = wx.BoxSizer(wx.VERTICAL)
959 SZR_corner.Add((20, 20), 1, wx.EXPAND, 0)
960 SZR_corner.AddWindow(SZR_inner, 0, wx.EXPAND)
961 SZR_corner.Add((20, 20), 1, wx.EXPAND, 0)
962
963 self.__WIN_corner.SetSizer(SZR_corner)
964 SZR_corner.Fit(self.__WIN_corner)
965
967 self.__WIN_corner.Layout()
968
969 - def __cells_to_data(self, cells=None, exclude_multi_cells=False, auto_include_multi_cells=False):
970 """List of <cells> must be in row / col order."""
971 data = []
972 for row, col in cells:
973 try:
974
975 data_list = self.__cell_data[col][row]
976 except KeyError:
977 continue
978
979 if len(data_list) == 1:
980 data.append(data_list[0])
981 continue
982
983 if exclude_multi_cells:
984 gmDispatcher.send(signal = u'statustext', msg = _('Excluding multi-result field from further processing.'))
985 continue
986
987 if auto_include_multi_cells:
988 data.extend(data_list)
989 continue
990
991 data_to_include = self.__get_choices_from_multi_cell(cell_data = data_list)
992 if data_to_include is None:
993 continue
994 data.extend(data_to_include)
995
996 return data
997
999 data = gmListWidgets.get_choices_from_list (
1000 parent = self,
1001 msg = _(
1002 'Your selection includes a field with multiple results.\n'
1003 '\n'
1004 'Please select the individual results you want to work on:'
1005 ),
1006 caption = _('Selecting test results'),
1007 choices = [ [d['clin_when'], u'%s: %s' % (d['abbrev_tt'], d['name_tt']), d['unified_val']] for d in cell_data ],
1008 columns = [ _('Date / Time'), _('Test'), _('Result') ],
1009 data = cell_data,
1010 single_selection = single_selection
1011 )
1012 return data
1013
1014
1015
1017
1018 self.GetGridWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_cells)
1019 self.GetGridRowLabelWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_row_labels)
1020
1021
1022
1023 self.Bind(wx.EVT_SIZE, self.__resize_corner_window)
1024
1025
1026 self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.__on_cell_left_dclicked)
1027
1029 col = evt.GetCol()
1030 row = evt.GetRow()
1031
1032
1033 try:
1034 self.__cell_data[col][row]
1035 except KeyError:
1036
1037
1038 return
1039
1040 if len(self.__cell_data[col][row]) > 1:
1041 data = self.__get_choices_from_multi_cell(cell_data = self.__cell_data[col][row], single_selection = True)
1042 else:
1043 data = self.__cell_data[col][row][0]
1044
1045 if data is None:
1046 return
1047
1048 edit_measurement(parent = self, measurement = data, single_entry = True)
1049
1050
1051
1052
1053
1054
1055
1057
1058
1059
1060 x, y = self.CalcUnscrolledPosition(evt.GetX(), evt.GetY())
1061
1062 row = self.YToRow(y)
1063
1064 if self.__prev_label_row == row:
1065 return
1066
1067 self.__prev_label_row == row
1068
1069 evt.GetEventObject().SetToolTipString(self.get_row_tooltip(row = row))
1070
1071
1072
1073
1074
1075
1076
1077
1079 """Calculate where the mouse is and set the tooltip dynamically."""
1080
1081
1082
1083 x, y = self.CalcUnscrolledPosition(evt.GetX(), evt.GetY())
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097 row, col = self.XYToCell(x, y)
1098
1099 if (row == self.__prev_row) and (col == self.__prev_col):
1100 return
1101
1102 self.__prev_row = row
1103 self.__prev_col = col
1104
1105 evt.GetEventObject().SetToolTipString(self.get_cell_tooltip(col=col, row=row))
1106
1107
1108
1112
1113 patient = property(lambda x:x, _set_patient)
1114
1115 from Gnumed.wxGladeWidgets import wxgMeasurementsPnl
1116
1117 -class cMeasurementsPnl(wxgMeasurementsPnl.wxgMeasurementsPnl, gmRegetMixin.cRegetOnPaintMixin):
1118 """Panel holding a grid with lab data. Used as notebook page."""
1119
1126
1127
1128
1130 gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._on_pre_patient_selection)
1131 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._on_post_patient_selection)
1132 gmDispatcher.connect(signal = u'test_result_mod_db', receiver = self._schedule_data_reget)
1133 gmDispatcher.connect(signal = u'reviewed_test_results_mod_db', receiver = self._schedule_data_reget)
1134
1136 wx.CallAfter(self.__on_post_patient_selection)
1137
1139 self._schedule_data_reget()
1140
1142 wx.CallAfter(self.__on_pre_patient_selection)
1143
1146
1149
1152
1158
1161
1164
1167
1168
1169
1171 self.__action_button_popup = wx.Menu(title = _('Perform on selected results:'))
1172
1173 menu_id = wx.NewId()
1174 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('Review and &sign')))
1175 wx.EVT_MENU(self.__action_button_popup, menu_id, self.__on_sign_current_selection)
1176
1177 menu_id = wx.NewId()
1178 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('Plot')))
1179 wx.EVT_MENU(self.__action_button_popup, menu_id, self.__on_plot_current_selection)
1180
1181 menu_id = wx.NewId()
1182 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('Export to &file')))
1183
1184 self.__action_button_popup.Enable(id = menu_id, enable = False)
1185
1186 menu_id = wx.NewId()
1187 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('Export to &clipboard')))
1188
1189 self.__action_button_popup.Enable(id = menu_id, enable = False)
1190
1191 menu_id = wx.NewId()
1192 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('&Delete')))
1193 wx.EVT_MENU(self.__action_button_popup, menu_id, self.__on_delete_current_selection)
1194
1195
1196
1197
1198
1199
1200
1209
1210
1211
1212 from Gnumed.wxGladeWidgets import wxgMeasurementsReviewDlg
1213
1215
1217
1218 try:
1219 tests = kwargs['tests']
1220 del kwargs['tests']
1221 test_count = len(tests)
1222 try: del kwargs['test_count']
1223 except KeyError: pass
1224 except KeyError:
1225 tests = None
1226 test_count = kwargs['test_count']
1227 del kwargs['test_count']
1228
1229 wxgMeasurementsReviewDlg.wxgMeasurementsReviewDlg.__init__(self, *args, **kwargs)
1230
1231 if tests is None:
1232 msg = _('%s results selected. Too many to list individually.') % test_count
1233 else:
1234 msg = ' // '.join (
1235 [ u'%s: %s %s (%s)' % (
1236 t['unified_abbrev'],
1237 t['unified_val'],
1238 t['val_unit'],
1239 t['clin_when'].strftime('%x').decode(gmI18N.get_encoding())
1240 ) for t in tests
1241 ]
1242 )
1243
1244 self._LBL_tests.SetLabel(msg)
1245
1246 if test_count == 1:
1247 self._TCTRL_comment.Enable(True)
1248 self._TCTRL_comment.SetValue(gmTools.coalesce(tests[0]['review_comment'], u''))
1249 if tests[0]['you_are_responsible']:
1250 self._CHBOX_responsible.Enable(False)
1251
1252 self.Fit()
1253
1254
1255
1261
1262 from Gnumed.wxGladeWidgets import wxgMeasurementEditAreaPnl
1263
1264 -class cMeasurementEditAreaPnl(wxgMeasurementEditAreaPnl.wxgMeasurementEditAreaPnl, gmEditArea.cGenericEditAreaMixin):
1265 """This edit area saves *new* measurements into the active patient only."""
1266
1283
1284
1285
1287 self._PRW_test.SetText(u'', None, True)
1288 self.__refresh_loinc_info()
1289 self.__refresh_previous_value()
1290 self.__update_units_context()
1291 self._TCTRL_result.SetValue(u'')
1292 self._PRW_units.SetText(u'', None, True)
1293 self._PRW_abnormality_indicator.SetText(u'', None, True)
1294 if self.__default_date is None:
1295 self._DPRW_evaluated.SetData(data = pyDT.datetime.now(tz = gmDateTime.gmCurrentLocalTimezone))
1296 else:
1297 self._DPRW_evaluated.SetData(data = None)
1298 self._TCTRL_note_test_org.SetValue(u'')
1299 self._PRW_intended_reviewer.SetData(gmStaff.gmCurrentProvider()['pk_staff'])
1300 self._PRW_problem.SetData()
1301 self._TCTRL_narrative.SetValue(u'')
1302 self._CHBOX_review.SetValue(False)
1303 self._CHBOX_abnormal.SetValue(False)
1304 self._CHBOX_relevant.SetValue(False)
1305 self._CHBOX_abnormal.Enable(False)
1306 self._CHBOX_relevant.Enable(False)
1307 self._TCTRL_review_comment.SetValue(u'')
1308 self._TCTRL_normal_min.SetValue(u'')
1309 self._TCTRL_normal_max.SetValue(u'')
1310 self._TCTRL_normal_range.SetValue(u'')
1311 self._TCTRL_target_min.SetValue(u'')
1312 self._TCTRL_target_max.SetValue(u'')
1313 self._TCTRL_target_range.SetValue(u'')
1314 self._TCTRL_norm_ref_group.SetValue(u'')
1315
1316 self._PRW_test.SetFocus()
1317
1319 self._PRW_test.SetData(data = self.data['pk_test_type'])
1320 self.__refresh_loinc_info()
1321 self.__refresh_previous_value()
1322 self.__update_units_context()
1323 self._TCTRL_result.SetValue(self.data['unified_val'])
1324 self._PRW_units.SetText(self.data['val_unit'], self.data['val_unit'], True)
1325 self._PRW_abnormality_indicator.SetText (
1326 gmTools.coalesce(self.data['abnormality_indicator'], u''),
1327 gmTools.coalesce(self.data['abnormality_indicator'], u''),
1328 True
1329 )
1330 self._DPRW_evaluated.SetData(data = self.data['clin_when'])
1331 self._TCTRL_note_test_org.SetValue(gmTools.coalesce(self.data['note_test_org'], u''))
1332 self._PRW_intended_reviewer.SetData(self.data['pk_intended_reviewer'])
1333 self._PRW_problem.SetData(self.data['pk_episode'])
1334 self._TCTRL_narrative.SetValue(gmTools.coalesce(self.data['comment'], u''))
1335 self._CHBOX_review.SetValue(False)
1336 self._CHBOX_abnormal.SetValue(gmTools.coalesce(self.data['is_technically_abnormal'], False))
1337 self._CHBOX_relevant.SetValue(gmTools.coalesce(self.data['is_clinically_relevant'], False))
1338 self._CHBOX_abnormal.Enable(False)
1339 self._CHBOX_relevant.Enable(False)
1340 self._TCTRL_review_comment.SetValue(gmTools.coalesce(self.data['review_comment'], u''))
1341 self._TCTRL_normal_min.SetValue(unicode(gmTools.coalesce(self.data['val_normal_min'], u'')))
1342 self._TCTRL_normal_max.SetValue(unicode(gmTools.coalesce(self.data['val_normal_max'], u'')))
1343 self._TCTRL_normal_range.SetValue(gmTools.coalesce(self.data['val_normal_range'], u''))
1344 self._TCTRL_target_min.SetValue(unicode(gmTools.coalesce(self.data['val_target_min'], u'')))
1345 self._TCTRL_target_max.SetValue(unicode(gmTools.coalesce(self.data['val_target_max'], u'')))
1346 self._TCTRL_target_range.SetValue(gmTools.coalesce(self.data['val_target_range'], u''))
1347 self._TCTRL_norm_ref_group.SetValue(gmTools.coalesce(self.data['norm_ref_group'], u''))
1348
1349 self._TCTRL_result.SetFocus()
1350
1352 self._refresh_from_existing()
1353
1354 self._PRW_test.SetText(u'', None, True)
1355 self.__refresh_loinc_info()
1356 self.__refresh_previous_value()
1357 self.__update_units_context()
1358 self._TCTRL_result.SetValue(u'')
1359 self._PRW_units.SetText(u'', None, True)
1360 self._PRW_abnormality_indicator.SetText(u'', None, True)
1361
1362 self._TCTRL_note_test_org.SetValue(u'')
1363 self._TCTRL_narrative.SetValue(u'')
1364 self._CHBOX_review.SetValue(False)
1365 self._CHBOX_abnormal.SetValue(False)
1366 self._CHBOX_relevant.SetValue(False)
1367 self._CHBOX_abnormal.Enable(False)
1368 self._CHBOX_relevant.Enable(False)
1369 self._TCTRL_review_comment.SetValue(u'')
1370 self._TCTRL_normal_min.SetValue(u'')
1371 self._TCTRL_normal_max.SetValue(u'')
1372 self._TCTRL_normal_range.SetValue(u'')
1373 self._TCTRL_target_min.SetValue(u'')
1374 self._TCTRL_target_max.SetValue(u'')
1375 self._TCTRL_target_range.SetValue(u'')
1376 self._TCTRL_norm_ref_group.SetValue(u'')
1377
1378 self._PRW_test.SetFocus()
1379
1381
1382 validity = True
1383
1384 if not self._DPRW_evaluated.is_valid_timestamp():
1385 self._DPRW_evaluated.display_as_valid(False)
1386 validity = False
1387 else:
1388 self._DPRW_evaluated.display_as_valid(True)
1389
1390 if self._TCTRL_result.GetValue().strip() == u'':
1391 validity = False
1392 self.display_ctrl_as_valid(self._TCTRL_result, False)
1393 else:
1394 self.display_ctrl_as_valid(self._TCTRL_result, True)
1395
1396 if self._PRW_problem.GetValue().strip() == u'':
1397 self._PRW_problem.display_as_valid(False)
1398 validity = False
1399 else:
1400 self._PRW_problem.display_as_valid(True)
1401
1402 if self._PRW_test.GetValue().strip() == u'':
1403 self._PRW_test.display_as_valid(False)
1404 validity = False
1405 else:
1406 self._PRW_test.display_as_valid(True)
1407
1408 if self._PRW_intended_reviewer.GetData() is None:
1409 self._PRW_intended_reviewer.display_as_valid(False)
1410 validity = False
1411 else:
1412 self._PRW_intended_reviewer.display_as_valid(True)
1413
1414 if self._PRW_units.GetValue().strip() == u'':
1415 self._PRW_units.display_as_valid(False)
1416 validity = False
1417 else:
1418 self._PRW_units.display_as_valid(True)
1419
1420 ctrls = [self._TCTRL_normal_min, self._TCTRL_normal_max, self._TCTRL_target_min, self._TCTRL_target_max]
1421 for widget in ctrls:
1422 val = widget.GetValue().strip()
1423 if val == u'':
1424 continue
1425 try:
1426 decimal.Decimal(val.replace(',', u'.', 1))
1427 self.display_ctrl_as_valid(widget, True)
1428 except:
1429 validity = False
1430 self.display_ctrl_as_valid(widget, False)
1431
1432 if validity is False:
1433 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save result. Invalid or missing essential input.'))
1434
1435 return validity
1436
1438
1439 emr = gmPerson.gmCurrentPatient().get_emr()
1440
1441 success, result = gmTools.input2decimal(self._TCTRL_result.GetValue())
1442 if success:
1443 v_num = result
1444 v_al = None
1445 else:
1446 v_al = self._TCTRL_result.GetValue().strip()
1447 v_num = None
1448
1449 pk_type = self._PRW_test.GetData()
1450 if pk_type is None:
1451 tt = gmPathLab.create_measurement_type (
1452 lab = None,
1453 abbrev = self._PRW_test.GetValue().strip(),
1454 name = self._PRW_test.GetValue().strip(),
1455 unit = gmTools.coalesce(self._PRW_units.GetData(), self._PRW_units.GetValue()).strip()
1456 )
1457 pk_type = tt['pk_test_type']
1458
1459 tr = emr.add_test_result (
1460 episode = self._PRW_problem.GetData(can_create=True, is_open=False),
1461 type = pk_type,
1462 intended_reviewer = self._PRW_intended_reviewer.GetData(),
1463 val_num = v_num,
1464 val_alpha = v_al,
1465 unit = self._PRW_units.GetValue()
1466 )
1467
1468 tr['clin_when'] = self._DPRW_evaluated.GetData().get_pydt()
1469
1470 ctrls = [
1471 ('abnormality_indicator', self._PRW_abnormality_indicator),
1472 ('note_test_org', self._TCTRL_note_test_org),
1473 ('comment', self._TCTRL_narrative),
1474 ('val_normal_range', self._TCTRL_normal_range),
1475 ('val_target_range', self._TCTRL_target_range),
1476 ('norm_ref_group', self._TCTRL_norm_ref_group)
1477 ]
1478 for field, widget in ctrls:
1479 tr[field] = widget.GetValue().strip()
1480
1481 ctrls = [
1482 ('val_normal_min', self._TCTRL_normal_min),
1483 ('val_normal_max', self._TCTRL_normal_max),
1484 ('val_target_min', self._TCTRL_target_min),
1485 ('val_target_max', self._TCTRL_target_max)
1486 ]
1487 for field, widget in ctrls:
1488 val = widget.GetValue().strip()
1489 if val == u'':
1490 tr[field] = None
1491 else:
1492 tr[field] = decimal.Decimal(val.replace(',', u'.', 1))
1493
1494 tr.save_payload()
1495
1496 if self._CHBOX_review.GetValue() is True:
1497 tr.set_review (
1498 technically_abnormal = self._CHBOX_abnormal.GetValue(),
1499 clinically_relevant = self._CHBOX_relevant.GetValue(),
1500 comment = gmTools.none_if(self._TCTRL_review_comment.GetValue().strip(), u''),
1501 make_me_responsible = False
1502 )
1503
1504 self.data = tr
1505
1506 wx.CallAfter (
1507 plot_adjacent_measurements,
1508 test = self.data,
1509 plot_singular_result = False,
1510 use_default_template = True
1511 )
1512
1513 return True
1514
1516
1517 success, result = gmTools.input2decimal(self._TCTRL_result.GetValue())
1518 if success:
1519 v_num = result
1520 v_al = None
1521 else:
1522 v_num = None
1523 v_al = self._TCTRL_result.GetValue().strip()
1524
1525 pk_type = self._PRW_test.GetData()
1526 if pk_type is None:
1527 tt = gmPathLab.create_measurement_type (
1528 lab = None,
1529 abbrev = self._PRW_test.GetValue().strip(),
1530 name = self._PRW_test.GetValue().strip(),
1531 unit = gmTools.none_if(self._PRW_units.GetValue().strip(), u'')
1532 )
1533 pk_type = tt['pk_test_type']
1534
1535 tr = self.data
1536
1537 tr['pk_episode'] = self._PRW_problem.GetData(can_create=True, is_open=False)
1538 tr['pk_test_type'] = pk_type
1539 tr['pk_intended_reviewer'] = self._PRW_intended_reviewer.GetData()
1540 tr['val_num'] = v_num
1541 tr['val_alpha'] = v_al
1542 tr['val_unit'] = gmTools.coalesce(self._PRW_units.GetData(), self._PRW_units.GetValue()).strip()
1543 tr['clin_when'] = self._DPRW_evaluated.GetData().get_pydt()
1544
1545 ctrls = [
1546 ('abnormality_indicator', self._PRW_abnormality_indicator),
1547 ('note_test_org', self._TCTRL_note_test_org),
1548 ('comment', self._TCTRL_narrative),
1549 ('val_normal_range', self._TCTRL_normal_range),
1550 ('val_target_range', self._TCTRL_target_range),
1551 ('norm_ref_group', self._TCTRL_norm_ref_group)
1552 ]
1553 for field, widget in ctrls:
1554 tr[field] = widget.GetValue().strip()
1555
1556 ctrls = [
1557 ('val_normal_min', self._TCTRL_normal_min),
1558 ('val_normal_max', self._TCTRL_normal_max),
1559 ('val_target_min', self._TCTRL_target_min),
1560 ('val_target_max', self._TCTRL_target_max)
1561 ]
1562 for field, widget in ctrls:
1563 val = widget.GetValue().strip()
1564 if val == u'':
1565 tr[field] = None
1566 else:
1567 tr[field] = decimal.Decimal(val.replace(',', u'.', 1))
1568
1569 tr.save_payload()
1570
1571 if self._CHBOX_review.GetValue() is True:
1572 tr.set_review (
1573 technically_abnormal = self._CHBOX_abnormal.GetValue(),
1574 clinically_relevant = self._CHBOX_relevant.GetValue(),
1575 comment = gmTools.none_if(self._TCTRL_review_comment.GetValue().strip(), u''),
1576 make_me_responsible = False
1577 )
1578
1579 wx.CallAfter (
1580 plot_adjacent_measurements,
1581 test = self.data,
1582 plot_singular_result = False,
1583 use_default_template = True
1584 )
1585
1586 return True
1587
1588
1589
1593
1595 self.__refresh_loinc_info()
1596 self.__refresh_previous_value()
1597 self.__update_units_context()
1598
1600
1601 if not self._CHBOX_review.GetValue():
1602 self._CHBOX_abnormal.SetValue(self._PRW_abnormality_indicator.GetValue().strip() != u'')
1603
1605 self._CHBOX_abnormal.Enable(self._CHBOX_review.GetValue())
1606 self._CHBOX_relevant.Enable(self._CHBOX_review.GetValue())
1607 self._TCTRL_review_comment.Enable(self._CHBOX_review.GetValue())
1608
1625
1626
1627
1629
1630 self._PRW_units.unset_context(context = u'loinc')
1631
1632 tt = self._PRW_test.GetData(as_instance = True)
1633
1634 if tt is None:
1635 self._PRW_units.unset_context(context = u'pk_type')
1636 if self._PRW_test.GetValue().strip() == u'':
1637 self._PRW_units.unset_context(context = u'test_name')
1638 else:
1639 self._PRW_units.set_context(context = u'test_name', val = self._PRW_test.GetValue().strip())
1640 return
1641
1642 self._PRW_units.set_context(context = u'pk_type', val = tt['pk_test_type'])
1643 self._PRW_units.set_context(context = u'test_name', val = tt['name'])
1644
1645 if tt['loinc'] is None:
1646 return
1647
1648 self._PRW_units.set_context(context = u'loinc', val = tt['loinc'])
1649
1651
1652 self._TCTRL_loinc.SetValue(u'')
1653
1654 if self._PRW_test.GetData() is None:
1655 return
1656
1657 tt = self._PRW_test.GetData(as_instance = True)
1658
1659 if tt['loinc'] is None:
1660 return
1661
1662 info = gmLOINC.loinc2term(loinc = tt['loinc'])
1663 if len(info) == 0:
1664 self._TCTRL_loinc.SetValue(u'')
1665 return
1666
1667 self._TCTRL_loinc.SetValue(u'%s: %s' % (tt['loinc'], info[0]))
1668
1691
1692
1693
1716
1717 def delete(measurement_type):
1718 if measurement_type.in_use:
1719 gmDispatcher.send (
1720 signal = 'statustext',
1721 beep = True,
1722 msg = _('Cannot delete measurement type [%s (%s)] because it is in use.') % (measurement_type['name'], measurement_type['abbrev'])
1723 )
1724 return False
1725 gmPathLab.delete_measurement_type(measurement_type = measurement_type['pk_test_type'])
1726 return True
1727
1728 def get_tooltip(test_type):
1729 return test_type.format()
1730
1731 def refresh(lctrl):
1732 mtypes = gmPathLab.get_measurement_types(order_by = 'name, abbrev')
1733 items = [ [
1734 m['abbrev'],
1735 m['name'],
1736 gmTools.coalesce(m['conversion_unit'], u''),
1737 gmTools.coalesce(m['loinc'], u''),
1738 gmTools.coalesce(m['comment_type'], u''),
1739 gmTools.coalesce(m['name_org'], u'?'),
1740 gmTools.coalesce(m['comment_org'], u''),
1741 m['pk_test_type']
1742 ] for m in mtypes ]
1743 lctrl.set_string_items(items)
1744 lctrl.set_data(mtypes)
1745
1746 msg = _(
1747 '\n'
1748 'These are the measurement types currently defined in GNUmed.\n'
1749 '\n'
1750 )
1751
1752 gmListWidgets.get_choices_from_list (
1753 parent = parent,
1754 msg = msg,
1755 caption = _('Showing measurement types.'),
1756 columns = [ _('Abbrev'), _('Name'), _('Unit'), _('LOINC'), _('Comment'), _('Org'), _('Comment'), u'#' ],
1757 single_selection = True,
1758 refresh_callback = refresh,
1759 edit_callback = edit,
1760 new_callback = edit,
1761 delete_callback = delete,
1762 list_tooltip_callback = get_tooltip
1763 )
1764
1766
1768
1769 query = u"""
1770 SELECT DISTINCT ON (field_label)
1771 pk_test_type AS data,
1772 name
1773 || ' ('
1774 || coalesce (
1775 (SELECT unit || ' @ ' || organization FROM clin.v_test_orgs c_vto WHERE c_vto.pk_test_org = c_vtt.pk_test_org),
1776 '%(in_house)s'
1777 )
1778 || ')'
1779 AS field_label,
1780 name
1781 || ' ('
1782 || abbrev || ', '
1783 || coalesce(abbrev_meta || ': ' || name_meta || ', ', '')
1784 || coalesce (
1785 (SELECT unit || ' @ ' || organization FROM clin.v_test_orgs c_vto WHERE c_vto.pk_test_org = c_vtt.pk_test_org),
1786 '%(in_house)s'
1787 )
1788 || ')'
1789 AS list_label
1790 FROM
1791 clin.v_test_types c_vtt
1792 WHERE
1793 abbrev_meta %%(fragment_condition)s
1794 OR
1795 name_meta %%(fragment_condition)s
1796 OR
1797 abbrev %%(fragment_condition)s
1798 OR
1799 name %%(fragment_condition)s
1800 ORDER BY field_label
1801 LIMIT 50""" % {'in_house': _('generic / in house lab')}
1802
1803 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
1804 mp.setThresholds(1, 2, 4)
1805 mp.word_separators = '[ \t:@]+'
1806 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
1807 self.matcher = mp
1808 self.SetToolTipString(_('Select the type of measurement.'))
1809 self.selection_only = False
1810
1816
1817 from Gnumed.wxGladeWidgets import wxgMeasurementTypeEAPnl
1818
1819 -class cMeasurementTypeEAPnl(wxgMeasurementTypeEAPnl.wxgMeasurementTypeEAPnl, gmEditArea.cGenericEditAreaMixin):
1820
1837
1838
1840
1841
1842 query = u"""
1843 select distinct on (name)
1844 pk,
1845 name
1846 from clin.test_type
1847 where
1848 name %(fragment_condition)s
1849 order by name
1850 limit 50"""
1851 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
1852 mp.setThresholds(1, 2, 4)
1853 self._PRW_name.matcher = mp
1854 self._PRW_name.selection_only = False
1855 self._PRW_name.add_callback_on_lose_focus(callback = self._on_name_lost_focus)
1856
1857
1858 query = u"""
1859 select distinct on (abbrev)
1860 pk,
1861 abbrev
1862 from clin.test_type
1863 where
1864 abbrev %(fragment_condition)s
1865 order by abbrev
1866 limit 50"""
1867 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
1868 mp.setThresholds(1, 2, 3)
1869 self._PRW_abbrev.matcher = mp
1870 self._PRW_abbrev.selection_only = False
1871
1872
1873 self._PRW_conversion_unit.selection_only = False
1874
1875
1876 query = u"""
1877 SELECT DISTINCT ON (list_label)
1878 data,
1879 field_label,
1880 list_label
1881 FROM ((
1882
1883 SELECT
1884 loinc AS data,
1885 loinc AS field_label,
1886 (loinc || ': ' || abbrev || ' (' || name || ')') AS list_label
1887 FROM clin.test_type
1888 WHERE loinc %(fragment_condition)s
1889 LIMIT 50
1890
1891 ) UNION ALL (
1892
1893 SELECT
1894 code AS data,
1895 code AS field_label,
1896 (code || ': ' || term) AS list_label
1897 FROM ref.v_coded_terms
1898 WHERE
1899 coding_system = 'LOINC'
1900 AND
1901 lang = i18n.get_curr_lang()
1902 AND
1903 (code %(fragment_condition)s
1904 OR
1905 term %(fragment_condition)s)
1906 LIMIT 50
1907
1908 ) UNION ALL (
1909
1910 SELECT
1911 code AS data,
1912 code AS field_label,
1913 (code || ': ' || term) AS list_label
1914 FROM ref.v_coded_terms
1915 WHERE
1916 coding_system = 'LOINC'
1917 AND
1918 lang = 'en_EN'
1919 AND
1920 (code %(fragment_condition)s
1921 OR
1922 term %(fragment_condition)s)
1923 LIMIT 50
1924
1925 ) UNION ALL (
1926
1927 SELECT
1928 code AS data,
1929 code AS field_label,
1930 (code || ': ' || term) AS list_label
1931 FROM ref.v_coded_terms
1932 WHERE
1933 coding_system = 'LOINC'
1934 AND
1935 (code %(fragment_condition)s
1936 OR
1937 term %(fragment_condition)s)
1938 LIMIT 50
1939 )
1940 ) AS all_known_loinc
1941
1942 ORDER BY list_label
1943 LIMIT 50"""
1944 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
1945 mp.setThresholds(1, 2, 4)
1946 self._PRW_loinc.matcher = mp
1947 self._PRW_loinc.selection_only = False
1948 self._PRW_loinc.add_callback_on_lose_focus(callback = self._on_loinc_lost_focus)
1949
1951
1952 test = self._PRW_name.GetValue().strip()
1953
1954 if test == u'':
1955 self._PRW_conversion_unit.unset_context(context = u'test_name')
1956 return
1957
1958 self._PRW_conversion_unit.set_context(context = u'test_name', val = test)
1959
1961 loinc = self._PRW_loinc.GetData()
1962
1963 if loinc is None:
1964 self._TCTRL_loinc_info.SetValue(u'')
1965 self._PRW_conversion_unit.unset_context(context = u'loinc')
1966 return
1967
1968 self._PRW_conversion_unit.set_context(context = u'loinc', val = loinc)
1969
1970 info = gmLOINC.loinc2term(loinc = loinc)
1971 if len(info) == 0:
1972 self._TCTRL_loinc_info.SetValue(u'')
1973 return
1974
1975 self._TCTRL_loinc_info.SetValue(info[0])
1976
1977
1978
1980
1981 has_errors = False
1982 for field in [self._PRW_name, self._PRW_abbrev, self._PRW_conversion_unit]:
1983 if field.GetValue().strip() in [u'', None]:
1984 has_errors = True
1985 field.display_as_valid(valid = False)
1986 else:
1987 field.display_as_valid(valid = True)
1988 field.Refresh()
1989
1990 return (not has_errors)
1991
1993
1994 pk_org = self._PRW_test_org.GetData()
1995 if pk_org is None:
1996 pk_org = gmPathLab.create_test_org (
1997 name = gmTools.none_if(self._PRW_test_org.GetValue().strip(), u''),
1998 comment = gmTools.none_if(self._TCTRL_comment_org.GetValue().strip(), u'')
1999 )['pk_test_org']
2000
2001 tt = gmPathLab.create_measurement_type (
2002 lab = pk_org,
2003 abbrev = self._PRW_abbrev.GetValue().strip(),
2004 name = self._PRW_name.GetValue().strip(),
2005 unit = gmTools.coalesce (
2006 self._PRW_conversion_unit.GetData(),
2007 self._PRW_conversion_unit.GetValue()
2008 ).strip()
2009 )
2010 if self._PRW_loinc.GetData() is not None:
2011 tt['loinc'] = gmTools.none_if(self._PRW_loinc.GetData().strip(), u'')
2012 else:
2013 tt['loinc'] = gmTools.none_if(self._PRW_loinc.GetValue().strip(), u'')
2014 tt['comment_type'] = gmTools.none_if(self._TCTRL_comment_type.GetValue().strip(), u'')
2015 tt['pk_meta_test_type'] = self._PRW_meta_type.GetData()
2016
2017 tt.save()
2018
2019 self.data = tt
2020
2021 return True
2022
2050
2052 self._PRW_name.SetText(u'', None, True)
2053 self._on_name_lost_focus()
2054 self._PRW_abbrev.SetText(u'', None, True)
2055 self._PRW_conversion_unit.SetText(u'', None, True)
2056 self._PRW_loinc.SetText(u'', None, True)
2057 self._on_loinc_lost_focus()
2058 self._TCTRL_comment_type.SetValue(u'')
2059 self._PRW_test_org.SetText(u'', None, True)
2060 self._TCTRL_comment_org.SetValue(u'')
2061 self._PRW_meta_type.SetText(u'', None, True)
2062
2063 self._PRW_name.SetFocus()
2064
2066 self._PRW_name.SetText(self.data['name'], self.data['name'], True)
2067 self._on_name_lost_focus()
2068 self._PRW_abbrev.SetText(self.data['abbrev'], self.data['abbrev'], True)
2069 self._PRW_conversion_unit.SetText (
2070 gmTools.coalesce(self.data['conversion_unit'], u''),
2071 self.data['conversion_unit'],
2072 True
2073 )
2074 self._PRW_loinc.SetText (
2075 gmTools.coalesce(self.data['loinc'], u''),
2076 self.data['loinc'],
2077 True
2078 )
2079 self._on_loinc_lost_focus()
2080 self._TCTRL_comment_type.SetValue(gmTools.coalesce(self.data['comment_type'], u''))
2081 self._PRW_test_org.SetText (
2082 gmTools.coalesce(self.data['pk_test_org'], u'', self.data['name_org']),
2083 self.data['pk_test_org'],
2084 True
2085 )
2086 self._TCTRL_comment_org.SetValue(gmTools.coalesce(self.data['comment_org'], u''))
2087 if self.data['pk_meta_test_type'] is None:
2088 self._PRW_meta_type.SetText(u'', None, True)
2089 else:
2090 self._PRW_meta_type.SetText(u'%s: %s' % (self.data['abbrev_meta'], self.data['name_meta']), self.data['pk_meta_test_type'], True)
2091
2092 self._PRW_name.SetFocus()
2093
2104
2105
2106 _SQL_units_from_test_results = u"""
2107 -- via clin.v_test_results.pk_type (for types already used in results)
2108 SELECT
2109 val_unit AS data,
2110 val_unit AS field_label,
2111 val_unit || ' (' || name_tt || ')' AS list_label,
2112 1 AS rank
2113 FROM
2114 clin.v_test_results
2115 WHERE
2116 (
2117 val_unit %(fragment_condition)s
2118 OR
2119 conversion_unit %(fragment_condition)s
2120 )
2121 %(ctxt_type_pk)s
2122 %(ctxt_test_name)s
2123 """
2124
2125 _SQL_units_from_test_types = u"""
2126 -- via clin.test_type (for types not yet used in results)
2127 SELECT
2128 conversion_unit AS data,
2129 conversion_unit AS field_label,
2130 conversion_unit || ' (' || name || ')' AS list_label,
2131 2 AS rank
2132 FROM
2133 clin.test_type
2134 WHERE
2135 conversion_unit %(fragment_condition)s
2136 %(ctxt_ctt)s
2137 """
2138
2139 _SQL_units_from_loinc_ipcc = u"""
2140 -- via ref.loinc.ipcc_units
2141 SELECT
2142 ipcc_units AS data,
2143 ipcc_units AS field_label,
2144 ipcc_units || ' (LOINC.ipcc: ' || term || ')' AS list_label,
2145 3 AS rank
2146 FROM
2147 ref.loinc
2148 WHERE
2149 ipcc_units %(fragment_condition)s
2150 %(ctxt_loinc)s
2151 %(ctxt_loinc_term)s
2152 """
2153
2154 _SQL_units_from_loinc_submitted = u"""
2155 -- via ref.loinc.submitted_units
2156 SELECT
2157 submitted_units AS data,
2158 submitted_units AS field_label,
2159 submitted_units || ' (LOINC.submitted:' || term || ')' AS list_label,
2160 3 AS rank
2161 FROM
2162 ref.loinc
2163 WHERE
2164 submitted_units %(fragment_condition)s
2165 %(ctxt_loinc)s
2166 %(ctxt_loinc_term)s
2167 """
2168
2169 _SQL_units_from_loinc_example = u"""
2170 -- via ref.loinc.example_units
2171 SELECT
2172 example_units AS data,
2173 example_units AS field_label,
2174 example_units || ' (LOINC.example: ' || term || ')' AS list_label,
2175 3 AS rank
2176 FROM
2177 ref.loinc
2178 WHERE
2179 example_units %(fragment_condition)s
2180 %(ctxt_loinc)s
2181 %(ctxt_loinc_term)s
2182 """
2183
2184 _SQL_units_from_atc = u"""
2185 -- via ref.atc.unit
2186 SELECT
2187 unit AS data,
2188 unit AS field_label,
2189 unit || ' (ATC: ' || term || ')' AS list_label,
2190 2 AS rank
2191 FROM
2192 ref.atc
2193 WHERE
2194 unit IS NOT NULL
2195 AND
2196 unit %(fragment_condition)s
2197 """
2198
2199 _SQL_units_from_consumable_substance = u"""
2200 -- via ref.consumable_substance.unit
2201 SELECT
2202 unit AS data,
2203 unit AS field_label,
2204 unit || ' (' || description || ')' AS list_label,
2205 2 AS rank
2206 FROM
2207 ref.consumable_substance
2208 WHERE
2209 unit %(fragment_condition)s
2210 %(ctxt_substance)s
2211 """
2212
2213
2215
2217
2218 query = u"""
2219 SELECT DISTINCT ON (data)
2220 data,
2221 field_label,
2222 list_label
2223 FROM (
2224
2225 SELECT
2226 data,
2227 field_label,
2228 list_label,
2229 rank
2230 FROM (
2231 (%s) UNION ALL
2232 (%s) UNION ALL
2233 (%s) UNION ALL
2234 (%s) UNION ALL
2235 (%s) UNION ALL
2236 (%s) UNION ALL
2237 (%s)
2238 ) AS all_matching_units
2239 WHERE data IS NOT NULL
2240 ORDER BY rank
2241
2242 ) AS ranked_matching_units
2243 LIMIT 50""" % (
2244 _SQL_units_from_test_results,
2245 _SQL_units_from_test_types,
2246 _SQL_units_from_loinc_ipcc,
2247 _SQL_units_from_loinc_submitted,
2248 _SQL_units_from_loinc_example,
2249 _SQL_units_from_atc,
2250 _SQL_units_from_consumable_substance
2251 )
2252
2253 ctxt = {
2254 'ctxt_type_pk': {
2255 'where_part': u'AND pk_test_type = %(pk_type)s',
2256 'placeholder': u'pk_type'
2257 },
2258 'ctxt_test_name': {
2259 'where_part': u'AND %(test_name)s IN (name_tt, name_meta, abbrev_meta)',
2260 'placeholder': u'test_name'
2261 },
2262 'ctxt_ctt': {
2263 'where_part': u'AND %(test_name)s IN (name, abbrev)',
2264 'placeholder': u'test_name'
2265 },
2266 'ctxt_loinc': {
2267 'where_part': u'AND code = %(loinc)s',
2268 'placeholder': u'loinc'
2269 },
2270 'ctxt_loinc_term': {
2271 'where_part': u'AND term ~* %(test_name)s',
2272 'placeholder': u'test_name'
2273 },
2274 'ctxt_substance': {
2275 'where_part': u'AND description ~* %(substance)s',
2276 'placeholder': u'substance'
2277 }
2278 }
2279
2280 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query, context = ctxt)
2281 mp.setThresholds(1, 2, 4)
2282
2283 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
2284 self.matcher = mp
2285 self.SetToolTipString(_('Select the desired unit for the amount or measurement.'))
2286 self.selection_only = False
2287 self.phrase_separators = u'[;|]+'
2288
2289
2290
2292
2294
2295 query = u"""
2296 select distinct abnormality_indicator,
2297 abnormality_indicator, abnormality_indicator
2298 from clin.v_test_results
2299 where
2300 abnormality_indicator %(fragment_condition)s
2301 order by abnormality_indicator
2302 limit 25"""
2303
2304 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
2305 mp.setThresholds(1, 1, 2)
2306 mp.ignored_chars = "[.'\\\[\]#$%_]+" + '"'
2307 mp.word_separators = '[ \t&:]+'
2308 gmPhraseWheel.cPhraseWheel.__init__ (
2309 self,
2310 *args,
2311 **kwargs
2312 )
2313 self.matcher = mp
2314 self.SetToolTipString(_('Select an indicator for the level of abnormality.'))
2315 self.selection_only = False
2316
2317
2318
2330
2332
2333 if parent is None:
2334 parent = wx.GetApp().GetTopWindow()
2335
2336
2337 def edit(org=None):
2338 return edit_measurement_org(parent = parent, org = org)
2339
2340 def refresh(lctrl):
2341 orgs = gmPathLab.get_test_orgs()
2342 lctrl.set_string_items ([
2343 (o['unit'], o['organization'], gmTools.coalesce(o['test_org_contact'], u''), gmTools.coalesce(o['comment'], u''), o['pk_test_org'])
2344 for o in orgs
2345 ])
2346 lctrl.set_data(orgs)
2347
2348 def delete(test_org):
2349 gmPathLab.delete_test_org(test_org = test_org['pk_test_org'])
2350 return True
2351
2352 gmListWidgets.get_choices_from_list (
2353 parent = parent,
2354 msg = _('\nThese are the diagnostic orgs (path labs etc) currently defined in GNUmed.\n\n'),
2355 caption = _('Showing diagnostic orgs.'),
2356 columns = [_('Name'), _('Organization'), _('Contact'), _('Comment'), u'#'],
2357 single_selection = True,
2358 refresh_callback = refresh,
2359 edit_callback = edit,
2360 new_callback = edit,
2361 delete_callback = delete
2362 )
2363
2364
2365 from Gnumed.wxGladeWidgets import wxgMeasurementOrgEAPnl
2366
2367 -class cMeasurementOrgEAPnl(wxgMeasurementOrgEAPnl.wxgMeasurementOrgEAPnl, gmEditArea.cGenericEditAreaMixin):
2368
2384
2385
2386
2387
2388
2389
2390
2391
2393 has_errors = False
2394 if self._PRW_org_unit.GetData() is None:
2395 if self._PRW_org_unit.GetValue().strip() == u'':
2396 has_errors = True
2397 self._PRW_org_unit.display_as_valid(valid = False)
2398 else:
2399 self._PRW_org_unit.display_as_valid(valid = True)
2400 else:
2401 self._PRW_org_unit.display_as_valid(valid = True)
2402
2403 return (not has_errors)
2404
2415
2435
2440
2445
2447 self._refresh_as_new()
2448
2451
2453
2455
2456 query = u"""
2457 SELECT DISTINCT ON (list_label)
2458 pk AS data,
2459 unit || ' (' || organization || ')' AS field_label,
2460 unit || ' @ ' || organization AS list_label
2461 FROM clin.v_test_orgs
2462 WHERE
2463 unit %(fragment_condition)s
2464 OR
2465 organization %(fragment_condition)s
2466 ORDER BY list_label
2467 LIMIT 50"""
2468 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
2469 mp.setThresholds(1, 2, 4)
2470
2471 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
2472 self.matcher = mp
2473 self.SetToolTipString(_('The name of the path lab/diagnostic organisation.'))
2474 self.selection_only = False
2475
2488
2491
2492
2531
2576
2577
2578
2579
2580 if __name__ == '__main__':
2581
2582 from Gnumed.pycommon import gmLog2
2583
2584 gmI18N.activate_locale()
2585 gmI18N.install_domain()
2586 gmDateTime.init()
2587
2588
2596
2604
2605
2606
2607
2608
2609
2610
2611 if (len(sys.argv) > 1) and (sys.argv[1] == 'test'):
2612
2613 test_test_ea_pnl()
2614
2615
2616
2617