1 """GNUmed measurements related business objects."""
2
3
4
5 __author__ = "K.Hilbert <Karsten.Hilbert@gmx.net>"
6 __license__ = "GPL"
7
8
9 import types
10 import sys
11 import logging
12 import codecs
13 import decimal
14
15
16 if __name__ == '__main__':
17 sys.path.insert(0, '../../')
18
19 from Gnumed.pycommon import gmDateTime
20 if __name__ == '__main__':
21 from Gnumed.pycommon import gmLog2
22 from Gnumed.pycommon import gmI18N
23 gmDateTime.init()
24 from Gnumed.pycommon import gmExceptions
25 from Gnumed.pycommon import gmBusinessDBObject
26 from Gnumed.pycommon import gmPG2
27 from Gnumed.pycommon import gmTools
28 from Gnumed.pycommon import gmDispatcher
29 from Gnumed.pycommon import gmHooks
30 from Gnumed.business import gmOrganization
31 from Gnumed.business import gmCoding
32
33
34 _log = logging.getLogger('gm.lab')
35
36
40
41 gmDispatcher.connect(_on_test_result_modified, u'test_result_mod_db')
42
43
44 -class cTestOrg(gmBusinessDBObject.cBusinessDBObject):
45 """Represents one test org/lab."""
46 _cmd_fetch_payload = u"""SELECT * FROM clin.v_test_orgs WHERE pk_test_org = %s"""
47 _cmds_store_payload = [
48 u"""UPDATE clin.test_org SET
49 fk_org_unit = %(pk_org_unit)s,
50 contact = gm.nullify_empty_string(%(test_org_contact)s),
51 comment = gm.nullify_empty_string(%(comment)s)
52 WHERE
53 pk = %(pk_test_org)s
54 AND
55 xmin = %(xmin_test_org)s
56 RETURNING
57 xmin AS xmin_test_org
58 """
59 ]
60 _updatable_fields = [
61 u'pk_org_unit',
62 u'test_org_contact',
63 u'comment'
64 ]
65
67
68 if name is None:
69 name = _('inhouse lab')
70 comment = _('auto-generated')
71
72
73 if pk_org_unit is None:
74 org = gmOrganization.org_exists(organization = name)
75 if org is None:
76 org = gmOrganization.create_org (
77 organization = name,
78 category = u'Laboratory'
79 )
80 org_unit = gmOrganization.create_org_unit (
81 pk_organization = org['pk_org'],
82 unit = name
83 )
84 pk_org_unit = org_unit['pk_org_unit']
85
86
87 args = {'pk_unit': pk_org_unit}
88 cmd = u'SELECT pk_test_org FROM clin.v_test_orgs WHERE pk_org_unit = %(pk_unit)s'
89 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
90
91 if len(rows) == 0:
92 cmd = u'INSERT INTO clin.test_org (fk_org_unit) VALUES (%(pk_unit)s) RETURNING pk'
93 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True)
94
95 test_org = cTestOrg(aPK_obj = rows[0][0])
96 if comment is not None:
97 comment = comment.strip()
98 test_org['comment'] = comment
99 test_org.save()
100
101 return test_org
102
104 args = {'pk': test_org}
105 cmd = u"""
106 DELETE FROM clin.test_org
107 WHERE
108 pk = %(pk)s
109 AND
110 NOT EXISTS (SELECT 1 FROM clin.lab_request WHERE fk_test_org = %(pk)s LIMIT 1)
111 AND
112 NOT EXISTS (SELECT 1 FROM clin.test_type WHERE fk_test_org = %(pk)s LIMIT 1)
113 """
114 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
115
117 cmd = u'SELECT * FROM clin.v_test_orgs ORDER BY %s' % order_by
118 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = True)
119 return [ cTestOrg(row = {'pk_field': 'pk_test_org', 'data': r, 'idx': idx}) for r in rows ]
120
121
122
123
124 _SQL_get_test_panels = u"SELECT * FROM clin.v_test_panels WHERE %s"
125
126 -class cTestPanel(gmBusinessDBObject.cBusinessDBObject):
127 """Represents a grouping/listing of tests into a panel."""
128
129 _cmd_fetch_payload = _SQL_get_test_panels % u"pk_test_panel = %s"
130 _cmds_store_payload = [
131 u"""
132 UPDATE clin.test_panel SET
133 description = gm.nullify_empty_string(%(description)s),
134 comment = gm.nullify_empty_string(%(comment)s),
135 fk_test_types = %(pk_test_types)s
136 WHERE
137 pk = %(pk_test_panel)s
138 AND
139 xmin = %(xmin_test_panel)s
140 RETURNING
141 xmin AS xmin_test_panel
142 """
143 ]
144 _updatable_fields = [
145 u'description',
146 u'comment',
147 u'pk_test_types'
148 ]
149
151 """<pk_code> must be a value from ref.coding_system_root.pk_coding_system (clin.lnk_code2item_root.fk_generic_code)"""
152 cmd = u"INSERT INTO clin.lnk_code2tst_pnl (fk_item, fk_generic_code) values (%(tp)s, %(code)s)"
153 args = {
154 'tp': self._payload[self._idx['pk_test_panel']],
155 'code': pk_code
156 }
157 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
158 return True
159
161 """<pk_code> must be a value from ref.coding_system_root.pk_coding_system (clin.lnk_code2item_root.fk_generic_code)"""
162 cmd = u"DELETE FROM clin.lnk_code2tst_pnl WHERE fk_item = %(tp)s AND fk_generic_code = %(code)s"
163 args = {
164 'tp': self._payload[self._idx['pk_test_panel']],
165 'code': pk_code
166 }
167 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
168 return True
169
170
171
173 if self._payload[self._idx['pk_test_types']] is None:
174 return None
175
176 rows, idx = gmPG2.run_ro_queries (
177 queries = [{
178 'cmd': _SQL_get_test_types % u'pk_test_type IN %(pks)s ORDER BY unified_abbrev',
179 'args': {'pks': tuple(self._payload[self._idx['pk_test_types']])}
180 }],
181 get_col_idx = True
182 )
183 return [ cMeasurementType(row = {'data': r, 'idx': idx, 'pk_field': 'pk_test_type'}) for r in rows ]
184
185 test_types = property(_get_test_types, lambda x:x)
186
188 if len(self._payload[self._idx['pk_generic_codes']]) == 0:
189 return []
190
191 cmd = gmCoding._SQL_get_generic_linked_codes % u'pk_generic_code IN %(pks)s'
192 args = {'pks': tuple(self._payload[self._idx['pk_generic_codes']])}
193 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
194 return [ gmCoding.cGenericLinkedCode(row = {'data': r, 'idx': idx, 'pk_field': 'pk_lnk_code2item'}) for r in rows ]
195
197 queries = []
198
199 if len(self._payload[self._idx['pk_generic_codes']]) > 0:
200 queries.append ({
201 'cmd': u'DELETE FROM clin.lnk_code2tst_pnl WHERE fk_item = %(tp)s AND fk_generic_code IN %(codes)s',
202 'args': {
203 'tp': self._payload[self._idx['pk_test_panel']],
204 'codes': tuple(self._payload[self._idx['pk_generic_codes']])
205 }
206 })
207
208 for pk_code in pk_codes:
209 queries.append ({
210 'cmd': u'INSERT INTO clin.lnk_code2test_panel (fk_item, fk_generic_code) VALUES (%(tp)s, %(pk_code)s)',
211 'args': {
212 'tp': self._payload[self._idx['pk_test_panel']],
213 'pk_code': pk_code
214 }
215 })
216 if len(queries) == 0:
217 return
218
219 rows, idx = gmPG2.run_rw_queries(queries = queries)
220 return
221
222 generic_codes = property(_get_generic_codes, _set_generic_codes)
223
263
265 if order_by is None:
266 order_by = u'true'
267 else:
268 order_by = u'true ORDER BY %s' % order_by
269
270 cmd = _SQL_get_test_panels % order_by
271 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = True)
272 return [ cTestPanel(row = {'data': r, 'idx': idx, 'pk_field': 'pk_test_panel'}) for r in rows ]
273
275
276 args = {u'desc': description.strip()}
277 cmd = u"""
278 INSERT INTO clin.test_panel (description)
279 VALUES (gm.nullify_empty_string(%(desc)s))
280 RETURNING pk
281 """
282 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True, get_col_idx = False)
283
284 return cTestPanel(aPK_obj = rows[0]['pk'])
285
287 args = {'pk': pk}
288 cmd = u"DELETE FROM clin.test_panel WHERE pk = %(pk)s"
289 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
290 return True
291
292
301
306
311
312
313 _SQL_get_test_types = u"SELECT * FROM clin.v_test_types WHERE %s"
314
316 """Represents one test result type."""
317
318 _cmd_fetch_payload = _SQL_get_test_types % u"pk_test_type = %s"
319
320 _cmds_store_payload = [
321 u"""UPDATE clin.test_type SET
322 abbrev = gm.nullify_empty_string(%(abbrev)s),
323 name = gm.nullify_empty_string(%(name)s),
324 loinc = gm.nullify_empty_string(%(loinc)s),
325 comment = gm.nullify_empty_string(%(comment_type)s),
326 conversion_unit = gm.nullify_empty_string(%(conversion_unit)s),
327 fk_test_org = %(pk_test_org)s,
328 fk_meta_test_type = %(pk_meta_test_type)s
329 WHERE
330 pk = %(pk_test_type)s
331 AND
332 xmin = %(xmin_test_type)s
333 RETURNING
334 xmin AS xmin_test_type"""
335 ]
336
337 _updatable_fields = [
338 'abbrev',
339 'name',
340 'loinc',
341 'comment_type',
342 'conversion_unit',
343 'pk_test_org',
344 'pk_meta_test_type'
345 ]
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
363 cmd = u'SELECT EXISTS(SELECT 1 FROM clin.test_result WHERE fk_type = %(pk_type)s)'
364 args = {'pk_type': self._payload[self._idx['pk_test_type']]}
365 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
366 return rows[0][0]
367
368 in_use = property(_get_in_use, lambda x:x)
369
386
388 if self._payload[self._idx['pk_test_panels']] is None:
389 return None
390
391 return [ cTestPanel(aPK_obj = pk) for pk in self._payload[self._idx['pk_test_panels']] ]
392
393 test_panels = property(_get_test_panels, lambda x:x)
394
447
448
450 cmd = u'select * from clin.v_test_types %s' % gmTools.coalesce(order_by, u'', u'order by %s')
451 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = True)
452 return [ cMeasurementType(row = {'pk_field': 'pk_test_type', 'data': r, 'idx': idx}) for r in rows ]
453
455
456 if (abbrev is None) and (name is None):
457 raise ValueError('must have <abbrev> and/or <name> set')
458
459 where_snippets = []
460
461 if lab is None:
462 where_snippets.append('pk_test_org IS NULL')
463 else:
464 try:
465 int(lab)
466 where_snippets.append('pk_test_org = %(lab)s')
467 except (TypeError, ValueError):
468 where_snippets.append('pk_test_org = (SELECT pk_test_org FROM clin.v_test_orgs WHERE unit = %(lab)s)')
469
470 if abbrev is not None:
471 where_snippets.append('abbrev = %(abbrev)s')
472
473 if name is not None:
474 where_snippets.append('name = %(name)s')
475
476 where_clause = u' and '.join(where_snippets)
477 cmd = u"select * from clin.v_test_types where %s" % where_clause
478 args = {'lab': lab, 'abbrev': abbrev, 'name': name}
479
480 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
481
482 if len(rows) == 0:
483 return None
484
485 tt = cMeasurementType(row = {'pk_field': 'pk_test_type', 'data': rows[0], 'idx': idx})
486 return tt
487
489 cmd = u'delete from clin.test_type where pk = %(pk)s'
490 args = {'pk': measurement_type}
491 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
492
494 """Create or get test type."""
495
496 ttype = find_measurement_type(lab = lab, abbrev = abbrev, name = name)
497
498 if ttype is not None:
499 return ttype
500
501 _log.debug('creating test type [%s:%s:%s:%s]', lab, abbrev, name, unit)
502
503
504 if unit is None:
505 _log.error('need <unit> to create test type: %s:%s:%s:%s' % (lab, abbrev, name, unit))
506 raise ValueError('need <unit> to create test type')
507
508
509 cols = []
510 val_snippets = []
511 vals = {}
512
513
514 if lab is None:
515 lab = create_test_org()['pk_test_org']
516
517 cols.append('fk_test_org')
518 try:
519 vals['lab'] = int(lab)
520 val_snippets.append('%(lab)s')
521 except:
522 vals['lab'] = lab
523 val_snippets.append('(SELECT pk_test_org FROM clin.v_test_orgs WHERE unit = %(lab)s)')
524
525
526 cols.append('abbrev')
527 val_snippets.append('%(abbrev)s')
528 vals['abbrev'] = abbrev
529
530
531 cols.append('conversion_unit')
532 val_snippets.append('%(unit)s')
533 vals['unit'] = unit
534
535
536 if name is not None:
537 cols.append('name')
538 val_snippets.append('%(name)s')
539 vals['name'] = name
540
541 col_clause = u', '.join(cols)
542 val_clause = u', '.join(val_snippets)
543 queries = [
544 {'cmd': u'insert into clin.test_type (%s) values (%s)' % (col_clause, val_clause), 'args': vals},
545 {'cmd': u"select * from clin.v_test_types where pk_test_type = currval(pg_get_serial_sequence('clin.test_type', 'pk'))"}
546 ]
547 rows, idx = gmPG2.run_rw_queries(queries = queries, get_col_idx = True, return_data = True)
548 ttype = cMeasurementType(row = {'pk_field': 'pk_test_type', 'data': rows[0], 'idx': idx})
549
550 return ttype
551
552
553 -class cTestResult(gmBusinessDBObject.cBusinessDBObject):
554 """Represents one test result."""
555
556 _cmd_fetch_payload = u"select * from clin.v_test_results where pk_test_result = %s"
557
558 _cmds_store_payload = [
559 u"""update clin.test_result set
560 clin_when = %(clin_when)s,
561 narrative = nullif(trim(%(comment)s), ''),
562 val_num = %(val_num)s,
563 val_alpha = nullif(trim(%(val_alpha)s), ''),
564 val_unit = nullif(trim(%(val_unit)s), ''),
565 val_normal_min = %(val_normal_min)s,
566 val_normal_max = %(val_normal_max)s,
567 val_normal_range = nullif(trim(%(val_normal_range)s), ''),
568 val_target_min = %(val_target_min)s,
569 val_target_max = %(val_target_max)s,
570 val_target_range = nullif(trim(%(val_target_range)s), ''),
571 abnormality_indicator = nullif(trim(%(abnormality_indicator)s), ''),
572 norm_ref_group = nullif(trim(%(norm_ref_group)s), ''),
573 note_test_org = nullif(trim(%(note_test_org)s), ''),
574 material = nullif(trim(%(material)s), ''),
575 material_detail = nullif(trim(%(material_detail)s), ''),
576 fk_intended_reviewer = %(pk_intended_reviewer)s,
577 fk_encounter = %(pk_encounter)s,
578 fk_episode = %(pk_episode)s,
579 fk_type = %(pk_test_type)s,
580 fk_request = %(pk_request)s
581 where
582 pk = %(pk_test_result)s and
583 xmin = %(xmin_test_result)s""",
584 u"""select xmin_test_result from clin.v_test_results where pk_test_result = %(pk_test_result)s"""
585 ]
586
587 _updatable_fields = [
588 'clin_when',
589 'comment',
590 'val_num',
591 'val_alpha',
592 'val_unit',
593 'val_normal_min',
594 'val_normal_max',
595 'val_normal_range',
596 'val_target_min',
597 'val_target_max',
598 'val_target_range',
599 'abnormality_indicator',
600 'norm_ref_group',
601 'note_test_org',
602 'material',
603 'material_detail',
604 'pk_intended_reviewer',
605 'pk_encounter',
606 'pk_episode',
607 'pk_test_type',
608 'pk_request'
609 ]
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
914
916
917 cmd = u"""
918 select
919 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)
920 pk_patient,
921 val_unit,
922 val_normal_min, val_normal_max, val_normal_range,
923 val_target_min, val_target_max, val_target_range,
924 norm_ref_group,
925 coalesce(norm_ref_group, '') as norm_ref_group_str
926 from
927 clin.v_test_results
928 where
929 pk_test_type = %(pk_type)s
930 """
931 args = {'pk_type': self._payload[self._idx['pk_test_type']]}
932 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
933 return rows
934
936 raise AttributeError('[%s]: reference ranges not settable') % self.__class__.__name__
937
938 reference_ranges = property(_get_reference_ranges, _set_reference_ranges)
939
942
943 test_type = property(_get_test_type, lambda x:x)
944
945 - def set_review(self, technically_abnormal=None, clinically_relevant=None, comment=None, make_me_responsible=False):
946
947
948 if self._payload[self._idx['reviewed']]:
949 self.__change_existing_review (
950 technically_abnormal = technically_abnormal,
951 clinically_relevant = clinically_relevant,
952 comment = comment
953 )
954 else:
955
956
957 if technically_abnormal is None:
958 if clinically_relevant is None:
959 comment = gmTools.none_if(comment, u'', strip_string = True)
960 if comment is None:
961 if make_me_responsible is False:
962 return True
963 self.__set_new_review (
964 technically_abnormal = technically_abnormal,
965 clinically_relevant = clinically_relevant,
966 comment = comment
967 )
968
969 if make_me_responsible is True:
970 cmd = u"SELECT pk FROM dem.staff WHERE db_user = current_user"
971 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}])
972 self['pk_intended_reviewer'] = rows[0][0]
973 self.save_payload()
974 return
975
976 self.refetch_payload()
977
978 - def get_adjacent_results(self, desired_earlier_results=1, desired_later_results=1, max_offset=None):
979
980 if desired_earlier_results < 1:
981 raise ValueError('<desired_earlier_results> must be > 0')
982
983 if desired_later_results < 1:
984 raise ValueError('<desired_later_results> must be > 0')
985
986 args = {
987 'pat': self._payload[self._idx['pk_patient']],
988 'ttyp': self._payload[self._idx['pk_test_type']],
989 'tloinc': self._payload[self._idx['loinc_tt']],
990 'mtyp': self._payload[self._idx['pk_meta_test_type']],
991 'mloinc': self._payload[self._idx['loinc_meta']],
992 'when': self._payload[self._idx['clin_when']],
993 'offset': max_offset
994 }
995 WHERE = u'((pk_test_type = %(ttyp)s) OR (loinc_tt = %(tloinc)s))'
996 WHERE_meta = u'((pk_meta_test_type = %(mtyp)s) OR (loinc_meta = %(mloinc)s))'
997 if max_offset is not None:
998 WHERE = WHERE + u' AND (clin_when BETWEEN (%(when)s - %(offset)s) AND (%(when)s + %(offset)s))'
999 WHERE_meta = WHERE_meta + u' AND (clin_when BETWEEN (%(when)s - %(offset)s) AND (%(when)s + %(offset)s))'
1000
1001 SQL = u"""
1002 SELECT * FROM clin.v_test_results
1003 WHERE
1004 pk_patient = %%(pat)s
1005 AND
1006 clin_when %s %%(when)s
1007 AND
1008 %s
1009 ORDER BY clin_when
1010 LIMIT %s"""
1011
1012
1013 earlier_results = []
1014
1015 cmd = SQL % (u'<', WHERE, desired_earlier_results)
1016 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
1017 if len(rows) > 0:
1018 earlier_results.extend([ cTestResult(row = {'pk_field': 'pk_test_result', 'idx': idx, 'data': r}) for r in rows ])
1019
1020 missing_results = desired_earlier_results - len(earlier_results)
1021 if missing_results > 0:
1022 cmd = SQL % (u'<', WHERE_meta, missing_results)
1023 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
1024 if len(rows) > 0:
1025 earlier_results.extend([ cTestResult(row = {'pk_field': 'pk_test_result', 'idx': idx, 'data': r}) for r in rows ])
1026
1027
1028 later_results = []
1029
1030 cmd = SQL % (u'>', WHERE, desired_later_results)
1031 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
1032 if len(rows) > 0:
1033 later_results.extend([ cTestResult(row = {'pk_field': 'pk_test_result', 'idx': idx, 'data': r}) for r in rows ])
1034
1035 missing_results = desired_later_results - len(later_results)
1036 if missing_results > 0:
1037 cmd = SQL % (u'>', WHERE_meta, missing_results)
1038 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
1039 if len(rows) > 0:
1040 later_results.extend([ cTestResult(row = {'pk_field': 'pk_test_result', 'idx': idx, 'data': r}) for r in rows ])
1041
1042 return earlier_results, later_results
1043
1044
1045
1046 - def __set_new_review(self, technically_abnormal=None, clinically_relevant=None, comment=None):
1047 """Add a review to a row.
1048
1049 - if technically abnormal is not provided/None it will be set
1050 to True if the lab's indicator has a meaningful value
1051 - if clinically relevant is not provided/None it is set to
1052 whatever technically abnormal is
1053 """
1054 if technically_abnormal is None:
1055 technically_abnormal = False
1056 if self._payload[self._idx['abnormality_indicator']] is not None:
1057 if self._payload[self._idx['abnormality_indicator']].strip() != u'':
1058 technically_abnormal = True
1059
1060 if clinically_relevant is None:
1061 clinically_relevant = technically_abnormal
1062
1063 cmd = u"""
1064 INSERT INTO clin.reviewed_test_results (
1065 fk_reviewed_row,
1066 is_technically_abnormal,
1067 clinically_relevant,
1068 comment
1069 ) VALUES (
1070 %(pk)s,
1071 %(abnormal)s,
1072 %(relevant)s,
1073 gm.nullify_empty_string(%(cmt)s)
1074 )"""
1075 args = {
1076 'pk': self._payload[self._idx['pk_test_result']],
1077 'abnormal': technically_abnormal,
1078 'relevant': clinically_relevant,
1079 'cmt': comment
1080 }
1081
1082 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
1083
1085 """Change a review on a row.
1086
1087 - if technically abnormal/clinically relevant are
1088 None they are not set
1089 """
1090 args = {
1091 'pk_row': self._payload[self._idx['pk_test_result']],
1092 'abnormal': technically_abnormal,
1093 'relevant': clinically_relevant,
1094 'cmt': comment
1095 }
1096
1097 set_parts = [
1098 u'fk_reviewer = (SELECT pk FROM dem.staff WHERE db_user = current_user)',
1099 u'comment = gm.nullify_empty_string(%(cmt)s)'
1100 ]
1101
1102 if technically_abnormal is not None:
1103 set_parts.append(u'is_technically_abnormal = %(abnormal)s')
1104
1105 if clinically_relevant is not None:
1106 set_parts.append(u'clinically_relevant = %(relevant)s')
1107
1108 cmd = u"""
1109 UPDATE clin.reviewed_test_results SET
1110 %s
1111 WHERE
1112 fk_reviewed_row = %%(pk_row)s
1113 """ % u',\n '.join(set_parts)
1114
1115 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
1116
1117
1118 -def get_test_results(pk_patient=None, encounters=None, episodes=None, order_by=None):
1119
1120 where_parts = []
1121
1122 if pk_patient is not None:
1123 where_parts.append(u'pk_patient = %(pat)s')
1124 args = {'pat': pk_patient}
1125
1126
1127
1128
1129
1130 if encounters is not None:
1131 where_parts.append(u'pk_encounter IN %(encs)s')
1132 args['encs'] = tuple(encounters)
1133
1134 if episodes is not None:
1135 where_parts.append(u'pk_episode IN %(epis)s')
1136 args['epis'] = tuple(episodes)
1137
1138 if order_by is None:
1139 order_by = u''
1140 else:
1141 order_by = u'ORDER BY %s' % order_by
1142
1143 cmd = u"""
1144 SELECT * FROM clin.v_test_results
1145 WHERE %s
1146 %s
1147 """ % (
1148 u' AND '.join(where_parts),
1149 order_by
1150 )
1151 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
1152
1153 tests = [ cTestResult(row = {'pk_field': 'pk_test_result', 'idx': idx, 'data': r}) for r in rows ]
1154 return tests
1155
1156
1158
1159 if None not in [test_type, loinc]:
1160 raise ValueError('either <test_type> or <loinc> must be None')
1161
1162 args = {
1163 'pat': patient,
1164 'ttyp': test_type,
1165 'loinc': loinc,
1166 'ts': timestamp,
1167 'intv': tolerance_interval
1168 }
1169
1170 where_parts = [u'pk_patient = %(pat)s']
1171 if test_type is not None:
1172 where_parts.append(u'pk_test_type = %(ttyp)s')
1173 elif loinc is not None:
1174 where_parts.append(u'((loinc_tt IN %(loinc)s) OR (loinc_meta IN %(loinc)s))')
1175 args['loinc'] = tuple(loinc)
1176
1177 if tolerance_interval is None:
1178 where_parts.append(u'clin_when = %(ts)s')
1179 else:
1180 where_parts.append(u'clin_when between (%(ts)s - %(intv)s::interval) AND (%(ts)s + %(intv)s::interval)')
1181
1182 cmd = u"""
1183 SELECT * FROM clin.v_test_results
1184 WHERE
1185 %s
1186 ORDER BY
1187 abs(extract(epoch from age(clin_when, %%(ts)s)))
1188 LIMIT 1""" % u' AND '.join(where_parts)
1189
1190 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
1191 if len(rows) == 0:
1192 return None
1193
1194 return cTestResult(row = {'pk_field': 'pk_test_result', 'idx': idx, 'data': rows[0]})
1195
1196
1198
1199 if None not in [test_type, loinc]:
1200 raise ValueError('either <test_type> or <loinc> must be None')
1201
1202 if no_of_results < 1:
1203 raise ValueError('<no_of_results> must be > 0')
1204
1205 args = {
1206 'pat': patient,
1207 'ttyp': test_type,
1208 'loinc': loinc
1209 }
1210
1211 where_parts = [u'pk_patient = %(pat)s']
1212 if test_type is not None:
1213 where_parts.append(u'pk_test_type = %(ttyp)s')
1214 elif loinc is not None:
1215 where_parts.append(u'((loinc_tt IN %(loinc)s) OR (loinc_meta IN %(loinc)s))')
1216 args['loinc'] = tuple(loinc)
1217
1218 cmd = u"""
1219 SELECT * FROM clin.v_test_results
1220 WHERE
1221 %s
1222 ORDER BY clin_when DESC
1223 LIMIT %s""" % (
1224 u' AND '.join(where_parts),
1225 no_of_results
1226 )
1227 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
1228 if len(rows) == 0:
1229 return None
1230
1231 if no_of_results == 1:
1232 return cTestResult(row = {'pk_field': 'pk_test_result', 'idx': idx, 'data': rows[0]})
1233
1234 return [ cTestResult(row = {'pk_field': 'pk_test_result', 'idx': idx, 'data': r}) for r in rows ]
1235
1236
1238 try:
1239 pk = int(result)
1240 except (TypeError, AttributeError):
1241 pk = result['pk_test_result']
1242
1243 cmd = u'DELETE FROM clin.test_result WHERE pk = %(pk)s'
1244 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': {'pk': pk}}])
1245
1246
1247 -def create_test_result(encounter=None, episode=None, type=None, intended_reviewer=None, val_num=None, val_alpha=None, unit=None):
1248
1249 cmd1 = u"""
1250 insert into clin.test_result (
1251 fk_encounter,
1252 fk_episode,
1253 fk_type,
1254 fk_intended_reviewer,
1255 val_num,
1256 val_alpha,
1257 val_unit
1258 ) values (
1259 %(enc)s,
1260 %(epi)s,
1261 %(type)s,
1262 %(rev)s,
1263 %(v_num)s,
1264 %(v_alpha)s,
1265 %(unit)s
1266 )"""
1267
1268 cmd2 = u"""
1269 select *
1270 from
1271 clin.v_test_results
1272 where
1273 pk_test_result = currval(pg_get_serial_sequence('clin.test_result', 'pk'))"""
1274
1275 args = {
1276 u'enc': encounter,
1277 u'epi': episode,
1278 u'type': type,
1279 u'rev': intended_reviewer,
1280 u'v_num': val_num,
1281 u'v_alpha': val_alpha,
1282 u'unit': unit
1283 }
1284
1285 rows, idx = gmPG2.run_rw_queries (
1286 queries = [
1287 {'cmd': cmd1, 'args': args},
1288 {'cmd': cmd2}
1289 ],
1290 return_data = True,
1291 get_col_idx = True
1292 )
1293
1294 tr = cTestResult(row = {
1295 'pk_field': 'pk_test_result',
1296 'idx': idx,
1297 'data': rows[0]
1298 })
1299
1300 return tr
1301
1302
1313
1314
1315 -def __tests2latex_minipage(results=None, width=u'1.5cm', show_time=False, show_range=True):
1316
1317 if len(results) == 0:
1318 return u'\\begin{minipage}{%s} \\end{minipage}' % width
1319
1320 lines = []
1321 for t in results:
1322
1323 tmp = u''
1324
1325 if show_time:
1326 tmp += u'{\\tiny (%s)} ' % t['clin_when'].strftime('%H:%M')
1327
1328 tmp += u'%.8s' % t['unified_val']
1329
1330 lines.append(tmp)
1331 tmp = u''
1332
1333 if show_range:
1334 has_range = (
1335 t['unified_target_range'] is not None
1336 or
1337 t['unified_target_min'] is not None
1338 or
1339 t['unified_target_max'] is not None
1340 )
1341 if has_range:
1342 if t['unified_target_range'] is not None:
1343 tmp += u'{\\tiny %s}' % t['unified_target_range']
1344 else:
1345 tmp += u'{\\tiny %s}' % (
1346 gmTools.coalesce(t['unified_target_min'], u'- ', u'%s - '),
1347 gmTools.coalesce(t['unified_target_max'], u'', u'%s')
1348 )
1349 lines.append(tmp)
1350
1351 return u'\\begin{minipage}{%s} \\begin{flushright} %s \\end{flushright} \\end{minipage}' % (width, u' \\\\ '.join(lines))
1352
1353
1355
1356 if len(results) == 0:
1357 return u''
1358
1359 lines = []
1360 for t in results:
1361
1362 tmp = u''
1363
1364 if show_time:
1365 tmp += u'\\tiny %s ' % t['clin_when'].strftime('%H:%M')
1366
1367 tmp += u'\\normalsize %.8s' % t['unified_val']
1368
1369 lines.append(tmp)
1370 tmp = u'\\tiny %s' % gmTools.coalesce(t['val_unit'], u'', u'%s ')
1371
1372 if not show_range:
1373 lines.append(tmp)
1374 continue
1375
1376 has_range = (
1377 t['unified_target_range'] is not None
1378 or
1379 t['unified_target_min'] is not None
1380 or
1381 t['unified_target_max'] is not None
1382 )
1383
1384 if not has_range:
1385 lines.append(tmp)
1386 continue
1387
1388 if t['unified_target_range'] is not None:
1389 tmp += u'[%s]' % t['unified_target_range']
1390 else:
1391 tmp += u'[%s%s]' % (
1392 gmTools.coalesce(t['unified_target_min'], u'--', u'%s--'),
1393 gmTools.coalesce(t['unified_target_max'], u'', u'%s')
1394 )
1395 lines.append(tmp)
1396
1397 return u' \\\\ '.join(lines)
1398
1399
1475
1476
1478
1479 if filename is None:
1480 filename = gmTools.get_unique_filename(prefix = u'gm2gpl-', suffix = '.dat')
1481
1482
1483 series = {}
1484 for r in results:
1485 try:
1486 series[r['unified_name']].append(r)
1487 except KeyError:
1488 series[r['unified_name']] = [r]
1489
1490 gp_data = codecs.open(filename, 'wb', 'utf8')
1491
1492 gp_data.write(u'# %s\n' % _('GNUmed test results export for Gnuplot plotting'))
1493 gp_data.write(u'# -------------------------------------------------------------\n')
1494 gp_data.write(u'# first line of index: test type abbreviation & name\n')
1495 gp_data.write(u'#\n')
1496 gp_data.write(u'# clin_when at full precision\n')
1497 gp_data.write(u'# value\n')
1498 gp_data.write(u'# unit\n')
1499 gp_data.write(u'# unified (target or normal) range: lower bound\n')
1500 gp_data.write(u'# unified (target or normal) range: upper bound\n')
1501 gp_data.write(u'# normal range: lower bound\n')
1502 gp_data.write(u'# normal range: upper bound\n')
1503 gp_data.write(u'# target range: lower bound\n')
1504 gp_data.write(u'# target range: upper bound\n')
1505 gp_data.write(u'# clin_when formatted into string as x-axis tic label\n')
1506 gp_data.write(u'# -------------------------------------------------------------\n')
1507
1508 for test_type in series.keys():
1509 if len(series[test_type]) == 0:
1510 continue
1511
1512 r = series[test_type][0]
1513 title = u'%s (%s)' % (
1514 r['unified_abbrev'],
1515 r['unified_name']
1516 )
1517 gp_data.write(u'\n\n"%s" "%s"\n' % (title, title))
1518
1519 prev_date = None
1520 prev_year = None
1521 for r in series[test_type]:
1522 curr_date = r['clin_when'].strftime('%Y-%m-%d')
1523 if curr_date == prev_date:
1524 gp_data.write(u'\n# %s\n' % _('blank line inserted to allow for discontinued line drawing for same-day values'))
1525 if show_year:
1526 if r['clin_when'].year == prev_year:
1527 when_template = '%b %d %H:%M'
1528 else:
1529 when_template = '%b %d %H:%M (%Y)'
1530 prev_year = r['clin_when'].year
1531 else:
1532 when_template = '%b %d'
1533 gp_data.write (u'%s %s "%s" %s %s %s %s %s %s "%s"\n' % (
1534 r['clin_when'].strftime('%Y-%m-%d_%H:%M'),
1535 r['unified_val'],
1536 gmTools.coalesce(r['val_unit'], u'"<?>"'),
1537 gmTools.coalesce(r['unified_target_min'], u'"<?>"'),
1538 gmTools.coalesce(r['unified_target_max'], u'"<?>"'),
1539 gmTools.coalesce(r['val_normal_min'], u'"<?>"'),
1540 gmTools.coalesce(r['val_normal_max'], u'"<?>"'),
1541 gmTools.coalesce(r['val_target_min'], u'"<?>"'),
1542 gmTools.coalesce(r['val_target_max'], u'"<?>"'),
1543 gmDateTime.pydt_strftime (
1544 r['clin_when'],
1545 format = when_template,
1546 accuracy = gmDateTime.acc_minutes
1547 )
1548 ))
1549 prev_date = curr_date
1550
1551 gp_data.close()
1552
1553 return filename
1554
1555
1556 -class cLabResult(gmBusinessDBObject.cBusinessDBObject):
1557 """Represents one lab result."""
1558
1559 _cmd_fetch_payload = """
1560 select *, xmin_test_result from v_results4lab_req
1561 where pk_result=%s"""
1562 _cmds_lock_rows_for_update = [
1563 """select 1 from test_result where pk=%(pk_result)s and xmin=%(xmin_test_result)s for update"""
1564 ]
1565 _cmds_store_payload = [
1566 """update test_result set
1567 clin_when = %(val_when)s,
1568 narrative = %(progress_note_result)s,
1569 fk_type = %(pk_test_type)s,
1570 val_num = %(val_num)s::numeric,
1571 val_alpha = %(val_alpha)s,
1572 val_unit = %(val_unit)s,
1573 val_normal_min = %(val_normal_min)s,
1574 val_normal_max = %(val_normal_max)s,
1575 val_normal_range = %(val_normal_range)s,
1576 val_target_min = %(val_target_min)s,
1577 val_target_max = %(val_target_max)s,
1578 val_target_range = %(val_target_range)s,
1579 abnormality_indicator = %(abnormal)s,
1580 norm_ref_group = %(ref_group)s,
1581 note_provider = %(note_provider)s,
1582 material = %(material)s,
1583 material_detail = %(material_detail)s
1584 where pk = %(pk_result)s""",
1585 """select xmin_test_result from v_results4lab_req where pk_result=%(pk_result)s"""
1586 ]
1587
1588 _updatable_fields = [
1589 'val_when',
1590 'progress_note_result',
1591 'val_num',
1592 'val_alpha',
1593 'val_unit',
1594 'val_normal_min',
1595 'val_normal_max',
1596 'val_normal_range',
1597 'val_target_min',
1598 'val_target_max',
1599 'val_target_range',
1600 'abnormal',
1601 'ref_group',
1602 'note_provider',
1603 'material',
1604 'material_detail'
1605 ]
1606
1607 - def __init__(self, aPK_obj=None, row=None):
1608 """Instantiate.
1609
1610 aPK_obj as dict:
1611 - patient_id
1612 - when_field (see view definition)
1613 - when
1614 - test_type
1615 - val_num
1616 - val_alpha
1617 - unit
1618 """
1619
1620 if aPK_obj is None:
1621 gmBusinessDBObject.cBusinessDBObject.__init__(self, row=row)
1622 return
1623 pk = aPK_obj
1624
1625 if type(aPK_obj) == types.DictType:
1626
1627 if None in [aPK_obj['patient_id'], aPK_obj['when'], aPK_obj['when_field'], aPK_obj['test_type'], aPK_obj['unit']]:
1628 raise gmExceptions.ConstructorError, 'parameter error: %s' % aPK_obj
1629 if (aPK_obj['val_num'] is None) and (aPK_obj['val_alpha'] is None):
1630 raise gmExceptions.ConstructorError, 'parameter error: val_num and val_alpha cannot both be None'
1631
1632 where_snippets = [
1633 'pk_patient=%(patient_id)s',
1634 'pk_test_type=%(test_type)s',
1635 '%s=%%(when)s' % aPK_obj['when_field'],
1636 'val_unit=%(unit)s'
1637 ]
1638 if aPK_obj['val_num'] is not None:
1639 where_snippets.append('val_num=%(val_num)s::numeric')
1640 if aPK_obj['val_alpha'] is not None:
1641 where_snippets.append('val_alpha=%(val_alpha)s')
1642
1643 where_clause = ' and '.join(where_snippets)
1644 cmd = "select pk_result from v_results4lab_req where %s" % where_clause
1645 data = gmPG.run_ro_query('historica', cmd, None, aPK_obj)
1646 if data is None:
1647 raise gmExceptions.ConstructorError, 'error getting lab result for: %s' % aPK_obj
1648 if len(data) == 0:
1649 raise gmExceptions.NoSuchClinItemError, 'no lab result for: %s' % aPK_obj
1650 pk = data[0][0]
1651
1652 gmBusinessDBObject.cBusinessDBObject.__init__(self, aPK_obj=pk)
1653
1655 cmd = """
1656 select
1657 %s,
1658 vbp.title,
1659 vbp.firstnames,
1660 vbp.lastnames,
1661 vbp.dob
1662 from v_basic_person vbp
1663 where vbp.pk_identity=%%s""" % self._payload[self._idx['pk_patient']]
1664 pat = gmPG.run_ro_query('historica', cmd, None, self._payload[self._idx['pk_patient']])
1665 return pat[0]
1666
1667 -class cLabRequest(gmBusinessDBObject.cBusinessDBObject):
1668 """Represents one lab request."""
1669
1670 _cmd_fetch_payload = """
1671 select *, xmin_lab_request from v_lab_requests
1672 where pk_request=%s"""
1673 _cmds_lock_rows_for_update = [
1674 """select 1 from lab_request where pk=%(pk_request)s and xmin=%(xmin_lab_request)s for update"""
1675 ]
1676 _cmds_store_payload = [
1677 """update lab_request set
1678 request_id=%(request_id)s,
1679 lab_request_id=%(lab_request_id)s,
1680 clin_when=%(sampled_when)s,
1681 lab_rxd_when=%(lab_rxd_when)s,
1682 results_reported_when=%(results_reported_when)s,
1683 request_status=%(request_status)s,
1684 is_pending=%(is_pending)s::bool,
1685 narrative=%(progress_note)s
1686 where pk=%(pk_request)s""",
1687 """select xmin_lab_request from v_lab_requests where pk_request=%(pk_request)s"""
1688 ]
1689 _updatable_fields = [
1690 'request_id',
1691 'lab_request_id',
1692 'sampled_when',
1693 'lab_rxd_when',
1694 'results_reported_when',
1695 'request_status',
1696 'is_pending',
1697 'progress_note'
1698 ]
1699
1700 - def __init__(self, aPK_obj=None, row=None):
1701 """Instantiate lab request.
1702
1703 The aPK_obj can be either a dict with the keys "req_id"
1704 and "lab" or a simple primary key.
1705 """
1706
1707 if aPK_obj is None:
1708 gmBusinessDBObject.cBusinessDBObject.__init__(self, row=row)
1709 return
1710 pk = aPK_obj
1711
1712 if type(aPK_obj) == types.DictType:
1713
1714 try:
1715 aPK_obj['req_id']
1716 aPK_obj['lab']
1717 except:
1718 _log.exception('[%s:??]: faulty <aPK_obj> structure: [%s]' % (self.__class__.__name__, aPK_obj), sys.exc_info())
1719 raise gmExceptions.ConstructorError, '[%s:??]: cannot derive PK from [%s]' % (self.__class__.__name__, aPK_obj)
1720
1721 where_snippets = []
1722 vals = {}
1723 where_snippets.append('request_id=%(req_id)s')
1724 if type(aPK_obj['lab']) == types.IntType:
1725 where_snippets.append('pk_test_org=%(lab)s')
1726 else:
1727 where_snippets.append('lab_name=%(lab)s')
1728 where_clause = ' and '.join(where_snippets)
1729 cmd = "select pk_request from v_lab_requests where %s" % where_clause
1730
1731 data = gmPG.run_ro_query('historica', cmd, None, aPK_obj)
1732 if data is None:
1733 raise gmExceptions.ConstructorError, '[%s:??]: error getting lab request for [%s]' % (self.__class__.__name__, aPK_obj)
1734 if len(data) == 0:
1735 raise gmExceptions.NoSuchClinItemError, '[%s:??]: no lab request for [%s]' % (self.__class__.__name__, aPK_obj)
1736 pk = data[0][0]
1737
1738 gmBusinessDBObject.cBusinessDBObject.__init__(self, aPK_obj=pk)
1739
1741 cmd = """
1742 select vpi.pk_patient, vbp.title, vbp.firstnames, vbp.lastnames, vbp.dob
1743 from v_pat_items vpi, v_basic_person vbp
1744 where
1745 vpi.pk_item=%s
1746 and
1747 vbp.pk_identity=vpi.pk_patient"""
1748 pat = gmPG.run_ro_query('historica', cmd, None, self._payload[self._idx['pk_item']])
1749 if pat is None:
1750 _log.error('cannot get patient for lab request [%s]' % self._payload[self._idx['pk_item']])
1751 return None
1752 if len(pat) == 0:
1753 _log.error('no patient associated with lab request [%s]' % self._payload[self._idx['pk_item']])
1754 return None
1755 return pat[0]
1756
1757
1758
1759 -def create_lab_request(lab=None, req_id=None, pat_id=None, encounter_id=None, episode_id=None):
1760 """Create or get lab request.
1761
1762 returns tuple (status, value):
1763 (True, lab request instance)
1764 (False, error message)
1765 (None, housekeeping_todo primary key)
1766 """
1767 req = None
1768 aPK_obj = {
1769 'lab': lab,
1770 'req_id': req_id
1771 }
1772 try:
1773 req = cLabRequest (aPK_obj)
1774 except gmExceptions.NoSuchClinItemError, msg:
1775 _log.info('%s: will try to create lab request' % str(msg))
1776 except gmExceptions.ConstructorError, msg:
1777 _log.exception(str(msg), sys.exc_info(), verbose=0)
1778 return (False, msg)
1779
1780 if req is not None:
1781 db_pat = req.get_patient()
1782 if db_pat is None:
1783 _log.error('cannot cross-check patient on lab request')
1784 return (None, '')
1785
1786 if pat_id != db_pat[0]:
1787 _log.error('lab request found for [%s:%s] but patient mismatch: expected [%s], in DB [%s]' % (lab, req_id, pat_id, db_pat))
1788 me = '$RCSfile: gmPathLab.py,v $ $Revision: 1.81 $'
1789 to = 'user'
1790 prob = _('The lab request already exists but belongs to a different patient.')
1791 sol = _('Verify which patient this lab request really belongs to.')
1792 ctxt = _('lab [%s], request ID [%s], expected link with patient [%s], currently linked to patient [%s]') % (lab, req_id, pat_id, db_pat)
1793 cat = 'lab'
1794 status, data = gmPG.add_housekeeping_todo(me, to, prob, sol, ctxt, cat)
1795 return (None, data)
1796 return (True, req)
1797
1798 queries = []
1799 if type(lab) is types.IntType:
1800 cmd = "insert into lab_request (fk_encounter, fk_episode, fk_test_org, request_id) values (%s, %s, %s, %s)"
1801 else:
1802 cmd = "insert into lab_request (fk_encounter, fk_episode, fk_test_org, request_id) values (%s, %s, (select pk from test_org where internal_OBSOLETE_name=%s), %s)"
1803 queries.append((cmd, [encounter_id, episode_id, str(lab), req_id]))
1804 cmd = "select currval('lab_request_pk_seq')"
1805 queries.append((cmd, []))
1806
1807 result, err = gmPG.run_commit('historica', queries, True)
1808 if result is None:
1809 return (False, err)
1810 try:
1811 req = cLabRequest(aPK_obj=result[0][0])
1812 except gmExceptions.ConstructorError, msg:
1813 _log.exception(str(msg), sys.exc_info(), verbose=0)
1814 return (False, msg)
1815 return (True, req)
1816
1817 -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):
1818 tres = None
1819 data = {
1820 'patient_id': patient_id,
1821 'when_field': when_field,
1822 'when': when,
1823 'test_type': test_type,
1824 'val_num': val_num,
1825 'val_alpha': val_alpha,
1826 'unit': unit
1827 }
1828 try:
1829 tres = cLabResult(aPK_obj=data)
1830
1831 _log.error('will not overwrite existing test result')
1832 _log.debug(str(tres))
1833 return (None, tres)
1834 except gmExceptions.NoSuchClinItemError:
1835 _log.debug('test result not found - as expected, will create it')
1836 except gmExceptions.ConstructorError, msg:
1837 _log.exception(str(msg), sys.exc_info(), verbose=0)
1838 return (False, msg)
1839 if request is None:
1840 return (False, _('need lab request when inserting lab result'))
1841
1842 if encounter_id is None:
1843 encounter_id = request['pk_encounter']
1844 queries = []
1845 cmd = "insert into test_result (fk_encounter, fk_episode, fk_type, val_num, val_alpha, val_unit) values (%s, %s, %s, %s, %s, %s)"
1846 queries.append((cmd, [encounter_id, request['pk_episode'], test_type, val_num, val_alpha, unit]))
1847 cmd = "insert into lnk_result2lab_req (fk_result, fk_request) values ((select currval('test_result_pk_seq')), %s)"
1848 queries.append((cmd, [request['pk_request']]))
1849 cmd = "select currval('test_result_pk_seq')"
1850 queries.append((cmd, []))
1851
1852 result, err = gmPG.run_commit('historica', queries, True)
1853 if result is None:
1854 return (False, err)
1855 try:
1856 tres = cLabResult(aPK_obj=result[0][0])
1857 except gmExceptions.ConstructorError, msg:
1858 _log.exception(str(msg), sys.exc_info(), verbose=0)
1859 return (False, msg)
1860 return (True, tres)
1861
1863
1864 if limit < 1:
1865 limit = 1
1866
1867 lim = limit + 1
1868 cmd = """
1869 select pk_result
1870 from v_results4lab_req
1871 where reviewed is false
1872 order by pk_patient
1873 limit %s""" % lim
1874 rows = gmPG.run_ro_query('historica', cmd)
1875 if rows is None:
1876 _log.error('error retrieving unreviewed lab results')
1877 return (None, _('error retrieving unreviewed lab results'))
1878 if len(rows) == 0:
1879 return (False, [])
1880
1881 if len(rows) == lim:
1882 more_avail = True
1883
1884 del rows[limit]
1885 else:
1886 more_avail = False
1887 results = []
1888 for row in rows:
1889 try:
1890 results.append(cLabResult(aPK_obj=row[0]))
1891 except gmExceptions.ConstructorError:
1892 _log.exception('skipping unreviewed lab result [%s]' % row[0], sys.exc_info(), verbose=0)
1893 return (more_avail, results)
1894
1896 lim = limit + 1
1897 cmd = "select pk from lab_request where is_pending is true limit %s" % lim
1898 rows = gmPG.run_ro_query('historica', cmd)
1899 if rows is None:
1900 _log.error('error retrieving pending lab requests')
1901 return (None, None)
1902 if len(rows) == 0:
1903 return (False, [])
1904 results = []
1905
1906 if len(rows) == lim:
1907 too_many = True
1908
1909 del rows[limit]
1910 else:
1911 too_many = False
1912 requests = []
1913 for row in rows:
1914 try:
1915 requests.append(cLabRequest(aPK_obj=row[0]))
1916 except gmExceptions.ConstructorError:
1917 _log.exception('skipping pending lab request [%s]' % row[0], sys.exc_info(), verbose=0)
1918 return (too_many, requests)
1919
1921 """Get logically next request ID for given lab.
1922
1923 - incrementor_func:
1924 - if not supplied the next ID is guessed
1925 - if supplied it is applied to the most recently used ID
1926 """
1927 if type(lab) == types.IntType:
1928 lab_snippet = 'vlr.fk_test_org=%s'
1929 else:
1930 lab_snippet = 'vlr.lab_name=%s'
1931 lab = str(lab)
1932 cmd = """
1933 select request_id
1934 from lab_request lr0
1935 where lr0.clin_when = (
1936 select max(vlr.sampled_when)
1937 from v_lab_requests vlr
1938 where %s
1939 )""" % lab_snippet
1940 rows = gmPG.run_ro_query('historica', cmd, None, lab)
1941 if rows is None:
1942 _log.warning('error getting most recently used request ID for lab [%s]' % lab)
1943 return ''
1944 if len(rows) == 0:
1945 return ''
1946 most_recent = rows[0][0]
1947
1948 if incrementor_func is not None:
1949 try:
1950 next = incrementor_func(most_recent)
1951 except TypeError:
1952 _log.error('cannot call incrementor function [%s]' % str(incrementor_func))
1953 return most_recent
1954 return next
1955
1956 for pos in range(len(most_recent)):
1957 header = most_recent[:pos]
1958 trailer = most_recent[pos:]
1959 try:
1960 return '%s%s' % (header, str(int(trailer) + 1))
1961 except ValueError:
1962 header = most_recent[:-1]
1963 trailer = most_recent[-1:]
1964 return '%s%s' % (header, chr(ord(trailer) + 1))
1965
1967 """Calculate BMI.
1968
1969 mass: kg
1970 height: cm
1971 age: not yet used
1972
1973 returns:
1974 (True/False, data)
1975 True: data = (bmi, lower_normal, upper_normal)
1976 False: data = error message
1977 """
1978 converted, mass = gmTools.input2decimal(mass)
1979 if not converted:
1980 return False, u'mass: cannot convert <%s> to Decimal' % mass
1981
1982 converted, height = gmTools.input2decimal(height)
1983 if not converted:
1984 return False, u'height: cannot convert <%s> to Decimal' % height
1985
1986 approx_surface = (height / decimal.Decimal(100))**2
1987 bmi = mass / approx_surface
1988
1989 print mass, height, '->', approx_surface, '->', bmi
1990
1991 lower_normal_mass = 20.0 * approx_surface
1992 upper_normal_mass = 25.0 * approx_surface
1993
1994 return True, (bmi, lower_normal_mass, upper_normal_mass)
1995
1996
1997
1998 if __name__ == '__main__':
1999
2000 if len(sys.argv) < 2:
2001 sys.exit()
2002
2003 if sys.argv[1] != 'test':
2004 sys.exit()
2005
2006 import time
2007
2008 gmI18N.activate_locale()
2009 gmI18N.install_domain()
2010
2011
2013 tr = create_test_result (
2014 encounter = 1,
2015 episode = 1,
2016 type = 1,
2017 intended_reviewer = 1,
2018 val_num = '12',
2019 val_alpha=None,
2020 unit = 'mg/dl'
2021 )
2022 print tr
2023 return tr
2024
2028
2033
2035 print "test_result()"
2036
2037 data = {
2038 'patient_id': 12,
2039 'when_field': 'val_when',
2040 'when': '2000-09-17 18:23:00+02',
2041 'test_type': 9,
2042 'val_num': 17.3,
2043 'val_alpha': None,
2044 'unit': 'mg/l'
2045 }
2046 lab_result = cLabResult(aPK_obj=data)
2047 print lab_result
2048 fields = lab_result.get_fields()
2049 for field in fields:
2050 print field, ':', lab_result[field]
2051 print "updatable:", lab_result.get_updatable_fields()
2052 print time.time()
2053 print lab_result.get_patient()
2054 print time.time()
2055
2057 print "test_request()"
2058 try:
2059
2060
2061 data = {
2062 'req_id': 'EML#SC937-0176-CEC#11',
2063 'lab': 'Enterprise Main Lab'
2064 }
2065 lab_req = cLabRequest(aPK_obj=data)
2066 except gmExceptions.ConstructorError, msg:
2067 print "no such lab request:", msg
2068 return
2069 print lab_req
2070 fields = lab_req.get_fields()
2071 for field in fields:
2072 print field, ':', lab_req[field]
2073 print "updatable:", lab_req.get_updatable_fields()
2074 print time.time()
2075 print lab_req.get_patient()
2076 print time.time()
2077
2082
2087
2095
2100
2105
2114
2116 done, data = calculate_bmi(mass = sys.argv[2], height = sys.argv[3])
2117 bmi, low, high = data
2118
2119 print "BMI:", bmi
2120 print "low:", low, "kg"
2121 print "hi :", high, "kg"
2122
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142 test_test_panel()
2143
2144
2145