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