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

# Source Code for Module Gnumed.business.gmMedication

       1  # -*- coding: utf8 -*-
       2  """Medication handling code.
       3
       4  license: GPL v2 or later
       5  """
       6  #============================================================
       7  __version__ = "$Revision: 1.21 $"
       8  __author__ = "K.Hilbert <Karsten.Hilbert@gmx.net>"
       9
      10  import sys
      11  import logging
      12  import csv
      13  import codecs
      14  import os
      15  import re as regex
      16  import subprocess
      17  import decimal
      18  from xml.etree import ElementTree as etree
      19
      20
      21  if __name__ == '__main__':
      22          sys.path.insert(0, '../../')
      23          _ = lambda x:x
      24  from Gnumed.pycommon import gmBusinessDBObject
      25  from Gnumed.pycommon import gmTools
      26  from Gnumed.pycommon import gmShellAPI
      27  from Gnumed.pycommon import gmPG2
      28  from Gnumed.pycommon import gmDispatcher
      29  from Gnumed.pycommon import gmMatchProvider
      30  from Gnumed.pycommon import gmHooks
      31  from Gnumed.pycommon import gmDateTime
      32
      33  from Gnumed.business import gmATC
      34  from Gnumed.business import gmAllergy
      35  from Gnumed.business.gmDocuments import DOCUMENT_TYPE_PRESCRIPTION
      36  from Gnumed.business.gmDocuments import create_document_type
      37
      38
      39  _log = logging.getLogger('gm.meds')
      40  _log.info(__version__)
      41
      42
      43  DEFAULT_MEDICATION_HISTORY_EPISODE = _('Medication history')
      44  #============================================================
      45 -def _on_substance_intake_modified():
      46          """Always relates to the active patient."""
      47          gmHooks.run_hook_script(hook = u'after_substance_intake_modified')
      48
      49  gmDispatcher.connect(_on_substance_intake_modified, u'substance_intake_mod_db')
      50
      51  #============================================================
      52 -def drug2renal_insufficiency_url(search_term=None):
      53
      54          if search_term is None:
      55                  return u'http://www.dosing.de'
      56
      57          terms = []
      58          names = []
      59
      60          if isinstance(search_term, cBrandedDrug):
      61                  if search_term['atc'] is not None:
      62                          terms.append(search_term['atc'])
      63
      64          elif isinstance(search_term, cSubstanceIntakeEntry):
      65                  names.append(search_term['substance'])
      66                  if search_term['atc_brand'] is not None:
      67                          terms.append(search_term['atc_brand'])
      68                  if search_term['atc_substance'] is not None:
      69                          terms.append(search_term['atc_substance'])
      70
      71          elif search_term is not None:
      72                  names.append(u'%s' % search_term)
      73                  terms.extend(gmATC.text2atc(text = u'%s' % search_term, fuzzy = True))
      74
      75          for name in names:
      76                  if name.endswith('e'):
      77                          terms.append(name[:-1])
      78                  else:
      79                          terms.append(name)
      80
      81          #url_template = u'http://www.google.de/#q=site%%3Adosing.de+%s'
      82          #url = url_template % u'+OR+'.join(terms)
      83
      84          url_template = u'http://www.google.com/search?hl=de&source=hp&q=site%%3Adosing.de+%s&btnG=Google-Suche'
      85          url = url_template % u'+OR+'.join(terms)
      86
      87          _log.debug(u'renal insufficiency URL: %s', url)
      88
      89          return url
      90  #============================================================
      91  # this should be in gmCoding.py
      92 -def create_data_source(long_name=None, short_name=None, version=None, source=None, language=None):
      93
      94                  args = {
      95                          'lname': long_name,
      96                          'sname': short_name,
      97                          'ver': version,
      98                          'src': source,
      99                          'lang': language
     100                  }
     101
     102                  cmd = u"""select pk from ref.data_source where name_long = %(lname)s and name_short = %(sname)s and version = %(ver)s"""
     103                  rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
     104                  if len(rows) > 0:
     105                          return rows[0]['pk']
     106
     107                  cmd = u"""
     108                          INSERT INTO ref.data_source (name_long, name_short, version, source, lang)
     109                          VALUES (
     110                                  %(lname)s,
     111                                  %(sname)s,
     112                                  %(ver)s,
     113                                  %(src)s,
     114                                  %(lang)s
     115                          )
     116                          returning pk
     117                          """
     118                  rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True)
     119
     120                  return rows[0]['pk']
     121  #============================================================
     122  # wishlist:
     123  # - --conf-file= for glwin.exe
     124  # - wirkstoff: Konzentration auch in Multiprodukten
     125  # - wirkstoff: ATC auch in Multiprodukten
     126  # - Suche nach ATC per CLI
     127
     128 -class cGelbeListeCSVFile(object):
     129          """Iterator over a Gelbe Liste/MMI v8.2 CSV file."""
     130
     131          version = u'Gelbe Liste/MMI v8.2 CSV file interface'
     132          default_transfer_file_windows = r"c:\rezept.txt"
     133          #default_encoding = 'cp1252'
     134          default_encoding = 'cp1250'
     135          csv_fieldnames = [
     136                  u'name',
     137                  u'packungsgroesse',                                     # obsolete, use "packungsmenge"
     138                  u'darreichungsform',
     139                  u'packungstyp',
     140                  u'festbetrag',
     141                  u'avp',
     142                  u'hersteller',
     143                  u'rezepttext',
     144                  u'pzn',
     145                  u'status_vertrieb',
     146                  u'status_rezeptpflicht',
     147                  u'status_fachinfo',
     148                  u'btm',
     149                  u'atc',
     150                  u'anzahl_packungen',
     151                  u'zuzahlung_pro_packung',
     152                  u'einheit',
     153                  u'schedule_morgens',
     154                  u'schedule_mittags',
     155                  u'schedule_abends',
     156                  u'schedule_nachts',
     157                  u'status_dauermedikament',
     158                  u'status_hausliste',
     159                  u'status_negativliste',
     160                  u'ik_nummer',
     161                  u'status_rabattvertrag',
     162                  u'wirkstoffe',
     163                  u'wirkstoffmenge',
     164                  u'wirkstoffeinheit',
     165                  u'wirkstoffmenge_bezug',
     166                  u'wirkstoffmenge_bezugseinheit',
     167                  u'status_import',
     168                  u'status_lifestyle',
     169                  u'status_ausnahmeliste',
     170                  u'packungsmenge',
     171                  u'apothekenpflicht',
     172                  u'status_billigere_packung',
     173                  u'rezepttyp',
     174                  u'besonderes_arzneimittel',                     # Abstimmungsverfahren SGB-V
     175                  u't_rezept_pflicht',                            # Thalidomid-Rezept
     176                  u'erstattbares_medizinprodukt',
     177                  u'hilfsmittel',
     178                  u'hzv_rabattkennung',
     179                  u'hzv_preis'
     180          ]
     181          boolean_fields = [
     182                  u'status_rezeptpflicht',
     183                  u'status_fachinfo',
     184                  u'btm',
     185                  u'status_dauermedikament',
     186                  u'status_hausliste',
     187                  u'status_negativliste',
     188                  u'status_rabattvertrag',
     189                  u'status_import',
     190                  u'status_lifestyle',
     191                  u'status_ausnahmeliste',
     192                  u'apothekenpflicht',
     193                  u'status_billigere_packung',
     194                  u'besonderes_arzneimittel',                     # Abstimmungsverfahren SGB-V
     195                  u't_rezept_pflicht',
     196                  u'erstattbares_medizinprodukt',
     197                  u'hilfsmittel'
     198          ]
     199          #--------------------------------------------------------
     200 -        def __init__(self, filename=None):
     201
     202                  _log.info(cGelbeListeCSVFile.version)
     203
     204                  self.filename = filename
     205                  if filename is None:
     206                          self.filename = cGelbeListeCSVFile.default_transfer_file_windows
     207
     208                  _log.debug('reading Gelbe Liste/MMI drug data from [%s]', self.filename)
     209
     210                  self.csv_file = codecs.open(filename = filename, mode = 'rUb', encoding = cGelbeListeCSVFile.default_encoding)
     211
     212                  self.csv_lines = gmTools.unicode_csv_reader (
     213                          self.csv_file,
     214                          fieldnames = cGelbeListeCSVFile.csv_fieldnames,
     215                          delimiter = ';',
     216                          quotechar = '"',
     217                          dict = True
     218                  )
     219          #--------------------------------------------------------
     220 -        def __iter__(self):
     221                  return self
     222          #--------------------------------------------------------
     223 -        def next(self):
     224                  line = self.csv_lines.next()
     225
     226                  for field in cGelbeListeCSVFile.boolean_fields:
     227                          line[field] = (line[field].strip() == u'T')
     228
     229                  # split field "Wirkstoff" by ";"
     230                  if line['wirkstoffe'].strip() == u'':
     231                          line['wirkstoffe'] = []
     232                  else:
     233                          line['wirkstoffe'] = [ wirkstoff.strip() for wirkstoff in line['wirkstoffe'].split(u';') ]
     234
     235                  return line
     236          #--------------------------------------------------------
     237 -        def close(self, truncate=True):
     238                  try: self.csv_file.close()
     239                  except: pass
     240
     241                  if truncate:
     242                          try: os.open(self.filename, 'wb').close
     243                          except: pass
     244          #--------------------------------------------------------
     245 -        def _get_has_unknown_fields(self):
     246                  return (gmTools.default_csv_reader_rest_key in self.csv_fieldnames)
     247
     248          has_unknown_fields = property(_get_has_unknown_fields, lambda x:x)
     249  #============================================================
     250 -class cDrugDataSourceInterface(object):
     251
     252          #--------------------------------------------------------
     253 -        def __init__(self):
     254                  self.patient = None
     255                  self.reviewer = None
     256                  self.custom_path_to_binary = None
     257          #--------------------------------------------------------
     258 -        def get_data_source_version(self):
     259                  raise NotImplementedError
     260          #--------------------------------------------------------
     261 -        def create_data_source_entry(self):
     262                  raise NotImplementedError
     263          #--------------------------------------------------------
     264 -        def switch_to_frontend(self, blocking=False):
     265                  raise NotImplementedError
     266          #--------------------------------------------------------
     267 -        def import_drugs(self):
     268                  self.switch_to_frontend()
     269          #--------------------------------------------------------
     270 -        def check_interactions(self, substance_intakes=None):
     271                  self.switch_to_frontend()
     272          #--------------------------------------------------------
     273 -        def show_info_on_drug(self, substance_intake=None):
     274                  self.switch_to_frontend()
     275          #--------------------------------------------------------
     276 -        def show_info_on_substance(self, substance_intake=None):
     277                  self.switch_to_frontend()
     278          #--------------------------------------------------------
     279 -        def prescribe(self, substance_intakes=None):
     280                  self.switch_to_frontend()
     281                  return []
     282  #============================================================
     283 -class cFreeDiamsInterface(cDrugDataSourceInterface):
     284
     285          version = u'FreeDiams v0.5.4 interface'
     286          default_encoding = 'utf8'
     287          default_dob_format = '%Y/%m/%d'
     288
     289          map_gender2mf = {
     290                  'm': u'M',
     291                  'f': u'F',
     292                  'tf': u'H',
     293                  'tm': u'H',
     294                  'h': u'H'
     295          }
     296          #--------------------------------------------------------
     297 -        def __init__(self):
     298                  cDrugDataSourceInterface.__init__(self)
     299                  _log.info(cFreeDiamsInterface.version)
     300
     301                  self.__imported_drugs = []
     302
     303                  self.__gm2fd_filename = gmTools.get_unique_filename(prefix = r'gm2freediams-', suffix = r'.xml')
     304                  _log.debug('GNUmed -> FreeDiams "exchange-in" file: %s', self.__gm2fd_filename)
     305                  self.__fd2gm_filename = gmTools.get_unique_filename(prefix = r'freediams2gm-', suffix = r'.xml')
     306                  _log.debug('GNUmed <-> FreeDiams "exchange-out"/"prescription" file: %s', self.__fd2gm_filename)
     307                  paths = gmTools.gmPaths()
     308                  self.__fd4gm_config_file = os.path.join(paths.home_dir, '.gnumed', 'freediams4gm.conf')
     309
     310                  self.path_to_binary = None
     311                  self.__detect_binary()
     312          #--------------------------------------------------------
     313 -        def get_data_source_version(self):
     314                  # ~/.freediams/config.ini: [License] -> AcceptedVersion=....
     315
     316                  if not self.__detect_binary():
     317                          return False
     318
     319                  freediams = subprocess.Popen (
     320                          args = u'--version',                            # --version or -version or -v
     321                          executable = self.path_to_binary,
     322                          stdout = subprocess.PIPE,
     323                          stderr = subprocess.PIPE,
     324  #                       close_fds = True,                                       # Windows can't do that in conjunction with stdout/stderr = ... :-(
     325                          universal_newlines = True
     326                  )
     327                  data, errors = freediams.communicate()
     328                  version = regex.search('FreeDiams\s\d.\d.\d', data).group().split()[1]
     329                  _log.debug('FreeDiams %s', version)
     330
     331                  return version
     332          #--------------------------------------------------------
     333 -        def create_data_source_entry(self):
     334                  return create_data_source (
     335                          long_name = u'"FreeDiams" Drug Database Frontend',
     336                          short_name = u'FreeDiams',
     337                          version = self.get_data_source_version(),
     338                          source = u'http://ericmaeker.fr/FreeMedForms/di-manual/index.html',
     339                          language = u'fr'                        # actually to be multi-locale
     340                  )
     341          #--------------------------------------------------------
     342 -        def switch_to_frontend(self, blocking=False, mode='interactions'):
     343                  """http://ericmaeker.fr/FreeMedForms/di-manual/en/html/ligne_commandes.html"""
     344
     345                  _log.debug('calling FreeDiams in [%s] mode', mode)
     346
     347                  self.__imported_drugs = []
     348
     349                  if not self.__detect_binary():
     350                          return False
     351
     352                  self.__create_gm2fd_file(mode = mode)
     353
     354                  args = u'--exchange-in="%s"' % (self.__gm2fd_filename)
     355                  cmd = r'%s %s' % (self.path_to_binary, args)
     356                  if os.name == 'nt':
     357                          blocking = True
     358                  if not gmShellAPI.run_command_in_shell(command = cmd, blocking = blocking):
     359                          _log.error('problem switching to the FreeDiams drug database')
     360                          return False
     361
     362                  if blocking == True:
     363                          self.import_fd2gm_file_as_drugs()
     364
     365                  return True
     366          #--------------------------------------------------------
     367 -        def import_drugs(self):
     368                  self.switch_to_frontend(blocking = True)
     369          #--------------------------------------------------------
     370 -        def check_interactions(self, substance_intakes=None):
     371                  if substance_intakes is None:
     372                          return
     373                  if len(substance_intakes) < 2:
     374                          return
     375
     376                  self.__create_prescription_file(substance_intakes = substance_intakes)
     377                  self.switch_to_frontend(mode = 'interactions', blocking = False)
     378          #--------------------------------------------------------
     379 -        def show_info_on_drug(self, substance_intake=None):
     380                  if substance_intake is None:
     381                          return
     382
     383                  self.__create_prescription_file(substance_intakes = [substance_intake])
     384                  self.switch_to_frontend(mode = 'interactions', blocking = False)
     385          #--------------------------------------------------------
     386 -        def show_info_on_substance(self, substance_intake=None):
     387                  self.show_info_on_drug(substance_intake = substance_intake)
     388          #--------------------------------------------------------
     389 -        def prescribe(self, substance_intakes=None):
     390                  if substance_intakes is None:
     391                          if not self.__export_latest_prescription():
     392                                  self.__create_prescription_file()
     393                  else:
     394                          self.__create_prescription_file(substance_intakes = substance_intakes)
     395
     396                  self.switch_to_frontend(mode = 'prescription', blocking = True)
     397                  self.import_fd2gm_file_as_prescription()
     398
     399                  return self.__imported_drugs
     400          #--------------------------------------------------------
     401          # internal helpers
     402          #--------------------------------------------------------
     403 -        def __detect_binary(self):
     404
     405                  if self.path_to_binary is not None:
     406                          return True
     407
     408                  found, cmd = gmShellAPI.find_first_binary(binaries = [
     409                          r'/usr/bin/freediams',
     410                          r'freediams',
     411                          r'/Applications/FreeDiams.app/Contents/MacOs/FreeDiams',
     412                          r'C:\Program Files (x86)\FreeDiams\freediams.exe',
     413                          r'C:\Program Files\FreeDiams\freediams.exe',
     414                          r'c:\programs\freediams\freediams.exe',
     415                          r'freediams.exe'
     416                  ])
     417
     418                  if found:
     419                          self.path_to_binary = cmd
     420                          return True
     421
     422                  try:
     423                          self.custom_path_to_binary
     424                  except AttributeError:
     425                          _log.error('cannot find FreeDiams binary, no custom path set')
     426                          return False
     427
     428                  if self.custom_path_to_binary is None:
     429                          _log.error('cannot find FreeDiams binary')
     430                          return False
     431
     432                  found, cmd = gmShellAPI.detect_external_binary(binary = self.custom_path_to_binary)
     433                  if found:
     434                          self.path_to_binary = cmd
     435                          return True
     436
     437                  _log.error('cannot find FreeDiams binary')
     438                  return False
     439          #--------------------------------------------------------
     440 -        def __export_latest_prescription(self):
     441
     442                  if self.patient is None:
     443                          _log.debug('cannot export latest FreeDiams prescriptions w/o patient')
     444                          return False
     445
     446                  docs = self.patient.get_document_folder()
     447                  prescription = docs.get_latest_freediams_prescription()
     448                  if prescription is None:
     449                          _log.debug('no FreeDiams prescription available')
     450                          return False
     451
     452                  for part in prescription.parts:
     453                          if part['filename'] == u'freediams-prescription.xml':
     454                                  if part.export_to_file(filename = self.__fd2gm_filename) is not None:
     455                                          return True
     456
     457                  _log.error('cannot export latest FreeDiams prescription to XML file')
     458
     459                  return False
     460          #--------------------------------------------------------
     461 -        def __create_prescription_file(self, substance_intakes=None):
     462                  """FreeDiams calls this exchange-out or prescription file.
     463
     464                          CIS stands for Unique Speciality Identifier (eg bisoprolol 5 mg, gel).
     465                          CIS is AFSSAPS specific, but pharmacist can retreive drug name with the CIS.
     466                          AFSSAPS is the French FDA.
     467
     468                          CIP stands for Unique Presentation Identifier (eg 30 pills plaq)
     469                          CIP if you want to specify the packaging of the drug (30 pills
     470                          thermoformed tablet...) -- actually not really usefull for french
     471                          doctors.
     472                          # .external_code_type: u'FR-CIS'
     473                          # .external_cod: the CIS value
     474
     475                  OnlyForTest:
     476                          OnlyForTest drugs will be processed by the IA Engine but
     477                          not printed (regardless of FreeDiams mode). They are shown
     478                          in gray in the prescription view.
     479
     480                          Select-only is a mode where FreeDiams creates a list of drugs
     481                          not a full prescription. In this list, users can add ForTestOnly
     482                          drug if they want to
     483                                  1. print the list without some drugs
     484                                  2. but including these drugs in the IA engine calculation
     485
     486                          Select-Only mode does not have any relation with the ForTestOnly drugs.
     487
     488                  IsTextual:
     489                          What is the use and significance of the
     490                                  <IsTextual>true/false</IsTextual>
     491                          flag when both <DrugName> and <TextualDrugName> exist ?
     492
     493                          This tag must be setted even if it sounds like a duplicated
     494                          data. This tag is needed inside FreeDiams code.
     495
     496                  INN:
     497                          GNUmed will pass the substance in <TextualDrugName
     498                          and will also pass <INN>True</INN>.
     499
     500                          Eric:   Nop, this is not usefull because pure textual drugs
     501                                          are not processed but just shown.
     502                  """
     503                  # virginize file
     504                  open(self.__fd2gm_filename, 'wb').close()
     505
     506                  # make sure we've got something to do
     507                  if substance_intakes is None:
     508                          if self.patient is None:
     509                                  _log.warning('cannot create prescription file because there is neither a patient nor a substance intake list')
     510                                  # do fail because __export_latest_prescription() should not have been called without patient
     511                                  return False
     512                          emr = self.patient.get_emr()
     513                          substance_intakes = emr.get_current_substance_intake (
     514                                  include_inactive = False,
     515                                  include_unapproved = True
     516                          )
     517
     518                  drug_snippets = []
     519
     520                  # process FD drugs
     521                  fd_intakes = [ i for i in substance_intakes if (
     522                          (i['intake_is_approved_of'] is True)
     523                                  and
     524                          (i['external_code_type_brand'] is not None)
     525                                  and
     526                          (i['external_code_type_brand'].startswith(u'FreeDiams::'))
     527                  )]
     528
     529                  intakes_pooled_by_brand = {}
     530                  for intake in fd_intakes:
     531                          # this will leave only one entry per brand
     532                          # but FreeDiams knows the components ...
     533                          intakes_pooled_by_brand[intake['brand']] = intake
     534                  del fd_intakes
     535
     536                  drug_snippet = u"""<Prescription>
     537                          <IsTextual>False</IsTextual>
     538                          <DrugName>%s</DrugName>
     539                          <Drug_UID>%s</Drug_UID>
     540                          <Drug_UID_type>%s</Drug_UID_type>               <!-- not yet supported by FreeDiams -->
     541                  </Prescription>"""
     542
     543                  last_db_id = u'CA_HCDPD'
     544                  for intake in intakes_pooled_by_brand.values():
     545                          last_db_id = gmTools.xml_escape_string(text = intake['external_code_type_brand'].replace(u'FreeDiams::', u'').split(u'::')[0])
     546                          drug_snippets.append(drug_snippet % (
     547                                  gmTools.xml_escape_string(text = intake['brand'].strip()),
     548                                  gmTools.xml_escape_string(text = intake['external_code_brand'].strip()),
     549                                  last_db_id
     550                          ))
     551
     552                  # process non-FD drugs
     553                  non_fd_intakes = [ i for i in substance_intakes if (
     554                          (i['intake_is_approved_of'] is True)
     555                          and (
     556                                  (i['external_code_type_brand'] is None)
     557                                          or
     558                                  (not i['external_code_type_brand'].startswith(u'FreeDiams::'))
     559                          )
     560                  )]
     561
     562                  non_fd_brand_intakes = [ i for i in non_fd_intakes if i['brand'] is not None ]
     563                  non_fd_substance_intakes = [ i for i in non_fd_intakes if i['brand'] is None ]
     564                  del non_fd_intakes
     565
     566                  drug_snippet = u"""<Prescription>
     567                          <IsTextual>True</IsTextual>
     568                          <TextualDrugName>%s</TextualDrugName>
     569                  </Prescription>"""
     570
     571                  for intake in non_fd_substance_intakes:
     572                          drug_name = u'%s %s%s (%s)%s' % (
     573                                  intake['substance'],
     574                                  intake['amount'],
     575                                  intake['unit'],
     576                                  intake['preparation'],
     577                                  gmTools.coalesce(intake['schedule'], u'', _('\n Take: %s'))
     578                          )
     579                          drug_snippets.append(drug_snippet % gmTools.xml_escape_string(text = drug_name.strip()))
     580
     581                  intakes_pooled_by_brand = {}
     582                  for intake in non_fd_brand_intakes:
     583                          brand = u'%s %s' % (intake['brand'], intake['preparation'])
     584                          try:
     585                                  intakes_pooled_by_brand[brand].append(intake)
     586                          except KeyError:
     587                                  intakes_pooled_by_brand[brand] = [intake]
     588
     589                  for brand, comps in intakes_pooled_by_brand.iteritems():
     590                          drug_name = u'%s\n' % brand
     591                          for comp in comps:
     592                                  drug_name += u'  %s %s%s\n' % (
     593                                          comp['substance'],
     594                                          comp['amount'],
     595                                          comp['unit']
     596                          )
     597                          if comps[0]['schedule'] is not None:
     598                                  drug_name += gmTools.coalesce(comps[0]['schedule'], u'', _('Take: %s'))
     599                          drug_snippets.append(drug_snippet % gmTools.xml_escape_string(text = drug_name.strip()))
     600
     601                  # assemble XML file
     602                  xml = u"""<?xml version = "1.0" encoding = "UTF-8"?>
     603
     604  <FreeDiams>
     605          <DrugsDatabaseName>%s</DrugsDatabaseName>
     606          <FullPrescription version="0.5.0">
     607
     608                  %s
     609
     610          </FullPrescription>
     611  </FreeDiams>
     612  """
     613
     614                  xml_file = codecs.open(self.__fd2gm_filename, 'wb', 'utf8')
     615                  xml_file.write(xml % (
     616                          last_db_id,
     617                          u'\n\t\t'.join(drug_snippets)
     618                  ))
     619                  xml_file.close()
     620
     621                  return True
     622          #--------------------------------------------------------
     623 -        def __create_gm2fd_file(self, mode='interactions'):
     624
     625                  if mode == 'interactions':
     626                          mode = u'select-only'
     627                  elif mode == 'prescription':
     628                          mode = u'prescriber'
     629                  else:
     630                          mode = u'select-only'
     631
     632                  xml_file = codecs.open(self.__gm2fd_filename, 'wb', 'utf8')
     633
     634                  xml = u"""<?xml version="1.0" encoding="UTF-8"?>
     635
     636  <FreeDiams_In version="0.5.0">
     637          <EMR name="GNUmed" uid="unused"/>
     638          <ConfigFile value="%s"/>
     639          <ExchangeOut value="%s" format="xml"/>
     640          <!-- <DrugsDatabase uid="can be set to a specific DB"/> -->
     641          <Ui editmode="%s" blockPatientDatas="1"/>
     642          %%s
     643  </FreeDiams_In>
     644
     645  <!--
     646                  # FIXME: search by LOINC code and add (as soon as supported by FreeDiams ...)
     647                  <Creatinine value="12" unit="mg/l or mmol/l"/>
     648                  <Weight value="70" unit="kg or pd" />
     649                  <Height value="170" unit="cm or "/>
     650                  <ICD10 value="J11.0;A22;Z23"/>
     651  -->
     652  """             % (
     653                          self.__fd4gm_config_file,
     654                          self.__fd2gm_filename,
     655                          mode
     656                  )
     657
     658                  if self.patient is None:
     659                          xml_file.write(xml % u'')
     660                          xml_file.close()
     661                          return
     662
     663                  name = self.patient.get_active_name()
     664                  if self.patient['dob'] is None:
     665                          dob = u''
     666                  else:
     667                          dob = self.patient['dob'].strftime(cFreeDiamsInterface.default_dob_format)
     668
     669                  emr = self.patient.get_emr()
     670                  allgs = emr.get_allergies()
     671                  atc_allgs = [
     672                          a['atc_code'] for a in allgs if ((a['atc_code'] is not None) and (a['type'] == u'allergy'))
     673                  ]
     674                  atc_sens = [
     675                          a['atc_code'] for a in allgs if ((a['atc_code'] is not None) and (a['type'] == u'sensitivity'))
     676                  ]
     677                  inn_allgs = [
     678                          a['allergene'] for a in allgs if ((a['allergene'] is not None) and (a['type'] == u'allergy'))
     679                  ]
     680                  inn_sens = [
     681                          a['allergene'] for a in allgs if ((a['allergene'] is not None) and (a['type'] == u'sensitivity'))
     682                  ]
     683                  # this is rather fragile: FreeDiams won't know what type of UID this is
     684                  # (but it will assume it is of the type of the drug database in use)
     685                  # but eventually FreeDiams puts all drugs into one database :-)
     686                  uid_allgs = [
     687                          a['substance_code'] for a in allgs if ((a['substance_code'] is not None) and (a['type'] == u'allergy'))
     688                  ]
     689                  uid_sens = [
     690                          a['substance_code'] for a in allgs if ((a['substance_code'] is not None) and (a['type'] == u'sensitivity'))
     691                  ]
     692
     693                  patient_xml = u"""<Patient>
     694                  <Identity
     695                            lastnames="%s"
     696                            firstnames="%s"
     697                            uid="%s"
     698                            dob="%s"
     699                            gender="%s"
     700                  />
     701                  <ATCAllergies value="%s"/>
     702                  <ATCIntolerances value="%s"/>
     703
     704                  <InnAllergies value="%s"/>
     705                  <InnIntolerances value="%s"/>
     706
     707                  <DrugsUidAllergies value="%s"/>
     708                  <DrugsUidIntolerances value="%s"/>
     709          </Patient>
     710  """             % (
     711                          gmTools.xml_escape_string(text = name['lastnames']),
     712                          gmTools.xml_escape_string(text = name['firstnames']),
     713                          self.patient.ID,
     714                          dob,
     715                          cFreeDiamsInterface.map_gender2mf[self.patient['gender']],
     716                          gmTools.xml_escape_string(text = u';'.join(atc_allgs)),
     717                          gmTools.xml_escape_string(text = u';'.join(atc_sens)),
     718                          gmTools.xml_escape_string(text = u';'.join(inn_allgs)),
     719                          gmTools.xml_escape_string(text = u';'.join(inn_sens)),
     720                          gmTools.xml_escape_string(text = u';'.join(uid_allgs)),
     721                          gmTools.xml_escape_string(text = u';'.join(uid_sens))
     722                  )
     723
     724                  xml_file.write(xml % patient_xml)
     725                  xml_file.close()
     726          #--------------------------------------------------------
     727 -        def import_fd2gm_file_as_prescription(self, filename=None):
     728
     729                  if filename is None:
     730                          filename = self.__fd2gm_filename
     731
     732                  fd2gm_xml = etree.ElementTree()
     733                  fd2gm_xml.parse(filename)
     734
     735                  pdfs = fd2gm_xml.findall('ExtraDatas/Printed')
     736                  if len(pdfs) == 0:
     737                          return
     738
     739                  fd_filenames = []
     740                  for pdf in pdfs:
     741                          fd_filenames.append(pdf.attrib['file'])
     742
     743                  docs = self.patient.get_document_folder()
     744                  emr = self.patient.get_emr()
     745
     746                  prescription = docs.add_document (
     747                          document_type = create_document_type (
     748                                  document_type = DOCUMENT_TYPE_PRESCRIPTION
     749                          )['pk_doc_type'],
     750                          encounter = emr.active_encounter['pk_encounter'],
     751                          episode = emr.add_episode (
     752                                  episode_name = DEFAULT_MEDICATION_HISTORY_EPISODE,
     753                                  is_open = False
     754                          )['pk_episode']
     755                  )
     756                  prescription['ext_ref'] = u'FreeDiams'
     757                  prescription.save()
     758                  fd_filenames.append(filename)
     759                  success, msg, parts = prescription.add_parts_from_files(files = fd_filenames)
     760                  if not success:
     761                          _log.error(msg)
     762                          return
     763
     764                  for part in parts:
     765                          part['obj_comment'] = _('copy')
     766                          part.save()
     767
     768                  xml_part = parts[-1]
     769                  xml_part['filename'] = u'freediams-prescription.xml'
     770                  xml_part['obj_comment'] = _('data')
     771                  xml_part.save()
     772
     773                  # are we the intended reviewer ?
     774                  from Gnumed.business.gmPerson import gmCurrentProvider
     775                  me = gmCurrentProvider()
     776                  # if so: auto-sign the prescription
     777                  if xml_part['pk_intended_reviewer'] == me['pk_staff']:
     778                          prescription.set_reviewed(technically_abnormal = False, clinically_relevant = False)
     779          #--------------------------------------------------------
     780 -        def import_fd2gm_file_as_drugs(self, filename=None):
     781                  """
     782                          If returning textual prescriptions (say, drugs which FreeDiams
     783                          did not know) then "IsTextual" will be True and UID will be -1.
     784                  """
     785                  if filename is None:
     786                          filename = self.__fd2gm_filename
     787
     788                  # FIXME: do not import IsTextual drugs, or rather, make that configurable
     789
     790                  fd2gm_xml = etree.ElementTree()
     791                  fd2gm_xml.parse(filename)
     792
     793                  data_src_pk = self.create_data_source_entry()
     794
     795                  db_def = fd2gm_xml.find('DrugsDatabaseName')
     796                  db_id = db_def.text.strip()
     797                  drug_id_name = db_def.attrib['drugUidName']
     798                  fd_xml_drug_entries = fd2gm_xml.findall('FullPrescription/Prescription')
     799
     800                  self.__imported_drugs = []
     801                  for fd_xml_drug in fd_xml_drug_entries:
     802                          drug_uid = fd_xml_drug.find('Drug_UID').text.strip()
     803                          if drug_uid == u'-1':
     804                                  _log.debug('skipping textual drug')
     805                                  continue                # it's a TextualDrug, skip it
     806                          drug_name = fd_xml_drug.find('DrugName').text.replace(', )', ')').strip()
     807                          drug_form = fd_xml_drug.find('DrugForm').text.strip()
     808                          drug_atc = fd_xml_drug.find('DrugATC')
     809                          if drug_atc is None:
     810                                  drug_atc = u''
     811                          else:
     812                                  if drug_atc.text is None:
     813                                          drug_atc = u''
     814                                  else:
     815                                          drug_atc = drug_atc.text.strip()
     816
     817                          # create new branded drug
     818                          new_drug = create_branded_drug(brand_name = drug_name, preparation = drug_form, return_existing = True)
     819                          self.__imported_drugs.append(new_drug)
     820                          new_drug['is_fake_brand'] = False
     821                          new_drug['atc'] = drug_atc
     822                          new_drug['external_code_type'] = u'FreeDiams::%s::%s' % (db_id, drug_id_name)
     823                          new_drug['external_code'] = drug_uid
     824                          new_drug['pk_data_source'] = data_src_pk
     825                          new_drug.save()
     826
     827                          # parse XML for composition records
     828                          fd_xml_components = fd_xml_drug.getiterator('Composition')
     829                          comp_data = {}
     830                          for fd_xml_comp in fd_xml_components:
     831
     832                                  data = {}
     833
     834                                  amount = regex.match(r'\d+[.,]{0,1}\d*', fd_xml_comp.attrib['strenght'].strip())                        # sic, typo
     835                                  if amount is None:
     836                                          amount = 99999
     837                                  else:
     838                                          amount = amount.group()
     839                                  data['amount'] = amount
     840
     841                                  unit = regex.sub(r'\d+[.,]{0,1}\d*', u'', fd_xml_comp.attrib['strenght'].strip()).strip()       # sic, typo
     842                                  if unit == u'':
     843                                          unit = u'*?*'
     844                                  data['unit'] = unit
     845
     846                                  molecule_name = fd_xml_comp.attrib['molecularName'].strip()
     847                                  if molecule_name != u'':
     848                                          create_consumable_substance(substance = molecule_name, atc = None, amount = amount, unit = unit)
     849                                  data['molecule_name'] = molecule_name
     850
     851                                  inn_name = fd_xml_comp.attrib['inn'].strip()
     852                                  if inn_name != u'':
     853                                          create_consumable_substance(substance = inn_name, atc = None, amount = amount, unit = unit)
     854                                  data['inn_name'] = molecule_name
     855
     856                                  if molecule_name == u'':
     857                                          data['substance'] = inn_name
     858                                          _log.info('linking INN [%s] rather than molecularName as component', inn_name)
     859                                  else:
     860                                          data['substance'] = molecule_name
     861
     862                                  data['nature'] = fd_xml_comp.attrib['nature'].strip()
     863                                  data['nature_ID'] = fd_xml_comp.attrib['natureLink'].strip()
     864
     865                                  # merge composition records of SA/FT nature
     866                                  try:
     867                                          old_data = comp_data[data['nature_ID']]
     868                                          # normalize INN
     869                                          if old_data['inn_name'] == u'':
     870                                                  old_data['inn_name'] = data['inn_name']
     871                                          if data['inn_name'] == u'':
     872                                                  data['inn_name'] = old_data['inn_name']
     873                                          # normalize molecule
     874                                          if old_data['molecule_name'] == u'':
     875                                                  old_data['molecule_name'] = data['molecule_name']
     876                                          if data['molecule_name'] == u'':
     877                                                  data['molecule_name'] = old_data['molecule_name']
     878                                          # FT: transformed form
     879                                          # SA: active substance
     880                                          # it would be preferable to use the SA record because that's what's *actually*
     881                                          # contained in the drug, however FreeDiams does not list the amount thereof
     882                                          # (rather that of the INN)
     883                                          if data['nature'] == u'FT':
     884                                                  comp_data[data['nature_ID']] = data
     885                                          else:
     886                                                  comp_data[data['nature_ID']] = old_data
     887
     888                                  # or create new record
     889                                  except KeyError:
     890                                          comp_data[data['nature_ID']] = data
     891
     892                          # actually create components from (possibly merged) composition records
     893                          for key, data in comp_data.items():
     894                                  new_drug.add_component (
     895                                          substance = data['substance'],
     896                                          atc = None,
     897                                          amount = data['amount'],
     898                                          unit = data['unit']
     899                                  )
     900  #============================================================
     901 -class cGelbeListeWindowsInterface(cDrugDataSourceInterface):
     902          """Support v8.2 CSV file interface only."""
     903
     904          version = u'Gelbe Liste/MMI v8.2 interface'
     905          default_encoding = 'cp1250'
     906          bdt_line_template = u'%03d6210#%s\r\n'          # Medikament verordnet auf Kassenrezept
     907          bdt_line_base_length = 8
     908          #--------------------------------------------------------
     909 -        def __init__(self):
     910
     911                  cDrugDataSourceInterface.__init__(self)
     912
     913                  _log.info(u'%s (native Windows)', cGelbeListeWindowsInterface.version)
     914
     915                  self.path_to_binary = r'C:\Programme\MMI PHARMINDEX\glwin.exe'
     916                  self.args = r'-KEEPBACKGROUND -PRESCRIPTIONFILE %s -CLOSETOTRAY'
     917
     918                  paths = gmTools.gmPaths()
     919
     920                  self.default_csv_filename = os.path.join(paths.home_dir, '.gnumed', 'tmp', 'rezept.txt')
     921                  self.default_csv_filename_arg = os.path.join(paths.home_dir, '.gnumed', 'tmp')
     922                  self.interactions_filename = os.path.join(paths.home_dir, '.gnumed', 'tmp', 'gm2mmi.bdt')
     923                  self.data_date_filename = r'C:\Programme\MMI PHARMINDEX\datadate.txt'
     924
     925                  self.__data_date = None
     926                  self.__online_update_date = None
     927
     928                  # use adjusted config.dat
     929          #--------------------------------------------------------
     930 -        def get_data_source_version(self, force_reload=False):
     931
     932                  if self.__data_date is not None:
     933                          if not force_reload:
     934                                  return {
     935                                          'data': self.__data_date,
     936                                          'online_update': self.__online_update_date
     937                                  }
     938                  try:
     939                          open(self.data_date_filename, 'wb').close()
     940                  except StandardError:
     941                          _log.error('problem querying the MMI drug database for version information')
     942                          _log.exception('cannot create MMI drug database version file [%s]', self.data_date_filename)
     943                          self.__data_date = None
     944                          self.__online_update_date = None
     945                          return {
     946                                  'data': u'?',
     947                                  'online_update': u'?'
     948                          }
     949
     950                  cmd = u'%s -DATADATE' % self.path_to_binary
     951                  if not gmShellAPI.run_command_in_shell(command = cmd, blocking = True):
     952                          _log.error('problem querying the MMI drug database for version information')
     953                          self.__data_date = None
     954                          self.__online_update_date = None
     955                          return {
     956                                  'data': u'?',
     957                                  'online_update': u'?'
     958                          }
     959
     960                  try:
     961                          version_file = open(self.data_date_filename, 'rU')
     962                  except StandardError:
     963                          _log.error('problem querying the MMI drug database for version information')
     964                          _log.exception('cannot open MMI drug database version file [%s]', self.data_date_filename)
     965                          self.__data_date = None
     966                          self.__online_update_date = None
     967                          return {
     968                                  'data': u'?',
     969                                  'online_update': u'?'
     970                          }
     971
     972                  self.__data_date = version_file.readline()[:10]
     973                  self.__online_update_date = version_file.readline()[:10]
     974                  version_file.close()
     975
     976                  return {
     977                          'data': self.__data_date,
     978                          'online_update': self.__online_update_date
     979                  }
     980          #--------------------------------------------------------
     981 -        def create_data_source_entry(self):
     982                  versions = self.get_data_source_version()
     983
     984                  return create_data_source (
     985                          long_name = u'Medikamentendatenbank "mmi PHARMINDEX" (Gelbe Liste)',
     986                          short_name = u'GL/MMI',
     987                          version = u'Daten: %s, Preise (Onlineupdate): %s' % (versions['data'], versions['online_update']),
     988                          source = u'Medizinische Medien Informations GmbH, Am Forsthaus Gravenbruch 7, 63263 Neu-Isenburg',
     989                          language = u'de'
     990                  )
     991          #--------------------------------------------------------
     992 -        def switch_to_frontend(self, blocking=False, cmd=None):
     993
     994                  try:
     995                          # must make sure csv file exists
     996                          open(self.default_csv_filename, 'wb').close()
     997                  except IOError:
     998                          _log.exception('problem creating GL/MMI <-> GNUmed exchange file')
     999                          return False
    1000
    1001                  if cmd is None:
    1002                          cmd = (u'%s %s' % (self.path_to_binary, self.args)) % self.default_csv_filename_arg
    1003
    1004                  if os.name == 'nt':
    1005                          blocking = True
    1006                  if not gmShellAPI.run_command_in_shell(command = cmd, blocking = blocking):
    1007                          _log.error('problem switching to the MMI drug database')
    1008                          # apparently on the first call MMI does not
    1009                          # consistently return 0 on success
    1010  #                       return False
    1011
    1012                  return True
    1013          #--------------------------------------------------------
    1014 -        def __let_user_select_drugs(self):
    1015
    1016                  # better to clean up interactions file
    1017                  open(self.interactions_filename, 'wb').close()
    1018
    1019                  if not self.switch_to_frontend(blocking = True):
    1020                          return None
    1021
    1022                  return cGelbeListeCSVFile(filename = self.default_csv_filename)
    1023          #--------------------------------------------------------
    1024 -        def import_drugs_as_substances(self):
    1025
    1026                  selected_drugs = self.__let_user_select_drugs()
    1027                  if selected_drugs is None:
    1028                          return None
    1029
    1030                  new_substances = []
    1031
    1032                  for drug in selected_drugs:
    1033                          atc = None                                                      # hopefully MMI eventually supports atc-per-substance in a drug...
    1034                          if len(drug['wirkstoffe']) == 1:
    1035                                  atc = drug['atc']
    1036                          for wirkstoff in drug['wirkstoffe']:
    1037                                  new_substances.append(create_consumable_substance(substance = wirkstoff, atc = atc, amount = amount, unit = unit))
    1038
    1039                  selected_drugs.close()
    1040
    1041                  return new_substances
    1042          #--------------------------------------------------------
    1043 -        def import_drugs(self):
    1044
    1045                  selected_drugs = self.__let_user_select_drugs()
    1046                  if selected_drugs is None:
    1047                          return None
    1048
    1049                  data_src_pk = self.create_data_source_entry()
    1050
    1051                  new_drugs = []
    1052                  new_substances = []
    1053
    1054                  for entry in selected_drugs:
    1055
    1056                          _log.debug('importing drug: %s %s', entry['name'], entry['darreichungsform'])
    1057
    1058                          if entry[u'hilfsmittel']:
    1059                                  _log.debug('skipping Hilfsmittel')
    1060                                  continue
    1061
    1062                          if entry[u'erstattbares_medizinprodukt']:
    1063                                  _log.debug('skipping sonstiges Medizinprodukt')
    1064                                  continue
    1065
    1066                          # create branded drug (or get it if it already exists)
    1067                          drug = create_branded_drug(brand_name = entry['name'], preparation = entry['darreichungsform'])
    1068                          if drug is None:
    1069                                  drug = get_drug_by_brand(brand_name = entry['name'], preparation = entry['darreichungsform'])
    1070                          new_drugs.append(drug)
    1071
    1072                          # update fields
    1073                          drug['is_fake_brand'] = False
    1074                          drug['atc'] = entry['atc']
    1075                          drug['external_code_type'] = u'DE-PZN'
    1076                          drug['external_code'] = entry['pzn']
    1077                          drug['fk_data_source'] = data_src_pk
    1078                          drug.save()
    1079
    1080                          # add components to brand
    1081                          atc = None                                                      # hopefully MMI eventually supports atc-per-substance in a drug...
    1082                          if len(entry['wirkstoffe']) == 1:
    1083                                  atc = entry['atc']
    1084                          for wirkstoff in entry['wirkstoffe']:
    1085                                  drug.add_component(substance = wirkstoff, atc = atc)
    1086
    1087                          # create as consumable substances, too
    1088                          atc = None                                                      # hopefully MMI eventually supports atc-per-substance in a drug...
    1089                          if len(entry['wirkstoffe']) == 1:
    1090                                  atc = entry['atc']
    1091                          for wirkstoff in entry['wirkstoffe']:
    1092                                  new_substances.append(create_consumable_substance(substance = wirkstoff, atc = atc, amount = amount, unit = unit))
    1093
    1094                  return new_drugs, new_substances
    1095          #--------------------------------------------------------
    1096 -        def check_interactions(self, drug_ids_list=None, substances=None):
    1097                  """For this to work the BDT interaction check must be configured in the MMI."""
    1098
    1099                  if drug_ids_list is None:
    1100                          if substances is None:
    1101                                  return
    1102                          if len(substances) < 2:
    1103                                  return
    1104                          drug_ids_list = [ (s.external_code_type, s.external_code) for s in substances ]
    1105                          drug_ids_list = [ code_value for code_type, code_value in drug_ids_list if (code_value is not None) and (code_type == u'DE-PZN')]
    1106
    1107                  else:
    1108                          if len(drug_ids_list) < 2:
    1109                                  return
    1110
    1111                  if drug_ids_list < 2:
    1112                          return
    1113
    1114                  bdt_file = codecs.open(filename = self.interactions_filename, mode = 'wb', encoding = cGelbeListeWindowsInterface.default_encoding)
    1115
    1116                  for pzn in drug_ids_list:
    1117                          pzn = pzn.strip()
    1118                          lng = cGelbeListeWindowsInterface.bdt_line_base_length + len(pzn)
    1119                          bdt_file.write(cGelbeListeWindowsInterface.bdt_line_template % (lng, pzn))
    1120
    1121                  bdt_file.close()
    1122
    1123                  self.switch_to_frontend(blocking = True)
    1124          #--------------------------------------------------------
    1125 -        def show_info_on_drug(self, drug=None):
    1126                  self.switch_to_frontend(blocking = True)
    1127          #--------------------------------------------------------
    1128 -        def show_info_on_substance(self, substance=None):
    1129
    1130                  cmd = None
    1131
    1132                  if substance.external_code_type == u'DE-PZN':
    1133                          cmd = u'%s -PZN %s' % (self.path_to_binary, substance.external_code)
    1134
    1135                  if cmd is None:
    1136                          name = gmTools.coalesce (
    1137                                  substance['brand'],
    1138                                  substance['substance']
    1139                          )
    1140                          cmd = u'%s -NAME %s' % (self.path_to_binary, name)
    1141
    1142                  # better to clean up interactions file
    1143                  open(self.interactions_filename, 'wb').close()
    1144
    1145                  self.switch_to_frontend(cmd = cmd)
    1146  #============================================================
    1147 -class cGelbeListeWineInterface(cGelbeListeWindowsInterface):
    1148
    1149 -        def __init__(self):
    1150                  cGelbeListeWindowsInterface.__init__(self)
    1151
    1152                  _log.info(u'%s (WINE extension)', cGelbeListeWindowsInterface.version)
    1153
    1154                  # FIXME: if -CLOSETOTRAY is used GNUmed cannot detect the end of MMI
    1155                  self.path_to_binary = r'wine "C:\Programme\MMI PHARMINDEX\glwin.exe"'
    1156                  self.args = r'"-PRESCRIPTIONFILE %s -KEEPBACKGROUND"'
    1157
    1158                  paths = gmTools.gmPaths()
    1159
    1160                  self.default_csv_filename = os.path.join(paths.home_dir, '.wine', 'drive_c', 'windows', 'temp', 'mmi2gm.csv')
    1161                  self.default_csv_filename_arg = r'c:\windows\temp\mmi2gm.csv'
    1162                  self.interactions_filename = os.path.join(paths.home_dir, '.wine', 'drive_c', 'windows', 'temp', 'gm2mmi.bdt')
    1163                  self.data_date_filename = os.path.join(paths.home_dir, '.wine', 'drive_c', 'Programme', 'MMI PHARMINDEX', 'datadate.txt')
    1164  #============================================================
    1165 -class cIfapInterface(cDrugDataSourceInterface):
    1166          """empirical CSV interface"""
    1167
    1168 -        def __init__(self):
    1169                  pass
    1170
    1171 -        def print_transfer_file(self, filename=None):
    1172
    1173                  try:
    1174                          csv_file = open(filename, 'rb')                                         # FIXME: encoding ?
    1175                  except:
    1176                          _log.exception('cannot access [%s]', filename)
    1177                          csv_file = None
    1178
    1179                  field_names = u'PZN Handelsname Form Abpackungsmenge Einheit Preis1 Hersteller Preis2 rezeptpflichtig Festbetrag Packungszahl Packungsgr\xf6\xdfe'.split()
    1180
    1181                  if csv_file is None:
    1182                          return False
    1183
    1184                  csv_lines = csv.DictReader (
    1185                          csv_file,
    1186                          fieldnames = field_names,
    1187                          delimiter = ';'
    1188                  )
    1189
    1190                  for line in csv_lines:
    1191                          print "--------------------------------------------------------------------"[:31]
    1192                          for key in field_names:
    1193                                  tmp = ('%s                                                ' % key)[:30]
    1194                                  print '%s: %s' % (tmp, line[key])
    1195
    1196                  csv_file.close()
    1197
    1198  #                               narr = u'%sx %s %s %s (\u2258 %s %s) von %s (%s)' % (
    1199  #                                       line['Packungszahl'].strip(),
    1200  #                                       line['Handelsname'].strip(),
    1201  #                                       line['Form'].strip(),
    1202  #                                       line[u'Packungsgr\xf6\xdfe'].strip(),
    1203  #                                       line['Abpackungsmenge'].strip(),
    1204  #                                       line['Einheit'].strip(),
    1205  #                                       line['Hersteller'].strip(),
    1206  #                                       line['PZN'].strip()
    1207  #                               )
    1208  #============================================================
    1209  drug_data_source_interfaces = {
    1210          'Deutschland: Gelbe Liste/MMI (Windows)': cGelbeListeWindowsInterface,
    1211          'Deutschland: Gelbe Liste/MMI (WINE)': cGelbeListeWineInterface,
    1212          'FreeDiams (FR, US, CA, ZA)': cFreeDiamsInterface
    1213  }
    1214
    1215  #============================================================
    1216  #============================================================
    1217  # substances in use across all patients
    1218  #------------------------------------------------------------
    1219  _SQL_get_consumable_substance = u"""
    1220          SELECT *, xmin
    1221          FROM ref.consumable_substance
    1222          WHERE %s
    1223  """
    1224
    1225 -class cConsumableSubstance(gmBusinessDBObject.cBusinessDBObject):
    1226
    1227          _cmd_fetch_payload = _SQL_get_consumable_substance % u"pk = %s"
    1228          _cmds_store_payload = [
    1229                  u"""UPDATE ref.consumable_substance SET
    1230                                  description = %(description)s,
    1231                                  atc_code = gm.nullify_empty_string(%(atc_code)s),
    1232                                  amount = %(amount)s,
    1233                                  unit = gm.nullify_empty_string(%(unit)s)
    1234                          WHERE
    1235                                  pk = %(pk)s
    1236                                          AND
    1237                                  xmin = %(xmin)s
    1238                                          AND
    1239                                  -- must not currently be used with a patient directly
    1240                                  NOT EXISTS (
    1241                                          SELECT 1
    1242                                          FROM clin.substance_intake
    1243                                          WHERE
    1244                                                  fk_drug_component IS NULL
    1245                                                          AND
    1246                                                  fk_substance = %(pk)s
    1247                                          LIMIT 1
    1248                                  )
    1249                                          AND
    1250                                  -- must not currently be used with a patient indirectly, either
    1251                                  NOT EXISTS (
    1252                                          SELECT 1
    1253                                          FROM clin.substance_intake
    1254                                          WHERE
    1255                                                  fk_drug_component IS NOT NULL
    1256                                                          AND
    1257                                                  fk_drug_component = (
    1258                                                          SELECT r_ls2b.pk
    1259                                                          FROM ref.lnk_substance2brand r_ls2b
    1260                                                          WHERE fk_substance = %(pk)s
    1261                                                  )
    1262                                          LIMIT 1
    1263                                  )
    1264  --                              -- must not currently be used with a branded drug
    1265  --                              -- (but this would make it rather hard fixing branded drugs which contain only this substance)
    1266  --                              NOT EXISTS (
    1267  --                                      SELECT 1
    1268  --                                      FROM ref.lnk_substance2brand
    1269  --                                      WHERE fk_substance = %(pk)s
    1270  --                                      LIMIT 1
    1271  --                              )
    1272                          RETURNING
    1273                                  xmin
    1274                  """
    1275          ]
    1276          _updatable_fields = [
    1277                  u'description',
    1278                  u'atc_code',
    1279                  u'amount',
    1280                  u'unit'
    1281          ]
    1282          #--------------------------------------------------------
    1283 -        def save_payload(self, conn=None):
    1284                  success, data = super(self.__class__, self).save_payload(conn = conn)
    1285
    1286                  if not success:
    1287                          return (success, data)
    1288
    1289                  if self._payload[self._idx['atc_code']] is not None:
    1290                          atc = self._payload[self._idx['atc_code']].strip()
    1291                          if atc != u'':
    1292                                  gmATC.propagate_atc (
    1293                                          substance = self._payload[self._idx['description']].strip(),
    1294                                          atc = atc
    1295                                  )
    1296
    1297                  return (success, data)
    1298          #--------------------------------------------------------
    1299          # properties
    1300          #--------------------------------------------------------
    1301 -        def _get_is_in_use_by_patients(self):
    1302                  cmd = u"""
    1303                          SELECT
    1304                                  EXISTS (
    1305                                          SELECT 1
    1306                                          FROM clin.substance_intake
    1307                                          WHERE
    1308                                                  fk_drug_component IS NULL
    1309                                                          AND
    1310                                                  fk_substance = %(pk)s
    1311                                          LIMIT 1
    1312                                  ) OR EXISTS (
    1313                                          SELECT 1
    1314                                          FROM clin.substance_intake
    1315                                          WHERE
    1316                                                  fk_drug_component IS NOT NULL
    1317                                                          AND
    1318                                                  fk_drug_component IN (
    1319                                                          SELECT r_ls2b.pk
    1320                                                          FROM ref.lnk_substance2brand r_ls2b
    1321                                                          WHERE fk_substance = %(pk)s
    1322                                                  )
    1323                                          LIMIT 1
    1324                                  )"""
    1325                  args = {'pk': self.pk_obj}
    1326
    1327                  rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
    1328                  return rows[0][0]
    1329
    1330          is_in_use_by_patients = property(_get_is_in_use_by_patients, lambda x:x)
    1331          #--------------------------------------------------------
    1332 -        def _get_is_drug_component(self):
    1333                  cmd = u"""
    1334                          SELECT EXISTS (
    1335                                  SELECT 1
    1336                                  FROM ref.lnk_substance2brand
    1337                                  WHERE fk_substance = %(pk)s
    1338                                  LIMIT 1
    1339                          )"""
    1340                  args = {'pk': self.pk_obj}
    1341
    1342                  rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
    1343                  return rows[0][0]
    1344
    1345          is_drug_component = property(_get_is_drug_component, lambda x:x)
    1346  #------------------------------------------------------------
    1347 -def get_consumable_substances(order_by=None):
    1348          if order_by is None:
    1349                  order_by = u'true'
    1350          else:
    1351                  order_by = u'true ORDER BY %s' % order_by
    1352          cmd = _SQL_get_consumable_substance % order_by
    1353          rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = True)
    1354          return [ cConsumableSubstance(row = {'data': r, 'idx': idx, 'pk_field': 'pk'}) for r in rows ]
    1355  #------------------------------------------------------------
    1356 -def create_consumable_substance(substance=None, atc=None, amount=None, unit=None):
    1357
    1358          substance = substance
    1359          if atc is not None:
    1360                  atc = atc.strip()
    1361
    1362          converted, amount = gmTools.input2decimal(amount)
    1363          if not converted:
    1364                  raise ValueError('<amount> must be a number: %s (%s)', amount, type(amount))
    1365
    1366          args = {
    1367                  'desc': substance.strip(),
    1368                  'amount': amount,
    1369                  'unit': unit.strip(),
    1370                  'atc': atc
    1371          }
    1372          cmd = u"""
    1373                  SELECT pk FROM ref.consumable_substance
    1374                  WHERE
    1375                          lower(description) = lower(%(desc)s)
    1376                                  AND
    1377                          amount = %(amount)s
    1378                                  AND
    1379                          unit = %(unit)s
    1380                  """
    1381          rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
    1382
    1383          if len(rows) == 0:
    1384                  cmd = u"""
    1385                          INSERT INTO ref.consumable_substance (description, atc_code, amount, unit) VALUES (
    1386                                  %(desc)s,
    1387                                  gm.nullify_empty_string(%(atc)s),
    1388                                  %(amount)s,
    1389                                  gm.nullify_empty_string(%(unit)s)
    1390                          ) RETURNING pk"""
    1391                  rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True, get_col_idx = False)
    1392
    1393          gmATC.propagate_atc(substance = substance, atc = atc)
    1394
    1395          return cConsumableSubstance(aPK_obj = rows[0]['pk'])
    1396  #------------------------------------------------------------
    1397 -def delete_consumable_substance(substance=None):
    1398          args = {'pk': substance}
    1399          cmd = u"""
    1400  DELETE FROM ref.consumable_substance
    1401  WHERE
    1402          pk = %(pk)s
    1403                  AND
    1404
    1405          -- must not currently be used with a patient
    1406          NOT EXISTS (
    1407                  SELECT 1
    1408                  FROM clin.v_pat_substance_intake
    1409                  WHERE pk_substance = %(pk)s
    1410                  LIMIT 1
    1411          )
    1412                  AND
    1413
    1414          -- must not currently be used with a branded drug
    1415          NOT EXISTS (
    1416                  SELECT 1
    1417                  FROM ref.lnk_substance2brand
    1418                  WHERE fk_substance = %(pk)s
    1419                  LIMIT 1
    1420          )"""
    1421          gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
    1422          return True
    1423  #------------------------------------------------------------
    1424 -class cSubstanceMatchProvider(gmMatchProvider.cMatchProvider_SQL2):
    1425
    1426          _pattern = regex.compile(r'^\D+\s*\d+$', regex.UNICODE | regex.LOCALE)
    1427          _query1 = u"""
    1428                  SELECT
    1429                          pk::text,
    1430                          (description || ' ' || amount || ' ' || unit) as subst
    1431                  FROM ref.consumable_substance
    1432                  WHERE description %(fragment_condition)s
    1433                  ORDER BY subst
    1434                  LIMIT 50"""
    1435          _query2 = u"""
    1436                  SELECT
    1437                          pk::text,
    1438                          (description || ' ' || amount || ' ' || unit) as subst
    1439                  FROM ref.consumable_substance
    1440                  WHERE
    1441                          %(fragment_condition)s
    1442                  ORDER BY subst
    1443                  LIMIT 50"""
    1444
    1445          #--------------------------------------------------------
    1446 -        def getMatchesByPhrase(self, aFragment):
    1447                  """Return matches for aFragment at start of phrases."""
    1448
    1449                  if cSubstanceMatchProvider._pattern.match(aFragment):
    1450                          self._queries = [cSubstanceMatchProvider._query2]
    1451                          fragment_condition = """description ILIKE %(desc)s
    1452                                  AND
    1453                          amount::text ILIKE %(amount)s"""
    1454                          self._args['desc'] = u'%s%%' % regex.sub(r'\s*\d+$', u'', aFragment)
    1455                          self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment)
    1456                  else:
    1457                          self._queries = [cSubstanceMatchProvider._query1]
    1458                          fragment_condition = u"ILIKE %(fragment)s"
    1459                          self._args['fragment'] = u"%s%%" % aFragment
    1460
    1461                  return self._find_matches(fragment_condition)
    1462          #--------------------------------------------------------
    1463 -        def getMatchesByWord(self, aFragment):
    1464                  """Return matches for aFragment at start of words inside phrases."""
    1465
    1466                  if cSubstanceMatchProvider._pattern.match(aFragment):
    1467                          self._queries = [cSubstanceMatchProvider._query2]
    1468
    1469                          desc = regex.sub(r'\s*\d+$', u'', aFragment)
    1470                          desc = gmPG2.sanitize_pg_regex(expression = desc, escape_all = False)
    1471
    1472                          fragment_condition = """description ~* %(desc)s
    1473                                  AND
    1474                          amount::text ILIKE %(amount)s"""
    1475
    1476                          self._args['desc'] = u"( %s)|(^%s)" % (desc, desc)
    1477                          self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment)
    1478                  else:
    1479                          self._queries = [cSubstanceMatchProvider._query1]
    1480                          fragment_condition = u"~* %(fragment)s"
    1481                          aFragment = gmPG2.sanitize_pg_regex(expression = aFragment, escape_all = False)
    1482                          self._args['fragment'] = u"( %s)|(^%s)" % (aFragment, aFragment)
    1483
    1484                  return self._find_matches(fragment_condition)
    1485          #--------------------------------------------------------
    1486 -        def getMatchesBySubstr(self, aFragment):
    1487                  """Return matches for aFragment as a true substring."""
    1488
    1489                  if cSubstanceMatchProvider._pattern.match(aFragment):
    1490                          self._queries = [cSubstanceMatchProvider._query2]
    1491                          fragment_condition = """description ILIKE %(desc)s
    1492                                  AND
    1493                          amount::text ILIKE %(amount)s"""
    1494                          self._args['desc'] = u'%%%s%%' % regex.sub(r'\s*\d+$', u'', aFragment)
    1495                          self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment)
    1496                  else:
    1497                          self._queries = [cSubstanceMatchProvider._query1]
    1498                          fragment_condition = u"ILIKE %(fragment)s"
    1499                          self._args['fragment'] = u"%%%s%%" % aFragment
    1500
    1501                  return self._find_matches(fragment_condition)
    1502  #============================================================
    1503 -class cSubstanceIntakeEntry(gmBusinessDBObject.cBusinessDBObject):
    1504          """Represents a substance currently taken by a patient."""
    1505
    1506          _cmd_fetch_payload = u"SELECT * FROM clin.v_pat_substance_intake WHERE pk_substance_intake = %s"
    1507          _cmds_store_payload = [
    1508                  u"""UPDATE clin.substance_intake SET
    1509                                  clin_when = %(started)s,
    1510                                  discontinued = %(discontinued)s,
    1511                                  discontinue_reason = gm.nullify_empty_string(%(discontinue_reason)s),
    1512                                  schedule = gm.nullify_empty_string(%(schedule)s),
    1513                                  aim = gm.nullify_empty_string(%(aim)s),
    1514                                  narrative = gm.nullify_empty_string(%(notes)s),
    1515                                  intake_is_approved_of = %(intake_is_approved_of)s,
    1516                                  fk_episode = %(pk_episode)s,
    1517
    1518                                  preparation = (
    1519                                          case
    1520                                                  when %(pk_brand)s is NULL then %(preparation)s
    1521                                                  else NULL
    1522                                          end
    1523                                  )::text,
    1524
    1525                                  is_long_term = (
    1526                                          case
    1527                                                  when (
    1528                                                          (%(is_long_term)s is False)
    1529                                                                  and
    1530                                                          (%(duration)s is NULL)
    1531                                                  ) is True then null
    1532                                                  else %(is_long_term)s
    1533                                          end
    1534                                  )::boolean,
    1535
    1536                                  duration = (
    1537                                          case
    1538                                                  when %(is_long_term)s is True then null
    1539                                                  else %(duration)s
    1540                                          end
    1541                                  )::interval
    1542                          WHERE
    1543                                  pk = %(pk_substance_intake)s
    1544                                          AND
    1545                                  xmin = %(xmin_substance_intake)s
    1546                          RETURNING
    1547                                  xmin as xmin_substance_intake
    1548                  """
    1549          ]
    1550          _updatable_fields = [
    1551                  u'started',
    1552                  u'discontinued',
    1553                  u'discontinue_reason',
    1554                  u'preparation',
    1555                  u'intake_is_approved_of',
    1556                  u'schedule',
    1557                  u'duration',
    1558                  u'aim',
    1559                  u'is_long_term',
    1560                  u'notes',
    1561                  u'pk_episode'
    1562          ]
    1563          #--------------------------------------------------------
    1564 -        def format(self, left_margin=0, date_format='%Y %B %d', one_line=True, allergy=None):
    1565                  if one_line:
    1566                          return self.format_as_one_line(left_margin = left_margin, date_format = date_format)
    1567
    1568                  return self.format_as_multiple_lines(left_margin = left_margin, date_format = date_format, allergy = allergy)
    1569          #--------------------------------------------------------
    1570 -        def format_as_one_line(self, left_margin=0, date_format='%Y %B %d'):
    1571
    1572                  if self._payload[self._idx['duration']] is None:
    1573                          duration = gmTools.bool2subst (
    1574                                  self._payload[self._idx['is_long_term']],
    1575                                  _('long-term'),
    1576                                  _('short-term'),
    1577                                  _('?short-term')
    1578                          )
    1579                  else:
    1580                          duration = gmDateTime.format_interval (
    1581                                  self._payload[self._idx['duration']],
    1582                                  accuracy_wanted = gmDateTime.acc_days
    1583                          )
    1584
    1585                  line = u'%s%s (%s %s): %s %s%s %s (%s)' % (
    1586                          u' ' * left_margin,
    1587                          self._payload[self._idx['started']].strftime(date_format),
    1588                          gmTools.u_right_arrow,
    1589                          duration,
    1590                          self._payload[self._idx['substance']],
    1591                          self._payload[self._idx['amount']],
    1592                          self._payload[self._idx['unit']],
    1593                          self._payload[self._idx['preparation']],
    1594                          gmTools.bool2subst(self._payload[self._idx['is_currently_active']], _('ongoing'), _('inactive'), _('?ongoing'))
    1595                  )
    1596
    1597                  return line
    1598          #--------------------------------------------------------
    1599 -        def format_as_multiple_lines(self, left_margin=0, date_format='%Y %B %d', allergy=None):
    1600
    1601                  txt = _('Substance intake entry (%s, %s)   [#%s]                     \n') % (
    1602                          gmTools.bool2subst (
    1603                                  boolean = self._payload[self._idx['is_currently_active']],
    1604                                  true_return = gmTools.bool2subst (
    1605                                          boolean = self._payload[self._idx['seems_inactive']],
    1606                                          true_return = _('active, needs check'),
    1607                                          false_return = _('active'),
    1608                                          none_return = _('assumed active')
    1609                                  ),
    1610                                  false_return = _('inactive')
    1611                          ),
    1612                          gmTools.bool2subst (
    1613                                  boolean = self._payload[self._idx['intake_is_approved_of']],
    1614                                  true_return = _('approved'),
    1615                                  false_return = _('unapproved')
    1616                          ),
    1617                          self._payload[self._idx['pk_substance_intake']]
    1618                  )
    1619
    1620                  if allergy is not None:
    1621                          certainty = gmTools.bool2subst(allergy['definite'], _('definite'), _('suspected'))
    1622                          txt += u'\n'
    1623                          txt += u' !! ---- Cave ---- !!\n'
    1624                          txt += u' %s (%s): %s (%s)\n' % (
    1625                                  allergy['l10n_type'],
    1626                                  certainty,
    1627                                  allergy['descriptor'],
    1628                                  gmTools.coalesce(allergy['reaction'], u'')[:40]
    1629                          )
    1630                          txt += u'\n'
    1631
    1632                  txt += u' ' + _('Substance: %s   [#%s]\n') % (self._payload[self._idx['substance']], self._payload[self._idx['pk_substance']])
    1633                  txt += u' ' + _('Preparation: %s\n') % self._payload[self._idx['preparation']]
    1634                  txt += u' ' + _('Amount per dose: %s %s') % (self._payload[self._idx['amount']], self._payload[self._idx['unit']])
    1635                  if self.ddd is not None:
    1636                          txt += u' (DDD: %s %s)' % (self.ddd['ddd'], self.ddd['unit'])
    1637                  txt += u'\n'
    1638                  txt += gmTools.coalesce(self._payload[self._idx['atc_substance']], u'', _(' ATC (substance): %s\n'))
    1639
    1640                  txt += u'\n'
    1641
    1642                  txt += gmTools.coalesce (
    1643                          self._payload[self._idx['brand']],
    1644                          u'',
    1645                          _(' Brand name: %%s   [#%s]\n') % self._payload[self._idx['pk_brand']]
    1646                  )
    1647                  txt += gmTools.coalesce(self._payload[self._idx['atc_brand']], u'', _(' ATC (brand): %s\n'))
    1648
    1649                  txt += u'\n'
    1650
    1651                  txt += gmTools.coalesce(self._payload[self._idx['schedule']], u'', _(' Regimen: %s\n'))
    1652
    1653                  if self._payload[self._idx['is_long_term']]:
    1654                          duration = u' %s %s' % (gmTools.u_right_arrow, gmTools.u_infinity)
    1655                  else:
    1656                          if self._payload[self._idx['duration']] is None:
    1657                                  duration = u''
    1658                          else:
    1659                                  duration = u' %s %s' % (gmTools.u_right_arrow, gmDateTime.format_interval(self._payload[self._idx['duration']], gmDateTime.acc_days))
    1660
    1661                  txt += _(' Started %s%s%s\n') % (
    1662                          gmDateTime.pydt_strftime (
    1663                                  self._payload[self._idx['started']],
    1664                                  format = date_format,
    1665                                  accuracy = gmDateTime.acc_days
    1666                          ),
    1667                          duration,
    1668                          gmTools.bool2subst(self._payload[self._idx['is_long_term']], _(' (long-term)'), _(' (short-term)'), u'')
    1669                  )
    1670
    1671                  if self._payload[self._idx['discontinued']] is not None:
    1672                          txt += _(' Discontinued %s\n') % (
    1673                                  gmDateTime.pydt_strftime (
    1674                                          self._payload[self._idx['discontinued']],
    1675                                          format = date_format,
    1676                                          accuracy = gmDateTime.acc_days
    1677                                  )
    1678                          )
    1679                          txt += _(' Reason: %s\n') % self._payload[self._idx['discontinue_reason']]
    1680
    1681                  txt += u'\n'
    1682
    1683                  txt += gmTools.coalesce(self._payload[self._idx['aim']], u'', _(' Aim: %s\n'))
    1684                  txt += gmTools.coalesce(self._payload[self._idx['episode']], u'', _(' Episode: %s\n'))
    1685                  txt += gmTools.coalesce(self._payload[self._idx['health_issue']], u'', _(' Health issue: %s\n'))
    1686                  txt += gmTools.coalesce(self._payload[self._idx['notes']], u'', _(' Advice: %s\n'))
    1687
    1688                  txt += u'\n'
    1689
    1690                  txt += _(u'Revision: #%(row_ver)s, %(mod_when)s by %(mod_by)s.') % {
    1691                          'row_ver': self._payload[self._idx['row_version']],
    1692                          'mod_when': gmDateTime.pydt_strftime(self._payload[self._idx['modified_when']]),
    1693                          'mod_by': self._payload[self._idx['modified_by']]
    1694                  }
    1695
    1696                  return txt
    1697          #--------------------------------------------------------
    1698 -        def turn_into_allergy(self, encounter_id=None, allergy_type='allergy'):
    1699                  allg = gmAllergy.create_allergy (
    1700                          allergene = self._payload[self._idx['substance']],
    1701                          allg_type = allergy_type,
    1702                          episode_id = self._payload[self._idx['pk_episode']],
    1703                          encounter_id = encounter_id
    1704                  )
    1705                  allg['substance'] = gmTools.coalesce (
    1706                          self._payload[self._idx['brand']],
    1707                          self._payload[self._idx['substance']]
    1708                  )
    1709                  allg['reaction'] = self._payload[self._idx['discontinue_reason']]
    1710                  allg['atc_code'] = gmTools.coalesce(self._payload[self._idx['atc_substance']], self._payload[self._idx['atc_brand']])
    1711                  if self._payload[self._idx['external_code_brand']] is not None:
    1712                          allg['substance_code'] = u'%s::::%s' % (self._payload[self._idx['external_code_type_brand']], self._payload[self._idx['external_code_brand']])
    1713
    1714                  if self._payload[self._idx['pk_brand']] is None:
    1715                          allg['generics'] = self._payload[self._idx['substance']]
    1716                  else:
    1717                          comps = [ c['substance'] for c in self.containing_drug.components ]
    1718                          if len(comps) == 0:
    1719                                  allg['generics'] = self._payload[self._idx['substance']]
    1720                          else:
    1721                                  allg['generics'] = u'; '.join(comps)
    1722
    1723                  allg.save()
    1724                  return allg
    1725          #--------------------------------------------------------
    1726          # properties
    1727          #--------------------------------------------------------
    1728 -        def _get_ddd(self):
    1729
    1730                  try: self.__ddd
    1731                  except AttributeError: self.__ddd = None
    1732
    1733                  if self.__ddd is not None:
    1734                          return self.__ddd
    1735
    1736                  if self._payload[self._idx['atc_substance']] is not None:
    1737                          ddd = gmATC.atc2ddd(atc = self._payload[self._idx['atc_substance']])
    1738                          if len(ddd) != 0:
    1739                                  self.__ddd = ddd[0]
    1740                  else:
    1741                          if self._payload[self._idx['atc_brand']] is not None:
    1742                                  ddd = gmATC.atc2ddd(atc = self._payload[self._idx['atc_brand']])
    1743                                  if len(ddd) != 0:
    1744                                          self.__ddd = ddd[0]
    1745
    1746                  return self.__ddd
    1747
    1748          ddd = property(_get_ddd, lambda x:x)
    1749          #--------------------------------------------------------
    1750 -        def _get_external_code(self):
    1751                  drug = self.containing_drug
    1752
    1753                  if drug is None:
    1754                          return None
    1755
    1756                  return drug.external_code
    1757
    1758          external_code = property(_get_external_code, lambda x:x)
    1759          #--------------------------------------------------------
    1760 -        def _get_external_code_type(self):
    1761                  drug = self.containing_drug
    1762
    1763                  if drug is None:
    1764                          return None
    1765
    1766                  return drug.external_code_type
    1767
    1768          external_code_type = property(_get_external_code_type, lambda x:x)
    1769          #--------------------------------------------------------
    1770 -        def _get_containing_drug(self):
    1771                  if self._payload[self._idx['pk_brand']] is None:
    1772                          return None
    1773
    1774                  return cBrandedDrug(aPK_obj = self._payload[self._idx['pk_brand']])
    1775
    1776          containing_drug = property(_get_containing_drug, lambda x:x)
    1777          #--------------------------------------------------------
    1778 -        def _get_parsed_schedule(self):
    1779                  tests = [
    1780                          # lead, trail
    1781                          '       1-1-1-1 ',
    1782                          # leading dose
    1783                          '1-1-1-1',
    1784                          '22-1-1-1',
    1785                          '1/3-1-1-1',
    1786                          '/4-1-1-1'
    1787                  ]
    1788                  pattern = "^(\d\d|/\d|\d/\d|\d)[\s-]{1,5}\d{0,2}[\s-]{1,5}\d{0,2}[\s-]{1,5}\d{0,2}$"
    1789                  for test in tests:
    1790                          print test.strip(), ":", regex.match(pattern, test.strip())
    1791  #------------------------------------------------------------
    1792 -def substance_intake_exists(pk_component=None, pk_substance=None, pk_identity=None):
    1793          args = {'comp': pk_component, 'subst': pk_substance, 'pat': pk_identity}
    1794
    1795          where_clause = u"""
    1796                  fk_encounter IN (
    1797                          SELECT pk FROM clin.encounter WHERE fk_patient = %(pat)s
    1798                  )
    1799                          AND
    1800                  """
    1801
    1802          if pk_substance is not None:
    1803                  where_clause += u'fk_substance = %(subst)s'
    1804          if pk_component is not None:
    1805                  where_clause += u'fk_drug_component = %(comp)s'
    1806
    1807          cmd = u"""SELECT exists (
    1808          SELECT 1 FROM clin.substance_intake
    1809          WHERE
    1810                  %s
    1811          LIMIT 1
    1812          )""" % where_clause
    1813
    1814          rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}])
    1815          return rows[0][0]
    1816  #------------------------------------------------------------
    1817 -def create_substance_intake(pk_substance=None, pk_component=None, preparation=None, encounter=None, episode=None):
    1818
    1819          args = {
    1820                  'enc': encounter,
    1821                  'epi': episode,
    1822                  'comp': pk_component,
    1823                  'subst': pk_substance,
    1824                  'prep': preparation
    1825          }
    1826
    1827          if pk_component is None:
    1828                  cmd = u"""
    1829                          INSERT INTO clin.substance_intake (
    1830                                  fk_encounter,
    1831                                  fk_episode,
    1832                                  intake_is_approved_of,
    1833                                  fk_substance,
    1834                                  preparation
    1835                          ) VALUES (
    1836                                  %(enc)s,
    1837                                  %(epi)s,
    1838                                  False,
    1839                                  %(subst)s,
    1840                                  %(prep)s
    1841                          )
    1842                          RETURNING pk"""
    1843          else:
    1844                  cmd = u"""
    1845                          INSERT INTO clin.substance_intake (
    1846                                  fk_encounter,
    1847                                  fk_episode,
    1848                                  intake_is_approved_of,
    1849                                  fk_drug_component
    1850                          ) VALUES (
    1851                                  %(enc)s,
    1852                                  %(epi)s,
    1853                                  False,
    1854                                  %(comp)s
    1855                          )
    1856                          RETURNING pk"""
    1857
    1858          try:
    1859                  rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True)
    1860          except gmPG2.dbapi.InternalError, e:
    1861                  if e.pgerror is None:
    1862                          raise
    1863                  if 'prevent_duplicate_component' in e.pgerror:
    1864                          _log.exception('will not create duplicate substance intake entry')
    1865                          _log.error(e.pgerror)
    1866                          return None
    1867                  raise
    1868
    1869          return cSubstanceIntakeEntry(aPK_obj = rows[0][0])
    1870  #------------------------------------------------------------
    1871 -def delete_substance_intake(substance=None):
    1872          cmd = u'delete from clin.substance_intake where pk = %(pk)s'
    1873          gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': {'pk': substance}}])
    1874  #------------------------------------------------------------
    1875 -def format_substance_intake_notes(emr=None, output_format=u'latex', table_type=u'by-brand'):
    1876
    1877          tex =  u'\n{\\small\n'
    1878          tex += u'\\noindent %s\n' % _('Additional notes')
    1879          tex += u'\n'
    1880          tex += u'\\noindent \\begin{tabularx}{\\textwidth}{|X|l|X|p{7.5cm}|}\n'
    1881          tex += u'\\hline\n'
    1882          tex += u'%s {\\scriptsize (%s)} & %s & %s \\\\ \n' % (_('Substance'), _('Brand'), _('Strength'), _('Advice'))
    1883          tex += u'\\hline\n'
    1884          tex += u'%s\n'
    1885          tex += u'\n'
    1886          tex += u'\\end{tabularx}\n'
    1887          tex += u'}\n'
    1888
    1889          current_meds = emr.get_current_substance_intake (
    1890                  include_inactive = False,
    1891                  include_unapproved = False,
    1892                  order_by = u'brand, substance'
    1893          )
    1894
    1895          # create lines
    1896          lines = []
    1897          for med in current_meds:
    1898                  lines.append(u'%s%s %s & %s%s & %s \\\\ \n \\hline \n' % (
    1899                          med['substance'],
    1900                          gmTools.coalesce(med['brand'], u'', u' {\\scriptsize (%s)}'),
    1901                          med['preparation'],
    1902                          med['amount'],
    1903                          med['unit'],
    1904                          gmTools.coalesce(med['notes'], u'', u'{\\scriptsize %s}')
    1905                  ))
    1906
    1907          return tex % u' \n'.join(lines)
    1908
    1909  #------------------------------------------------------------
    1910 -def format_substance_intake(emr=None, output_format=u'latex', table_type=u'by-brand'):
    1911
    1912          tex =  u'\\noindent %s {\\tiny (%s)\\par}\n' % (_('Medication list'), _('ordered by brand'))
    1913          tex += u'\n'
    1914          tex += u'\\noindent \\begin{tabular}{|l|l|}\n'
    1915          tex += u'\\hline\n'
    1916          tex += u'%s & %s \\\\ \n' % (_('Drug'), _('Regimen'))
    1917          tex += u'\\hline\n'
    1918          tex += u'\n'
    1919          tex += u'\\hline\n'
    1920          tex += u'%s\n'
    1921          tex += u'\n'
    1922          tex += u'\\end{tabular}\n'
    1923
    1924          current_meds = emr.get_current_substance_intake (
    1925                  include_inactive = False,
    1926                  include_unapproved = False,
    1927                  order_by = u'brand, substance'
    1928          )
    1929
    1930          # aggregate data
    1931          line_data = {}
    1932          for med in current_meds:
    1933                  identifier = gmTools.coalesce(med['brand'], med['substance'])
    1934
    1935                  try:
    1936                          line_data[identifier]
    1937                  except KeyError:
    1938                          line_data[identifier] = {'brand': u'', 'preparation': u'', 'schedule': u'', 'aims': [], 'strengths': []}
    1939
    1940                  line_data[identifier]['brand'] = identifier
    1941                  line_data[identifier]['strengths'].append(u'%s%s' % (med['amount'], med['unit'].strip()))
    1942                  line_data[identifier]['preparation'] = med['preparation']
    1943                  line_data[identifier]['schedule'] = gmTools.coalesce(med['schedule'], u'')
    1944                  if med['aim'] not in line_data[identifier]['aims']:
    1945                          line_data[identifier]['aims'].append(med['aim'])
    1946
    1947          # create lines
    1948          already_seen = []
    1949          lines = []
    1950          line1_template = u'%s %s & %s \\\\'
    1951          line2_template = u' & {\\scriptsize %s\\par} \\\\'
    1952
    1953          for med in current_meds:
    1954                  identifier = gmTools.coalesce(med['brand'], med['substance'])
    1955
    1956                  if identifier in already_seen:
    1957                          continue
    1958
    1959                  already_seen.append(identifier)
    1960
    1961                  lines.append (line1_template % (
    1962                          line_data[identifier]['brand'],
    1963                          line_data[identifier]['preparation'],
    1964                          line_data[identifier]['schedule']
    1965                  ))
    1966
    1967                  strengths = u'/'.join(line_data[identifier]['strengths'])
    1968                  if strengths == u'':
    1969                          template = u' & {\\scriptsize %s\\par} \\\\'
    1970                          for aim in line_data[identifier]['aims']:
    1971                                  lines.append(template % aim)
    1972                  else:
    1973                          if len(line_data[identifier]['aims']) == 0:
    1974                                  template = u'%s & \\\\'
    1975                                  lines.append(template % strengths)
    1976                          else:
    1977                                  template = u'%s & {\\scriptsize %s\\par} \\\\'
    1978                                  lines.append(template % (strengths, line_data[identifier]['aims'][0]))
    1979                                  template = u' & {\\scriptsize %s\\par} \\\\'
    1980                                  for aim in line_data[identifier]['aims'][1:]:
    1981                                          lines.append(template % aim)
    1982
    1983                  lines.append(u'\\hline')
    1984
    1985          return tex % u' \n'.join(lines)
    1986  #============================================================
    1987  _SQL_get_drug_components = u'SELECT * FROM ref.v_drug_components WHERE %s'
    1988
    1989 -class cDrugComponent(gmBusinessDBObject.cBusinessDBObject):
    1990
    1991          _cmd_fetch_payload = _SQL_get_drug_components % u'pk_component = %s'
    1992          _cmds_store_payload = [
    1993                  u"""UPDATE ref.lnk_substance2brand SET
    1994                                  fk_brand = %(pk_brand)s,
    1995                                  fk_substance = %(pk_consumable_substance)s
    1996                          WHERE
    1997                                  NOT EXISTS (
    1998                                          SELECT 1
    1999                                          FROM clin.substance_intake
    2000                                          WHERE fk_drug_component = %(pk_component)s
    2001                                          LIMIT 1
    2002                                  )
    2003                                          AND
    2004                                  pk = %(pk_component)s
    2005                                          AND
    2006                                  xmin = %(xmin_lnk_substance2brand)s
    2007                          RETURNING
    2008                                  xmin AS xmin_lnk_substance2brand
    2009                  """
    2010          ]
    2011          _updatable_fields = [
    2012                  u'pk_brand',
    2013                  u'pk_consumable_substance'
    2014          ]
    2015          #--------------------------------------------------------
    2016          # properties
    2017          #--------------------------------------------------------
    2018 -        def _get_containing_drug(self):
    2019                  return cBrandedDrug(aPK_obj = self._payload[self._idx['pk_brand']])
    2020
    2021          containing_drug = property(_get_containing_drug, lambda x:x)
    2022          #--------------------------------------------------------
    2023 -        def _get_is_in_use_by_patients(self):
    2024                  return self._payload[self._idx['is_in_use']]
    2025
    2026          is_in_use_by_patients = property(_get_is_in_use_by_patients, lambda x:x)
    2027          #--------------------------------------------------------
    2028 -        def _get_substance(self):
    2029                  return cConsumableSubstance(aPK_obj = self._payload[self._idx['pk_consumable_substance']])
    2030
    2031          substance =  property(_get_substance, lambda x:x)
    2032  #------------------------------------------------------------
    2033 -def get_drug_components():
    2034          cmd = _SQL_get_drug_components % u'true ORDER BY brand, substance'
    2035          rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = True)
    2036          return [ cDrugComponent(row = {'data': r, 'idx': idx, 'pk_field': 'pk_component'}) for r in rows ]
    2037  #------------------------------------------------------------
    2038 -class cDrugComponentMatchProvider(gmMatchProvider.cMatchProvider_SQL2):
    2039
    2040          _pattern = regex.compile(r'^\D+\s*\d+$', regex.UNICODE | regex.LOCALE)
    2041          _query_desc_only = u"""
    2042                  SELECT DISTINCT ON (component)
    2043                          pk_component,
    2044                          (substance || ' ' || amount || unit || ' ' || preparation || ' (' || brand ||  ')')
    2045                                  AS component
    2046                  FROM ref.v_drug_components
    2047                  WHERE
    2048                          substance %(fragment_condition)s
    2049                                  OR
    2050                          brand %(fragment_condition)s
    2051                  ORDER BY component
    2052                  LIMIT 50"""
    2053          _query_desc_and_amount = u"""
    2054
    2055                  SELECT DISTINCT ON (component)
    2056                          pk_component,
    2057                          (substance || ' ' || amount || unit || ' ' || preparation || ' (' || brand ||  ')')
    2058                                  AS component
    2059                  FROM ref.v_drug_components
    2060                  WHERE
    2061                          %(fragment_condition)s
    2062                  ORDER BY component
    2063                  LIMIT 50"""
    2064          #--------------------------------------------------------
    2065 -        def getMatchesByPhrase(self, aFragment):
    2066                  """Return matches for aFragment at start of phrases."""
    2067
    2068                  if cDrugComponentMatchProvider._pattern.match(aFragment):
    2069                          self._queries = [cDrugComponentMatchProvider._query_desc_and_amount]
    2070                          fragment_condition = """(substance ILIKE %(desc)s OR brand ILIKE %(desc)s)
    2071                                  AND
    2072                          amount::text ILIKE %(amount)s"""
    2073                          self._args['desc'] = u'%s%%' % regex.sub(r'\s*\d+$', u'', aFragment)
    2074                          self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment)
    2075                  else:
    2076                          self._queries = [cDrugComponentMatchProvider._query_desc_only]
    2077                          fragment_condition = u"ILIKE %(fragment)s"
    2078                          self._args['fragment'] = u"%s%%" % aFragment
    2079
    2080                  return self._find_matches(fragment_condition)
    2081          #--------------------------------------------------------
    2082 -        def getMatchesByWord(self, aFragment):
    2083                  """Return matches for aFragment at start of words inside phrases."""
    2084
    2085                  if cDrugComponentMatchProvider._pattern.match(aFragment):
    2086                          self._queries = [cDrugComponentMatchProvider._query_desc_and_amount]
    2087
    2088                          desc = regex.sub(r'\s*\d+$', u'', aFragment)
    2089                          desc = gmPG2.sanitize_pg_regex(expression = desc, escape_all = False)
    2090
    2091                          fragment_condition = """(substance ~* %(desc)s OR brand ~* %(desc)s)
    2092                                  AND
    2093                          amount::text ILIKE %(amount)s"""
    2094
    2095                          self._args['desc'] = u"( %s)|(^%s)" % (desc, desc)
    2096                          self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment)
    2097                  else:
    2098                          self._queries = [cDrugComponentMatchProvider._query_desc_only]
    2099                          fragment_condition = u"~* %(fragment)s"
    2100                          aFragment = gmPG2.sanitize_pg_regex(expression = aFragment, escape_all = False)
    2101                          self._args['fragment'] = u"( %s)|(^%s)" % (aFragment, aFragment)
    2102
    2103                  return self._find_matches(fragment_condition)
    2104          #--------------------------------------------------------
    2105 -        def getMatchesBySubstr(self, aFragment):
    2106                  """Return matches for aFragment as a true substring."""
    2107
    2108                  if cDrugComponentMatchProvider._pattern.match(aFragment):
    2109                          self._queries = [cDrugComponentMatchProvider._query_desc_and_amount]
    2110                          fragment_condition = """(substance ILIKE %(desc)s OR brand ILIKE %(desc)s)
    2111                                  AND
    2112                          amount::text ILIKE %(amount)s"""
    2113                          self._args['desc'] = u'%%%s%%' % regex.sub(r'\s*\d+$', u'', aFragment)
    2114                          self._args['amount'] = u'%s%%' % regex.sub(r'^\D+\s*', u'', aFragment)
    2115                  else:
    2116                          self._queries = [cDrugComponentMatchProvider._query_desc_only]
    2117                          fragment_condition = u"ILIKE %(fragment)s"
    2118                          self._args['fragment'] = u"%%%s%%" % aFragment
    2119
    2120                  return self._find_matches(fragment_condition)
    2121
    2122  #============================================================
    2123 -class cBrandedDrug(gmBusinessDBObject.cBusinessDBObject):
    2124          """Represents a drug as marketed by a manufacturer."""
    2125
    2126          _cmd_fetch_payload = u"SELECT * FROM ref.v_branded_drugs WHERE pk_brand = %s"
    2127          _cmds_store_payload = [
    2128                  u"""UPDATE ref.branded_drug SET
    2129                                  description = %(brand)s,
    2130                                  preparation = %(preparation)s,
    2131                                  atc_code = gm.nullify_empty_string(%(atc)s),
    2132                                  external_code = gm.nullify_empty_string(%(external_code)s),
    2133                                  external_code_type = gm.nullify_empty_string(%(external_code_type)s),
    2134                                  is_fake = %(is_fake_brand)s,
    2135                                  fk_data_source = %(pk_data_source)s
    2136                          WHERE
    2137                                  pk = %(pk_brand)s
    2138                                          AND
    2139                                  xmin = %(xmin_branded_drug)s
    2140                          RETURNING
    2141                                  xmin AS xmin_branded_drug
    2142                  """
    2143          ]
    2144          _updatable_fields = [
    2145                  u'brand',
    2146                  u'preparation',
    2147                  u'atc',
    2148                  u'is_fake_brand',
    2149                  u'external_code',
    2150                  u'external_code_type',
    2151                  u'pk_data_source'
    2152          ]
    2153          #--------------------------------------------------------
    2154 -        def save_payload(self, conn=None):
    2155                  success, data = super(self.__class__, self).save_payload(conn = conn)
    2156
    2157                  if not success:
    2158                          return (success, data)
    2159
    2160                  if self._payload[self._idx['atc']] is not None:
    2161                          atc = self._payload[self._idx['atc']].strip()
    2162                          if atc != u'':
    2163                                  gmATC.propagate_atc (
    2164                                          substance = self._payload[self._idx['brand']].strip(),
    2165                                          atc = atc
    2166                                  )
    2167
    2168                  return (success, data)
    2169          #--------------------------------------------------------
    2170 -        def set_substances_as_components(self, substances=None):
    2171
    2172                  if self.is_in_use_by_patients:
    2173                          return False
    2174
    2175                  args = {'brand': self._payload[self._idx['pk_brand']]}
    2176
    2177                  queries = [{'cmd': u"DELETE FROM ref.lnk_substance2brand WHERE fk_brand = %(brand)s", 'args': args}]
    2178                  cmd = u'INSERT INTO ref.lnk_substance2brand (fk_brand, fk_substance) VALUES (%%(brand)s, %s)'
    2179                  for s in substances:
    2180                          queries.append({'cmd': cmd % s['pk'], 'args': args})
    2181
    2182                  gmPG2.run_rw_queries(queries = queries)
    2183                  self.refetch_payload()
    2184
    2185                  return True
    2186          #--------------------------------------------------------
    2187 -        def add_component(self, substance=None, atc=None, amount=None, unit=None, pk_substance=None):
    2188
    2189                  args = {
    2190                          'brand': self.pk_obj,
    2191                          'subst': substance,
    2192                          'atc': atc,
    2193                          'pk_subst': pk_substance
    2194                  }
    2195
    2196                  if pk_substance is None:
    2197                          consumable = create_consumable_substance(substance = substance, atc = atc, amount = amount, unit = unit)
    2198                          args['pk_subst'] = consumable['pk']
    2199
    2200                  # already a component
    2201                  cmd = u"""
    2202                          SELECT pk_component
    2203                          FROM ref.v_drug_components
    2204                          WHERE
    2205                                  pk_brand = %(brand)s
    2206                                          AND
    2207                                  ((
    2208                                          (lower(substance) = lower(%(subst)s))
    2209                                                  OR
    2210                                          (lower(atc_substance) = lower(%(atc)s))
    2211                                                  OR
    2212                                          (pk_consumable_substance = %(pk_subst)s)
    2213                                  ) IS TRUE)
    2214                  """
    2215                  rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
    2216
    2217                  if len(rows) > 0:
    2218                          return
    2219
    2220                  # create it
    2221                  cmd = u"""
    2222                          INSERT INTO ref.lnk_substance2brand (fk_brand, fk_substance)
    2223                          VALUES (%(brand)s, %(pk_subst)s)
    2224                  """
    2225                  gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
    2226                  self.refetch_payload()
    2227          #------------------------------------------------------------
    2228 -        def remove_component(self, substance=None):
    2229                  if len(self._payload[self._idx['components']]) == 1:
    2230                          _log.error('cannot remove the only component of a drug')
    2231                          return False
    2232
    2233                  args = {'brand': self.pk_obj, 'comp': substance}
    2234                  cmd = u"""
    2235                          DELETE FROM ref.lnk_substance2brand
    2236                          WHERE
    2237                                  fk_brand = %(brand)s
    2238                                          AND
    2239                                  fk_substance = %(comp)s
    2240                                          AND
    2241                                  NOT EXISTS (
    2242                                          SELECT 1
    2243                                          FROM clin.substance_intake
    2244                                          WHERE fk_drug_component = %(comp)s
    2245                                          LIMIT 1
    2246                                  )
    2247                  """
    2248                  gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
    2249                  self.refetch_payload()
    2250
    2251                  return True
    2252          #--------------------------------------------------------
    2253          # properties
    2254          #--------------------------------------------------------
    2255 -        def _get_external_code(self):
    2256                  if self._payload[self._idx['external_code']] is None:
    2257                          return None
    2258
    2259                  return self._payload[self._idx['external_code']]
    2260
    2261          external_code = property(_get_external_code, lambda x:x)
    2262          #--------------------------------------------------------
    2263 -        def _get_external_code_type(self):
    2264
    2265                  # FIXME: maybe evaluate fk_data_source ?
    2266                  if self._payload[self._idx['external_code_type']] is None:
    2267                          return None
    2268
    2269                  return self._payload[self._idx['external_code_type']]
    2270
    2271          external_code_type = property(_get_external_code_type, lambda x:x)
    2272          #--------------------------------------------------------
    2273 -        def _get_components(self):
    2274                  cmd = _SQL_get_drug_components % u'pk_brand = %(brand)s'
    2275                  args = {'brand': self._payload[self._idx['pk_brand']]}
    2276                  rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
    2277                  return [ cDrugComponent(row = {'data': r, 'idx': idx, 'pk_field': 'pk_component'}) for r in rows ]
    2278
    2279          components = property(_get_components, lambda x:x)
    2280          #--------------------------------------------------------
    2281 -        def _get_components_as_substances(self):
    2282                  if self._payload[self._idx['pk_substances']] is None:
    2283                          return []
    2284                  cmd = _SQL_get_consumable_substance % u'pk IN %(pks)s'
    2285                  args = {'pks': tuple(self._payload[self._idx['pk_substances']])}
    2286                  rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
    2287                  return [ cConsumableSubstance(row = {'data': r, 'idx': idx, 'pk_field': 'pk'}) for r in rows ]
    2288
    2289          components_as_substances = property(_get_components_as_substances, lambda x:x)
    2290          #--------------------------------------------------------
    2291 -        def _get_is_vaccine(self):
    2292                  cmd = u'SELECT EXISTS (SELECT 1 FROM clin.vaccine WHERE fk_brand = %(fk_brand)s)'
    2293                  args = {'fk_brand': self._payload[self._idx['pk_brand']]}
    2294                  rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
    2295                  return rows[0][0]
    2296
    2297          is_vaccine = property(_get_is_vaccine, lambda x:x)
    2298          #--------------------------------------------------------
    2299 -        def _get_is_in_use_by_patients(self):
    2300                  cmd = u"""
    2301                          SELECT EXISTS (
    2302                                  SELECT 1
    2303                                  FROM clin.substance_intake
    2304                                  WHERE
    2305                                          fk_drug_component IS NOT NULL
    2306                                                  AND
    2307                                          fk_drug_component IN (
    2308                                                  SELECT r_ls2b.pk
    2309                                                  FROM ref.lnk_substance2brand r_ls2b
    2310                                                  WHERE fk_brand = %(pk)s
    2311                                          )
    2312                                  LIMIT 1
    2313                          )"""
    2314                  args = {'pk': self.pk_obj}
    2315
    2316                  rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
    2317                  return rows[0][0]
    2318
    2319          is_in_use_by_patients = property(_get_is_in_use_by_patients, lambda x:x)
    2320  #------------------------------------------------------------
    2321 -def get_branded_drugs():
    2322          cmd = u'SELECT pk FROM ref.branded_drug ORDER BY description'
    2323          rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = False)
    2324          return [ cBrandedDrug(aPK_obj = r['pk']) for r in rows ]
    2325  #------------------------------------------------------------
    2326 -def get_drug_by_brand(brand_name=None, preparation=None):
    2327          args = {'brand': brand_name, 'prep': preparation}
    2328
    2329          cmd = u'SELECT pk FROM ref.branded_drug WHERE lower(description) = lower(%(brand)s) AND lower(preparation) = lower(%(prep)s)'
    2330          rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
    2331
    2332          if len(rows) == 0:
    2333                  return None
    2334
    2335          return cBrandedDrug(aPK_obj = rows[0]['pk'])
    2336  #------------------------------------------------------------
    2337 -def create_branded_drug(brand_name=None, preparation=None, return_existing=False):
    2338
    2339          if preparation is None:
    2340                  preparation = _('units')
    2341
    2342          if preparation.strip() == u'':
    2343                  preparation = _('units')
    2344
    2345          if return_existing:
    2346                  drug = get_drug_by_brand(brand_name = brand_name, preparation = preparation)
    2347                  if drug is not None:
    2348                          return drug
    2349
    2350          cmd = u'INSERT INTO ref.branded_drug (description, preparation) VALUES (%(brand)s, %(prep)s) RETURNING pk'
    2351          args = {'brand': brand_name, 'prep': preparation}
    2352          rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True, get_col_idx = False)
    2353
    2354          return cBrandedDrug(aPK_obj = rows[0]['pk'])
    2355  #------------------------------------------------------------
    2356 -def delete_branded_drug(brand=None):
    2357          queries = []
    2358          args = {'pk': brand}
    2359
    2360          # delete components
    2361          cmd = u"""
    2362                  DELETE FROM ref.lnk_substance2brand
    2363                  WHERE
    2364                          fk_brand = %(pk)s
    2365                                  AND
    2366                          NOT EXISTS (
    2367                                  SELECT 1
    2368                                  FROM clin.v_pat_substance_intake
    2369                                  WHERE pk_brand = %(pk)s
    2370                                  LIMIT 1
    2371                          )
    2372          """
    2373          queries.append({'cmd': cmd, 'args': args})
    2374
    2375          # delete drug
    2376          cmd = u"""
    2377                  DELETE FROM ref.branded_drug
    2378                  WHERE
    2379                          pk = %(pk)s
    2380                                  AND
    2381                          NOT EXISTS (
    2382                                  SELECT 1
    2383                                  FROM clin.v_pat_substance_intake
    2384                                  WHERE pk_brand = %(pk)s
    2385                                  LIMIT 1
    2386                          )
    2387          """
    2388          queries.append({'cmd': cmd, 'args': args})
    2389
    2390          gmPG2.run_rw_queries(queries = queries)
    2391  #============================================================
    2392  # main
    2393  #------------------------------------------------------------
    2394  if __name__ == "__main__":
    2395
    2396          if len(sys.argv) < 2:
    2397                  sys.exit()
    2398
    2399          if sys.argv[1] != 'test':
    2400                  sys.exit()
    2401
    2402          from Gnumed.pycommon import gmLog2
    2403          from Gnumed.pycommon import gmI18N
    2404          from Gnumed.business import gmPerson
    2405
    2406          gmI18N.activate_locale()
    2407  #       gmDateTime.init()
    2408          #--------------------------------------------------------
    2409 -        def test_MMI_interface():
    2410                  mmi = cGelbeListeWineInterface()
    2411                  print mmi
    2412                  print "interface definition:", mmi.version
    2413                  print "database versions:   ", mmi.get_data_source_version()
    2414          #--------------------------------------------------------
    2415 -        def test_MMI_file():
    2416                  mmi_file = cGelbeListeCSVFile(filename = sys.argv[2])
    2417                  for drug in mmi_file:
    2418                          print "-------------"
    2419                          print '"%s" (ATC: %s / PZN: %s)' % (drug['name'], drug['atc'], drug['pzn'])
    2420                          for stoff in drug['wirkstoffe']:
    2421                                  print " Wirkstoff:", stoff
    2422                          raw_input()
    2423                          if mmi_file.has_unknown_fields is not None:
    2424                                  print "has extra data under [%s]" % gmTools.default_csv_reader_rest_key
    2425                          for key in mmi_file.csv_fieldnames:
    2426                                  print key, '->', drug[key]
    2427                          raw_input()
    2428                  mmi_file.close()
    2429          #--------------------------------------------------------
    2430 -        def test_mmi_switch_to():
    2431                  mmi = cGelbeListeWineInterface()
    2432                  mmi.switch_to_frontend(blocking = True)
    2433          #--------------------------------------------------------
    2434 -        def test_mmi_let_user_select_drugs():
    2435                  mmi = cGelbeListeWineInterface()
    2436                  mmi_file = mmi.__let_user_select_drugs()
    2437                  for drug in mmi_file:
    2438                          print "-------------"
    2439                          print '"%s" (ATC: %s / PZN: %s)' % (drug['name'], drug['atc'], drug['pzn'])
    2440                          for stoff in drug['wirkstoffe']:
    2441                                  print " Wirkstoff:", stoff
    2442                          print drug
    2443                  mmi_file.close()
    2444          #--------------------------------------------------------
    2445 -        def test_mmi_import_drugs():
    2446                  mmi = cGelbeListeWineInterface()
    2447                  mmi.import_drugs()
    2448          #--------------------------------------------------------
    2449 -        def test_mmi_interaction_check():
    2450                  mmi = cGelbeListeInterface()
    2451                  print mmi
    2452                  print "interface definition:", mmi.version
    2453                  # Metoprolol + Hct vs Citalopram
    2454                  diclofenac = '7587712'
    2455                  phenprocoumon = '4421744'
    2456                  mmi.check_interactions(drug_ids_list = [diclofenac, phenprocoumon])
    2457          #--------------------------------------------------------
    2458          # FreeDiams
    2459          #--------------------------------------------------------
    2460 -        def test_fd_switch_to():
    2461                  gmPerson.set_active_patient(patient = gmPerson.cIdentity(aPK_obj = 12))
    2462                  fd = cFreeDiamsInterface()
    2463                  fd.patient = gmPerson.gmCurrentPatient()
    2464  #               fd.switch_to_frontend(blocking = True)
    2465                  fd.import_fd2gm_file_as_drugs(filename = sys.argv[2])
    2466          #--------------------------------------------------------
    2467 -        def test_fd_show_interactions():
    2468                  gmPerson.set_active_patient(patient = gmPerson.cIdentity(aPK_obj = 12))
    2469                  fd = cFreeDiamsInterface()
    2470                  fd.patient = gmPerson.gmCurrentPatient()
    2471                  fd.check_interactions(substances = fd.patient.get_emr().get_current_substance_intake(include_unapproved = True))
    2472          #--------------------------------------------------------
    2473          # generic
    2474          #--------------------------------------------------------
    2475 -        def test_create_substance_intake():
    2476                  drug = create_substance_intake (
    2477                          pk_component = 2,
    2478                          encounter = 1,
    2479                          episode = 1
    2480                  )
    2481                  print drug
    2482          #--------------------------------------------------------
    2483 -        def test_show_components():
    2484                  drug = cBrandedDrug(aPK_obj = sys.argv[2])
    2485                  print drug
    2486                  print drug.components
    2487          #--------------------------------------------------------
    2488 -        def test_get_consumable_substances():
    2489                  for s in get_consumable_substances():
    2490                          print s
    2491          #--------------------------------------------------------
    2492 -        def test_drug2renal_insufficiency_url():
    2493                  drug2renal_insufficiency_url(search_term = 'Metoprolol')
    2494          #--------------------------------------------------------
    2495          # MMI/Gelbe Liste
    2496          #test_MMI_interface()
    2497          #test_MMI_file()
    2498          #test_mmi_switch_to()
    2499          #test_mmi_let_user_select_drugs()
    2500          #test_mmi_import_substances()
    2501          #test_mmi_import_drugs()
    2502
    2503          # FreeDiams
    2504          #test_fd_switch_to()
    2505          #test_fd_show_interactions()
    2506
    2507          # generic
    2508          #test_interaction_check()
    2509          #test_create_substance_intake()
    2510          #test_show_components()
    2511          #test_get_consumable_substances()
    2512
    2513          test_drug2renal_insufficiency_url()
    2514  #============================================================
    2515