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

Source Code for Module Gnumed.business.gmGenericEMRItem

  1  # -*- coding: utf-8 -*- 
  2  """GNUmed clinical business object in generic form. 
  3   
  4  license: GPL v2 or later 
  5  """ 
  6  #============================================================ 
  7  __author__ = "<karsten.hilbert@gmx.net>" 
  8   
  9  import sys 
 10  import logging 
 11   
 12   
 13  if __name__ == '__main__': 
 14          sys.path.insert(0, '../../') 
 15  from Gnumed.pycommon import gmBusinessDBObject 
 16  from Gnumed.pycommon import gmPG2 
 17  from Gnumed.pycommon import gmTools 
 18  from Gnumed.pycommon import gmDateTime 
 19   
 20  from Gnumed.business import gmSoapDefs 
 21  from Gnumed.business.gmEMRStructItems import cHealthIssue 
 22  from Gnumed.business.gmEMRStructItems import cEncounter 
 23  from Gnumed.business.gmEMRStructItems import cEpisode 
 24  from Gnumed.business.gmEMRStructItems import cHospitalStay 
 25  from Gnumed.business.gmEMRStructItems import cPerformedProcedure 
 26  from Gnumed.business.gmExternalCare import cExternalCareItem 
 27  from Gnumed.business.gmVaccination import cVaccination 
 28  from Gnumed.business.gmClinNarrative import cNarrative 
 29  from Gnumed.business.gmMedication import cSubstanceIntakeEntry 
 30  from Gnumed.business.gmAllergy import cAllergy 
 31  from Gnumed.business.gmAllergy import cAllergyState 
 32  from Gnumed.business.gmFamilyHistory import cFamilyHistory 
 33  from Gnumed.business.gmAutoHints import cSuppressedHint 
 34  from Gnumed.business.gmAutoHints import cDynamicHint 
 35  from Gnumed.business.gmDocuments import cDocument 
 36  from Gnumed.business.gmProviderInbox import cInboxMessage 
 37  from Gnumed.business.gmPathLab import cTestResult 
 38   
 39  _log = logging.getLogger('gm.emr') 
 40   
 41  #============================================================ 
 42  _MAP_generic_emr_item_table2type_str = { 
 43          'clin.encounter': _('Encounter'), 
 44          'clin.episode': _('Episode'), 
 45          'clin.health_issue': _('Health issue'), 
 46          'clin.external_care': _('External care'), 
 47          'clin.vaccination': _('Vaccination'), 
 48          'clin.clin_narrative': _('Progress note'), 
 49          'clin.test_result': _('Test result'), 
 50          'clin.substance_intake': _('Substance intake'), 
 51          'clin.hospital_stay': _('Hospital stay'), 
 52          'clin.procedure': _('Performed procedure'), 
 53          'clin.allergy': _('Allergy'), 
 54          'clin.allergy_state': _('Allergy state'), 
 55          'clin.family_history': _('Family history'), 
 56          'blobs.doc_med': _('Document'), 
 57          'dem.message_inbox': _('Inbox message'), 
 58          'ref.auto_hint': _('Dynamic hint') 
 59  } 
 60   
 61  _MAP_generic_emr_item_table2class = { 
 62          'clin.encounter': cEncounter, 
 63          'clin.episode': cEpisode, 
 64          'clin.health_issue': cHealthIssue, 
 65          'clin.external_care': cExternalCareItem, 
 66          'clin.vaccination': cVaccination, 
 67          'clin.clin_narrative': cNarrative, 
 68          'clin.test_result': cTestResult, 
 69          'clin.substance_intake': cSubstanceIntakeEntry, 
 70          'clin.hospital_stay': cHospitalStay, 
 71          'clin.procedure': cPerformedProcedure, 
 72          'clin.allergy': cAllergy, 
 73          'clin.allergy_state': cAllergyState, 
 74          'clin.family_history': cFamilyHistory, 
 75          'clin.suppressed_hint': cSuppressedHint, 
 76          'blobs.doc_med': cDocument, 
 77          'dem.message_inbox': cInboxMessage, 
 78          'ref.auto_hint': cDynamicHint 
 79  } 
 80   
 81  #============================================================ 
 82  # generic items in clin.v_emr_journal 
 83  #------------------------------------------------------------ 
 84  _SQL_get_generic_emr_items = """SELECT 
 85          to_char(c_vej.clin_when, 'YYYY-MM-DD') AS date, 
 86          c_vej.clin_when, 
 87          coalesce(c_vej.soap_cat, '') as soap_cat, 
 88          c_vej.narrative, 
 89          c_vej.src_table, 
 90          c_scr.rank AS scr, 
 91          c_vej.modified_when, 
 92          to_char(c_vej.modified_when, 'YYYY-MM-DD HH24:MI') AS date_modified, 
 93          c_vej.modified_by, 
 94          c_vej.row_version, 
 95          c_vej.pk_episode, 
 96          c_vej.pk_encounter, 
 97          c_vej.soap_cat as real_soap_cat, 
 98          c_vej.src_pk, 
 99          c_vej.pk_health_issue, 
100          c_vej.health_issue, 
101          c_vej.episode, 
102          c_vej.issue_active, 
103          c_vej.issue_clinically_relevant, 
104          c_vej.episode_open, 
105          c_vej.encounter_started, 
106          c_vej.encounter_last_affirmed, 
107          c_vej.encounter_l10n_type, 
108          c_vej.pk_patient, 
109          -1 AS xmin_dummy 
110  FROM 
111          clin.v_emr_journal c_vej 
112                  JOIN clin.soap_cat_ranks c_scr on (c_scr.soap_cat IS NOT DISTINCT FROM c_vej.soap_cat) 
113  """ 
114   
115  _SQL_get_hints_as_generic_emr_items = """SELECT 
116          to_char(now(), 'YYYY-MM-DD') AS date, 
117          now() as clin_when, 
118          'u'::text as soap_cat, 
119          hints.title || E'\n' || hints.hint 
120                  as narrative, 
121          -- .src_table does not correspond with the 
122          -- .src_pk column source because it is generated 
123          -- from clin.get_hints_for_patient() 
124          'ref.auto_hint'::text as src_table, 
125          c_scr.rank AS scr, 
126          now() as modified_when, 
127          to_char(now(), 'YYYY-MM-DD HH24:MI') AS date_modified, 
128          current_user as modified_by, 
129          0::integer as row_version, 
130          NULL::integer as pk_episode, 
131          %(pk_enc)s as pk_encounter, 
132          'u'::text as real_soap_cat, 
133          hints.pk_auto_hint as src_pk, 
134          NULL::integer as pk_health_issue, 
135          ''::text as health_issue, 
136          ''::text as episode, 
137          False as issue_active, 
138          False as issue_clinically_relevant, 
139          False as episode_open, 
140          %(enc_start)s as encounter_started, 
141          %(enc_last_affirmed)s  as encounter_last_affirmed, 
142          %(enc_type)s as encounter_l10n_type, 
143          %(enc_pat)s as pk_patient, 
144          -1 AS xmin_dummy 
145  FROM 
146          clin.get_hints_for_patient(%(enc_pat)s) AS hints 
147                  JOIN clin.soap_cat_ranks c_scr ON (c_scr.soap_cat = 'u') 
148  """ 
149   
150  __SQL_union = """( 
151          %s 
152  ) UNION ALL ( 
153          %s 
154  )""" 
155   
156  #------------------------------------------------------------ 
157 -class cGenericEMRItem(gmBusinessDBObject.cBusinessDBObject):
158 """Represents an entry in clin.v_emr_journal.""" 159 160 _cmd_fetch_payload = _SQL_get_generic_emr_items + "WHERE src_table = %(src_table)s AND src_pk = %(src_pk)s" 161 _cmds_store_payload = [] 162 # [ """ 163 # -- typically the underlying table name 164 # UPDATE xxx.xxx SET 165 # -- typically "table_col = %(view_col)s" 166 # xxx = %(xxx)s, 167 # xxx = gm.nullify_empty_string(%(xxx)s) 168 # WHERE 169 # pk = %(pk_XXX)s 170 # AND 171 # xmin = %(xmin_dummy)s 172 # RETURNING 173 # xmin AS xmin_dummy 174 # -- also return columns which are calculated in the view used by 175 # -- the initial SELECT such that they will further on contain their 176 # -- updated value: 177 # --, ... 178 # --, ... 179 # """ 180 # ] 181 # view columns that can be updated: 182 _updatable_fields = [''] 183 184 #--------------------------------------------------------
185 - def format(self, eol=None):
186 lines = self.formatted_header 187 lines.append(gmTools.u_box_horiz_4dashes * 40) 188 lines.extend(self._payload[self._idx['narrative']].strip().split('\n')) 189 lines.append('') 190 lines.append(_(' rev %s (%s) by %s in <%s>') % ( 191 self._payload[self._idx['row_version']], 192 self._payload[self._idx['date_modified']], 193 self._payload[self._idx['modified_by']], 194 self._payload[self._idx['src_table']] 195 )) 196 if eol is None: 197 return lines 198 return eol.join(lines)
199 200 #--------------------------------------------------------
201 - def format_header(self, eol=None):
202 lines = [] 203 lines.append(_('Chart entry (%s): %s [#%s in %s]') % ( 204 self.i18n_soap_cat, 205 self.item_type_str, 206 self._payload[self._idx['src_pk']], 207 self._payload[self._idx['src_table']] 208 )) 209 lines.append(_(' Modified: %s by %s (%s rev %s)') % ( 210 self._payload[self._idx['date_modified']], 211 self._payload[self._idx['modified_by']], 212 gmTools.u_arrow2right, 213 self._payload[self._idx['row_version']] 214 )) 215 lines.append('') 216 if self._payload[self._idx['health_issue']] is None: 217 issue_info = gmTools.u_diameter 218 else: 219 issue_info = '%s%s' % ( 220 self._payload[self._idx['health_issue']], 221 gmTools.bool2subst(self._payload[self._idx['issue_active']], ' (' + _('active') + ')', ' (' + _('inactive') + ')', '') 222 ) 223 lines.append(_('Health issue: %s') % issue_info) 224 if self._payload[self._idx['episode']] is None: 225 episode_info = gmTools.u_diameter 226 else: 227 episode_info = '%s%s' % ( 228 self._payload[self._idx['episode']], 229 gmTools.bool2subst(self._payload[self._idx['episode_open']], ' (' + _('open') + ')', ' (' + _('closed') + ')', '') 230 ) 231 lines.append(_('Episode: %s') % episode_info) 232 if self._payload[self._idx['encounter_started']] is None: 233 enc_info = gmTools.u_diameter 234 else: 235 enc_info = '%s - %s (%s)' % ( 236 gmDateTime.pydt_strftime(self._payload[self._idx['encounter_started']], '%Y %b %d %H:%M'), 237 self._payload[self._idx['encounter_last_affirmed']].strftime('%H:%M'), 238 self._payload[self._idx['encounter_l10n_type']] 239 ) 240 lines.append(_('Encounter: %s') % enc_info) 241 if eol is None: 242 return lines 243 return eol.join(lines)
244 245 formatted_header = property(format_header) 246 247 #--------------------------------------------------------
248 - def __get_item_type_str(self):
249 try: 250 return _MAP_generic_emr_item_table2type_str[self._payload[self._idx['src_table']]] 251 except KeyError: 252 return '[%s:%s]' % ( 253 self._payload[self._idx['src_table']], 254 self._payload[self._idx['src_pk']] 255 )
256 257 item_type_str = property(__get_item_type_str) 258 259 #--------------------------------------------------------
260 - def __get_i18n_soap_cat(self):
261 return gmSoapDefs.soap_cat2l10n[self._payload[self._idx['soap_cat']]]
262 263 i18n_soap_cat = property(__get_i18n_soap_cat) 264 265 #--------------------------------------------------------
266 - def __get_specialized_item(self):
267 item_class = _MAP_generic_emr_item_table2class[self._payload[self._idx['src_table']]] 268 return item_class(aPK_obj = self._payload[self._idx['src_pk']])
269 270 specialized_item = property(__get_specialized_item)
271 272 #------------------------------------------------------------
273 -def get_generic_emr_items(encounters=None, episodes=None, issues=None, patient=None, soap_cats=None, time_range=None, order_by=None, active_encounter=None, return_pks=False):
274 275 faulty_args = ( 276 (patient is None) and 277 (encounters is None) and 278 (episodes is None) and 279 (issues is None) and 280 (active_encounter is None) 281 ) 282 assert not faulty_args, 'one of <patient>, <episodes>, <issues>, <active_encounter> must not be None' 283 284 if (patient is not None) and (active_encounter is not None): 285 if patient != active_encounter['pk_patient']: 286 raise AssertionError('<patient> (%s) and <active_encounter>["pk_patient"] (%s) must match, if both given', patient, active_encounter['pk_patient']) 287 288 if order_by is None: 289 order_by = 'ORDER BY clin_when, pk_episode, scr, modified_when, src_table' 290 else: 291 order_by = 'ORDER BY %s' % order_by 292 293 if (patient is None) and (active_encounter is not None): 294 patient = active_encounter['pk_patient'] 295 296 where_parts = [] 297 args = {} 298 299 if patient is not None: 300 where_parts.append('c_vej.pk_patient = %(pat)s') 301 args['pat'] = patient 302 303 if soap_cats is not None: 304 # work around bug in psycopg2 not being able to properly 305 # adapt None to NULL inside tuples 306 if None in soap_cats: 307 where_parts.append('((c_vej.soap_cat IN %(soap_cat)s) OR (c_vej.soap_cat IS NULL))') 308 soap_cats.remove(None) 309 else: 310 where_parts.append('c_vej.soap_cat IN %(soap_cat)s') 311 args['soap_cat'] = tuple(soap_cats) 312 313 if time_range is not None: 314 where_parts.append("c_vej.clin_when > (now() - '%s days'::interval)" % time_range) 315 316 if encounters is not None: 317 where_parts.append("c_vej.pk_encounter IN %(encs)s") 318 args['encs'] = tuple(encounters) 319 320 if episodes is not None: 321 where_parts.append("c_vej.pk_episode IN %(epis)s") 322 args['epis'] = tuple(episodes) 323 324 if issues is not None: 325 where_parts.append("c_vej.pk_health_issue IN %(issues)s") 326 args['issues'] = tuple(issues) 327 328 cmd_journal = _SQL_get_generic_emr_items 329 if len(where_parts) > 0: 330 cmd_journal += '\nWHERE\n\t' 331 cmd_journal += '\t\tAND\t'.join(where_parts) 332 333 if active_encounter is None: 334 cmd = cmd_journal + '\n' + order_by 335 else: 336 args['pk_enc'] = active_encounter['pk_encounter'] 337 args['enc_start'] = active_encounter['started'] 338 args['enc_last_affirmed'] = active_encounter['last_affirmed'] 339 args['enc_type'] = active_encounter['l10n_type'] 340 args['enc_pat'] = active_encounter['pk_patient'] 341 cmd = __SQL_union % ( 342 cmd_journal, 343 _SQL_get_hints_as_generic_emr_items 344 ) + '\n' + order_by 345 346 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True) 347 if return_pks: 348 return [ { 349 'src_table': r['src_table'], 350 'src_pk': r['src_pk'] 351 } for r in rows ] 352 353 return [ cGenericEMRItem(row = { 354 'data': r, 355 'idx': idx, 356 'pk_obj': {'src_table': r['src_table'], 'src_pk': r['src_pk']} 357 } ) for r in rows ]
358 359 #------------------------------------------------------------
360 -def delete_xxx(pk_XXX=None):
361 # forward to specialized item 362 args = {'pk': pk_XXX} 363 cmd = u"DELETE FROM xxx.xxx WHERE pk = %(pk)s" 364 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}]) 365 return True
366 367 #------------------------------------------------------------
368 -def generic_item_type_str(table):
369 try: 370 return _MAP_generic_emr_item_table2type_str[table] 371 except KeyError: 372 return _('unmapped entry type from table [%s]') % table
373 374 #------------------------------------------------------------ 375 # widget code 376 # remember to add in clinical item generic workflows 377 #------------------------------------------------------------
378 -def edit_xxx(parent=None, xxx=None, single_entry=False, presets=None):
379 pass
380 #------------------------------------------------------------
381 -def delete_xxx():
382 pass
383 #------------------------------------------------------------
384 -def manage_xxx():
385 pass
386 #------------------------------------------------------------ 387 388 #============================================================ 389 # main - unit testing 390 #------------------------------------------------------------ 391 if __name__ == '__main__': 392 393 if len(sys.argv) < 2: 394 sys.exit() 395 396 if sys.argv[1] != 'test': 397 sys.exit() 398 399 # gmI18N.activate_locale() 400 # gmI18N.install_domain('gnumed') 401 402 #--------------------------------------------------------
403 - def test_gen_item():
404 for item in get_generic_emr_items ( 405 order_by = None, 406 patient = 12, 407 return_pks = False 408 ): 409 print('------------------------------') 410 print(item.format(eol = '\n')) 411 print(item.staff_id) 412 input('<next>')
413 #print(item.specialized_item) 414 #input('<next>') 415 416 #-------------------------------------------------------- 417 test_gen_item() 418