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