Package Gnumed :: Package business :: Module gmClinNarrative
[frames] | no frames]

Source Code for Module Gnumed.business.gmClinNarrative

  1  """GNUmed clinical narrative business object. 
  2   
  3  """ 
  4  #============================================================ 
  5  __version__ = "$Revision: 1.45 $" 
  6  __author__ = "Carlos Moro <cfmoro1976@yahoo.es>, Karsten Hilbert <Karsten.Hilbert@gmx.net>" 
  7  __license__ = 'GPL (for details see http://gnu.org)' 
  8   
  9  import sys, logging 
 10   
 11   
 12  if __name__ == '__main__': 
 13          sys.path.insert(0, '../../') 
 14  from Gnumed.pycommon import gmPG2, gmExceptions, gmBusinessDBObject, gmTools 
 15   
 16  try: 
 17          _('dummy-no-need-to-translate-but-make-epydoc-happy') 
 18  except NameError: 
 19          _ = lambda x:x 
 20   
 21  _log = logging.getLogger('gm.emr') 
 22  _log.info(__version__) 
 23   
 24   
 25  soap_cat2l10n = { 
 26          's': _('soap_S').replace(u'soap_', u''), 
 27          'o': _('soap_O').replace(u'soap_', u''), 
 28          'a': _('soap_A').replace(u'soap_', u''), 
 29          'p': _('soap_P').replace(u'soap_', u''), 
 30          #None: _('soap_ADMIN').replace(u'soap_', u'') 
 31          None: gmTools.u_ellipsis 
 32  } 
 33   
 34  soap_cat2l10n_str = { 
 35          's': _('soap_Subjective').replace(u'soap_', u''), 
 36          'o': _('soap_Objective').replace(u'soap_', u''), 
 37          'a': _('soap_Assessment').replace(u'soap_', u''), 
 38          'p': _('soap_Plan').replace(u'soap_', u''), 
 39          None: _('soap_Administrative').replace(u'soap_', u'') 
 40  } 
 41   
 42  l10n2soap_cat = { 
 43          _('soap_S').replace(u'soap_', u''): 's', 
 44          _('soap_O').replace(u'soap_', u''): 'o', 
 45          _('soap_A').replace(u'soap_', u''): 'a', 
 46          _('soap_P').replace(u'soap_', u''): 'p', 
 47          #_('soap_ADMIN').replace(u'soap_', u''): None 
 48          gmTools.u_ellipsis: None 
 49  } 
 50  #============================================================ 
51 -class cDiag(gmBusinessDBObject.cBusinessDBObject):
52 """Represents one real diagnosis. 53 """ 54 _cmd_fetch_payload = u"select *, xmin_clin_diag, xmin_clin_narrative from clin.v_pat_diag where pk_diag=%s" 55 _cmds_store_payload = [ 56 u"""update clin.clin_diag set 57 laterality=%()s, 58 laterality=%(laterality)s, 59 is_chronic=%(is_chronic)s::boolean, 60 is_active=%(is_active)s::boolean, 61 is_definite=%(is_definite)s::boolean, 62 clinically_relevant=%(clinically_relevant)s::boolean 63 where 64 pk=%(pk_diag)s and 65 xmin=%(xmin_clin_diag)s""", 66 u"""update clin.clin_narrative set 67 narrative=%(diagnosis)s 68 where 69 pk=%(pk_diag)s and 70 xmin=%(xmin_clin_narrative)s""", 71 u"""select xmin_clin_diag, xmin_clin_narrative from clin.v_pat_diag where pk_diag=%s(pk_diag)s""" 72 ] 73 74 _updatable_fields = [ 75 'diagnosis', 76 'laterality', 77 'is_chronic', 78 'is_active', 79 'is_definite', 80 'clinically_relevant' 81 ] 82 #--------------------------------------------------------
83 - def get_codes(self):
84 """ 85 Retrieves codes linked to this diagnosis 86 """ 87 cmd = u"select code, coding_system from clin.v_codes4diag where diagnosis=%s" 88 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self._payload[self._idx['diagnosis']]]}]) 89 return rows
90 #--------------------------------------------------------
91 - def add_code(self, code=None, coding_system=None):
92 """ 93 Associates a code (from coding system) with this diagnosis. 94 """ 95 # insert new code 96 cmd = u"select clin.add_coded_phrase (%(diag)s, %(code)s, %(sys)s)" 97 args = { 98 'diag': self._payload[self._idx['diagnosis']], 99 'code': code, 100 'sys': coding_system 101 } 102 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}]) 103 return True
104 #============================================================
105 -class cNarrative(gmBusinessDBObject.cBusinessDBObject):
106 """Represents one clinical free text entry. 107 """ 108 _cmd_fetch_payload = u"select *, xmin_clin_narrative from clin.v_pat_narrative where pk_narrative=%s" 109 _cmds_store_payload = [ 110 u"""update clin.clin_narrative set 111 narrative = %(narrative)s, 112 clin_when = %(date)s, 113 soap_cat = lower(%(soap_cat)s), 114 fk_encounter = %(pk_encounter)s 115 where 116 pk=%(pk_narrative)s and 117 xmin=%(xmin_clin_narrative)s""", 118 u"""select xmin_clin_narrative from clin.v_pat_narrative where pk_narrative=%(pk_narrative)s""" 119 ] 120 121 _updatable_fields = [ 122 'narrative', 123 'date', 124 'soap_cat', 125 'pk_episode', 126 'pk_encounter' 127 ] 128 129 #xxxxxxxxxxxxxxxx 130 # support row_version in view 131 132 #--------------------------------------------------------
133 - def get_codes(self):
134 """Retrieves codes linked to *this* narrative. 135 """ 136 cmd = u"select code, xfk_coding_system from clin.coded_phrase where term=%s" 137 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self._payload[self._idx['narrative']]]}]) 138 return rows
139 #--------------------------------------------------------
140 - def add_code(self, code=None, coding_system=None):
141 """ 142 Associates a code (from coding system) with this narrative. 143 """ 144 # insert new code 145 cmd = u"select clin.add_coded_phrase (%(narr)s, %(code)s, %(sys)s)" 146 args = { 147 'narr': self._payload[self._idx['narrative']], 148 'code': code, 149 'sys': coding_system 150 } 151 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}]) 152 return True
153 #--------------------------------------------------------
154 - def format(self, left_margin=u'', fancy=False, width=75):
155 156 if fancy: 157 # FIXME: add revision 158 txt = gmTools.wrap ( 159 text = _('%s: %s by %.8s\n%s') % ( 160 self._payload[self._idx['date']].strftime('%x %H:%M'), 161 soap_cat2l10n_str[self._payload[self._idx['soap_cat']]], 162 self._payload[self._idx['provider']], 163 self._payload[self._idx['narrative']] 164 ), 165 width = width, 166 initial_indent = u'', 167 subsequent_indent = left_margin + u' ' 168 ) 169 else: 170 txt = u'%s [%s]: %s (%.8s)' % ( 171 self._payload[self._idx['date']].strftime('%x %H:%M'), 172 soap_cat2l10n[self._payload[self._idx['soap_cat']]], 173 self._payload[self._idx['narrative']], 174 self._payload[self._idx['provider']] 175 ) 176 if len(txt) > width: 177 txt = txt[:width] + gmTools.u_ellipsis 178 179 return txt
180 181 # lines.append('-- %s ----------' % gmClinNarrative.soap_cat2l10n_str[soap_cat]) 182 183 #============================================================ 184 # convenience functions 185 #============================================================
186 -def search_text_across_emrs(search_term=None):
187 188 if search_term is None: 189 return [] 190 191 if search_term.strip() == u'': 192 return [] 193 194 cmd = u'select * from clin.v_narrative4search where narrative ~* %(term)s order by pk_patient limit 1000' 195 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': {'term': search_term}}], get_col_idx = False) 196 197 return rows
198 #============================================================
199 -def create_clin_narrative(narrative=None, soap_cat=None, episode_id=None, encounter_id=None):
200 """Creates a new clinical narrative entry 201 202 narrative - free text clinical narrative 203 soap_cat - soap category 204 episode_id - episodes's primary key 205 encounter_id - encounter's primary key 206 """ 207 # any of the args being None (except soap_cat) should fail the SQL code 208 209 # sanity checks: 210 211 # 1) silently do not insert empty narrative 212 narrative = narrative.strip() 213 if narrative == u'': 214 return (True, None) 215 216 # 2) also, silently do not insert true duplicates 217 # FIXME: this should check for .provider = current_user but 218 # FIXME: the view has provider mapped to their staff alias 219 cmd = u""" 220 select *, xmin_clin_narrative from clin.v_pat_narrative where 221 pk_encounter = %(enc)s 222 and pk_episode = %(epi)s 223 and soap_cat = %(soap)s 224 and narrative = %(narr)s 225 """ 226 args = { 227 'enc': encounter_id, 228 'epi': episode_id, 229 'soap': soap_cat, 230 'narr': narrative 231 } 232 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True) 233 if len(rows) == 1: 234 narrative = cNarrative(row = {'pk_field': 'pk_narrative', 'data': rows[0], 'idx': idx}) 235 return (True, narrative) 236 237 # insert new narrative 238 queries = [ 239 {'cmd': u"insert into clin.clin_narrative (fk_encounter, fk_episode, narrative, soap_cat) values (%s, %s, %s, lower(%s))", 240 'args': [encounter_id, episode_id, narrative, soap_cat] 241 }, 242 {'cmd': u"select currval('clin.clin_narrative_pk_seq')"} 243 ] 244 rows, idx = gmPG2.run_rw_queries(queries = queries, return_data=True) 245 246 narrative = cNarrative(aPK_obj = rows[0][0]) 247 return (True, narrative)
248 #------------------------------------------------------------
249 -def delete_clin_narrative(narrative=None):
250 """Deletes a clin.clin_narrative row by it's PK.""" 251 cmd = u"delete from clin.clin_narrative where pk=%s" 252 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': [narrative]}]) 253 return True
254 #============================================================ 255 # main 256 #------------------------------------------------------------ 257 if __name__ == '__main__': 258 259 from Gnumed.pycommon import gmI18N 260 gmI18N.activate_locale() 261 gmI18N.install_domain(domain = 'gnumed') 262
263 - def test_diag():
264 print "\nDiagnose test" 265 print "-------------" 266 diagnose = cDiag(aPK_obj=2) 267 fields = diagnose.get_fields() 268 for field in fields: 269 print field, ':', diagnose[field] 270 print "updatable:", diagnose.get_updatable_fields() 271 print "codes:", diagnose.get_codes()
272 #print "adding code..." 273 #diagnose.add_code('Test code', 'Test coding system') 274 #print "codes:", diagnose.get_codes() 275
276 - def test_narrative():
277 print "\nnarrative test" 278 print "--------------" 279 narrative = cNarrative(aPK_obj=7) 280 fields = narrative.get_fields() 281 for field in fields: 282 print field, ':', narrative[field] 283 print "updatable:", narrative.get_updatable_fields() 284 print "codes:", narrative.get_codes()
285 #print "adding code..." 286 #narrative.add_code('Test code', 'Test coding system') 287 #print "codes:", diagnose.get_codes() 288 289 #print "creating narrative..." 290 #status, new_narrative = create_clin_narrative(narrative = 'Test narrative', soap_cat = 'a', episode_id=1, encounter_id=2) 291 #print new_narrative 292 293 #-----------------------------------------
294 - def test_search_text_across_emrs():
295 results = search_text_across_emrs('cut') 296 for r in results: 297 print r
298 #----------------------------------------- 299 300 #test_search_text_across_emrs() 301 test_diag() 302 test_narrative() 303 304 #============================================================ 305 # $Log: gmClinNarrative.py,v $ 306 # Revision 1.45 2010/01/31 16:30:20 ncq 307 # - cleanup 308 # 309 # Revision 1.44 2009/12/26 19:05:08 ncq 310 # - improved comment 311 # 312 # Revision 1.43 2009/11/13 20:46:49 ncq 313 # - limit EMR search to 1000 results to remain sane 314 # 315 # Revision 1.42 2009/11/08 20:42:00 ncq 316 # - search across EMRs 317 # 318 # Revision 1.41 2009/06/29 14:50:30 ncq 319 # - use ellipsis for administrational soap rows 320 # 321 # Revision 1.40 2009/05/13 12:17:43 ncq 322 # - enable setting of fk_encounter 323 # 324 # Revision 1.39 2009/04/16 12:46:26 ncq 325 # - episode/encounter patient consistency check now moved to database 326 # 327 # Revision 1.38 2009/01/15 11:31:00 ncq 328 # - cleanup 329 # 330 # Revision 1.37 2008/12/27 15:49:21 ncq 331 # - raise exception on integrity problems in create_narrative 332 # 333 # Revision 1.36 2008/12/18 21:25:56 ncq 334 # - add format() to cClinNarrative 335 # 336 # Revision 1.35 2008/08/15 15:55:14 ncq 337 # - cleanup 338 # 339 # Revision 1.34 2008/05/07 15:15:15 ncq 340 # - use replace() rather than strip() to remove suffixes 341 # 342 # Revision 1.33 2008/04/22 21:11:05 ncq 343 # - define _() for testing 344 # 345 # Revision 1.32 2008/04/11 12:20:16 ncq 346 # - soap_cat2l10n_str 347 # 348 # Revision 1.31 2008/01/30 13:34:49 ncq 349 # - switch to std lib logging 350 # 351 # Revision 1.30 2008/01/22 22:02:29 ncq 352 # - add_coded_term -> add_coded_phrase 353 # 354 # Revision 1.29 2008/01/07 11:40:21 ncq 355 # - fix faulty comparison 356 # 357 # Revision 1.28 2008/01/06 08:08:25 ncq 358 # - check for duplicate narrative before insertion 359 # 360 # Revision 1.27 2007/11/05 12:09:29 ncq 361 # - support admin soap type 362 # 363 # Revision 1.26 2007/09/10 12:31:55 ncq 364 # - improve test suite 365 # 366 # Revision 1.25 2007/09/07 22:36:44 ncq 367 # - soap_cat2l10n and back 368 # 369 # Revision 1.24 2006/11/20 15:55:12 ncq 370 # - gmPG2.run_ro_queries *always* returns (rows, idx) so be aware of that 371 # 372 # Revision 1.23 2006/10/08 15:02:14 ncq 373 # - convert to gmPG2 374 # - convert to cBusinessDBObject 375 # 376 # Revision 1.22 2006/07/19 20:25:00 ncq 377 # - gmPyCompat.py is history 378 # 379 # Revision 1.21 2006/05/06 18:51:55 ncq 380 # - remove comment 381 # 382 # Revision 1.20 2005/11/27 12:44:57 ncq 383 # - clinical tables are in schema "clin" now 384 # 385 # Revision 1.19 2005/10/10 18:27:34 ncq 386 # - v_pat_narrative already HAS .provider 387 # 388 # Revision 1.18 2005/10/08 12:33:09 sjtan 389 # 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. 390 # 391 # Revision 1.17 2005/09/19 16:32:02 ncq 392 # - remove is_rfe/is_aoe/cRFE/cAOE 393 # 394 # Revision 1.16 2005/06/09 21:29:16 ncq 395 # - added missing s in %()s 396 # 397 # Revision 1.15 2005/05/17 08:00:09 ncq 398 # - in create_narrative() ignore empty narrative 399 # 400 # Revision 1.14 2005/04/11 17:53:47 ncq 401 # - id_patient -> pk_patient fix 402 # 403 # Revision 1.13 2005/04/08 13:27:54 ncq 404 # - adapt get_codes() 405 # 406 # Revision 1.12 2005/01/31 09:21:48 ncq 407 # - use commit2() 408 # - add delete_clin_narrative() 409 # 410 # Revision 1.11 2005/01/02 19:55:30 ncq 411 # - don't need _xmins_refetch_col_pos anymore 412 # 413 # Revision 1.10 2004/12/20 16:45:49 ncq 414 # - gmBusinessDBObject now requires refetching of XMIN after save_payload 415 # 416 # Revision 1.9 2004/11/03 22:32:34 ncq 417 # - support _cmds_lock_rows_for_update in business object base class 418 # 419 # Revision 1.8 2004/09/25 13:26:35 ncq 420 # - is_significant -> clinically_relevant 421 # 422 # Revision 1.7 2004/08/11 09:42:50 ncq 423 # - point clin_narrative VO to v_pat_narrative 424 # - robustify by applying lower() to soap_cat on insert/update 425 # 426 # Revision 1.6 2004/07/25 23:23:39 ncq 427 # - Carlos made cAOE.get_diagnosis() return a cDiag instead of a list 428 # 429 # Revision 1.5 2004/07/14 09:10:21 ncq 430 # - Carlos' relentless work brings us get_codes(), 431 # get_possible_codes() and adjustions for the fact 432 # that we can now code any soap row 433 # 434 # Revision 1.4 2004/07/07 15:05:51 ncq 435 # - syntax fixes by Carlos 436 # - get_codes(), get_possible_codes() 437 # - talk to the right views 438 # 439 # Revision 1.3 2004/07/06 00:09:19 ncq 440 # - Carlos added create_clin_narrative(), cDiag, cNarrative, and unit tests - nice work ! 441 # 442 # Revision 1.2 2004/07/05 10:24:46 ncq 443 # - use v_pat_rfe/aoe, by Carlos 444 # 445 # Revision 1.1 2004/07/04 13:24:31 ncq 446 # - add cRFE/cAOE 447 # - use in get_rfes(), get_aoes() 448 # 449