Gnumed.business.gmGenericEMRItem

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