| Trees | Indices | Help |
|
|---|
|
|
1 """GNUmed measurements related business objects."""
2 #============================================================
3 # $Source: /cvsroot/gnumed/gnumed/gnumed/client/business/gmPathLab.py,v $
4 # $Id: gmPathLab.py,v 1.81 2010/02/06 20:45:44 ncq Exp $
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 # FIXME: use UCUM from Regenstrief Institute
25
26 #============================================================
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 #============================================================
64 """Represents one meta test type under which actual test types can be aggregated."""
65
66 _cmd_fetch_payload = u"""select * from clin.meta_test_type where pk = %s"""
67
68 _cmds_store_payload = []
69
70 _updatable_fields = []
71 #------------------------------------------------------------
73 cmd = u'delete from clin.meta_test_type where pk = %(pk)s'
74 args = {'pk': meta_type}
75 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
76 #------------------------------------------------------------
78 cmd = u'select * from clin.meta_test_type'
79 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = True)
80 return [ cMetaTestType(row = {'pk_field': 'pk', 'data': r, 'idx': idx}) for r in rows ]
81 #============================================================
83 """Represents one unified test type."""
84
85 # FIXME: if we ever want to write we need to include XMIN in the view
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 #--------------------------------------------------------
140
141 # find fk_test_org from name
142 if (attribute == 'fk_test_org') and (value is not None):
143 try:
144 int(value)
145 except:
146 cmd = u"select pk from clin.test_org where internal_name = %(val)s"
147 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': {'val': value}}])
148 if len(rows) == 0:
149 raise ValueError('[%s]: no test org for [%s], cannot set <%s>' % (self.__class__.__name__, value, attribute))
150 value = rows[0][0]
151
152 gmBusinessDBObject.cBusinessDBObject.__setitem__(self, attribute, value)
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 # found ?
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 # not found, so create it
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 # make query
222 cols = []
223 val_snippets = []
224 vals = {}
225
226 # lab
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 # code
239 cols.append('abbrev')
240 val_snippets.append('%(abbrev)s')
241 vals['abbrev'] = abbrev
242
243 # unit
244 cols.append('conversion_unit')
245 val_snippets.append('%(unit)s')
246 vals['unit'] = unit
247
248 # name
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 # use 1st result only, ignore if more than one
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 #============================================================
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 #--------------------------------------------------------
351
352 lines = []
353
354 lines.append(u' %s %s (%s): %s %s%s' % (
355 self._payload[self._idx['clin_when']].strftime(date_format),
356 self._payload[self._idx['unified_abbrev']],
357 self._payload[self._idx['unified_name']],
358 self._payload[self._idx['unified_val']],
359 self._payload[self._idx['val_unit']],
360 gmTools.coalesce(self._payload[self._idx['abnormality_indicator']], u'', u' (%s)')
361 ))
362
363 if with_comments:
364 if gmTools.coalesce(self._payload[self._idx['comment']], u'').strip() != u'':
365 lines.append(_(' Doc: %s') % self._payload[self._idx['comment']].strip())
366 if gmTools.coalesce(self._payload[self._idx['note_test_org']], u'').strip() != u'':
367 lines.append(_(' MTA: %s') % self._payload[self._idx['note_test_org']].strip())
368
369 if with_review:
370 if self._payload[self._idx['reviewed']]:
371 if self._payload[self._idx['is_clinically_relevant']]:
372 lines.append(u' %s %s: %s' % (
373 self._payload[self._idx['last_reviewer']],
374 self._payload[self._idx['last_reviewed']].strftime('%Y-%m-%d %H:%M'),
375 gmTools.bool2subst (
376 self._payload[self._idx['is_technically_abnormal']],
377 _('abnormal and relevant'),
378 _('normal but relevant')
379 )
380 ))
381 else:
382 lines.append(_(' unreviewed'))
383
384 return lines
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
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 # FIXME: this is not concurrency safe
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 # internal API
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 #--------------------------------------------------------
484 - def __change_existing_review(self, technically_abnormal=None, clinically_relevant=None, comment=None):
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 #============================================================
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 #--------------------------------------------------------
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 # instantiate from row data ?
647 if aPK_obj is None:
648 gmBusinessDBObject.cBusinessDBObject.__init__(self, row=row)
649 return
650 pk = aPK_obj
651 # find PK from row data ?
652 if type(aPK_obj) == types.DictType:
653 # sanity checks
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 # get PK
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 # instantiate class
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 #============================================================
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 #--------------------------------------------------------
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 # instantiate from row data ?
734 if aPK_obj is None:
735 gmBusinessDBObject.cBusinessDBObject.__init__(self, row=row)
736 return
737 pk = aPK_obj
738 # instantiate from "req_id" and "lab" ?
739 if type(aPK_obj) == types.DictType:
740 # sanity check
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 # generate query
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 # get pk
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 # instantiate class
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 # convenience functions
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 # found
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 # yes but ambigous
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 # not found
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 # insert new
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 # exists already, so fail
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 # not found
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 # insert new
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 # sanity check
891 if limit < 1:
892 limit = 1
893 # retrieve one more row than needed so we know there's more available ;-)
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 # more than LIMIT rows ?
908 if len(rows) == lim:
909 more_avail = True
910 # but deliver only LIMIT rows so that our assumption holds true...
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 # more than LIMIT rows ?
933 if len(rows) == lim:
934 too_many = True
935 # but deliver only LIMIT rows so that our assumption holds true...
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 # apply supplied incrementor
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 # try to be smart ourselves
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 # main - unit testing
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 # lab_result = cLabResult(aPK_obj=4)
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 # lab_req = cLabRequest(aPK_obj=1)
1048 # lab_req = cLabRequest(req_id='EML#SC937-0176-CEC#11', lab=2)
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 #--------------------------------------------------------
1077 print create_measurement_type (
1078 lab = None,
1079 abbrev = u'tBZ2',
1080 unit = u'mg%',
1081 name = 'BZ (test 2)'
1082 )
1083 #--------------------------------------------------------
1088 #--------------------------------------------------------
1093 #--------------------------------------------------------
1094 if (len(sys.argv) > 1) and (sys.argv[1] == 'test'):
1095
1096 #test_result()
1097 #test_create_test_result()
1098 #test_delete_test_result()
1099 #test_create_measurement_type()
1100 #test_lab_result()
1101 #test_request()
1102 #test_create_result()
1103 #test_unreviewed()
1104 #test_pending()
1105 #test_meta_test_type()
1106 test_test_type()
1107
1108 #============================================================
1109 # $Log: gmPathLab.py,v $
1110 # Revision 1.81 2010/02/06 20:45:44 ncq
1111 # - fix get-most-recent-result
1112 #
1113 # Revision 1.80 2010/02/02 13:50:15 ncq
1114 # - add test org handling
1115 #
1116 # Revision 1.79 2009/12/03 17:45:29 ncq
1117 # - implement cUnifiedTestType
1118 #
1119 # Revision 1.78 2009/09/17 21:52:13 ncq
1120 # - add .in_use property to test types
1121 #
1122 # Revision 1.77 2009/09/01 22:20:40 ncq
1123 # - nullify empty strings where appropriate
1124 # - support order_by on getting measurement types
1125 #
1126 # Revision 1.76 2009/08/08 12:16:48 ncq
1127 # - must us AS to supply pk
1128 #
1129 # Revision 1.75 2009/08/03 20:47:24 ncq
1130 # - fix storing measurement type and test org
1131 #
1132 # Revision 1.74 2009/07/23 16:30:45 ncq
1133 # - test -> measurement
1134 # - code -> abbrev
1135 #
1136 # Revision 1.73 2009/07/06 14:56:54 ncq
1137 # - make update of test result more resilient re "" == null
1138 # - much improved test result formatting
1139 #
1140 # Revision 1.72 2009/06/04 14:45:15 ncq
1141 # - re-import lost adjustments
1142 #
1143 # Revision 1.72 2009/05/28 10:45:57 ncq
1144 # - adjust to test table changes
1145 #
1146 # Revision 1.71 2009/05/26 09:21:13 ncq
1147 # - delete_meta_type
1148 # - cTestType -> cMeasurementType
1149 # - delete_measurement_type
1150 #
1151 # Revision 1.70 2009/05/24 16:27:34 ncq
1152 # - support meta test types
1153 # - better support test types
1154 #
1155 # Revision 1.69 2009/03/18 14:27:28 ncq
1156 # - add comment
1157 #
1158 # Revision 1.68 2009/02/27 12:38:16 ncq
1159 # - improved results formatting
1160 #
1161 # Revision 1.67 2009/02/18 19:17:56 ncq
1162 # - properly test for NULLness when formatting results
1163 #
1164 # Revision 1.66 2009/01/02 11:34:50 ncq
1165 # - cleanup
1166 #
1167 # Revision 1.65 2008/10/22 12:04:55 ncq
1168 # - use %x in strftime
1169 #
1170 # Revision 1.64 2008/07/14 13:45:08 ncq
1171 # - add .format to test results
1172 #
1173 # Revision 1.63 2008/06/24 13:54:47 ncq
1174 # - delete_test_result and test
1175 #
1176 # Revision 1.62 2008/06/23 21:49:19 ncq
1177 # - pimp cTestType, find/create_test_type
1178 # - some tests
1179 #
1180 # Revision 1.61 2008/06/19 15:24:47 ncq
1181 # - fix updating cTestResult
1182 #
1183 # Revision 1.60 2008/06/16 15:01:53 ncq
1184 # - create_test_result
1185 # - test suite cleanup
1186 # - reference_ranges property on cTestResult
1187 #
1188 # Revision 1.59 2008/04/22 21:15:16 ncq
1189 # - cTestResult
1190 #
1191 # Revision 1.58 2008/02/25 17:31:41 ncq
1192 # - logging cleanup
1193 #
1194 # Revision 1.57 2008/01/30 13:34:50 ncq
1195 # - switch to std lib logging
1196 #
1197 # Revision 1.56 2007/07/17 11:13:25 ncq
1198 # - no more gmClinItem
1199 #
1200 # Revision 1.55 2007/03/08 11:31:08 ncq
1201 # - just cleanup
1202 #
1203 # Revision 1.54 2007/01/09 12:56:18 ncq
1204 # - comment
1205 #
1206 # Revision 1.53 2006/10/25 07:17:40 ncq
1207 # - no more gmPG
1208 # - no more cClinItem
1209 #
1210 # Revision 1.52 2006/07/19 20:25:00 ncq
1211 # - gmPyCompat.py is history
1212 #
1213 # Revision 1.51 2005/10/26 21:16:26 ncq
1214 # - adjust to changes in reviewed status handling
1215 #
1216 # Revision 1.50 2005/04/27 12:37:32 sjtan
1217 #
1218 # id_patient -> pk_patient
1219 #
1220 # Revision 1.49 2005/03/23 18:31:19 ncq
1221 # - v_patient_items -> v_pat_items
1222 #
1223 # Revision 1.48 2005/02/15 18:29:03 ncq
1224 # - test_result.id -> pk
1225 #
1226 # Revision 1.47 2005/02/13 15:45:31 ncq
1227 # - v_basic_person.i_pk -> pk_identity
1228 #
1229 # Revision 1.46 2005/01/02 19:55:30 ncq
1230 # - don't need _xmins_refetch_col_pos anymore
1231 #
1232 # Revision 1.45 2004/12/27 16:48:11 ncq
1233 # - fix create_lab_request() to use proper aPK_obj syntax
1234 #
1235 # Revision 1.44 2004/12/20 16:45:49 ncq
1236 # - gmBusinessDBObject now requires refetching of XMIN after save_payload
1237 #
1238 # Revision 1.43 2004/12/14 03:27:56 ihaywood
1239 # xmin_rest_result -> xmin_test_result
1240 #
1241 # Carlos used a very old version of the SOAP2.py for no good reason, fixed.
1242 #
1243 # Revision 1.42 2004/11/03 22:32:34 ncq
1244 # - support _cmds_lock_rows_for_update in business object base class
1245 #
1246 # Revision 1.41 2004/10/18 09:48:20 ncq
1247 # - must have been asleep at the keyboard
1248 #
1249 # Revision 1.40 2004/10/18 09:46:02 ncq
1250 # - fix create_lab_result()
1251 #
1252 # Revision 1.39 2004/10/15 09:05:08 ncq
1253 # - converted cLabResult to allow use of row __init__()
1254 #
1255 # Revision 1.38 2004/10/12 18:32:52 ncq
1256 # - allow cLabRequest and cTestType to be filled from bulk fetch row data
1257 # - cLabResult not adapted yet
1258 #
1259 # Revision 1.37 2004/09/29 10:25:04 ncq
1260 # - basic_unit->conversion_unit
1261 #
1262 # Revision 1.36 2004/09/18 13:51:56 ncq
1263 # - support val_target_*
1264 #
1265 # Revision 1.35 2004/07/02 00:20:54 ncq
1266 # - v_patient_items.id_item -> pk_item
1267 #
1268 # Revision 1.34 2004/06/28 15:14:50 ncq
1269 # - use v_lab_requests
1270 #
1271 # Revision 1.33 2004/06/28 12:18:52 ncq
1272 # - more id_* -> fk_*
1273 #
1274 # Revision 1.32 2004/06/26 07:33:55 ncq
1275 # - id_episode -> fk/pk_episode
1276 #
1277 # Revision 1.31 2004/06/18 13:33:58 ncq
1278 # - saner logging
1279 #
1280 # Revision 1.30 2004/06/16 17:16:56 ncq
1281 # - correctly handle val_num/val_alpha in create_lab_result so
1282 # we can safely detect duplicates
1283 #
1284 # Revision 1.29 2004/06/01 23:56:39 ncq
1285 # - improved error handling in several places
1286 #
1287 # Revision 1.28 2004/05/30 20:12:33 ncq
1288 # - make create_lab_result() handle request objects, not request_id
1289 #
1290 # Revision 1.27 2004/05/26 15:45:25 ncq
1291 # - get_next_request_ID()
1292 #
1293 # Revision 1.26 2004/05/25 13:29:20 ncq
1294 # - order unreviewed results by pk_patient
1295 #
1296 # Revision 1.25 2004/05/25 00:20:47 ncq
1297 # - fix reversal of is_pending in get_pending_requests()
1298 #
1299 # Revision 1.24 2004/05/25 00:07:31 ncq
1300 # - speed up get_patient in test_result
1301 #
1302 # Revision 1.23 2004/05/24 23:34:53 ncq
1303 # - optimize get_patient in cLabRequest()
1304 #
1305 # Revision 1.22 2004/05/24 14:59:45 ncq
1306 # - get_pending_requests()
1307 #
1308 # Revision 1.21 2004/05/24 14:35:00 ncq
1309 # - get_unreviewed_results() now returns status of more_available
1310 #
1311 # Revision 1.20 2004/05/24 14:15:54 ncq
1312 # - get_unreviewed_results()
1313 #
1314 # Revision 1.19 2004/05/14 13:17:27 ncq
1315 # - less useless verbosity
1316 # - cleanup
1317 #
1318 # Revision 1.18 2004/05/13 00:03:17 ncq
1319 # - aPKey -> aPK_obj
1320 #
1321 # Revision 1.17 2004/05/11 01:37:21 ncq
1322 # - create_test_result -> create_lab_result
1323 # - need to insert into lnk_result2lab_req, too, in create_lab_result
1324 #
1325 # Revision 1.16 2004/05/08 22:13:11 ncq
1326 # - cleanup
1327 #
1328 # Revision 1.15 2004/05/08 17:29:18 ncq
1329 # - us NoSuchClinItemError
1330 #
1331 # Revision 1.14 2004/05/06 23:37:19 ncq
1332 # - lab result _update_payload update
1333 # - lab result.__init__ now supports values other than the PK
1334 # - add create_test_result()
1335 #
1336 # Revision 1.13 2004/05/04 07:55:00 ncq
1337 # - correctly detect "no such lab request" condition in create_lab_request()
1338 # - fail gracefully in test_request()
1339 #
1340 # Revision 1.12 2004/05/03 22:25:10 shilbert
1341 # - some typos fixed
1342 #
1343 # Revision 1.11 2004/05/03 15:30:58 ncq
1344 # - add create_lab_request()
1345 # - add cLabResult.get_patient()
1346 #
1347 # Revision 1.10 2004/05/03 12:50:34 ncq
1348 # - relative imports
1349 # - str()ify some things
1350 #
1351 # Revision 1.9 2004/05/02 22:56:36 ncq
1352 # - add create_lab_request()
1353 #
1354 # Revision 1.8 2004/04/26 21:56:19 ncq
1355 # - add cLabRequest.get_patient()
1356 # - add create_test_type()
1357 #
1358 # Revision 1.7 2004/04/21 15:27:38 ncq
1359 # - map 8407 to string for ldt import
1360 #
1361 # Revision 1.6 2004/04/20 00:14:30 ncq
1362 # - cTestType invented
1363 #
1364 # Revision 1.5 2004/04/19 12:42:41 ncq
1365 # - fix cLabRequest._cms_store_payload
1366 # - modularize testing
1367 #
1368 # Revision 1.4 2004/04/18 18:50:36 ncq
1369 # - override __init__() thusly removing the unholy _pre/post_init() business
1370 #
1371 # Revision 1.3 2004/04/12 22:59:38 ncq
1372 # - add lab request
1373 #
1374 # Revision 1.2 2004/04/11 12:07:54 ncq
1375 # - better unit testing
1376 #
1377 # Revision 1.1 2004/04/11 12:04:55 ncq
1378 # - first version
1379 #
1380
| Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Tue Feb 9 04:01:46 2010 | http://epydoc.sourceforge.net |