Gnumed.business.gmMedication

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