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