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
20
21 _log = logging.getLogger('gm.lab')
22 _log.info(__version__)
23
24
25
26
27 -class cTestOrg(gmBusinessDBObject.cBusinessDBObject):
28 """Represents one test org/lab."""
29
30 _cmd_fetch_payload = u"""SELECT *, xmin FROM clin.test_org WHERE pk = %s"""
31
32 _cmds_store_payload = [
33 u"""UPDATE clin.test_org SET
34 internal_name = gm.nullify_empty_string(%(internal_name)s),
35 contact = gm.nullify_empty_string(%(contact)s),
36 comment = gm.nullify_empty_string(%(comment)s)
37 WHERE
38 pk = %(pk)s
39 AND
40 xmin = %(xmin)s
41 RETURNING
42 xmin
43 """
44 ]
45
46 _updatable_fields = [
47 u'internal_name',
48 u'contact',
49 u'comment'
50 ]
51
53 cmd = u'insert into clin.test_org (internal_name) values (%(name)s) returning pk'
54 args = {'name': name.strip()}
55 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True)
56 return cTestOrg(aPK_obj = rows[0]['pk'])
57
59 cmd = u'select *, xmin from clin.test_org order by %s' % order_by
60 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = True)
61 return [ cTestOrg(row = {'pk_field': 'pk', 'data': r, 'idx': idx}) for r in rows ]
62
71
76
81
83 """Represents one unified test type."""
84
85
86 _cmd_fetch_payload = u"""select * from clin.v_unified_test_types where pk_test_type = %s"""
87
88 _cmds_store_payload = []
89
90 _updatable_fields = []
91
93 cmd = u"""
94 SELECT pk_test_result, clin_when
95 FROM clin.v_test_results
96 WHERE
97 pk_patient = %(pat)s
98 AND
99 pk_meta_test_type = %(pkmtt)s
100 ORDER BY clin_when DESC
101 LIMIT 1
102 """
103 args = {'pat': pk_patient, 'pkmtt': self._payload[self._idx['pk_meta_test_type']]}
104 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
105 if len(rows) == 0:
106 return None
107 return cTestResult(aPK_obj = rows[0]['pk_test_result'])
108
110 """Represents one test result type."""
111
112 _cmd_fetch_payload = u"""select * from clin.v_test_types where pk_test_type = %s"""
113
114 _cmds_store_payload = [
115 u"""update clin.test_type set
116 abbrev = %(abbrev)s,
117 name = %(name)s,
118 loinc = gm.nullify_empty_string(%(loinc)s),
119 code = gm.nullify_empty_string(%(code)s),
120 coding_system = gm.nullify_empty_string(%(coding_system)s),
121 comment = gm.nullify_empty_string(%(comment_type)s),
122 conversion_unit = gm.nullify_empty_string(%(conversion_unit)s),
123 fk_test_org = %(pk_test_org)s
124 where pk = %(pk_test_type)s""",
125 u"""select xmin_test_type from clin.v_test_types where pk_test_type = %(pk_test_type)s"""
126 ]
127
128 _updatable_fields = [
129 'abbrev',
130 'name',
131 'loinc',
132 'code',
133 'coding_system',
134 'comment_type',
135 'conversion_unit',
136 'pk_test_org'
137 ]
138
153
155 cmd = u'select exists(select 1 from clin.test_result where fk_type = %(pk_type)s)'
156 args = {'pk_type': self._payload[self._idx['pk_test_type']]}
157 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
158 return rows[0][0]
159
160 in_use = property(_get_in_use, lambda x:x)
161
163 cmd = u'select * from clin.v_test_types %s' % gmTools.coalesce(order_by, u'', u'order by %s')
164 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = True)
165 return [ cMeasurementType(row = {'pk_field': 'pk_test_type', 'data': r, 'idx': idx}) for r in rows ]
166
168
169 if (abbrev is None) and (name is None):
170 raise ArgumentError('must have <abbrev> and/or <name> set')
171
172 where_snippets = []
173
174 if lab is None:
175 where_snippets.append('pk_test_org is null')
176 else:
177 try:
178 int(lab)
179 where_snippets.append('pk_test_org = %(lab)s')
180 except (TypeError, ValueError):
181 where_snippets.append('pk_test_org = (select pk from clin.test_org where internal_name = %(lab)s)')
182
183 if abbrev is not None:
184 where_snippets.append('abbrev = %(abbrev)s')
185
186 if name is not None:
187 where_snippets.append('name = %(name)s')
188
189 where_clause = u' and '.join(where_snippets)
190 cmd = u"select * from clin.v_test_types where %s" % where_clause
191 args = {'lab': lab, 'abbrev': abbrev, 'name': name}
192
193 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
194
195 if len(rows) == 0:
196 return None
197
198 tt = cMeasurementType(row = {'pk_field': 'pk_test_type', 'data': rows[0], 'idx': idx})
199 return tt
200
202 cmd = u'delete from clin.test_type where pk = %(pk)s'
203 args = {'pk': measurement_type}
204 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
205
207 """Create or get test type."""
208
209 ttype = find_measurement_type(lab = lab, abbrev = abbrev, name = name)
210
211 if ttype is not None:
212 return ttype
213
214 _log.debug('creating test type [%s:%s:%s:%s]', lab, abbrev, name, unit)
215
216
217 if unit is None:
218 _log.error('need <unit> to create test type: %s:%s:%s:%s' % (lab, abbrev, name, unit))
219 raise ValueError('need <unit> to create test type')
220
221
222 cols = []
223 val_snippets = []
224 vals = {}
225
226
227 if lab is None:
228 lab = create_measurement_org()
229
230 cols.append('fk_test_org')
231 try:
232 vals['lab'] = int(lab)
233 val_snippets.append('%(lab)s')
234 except:
235 vals['lab'] = lab
236 val_snippets.append('(select pk from clin.test_org where internal_name = %(lab)s)')
237
238
239 cols.append('abbrev')
240 val_snippets.append('%(abbrev)s')
241 vals['abbrev'] = abbrev
242
243
244 cols.append('conversion_unit')
245 val_snippets.append('%(unit)s')
246 vals['unit'] = unit
247
248
249 if name is not None:
250 cols.append('name')
251 val_snippets.append('%(name)s')
252 vals['name'] = name
253
254 col_clause = u', '.join(cols)
255 val_clause = u', '.join(val_snippets)
256 queries = [
257 {'cmd': u'insert into clin.test_type (%s) values (%s)' % (col_clause, val_clause), 'args': vals},
258 {'cmd': u"select * from clin.v_test_types where pk_test_type = currval(pg_get_serial_sequence('clin.test_type', 'pk'))"}
259 ]
260 rows, idx = gmPG2.run_rw_queries(queries = queries, get_col_idx = True, return_data = True)
261 ttype = cMeasurementType(row = {'pk_field': 'pk_test_type', 'data': rows[0], 'idx': idx})
262
263 return ttype
264
266
267 if name is None:
268 name = _('inhouse lab')
269 comment = _('auto-generated')
270
271 cmd = u'select * from clin.test_org where internal_name = %(name)s'
272 if comment is not None:
273 comment = comment.strip()
274 args = {'name': name, 'cmt': comment}
275 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
276
277 if len(rows) == 0:
278 queries = [
279 {'cmd': u'insert into clin.test_org (fk_org, internal_name, comment) values (null, %(name)s, %(cmt)s)', 'args': args},
280 {'cmd': u"select currval(pg_get_serial_sequence('clin.test_org', 'pk')) as pk"}
281 ]
282 else:
283
284 args['pk'] = rows[0]['pk']
285 queries = [
286 {'cmd': u'update clin.test_org set comment = %(cmt)s where pk = %(pk)s', 'args': args},
287 {'cmd': u'select %(pk)s as pk', 'args': args}
288 ]
289
290 rows, idx = gmPG2.run_rw_queries(queries = queries, return_data = True)
291
292 return rows[0]['pk']
293
294 -class cTestResult(gmBusinessDBObject.cBusinessDBObject):
295 """Represents one test result."""
296
297 _cmd_fetch_payload = u"select * from clin.v_test_results where pk_test_result = %s"
298
299 _cmds_store_payload = [
300 u"""update clin.test_result set
301 clin_when = %(clin_when)s,
302 narrative = nullif(trim(%(comment)s), ''),
303 val_num = %(val_num)s,
304 val_alpha = nullif(trim(%(val_alpha)s), ''),
305 val_unit = nullif(trim(%(val_unit)s), ''),
306 val_normal_min = %(val_normal_min)s,
307 val_normal_max = %(val_normal_max)s,
308 val_normal_range = nullif(trim(%(val_normal_range)s), ''),
309 val_target_min = %(val_target_min)s,
310 val_target_max = %(val_target_max)s,
311 val_target_range = nullif(trim(%(val_target_range)s), ''),
312 abnormality_indicator = nullif(trim(%(abnormality_indicator)s), ''),
313 norm_ref_group = nullif(trim(%(norm_ref_group)s), ''),
314 note_test_org = nullif(trim(%(note_test_org)s), ''),
315 material = nullif(trim(%(material)s), ''),
316 material_detail = nullif(trim(%(material_detail)s), ''),
317 fk_intended_reviewer = %(pk_intended_reviewer)s,
318 fk_encounter = %(pk_encounter)s,
319 fk_episode = %(pk_episode)s,
320 fk_type = %(pk_test_type)s
321 where
322 pk = %(pk_test_result)s and
323 xmin = %(xmin_test_result)s""",
324 u"""select xmin_test_result from clin.v_test_results where pk_test_result = %(pk_test_result)s"""
325 ]
326
327 _updatable_fields = [
328 'clin_when',
329 'comment',
330 'val_num',
331 'val_alpha',
332 'val_unit',
333 'val_normal_min',
334 'val_normal_max',
335 'val_normal_range',
336 'val_target_min',
337 'val_target_max',
338 'val_target_range',
339 'abnormality_indicator',
340 'norm_ref_group',
341 'note_test_org',
342 'material',
343 'material_detail',
344 'pk_intended_reviewer',
345 'pk_encounter',
346 'pk_episode',
347 'pk_test_type'
348 ]
349
385
387
388 cmd = u"""
389 select
390 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)
391 pk_patient,
392 val_unit,
393 val_normal_min, val_normal_max, val_normal_range,
394 val_target_min, val_target_max, val_target_range,
395 norm_ref_group,
396 coalesce(norm_ref_group, '') as norm_ref_group_str
397 from
398 clin.v_test_results
399 where
400 pk_test_type = %(pk_type)s
401 """
402 args = {'pk_type': self._payload[self._idx['pk_test_type']]}
403 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
404 return rows
405
407 raise AttributeError('[%s]: reference ranges not settable') % self.__class__.__name__
408
409 reference_ranges = property(_get_reference_ranges, _set_reference_ranges)
410
411 - def set_review(self, technically_abnormal=None, clinically_relevant=None, comment=None, make_me_responsible=False):
412
413 if comment is not None:
414 comment = comment.strip()
415
416 if ((technically_abnormal is None) and
417 (clinically_relevant is None) and
418 (comment is None) and
419 (make_me_responsible is False)):
420 return True
421
422
423 if self._payload[self._idx['reviewed']]:
424 self.__change_existing_review (
425 technically_abnormal = technically_abnormal,
426 clinically_relevant = clinically_relevant,
427 comment = comment
428 )
429 else:
430 self.__set_new_review (
431 technically_abnormal = technically_abnormal,
432 clinically_relevant = clinically_relevant,
433 comment = comment
434 )
435
436 if make_me_responsible is True:
437 cmd = u"select pk from dem.staff where db_user = current_user"
438 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}])
439 self['pk_intended_reviewer'] = rows[0][0]
440 self.save_payload()
441 else:
442 self.refetch_payload()
443
444
445
446 - def __set_new_review(self, technically_abnormal=None, clinically_relevant=None, comment=None):
447 """Add a review to a row.
448
449 - if technically abnormal is not provided/None it will be set
450 to True if the lab's indicator has a meaningful value
451 - if clinically relevant is not provided/None it is set to
452 whatever technically abnormal is
453 """
454 if technically_abnormal is None:
455 technically_abnormal = False
456 if self._payload[self._idx['abnormality_indicator']] is not None:
457 if self._payload[self._idx['abnormality_indicator']].strip() != u'':
458 technically_abnormal = True
459
460 if clinically_relevant is None:
461 clinically_relevant = technically_abnormal
462
463 cmd = u"""
464 insert into clin.reviewed_test_results (
465 fk_reviewed_row,
466 is_technically_abnormal,
467 clinically_relevant,
468 comment
469 ) values (
470 %(pk)s,
471 %(abnormal)s,
472 %(relevant)s,
473 %(cmt)s
474 )"""
475 args = {
476 'pk': self._payload[self._idx['pk_test_result']],
477 'abnormal': technically_abnormal,
478 'relevant': clinically_relevant,
479 'cmt': comment
480 }
481
482 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
483
485 """Change a review on a row.
486
487 - if technically abnormal/clinically relevant/comment are
488 None (or empty) they are not set
489 """
490 args = {
491 'pk_row': self._payload[self._idx['pk_test_result']],
492 'abnormal': technically_abnormal,
493 'relevant': clinically_relevant
494 }
495
496 set_parts = []
497
498 if technically_abnormal is not None:
499 set_parts.append(u'is_technically_abnormal = %(abnormal)s')
500
501 if clinically_relevant is not None:
502 set_parts.append(u'clinically_relevant= %(relevant)s')
503
504 if comment is not None:
505 set_parts.append('comment = %(cmt)s')
506 args['cmt'] = comment
507
508 cmd = u"""
509 update clin.reviewed_test_results set
510 fk_reviewer = (select pk from dem.staff where db_user = current_user),
511 %s
512 where
513 fk_reviewed_row = %%(pk_row)s
514 """ % u',\n '.join(set_parts)
515
516 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
517
519
520 try:
521 pk = int(result)
522 except (TypeError, AttributeError):
523 pk = result['pk_test_result']
524
525 cmd = u'delete from clin.test_result where pk = %(pk)s'
526 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': {'pk': pk}}])
527
528 -def create_test_result(encounter=None, episode=None, type=None, intended_reviewer=None, val_num=None, val_alpha=None, unit=None):
529
530 cmd1 = u"""
531 insert into clin.test_result (
532 fk_encounter,
533 fk_episode,
534 fk_type,
535 fk_intended_reviewer,
536 val_num,
537 val_alpha,
538 val_unit
539 ) values (
540 %(enc)s,
541 %(epi)s,
542 %(type)s,
543 %(rev)s,
544 %(v_num)s,
545 %(v_alpha)s,
546 %(unit)s
547 )"""
548
549 cmd2 = u"""
550 select *
551 from
552 clin.v_test_results
553 where
554 pk_test_result = currval(pg_get_serial_sequence('clin.test_result', 'pk'))"""
555
556 args = {
557 u'enc': encounter,
558 u'epi': episode,
559 u'type': type,
560 u'rev': intended_reviewer,
561 u'v_num': val_num,
562 u'v_alpha': val_alpha,
563 u'unit': unit
564 }
565
566 rows, idx = gmPG2.run_rw_queries (
567 queries = [
568 {'cmd': cmd1, 'args': args},
569 {'cmd': cmd2}
570 ],
571 return_data = True,
572 get_col_idx = True
573 )
574
575 tr = cTestResult(row = {
576 'pk_field': 'pk_test_result',
577 'idx': idx,
578 'data': rows[0]
579 })
580
581 return tr
582
583 -class cLabResult(gmBusinessDBObject.cBusinessDBObject):
584 """Represents one lab result."""
585
586 _cmd_fetch_payload = """
587 select *, xmin_test_result from v_results4lab_req
588 where pk_result=%s"""
589 _cmds_lock_rows_for_update = [
590 """select 1 from test_result where pk=%(pk_result)s and xmin=%(xmin_test_result)s for update"""
591 ]
592 _cmds_store_payload = [
593 """update test_result set
594 clin_when = %(val_when)s,
595 narrative = %(progress_note_result)s,
596 fk_type = %(pk_test_type)s,
597 val_num = %(val_num)s::numeric,
598 val_alpha = %(val_alpha)s,
599 val_unit = %(val_unit)s,
600 val_normal_min = %(val_normal_min)s,
601 val_normal_max = %(val_normal_max)s,
602 val_normal_range = %(val_normal_range)s,
603 val_target_min = %(val_target_min)s,
604 val_target_max = %(val_target_max)s,
605 val_target_range = %(val_target_range)s,
606 abnormality_indicator = %(abnormal)s,
607 norm_ref_group = %(ref_group)s,
608 note_provider = %(note_provider)s,
609 material = %(material)s,
610 material_detail = %(material_detail)s
611 where pk = %(pk_result)s""",
612 """select xmin_test_result from v_results4lab_req where pk_result=%(pk_result)s"""
613 ]
614
615 _updatable_fields = [
616 'val_when',
617 'progress_note_result',
618 'val_num',
619 'val_alpha',
620 'val_unit',
621 'val_normal_min',
622 'val_normal_max',
623 'val_normal_range',
624 'val_target_min',
625 'val_target_max',
626 'val_target_range',
627 'abnormal',
628 'ref_group',
629 'note_provider',
630 'material',
631 'material_detail'
632 ]
633
634 - def __init__(self, aPK_obj=None, row=None):
635 """Instantiate.
636
637 aPK_obj as dict:
638 - patient_id
639 - when_field (see view definition)
640 - when
641 - test_type
642 - val_num
643 - val_alpha
644 - unit
645 """
646
647 if aPK_obj is None:
648 gmBusinessDBObject.cBusinessDBObject.__init__(self, row=row)
649 return
650 pk = aPK_obj
651
652 if type(aPK_obj) == types.DictType:
653
654 if None in [aPK_obj['patient_id'], aPK_obj['when'], aPK_obj['when_field'], aPK_obj['test_type'], aPK_obj['unit']]:
655 raise gmExceptions.ConstructorError, 'parameter error: %s' % aPK_obj
656 if (aPK_obj['val_num'] is None) and (aPK_obj['val_alpha'] is None):
657 raise gmExceptions.ConstructorError, 'parameter error: val_num and val_alpha cannot both be None'
658
659 where_snippets = [
660 'pk_patient=%(patient_id)s',
661 'pk_test_type=%(test_type)s',
662 '%s=%%(when)s' % aPK_obj['when_field'],
663 'val_unit=%(unit)s'
664 ]
665 if aPK_obj['val_num'] is not None:
666 where_snippets.append('val_num=%(val_num)s::numeric')
667 if aPK_obj['val_alpha'] is not None:
668 where_snippets.append('val_alpha=%(val_alpha)s')
669
670 where_clause = ' and '.join(where_snippets)
671 cmd = "select pk_result from v_results4lab_req where %s" % where_clause
672 data = gmPG.run_ro_query('historica', cmd, None, aPK_obj)
673 if data is None:
674 raise gmExceptions.ConstructorError, 'error getting lab result for: %s' % aPK_obj
675 if len(data) == 0:
676 raise gmExceptions.NoSuchClinItemError, 'no lab result for: %s' % aPK_obj
677 pk = data[0][0]
678
679 gmBusinessDBObject.cBusinessDBObject.__init__(self, aPK_obj=pk)
680
682 cmd = """
683 select
684 %s,
685 vbp.title,
686 vbp.firstnames,
687 vbp.lastnames,
688 vbp.dob
689 from v_basic_person vbp
690 where vbp.pk_identity=%%s""" % self._payload[self._idx['pk_patient']]
691 pat = gmPG.run_ro_query('historica', cmd, None, self._payload[self._idx['pk_patient']])
692 return pat[0]
693
694 -class cLabRequest(gmBusinessDBObject.cBusinessDBObject):
695 """Represents one lab request."""
696
697 _cmd_fetch_payload = """
698 select *, xmin_lab_request from v_lab_requests
699 where pk_request=%s"""
700 _cmds_lock_rows_for_update = [
701 """select 1 from lab_request where pk=%(pk_request)s and xmin=%(xmin_lab_request)s for update"""
702 ]
703 _cmds_store_payload = [
704 """update lab_request set
705 request_id=%(request_id)s,
706 lab_request_id=%(lab_request_id)s,
707 clin_when=%(sampled_when)s,
708 lab_rxd_when=%(lab_rxd_when)s,
709 results_reported_when=%(results_reported_when)s,
710 request_status=%(request_status)s,
711 is_pending=%(is_pending)s::bool,
712 narrative=%(progress_note)s
713 where pk=%(pk_request)s""",
714 """select xmin_lab_request from v_lab_requests where pk_request=%(pk_request)s"""
715 ]
716 _updatable_fields = [
717 'request_id',
718 'lab_request_id',
719 'sampled_when',
720 'lab_rxd_when',
721 'results_reported_when',
722 'request_status',
723 'is_pending',
724 'progress_note'
725 ]
726
727 - def __init__(self, aPK_obj=None, row=None):
728 """Instantiate lab request.
729
730 The aPK_obj can be either a dict with the keys "req_id"
731 and "lab" or a simple primary key.
732 """
733
734 if aPK_obj is None:
735 gmBusinessDBObject.cBusinessDBObject.__init__(self, row=row)
736 return
737 pk = aPK_obj
738
739 if type(aPK_obj) == types.DictType:
740
741 try:
742 aPK_obj['req_id']
743 aPK_obj['lab']
744 except:
745 _log.exception('[%s:??]: faulty <aPK_obj> structure: [%s]' % (self.__class__.__name__, aPK_obj), sys.exc_info())
746 raise gmExceptions.ConstructorError, '[%s:??]: cannot derive PK from [%s]' % (self.__class__.__name__, aPK_obj)
747
748 where_snippets = []
749 vals = {}
750 where_snippets.append('request_id=%(req_id)s')
751 if type(aPK_obj['lab']) == types.IntType:
752 where_snippets.append('pk_test_org=%(lab)s')
753 else:
754 where_snippets.append('lab_name=%(lab)s')
755 where_clause = ' and '.join(where_snippets)
756 cmd = "select pk_request from v_lab_requests where %s" % where_clause
757
758 data = gmPG.run_ro_query('historica', cmd, None, aPK_obj)
759 if data is None:
760 raise gmExceptions.ConstructorError, '[%s:??]: error getting lab request for [%s]' % (self.__class__.__name__, aPK_obj)
761 if len(data) == 0:
762 raise gmExceptions.NoSuchClinItemError, '[%s:??]: no lab request for [%s]' % (self.__class__.__name__, aPK_obj)
763 pk = data[0][0]
764
765 gmBusinessDBObject.cBusinessDBObject.__init__(self, aPK_obj=pk)
766
768 cmd = """
769 select vpi.pk_patient, vbp.title, vbp.firstnames, vbp.lastnames, vbp.dob
770 from v_pat_items vpi, v_basic_person vbp
771 where
772 vpi.pk_item=%s
773 and
774 vbp.pk_identity=vpi.pk_patient"""
775 pat = gmPG.run_ro_query('historica', cmd, None, self._payload[self._idx['pk_item']])
776 if pat is None:
777 _log.error('cannot get patient for lab request [%s]' % self._payload[self._idx['pk_item']])
778 return None
779 if len(pat) == 0:
780 _log.error('no patient associated with lab request [%s]' % self._payload[self._idx['pk_item']])
781 return None
782 return pat[0]
783
784
785
786 -def create_lab_request(lab=None, req_id=None, pat_id=None, encounter_id=None, episode_id=None):
787 """Create or get lab request.
788
789 returns tuple (status, value):
790 (True, lab request instance)
791 (False, error message)
792 (None, housekeeping_todo primary key)
793 """
794 req = None
795 aPK_obj = {
796 'lab': lab,
797 'req_id': req_id
798 }
799 try:
800 req = cLabRequest (aPK_obj)
801 except gmExceptions.NoSuchClinItemError, msg:
802 _log.info('%s: will try to create lab request' % str(msg))
803 except gmExceptions.ConstructorError, msg:
804 _log.exception(str(msg), sys.exc_info(), verbose=0)
805 return (False, msg)
806
807 if req is not None:
808 db_pat = req.get_patient()
809 if db_pat is None:
810 _log.error('cannot cross-check patient on lab request')
811 return (None, '')
812
813 if pat_id != db_pat[0]:
814 _log.error('lab request found for [%s:%s] but patient mismatch: expected [%s], in DB [%s]' % (lab, req_id, pat_id, db_pat))
815 me = '$RCSfile: gmPathLab.py,v $ $Revision: 1.81 $'
816 to = 'user'
817 prob = _('The lab request already exists but belongs to a different patient.')
818 sol = _('Verify which patient this lab request really belongs to.')
819 ctxt = _('lab [%s], request ID [%s], expected link with patient [%s], currently linked to patient [%s]') % (lab, req_id, pat_id, db_pat)
820 cat = 'lab'
821 status, data = gmPG.add_housekeeping_todo(me, to, prob, sol, ctxt, cat)
822 return (None, data)
823 return (True, req)
824
825 queries = []
826 if type(lab) is types.IntType:
827 cmd = "insert into lab_request (fk_encounter, fk_episode, fk_test_org, request_id) values (%s, %s, %s, %s)"
828 else:
829 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)"
830 queries.append((cmd, [encounter_id, episode_id, str(lab), req_id]))
831 cmd = "select currval('lab_request_pk_seq')"
832 queries.append((cmd, []))
833
834 result, err = gmPG.run_commit('historica', queries, True)
835 if result is None:
836 return (False, err)
837 try:
838 req = cLabRequest(aPK_obj=result[0][0])
839 except gmExceptions.ConstructorError, msg:
840 _log.exception(str(msg), sys.exc_info(), verbose=0)
841 return (False, msg)
842 return (True, req)
843
844 -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):
845 tres = None
846 data = {
847 'patient_id': patient_id,
848 'when_field': when_field,
849 'when': when,
850 'test_type': test_type,
851 'val_num': val_num,
852 'val_alpha': val_alpha,
853 'unit': unit
854 }
855 try:
856 tres = cLabResult(aPK_obj=data)
857
858 _log.error('will not overwrite existing test result')
859 _log.debug(str(tres))
860 return (None, tres)
861 except gmExceptions.NoSuchClinItemError:
862 _log.debug('test result not found - as expected, will create it')
863 except gmExceptions.ConstructorError, msg:
864 _log.exception(str(msg), sys.exc_info(), verbose=0)
865 return (False, msg)
866 if request is None:
867 return (False, _('need lab request when inserting lab result'))
868
869 if encounter_id is None:
870 encounter_id = request['pk_encounter']
871 queries = []
872 cmd = "insert into test_result (fk_encounter, fk_episode, fk_type, val_num, val_alpha, val_unit) values (%s, %s, %s, %s, %s, %s)"
873 queries.append((cmd, [encounter_id, request['pk_episode'], test_type, val_num, val_alpha, unit]))
874 cmd = "insert into lnk_result2lab_req (fk_result, fk_request) values ((select currval('test_result_pk_seq')), %s)"
875 queries.append((cmd, [request['pk_request']]))
876 cmd = "select currval('test_result_pk_seq')"
877 queries.append((cmd, []))
878
879 result, err = gmPG.run_commit('historica', queries, True)
880 if result is None:
881 return (False, err)
882 try:
883 tres = cLabResult(aPK_obj=result[0][0])
884 except gmExceptions.ConstructorError, msg:
885 _log.exception(str(msg), sys.exc_info(), verbose=0)
886 return (False, msg)
887 return (True, tres)
888
890
891 if limit < 1:
892 limit = 1
893
894 lim = limit + 1
895 cmd = """
896 select pk_result
897 from v_results4lab_req
898 where reviewed is false
899 order by pk_patient
900 limit %s""" % lim
901 rows = gmPG.run_ro_query('historica', cmd)
902 if rows is None:
903 _log.error('error retrieving unreviewed lab results')
904 return (None, _('error retrieving unreviewed lab results'))
905 if len(rows) == 0:
906 return (False, [])
907
908 if len(rows) == lim:
909 more_avail = True
910
911 del rows[limit]
912 else:
913 more_avail = False
914 results = []
915 for row in rows:
916 try:
917 results.append(cLabResult(aPK_obj=row[0]))
918 except gmExceptions.ConstructorError:
919 _log.exception('skipping unreviewed lab result [%s]' % row[0], sys.exc_info(), verbose=0)
920 return (more_avail, results)
921
923 lim = limit + 1
924 cmd = "select pk from lab_request where is_pending is true limit %s" % lim
925 rows = gmPG.run_ro_query('historica', cmd)
926 if rows is None:
927 _log.error('error retrieving pending lab requests')
928 return (None, None)
929 if len(rows) == 0:
930 return (False, [])
931 results = []
932
933 if len(rows) == lim:
934 too_many = True
935
936 del rows[limit]
937 else:
938 too_many = False
939 requests = []
940 for row in rows:
941 try:
942 requests.append(cLabRequest(aPK_obj=row[0]))
943 except gmExceptions.ConstructorError:
944 _log.exception('skipping pending lab request [%s]' % row[0], sys.exc_info(), verbose=0)
945 return (too_many, requests)
946
948 """Get logically next request ID for given lab.
949
950 - lab either test_org PK or test_org.internal_name
951 - incrementor_func:
952 - if not supplied the next ID is guessed
953 - if supplied it is applied to the most recently used ID
954 """
955 if type(lab) == types.IntType:
956 lab_snippet = 'vlr.fk_test_org=%s'
957 else:
958 lab_snippet = 'vlr.lab_name=%s'
959 lab = str(lab)
960 cmd = """
961 select request_id
962 from lab_request lr0
963 where lr0.clin_when = (
964 select max(vlr.sampled_when)
965 from v_lab_requests vlr
966 where %s
967 )""" % lab_snippet
968 rows = gmPG.run_ro_query('historica', cmd, None, lab)
969 if rows is None:
970 _log.warning('error getting most recently used request ID for lab [%s]' % lab)
971 return ''
972 if len(rows) == 0:
973 return ''
974 most_recent = rows[0][0]
975
976 if incrementor_func is not None:
977 try:
978 next = incrementor_func(most_recent)
979 except TypeError:
980 _log.error('cannot call incrementor function [%s]' % str(incrementor_func))
981 return most_recent
982 return next
983
984 for pos in range(len(most_recent)):
985 header = most_recent[:pos]
986 trailer = most_recent[pos:]
987 try:
988 return '%s%s' % (header, str(int(trailer) + 1))
989 except ValueError:
990 header = most_recent[:-1]
991 trailer = most_recent[-1:]
992 return '%s%s' % (header, chr(ord(trailer) + 1))
993
994
995
996 if __name__ == '__main__':
997 import time
998
999
1001 tr = create_test_result (
1002 encounter = 1,
1003 episode = 1,
1004 type = 1,
1005 intended_reviewer = 1,
1006 val_num = '12',
1007 val_alpha=None,
1008 unit = 'mg/dl'
1009 )
1010 print tr
1011 return tr
1012
1016
1021
1023 print "test_result()"
1024
1025 data = {
1026 'patient_id': 12,
1027 'when_field': 'val_when',
1028 'when': '2000-09-17 18:23:00+02',
1029 'test_type': 9,
1030 'val_num': 17.3,
1031 'val_alpha': None,
1032 'unit': 'mg/l'
1033 }
1034 lab_result = cLabResult(aPK_obj=data)
1035 print lab_result
1036 fields = lab_result.get_fields()
1037 for field in fields:
1038 print field, ':', lab_result[field]
1039 print "updatable:", lab_result.get_updatable_fields()
1040 print time.time()
1041 print lab_result.get_patient()
1042 print time.time()
1043
1045 print "test_request()"
1046 try:
1047
1048
1049 data = {
1050 'req_id': 'EML#SC937-0176-CEC#11',
1051 'lab': 'Enterprise Main Lab'
1052 }
1053 lab_req = cLabRequest(aPK_obj=data)
1054 except gmExceptions.ConstructorError, msg:
1055 print "no such lab request:", msg
1056 return
1057 print lab_req
1058 fields = lab_req.get_fields()
1059 for field in fields:
1060 print field, ':', lab_req[field]
1061 print "updatable:", lab_req.get_updatable_fields()
1062 print time.time()
1063 print lab_req.get_patient()
1064 print time.time()
1065
1070
1075
1083
1088
1093
1094 if (len(sys.argv) > 1) and (sys.argv[1] == 'test'):
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106 test_test_type()
1107
1108
1109
1110
1111
1112
1113
1114
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