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