| Home | Trees | Indices | Help |
|
|---|
|
|
1 # -*- coding: utf8 -*-
2 """GNUmed health related business object.
3
4 license: GPL v2 or later
5 """
6 #============================================================
7 __version__ = "$Revision: 1.157 $"
8 __author__ = "Carlos Moro <cfmoro1976@yahoo.es>, <karsten.hilbert@gmx.net>"
9
10 import types, sys, string, datetime, logging, time
11
12
13 if __name__ == '__main__':
14 sys.path.insert(0, '../../')
15 from Gnumed.pycommon import gmPG2
16 from Gnumed.pycommon import gmI18N
17 from Gnumed.pycommon import gmTools
18 from Gnumed.pycommon import gmDateTime
19 from Gnumed.pycommon import gmBusinessDBObject
20 from Gnumed.pycommon import gmNull
21 from Gnumed.pycommon import gmExceptions
22
23 from Gnumed.business import gmClinNarrative
24 from Gnumed.business import gmCoding
25
26
27 _log = logging.getLogger('gm.emr')
28 _log.info(__version__)
29
30 try: _
31 except NameError: _ = lambda x:x
32 #============================================================
33 # diagnostic certainty classification
34 #============================================================
35 __diagnostic_certainty_classification_map = None
36
38
39 global __diagnostic_certainty_classification_map
40
41 if __diagnostic_certainty_classification_map is None:
42 __diagnostic_certainty_classification_map = {
43 None: u'',
44 u'A': _(u'A: Sign'),
45 u'B': _(u'B: Cluster of signs'),
46 u'C': _(u'C: Syndromic diagnosis'),
47 u'D': _(u'D: Scientific diagnosis')
48 }
49
50 try:
51 return __diagnostic_certainty_classification_map[classification]
52 except KeyError:
53 return _(u'<%s>: unknown diagnostic certainty classification') % classification
54 #============================================================
55 # Health Issues API
56 #============================================================
57 laterality2str = {
58 None: u'?',
59 u'na': u'',
60 u'sd': _('bilateral'),
61 u'ds': _('bilateral'),
62 u's': _('left'),
63 u'd': _('right')
64 }
65
66 #============================================================
68 """Represents one health issue."""
69
70 _cmd_fetch_payload = u"select *, xmin_health_issue from clin.v_health_issues where pk_health_issue=%s"
71 _cmds_store_payload = [
72 u"""update clin.health_issue set
73 description = %(description)s,
74 summary = gm.nullify_empty_string(%(summary)s),
75 age_noted = %(age_noted)s,
76 laterality = gm.nullify_empty_string(%(laterality)s),
77 grouping = gm.nullify_empty_string(%(grouping)s),
78 diagnostic_certainty_classification = gm.nullify_empty_string(%(diagnostic_certainty_classification)s),
79 is_active = %(is_active)s,
80 clinically_relevant = %(clinically_relevant)s,
81 is_confidential = %(is_confidential)s,
82 is_cause_of_death = %(is_cause_of_death)s
83 where
84 pk = %(pk_health_issue)s and
85 xmin = %(xmin_health_issue)s""",
86 u"select xmin as xmin_health_issue from clin.health_issue where pk = %(pk_health_issue)s"
87 ]
88 _updatable_fields = [
89 'description',
90 'summary',
91 'grouping',
92 'age_noted',
93 'laterality',
94 'is_active',
95 'clinically_relevant',
96 'is_confidential',
97 'is_cause_of_death',
98 'diagnostic_certainty_classification'
99 ]
100 #--------------------------------------------------------
101 - def __init__(self, aPK_obj=None, encounter=None, name='xxxDEFAULTxxx', patient=None, row=None):
102 pk = aPK_obj
103
104 if (pk is not None) or (row is not None):
105 gmBusinessDBObject.cBusinessDBObject.__init__(self, aPK_obj=pk, row=row)
106 return
107
108 if patient is None:
109 cmd = u"""select *, xmin_health_issue from clin.v_health_issues
110 where
111 description = %(desc)s
112 and
113 pk_patient = (select fk_patient from clin.encounter where pk = %(enc)s)"""
114 else:
115 cmd = u"""select *, xmin_health_issue from clin.v_health_issues
116 where
117 description = %(desc)s
118 and
119 pk_patient = %(pat)s"""
120
121 queries = [{'cmd': cmd, 'args': {'enc': encounter, 'desc': name, 'pat': patient}}]
122 rows, idx = gmPG2.run_ro_queries(queries = queries, get_col_idx = True)
123
124 if len(rows) == 0:
125 raise gmExceptions.NoSuchBusinessObjectError, 'no health issue for [enc:%s::desc:%s::pat:%s]' % (encounter, name, patient)
126
127 pk = rows[0][0]
128 r = {'idx': idx, 'data': rows[0], 'pk_field': 'pk_health_issue'}
129
130 gmBusinessDBObject.cBusinessDBObject.__init__(self, row=r)
131 #--------------------------------------------------------
132 # external API
133 #--------------------------------------------------------
135 """Method for issue renaming.
136
137 @param description
138 - the new descriptive name for the issue
139 @type description
140 - a string instance
141 """
142 # sanity check
143 if not type(description) in [str, unicode] or description.strip() == '':
144 _log.error('<description> must be a non-empty string')
145 return False
146 # update the issue description
147 old_description = self._payload[self._idx['description']]
148 self._payload[self._idx['description']] = description.strip()
149 self._is_modified = True
150 successful, data = self.save_payload()
151 if not successful:
152 _log.error('cannot rename health issue [%s] with [%s]' % (self, description))
153 self._payload[self._idx['description']] = old_description
154 return False
155 return True
156 #--------------------------------------------------------
158 cmd = u"SELECT * FROM clin.v_pat_episodes WHERE pk_health_issue = %(pk)s"
159 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': {'pk': self.pk_obj}}], get_col_idx = True)
160 return [ cEpisode(row = {'data': r, 'idx': idx, 'pk_field': 'pk_episode'}) for r in rows ]
161 #--------------------------------------------------------
163 """ttl in days"""
164 open_episode = self.get_open_episode()
165 if open_episode is None:
166 return True
167 earliest, latest = open_episode.get_access_range()
168 ttl = datetime.timedelta(ttl)
169 now = datetime.datetime.now(tz=latest.tzinfo)
170 if (latest + ttl) > now:
171 return False
172 open_episode['episode_open'] = False
173 success, data = open_episode.save_payload()
174 if success:
175 return True
176 return False # should be an exception
177 #--------------------------------------------------------
179 open_episode = self.get_open_episode()
180 open_episode['episode_open'] = False
181 success, data = open_episode.save_payload()
182 if success:
183 return True
184 return False
185 #--------------------------------------------------------
187 cmd = u"select exists (select 1 from clin.episode where fk_health_issue = %s and is_open is True)"
188 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj]}])
189 return rows[0][0]
190 #--------------------------------------------------------
192 cmd = u"select pk from clin.episode where fk_health_issue = %s and is_open is True"
193 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj]}])
194 if len(rows) == 0:
195 return None
196 return cEpisode(aPK_obj=rows[0][0])
197 #--------------------------------------------------------
199 if self._payload[self._idx['age_noted']] is None:
200 return u'<???>'
201
202 # since we've already got an interval we are bound to use it,
203 # further transformation will only introduce more errors,
204 # later we can improve this deeper inside
205 return gmDateTime.format_interval_medically(self._payload[self._idx['age_noted']])
206 #--------------------------------------------------------
208 """<pk_code> must be a value from ref.coding_system_root.pk_coding_system (clin.lnk_code2item_root.fk_generic_code)"""
209 cmd = u"INSERT INTO clin.lnk_code2h_issue (fk_item, fk_generic_code) values (%(item)s, %(code)s)"
210 args = {
211 'item': self._payload[self._idx['pk_health_issue']],
212 'code': pk_code
213 }
214 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
215 return True
216 #--------------------------------------------------------
218 """<pk_code> must be a value from ref.coding_system_root.pk_coding_system (clin.lnk_code2item_root.fk_generic_code)"""
219 cmd = u"DELETE FROM clin.lnk_code2h_issue WHERE fk_item = %(item)s AND fk_generic_code = %(code)s"
220 args = {
221 'item': self._payload[self._idx['pk_health_issue']],
222 'code': pk_code
223 }
224 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
225 return True
226 #--------------------------------------------------------
228 rows = gmClinNarrative.get_as_journal (
229 issues = (self.pk_obj,),
230 order_by = u'pk_episode, pk_encounter, clin_when, scr, src_table'
231 )
232
233 if len(rows) == 0:
234 return u''
235
236 left_margin = u' ' * left_margin
237
238 lines = []
239 lines.append(_('Clinical data generated during encounters under this health issue:'))
240
241 prev_epi = None
242 for row in rows:
243 if row['pk_episode'] != prev_epi:
244 lines.append(u'')
245 prev_epi = row['pk_episode']
246
247 when = row['clin_when'].strftime(date_format).decode(gmI18N.get_encoding())
248 top_row = u'%s%s %s (%s) %s' % (
249 gmTools.u_box_top_left_arc,
250 gmTools.u_box_horiz_single,
251 gmClinNarrative.soap_cat2l10n_str[row['real_soap_cat']],
252 when,
253 gmTools.u_box_horiz_single * 5
254 )
255 soap = gmTools.wrap (
256 text = row['narrative'],
257 width = 60,
258 initial_indent = u' ',
259 subsequent_indent = u' ' + left_margin
260 )
261 row_ver = u''
262 if row['row_version'] > 0:
263 row_ver = u'v%s: ' % row['row_version']
264 bottom_row = u'%s%s %s, %s%s %s' % (
265 u' ' * 40,
266 gmTools.u_box_horiz_light_heavy,
267 row['modified_by'],
268 row_ver,
269 row['date_modified'],
270 gmTools.u_box_horiz_heavy_light
271 )
272
273 lines.append(top_row)
274 lines.append(soap)
275 lines.append(bottom_row)
276
277 eol_w_margin = u'\n%s' % left_margin
278 return left_margin + eol_w_margin.join(lines) + u'\n'
279 #--------------------------------------------------------
281
282 if patient.ID != self._payload[self._idx['pk_patient']]:
283 msg = '<patient>.ID = %s but health issue %s belongs to patient %s' % (
284 patient.ID,
285 self._payload[self._idx['pk_health_issue']],
286 self._payload[self._idx['pk_patient']]
287 )
288 raise ValueError(msg)
289
290 lines = []
291
292 lines.append(_('Health Issue %s%s%s%s [#%s]') % (
293 u'\u00BB',
294 self._payload[self._idx['description']],
295 u'\u00AB',
296 gmTools.coalesce (
297 initial = self.laterality_description,
298 instead = u'',
299 template_initial = u' (%s)',
300 none_equivalents = [None, u'', u'?']
301 ),
302 self._payload[self._idx['pk_health_issue']]
303 ))
304
305 if self._payload[self._idx['is_confidential']]:
306 lines.append('')
307 lines.append(_(' ***** CONFIDENTIAL *****'))
308 lines.append('')
309
310 if self._payload[self._idx['is_cause_of_death']]:
311 lines.append('')
312 lines.append(_(' contributed to death of patient'))
313 lines.append('')
314
315 enc = cEncounter(aPK_obj = self._payload[self._idx['pk_encounter']])
316 lines.append (_(' Created during encounter: %s (%s - %s) [#%s]') % (
317 enc['l10n_type'],
318 enc['started_original_tz'].strftime('%Y-%m-%d %H:%M'),
319 enc['last_affirmed_original_tz'].strftime('%H:%M'),
320 self._payload[self._idx['pk_encounter']]
321 ))
322
323 if self._payload[self._idx['age_noted']] is not None:
324 lines.append(_(' Noted at age: %s') % self.age_noted_human_readable())
325
326 lines.append(u' ' + _('Status') + u': %s, %s%s' % (
327 gmTools.bool2subst(self._payload[self._idx['is_active']], _('active'), _('inactive')),
328 gmTools.bool2subst(self._payload[self._idx['clinically_relevant']], _('clinically relevant'), _('not clinically relevant')),
329 gmTools.coalesce (
330 initial = diagnostic_certainty_classification2str(self._payload[self._idx['diagnostic_certainty_classification']]),
331 instead = u'',
332 template_initial = u', %s',
333 none_equivalents = [None, u'']
334 )
335 ))
336
337 if self._payload[self._idx['summary']] is not None:
338 lines.append(u'')
339 lines.append(gmTools.wrap (
340 text = self._payload[self._idx['summary']],
341 width = 60,
342 initial_indent = u' ',
343 subsequent_indent = u' '
344 ))
345
346 # codes
347 codes = self.generic_codes
348 if len(codes) > 0:
349 lines.append(u'')
350 for c in codes:
351 lines.append(u' %s: %s (%s - %s)' % (
352 c['code'],
353 c['term'],
354 c['name_short'],
355 c['version']
356 ))
357 del codes
358
359 lines.append(u'')
360
361 emr = patient.get_emr()
362
363 # episodes
364 epis = emr.get_episodes(issues = [self._payload[self._idx['pk_health_issue']]])
365 if epis is None:
366 lines.append(_('Error retrieving episodes for this health issue.'))
367 elif len(epis) == 0:
368 lines.append(_('There are no episodes for this health issue.'))
369 else:
370 lines.append (
371 _('Episodes: %s (most recent: %s%s%s)') % (
372 len(epis),
373 gmTools.u_left_double_angle_quote,
374 emr.get_most_recent_episode(issue = self._payload[self._idx['pk_health_issue']])['description'],
375 gmTools.u_right_double_angle_quote
376 )
377 )
378 for epi in epis:
379 lines.append(u' \u00BB%s\u00AB (%s)' % (
380 epi['description'],
381 gmTools.bool2subst(epi['episode_open'], _('ongoing'), _('closed'))
382 ))
383
384 lines.append('')
385
386 # encounters
387 first_encounter = emr.get_first_encounter(issue_id = self._payload[self._idx['pk_health_issue']])
388 last_encounter = emr.get_last_encounter(issue_id = self._payload[self._idx['pk_health_issue']])
389
390 if first_encounter is None or last_encounter is None:
391 lines.append(_('No encounters found for this health issue.'))
392 else:
393 encs = emr.get_encounters(issues = [self._payload[self._idx['pk_health_issue']]])
394 lines.append(_('Encounters: %s (%s - %s):') % (
395 len(encs),
396 first_encounter['started_original_tz'].strftime('%m/%Y'),
397 last_encounter['last_affirmed_original_tz'].strftime('%m/%Y')
398 ))
399 lines.append(_(' Most recent: %s - %s') % (
400 last_encounter['started_original_tz'].strftime('%Y-%m-%d %H:%M'),
401 last_encounter['last_affirmed_original_tz'].strftime('%H:%M')
402 ))
403
404 # medications
405 meds = emr.get_current_substance_intake (
406 issues = [ self._payload[self._idx['pk_health_issue']] ],
407 order_by = u'is_currently_active, started, substance'
408 )
409
410 if len(meds) > 0:
411 lines.append(u'')
412 lines.append(_('Active medications: %s') % len(meds))
413 for m in meds:
414 lines.append(m.format(left_margin = (left_margin + 1)))
415 del meds
416
417 # hospital stays
418 stays = emr.get_hospital_stays (
419 issues = [ self._payload[self._idx['pk_health_issue']] ]
420 )
421 if len(stays) > 0:
422 lines.append(u'')
423 lines.append(_('Hospital stays: %s') % len(stays))
424 for s in stays:
425 lines.append(s.format(left_margin = (left_margin + 1)))
426 del stays
427
428 # procedures
429 procs = emr.get_performed_procedures (
430 issues = [ self._payload[self._idx['pk_health_issue']] ]
431 )
432 if len(procs) > 0:
433 lines.append(u'')
434 lines.append(_('Procedures performed: %s') % len(procs))
435 for p in procs:
436 lines.append(p.format(left_margin = (left_margin + 1)))
437 del procs
438
439 # family history
440 fhx = emr.get_family_history(issues = [ self._payload[self._idx['pk_health_issue']] ])
441 if len(fhx) > 0:
442 lines.append(u'')
443 lines.append(_('Family History: %s') % len(fhx))
444 for f in fhx:
445 lines.append(f.format (
446 left_margin = (left_margin + 1),
447 include_episode = True,
448 include_comment = True,
449 include_codes = False
450 ))
451 del fhx
452
453 epis = self.get_episodes()
454 if len(epis) > 0:
455 epi_pks = [ e['pk_episode'] for e in epis ]
456
457 # documents
458 doc_folder = patient.get_document_folder()
459 docs = doc_folder.get_documents(episodes = epi_pks)
460 if len(docs) > 0:
461 lines.append(u'')
462 lines.append(_('Documents: %s') % len(docs))
463 del docs
464
465 # test results
466 tests = emr.get_test_results_by_date(episodes = epi_pks)
467 if len(tests) > 0:
468 lines.append(u'')
469 lines.append(_('Measurements and Results: %s') % len(tests))
470 del tests
471
472 # vaccinations
473 vaccs = emr.get_vaccinations(episodes = epi_pks)
474 if len(vaccs) > 0:
475 lines.append(u'')
476 lines.append(_('Vaccinations:'))
477 for vacc in vaccs:
478 lines.extend(vacc.format(with_reaction = True))
479 del vaccs
480
481 del epis
482
483 left_margin = u' ' * left_margin
484 eol_w_margin = u'\n%s' % left_margin
485 return left_margin + eol_w_margin.join(lines) + u'\n'
486 #--------------------------------------------------------
487 # properties
488 #--------------------------------------------------------
489 episodes = property(get_episodes, lambda x:x)
490 #--------------------------------------------------------
491 open_episode = property(get_open_episode, lambda x:x)
492 #--------------------------------------------------------
494 cmd = u"""SELECT
495 coalesce (
496 (SELECT pk FROM clin.episode WHERE fk_health_issue = %(issue)s AND is_open IS TRUE),
497 (SELECT pk FROM clin.v_pat_episodes WHERE fk_health_issue = %(issue)s ORDER BY last_affirmed DESC limit 1)
498 )"""
499 args = {'issue': self.pk_obj}
500 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
501 if len(rows) == 0:
502 return None
503 return cEpisode(aPK_obj = rows[0][0])
504
505 latest_episode = property(_get_latest_episode, lambda x:x)
506 #--------------------------------------------------------
508 try:
509 return laterality2str[self._payload[self._idx['laterality']]]
510 except KeyError:
511 return u'<???>'
512
513 laterality_description = property(_get_laterality_description, lambda x:x)
514 #--------------------------------------------------------
516 return diagnostic_certainty_classification2str(self._payload[self._idx['diagnostic_certainty_classification']])
517
518 diagnostic_certainty_description = property(_get_diagnostic_certainty_description, lambda x:x)
519 #--------------------------------------------------------
521 if len(self._payload[self._idx['pk_generic_codes']]) == 0:
522 return []
523
524 cmd = gmCoding._SQL_get_generic_linked_codes % u'pk_generic_code IN %(pks)s'
525 args = {'pks': tuple(self._payload[self._idx['pk_generic_codes']])}
526 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
527 return [ gmCoding.cGenericLinkedCode(row = {'data': r, 'idx': idx, 'pk_field': 'pk_lnk_code2item'}) for r in rows ]
528
530 queries = []
531 # remove all codes
532 if len(self._payload[self._idx['pk_generic_codes']]) > 0:
533 queries.append ({
534 'cmd': u'DELETE FROM clin.lnk_code2h_issue WHERE fk_item = %(issue)s AND fk_generic_code IN %(codes)s',
535 'args': {
536 'issue': self._payload[self._idx['pk_health_issue']],
537 'codes': tuple(self._payload[self._idx['pk_generic_codes']])
538 }
539 })
540 # add new codes
541 for pk_code in pk_codes:
542 queries.append ({
543 'cmd': u'INSERT INTO clin.lnk_code2h_issue (fk_item, fk_generic_code) VALUES (%(issue)s, %(pk_code)s)',
544 'args': {
545 'issue': self._payload[self._idx['pk_health_issue']],
546 'pk_code': pk_code
547 }
548 })
549 if len(queries) == 0:
550 return
551 # run it all in one transaction
552 rows, idx = gmPG2.run_rw_queries(queries = queries)
553 return
554
555 generic_codes = property(_get_generic_codes, _set_generic_codes)
556 #============================================================
558 """Creates a new health issue for a given patient.
559
560 description - health issue name
561 """
562 try:
563 h_issue = cHealthIssue(name = description, encounter = encounter, patient = patient)
564 return h_issue
565 except gmExceptions.NoSuchBusinessObjectError:
566 pass
567
568 queries = []
569 cmd = u"insert into clin.health_issue (description, fk_encounter) values (%(desc)s, %(enc)s)"
570 queries.append({'cmd': cmd, 'args': {'desc': description, 'enc': encounter}})
571
572 cmd = u"select currval('clin.health_issue_pk_seq')"
573 queries.append({'cmd': cmd})
574
575 rows, idx = gmPG2.run_rw_queries(queries = queries, return_data = True)
576 h_issue = cHealthIssue(aPK_obj = rows[0][0])
577
578 return h_issue
579 #-----------------------------------------------------------
581 if isinstance(health_issue, cHealthIssue):
582 pk = health_issue['pk_health_issue']
583 else:
584 pk = int(health_issue)
585
586 try:
587 gmPG2.run_rw_queries(queries = [{'cmd': u'delete from clin.health_issue where pk=%(pk)s', 'args': {'pk': pk}}])
588 except gmPG2.dbapi.IntegrityError:
589 # should be parsing pgcode/and or error message
590 _log.exception('cannot delete health issue')
591 raise gmExceptions.DatabaseObjectInUseError('cannot delete health issue, it is in use')
592 #------------------------------------------------------------
593 # use as dummy for unassociated episodes
595 issue = {
596 'pk_health_issue': None,
597 'description': _('Unattributed episodes'),
598 'age_noted': None,
599 'laterality': u'na',
600 'is_active': True,
601 'clinically_relevant': True,
602 'is_confidential': None,
603 'is_cause_of_death': False,
604 'is_dummy': True,
605 'grouping': None
606 }
607 return issue
608 #-----------------------------------------------------------
610 return cProblem (
611 aPK_obj = {
612 'pk_patient': health_issue['pk_patient'],
613 'pk_health_issue': health_issue['pk_health_issue'],
614 'pk_episode': None
615 },
616 try_potential_problems = allow_irrelevant
617 )
618 #============================================================
619 # episodes API
620 #============================================================
622 """Represents one clinical episode.
623 """
624 _cmd_fetch_payload = u"select * from clin.v_pat_episodes where pk_episode=%s"
625 _cmds_store_payload = [
626 u"""update clin.episode set
627 fk_health_issue = %(pk_health_issue)s,
628 is_open = %(episode_open)s::boolean,
629 description = %(description)s,
630 summary = gm.nullify_empty_string(%(summary)s),
631 diagnostic_certainty_classification = gm.nullify_empty_string(%(diagnostic_certainty_classification)s)
632 where
633 pk = %(pk_episode)s and
634 xmin = %(xmin_episode)s""",
635 u"""select xmin_episode from clin.v_pat_episodes where pk_episode = %(pk_episode)s"""
636 ]
637 _updatable_fields = [
638 'pk_health_issue',
639 'episode_open',
640 'description',
641 'summary',
642 'diagnostic_certainty_classification'
643 ]
644 #--------------------------------------------------------
645 - def __init__(self, aPK_obj=None, id_patient=None, name='xxxDEFAULTxxx', health_issue=None, row=None, encounter=None):
646 pk = aPK_obj
647 if pk is None and row is None:
648
649 where_parts = [u'description = %(desc)s']
650
651 if id_patient is not None:
652 where_parts.append(u'pk_patient = %(pat)s')
653
654 if health_issue is not None:
655 where_parts.append(u'pk_health_issue = %(issue)s')
656
657 if encounter is not None:
658 where_parts.append(u'pk_patient = (select fk_patient from clin.encounter where pk = %(enc)s)')
659
660 args = {
661 'pat': id_patient,
662 'issue': health_issue,
663 'enc': encounter,
664 'desc': name
665 }
666
667 cmd = u"select * from clin.v_pat_episodes where %s" % u' and '.join(where_parts)
668
669 rows, idx = gmPG2.run_ro_queries(
670 queries = [{'cmd': cmd, 'args': args}],
671 get_col_idx=True
672 )
673
674 if len(rows) == 0:
675 raise gmExceptions.NoSuchBusinessObjectError, 'no episode for [%s:%s:%s:%s]' % (id_patient, name, health_issue, encounter)
676
677 r = {'idx': idx, 'data': rows[0], 'pk_field': 'pk_episode'}
678 gmBusinessDBObject.cBusinessDBObject.__init__(self, row=r)
679
680 else:
681 gmBusinessDBObject.cBusinessDBObject.__init__(self, aPK_obj=pk, row=row)
682 #--------------------------------------------------------
683 # external API
684 #--------------------------------------------------------
686 """Get earliest and latest access to this episode.
687
688 Returns a tuple(earliest, latest).
689 """
690 cmd = u"""
691 select
692 min(earliest),
693 max(latest)
694 from (
695 (select
696 (case when clin_when < modified_when
697 then clin_when
698 else modified_when
699 end) as earliest,
700 (case when clin_when > modified_when
701 then clin_when
702 else modified_when
703 end) as latest
704 from
705 clin.clin_root_item
706 where
707 fk_episode = %(pk)s
708
709 ) union all (
710
711 select
712 modified_when as earliest,
713 modified_when as latest
714 from
715 clin.episode
716 where
717 pk = %(pk)s
718 )
719 ) as ranges"""
720 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': {'pk': self.pk_obj}}])
721 if len(rows) == 0:
722 return (gmNull.cNull(warn=False), gmNull.cNull(warn=False))
723 return (rows[0][0], rows[0][1])
724 #--------------------------------------------------------
727 #--------------------------------------------------------
729 return gmClinNarrative.get_narrative (
730 soap_cats = soap_cats,
731 encounters = encounters,
732 episodes = [self.pk_obj],
733 order_by = order_by
734 )
735 #--------------------------------------------------------
737 """Method for episode editing, that is, episode renaming.
738
739 @param description
740 - the new descriptive name for the encounter
741 @type description
742 - a string instance
743 """
744 # sanity check
745 if description.strip() == '':
746 _log.error('<description> must be a non-empty string instance')
747 return False
748 # update the episode description
749 old_description = self._payload[self._idx['description']]
750 self._payload[self._idx['description']] = description.strip()
751 self._is_modified = True
752 successful, data = self.save_payload()
753 if not successful:
754 _log.error('cannot rename episode [%s] to [%s]' % (self, description))
755 self._payload[self._idx['description']] = old_description
756 return False
757 return True
758 #--------------------------------------------------------
760 """<pk_code> must be a value from ref.coding_system_root.pk_coding_system (clin.lnk_code2item_root.fk_generic_code)"""
761
762 if pk_code in self._payload[self._idx['pk_generic_codes']]:
763 return
764
765 cmd = u"""
766 INSERT INTO clin.lnk_code2episode
767 (fk_item, fk_generic_code)
768 SELECT
769 %(item)s,
770 %(code)s
771 WHERE NOT EXISTS (
772 SELECT 1 FROM clin.lnk_code2episode
773 WHERE
774 fk_item = %(item)s
775 AND
776 fk_generic_code = %(code)s
777 )"""
778 args = {
779 'item': self._payload[self._idx['pk_episode']],
780 'code': pk_code
781 }
782 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
783 return
784 #--------------------------------------------------------
786 """<pk_code> must be a value from ref.coding_system_root.pk_coding_system (clin.lnk_code2item_root.fk_generic_code)"""
787 cmd = u"DELETE FROM clin.lnk_code2episode WHERE fk_item = %(item)s AND fk_generic_code = %(code)s"
788 args = {
789 'item': self._payload[self._idx['pk_episode']],
790 'code': pk_code
791 }
792 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
793 return True
794 #--------------------------------------------------------
796 rows = gmClinNarrative.get_as_journal (
797 episodes = (self.pk_obj,),
798 order_by = u'pk_encounter, clin_when, scr, src_table'
799 #order_by = u'pk_encounter, scr, clin_when, src_table'
800 )
801
802 if len(rows) == 0:
803 return u''
804
805 lines = []
806
807 lines.append(_('Clinical data generated during encounters within this episode:'))
808
809 left_margin = u' ' * left_margin
810
811 prev_enc = None
812 for row in rows:
813 if row['pk_encounter'] != prev_enc:
814 lines.append(u'')
815 prev_enc = row['pk_encounter']
816
817 when = row['clin_when'].strftime(date_format).decode(gmI18N.get_encoding())
818 top_row = u'%s%s %s (%s) %s' % (
819 gmTools.u_box_top_left_arc,
820 gmTools.u_box_horiz_single,
821 gmClinNarrative.soap_cat2l10n_str[row['real_soap_cat']],
822 when,
823 gmTools.u_box_horiz_single * 5
824 )
825 soap = gmTools.wrap (
826 text = row['narrative'],
827 width = 60,
828 initial_indent = u' ',
829 subsequent_indent = u' ' + left_margin
830 )
831 row_ver = u''
832 if row['row_version'] > 0:
833 row_ver = u'v%s: ' % row['row_version']
834 bottom_row = u'%s%s %s, %s%s %s' % (
835 u' ' * 40,
836 gmTools.u_box_horiz_light_heavy,
837 row['modified_by'],
838 row_ver,
839 row['date_modified'],
840 gmTools.u_box_horiz_heavy_light
841 )
842
843 lines.append(top_row)
844 lines.append(soap)
845 lines.append(bottom_row)
846
847 eol_w_margin = u'\n%s' % left_margin
848 return left_margin + eol_w_margin.join(lines) + u'\n'
849 #--------------------------------------------------------
851
852 if patient.ID != self._payload[self._idx['pk_patient']]:
853 msg = '<patient>.ID = %s but episode %s belongs to patient %s' % (
854 patient.ID,
855 self._payload[self._idx['pk_episode']],
856 self._payload[self._idx['pk_patient']]
857 )
858 raise ValueError(msg)
859
860 lines = []
861
862 # episode details
863 lines.append (_('Episode %s%s%s [#%s]') % (
864 gmTools.u_left_double_angle_quote,
865 self._payload[self._idx['description']],
866 gmTools.u_right_double_angle_quote,
867 self._payload[self._idx['pk_episode']]
868 ))
869
870 enc = cEncounter(aPK_obj = self._payload[self._idx['pk_encounter']])
871 lines.append (u' ' + _('Created during encounter: %s (%s - %s) [#%s]') % (
872 enc['l10n_type'],
873 enc['started_original_tz'].strftime('%Y-%m-%d %H:%M'),
874 enc['last_affirmed_original_tz'].strftime('%H:%M'),
875 self._payload[self._idx['pk_encounter']]
876 ))
877
878 emr = patient.get_emr()
879 encs = emr.get_encounters(episodes = [self._payload[self._idx['pk_episode']]])
880 first_encounter = None
881 last_encounter = None
882 if (encs is not None) and (len(encs) > 0):
883 first_encounter = emr.get_first_encounter(episode_id = self._payload[self._idx['pk_episode']])
884 last_encounter = emr.get_last_encounter(episode_id = self._payload[self._idx['pk_episode']])
885 if self._payload[self._idx['episode_open']]:
886 end = gmDateTime.pydt_now_here()
887 end_str = gmTools.u_ellipsis
888 else:
889 end = last_encounter['last_affirmed']
890 end_str = last_encounter['last_affirmed'].strftime('%m/%Y')
891 age = gmDateTime.format_interval_medically(end - first_encounter['started'])
892 lines.append(_(' Duration: %s (%s - %s)') % (
893 age,
894 first_encounter['started'].strftime('%m/%Y'),
895 end_str
896 ))
897
898 lines.append(u' ' + _('Status') + u': %s%s' % (
899 gmTools.bool2subst(self._payload[self._idx['episode_open']], _('active'), _('finished')),
900 gmTools.coalesce (
901 initial = diagnostic_certainty_classification2str(self._payload[self._idx['diagnostic_certainty_classification']]),
902 instead = u'',
903 template_initial = u', %s',
904 none_equivalents = [None, u'']
905 )
906 ))
907
908 if self._payload[self._idx['summary']] is not None:
909 lines.append(u'')
910 lines.append(gmTools.wrap (
911 text = self._payload[self._idx['summary']],
912 width = 60,
913 initial_indent = u' ',
914 subsequent_indent = u' '
915 )
916 )
917
918 # codes
919 codes = self.generic_codes
920 if len(codes) > 0:
921 lines.append(u'')
922 for c in codes:
923 lines.append(u' %s: %s (%s - %s)' % (
924 c['code'],
925 c['term'],
926 c['name_short'],
927 c['version']
928 ))
929 del codes
930
931 lines.append(u'')
932
933 # encounters
934 if encs is None:
935 lines.append(_('Error retrieving encounters for this episode.'))
936 elif len(encs) == 0:
937 lines.append(_('There are no encounters for this episode.'))
938 else:
939 lines.append(_('Last worked on: %s\n') % last_encounter['last_affirmed_original_tz'].strftime('%Y-%m-%d %H:%M'))
940
941 if len(encs) < 4:
942 line = _('%s encounter(s) (%s - %s):')
943 else:
944 line = _('1st and (up to 3) most recent (of %s) encounters (%s - %s):')
945 lines.append(line % (
946 len(encs),
947 first_encounter['started'].strftime('%m/%Y'),
948 last_encounter['last_affirmed'].strftime('%m/%Y')
949 ))
950
951 lines.append(u' %s - %s (%s):%s' % (
952 first_encounter['started_original_tz'].strftime('%Y-%m-%d %H:%M'),
953 first_encounter['last_affirmed_original_tz'].strftime('%H:%M'),
954 first_encounter['l10n_type'],
955 gmTools.coalesce (
956 first_encounter['assessment_of_encounter'],
957 gmTools.coalesce (
958 first_encounter['reason_for_encounter'],
959 u'',
960 u' \u00BB%s\u00AB' + (u' (%s)' % _('RFE'))
961 ),
962 u' \u00BB%s\u00AB' + (u' (%s)' % _('AOE'))
963 )
964 ))
965
966 if len(encs) > 4:
967 lines.append(_(' ... %s skipped ...') % (len(encs) - 4))
968
969 for enc in encs[1:][-3:]:
970 lines.append(u' %s - %s (%s):%s' % (
971 enc['started_original_tz'].strftime('%Y-%m-%d %H:%M'),
972 enc['last_affirmed_original_tz'].strftime('%H:%M'),
973 enc['l10n_type'],
974 gmTools.coalesce (
975 enc['assessment_of_encounter'],
976 gmTools.coalesce (
977 enc['reason_for_encounter'],
978 u'',
979 u' \u00BB%s\u00AB' + (u' (%s)' % _('RFE'))
980 ),
981 u' \u00BB%s\u00AB' + (u' (%s)' % _('AOE'))
982 )
983 ))
984 del encs
985
986 # spell out last encounter
987 if last_encounter is not None:
988 lines.append('')
989 lines.append(_('Progress notes in most recent encounter:'))
990 lines.extend(last_encounter.format_soap (
991 episodes = [ self._payload[self._idx['pk_episode']] ],
992 left_margin = left_margin,
993 soap_cats = 'soap',
994 emr = emr
995 ))
996
997 # documents
998 doc_folder = patient.get_document_folder()
999 docs = doc_folder.get_documents (
1000 episodes = [ self._payload[self._idx['pk_episode']] ]
1001 )
1002
1003 if len(docs) > 0:
1004 lines.append('')
1005 lines.append(_('Documents: %s') % len(docs))
1006
1007 for d in docs:
1008 lines.append(u' %s %s:%s%s' % (
1009 d['clin_when'].strftime('%Y-%m-%d'),
1010 d['l10n_type'],
1011 gmTools.coalesce(d['comment'], u'', u' "%s"'),
1012 gmTools.coalesce(d['ext_ref'], u'', u' (%s)')
1013 ))
1014 del docs
1015
1016 # hospital stays
1017 stays = emr.get_hospital_stays(episodes = [ self._payload[self._idx['pk_episode']] ])
1018 if len(stays) > 0:
1019 lines.append('')
1020 lines.append(_('Hospital stays: %s') % len(stays))
1021 for s in stays:
1022 lines.append(s.format(left_margin = (left_margin + 1)))
1023 del stays
1024
1025 # procedures
1026 procs = emr.get_performed_procedures(episodes = [ self._payload[self._idx['pk_episode']] ])
1027 if len(procs) > 0:
1028 lines.append(u'')
1029 lines.append(_('Procedures performed: %s') % len(procs))
1030 for p in procs:
1031 lines.append(p.format (
1032 left_margin = (left_margin + 1),
1033 include_episode = False,
1034 include_codes = True
1035 ))
1036 del procs
1037
1038 # family history
1039 fhx = emr.get_family_history(episodes = [ self._payload[self._idx['pk_episode']] ])
1040 if len(fhx) > 0:
1041 lines.append(u'')
1042 lines.append(_('Family History: %s') % len(fhx))
1043 for f in fhx:
1044 lines.append(f.format (
1045 left_margin = (left_margin + 1),
1046 include_episode = False,
1047 include_comment = True,
1048 include_codes = True
1049 ))
1050 del fhx
1051
1052 # test results
1053 tests = emr.get_test_results_by_date(episodes = [ self._payload[self._idx['pk_episode']] ])
1054
1055 if len(tests) > 0:
1056 lines.append('')
1057 lines.append(_('Measurements and Results:'))
1058
1059 for t in tests:
1060 lines.extend(t.format (
1061 with_review = False,
1062 with_comments = False,
1063 date_format = '%Y-%m-%d'
1064 ))
1065 del tests
1066
1067 # vaccinations
1068 vaccs = emr.get_vaccinations (
1069 episodes = [ self._payload[self._idx['pk_episode']] ],
1070 order_by = u'date_given DESC, vaccine'
1071 )
1072
1073 if len(vaccs) > 0:
1074 lines.append(u'')
1075 lines.append(_('Vaccinations:'))
1076
1077 for vacc in vaccs:
1078 lines.extend(vacc.format (
1079 with_indications = True,
1080 with_comment = True,
1081 with_reaction = True,
1082 date_format = '%Y-%m-%d'
1083 ))
1084 del vaccs
1085
1086 left_margin = u' ' * left_margin
1087 eol_w_margin = u'\n%s' % left_margin
1088 return left_margin + eol_w_margin.join(lines) + u'\n'
1089 #--------------------------------------------------------
1090 # properties
1091 #--------------------------------------------------------
1093 return diagnostic_certainty_classification2str(self._payload[self._idx['diagnostic_certainty_classification']])
1094
1095 diagnostic_certainty_description = property(_get_diagnostic_certainty_description, lambda x:x)
1096 #--------------------------------------------------------
1098 if len(self._payload[self._idx['pk_generic_codes']]) == 0:
1099 return []
1100
1101 cmd = gmCoding._SQL_get_generic_linked_codes % u'pk_generic_code IN %(pks)s'
1102 args = {'pks': tuple(self._payload[self._idx['pk_generic_codes']])}
1103 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
1104 return [ gmCoding.cGenericLinkedCode(row = {'data': r, 'idx': idx, 'pk_field': 'pk_lnk_code2item'}) for r in rows ]
1105
1107 queries = []
1108 # remove all codes
1109 if len(self._payload[self._idx['pk_generic_codes']]) > 0:
1110 queries.append ({
1111 'cmd': u'DELETE FROM clin.lnk_code2episode WHERE fk_item = %(epi)s AND fk_generic_code IN %(codes)s',
1112 'args': {
1113 'epi': self._payload[self._idx['pk_episode']],
1114 'codes': tuple(self._payload[self._idx['pk_generic_codes']])
1115 }
1116 })
1117 # add new codes
1118 for pk_code in pk_codes:
1119 queries.append ({
1120 'cmd': u'INSERT INTO clin.lnk_code2episode (fk_item, fk_generic_code) VALUES (%(epi)s, %(pk_code)s)',
1121 'args': {
1122 'epi': self._payload[self._idx['pk_episode']],
1123 'pk_code': pk_code
1124 }
1125 })
1126 if len(queries) == 0:
1127 return
1128 # run it all in one transaction
1129 rows, idx = gmPG2.run_rw_queries(queries = queries)
1130 return
1131
1132 generic_codes = property(_get_generic_codes, _set_generic_codes)
1133 #--------------------------------------------------------
1135 cmd = u"""SELECT EXISTS (
1136 SELECT 1 FROM clin.clin_narrative
1137 WHERE
1138 fk_episode = %(epi)s
1139 AND
1140 fk_encounter IN (
1141 SELECT pk FROM clin.encounter WHERE fk_patient = %(pat)s
1142 )
1143 )"""
1144 args = {
1145 u'pat': self._payload[self._idx['pk_patient']],
1146 u'epi': self._payload[self._idx['pk_episode']]
1147 }
1148 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
1149 return rows[0][0]
1150
1151 has_narrative = property(_get_has_narrative, lambda x:x)
1152 #============================================================
1153 -def create_episode(pk_health_issue=None, episode_name=None, is_open=False, allow_dupes=False, encounter=None):
1154 """Creates a new episode for a given patient's health issue.
1155
1156 pk_health_issue - given health issue PK
1157 episode_name - name of episode
1158 """
1159 if not allow_dupes:
1160 try:
1161 episode = cEpisode(name=episode_name, health_issue=pk_health_issue, encounter = encounter)
1162 if episode['episode_open'] != is_open:
1163 episode['episode_open'] = is_open
1164 episode.save_payload()
1165 return episode
1166 except gmExceptions.ConstructorError:
1167 pass
1168
1169 queries = []
1170 cmd = u"insert into clin.episode (fk_health_issue, description, is_open, fk_encounter) values (%s, %s, %s::boolean, %s)"
1171 queries.append({'cmd': cmd, 'args': [pk_health_issue, episode_name, is_open, encounter]})
1172 queries.append({'cmd': cEpisode._cmd_fetch_payload % u"currval('clin.episode_pk_seq')"})
1173 rows, idx = gmPG2.run_rw_queries(queries = queries, return_data=True, get_col_idx=True)
1174
1175 episode = cEpisode(row={'data': rows[0], 'idx': idx, 'pk_field': 'pk_episode'})
1176 return episode
1177 #-----------------------------------------------------------
1179 if isinstance(episode, cEpisode):
1180 pk = episode['pk_episode']
1181 else:
1182 pk = int(episode)
1183
1184 try:
1185 gmPG2.run_rw_queries(queries = [{'cmd': u'delete from clin.episode where pk=%(pk)s', 'args': {'pk': pk}}])
1186 except gmPG2.dbapi.IntegrityError:
1187 # should be parsing pgcode/and or error message
1188 _log.exception('cannot delete episode')
1189 raise gmExceptions.DatabaseObjectInUseError('cannot delete episode, it is in use')
1190 #-----------------------------------------------------------
1192 return cProblem (
1193 aPK_obj = {
1194 'pk_patient': episode['pk_patient'],
1195 'pk_episode': episode['pk_episode'],
1196 'pk_health_issue': episode['pk_health_issue']
1197 },
1198 try_potential_problems = allow_closed
1199 )
1200 #============================================================
1201 # encounter API
1202 #============================================================
1204 """Represents one encounter."""
1205
1206 _cmd_fetch_payload = u"select * from clin.v_pat_encounters where pk_encounter = %s"
1207 _cmds_store_payload = [
1208 u"""update clin.encounter set
1209 started = %(started)s,
1210 last_affirmed = %(last_affirmed)s,
1211 fk_location = %(pk_location)s,
1212 fk_type = %(pk_type)s,
1213 reason_for_encounter = gm.nullify_empty_string(%(reason_for_encounter)s),
1214 assessment_of_encounter = gm.nullify_empty_string(%(assessment_of_encounter)s)
1215 where
1216 pk = %(pk_encounter)s and
1217 xmin = %(xmin_encounter)s""",
1218 u"""select xmin_encounter from clin.v_pat_encounters where pk_encounter=%(pk_encounter)s"""
1219 ]
1220 _updatable_fields = [
1221 'started',
1222 'last_affirmed',
1223 'pk_location',
1224 'pk_type',
1225 'reason_for_encounter',
1226 'assessment_of_encounter'
1227 ]
1228 #--------------------------------------------------------
1230 """Set the encounter as the active one.
1231
1232 "Setting active" means making sure the encounter
1233 row has the youngest "last_affirmed" timestamp of
1234 all encounter rows for this patient.
1235 """
1236 self['last_affirmed'] = gmDateTime.pydt_now_here()
1237 self.save()
1238 #--------------------------------------------------------
1240 """
1241 Moves every element currently linked to the current encounter
1242 and the source_episode onto target_episode.
1243
1244 @param source_episode The episode the elements are currently linked to.
1245 @type target_episode A cEpisode intance.
1246 @param target_episode The episode the elements will be relinked to.
1247 @type target_episode A cEpisode intance.
1248 """
1249 if source_episode['pk_episode'] == target_episode['pk_episode']:
1250 return True
1251
1252 queries = []
1253 cmd = u"""
1254 UPDATE clin.clin_root_item
1255 SET fk_episode = %(trg)s
1256 WHERE
1257 fk_encounter = %(enc)s AND
1258 fk_episode = %(src)s
1259 """
1260 rows, idx = gmPG2.run_rw_queries(queries = [{
1261 'cmd': cmd,
1262 'args': {
1263 'trg': target_episode['pk_episode'],
1264 'enc': self.pk_obj,
1265 'src': source_episode['pk_episode']
1266 }
1267 }])
1268 self.refetch_payload()
1269 return True
1270 #--------------------------------------------------------
1272
1273 relevant_fields = [
1274 'pk_location',
1275 'pk_type',
1276 'pk_patient',
1277 'reason_for_encounter',
1278 'assessment_of_encounter'
1279 ]
1280 for field in relevant_fields:
1281 if self._payload[self._idx[field]] != another_object[field]:
1282 _log.debug('mismatch on [%s]: "%s" vs. "%s"', field, self._payload[self._idx[field]], another_object[field])
1283 return False
1284
1285 relevant_fields = [
1286 'started',
1287 'last_affirmed',
1288 ]
1289 for field in relevant_fields:
1290 if self._payload[self._idx[field]] is None:
1291 if another_object[field] is None:
1292 continue
1293 _log.debug('mismatch on [%s]: "%s" vs. "%s"', field, self._payload[self._idx[field]], another_object[field])
1294 return False
1295
1296 if another_object[field] is None:
1297 return False
1298
1299 # compares at minute granularity
1300 if self._payload[self._idx[field]].strftime('%Y-%m-%d %H:%M') != another_object[field].strftime('%Y-%m-%d %H:%M'):
1301 _log.debug('mismatch on [%s]: "%s" vs. "%s"', field, self._payload[self._idx[field]], another_object[field])
1302 return False
1303
1304 # compare codes
1305 # 1) RFE
1306 if another_object['pk_generic_codes_rfe'] is None:
1307 if self._payload[self._idx['pk_generic_codes_rfe']] is not None:
1308 return False
1309 if another_object['pk_generic_codes_rfe'] is not None:
1310 if self._payload[self._idx['pk_generic_codes_rfe']] is None:
1311 return False
1312 if (
1313 (another_object['pk_generic_codes_rfe'] is None)
1314 and
1315 (self._payload[self._idx['pk_generic_codes_rfe']] is None)
1316 ) is False:
1317 if set(another_object['pk_generic_codes_rfe']) != set(self._payload[self._idx['pk_generic_codes_rfe']]):
1318 return False
1319 # 2) AOE
1320 if another_object['pk_generic_codes_aoe'] is None:
1321 if self._payload[self._idx['pk_generic_codes_aoe']] is not None:
1322 return False
1323 if another_object['pk_generic_codes_aoe'] is not None:
1324 if self._payload[self._idx['pk_generic_codes_aoe']] is None:
1325 return False
1326 if (
1327 (another_object['pk_generic_codes_aoe'] is None)
1328 and
1329 (self._payload[self._idx['pk_generic_codes_aoe']] is None)
1330 ) is False:
1331 if set(another_object['pk_generic_codes_aoe']) != set(self._payload[self._idx['pk_generic_codes_aoe']]):
1332 return False
1333
1334 return True
1335 #--------------------------------------------------------
1337 cmd = u"""
1338 select exists (
1339 select 1 from clin.v_pat_items where pk_patient = %(pat)s and pk_encounter = %(enc)s
1340 union all
1341 select 1 from blobs.v_doc_med where pk_patient = %(pat)s and pk_encounter = %(enc)s
1342 )"""
1343 args = {
1344 'pat': self._payload[self._idx['pk_patient']],
1345 'enc': self.pk_obj
1346 }
1347 rows, idx = gmPG2.run_ro_queries (
1348 queries = [{
1349 'cmd': cmd,
1350 'args': args
1351 }]
1352 )
1353 return rows[0][0]
1354 #--------------------------------------------------------
1356 cmd = u"""
1357 select exists (
1358 select 1 from clin.v_pat_items where pk_patient=%(pat)s and pk_encounter=%(enc)s
1359 )"""
1360 args = {
1361 'pat': self._payload[self._idx['pk_patient']],
1362 'enc': self.pk_obj
1363 }
1364 rows, idx = gmPG2.run_ro_queries (
1365 queries = [{
1366 'cmd': cmd,
1367 'args': args
1368 }]
1369 )
1370 return rows[0][0]
1371 #--------------------------------------------------------
1373 """soap_cats: <space> = admin category"""
1374
1375 if soap_cats is None:
1376 soap_cats = u'soap '
1377 else:
1378 soap_cats = soap_cats.lower()
1379
1380 cats = []
1381 for cat in soap_cats:
1382 if cat in u'soap':
1383 cats.append(cat)
1384 continue
1385 if cat == u' ':
1386 cats.append(None)
1387
1388 cmd = u"""
1389 SELECT EXISTS (
1390 SELECT 1 FROM clin.clin_narrative
1391 WHERE
1392 fk_encounter = %(enc)s
1393 AND
1394 soap_cat IN %(cats)s
1395 LIMIT 1
1396 )
1397 """
1398 args = {'enc': self._payload[self._idx['pk_encounter']], 'cats': tuple(cats)}
1399 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd,'args': args}])
1400 return rows[0][0]
1401 #--------------------------------------------------------
1403 cmd = u"""
1404 select exists (
1405 select 1 from blobs.v_doc_med where pk_patient = %(pat)s and pk_encounter = %(enc)s
1406 )"""
1407 args = {
1408 'pat': self._payload[self._idx['pk_patient']],
1409 'enc': self.pk_obj
1410 }
1411 rows, idx = gmPG2.run_ro_queries (
1412 queries = [{
1413 'cmd': cmd,
1414 'args': args
1415 }]
1416 )
1417 return rows[0][0]
1418 #--------------------------------------------------------
1420
1421 if soap_cat is not None:
1422 soap_cat = soap_cat.lower()
1423
1424 if episode is None:
1425 epi_part = u'fk_episode is null'
1426 else:
1427 epi_part = u'fk_episode = %(epi)s'
1428
1429 cmd = u"""
1430 select narrative
1431 from clin.clin_narrative
1432 where
1433 fk_encounter = %%(enc)s
1434 and
1435 soap_cat = %%(cat)s
1436 and
1437 %s
1438 order by clin_when desc
1439 limit 1
1440 """ % epi_part
1441
1442 args = {'enc': self.pk_obj, 'cat': soap_cat, 'epi': episode}
1443
1444 rows, idx = gmPG2.run_ro_queries (
1445 queries = [{
1446 'cmd': cmd,
1447 'args': args
1448 }]
1449 )
1450 if len(rows) == 0:
1451 return None
1452
1453 return rows[0][0]
1454 #--------------------------------------------------------
1456 cmd = u"""
1457 SELECT * FROM clin.v_pat_episodes
1458 WHERE
1459 pk_episode IN (
1460
1461 SELECT DISTINCT fk_episode
1462 FROM clin.clin_root_item
1463 WHERE fk_encounter = %%(enc)s
1464
1465 UNION
1466
1467 SELECT DISTINCT fk_episode
1468 FROM blobs.doc_med
1469 WHERE fk_encounter = %%(enc)s
1470 )
1471 %s"""
1472 args = {'enc': self.pk_obj}
1473 if exclude is not None:
1474 cmd = cmd % u'AND pk_episode NOT IN %(excluded)s'
1475 args['excluded'] = tuple(exclude)
1476 else:
1477 cmd = cmd % u''
1478
1479 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
1480
1481 return [ cEpisode(row = {'data': r, 'idx': idx, 'pk_field': 'pk_episode'}) for r in rows ]
1482 #--------------------------------------------------------
1484 """<pk_code> must be a value from ref.coding_system_root.pk_coding_system (clin.lnk_code2item_root.fk_generic_code)"""
1485 if field == u'rfe':
1486 cmd = u"INSERT INTO clin.lnk_code2rfe (fk_item, fk_generic_code) values (%(item)s, %(code)s)"
1487 elif field == u'aoe':
1488 cmd = u"INSERT INTO clin.lnk_code2aoe (fk_item, fk_generic_code) values (%(item)s, %(code)s)"
1489 else:
1490 raise ValueError('<field> must be one of "rfe" or "aoe", not "%s"', field)
1491 args = {
1492 'item': self._payload[self._idx['pk_encounter']],
1493 'code': pk_code
1494 }
1495 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
1496 return True
1497 #--------------------------------------------------------
1499 """<pk_code> must be a value from ref.coding_system_root.pk_coding_system (clin.lnk_code2item_root.fk_generic_code)"""
1500 if field == u'rfe':
1501 cmd = u"DELETE FROM clin.lnk_code2rfe WHERE fk_item = %(item)s AND fk_generic_code = %(code)s"
1502 elif field == u'aoe':
1503 cmd = u"DELETE FROM clin.lnk_code2aoe WHERE fk_item = %(item)s AND fk_generic_code = %(code)s"
1504 else:
1505 raise ValueError('<field> must be one of "rfe" or "aoe", not "%s"', field)
1506 args = {
1507 'item': self._payload[self._idx['pk_encounter']],
1508 'code': pk_code
1509 }
1510 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
1511 return True
1512 #--------------------------------------------------------
1513 - def format_soap(self, episodes=None, left_margin=0, soap_cats='soap', emr=None, issues=None):
1514
1515 lines = []
1516 for soap_cat in soap_cats:
1517 soap_cat_narratives = emr.get_clin_narrative (
1518 episodes = episodes,
1519 issues = issues,
1520 encounters = [self._payload[self._idx['pk_encounter']]],
1521 soap_cats = [soap_cat]
1522 )
1523 if soap_cat_narratives is None:
1524 continue
1525 if len(soap_cat_narratives) == 0:
1526 continue
1527
1528 lines.append(u'%s%s %s %s' % (
1529 gmTools.u_box_top_left_arc,
1530 gmTools.u_box_horiz_single,
1531 gmClinNarrative.soap_cat2l10n_str[soap_cat],
1532 gmTools.u_box_horiz_single * 5
1533 ))
1534 for soap_entry in soap_cat_narratives:
1535 txt = gmTools.wrap (
1536 text = soap_entry['narrative'],
1537 width = 75,
1538 initial_indent = u'',
1539 subsequent_indent = (u' ' * left_margin)
1540 )
1541 lines.append(txt)
1542 when = gmDateTime.pydt_strftime (
1543 soap_entry['date'],
1544 format = '%Y-%m-%d %H:%M',
1545 accuracy = gmDateTime.acc_minutes
1546 )
1547 txt = u'%s%s %.8s, %s %s' % (
1548 u' ' * 40,
1549 gmTools.u_box_horiz_light_heavy,
1550 soap_entry['provider'],
1551 when,
1552 gmTools.u_box_horiz_heavy_light
1553 )
1554 lines.append(txt)
1555 lines.append('')
1556
1557 return lines
1558 #--------------------------------------------------------
1560
1561 nothing2format = (
1562 (self._payload[self._idx['reason_for_encounter']] is None)
1563 and
1564 (self._payload[self._idx['assessment_of_encounter']] is None)
1565 and
1566 (self.has_soap_narrative(soap_cats = u'soap') is False)
1567 )
1568 if nothing2format:
1569 return u''
1570
1571 if date_format is None:
1572 date_format = '%A, %B %d %Y'
1573
1574 tex = u'\\multicolumn{2}{l}{%s: %s ({\\footnotesize %s - %s})} \\tabularnewline \n' % (
1575 gmTools.tex_escape_string(self._payload[self._idx['l10n_type']]),
1576 self._payload[self._idx['started']].strftime(date_format).decode(gmI18N.get_encoding()),
1577 self._payload[self._idx['started']].strftime('%H:%M'),
1578 self._payload[self._idx['last_affirmed']].strftime('%H:%M')
1579 )
1580 tex += u'\\hline \\tabularnewline \n'
1581
1582 for epi in self.get_episodes():
1583 soaps = epi.get_narrative(soap_cats = soap_cats, encounters = [self.pk_obj], order_by = soap_order)
1584 if len(soaps) == 0:
1585 continue
1586 tex += u'\\multicolumn{2}{l}{\\emph{%s: %s%s}} \\tabularnewline \n' % (
1587 gmTools.tex_escape_string(_('Problem')),
1588 gmTools.tex_escape_string(epi['description']),
1589 gmTools.coalesce (
1590 initial = diagnostic_certainty_classification2str(epi['diagnostic_certainty_classification']),
1591 instead = u'',
1592 template_initial = u' {\\footnotesize [%s]}',
1593 none_equivalents = [None, u'']
1594 )
1595 )
1596 if epi['pk_health_issue'] is not None:
1597 tex += u'\\multicolumn{2}{l}{\\emph{%s: %s%s}} \\tabularnewline \n' % (
1598 gmTools.tex_escape_string(_('Health issue')),
1599 gmTools.tex_escape_string(epi['health_issue']),
1600 gmTools.coalesce (
1601 initial = diagnostic_certainty_classification2str(epi['diagnostic_certainty_classification_issue']),
1602 instead = u'',
1603 template_initial = u' {\\footnotesize [%s]}',
1604 none_equivalents = [None, u'']
1605 )
1606 )
1607 for soap in soaps:
1608 tex += u'{\\small %s} & {\\small %s} \\tabularnewline \n' % (
1609 gmClinNarrative.soap_cat2l10n[soap['soap_cat']],
1610 gmTools.tex_escape_string(soap['narrative'].strip(u'\n'))
1611 )
1612 tex += u' & \\tabularnewline \n'
1613
1614 if self._payload[self._idx['reason_for_encounter']] is not None:
1615 tex += u'%s & %s \\tabularnewline \n' % (
1616 gmTools.tex_escape_string(_('RFE')),
1617 gmTools.tex_escape_string(self._payload[self._idx['reason_for_encounter']])
1618 )
1619 if self._payload[self._idx['assessment_of_encounter']] is not None:
1620 tex += u'%s & %s \\tabularnewline \n' % (
1621 gmTools.tex_escape_string(_('AOE')),
1622 gmTools.tex_escape_string(self._payload[self._idx['assessment_of_encounter']])
1623 )
1624
1625 tex += u'\\hline \\tabularnewline \n'
1626 tex += u' & \\tabularnewline \n'
1627
1628 return tex
1629 #--------------------------------------------------------
1630 - def format(self, episodes=None, with_soap=False, left_margin=0, patient=None, issues=None, with_docs=True, with_tests=True, fancy_header=True, with_vaccinations=True, with_co_encountlet_hints=False, with_rfe_aoe=False, with_family_history=True):
1631 """Format an encounter.
1632
1633 with_co_encountlet_hints:
1634 - whether to include which *other* episodes were discussed during this encounter
1635 - (only makes sense if episodes != None)
1636 """
1637 lines = []
1638
1639 if fancy_header:
1640 lines.append(u'%s%s: %s - %s (@%s)%s [#%s]' % (
1641 u' ' * left_margin,
1642 self._payload[self._idx['l10n_type']],
1643 self._payload[self._idx['started_original_tz']].strftime('%Y-%m-%d %H:%M'),
1644 self._payload[self._idx['last_affirmed_original_tz']].strftime('%H:%M'),
1645 self._payload[self._idx['source_time_zone']],
1646 gmTools.coalesce(self._payload[self._idx['assessment_of_encounter']], u'', u' \u00BB%s\u00AB'),
1647 self._payload[self._idx['pk_encounter']]
1648 ))
1649
1650 lines.append(_(' your time: %s - %s (@%s = %s%s)\n') % (
1651 self._payload[self._idx['started']].strftime('%Y-%m-%d %H:%M'),
1652 self._payload[self._idx['last_affirmed']].strftime('%H:%M'),
1653 gmDateTime.current_local_iso_numeric_timezone_string,
1654 gmTools.bool2subst (
1655 gmDateTime.dst_currently_in_effect,
1656 gmDateTime.py_dst_timezone_name,
1657 gmDateTime.py_timezone_name
1658 ),
1659 gmTools.bool2subst(gmDateTime.dst_currently_in_effect, u' - ' + _('daylight savings time in effect'), u'')
1660 ))
1661
1662 if self._payload[self._idx['reason_for_encounter']] is not None:
1663 lines.append(u'%s: %s' % (_('RFE'), self._payload[self._idx['reason_for_encounter']]))
1664 codes = self.generic_codes_rfe
1665 for c in codes:
1666 lines.append(u' %s: %s (%s - %s)' % (
1667 c['code'],
1668 c['term'],
1669 c['name_short'],
1670 c['version']
1671 ))
1672 if len(codes) > 0:
1673 lines.append(u'')
1674
1675 if self._payload[self._idx['assessment_of_encounter']] is not None:
1676 lines.append(u'%s: %s' % (_('AOE'), self._payload[self._idx['assessment_of_encounter']]))
1677 codes = self.generic_codes_aoe
1678 for c in codes:
1679 lines.append(u' %s: %s (%s - %s)' % (
1680 c['code'],
1681 c['term'],
1682 c['name_short'],
1683 c['version']
1684 ))
1685 if len(codes) > 0:
1686 lines.append(u'')
1687 del codes
1688
1689 else:
1690 lines.append(u'%s%s: %s - %s%s' % (
1691 u' ' * left_margin,
1692 self._payload[self._idx['l10n_type']],
1693 self._payload[self._idx['started_original_tz']].strftime('%Y-%m-%d %H:%M'),
1694 self._payload[self._idx['last_affirmed_original_tz']].strftime('%H:%M'),
1695 gmTools.coalesce(self._payload[self._idx['assessment_of_encounter']], u'', u' \u00BB%s\u00AB')
1696 ))
1697 if with_rfe_aoe:
1698 if self._payload[self._idx['reason_for_encounter']] is not None:
1699 lines.append(u'%s: %s' % (_('RFE'), self._payload[self._idx['reason_for_encounter']]))
1700 codes = self.generic_codes_rfe
1701 for c in codes:
1702 lines.append(u' %s: %s (%s - %s)' % (
1703 c['code'],
1704 c['term'],
1705 c['name_short'],
1706 c['version']
1707 ))
1708 if len(codes) > 0:
1709 lines.append(u'')
1710 if self._payload[self._idx['assessment_of_encounter']] is not None:
1711 lines.append(u'%s: %s' % (_('AOE'), self._payload[self._idx['assessment_of_encounter']]))
1712 codes = self.generic_codes_aoe
1713 if len(codes) > 0:
1714 lines.append(u'')
1715 for c in codes:
1716 lines.append(u' %s: %s (%s - %s)' % (
1717 c['code'],
1718 c['term'],
1719 c['name_short'],
1720 c['version']
1721 ))
1722 if len(codes) > 0:
1723 lines.append(u'')
1724 del codes
1725
1726 if with_soap:
1727 lines.append(u'')
1728
1729 if patient.ID != self._payload[self._idx['pk_patient']]:
1730 msg = '<patient>.ID = %s but encounter %s belongs to patient %s' % (
1731 patient.ID,
1732 self._payload[self._idx['pk_encounter']],
1733 self._payload[self._idx['pk_patient']]
1734 )
1735 raise ValueError(msg)
1736
1737 emr = patient.get_emr()
1738
1739 lines.extend(self.format_soap (
1740 episodes = episodes,
1741 left_margin = left_margin,
1742 soap_cats = 'soap',
1743 emr = emr,
1744 issues = issues
1745 ))
1746
1747 # # family history
1748 # if with_family_history:
1749 # if episodes is not None:
1750 # fhx = emr.get_family_history(episodes = episodes)
1751 # if len(fhx) > 0:
1752 # lines.append(u'')
1753 # lines.append(_('Family History: %s') % len(fhx))
1754 # for f in fhx:
1755 # lines.append(f.format (
1756 # left_margin = (left_margin + 1),
1757 # include_episode = False,
1758 # include_comment = True
1759 # ))
1760 # del fhx
1761
1762 # test results
1763 if with_tests:
1764 tests = emr.get_test_results_by_date (
1765 episodes = episodes,
1766 encounter = self._payload[self._idx['pk_encounter']]
1767 )
1768 if len(tests) > 0:
1769 lines.append('')
1770 lines.append(_('Measurements and Results:'))
1771
1772 for t in tests:
1773 lines.extend(t.format())
1774
1775 del tests
1776
1777 # vaccinations
1778 if with_vaccinations:
1779 vaccs = emr.get_vaccinations (
1780 episodes = episodes,
1781 encounters = [ self._payload[self._idx['pk_encounter']] ],
1782 order_by = u'date_given DESC, vaccine'
1783 )
1784
1785 if len(vaccs) > 0:
1786 lines.append(u'')
1787 lines.append(_('Vaccinations:'))
1788
1789 for vacc in vaccs:
1790 lines.extend(vacc.format (
1791 with_indications = True,
1792 with_comment = True,
1793 with_reaction = True,
1794 date_format = '%Y-%m-%d'
1795 ))
1796 del vaccs
1797
1798 # documents
1799 if with_docs:
1800 doc_folder = patient.get_document_folder()
1801 docs = doc_folder.get_documents (
1802 episodes = episodes,
1803 encounter = self._payload[self._idx['pk_encounter']]
1804 )
1805
1806 if len(docs) > 0:
1807 lines.append(u'')
1808 lines.append(_('Documents:'))
1809
1810 for d in docs:
1811 lines.append(u' %s %s:%s%s' % (
1812 d['clin_when'].strftime('%Y-%m-%d'),
1813 d['l10n_type'],
1814 gmTools.coalesce(d['comment'], u'', u' "%s"'),
1815 gmTools.coalesce(d['ext_ref'], u'', u' (%s)')
1816 ))
1817
1818 del docs
1819
1820 # co-encountlets
1821 if with_co_encountlet_hints:
1822 if episodes is not None:
1823 other_epis = self.get_episodes(exclude = episodes)
1824 if len(other_epis) > 0:
1825 lines.append(u'')
1826 lines.append(_('%s other episodes touched upon during this encounter:') % len(other_epis))
1827 for epi in other_epis:
1828 lines.append(u' %s%s%s%s' % (
1829 gmTools.u_left_double_angle_quote,
1830 epi['description'],
1831 gmTools.u_right_double_angle_quote,
1832 gmTools.coalesce(epi['health_issue'], u'', u' (%s)')
1833 ))
1834
1835 eol_w_margin = u'\n%s' % (u' ' * left_margin)
1836 return u'%s\n' % eol_w_margin.join(lines)
1837 #--------------------------------------------------------
1838 # properties
1839 #--------------------------------------------------------
1841 if len(self._payload[self._idx['pk_generic_codes_rfe']]) == 0:
1842 return []
1843
1844 cmd = gmCoding._SQL_get_generic_linked_codes % u'pk_generic_code IN %(pks)s'
1845 args = {'pks': tuple(self._payload[self._idx['pk_generic_codes_rfe']])}
1846 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
1847 return [ gmCoding.cGenericLinkedCode(row = {'data': r, 'idx': idx, 'pk_field': 'pk_lnk_code2item'}) for r in rows ]
1848
1850 queries = []
1851 # remove all codes
1852 if len(self._payload[self._idx['pk_generic_codes_rfe']]) > 0:
1853 queries.append ({
1854 'cmd': u'DELETE FROM clin.lnk_code2rfe WHERE fk_item = %(enc)s AND fk_generic_code IN %(codes)s',
1855 'args': {
1856 'enc': self._payload[self._idx['pk_encounter']],
1857 'codes': tuple(self._payload[self._idx['pk_generic_codes_rfe']])
1858 }
1859 })
1860 # add new codes
1861 for pk_code in pk_codes:
1862 queries.append ({
1863 'cmd': u'INSERT INTO clin.lnk_code2rfe (fk_item, fk_generic_code) VALUES (%(enc)s, %(pk_code)s)',
1864 'args': {
1865 'enc': self._payload[self._idx['pk_encounter']],
1866 'pk_code': pk_code
1867 }
1868 })
1869 if len(queries) == 0:
1870 return
1871 # run it all in one transaction
1872 rows, idx = gmPG2.run_rw_queries(queries = queries)
1873 self.refetch_payload()
1874 return
1875
1876 generic_codes_rfe = property(_get_generic_codes_rfe, _set_generic_codes_rfe)
1877 #--------------------------------------------------------
1879 if len(self._payload[self._idx['pk_generic_codes_aoe']]) == 0:
1880 return []
1881
1882 cmd = gmCoding._SQL_get_generic_linked_codes % u'pk_generic_code IN %(pks)s'
1883 args = {'pks': tuple(self._payload[self._idx['pk_generic_codes_aoe']])}
1884 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
1885 return [ gmCoding.cGenericLinkedCode(row = {'data': r, 'idx': idx, 'pk_field': 'pk_lnk_code2item'}) for r in rows ]
1886
1888 queries = []
1889 # remove all codes
1890 if len(self._payload[self._idx['pk_generic_codes_aoe']]) > 0:
1891 queries.append ({
1892 'cmd': u'DELETE FROM clin.lnk_code2aoe WHERE fk_item = %(enc)s AND fk_generic_code IN %(codes)s',
1893 'args': {
1894 'enc': self._payload[self._idx['pk_encounter']],
1895 'codes': tuple(self._payload[self._idx['pk_generic_codes_aoe']])
1896 }
1897 })
1898 # add new codes
1899 for pk_code in pk_codes:
1900 queries.append ({
1901 'cmd': u'INSERT INTO clin.lnk_code2aoe (fk_item, fk_generic_code) VALUES (%(enc)s, %(pk_code)s)',
1902 'args': {
1903 'enc': self._payload[self._idx['pk_encounter']],
1904 'pk_code': pk_code
1905 }
1906 })
1907 if len(queries) == 0:
1908 return
1909 # run it all in one transaction
1910 rows, idx = gmPG2.run_rw_queries(queries = queries)
1911 self.refetch_payload()
1912 return
1913
1914 generic_codes_aoe = property(_get_generic_codes_aoe, _set_generic_codes_aoe)
1915 #-----------------------------------------------------------
1917 """Creates a new encounter for a patient.
1918
1919 fk_patient - patient PK
1920 fk_location - encounter location
1921 enc_type - type of encounter
1922
1923 FIXME: we don't deal with location yet
1924 """
1925 if enc_type is None:
1926 enc_type = u'in surgery'
1927 # insert new encounter
1928 queries = []
1929 try:
1930 enc_type = int(enc_type)
1931 cmd = u"""
1932 INSERT INTO clin.encounter (
1933 fk_patient, fk_location, fk_type
1934 ) VALUES (
1935 %(pat)s,
1936 -1,
1937 %(typ)s
1938 ) RETURNING pk"""
1939 except ValueError:
1940 enc_type = enc_type
1941 cmd = u"""
1942 insert into clin.encounter (
1943 fk_patient, fk_location, fk_type
1944 ) values (
1945 %(pat)s,
1946 -1,
1947 coalesce((select pk from clin.encounter_type where description = %(typ)s), 0)
1948 ) RETURNING pk"""
1949 args = {'pat': fk_patient, 'typ': enc_type}
1950 queries.append({'cmd': cmd, 'args': args})
1951 rows, idx = gmPG2.run_rw_queries(queries = queries, return_data = True, get_col_idx = False)
1952 encounter = cEncounter(aPK_obj = rows[0]['pk'])
1953
1954 return encounter
1955 #-----------------------------------------------------------
1957
1958 rows, idx = gmPG2.run_rw_queries(
1959 queries = [{
1960 'cmd': u"select i18n.upd_tx(%(desc)s, %(l10n_desc)s)",
1961 'args': {'desc': description, 'l10n_desc': l10n_description}
1962 }],
1963 return_data = True
1964 )
1965
1966 success = rows[0][0]
1967 if not success:
1968 _log.warning('updating encounter type [%s] to [%s] failed', description, l10n_description)
1969
1970 return {'description': description, 'l10n_description': l10n_description}
1971 #-----------------------------------------------------------
1973 """This will attempt to create a NEW encounter type."""
1974
1975 # need a system name, so derive one if necessary
1976 if description is None:
1977 description = l10n_description
1978
1979 args = {
1980 'desc': description,
1981 'l10n_desc': l10n_description
1982 }
1983
1984 _log.debug('creating encounter type: %s, %s', description, l10n_description)
1985
1986 # does it exist already ?
1987 cmd = u"select description, _(description) from clin.encounter_type where description = %(desc)s"
1988 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
1989
1990 # yes
1991 if len(rows) > 0:
1992 # both system and l10n name are the same so all is well
1993 if (rows[0][0] == description) and (rows[0][1] == l10n_description):
1994 _log.info('encounter type [%s] already exists with the proper translation')
1995 return {'description': description, 'l10n_description': l10n_description}
1996
1997 # or maybe there just wasn't a translation to
1998 # the current language for this type yet ?
1999 cmd = u"select exists (select 1 from i18n.translations where orig = %(desc)s and lang = i18n.get_curr_lang())"
2000 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
2001
2002 # there was, so fail
2003 if rows[0][0]:
2004 _log.error('encounter type [%s] already exists but with another translation')
2005 return None
2006
2007 # else set it
2008 cmd = u"select i18n.upd_tx(%(desc)s, %(l10n_desc)s)"
2009 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
2010 return {'description': description, 'l10n_description': l10n_description}
2011
2012 # no
2013 queries = [
2014 {'cmd': u"insert into clin.encounter_type (description) values (%(desc)s)", 'args': args},
2015 {'cmd': u"select i18n.upd_tx(%(desc)s, %(l10n_desc)s)", 'args': args}
2016 ]
2017 rows, idx = gmPG2.run_rw_queries(queries = queries)
2018
2019 return {'description': description, 'l10n_description': l10n_description}
2020 #-----------------------------------------------------------
2022 cmd = u"""
2023 SELECT
2024 _(description) AS l10n_description,
2025 description
2026 FROM
2027 clin.encounter_type
2028 ORDER BY
2029 l10n_description
2030 """
2031 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}])
2032 return rows
2033 #-----------------------------------------------------------
2035 cmd = u"SELECT * from clin.encounter_type where description = %s"
2036 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [description]}])
2037 return rows
2038 #-----------------------------------------------------------
2040 cmd = u"delete from clin.encounter_type where description = %(desc)s"
2041 args = {'desc': description}
2042 try:
2043 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
2044 except gmPG2.dbapi.IntegrityError, e:
2045 if e.pgcode == gmPG2.sql_error_codes.FOREIGN_KEY_VIOLATION:
2046 return False
2047 raise
2048
2049 return True
2050 #============================================================
2052 """Represents one problem.
2053
2054 problems are the aggregation of
2055 .clinically_relevant=True issues and
2056 .is_open=True episodes
2057 """
2058 _cmd_fetch_payload = u'' # will get programmatically defined in __init__
2059 _cmds_store_payload = [u"select 1"]
2060 _updatable_fields = []
2061
2062 #--------------------------------------------------------
2064 """Initialize.
2065
2066 aPK_obj must contain the keys
2067 pk_patient
2068 pk_episode
2069 pk_health_issue
2070 """
2071 if aPK_obj is None:
2072 raise gmExceptions.ConstructorError, 'cannot instatiate cProblem for PK: [%s]' % (aPK_obj)
2073
2074 # As problems are rows from a view of different emr struct items,
2075 # the PK can't be a single field and, as some of the values of the
2076 # composed PK may be None, they must be queried using 'is null',
2077 # so we must programmatically construct the SQL query
2078 where_parts = []
2079 pk = {}
2080 for col_name in aPK_obj.keys():
2081 val = aPK_obj[col_name]
2082 if val is None:
2083 where_parts.append('%s IS NULL' % col_name)
2084 else:
2085 where_parts.append('%s = %%(%s)s' % (col_name, col_name))
2086 pk[col_name] = val
2087
2088 # try to instantiate from true problem view
2089 cProblem._cmd_fetch_payload = u"""
2090 SELECT *, False as is_potential_problem
2091 FROM clin.v_problem_list
2092 WHERE %s""" % u' AND '.join(where_parts)
2093
2094 try:
2095 gmBusinessDBObject.cBusinessDBObject.__init__(self, aPK_obj=pk)
2096 return
2097 except gmExceptions.ConstructorError:
2098 _log.exception('actual problem not found, trying "potential" problems')
2099 if try_potential_problems is False:
2100 raise
2101
2102 # try to instantiate from potential-problems view
2103 cProblem._cmd_fetch_payload = u"""
2104 SELECT *, True as is_potential_problem
2105 FROM clin.v_potential_problem_list
2106 WHERE %s""" % u' AND '.join(where_parts)
2107 gmBusinessDBObject.cBusinessDBObject.__init__(self, aPK_obj=pk)
2108 #--------------------------------------------------------
2110 """
2111 Retrieve the cEpisode instance equivalent to this problem.
2112 The problem's type attribute must be 'episode'
2113 """
2114 if self._payload[self._idx['type']] != 'episode':
2115 _log.error('cannot convert problem [%s] of type [%s] to episode' % (self._payload[self._idx['problem']], self._payload[self._idx['type']]))
2116 return None
2117 return cEpisode(aPK_obj = self._payload[self._idx['pk_episode']])
2118 #--------------------------------------------------------
2120 """
2121 Retrieve the cHealthIssue instance equivalent to this problem.
2122 The problem's type attribute must be 'issue'
2123 """
2124 if self._payload[self._idx['type']] != 'issue':
2125 _log.error('cannot convert problem [%s] of type [%s] to health issue' % (self._payload[self._idx['problem']], self._payload[self._idx['type']]))
2126 return None
2127 return cHealthIssue(aPK_obj = self._payload[self._idx['pk_health_issue']])
2128 #--------------------------------------------------------
2130
2131 if self._payload[self._idx['type']] == u'issue':
2132 episodes = [ cHealthIssue(aPK_obj = self._payload[self._idx['pk_health_issue']]).latest_episode ]
2133 #xxxxxxxxxxxxx
2134
2135 emr = patient.get_emr()
2136
2137 doc_folder = gmDocuments.cDocumentFolder(aPKey = patient.ID)
2138 return doc_folder.get_visual_progress_notes (
2139 health_issue = self._payload[self._idx['pk_health_issue']],
2140 episode = self._payload[self._idx['pk_episode']]
2141 )
2142 #--------------------------------------------------------
2143 # properties
2144 #--------------------------------------------------------
2145 # doubles as 'diagnostic_certainty_description' getter:
2147 return diagnostic_certainty_classification2str(self._payload[self._idx['diagnostic_certainty_classification']])
2148
2149 diagnostic_certainty_description = property(get_diagnostic_certainty_description, lambda x:x)
2150 #--------------------------------------------------------
2152 if self._payload[self._idx['type']] == u'issue':
2153 cmd = u"""
2154 SELECT * FROM clin.v_linked_codes WHERE
2155 item_table = 'clin.lnk_code2h_issue'::regclass
2156 AND
2157 pk_item = %(item)s
2158 """
2159 args = {'item': self._payload[self._idx['pk_health_issue']]}
2160 else:
2161 cmd = u"""
2162 SELECT * FROM clin.v_linked_codes WHERE
2163 item_table = 'clin.lnk_code2episode'::regclass
2164 AND
2165 pk_item = %(item)s
2166 """
2167 args = {'item': self._payload[self._idx['pk_episode']]}
2168
2169 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
2170 return [ gmCoding.cGenericLinkedCode(row = {'data': r, 'idx': idx, 'pk_field': 'pk_lnk_code2item'}) for r in rows ]
2171
2172 generic_codes = property(_get_generic_codes, lambda x:x)
2173 #-----------------------------------------------------------
2175 """Retrieve the cEpisode instance equivalent to the given problem.
2176
2177 The problem's type attribute must be 'episode'
2178
2179 @param problem: The problem to retrieve its related episode for
2180 @type problem: A gmEMRStructItems.cProblem instance
2181 """
2182 if isinstance(problem, cEpisode):
2183 return problem
2184
2185 exc = TypeError('cannot convert [%s] to episode' % problem)
2186
2187 if not isinstance(problem, cProblem):
2188 raise exc
2189
2190 if problem['type'] != 'episode':
2191 raise exc
2192
2193 return cEpisode(aPK_obj = problem['pk_episode'])
2194 #-----------------------------------------------------------
2196 """Retrieve the cIssue instance equivalent to the given problem.
2197
2198 The problem's type attribute must be 'issue'.
2199
2200 @param problem: The problem to retrieve the corresponding issue for
2201 @type problem: A gmEMRStructItems.cProblem instance
2202 """
2203 if isinstance(problem, cHealthIssue):
2204 return problem
2205
2206 exc = TypeError('cannot convert [%s] to health issue' % problem)
2207
2208 if not isinstance(problem, cProblem):
2209 raise exc
2210
2211 if problem['type'] != 'issue':
2212 raise exc
2213
2214 return cHealthIssue(aPK_obj = problem['pk_health_issue'])
2215 #-----------------------------------------------------------
2217 """Transform given problem into either episode or health issue instance.
2218 """
2219 if isinstance(problem, (cEpisode, cHealthIssue)):
2220 return problem
2221
2222 exc = TypeError('cannot reclass [%s] instance to either episode or health issue' % type(problem))
2223
2224 if not isinstance(problem, cProblem):
2225 _log.debug(u'%s' % problem)
2226 raise exc
2227
2228 if problem['type'] == 'episode':
2229 return cEpisode(aPK_obj = problem['pk_episode'])
2230
2231 if problem['type'] == 'issue':
2232 return cHealthIssue(aPK_obj = problem['pk_health_issue'])
2233
2234 raise exc
2235 #============================================================
2237
2238 _cmd_fetch_payload = u"select * from clin.v_pat_hospital_stays where pk_hospital_stay = %s"
2239 _cmds_store_payload = [
2240 u"""update clin.hospital_stay set
2241 clin_when = %(admission)s,
2242 discharge = %(discharge)s,
2243 narrative = gm.nullify_empty_string(%(hospital)s),
2244 fk_episode = %(pk_episode)s,
2245 fk_encounter = %(pk_encounter)s
2246 where
2247 pk = %(pk_hospital_stay)s and
2248 xmin = %(xmin_hospital_stay)s""",
2249 u"""select xmin_hospital_stay from clin.v_pat_hospital_stays where pk_hospital_stay = %(pk_hospital_stay)s"""
2250 ]
2251 _updatable_fields = [
2252 'admission',
2253 'discharge',
2254 'hospital',
2255 'pk_episode',
2256 'pk_encounter'
2257 ]
2258 #-------------------------------------------------------
2260
2261 if self._payload[self._idx['discharge']] is not None:
2262 dis = u' - %s' % self._payload[self._idx['discharge']].strftime('%Y %b %d').decode(gmI18N.get_encoding())
2263 else:
2264 dis = u''
2265
2266 line = u'%s%s%s%s: %s%s%s' % (
2267 u' ' * left_margin,
2268 self._payload[self._idx['admission']].strftime('%Y %b %d').decode(gmI18N.get_encoding()),
2269 dis,
2270 gmTools.coalesce(self._payload[self._idx['hospital']], u'', u' (%s)'),
2271 gmTools.u_left_double_angle_quote,
2272 self._payload[self._idx['episode']],
2273 gmTools.u_right_double_angle_quote
2274 )
2275
2276 return line
2277 #-----------------------------------------------------------
2279
2280 queries = [{
2281 'cmd': u'SELECT * FROM clin.v_pat_hospital_stays WHERE pk_patient = %(pat)s ORDER BY admission',
2282 'args': {'pat': patient}
2283 }]
2284
2285 rows, idx = gmPG2.run_ro_queries(queries = queries, get_col_idx = True)
2286
2287 return [ cHospitalStay(row = {'idx': idx, 'data': r, 'pk_field': 'pk_hospital_stay'}) for r in rows ]
2288 #-----------------------------------------------------------
2290
2291 queries = [{
2292 'cmd': u'INSERT INTO clin.hospital_stay (fk_encounter, fk_episode) VALUES (%(enc)s, %(epi)s) RETURNING pk',
2293 'args': {'enc': encounter, 'epi': episode}
2294 }]
2295 rows, idx = gmPG2.run_rw_queries(queries = queries, return_data = True)
2296
2297 return cHospitalStay(aPK_obj = rows[0][0])
2298 #-----------------------------------------------------------
2300 cmd = u'DELETE FROM clin.hospital_stay WHERE pk = %(pk)s'
2301 args = {'pk': stay}
2302 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
2303 return True
2304 #============================================================
2306
2307 _cmd_fetch_payload = u"select * from clin.v_pat_procedures where pk_procedure = %s"
2308 _cmds_store_payload = [
2309 u"""UPDATE clin.procedure SET
2310 soap_cat = 'p',
2311 clin_when = %(clin_when)s,
2312 clin_end = %(clin_end)s,
2313 is_ongoing = %(is_ongoing)s,
2314 clin_where = NULLIF (
2315 COALESCE (
2316 %(pk_hospital_stay)s::TEXT,
2317 gm.nullify_empty_string(%(clin_where)s)
2318 ),
2319 %(pk_hospital_stay)s::TEXT
2320 ),
2321 narrative = gm.nullify_empty_string(%(performed_procedure)s),
2322 fk_hospital_stay = %(pk_hospital_stay)s,
2323 fk_episode = %(pk_episode)s,
2324 fk_encounter = %(pk_encounter)s
2325 WHERE
2326 pk = %(pk_procedure)s AND
2327 xmin = %(xmin_procedure)s
2328 RETURNING xmin as xmin_procedure"""
2329 ]
2330 _updatable_fields = [
2331 'clin_when',
2332 'clin_end',
2333 'is_ongoing',
2334 'clin_where',
2335 'performed_procedure',
2336 'pk_hospital_stay',
2337 'pk_episode',
2338 'pk_encounter'
2339 ]
2340 #-------------------------------------------------------
2342
2343 if (attribute == 'pk_hospital_stay') and (value is not None):
2344 gmBusinessDBObject.cBusinessDBObject.__setitem__(self, 'clin_where', None)
2345
2346 if (attribute == 'clin_where') and (value is not None) and (value.strip() != u''):
2347 gmBusinessDBObject.cBusinessDBObject.__setitem__(self, 'pk_hospital_stay', None)
2348
2349 gmBusinessDBObject.cBusinessDBObject.__setitem__(self, attribute, value)
2350 #-------------------------------------------------------
2352
2353 if self._payload[self._idx['is_ongoing']]:
2354 end = _(' (ongoing)')
2355 else:
2356 end = self._payload[self._idx['clin_end']]
2357 if end is None:
2358 end = u''
2359 else:
2360 end = u' - %s' % end.strftime('%Y %b %d').decode(gmI18N.get_encoding())
2361
2362 line = u'%s%s%s, %s: %s' % (
2363 (u' ' * left_margin),
2364 self._payload[self._idx['clin_when']].strftime('%Y %b %d').decode(gmI18N.get_encoding()),
2365 end,
2366 self._payload[self._idx['clin_where']],
2367 self._payload[self._idx['performed_procedure']]
2368 )
2369 if include_episode:
2370 line = u'%s (%s)' % (line, self._payload[self._idx['episode']])
2371
2372 if include_codes:
2373 codes = self.generic_codes
2374 if len(codes) > 0:
2375 line += u'\n'
2376 for c in codes:
2377 line += u'%s %s: %s (%s - %s)\n' % (
2378 (u' ' * left_margin),
2379 c['code'],
2380 c['term'],
2381 c['name_short'],
2382 c['version']
2383 )
2384 del codes
2385
2386 return line
2387 #--------------------------------------------------------
2389 """<pk_code> must be a value from ref.coding_system_root.pk_coding_system (clin.lnk_code2item_root.fk_generic_code)"""
2390 cmd = u"INSERT INTO clin.lnk_code2procedure (fk_item, fk_generic_code) values (%(issue)s, %(code)s)"
2391 args = {
2392 'issue': self._payload[self._idx['pk_procedure']],
2393 'code': pk_code
2394 }
2395 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
2396 return True
2397 #--------------------------------------------------------
2399 """<pk_code> must be a value from ref.coding_system_root.pk_coding_system (clin.lnk_code2item_root.fk_generic_code)"""
2400 cmd = u"DELETE FROM clin.lnk_code2procedure WHERE fk_item = %(issue)s AND fk_generic_code = %(code)s"
2401 args = {
2402 'issue': self._payload[self._idx['pk_procedure']],
2403 'code': pk_code
2404 }
2405 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
2406 return True
2407 #--------------------------------------------------------
2408 # properties
2409 #--------------------------------------------------------
2411 if len(self._payload[self._idx['pk_generic_codes']]) == 0:
2412 return []
2413
2414 cmd = gmCoding._SQL_get_generic_linked_codes % u'pk_generic_code IN %(pks)s'
2415 args = {'pks': tuple(self._payload[self._idx['pk_generic_codes']])}
2416 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
2417 return [ gmCoding.cGenericLinkedCode(row = {'data': r, 'idx': idx, 'pk_field': 'pk_lnk_code2item'}) for r in rows ]
2418
2420 queries = []
2421 # remove all codes
2422 if len(self._payload[self._idx['pk_generic_codes']]) > 0:
2423 queries.append ({
2424 'cmd': u'DELETE FROM clin.lnk_code2procedure WHERE fk_item = %(proc)s AND fk_generic_code IN %(codes)s',
2425 'args': {
2426 'proc': self._payload[self._idx['pk_procedure']],
2427 'codes': tuple(self._payload[self._idx['pk_generic_codes']])
2428 }
2429 })
2430 # add new codes
2431 for pk_code in pk_codes:
2432 queries.append ({
2433 'cmd': u'INSERT INTO clin.lnk_code2procedure (fk_item, fk_generic_code) VALUES (%(proc)s, %(pk_code)s)',
2434 'args': {
2435 'proc': self._payload[self._idx['pk_procedure']],
2436 'pk_code': pk_code
2437 }
2438 })
2439 if len(queries) == 0:
2440 return
2441 # run it all in one transaction
2442 rows, idx = gmPG2.run_rw_queries(queries = queries)
2443 return
2444
2445 generic_codes = property(_get_generic_codes, _set_generic_codes)
2446 #-----------------------------------------------------------
2448
2449 queries = [
2450 {
2451 'cmd': u'select * from clin.v_pat_procedures where pk_patient = %(pat)s order by clin_when',
2452 'args': {'pat': patient}
2453 }
2454 ]
2455
2456 rows, idx = gmPG2.run_ro_queries(queries = queries, get_col_idx = True)
2457
2458 return [ cPerformedProcedure(row = {'idx': idx, 'data': r, 'pk_field': 'pk_procedure'}) for r in rows ]
2459 #-----------------------------------------------------------
2460 -def create_performed_procedure(encounter=None, episode=None, location=None, hospital_stay=None, procedure=None):
2461
2462 queries = [{
2463 'cmd': u"""
2464 INSERT INTO clin.procedure (
2465 fk_encounter,
2466 fk_episode,
2467 soap_cat,
2468 clin_where,
2469 fk_hospital_stay,
2470 narrative
2471 ) VALUES (
2472 %(enc)s,
2473 %(epi)s,
2474 'p',
2475 gm.nullify_empty_string(%(loc)s),
2476 %(stay)s,
2477 gm.nullify_empty_string(%(proc)s)
2478 )
2479 RETURNING pk""",
2480 'args': {'enc': encounter, 'epi': episode, 'loc': location, 'stay': hospital_stay, 'proc': procedure}
2481 }]
2482
2483 rows, idx = gmPG2.run_rw_queries(queries = queries, return_data = True)
2484
2485 return cPerformedProcedure(aPK_obj = rows[0][0])
2486 #-----------------------------------------------------------
2488 cmd = u'delete from clin.procedure where pk = %(pk)s'
2489 args = {'pk': procedure}
2490 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
2491 return True
2492 #============================================================
2493 # main - unit testing
2494 #------------------------------------------------------------
2495 if __name__ == '__main__':
2496
2497 if len(sys.argv) < 2:
2498 sys.exit()
2499
2500 if sys.argv[1] != 'test':
2501 sys.exit()
2502
2503 #--------------------------------------------------------
2504 # define tests
2505 #--------------------------------------------------------
2507 print "\nProblem test"
2508 print "------------"
2509 prob = cProblem(aPK_obj={'pk_patient': 12, 'pk_health_issue': 1, 'pk_episode': None})
2510 print prob
2511 fields = prob.get_fields()
2512 for field in fields:
2513 print field, ':', prob[field]
2514 print '\nupdatable:', prob.get_updatable_fields()
2515 epi = prob.get_as_episode()
2516 print '\nas episode:'
2517 if epi is not None:
2518 for field in epi.get_fields():
2519 print ' .%s : %s' % (field, epi[field])
2520 #--------------------------------------------------------
2522 print "\nhealth issue test"
2523 print "-----------------"
2524 h_issue = cHealthIssue(aPK_obj=2)
2525 print h_issue
2526 fields = h_issue.get_fields()
2527 for field in fields:
2528 print field, ':', h_issue[field]
2529 print "has open episode:", h_issue.has_open_episode()
2530 print "open episode:", h_issue.get_open_episode()
2531 print "updateable:", h_issue.get_updatable_fields()
2532 h_issue.close_expired_episode(ttl=7300)
2533 h_issue = cHealthIssue(encounter = 1, name = u'post appendectomy/peritonitis')
2534 print h_issue
2535 print h_issue.format_as_journal()
2536 #--------------------------------------------------------
2538 print "\nepisode test"
2539 print "------------"
2540 episode = cEpisode(aPK_obj=1)
2541 print episode
2542 fields = episode.get_fields()
2543 for field in fields:
2544 print field, ':', episode[field]
2545 print "updatable:", episode.get_updatable_fields()
2546 raw_input('ENTER to continue')
2547
2548 old_description = episode['description']
2549 old_enc = cEncounter(aPK_obj = 1)
2550
2551 desc = '1-%s' % episode['description']
2552 print "==> renaming to", desc
2553 successful = episode.rename (
2554 description = desc
2555 )
2556 if not successful:
2557 print "error"
2558 else:
2559 print "success"
2560 for field in fields:
2561 print field, ':', episode[field]
2562
2563 print "episode range:", episode.get_access_range()
2564
2565 raw_input('ENTER to continue')
2566
2567 #--------------------------------------------------------
2569 print "\nencounter test"
2570 print "--------------"
2571 encounter = cEncounter(aPK_obj=1)
2572 print encounter
2573 fields = encounter.get_fields()
2574 for field in fields:
2575 print field, ':', encounter[field]
2576 print "updatable:", encounter.get_updatable_fields()
2577 #--------------------------------------------------------
2579 encounter = cEncounter(aPK_obj=1)
2580 print encounter
2581 print ""
2582 print encounter.format_latex()
2583 #--------------------------------------------------------
2585 procs = get_performed_procedures(patient = 12)
2586 for proc in procs:
2587 print proc.format(left_margin=2)
2588 #--------------------------------------------------------
2590 stay = create_hospital_stay(encounter = 1, episode = 2)
2591 stay['hospital'] = u'Starfleet Galaxy General Hospital'
2592 stay.save_payload()
2593 print stay
2594 for s in get_patient_hospital_stays(12):
2595 print s
2596 delete_hospital_stay(stay['pk_hospital_stay'])
2597 stay = create_hospital_stay(encounter = 1, episode = 4)
2598 #--------------------------------------------------------
2600 tests = [None, 'A', 'B', 'C', 'D', 'E']
2601
2602 for t in tests:
2603 print type(t), t
2604 print type(diagnostic_certainty_classification2str(t)), diagnostic_certainty_classification2str(t)
2605 #--------------------------------------------------------
2610 #--------------------------------------------------------
2611 # run them
2612 #test_episode()
2613 #test_problem()
2614 #test_encounter()
2615 #test_health_issue()
2616 #test_hospital_stay()
2617 #test_performed_procedure()
2618 #test_diagnostic_certainty_classification_map()
2619 #test_encounter2latex()
2620 test_episode_codes()
2621 #============================================================
2622
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Tue Oct 18 04:00:33 2011 | http://epydoc.sourceforge.net |