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
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
591 -class cLabResult(gmBusinessDBObject.cBusinessDBObject):
592 """Represents one lab result."""
593
594 _cmd_fetch_payload = """
595 select *, xmin_test_result from v_results4lab_req
596 where pk_result=%s"""
597 _cmds_lock_rows_for_update = [
598 """select 1 from test_result where pk=%(pk_result)s and xmin=%(xmin_test_result)s for update"""
599 ]
600 _cmds_store_payload = [
601 """update test_result set
602 clin_when = %(val_when)s,
603 narrative = %(progress_note_result)s,
604 fk_type = %(pk_test_type)s,
605 val_num = %(val_num)s::numeric,
606 val_alpha = %(val_alpha)s,
607 val_unit = %(val_unit)s,
608 val_normal_min = %(val_normal_min)s,
609 val_normal_max = %(val_normal_max)s,
610 val_normal_range = %(val_normal_range)s,
611 val_target_min = %(val_target_min)s,
612 val_target_max = %(val_target_max)s,
613 val_target_range = %(val_target_range)s,
614 abnormality_indicator = %(abnormal)s,
615 norm_ref_group = %(ref_group)s,
616 note_provider = %(note_provider)s,
617 material = %(material)s,
618 material_detail = %(material_detail)s
619 where pk = %(pk_result)s""",
620 """select xmin_test_result from v_results4lab_req where pk_result=%(pk_result)s"""
621 ]
622
623 _updatable_fields = [
624 'val_when',
625 'progress_note_result',
626 'val_num',
627 'val_alpha',
628 'val_unit',
629 'val_normal_min',
630 'val_normal_max',
631 'val_normal_range',
632 'val_target_min',
633 'val_target_max',
634 'val_target_range',
635 'abnormal',
636 'ref_group',
637 'note_provider',
638 'material',
639 'material_detail'
640 ]
641
642 - def __init__(self, aPK_obj=None, row=None):
643 """Instantiate.
644
645 aPK_obj as dict:
646 - patient_id
647 - when_field (see view definition)
648 - when
649 - test_type
650 - val_num
651 - val_alpha
652 - unit
653 """
654
655 if aPK_obj is None:
656 gmBusinessDBObject.cBusinessDBObject.__init__(self, row=row)
657 return
658 pk = aPK_obj
659
660 if type(aPK_obj) == types.DictType:
661
662 if None in [aPK_obj['patient_id'], aPK_obj['when'], aPK_obj['when_field'], aPK_obj['test_type'], aPK_obj['unit']]:
663 raise gmExceptions.ConstructorError, 'parameter error: %s' % aPK_obj
664 if (aPK_obj['val_num'] is None) and (aPK_obj['val_alpha'] is None):
665 raise gmExceptions.ConstructorError, 'parameter error: val_num and val_alpha cannot both be None'
666
667 where_snippets = [
668 'pk_patient=%(patient_id)s',
669 'pk_test_type=%(test_type)s',
670 '%s=%%(when)s' % aPK_obj['when_field'],
671 'val_unit=%(unit)s'
672 ]
673 if aPK_obj['val_num'] is not None:
674 where_snippets.append('val_num=%(val_num)s::numeric')
675 if aPK_obj['val_alpha'] is not None:
676 where_snippets.append('val_alpha=%(val_alpha)s')
677
678 where_clause = ' and '.join(where_snippets)
679 cmd = "select pk_result from v_results4lab_req where %s" % where_clause
680 data = gmPG.run_ro_query('historica', cmd, None, aPK_obj)
681 if data is None:
682 raise gmExceptions.ConstructorError, 'error getting lab result for: %s' % aPK_obj
683 if len(data) == 0:
684 raise gmExceptions.NoSuchClinItemError, 'no lab result for: %s' % aPK_obj
685 pk = data[0][0]
686
687 gmBusinessDBObject.cBusinessDBObject.__init__(self, aPK_obj=pk)
688
690 cmd = """
691 select
692 %s,
693 vbp.title,
694 vbp.firstnames,
695 vbp.lastnames,
696 vbp.dob
697 from v_basic_person vbp
698 where vbp.pk_identity=%%s""" % self._payload[self._idx['pk_patient']]
699 pat = gmPG.run_ro_query('historica', cmd, None, self._payload[self._idx['pk_patient']])
700 return pat[0]
701
702 -class cLabRequest(gmBusinessDBObject.cBusinessDBObject):
703 """Represents one lab request."""
704
705 _cmd_fetch_payload = """
706 select *, xmin_lab_request from v_lab_requests
707 where pk_request=%s"""
708 _cmds_lock_rows_for_update = [
709 """select 1 from lab_request where pk=%(pk_request)s and xmin=%(xmin_lab_request)s for update"""
710 ]
711 _cmds_store_payload = [
712 """update lab_request set
713 request_id=%(request_id)s,
714 lab_request_id=%(lab_request_id)s,
715 clin_when=%(sampled_when)s,
716 lab_rxd_when=%(lab_rxd_when)s,
717 results_reported_when=%(results_reported_when)s,
718 request_status=%(request_status)s,
719 is_pending=%(is_pending)s::bool,
720 narrative=%(progress_note)s
721 where pk=%(pk_request)s""",
722 """select xmin_lab_request from v_lab_requests where pk_request=%(pk_request)s"""
723 ]
724 _updatable_fields = [
725 'request_id',
726 'lab_request_id',
727 'sampled_when',
728 'lab_rxd_when',
729 'results_reported_when',
730 'request_status',
731 'is_pending',
732 'progress_note'
733 ]
734
735 - def __init__(self, aPK_obj=None, row=None):
736 """Instantiate lab request.
737
738 The aPK_obj can be either a dict with the keys "req_id"
739 and "lab" or a simple primary key.
740 """
741
742 if aPK_obj is None:
743 gmBusinessDBObject.cBusinessDBObject.__init__(self, row=row)
744 return
745 pk = aPK_obj
746
747 if type(aPK_obj) == types.DictType:
748
749 try:
750 aPK_obj['req_id']
751 aPK_obj['lab']
752 except:
753 _log.exception('[%s:??]: faulty <aPK_obj> structure: [%s]' % (self.__class__.__name__, aPK_obj), sys.exc_info())
754 raise gmExceptions.ConstructorError, '[%s:??]: cannot derive PK from [%s]' % (self.__class__.__name__, aPK_obj)
755
756 where_snippets = []
757 vals = {}
758 where_snippets.append('request_id=%(req_id)s')
759 if type(aPK_obj['lab']) == types.IntType:
760 where_snippets.append('pk_test_org=%(lab)s')
761 else:
762 where_snippets.append('lab_name=%(lab)s')
763 where_clause = ' and '.join(where_snippets)
764 cmd = "select pk_request from v_lab_requests where %s" % where_clause
765
766 data = gmPG.run_ro_query('historica', cmd, None, aPK_obj)
767 if data is None:
768 raise gmExceptions.ConstructorError, '[%s:??]: error getting lab request for [%s]' % (self.__class__.__name__, aPK_obj)
769 if len(data) == 0:
770 raise gmExceptions.NoSuchClinItemError, '[%s:??]: no lab request for [%s]' % (self.__class__.__name__, aPK_obj)
771 pk = data[0][0]
772
773 gmBusinessDBObject.cBusinessDBObject.__init__(self, aPK_obj=pk)
774
776 cmd = """
777 select vpi.pk_patient, vbp.title, vbp.firstnames, vbp.lastnames, vbp.dob
778 from v_pat_items vpi, v_basic_person vbp
779 where
780 vpi.pk_item=%s
781 and
782 vbp.pk_identity=vpi.pk_patient"""
783 pat = gmPG.run_ro_query('historica', cmd, None, self._payload[self._idx['pk_item']])
784 if pat is None:
785 _log.error('cannot get patient for lab request [%s]' % self._payload[self._idx['pk_item']])
786 return None
787 if len(pat) == 0:
788 _log.error('no patient associated with lab request [%s]' % self._payload[self._idx['pk_item']])
789 return None
790 return pat[0]
791
792
793
794 -def create_lab_request(lab=None, req_id=None, pat_id=None, encounter_id=None, episode_id=None):
795 """Create or get lab request.
796
797 returns tuple (status, value):
798 (True, lab request instance)
799 (False, error message)
800 (None, housekeeping_todo primary key)
801 """
802 req = None
803 aPK_obj = {
804 'lab': lab,
805 'req_id': req_id
806 }
807 try:
808 req = cLabRequest (aPK_obj)
809 except gmExceptions.NoSuchClinItemError, msg:
810 _log.info('%s: will try to create lab request' % str(msg))
811 except gmExceptions.ConstructorError, msg:
812 _log.exception(str(msg), sys.exc_info(), verbose=0)
813 return (False, msg)
814
815 if req is not None:
816 db_pat = req.get_patient()
817 if db_pat is None:
818 _log.error('cannot cross-check patient on lab request')
819 return (None, '')
820
821 if pat_id != db_pat[0]:
822 _log.error('lab request found for [%s:%s] but patient mismatch: expected [%s], in DB [%s]' % (lab, req_id, pat_id, db_pat))
823 me = '$RCSfile: gmPathLab.py,v $ $Revision: 1.81 $'
824 to = 'user'
825 prob = _('The lab request already exists but belongs to a different patient.')
826 sol = _('Verify which patient this lab request really belongs to.')
827 ctxt = _('lab [%s], request ID [%s], expected link with patient [%s], currently linked to patient [%s]') % (lab, req_id, pat_id, db_pat)
828 cat = 'lab'
829 status, data = gmPG.add_housekeeping_todo(me, to, prob, sol, ctxt, cat)
830 return (None, data)
831 return (True, req)
832
833 queries = []
834 if type(lab) is types.IntType:
835 cmd = "insert into lab_request (fk_encounter, fk_episode, fk_test_org, request_id) values (%s, %s, %s, %s)"
836 else:
837 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)"
838 queries.append((cmd, [encounter_id, episode_id, str(lab), req_id]))
839 cmd = "select currval('lab_request_pk_seq')"
840 queries.append((cmd, []))
841
842 result, err = gmPG.run_commit('historica', queries, True)
843 if result is None:
844 return (False, err)
845 try:
846 req = cLabRequest(aPK_obj=result[0][0])
847 except gmExceptions.ConstructorError, msg:
848 _log.exception(str(msg), sys.exc_info(), verbose=0)
849 return (False, msg)
850 return (True, req)
851
852 -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):
853 tres = None
854 data = {
855 'patient_id': patient_id,
856 'when_field': when_field,
857 'when': when,
858 'test_type': test_type,
859 'val_num': val_num,
860 'val_alpha': val_alpha,
861 'unit': unit
862 }
863 try:
864 tres = cLabResult(aPK_obj=data)
865
866 _log.error('will not overwrite existing test result')
867 _log.debug(str(tres))
868 return (None, tres)
869 except gmExceptions.NoSuchClinItemError:
870 _log.debug('test result not found - as expected, will create it')
871 except gmExceptions.ConstructorError, msg:
872 _log.exception(str(msg), sys.exc_info(), verbose=0)
873 return (False, msg)
874 if request is None:
875 return (False, _('need lab request when inserting lab result'))
876
877 if encounter_id is None:
878 encounter_id = request['pk_encounter']
879 queries = []
880 cmd = "insert into test_result (fk_encounter, fk_episode, fk_type, val_num, val_alpha, val_unit) values (%s, %s, %s, %s, %s, %s)"
881 queries.append((cmd, [encounter_id, request['pk_episode'], test_type, val_num, val_alpha, unit]))
882 cmd = "insert into lnk_result2lab_req (fk_result, fk_request) values ((select currval('test_result_pk_seq')), %s)"
883 queries.append((cmd, [request['pk_request']]))
884 cmd = "select currval('test_result_pk_seq')"
885 queries.append((cmd, []))
886
887 result, err = gmPG.run_commit('historica', queries, True)
888 if result is None:
889 return (False, err)
890 try:
891 tres = cLabResult(aPK_obj=result[0][0])
892 except gmExceptions.ConstructorError, msg:
893 _log.exception(str(msg), sys.exc_info(), verbose=0)
894 return (False, msg)
895 return (True, tres)
896
898
899 if limit < 1:
900 limit = 1
901
902 lim = limit + 1
903 cmd = """
904 select pk_result
905 from v_results4lab_req
906 where reviewed is false
907 order by pk_patient
908 limit %s""" % lim
909 rows = gmPG.run_ro_query('historica', cmd)
910 if rows is None:
911 _log.error('error retrieving unreviewed lab results')
912 return (None, _('error retrieving unreviewed lab results'))
913 if len(rows) == 0:
914 return (False, [])
915
916 if len(rows) == lim:
917 more_avail = True
918
919 del rows[limit]
920 else:
921 more_avail = False
922 results = []
923 for row in rows:
924 try:
925 results.append(cLabResult(aPK_obj=row[0]))
926 except gmExceptions.ConstructorError:
927 _log.exception('skipping unreviewed lab result [%s]' % row[0], sys.exc_info(), verbose=0)
928 return (more_avail, results)
929
931 lim = limit + 1
932 cmd = "select pk from lab_request where is_pending is true limit %s" % lim
933 rows = gmPG.run_ro_query('historica', cmd)
934 if rows is None:
935 _log.error('error retrieving pending lab requests')
936 return (None, None)
937 if len(rows) == 0:
938 return (False, [])
939 results = []
940
941 if len(rows) == lim:
942 too_many = True
943
944 del rows[limit]
945 else:
946 too_many = False
947 requests = []
948 for row in rows:
949 try:
950 requests.append(cLabRequest(aPK_obj=row[0]))
951 except gmExceptions.ConstructorError:
952 _log.exception('skipping pending lab request [%s]' % row[0], sys.exc_info(), verbose=0)
953 return (too_many, requests)
954
956 """Get logically next request ID for given lab.
957
958 - lab either test_org PK or test_org.internal_name
959 - incrementor_func:
960 - if not supplied the next ID is guessed
961 - if supplied it is applied to the most recently used ID
962 """
963 if type(lab) == types.IntType:
964 lab_snippet = 'vlr.fk_test_org=%s'
965 else:
966 lab_snippet = 'vlr.lab_name=%s'
967 lab = str(lab)
968 cmd = """
969 select request_id
970 from lab_request lr0
971 where lr0.clin_when = (
972 select max(vlr.sampled_when)
973 from v_lab_requests vlr
974 where %s
975 )""" % lab_snippet
976 rows = gmPG.run_ro_query('historica', cmd, None, lab)
977 if rows is None:
978 _log.warning('error getting most recently used request ID for lab [%s]' % lab)
979 return ''
980 if len(rows) == 0:
981 return ''
982 most_recent = rows[0][0]
983
984 if incrementor_func is not None:
985 try:
986 next = incrementor_func(most_recent)
987 except TypeError:
988 _log.error('cannot call incrementor function [%s]' % str(incrementor_func))
989 return most_recent
990 return next
991
992 for pos in range(len(most_recent)):
993 header = most_recent[:pos]
994 trailer = most_recent[pos:]
995 try:
996 return '%s%s' % (header, str(int(trailer) + 1))
997 except ValueError:
998 header = most_recent[:-1]
999 trailer = most_recent[-1:]
1000 return '%s%s' % (header, chr(ord(trailer) + 1))
1001
1002
1003
1004 if __name__ == '__main__':
1005 import time
1006
1007
1009 tr = create_test_result (
1010 encounter = 1,
1011 episode = 1,
1012 type = 1,
1013 intended_reviewer = 1,
1014 val_num = '12',
1015 val_alpha=None,
1016 unit = 'mg/dl'
1017 )
1018 print tr
1019 return tr
1020
1024
1029
1031 print "test_result()"
1032
1033 data = {
1034 'patient_id': 12,
1035 'when_field': 'val_when',
1036 'when': '2000-09-17 18:23:00+02',
1037 'test_type': 9,
1038 'val_num': 17.3,
1039 'val_alpha': None,
1040 'unit': 'mg/l'
1041 }
1042 lab_result = cLabResult(aPK_obj=data)
1043 print lab_result
1044 fields = lab_result.get_fields()
1045 for field in fields:
1046 print field, ':', lab_result[field]
1047 print "updatable:", lab_result.get_updatable_fields()
1048 print time.time()
1049 print lab_result.get_patient()
1050 print time.time()
1051
1053 print "test_request()"
1054 try:
1055
1056
1057 data = {
1058 'req_id': 'EML#SC937-0176-CEC#11',
1059 'lab': 'Enterprise Main Lab'
1060 }
1061 lab_req = cLabRequest(aPK_obj=data)
1062 except gmExceptions.ConstructorError, msg:
1063 print "no such lab request:", msg
1064 return
1065 print lab_req
1066 fields = lab_req.get_fields()
1067 for field in fields:
1068 print field, ':', lab_req[field]
1069 print "updatable:", lab_req.get_updatable_fields()
1070 print time.time()
1071 print lab_req.get_patient()
1072 print time.time()
1073
1078
1083
1091
1096
1101
1102 if (len(sys.argv) > 1) and (sys.argv[1] == 'test'):
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114 test_test_type()
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
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