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 lines.append(_('Event: %s') % self._payload[self._idx['clin_when']].strftime('%Y %b %d %H:%M')) 242 if eol is None: 243 return lines 244 245 return eol.join(lines)
246 247 formatted_header = property(format_header) 248 249 #--------------------------------------------------------
250 - def __get_item_type_str(self):
251 try: 252 return _MAP_generic_emr_item_table2type_str[self._payload[self._idx['src_table']]] 253 except KeyError: 254 return '[%s:%s]' % ( 255 self._payload[self._idx['src_table']], 256 self._payload[self._idx['src_pk']] 257 )
258 259 item_type_str = property(__get_item_type_str) 260 261 #--------------------------------------------------------
262 - def __get_i18n_soap_cat(self):
263 return gmSoapDefs.soap_cat2l10n[self._payload[self._idx['soap_cat']]]
264 265 i18n_soap_cat = property(__get_i18n_soap_cat) 266 267 #--------------------------------------------------------
268 - def __get_specialized_item(self):
269 item_class = _MAP_generic_emr_item_table2class[self._payload[self._idx['src_table']]] 270 return item_class(aPK_obj = self._payload[self._idx['src_pk']])
271 272 specialized_item = property(__get_specialized_item)
273 274 #------------------------------------------------------------
275 -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):
276 277 faulty_args = ( 278 (patient is None) and 279 (encounters is None) and 280 (episodes is None) and 281 (issues is None) and 282 (active_encounter is None) 283 ) 284 assert not faulty_args, 'one of <patient>, <episodes>, <issues>, <active_encounter> must not be None' 285 286 if (patient is not None) and (active_encounter is not None): 287 if patient != active_encounter['pk_patient']: 288 raise AssertionError('<patient> (%s) and <active_encounter>["pk_patient"] (%s) must match, if both given', patient, active_encounter['pk_patient']) 289 290 if order_by is None: 291 order_by = 'ORDER BY clin_when, pk_episode, scr, modified_when, src_table' 292 else: 293 order_by = 'ORDER BY %s' % order_by 294 295 if (patient is None) and (active_encounter is not None): 296 patient = active_encounter['pk_patient'] 297 298 where_parts = [] 299 args = {} 300 301 if patient is not None: 302 where_parts.append('c_vej.pk_patient = %(pat)s') 303 args['pat'] = patient 304 305 if soap_cats is not None: 306 # work around bug in psycopg2 not being able to properly 307 # adapt None to NULL inside tuples 308 if None in soap_cats: 309 where_parts.append('((c_vej.soap_cat IN %(soap_cat)s) OR (c_vej.soap_cat IS NULL))') 310 soap_cats.remove(None) 311 else: 312 where_parts.append('c_vej.soap_cat IN %(soap_cat)s') 313 args['soap_cat'] = tuple(soap_cats) 314 315 if time_range is not None: 316 where_parts.append("c_vej.clin_when > (now() - '%s days'::interval)" % time_range) 317 318 if encounters is not None: 319 where_parts.append("c_vej.pk_encounter IN %(encs)s") 320 args['encs'] = tuple(encounters) 321 322 if episodes is not None: 323 where_parts.append("c_vej.pk_episode IN %(epis)s") 324 args['epis'] = tuple(episodes) 325 326 if issues is not None: 327 where_parts.append("c_vej.pk_health_issue IN %(issues)s") 328 args['issues'] = tuple(issues) 329 330 cmd_journal = _SQL_get_generic_emr_items 331 if len(where_parts) > 0: 332 cmd_journal += '\nWHERE\n\t' 333 cmd_journal += '\t\tAND\t'.join(where_parts) 334 335 if active_encounter is None: 336 cmd = cmd_journal + '\n' + order_by 337 else: 338 args['pk_enc'] = active_encounter['pk_encounter'] 339 args['enc_start'] = active_encounter['started'] 340 args['enc_last_affirmed'] = active_encounter['last_affirmed'] 341 args['enc_type'] = active_encounter['l10n_type'] 342 args['enc_pat'] = active_encounter['pk_patient'] 343 cmd = __SQL_union % ( 344 cmd_journal, 345 _SQL_get_hints_as_generic_emr_items 346 ) + '\n' + order_by 347 348 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True) 349 if return_pks: 350 return [ { 351 'src_table': r['src_table'], 352 'src_pk': r['src_pk'] 353 } for r in rows ] 354 355 return [ cGenericEMRItem(row = { 356 'data': r, 357 'idx': idx, 358 'pk_obj': {'src_table': r['src_table'], 'src_pk': r['src_pk']} 359 } ) for r in rows ]
360 361 #------------------------------------------------------------
362 -def delete_xxx(pk_XXX=None):
363 # forward to specialized item 364 args = {'pk': pk_XXX} 365 cmd = u"DELETE FROM xxx.xxx WHERE pk = %(pk)s" 366 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}]) 367 return True
368 369 #------------------------------------------------------------
370 -def generic_item_type_str(table):
371 try: 372 return _MAP_generic_emr_item_table2type_str[table] 373 except KeyError: 374 return _('unmapped entry type from table [%s]') % table
375 376 #------------------------------------------------------------ 377 # widget code 378 # remember to add in clinical item generic workflows 379 #------------------------------------------------------------
380 -def edit_xxx(parent=None, xxx=None, single_entry=False, presets=None):
381 pass
382 #------------------------------------------------------------
383 -def delete_xxx():
384 pass
385 #------------------------------------------------------------
386 -def manage_xxx():
387 pass
388 #------------------------------------------------------------ 389 390 #============================================================ 391 # main - unit testing 392 #------------------------------------------------------------ 393 if __name__ == '__main__': 394 395 if len(sys.argv) < 2: 396 sys.exit() 397 398 if sys.argv[1] != 'test': 399 sys.exit() 400 401 # gmI18N.activate_locale() 402 # gmI18N.install_domain('gnumed') 403 404 #--------------------------------------------------------
405 - def test_gen_item():
406 for item in get_generic_emr_items ( 407 order_by = None, 408 patient = 12, 409 return_pks = False 410 ): 411 print('------------------------------') 412 print(item.format(eol = '\n')) 413 print(item.staff_id) 414 input('<next>')
415 #print(item.specialized_item) 416 #input('<next>') 417 418 #-------------------------------------------------------- 419 test_gen_item() 420