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