| Home | Trees | Indices | Help |
|
|---|
|
|
1 """GNUmed measurement widgets."""
2 #================================================================
3 __author__ = "Karsten Hilbert <Karsten.Hilbert@gmx.net>"
4 __license__ = "GPL"
5
6
7 import sys, logging, datetime as pyDT, decimal, os, subprocess, codecs
8 import os.path
9
10
11 import wx, wx.grid, wx.lib.hyperlink
12
13
14 if __name__ == '__main__':
15 sys.path.insert(0, '../../')
16 from Gnumed.business import gmPerson
17 from Gnumed.business import gmStaff
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 from Gnumed.business import gmOrganization
24
25 from Gnumed.pycommon import gmTools
26 from Gnumed.pycommon import gmNetworkTools
27 from Gnumed.pycommon import gmI18N
28 from Gnumed.pycommon import gmShellAPI
29 from Gnumed.pycommon import gmCfg
30 from Gnumed.pycommon import gmDateTime
31 from Gnumed.pycommon import gmMatchProvider
32 from Gnumed.pycommon import gmDispatcher
33
34 from Gnumed.wxpython import gmRegetMixin
35 from Gnumed.wxpython import gmEditArea
36 from Gnumed.wxpython import gmPhraseWheel
37 from Gnumed.wxpython import gmListWidgets
38 from Gnumed.wxpython import gmGuiHelpers
39 from Gnumed.wxpython import gmAuthWidgets
40 from Gnumed.wxpython import gmOrganizationWidgets
41
42
43 _log = logging.getLogger('gm.ui')
44
45 #================================================================
46 # LOINC related widgets
47 #================================================================
49
50 wx.BeginBusyCursor()
51
52 gmDispatcher.send(signal = 'statustext', msg = _('Updating LOINC data can take quite a while...'), beep = True)
53
54 # download
55 loinc_zip = gmNetworkTools.download_file(url = 'http://www.gnumed.de/downloads/data/loinc/loinctab.zip', suffix = '.zip')
56 if loinc_zip is None:
57 wx.EndBusyCursor()
58 gmGuiHelpers.gm_show_warning (
59 aTitle = _('Downloading LOINC'),
60 aMessage = _('Error downloading the latest LOINC data.\n')
61 )
62 return False
63
64 _log.debug('downloaded zipped LOINC data into [%s]', loinc_zip)
65
66 loinc_dir = gmNetworkTools.unzip_data_pack(filename = loinc_zip)
67
68 # split master data file
69 data_fname, license_fname = gmLOINC.split_LOINCDBTXT(input_fname = os.path.join(loinc_dir, 'LOINCDB.TXT'))
70
71 wx.EndBusyCursor()
72
73 conn = gmAuthWidgets.get_dbowner_connection(procedure = _('importing LOINC reference data'))
74 if conn is None:
75 return False
76
77 wx.BeginBusyCursor()
78
79 # import data
80 if gmLOINC.loinc_import(data_fname = data_fname, license_fname = license_fname, conn = conn):
81 gmDispatcher.send(signal = 'statustext', msg = _('Successfully imported LOINC reference data.'))
82 else:
83 gmDispatcher.send(signal = 'statustext', msg = _('Importing LOINC reference data failed.'), beep = True)
84
85 wx.EndBusyCursor()
86 return True
87 #================================================================
88 # convenience functions
89 #================================================================
91
92 dbcfg = gmCfg.cCfgSQL()
93
94 url = dbcfg.get (
95 option = u'external.urls.measurements_search',
96 workplace = gmSurgery.gmCurrentPractice().active_workplace,
97 bias = 'user',
98 default = u"http://www.google.de/search?as_oq=%(search_term)s&num=10&as_sitesearch=laborlexikon.de"
99 )
100
101 base_url = dbcfg.get2 (
102 option = u'external.urls.measurements_encyclopedia',
103 workplace = gmSurgery.gmCurrentPractice().active_workplace,
104 bias = 'user',
105 default = u'http://www.laborlexikon.de'
106 )
107
108 if measurement_type is None:
109 url = base_url
110
111 measurement_type = measurement_type.strip()
112
113 if measurement_type == u'':
114 url = base_url
115
116 url = url % {'search_term': measurement_type}
117
118 gmNetworkTools.open_url_in_browser(url = url)
119
120 #----------------------------------------------------------------
122 ea = cMeasurementEditAreaPnl(parent = parent, id = -1)
123 ea.data = measurement
124 ea.mode = gmTools.coalesce(measurement, 'new', 'edit')
125 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = single_entry)
126 dlg.SetTitle(gmTools.coalesce(measurement, _('Adding new measurement'), _('Editing measurement')))
127 if dlg.ShowModal() == wx.ID_OK:
128 dlg.Destroy()
129 return True
130 dlg.Destroy()
131 return False
132
133 #----------------------------------------------------------------
135
136 if parent is None:
137 parent = wx.GetApp().GetTopWindow()
138
139 if emr is None:
140 emr = gmPerson.gmCurrentPatient().emr
141
142 #------------------------------------------------------------
143 def edit(measurement=None):
144 return edit_measurement(parent = parent, measurement = measurement, single_entry = True)
145 #------------------------------------------------------------
146 def delete(measurement):
147 gmPathLab.delete_test_result(result = measurement)
148 return True
149 #------------------------------------------------------------
150 def get_tooltip(measurement):
151 return measurement.format(with_review=True, with_evaluation=True, with_ranges=True)
152 #------------------------------------------------------------
153 def refresh(lctrl):
154 results = emr.get_test_results(order_by = 'clin_when DESC, unified_abbrev, unified_name')
155 items = [ [
156 gmDateTime.pydt_strftime (
157 r['clin_when'],
158 '%Y %b %d %H:%M',
159 accuracy = gmDateTime.acc_minutes
160 ),
161 r['unified_abbrev'],
162 u'%s%s%s' % (
163 r['unified_val'],
164 gmTools.coalesce(r['val_unit'], u'', u' %s'),
165 gmTools.coalesce(r['abnormality_indicator'], u'', u' %s')
166 ),
167 r['unified_name'],
168 gmTools.coalesce(r['comment'], u''),
169 r['pk_test_result']
170 ] for r in results ]
171 lctrl.set_string_items(items)
172 lctrl.set_data(results)
173 #------------------------------------------------------------
174 msg = _('Test results (ordered reverse-chronologicallly)')
175
176 return gmListWidgets.get_choices_from_list (
177 parent = parent,
178 msg = msg,
179 caption = _('Showing test results.'),
180 columns = [ _('When'), _('Abbrev'), _('Value'), _('Name'), _('Comment'), u'#' ],
181 single_selection = single_selection,
182 can_return_empty = False,
183 refresh_callback = refresh,
184 edit_callback = edit,
185 new_callback = edit,
186 delete_callback = delete,
187 list_tooltip_callback = get_tooltip
188 )
189
190 #================================================================
192
193 from Gnumed.wxpython import gmFormWidgets
194
195 if parent is None:
196 parent = wx.GetApp().GetTopWindow()
197
198 template = gmFormWidgets.manage_form_templates (
199 parent = parent,
200 active_only = True,
201 template_types = [u'gnuplot script']
202 )
203
204 option = u'form_templates.default_gnuplot_template'
205
206 if template is None:
207 gmDispatcher.send(signal = 'statustext', msg = _('No default Gnuplot script template selected.'), beep = True)
208 return None
209
210 if template['engine'] != u'G':
211 gmDispatcher.send(signal = 'statustext', msg = _('No default Gnuplot script template selected.'), beep = True)
212 return None
213
214 dbcfg = gmCfg.cCfgSQL()
215 dbcfg.set (
216 workplace = gmSurgery.gmCurrentPractice().active_workplace,
217 option = option,
218 value = u'%s - %s' % (template['name_long'], template['external_version'])
219 )
220 return template
221
222 #============================================================
224
225 option = u'form_templates.default_gnuplot_template'
226
227 dbcfg = gmCfg.cCfgSQL()
228
229 # load from option
230 default_template_name = dbcfg.get2 (
231 option = option,
232 workplace = gmSurgery.gmCurrentPractice().active_workplace,
233 bias = 'user'
234 )
235
236 # not configured -> try to configure
237 if default_template_name is None:
238 gmDispatcher.send('statustext', msg = _('No default Gnuplot template configured.'), beep = False)
239 default_template = configure_default_gnuplot_template(parent = parent)
240 # still not configured -> return
241 if default_template is None:
242 gmGuiHelpers.gm_show_error (
243 aMessage = _('There is no default Gnuplot one-type script template configured.'),
244 aTitle = _('Plotting test results')
245 )
246 return None
247 return default_template
248
249 # now it MUST be configured (either newly or previously)
250 # but also *validly* ?
251 try:
252 name, ver = default_template_name.split(u' - ')
253 except:
254 # not valid
255 _log.exception('problem splitting Gnuplot script template name [%s]', default_template_name)
256 gmDispatcher.send(signal = 'statustext', msg = _('Problem loading Gnuplot script template.'), beep = True)
257 return None
258
259 default_template = gmForms.get_form_template(name_long = name, external_version = ver)
260 if default_template is None:
261 default_template = configure_default_gnuplot_template(parent = parent)
262 # still not configured -> return
263 if default_template is None:
264 gmGuiHelpers.gm_show_error (
265 aMessage = _('Cannot load default Gnuplot script template [%s - %s]') % (name, ver),
266 aTitle = _('Plotting test results')
267 )
268 return None
269
270 return default_template
271
272 #----------------------------------------------------------------
273 -def plot_measurements(parent=None, tests=None, format=None, show_year = True, use_default_template=False):
274
275 from Gnumed.wxpython import gmFormWidgets
276
277 # only valid for one-type plotting
278 if use_default_template:
279 template = get_default_gnuplot_template()
280 else:
281 template = gmFormWidgets.manage_form_templates (
282 parent = parent,
283 active_only = True,
284 template_types = [u'gnuplot script']
285 )
286
287 if template is None:
288 gmGuiHelpers.gm_show_error (
289 aMessage = _('Cannot plot without a plot script.'),
290 aTitle = _('Plotting test results')
291 )
292 return False
293
294 fname_data = gmPathLab.export_results_for_gnuplot(results = tests, show_year = show_year)
295
296 script = template.instantiate()
297 script.data_filename = fname_data
298 script.generate_output(format = format) # Gnuplot output terminal, wxt = wxWidgets window
299
300 #----------------------------------------------------------------
301 -def plot_adjacent_measurements(parent=None, test=None, format=None, show_year=True, plot_singular_result=True, use_default_template=False):
302
303 earlier, later = test.get_adjacent_results(desired_earlier_results = 2, desired_later_results = 2)
304 results2plot = []
305 if earlier is not None:
306 results2plot.extend(earlier)
307 results2plot.append(test)
308 if later is not None:
309 results2plot.extend(later)
310 if len(results2plot) == 1:
311 if not plot_singular_result:
312 return
313 plot_measurements (
314 parent = parent,
315 tests = results2plot,
316 format = format,
317 show_year = show_year,
318 use_default_template = use_default_template
319 )
320 #================================================================
321 #from Gnumed.wxGladeWidgets import wxgPrimaryCareVitalsInputPnl
322
323 # Taillenumfang: Mitte zwischen unterster Rippe und
324 # hoechstem Teil des Beckenkamms
325 # Maenner: maessig: 94-102, deutlich: > 102 .. erhoeht
326 # Frauen: maessig: 80-88, deutlich: > 88 .. erhoeht
327
328 #================================================================
329 # display widgets
330 #================================================================
332 """A grid class for displaying measurment results.
333
334 - does NOT listen to the currently active patient
335 - thereby it can display any patient at any time
336 """
337 # FIXME: sort-by-battery
338 # FIXME: filter-by-battery
339 # FIXME: filter out empty
340 # FIXME: filter by tests of a selected date
341 # FIXME: dates DESC/ASC by cfg
342 # FIXME: mouse over column header: display date info
344
345 wx.grid.Grid.__init__(self, *args, **kwargs)
346
347 self.__patient = None
348 self.__panel_to_show = None
349 self.__show_by_panel = False
350 self.__cell_data = {}
351 self.__row_label_data = []
352
353 self.__prev_row = None
354 self.__prev_col = None
355 self.__prev_label_row = None
356 self.__date_format = str((_('lab_grid_date_format::%Y\n%b %d')).lstrip('lab_grid_date_format::'))
357
358 self.__init_ui()
359 self.__register_events()
360 #------------------------------------------------------------
361 # external API
362 #------------------------------------------------------------
364 if not self.IsSelection():
365 gmDispatcher.send(signal = u'statustext', msg = _('No results selected for deletion.'))
366 return True
367
368 selected_cells = self.get_selected_cells()
369 if len(selected_cells) > 20:
370 results = None
371 msg = _(
372 'There are %s results marked for deletion.\n'
373 '\n'
374 'Are you sure you want to delete these results ?'
375 ) % len(selected_cells)
376 else:
377 results = self.__cells_to_data(cells = selected_cells, exclude_multi_cells = False)
378 txt = u'\n'.join([ u'%s %s (%s): %s %s%s' % (
379 r['clin_when'].strftime('%x %H:%M').decode(gmI18N.get_encoding()),
380 r['unified_abbrev'],
381 r['unified_name'],
382 r['unified_val'],
383 r['val_unit'],
384 gmTools.coalesce(r['abnormality_indicator'], u'', u' (%s)')
385 ) for r in results
386 ])
387 msg = _(
388 'The following results are marked for deletion:\n'
389 '\n'
390 '%s\n'
391 '\n'
392 'Are you sure you want to delete these results ?'
393 ) % txt
394
395 dlg = gmGuiHelpers.c2ButtonQuestionDlg (
396 self,
397 -1,
398 caption = _('Deleting test results'),
399 question = msg,
400 button_defs = [
401 {'label': _('Delete'), 'tooltip': _('Yes, delete all the results.'), 'default': False},
402 {'label': _('Cancel'), 'tooltip': _('No, do NOT delete any results.'), 'default': True}
403 ]
404 )
405 decision = dlg.ShowModal()
406
407 if decision == wx.ID_YES:
408 if results is None:
409 results = self.__cells_to_data(cells = selected_cells, exclude_multi_cells = False)
410 for result in results:
411 gmPathLab.delete_test_result(result)
412 #------------------------------------------------------------
414 if not self.IsSelection():
415 gmDispatcher.send(signal = u'statustext', msg = _('Cannot sign results. No results selected.'))
416 return True
417
418 selected_cells = self.get_selected_cells()
419 if len(selected_cells) > 10:
420 test_count = len(selected_cells)
421 tests = None
422 else:
423 test_count = None
424 tests = self.__cells_to_data(cells = selected_cells, exclude_multi_cells = False)
425 if len(tests) == 0:
426 return True
427
428 dlg = cMeasurementsReviewDlg (
429 self,
430 -1,
431 tests = tests,
432 test_count = test_count
433 )
434 decision = dlg.ShowModal()
435
436 if decision == wx.ID_APPLY:
437 wx.BeginBusyCursor()
438
439 if dlg._RBTN_confirm_abnormal.GetValue():
440 abnormal = None
441 elif dlg._RBTN_results_normal.GetValue():
442 abnormal = False
443 else:
444 abnormal = True
445
446 if dlg._RBTN_confirm_relevance.GetValue():
447 relevant = None
448 elif dlg._RBTN_results_not_relevant.GetValue():
449 relevant = False
450 else:
451 relevant = True
452
453 if tests is None:
454 tests = self.__cells_to_data(cells = selected_cells, exclude_multi_cells = False)
455
456 comment = None
457 if len(tests) == 1:
458 comment = dlg._TCTRL_comment.GetValue()
459
460 for test in tests:
461 test.set_review (
462 technically_abnormal = abnormal,
463 clinically_relevant = relevant,
464 comment = comment,
465 make_me_responsible = dlg._CHBOX_responsible.IsChecked()
466 )
467
468 wx.EndBusyCursor()
469
470 dlg.Destroy()
471 #------------------------------------------------------------
473
474 if not self.IsSelection():
475 gmDispatcher.send(signal = u'statustext', msg = _('Cannot plot results. No results selected.'))
476 return True
477
478 tests = self.__cells_to_data (
479 cells = self.get_selected_cells(),
480 exclude_multi_cells = False,
481 auto_include_multi_cells = True
482 )
483
484 plot_measurements(parent = self, tests = tests)
485 #------------------------------------------------------------
487
488 sel_block_top_left = self.GetSelectionBlockTopLeft()
489 sel_block_bottom_right = self.GetSelectionBlockBottomRight()
490 sel_cols = self.GetSelectedCols()
491 sel_rows = self.GetSelectedRows()
492
493 selected_cells = []
494
495 # individually selected cells (ctrl-click)
496 selected_cells += self.GetSelectedCells()
497
498 # selected rows
499 selected_cells += list (
500 (row, col)
501 for row in sel_rows
502 for col in xrange(self.GetNumberCols())
503 )
504
505 # selected columns
506 selected_cells += list (
507 (row, col)
508 for row in xrange(self.GetNumberRows())
509 for col in sel_cols
510 )
511
512 # selection blocks
513 for top_left, bottom_right in zip(self.GetSelectionBlockTopLeft(), self.GetSelectionBlockBottomRight()):
514 selected_cells += [
515 (row, col)
516 for row in xrange(top_left[0], bottom_right[0] + 1)
517 for col in xrange(top_left[1], bottom_right[1] + 1)
518 ]
519
520 return set(selected_cells)
521 #------------------------------------------------------------
522 - def select_cells(self, unsigned_only=False, accountables_only=False, keep_preselections=False):
523 """Select a range of cells according to criteria.
524
525 unsigned_only: include only those which are not signed at all yet
526 accountable_only: include only those for which the current user is responsible
527 keep_preselections: broaden (rather than replace) the range of selected cells
528
529 Combinations are powerful !
530 """
531 wx.BeginBusyCursor()
532 self.BeginBatch()
533
534 if not keep_preselections:
535 self.ClearSelection()
536
537 for col_idx in self.__cell_data.keys():
538 for row_idx in self.__cell_data[col_idx].keys():
539 # loop over results in cell and only include
540 # those multi-value cells that are not ambiguous
541 do_not_include = False
542 for result in self.__cell_data[col_idx][row_idx]:
543 if unsigned_only:
544 if result['reviewed']:
545 do_not_include = True
546 break
547 if accountables_only:
548 if not result['you_are_responsible']:
549 do_not_include = True
550 break
551 if do_not_include:
552 continue
553
554 self.SelectBlock(row_idx, col_idx, row_idx, col_idx, addToSelected = True)
555
556 self.EndBatch()
557 wx.EndBusyCursor()
558 #------------------------------------------------------------
560 self.empty_grid()
561 if self.__patient is None:
562 return
563
564 if self.__show_by_panel:
565 self.__repopulate_grid_by_panel()
566 return
567
568 self.__repopulate_grid_all_results()
569 #------------------------------------------------------------
571
572 if self.__panel_to_show is None:
573 return
574
575 emr = self.__patient.get_emr()
576
577 # rows
578 self.__row_label_data = self.__panel_to_show.test_types
579 row_labels = [ u'%s%s' % (
580 gmTools.bool2subst(test['is_fake_meta_type'], u'', gmTools.u_sum, u''),
581 test['unified_abbrev']
582 ) for test in self.__row_label_data
583 ]
584 if len(row_labels) == 0:
585 return
586
587 # columns
588 column_labels = [
589 date[0].strftime(self.__date_format) for date in emr.get_dates_for_results (
590 tests = self.__panel_to_show['pk_test_types'],
591 # FIXME: make configurable
592 reverse_chronological = True
593 )
594 ]
595 results = emr.get_test_results_by_date (
596 tests = self.__panel_to_show['pk_test_types'],
597 # FIXME: make configurable
598 reverse_chronological = True
599 )
600
601 self.BeginBatch()
602
603 # rows
604 self.AppendRows(numRows = len(row_labels))
605 for row_idx in range(len(row_labels)):
606 self.SetRowLabelValue(row_idx, row_labels[row_idx])
607
608 # columns
609 self.AppendCols(numCols = len(column_labels))
610 for date_idx in range(len(column_labels)):
611 self.SetColLabelValue(date_idx, column_labels[date_idx])
612
613 # cell values (list of test results)
614 for result in results:
615 row = row_labels.index(u'%s%s' % (
616 gmTools.bool2subst(result['is_fake_meta_type'], u'', gmTools.u_sum, u''),
617 result['unified_abbrev']
618 ))
619 col = column_labels.index(result['clin_when'].strftime(self.__date_format))
620
621 try:
622 self.__cell_data[col]
623 except KeyError:
624 self.__cell_data[col] = {}
625
626 # the tooltip always shows the youngest sub result details
627 if self.__cell_data[col].has_key(row):
628 self.__cell_data[col][row].append(result)
629 self.__cell_data[col][row].sort(key = lambda x: x['clin_when'], reverse = True)
630 else:
631 self.__cell_data[col][row] = [result]
632
633 # rebuild cell display string
634 vals2display = []
635 for sub_result in self.__cell_data[col][row]:
636
637 # is the sub_result technically abnormal ?
638 ind = gmTools.coalesce(sub_result['abnormality_indicator'], u'').strip()
639 if ind != u'':
640 lab_abnormality_indicator = u' (%s)' % ind[:3]
641 else:
642 lab_abnormality_indicator = u''
643 # - if noone reviewed - use what the lab thinks
644 if sub_result['is_technically_abnormal'] is None:
645 abnormality_indicator = lab_abnormality_indicator
646 # - if someone reviewed and decreed normality - use that
647 elif sub_result['is_technically_abnormal'] is False:
648 abnormality_indicator = u''
649 # - if someone reviewed and decreed abnormality ...
650 else:
651 # ... invent indicator if the lab did't use one
652 if lab_abnormality_indicator == u'':
653 # FIXME: calculate from min/max/range
654 abnormality_indicator = u' (%s)' % gmTools.u_plus_minus
655 # ... else use indicator the lab used
656 else:
657 abnormality_indicator = lab_abnormality_indicator
658
659 # is the sub_result relevant clinically ?
660 # FIXME: take into account primary_GP once we support that
661 sub_result_relevant = sub_result['is_clinically_relevant']
662 if sub_result_relevant is None:
663 # FIXME: calculate from clinical range
664 sub_result_relevant = False
665
666 missing_review = False
667 # warn on missing review if
668 # a) no review at all exists or
669 if not sub_result['reviewed']:
670 missing_review = True
671 # b) there is a review but
672 else:
673 # current user is reviewer and hasn't reviewed
674 if sub_result['you_are_responsible'] and not sub_result['review_by_you']:
675 missing_review = True
676
677 # can we display the full sub_result length ?
678 if len(sub_result['unified_val']) > 8:
679 tmp = u'%.7s%s' % (sub_result['unified_val'][:7], gmTools.u_ellipsis)
680 else:
681 tmp = u'%.8s' % sub_result['unified_val'][:8]
682
683 # abnormal ?
684 tmp = u'%s%.6s' % (tmp, abnormality_indicator)
685
686 # is there a comment ?
687 has_sub_result_comment = gmTools.coalesce (
688 gmTools.coalesce(sub_result['note_test_org'], sub_result['comment']),
689 u''
690 ).strip() != u''
691 if has_sub_result_comment:
692 tmp = u'%s %s' % (tmp, gmTools.u_ellipsis)
693
694 # lacking a review ?
695 if missing_review:
696 tmp = u'%s %s' % (tmp, gmTools.u_writing_hand)
697
698 # part of a multi-result cell ?
699 if len(self.__cell_data[col][row]) > 1:
700 tmp = u'%s %s' % (sub_result['clin_when'].strftime('%H:%M'), tmp)
701
702 vals2display.append(tmp)
703
704 self.SetCellValue(row, col, u'\n'.join(vals2display))
705 self.SetCellAlignment(row, col, horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE)
706 # font = self.GetCellFont(row, col)
707 # if not font.IsFixedWidth():
708 # font.SetFamily(family = wx.FONTFAMILY_MODERN)
709 # FIXME: what about partial sub results being relevant ??
710 if sub_result_relevant:
711 font = self.GetCellFont(row, col)
712 self.SetCellTextColour(row, col, 'firebrick')
713 font.SetWeight(wx.FONTWEIGHT_BOLD)
714 self.SetCellFont(row, col, font)
715 # self.SetCellFont(row, col, font)
716
717 self.AutoSize()
718 self.EndBatch()
719 return
720 #------------------------------------------------------------
722 emr = self.__patient.get_emr()
723
724 self.__row_label_data = emr.get_test_types_for_results()
725 test_type_labels = [ u'%s%s' % (
726 gmTools.bool2subst(test['is_fake_meta_type'], u'', gmTools.u_sum, u''),
727 test['unified_abbrev']
728 ) for test in self.__row_label_data
729 ]
730 if len(test_type_labels) == 0:
731 return
732
733 test_date_labels = [ date[0].strftime(self.__date_format) for date in emr.get_dates_for_results() ]
734 results = emr.get_test_results_by_date()
735
736 self.BeginBatch()
737
738 # rows
739 self.AppendRows(numRows = len(test_type_labels))
740 for row_idx in range(len(test_type_labels)):
741 self.SetRowLabelValue(row_idx, test_type_labels[row_idx])
742
743 # columns
744 self.AppendCols(numCols = len(test_date_labels))
745 for date_idx in range(len(test_date_labels)):
746 self.SetColLabelValue(date_idx, test_date_labels[date_idx])
747
748 # cell values (list of test results)
749 for result in results:
750 row = test_type_labels.index(u'%s%s' % (
751 gmTools.bool2subst(result['is_fake_meta_type'], u'', gmTools.u_sum, u''),
752 result['unified_abbrev']
753 ))
754 col = test_date_labels.index(result['clin_when'].strftime(self.__date_format))
755
756 try:
757 self.__cell_data[col]
758 except KeyError:
759 self.__cell_data[col] = {}
760
761 # the tooltip always shows the youngest sub result details
762 if self.__cell_data[col].has_key(row):
763 self.__cell_data[col][row].append(result)
764 self.__cell_data[col][row].sort(key = lambda x: x['clin_when'], reverse = True)
765 else:
766 self.__cell_data[col][row] = [result]
767
768 # rebuild cell display string
769 vals2display = []
770 for sub_result in self.__cell_data[col][row]:
771
772 # is the sub_result technically abnormal ?
773 ind = gmTools.coalesce(sub_result['abnormality_indicator'], u'').strip()
774 if ind != u'':
775 lab_abnormality_indicator = u' (%s)' % ind[:3]
776 else:
777 lab_abnormality_indicator = u''
778 # - if noone reviewed - use what the lab thinks
779 if sub_result['is_technically_abnormal'] is None:
780 abnormality_indicator = lab_abnormality_indicator
781 # - if someone reviewed and decreed normality - use that
782 elif sub_result['is_technically_abnormal'] is False:
783 abnormality_indicator = u''
784 # - if someone reviewed and decreed abnormality ...
785 else:
786 # ... invent indicator if the lab did't use one
787 if lab_abnormality_indicator == u'':
788 # FIXME: calculate from min/max/range
789 abnormality_indicator = u' (%s)' % gmTools.u_plus_minus
790 # ... else use indicator the lab used
791 else:
792 abnormality_indicator = lab_abnormality_indicator
793
794 # is the sub_result relevant clinically ?
795 # FIXME: take into account primary_GP once we support that
796 sub_result_relevant = sub_result['is_clinically_relevant']
797 if sub_result_relevant is None:
798 # FIXME: calculate from clinical range
799 sub_result_relevant = False
800
801 missing_review = False
802 # warn on missing review if
803 # a) no review at all exists or
804 if not sub_result['reviewed']:
805 missing_review = True
806 # b) there is a review but
807 else:
808 # current user is reviewer and hasn't reviewed
809 if sub_result['you_are_responsible'] and not sub_result['review_by_you']:
810 missing_review = True
811
812 # can we display the full sub_result length ?
813 if len(sub_result['unified_val']) > 8:
814 tmp = u'%.7s%s' % (sub_result['unified_val'][:7], gmTools.u_ellipsis)
815 else:
816 tmp = u'%.8s' % sub_result['unified_val'][:8]
817
818 # abnormal ?
819 tmp = u'%s%.6s' % (tmp, abnormality_indicator)
820
821 # is there a comment ?
822 has_sub_result_comment = gmTools.coalesce (
823 gmTools.coalesce(sub_result['note_test_org'], sub_result['comment']),
824 u''
825 ).strip() != u''
826 if has_sub_result_comment:
827 tmp = u'%s %s' % (tmp, gmTools.u_ellipsis)
828
829 # lacking a review ?
830 if missing_review:
831 tmp = u'%s %s' % (tmp, gmTools.u_writing_hand)
832
833 # part of a multi-result cell ?
834 if len(self.__cell_data[col][row]) > 1:
835 tmp = u'%s %s' % (sub_result['clin_when'].strftime('%H:%M'), tmp)
836
837 vals2display.append(tmp)
838
839 self.SetCellValue(row, col, u'\n'.join(vals2display))
840 self.SetCellAlignment(row, col, horiz = wx.ALIGN_RIGHT, vert = wx.ALIGN_CENTRE)
841 # font = self.GetCellFont(row, col)
842 # if not font.IsFixedWidth():
843 # font.SetFamily(family = wx.FONTFAMILY_MODERN)
844 # FIXME: what about partial sub results being relevant ??
845 if sub_result_relevant:
846 font = self.GetCellFont(row, col)
847 self.SetCellTextColour(row, col, 'firebrick')
848 font.SetWeight(wx.FONTWEIGHT_BOLD)
849 self.SetCellFont(row, col, font)
850 # self.SetCellFont(row, col, font)
851
852 self.AutoSize()
853 self.EndBatch()
854 return
855 #------------------------------------------------------------
857 self.BeginBatch()
858 self.ClearGrid()
859 # Windows cannot do nothing, it rather decides to assert()
860 # on thinking it is supposed to do nothing
861 if self.GetNumberRows() > 0:
862 self.DeleteRows(pos = 0, numRows = self.GetNumberRows())
863 if self.GetNumberCols() > 0:
864 self.DeleteCols(pos = 0, numCols = self.GetNumberCols())
865 self.EndBatch()
866 self.__cell_data = {}
867 self.__row_label_data = []
868 #------------------------------------------------------------
870 # display test info (unified, which tests are grouped, which panels
871 # they belong to include details about test types included,
872
873 # sometimes, for some reason, there is no row and
874 # wxPython still tries to find a tooltip for it
875 try:
876 tt = self.__row_label_data[row]
877 except IndexError:
878 return u' '
879
880 return tt.format(patient = self.__patient.ID)
881 #------------------------------------------------------------
883 try:
884 d = self.__cell_data[col][row]
885 except KeyError:
886 # FIXME: maybe display the most recent or when the most recent was ?
887 d = None
888
889 if d is None:
890 return u' '
891
892 is_multi_cell = False
893 if len(d) > 1:
894 is_multi_cell = True
895 d = d[0]
896
897 tt = u''
898 # header
899 if is_multi_cell:
900 tt += _(u'Details of most recent (topmost) result ! \n')
901 tt += d.format(with_review = True, with_evaluation = True, with_ranges = True)
902 return tt
903 #------------------------------------------------------------
904 # internal helpers
905 #------------------------------------------------------------
907 self.CreateGrid(0, 1)
908 self.EnableEditing(0)
909 self.EnableDragGridSize(1)
910 self.SetMinSize(wx.DefaultSize)
911
912 # setting this screws up the labels: they are cut off and displaced
913 #self.SetColLabelAlignment(wx.ALIGN_CENTER, wx.ALIGN_BOTTOM)
914
915 self.SetRowLabelSize(wx.grid.GRID_AUTOSIZE) # starting with 2.8.8
916 #self.SetRowLabelSize(150)
917 self.SetRowLabelAlignment(horiz = wx.ALIGN_LEFT, vert = wx.ALIGN_CENTRE)
918
919 # add link to left upper corner
920 dbcfg = gmCfg.cCfgSQL()
921 url = dbcfg.get2 (
922 option = u'external.urls.measurements_encyclopedia',
923 workplace = gmSurgery.gmCurrentPractice().active_workplace,
924 bias = 'user',
925 default = u'http://www.laborlexikon.de'
926 )
927
928 self.__WIN_corner = self.GetGridCornerLabelWindow() # a wx.Window instance
929
930 LNK_lab = wx.lib.hyperlink.HyperLinkCtrl (
931 self.__WIN_corner,
932 -1,
933 label = _('Tests'),
934 style = wx.HL_DEFAULT_STYLE # wx.TE_READONLY|wx.TE_CENTRE| wx.NO_BORDER |
935 )
936 LNK_lab.SetURL(url)
937 LNK_lab.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BACKGROUND))
938 LNK_lab.SetToolTipString(_(
939 'Navigate to an encyclopedia of measurements\n'
940 'and test methods on the web.\n'
941 '\n'
942 ' <%s>'
943 ) % url)
944
945 SZR_inner = wx.BoxSizer(wx.HORIZONTAL)
946 SZR_inner.Add((20, 20), 1, wx.EXPAND, 0) # spacer
947 SZR_inner.Add(LNK_lab, 0, wx.ALIGN_CENTER_VERTICAL, 0) #wx.ALIGN_CENTER wx.EXPAND
948 SZR_inner.Add((20, 20), 1, wx.EXPAND, 0) # spacer
949
950 SZR_corner = wx.BoxSizer(wx.VERTICAL)
951 SZR_corner.Add((20, 20), 1, wx.EXPAND, 0) # spacer
952 SZR_corner.AddWindow(SZR_inner, 0, wx.EXPAND) # inner sizer with centered hyperlink
953 SZR_corner.Add((20, 20), 1, wx.EXPAND, 0) # spacer
954
955 self.__WIN_corner.SetSizer(SZR_corner)
956 SZR_corner.Fit(self.__WIN_corner)
957 #------------------------------------------------------------
960 #------------------------------------------------------------
961 - def __cells_to_data(self, cells=None, exclude_multi_cells=False, auto_include_multi_cells=False):
962 """List of <cells> must be in row / col order."""
963 data = []
964 for row, col in cells:
965 try:
966 # cell data is stored col / row
967 data_list = self.__cell_data[col][row]
968 except KeyError:
969 continue
970
971 if len(data_list) == 1:
972 data.append(data_list[0])
973 continue
974
975 if exclude_multi_cells:
976 gmDispatcher.send(signal = u'statustext', msg = _('Excluding multi-result field from further processing.'))
977 continue
978
979 if auto_include_multi_cells:
980 data.extend(data_list)
981 continue
982
983 data_to_include = self.__get_choices_from_multi_cell(cell_data = data_list)
984 if data_to_include is None:
985 continue
986 data.extend(data_to_include)
987
988 return data
989 #------------------------------------------------------------
991 data = gmListWidgets.get_choices_from_list (
992 parent = self,
993 msg = _(
994 'Your selection includes a field with multiple results.\n'
995 '\n'
996 'Please select the individual results you want to work on:'
997 ),
998 caption = _('Selecting test results'),
999 choices = [ [d['clin_when'], u'%s: %s' % (d['abbrev_tt'], d['name_tt']), d['unified_val']] for d in cell_data ],
1000 columns = [ _('Date / Time'), _('Test'), _('Result') ],
1001 data = cell_data,
1002 single_selection = single_selection
1003 )
1004 return data
1005 #------------------------------------------------------------
1006 # event handling
1007 #------------------------------------------------------------
1009 # dynamic tooltips: GridWindow, GridRowLabelWindow, GridColLabelWindow, GridCornerLabelWindow
1010 self.GetGridWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_cells)
1011 self.GetGridRowLabelWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_row_labels)
1012 #self.GetGridColLabelWindow().Bind(wx.EVT_MOTION, self.__on_mouse_over_col_labels)
1013
1014 # sizing left upper corner window
1015 self.Bind(wx.EVT_SIZE, self.__resize_corner_window)
1016
1017 # editing cells
1018 self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.__on_cell_left_dclicked)
1019 #------------------------------------------------------------
1021 col = evt.GetCol()
1022 row = evt.GetRow()
1023
1024 # empty cell, perhaps ?
1025 try:
1026 self.__cell_data[col][row]
1027 except KeyError:
1028 # FIXME: invoke editor for adding value for day of that column
1029 # FIMXE: and test of that row
1030 return
1031
1032 if len(self.__cell_data[col][row]) > 1:
1033 data = self.__get_choices_from_multi_cell(cell_data = self.__cell_data[col][row], single_selection = True)
1034 else:
1035 data = self.__cell_data[col][row][0]
1036
1037 if data is None:
1038 return
1039
1040 edit_measurement(parent = self, measurement = data, single_entry = True)
1041 #------------------------------------------------------------
1042 # def OnMouseMotionRowLabel(self, evt):
1043 # x, y = self.CalcUnscrolledPosition(evt.GetPosition())
1044 # row = self.YToRow(y)
1045 # label = self.table().GetRowHelpValue(row)
1046 # self.GetGridRowLabelWindow().SetToolTipString(label or "")
1047 # evt.Skip()
1049
1050 # Use CalcUnscrolledPosition() to get the mouse position within the
1051 # entire grid including what's offscreen
1052 x, y = self.CalcUnscrolledPosition(evt.GetX(), evt.GetY())
1053
1054 row = self.YToRow(y)
1055
1056 if self.__prev_label_row == row:
1057 return
1058
1059 self.__prev_label_row == row
1060
1061 evt.GetEventObject().SetToolTipString(self.get_row_tooltip(row = row))
1062 #------------------------------------------------------------
1063 # def OnMouseMotionColLabel(self, evt):
1064 # x, y = self.CalcUnscrolledPosition(evt.GetPosition())
1065 # col = self.XToCol(x)
1066 # label = self.table().GetColHelpValue(col)
1067 # self.GetGridColLabelWindow().SetToolTipString(label or "")
1068 # evt.Skip()
1069 #------------------------------------------------------------
1071 """Calculate where the mouse is and set the tooltip dynamically."""
1072
1073 # Use CalcUnscrolledPosition() to get the mouse position within the
1074 # entire grid including what's offscreen
1075 x, y = self.CalcUnscrolledPosition(evt.GetX(), evt.GetY())
1076
1077 # use this logic to prevent tooltips outside the actual cells
1078 # apply to GetRowSize, too
1079 # tot = 0
1080 # for col in xrange(self.NumberCols):
1081 # tot += self.GetColSize(col)
1082 # if xpos <= tot:
1083 # self.tool_tip.Tip = 'Tool tip for Column %s' % (
1084 # self.GetColLabelValue(col))
1085 # break
1086 # else: # mouse is in label area beyond the right-most column
1087 # self.tool_tip.Tip = ''
1088
1089 row, col = self.XYToCell(x, y)
1090
1091 if (row == self.__prev_row) and (col == self.__prev_col):
1092 return
1093
1094 self.__prev_row = row
1095 self.__prev_col = col
1096
1097 evt.GetEventObject().SetToolTipString(self.get_cell_tooltip(col=col, row=row))
1098 #------------------------------------------------------------
1099 # properties
1100 #------------------------------------------------------------
1104
1105 patient = property(lambda x:x, _set_patient)
1106 #------------------------------------------------------------
1110
1111 panel_to_show = property(lambda x:x, _set_panel_to_show)
1112 #------------------------------------------------------------
1116
1117 show_by_panel = property(lambda x:x, _set_show_by_panel)
1118 #================================================================
1119 from Gnumed.wxGladeWidgets import wxgMeasurementsPnl
1120
1121 -class cMeasurementsPnl(wxgMeasurementsPnl.wxgMeasurementsPnl, gmRegetMixin.cRegetOnPaintMixin):
1122 """Panel holding a grid with lab data. Used as notebook page."""
1123
1125
1126 wxgMeasurementsPnl.wxgMeasurementsPnl.__init__(self, *args, **kwargs)
1127 gmRegetMixin.cRegetOnPaintMixin.__init__(self)
1128 self.__init_ui()
1129 self.__register_interests()
1130 #--------------------------------------------------------
1131 # event handling
1132 #--------------------------------------------------------
1134 gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._on_pre_patient_selection)
1135 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._on_post_patient_selection)
1136 gmDispatcher.connect(signal = u'test_result_mod_db', receiver = self._schedule_data_reget)
1137 gmDispatcher.connect(signal = u'reviewed_test_results_mod_db', receiver = self._schedule_data_reget)
1138 #--------------------------------------------------------
1141 #--------------------------------------------------------
1144 #--------------------------------------------------------
1147 #--------------------------------------------------------
1151 #--------------------------------------------------------
1154 #--------------------------------------------------------
1157 #--------------------------------------------------------
1163 #--------------------------------------------------------
1166 #--------------------------------------------------------
1168 self.data_grid.sign_current_selection()
1169 #--------------------------------------------------------
1171 self.data_grid.plot_current_selection()
1172 #--------------------------------------------------------
1174 self.data_grid.delete_current_selection()
1175 #--------------------------------------------------------
1178 #--------------------------------------------------------
1180 if panel is None:
1181 self._TCTRL_panel_comment.SetValue(u'')
1182 self.panel_data_grid.panel_to_show = None
1183 self.panel_data_grid.Hide()
1184 else:
1185 pnl = self._PRW_panel.GetData(as_instance = True)
1186 self._TCTRL_panel_comment.SetValue(gmTools.coalesce (
1187 pnl['comment'],
1188 u''
1189 ))
1190 self.panel_data_grid.panel_to_show = pnl
1191 self.panel_data_grid.Show()
1192 self.Layout()
1193 #self.Refresh()
1194 #--------------------------------------------------------
1197 #--------------------------------------------------------
1199 self._TCTRL_panel_comment.SetValue(u'')
1200 if self._PRW_panel.GetValue().strip() == u'':
1201 self.panel_data_grid.panel_to_show = None
1202 self.panel_data_grid.Hide()
1203 self.Layout()
1204 #--------------------------------------------------------
1205 # internal API
1206 #--------------------------------------------------------
1208 self.__action_button_popup = wx.Menu(title = _('Perform on selected results:'))
1209
1210 menu_id = wx.NewId()
1211 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('Review and &sign')))
1212 wx.EVT_MENU(self.__action_button_popup, menu_id, self.__on_sign_current_selection)
1213
1214 menu_id = wx.NewId()
1215 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('Plot')))
1216 wx.EVT_MENU(self.__action_button_popup, menu_id, self.__on_plot_current_selection)
1217
1218 menu_id = wx.NewId()
1219 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('Export to &file')))
1220 #wx.EVT_MENU(self.__action_button_popup, menu_id, self.data_grid.current_selection_to_file)
1221 self.__action_button_popup.Enable(id = menu_id, enable = False)
1222
1223 menu_id = wx.NewId()
1224 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('Export to &clipboard')))
1225 #wx.EVT_MENU(self.__action_button_popup, menu_id, self.data_grid.current_selection_to_clipboard)
1226 self.__action_button_popup.Enable(id = menu_id, enable = False)
1227
1228 menu_id = wx.NewId()
1229 self.__action_button_popup.AppendItem(wx.MenuItem(self.__action_button_popup, menu_id, _('&Delete')))
1230 wx.EVT_MENU(self.__action_button_popup, menu_id, self.__on_delete_current_selection)
1231
1232 # FIXME: create inbox message to staff to phone patient to come in
1233 # FIXME: generate and let edit a SOAP narrative and include the values
1234
1235 self._PRW_panel.add_callback_on_selection(callback = self._on_panel_selected)
1236 self._PRW_panel.add_callback_on_modified(callback = self._on_panel_selection_modified)
1237
1238 self.panel_data_grid.show_by_panel = True
1239 self.panel_data_grid.panel_to_show = None
1240 self.panel_data_grid.Hide()
1241 self.Layout()
1242
1243 self._PRW_panel.SetFocus()
1244 #--------------------------------------------------------
1245 # reget mixin API
1246 #--------------------------------------------------------
1257
1258 #================================================================
1259 # editing widgets
1260 #================================================================
1261 from Gnumed.wxGladeWidgets import wxgMeasurementsReviewDlg
1262
1264
1266
1267 try:
1268 tests = kwargs['tests']
1269 del kwargs['tests']
1270 test_count = len(tests)
1271 try: del kwargs['test_count']
1272 except KeyError: pass
1273 except KeyError:
1274 tests = None
1275 test_count = kwargs['test_count']
1276 del kwargs['test_count']
1277
1278 wxgMeasurementsReviewDlg.wxgMeasurementsReviewDlg.__init__(self, *args, **kwargs)
1279
1280 if tests is None:
1281 msg = _('%s results selected. Too many to list individually.') % test_count
1282 else:
1283 msg = ' // '.join (
1284 [ u'%s: %s %s (%s)' % (
1285 t['unified_abbrev'],
1286 t['unified_val'],
1287 t['val_unit'],
1288 t['clin_when'].strftime('%x').decode(gmI18N.get_encoding())
1289 ) for t in tests
1290 ]
1291 )
1292
1293 self._LBL_tests.SetLabel(msg)
1294
1295 if test_count == 1:
1296 self._TCTRL_comment.Enable(True)
1297 self._TCTRL_comment.SetValue(gmTools.coalesce(tests[0]['review_comment'], u''))
1298 if tests[0]['you_are_responsible']:
1299 self._CHBOX_responsible.Enable(False)
1300
1301 self.Fit()
1302 #--------------------------------------------------------
1303 # event handling
1304 #--------------------------------------------------------
1310 #================================================================
1311 from Gnumed.wxGladeWidgets import wxgMeasurementEditAreaPnl
1312
1313 -class cMeasurementEditAreaPnl(wxgMeasurementEditAreaPnl.wxgMeasurementEditAreaPnl, gmEditArea.cGenericEditAreaMixin):
1314 """This edit area saves *new* measurements into the active patient only."""
1315
1317
1318 try:
1319 self.__default_date = kwargs['date']
1320 del kwargs['date']
1321 except KeyError:
1322 self.__default_date = None
1323
1324 wxgMeasurementEditAreaPnl.wxgMeasurementEditAreaPnl.__init__(self, *args, **kwargs)
1325 gmEditArea.cGenericEditAreaMixin.__init__(self)
1326
1327 self.__register_interests()
1328
1329 self.successful_save_msg = _('Successfully saved measurement.')
1330
1331 self._DPRW_evaluated.display_accuracy = gmDateTime.acc_minutes
1332 #--------------------------------------------------------
1333 # generic edit area mixin API
1334 #--------------------------------------------------------
1336 self._PRW_test.SetText(u'', None, True)
1337 self.__refresh_loinc_info()
1338 self.__refresh_previous_value()
1339 self.__update_units_context()
1340 self._TCTRL_result.SetValue(u'')
1341 self._PRW_units.SetText(u'', None, True)
1342 self._PRW_abnormality_indicator.SetText(u'', None, True)
1343 if self.__default_date is None:
1344 self._DPRW_evaluated.SetData(data = pyDT.datetime.now(tz = gmDateTime.gmCurrentLocalTimezone))
1345 else:
1346 self._DPRW_evaluated.SetData(data = None)
1347 self._TCTRL_note_test_org.SetValue(u'')
1348 self._PRW_intended_reviewer.SetData(gmStaff.gmCurrentProvider()['pk_staff'])
1349 self._PRW_problem.SetData()
1350 self._TCTRL_narrative.SetValue(u'')
1351 self._CHBOX_review.SetValue(False)
1352 self._CHBOX_abnormal.SetValue(False)
1353 self._CHBOX_relevant.SetValue(False)
1354 self._CHBOX_abnormal.Enable(False)
1355 self._CHBOX_relevant.Enable(False)
1356 self._TCTRL_review_comment.SetValue(u'')
1357 self._TCTRL_normal_min.SetValue(u'')
1358 self._TCTRL_normal_max.SetValue(u'')
1359 self._TCTRL_normal_range.SetValue(u'')
1360 self._TCTRL_target_min.SetValue(u'')
1361 self._TCTRL_target_max.SetValue(u'')
1362 self._TCTRL_target_range.SetValue(u'')
1363 self._TCTRL_norm_ref_group.SetValue(u'')
1364
1365 self._PRW_test.SetFocus()
1366 #--------------------------------------------------------
1368 self._PRW_test.SetData(data = self.data['pk_test_type'])
1369 self.__refresh_loinc_info()
1370 self.__refresh_previous_value()
1371 self.__update_units_context()
1372 self._TCTRL_result.SetValue(self.data['unified_val'])
1373 self._PRW_units.SetText(self.data['val_unit'], self.data['val_unit'], True)
1374 self._PRW_abnormality_indicator.SetText (
1375 gmTools.coalesce(self.data['abnormality_indicator'], u''),
1376 gmTools.coalesce(self.data['abnormality_indicator'], u''),
1377 True
1378 )
1379 self._DPRW_evaluated.SetData(data = self.data['clin_when'])
1380 self._TCTRL_note_test_org.SetValue(gmTools.coalesce(self.data['note_test_org'], u''))
1381 self._PRW_intended_reviewer.SetData(self.data['pk_intended_reviewer'])
1382 self._PRW_problem.SetData(self.data['pk_episode'])
1383 self._TCTRL_narrative.SetValue(gmTools.coalesce(self.data['comment'], u''))
1384 self._CHBOX_review.SetValue(False)
1385 self._CHBOX_abnormal.SetValue(gmTools.coalesce(self.data['is_technically_abnormal'], False))
1386 self._CHBOX_relevant.SetValue(gmTools.coalesce(self.data['is_clinically_relevant'], False))
1387 self._CHBOX_abnormal.Enable(False)
1388 self._CHBOX_relevant.Enable(False)
1389 self._TCTRL_review_comment.SetValue(gmTools.coalesce(self.data['review_comment'], u''))
1390 self._TCTRL_normal_min.SetValue(unicode(gmTools.coalesce(self.data['val_normal_min'], u'')))
1391 self._TCTRL_normal_max.SetValue(unicode(gmTools.coalesce(self.data['val_normal_max'], u'')))
1392 self._TCTRL_normal_range.SetValue(gmTools.coalesce(self.data['val_normal_range'], u''))
1393 self._TCTRL_target_min.SetValue(unicode(gmTools.coalesce(self.data['val_target_min'], u'')))
1394 self._TCTRL_target_max.SetValue(unicode(gmTools.coalesce(self.data['val_target_max'], u'')))
1395 self._TCTRL_target_range.SetValue(gmTools.coalesce(self.data['val_target_range'], u''))
1396 self._TCTRL_norm_ref_group.SetValue(gmTools.coalesce(self.data['norm_ref_group'], u''))
1397
1398 self._TCTRL_result.SetFocus()
1399 #--------------------------------------------------------
1401 self._refresh_from_existing()
1402
1403 self._PRW_test.SetText(u'', None, True)
1404 self.__refresh_loinc_info()
1405 self.__refresh_previous_value()
1406 self.__update_units_context()
1407 self._TCTRL_result.SetValue(u'')
1408 self._PRW_units.SetText(u'', None, True)
1409 self._PRW_abnormality_indicator.SetText(u'', None, True)
1410 # self._DPRW_evaluated
1411 self._TCTRL_note_test_org.SetValue(u'')
1412 self._TCTRL_narrative.SetValue(u'')
1413 self._CHBOX_review.SetValue(False)
1414 self._CHBOX_abnormal.SetValue(False)
1415 self._CHBOX_relevant.SetValue(False)
1416 self._CHBOX_abnormal.Enable(False)
1417 self._CHBOX_relevant.Enable(False)
1418 self._TCTRL_review_comment.SetValue(u'')
1419 self._TCTRL_normal_min.SetValue(u'')
1420 self._TCTRL_normal_max.SetValue(u'')
1421 self._TCTRL_normal_range.SetValue(u'')
1422 self._TCTRL_target_min.SetValue(u'')
1423 self._TCTRL_target_max.SetValue(u'')
1424 self._TCTRL_target_range.SetValue(u'')
1425 self._TCTRL_norm_ref_group.SetValue(u'')
1426
1427 self._PRW_test.SetFocus()
1428 #--------------------------------------------------------
1430
1431 validity = True
1432
1433 if not self._DPRW_evaluated.is_valid_timestamp():
1434 self._DPRW_evaluated.display_as_valid(False)
1435 validity = False
1436 else:
1437 self._DPRW_evaluated.display_as_valid(True)
1438
1439 if self._TCTRL_result.GetValue().strip() == u'':
1440 validity = False
1441 self.display_ctrl_as_valid(self._TCTRL_result, False)
1442 else:
1443 self.display_ctrl_as_valid(self._TCTRL_result, True)
1444
1445 if self._PRW_problem.GetValue().strip() == u'':
1446 self._PRW_problem.display_as_valid(False)
1447 validity = False
1448 else:
1449 self._PRW_problem.display_as_valid(True)
1450
1451 if self._PRW_test.GetValue().strip() == u'':
1452 self._PRW_test.display_as_valid(False)
1453 validity = False
1454 else:
1455 self._PRW_test.display_as_valid(True)
1456
1457 if self._PRW_intended_reviewer.GetData() is None:
1458 self._PRW_intended_reviewer.display_as_valid(False)
1459 validity = False
1460 else:
1461 self._PRW_intended_reviewer.display_as_valid(True)
1462
1463 if self._PRW_units.GetValue().strip() == u'':
1464 self._PRW_units.display_as_valid(False)
1465 validity = False
1466 else:
1467 self._PRW_units.display_as_valid(True)
1468
1469 ctrls = [self._TCTRL_normal_min, self._TCTRL_normal_max, self._TCTRL_target_min, self._TCTRL_target_max]
1470 for widget in ctrls:
1471 val = widget.GetValue().strip()
1472 if val == u'':
1473 continue
1474 try:
1475 decimal.Decimal(val.replace(',', u'.', 1))
1476 self.display_ctrl_as_valid(widget, True)
1477 except:
1478 validity = False
1479 self.display_ctrl_as_valid(widget, False)
1480
1481 if validity is False:
1482 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save result. Invalid or missing essential input.'))
1483
1484 return validity
1485 #--------------------------------------------------------
1487
1488 emr = gmPerson.gmCurrentPatient().get_emr()
1489
1490 success, result = gmTools.input2decimal(self._TCTRL_result.GetValue())
1491 if success:
1492 v_num = result
1493 v_al = None
1494 else:
1495 v_al = self._TCTRL_result.GetValue().strip()
1496 v_num = None
1497
1498 pk_type = self._PRW_test.GetData()
1499 if pk_type is None:
1500 tt = gmPathLab.create_measurement_type (
1501 lab = None,
1502 abbrev = self._PRW_test.GetValue().strip(),
1503 name = self._PRW_test.GetValue().strip(),
1504 unit = gmTools.coalesce(self._PRW_units.GetData(), self._PRW_units.GetValue()).strip()
1505 )
1506 pk_type = tt['pk_test_type']
1507
1508 tr = emr.add_test_result (
1509 episode = self._PRW_problem.GetData(can_create=True, is_open=False),
1510 type = pk_type,
1511 intended_reviewer = self._PRW_intended_reviewer.GetData(),
1512 val_num = v_num,
1513 val_alpha = v_al,
1514 unit = self._PRW_units.GetValue()
1515 )
1516
1517 tr['clin_when'] = self._DPRW_evaluated.GetData().get_pydt()
1518
1519 ctrls = [
1520 ('abnormality_indicator', self._PRW_abnormality_indicator),
1521 ('note_test_org', self._TCTRL_note_test_org),
1522 ('comment', self._TCTRL_narrative),
1523 ('val_normal_range', self._TCTRL_normal_range),
1524 ('val_target_range', self._TCTRL_target_range),
1525 ('norm_ref_group', self._TCTRL_norm_ref_group)
1526 ]
1527 for field, widget in ctrls:
1528 tr[field] = widget.GetValue().strip()
1529
1530 ctrls = [
1531 ('val_normal_min', self._TCTRL_normal_min),
1532 ('val_normal_max', self._TCTRL_normal_max),
1533 ('val_target_min', self._TCTRL_target_min),
1534 ('val_target_max', self._TCTRL_target_max)
1535 ]
1536 for field, widget in ctrls:
1537 val = widget.GetValue().strip()
1538 if val == u'':
1539 tr[field] = None
1540 else:
1541 tr[field] = decimal.Decimal(val.replace(',', u'.', 1))
1542
1543 tr.save_payload()
1544
1545 if self._CHBOX_review.GetValue() is True:
1546 tr.set_review (
1547 technically_abnormal = self._CHBOX_abnormal.GetValue(),
1548 clinically_relevant = self._CHBOX_relevant.GetValue(),
1549 comment = gmTools.none_if(self._TCTRL_review_comment.GetValue().strip(), u''),
1550 make_me_responsible = False
1551 )
1552
1553 self.data = tr
1554
1555 wx.CallAfter (
1556 plot_adjacent_measurements,
1557 test = self.data,
1558 plot_singular_result = False,
1559 use_default_template = True
1560 )
1561
1562 return True
1563 #--------------------------------------------------------
1565
1566 success, result = gmTools.input2decimal(self._TCTRL_result.GetValue())
1567 if success:
1568 v_num = result
1569 v_al = None
1570 else:
1571 v_num = None
1572 v_al = self._TCTRL_result.GetValue().strip()
1573
1574 pk_type = self._PRW_test.GetData()
1575 if pk_type is None:
1576 tt = gmPathLab.create_measurement_type (
1577 lab = None,
1578 abbrev = self._PRW_test.GetValue().strip(),
1579 name = self._PRW_test.GetValue().strip(),
1580 unit = gmTools.none_if(self._PRW_units.GetValue().strip(), u'')
1581 )
1582 pk_type = tt['pk_test_type']
1583
1584 tr = self.data
1585
1586 tr['pk_episode'] = self._PRW_problem.GetData(can_create=True, is_open=False)
1587 tr['pk_test_type'] = pk_type
1588 tr['pk_intended_reviewer'] = self._PRW_intended_reviewer.GetData()
1589 tr['val_num'] = v_num
1590 tr['val_alpha'] = v_al
1591 tr['val_unit'] = gmTools.coalesce(self._PRW_units.GetData(), self._PRW_units.GetValue()).strip()
1592 tr['clin_when'] = self._DPRW_evaluated.GetData().get_pydt()
1593
1594 ctrls = [
1595 ('abnormality_indicator', self._PRW_abnormality_indicator),
1596 ('note_test_org', self._TCTRL_note_test_org),
1597 ('comment', self._TCTRL_narrative),
1598 ('val_normal_range', self._TCTRL_normal_range),
1599 ('val_target_range', self._TCTRL_target_range),
1600 ('norm_ref_group', self._TCTRL_norm_ref_group)
1601 ]
1602 for field, widget in ctrls:
1603 tr[field] = widget.GetValue().strip()
1604
1605 ctrls = [
1606 ('val_normal_min', self._TCTRL_normal_min),
1607 ('val_normal_max', self._TCTRL_normal_max),
1608 ('val_target_min', self._TCTRL_target_min),
1609 ('val_target_max', self._TCTRL_target_max)
1610 ]
1611 for field, widget in ctrls:
1612 val = widget.GetValue().strip()
1613 if val == u'':
1614 tr[field] = None
1615 else:
1616 tr[field] = decimal.Decimal(val.replace(',', u'.', 1))
1617
1618 tr.save_payload()
1619
1620 if self._CHBOX_review.GetValue() is True:
1621 tr.set_review (
1622 technically_abnormal = self._CHBOX_abnormal.GetValue(),
1623 clinically_relevant = self._CHBOX_relevant.GetValue(),
1624 comment = gmTools.none_if(self._TCTRL_review_comment.GetValue().strip(), u''),
1625 make_me_responsible = False
1626 )
1627
1628 wx.CallAfter (
1629 plot_adjacent_measurements,
1630 test = self.data,
1631 plot_singular_result = False,
1632 use_default_template = True
1633 )
1634
1635 return True
1636 #--------------------------------------------------------
1637 # event handling
1638 #--------------------------------------------------------
1640 self._PRW_test.add_callback_on_lose_focus(self._on_leave_test_prw)
1641 self._PRW_abnormality_indicator.add_callback_on_lose_focus(self._on_leave_indicator_prw)
1642 #--------------------------------------------------------
1644 self.__refresh_loinc_info()
1645 self.__refresh_previous_value()
1646 self.__update_units_context()
1647 #--------------------------------------------------------
1649 # if the user hasn't explicitly enabled reviewing
1650 if not self._CHBOX_review.GetValue():
1651 self._CHBOX_abnormal.SetValue(self._PRW_abnormality_indicator.GetValue().strip() != u'')
1652 #--------------------------------------------------------
1654 self._CHBOX_abnormal.Enable(self._CHBOX_review.GetValue())
1655 self._CHBOX_relevant.Enable(self._CHBOX_review.GetValue())
1656 self._TCTRL_review_comment.Enable(self._CHBOX_review.GetValue())
1657 #--------------------------------------------------------
1674 #--------------------------------------------------------
1675 # internal helpers
1676 #--------------------------------------------------------
1678
1679 self._PRW_units.unset_context(context = u'loinc')
1680
1681 tt = self._PRW_test.GetData(as_instance = True)
1682
1683 if tt is None:
1684 self._PRW_units.unset_context(context = u'pk_type')
1685 if self._PRW_test.GetValue().strip() == u'':
1686 self._PRW_units.unset_context(context = u'test_name')
1687 else:
1688 self._PRW_units.set_context(context = u'test_name', val = self._PRW_test.GetValue().strip())
1689 return
1690
1691 self._PRW_units.set_context(context = u'pk_type', val = tt['pk_test_type'])
1692 self._PRW_units.set_context(context = u'test_name', val = tt['name'])
1693
1694 if tt['loinc'] is None:
1695 return
1696
1697 self._PRW_units.set_context(context = u'loinc', val = tt['loinc'])
1698 #--------------------------------------------------------
1700
1701 self._TCTRL_loinc.SetValue(u'')
1702
1703 if self._PRW_test.GetData() is None:
1704 return
1705
1706 tt = self._PRW_test.GetData(as_instance = True)
1707
1708 if tt['loinc'] is None:
1709 return
1710
1711 info = gmLOINC.loinc2term(loinc = tt['loinc'])
1712 if len(info) == 0:
1713 self._TCTRL_loinc.SetValue(u'')
1714 return
1715
1716 self._TCTRL_loinc.SetValue(u'%s: %s' % (tt['loinc'], info[0]))
1717 #--------------------------------------------------------
1719 self._TCTRL_previous_value.SetValue(u'')
1720 # it doesn't make much sense to show the most
1721 # recent value when editing an existing one
1722 if self.data is not None:
1723 return
1724 if self._PRW_test.GetData() is None:
1725 return
1726 tt = self._PRW_test.GetData(as_instance = True)
1727 most_recent = tt.get_most_recent_results (
1728 no_of_results = 1,
1729 patient = gmPerson.gmCurrentPatient().ID
1730 )
1731 if most_recent is None:
1732 return
1733 self._TCTRL_previous_value.SetValue(_('%s ago: %s%s%s - %s') % (
1734 gmDateTime.format_interval_medically(gmDateTime.pydt_now_here() - most_recent['clin_when']),
1735 most_recent['unified_val'],
1736 most_recent['val_unit'],
1737 gmTools.coalesce(most_recent['abnormality_indicator'], u'', u' (%s)'),
1738 most_recent['name_tt']
1739 ))
1740
1741 #================================================================
1742 # measurement type handling
1743 #================================================================
1745
1746 if parent is None:
1747 parent = wx.GetApp().GetTopWindow()
1748
1749 if msg is None:
1750 msg = _('Pick the relevant measurement types.')
1751
1752 if right_column is None:
1753 right_columns = [_('Picked')]
1754 else:
1755 right_columns = [right_column]
1756
1757 picker = gmListWidgets.cItemPickerDlg(parent, -1, msg = msg)
1758 picker.set_columns(columns = [_('Known measurement types')], columns_right = right_columns)
1759 types = gmPathLab.get_measurement_types(order_by = 'unified_abbrev')
1760 picker.set_choices (
1761 choices = [
1762 u'%s: %s%s' % (
1763 t['unified_abbrev'],
1764 t['unified_name'],
1765 gmTools.coalesce(t['name_org'], u'', u' (%s)')
1766 )
1767 for t in types
1768 ],
1769 data = types
1770 )
1771 if picks is not None:
1772 picker.set_picks (
1773 picks = [
1774 u'%s: %s%s' % (
1775 p['unified_abbrev'],
1776 p['unified_name'],
1777 gmTools.coalesce(p['name_org'], u'', u' (%s)')
1778 )
1779 for p in picks
1780 ],
1781 data = picks
1782 )
1783 result = picker.ShowModal()
1784
1785 if result == wx.ID_CANCEL:
1786 picker.Destroy()
1787 return None
1788
1789 picks = picker.picks
1790 picker.Destroy()
1791 return picks
1792
1793 #----------------------------------------------------------------
1795
1796 if parent is None:
1797 parent = wx.GetApp().GetTopWindow()
1798
1799 #------------------------------------------------------------
1800 def edit(test_type=None):
1801 ea = cMeasurementTypeEAPnl(parent = parent, id = -1, type = test_type)
1802 dlg = gmEditArea.cGenericEditAreaDlg2 (
1803 parent = parent,
1804 id = -1,
1805 edit_area = ea,
1806 single_entry = gmTools.bool2subst((test_type is None), False, True)
1807 )
1808 dlg.SetTitle(gmTools.coalesce(test_type, _('Adding measurement type'), _('Editing measurement type')))
1809
1810 if dlg.ShowModal() == wx.ID_OK:
1811 dlg.Destroy()
1812 return True
1813
1814 dlg.Destroy()
1815 return False
1816 #------------------------------------------------------------
1817 def delete(measurement_type):
1818 if measurement_type.in_use:
1819 gmDispatcher.send (
1820 signal = 'statustext',
1821 beep = True,
1822 msg = _('Cannot delete measurement type [%s (%s)] because it is in use.') % (measurement_type['name'], measurement_type['abbrev'])
1823 )
1824 return False
1825 gmPathLab.delete_measurement_type(measurement_type = measurement_type['pk_test_type'])
1826 return True
1827 #------------------------------------------------------------
1828 def get_tooltip(test_type):
1829 return test_type.format()
1830 #------------------------------------------------------------
1831 def refresh(lctrl):
1832 mtypes = gmPathLab.get_measurement_types(order_by = 'name, abbrev')
1833 items = [ [
1834 m['abbrev'],
1835 m['name'],
1836 gmTools.coalesce(m['conversion_unit'], u''),
1837 gmTools.coalesce(m['loinc'], u''),
1838 gmTools.coalesce(m['comment_type'], u''),
1839 gmTools.coalesce(m['name_org'], u'?'),
1840 gmTools.coalesce(m['comment_org'], u''),
1841 m['pk_test_type']
1842 ] for m in mtypes ]
1843 lctrl.set_string_items(items)
1844 lctrl.set_data(mtypes)
1845 #------------------------------------------------------------
1846 msg = _(
1847 '\n'
1848 'These are the measurement types currently defined in GNUmed.\n'
1849 '\n'
1850 )
1851
1852 gmListWidgets.get_choices_from_list (
1853 parent = parent,
1854 msg = msg,
1855 caption = _('Showing measurement types.'),
1856 columns = [ _('Abbrev'), _('Name'), _('Unit'), _('LOINC'), _('Comment'), _('Org'), _('Comment'), u'#' ],
1857 single_selection = True,
1858 refresh_callback = refresh,
1859 edit_callback = edit,
1860 new_callback = edit,
1861 delete_callback = delete,
1862 list_tooltip_callback = get_tooltip
1863 )
1864 #----------------------------------------------------------------
1866
1868
1869 query = u"""
1870 SELECT DISTINCT ON (field_label)
1871 pk_test_type AS data,
1872 name
1873 || ' ('
1874 || coalesce (
1875 (SELECT unit || ' @ ' || organization FROM clin.v_test_orgs c_vto WHERE c_vto.pk_test_org = c_vtt.pk_test_org),
1876 '%(in_house)s'
1877 )
1878 || ')'
1879 AS field_label,
1880 name
1881 || ' ('
1882 || abbrev || ', '
1883 || coalesce(abbrev_meta || ': ' || name_meta || ', ', '')
1884 || coalesce (
1885 (SELECT unit || ' @ ' || organization FROM clin.v_test_orgs c_vto WHERE c_vto.pk_test_org = c_vtt.pk_test_org),
1886 '%(in_house)s'
1887 )
1888 || ')'
1889 AS list_label
1890 FROM
1891 clin.v_test_types c_vtt
1892 WHERE
1893 abbrev_meta %%(fragment_condition)s
1894 OR
1895 name_meta %%(fragment_condition)s
1896 OR
1897 abbrev %%(fragment_condition)s
1898 OR
1899 name %%(fragment_condition)s
1900 ORDER BY field_label
1901 LIMIT 50""" % {'in_house': _('generic / in house lab')}
1902
1903 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
1904 mp.setThresholds(1, 2, 4)
1905 mp.word_separators = '[ \t:@]+'
1906 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
1907 self.matcher = mp
1908 self.SetToolTipString(_('Select the type of measurement.'))
1909 self.selection_only = False
1910 #------------------------------------------------------------
1912 if self.GetData() is None:
1913 return None
1914
1915 return gmPathLab.cMeasurementType(aPK_obj = self.GetData())
1916 #----------------------------------------------------------------
1917 from Gnumed.wxGladeWidgets import wxgMeasurementTypeEAPnl
1918
1919 -class cMeasurementTypeEAPnl(wxgMeasurementTypeEAPnl.wxgMeasurementTypeEAPnl, gmEditArea.cGenericEditAreaMixin):
1920
1922
1923 try:
1924 data = kwargs['type']
1925 del kwargs['type']
1926 except KeyError:
1927 data = None
1928
1929 wxgMeasurementTypeEAPnl.wxgMeasurementTypeEAPnl.__init__(self, *args, **kwargs)
1930 gmEditArea.cGenericEditAreaMixin.__init__(self)
1931 self.mode = 'new'
1932 self.data = data
1933 if data is not None:
1934 self.mode = 'edit'
1935
1936 self.__init_ui()
1937
1938 #----------------------------------------------------------------
1940
1941 # name phraseweel
1942 query = u"""
1943 select distinct on (name)
1944 pk,
1945 name
1946 from clin.test_type
1947 where
1948 name %(fragment_condition)s
1949 order by name
1950 limit 50"""
1951 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
1952 mp.setThresholds(1, 2, 4)
1953 self._PRW_name.matcher = mp
1954 self._PRW_name.selection_only = False
1955 self._PRW_name.add_callback_on_lose_focus(callback = self._on_name_lost_focus)
1956
1957 # abbreviation
1958 query = u"""
1959 select distinct on (abbrev)
1960 pk,
1961 abbrev
1962 from clin.test_type
1963 where
1964 abbrev %(fragment_condition)s
1965 order by abbrev
1966 limit 50"""
1967 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
1968 mp.setThresholds(1, 2, 3)
1969 self._PRW_abbrev.matcher = mp
1970 self._PRW_abbrev.selection_only = False
1971
1972 # unit
1973 self._PRW_conversion_unit.selection_only = False
1974
1975 # loinc
1976 query = u"""
1977 SELECT DISTINCT ON (list_label)
1978 data,
1979 field_label,
1980 list_label
1981 FROM ((
1982
1983 SELECT
1984 loinc AS data,
1985 loinc AS field_label,
1986 (loinc || ': ' || abbrev || ' (' || name || ')') AS list_label
1987 FROM clin.test_type
1988 WHERE loinc %(fragment_condition)s
1989 LIMIT 50
1990
1991 ) UNION ALL (
1992
1993 SELECT
1994 code AS data,
1995 code AS field_label,
1996 (code || ': ' || term) AS list_label
1997 FROM ref.v_coded_terms
1998 WHERE
1999 coding_system = 'LOINC'
2000 AND
2001 lang = i18n.get_curr_lang()
2002 AND
2003 (code %(fragment_condition)s
2004 OR
2005 term %(fragment_condition)s)
2006 LIMIT 50
2007
2008 ) UNION ALL (
2009
2010 SELECT
2011 code AS data,
2012 code AS field_label,
2013 (code || ': ' || term) AS list_label
2014 FROM ref.v_coded_terms
2015 WHERE
2016 coding_system = 'LOINC'
2017 AND
2018 lang = 'en_EN'
2019 AND
2020 (code %(fragment_condition)s
2021 OR
2022 term %(fragment_condition)s)
2023 LIMIT 50
2024
2025 ) UNION ALL (
2026
2027 SELECT
2028 code AS data,
2029 code AS field_label,
2030 (code || ': ' || term) AS list_label
2031 FROM ref.v_coded_terms
2032 WHERE
2033 coding_system = 'LOINC'
2034 AND
2035 (code %(fragment_condition)s
2036 OR
2037 term %(fragment_condition)s)
2038 LIMIT 50
2039 )
2040 ) AS all_known_loinc
2041
2042 ORDER BY list_label
2043 LIMIT 50"""
2044 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
2045 mp.setThresholds(1, 2, 4)
2046 self._PRW_loinc.matcher = mp
2047 self._PRW_loinc.selection_only = False
2048 self._PRW_loinc.add_callback_on_lose_focus(callback = self._on_loinc_lost_focus)
2049 #----------------------------------------------------------------
2051
2052 test = self._PRW_name.GetValue().strip()
2053
2054 if test == u'':
2055 self._PRW_conversion_unit.unset_context(context = u'test_name')
2056 return
2057
2058 self._PRW_conversion_unit.set_context(context = u'test_name', val = test)
2059 #----------------------------------------------------------------
2061 loinc = self._PRW_loinc.GetData()
2062
2063 if loinc is None:
2064 self._TCTRL_loinc_info.SetValue(u'')
2065 self._PRW_conversion_unit.unset_context(context = u'loinc')
2066 return
2067
2068 self._PRW_conversion_unit.set_context(context = u'loinc', val = loinc)
2069
2070 info = gmLOINC.loinc2term(loinc = loinc)
2071 if len(info) == 0:
2072 self._TCTRL_loinc_info.SetValue(u'')
2073 return
2074
2075 self._TCTRL_loinc_info.SetValue(info[0])
2076 #----------------------------------------------------------------
2077 # generic Edit Area mixin API
2078 #----------------------------------------------------------------
2080
2081 has_errors = False
2082 for field in [self._PRW_name, self._PRW_abbrev, self._PRW_conversion_unit]:
2083 if field.GetValue().strip() in [u'', None]:
2084 has_errors = True
2085 field.display_as_valid(valid = False)
2086 else:
2087 field.display_as_valid(valid = True)
2088 field.Refresh()
2089
2090 return (not has_errors)
2091 #----------------------------------------------------------------
2093
2094 pk_org = self._PRW_test_org.GetData()
2095 if pk_org is None:
2096 pk_org = gmPathLab.create_test_org (
2097 name = gmTools.none_if(self._PRW_test_org.GetValue().strip(), u''),
2098 comment = gmTools.none_if(self._TCTRL_comment_org.GetValue().strip(), u'')
2099 )['pk_test_org']
2100
2101 tt = gmPathLab.create_measurement_type (
2102 lab = pk_org,
2103 abbrev = self._PRW_abbrev.GetValue().strip(),
2104 name = self._PRW_name.GetValue().strip(),
2105 unit = gmTools.coalesce (
2106 self._PRW_conversion_unit.GetData(),
2107 self._PRW_conversion_unit.GetValue()
2108 ).strip()
2109 )
2110 if self._PRW_loinc.GetData() is not None:
2111 tt['loinc'] = gmTools.none_if(self._PRW_loinc.GetData().strip(), u'')
2112 else:
2113 tt['loinc'] = gmTools.none_if(self._PRW_loinc.GetValue().strip(), u'')
2114 tt['comment_type'] = gmTools.none_if(self._TCTRL_comment_type.GetValue().strip(), u'')
2115 tt['pk_meta_test_type'] = self._PRW_meta_type.GetData()
2116
2117 tt.save()
2118
2119 self.data = tt
2120
2121 return True
2122 #----------------------------------------------------------------
2124
2125 pk_org = self._PRW_test_org.GetData()
2126 if pk_org is None:
2127 pk_org = gmPathLab.create_test_org (
2128 name = gmTools.none_if(self._PRW_test_org.GetValue().strip(), u''),
2129 comment = gmTools.none_if(self._TCTRL_comment_org.GetValue().strip(), u'')
2130 )['pk_test_org']
2131
2132 self.data['pk_test_org'] = pk_org
2133 self.data['abbrev'] = self._PRW_abbrev.GetValue().strip()
2134 self.data['name'] = self._PRW_name.GetValue().strip()
2135 self.data['conversion_unit'] = gmTools.coalesce (
2136 self._PRW_conversion_unit.GetData(),
2137 self._PRW_conversion_unit.GetValue()
2138 ).strip()
2139 if self._PRW_loinc.GetData() is not None:
2140 self.data['loinc'] = gmTools.none_if(self._PRW_loinc.GetData().strip(), u'')
2141 if self._PRW_loinc.GetData() is not None:
2142 self.data['loinc'] = gmTools.none_if(self._PRW_loinc.GetData().strip(), u'')
2143 else:
2144 self.data['loinc'] = gmTools.none_if(self._PRW_loinc.GetValue().strip(), u'')
2145 self.data['comment_type'] = gmTools.none_if(self._TCTRL_comment_type.GetValue().strip(), u'')
2146 self.data['pk_meta_test_type'] = self._PRW_meta_type.GetData()
2147 self.data.save()
2148
2149 return True
2150 #----------------------------------------------------------------
2152 self._PRW_name.SetText(u'', None, True)
2153 self._on_name_lost_focus()
2154 self._PRW_abbrev.SetText(u'', None, True)
2155 self._PRW_conversion_unit.SetText(u'', None, True)
2156 self._PRW_loinc.SetText(u'', None, True)
2157 self._on_loinc_lost_focus()
2158 self._TCTRL_comment_type.SetValue(u'')
2159 self._PRW_test_org.SetText(u'', None, True)
2160 self._TCTRL_comment_org.SetValue(u'')
2161 self._PRW_meta_type.SetText(u'', None, True)
2162
2163 self._PRW_name.SetFocus()
2164 #----------------------------------------------------------------
2166 self._PRW_name.SetText(self.data['name'], self.data['name'], True)
2167 self._on_name_lost_focus()
2168 self._PRW_abbrev.SetText(self.data['abbrev'], self.data['abbrev'], True)
2169 self._PRW_conversion_unit.SetText (
2170 gmTools.coalesce(self.data['conversion_unit'], u''),
2171 self.data['conversion_unit'],
2172 True
2173 )
2174 self._PRW_loinc.SetText (
2175 gmTools.coalesce(self.data['loinc'], u''),
2176 self.data['loinc'],
2177 True
2178 )
2179 self._on_loinc_lost_focus()
2180 self._TCTRL_comment_type.SetValue(gmTools.coalesce(self.data['comment_type'], u''))
2181 self._PRW_test_org.SetText (
2182 gmTools.coalesce(self.data['pk_test_org'], u'', self.data['name_org']),
2183 self.data['pk_test_org'],
2184 True
2185 )
2186 self._TCTRL_comment_org.SetValue(gmTools.coalesce(self.data['comment_org'], u''))
2187 if self.data['pk_meta_test_type'] is None:
2188 self._PRW_meta_type.SetText(u'', None, True)
2189 else:
2190 self._PRW_meta_type.SetText(u'%s: %s' % (self.data['abbrev_meta'], self.data['name_meta']), self.data['pk_meta_test_type'], True)
2191
2192 self._PRW_name.SetFocus()
2193 #----------------------------------------------------------------
2195 self._refresh_as_new()
2196 self._PRW_test_org.SetText (
2197 gmTools.coalesce(self.data['pk_test_org'], u'', self.data['name_org']),
2198 self.data['pk_test_org'],
2199 True
2200 )
2201 self._TCTRL_comment_org.SetValue(gmTools.coalesce(self.data['comment_org'], u''))
2202
2203 self._PRW_name.SetFocus()
2204
2205 #================================================================
2206 _SQL_units_from_test_results = u"""
2207 -- via clin.v_test_results.pk_type (for types already used in results)
2208 SELECT
2209 val_unit AS data,
2210 val_unit AS field_label,
2211 val_unit || ' (' || name_tt || ')' AS list_label,
2212 1 AS rank
2213 FROM
2214 clin.v_test_results
2215 WHERE
2216 (
2217 val_unit %(fragment_condition)s
2218 OR
2219 conversion_unit %(fragment_condition)s
2220 )
2221 %(ctxt_type_pk)s
2222 %(ctxt_test_name)s
2223 """
2224
2225 _SQL_units_from_test_types = u"""
2226 -- via clin.test_type (for types not yet used in results)
2227 SELECT
2228 conversion_unit AS data,
2229 conversion_unit AS field_label,
2230 conversion_unit || ' (' || name || ')' AS list_label,
2231 2 AS rank
2232 FROM
2233 clin.test_type
2234 WHERE
2235 conversion_unit %(fragment_condition)s
2236 %(ctxt_ctt)s
2237 """
2238
2239 _SQL_units_from_loinc_ipcc = u"""
2240 -- via ref.loinc.ipcc_units
2241 SELECT
2242 ipcc_units AS data,
2243 ipcc_units AS field_label,
2244 ipcc_units || ' (LOINC.ipcc: ' || term || ')' AS list_label,
2245 3 AS rank
2246 FROM
2247 ref.loinc
2248 WHERE
2249 ipcc_units %(fragment_condition)s
2250 %(ctxt_loinc)s
2251 %(ctxt_loinc_term)s
2252 """
2253
2254 _SQL_units_from_loinc_submitted = u"""
2255 -- via ref.loinc.submitted_units
2256 SELECT
2257 submitted_units AS data,
2258 submitted_units AS field_label,
2259 submitted_units || ' (LOINC.submitted:' || term || ')' AS list_label,
2260 3 AS rank
2261 FROM
2262 ref.loinc
2263 WHERE
2264 submitted_units %(fragment_condition)s
2265 %(ctxt_loinc)s
2266 %(ctxt_loinc_term)s
2267 """
2268
2269 _SQL_units_from_loinc_example = u"""
2270 -- via ref.loinc.example_units
2271 SELECT
2272 example_units AS data,
2273 example_units AS field_label,
2274 example_units || ' (LOINC.example: ' || term || ')' AS list_label,
2275 3 AS rank
2276 FROM
2277 ref.loinc
2278 WHERE
2279 example_units %(fragment_condition)s
2280 %(ctxt_loinc)s
2281 %(ctxt_loinc_term)s
2282 """
2283
2284 _SQL_units_from_atc = u"""
2285 -- via ref.atc.unit
2286 SELECT
2287 unit AS data,
2288 unit AS field_label,
2289 unit || ' (ATC: ' || term || ')' AS list_label,
2290 2 AS rank
2291 FROM
2292 ref.atc
2293 WHERE
2294 unit IS NOT NULL
2295 AND
2296 unit %(fragment_condition)s
2297 """
2298
2299 _SQL_units_from_consumable_substance = u"""
2300 -- via ref.consumable_substance.unit
2301 SELECT
2302 unit AS data,
2303 unit AS field_label,
2304 unit || ' (' || description || ')' AS list_label,
2305 2 AS rank
2306 FROM
2307 ref.consumable_substance
2308 WHERE
2309 unit %(fragment_condition)s
2310 %(ctxt_substance)s
2311 """
2312
2313 #----------------------------------------------------------------
2315
2317
2318 query = u"""
2319 SELECT DISTINCT ON (data)
2320 data,
2321 field_label,
2322 list_label
2323 FROM (
2324
2325 SELECT
2326 data,
2327 field_label,
2328 list_label,
2329 rank
2330 FROM (
2331 (%s) UNION ALL
2332 (%s) UNION ALL
2333 (%s) UNION ALL
2334 (%s) UNION ALL
2335 (%s) UNION ALL
2336 (%s) UNION ALL
2337 (%s)
2338 ) AS all_matching_units
2339 WHERE data IS NOT NULL
2340 ORDER BY rank
2341
2342 ) AS ranked_matching_units
2343 LIMIT 50""" % (
2344 _SQL_units_from_test_results,
2345 _SQL_units_from_test_types,
2346 _SQL_units_from_loinc_ipcc,
2347 _SQL_units_from_loinc_submitted,
2348 _SQL_units_from_loinc_example,
2349 _SQL_units_from_atc,
2350 _SQL_units_from_consumable_substance
2351 )
2352
2353 ctxt = {
2354 'ctxt_type_pk': {
2355 'where_part': u'AND pk_test_type = %(pk_type)s',
2356 'placeholder': u'pk_type'
2357 },
2358 'ctxt_test_name': {
2359 'where_part': u'AND %(test_name)s IN (name_tt, name_meta, abbrev_meta)',
2360 'placeholder': u'test_name'
2361 },
2362 'ctxt_ctt': {
2363 'where_part': u'AND %(test_name)s IN (name, abbrev)',
2364 'placeholder': u'test_name'
2365 },
2366 'ctxt_loinc': {
2367 'where_part': u'AND code = %(loinc)s',
2368 'placeholder': u'loinc'
2369 },
2370 'ctxt_loinc_term': {
2371 'where_part': u'AND term ~* %(test_name)s',
2372 'placeholder': u'test_name'
2373 },
2374 'ctxt_substance': {
2375 'where_part': u'AND description ~* %(substance)s',
2376 'placeholder': u'substance'
2377 }
2378 }
2379
2380 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query, context = ctxt)
2381 mp.setThresholds(1, 2, 4)
2382 #mp.print_queries = True
2383 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
2384 self.matcher = mp
2385 self.SetToolTipString(_('Select the desired unit for the amount or measurement.'))
2386 self.selection_only = False
2387 self.phrase_separators = u'[;|]+'
2388 #================================================================
2389
2390 #================================================================
2392
2394
2395 query = u"""
2396 select distinct abnormality_indicator,
2397 abnormality_indicator, abnormality_indicator
2398 from clin.v_test_results
2399 where
2400 abnormality_indicator %(fragment_condition)s
2401 order by abnormality_indicator
2402 limit 25"""
2403
2404 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
2405 mp.setThresholds(1, 1, 2)
2406 mp.ignored_chars = "[.'\\\[\]#$%_]+" + '"'
2407 mp.word_separators = '[ \t&:]+'
2408 gmPhraseWheel.cPhraseWheel.__init__ (
2409 self,
2410 *args,
2411 **kwargs
2412 )
2413 self.matcher = mp
2414 self.SetToolTipString(_('Select an indicator for the level of abnormality.'))
2415 self.selection_only = False
2416
2417 #================================================================
2418 # measurement org widgets / functions
2419 #----------------------------------------------------------------
2421 ea = cMeasurementOrgEAPnl(parent = parent, id = -1)
2422 ea.data = org
2423 ea.mode = gmTools.coalesce(org, 'new', 'edit')
2424 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea)
2425 dlg.SetTitle(gmTools.coalesce(org, _('Adding new diagnostic org'), _('Editing diagnostic org')))
2426 if dlg.ShowModal() == wx.ID_OK:
2427 dlg.Destroy()
2428 return True
2429 dlg.Destroy()
2430 return False
2431 #----------------------------------------------------------------
2433
2434 if parent is None:
2435 parent = wx.GetApp().GetTopWindow()
2436
2437 #------------------------------------------------------------
2438 def edit(org=None):
2439 return edit_measurement_org(parent = parent, org = org)
2440 #------------------------------------------------------------
2441 def refresh(lctrl):
2442 orgs = gmPathLab.get_test_orgs()
2443 lctrl.set_string_items ([
2444 (o['unit'], o['organization'], gmTools.coalesce(o['test_org_contact'], u''), gmTools.coalesce(o['comment'], u''), o['pk_test_org'])
2445 for o in orgs
2446 ])
2447 lctrl.set_data(orgs)
2448 #------------------------------------------------------------
2449 def delete(test_org):
2450 gmPathLab.delete_test_org(test_org = test_org['pk_test_org'])
2451 return True
2452 #------------------------------------------------------------
2453 gmListWidgets.get_choices_from_list (
2454 parent = parent,
2455 msg = _('\nThese are the diagnostic orgs (path labs etc) currently defined in GNUmed.\n\n'),
2456 caption = _('Showing diagnostic orgs.'),
2457 columns = [_('Name'), _('Organization'), _('Contact'), _('Comment'), u'#'],
2458 single_selection = True,
2459 refresh_callback = refresh,
2460 edit_callback = edit,
2461 new_callback = edit,
2462 delete_callback = delete
2463 )
2464
2465 #----------------------------------------------------------------
2466 from Gnumed.wxGladeWidgets import wxgMeasurementOrgEAPnl
2467
2468 -class cMeasurementOrgEAPnl(wxgMeasurementOrgEAPnl.wxgMeasurementOrgEAPnl, gmEditArea.cGenericEditAreaMixin):
2469
2471
2472 try:
2473 data = kwargs['org']
2474 del kwargs['org']
2475 except KeyError:
2476 data = None
2477
2478 wxgMeasurementOrgEAPnl.wxgMeasurementOrgEAPnl.__init__(self, *args, **kwargs)
2479 gmEditArea.cGenericEditAreaMixin.__init__(self)
2480
2481 self.mode = 'new'
2482 self.data = data
2483 if data is not None:
2484 self.mode = 'edit'
2485
2486 #self.__init_ui()
2487 #----------------------------------------------------------------
2488 # def __init_ui(self):
2489 # # adjust phrasewheels etc
2490 #----------------------------------------------------------------
2491 # generic Edit Area mixin API
2492 #----------------------------------------------------------------
2494 has_errors = False
2495 if self._PRW_org_unit.GetData() is None:
2496 if self._PRW_org_unit.GetValue().strip() == u'':
2497 has_errors = True
2498 self._PRW_org_unit.display_as_valid(valid = False)
2499 else:
2500 self._PRW_org_unit.display_as_valid(valid = True)
2501 else:
2502 self._PRW_org_unit.display_as_valid(valid = True)
2503
2504 return (not has_errors)
2505 #----------------------------------------------------------------
2507 data = gmPathLab.create_test_org (
2508 name = self._PRW_org_unit.GetValue().strip(),
2509 comment = self._TCTRL_comment.GetValue().strip(),
2510 pk_org_unit = self._PRW_org_unit.GetData()
2511 )
2512 data['test_org_contact'] = self._TCTRL_contact.GetValue().strip()
2513 data.save()
2514 self.data = data
2515 return True
2516 #----------------------------------------------------------------
2518 # get or create the org unit
2519 name = self._PRW_org_unit.GetValue().strip()
2520 org = gmOrganization.org_exists(organization = name)
2521 if org is None:
2522 org = gmOrganization.create_org (
2523 organization = name,
2524 category = u'Laboratory'
2525 )
2526 org_unit = gmOrganization.create_org_unit (
2527 pk_organization = org['pk_org'],
2528 unit = name
2529 )
2530 # update test_org fields
2531 self.data['pk_org_unit'] = org_unit['pk_org_unit']
2532 self.data['test_org_contact'] = self._TCTRL_contact.GetValue().strip()
2533 self.data['comment'] = self._TCTRL_comment.GetValue().strip()
2534 self.data.save()
2535 return True
2536 #----------------------------------------------------------------
2538 self._PRW_org_unit.SetText(value = u'', data = None)
2539 self._TCTRL_contact.SetValue(u'')
2540 self._TCTRL_comment.SetValue(u'')
2541 #----------------------------------------------------------------
2543 self._PRW_org_unit.SetText(value = self.data['unit'], data = self.data['pk_org_unit'])
2544 self._TCTRL_contact.SetValue(gmTools.coalesce(self.data['test_org_contact'], u''))
2545 self._TCTRL_comment.SetValue(gmTools.coalesce(self.data['comment'], u''))
2546 #----------------------------------------------------------------
2549 #----------------------------------------------------------------
2552
2553 #----------------------------------------------------------------
2555
2557
2558 query = u"""
2559 SELECT DISTINCT ON (list_label)
2560 pk AS data,
2561 unit || ' (' || organization || ')' AS field_label,
2562 unit || ' @ ' || organization AS list_label
2563 FROM clin.v_test_orgs
2564 WHERE
2565 unit %(fragment_condition)s
2566 OR
2567 organization %(fragment_condition)s
2568 ORDER BY list_label
2569 LIMIT 50"""
2570 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
2571 mp.setThresholds(1, 2, 4)
2572 #mp.word_separators = '[ \t:@]+'
2573 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
2574 self.matcher = mp
2575 self.SetToolTipString(_('The name of the path lab/diagnostic organisation.'))
2576 self.selection_only = False
2577 #------------------------------------------------------------
2579 if self.GetData() is not None:
2580 _log.debug('data already set, not creating')
2581 return
2582
2583 if self.GetValue().strip() == u'':
2584 _log.debug('cannot create new lab, missing name')
2585 return
2586
2587 lab = gmPathLab.create_test_org(name = self.GetValue().strip())
2588 self.SetText(value = lab['unit'], data = lab['pk_test_org'])
2589 return
2590 #------------------------------------------------------------
2593
2594 #================================================================
2596
2597 if parent is None:
2598 parent = wx.GetApp().GetTopWindow()
2599
2600 msg = _(
2601 '\n'
2602 'These are the meta test types currently defined in GNUmed.\n'
2603 '\n'
2604 'Meta test types allow you to aggregate several actual test types used\n'
2605 'by pathology labs into one logical type.\n'
2606 '\n'
2607 'This is useful for grouping together results of tests which come under\n'
2608 'different names but really are the same thing. This often happens when\n'
2609 'you switch labs or the lab starts using another test method.\n'
2610 )
2611
2612 mtts = gmPathLab.get_meta_test_types()
2613
2614 gmListWidgets.get_choices_from_list (
2615 parent = parent,
2616 msg = msg,
2617 caption = _('Showing meta test types.'),
2618 columns = [_('Abbrev'), _('Name'), _('LOINC'), _('Comment'), u'#'],
2619 choices = [ [
2620 m['abbrev'],
2621 m['name'],
2622 gmTools.coalesce(m['loinc'], u''),
2623 gmTools.coalesce(m['comment'], u''),
2624 m['pk']
2625 ] for m in mtts ],
2626 data = mtts,
2627 single_selection = True,
2628 #edit_callback = edit,
2629 #new_callback = edit,
2630 #delete_callback = delete,
2631 #refresh_callback = refresh
2632 )
2633 #----------------------------------------------------------------
2635
2637
2638 query = u"""
2639 SELECT DISTINCT ON (field_label)
2640 c_mtt.pk
2641 AS data,
2642 c_mtt.abbrev || ': ' || name
2643 AS field_label,
2644 c_mtt.abbrev || ': ' || name
2645 || coalesce (
2646 ' (' || c_mtt.comment || ')',
2647 ''
2648 )
2649 || coalesce (
2650 ', LOINC: ' || c_mtt.loinc,
2651 ''
2652 )
2653 AS list_label
2654 FROM
2655 clin.meta_test_type c_mtt
2656 WHERE
2657 abbrev %(fragment_condition)s
2658 OR
2659 name %(fragment_condition)s
2660 OR
2661 loinc %(fragment_condition)s
2662 ORDER BY field_label
2663 LIMIT 50"""
2664
2665 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
2666 mp.setThresholds(1, 2, 4)
2667 mp.word_separators = '[ \t:@]+'
2668 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
2669 self.matcher = mp
2670 self.SetToolTipString(_('Select the meta test type.'))
2671 self.selection_only = True
2672 #------------------------------------------------------------
2674 if self.GetData() is None:
2675 return None
2676
2677 return gmPathLab.cMetaTestType(aPK_obj = self.GetData())
2678
2679 #================================================================
2680 # test panel handling
2681 #================================================================
2683 ea = cTestPanelEAPnl(parent = parent, id = -1)
2684 ea.data = test_panel
2685 ea.mode = gmTools.coalesce(test_panel, 'new', 'edit')
2686 dlg = gmEditArea.cGenericEditAreaDlg2 (
2687 parent = parent,
2688 id = -1,
2689 edit_area = ea,
2690 single_entry = gmTools.bool2subst((test_panel is None), False, True)
2691 )
2692 dlg.SetTitle(gmTools.coalesce(test_panel, _('Adding new test panel'), _('Editing test panel')))
2693 if dlg.ShowModal() == wx.ID_OK:
2694 dlg.Destroy()
2695 return True
2696 dlg.Destroy()
2697 return False
2698
2699 #----------------------------------------------------------------
2701
2702 if parent is None:
2703 parent = wx.GetApp().GetTopWindow()
2704
2705 #------------------------------------------------------------
2706 def edit(test_panel=None):
2707 return edit_test_panel(parent = parent, test_panel = test_panel)
2708 #------------------------------------------------------------
2709 def delete(test_panel):
2710 gmPathLab.delete_test_panel(pk = test_panel['pk_test_panel'])
2711 return True
2712 #------------------------------------------------------------
2713 def get_tooltip(test_panel):
2714 return test_panel.format()
2715 #------------------------------------------------------------
2716 def refresh(lctrl):
2717 panels = gmPathLab.get_test_panels(order_by = 'description')
2718 items = [ [
2719 p['description'],
2720 gmTools.coalesce(p['comment'], u''),
2721 p['pk_test_panel']
2722 ] for p in panels ]
2723 lctrl.set_string_items(items)
2724 lctrl.set_data(panels)
2725 #------------------------------------------------------------
2726 msg = _(
2727 '\n'
2728 'Test panels as defined in GNUmed.\n'
2729 )
2730
2731 gmListWidgets.get_choices_from_list (
2732 parent = parent,
2733 msg = msg,
2734 caption = _('Showing test panels.'),
2735 columns = [ _('Name'), _('Comment'), u'#' ],
2736 single_selection = True,
2737 refresh_callback = refresh,
2738 edit_callback = edit,
2739 new_callback = edit,
2740 delete_callback = delete,
2741 list_tooltip_callback = get_tooltip
2742 )
2743
2744 #----------------------------------------------------------------
2746
2748 query = u"""
2749 SELECT
2750 pk_test_panel
2751 AS data,
2752 description
2753 AS field_label,
2754 description
2755 AS list_label
2756 FROM
2757 clin.v_test_panels
2758 WHERE
2759 description %(fragment_condition)s
2760 ORDER BY field_label
2761 LIMIT 30"""
2762 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
2763 mp.setThresholds(1, 2, 4)
2764 #mp.word_separators = '[ \t:@]+'
2765 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
2766 self.matcher = mp
2767 self.SetToolTipString(_('Select a test panel.'))
2768 self.selection_only = True
2769 #------------------------------------------------------------
2771 if self.GetData() is None:
2772 return None
2773 return gmPathLab.cTestPanel(aPK_obj = self.GetData())
2774 #------------------------------------------------------------
2776 if self.GetData() is None:
2777 return None
2778 return gmPathLab.cTestPanel(aPK_obj = self.GetData()).format()
2779
2780 #====================================================================
2781 from Gnumed.wxGladeWidgets import wxgTestPanelEAPnl
2782
2784
2786
2787 try:
2788 data = kwargs['panel']
2789 del kwargs['panel']
2790 except KeyError:
2791 data = None
2792
2793 wxgTestPanelEAPnl.wxgTestPanelEAPnl.__init__(self, *args, **kwargs)
2794 gmEditArea.cGenericEditAreaMixin.__init__(self)
2795
2796 self._test_types = None
2797
2798 self.mode = 'new'
2799 self.data = data
2800 if data is not None:
2801 self.mode = 'edit'
2802
2803 #self.__init_ui()
2804 #----------------------------------------------------------------
2805 # def __init_ui(self):
2806 # # adjust phrasewheels etc
2807 #----------------------------------------------------------------
2808 # generic Edit Area mixin API
2809 #----------------------------------------------------------------
2811 validity = True
2812
2813 if self._test_types is None:
2814 validity = False
2815 gmDispatcher.send(signal = 'statustext', msg = _('No test types selected.'))
2816 self._BTN_select_tests.SetFocus()
2817
2818 if self._TCTRL_description.GetValue().strip() == u'':
2819 validity = False
2820 self.display_tctrl_as_valid(tctrl = self._TCTRL_description, valid = False)
2821 self._TCTRL_description.SetFocus()
2822 else:
2823 self.display_tctrl_as_valid(tctrl = self._TCTRL_description, valid = True)
2824
2825 return validity
2826 #----------------------------------------------------------------
2828 data = gmPathLab.create_test_panel(description = self._TCTRL_description.GetValue().strip())
2829 data['comment'] = self._TCTRL_comment.GetValue().strip()
2830 data['pk_test_types'] = [ tt['pk_test_type'] for tt in self._test_types ]
2831 data.save()
2832 data.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ]
2833 self.data = data
2834 return True
2835 #----------------------------------------------------------------
2837 self.data['description'] = self._TCTRL_description.GetValue().strip()
2838 self.data['comment'] = self._TCTRL_comment.GetValue().strip()
2839 self.data['pk_test_types'] = [ tt['pk_test_type'] for tt in self._test_types ]
2840 self.data.save()
2841 self.data.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ]
2842 return True
2843 #----------------------------------------------------------------
2845 self._TCTRL_tests.SetValue(u'')
2846 self._test_types = test_types
2847 if self._test_types is None:
2848 return
2849 tmp = u';\n'.join ([
2850 u'%s: %s%s' % (
2851 t['unified_abbrev'],
2852 t['unified_name'],
2853 gmTools.coalesce(t['name_org'], u'', u' (%s)')
2854 )
2855 for t in self._test_types
2856 ])
2857 self._TCTRL_tests.SetValue(tmp)
2858 #----------------------------------------------------------------
2860 self._TCTRL_description.SetValue(u'')
2861 self._TCTRL_comment.SetValue(u'')
2862 self.__refresh_test_types_field()
2863 self._PRW_codes.SetText()
2864
2865 self._TCTRL_description.SetFocus()
2866 #----------------------------------------------------------------
2870 #----------------------------------------------------------------
2872 self._TCTRL_description.SetValue(self.data['description'])
2873 self._TCTRL_comment.SetValue(gmTools.coalesce(self.data['comment'], u''))
2874 self.__refresh_test_types_field(test_types = self.data.test_types)
2875 val, data = self._PRW_codes.generic_linked_codes2item_dict(self.data.generic_codes)
2876 self._PRW_codes.SetText(val, data)
2877
2878 self._BTN_select_tests.SetFocus()
2879 #----------------------------------------------------------------
2895
2896 #================================================================
2897 # main
2898 #----------------------------------------------------------------
2899 if __name__ == '__main__':
2900
2901 from Gnumed.pycommon import gmLog2
2902 from Gnumed.wxpython import gmPatSearchWidgets
2903
2904 gmI18N.activate_locale()
2905 gmI18N.install_domain()
2906 gmDateTime.init()
2907
2908 #------------------------------------------------------------
2910 pat = gmPersonSearch.ask_for_patient()
2911 app = wx.PyWidgetTester(size = (500, 300))
2912 lab_grid = cMeasurementsGrid(parent = app.frame, id = -1)
2913 lab_grid.patient = pat
2914 app.frame.Show()
2915 app.MainLoop()
2916 #------------------------------------------------------------
2918 pat = gmPersonSearch.ask_for_patient()
2919 gmPatSearchWidgets.set_active_patient(patient=pat)
2920 app = wx.PyWidgetTester(size = (500, 300))
2921 ea = cMeasurementEditAreaPnl(parent = app.frame, id = -1)
2922 app.frame.Show()
2923 app.MainLoop()
2924 #------------------------------------------------------------
2925 # def test_primary_care_vitals_pnl():
2926 # app = wx.PyWidgetTester(size = (500, 300))
2927 # pnl = wxgPrimaryCareVitalsInputPnl.wxgPrimaryCareVitalsInputPnl(parent = app.frame, id = -1)
2928 # app.frame.Show()
2929 # app.MainLoop()
2930 #------------------------------------------------------------
2931 if (len(sys.argv) > 1) and (sys.argv[1] == 'test'):
2932 #test_grid()
2933 test_test_ea_pnl()
2934 #test_primary_care_vitals_pnl()
2935
2936 #================================================================
2937
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Fri Feb 1 03:57:01 2013 | http://epydoc.sourceforge.net |