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

# Source Code for Module Gnumed.business.gmAllergy

      1  # -*- coding: utf-8 -*-
      2  """GNUmed allergy related business object."""
      3  #============================================================
      4  __author__ = "Carlos Moro <cfmoro1976@yahoo.es>"
      5  __license__ = "GPL v2 or later"
      6
      7  import types, sys, logging, datetime as pyDT
      8
      9
     10  if __name__ == '__main__':
     11          sys.path.insert(0, '../../')
     12  from Gnumed.pycommon import gmPG2
     13  from Gnumed.pycommon import gmI18N
     14  from Gnumed.pycommon import gmBusinessDBObject
     15  from Gnumed.pycommon import gmDateTime
     16  from Gnumed.pycommon import gmTools
     17
     18
     19  _log = logging.getLogger('gm.domain')
     20  #============================================================
     21  # allergy state related code
     22  #============================================================
     23  allergy_states = [
     24          None,           # unknown
     25          0,                      # no allergies
     26          1                       # some allergies
     27  ]
     28  #------------------------------------------------------------
     29 -def ensure_has_allergy_state(encounter=None):
     30
     31          _log.debug('checking allergy state for identity of encounter [%s]', encounter)
     32
     33          args = {'enc': encounter}
     34          cmd_create = """
     35                  INSERT INTO clin.allergy_state (
     36                          fk_encounter,
     37                          has_allergy
     38                  )       SELECT
     39                                  %(enc)s,
     40                                  NULL
     41                          WHERE NOT EXISTS (
     42                                  SELECT 1 FROM clin.v_pat_allergy_state
     43                                  WHERE pk_patient = (
     44                                          SELECT fk_patient FROM clin.encounter WHERE pk = %(enc)s
     45                                  )
     46                          )
     47          """
     48          cmd_search = """
     49                  SELECT pk_allergy_state FROM clin.v_pat_allergy_state
     50                  WHERE pk_patient = (
     51                          SELECT fk_patient FROM clin.encounter WHERE pk = %(enc)s
     52                  )
     53          """
     54          rows, idx = gmPG2.run_rw_queries (
     55                  queries = [
     56                          {'cmd': cmd_create, 'args': args},
     57                          {'cmd': cmd_search, 'args': args}
     58                  ],
     59                  return_data = True
     60          )
     61
     62          return cAllergyState(aPK_obj = rows[0][0])
     63
     64  #------------------------------------------------------------
     65 -class cAllergyState(gmBusinessDBObject.cBusinessDBObject):
     66          """Represents the allergy state of one patient."""
     67
     68          _cmd_fetch_payload = "select * from clin.v_pat_allergy_state where pk_allergy_state = %s"
     69          _cmds_store_payload = [
     70                  """update clin.allergy_state set
     71                                  last_confirmed = %(last_confirmed)s,
     72                                  has_allergy = %(has_allergy)s,
     73                                  comment = gm.nullify_empty_string(%(comment)s)
     74                          where
     75                                  pk = %(pk_allergy_state)s and
     76                                  xmin = %(xmin_allergy_state)s""",
     77                  """select xmin_allergy_state from clin.v_pat_allergy_state where pk_allergy_state = %(pk_allergy_state)s"""
     78          ]
     79          _updatable_fields = [
     80                  'last_confirmed',               # special value u'now' will set to datetime.datetime.now() in the local time zone
     81                  'has_allergy',                  # verified against allergy_states (see above)
     82                  'comment'                               # u'' maps to None / NULL
     83          ]
     84
     85          #--------------------------------------------------------
     86 -        def format_maximum_information(self, patient=None):
     87                  lines = []
     88                  lines.append('%s (%s)' % (
     89                          self.state_string,
     90                          gmDateTime.pydt_strftime(self['last_confirmed'], '%Y %b %d', none_str = '?')
     91                  ))
     92                  if self._payload[self._idx['comment']] is not None:
     93                          lines.append(' %s' % self._payload[self._idx['comment']])
     94                  return lines
     95
     96          #--------------------------------------------------------
     97          # properties
     98          #--------------------------------------------------------
     99 -        def _get_as_string(self):
    100                  if self._payload[self._idx['has_allergy']] is None:
    101                          return _('unknown allergy state')
    102                  if self._payload[self._idx['has_allergy']] == 0:
    103                          return _('no known allergies')
    104                  if self._payload[self._idx['has_allergy']] == 1:
    105                          return _('*does* have allergies')
    106                  _log.error('unknown allergy state [%s]', self._payload[self._idx['has_allergy']])
    107                  return _('ERROR: unknown allergy state [%s]') % self._payload[self._idx['has_allergy']]
    108
    109 -        def _set_string(self, value):
    110                  raise AttributeError('invalid to set allergy state string')
    111
    112          state_string = property(_get_as_string, _set_string)
    113
    114          #--------------------------------------------------------
    115 -        def _get_as_symbol(self):
    116                  if self._payload[self._idx['has_allergy']] is None:
    117                          if self._payload[self._idx['comment']] is None:
    118                                  return '?'
    119                          else:
    120                                  return '?!'
    121                  if self._payload[self._idx['has_allergy']] == 0:
    122                          if self._payload[self._idx['comment']] is None:
    123                                  return '\u2300'
    124                          else:
    125                                  return '\u2300!'
    126                  if self._payload[self._idx['has_allergy']] == 1:
    127                          return '!'
    128                  _log.error('unknown allergy state [%s]', self._payload[self._idx['has_allergy']])
    129                  return _('ERROR: unknown allergy state [%s]') % self._payload[self._idx['has_allergy']]
    130
    131          state_symbol = property(_get_as_symbol, lambda x:x)
    132
    133          #--------------------------------------------------------
    134 -        def _get_as_amts_latex(self, strict=True):
    135                  table_rows = []
    136                  # Trennzeile als leere Zeile für bessere Lesbarkeit
    137                  table_rows.append('\\multicolumn{11}{l}{}\\tabularnewline')
    138                  # Zwischenüberschrift: 31 Zeichen, $..., 14pt, no frame, \textwidth
    139                  state = '%s (%s)' % (
    140                          self.state_string,
    141                          gmDateTime.pydt_strftime(self['last_confirmed'], '%b %Y', none_str = '?')
    142                  )
    143                  if strict:
    144                          state = state[:31]
    145                  table_rows.append('\\multicolumn{11}{>{\\RaggedRight}p{27.9cm}}{\\rule{0pt}{4.5mm} \\fontsize{14pt}{16pt}\selectfont %s\label{AnchorAllergieDetails}}\\tabularnewline' % gmTools.tex_escape_string(state))
    146                  # Freitextzeile: 200 Zeichen, @..., \textwidth
    147                  if self['comment'] is not None:
    148                          if strict:
    149                                  cmt = self['comment'].strip()[:200]
    150                          else:
    151                                  cmt = self['comment'].strip()
    152                          table_rows.append('\\multicolumn{11}{>{\\RaggedRight}p{27.9cm}}{%s}\\tabularnewline') % gmTools.tex_escape_string(cmt)
    153                  return table_rows
    154
    155          as_amts_latex = property(_get_as_amts_latex, lambda x:x)
    156
    157          #--------------------------------------------------------
    158 -        def _get_as_amts_data_v_2_0(self, strict=True):
    159                  lines = []
    160                  # Trennzeile für bessere Lesbarkeit als leere Zwischenüberschrift
    161                  lines.append('$ ')
    162                  # Zwischenüberschrift: 31 Zeichen, $..., \textwidth
    163                  txt = '$%s (%s)' % (
    164                          self.state_string,
    165                          gmDateTime.pydt_strftime(self['last_confirmed'], '%b %Y', none_str = '?')
    166                  )
    167                  if strict:
    168                          lines.append(txt[:32])
    169                  else:
    170                          lines.append(txt)
    171                  # Freitextzeile: 200 Zeichen, @..., \textwidth
    172                  if self['comment'] is not None:
    173                          if strict:
    174                                  lines.append('@%s' % self['comment'][:200])
    175                          else:
    176                                  lines.append('@%s' % self['comment'])
    177                  return lines
    178
    179          #--------------------------------------------------------
    180 -        def _get_as_amts_data(self, strict=True):
    181                  # Zwischenüberschrift
    182                  state = '%s (%s)' % (self.state_string, gmDateTime.pydt_strftime(self['last_confirmed'], '%b %Y', none_str = '?'))
    183                  if strict:
    184                          state = state[:32]
    185                  # Freitextzeile
    186                  if self['comment'] is None:
    187                          comment = ''
    188                  else:
    189                          comment = '<X t="%s"/>' % self['comment']
    190                          if strict:
    191                                  comment = '<X t="%s"/>' % self['comment'][:200]
    192                  return '<S t="%s">%s%%s</S>' % (state, comment)
    193
    194          as_amts_data = property(_get_as_amts_data, lambda x:x)
    195
    196          #--------------------------------------------------------
    197 -        def __setitem__(self, attribute, value):
    198                  if attribute == 'comment':
    199                          if value is not None:
    200                                  if value.strip() == '':
    201                                          value = None
    202
    203                  elif attribute == 'last_confirmed':
    204                          if value == 'now':
    205                                  value = pyDT.datetime.now(tz = gmDateTime.gmCurrentLocalTimezone)
    206
    207                  elif attribute == 'has_allergy':
    208                          if value not in allergy_states:
    209                                  raise ValueError('invalid allergy state [%s]' % value)
    210
    211                  gmBusinessDBObject.cBusinessDBObject.__setitem__(self, attribute, value)
    212
    213  #============================================================
    214 -class cAllergy(gmBusinessDBObject.cBusinessDBObject):
    215          """Represents one allergy item.
    216
    217          Actually, those things are really things to "avoid".
    218          Allergy is just one of several reasons for that.
    219          See Adrian's post on gm-dev.
    220
    221          Another word might be Therapeutic Precautions.
    222          """
    223          _cmd_fetch_payload = "SELECT * FROM clin.v_pat_allergies WHERE pk_allergy = %s"
    224          _cmds_store_payload = [
    225                  """UPDATE clin.allergy SET
    226                                  clin_when = %(date)s,
    227                                  substance = %(substance)s,
    228                                  substance_code = %(substance_code)s,
    229                                  generics = %(generics)s,
    230                                  allergene = %(allergene)s,
    231                                  atc_code = %(atc_code)s,
    232                                  fk_type = %(pk_type)s,
    233                                  generic_specific = %(generic_specific)s::boolean,
    234                                  definite = %(definite)s::boolean,
    235                                  narrative = %(reaction)s
    236                          WHERE
    237                                  pk = %(pk_allergy)s AND
    238                                  xmin = %(xmin_allergy)s""",
    239                  """SELECT xmin_allergy FROM clin.v_pat_allergies WHERE pk_allergy=%(pk_allergy)s"""
    240          ]
    241          _updatable_fields = [
    242                  'date',
    243                  'substance',
    244                  'substance_code',
    245                  'generics',
    246                  'allergene',
    247                  'atc_code',
    248                  'pk_type',
    249                  'generic_specific',
    250                  'definite',
    251                  'reaction'
    252          ]
    253          #--------------------------------------------------------
    254 -        def format_maximum_information(self, patient=None):
    255                  lines = []
    256                  lines.append('%s%s: %s     [#%s]' % (
    257                          self._payload[self._idx['l10n_type']],
    258                          gmTools.bool2subst (
    259                                  self._payload[self._idx['definite']],
    260                                  ' (%s)' % _('definite'),
    261                                  ' (%s)' % _('indefinite'),
    262                                  ''
    263                          ),
    264                          self._payload[self._idx['descriptor']],
    265                          self._payload[self._idx['pk_allergy']]
    266                  ))
    267                  if self._payload[self._idx['reaction']] is not None:
    268                          lines.append(' ' + _('Reaction:') + ' ' + self._payload[self._idx['reaction']])
    269                  if self._payload[self._idx['date']] is not None:
    270                          lines.append(' ' + _('Noted:') + ' ' + gmDateTime.pydt_strftime(self._payload[self._idx['date']], '%Y %b %d'))
    271                  if self._payload[self._idx['allergene']] is not None:
    272                          lines.append(' ' + _('Allergene:') + ' ' + self._payload[self._idx['allergene']])
    273                  if self._payload[self._idx['substance']] is not None:
    274                          lines.append(' ' + _('Substance:') + ' ' + self._payload[self._idx['substance']])
    275                  if self._payload[self._idx['substance_code']] is not None:
    276                          lines.append(' ' + _('Code:') + ' ' + self._payload[self._idx['substance_code']])
    277                  if self._payload[self._idx['atc_code']] is not None:
    278                          lines.append(' ' + _('ATC:') + ' ' + self._payload[self._idx['atc_code']])
    279                  lines.append(' ' + _('Specific to:') + ' ' + gmTools.bool2subst (
    280                          self._payload[self._idx['generic_specific']],
    281                          _('this substance only'),
    282                          _('drug class'),
    283                          _('unknown')
    284                  ))
    285                  if self._payload[self._idx['generics']] is not None:
    286                          lines.append(' ' + _('Generics:') + ' ' + self._payload[self._idx['generics']])
    287
    288                  return lines
    289
    290          #--------------------------------------------------------
    291 -        def __setitem__(self, attribute, value):
    292                  if attribute == 'pk_type':
    293                          if value in ['allergy', 'sensitivity']:
    294                                  cmd = 'select pk from clin._enum_allergy_type where value=%s'
    295                                  rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [value]}])
    296                                  value = rows[0][0]
    297
    298                  gmBusinessDBObject.cBusinessDBObject.__setitem__(self, attribute, value)
    299
    300          #--------------------------------------------------------
    301 -        def _get_as_amts_latex(self, strict=True):
    302                  # Freitextzeile: 200 Zeichen, @...
    303                  cells = ['\\multicolumn{1}{>{\\RaggedRight}p{4cm}}{%s}' % gmTools.tex_escape_string(self['descriptor'])]
    304                  txt = '%s%s' % (
    305                          self['l10n_type'],
    306                          gmTools.coalesce(self['reaction'], '', ': %s')
    307                  )
    308                  if strict:
    309                          txt = txt[:(200-len(self['descriptor']))]
    310                  cells.append('\\multicolumn{10}{>{\\RaggedRight}p{23.9cm}}{%s}' % gmTools.tex_escape_string(txt))
    311                  table_row = ' & '.join(cells)
    312                  table_row += '\\tabularnewline'
    313                  return table_row
    314
    315          as_amts_latex = property(_get_as_amts_latex, lambda x:x)
    316
    317          #--------------------------------------------------------
    318 -        def _get_as_amts_data_v_2_0(self, strict=True):
    319                  # Freitextzeile: 200 Zeichen, @..., \textwidth
    320                  txt = '@%s %s%s' % (
    321                          self['descriptor'],
    322                          self['l10n_type'],
    323                          gmTools.coalesce(self['reaction'], '', ': %s')
    324                  )
    325                  if strict:
    326                          return txt[:200]
    327                  return txt
    328
    329          #--------------------------------------------------------
    330 -        def _get_as_amts_data(self, strict=True):
    331                  txt = '%s %s%s' % (
    332                          self['descriptor'],
    333                          self['l10n_type'],
    334                          gmTools.coalesce(self['reaction'], '', ': %s')
    335                  )
    336                  if strict:
    337                          txt = txt[:200]
    338                  # Freitextzeile: 200 Zeichen
    339                  return '<X t="%s"/>' % txt
    340
    341          as_amts_data = property(_get_as_amts_data, lambda x:x)
    342
    343  #============================================================
    344  # convenience functions
    345  #------------------------------------------------------------
    346 -def create_allergy(allergene=None, allg_type=None, episode_id=None, encounter_id=None):
    347          """Creates a new allergy clinical item.
    348
    349          allergene - allergic substance
    350          allg_type - allergy or sensitivity, pk or string
    351          encounter_id - encounter's primary key
    352          episode_id - episode's primary key
    353          """
    354          cmd = """
    355                  SELECT pk_allergy
    356                  FROM clin.v_pat_allergies
    357                  WHERE
    358                          pk_patient = (SELECT fk_patient FROM clin.encounter WHERE pk = %(enc)s)
    359                                  AND
    360                          allergene = %(allergene)s
    361          """
    362          #args = {'enc': encounter_id, 'substance': substance}
    363          args = {'enc': encounter_id, 'allergene': allergene}
    364          rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
    365          if len(rows) > 0:
    366                  # don't implicitely change existing data
    367                  return cAllergy(aPK_obj = rows[0][0])
    368
    369          # insert new allergy
    370          queries = []
    371
    372          if type(allg_type) == int:
    373                  cmd = """
    374                          insert into clin.allergy (fk_type, fk_encounter, fk_episode, allergene, substance)
    375                          values (%s, %s, %s, %s, %s)"""
    376          else:
    377                  cmd = """
    378                          insert into clin.allergy (fk_type, fk_encounter, fk_episode,  allergene, substance)
    379                          values ((select pk from clin._enum_allergy_type where value = %s), %s, %s, %s, %s)"""
    380          queries.append({'cmd': cmd, 'args': [allg_type, encounter_id, episode_id, allergene, allergene]})
    381
    382          cmd = "select currval('clin.allergy_id_seq')"
    383          queries.append({'cmd': cmd})
    384
    385          rows, idx = gmPG2.run_rw_queries(queries=queries, return_data=True)
    386          allergy = cAllergy(aPK_obj = rows[0][0])
    387
    388          return allergy
    389
    390  #============================================================
    391  # main - unit testing
    392  #------------------------------------------------------------
    393  if __name__ == '__main__':
    394
    395          allg = cAllergy(aPK_obj=1)
    396          print(allg)
    397          fields = allg.get_fields()
    398          for field in fields:
    399                  print(field, ':', allg[field])
    400          print("updatable:", allg.get_updatable_fields())
    401          enc_id = allg['pk_encounter']
    402          epi_id = allg['pk_episode']
    403          status, allg = create_allergy (
    404                  allergene = 'test substance',
    405                  allg_type = 1,
    406                  episode_id = epi_id,
    407                  encounter_id = enc_id
    408          )
    409          print(allg)
    410          allg['reaction'] = 'hehehe'
    411          status, data = allg.save_payload()
    412          print('status:', status)
    413          print('data:', data)
    414          print(allg)
    415