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