| Home | Trees | Indices | Help |
|
|---|
|
|
1 # -*- coding: utf8 -*-
2 """GNUmed health related business object.
3
4 license: GPL
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, gmExceptions, gmNull, gmBusinessDBObject, gmDateTime, gmTools, gmI18N
16 from Gnumed.business import gmClinNarrative
17
18
19 _log = logging.getLogger('gm.emr')
20 _log.info(__version__)
21
22 try: _
23 except NameError: _ = lambda x:x
24 #============================================================
25 # diagnostic certainty classification
26 #============================================================
27 __diagnostic_certainty_classification_map = None
28
30
31 global __diagnostic_certainty_classification_map
32
33 if __diagnostic_certainty_classification_map is None:
34 __diagnostic_certainty_classification_map = {
35 None: u'',
36 u'A': _(u'A: Sign'),
37 u'B': _(u'B: Cluster of signs'),
38 u'C': _(u'C: Syndromic diagnosis'),
39 u'D': _(u'D: Scientific diagnosis')
40 }
41
42 try:
43 return __diagnostic_certainty_classification_map[classification]
44 except KeyError:
45 return _(u'%s: unknown diagnostic certainty classification') % classification
46 #============================================================
47 # Health Issues API
48 #============================================================
49 laterality2str = {
50 None: u'?',
51 u'na': u'',
52 u'sd': _('bilateral'),
53 u'ds': _('bilateral'),
54 u's': _('left'),
55 u'd': _('right')
56 }
57
58 #============================================================
60 """Represents one health issue."""
61
62 _cmd_fetch_payload = u"select *, xmin_health_issue from clin.v_health_issues where pk_health_issue=%s"
63 _cmds_store_payload = [
64 u"""update clin.health_issue set
65 description = %(description)s,
66 age_noted = %(age_noted)s,
67 laterality = gm.nullify_empty_string(%(laterality)s),
68 grouping = gm.nullify_empty_string(%(grouping)s),
69 diagnostic_certainty_classification = gm.nullify_empty_string(%(diagnostic_certainty_classification)s),
70 is_active = %(is_active)s,
71 clinically_relevant = %(clinically_relevant)s,
72 is_confidential = %(is_confidential)s,
73 is_cause_of_death = %(is_cause_of_death)s
74 where
75 pk = %(pk_health_issue)s and
76 xmin = %(xmin_health_issue)s""",
77 u"select xmin as xmin_health_issue from clin.health_issue where pk = %(pk_health_issue)s"
78 ]
79 _updatable_fields = [
80 'description',
81 'grouping',
82 'age_noted',
83 'laterality',
84 'is_active',
85 'clinically_relevant',
86 'is_confidential',
87 'is_cause_of_death',
88 'diagnostic_certainty_classification'
89 ]
90 #--------------------------------------------------------
91 - def __init__(self, aPK_obj=None, encounter=None, name='xxxDEFAULTxxx', patient=None, row=None):
92 pk = aPK_obj
93
94 if (pk is not None) or (row is not None):
95 gmBusinessDBObject.cBusinessDBObject.__init__(self, aPK_obj=pk, row=row)
96 return
97
98 if patient is None:
99 cmd = u"""select *, xmin_health_issue from clin.v_health_issues
100 where
101 description = %(desc)s
102 and
103 pk_patient = (select fk_patient from clin.encounter where pk = %(enc)s)"""
104 else:
105 cmd = u"""select *, xmin_health_issue from clin.v_health_issues
106 where
107 description = %(desc)s
108 and
109 pk_patient = %(pat)s"""
110
111 queries = [{'cmd': cmd, 'args': {'enc': encounter, 'desc': name, 'pat': patient}}]
112 rows, idx = gmPG2.run_ro_queries(queries = queries, get_col_idx = True)
113
114 if len(rows) == 0:
115 raise gmExceptions.NoSuchBusinessObjectError, 'no health issue for [enc:%s::desc:%s::pat:%s]' % (encounter, name, patient)
116
117 pk = rows[0][0]
118 r = {'idx': idx, 'data': rows[0], 'pk_field': 'pk_health_issue'}
119
120 gmBusinessDBObject.cBusinessDBObject.__init__(self, row=r)
121 #--------------------------------------------------------
123 """Method for issue renaming.
124
125 @param description
126 - the new descriptive name for the issue
127 @type description
128 - a string instance
129 """
130 # sanity check
131 if not type(description) in [str, unicode] or description.strip() == '':
132 _log.error('<description> must be a non-empty string')
133 return False
134 # update the issue description
135 old_description = self._payload[self._idx['description']]
136 self._payload[self._idx['description']] = description.strip()
137 self._is_modified = True
138 successful, data = self.save_payload()
139 if not successful:
140 _log.error('cannot rename health issue [%s] with [%s]' % (self, description))
141 self._payload[self._idx['description']] = old_description
142 return False
143 return True
144 #--------------------------------------------------------
146 cmd = u"select * from clin.v_pat_episodes where pk_health_issue = %(pk)s"
147 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': {'pk': self.pk_obj}}], get_col_idx = True)
148 return [ cEpisode(row = {'data': r, 'idx': idx, 'pk_field': 'pk_episode'}) for r in rows ]
149 #--------------------------------------------------------
151 """ttl in days"""
152 open_episode = self.get_open_episode()
153 if open_episode is None:
154 return True
155 earliest, latest = open_episode.get_access_range()
156 ttl = datetime.timedelta(ttl)
157 now = datetime.datetime.now(tz=latest.tzinfo)
158 if (latest + ttl) > now:
159 return False
160 open_episode['episode_open'] = False
161 success, data = open_episode.save_payload()
162 if success:
163 return True
164 return False # should be an exception
165 #--------------------------------------------------------
167 open_episode = self.get_open_episode()
168 open_episode['episode_open'] = False
169 success, data = open_episode.save_payload()
170 if success:
171 return True
172 return False
173 #--------------------------------------------------------
175 cmd = u"select exists (select 1 from clin.episode where fk_health_issue = %s and is_open is True)"
176 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj]}])
177 return rows[0][0]
178 #--------------------------------------------------------
180 cmd = u"select pk from clin.episode where fk_health_issue = %s and is_open is True"
181 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj]}])
182 if len(rows) == 0:
183 return None
184 return cEpisode(aPK_obj=rows[0][0])
185 #--------------------------------------------------------
187 if self._payload[self._idx['age_noted']] is None:
188 return u'<???>'
189
190 # since we've already got an interval we are bound to use it,
191 # further transformation will only introduce more errors,
192 # later we can improve this deeper inside
193 return gmDateTime.format_interval_medically(self._payload[self._idx['age_noted']])
194 #--------------------------------------------------------
196 try:
197 return laterality2str[self._payload[self._idx['laterality']]]
198 except KeyError:
199 return u'<???>'
200
201 laterality_description = property(_get_laterality_description, lambda x:x)
202 #--------------------------------------------------------
204 return diagnostic_certainty_classification2str(self._payload[self._idx['diagnostic_certainty_classification']])
205
206 diagnostic_certainty_description = property(_get_diagnostic_certainty_description, lambda x:x)
207 #--------------------------------------------------------
209
210 if patient.ID != self._payload[self._idx['pk_patient']]:
211 msg = '<patient>.ID = %s but health issue %s belongs to patient %s' % (
212 patient.ID,
213 self._payload[self._idx['pk_health_issue']],
214 self._payload[self._idx['pk_patient']]
215 )
216 raise ValueError(msg)
217
218 lines = []
219
220 lines.append(_('Health Issue %s%s%s%s [#%s]') % (
221 u'\u00BB',
222 self._payload[self._idx['description']],
223 u'\u00AB',
224 gmTools.coalesce (
225 initial = self.laterality_description,
226 instead = u'',
227 template_initial = u' (%s)',
228 none_equivalents = [None, u'', u'?']
229 ),
230 self._payload[self._idx['pk_health_issue']]
231 ))
232
233 if self._payload[self._idx['is_confidential']]:
234 lines.append('')
235 lines.append(_(' ***** CONFIDENTIAL *****'))
236 lines.append('')
237
238 if self._payload[self._idx['is_cause_of_death']]:
239 lines.append('')
240 lines.append(_(' contributed to death of patient'))
241 lines.append('')
242
243 enc = cEncounter(aPK_obj = self._payload[self._idx['pk_encounter']])
244 lines.append (_(' Created during encounter: %s (%s - %s) [#%s]') % (
245 enc['l10n_type'],
246 enc['started_original_tz'].strftime('%Y-%m-%d %H:%M'),
247 enc['last_affirmed_original_tz'].strftime('%H:%M'),
248 self._payload[self._idx['pk_encounter']]
249 ))
250
251 if self._payload[self._idx['age_noted']] is not None:
252 lines.append(_(' Noted at age: %s') % self.age_noted_human_readable())
253
254 lines.append(_(' Status: %s, %s%s') % (
255 gmTools.bool2subst(self._payload[self._idx['is_active']], _('active'), _('inactive')),
256 gmTools.bool2subst(self._payload[self._idx['clinically_relevant']], _('clinically relevant'), _('not clinically relevant')),
257 gmTools.coalesce (
258 initial = diagnostic_certainty_classification2str(self._payload[self._idx['diagnostic_certainty_classification']]),
259 instead = u'',
260 template_initial = u', %s',
261 none_equivalents = [None, u'']
262 ),
263 ))
264 lines.append('')
265
266 emr = patient.get_emr()
267
268 # episodes
269 epis = emr.get_episodes(issues = [self._payload[self._idx['pk_health_issue']]])
270 if epis is None:
271 lines.append(_('Error retrieving episodes for this health issue.'))
272 elif len(epis) == 0:
273 lines.append(_('There are no episodes for this health issue.'))
274 else:
275 lines.append (
276 _('Episodes: %s (most recent: %s%s%s)') % (
277 len(epis),
278 gmTools.u_left_double_angle_quote,
279 emr.get_most_recent_episode(issue = self._payload[self._idx['pk_health_issue']])['description'],
280 gmTools.u_right_double_angle_quote
281 )
282 )
283 lines.append('')
284 for epi in epis:
285 lines.append(u' \u00BB%s\u00AB (%s)' % (
286 epi['description'],
287 gmTools.bool2subst(epi['episode_open'], _('ongoing'), _('closed'))
288 ))
289
290 lines.append('')
291
292 # encounters
293 first_encounter = emr.get_first_encounter(issue_id = self._payload[self._idx['pk_health_issue']])
294 last_encounter = emr.get_last_encounter(issue_id = self._payload[self._idx['pk_health_issue']])
295
296 if first_encounter is None or last_encounter is None:
297 lines.append(_('No encounters found for this health issue.'))
298 else:
299 encs = emr.get_encounters(issues = [self._payload[self._idx['pk_health_issue']]])
300 lines.append(_('Encounters: %s (%s - %s):') % (
301 len(encs),
302 first_encounter['started_original_tz'].strftime('%m/%Y'),
303 last_encounter['last_affirmed_original_tz'].strftime('%m/%Y')
304 ))
305 lines.append(_(' Most recent: %s - %s') % (
306 last_encounter['started_original_tz'].strftime('%Y-%m-%d %H:%M'),
307 last_encounter['last_affirmed_original_tz'].strftime('%H:%M')
308 ))
309
310 # medications
311 meds = emr.get_current_substance_intake (
312 issues = [ self._payload[self._idx['pk_health_issue']] ],
313 order_by = u'is_currently_active, started, substance'
314 )
315
316 if len(meds) > 0:
317 lines.append(u'')
318 lines.append(_('Active medications: %s') % len(meds))
319 for m in meds:
320 lines.append(m.format(left_margin = (left_margin + 1)))
321 del meds
322
323 # hospital stays
324 stays = emr.get_hospital_stays (
325 issues = [ self._payload[self._idx['pk_health_issue']] ]
326 )
327
328 if len(stays) > 0:
329 lines.append(u'')
330 lines.append(_('Hospital stays: %s') % len(stays))
331 for s in stays:
332 lines.append(s.format(left_margin = (left_margin + 1)))
333 del stays
334
335 # procedures
336 procs = emr.get_performed_procedures (
337 issues = [ self._payload[self._idx['pk_health_issue']] ]
338 )
339
340 if len(procs) > 0:
341 lines.append(u'')
342 lines.append(_('Procedures performed: %s') % len(procs))
343 for p in procs:
344 lines.append(p.format(left_margin = (left_margin + 1)))
345 del procs
346
347 epis = self.get_episodes()
348 if len(epis) > 0:
349 epi_pks = [ e['pk_episode'] for e in epis ]
350
351 # documents
352 doc_folder = patient.get_document_folder()
353 docs = doc_folder.get_documents(episodes = epi_pks)
354 if len(docs) > 0:
355 lines.append(u'')
356 lines.append(_('Documents: %s') % len(docs))
357 del docs
358
359 # test results
360 tests = emr.get_test_results_by_date(episodes = epi_pks)
361 if len(tests) > 0:
362 lines.append(u'')
363 lines.append(_('Measurements and Results: %s') % len(tests))
364 del tests
365
366 # vaccinations
367 vaccs = emr.get_vaccinations(episodes = epi_pks)
368 if len(vaccs) > 0:
369 lines.append(u'')
370 lines.append(_('Vaccinations:'))
371 for vacc in vaccs:
372 lines.extend(vacc.format(with_reaction = True))
373 del vaccs
374
375 del epis
376
377 left_margin = u' ' * left_margin
378 eol_w_margin = u'\n%s' % left_margin
379 return left_margin + eol_w_margin.join(lines) + u'\n'
380 #============================================================
382 """Creates a new health issue for a given patient.
383
384 description - health issue name
385 """
386 try:
387 h_issue = cHealthIssue(name = description, encounter = encounter, patient = patient)
388 return h_issue
389 except gmExceptions.NoSuchBusinessObjectError:
390 pass
391
392 queries = []
393 cmd = u"insert into clin.health_issue (description, fk_encounter) values (%(desc)s, %(enc)s)"
394 queries.append({'cmd': cmd, 'args': {'desc': description, 'enc': encounter}})
395
396 cmd = u"select currval('clin.health_issue_pk_seq')"
397 queries.append({'cmd': cmd})
398
399 rows, idx = gmPG2.run_rw_queries(queries = queries, return_data = True)
400 h_issue = cHealthIssue(aPK_obj = rows[0][0])
401
402 return h_issue
403 #-----------------------------------------------------------
405 if isinstance(health_issue, cHealthIssue):
406 pk = health_issue['pk_health_issue']
407 else:
408 pk = int(health_issue)
409
410 try:
411 gmPG2.run_rw_queries(queries = [{'cmd': u'delete from clin.health_issue where pk=%(pk)s', 'args': {'pk': pk}}])
412 except gmPG2.dbapi.IntegrityError:
413 # should be parsing pgcode/and or error message
414 _log.exception('cannot delete health issue')
415 raise gmExceptions.DatabaseObjectInUseError('cannot delete health issue, it is in use')
416 #------------------------------------------------------------
417 # use as dummy for unassociated episodes
419 issue = {
420 'pk_health_issue': None,
421 'description': _('Unattributed episodes'),
422 'age_noted': None,
423 'laterality': u'na',
424 'is_active': True,
425 'clinically_relevant': True,
426 'is_confidential': None,
427 'is_cause_of_death': False,
428 'is_dummy': True
429 }
430 return issue
431 #-----------------------------------------------------------
433 return cProblem (
434 aPK_obj = {
435 'pk_patient': health_issue['pk_patient'],
436 'pk_health_issue': health_issue['pk_health_issue'],
437 'pk_episode': None
438 },
439 try_potential_problems = allow_irrelevant
440 )
441 #============================================================
442 # episodes API
443 #============================================================
445 """Represents one clinical episode.
446 """
447 _cmd_fetch_payload = u"select * from clin.v_pat_episodes where pk_episode=%s"
448 _cmds_store_payload = [
449 u"""update clin.episode set
450 fk_health_issue = %(pk_health_issue)s,
451 is_open = %(episode_open)s::boolean,
452 description = %(description)s,
453 diagnostic_certainty_classification = gm.nullify_empty_string(%(diagnostic_certainty_classification)s)
454 where
455 pk = %(pk_episode)s and
456 xmin = %(xmin_episode)s""",
457 u"""select xmin_episode from clin.v_pat_episodes where pk_episode = %(pk_episode)s"""
458 ]
459 _updatable_fields = [
460 'pk_health_issue',
461 'episode_open',
462 'description',
463 'diagnostic_certainty_classification'
464 ]
465 #--------------------------------------------------------
466 - def __init__(self, aPK_obj=None, id_patient=None, name='xxxDEFAULTxxx', health_issue=None, row=None, encounter=None):
467 pk = aPK_obj
468 if pk is None and row is None:
469
470 where_parts = [u'description = %(desc)s']
471
472 if id_patient is not None:
473 where_parts.append(u'pk_patient = %(pat)s')
474
475 if health_issue is not None:
476 where_parts.append(u'pk_health_issue = %(issue)s')
477
478 if encounter is not None:
479 where_parts.append(u'pk_patient = (select fk_patient from clin.encounter where pk = %(enc)s)')
480
481 args = {
482 'pat': id_patient,
483 'issue': health_issue,
484 'enc': encounter,
485 'desc': name
486 }
487
488 cmd = u"select * from clin.v_pat_episodes where %s" % u' and '.join(where_parts)
489
490 rows, idx = gmPG2.run_ro_queries(
491 queries = [{'cmd': cmd, 'args': args}],
492 get_col_idx=True
493 )
494
495 if len(rows) == 0:
496 raise gmExceptions.NoSuchBusinessObjectError, 'no episode for [%s:%s:%s:%s]' % (id_patient, name, health_issue, encounter)
497
498 r = {'idx': idx, 'data': rows[0], 'pk_field': 'pk_episode'}
499 gmBusinessDBObject.cBusinessDBObject.__init__(self, row=r)
500
501 else:
502 gmBusinessDBObject.cBusinessDBObject.__init__(self, aPK_obj=pk, row=row)
503 #--------------------------------------------------------
505 """Get earliest and latest access to this episode.
506
507 Returns a tuple(earliest, latest).
508 """
509 cmd = u"""
510 select
511 min(earliest),
512 max(latest)
513 from (
514 (select
515 (case when clin_when < modified_when
516 then clin_when
517 else modified_when
518 end) as earliest,
519 (case when clin_when > modified_when
520 then clin_when
521 else modified_when
522 end) as latest
523 from
524 clin.clin_root_item
525 where
526 fk_episode = %(pk)s
527
528 ) union all (
529
530 select
531 modified_when as earliest,
532 modified_when as latest
533 from
534 clin.episode
535 where
536 pk = %(pk)s
537 )
538 ) as ranges"""
539 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': {'pk': self.pk_obj}}])
540 if len(rows) == 0:
541 return (gmNull.cNull(warn=False), gmNull.cNull(warn=False))
542 return (rows[0][0], rows[0][1])
543 #--------------------------------------------------------
546 #--------------------------------------------------------
548 """Method for episode editing, that is, episode renaming.
549
550 @param description
551 - the new descriptive name for the encounter
552 @type description
553 - a string instance
554 """
555 # sanity check
556 if description.strip() == '':
557 _log.error('<description> must be a non-empty string instance')
558 return False
559 # update the episode description
560 old_description = self._payload[self._idx['description']]
561 self._payload[self._idx['description']] = description.strip()
562 self._is_modified = True
563 successful, data = self.save_payload()
564 if not successful:
565 _log.error('cannot rename episode [%s] to [%s]' % (self, description))
566 self._payload[self._idx['description']] = old_description
567 return False
568 return True
569 #--------------------------------------------------------
571 return diagnostic_certainty_classification2str(self._payload[self._idx['diagnostic_certainty_classification']])
572
573 diagnostic_certainty_description = property(_get_diagnostic_certainty_description, lambda x:x)
574 #--------------------------------------------------------
576
577 if patient.ID != self._payload[self._idx['pk_patient']]:
578 msg = '<patient>.ID = %s but episode %s belongs to patient %s' % (
579 patient.ID,
580 self._payload[self._idx['pk_episode']],
581 self._payload[self._idx['pk_patient']]
582 )
583 raise ValueError(msg)
584
585 lines = []
586
587 # episode details
588 lines.append (_('Episode %s%s%s (%s%s) [#%s]\n') % (
589 gmTools.u_left_double_angle_quote,
590 self._payload[self._idx['description']],
591 gmTools.u_right_double_angle_quote,
592 gmTools.coalesce (
593 initial = diagnostic_certainty_classification2str(self._payload[self._idx['diagnostic_certainty_classification']]),
594 instead = u'',
595 template_initial = u'%s, ',
596 none_equivalents = [None, u'']
597 ),
598 gmTools.bool2subst(self._payload[self._idx['episode_open']], _('active'), _('finished')),
599 self._payload[self._idx['pk_episode']]
600 ))
601
602 enc = cEncounter(aPK_obj = self._payload[self._idx['pk_encounter']])
603 lines.append (_('Created during encounter: %s (%s - %s) [#%s]\n') % (
604 enc['l10n_type'],
605 enc['started_original_tz'].strftime('%Y-%m-%d %H:%M'),
606 enc['last_affirmed_original_tz'].strftime('%H:%M'),
607 self._payload[self._idx['pk_encounter']]
608 ))
609
610 # encounters
611 emr = patient.get_emr()
612 encs = emr.get_encounters(episodes = [self._payload[self._idx['pk_episode']]])
613 first_encounter = None
614 last_encounter = None
615 if encs is None:
616 lines.append(_('Error retrieving encounters for this episode.'))
617 elif len(encs) == 0:
618 lines.append(_('There are no encounters for this episode.'))
619 else:
620 first_encounter = emr.get_first_encounter(episode_id = self._payload[self._idx['pk_episode']])
621 last_encounter = emr.get_last_encounter(episode_id = self._payload[self._idx['pk_episode']])
622
623 lines.append(_('Last worked on: %s\n') % last_encounter['last_affirmed_original_tz'].strftime('%Y-%m-%d %H:%M'))
624
625 lines.append(_('1st and (up to 3) most recent (of %s) encounters (%s - %s):') % (
626 len(encs),
627 first_encounter['started'].strftime('%m/%Y'),
628 last_encounter['last_affirmed'].strftime('%m/%Y')
629 ))
630
631 lines.append(u' %s - %s (%s):%s' % (
632 first_encounter['started_original_tz'].strftime('%Y-%m-%d %H:%M'),
633 first_encounter['last_affirmed_original_tz'].strftime('%H:%M'),
634 first_encounter['l10n_type'],
635 gmTools.coalesce (
636 first_encounter['assessment_of_encounter'],
637 gmTools.coalesce (
638 first_encounter['reason_for_encounter'],
639 u'',
640 u' \u00BB%s\u00AB' + (u' (%s)' % _('RFE'))
641 ),
642 u' \u00BB%s\u00AB' + (u' (%s)' % _('AOE'))
643 )
644 ))
645
646 if len(encs) > 4:
647 lines.append(_(' ... %s skipped ...') % (len(encs) - 4))
648
649 for enc in encs[1:][-3:]:
650 lines.append(u' %s - %s (%s):%s' % (
651 enc['started_original_tz'].strftime('%Y-%m-%d %H:%M'),
652 enc['last_affirmed_original_tz'].strftime('%H:%M'),
653 enc['l10n_type'],
654 gmTools.coalesce (
655 enc['assessment_of_encounter'],
656 gmTools.coalesce (
657 enc['reason_for_encounter'],
658 u'',
659 u' \u00BB%s\u00AB' + (u' (%s)' % _('RFE'))
660 ),
661 u' \u00BB%s\u00AB' + (u' (%s)' % _('AOE'))
662 )
663 ))
664 del encs
665
666 # spell out last encounter
667 if last_encounter is not None:
668 lines.append('')
669 lines.append(_('Progress notes in most recent encounter:'))
670 lines.extend(last_encounter.format_soap (
671 episodes = [ self._payload[self._idx['pk_episode']] ],
672 left_margin = left_margin,
673 soap_cats = 'soap',
674 emr = emr
675 ))
676
677 # documents
678 doc_folder = patient.get_document_folder()
679 docs = doc_folder.get_documents (
680 episodes = [ self._payload[self._idx['pk_episode']] ]
681 )
682
683 if len(docs) > 0:
684 lines.append('')
685 lines.append(_('Documents: %s') % len(docs))
686
687 for d in docs:
688 lines.append(u' %s %s:%s%s' % (
689 d['clin_when'].strftime('%Y-%m-%d'),
690 d['l10n_type'],
691 gmTools.coalesce(d['comment'], u'', u' "%s"'),
692 gmTools.coalesce(d['ext_ref'], u'', u' (%s)')
693 ))
694 del docs
695
696 # hospital stays
697 stays = emr.get_hospital_stays (
698 episodes = [ self._payload[self._idx['pk_episode']] ]
699 )
700
701 if len(stays) > 0:
702 lines.append('')
703 lines.append(_('Hospital stays: %s') % len(stays))
704
705 for s in stays:
706 lines.append(s.format(left_margin = (left_margin + 1)))
707 del stays
708
709 # procedures
710 procs = emr.get_performed_procedures (
711 episodes = [ self._payload[self._idx['pk_episode']] ]
712 )
713
714 if len(procs) > 0:
715 lines.append(u'')
716 lines.append(_('Procedures performed: %s') % len(procs))
717 for p in procs:
718 lines.append(p.format(left_margin = (left_margin + 1), include_episode = False))
719 del procs
720
721 # test results
722 tests = emr.get_test_results_by_date(episodes = [ self._payload[self._idx['pk_episode']] ])
723
724 if len(tests) > 0:
725 lines.append('')
726 lines.append(_('Measurements and Results:'))
727
728 for t in tests:
729 lines.extend(t.format (
730 with_review = False,
731 with_comments = False,
732 date_format = '%Y-%m-%d'
733 ))
734 del tests
735
736 # vaccinations
737 vaccs = emr.get_vaccinations(episodes = [ self._payload[self._idx['pk_episode']] ])
738
739 if len(vaccs) > 0:
740 lines.append(u'')
741 lines.append(_('Vaccinations:'))
742
743 for vacc in vaccs:
744 lines.extend(vacc.format (
745 with_indications = True,
746 with_comment = True,
747 with_reaction = True,
748 date_format = '%Y-%m-%d'
749 ))
750 del vaccs
751
752 left_margin = u' ' * left_margin
753 eol_w_margin = u'\n%s' % left_margin
754 return left_margin + eol_w_margin.join(lines) + u'\n'
755 #============================================================
756 -def create_episode(pk_health_issue=None, episode_name=None, is_open=False, allow_dupes=False, encounter=None):
757 """Creates a new episode for a given patient's health issue.
758
759 pk_health_issue - given health issue PK
760 episode_name - name of episode
761 """
762 if not allow_dupes:
763 try:
764 episode = cEpisode(name=episode_name, health_issue=pk_health_issue, encounter = encounter)
765 if episode['episode_open'] != is_open:
766 episode['episode_open'] = is_open
767 episode.save_payload()
768 return episode
769 except gmExceptions.ConstructorError:
770 pass
771
772 queries = []
773 cmd = u"insert into clin.episode (fk_health_issue, description, is_open, fk_encounter) values (%s, %s, %s::boolean, %s)"
774 queries.append({'cmd': cmd, 'args': [pk_health_issue, episode_name, is_open, encounter]})
775 queries.append({'cmd': cEpisode._cmd_fetch_payload % u"currval('clin.episode_pk_seq')"})
776 rows, idx = gmPG2.run_rw_queries(queries = queries, return_data=True, get_col_idx=True)
777
778 episode = cEpisode(row={'data': rows[0], 'idx': idx, 'pk_field': 'pk_episode'})
779 return episode
780 #-----------------------------------------------------------
782 if isinstance(episode, cEpisode):
783 pk = episode['pk_episode']
784 else:
785 pk = int(episode)
786
787 try:
788 gmPG2.run_rw_queries(queries = [{'cmd': u'delete from clin.episode where pk=%(pk)s', 'args': {'pk': pk}}])
789 except gmPG2.dbapi.IntegrityError:
790 # should be parsing pgcode/and or error message
791 _log.exception('cannot delete episode')
792 raise gmExceptions.DatabaseObjectInUseError('cannot delete episode, it is in use')
793 #-----------------------------------------------------------
795 return cProblem (
796 aPK_obj = {
797 'pk_patient': episode['pk_patient'],
798 'pk_episode': episode['pk_episode'],
799 'pk_health_issue': episode['pk_health_issue']
800 },
801 try_potential_problems = allow_closed
802 )
803 #============================================================
804 # encounter API
805 #============================================================
807 """Represents one encounter."""
808 _cmd_fetch_payload = u"select * from clin.v_pat_encounters where pk_encounter = %s"
809 _cmds_store_payload = [
810 u"""update clin.encounter set
811 started = %(started)s,
812 last_affirmed = %(last_affirmed)s,
813 fk_location = %(pk_location)s,
814 fk_type = %(pk_type)s,
815 reason_for_encounter = gm.nullify_empty_string(%(reason_for_encounter)s),
816 assessment_of_encounter = gm.nullify_empty_string(%(assessment_of_encounter)s)
817 where
818 pk = %(pk_encounter)s and
819 xmin = %(xmin_encounter)s""",
820 u"""select xmin_encounter from clin.v_pat_encounters where pk_encounter=%(pk_encounter)s"""
821 ]
822 _updatable_fields = [
823 'started',
824 'last_affirmed',
825 'pk_location',
826 'pk_type',
827 'reason_for_encounter',
828 'assessment_of_encounter'
829 ]
830 #--------------------------------------------------------
832 """Set the enconter as the active one.
833
834 "Setting active" means making sure the encounter
835 row has the youngest "last_affirmed" timestamp of
836 all encounter rows for this patient.
837 """
838 self['last_affirmed'] = gmDateTime.pydt_now_here()
839 self.save()
840 #--------------------------------------------------------
842 """
843 Moves every element currently linked to the current encounter
844 and the source_episode onto target_episode.
845
846 @param source_episode The episode the elements are currently linked to.
847 @type target_episode A cEpisode intance.
848 @param target_episode The episode the elements will be relinked to.
849 @type target_episode A cEpisode intance.
850 """
851 if source_episode['pk_episode'] == target_episode['pk_episode']:
852 return True
853
854 queries = []
855 cmd = u"""
856 UPDATE clin.clin_root_item
857 SET fk_episode = %(trg)s
858 WHERE
859 fk_encounter = %(enc)s AND
860 fk_episode = %(src)s
861 """
862 rows, idx = gmPG2.run_rw_queries(queries = [{
863 'cmd': cmd,
864 'args': {
865 'trg': target_episode['pk_episode'],
866 'enc': self.pk_obj,
867 'src': source_episode['pk_episode']
868 }
869 }])
870 self.refetch_payload()
871 return True
872 #--------------------------------------------------------
874
875 relevant_fields = [
876 'pk_location',
877 'pk_type',
878 'pk_patient',
879 'reason_for_encounter',
880 'assessment_of_encounter'
881 ]
882 for field in relevant_fields:
883 if self._payload[self._idx[field]] != another_object[field]:
884 _log.debug('mismatch on [%s]: "%s" vs. "%s"', field, self._payload[self._idx[field]], another_object[field])
885 return False
886
887 relevant_fields = [
888 'started',
889 'last_affirmed',
890 ]
891 for field in relevant_fields:
892 if self._payload[self._idx[field]] is None:
893 if another_object[field] is None:
894 continue
895 _log.debug('mismatch on [%s]: "%s" vs. "%s"', field, self._payload[self._idx[field]], another_object[field])
896 return False
897
898 if another_object[field] is None:
899 return False
900
901 #if self._payload[self._idx[field]].strftime('%Y-%m-%d %H:%M:%S %Z') != another_object[field].strftime('%Y-%m-%d %H:%M:%S %Z'):
902 if self._payload[self._idx[field]].strftime('%Y-%m-%d %H:%M') != another_object[field].strftime('%Y-%m-%d %H:%M'):
903 _log.debug('mismatch on [%s]: "%s" vs. "%s"', field, self._payload[self._idx[field]], another_object[field])
904 return False
905
906 return True
907 #--------------------------------------------------------
909 cmd = u"""
910 select exists (
911 select 1 from clin.v_pat_items where pk_patient = %(pat)s and pk_encounter = %(enc)s
912 union all
913 select 1 from blobs.v_doc_med where pk_patient = %(pat)s and pk_encounter = %(enc)s
914 )"""
915 args = {
916 'pat': self._payload[self._idx['pk_patient']],
917 'enc': self.pk_obj
918 }
919 rows, idx = gmPG2.run_ro_queries (
920 queries = [{
921 'cmd': cmd,
922 'args': args
923 }]
924 )
925 return rows[0][0]
926 #--------------------------------------------------------
928 cmd = u"""
929 select exists (
930 select 1 from clin.v_pat_items where pk_patient=%(pat)s and pk_encounter=%(enc)s
931 )"""
932 args = {
933 'pat': self._payload[self._idx['pk_patient']],
934 'enc': self.pk_obj
935 }
936 rows, idx = gmPG2.run_ro_queries (
937 queries = [{
938 'cmd': cmd,
939 'args': args
940 }]
941 )
942 return rows[0][0]
943 #--------------------------------------------------------
945 cmd = u"""
946 select exists (
947 select 1 from blobs.v_doc_med where pk_patient = %(pat)s and pk_encounter = %(enc)s
948 )"""
949 args = {
950 'pat': self._payload[self._idx['pk_patient']],
951 'enc': self.pk_obj
952 }
953 rows, idx = gmPG2.run_ro_queries (
954 queries = [{
955 'cmd': cmd,
956 'args': args
957 }]
958 )
959 return rows[0][0]
960 #--------------------------------------------------------
962
963 if soap_cat is not None:
964 soap_cat = soap_cat.lower()
965
966 if episode is None:
967 epi_part = u'fk_episode is null'
968 else:
969 epi_part = u'fk_episode = %(epi)s'
970
971 cmd = u"""
972 select narrative
973 from clin.clin_narrative
974 where
975 fk_encounter = %%(enc)s
976 and
977 soap_cat = %%(cat)s
978 and
979 %s
980 order by clin_when desc
981 limit 1
982 """ % epi_part
983
984 args = {'enc': self.pk_obj, 'cat': soap_cat, 'epi': episode}
985
986 rows, idx = gmPG2.run_ro_queries (
987 queries = [{
988 'cmd': cmd,
989 'args': args
990 }]
991 )
992 if len(rows) == 0:
993 return None
994
995 return rows[0][0]
996 #--------------------------------------------------------
998
999 lines = []
1000 for soap_cat in soap_cats:
1001 soap_cat_narratives = emr.get_clin_narrative (
1002 episodes = episodes,
1003 issues = issues,
1004 encounters = [self._payload[self._idx['pk_encounter']]],
1005 soap_cats = [soap_cat]
1006 )
1007 if soap_cat_narratives is None:
1008 continue
1009 if len(soap_cat_narratives) == 0:
1010 continue
1011
1012 lines.append(u'-- %s ----------' % gmClinNarrative.soap_cat2l10n_str[soap_cat])
1013 for soap_entry in soap_cat_narratives:
1014 txt = gmTools.wrap (
1015 text = u'%s\n (%.8s %s)' % (
1016 soap_entry['narrative'],
1017 soap_entry['provider'],
1018 soap_entry['date'].strftime('%Y-%m-%d %H:%M')
1019 ),
1020 width = 75,
1021 initial_indent = u'',
1022 subsequent_indent = (u' ' * left_margin)
1023 )
1024 lines.append(txt)
1025 lines.append('')
1026
1027 return lines
1028 #--------------------------------------------------------
1029 - 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):
1030
1031 lines = []
1032
1033 if fancy_header:
1034 lines.append(u'%s%s: %s - %s (@%s)%s [#%s]' % (
1035 u' ' * left_margin,
1036 self._payload[self._idx['l10n_type']],
1037 self._payload[self._idx['started_original_tz']].strftime('%Y-%m-%d %H:%M'),
1038 self._payload[self._idx['last_affirmed_original_tz']].strftime('%H:%M'),
1039 self._payload[self._idx['source_time_zone']],
1040 gmTools.coalesce(self._payload[self._idx['assessment_of_encounter']], u'', u' \u00BB%s\u00AB'),
1041 self._payload[self._idx['pk_encounter']]
1042 ))
1043
1044 lines.append(_(' your time: %s - %s (@%s = %s%s)\n') % (
1045 self._payload[self._idx['started']].strftime('%Y-%m-%d %H:%M'),
1046 self._payload[self._idx['last_affirmed']].strftime('%H:%M'),
1047 gmDateTime.current_local_iso_numeric_timezone_string,
1048 gmTools.bool2subst (
1049 gmDateTime.dst_currently_in_effect,
1050 gmDateTime.py_dst_timezone_name,
1051 gmDateTime.py_timezone_name
1052 ),
1053 gmTools.bool2subst(gmDateTime.dst_currently_in_effect, u' - ' + _('daylight savings time in effect'), u'')
1054 ))
1055
1056 lines.append(u'%s: %s' % (
1057 _('RFE'),
1058 gmTools.coalesce(self._payload[self._idx['reason_for_encounter']], u'')
1059 ))
1060 lines.append(u'%s: %s' % (
1061 _('AOE'),
1062 gmTools.coalesce(self._payload[self._idx['assessment_of_encounter']], u'')
1063 ))
1064
1065 else:
1066 lines.append(u'%s%s: %s - %s%s' % (
1067 u' ' * left_margin,
1068 self._payload[self._idx['l10n_type']],
1069 self._payload[self._idx['started_original_tz']].strftime('%Y-%m-%d %H:%M'),
1070 self._payload[self._idx['last_affirmed_original_tz']].strftime('%H:%M'),
1071 gmTools.coalesce(self._payload[self._idx['assessment_of_encounter']], u'', u' \u00BB%s\u00AB')
1072 ))
1073
1074 if with_soap:
1075 lines.append(u'')
1076
1077 if patient.ID != self._payload[self._idx['pk_patient']]:
1078 msg = '<patient>.ID = %s but encounter %s belongs to patient %s' % (
1079 patient.ID,
1080 self._payload[self._idx['pk_encounter']],
1081 self._payload[self._idx['pk_patient']]
1082 )
1083 raise ValueError(msg)
1084
1085 emr = patient.get_emr()
1086
1087 lines.extend(self.format_soap (
1088 episodes = episodes,
1089 left_margin = left_margin,
1090 soap_cats = 'soap',
1091 emr = emr,
1092 issues = issues
1093 ))
1094
1095 # test results
1096 if with_tests:
1097 tests = emr.get_test_results_by_date (
1098 episodes = episodes,
1099 encounter = self._payload[self._idx['pk_encounter']]
1100 )
1101 if len(tests) > 0:
1102 lines.append('')
1103 lines.append(_('Measurements and Results:'))
1104
1105 for t in tests:
1106 lines.extend(t.format())
1107
1108 del tests
1109
1110 # vaccinations
1111 if with_vaccinations:
1112 vaccs = emr.get_vaccinations (
1113 episodes = episodes,
1114 encounters = [ self._payload[self._idx['pk_encounter']] ]
1115 )
1116
1117 if len(vaccs) > 0:
1118 lines.append(u'')
1119 lines.append(_('Vaccinations:'))
1120
1121 for vacc in vaccs:
1122 lines.extend(vacc.format (
1123 with_indications = True,
1124 with_comment = True,
1125 with_reaction = True,
1126 date_format = '%Y-%m-%d'
1127 ))
1128 del vaccs
1129
1130 # documents
1131 if with_docs:
1132 doc_folder = patient.get_document_folder()
1133 docs = doc_folder.get_documents (
1134 episodes = episodes,
1135 encounter = self._payload[self._idx['pk_encounter']]
1136 )
1137
1138 if len(docs) > 0:
1139 lines.append('')
1140 lines.append(_('Documents:'))
1141
1142 for d in docs:
1143 lines.append(u' %s %s:%s%s' % (
1144 d['clin_when'].strftime('%Y-%m-%d'),
1145 d['l10n_type'],
1146 gmTools.coalesce(d['comment'], u'', u' "%s"'),
1147 gmTools.coalesce(d['ext_ref'], u'', u' (%s)')
1148 ))
1149
1150 del docs
1151
1152 eol_w_margin = u'\n%s' % (u' ' * left_margin)
1153 return u'%s\n' % eol_w_margin.join(lines)
1154 #-----------------------------------------------------------
1156 """Creates a new encounter for a patient.
1157
1158 fk_patient - patient PK
1159 fk_location - encounter location
1160 enc_type - type of encounter
1161
1162 FIXME: we don't deal with location yet
1163 """
1164 if enc_type is None:
1165 enc_type = u'in surgery'
1166 # insert new encounter
1167 queries = []
1168 try:
1169 enc_type = int(enc_type)
1170 cmd = u"""
1171 insert into clin.encounter (
1172 fk_patient, fk_location, fk_type
1173 ) values (
1174 %s, -1, %s
1175 )"""
1176 except ValueError:
1177 enc_type = enc_type
1178 cmd = u"""
1179 insert into clin.encounter (
1180 fk_patient, fk_location, fk_type
1181 ) values (
1182 %s, -1, coalesce((select pk from clin.encounter_type where description=%s), 0)
1183 )"""
1184 queries.append({'cmd': cmd, 'args': [fk_patient, enc_type]})
1185 queries.append({'cmd': cEncounter._cmd_fetch_payload % u"currval('clin.encounter_pk_seq')"})
1186 rows, idx = gmPG2.run_rw_queries(queries=queries, return_data=True, get_col_idx=True)
1187 encounter = cEncounter(row={'data': rows[0], 'idx': idx, 'pk_field': 'pk_encounter'})
1188
1189 return encounter
1190 #-----------------------------------------------------------
1192
1193 rows, idx = gmPG2.run_rw_queries(
1194 queries = [{
1195 'cmd': u"select i18n.upd_tx(%(desc)s, %(l10n_desc)s)",
1196 'args': {'desc': description, 'l10n_desc': l10n_description}
1197 }],
1198 return_data = True
1199 )
1200
1201 success = rows[0][0]
1202 if not success:
1203 _log.warning('updating encounter type [%s] to [%s] failed', description, l10n_description)
1204
1205 return {'description': description, 'l10n_description': l10n_description}
1206 #-----------------------------------------------------------
1208 """This will attempt to create a NEW encounter type."""
1209
1210 # need a system name, so derive one if necessary
1211 if description is None:
1212 description = l10n_description
1213
1214 args = {
1215 'desc': description,
1216 'l10n_desc': l10n_description
1217 }
1218
1219 _log.debug('creating encounter type: %s, %s', description, l10n_description)
1220
1221 # does it exist already ?
1222 cmd = u"select description, _(description) from clin.encounter_type where description = %(desc)s"
1223 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
1224
1225 # yes
1226 if len(rows) > 0:
1227 # both system and l10n name are the same so all is well
1228 if (rows[0][0] == description) and (rows[0][1] == l10n_description):
1229 _log.info('encounter type [%s] already exists with the proper translation')
1230 return {'description': description, 'l10n_description': l10n_description}
1231
1232 # or maybe there just wasn't a translation to
1233 # the current language for this type yet ?
1234 cmd = u"select exists (select 1 from i18n.translations where orig = %(desc)s and lang = i18n.get_curr_lang())"
1235 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
1236
1237 # there was, so fail
1238 if rows[0][0]:
1239 _log.error('encounter type [%s] already exists but with another translation')
1240 return None
1241
1242 # else set it
1243 cmd = u"select i18n.upd_tx(%(desc)s, %(l10n_desc)s)"
1244 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
1245 return {'description': description, 'l10n_description': l10n_description}
1246
1247 # no
1248 queries = [
1249 {'cmd': u"insert into clin.encounter_type (description) values (%(desc)s)", 'args': args},
1250 {'cmd': u"select i18n.upd_tx(%(desc)s, %(l10n_desc)s)", 'args': args}
1251 ]
1252 rows, idx = gmPG2.run_rw_queries(queries = queries)
1253
1254 return {'description': description, 'l10n_description': l10n_description}
1255 #-----------------------------------------------------------
1257 cmd = u"select _(description) as l10n_description, description from clin.encounter_type"
1258 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}])
1259 return rows
1260 #-----------------------------------------------------------
1262 cmd = u"SELECT * from clin.encounter_type where description = %s"
1263 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [description]}])
1264 return rows
1265 #-----------------------------------------------------------
1267 cmd = u"delete from clin.encounter_type where description = %(desc)s"
1268 args = {'desc': description}
1269 try:
1270 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
1271 except gmPG2.dbapi.IntegrityError, e:
1272 if e.pgcode == gmPG2.sql_error_codes.FOREIGN_KEY_VIOLATION:
1273 return False
1274 raise
1275
1276 return True
1277 #============================================================
1279 """Represents one problem.
1280
1281 problems are the aggregation of
1282 .clinically_relevant=True issues and
1283 .is_open=True episodes
1284 """
1285 _cmd_fetch_payload = u'' # will get programmatically defined in __init__
1286 _cmds_store_payload = [u"select 1"]
1287 _updatable_fields = []
1288
1289 #--------------------------------------------------------
1291 """Initialize.
1292
1293 aPK_obj must contain the keys
1294 pk_patient
1295 pk_episode
1296 pk_health_issue
1297 """
1298 if aPK_obj is None:
1299 raise gmExceptions.ConstructorError, 'cannot instatiate cProblem for PK: [%s]' % (aPK_obj)
1300
1301 # As problems are rows from a view of different emr struct items,
1302 # the PK can't be a single field and, as some of the values of the
1303 # composed PK may be None, they must be queried using 'is null',
1304 # so we must programmatically construct the SQL query
1305 where_parts = []
1306 pk = {}
1307 for col_name in aPK_obj.keys():
1308 val = aPK_obj[col_name]
1309 if val is None:
1310 where_parts.append('%s IS NULL' % col_name)
1311 else:
1312 where_parts.append('%s = %%(%s)s' % (col_name, col_name))
1313 pk[col_name] = val
1314
1315 # try to instantiate from true problem view
1316 cProblem._cmd_fetch_payload = u"""
1317 SELECT *, False as is_potential_problem
1318 FROM clin.v_problem_list
1319 WHERE %s""" % u' AND '.join(where_parts)
1320
1321 try:
1322 gmBusinessDBObject.cBusinessDBObject.__init__(self, aPK_obj=pk)
1323 return
1324 except gmExceptions.ConstructorError:
1325 _log.exception('problem not found, trying potential problems')
1326 if try_potential_problems is False:
1327 raise
1328
1329 # try to instantiate from non-problem view
1330 cProblem._cmd_fetch_payload = u"""
1331 SELECT *, True as is_potential_problem
1332 FROM clin.v_potential_problem_list
1333 WHERE %s""" % u' AND '.join(where_parts)
1334 gmBusinessDBObject.cBusinessDBObject.__init__(self, aPK_obj=pk)
1335 #--------------------------------------------------------
1337 """
1338 Retrieve the cEpisode instance equivalent to this problem.
1339 The problem's type attribute must be 'episode'
1340 """
1341 if self._payload[self._idx['type']] != 'episode':
1342 _log.error('cannot convert problem [%s] of type [%s] to episode' % (self._payload[self._idx['problem']], self._payload[self._idx['type']]))
1343 return None
1344 return cEpisode(aPK_obj=self._payload[self._idx['pk_episode']])
1345 #--------------------------------------------------------
1346 # doubles as 'diagnostic_certainty_description' getter:
1348 return diagnostic_certainty_classification2str(self._payload[self._idx['diagnostic_certainty_classification']])
1349
1350 diagnostic_certainty_description = property(get_diagnostic_certainty_description, lambda x:x)
1351 #============================================================
1353
1354 _cmd_fetch_payload = u"select * from clin.v_pat_hospital_stays where pk_hospital_stay = %s"
1355 _cmds_store_payload = [
1356 u"""update clin.hospital_stay set
1357 clin_when = %(admission)s,
1358 discharge = %(discharge)s,
1359 narrative = gm.nullify_empty_string(%(hospital)s),
1360 fk_episode = %(pk_episode)s,
1361 fk_encounter = %(pk_encounter)s
1362 where
1363 pk = %(pk_hospital_stay)s and
1364 xmin = %(xmin_hospital_stay)s""",
1365 u"""select xmin_hospital_stay from clin.v_pat_hospital_stays where pk_hospital_stay = %(pk_hospital_stay)s"""
1366 ]
1367 _updatable_fields = [
1368 'admission',
1369 'discharge',
1370 'hospital',
1371 'pk_episode',
1372 'pk_encounter'
1373 ]
1374 #-------------------------------------------------------
1376
1377 if self._payload[self._idx['discharge']] is not None:
1378 dis = u' - %s' % self._payload[self._idx['discharge']].strftime('%Y %b %d').decode(gmI18N.get_encoding())
1379 else:
1380 dis = u''
1381
1382 line = u'%s%s%s%s: %s%s%s' % (
1383 u' ' * left_margin,
1384 self._payload[self._idx['admission']].strftime('%Y %b %d').decode(gmI18N.get_encoding()),
1385 dis,
1386 gmTools.coalesce(self._payload[self._idx['hospital']], u'', u' (%s)'),
1387 gmTools.u_left_double_angle_quote,
1388 self._payload[self._idx['episode']],
1389 gmTools.u_right_double_angle_quote
1390 )
1391
1392 return line
1393 #-----------------------------------------------------------
1395
1396 queries = [
1397 {
1398 'cmd': u'select * from clin.v_pat_hospital_stays where pk_patient = %(pat)s order by admission',
1399 'args': {'pat': patient}
1400 }
1401 ]
1402
1403 rows, idx = gmPG2.run_ro_queries(queries = queries, get_col_idx = True)
1404
1405 return [ cHospitalStay(row = {'idx': idx, 'data': r, 'pk_field': 'pk_hospital_stay'}) for r in rows ]
1406 #-----------------------------------------------------------
1408
1409 queries = [
1410 {
1411 'cmd': u'insert into clin.hospital_stay (fk_encounter, fk_episode) values (%(enc)s, %(epi)s)',
1412 'args': {'enc': encounter, 'epi': episode}
1413 },
1414 {'cmd': u"select currval('clin.hospital_stay_pk_seq')"}
1415 ]
1416
1417 rows, idx = gmPG2.run_rw_queries(queries = queries, return_data = True)
1418
1419 return cHospitalStay(aPK_obj = rows[0][0])
1420 #-----------------------------------------------------------
1422 cmd = u'delete from clin.hospital_stay where pk = %(pk)s'
1423 args = {'pk': stay}
1424 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
1425 return True
1426 #============================================================
1428
1429 _cmd_fetch_payload = u"select * from clin.v_pat_procedures where pk_procedure = %s"
1430 _cmds_store_payload = [
1431 u"""UPDATE clin.procedure SET
1432 clin_when = %(clin_when)s,
1433 clin_where = NULLIF (
1434 COALESCE (
1435 %(pk_hospital_stay)s::TEXT,
1436 gm.nullify_empty_string(%(clin_where)s)
1437 ),
1438 %(pk_hospital_stay)s::TEXT
1439 ),
1440 narrative = gm.nullify_empty_string(%(performed_procedure)s),
1441 fk_hospital_stay = %(pk_hospital_stay)s,
1442 fk_episode = %(pk_episode)s,
1443 fk_encounter = %(pk_encounter)s
1444 WHERE
1445 pk = %(pk_procedure)s AND
1446 xmin = %(xmin_procedure)s
1447 RETURNING xmin as xmin_procedure"""
1448 # ,u"""select xmin_procedure from clin.v_pat_procedures where pk_procedure = %(pk_procedure)s"""
1449 ]
1450 _updatable_fields = [
1451 'clin_when',
1452 'clin_where',
1453 'performed_procedure',
1454 'pk_hospital_stay',
1455 'pk_episode',
1456 'pk_encounter'
1457 ]
1458 #-------------------------------------------------------
1460
1461 if (attribute == 'pk_hospital_stay') and (value is not None):
1462 gmBusinessDBObject.cBusinessDBObject.__setitem__(self, 'clin_where', None)
1463
1464 if (attribute == 'clin_where') and (value is not None) and (value.strip() != u''):
1465 gmBusinessDBObject.cBusinessDBObject.__setitem__(self, 'pk_hospital_stay', None)
1466
1467 gmBusinessDBObject.cBusinessDBObject.__setitem__(self, attribute, value)
1468 #-------------------------------------------------------
1470
1471 line = u'%s%s (%s): %s' % (
1472 (u' ' * left_margin),
1473 self._payload[self._idx['clin_when']].strftime('%Y %b %d').decode(gmI18N.get_encoding()),
1474 self._payload[self._idx['clin_where']],
1475 self._payload[self._idx['performed_procedure']]
1476 )
1477 if include_episode:
1478 line = u'%s (%s)' % (line, self._payload[self._idx['episode']])
1479
1480 return line
1481 #-----------------------------------------------------------
1483
1484 queries = [
1485 {
1486 'cmd': u'select * from clin.v_pat_procedures where pk_patient = %(pat)s order by clin_when',
1487 'args': {'pat': patient}
1488 }
1489 ]
1490
1491 rows, idx = gmPG2.run_ro_queries(queries = queries, get_col_idx = True)
1492
1493 return [ cPerformedProcedure(row = {'idx': idx, 'data': r, 'pk_field': 'pk_procedure'}) for r in rows ]
1494 #-----------------------------------------------------------
1495 -def create_performed_procedure(encounter=None, episode=None, location=None, hospital_stay=None, procedure=None):
1496
1497 queries = [{
1498 'cmd': u"""
1499 INSERT INTO clin.procedure (
1500 fk_encounter,
1501 fk_episode,
1502 clin_where,
1503 fk_hospital_stay,
1504 narrative
1505 ) VALUES (
1506 %(enc)s,
1507 %(epi)s,
1508 gm.nullify_empty_string(%(loc)s),
1509 %(stay)s,
1510 gm.nullify_empty_string(%(proc)s)
1511 )
1512 RETURNING pk""",
1513 'args': {'enc': encounter, 'epi': episode, 'loc': location, 'stay': hospital_stay, 'proc': procedure}
1514 }]
1515
1516 rows, idx = gmPG2.run_rw_queries(queries = queries, return_data = True)
1517
1518 return cPerformedProcedure(aPK_obj = rows[0][0])
1519 #-----------------------------------------------------------
1521 cmd = u'delete from clin.procedure where pk = %(pk)s'
1522 args = {'pk': procedure}
1523 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
1524 return True
1525 #============================================================
1526 # main - unit testing
1527 #------------------------------------------------------------
1528 if __name__ == '__main__':
1529
1530 if len(sys.argv) < 2:
1531 sys.exit()
1532
1533 if sys.argv[1] != 'test':
1534 sys.exit()
1535
1536 #--------------------------------------------------------
1537 # define tests
1538 #--------------------------------------------------------
1540 print "\nProblem test"
1541 print "------------"
1542 prob = cProblem(aPK_obj={'pk_patient': 12, 'pk_health_issue': 1, 'pk_episode': None})
1543 print prob
1544 fields = prob.get_fields()
1545 for field in fields:
1546 print field, ':', prob[field]
1547 print '\nupdatable:', prob.get_updatable_fields()
1548 epi = prob.get_as_episode()
1549 print '\nas episode:'
1550 if epi is not None:
1551 for field in epi.get_fields():
1552 print ' .%s : %s' % (field, epi[field])
1553 #--------------------------------------------------------
1555 print "\nhealth issue test"
1556 print "-----------------"
1557 h_issue = cHealthIssue(aPK_obj=2)
1558 print h_issue
1559 fields = h_issue.get_fields()
1560 for field in fields:
1561 print field, ':', h_issue[field]
1562 print "has open episode:", h_issue.has_open_episode()
1563 print "open episode:", h_issue.get_open_episode()
1564 print "updateable:", h_issue.get_updatable_fields()
1565 h_issue.close_expired_episode(ttl=7300)
1566 h_issue = cHealthIssue(encounter = 1, name = u'post appendectomy/peritonitis')
1567 print h_issue
1568 #--------------------------------------------------------
1570 print "\nepisode test"
1571 print "------------"
1572 episode = cEpisode(aPK_obj=1)
1573 print episode
1574 fields = episode.get_fields()
1575 for field in fields:
1576 print field, ':', episode[field]
1577 print "updatable:", episode.get_updatable_fields()
1578 raw_input('ENTER to continue')
1579
1580 old_description = episode['description']
1581 old_enc = cEncounter(aPK_obj = 1)
1582
1583 desc = '1-%s' % episode['description']
1584 print "==> renaming to", desc
1585 successful = episode.rename (
1586 description = desc
1587 )
1588 if not successful:
1589 print "error"
1590 else:
1591 print "success"
1592 for field in fields:
1593 print field, ':', episode[field]
1594
1595 print "episode range:", episode.get_access_range()
1596
1597 raw_input('ENTER to continue')
1598
1599 #--------------------------------------------------------
1601 print "\nencounter test"
1602 print "--------------"
1603 encounter = cEncounter(aPK_obj=1)
1604 print encounter
1605 fields = encounter.get_fields()
1606 for field in fields:
1607 print field, ':', encounter[field]
1608 print "updatable:", encounter.get_updatable_fields()
1609 #--------------------------------------------------------
1611 procs = get_performed_procedures(patient = 12)
1612 for proc in procs:
1613 print proc.format(left_margin=2)
1614 #--------------------------------------------------------
1616 stay = create_hospital_stay(encounter = 1, episode = 2)
1617 stay['hospital'] = u'Starfleet Galaxy General Hospital'
1618 stay.save_payload()
1619 print stay
1620 for s in get_patient_hospital_stays(12):
1621 print s
1622 delete_hospital_stay(stay['pk_hospital_stay'])
1623 stay = create_hospital_stay(encounter = 1, episode = 4)
1624 #--------------------------------------------------------
1626 tests = [None, 'A', 'B', 'C', 'D', 'E']
1627
1628 for t in tests:
1629 print type(t), t
1630 print type(diagnostic_certainty_classification2str(t)), diagnostic_certainty_classification2str(t)
1631
1632 #--------------------------------------------------------
1633 # run them
1634 #test_episode()
1635 #test_problem()
1636 #test_encounter()
1637 #test_health_issue()
1638 #test_hospital_stay()
1639 #test_performed_procedure()
1640 test_diagnostic_certainty_classification_map()
1641 #============================================================
1642
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Thu Sep 9 04:06:42 2010 | http://epydoc.sourceforge.net |