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