1 """GNUmed measurements related business objects."""
2
3 __version__ = "$Revision: 1.81 $"
4 __author__ = "K.Hilbert <Karsten.Hilbert@gmx.net>"
5 __license__ = "GPL"
6
7
8 import types, sys, logging, codecs, decimal
9
10 if __name__ == '__main__':
11 sys.path.insert(0, '../../')
12
13 from Gnumed.pycommon import gmDateTime
14 if __name__ == '__main__':
15 from Gnumed.pycommon import gmLog2
16 from Gnumed.pycommon import gmI18N
17 gmDateTime.init()
18 from Gnumed.pycommon import gmExceptions
19 from Gnumed.pycommon import gmBusinessDBObject
20 from Gnumed.pycommon import gmPG2
21 from Gnumed.pycommon import gmTools
22 from Gnumed.pycommon import gmDispatcher
23 from Gnumed.pycommon import gmHooks
24
25
26 _log = logging.getLogger('gm.lab')
27 _log.info(__version__)
28
29
30
31
35
36 gmDispatcher.connect(_on_test_result_modified, u'test_result_mod_db')
37
38
39 -class cTestOrg(gmBusinessDBObject.cBusinessDBObject):
40 """Represents one test org/lab."""
41
42 _cmd_fetch_payload = u"""SELECT *, xmin FROM clin.test_org WHERE pk = %s"""
43
44 _cmds_store_payload = [
45 u"""UPDATE clin.test_org SET
46 internal_name = gm.nullify_empty_string(%(internal_name)s),
47 contact = gm.nullify_empty_string(%(contact)s),
48 comment = gm.nullify_empty_string(%(comment)s)
49 WHERE
50 pk = %(pk)s
51 AND
52 xmin = %(xmin)s
53 RETURNING
54 xmin
55 """
56 ]
57
58 _updatable_fields = [
59 u'internal_name',
60 u'contact',
61 u'comment'
62 ]
63
65 cmd = u'insert into clin.test_org (internal_name) values (%(name)s) returning pk'
66 args = {'name': name.strip()}
67 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True)
68 return cTestOrg(aPK_obj = rows[0]['pk'])
69
71 cmd = u'select *, xmin from clin.test_org order by %s' % order_by
72 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = True)
73 return [ cTestOrg(row = {'pk_field': 'pk', 'data': r, 'idx': idx}) for r in rows ]
74
83
88
93
95 """Represents one unified test type."""
96
97
98 _cmd_fetch_payload = u"""select * from clin.v_unified_test_types where pk_test_type = %s"""
99
100 _cmds_store_payload = []
101
102 _updatable_fields = []
103
105 cmd = u"""
106 SELECT pk_test_result, clin_when
107 FROM clin.v_test_results
108 WHERE
109 pk_patient = %(pat)s
110 AND
111 pk_meta_test_type = %(pkmtt)s
112 ORDER BY clin_when DESC
113 LIMIT 1
114 """
115 args = {'pat': pk_patient, 'pkmtt': self._payload[self._idx['pk_meta_test_type']]}
116 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
117 if len(rows) == 0:
118 return None
119 return cTestResult(aPK_obj = rows[0]['pk_test_result'])
120
122 """Represents one test result type."""
123
124 _cmd_fetch_payload = u"""select * from clin.v_test_types where pk_test_type = %s"""
125
126 _cmds_store_payload = [
127 u"""update clin.test_type set
128 abbrev = %(abbrev)s,
129 name = %(name)s,
130 loinc = gm.nullify_empty_string(%(loinc)s),
131 code = gm.nullify_empty_string(%(code)s),
132 coding_system = gm.nullify_empty_string(%(coding_system)s),
133 comment = gm.nullify_empty_string(%(comment_type)s),
134 conversion_unit = gm.nullify_empty_string(%(conversion_unit)s),
135 fk_test_org = %(pk_test_org)s
136 where pk = %(pk_test_type)s""",
137 u"""select xmin_test_type from clin.v_test_types where pk_test_type = %(pk_test_type)s"""
138 ]
139
140 _updatable_fields = [
141 'abbrev',
142 'name',
143 'loinc',
144 'code',
145 'coding_system',
146 'comment_type',
147 'conversion_unit',
148 'pk_test_org'
149 ]
150
165
167 cmd = u'select exists(select 1 from clin.test_result where fk_type = %(pk_type)s)'
168 args = {'pk_type': self._payload[self._idx['pk_test_type']]}
169 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
170 return rows[0][0]
171
172 in_use = property(_get_in_use, lambda x:x)
173
175 cmd = u'select * from clin.v_test_types %s' % gmTools.coalesce(order_by, u'', u'order by %s')
176 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = True)
177 return [ cMeasurementType(row = {'pk_field': 'pk_test_type', 'data': r, 'idx': idx}) for r in rows ]
178
180
181 if (abbrev is None) and (name is None):
182 raise ValueError('must have <abbrev> and/or <name> set')
183
184 where_snippets = []
185
186 if lab is None:
187 where_snippets.append('pk_test_org is null')
188 else:
189 try:
190 int(lab)
191 where_snippets.append('pk_test_org = %(lab)s')
192 except (TypeError, ValueError):
193 where_snippets.append('pk_test_org = (select pk from clin.test_org where internal_name = %(lab)s)')
194
195 if abbrev is not None:
196 where_snippets.append('abbrev = %(abbrev)s')
197
198 if name is not None:
199 where_snippets.append('name = %(name)s')
200
201 where_clause = u' and '.join(where_snippets)
202 cmd = u"select * from clin.v_test_types where %s" % where_clause
203 args = {'lab': lab, 'abbrev': abbrev, 'name': name}
204
205 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
206
207 if len(rows) == 0:
208 return None
209
210 tt = cMeasurementType(row = {'pk_field': 'pk_test_type', 'data': rows[0], 'idx': idx})
211 return tt
212
214 cmd = u'delete from clin.test_type where pk = %(pk)s'
215 args = {'pk': measurement_type}
216 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
217
219 """Create or get test type."""
220
221 ttype = find_measurement_type(lab = lab, abbrev = abbrev, name = name)
222
223 if ttype is not None:
224 return ttype
225
226 _log.debug('creating test type [%s:%s:%s:%s]', lab, abbrev, name, unit)
227
228
229 if unit is None:
230 _log.error('need <unit> to create test type: %s:%s:%s:%s' % (lab, abbrev, name, unit))
231 raise ValueError('need <unit> to create test type')
232
233
234 cols = []
235 val_snippets = []
236 vals = {}
237
238
239 if lab is None:
240 lab = create_measurement_org()
241
242 cols.append('fk_test_org')
243 try:
244 vals['lab'] = int(lab)
245 val_snippets.append('%(lab)s')
246 except:
247 vals['lab'] = lab
248 val_snippets.append('(select pk from clin.test_org where internal_name = %(lab)s)')
249
250
251 cols.append('abbrev')
252 val_snippets.append('%(abbrev)s')
253 vals['abbrev'] = abbrev
254
255
256 cols.append('conversion_unit')
257 val_snippets.append('%(unit)s')
258 vals['unit'] = unit
259
260
261 if name is not None:
262 cols.append('name')
263 val_snippets.append('%(name)s')
264 vals['name'] = name
265
266 col_clause = u', '.join(cols)
267 val_clause = u', '.join(val_snippets)
268 queries = [
269 {'cmd': u'insert into clin.test_type (%s) values (%s)' % (col_clause, val_clause), 'args': vals},
270 {'cmd': u"select * from clin.v_test_types where pk_test_type = currval(pg_get_serial_sequence('clin.test_type', 'pk'))"}
271 ]
272 rows, idx = gmPG2.run_rw_queries(queries = queries, get_col_idx = True, return_data = True)
273 ttype = cMeasurementType(row = {'pk_field': 'pk_test_type', 'data': rows[0], 'idx': idx})
274
275 return ttype
276
278
279 if name is None:
280 name = _('inhouse lab')
281 comment = _('auto-generated')
282
283 cmd = u'select * from clin.test_org where internal_name = %(name)s'
284 if comment is not None:
285 comment = comment.strip()
286 args = {'name': name, 'cmt': comment}
287 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
288
289 if len(rows) == 0:
290 queries = [
291 {'cmd': u'insert into clin.test_org (fk_org, internal_name, comment) values (null, %(name)s, %(cmt)s)', 'args': args},
292 {'cmd': u"select currval(pg_get_serial_sequence('clin.test_org', 'pk')) as pk"}
293 ]
294 else:
295
296 args['pk'] = rows[0]['pk']
297 queries = [
298 {'cmd': u'update clin.test_org set comment = %(cmt)s where pk = %(pk)s', 'args': args},
299 {'cmd': u'select %(pk)s as pk', 'args': args}
300 ]
301
302 rows, idx = gmPG2.run_rw_queries(queries = queries, return_data = True)
303
304 return rows[0]['pk']
305
306 -class cTestResult(gmBusinessDBObject.cBusinessDBObject):
307 """Represents one test result."""
308
309 _cmd_fetch_payload = u"select * from clin.v_test_results where pk_test_result = %s"
310
311 _cmds_store_payload = [
312 u"""update clin.test_result set
313 clin_when = %(clin_when)s,
314 narrative = nullif(trim(%(comment)s), ''),
315 val_num = %(val_num)s,
316 val_alpha = nullif(trim(%(val_alpha)s), ''),
317 val_unit = nullif(trim(%(val_unit)s), ''),
318 val_normal_min = %(val_normal_min)s,
319 val_normal_max = %(val_normal_max)s,
320 val_normal_range = nullif(trim(%(val_normal_range)s), ''),
321 val_target_min = %(val_target_min)s,
322 val_target_max = %(val_target_max)s,
323 val_target_range = nullif(trim(%(val_target_range)s), ''),
324 abnormality_indicator = nullif(trim(%(abnormality_indicator)s), ''),
325 norm_ref_group = nullif(trim(%(norm_ref_group)s), ''),
326 note_test_org = nullif(trim(%(note_test_org)s), ''),
327 material = nullif(trim(%(material)s), ''),
328 material_detail = nullif(trim(%(material_detail)s), ''),
329 fk_intended_reviewer = %(pk_intended_reviewer)s,
330 fk_encounter = %(pk_encounter)s,
331 fk_episode = %(pk_episode)s,
332 fk_type = %(pk_test_type)s,
333 fk_request = %(pk_request)s
334 where
335 pk = %(pk_test_result)s and
336 xmin = %(xmin_test_result)s""",
337 u"""select xmin_test_result from clin.v_test_results where pk_test_result = %(pk_test_result)s"""
338 ]
339
340 _updatable_fields = [
341 'clin_when',
342 'comment',
343 'val_num',
344 'val_alpha',
345 'val_unit',
346 'val_normal_min',
347 'val_normal_max',
348 'val_normal_range',
349 'val_target_min',
350 'val_target_max',
351 'val_target_range',
352 'abnormality_indicator',
353 'norm_ref_group',
354 'note_test_org',
355 'material',
356 'material_detail',
357 'pk_intended_reviewer',
358 'pk_encounter',
359 'pk_episode',
360 'pk_test_type',
361 'pk_request'
362 ]
363
399
401
402 cmd = u"""
403 select
404 distinct on (norm_ref_group_str, val_unit, val_normal_min, val_normal_max, val_normal_range, val_target_min, val_target_max, val_target_range)
405 pk_patient,
406 val_unit,
407 val_normal_min, val_normal_max, val_normal_range,
408 val_target_min, val_target_max, val_target_range,
409 norm_ref_group,
410 coalesce(norm_ref_group, '') as norm_ref_group_str
411 from
412 clin.v_test_results
413 where
414 pk_test_type = %(pk_type)s
415 """
416 args = {'pk_type': self._payload[self._idx['pk_test_type']]}
417 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
418 return rows
419
421 raise AttributeError('[%s]: reference ranges not settable') % self.__class__.__name__
422
423 reference_ranges = property(_get_reference_ranges, _set_reference_ranges)
424
425 - def set_review(self, technically_abnormal=None, clinically_relevant=None, comment=None, make_me_responsible=False):
426
427 if comment is not None:
428 comment = comment.strip()
429
430 if ((technically_abnormal is None) and
431 (clinically_relevant is None) and
432 (comment is None) and
433 (make_me_responsible is False)):
434 return True
435
436
437 if self._payload[self._idx['reviewed']]:
438 self.__change_existing_review (
439 technically_abnormal = technically_abnormal,
440 clinically_relevant = clinically_relevant,
441 comment = comment
442 )
443 else:
444 self.__set_new_review (
445 technically_abnormal = technically_abnormal,
446 clinically_relevant = clinically_relevant,
447 comment = comment
448 )
449
450 if make_me_responsible is True:
451 cmd = u"select pk from dem.staff where db_user = current_user"
452 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}])
453 self['pk_intended_reviewer'] = rows[0][0]
454 self.save_payload()
455 else:
456 self.refetch_payload()
457
458
459
460 - def __set_new_review(self, technically_abnormal=None, clinically_relevant=None, comment=None):
461 """Add a review to a row.
462
463 - if technically abnormal is not provided/None it will be set
464 to True if the lab's indicator has a meaningful value
465 - if clinically relevant is not provided/None it is set to
466 whatever technically abnormal is
467 """
468 if technically_abnormal is None:
469 technically_abnormal = False
470 if self._payload[self._idx['abnormality_indicator']] is not None:
471 if self._payload[self._idx['abnormality_indicator']].strip() != u'':
472 technically_abnormal = True
473
474 if clinically_relevant is None:
475 clinically_relevant = technically_abnormal
476
477 cmd = u"""
478 insert into clin.reviewed_test_results (
479 fk_reviewed_row,
480 is_technically_abnormal,
481 clinically_relevant,
482 comment
483 ) values (
484 %(pk)s,
485 %(abnormal)s,
486 %(relevant)s,
487 %(cmt)s
488 )"""
489 args = {
490 'pk': self._payload[self._idx['pk_test_result']],
491 'abnormal': technically_abnormal,
492 'relevant': clinically_relevant,
493 'cmt': comment
494 }
495
496 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
497
499 """Change a review on a row.
500
501 - if technically abnormal/clinically relevant/comment are
502 None (or empty) they are not set
503 """
504 args = {
505 'pk_row': self._payload[self._idx['pk_test_result']],
506 'abnormal': technically_abnormal,
507 'relevant': clinically_relevant
508 }
509
510 set_parts = []
511
512 if technically_abnormal is not None:
513 set_parts.append(u'is_technically_abnormal = %(abnormal)s')
514
515 if clinically_relevant is not None:
516 set_parts.append(u'clinically_relevant= %(relevant)s')
517
518 if comment is not None:
519 set_parts.append('comment = %(cmt)s')
520 args['cmt'] = comment
521
522 cmd = u"""
523 update clin.reviewed_test_results set
524 fk_reviewer = (select pk from dem.staff where db_user = current_user),
525 %s
526 where
527 fk_reviewed_row = %%(pk_row)s
528 """ % u',\n '.join(set_parts)
529
530 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
531
533
534 try:
535 pk = int(result)
536 except (TypeError, AttributeError):
537 pk = result['pk_test_result']
538
539 cmd = u'delete from clin.test_result where pk = %(pk)s'
540 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': {'pk': pk}}])
541
542 -def create_test_result(encounter=None, episode=None, type=None, intended_reviewer=None, val_num=None, val_alpha=None, unit=None):
543
544 cmd1 = u"""
545 insert into clin.test_result (
546 fk_encounter,
547 fk_episode,
548 fk_type,
549 fk_intended_reviewer,
550 val_num,
551 val_alpha,
552 val_unit
553 ) values (
554 %(enc)s,
555 %(epi)s,
556 %(type)s,
557 %(rev)s,
558 %(v_num)s,
559 %(v_alpha)s,
560 %(unit)s
561 )"""
562
563 cmd2 = u"""
564 select *
565 from
566 clin.v_test_results
567 where
568 pk_test_result = currval(pg_get_serial_sequence('clin.test_result', 'pk'))"""
569
570 args = {
571 u'enc': encounter,
572 u'epi': episode,
573 u'type': type,
574 u'rev': intended_reviewer,
575 u'v_num': val_num,
576 u'v_alpha': val_alpha,
577 u'unit': unit
578 }
579
580 rows, idx = gmPG2.run_rw_queries (
581 queries = [
582 {'cmd': cmd1, 'args': args},
583 {'cmd': cmd2}
584 ],
585 return_data = True,
586 get_col_idx = True
587 )
588
589 tr = cTestResult(row = {
590 'pk_field': 'pk_test_result',
591 'idx': idx,
592 'data': rows[0]
593 })
594
595 return tr
596
607
608 -def __tests2latex_minipage(results=None, width=u'1.5cm', show_time=False, show_range=True):
609
610 if len(results) == 0:
611 return u'\\begin{minipage}{%s} \\end{minipage}' % width
612
613 lines = []
614 for t in results:
615
616 tmp = u''
617
618 if show_time:
619 tmp += u'{\\tiny (%s)} ' % t['clin_when'].strftime('%H:%M')
620
621 tmp += u'%.8s' % t['unified_val']
622
623 lines.append(tmp)
624 tmp = u''
625
626 if show_range:
627 has_range = (
628 t['unified_target_range'] is not None
629 or
630 t['unified_target_min'] is not None
631 or
632 t['unified_target_max'] is not None
633 )
634 if has_range:
635 if t['unified_target_range'] is not None:
636 tmp += u'{\\tiny %s}' % t['unified_target_range']
637 else:
638 tmp += u'{\\tiny %s}' % (
639 gmTools.coalesce(t['unified_target_min'], u'- ', u'%s - '),
640 gmTools.coalesce(t['unified_target_max'], u'', u'%s')
641 )
642 lines.append(tmp)
643
644 return u'\\begin{minipage}{%s} \\begin{flushright} %s \\end{flushright} \\end{minipage}' % (width, u' \\\\ '.join(lines))
645
647
648 if len(results) == 0:
649 return u''
650
651 lines = []
652 for t in results:
653
654 tmp = u''
655
656 if show_time:
657 tmp += u'\\tiny %s ' % t['clin_when'].strftime('%H:%M')
658
659 tmp += u'\\normalsize %.8s' % t['unified_val']
660
661 lines.append(tmp)
662 tmp = u'\\tiny %s' % gmTools.coalesce(t['val_unit'], u'', u'%s:')
663
664 if not show_range:
665 lines.append(tmp)
666 continue
667
668 has_range = (
669 t['unified_target_range'] is not None
670 or
671 t['unified_target_min'] is not None
672 or
673 t['unified_target_max'] is not None
674 )
675
676 if not has_range:
677 lines.append(tmp)
678 continue
679
680 if t['unified_target_range'] is not None:
681 tmp += t['unified_target_range']
682 else:
683 tmp += u'%s%s' % (
684 gmTools.coalesce(t['unified_target_min'], u'- ', u'%s - '),
685 gmTools.coalesce(t['unified_target_max'], u'', u'%s')
686 )
687 lines.append(tmp)
688
689 return u' \\\\ '.join(lines)
690
766
767
769
770 if filename is None:
771 filename = gmTools.get_unique_filename(prefix = u'gm2gpl-', suffix = '.dat')
772
773
774 series = {}
775 for r in results:
776 try:
777 series[r['unified_name']].append(r)
778 except KeyError:
779 series[r['unified_name']] = [r]
780
781 gp_data = codecs.open(filename, 'wb', 'utf8')
782
783 gp_data.write(u'# %s\n' % _('GNUmed test results export for Gnuplot plotting'))
784 gp_data.write(u'# -------------------------------------------------------------\n')
785 gp_data.write(u'# first line of index: test type abbreviation & name\n')
786 gp_data.write(u'#\n')
787 gp_data.write(u'# clin_when at full precision\n')
788 gp_data.write(u'# value\n')
789 gp_data.write(u'# unit\n')
790 gp_data.write(u'# unified (target or normal) range: lower bound\n')
791 gp_data.write(u'# unified (target or normal) range: upper bound\n')
792 gp_data.write(u'# normal range: lower bound\n')
793 gp_data.write(u'# normal range: upper bound\n')
794 gp_data.write(u'# target range: lower bound\n')
795 gp_data.write(u'# target range: upper bound\n')
796 gp_data.write(u'# clin_when formatted into string as x-axis tic label\n')
797 gp_data.write(u'# -------------------------------------------------------------\n')
798
799 for test_type in series.keys():
800 if len(series[test_type]) == 0:
801 continue
802
803 r = series[test_type][0]
804 title = u'%s (%s)' % (
805 r['unified_abbrev'],
806 r['unified_name']
807 )
808 gp_data.write(u'\n\n"%s" "%s"\n' % (title, title))
809
810 prev_date = None
811 prev_year = None
812 for r in series[test_type]:
813 curr_date = r['clin_when'].strftime('%Y-%m-%d')
814 if curr_date == prev_date:
815 gp_data.write(u'\n# %s\n' % _('blank line inserted to allow for discontinued line drawing for same-day values'))
816 if r['clin_when'].year == prev_year:
817 when_template = '%b %d %H:%M'
818 else:
819 when_template = '%b %d %H:%M (%Y)'
820 prev_year = r['clin_when'].year
821 gp_data.write (u'%s %s "%s" %s %s %s %s %s %s "%s"\n' % (
822 r['clin_when'].strftime('%Y-%m-%d_%H:%M'),
823 r['unified_val'],
824 gmTools.coalesce(r['val_unit'], u'"<?>"'),
825 gmTools.coalesce(r['unified_target_min'], u'"<?>"'),
826 gmTools.coalesce(r['unified_target_max'], u'"<?>"'),
827 gmTools.coalesce(r['val_normal_min'], u'"<?>"'),
828 gmTools.coalesce(r['val_normal_max'], u'"<?>"'),
829 gmTools.coalesce(r['val_target_min'], u'"<?>"'),
830 gmTools.coalesce(r['val_target_max'], u'"<?>"'),
831 gmDateTime.pydt_strftime (
832 r['clin_when'],
833 format = when_template,
834 accuracy = gmDateTime.acc_minutes
835 )
836 ))
837 prev_date = curr_date
838
839 gp_data.close()
840
841 return filename
842
843 -class cLabResult(gmBusinessDBObject.cBusinessDBObject):
844 """Represents one lab result."""
845
846 _cmd_fetch_payload = """
847 select *, xmin_test_result from v_results4lab_req
848 where pk_result=%s"""
849 _cmds_lock_rows_for_update = [
850 """select 1 from test_result where pk=%(pk_result)s and xmin=%(xmin_test_result)s for update"""
851 ]
852 _cmds_store_payload = [
853 """update test_result set
854 clin_when = %(val_when)s,
855 narrative = %(progress_note_result)s,
856 fk_type = %(pk_test_type)s,
857 val_num = %(val_num)s::numeric,
858 val_alpha = %(val_alpha)s,
859 val_unit = %(val_unit)s,
860 val_normal_min = %(val_normal_min)s,
861 val_normal_max = %(val_normal_max)s,
862 val_normal_range = %(val_normal_range)s,
863 val_target_min = %(val_target_min)s,
864 val_target_max = %(val_target_max)s,
865 val_target_range = %(val_target_range)s,
866 abnormality_indicator = %(abnormal)s,
867 norm_ref_group = %(ref_group)s,
868 note_provider = %(note_provider)s,
869 material = %(material)s,
870 material_detail = %(material_detail)s
871 where pk = %(pk_result)s""",
872 """select xmin_test_result from v_results4lab_req where pk_result=%(pk_result)s"""
873 ]
874
875 _updatable_fields = [
876 'val_when',
877 'progress_note_result',
878 'val_num',
879 'val_alpha',
880 'val_unit',
881 'val_normal_min',
882 'val_normal_max',
883 'val_normal_range',
884 'val_target_min',
885 'val_target_max',
886 'val_target_range',
887 'abnormal',
888 'ref_group',
889 'note_provider',
890 'material',
891 'material_detail'
892 ]
893
894 - def __init__(self, aPK_obj=None, row=None):
895 """Instantiate.
896
897 aPK_obj as dict:
898 - patient_id
899 - when_field (see view definition)
900 - when
901 - test_type
902 - val_num
903 - val_alpha
904 - unit
905 """
906
907 if aPK_obj is None:
908 gmBusinessDBObject.cBusinessDBObject.__init__(self, row=row)
909 return
910 pk = aPK_obj
911
912 if type(aPK_obj) == types.DictType:
913
914 if None in [aPK_obj['patient_id'], aPK_obj['when'], aPK_obj['when_field'], aPK_obj['test_type'], aPK_obj['unit']]:
915 raise gmExceptions.ConstructorError, 'parameter error: %s' % aPK_obj
916 if (aPK_obj['val_num'] is None) and (aPK_obj['val_alpha'] is None):
917 raise gmExceptions.ConstructorError, 'parameter error: val_num and val_alpha cannot both be None'
918
919 where_snippets = [
920 'pk_patient=%(patient_id)s',
921 'pk_test_type=%(test_type)s',
922 '%s=%%(when)s' % aPK_obj['when_field'],
923 'val_unit=%(unit)s'
924 ]
925 if aPK_obj['val_num'] is not None:
926 where_snippets.append('val_num=%(val_num)s::numeric')
927 if aPK_obj['val_alpha'] is not None:
928 where_snippets.append('val_alpha=%(val_alpha)s')
929
930 where_clause = ' and '.join(where_snippets)
931 cmd = "select pk_result from v_results4lab_req where %s" % where_clause
932 data = gmPG.run_ro_query('historica', cmd, None, aPK_obj)
933 if data is None:
934 raise gmExceptions.ConstructorError, 'error getting lab result for: %s' % aPK_obj
935 if len(data) == 0:
936 raise gmExceptions.NoSuchClinItemError, 'no lab result for: %s' % aPK_obj
937 pk = data[0][0]
938
939 gmBusinessDBObject.cBusinessDBObject.__init__(self, aPK_obj=pk)
940
942 cmd = """
943 select
944 %s,
945 vbp.title,
946 vbp.firstnames,
947 vbp.lastnames,
948 vbp.dob
949 from v_basic_person vbp
950 where vbp.pk_identity=%%s""" % self._payload[self._idx['pk_patient']]
951 pat = gmPG.run_ro_query('historica', cmd, None, self._payload[self._idx['pk_patient']])
952 return pat[0]
953
954 -class cLabRequest(gmBusinessDBObject.cBusinessDBObject):
955 """Represents one lab request."""
956
957 _cmd_fetch_payload = """
958 select *, xmin_lab_request from v_lab_requests
959 where pk_request=%s"""
960 _cmds_lock_rows_for_update = [
961 """select 1 from lab_request where pk=%(pk_request)s and xmin=%(xmin_lab_request)s for update"""
962 ]
963 _cmds_store_payload = [
964 """update lab_request set
965 request_id=%(request_id)s,
966 lab_request_id=%(lab_request_id)s,
967 clin_when=%(sampled_when)s,
968 lab_rxd_when=%(lab_rxd_when)s,
969 results_reported_when=%(results_reported_when)s,
970 request_status=%(request_status)s,
971 is_pending=%(is_pending)s::bool,
972 narrative=%(progress_note)s
973 where pk=%(pk_request)s""",
974 """select xmin_lab_request from v_lab_requests where pk_request=%(pk_request)s"""
975 ]
976 _updatable_fields = [
977 'request_id',
978 'lab_request_id',
979 'sampled_when',
980 'lab_rxd_when',
981 'results_reported_when',
982 'request_status',
983 'is_pending',
984 'progress_note'
985 ]
986
987 - def __init__(self, aPK_obj=None, row=None):
988 """Instantiate lab request.
989
990 The aPK_obj can be either a dict with the keys "req_id"
991 and "lab" or a simple primary key.
992 """
993
994 if aPK_obj is None:
995 gmBusinessDBObject.cBusinessDBObject.__init__(self, row=row)
996 return
997 pk = aPK_obj
998
999 if type(aPK_obj) == types.DictType:
1000
1001 try:
1002 aPK_obj['req_id']
1003 aPK_obj['lab']
1004 except:
1005 _log.exception('[%s:??]: faulty <aPK_obj> structure: [%s]' % (self.__class__.__name__, aPK_obj), sys.exc_info())
1006 raise gmExceptions.ConstructorError, '[%s:??]: cannot derive PK from [%s]' % (self.__class__.__name__, aPK_obj)
1007
1008 where_snippets = []
1009 vals = {}
1010 where_snippets.append('request_id=%(req_id)s')
1011 if type(aPK_obj['lab']) == types.IntType:
1012 where_snippets.append('pk_test_org=%(lab)s')
1013 else:
1014 where_snippets.append('lab_name=%(lab)s')
1015 where_clause = ' and '.join(where_snippets)
1016 cmd = "select pk_request from v_lab_requests where %s" % where_clause
1017
1018 data = gmPG.run_ro_query('historica', cmd, None, aPK_obj)
1019 if data is None:
1020 raise gmExceptions.ConstructorError, '[%s:??]: error getting lab request for [%s]' % (self.__class__.__name__, aPK_obj)
1021 if len(data) == 0:
1022 raise gmExceptions.NoSuchClinItemError, '[%s:??]: no lab request for [%s]' % (self.__class__.__name__, aPK_obj)
1023 pk = data[0][0]
1024
1025 gmBusinessDBObject.cBusinessDBObject.__init__(self, aPK_obj=pk)
1026
1028 cmd = """
1029 select vpi.pk_patient, vbp.title, vbp.firstnames, vbp.lastnames, vbp.dob
1030 from v_pat_items vpi, v_basic_person vbp
1031 where
1032 vpi.pk_item=%s
1033 and
1034 vbp.pk_identity=vpi.pk_patient"""
1035 pat = gmPG.run_ro_query('historica', cmd, None, self._payload[self._idx['pk_item']])
1036 if pat is None:
1037 _log.error('cannot get patient for lab request [%s]' % self._payload[self._idx['pk_item']])
1038 return None
1039 if len(pat) == 0:
1040 _log.error('no patient associated with lab request [%s]' % self._payload[self._idx['pk_item']])
1041 return None
1042 return pat[0]
1043
1044
1045
1046 -def create_lab_request(lab=None, req_id=None, pat_id=None, encounter_id=None, episode_id=None):
1047 """Create or get lab request.
1048
1049 returns tuple (status, value):
1050 (True, lab request instance)
1051 (False, error message)
1052 (None, housekeeping_todo primary key)
1053 """
1054 req = None
1055 aPK_obj = {
1056 'lab': lab,
1057 'req_id': req_id
1058 }
1059 try:
1060 req = cLabRequest (aPK_obj)
1061 except gmExceptions.NoSuchClinItemError, msg:
1062 _log.info('%s: will try to create lab request' % str(msg))
1063 except gmExceptions.ConstructorError, msg:
1064 _log.exception(str(msg), sys.exc_info(), verbose=0)
1065 return (False, msg)
1066
1067 if req is not None:
1068 db_pat = req.get_patient()
1069 if db_pat is None:
1070 _log.error('cannot cross-check patient on lab request')
1071 return (None, '')
1072
1073 if pat_id != db_pat[0]:
1074 _log.error('lab request found for [%s:%s] but patient mismatch: expected [%s], in DB [%s]' % (lab, req_id, pat_id, db_pat))
1075 me = '$RCSfile: gmPathLab.py,v $ $Revision: 1.81 $'
1076 to = 'user'
1077 prob = _('The lab request already exists but belongs to a different patient.')
1078 sol = _('Verify which patient this lab request really belongs to.')
1079 ctxt = _('lab [%s], request ID [%s], expected link with patient [%s], currently linked to patient [%s]') % (lab, req_id, pat_id, db_pat)
1080 cat = 'lab'
1081 status, data = gmPG.add_housekeeping_todo(me, to, prob, sol, ctxt, cat)
1082 return (None, data)
1083 return (True, req)
1084
1085 queries = []
1086 if type(lab) is types.IntType:
1087 cmd = "insert into lab_request (fk_encounter, fk_episode, fk_test_org, request_id) values (%s, %s, %s, %s)"
1088 else:
1089 cmd = "insert into lab_request (fk_encounter, fk_episode, fk_test_org, request_id) values (%s, %s, (select pk from test_org where internal_name=%s), %s)"
1090 queries.append((cmd, [encounter_id, episode_id, str(lab), req_id]))
1091 cmd = "select currval('lab_request_pk_seq')"
1092 queries.append((cmd, []))
1093
1094 result, err = gmPG.run_commit('historica', queries, True)
1095 if result is None:
1096 return (False, err)
1097 try:
1098 req = cLabRequest(aPK_obj=result[0][0])
1099 except gmExceptions.ConstructorError, msg:
1100 _log.exception(str(msg), sys.exc_info(), verbose=0)
1101 return (False, msg)
1102 return (True, req)
1103
1104 -def create_lab_result(patient_id=None, when_field=None, when=None, test_type=None, val_num=None, val_alpha=None, unit=None, encounter_id=None, request=None):
1105 tres = None
1106 data = {
1107 'patient_id': patient_id,
1108 'when_field': when_field,
1109 'when': when,
1110 'test_type': test_type,
1111 'val_num': val_num,
1112 'val_alpha': val_alpha,
1113 'unit': unit
1114 }
1115 try:
1116 tres = cLabResult(aPK_obj=data)
1117
1118 _log.error('will not overwrite existing test result')
1119 _log.debug(str(tres))
1120 return (None, tres)
1121 except gmExceptions.NoSuchClinItemError:
1122 _log.debug('test result not found - as expected, will create it')
1123 except gmExceptions.ConstructorError, msg:
1124 _log.exception(str(msg), sys.exc_info(), verbose=0)
1125 return (False, msg)
1126 if request is None:
1127 return (False, _('need lab request when inserting lab result'))
1128
1129 if encounter_id is None:
1130 encounter_id = request['pk_encounter']
1131 queries = []
1132 cmd = "insert into test_result (fk_encounter, fk_episode, fk_type, val_num, val_alpha, val_unit) values (%s, %s, %s, %s, %s, %s)"
1133 queries.append((cmd, [encounter_id, request['pk_episode'], test_type, val_num, val_alpha, unit]))
1134 cmd = "insert into lnk_result2lab_req (fk_result, fk_request) values ((select currval('test_result_pk_seq')), %s)"
1135 queries.append((cmd, [request['pk_request']]))
1136 cmd = "select currval('test_result_pk_seq')"
1137 queries.append((cmd, []))
1138
1139 result, err = gmPG.run_commit('historica', queries, True)
1140 if result is None:
1141 return (False, err)
1142 try:
1143 tres = cLabResult(aPK_obj=result[0][0])
1144 except gmExceptions.ConstructorError, msg:
1145 _log.exception(str(msg), sys.exc_info(), verbose=0)
1146 return (False, msg)
1147 return (True, tres)
1148
1150
1151 if limit < 1:
1152 limit = 1
1153
1154 lim = limit + 1
1155 cmd = """
1156 select pk_result
1157 from v_results4lab_req
1158 where reviewed is false
1159 order by pk_patient
1160 limit %s""" % lim
1161 rows = gmPG.run_ro_query('historica', cmd)
1162 if rows is None:
1163 _log.error('error retrieving unreviewed lab results')
1164 return (None, _('error retrieving unreviewed lab results'))
1165 if len(rows) == 0:
1166 return (False, [])
1167
1168 if len(rows) == lim:
1169 more_avail = True
1170
1171 del rows[limit]
1172 else:
1173 more_avail = False
1174 results = []
1175 for row in rows:
1176 try:
1177 results.append(cLabResult(aPK_obj=row[0]))
1178 except gmExceptions.ConstructorError:
1179 _log.exception('skipping unreviewed lab result [%s]' % row[0], sys.exc_info(), verbose=0)
1180 return (more_avail, results)
1181
1183 lim = limit + 1
1184 cmd = "select pk from lab_request where is_pending is true limit %s" % lim
1185 rows = gmPG.run_ro_query('historica', cmd)
1186 if rows is None:
1187 _log.error('error retrieving pending lab requests')
1188 return (None, None)
1189 if len(rows) == 0:
1190 return (False, [])
1191 results = []
1192
1193 if len(rows) == lim:
1194 too_many = True
1195
1196 del rows[limit]
1197 else:
1198 too_many = False
1199 requests = []
1200 for row in rows:
1201 try:
1202 requests.append(cLabRequest(aPK_obj=row[0]))
1203 except gmExceptions.ConstructorError:
1204 _log.exception('skipping pending lab request [%s]' % row[0], sys.exc_info(), verbose=0)
1205 return (too_many, requests)
1206
1208 """Get logically next request ID for given lab.
1209
1210 - lab either test_org PK or test_org.internal_name
1211 - incrementor_func:
1212 - if not supplied the next ID is guessed
1213 - if supplied it is applied to the most recently used ID
1214 """
1215 if type(lab) == types.IntType:
1216 lab_snippet = 'vlr.fk_test_org=%s'
1217 else:
1218 lab_snippet = 'vlr.lab_name=%s'
1219 lab = str(lab)
1220 cmd = """
1221 select request_id
1222 from lab_request lr0
1223 where lr0.clin_when = (
1224 select max(vlr.sampled_when)
1225 from v_lab_requests vlr
1226 where %s
1227 )""" % lab_snippet
1228 rows = gmPG.run_ro_query('historica', cmd, None, lab)
1229 if rows is None:
1230 _log.warning('error getting most recently used request ID for lab [%s]' % lab)
1231 return ''
1232 if len(rows) == 0:
1233 return ''
1234 most_recent = rows[0][0]
1235
1236 if incrementor_func is not None:
1237 try:
1238 next = incrementor_func(most_recent)
1239 except TypeError:
1240 _log.error('cannot call incrementor function [%s]' % str(incrementor_func))
1241 return most_recent
1242 return next
1243
1244 for pos in range(len(most_recent)):
1245 header = most_recent[:pos]
1246 trailer = most_recent[pos:]
1247 try:
1248 return '%s%s' % (header, str(int(trailer) + 1))
1249 except ValueError:
1250 header = most_recent[:-1]
1251 trailer = most_recent[-1:]
1252 return '%s%s' % (header, chr(ord(trailer) + 1))
1253
1255 """Calculate BMI.
1256
1257 mass: kg
1258 height: cm
1259 age: not yet used
1260
1261 returns:
1262 (True/False, data)
1263 True: data = (bmi, lower_normal, upper_normal)
1264 False: data = error message
1265 """
1266 converted, mass = gmTools.input2decimal(mass)
1267 if not converted:
1268 return False, u'mass: cannot convert <%s> to Decimal' % mass
1269
1270 converted, height = gmTools.input2decimal(height)
1271 if not converted:
1272 return False, u'height: cannot convert <%s> to Decimal' % height
1273
1274 approx_surface = (height / decimal.Decimal(100))**2
1275 bmi = mass / approx_surface
1276
1277 print mass, height, '->', approx_surface, '->', bmi
1278
1279 lower_normal_mass = 20.0 * approx_surface
1280 upper_normal_mass = 25.0 * approx_surface
1281
1282 return True, (bmi, lower_normal_mass, upper_normal_mass)
1283
1284
1285
1286 if __name__ == '__main__':
1287
1288 if len(sys.argv) < 2:
1289 sys.exit()
1290
1291 if sys.argv[1] != 'test':
1292 sys.exit()
1293
1294 import time
1295
1296 gmI18N.activate_locale()
1297 gmI18N.install_domain()
1298
1299
1301 tr = create_test_result (
1302 encounter = 1,
1303 episode = 1,
1304 type = 1,
1305 intended_reviewer = 1,
1306 val_num = '12',
1307 val_alpha=None,
1308 unit = 'mg/dl'
1309 )
1310 print tr
1311 return tr
1312
1316
1321
1323 print "test_result()"
1324
1325 data = {
1326 'patient_id': 12,
1327 'when_field': 'val_when',
1328 'when': '2000-09-17 18:23:00+02',
1329 'test_type': 9,
1330 'val_num': 17.3,
1331 'val_alpha': None,
1332 'unit': 'mg/l'
1333 }
1334 lab_result = cLabResult(aPK_obj=data)
1335 print lab_result
1336 fields = lab_result.get_fields()
1337 for field in fields:
1338 print field, ':', lab_result[field]
1339 print "updatable:", lab_result.get_updatable_fields()
1340 print time.time()
1341 print lab_result.get_patient()
1342 print time.time()
1343
1345 print "test_request()"
1346 try:
1347
1348
1349 data = {
1350 'req_id': 'EML#SC937-0176-CEC#11',
1351 'lab': 'Enterprise Main Lab'
1352 }
1353 lab_req = cLabRequest(aPK_obj=data)
1354 except gmExceptions.ConstructorError, msg:
1355 print "no such lab request:", msg
1356 return
1357 print lab_req
1358 fields = lab_req.get_fields()
1359 for field in fields:
1360 print field, ':', lab_req[field]
1361 print "updatable:", lab_req.get_updatable_fields()
1362 print time.time()
1363 print lab_req.get_patient()
1364 print time.time()
1365
1370
1375
1383
1388
1393
1402
1404 done, data = calculate_bmi(mass = sys.argv[2], height = sys.argv[3])
1405 bmi, low, high = data
1406
1407 print "BMI:", bmi
1408 print "low:", low, "kg"
1409 print "hi :", high, "kg"
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425 test_calculate_bmi()
1426
1427
1428