| Trees | Indices | Help |
|
|---|
|
|
1 # -*- coding: utf8 -*-
2 """GNUmed health related business object.
3
4 license: GPL
5 """
6 #============================================================
7 __version__ = "$Revision: 1.157 $"
8 __author__ = "Carlos Moro <cfmoro1976@yahoo.es>, <karsten.hilbert@gmx.net>"
9
10 import types, sys, string, datetime, logging, time
11
12
13 if __name__ == '__main__':
14 sys.path.insert(0, '../../')
15 from Gnumed.pycommon import gmPG2, gmExceptions, gmNull, gmBusinessDBObject, gmDateTime, gmTools, gmI18N
16 from Gnumed.business import gmClinNarrative
17
18
19 _log = logging.getLogger('gm.emr')
20 _log.info(__version__)
21
22 try: _
23 except NameError: _ = lambda x:x
24 #============================================================
25 # diagnostic certainty classification
26 #============================================================
27 __diagnostic_certainty_classification_map = None
28
30
31 global __diagnostic_certainty_classification_map
32
33 if __diagnostic_certainty_classification_map is None:
34 __diagnostic_certainty_classification_map = {
35 None: u'',
36 u'A': _(u'A: Sign'),
37 u'B': _(u'B: Cluster of signs'),
38 u'C': _(u'C: Syndromic diagnosis'),
39 u'D': _(u'D: Scientific diagnosis')
40 }
41
42 try:
43 return __diagnostic_certainty_classification_map[classification]
44 except KeyError:
45 return _(u'%s: unknown diagnostic certainty classification' % classification)
46 #============================================================
47 # Health Issues API
48 #============================================================
49 laterality2str = {
50 None: u'?',
51 u'na': u'',
52 u'sd': _('bilateral'),
53 u'ds': _('bilateral'),
54 u's': _('left'),
55 u'd': _('right')
56 }
57
58 #============================================================
60 """Represents one health issue."""
61
62 _cmd_fetch_payload = u"select *, xmin_health_issue from clin.v_health_issues where pk_health_issue=%s"
63 _cmds_store_payload = [
64 u"""update clin.health_issue set
65 description = %(description)s,
66 age_noted = %(age_noted)s,
67 laterality = gm.nullify_empty_string(%(laterality)s),
68 grouping = gm.nullify_empty_string(%(grouping)s),
69 diagnostic_certainty_classification = gm.nullify_empty_string(%(diagnostic_certainty_classification)s),
70 is_active = %(is_active)s,
71 clinically_relevant = %(clinically_relevant)s,
72 is_confidential = %(is_confidential)s,
73 is_cause_of_death = %(is_cause_of_death)s
74 where
75 pk = %(pk_health_issue)s and
76 xmin = %(xmin_health_issue)s""",
77 u"select xmin as xmin_health_issue from clin.health_issue where pk = %(pk_health_issue)s"
78 ]
79 _updatable_fields = [
80 'description',
81 'grouping',
82 'age_noted',
83 'laterality',
84 'is_active',
85 'clinically_relevant',
86 'is_confidential',
87 'is_cause_of_death',
88 'diagnostic_certainty_classification'
89 ]
90 #--------------------------------------------------------
91 - def __init__(self, aPK_obj=None, encounter=None, name='xxxDEFAULTxxx', patient=None, row=None):
92 pk = aPK_obj
93
94 if (pk is not None) or (row is not None):
95 gmBusinessDBObject.cBusinessDBObject.__init__(self, aPK_obj=pk, row=row)
96 return
97
98 if patient is None:
99 cmd = u"""select *, xmin_health_issue from clin.v_health_issues
100 where
101 description = %(desc)s
102 and
103 pk_patient = (select fk_patient from clin.encounter where pk = %(enc)s)"""
104 else:
105 cmd = u"""select *, xmin_health_issue from clin.v_health_issues
106 where
107 description = %(desc)s
108 and
109 pk_patient = %(pat)s"""
110
111 queries = [{'cmd': cmd, 'args': {'enc': encounter, 'desc': name, 'pat': patient}}]
112 rows, idx = gmPG2.run_ro_queries(queries = queries, get_col_idx = True)
113
114 if len(rows) == 0:
115 raise gmExceptions.NoSuchBusinessObjectError, 'no health issue for [enc:%s::desc:%s::pat:%s]' % (encounter, name, patient)
116
117 pk = rows[0][0]
118 r = {'idx': idx, 'data': rows[0], 'pk_field': 'pk_health_issue'}
119
120 gmBusinessDBObject.cBusinessDBObject.__init__(self, row=r)
121 #--------------------------------------------------------
123 """Method for issue renaming.
124
125 @param description
126 - the new descriptive name for the issue
127 @type description
128 - a string instance
129 """
130 # sanity check
131 if not type(description) in [str, unicode] or description.strip() == '':
132 _log.error('<description> must be a non-empty string')
133 return False
134 # update the issue description
135 old_description = self._payload[self._idx['description']]
136 self._payload[self._idx['description']] = description.strip()
137 self._is_modified = True
138 successful, data = self.save_payload()
139 if not successful:
140 _log.error('cannot rename health issue [%s] with [%s]' % (self, description))
141 self._payload[self._idx['description']] = old_description
142 return False
143 return True
144 #--------------------------------------------------------
146 cmd = u"select * from clin.v_pat_episodes where pk_health_issue = %(pk)s"
147 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': {'pk': self.pk_obj}}], get_col_idx = True)
148 return [ cEpisode(row = {'data': r, 'idx': idx, 'pk_field': 'pk_episode'}) for r in rows ]
149 #--------------------------------------------------------
151 """ttl in days"""
152 open_episode = self.get_open_episode()
153 if open_episode is None:
154 return True
155 earliest, latest = open_episode.get_access_range()
156 ttl = datetime.timedelta(ttl)
157 now = datetime.datetime.now(tz=latest.tzinfo)
158 if (latest + ttl) > now:
159 return False
160 open_episode['episode_open'] = False
161 success, data = open_episode.save_payload()
162 if success:
163 return True
164 return False # should be an exception
165 #--------------------------------------------------------
167 open_episode = self.get_open_episode()
168 open_episode['episode_open'] = False
169 success, data = open_episode.save_payload()
170 if success:
171 return True
172 return False
173 #--------------------------------------------------------
175 cmd = u"select exists (select 1 from clin.episode where fk_health_issue = %s and is_open is True)"
176 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj]}])
177 return rows[0][0]
178 #--------------------------------------------------------
180 cmd = u"select pk from clin.episode where fk_health_issue = %s and is_open is True"
181 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj]}])
182 if len(rows) == 0:
183 return None
184 return cEpisode(aPK_obj=rows[0][0])
185 #--------------------------------------------------------
187 if self._payload[self._idx['age_noted']] is None:
188 return u'<???>'
189
190 return gmDateTime.format_interval_medically(self._payload[self._idx['age_noted']])
191
192 # # seemingly silly but convinces PG to "nicely"
193 # # format the interval for us
194 # cmd = u"""select
195 #age (
196 # (select dob from dem.identity where pk = %(pat)s) + %(issue_age)s,
197 # (select dob from dem.identity where pk = %(pat)s)
198 #)::text
199 #|| ' (' || age (
200 # (select dob from dem.identity where pk = %(pat)s) + %(issue_age)s
201 #)::text || ' ago)'
202 #"""
203 # args = {
204 # 'pat': self._payload[self._idx['pk_patient']],
205 # 'issue_age': self._payload[self._idx['age_noted']]
206 # }
207 # rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
208 # return rows[0][0]
209 #--------------------------------------------------------
211 return laterality2str[self._payload[self._idx['laterality']]]
212
213 laterality_description = property(_get_laterality_description, lambda x:x)
214 #--------------------------------------------------------
216 return diagnostic_certainty_classification2str(self._payload[self._idx['diagnostic_certainty_classification']])
217
218 diagnostic_certainty_description = property(_get_diagnostic_certainty_description, lambda x:x)
219 #--------------------------------------------------------
221
222 if patient.ID != self._payload[self._idx['pk_patient']]:
223 msg = '<patient>.ID = %s but health issue %s belongs to patient %s' % (
224 patient.ID,
225 self._payload[self._idx['pk_health_issue']],
226 self._payload[self._idx['pk_patient']]
227 )
228 raise ValueError(msg)
229
230 lines = []
231
232 lines.append(_('Health Issue %s%s%s%s [#%s]') % (
233 u'\u00BB',
234 self._payload[self._idx['description']],
235 u'\u00AB',
236 gmTools.coalesce (
237 initial = laterality2str[self._payload[self._idx['laterality']]],
238 instead = u'',
239 template_initial = u' (%s)',
240 none_equivalents = [None, u'', u'?']
241 ),
242 self._payload[self._idx['pk_health_issue']]
243 ))
244
245 if self._payload[self._idx['is_confidential']]:
246 lines.append('')
247 lines.append(_(' ***** CONFIDENTIAL *****'))
248 lines.append('')
249
250 if self._payload[self._idx['is_cause_of_death']]:
251 lines.append('')
252 lines.append(_(' contributed to death of patient'))
253 lines.append('')
254
255 enc = cEncounter(aPK_obj = self._payload[self._idx['pk_encounter']])
256 lines.append (_(' Created during encounter: %s (%s - %s) [#%s]') % (
257 enc['l10n_type'],
258 enc['started_original_tz'].strftime('%Y-%m-%d %H:%M'),
259 enc['last_affirmed_original_tz'].strftime('%H:%M'),
260 self._payload[self._idx['pk_encounter']]
261 ))
262
263 if self._payload[self._idx['age_noted']] is not None:
264 lines.append(_(' Noted at age: %s') % self.age_noted_human_readable())
265
266 lines.append(_(' Status: %s, %s%s') % (
267 gmTools.bool2subst(self._payload[self._idx['is_active']], _('active'), _('inactive')),
268 gmTools.bool2subst(self._payload[self._idx['clinically_relevant']], _('clinically relevant'), _('not clinically relevant')),
269 gmTools.coalesce (
270 initial = diagnostic_certainty_classification2str(self._payload[self._idx['diagnostic_certainty_classification']]),
271 instead = u'',
272 template_initial = u', %s',
273 none_equivalents = [None, u'']
274 ),
275 ))
276 lines.append('')
277
278 emr = patient.get_emr()
279
280 # episodes
281 epis = emr.get_episodes(issues = [self._payload[self._idx['pk_health_issue']]])
282 if epis is None:
283 lines.append(_('Error retrieving episodes for this health issue.'))
284 elif len(epis) == 0:
285 lines.append(_('There are no episodes for this health issue.'))
286 else:
287 lines.append (
288 _('Episodes: %s (most recent: %s%s%s)') % (
289 len(epis),
290 gmTools.u_left_double_angle_quote,
291 emr.get_most_recent_episode(issue = self._payload[self._idx['pk_health_issue']])['description'],
292 gmTools.u_right_double_angle_quote
293 )
294 )
295 lines.append('')
296 for epi in epis:
297 lines.append(u' \u00BB%s\u00AB (%s)' % (
298 epi['description'],
299 gmTools.bool2subst(epi['episode_open'], _('ongoing'), _('closed'))
300 ))
301
302 lines.append('')
303
304 # encounters
305 first_encounter = emr.get_first_encounter(issue_id = self._payload[self._idx['pk_health_issue']])
306 last_encounter = emr.get_last_encounter(issue_id = self._payload[self._idx['pk_health_issue']])
307
308 if first_encounter is None or last_encounter is None:
309 lines.append(_('No encounters found for this health issue.'))
310 else:
311 encs = emr.get_encounters(issues = [self._payload[self._idx['pk_health_issue']]])
312 lines.append(_('Encounters: %s (%s - %s):') % (
313 len(encs),
314 first_encounter['started_original_tz'].strftime('%m/%Y'),
315 last_encounter['last_affirmed_original_tz'].strftime('%m/%Y')
316 ))
317 lines.append(_(' Most recent: %s - %s') % (
318 last_encounter['started_original_tz'].strftime('%Y-%m-%d %H:%M'),
319 last_encounter['last_affirmed_original_tz'].strftime('%H:%M')
320 ))
321
322 # medications
323 meds = emr.get_current_substance_intake (
324 issues = [ self._payload[self._idx['pk_health_issue']] ],
325 order_by = u'is_currently_active, started, substance'
326 )
327
328 if len(meds) > 0:
329 lines.append(u'')
330 lines.append(_('Active medications: %s') % len(meds))
331 for m in meds:
332 lines.append(m.format(left_margin = (left_margin + 1)))
333 del meds
334
335 # hospital stays
336 stays = emr.get_hospital_stays (
337 issues = [ self._payload[self._idx['pk_health_issue']] ]
338 )
339
340 if len(stays) > 0:
341 lines.append(u'')
342 lines.append(_('Hospital stays: %s') % len(stays))
343 for s in stays:
344 lines.append(s.format(left_margin = (left_margin + 1)))
345 del stays
346
347 # procedures
348 procs = emr.get_performed_procedures (
349 issues = [ self._payload[self._idx['pk_health_issue']] ]
350 )
351
352 if len(procs) > 0:
353 lines.append(u'')
354 lines.append(_('Procedures performed: %s') % len(procs))
355 for p in procs:
356 lines.append(p.format(left_margin = (left_margin + 1)))
357 del procs
358
359 epis = self.get_episodes()
360 # documents
361 doc_folder = patient.get_document_folder()
362 docs = doc_folder.get_documents(episodes = [ e['pk_episode'] for e in epis ])
363
364 if len(docs) > 0:
365 lines.append(u'')
366 lines.append(_('Documents: %s') % len(docs))
367 del docs
368
369 # rest results
370 tests = emr.get_test_results_by_date(episodes = [ e['pk_episode'] for e in epis ])
371 if len(tests) > 0:
372 lines.append(u'')
373 lines.append(_('Measurements and Results: %s') % len(tests))
374 del tests
375
376 del epis
377
378 left_margin = u' ' * left_margin
379 eol_w_margin = u'\n%s' % left_margin
380 return left_margin + eol_w_margin.join(lines) + u'\n'
381 #============================================================
383 """Creates a new health issue for a given patient.
384
385 description - health issue name
386 """
387 try:
388 h_issue = cHealthIssue(name = description, encounter = encounter, patient = patient)
389 return h_issue
390 except gmExceptions.NoSuchBusinessObjectError:
391 pass
392
393 queries = []
394 cmd = u"insert into clin.health_issue (description, fk_encounter) values (%(desc)s, %(enc)s)"
395 queries.append({'cmd': cmd, 'args': {'desc': description, 'enc': encounter}})
396
397 cmd = u"select currval('clin.health_issue_pk_seq')"
398 queries.append({'cmd': cmd})
399
400 rows, idx = gmPG2.run_rw_queries(queries = queries, return_data = True)
401 h_issue = cHealthIssue(aPK_obj = rows[0][0])
402
403 return h_issue
404 #-----------------------------------------------------------
406 if isinstance(health_issue, cHealthIssue):
407 pk = health_issue['pk_health_issue']
408 else:
409 pk = int(health_issue)
410
411 try:
412 gmPG2.run_rw_queries(queries = [{'cmd': u'delete from clin.health_issue where pk=%(pk)s', 'args': {'pk': pk}}])
413 except gmPG2.dbapi.IntegrityError:
414 # should be parsing pgcode/and or error message
415 _log.exception('cannot delete health issue')
416 raise gmExceptions.DatabaseObjectInUseError('cannot delete health issue, it is in use')
417 #------------------------------------------------------------
418 # use as dummy for unassociated episodes
420 issue = {
421 'pk_health_issue': None,
422 'description': _('Unattributed episodes'),
423 'age_noted': None,
424 'laterality': u'na',
425 'is_active': True,
426 'clinically_relevant': True,
427 'is_confidential': None,
428 'is_cause_of_death': False,
429 'is_dummy': True
430 }
431 return issue
432 #-----------------------------------------------------------
434 return cProblem (
435 aPK_obj = {
436 'pk_patient': health_issue['pk_patient'],
437 'pk_health_issue': health_issue['pk_health_issue'],
438 'pk_episode': None
439 },
440 try_potential_problems = allow_irrelevant
441 )
442 #============================================================
443 # episodes API
444 #============================================================
446 """Represents one clinical episode.
447 """
448 _cmd_fetch_payload = u"select * from clin.v_pat_episodes where pk_episode=%s"
449 _cmds_store_payload = [
450 u"""update clin.episode set
451 fk_health_issue = %(pk_health_issue)s,
452 is_open = %(episode_open)s::boolean,
453 description = %(description)s,
454 diagnostic_certainty_classification = gm.nullify_empty_string(%(diagnostic_certainty_classification)s)
455 where
456 pk = %(pk_episode)s and
457 xmin = %(xmin_episode)s""",
458 u"""select xmin_episode from clin.v_pat_episodes where pk_episode = %(pk_episode)s"""
459 ]
460 _updatable_fields = [
461 'pk_health_issue',
462 'episode_open',
463 'description',
464 'diagnostic_certainty_classification'
465 ]
466 #--------------------------------------------------------
467 - def __init__(self, aPK_obj=None, id_patient=None, name='xxxDEFAULTxxx', health_issue=None, row=None, encounter=None):
468 pk = aPK_obj
469 if pk is None and row is None:
470
471 where_parts = [u'description = %(desc)s']
472
473 if id_patient is not None:
474 where_parts.append(u'pk_patient = %(pat)s')
475
476 if health_issue is not None:
477 where_parts.append(u'pk_health_issue = %(issue)s')
478
479 if encounter is not None:
480 where_parts.append(u'pk_patient = (select fk_patient from clin.encounter where pk = %(enc)s)')
481
482 args = {
483 'pat': id_patient,
484 'issue': health_issue,
485 'enc': encounter,
486 'desc': name
487 }
488
489 cmd = u"select * from clin.v_pat_episodes where %s" % u' and '.join(where_parts)
490
491 rows, idx = gmPG2.run_ro_queries(
492 queries = [{'cmd': cmd, 'args': args}],
493 get_col_idx=True
494 )
495
496 if len(rows) == 0:
497 raise gmExceptions.NoSuchBusinessObjectError, 'no episode for [%s:%s:%s:%s]' % (id_patient, name, health_issue, encounter)
498
499 r = {'idx': idx, 'data': rows[0], 'pk_field': 'pk_episode'}
500 gmBusinessDBObject.cBusinessDBObject.__init__(self, row=r)
501
502 else:
503 gmBusinessDBObject.cBusinessDBObject.__init__(self, aPK_obj=pk, row=row)
504 #--------------------------------------------------------
506 """Get earliest and latest access to this episode.
507
508 Returns a tuple(earliest, latest).
509 """
510 cmd = u"""
511 select
512 min(earliest),
513 max(latest)
514 from (
515 (select
516 (case when clin_when < modified_when
517 then clin_when
518 else modified_when
519 end) as earliest,
520 (case when clin_when > modified_when
521 then clin_when
522 else modified_when
523 end) as latest
524 from
525 clin.clin_root_item
526 where
527 fk_episode = %(pk)s
528
529 ) union all (
530
531 select
532 modified_when as earliest,
533 modified_when as latest
534 from
535 clin.episode
536 where
537 pk = %(pk)s
538 )
539 ) as ranges"""
540 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': {'pk': self.pk_obj}}])
541 if len(rows) == 0:
542 return (gmNull.cNull(warn=False), gmNull.cNull(warn=False))
543 return (rows[0][0], rows[0][1])
544 #--------------------------------------------------------
547 #--------------------------------------------------------
549 """Method for episode editing, that is, episode renaming.
550
551 @param description
552 - the new descriptive name for the encounter
553 @type description
554 - a string instance
555 """
556 # sanity check
557 if description.strip() == '':
558 _log.error('<description> must be a non-empty string instance')
559 return False
560 # update the episode description
561 old_description = self._payload[self._idx['description']]
562 self._payload[self._idx['description']] = description.strip()
563 self._is_modified = True
564 successful, data = self.save_payload()
565 if not successful:
566 _log.error('cannot rename episode [%s] to [%s]' % (self, description))
567 self._payload[self._idx['description']] = old_description
568 return False
569 return True
570 #--------------------------------------------------------
572 return diagnostic_certainty_classification2str(self._payload[self._idx['diagnostic_certainty_classification']])
573
574 diagnostic_certainty_description = property(_get_diagnostic_certainty_description, lambda x:x)
575 #--------------------------------------------------------
577
578 if patient.ID != self._payload[self._idx['pk_patient']]:
579 msg = '<patient>.ID = %s but episode %s belongs to patient %s' % (
580 patient.ID,
581 self._payload[self._idx['pk_episode']],
582 self._payload[self._idx['pk_patient']]
583 )
584 raise ValueError(msg)
585
586 lines = []
587
588 # episode details
589 lines.append (_('Episode %s%s%s (%s%s) [#%s]\n') % (
590 gmTools.u_left_double_angle_quote,
591 self._payload[self._idx['description']],
592 gmTools.u_right_double_angle_quote,
593 gmTools.coalesce (
594 initial = diagnostic_certainty_classification2str(self._payload[self._idx['diagnostic_certainty_classification']]),
595 instead = u'',
596 template_initial = u'%s, ',
597 none_equivalents = [None, u'']
598 ),
599 gmTools.bool2subst(self._payload[self._idx['episode_open']], _('active'), _('finished')),
600 self._payload[self._idx['pk_episode']]
601 ))
602
603 enc = cEncounter(aPK_obj = self._payload[self._idx['pk_encounter']])
604 lines.append (_('Created during encounter: %s (%s - %s) [#%s]\n') % (
605 enc['l10n_type'],
606 enc['started_original_tz'].strftime('%Y-%m-%d %H:%M'),
607 enc['last_affirmed_original_tz'].strftime('%H:%M'),
608 self._payload[self._idx['pk_encounter']]
609 ))
610
611 # encounters
612 emr = patient.get_emr()
613 encs = emr.get_encounters(episodes = [self._payload[self._idx['pk_episode']]])
614 first_encounter = None
615 last_encounter = None
616 if encs is None:
617 lines.append(_('Error retrieving encounters for this episode.'))
618 elif len(encs) == 0:
619 lines.append(_('There are no encounters for this episode.'))
620 else:
621 first_encounter = emr.get_first_encounter(episode_id = self._payload[self._idx['pk_episode']])
622 last_encounter = emr.get_last_encounter(episode_id = self._payload[self._idx['pk_episode']])
623
624 lines.append(_('Last worked on: %s\n') % last_encounter['last_affirmed_original_tz'].strftime('%Y-%m-%d %H:%M'))
625
626 lines.append(_('1st and (up to 3) most recent (of %s) encounters (%s - %s):') % (
627 len(encs),
628 first_encounter['started'].strftime('%m/%Y'),
629 last_encounter['last_affirmed'].strftime('%m/%Y')
630 ))
631
632 lines.append(u' %s - %s (%s):%s' % (
633 first_encounter['started_original_tz'].strftime('%Y-%m-%d %H:%M'),
634 first_encounter['last_affirmed_original_tz'].strftime('%H:%M'),
635 first_encounter['l10n_type'],
636 gmTools.coalesce (
637 first_encounter['assessment_of_encounter'],
638 gmTools.coalesce (
639 first_encounter['reason_for_encounter'],
640 u'',
641 u' \u00BB%s\u00AB' + (u' (%s)' % _('RFE'))
642 ),
643 u' \u00BB%s\u00AB' + (u' (%s)' % _('AOE'))
644 )
645 ))
646
647 if len(encs) > 4:
648 lines.append(_(' ... %s skipped ...') % (len(encs) - 4))
649
650 for enc in encs[1:][-3:]:
651 lines.append(u' %s - %s (%s):%s' % (
652 enc['started_original_tz'].strftime('%Y-%m-%d %H:%M'),
653 enc['last_affirmed_original_tz'].strftime('%H:%M'),
654 enc['l10n_type'],
655 gmTools.coalesce (
656 enc['assessment_of_encounter'],
657 gmTools.coalesce (
658 enc['reason_for_encounter'],
659 u'',
660 u' \u00BB%s\u00AB' + (u' (%s)' % _('RFE'))
661 ),
662 u' \u00BB%s\u00AB' + (u' (%s)' % _('AOE'))
663 )
664 ))
665 del encs
666
667 # spell out last encounter
668 if last_encounter is not None:
669 lines.append('')
670 lines.append(_('Progress notes in most recent encounter:'))
671 lines.extend(last_encounter.format_soap (
672 episodes = [ self._payload[self._idx['pk_episode']] ],
673 left_margin = left_margin,
674 soap_cats = 'soap',
675 emr = emr
676 ))
677
678 # documents
679 doc_folder = patient.get_document_folder()
680 docs = doc_folder.get_documents (
681 episodes = [ self._payload[self._idx['pk_episode']] ]
682 )
683
684 if len(docs) > 0:
685 lines.append('')
686 lines.append(_('Documents: %s') % len(docs))
687
688 for d in docs:
689 lines.append(u' %s %s:%s%s' % (
690 d['clin_when'].strftime('%Y-%m-%d'),
691 d['l10n_type'],
692 gmTools.coalesce(d['comment'], u'', u' "%s"'),
693 gmTools.coalesce(d['ext_ref'], u'', u' (%s)')
694 ))
695 del docs
696
697 # hospital stays
698 stays = emr.get_hospital_stays (
699 episodes = [ self._payload[self._idx['pk_episode']] ]
700 )
701
702 if len(stays) > 0:
703 lines.append('')
704 lines.append(_('Hospital stays: %s') % len(stays))
705
706 for s in stays:
707 lines.append(s.format(left_margin = (left_margin + 1)))
708 del stays
709
710 # procedures
711 procs = emr.get_performed_procedures (
712 episodes = [ self._payload[self._idx['pk_episode']] ]
713 )
714
715 if len(procs) > 0:
716 lines.append(u'')
717 lines.append(_('Procedures performed: %s') % len(procs))
718 for p in procs:
719 lines.append(p.format(left_margin = (left_margin + 1), include_episode = False))
720 del procs
721
722 # test results
723 tests = emr.get_test_results_by_date(episodes = [ self._payload[self._idx['pk_episode']] ])
724
725 if len(tests) > 0:
726 lines.append('')
727 lines.append(_('Measurements and Results:'))
728
729 for t in tests:
730 lines.extend(t.format (
731 with_review = False,
732 with_comments = False,
733 date_format = '%Y-%m-%d'
734 ))
735 del tests
736
737 left_margin = u' ' * left_margin
738 eol_w_margin = u'\n%s' % left_margin
739 return left_margin + eol_w_margin.join(lines) + u'\n'
740 #============================================================
741 -def create_episode(pk_health_issue=None, episode_name=None, is_open=False, allow_dupes=False, encounter=None):
742 """Creates a new episode for a given patient's health issue.
743
744 pk_health_issue - given health issue PK
745 episode_name - name of episode
746 """
747 if not allow_dupes:
748 try:
749 episode = cEpisode(name=episode_name, health_issue=pk_health_issue, encounter = encounter)
750 if episode['episode_open'] != is_open:
751 episode['episode_open'] = is_open
752 episode.save_payload()
753 return episode
754 except gmExceptions.ConstructorError:
755 pass
756
757 queries = []
758 cmd = u"insert into clin.episode (fk_health_issue, description, is_open, fk_encounter) values (%s, %s, %s::boolean, %s)"
759 queries.append({'cmd': cmd, 'args': [pk_health_issue, episode_name, is_open, encounter]})
760 queries.append({'cmd': cEpisode._cmd_fetch_payload % u"currval('clin.episode_pk_seq')"})
761 rows, idx = gmPG2.run_rw_queries(queries = queries, return_data=True, get_col_idx=True)
762
763 episode = cEpisode(row={'data': rows[0], 'idx': idx, 'pk_field': 'pk_episode'})
764 return episode
765 #-----------------------------------------------------------
767 if isinstance(episode, cEpisode):
768 pk = episode['pk_episode']
769 else:
770 pk = int(episode)
771
772 try:
773 gmPG2.run_rw_queries(queries = [{'cmd': u'delete from clin.episode where pk=%(pk)s', 'args': {'pk': pk}}])
774 except gmPG2.dbapi.IntegrityError:
775 # should be parsing pgcode/and or error message
776 _log.exception('cannot delete episode')
777 raise gmExceptions.DatabaseObjectInUseError('cannot delete episode, it is in use')
778 #-----------------------------------------------------------
780 return cProblem (
781 aPK_obj = {
782 'pk_patient': episode['pk_patient'],
783 'pk_episode': episode['pk_episode'],
784 'pk_health_issue': episode['pk_health_issue']
785 },
786 try_potential_problems = allow_closed
787 )
788 #============================================================
789 # encounter API
790 #============================================================
792 """Represents one encounter."""
793 _cmd_fetch_payload = u"select * from clin.v_pat_encounters where pk_encounter = %s"
794 _cmds_store_payload = [
795 u"""update clin.encounter set
796 started = %(started)s,
797 last_affirmed = %(last_affirmed)s,
798 fk_location = %(pk_location)s,
799 fk_type = %(pk_type)s,
800 reason_for_encounter = gm.nullify_empty_string(%(reason_for_encounter)s),
801 assessment_of_encounter = gm.nullify_empty_string(%(assessment_of_encounter)s)
802 where
803 pk = %(pk_encounter)s and
804 xmin = %(xmin_encounter)s""",
805 u"""select xmin_encounter from clin.v_pat_encounters where pk_encounter=%(pk_encounter)s"""
806 ]
807 _updatable_fields = [
808 'started',
809 'last_affirmed',
810 'pk_location',
811 'pk_type',
812 'reason_for_encounter',
813 'assessment_of_encounter'
814 ]
815 #--------------------------------------------------------
817 """Set the enconter as the active one.
818
819 "Setting active" means making sure the encounter
820 row has the youngest "last_affirmed" timestamp of
821 all encounter rows for this patient.
822
823 staff_id - Provider's primary key
824 """
825 self._payload[self._idx['last_affirmed']] = datetime.datetime.now(tz = gmDateTime.gmCurrentLocalTimezone)
826 self.save_payload()
827 #--------------------------------------------------------
829 """
830 Moves every element currently linked to the current encounter
831 and the source_episode onto target_episode.
832
833 @param source_episode The episode the elements are currently linked to.
834 @type target_episode A cEpisode intance.
835 @param target_episode The episode the elements will be relinked to.
836 @type target_episode A cEpisode intance.
837 """
838 if source_episode['pk_episode'] == target_episode['pk_episode']:
839 return True
840
841 queries = []
842 cmd = u"""
843 UPDATE clin.clin_root_item
844 SET fk_episode = %(trg)s
845 WHERE
846 fk_encounter = %(enc)s AND
847 fk_episode = %(src)s
848 """
849 rows, idx = gmPG2.run_rw_queries(queries = [{
850 'cmd': cmd,
851 'args': {
852 'trg': target_episode['pk_episode'],
853 'enc': self.pk_obj,
854 'src': source_episode['pk_episode']
855 }
856 }])
857 self.refetch_payload()
858 return True
859 #--------------------------------------------------------
861
862 relevant_fields = [
863 'pk_location',
864 'pk_type',
865 'reason_for_encounter',
866 'assessment_of_encounter'
867 ]
868 for field in relevant_fields:
869 if self._payload[self._idx[field]] != another_object[field]:
870 return False
871
872 relevant_fields = [
873 'started',
874 'last_affirmed',
875 ]
876 for field in relevant_fields:
877 if self._payload[self._idx[field]] is None:
878 if another_object[field] is None:
879 continue
880 return False
881
882 if another_object[field] is None:
883 return False
884
885 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'):
886 return False
887
888 return True
889 #--------------------------------------------------------
891 cmd = u"""
892 select exists (
893 select 1 from clin.v_pat_items where pk_patient = %(pat)s and pk_encounter = %(enc)s
894 union all
895 select 1 from blobs.v_doc_med where pk_patient = %(pat)s and pk_encounter = %(enc)s
896 )"""
897 args = {
898 'pat': self._payload[self._idx['pk_patient']],
899 'enc': self.pk_obj
900 }
901 rows, idx = gmPG2.run_ro_queries (
902 queries = [{
903 'cmd': cmd,
904 'args': args
905 }]
906 )
907 return rows[0][0]
908 #--------------------------------------------------------
910 cmd = u"""
911 select exists (
912 select 1 from clin.v_pat_items where pk_patient=%(pat)s and pk_encounter=%(enc)s
913 )"""
914 args = {
915 'pat': self._payload[self._idx['pk_patient']],
916 'enc': self.pk_obj
917 }
918 rows, idx = gmPG2.run_ro_queries (
919 queries = [{
920 'cmd': cmd,
921 'args': args
922 }]
923 )
924 return rows[0][0]
925 #--------------------------------------------------------
927 cmd = u"""
928 select exists (
929 select 1 from blobs.v_doc_med where pk_patient = %(pat)s and pk_encounter = %(enc)s
930 )"""
931 args = {
932 'pat': self._payload[self._idx['pk_patient']],
933 'enc': self.pk_obj
934 }
935 rows, idx = gmPG2.run_ro_queries (
936 queries = [{
937 'cmd': cmd,
938 'args': args
939 }]
940 )
941 return rows[0][0]
942 #--------------------------------------------------------
944
945 if soap_cat is not None:
946 soap_cat = soap_cat.lower()
947
948 if episode is None:
949 epi_part = u'fk_episode is null'
950 else:
951 epi_part = u'fk_episode = %(epi)s'
952
953 cmd = u"""
954 select narrative
955 from clin.clin_narrative
956 where
957 fk_encounter = %%(enc)s
958 and
959 soap_cat = %%(cat)s
960 and
961 %s
962 order by clin_when desc
963 limit 1
964 """ % epi_part
965
966 args = {'enc': self.pk_obj, 'cat': soap_cat, 'epi': episode}
967
968 rows, idx = gmPG2.run_ro_queries (
969 queries = [{
970 'cmd': cmd,
971 'args': args
972 }]
973 )
974 if len(rows) == 0:
975 return None
976
977 return rows[0][0]
978 #--------------------------------------------------------
980
981 lines = []
982 for soap_cat in soap_cats:
983 soap_cat_narratives = emr.get_clin_narrative (
984 episodes = episodes,
985 issues = issues,
986 encounters = [self._payload[self._idx['pk_encounter']]],
987 soap_cats = [soap_cat]
988 )
989 if soap_cat_narratives is None:
990 continue
991 if len(soap_cat_narratives) == 0:
992 continue
993
994 lines.append(u'-- %s ----------' % gmClinNarrative.soap_cat2l10n_str[soap_cat])
995 for soap_entry in soap_cat_narratives:
996 txt = gmTools.wrap (
997 text = u'%s\n (%.8s %s)' % (
998 soap_entry['narrative'],
999 soap_entry['provider'],
1000 soap_entry['date'].strftime('%Y-%m-%d %H:%M')
1001 ),
1002 width = 75,
1003 initial_indent = u'',
1004 subsequent_indent = (u' ' * left_margin)
1005 )
1006 lines.append(txt)
1007 lines.append('')
1008
1009 return lines
1010 #--------------------------------------------------------
1011 - def format(self, episodes=None, with_soap=False, left_margin=0, patient=None, issues=None, with_docs=True, with_tests=True, fancy_header=True):
1012
1013 lines = []
1014
1015 if fancy_header:
1016 lines.append(u'%s%s: %s - %s (@%s)%s [#%s]' % (
1017 u' ' * left_margin,
1018 self._payload[self._idx['l10n_type']],
1019 self._payload[self._idx['started_original_tz']].strftime('%Y-%m-%d %H:%M'),
1020 self._payload[self._idx['last_affirmed_original_tz']].strftime('%H:%M'),
1021 self._payload[self._idx['source_time_zone']],
1022 gmTools.coalesce(self._payload[self._idx['assessment_of_encounter']], u'', u' \u00BB%s\u00AB'),
1023 self._payload[self._idx['pk_encounter']]
1024 ))
1025
1026 lines.append(_(' your time: %s - %s (@%s = %s%s)\n') % (
1027 self._payload[self._idx['started']].strftime('%Y-%m-%d %H:%M'),
1028 self._payload[self._idx['last_affirmed']].strftime('%H:%M'),
1029 gmDateTime.current_local_iso_numeric_timezone_string,
1030 gmTools.bool2subst (
1031 gmDateTime.dst_currently_in_effect,
1032 gmDateTime.py_dst_timezone_name,
1033 gmDateTime.py_timezone_name
1034 ),
1035 gmTools.bool2subst(gmDateTime.dst_currently_in_effect, u' - ' + _('daylight savings time in effect'), u'')
1036 ))
1037
1038 lines.append(u'%s: %s' % (
1039 _('RFE'),
1040 gmTools.coalesce(self._payload[self._idx['reason_for_encounter']], u'')
1041 ))
1042 lines.append(u'%s: %s' % (
1043 _('AOE'),
1044 gmTools.coalesce(self._payload[self._idx['assessment_of_encounter']], u'')
1045 ))
1046
1047 else:
1048 lines.append(u'%s%s: %s - %s%s' % (
1049 u' ' * left_margin,
1050 self._payload[self._idx['l10n_type']],
1051 self._payload[self._idx['started_original_tz']].strftime('%Y-%m-%d %H:%M'),
1052 self._payload[self._idx['last_affirmed_original_tz']].strftime('%H:%M'),
1053 gmTools.coalesce(self._payload[self._idx['assessment_of_encounter']], u'', u' \u00BB%s\u00AB')
1054 ))
1055
1056 if with_soap:
1057 lines.append(u'')
1058
1059 if patient.ID != self._payload[self._idx['pk_patient']]:
1060 msg = '<patient>.ID = %s but encounter %s belongs to patient %s' % (
1061 patient.ID,
1062 self._payload[self._idx['pk_encounter']],
1063 self._payload[self._idx['pk_patient']]
1064 )
1065 raise ValueError(msg)
1066
1067 emr = patient.get_emr()
1068
1069 lines.extend(self.format_soap (
1070 episodes = episodes,
1071 left_margin = left_margin,
1072 soap_cats = 'soap',
1073 emr = emr,
1074 issues = issues
1075 ))
1076
1077 # test results
1078 if with_tests:
1079 tests = emr.get_test_results_by_date (
1080 episodes = episodes,
1081 encounter = self._payload[self._idx['pk_encounter']]
1082 )
1083 if len(tests) > 0:
1084 lines.append('')
1085 lines.append(_('Measurements and Results:'))
1086
1087 for t in tests:
1088 lines.extend(t.format())
1089
1090 del tests
1091
1092 if with_docs:
1093 doc_folder = patient.get_document_folder()
1094 docs = doc_folder.get_documents (
1095 episodes = episodes,
1096 encounter = self._payload[self._idx['pk_encounter']]
1097 )
1098
1099 if len(docs) > 0:
1100 lines.append('')
1101 lines.append(_('Documents:'))
1102
1103 for d in docs:
1104 lines.append(u' %s %s:%s%s' % (
1105 d['clin_when'].strftime('%Y-%m-%d'),
1106 d['l10n_type'],
1107 gmTools.coalesce(d['comment'], u'', u' "%s"'),
1108 gmTools.coalesce(d['ext_ref'], u'', u' (%s)')
1109 ))
1110
1111 del docs
1112
1113 eol_w_margin = u'\n%s' % (u' ' * left_margin)
1114 return u'%s\n' % eol_w_margin.join(lines)
1115
1116 # special items (vaccinations, ...)
1117
1118 # try:
1119 # filtered_items.extend(emr.get_vaccinations(
1120 # since=self.__constraints['since'],
1121 # until=self.__constraints['until'],
1122 # encounters=self.__constraints['encounters'],
1123 # episodes=self.__constraints['episodes'],
1124 # issues=self.__constraints['issues']))
1125 # except:
1126 # _log.error("vaccination error? outside regime")
1127
1128 #-----------------------------------------------------------
1130 """Creates a new encounter for a patient.
1131
1132 fk_patient - patient PK
1133 fk_location - encounter location
1134 enc_type - type of encounter
1135
1136 FIXME: we don't deal with location yet
1137 """
1138 if enc_type is None:
1139 enc_type = u'in surgery'
1140 # insert new encounter
1141 queries = []
1142 try:
1143 enc_type = int(enc_type)
1144 cmd = u"""
1145 insert into clin.encounter (
1146 fk_patient, fk_location, fk_type
1147 ) values (
1148 %s, -1, %s
1149 )"""
1150 except ValueError:
1151 enc_type = enc_type
1152 cmd = u"""
1153 insert into clin.encounter (
1154 fk_patient, fk_location, fk_type
1155 ) values (
1156 %s, -1, coalesce((select pk from clin.encounter_type where description=%s), 0)
1157 )"""
1158 queries.append({'cmd': cmd, 'args': [fk_patient, enc_type]})
1159 queries.append({'cmd': cEncounter._cmd_fetch_payload % u"currval('clin.encounter_pk_seq')"})
1160 rows, idx = gmPG2.run_rw_queries(queries=queries, return_data=True, get_col_idx=True)
1161 encounter = cEncounter(row={'data': rows[0], 'idx': idx, 'pk_field': 'pk_encounter'})
1162
1163 return encounter
1164 #-----------------------------------------------------------
1166
1167 rows, idx = gmPG2.run_rw_queries(
1168 queries = [{
1169 'cmd': u"select i18n.upd_tx(%(desc)s, %(l10n_desc)s)",
1170 'args': {'desc': description, 'l10n_desc': l10n_description}
1171 }],
1172 return_data = True
1173 )
1174
1175 success = rows[0][0]
1176 if not success:
1177 _log.warning('updating encounter type [%s] to [%s] failed', description, l10n_description)
1178
1179 return {'description': description, 'l10n_description': l10n_description}
1180 #-----------------------------------------------------------
1182 """This will attempt to create a NEW encounter type."""
1183
1184 # need a system name, so derive one if necessary
1185 if description is None:
1186 description = l10n_description
1187
1188 args = {
1189 'desc': description,
1190 'l10n_desc': l10n_description
1191 }
1192
1193 _log.debug('creating encounter type: %s, %s', description, l10n_description)
1194
1195 # does it exist already ?
1196 cmd = u"select description, _(description) from clin.encounter_type where description = %(desc)s"
1197 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
1198
1199 # yes
1200 if len(rows) > 0:
1201 # both system and l10n name are the same so all is well
1202 if (rows[0][0] == description) and (rows[0][1] == l10n_description):
1203 _log.info('encounter type [%s] already exists with the proper translation')
1204 return {'description': description, 'l10n_description': l10n_description}
1205
1206 # or maybe there just wasn't a translation to
1207 # the current language for this type yet ?
1208 cmd = u"select exists (select 1 from i18n.translations where orig = %(desc)s and lang = i18n.get_curr_lang())"
1209 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
1210
1211 # there was, so fail
1212 if rows[0][0]:
1213 _log.error('encounter type [%s] already exists but with another translation')
1214 return None
1215
1216 # else set it
1217 cmd = u"select i18n.upd_tx(%(desc)s, %(l10n_desc)s)"
1218 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
1219 return {'description': description, 'l10n_description': l10n_description}
1220
1221 # no
1222 queries = [
1223 {'cmd': u"insert into clin.encounter_type (description) values (%(desc)s)", 'args': args},
1224 {'cmd': u"select i18n.upd_tx(%(desc)s, %(l10n_desc)s)", 'args': args}
1225 ]
1226 rows, idx = gmPG2.run_rw_queries(queries = queries)
1227
1228 return {'description': description, 'l10n_description': l10n_description}
1229 #-----------------------------------------------------------
1231 cmd = u"select _(description) as l10n_description, description from clin.encounter_type"
1232 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}])
1233 return rows
1234 #-----------------------------------------------------------
1236 cmd = u"SELECT * from clin.encounter_type where description = %s"
1237 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [description]}])
1238 return rows
1239 #-----------------------------------------------------------
1241 cmd = u"delete from clin.encounter_type where description = %(desc)s"
1242 args = {'desc': description}
1243 try:
1244 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
1245 except gmPG2.dbapi.IntegrityError, e:
1246 if e.pgcode == gmPG2.sql_error_codes.FOREIGN_KEY_VIOLATION:
1247 return False
1248 raise
1249
1250 return True
1251 #============================================================
1253 """Represents one problem.
1254
1255 problems are the aggregation of
1256 .clinically_relevant=True issues and
1257 .is_open=True episodes
1258 """
1259 _cmd_fetch_payload = u'' # will get programmatically defined in __init__
1260 _cmds_store_payload = [u"select 1"]
1261 _updatable_fields = []
1262
1263 #--------------------------------------------------------
1265 """Initialize.
1266
1267 aPK_obj must contain the keys
1268 pk_patient
1269 pk_episode
1270 pk_health_issue
1271 """
1272 if aPK_obj is None:
1273 raise gmExceptions.ConstructorError, 'cannot instatiate cProblem for PK: [%s]' % (aPK_obj)
1274
1275 # As problems are rows from a view of different emr struct items,
1276 # the PK can't be a single field and, as some of the values of the
1277 # composed PK may be None, they must be queried using 'is null',
1278 # so we must programmatically construct the SQL query
1279 where_parts = []
1280 pk = {}
1281 for col_name in aPK_obj.keys():
1282 val = aPK_obj[col_name]
1283 if val is None:
1284 where_parts.append('%s IS NULL' % col_name)
1285 else:
1286 where_parts.append('%s = %%(%s)s' % (col_name, col_name))
1287 pk[col_name] = val
1288
1289 # try to instantiate from true problem view
1290 cProblem._cmd_fetch_payload = u"""
1291 SELECT *, False as is_potential_problem
1292 FROM clin.v_problem_list
1293 WHERE %s""" % u' AND '.join(where_parts)
1294
1295 try:
1296 gmBusinessDBObject.cBusinessDBObject.__init__(self, aPK_obj=pk)
1297 return
1298 except gmExceptions.ConstructorError:
1299 _log.exception('problem not found, trying potential problems')
1300 if try_potential_problems is False:
1301 raise
1302
1303 # try to instantiate from non-problem view
1304 cProblem._cmd_fetch_payload = u"""
1305 SELECT *, True as is_potential_problem
1306 FROM clin.v_potential_problem_list
1307 WHERE %s""" % u' AND '.join(where_parts)
1308 gmBusinessDBObject.cBusinessDBObject.__init__(self, aPK_obj=pk)
1309 #--------------------------------------------------------
1311 """
1312 Retrieve the cEpisode instance equivalent to this problem.
1313 The problem's type attribute must be 'episode'
1314 """
1315 if self._payload[self._idx['type']] != 'episode':
1316 _log.error('cannot convert problem [%s] of type [%s] to episode' % (self._payload[self._idx['problem']], self._payload[self._idx['type']]))
1317 return None
1318 return cEpisode(aPK_obj=self._payload[self._idx['pk_episode']])
1319 #--------------------------------------------------------
1320 # doubles as 'diagnostic_certainty_description' getter:
1322 return diagnostic_certainty_classification2str(self._payload[self._idx['diagnostic_certainty_classification']])
1323
1324 diagnostic_certainty_description = property(get_diagnostic_certainty_description, lambda x:x)
1325 #============================================================
1327
1328 _cmd_fetch_payload = u"select * from clin.v_pat_hospital_stays where pk_hospital_stay = %s"
1329 _cmds_store_payload = [
1330 u"""update clin.hospital_stay set
1331 clin_when = %(admission)s,
1332 discharge = %(discharge)s,
1333 narrative = gm.nullify_empty_string(%(hospital)s),
1334 fk_episode = %(pk_episode)s,
1335 fk_encounter = %(pk_encounter)s
1336 where
1337 pk = %(pk_hospital_stay)s and
1338 xmin = %(xmin_hospital_stay)s""",
1339 u"""select xmin_hospital_stay from clin.v_pat_hospital_stays where pk_hospital_stay = %(pk_hospital_stay)s"""
1340 ]
1341 _updatable_fields = [
1342 'admission',
1343 'discharge',
1344 'hospital',
1345 'pk_episode',
1346 'pk_encounter'
1347 ]
1348 #-------------------------------------------------------
1350
1351 if self._payload[self._idx['discharge']] is not None:
1352 dis = u' - %s' % self._payload[self._idx['discharge']].strftime('%Y %b %d').decode(gmI18N.get_encoding())
1353 else:
1354 dis = u''
1355
1356 line = u'%s%s%s%s: %s%s%s' % (
1357 u' ' * left_margin,
1358 self._payload[self._idx['admission']].strftime('%Y %b %d').decode(gmI18N.get_encoding()),
1359 dis,
1360 gmTools.coalesce(self._payload[self._idx['hospital']], u'', u' (%s)'),
1361 gmTools.u_left_double_angle_quote,
1362 self._payload[self._idx['episode']],
1363 gmTools.u_right_double_angle_quote
1364 )
1365
1366 return line
1367 #-----------------------------------------------------------
1369
1370 queries = [
1371 {
1372 'cmd': u'select * from clin.v_pat_hospital_stays where pk_patient = %(pat)s order by admission',
1373 'args': {'pat': patient}
1374 }
1375 ]
1376
1377 rows, idx = gmPG2.run_ro_queries(queries = queries, get_col_idx = True)
1378
1379 return [ cHospitalStay(row = {'idx': idx, 'data': r, 'pk_field': 'pk_hospital_stay'}) for r in rows ]
1380 #-----------------------------------------------------------
1382
1383 queries = [
1384 {
1385 'cmd': u'insert into clin.hospital_stay (fk_encounter, fk_episode) values (%(enc)s, %(epi)s)',
1386 'args': {'enc': encounter, 'epi': episode}
1387 },
1388 {'cmd': u"select currval('clin.hospital_stay_pk_seq')"}
1389 ]
1390
1391 rows, idx = gmPG2.run_rw_queries(queries = queries, return_data = True)
1392
1393 return cHospitalStay(aPK_obj = rows[0][0])
1394 #-----------------------------------------------------------
1396 cmd = u'delete from clin.hospital_stay where pk = %(pk)s'
1397 args = {'pk': stay}
1398 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
1399 return True
1400 #============================================================
1402
1403 _cmd_fetch_payload = u"select * from clin.v_pat_procedures where pk_procedure = %s"
1404 _cmds_store_payload = [
1405 u"""update clin.procedure set
1406 clin_when = %(clin_when)s,
1407 clin_where = gm.nullify_empty_string(%(clin_where)s),
1408 narrative = gm.nullify_empty_string(%(performed_procedure)s),
1409 fk_hospital_stay = %(pk_hospital_stay)s,
1410 fk_episode = %(pk_episode)s,
1411 fk_encounter = %(pk_encounter)s
1412 where
1413 pk = %(pk_procedure)s and
1414 xmin = %(xmin_procedure)s
1415 """,
1416 u"""select xmin_procedure from clin.v_pat_procedures where pk_procedure = %(pk_procedure)s"""
1417 ]
1418 _updatable_fields = [
1419 'clin_when',
1420 'clin_where',
1421 'performed_procedure',
1422 'pk_hospital_stay',
1423 'pk_episode',
1424 'pk_encounter'
1425 ]
1426 #-------------------------------------------------------
1428
1429 if (attribute == 'pk_hospital_stay') and (value is not None):
1430 gmBusinessDBObject.cBusinessDBObject.__setitem__(self, 'clin_where', None)
1431
1432 if (attribute == 'clin_where') and (value is not None) and (value.strip() != u''):
1433 gmBusinessDBObject.cBusinessDBObject.__setitem__(self, 'pk_hospital_stay', None)
1434
1435 gmBusinessDBObject.cBusinessDBObject.__setitem__(self, attribute, value)
1436 #-------------------------------------------------------
1438
1439 line = u'%s%s (%s): %s' % (
1440 (u' ' * left_margin),
1441 self._payload[self._idx['clin_when']].strftime('%Y %b %d').decode(gmI18N.get_encoding()),
1442 self._payload[self._idx['clin_where']],
1443 self._payload[self._idx['performed_procedure']]
1444 )
1445 if include_episode:
1446 line = u'%s (%s)' % (line, self._payload[self._idx['episode']])
1447
1448 return line
1449 #-----------------------------------------------------------
1451
1452 queries = [
1453 {
1454 'cmd': u'select * from clin.v_pat_procedures where pk_patient = %(pat)s order by clin_when',
1455 'args': {'pat': patient}
1456 }
1457 ]
1458
1459 rows, idx = gmPG2.run_ro_queries(queries = queries, get_col_idx = True)
1460
1461 return [ cPerformedProcedure(row = {'idx': idx, 'data': r, 'pk_field': 'pk_procedure'}) for r in rows ]
1462 #-----------------------------------------------------------
1463 -def create_performed_procedure(encounter=None, episode=None, location=None, hospital_stay=None, procedure=None):
1464
1465 queries = [{
1466 'cmd': u"""
1467 insert into clin.procedure (
1468 fk_encounter,
1469 fk_episode,
1470 clin_where,
1471 fk_hospital_stay,
1472 narrative
1473 ) values (
1474 %(enc)s,
1475 %(epi)s,
1476 gm.nullify_empty_string(%(loc)s),
1477 %(stay)s,
1478 %(proc)s
1479 )
1480 returning pk""",
1481 'args': {'enc': encounter, 'epi': episode, 'loc': location, 'stay': hospital_stay, 'proc': procedure}
1482 }]
1483
1484 rows, idx = gmPG2.run_rw_queries(queries = queries, return_data = True)
1485
1486 return cPerformedProcedure(aPK_obj = rows[0][0])
1487 #-----------------------------------------------------------
1489 cmd = u'delete from clin.procedure where pk = %(pk)s'
1490 args = {'pk': procedure}
1491 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
1492 return True
1493 #============================================================
1494 # main - unit testing
1495 #------------------------------------------------------------
1496 if __name__ == '__main__':
1497
1498 #--------------------------------------------------------
1499 # define tests
1500 #--------------------------------------------------------
1502 print "\nProblem test"
1503 print "------------"
1504 prob = cProblem(aPK_obj={'pk_patient': 12, 'pk_health_issue': 1, 'pk_episode': None})
1505 print prob
1506 fields = prob.get_fields()
1507 for field in fields:
1508 print field, ':', prob[field]
1509 print '\nupdatable:', prob.get_updatable_fields()
1510 epi = prob.get_as_episode()
1511 print '\nas episode:'
1512 if epi is not None:
1513 for field in epi.get_fields():
1514 print ' .%s : %s' % (field, epi[field])
1515 #--------------------------------------------------------
1517 print "\nhealth issue test"
1518 print "-----------------"
1519 h_issue = cHealthIssue(aPK_obj=2)
1520 print h_issue
1521 fields = h_issue.get_fields()
1522 for field in fields:
1523 print field, ':', h_issue[field]
1524 print "has open episode:", h_issue.has_open_episode()
1525 print "open episode:", h_issue.get_open_episode()
1526 print "updateable:", h_issue.get_updatable_fields()
1527 h_issue.close_expired_episode(ttl=7300)
1528 h_issue = cHealthIssue(encounter = 1, name = u'post appendectomy/peritonitis')
1529 print h_issue
1530 #--------------------------------------------------------
1532 print "\nepisode test"
1533 print "------------"
1534 episode = cEpisode(aPK_obj=1)
1535 print episode
1536 fields = episode.get_fields()
1537 for field in fields:
1538 print field, ':', episode[field]
1539 print "updatable:", episode.get_updatable_fields()
1540 raw_input('ENTER to continue')
1541
1542 old_description = episode['description']
1543 old_enc = cEncounter(aPK_obj = 1)
1544
1545 desc = '1-%s' % episode['description']
1546 print "==> renaming to", desc
1547 successful = episode.rename (
1548 description = desc
1549 )
1550 if not successful:
1551 print "error"
1552 else:
1553 print "success"
1554 for field in fields:
1555 print field, ':', episode[field]
1556
1557 print "episode range:", episode.get_access_range()
1558
1559 raw_input('ENTER to continue')
1560
1561 #--------------------------------------------------------
1563 print "\nencounter test"
1564 print "--------------"
1565 encounter = cEncounter(aPK_obj=1)
1566 print encounter
1567 fields = encounter.get_fields()
1568 for field in fields:
1569 print field, ':', encounter[field]
1570 print "updatable:", encounter.get_updatable_fields()
1571 #--------------------------------------------------------
1573 procs = get_performed_procedures(patient = 12)
1574 for proc in procs:
1575 print proc.format(left_margin=2)
1576 #--------------------------------------------------------
1578 stay = create_hospital_stay(encounter = 1, episode = 2)
1579 stay['hospital'] = u'Starfleet Galaxy General Hospital'
1580 stay.save_payload()
1581 print stay
1582 for s in get_patient_hospital_stays(12):
1583 print s
1584 delete_hospital_stay(stay['pk_hospital_stay'])
1585 stay = create_hospital_stay(encounter = 1, episode = 4)
1586 #--------------------------------------------------------
1588 tests = [None, 'A', 'B', 'C', 'D', 'E']
1589
1590 for t in tests:
1591 print type(t), t
1592 print type(diagnostic_certainty_classification2str(t)), diagnostic_certainty_classification2str(t)
1593
1594 #--------------------------------------------------------
1595 if (len(sys.argv) > 1) and (sys.argv[1] == 'test'):
1596 # run them
1597 #test_episode()
1598 #test_problem()
1599 #test_encounter()
1600 #test_health_issue()
1601 #test_hospital_stay()
1602 #test_performed_procedure()
1603 test_diagnostic_certainty_classification_map()
1604 #============================================================
1605 # $Log: gmEMRStructItems.py,v $
1606 # Revision 1.157 2010/01/21 08:40:15 ncq
1607 # - make diagnostic certainty conversion robust against when _() is imported
1608 # - enable problem to return certainty description
1609 # - test certainty mapper
1610 #
1611 # Revision 1.156 2009/12/21 14:58:19 ncq
1612 # - for issue/episode show creation encounter
1613 # - %x -> %Y-%m-%d timestamp formatting
1614 #
1615 # Revision 1.155 2009/11/28 18:35:23 ncq
1616 # - fix health_issue2problem/episode2problem
1617 # - add is_dummy to dummy health issue
1618 # - enhance cProblem to allow for potential problems, too
1619 #
1620 # Revision 1.154 2009/11/13 21:01:45 ncq
1621 # - create-performed-proc
1622 #
1623 # Revision 1.153 2009/11/06 15:03:15 ncq
1624 # - better known-since formatting for health issue
1625 # - include meds in class formatting
1626 #
1627 # Revision 1.152 2009/10/20 10:24:03 ncq
1628 # - laterality/certainty properties
1629 #
1630 # Revision 1.151 2009/09/29 13:14:06 ncq
1631 # - fix faulty param in create_health_issue
1632 #
1633 # Revision 1.150 2009/09/23 14:31:25 ncq
1634 # - better ABCD long desc
1635 # - health issue
1636 # - teach init/create-* to take advantage of patient pk
1637 # - better formatting
1638 # - cProcedure etc
1639 #
1640 # Revision 1.149 2009/09/17 21:51:27 ncq
1641 # - add performed procedures support
1642 #
1643 # Revision 1.148 2009/09/15 15:24:48 ncq
1644 # - add format() to hospital stays and use it
1645 #
1646 # Revision 1.147 2009/09/13 18:22:55 ncq
1647 # - ignore return of save_payload() - it's a dummy
1648 #
1649 # Revision 1.146 2009/09/01 23:03:57 ncq
1650 # - better classification
1651 #
1652 # Revision 1.145 2009/09/01 22:14:15 ncq
1653 # - support diagnostic certainty on issues and episodes
1654 #
1655 # Revision 1.144 2009/07/16 09:51:53 ncq
1656 # - properly update enc type and check success
1657 #
1658 # Revision 1.143 2009/07/06 14:56:04 ncq
1659 # - consolidate date formatting
1660 # - use improved test results formatting
1661 #
1662 # Revision 1.142 2009/07/02 20:46:17 ncq
1663 # - get-episodes in issue
1664 # - include docs/tests in issue/episode formatting
1665 #
1666 # Revision 1.141 2009/07/01 17:05:56 ncq
1667 # - cleanup
1668 #
1669 # Revision 1.140 2009/06/29 14:59:18 ncq
1670 # - add get-latest-soap
1671 #
1672 # Revision 1.139 2009/06/20 12:33:52 ncq
1673 # - improved episode formatting as per list
1674 #
1675 # Revision 1.138 2009/06/04 14:32:16 ncq
1676 # - reimport lost comment
1677 #
1678 # Revision 1.138 2009/05/28 10:45:33 ncq
1679 # - comment added
1680 #
1681 # Revision 1.137 2009/04/13 10:52:14 ncq
1682 # - support same_payload on encounter
1683 #
1684 # Revision 1.136 2009/04/05 17:48:52 ncq
1685 # - support grouping
1686 #
1687 # Revision 1.135 2009/04/03 11:07:25 ncq
1688 # - include stays in issue formatting
1689 #
1690 # Revision 1.134 2009/04/03 10:39:40 ncq
1691 # - include hospital stays into episode formatting
1692 #
1693 # Revision 1.133 2009/04/03 09:31:13 ncq
1694 # - add hospital stay API
1695 #
1696 # Revision 1.132 2009/02/27 12:38:03 ncq
1697 # - improved SOAP formatting
1698 #
1699 # Revision 1.131 2009/02/23 08:46:01 ncq
1700 # - fix faulty column label
1701 #
1702 # Revision 1.130 2009/02/17 17:45:53 ncq
1703 # - fix create_health_issue
1704 #
1705 # Revision 1.129 2008/12/18 21:26:20 ncq
1706 # - u''ify some strings
1707 #
1708 # Revision 1.128 2008/12/09 23:21:00 ncq
1709 # - .date -> .clin_when in documents
1710 # - no more fk_patient in episode
1711 #
1712 # Revision 1.127 2008/12/01 12:36:13 ncq
1713 # - much improved formatting
1714 #
1715 # Revision 1.126 2008/11/24 11:09:01 ncq
1716 # - health issues now stem from a view
1717 # - no more fk_patient in clin.health_issue
1718 # - no more patient id in create_episode
1719 #
1720 # Revision 1.125 2008/11/20 18:40:53 ncq
1721 # - health_issue/episode2problem
1722 # - improved formatting
1723 #
1724 # Revision 1.124 2008/10/22 12:04:55 ncq
1725 # - use %x in strftime
1726 #
1727 # Revision 1.123 2008/10/12 15:13:30 ncq
1728 # - no more "foundational" in health issue
1729 #
1730 # Revision 1.122 2008/09/09 19:55:07 ncq
1731 # - Jerzy found a misspelling
1732 #
1733 # Revision 1.121 2008/09/04 11:52:17 ncq
1734 # - append an empty line per soap category
1735 #
1736 # Revision 1.120 2008/09/02 18:58:27 ncq
1737 # - fk_patient dropped from clin.health_issue
1738 #
1739 # Revision 1.119 2008/08/17 18:13:39 ncq
1740 # - add CRLF after date/time/provider in soap formatting as
1741 # suggested by Rogerio on the list
1742 #
1743 # Revision 1.118 2008/07/24 13:57:51 ncq
1744 # - update/create/delete_encounter_type
1745 #
1746 # Revision 1.117 2008/07/22 13:53:12 ncq
1747 # - cleanup
1748 #
1749 # Revision 1.116 2008/07/14 13:44:38 ncq
1750 # - add .format to episode
1751 # - factor out .format_soap from .format on encounter
1752 # - better visualize soap sections in output as per user request
1753 #
1754 # Revision 1.115 2008/07/12 15:20:30 ncq
1755 # - add format to health issue
1756 #
1757 # Revision 1.114 2008/06/26 21:17:59 ncq
1758 # - episode formatting: include docs
1759 # - encounter formatting: include results and docs
1760 #
1761 # Revision 1.113 2008/06/24 16:53:58 ncq
1762 # - include test results in encounter formatting such
1763 # as to be included in the EMR tree browser :-)
1764 #
1765 # Revision 1.112 2008/05/19 15:43:45 ncq
1766 # - adapt to TZ code changes
1767 #
1768 # Revision 1.111 2008/05/13 14:06:17 ncq
1769 # - remove superfluous \n
1770 # - add missing .
1771 #
1772 # Revision 1.110 2008/04/11 12:20:52 ncq
1773 # - format() on episode and encounter
1774 #
1775 # Revision 1.109 2008/03/05 22:24:31 ncq
1776 # - support fk_encounter in issue and episode creation
1777 #
1778 # Revision 1.108 2008/02/25 17:29:59 ncq
1779 # - logging cleanup
1780 #
1781 # Revision 1.107 2008/01/30 13:34:49 ncq
1782 # - switch to std lib logging
1783 #
1784 # Revision 1.106 2008/01/22 11:49:14 ncq
1785 # - cleanup
1786 # - free-standing -> Unattributed as per list
1787 #
1788 # Revision 1.105 2008/01/16 19:36:17 ncq
1789 # - improve get_encounter_types()
1790 #
1791 # Revision 1.104 2008/01/13 01:12:53 ncq
1792 # - age_noted_human_readable()
1793 #
1794 # Revision 1.103 2007/10/29 11:04:11 ncq
1795 # - properly handle NULL pk_health_issue in create_apisode() thereby
1796 # finding dupes in free-standing episodes, too
1797 # - this then asks for an explicit allow_dupes (defaulted to False)
1798 # in create_episode() as we may, indeed, wish to allow dupes
1799 # sometimes
1800 #
1801 # Revision 1.102 2007/10/11 12:00:17 ncq
1802 # - add has_narrative() and has_documents()
1803 #
1804 # Revision 1.101 2007/09/07 10:55:55 ncq
1805 # - get_dummy_health_issue()
1806 #
1807 # Revision 1.100 2007/08/15 14:56:30 ncq
1808 # - delete_health_issue()
1809 #
1810 # Revision 1.99 2007/05/18 13:25:56 ncq
1811 # - fix cEncounter.transfer_clinical_data()
1812 #
1813 # Revision 1.98 2007/05/14 10:32:50 ncq
1814 # - raise DatabaseObjectInUseError on psycopg2 integrity error
1815 #
1816 # Revision 1.97 2007/04/27 22:54:13 ncq
1817 # - when checking for existing episodes need to check
1818 # associated health issue, too, of course
1819 #
1820 # Revision 1.96 2007/04/02 18:35:20 ncq
1821 # - create_encounter now more exception-y
1822 #
1823 # Revision 1.95 2007/03/18 13:01:55 ncq
1824 # - a bit of cleanup
1825 #
1826 # Revision 1.94 2007/01/09 18:01:32 ncq
1827 # - let exceptions report errors
1828 #
1829 # Revision 1.93 2007/01/09 12:56:02 ncq
1830 # - create_episode() now always takes patient fk
1831 #
1832 # Revision 1.92 2007/01/04 22:50:04 ncq
1833 # - allow changing fk_patient in cEpisode
1834 #
1835 # Revision 1.91 2007/01/02 16:14:41 ncq
1836 # - fix close_expired_episode()
1837 #
1838 # Revision 1.90 2006/12/25 22:48:52 ncq
1839 # - add cEncounter.has_clinical_data()
1840 #
1841 # Revision 1.89 2006/12/22 16:53:31 ncq
1842 # - use timezone definition in gmDateTime
1843 #
1844 # Revision 1.88 2006/11/24 09:30:33 ncq
1845 # - make cHealthIssue save more of its members
1846 # - if_patient -> fk_patient
1847 # - do not log i18n()ed message so failure there doesn't stop us from creating a health issue or episode
1848 #
1849 # Revision 1.87 2006/11/05 17:02:25 ncq
1850 # - enable health issue and episode to be inited from row
1851 #
1852 # Revision 1.86 2006/10/28 14:59:38 ncq
1853 # - __ -> _
1854 #
1855 # Revision 1.85 2006/10/28 14:59:20 ncq
1856 # - when reading from views no need to explicitely load xmin_*, it's part of the view anyways
1857 # - reduce query duplication by reuse of _cmd_fetch_payload
1858 #
1859 # Revision 1.84 2006/10/10 07:26:37 ncq
1860 # - no more clinitem exceptions
1861 #
1862 # Revision 1.83 2006/10/09 12:18:18 ncq
1863 # - convert to gmPG2
1864 # - convert to cBusinessDBObject
1865 # - unicode queries
1866 # - robustified test suite
1867 #
1868 # Revision 1.82 2006/09/03 11:27:24 ncq
1869 # - use gmNull.cNull
1870 # - add cHealthIssue.get_open_episode()
1871 # - add cEpisode.get_access_range()
1872 #
1873 # Revision 1.81 2006/07/19 20:25:00 ncq
1874 # - gmPyCompat.py is history
1875 #
1876 # Revision 1.80 2006/06/26 12:27:53 ncq
1877 # - cleanup
1878 # - add close_episode() and has_open_episode() to cHealthIssue
1879 #
1880 # Revision 1.79 2006/06/05 22:00:53 ncq
1881 # - must be "episode_open", not "is_open"
1882 #
1883 # Revision 1.78 2006/05/06 18:53:56 ncq
1884 # - select age(...) <> ...; -> select ... <> now() - ...; as per Syan
1885 #
1886 # Revision 1.77 2006/03/09 21:11:49 ncq
1887 # - spell out rfe/aoe
1888 #
1889 # Revision 1.76 2006/02/27 22:38:36 ncq
1890 # - spell out rfe/aoe as per Richard's request
1891 #
1892 # Revision 1.75 2005/12/26 05:26:38 sjtan
1893 #
1894 # match schema
1895 #
1896 # Revision 1.74 2005/12/25 13:24:30 sjtan
1897 #
1898 # schema changes in names .
1899 #
1900 # Revision 1.73 2005/12/06 17:57:13 ncq
1901 # - more id->pk fixes
1902 #
1903 # Revision 1.72 2005/12/06 14:24:14 ncq
1904 # - clin.clin_health_issue/episode -> clin.health_issue/episode
1905 #
1906 # Revision 1.71 2005/11/27 12:56:19 ncq
1907 # - add get_encounter_types()
1908 #
1909 # Revision 1.70 2005/11/27 12:44:57 ncq
1910 # - clinical tables are in schema "clin" now
1911 #
1912 # Revision 1.69 2005/10/15 18:15:37 ncq
1913 # - cleanup
1914 # - clin_encounter has fk_*, not pk_*
1915 # - remove clin_encounter.pk_provider support
1916 # - fix cEncounter.set_active()
1917 # - comment on that transfer_clinical_data will work but will not
1918 # notify others about its changes
1919 #
1920 # Revision 1.68 2005/10/12 22:31:13 ncq
1921 # - encounter['rfe'] not mandatory anymore, hence don't need default
1922 # - encounters don't have a provider
1923 #
1924 # Revision 1.67 2005/10/11 21:49:36 ncq
1925 # - make create_encounter oblivious of emr object again
1926 #
1927 # Revision 1.66 2005/10/08 12:33:09 sjtan
1928 # tree can be updated now without refetching entire cache; done by passing emr object to create_xxxx methods and calling emr.update_cache(key,obj);refresh_historical_tree non-destructively checks for changes and removes removed nodes and adds them if cache mismatch.
1929 #
1930 # Revision 1.65 2005/10/04 19:21:31 sjtan
1931 # unicode and str are different but printable types.
1932 #
1933 # Revision 1.64 2005/09/25 01:00:47 ihaywood
1934 # bugfixes
1935 #
1936 # remember 2.6 uses "import wx" not "from wxPython import wx"
1937 # removed not null constraint on clin_encounter.rfe as has no value on instantiation
1938 # client doesn't try to set clin_encounter.description as it doesn't exist anymore
1939 #
1940 # Revision 1.63 2005/09/22 15:45:11 ncq
1941 # - clin_encounter.fk_provider removed
1942 #
1943 # Revision 1.62 2005/09/19 16:32:42 ncq
1944 # - add rfe/aoe support to clin_encounter
1945 # - remove get_aoes()/get_rfes()
1946 #
1947 # Revision 1.61 2005/09/12 15:05:58 ncq
1948 # - improve close_expired_episodes()
1949 #
1950 # Revision 1.60 2005/09/11 17:23:53 ncq
1951 # - close_expired_episodes()
1952 # - support is_open when adding episode
1953 #
1954 # Revision 1.59 2005/08/22 13:02:46 ncq
1955 # - prepare for 0.2
1956 #
1957 # Revision 1.58 2005/07/02 18:16:58 ncq
1958 # - default encounter type "in surgery" for 0.1, to become
1959 # "in surgery"-on-write later on
1960 #
1961 # Revision 1.57 2005/06/23 14:58:51 ncq
1962 # - clean up transfer_clinical_data()
1963 #
1964 # Revision 1.56 2005/06/20 18:48:51 ncq
1965 # - a little cleanup in transfer_data
1966 #
1967 # Revision 1.55 2005/06/20 13:03:38 cfmoro
1968 # Relink encounter to another episode
1969 #
1970 # Revision 1.54 2005/06/15 22:25:29 ncq
1971 # - issue.rename()
1972 #
1973 # Revision 1.53 2005/06/14 18:53:37 ncq
1974 # - really do rename in rename(), needs to set _is_modified to work
1975 #
1976 # Revision 1.52 2005/06/12 21:40:42 ncq
1977 # - cleanup
1978 #
1979 # Revision 1.51 2005/05/14 15:05:40 ncq
1980 # - show HH:MM in auto-created encounters
1981 #
1982 # Revision 1.50 2005/04/25 08:28:28 ncq
1983 # - episode now has .description
1984 #
1985 # Revision 1.49 2005/04/24 14:42:22 ncq
1986 # - add age_noted as changable
1987 #
1988 # Revision 1.48 2005/04/03 20:05:38 ncq
1989 # - cEpisode.set_active() doesn't make sense no more
1990 #
1991 # Revision 1.47 2005/03/29 07:22:38 ncq
1992 # - improve text for auto generated encounters
1993 #
1994 # Revision 1.46 2005/03/23 18:31:19 ncq
1995 # - v_patient_items -> v_pat_items
1996 #
1997 # Revision 1.45 2005/03/20 16:47:26 ncq
1998 # - cleanup
1999 #
2000 # Revision 1.44 2005/03/17 21:59:35 cfmoro
2001 # Fixed log comment
2002 #
2003 # Revision 1.43 2005/03/17 21:46:23 cfmoro
2004 # Simplified cEpisode.rename function
2005 #
2006 # Revision 1.42 2005/03/17 21:14:45 cfmoro
2007 # Improved exception handling in get_as_episode.
2008 #
2009 # Revision 1.41 2005/03/17 13:35:52 ncq
2010 # - cleanup and streamlining
2011 #
2012 # Revision 1.40 2005/03/16 19:10:06 cfmoro
2013 # Added cProblem.get_as_episode method
2014 #
2015 # Revision 1.39 2005/03/14 14:28:54 ncq
2016 # - id_patient -> pk_patient
2017 # - properly handle simplified episode naming in create_episode()
2018 #
2019 # Revision 1.38 2005/03/08 16:42:47 ncq
2020 # - there are episodes w/ and w/o fk_patient IS NULL so handle that
2021 # properly in set_active()
2022 #
2023 # Revision 1.37 2005/02/28 18:15:36 ncq
2024 # - proper fix for not being able to fetch unnamed episodes
2025 # is to require a name in the first place ...
2026 #
2027 # Revision 1.36 2005/02/20 10:30:49 sjtan
2028 #
2029 # unnamed episodes cannot be refetched.
2030 #
2031 # Revision 1.35 2005/01/31 12:58:24 ncq
2032 # - episode.rename() finally works
2033 #
2034 # Revision 1.34 2005/01/31 09:35:28 ncq
2035 # - add episode.get_description() to return clin_narrative row
2036 # - improve episode.rename() - works for adding new narrative now
2037 # - improve create_episode() - revise later
2038 # - improve unit testing
2039 #
2040 # Revision 1.33 2005/01/29 17:53:57 ncq
2041 # - debug/enhance create_episode
2042 #
2043 # Revision 1.32 2005/01/25 17:24:57 ncq
2044 # - streamlined cEpisode.rename()
2045 #
2046 # Revision 1.31 2005/01/25 01:36:19 cfmoro
2047 # Added cEpisode.rename method
2048 #
2049 # Revision 1.30 2005/01/15 20:24:35 ncq
2050 # - streamlined cProblem
2051 #
2052 # Revision 1.29 2005/01/15 19:55:55 cfmoro
2053 # Added problem support to emr
2054 #
2055 # Revision 1.28 2005/01/02 19:55:30 ncq
2056 # - don't need _xmins_refetch_col_pos anymore
2057 #
2058 # Revision 1.27 2004/12/20 16:45:49 ncq
2059 # - gmBusinessDBObject now requires refetching of XMIN after save_payload
2060 #
2061 # Revision 1.26 2004/12/15 10:42:09 ncq
2062 # - cClinEpisode not handles the fields properly
2063 #
2064 # Revision 1.25 2004/12/15 10:28:11 ncq
2065 # - fix create_episode() aka add_episode()
2066 #
2067 # Revision 1.24 2004/11/03 22:32:34 ncq
2068 # - support _cmds_lock_rows_for_update in business object base class
2069 #
2070 # Revision 1.23 2004/09/19 15:02:29 ncq
2071 # - episode: id -> pk, support fk_patient
2072 # - no default name in create_health_issue
2073 #
2074 # Revision 1.22 2004/07/05 10:24:46 ncq
2075 # - use v_pat_rfe/aoe, by Carlos
2076 #
2077 # Revision 1.21 2004/07/04 15:09:40 ncq
2078 # - when refactoring need to fix imports, too
2079 #
2080 # Revision 1.20 2004/07/04 13:24:31 ncq
2081 # - add cRFE/cAOE
2082 # - use in get_rfes(), get_aoes()
2083 #
2084 # Revision 1.19 2004/06/30 20:34:37 ncq
2085 # - cEncounter.get_RFEs()
2086 #
2087 # Revision 1.18 2004/06/26 23:45:50 ncq
2088 # - cleanup, id_* -> fk/pk_*
2089 #
2090 # Revision 1.17 2004/06/26 07:33:55 ncq
2091 # - id_episode -> fk/pk_episode
2092 #
2093 # Revision 1.16 2004/06/08 00:44:41 ncq
2094 # - v_pat_episodes now has description, not episode for name of episode
2095 #
2096 # Revision 1.15 2004/06/02 22:12:48 ncq
2097 # - cleanup
2098 #
2099 # Revision 1.14 2004/06/02 13:45:19 sjtan
2100 #
2101 # episode->description for update statement as well.
2102 #
2103 # Revision 1.13 2004/06/02 13:18:48 sjtan
2104 #
2105 # revert, as backend view definition was changed yesterday to be more consistent.
2106 #
2107 # Revision 1.12 2004/06/02 12:48:56 sjtan
2108 #
2109 # map episode to description in cursor.description, so can find as episode['description']
2110 # and also save.
2111 #
2112 # Revision 1.11 2004/06/01 23:53:56 ncq
2113 # - v_pat_episodes.episode -> *.description
2114 #
2115 # Revision 1.10 2004/06/01 08:20:14 ncq
2116 # - limit in get_lab_results
2117 #
2118 # Revision 1.9 2004/05/30 20:10:31 ncq
2119 # - cleanup
2120 #
2121 # Revision 1.8 2004/05/22 12:42:54 ncq
2122 # - add create_episode()
2123 # - cleanup add_episode()
2124 #
2125 # Revision 1.7 2004/05/18 22:36:52 ncq
2126 # - need mx.DateTime
2127 # - fix fields updatable in episode
2128 # - fix delete action in episode.set_active()
2129 #
2130 # Revision 1.6 2004/05/18 20:35:42 ncq
2131 # - cleanup
2132 #
2133 # Revision 1.5 2004/05/17 19:02:26 ncq
2134 # - encounter.set_active()
2135 # - improve create_encounter()
2136 #
2137 # Revision 1.4 2004/05/16 15:47:51 ncq
2138 # - add episode.set_active()
2139 #
2140 # Revision 1.3 2004/05/16 14:31:27 ncq
2141 # - cleanup
2142 # - allow health issue to be instantiated by name/patient
2143 # - create_health_issue()/create_encounter
2144 # - based on Carlos' work
2145 #
2146 # Revision 1.2 2004/05/12 14:28:53 ncq
2147 # - allow dict style pk definition in __init__ for multicolum primary keys (think views)
2148 # - self.pk -> self.pk_obj
2149 # - __init__(aPKey) -> __init__(aPK_obj)
2150 #
2151 # Revision 1.1 2004/04/17 12:18:50 ncq
2152 # - health issue, episode, encounter classes
2153 #
2154
| Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Tue Feb 9 04:01:52 2010 | http://epydoc.sourceforge.net |