Gnumed.business.gmDrugDataSources

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

# Source Code for Module Gnumed.business.gmDrugDataSources

       1  # -*- coding: utf-8 -*-
       2  """Code handling drug data sources (such as databases).
       3
       4  license: GPL v2 or later
       5  """
       6  #============================================================
       7  __author__ = "K.Hilbert <Karsten.Hilbert@gmx.net>"
       8
       9  import sys
      10  import csv
      11  import os
      12  import io
      13  import logging
      14  import subprocess
      15  import re as regex
      16  from xml.etree import ElementTree as etree
      17
      18
      19  if __name__ == '__main__':
      20          sys.path.insert(0, '../../')
      21          from Gnumed.pycommon import gmI18N
      22          gmI18N.activate_locale()
      23          gmI18N.install_domain('gnumed')
      24  from Gnumed.pycommon import gmTools
      25  from Gnumed.pycommon import gmShellAPI
      26  from Gnumed.business import gmMedication
      27  from Gnumed.business import gmCoding
      28
      29
      30  _log = logging.getLogger('gm.meds')
      31
      32  #============================================================
      33  # generic drug data source interface class
      34  #------------------------------------------------------------
      35 -class cDrugDataSourceInterface(object):
      36
      37          #--------------------------------------------------------
      38 -        def __init__(self):
      39                  self.patient = None
      40                  self.reviewer = None
      41                  self.custom_path_to_binary = None
      42          #--------------------------------------------------------
      43 -        def get_data_source_version(self):
      44                  raise NotImplementedError
      45          #--------------------------------------------------------
      46 -        def create_data_source_entry(self):
      47                  raise NotImplementedError
      48          #--------------------------------------------------------
      49 -        def switch_to_frontend(self, blocking=False):
      50                  raise NotImplementedError
      51          #--------------------------------------------------------
      52 -        def import_drugs(self):
      53                  self.switch_to_frontend()
      54          #--------------------------------------------------------
      55 -        def check_interactions(self, substance_intakes=None):
      56                  self.switch_to_frontend()
      57          #--------------------------------------------------------
      58 -        def show_info_on_drug(self, substance_intake=None):
      59                  self.switch_to_frontend()
      60          #--------------------------------------------------------
      61 -        def show_info_on_substance(self, substance_intake=None):
      62                  self.switch_to_frontend()
      63          #--------------------------------------------------------
      64 -        def prescribe(self, substance_intakes=None):
      65                  self.switch_to_frontend()
      66                  return []
      67
      68  #============================================================
      69  # Gelbe Liste
      70  #------------------------------------------------------------
      71  # wishlist:
      72  # - --conf-file= for glwin.exe
      73  # - wirkstoff: Konzentration auch in Multiprodukten
      74  # - wirkstoff: ATC auch in Multiprodukten
      75  # - Suche nach ATC per CLI
      76
      77 -class cGelbeListeCSVFile(object):
      78          """Iterator over a Gelbe Liste/MMI v8.2 CSV file."""
      79
      80          version = 'Gelbe Liste/MMI v8.2 CSV file interface'
      81          default_transfer_file_windows = r"c:\rezept.txt"
      82          #default_encoding = 'cp1252'
      83          default_encoding = 'cp1250'
      84          csv_fieldnames = [
      85                  'name',
      86                  'packungsgroesse',                                      # obsolete, use "packungsmenge"
      87                  'darreichungsform',
      88                  'packungstyp',
      89                  'festbetrag',
      90                  'avp',
      91                  'hersteller',
      92                  'rezepttext',
      93                  'pzn',
      94                  'status_vertrieb',
      95                  'status_rezeptpflicht',
      96                  'status_fachinfo',
      97                  'btm',
      98                  'atc',
      99                  'anzahl_packungen',
     100                  'zuzahlung_pro_packung',
     101                  'einheit',
     102                  'schedule_morgens',
     103                  'schedule_mittags',
     104                  'schedule_abends',
     105                  'schedule_nachts',
     106                  'status_dauermedikament',
     107                  'status_hausliste',
     108                  'status_negativliste',
     109                  'ik_nummer',
     110                  'status_rabattvertrag',
     111                  'wirkstoffe',
     112                  'wirkstoffmenge',
     113                  'wirkstoffeinheit',
     114                  'wirkstoffmenge_bezug',
     115                  'wirkstoffmenge_bezugseinheit',
     116                  'status_import',
     117                  'status_lifestyle',
     118                  'status_ausnahmeliste',
     119                  'packungsmenge',
     120                  'apothekenpflicht',
     121                  'status_billigere_packung',
     122                  'rezepttyp',
     123                  'besonderes_arzneimittel',                      # Abstimmungsverfahren SGB-V
     124                  't_rezept_pflicht',                             # Thalidomid-Rezept
     125                  'erstattbares_medizinprodukt',
     126                  'hilfsmittel',
     127                  'hzv_rabattkennung',
     128                  'hzv_preis'
     129          ]
     130          boolean_fields = [
     131                  'status_rezeptpflicht',
     132                  'status_fachinfo',
     133                  'btm',
     134                  'status_dauermedikament',
     135                  'status_hausliste',
     136                  'status_negativliste',
     137                  'status_rabattvertrag',
     138                  'status_import',
     139                  'status_lifestyle',
     140                  'status_ausnahmeliste',
     141                  'apothekenpflicht',
     142                  'status_billigere_packung',
     143                  'besonderes_arzneimittel',                      # Abstimmungsverfahren SGB-V
     144                  't_rezept_pflicht',
     145                  'erstattbares_medizinprodukt',
     146                  'hilfsmittel'
     147          ]
     148          #--------------------------------------------------------
     149 -        def __init__(self, filename=None):
     150
     151                  _log.info(cGelbeListeCSVFile.version)
     152
     153                  self.filename = filename
     154                  if filename is None:
     155                          self.filename = cGelbeListeCSVFile.default_transfer_file_windows
     156
     157                  _log.debug('reading Gelbe Liste/MMI drug data from [%s]', self.filename)
     158
     159                  self.csv_file = io.open(filename, mode = 'rt', encoding = cGelbeListeCSVFile.default_encoding)
     160
     161                  self.csv_lines = gmTools.unicode_csv_reader (
     162                          self.csv_file,
     163                          fieldnames = cGelbeListeCSVFile.csv_fieldnames,
     164                          delimiter = ';',
     165                          quotechar = '"',
     166                          dict = True
     167                  )
     168          #--------------------------------------------------------
     169 -        def __iter__(self):
     170                  return self
     171          #--------------------------------------------------------
     172 -        def next(self):
     173                  line = self.csv_lines.next()
     174
     175                  for field in cGelbeListeCSVFile.boolean_fields:
     176                          line[field] = (line[field].strip() == 'T')
     177
     178                  # split field "Wirkstoff" by ";"
     179                  if line['wirkstoffe'].strip() == '':
     180                          line['wirkstoffe'] = []
     181                  else:
     182                          line['wirkstoffe'] = [ wirkstoff.strip() for wirkstoff in line['wirkstoffe'].split(';') ]
     183
     184                  return line
     185          #--------------------------------------------------------
     186 -        def close(self, truncate=True):
     187                  try: self.csv_file.close()
     188                  except: pass
     189
     190                  if truncate:
     191                          try: os.open(self.filename, 'wb').close
     192                          except: pass
     193          #--------------------------------------------------------
     194 -        def _get_has_unknown_fields(self):
     195                  return (gmTools.default_csv_reader_rest_key in self.csv_fieldnames)
     196
     197          has_unknown_fields = property(_get_has_unknown_fields, lambda x:x)
     198
     199  #============================================================
     200 -class cGelbeListeWindowsInterface(cDrugDataSourceInterface):
     201          """Support v8.2 CSV file interface only."""
     202
     203          version = 'Gelbe Liste/MMI v8.2 interface'
     204          default_encoding = 'cp1250'
     205          bdt_line_template = '%03d6210#%s\r\n'           # Medikament verordnet auf Kassenrezept
     206          bdt_line_base_length = 8
     207          #--------------------------------------------------------
     208 -        def __init__(self):
     209
     210                  cDrugDataSourceInterface.__init__(self)
     211
     212                  _log.info('%s (native Windows)', cGelbeListeWindowsInterface.version)
     213
     214                  self.path_to_binary = r'C:\Programme\MMI PHARMINDEX\glwin.exe'
     215                  self.args = r'-KEEPBACKGROUND -PRESCRIPTIONFILE %s -CLOSETOTRAY'
     216
     217                  paths = gmTools.gmPaths()
     218
     219                  self.default_csv_filename = os.path.join(paths.tmp_dir, 'rezept.txt')
     220                  self.default_csv_filename_arg = paths.tmp_dir
     221                  self.interactions_filename = os.path.join(paths.tmp_dir, 'gm2mmi.bdt')
     222                  self.data_date_filename = r'C:\Programme\MMI PHARMINDEX\datadate.txt'
     223
     224                  self.__data_date = None
     225                  self.__online_update_date = None
     226
     227                  # use adjusted config.dat
     228          #--------------------------------------------------------
     229 -        def get_data_source_version(self, force_reload=False):
     230
     231                  if self.__data_date is not None:
     232                          if not force_reload:
     233                                  return {
     234                                          'data': self.__data_date,
     235                                          'online_update': self.__online_update_date
     236                                  }
     237                  try:
     238                          open(self.data_date_filename, 'wb').close()
     239                  except Exception:
     240                          _log.error('problem querying the MMI drug database for version information')
     241                          _log.exception('cannot create MMI drug database version file [%s]', self.data_date_filename)
     242                          self.__data_date = None
     243                          self.__online_update_date = None
     244                          return {
     245                                  'data': '?',
     246                                  'online_update': '?'
     247                          }
     248
     249                  cmd = '%s -DATADATE' % self.path_to_binary
     250                  if not gmShellAPI.run_command_in_shell(command = cmd, blocking = True):
     251                          _log.error('problem querying the MMI drug database for version information')
     252                          self.__data_date = None
     253                          self.__online_update_date = None
     254                          return {
     255                                  'data': '?',
     256                                  'online_update': '?'
     257                          }
     258
     259                  try:
     260                          version_file = io.open(self.data_date_filename, mode = 'rt', encoding = 'utf8')
     261                  except Exception:
     262                          _log.error('problem querying the MMI drug database for version information')
     263                          _log.exception('cannot open MMI drug database version file [%s]', self.data_date_filename)
     264                          self.__data_date = None
     265                          self.__online_update_date = None
     266                          return {
     267                                  'data': '?',
     268                                  'online_update': '?'
     269                          }
     270
     271                  self.__data_date = version_file.readline()[:10]
     272                  self.__online_update_date = version_file.readline()[:10]
     273                  version_file.close()
     274
     275                  return {
     276                          'data': self.__data_date,
     277                          'online_update': self.__online_update_date
     278                  }
     279          #--------------------------------------------------------
     280 -        def create_data_source_entry(self):
     281                  versions = self.get_data_source_version()
     282
     283                  return gmCoding.create_data_source (
     284                          long_name = 'Medikamentendatenbank "mmi PHARMINDEX" (Gelbe Liste)',
     285                          short_name = 'GL/MMI',
     286                          version = 'Daten: %s, Preise (Onlineupdate): %s' % (versions['data'], versions['online_update']),
     287                          source = 'Medizinische Medien Informations GmbH, Am Forsthaus Gravenbruch 7, 63263 Neu-Isenburg',
     288                          language = 'de'
     289                  )
     290          #--------------------------------------------------------
     291 -        def switch_to_frontend(self, blocking=False, cmd=None):
     292
     293                  try:
     294                          # must make sure csv file exists
     295                          open(self.default_csv_filename, 'wb').close()
     296                  except IOError:
     297                          _log.exception('problem creating GL/MMI <-> GNUmed exchange file')
     298                          return False
     299
     300                  if cmd is None:
     301                          cmd = ('%s %s' % (self.path_to_binary, self.args)) % self.default_csv_filename_arg
     302
     303                  if os.name == 'nt':
     304                          blocking = True
     305                  if not gmShellAPI.run_command_in_shell(command = cmd, blocking = blocking):
     306                          _log.error('problem switching to the MMI drug database')
     307                          # apparently on the first call MMI does not
     308                          # consistently return 0 on success
     309  #                       return False
     310
     311                  return True
     312          #--------------------------------------------------------
     313 -        def __let_user_select_drugs(self):
     314
     315                  # better to clean up interactions file
     316                  open(self.interactions_filename, 'wb').close()
     317
     318                  if not self.switch_to_frontend(blocking = True):
     319                          return None
     320
     321                  return cGelbeListeCSVFile(filename = self.default_csv_filename)
     322          #--------------------------------------------------------
     323 -        def import_drugs_as_substances(self):
     324
     325                  selected_drugs = self.__let_user_select_drugs()
     326                  if selected_drugs is None:
     327                          return None
     328
     329                  new_substances = []
     330
     331                  for drug in selected_drugs:
     332                          atc = None                                                      # hopefully MMI eventually supports atc-per-substance in a drug...
     333                          if len(drug['wirkstoffe']) == 1:
     334                                  atc = drug['atc']
     335                          for wirkstoff in drug['wirkstoffe']:
     336                                  new_substances.append(gmMedication.create_substance_dose(substance = wirkstoff, atc = atc, amount = amount, unit = unit))
     337
     338                  selected_drugs.close()
     339
     340                  return new_substances
     341          #--------------------------------------------------------
     342 -        def import_drugs(self):
     343
     344                  selected_drugs = self.__let_user_select_drugs()
     345                  if selected_drugs is None:
     346                          return None
     347
     348                  data_src_pk = self.create_data_source_entry()
     349
     350                  new_drugs = []
     351                  new_substances = []
     352
     353                  for entry in selected_drugs:
     354
     355                          _log.debug('importing drug: %s %s', entry['name'], entry['darreichungsform'])
     356
     357                          if entry['hilfsmittel']:
     358                                  _log.debug('skipping Hilfsmittel')
     359                                  continue
     360
     361                          if entry['erstattbares_medizinprodukt']:
     362                                  _log.debug('skipping sonstiges Medizinprodukt')
     363                                  continue
     364
     365                          # create drug product (or get it if it already exists)
     366                          drug = gmMedication.create_drug_product(product_name = entry['name'], preparation = entry['darreichungsform'])
     367                          if drug is None:
     368                                  drug = gmMedication.get_drug_by_name(product_name = entry['name'], preparation = entry['darreichungsform'])
     369                          new_drugs.append(drug)
     370
     371                          # update fields
     372                          drug['is_fake_product'] = False
     373                          drug['atc'] = entry['atc']
     374                          drug['external_code_type'] = 'DE-PZN'
     375                          drug['external_code'] = entry['pzn']
     376                          drug['fk_data_source'] = data_src_pk
     377                          drug.save()
     378
     379                          # add components to drug
     380                          atc = None                                                      # hopefully MMI eventually supports atc-per-substance in a drug...
     381                          if len(entry['wirkstoffe']) == 1:
     382                                  atc = entry['atc']
     383                          for wirkstoff in entry['wirkstoffe']:
     384                                  drug.add_component(substance = wirkstoff, atc = atc)
     385
     386                          # create as substance doses, too
     387                          atc = None                                                      # hopefully MMI eventually supports atc-per-substance in a drug...
     388                          if len(entry['wirkstoffe']) == 1:
     389                                  atc = entry['atc']
     390                          for wirkstoff in entry['wirkstoffe']:
     391                                  new_substances.append(gmMedication.create_substance_dose(substance = wirkstoff, atc = atc, amount = amount, unit = unit))
     392
     393                  return new_drugs, new_substances
     394          #--------------------------------------------------------
     395 -        def check_interactions(self, drug_ids_list=None, substances=None):
     396                  """For this to work the BDT interaction check must be configured in the MMI."""
     397
     398                  if drug_ids_list is None:
     399                          if substances is None:
     400                                  return
     401                          if len(substances) < 2:
     402                                  return
     403                          drug_ids_list = [ (s.external_code_type, s.external_code) for s in substances ]
     404                          drug_ids_list = [ code_value for code_type, code_value in drug_ids_list if (code_value is not None) and (code_type == 'DE-PZN')]
     405
     406                  else:
     407                          if len(drug_ids_list) < 2:
     408                                  return
     409
     410                  if drug_ids_list < 2:
     411                          return
     412
     413                  bdt_file = io.open(self.interactions_filename, mode = 'wt', encoding = cGelbeListeWindowsInterface.default_encoding)
     414
     415                  for pzn in drug_ids_list:
     416                          pzn = pzn.strip()
     417                          lng = cGelbeListeWindowsInterface.bdt_line_base_length + len(pzn)
     418                          bdt_file.write(cGelbeListeWindowsInterface.bdt_line_template % (lng, pzn))
     419
     420                  bdt_file.close()
     421
     422                  self.switch_to_frontend(blocking = True)
     423          #--------------------------------------------------------
     424 -        def show_info_on_drug(self, drug=None):
     425                  self.switch_to_frontend(blocking = True)
     426          #--------------------------------------------------------
     427 -        def show_info_on_substance(self, substance=None):
     428
     429                  cmd = None
     430
     431                  if substance.external_code_type == 'DE-PZN':
     432                          cmd = '%s -PZN %s' % (self.path_to_binary, substance.external_code)
     433
     434                  if cmd is None:
     435                          name = gmTools.coalesce (
     436                                  substance['product'],
     437                                  substance['substance']
     438                          )
     439                          cmd = '%s -NAME %s' % (self.path_to_binary, name)
     440
     441                  # better to clean up interactions file
     442                  open(self.interactions_filename, 'wb').close()
     443
     444                  self.switch_to_frontend(cmd = cmd)
     445
     446  #============================================================
     447 -class cGelbeListeWineInterface(cGelbeListeWindowsInterface):
     448
     449 -        def __init__(self):
     450                  cGelbeListeWindowsInterface.__init__(self)
     451
     452                  _log.info('%s (WINE extension)', cGelbeListeWindowsInterface.version)
     453
     454                  # FIXME: if -CLOSETOTRAY is used GNUmed cannot detect the end of MMI
     455                  self.path_to_binary = r'wine "C:\Programme\MMI PHARMINDEX\glwin.exe"'
     456                  self.args = r'"-PRESCRIPTIONFILE %s -KEEPBACKGROUND"'
     457
     458                  paths = gmTools.gmPaths()
     459
     460                  self.default_csv_filename = os.path.join(paths.home_dir, '.wine', 'drive_c', 'windows', 'temp', 'mmi2gm.csv')
     461                  self.default_csv_filename_arg = r'c:\windows\temp\mmi2gm.csv'
     462                  self.interactions_filename = os.path.join(paths.home_dir, '.wine', 'drive_c', 'windows', 'temp', 'gm2mmi.bdt')
     463                  self.data_date_filename = os.path.join(paths.home_dir, '.wine', 'drive_c', 'Programme', 'MMI PHARMINDEX', 'datadate.txt')
     464
     465  #============================================================
     466  # FreeDiams
     467  #------------------------------------------------------------
     468 -class cFreeDiamsInterface(cDrugDataSourceInterface):
     469
     470          version = 'FreeDiams interface'
     471          default_encoding = 'utf8'
     472          default_dob_format = '%Y/%m/%d'
     473
     474          map_gender2mf = {
     475                  'm': 'M',
     476                  'f': 'F',
     477                  'tf': 'H',
     478                  'tm': 'H',
     479                  'h': 'H'
     480          }
     481          #--------------------------------------------------------
     482 -        def __init__(self):
     483                  cDrugDataSourceInterface.__init__(self)
     484                  _log.info(cFreeDiamsInterface.version)
     485
     486                  self.__imported_drugs = []
     487
     488                  self.__gm2fd_filename = gmTools.get_unique_filename(prefix = r'gm2freediams-', suffix = r'.xml')
     489                  _log.debug('GNUmed -> FreeDiams "exchange-in" file: %s', self.__gm2fd_filename)
     490                  self.__fd2gm_filename = gmTools.get_unique_filename(prefix = r'freediams2gm-', suffix = r'.xml')
     491                  _log.debug('GNUmed <-> FreeDiams "exchange-out"/"prescription" file: %s', self.__fd2gm_filename)
     492                  paths = gmTools.gmPaths()
     493                  # this file can be modified by the user as needed:
     494                  self.__fd4gm_config_file = os.path.join(paths.home_dir, '.gnumed', 'freediams4gm.conf')
     495                  _log.debug('FreeDiams config file for GNUmed use: %s', self.__fd4gm_config_file)
     496
     497                  self.path_to_binary = None
     498                  self.__detect_binary()
     499          #--------------------------------------------------------
     500 -        def get_data_source_version(self):
     501                  # ~/.freediams/config.ini: [License] -> AcceptedVersion=....
     502
     503                  if not self.__detect_binary():
     504                          return False
     505
     506                  freediams = subprocess.Popen (
     507                          args = '--version',                             # --version or -version or -v
     508                          executable = self.path_to_binary,
     509                          stdout = subprocess.PIPE,
     510                          stderr = subprocess.PIPE,
     511  #                       close_fds = True,                                       # Windows can't do that in conjunction with stdout/stderr = ... :-(
     512                          universal_newlines = True
     513                  )
     514                  data, errors = freediams.communicate()
     515                  version = regex.search('FreeDiams\s\d.\d.\d', data).group().split()[1]
     516                  _log.debug('FreeDiams %s', version)
     517
     518                  return version
     519          #--------------------------------------------------------
     520 -        def create_data_source_entry(self):
     521                  return gmCoding.create_data_source (
     522                          long_name = '"FreeDiams" Drug Database Frontend',
     523                          short_name = 'FreeDiams',
     524                          version = self.get_data_source_version(),
     525                          source = 'http://ericmaeker.fr/FreeMedForms/di-manual/index.html',
     526                          language = 'fr'                 # actually to be multi-locale
     527                  )
     528          #--------------------------------------------------------
     529 -        def switch_to_frontend(self, blocking=False, mode='interactions'):
     530                  """http://ericmaeker.fr/FreeMedForms/di-manual/en/html/ligne_commandes.html"""
     531
     532                  _log.debug('calling FreeDiams in [%s] mode', mode)
     533
     534                  self.__imported_drugs = []
     535
     536                  if not self.__detect_binary():
     537                          return False
     538
     539                  self.__create_gm2fd_file(mode = mode)
     540
     541                  args = '--exchange-in="%s"' % (self.__gm2fd_filename)
     542                  cmd = r'%s %s' % (self.path_to_binary, args)
     543                  if os.name == 'nt':
     544                          blocking = True
     545                  if not gmShellAPI.run_command_in_shell(command = cmd, blocking = blocking):
     546                          _log.error('problem switching to the FreeDiams drug database')
     547                          return False
     548
     549                  if blocking == True:
     550                          self.import_fd2gm_file_as_drugs()
     551
     552                  return True
     553          #--------------------------------------------------------
     554 -        def import_drugs(self):
     555                  self.switch_to_frontend(blocking = True)
     556          #--------------------------------------------------------
     557 -        def check_interactions(self, substance_intakes=None):
     558                  if substance_intakes is None:
     559                          return
     560                  if len(substance_intakes) < 2:
     561                          return
     562
     563                  self.__create_prescription_file(substance_intakes = substance_intakes)
     564                  self.switch_to_frontend(mode = 'interactions', blocking = False)
     565          #--------------------------------------------------------
     566 -        def show_info_on_drug(self, substance_intake=None):
     567                  if substance_intake is None:
     568                          return
     569
     570                  self.__create_prescription_file(substance_intakes = [substance_intake])
     571                  self.switch_to_frontend(mode = 'interactions', blocking = False)
     572          #--------------------------------------------------------
     573 -        def show_info_on_substance(self, substance_intake=None):
     574                  self.show_info_on_drug(substance_intake = substance_intake)
     575          #--------------------------------------------------------
     576 -        def prescribe(self, substance_intakes=None):
     577                  if substance_intakes is None:
     578                          if not self.__export_latest_prescription():
     579                                  self.__create_prescription_file()
     580                  else:
     581                          self.__create_prescription_file(substance_intakes = substance_intakes)
     582
     583                  self.switch_to_frontend(mode = 'prescription', blocking = True)
     584                  self.import_fd2gm_file_as_prescription()
     585
     586                  return self.__imported_drugs
     587          #--------------------------------------------------------
     588          # internal helpers
     589          #--------------------------------------------------------
     590 -        def __detect_binary(self):
     591
     592                  if self.path_to_binary is not None:
     593                          return True
     594
     595                  found, cmd = gmShellAPI.find_first_binary(binaries = [
     596                          r'/usr/bin/freediams',
     597                          r'freediams',
     598                          r'/Applications/FreeDiams.app/Contents/MacOs/FreeDiams',
     599                          r'C:\Program Files (x86)\FreeDiams\freediams.exe',
     600                          r'C:\Program Files\FreeDiams\freediams.exe',
     601                          r'c:\programs\freediams\freediams.exe',
     602                          r'freediams.exe'
     603                  ])
     604
     605                  if found:
     606                          self.path_to_binary = cmd
     607                          return True
     608
     609                  try:
     610                          self.custom_path_to_binary
     611                  except AttributeError:
     612                          _log.error('cannot find FreeDiams binary, no custom path set')
     613                          return False
     614
     615                  if self.custom_path_to_binary is None:
     616                          _log.error('cannot find FreeDiams binary')
     617                          return False
     618
     619                  found, cmd = gmShellAPI.detect_external_binary(binary = self.custom_path_to_binary)
     620                  if found:
     621                          self.path_to_binary = cmd
     622                          return True
     623
     624                  _log.error('cannot find FreeDiams binary')
     625                  return False
     626          #--------------------------------------------------------
     627 -        def __export_latest_prescription(self):
     628
     629                  if self.patient is None:
     630                          _log.debug('cannot export latest FreeDiams prescriptions w/o patient')
     631                          return False
     632
     633                  docs = self.patient.get_document_folder()
     634                  prescription = docs.get_latest_freediams_prescription()
     635                  if prescription is None:
     636                          _log.debug('no FreeDiams prescription available')
     637                          return False
     638
     639                  for part in prescription.parts:
     640                          if part['filename'] == 'freediams-prescription.xml':
     641                                  if part.save_to_file(filename = self.__fd2gm_filename) is not None:
     642                                          return True
     643
     644                  _log.error('cannot export latest FreeDiams prescription to XML file')
     645
     646                  return False
     647          #--------------------------------------------------------
     648 -        def __create_prescription_file(self, substance_intakes=None):
     649                  """FreeDiams calls this exchange-out or prescription file.
     650
     651                          CIS stands for Unique Speciality Identifier (eg bisoprolol 5 mg, gel).
     652                          CIS is AFSSAPS specific, but pharmacist can retreive drug name with the CIS.
     653                          AFSSAPS is the French FDA.
     654
     655                          CIP stands for Unique Presentation Identifier (eg 30 pills plaq)
     656                          CIP if you want to specify the packaging of the drug (30 pills
     657                          thermoformed tablet...) -- actually not really usefull for french
     658                          doctors.
     659                          # .external_code_type: u'FR-CIS'
     660                          # .external_cod: the CIS value
     661
     662                  OnlyForTest:
     663                          OnlyForTest drugs will be processed by the IA Engine but
     664                          not printed (regardless of FreeDiams mode). They are shown
     665                          in gray in the prescription view.
     666
     667                          Select-only is a mode where FreeDiams creates a list of drugs
     668                          not a full prescription. In this list, users can add ForTestOnly
     669                          drug if they want to
     670                                  1. print the list without some drugs
     671                                  2. but including these drugs in the IA engine calculation
     672
     673                          Select-Only mode does not have any relation with the ForTestOnly drugs.
     674
     675                  IsTextual:
     676                          What is the use and significance of the
     677                                  <IsTextual>true/false</IsTextual>
     678                          flag when both <DrugName> and <TextualDrugName> exist ?
     679
     680                          This tag must be setted even if it sounds like a duplicated
     681                          data. This tag is needed inside FreeDiams code.
     682
     683                  INN:
     684                          GNUmed will pass the substance in <TextualDrugName
     685                          and will also pass <INN>True</INN>.
     686
     687                          Eric:   Nop, this is not usefull because pure textual drugs
     688                                          are not processed but just shown.
     689                  """
     690                  # virginize file
     691                  open(self.__fd2gm_filename, 'wb').close()
     692
     693                  # make sure we've got something to do
     694                  if substance_intakes is None:
     695                          if self.patient is None:
     696                                  _log.warning('cannot create prescription file because there is neither a patient nor a substance intake list')
     697                                  # do fail because __export_latest_prescription() should not have been called without patient
     698                                  return False
     699                          emr = self.patient.emr
     700                          substance_intakes = emr.get_current_medications (
     701                                  include_inactive = False,
     702                                  include_unapproved = True
     703                          )
     704
     705                  drug_snippets = []
     706
     707                  # process FD drugs
     708                  fd_intakes = [ i for i in substance_intakes if (
     709                          (i['intake_is_approved_of'] is True)
     710                                  and
     711                          (i['external_code_type_product'] is not None)
     712                                  and
     713                          (i['external_code_type_product'].startswith('FreeDiams::'))
     714                  )]
     715
     716                  intakes_pooled_by_product = {}
     717                  for intake in fd_intakes:
     718                          # this will leave only one entry per drug
     719                          # but FreeDiams knows the components ...
     720                          intakes_pooled_by_product[intake['product']] = intake
     721                  del fd_intakes
     722
     723                  drug_snippet = """<Prescription>
     724                          <Drug u1="%s" u2="" old="%s" u3="" db="%s">             <!-- "old" needs to be the same as "u1" if not known -->
     725                                  <DrugName>%s</DrugName>                                         <!-- just for identification when reading XML files -->
     726                          </Drug>
     727                  </Prescription>"""
     728
     729                  last_db_id = 'CA_HCDPD'
     730                  for intake in intakes_pooled_by_product.values():
     731                          last_db_id = gmTools.xml_escape_string(text = intake['external_code_type_product'].replace('FreeDiams::', '').split('::')[0])
     732                          drug_snippets.append(drug_snippet % (
     733                                  gmTools.xml_escape_string(text = intake['external_code_product'].strip()),
     734                                  gmTools.xml_escape_string(text = intake['external_code_product'].strip()),
     735                                  last_db_id,
     736                                  gmTools.xml_escape_string(text = intake['product'].strip())
     737                          ))
     738
     739                  # process non-FD drugs
     740                  non_fd_intakes = [ i for i in substance_intakes if (
     741                          (i['intake_is_approved_of'] is True)
     742                          and (
     743                                  (i['external_code_type_product'] is None)
     744                                          or
     745                                  (not i['external_code_type_product'].startswith('FreeDiams::'))
     746                          )
     747                  )]
     748
     749                  non_fd_product_intakes = [ i for i in non_fd_intakes if i['product'] is not None ]
     750                  non_fd_substance_intakes = [ i for i in non_fd_intakes if i['product'] is None ]
     751                  del non_fd_intakes
     752
     753                  drug_snippet = """<Prescription>
     754                          <Drug u1="-1" u2="" old="" u3="" db="">
     755                                  <DrugName>%s</DrugName>
     756                          </Drug>
     757                          <Dose Note="%s" IsTextual="true" IsAld="false"/>
     758                  </Prescription>"""
     759  #                               <DrugUidName></DrugUidName>
     760  #                               <DrugForm></DrugForm>
     761  #                               <DrugRoute></DrugRoute>
     762  #                               <DrugStrength/>
     763
     764                  for intake in non_fd_substance_intakes:
     765                          drug_name = '%s %s%s (%s)' % (
     766                                  intake['substance'],
     767                                  intake['amount'],
     768                                  intake['unit'],
     769                                  intake['l10n_preparation']
     770                          )
     771                          drug_snippets.append(drug_snippet % (
     772                                  gmTools.xml_escape_string(text = drug_name.strip()),
     773                                  gmTools.xml_escape_string(text = gmTools.coalesce(intake['schedule'], ''))
     774                          ))
     775
     776                  intakes_pooled_by_product = {}
     777                  for intake in non_fd_product_intakes:
     778                          prod = '%s %s' % (intake['product'], intake['l10n_preparation'])
     779                          try:
     780                                  intakes_pooled_by_product[prod].append(intake)
     781                          except KeyError:
     782                                  intakes_pooled_by_product[prod] = [intake]
     783
     784                  for product, comps in intakes_pooled_by_product.items():
     785                          drug_name = '%s\n' % product
     786                          for comp in comps:
     787                                  drug_name += '  %s %s%s\n' % (
     788                                          comp['substance'],
     789                                          comp['amount'],
     790                                          comp['unit']
     791                          )
     792                          drug_snippets.append(drug_snippet % (
     793                                  gmTools.xml_escape_string(text = drug_name.strip()),
     794                                  gmTools.xml_escape_string(text = gmTools.coalesce(comps[0]['schedule'], ''))
     795                          ))
     796
     797                  # assemble XML file
     798                  xml = """<?xml version = "1.0" encoding = "UTF-8"?>
     799  <!DOCTYPE FreeMedForms>
     800  <FreeDiams>
     801          <FullPrescription version="0.7.2">
     802                  %s
     803          </FullPrescription>
     804  </FreeDiams>
     805  """
     806
     807                  xml_file = io.open(self.__fd2gm_filename, mode = 'wt', encoding = 'utf8')
     808                  xml_file.write(xml % '\n\t\t'.join(drug_snippets))
     809                  xml_file.close()
     810
     811                  return True
     812          #--------------------------------------------------------
     813 -        def __create_gm2fd_file(self, mode='interactions'):
     814
     815                  if mode == 'interactions':
     816                          mode = 'select-only'
     817                  elif mode == 'prescription':
     818                          mode = 'prescriber'
     819                  else:
     820                          mode = 'select-only'
     821
     822                  xml_file = io.open(self.__gm2fd_filename, mode = 'wt', encoding = 'utf8')
     823
     824                  xml = """<?xml version="1.0" encoding="UTF-8"?>
     825
     826  <FreeDiams_In version="0.5.0">
     827          <EMR name="GNUmed" uid="unused"/>
     828          <ConfigFile value="%s"/>
     829          <ExchangeOut value="%s" format="xml"/>
     830          <!-- <DrugsDatabase uid="can be set to a specific DB"/> -->
     831          <Ui editmode="%s" blockPatientDatas="1"/>
     832          %%s
     833  </FreeDiams_In>
     834  """             % (
     835                          self.__fd4gm_config_file,
     836                          self.__fd2gm_filename,
     837                          mode
     838                  )
     839
     840                  if self.patient is None:
     841                          xml_file.write(xml % '')
     842                          xml_file.close()
     843                          return
     844
     845                  name = self.patient.get_active_name()
     846                  if self.patient['dob'] is None:
     847                          dob = ''
     848                  else:
     849                          dob = self.patient['dob'].strftime(cFreeDiamsInterface.default_dob_format)
     850
     851                  emr = self.patient.emr
     852                  allgs = emr.get_allergies()
     853                  atc_allgs = [
     854                          a['atc_code'] for a in allgs if ((a['atc_code'] is not None) and (a['type'] == 'allergy'))
     855                  ]
     856                  atc_sens = [
     857                          a['atc_code'] for a in allgs if ((a['atc_code'] is not None) and (a['type'] == 'sensitivity'))
     858                  ]
     859                  inn_allgs = [
     860                          a['allergene'] for a in allgs if ((a['allergene'] is not None) and (a['type'] == 'allergy'))
     861                  ]
     862                  inn_sens = [
     863                          a['allergene'] for a in allgs if ((a['allergene'] is not None) and (a['type'] == 'sensitivity'))
     864                  ]
     865                  # this is rather fragile: FreeDiams won't know what type of UID this is
     866                  # (but it will assume it is of the type of the drug database in use)
     867                  # but eventually FreeDiams puts all drugs into one database :-)
     868                  uid_allgs = [
     869                          a['substance_code'] for a in allgs if ((a['substance_code'] is not None) and (a['type'] == 'allergy'))
     870                  ]
     871                  uid_sens = [
     872                          a['substance_code'] for a in allgs if ((a['substance_code'] is not None) and (a['type'] == 'sensitivity'))
     873                  ]
     874
     875                  patient_xml = """<Patient>
     876                  <Identity
     877                            lastnames="%s"
     878                            firstnames="%s"
     879                            uid="%s"
     880                            dob="%s"
     881                            gender="%s"
     882                  />
     883                  <!-- can be <7 characters class codes: -->
     884                  <ATCAllergies value="%s"/>
     885                  <ATCIntolerances value="%s"/>
     886
     887                  <InnAllergies value="%s"/>
     888                  <InnIntolerances value="%s"/>
     889
     890                  <DrugsUidAllergies value="%s"/>
     891                  <DrugsUidIntolerances value="%s"/>
     892
     893                  <!--
     894                          # FIXME: search by LOINC code and add (as soon as supported by FreeDiams ...)
     895                          <Creatinine value="12" unit="mg/l or mmol/l"/>
     896                          <Weight value="70" unit="kg or pd" />
     897                          <WeightInGrams value="70"/>
     898                          <Height value="170" unit="cm or "/>
     899                          <HeightInCentimeters value="170"/>
     900                          <ICD10 value="J11.0;A22;Z23"/>
     901                  -->
     902
     903          </Patient>
     904  """             % (
     905                          gmTools.xml_escape_string(text = name['lastnames']),
     906                          gmTools.xml_escape_string(text = name['firstnames']),
     907                          self.patient.ID,
     908                          dob,
     909                          cFreeDiamsInterface.map_gender2mf[self.patient['gender']],
     910                          gmTools.xml_escape_string(text = ';'.join(atc_allgs)),
     911                          gmTools.xml_escape_string(text = ';'.join(atc_sens)),
     912                          gmTools.xml_escape_string(text = ';'.join(inn_allgs)),
     913                          gmTools.xml_escape_string(text = ';'.join(inn_sens)),
     914                          gmTools.xml_escape_string(text = ';'.join(uid_allgs)),
     915                          gmTools.xml_escape_string(text = ';'.join(uid_sens))
     916                  )
     917
     918                  xml_file.write(xml % patient_xml)
     919                  xml_file.close()
     920          #--------------------------------------------------------
     921 -        def import_fd2gm_file_as_prescription(self, filename=None):
     922
     923                  if filename is None:
     924                          filename = self.__fd2gm_filename
     925
     926                  _log.debug('importing FreeDiams prescription information from [%s]', filename)
     927
     928                  fd2gm_xml = etree.ElementTree()
     929                  fd2gm_xml.parse(filename)
     930
     931                  pdfs = fd2gm_xml.findall('ExtraDatas/Printed')
     932                  if len(pdfs) == 0:
     933                          _log.debug('no PDF prescription files listed')
     934                          return
     935
     936                  fd_filenames = []
     937                  for pdf in pdfs:
     938                          fd_filenames.append(pdf.attrib['file'])
     939
     940                  _log.debug('listed PDF prescription files: %s', fd_filenames)
     941
     942                  docs = self.patient.get_document_folder()
     943                  emr = self.patient.emr
     944
     945                  prescription = docs.add_prescription (
     946                          encounter = emr.active_encounter['pk_encounter'],
     947                          episode = emr.add_episode (
     948                                  episode_name = DEFAULT_MEDICATION_HISTORY_EPISODE,
     949                                  is_open = False
     950                          )['pk_episode']
     951                  )
     952                  prescription['ext_ref'] = 'FreeDiams'
     953                  prescription.save()
     954                  fd_filenames.append(filename)
     955                  success, msg, parts = prescription.add_parts_from_files(files = fd_filenames)
     956                  if not success:
     957                          _log.error(msg)
     958                          return
     959
     960                  for part in parts:
     961                          part['obj_comment'] = _('copy of printed prescription')
     962                          part.save()
     963
     964                  xml_part = parts[-1]
     965                  xml_part['filename'] = 'freediams-prescription.xml'
     966                  xml_part['obj_comment'] = _('prescription data')
     967                  xml_part.save()
     968
     969                  # are we the intended reviewer ?
     970                  from Gnumed.business.gmStaff import gmCurrentProvider
     971                  me = gmCurrentProvider()
     972                  # if so: auto-sign the prescription
     973                  if xml_part['pk_intended_reviewer'] == me['pk_staff']:
     974                          prescription.set_reviewed(technically_abnormal = False, clinically_relevant = False)
     975          #--------------------------------------------------------
     976 -        def import_fd2gm_file_as_drugs(self, filename=None):
     977                  """
     978                          If returning textual prescriptions (say, drugs which FreeDiams
     979                          did not know) then "IsTextual" will be True and UID will be -1.
     980                  """
     981                  if filename is None:
     982                          filename = self.__fd2gm_filename
     983
     984                  # FIXME: do not import IsTextual drugs, or rather, make that configurable
     985
     986                  fd2gm_xml = etree.ElementTree()
     987                  fd2gm_xml.parse(filename)
     988
     989                  data_src_pk = self.create_data_source_entry()
     990
     991                  xml_version = fd2gm_xml.find('FullPrescription').attrib['version']
     992                  _log.debug('fd2gm file version: %s', xml_version)
     993
     994                  if xml_version in ['0.6.0', '0.7.2']:
     995                          return self.__import_fd2gm_file_as_drugs_0_6_0(fd2gm_xml = fd2gm_xml, pk_data_source = data_src_pk)
     996
     997                  return self.__import_fd2gm_file_as_drugs_0_5(fd2gm_xml = fd2gm_xml, pk_data_source = data_src_pk)
     998          #--------------------------------------------------------
     999 -        def __import_fd2gm_file_as_drugs_0_6_0(self, fd2gm_xml=None, pk_data_source=None):
    1000
    1001  #               drug_id_name = db_def.attrib['drugUidName']
    1002                  fd_xml_prescriptions = fd2gm_xml.findall('FullPrescription/Prescription')
    1003
    1004                  self.__imported_drugs = []
    1005                  for fd_xml_prescription in fd_xml_prescriptions:
    1006                          drug_uid = fd_xml_prescription.find('Drug').attrib['u1'].strip()
    1007                          if drug_uid == '-1':
    1008                                  _log.debug('skipping textual drug')
    1009                                  continue
    1010                          drug_db =  fd_xml_prescription.find('Drug').attrib['db'].strip()
    1011                          drug_uid_name = fd_xml_prescription.find('Drug/DrugUidName').text.strip()
    1012                          #drug_uid_name = u'<%s>' % drug_db
    1013                          drug_name = fd_xml_prescription.find('Drug/DrugName').text.replace(', )', ')').strip()
    1014                          drug_form = fd_xml_prescription.find('Drug/DrugForm').text.strip()
    1015  #                       drug_atc = fd_xml_prescription.find('DrugATC')
    1016  #                       if drug_atc is None:
    1017  #                               drug_atc = u''
    1018  #                       else:
    1019  #                               if drug_atc.text is None:
    1020  #                                       drug_atc = u''
    1021  #                               else:
    1022  #                                       drug_atc = drug_atc.text.strip()
    1023
    1024                          # create new drug product
    1025                          new_drug = gmMedication.create_drug_product(product_name = drug_name, preparation = drug_form, return_existing = True)
    1026                          self.__imported_drugs.append(new_drug)
    1027                          new_drug['is_fake_product'] = False
    1028  #                       new_drug['atc'] = drug_atc
    1029                          new_drug['external_code_type'] = 'FreeDiams::%s::%s' % (drug_db, drug_uid_name)
    1030                          new_drug['external_code'] = drug_uid
    1031                          new_drug['pk_data_source'] = pk_data_source
    1032                          new_drug.save()
    1033
    1034                          # parse XML for composition records
    1035                          fd_xml_components = fd_xml_prescription.getiterator('Composition')
    1036                          comp_data = {}
    1037                          for fd_xml_comp in fd_xml_components:
    1038
    1039                                  data = {}
    1040
    1041                                  xml_strength = fd_xml_comp.attrib['strength'].strip()
    1042                                  amount = regex.match(r'^\d+[.,]{0,1}\d*', xml_strength)
    1043                                  if amount is None:
    1044                                          amount = 99999
    1045                                  else:
    1046                                          amount = amount.group()
    1047                                  data['amount'] = amount
    1048
    1049                                  #unit = regex.sub(r'\d+[.,]{0,1}\d*', u'', xml_strength).strip()
    1050                                  unit = (xml_strength[len(amount):]).strip()
    1051                                  if unit == '':
    1052                                          unit = '*?*'
    1053                                  data['unit'] = unit
    1054
    1055                                  # hopefully, FreeDiams gets their act together, eventually:
    1056                                  atc = regex.match(r'[A-Za-z]\d\d[A-Za-z]{2}\d\d', fd_xml_comp.attrib['atc'].strip())
    1057                                  if atc is None:
    1058                                          data['atc'] = None
    1059                                  else:
    1060                                          atc = atc.group()
    1061                                  data['atc'] = atc
    1062
    1063                                  molecule_name = fd_xml_comp.attrib['molecularName'].strip()
    1064                                  if molecule_name != '':
    1065                                          gmMedication.create_substance_dose(substance = molecule_name, atc = atc, amount = amount, unit = unit)
    1066                                  data['molecule_name'] = molecule_name
    1067
    1068                                  inn_name = fd_xml_comp.attrib['inn'].strip()
    1069                                  if inn_name != '':
    1070                                          gmMedication.create_substance_dose(substance = inn_name, atc = atc, amount = amount, unit = unit)
    1071                                  #data['inn_name'] = molecule_name
    1072                                  data['inn_name'] = inn_name
    1073
    1074                                  if molecule_name == '':
    1075                                          data['substance'] = inn_name
    1076                                          _log.info('linking INN [%s] rather than molecularName as component', inn_name)
    1077                                  else:
    1078                                          data['substance'] = molecule_name
    1079
    1080                                  data['nature'] = fd_xml_comp.attrib['nature'].strip()
    1081                                  data['nature_ID'] = fd_xml_comp.attrib['natureLink'].strip()
    1082
    1083                                  # merge composition records of SA/FT nature
    1084                                  try:
    1085                                          old_data = comp_data[data['nature_ID']]
    1086                                          # normalize INN
    1087                                          if old_data['inn_name'] == '':
    1088                                                  old_data['inn_name'] = data['inn_name']
    1089                                          if data['inn_name'] == '':
    1090                                                  data['inn_name'] = old_data['inn_name']
    1091                                          # normalize molecule
    1092                                          if old_data['molecule_name'] == '':
    1093                                                  old_data['molecule_name'] = data['molecule_name']
    1094                                          if data['molecule_name'] == '':
    1095                                                  data['molecule_name'] = old_data['molecule_name']
    1096                                          # normalize ATC
    1097                                          if old_data['atc'] == '':
    1098                                                  old_data['atc'] = data['atc']
    1099                                          if data['atc'] == '':
    1100                                                  data['atc'] = old_data['atc']
    1101                                          # FT: transformed form
    1102                                          # SA: active substance
    1103                                          # it would be preferable to use the SA record because that's what's *actually*
    1104                                          # contained in the drug, however FreeDiams does not list the amount thereof
    1105                                          # (rather that of the INN)
    1106                                          # FT and SA records of the same component carry the same nature_ID
    1107                                          if data['nature'] == 'FT':
    1108                                                  comp_data[data['nature_ID']] = data
    1109                                          else:
    1110                                                  comp_data[data['nature_ID']] = old_data
    1111
    1112                                  # or create new record
    1113                                  except KeyError:
    1114                                          comp_data[data['nature_ID']] = data
    1115
    1116                          # actually create components from (possibly merged) composition records
    1117                          for key, data in comp_data.items():
    1118                                  new_drug.add_component (
    1119                                          substance = data['substance'],
    1120                                          atc = data['atc'],
    1121                                          amount = data['amount'],
    1122                                          unit = data['unit']
    1123                                  )
    1124          #--------------------------------------------------------
    1125 -        def __import_fd2gm_file_as_drugs_0_5(self, fd2gm_xml=None, pk_data_source=None):
    1126
    1127                  db_def = fd2gm_xml.find('DrugsDatabaseName')
    1128                  db_id = db_def.text.strip()
    1129                  drug_id_name = db_def.attrib['drugUidName']
    1130                  fd_xml_drug_entries = fd2gm_xml.findall('FullPrescription/Prescription')
    1131
    1132                  self.__imported_drugs = []
    1133                  for fd_xml_drug in fd_xml_drug_entries:
    1134                          drug_uid = fd_xml_drug.find('Drug_UID').text.strip()
    1135                          if drug_uid == '-1':
    1136                                  _log.debug('skipping textual drug')
    1137                                  continue                # it's a TextualDrug, skip it
    1138                          drug_name = fd_xml_drug.find('DrugName').text.replace(', )', ')').strip()
    1139                          drug_form = fd_xml_drug.find('DrugForm').text.strip()
    1140                          drug_atc = fd_xml_drug.find('DrugATC')
    1141                          if drug_atc is None:
    1142                                  drug_atc = ''
    1143                          else:
    1144                                  if drug_atc.text is None:
    1145                                          drug_atc = ''
    1146                                  else:
    1147                                          drug_atc = drug_atc.text.strip()
    1148
    1149                          # create new drug product
    1150                          new_drug = gmMedication.create_drug_product(product_name = drug_name, preparation = drug_form, return_existing = True)
    1151                          self.__imported_drugs.append(new_drug)
    1152                          new_drug['is_fake_product'] = False
    1153                          new_drug['atc'] = drug_atc
    1154                          new_drug['external_code_type'] = 'FreeDiams::%s::%s' % (db_id, drug_id_name)
    1155                          new_drug['external_code'] = drug_uid
    1156                          new_drug['pk_data_source'] = pk_data_source
    1157                          new_drug.save()
    1158
    1159                          # parse XML for composition records
    1160                          fd_xml_components = fd_xml_drug.getiterator('Composition')
    1161                          comp_data = {}
    1162                          for fd_xml_comp in fd_xml_components:
    1163
    1164                                  data = {}
    1165
    1166                                  amount = regex.match(r'\d+[.,]{0,1}\d*', fd_xml_comp.attrib['strenght'].strip())                        # sic, typo
    1167                                  if amount is None:
    1168                                          amount = 99999
    1169                                  else:
    1170                                          amount = amount.group()
    1171                                  data['amount'] = amount
    1172
    1173                                  unit = regex.sub(r'\d+[.,]{0,1}\d*', '', fd_xml_comp.attrib['strenght'].strip()).strip()        # sic, typo
    1174                                  if unit == '':
    1175                                          unit = '*?*'
    1176                                  data['unit'] = unit
    1177
    1178                                  molecule_name = fd_xml_comp.attrib['molecularName'].strip()
    1179                                  if molecule_name != '':
    1180                                          gmMedication.create_substance_dose(substance = molecule_name, atc = None, amount = amount, unit = unit)
    1181                                  data['molecule_name'] = molecule_name
    1182
    1183                                  inn_name = fd_xml_comp.attrib['inn'].strip()
    1184                                  if inn_name != '':
    1185                                          gmMedication.create_substance_dose(substance = inn_name, atc = None, amount = amount, unit = unit)
    1186                                  data['inn_name'] = molecule_name
    1187
    1188                                  if molecule_name == '':
    1189                                          data['substance'] = inn_name
    1190                                          _log.info('linking INN [%s] rather than molecularName as component', inn_name)
    1191                                  else:
    1192                                          data['substance'] = molecule_name
    1193
    1194                                  data['nature'] = fd_xml_comp.attrib['nature'].strip()
    1195                                  data['nature_ID'] = fd_xml_comp.attrib['natureLink'].strip()
    1196
    1197                                  # merge composition records of SA/FT nature
    1198                                  try:
    1199                                          old_data = comp_data[data['nature_ID']]
    1200                                          # normalize INN
    1201                                          if old_data['inn_name'] == '':
    1202                                                  old_data['inn_name'] = data['inn_name']
    1203                                          if data['inn_name'] == '':
    1204                                                  data['inn_name'] = old_data['inn_name']
    1205                                          # normalize molecule
    1206                                          if old_data['molecule_name'] == '':
    1207                                                  old_data['molecule_name'] = data['molecule_name']
    1208                                          if data['molecule_name'] == '':
    1209                                                  data['molecule_name'] = old_data['molecule_name']
    1210                                          # FT: transformed form
    1211                                          # SA: active substance
    1212                                          # it would be preferable to use the SA record because that's what's *actually*
    1213                                          # contained in the drug, however FreeDiams does not list the amount thereof
    1214                                          # (rather that of the INN)
    1215                                          if data['nature'] == 'FT':
    1216                                                  comp_data[data['nature_ID']] = data
    1217                                          else:
    1218                                                  comp_data[data['nature_ID']] = old_data
    1219
    1220                                  # or create new record
    1221                                  except KeyError:
    1222                                          comp_data[data['nature_ID']] = data
    1223
    1224                          # actually create components from (possibly merged) composition records
    1225                          for key, data in comp_data.items():
    1226                                  new_drug.add_component (
    1227                                          substance = data['substance'],
    1228                                          amount = data['amount'],
    1229                                          unit = data['unit']
    1230                                  )
    1231
    1232  #============================================================
    1233  # Ifap
    1234  #------------------------------------------------------------
    1235 -class cIfapInterface(cDrugDataSourceInterface):
    1236          """empirical CSV interface"""
    1237
    1238 -        def __init__(self):
    1239                  pass
    1240
    1241 -        def print_transfer_file(self, filename=None):
    1242
    1243                  try:
    1244                          csv_file = io.open(filename, mode = 'rt', encoding = 'latin1')                                          # FIXME: encoding correct ?
    1245                  except:
    1246                          _log.exception('cannot access [%s]', filename)
    1247                          csv_file = None
    1248
    1249                  field_names = 'PZN Handelsname Form Abpackungsmenge Einheit Preis1 Hersteller Preis2 rezeptpflichtig Festbetrag Packungszahl Packungsgr\xf6\xdfe'.split()
    1250
    1251                  if csv_file is None:
    1252                          return False
    1253
    1254                  csv_lines = csv.DictReader (
    1255                          csv_file,
    1256                          fieldnames = field_names,
    1257                          delimiter = ';'
    1258                  )
    1259
    1260                  for line in csv_lines:
    1261                          print("--------------------------------------------------------------------"[:31])
    1262                          for key in field_names:
    1263                                  tmp = ('%s                                                ' % key)[:30]
    1264                                  print('%s: %s' % (tmp, line[key]))
    1265
    1266                  csv_file.close()
    1267
    1268  #                               narr = u'%sx %s %s %s (\u2258 %s %s) von %s (%s)' % (
    1269  #                                       line['Packungszahl'].strip(),
    1270  #                                       line['Handelsname'].strip(),
    1271  #                                       line['Form'].strip(),
    1272  #                                       line[u'Packungsgr\xf6\xdfe'].strip(),
    1273  #                                       line['Abpackungsmenge'].strip(),
    1274  #                                       line['Einheit'].strip(),
    1275  #                                       line['Hersteller'].strip(),
    1276  #                                       line['PZN'].strip()
    1277  #                               )
    1278
    1279  #============================================================
    1280  drug_data_source_interfaces = {
    1281          'Deutschland: Gelbe Liste/MMI (Windows)': cGelbeListeWindowsInterface,
    1282          'Deutschland: Gelbe Liste/MMI (WINE)': cGelbeListeWineInterface,
    1283          'FreeDiams (FR, US, CA, ZA)': cFreeDiamsInterface
    1284  }
    1285
    1286  #============================================================
    1287  # main
    1288  #------------------------------------------------------------
    1289  if __name__ == "__main__":
    1290
    1291          if len(sys.argv) < 2:
    1292                  sys.exit()
    1293
    1294          if sys.argv[1] != 'test':
    1295                  sys.exit()
    1296
    1297          from Gnumed.business import gmPerson
    1298
    1299          #--------------------------------------------------------
    1300 -        def test_MMI_interface():
    1301                  mmi = cGelbeListeWineInterface()
    1302                  print(mmi)
    1303                  print("interface definition:", mmi.version)
    1304                  print("database versions:   ", mmi.get_data_source_version())
    1305          #--------------------------------------------------------
    1306 -        def test_MMI_file():
    1307                  mmi_file = cGelbeListeCSVFile(filename = sys.argv[2])
    1308                  for drug in mmi_file:
    1309                          print("-------------")
    1310                          print('"%s" (ATC: %s / PZN: %s)' % (drug['name'], drug['atc'], drug['pzn']))
    1311                          for stoff in drug['wirkstoffe']:
    1312                                  print(" Wirkstoff:", stoff)
    1313                          input()
    1314                          if mmi_file.has_unknown_fields is not None:
    1315                                  print("has extra data under [%s]" % gmTools.default_csv_reader_rest_key)
    1316                          for key in mmi_file.csv_fieldnames:
    1317                                  print(key, '->', drug[key])
    1318                          input()
    1319                  mmi_file.close()
    1320          #--------------------------------------------------------
    1321 -        def test_mmi_switch_to():
    1322                  mmi = cGelbeListeWineInterface()
    1323                  mmi.switch_to_frontend(blocking = True)
    1324          #--------------------------------------------------------
    1325 -        def test_mmi_let_user_select_drugs():
    1326                  mmi = cGelbeListeWineInterface()
    1327                  mmi_file = mmi.__let_user_select_drugs()
    1328                  for drug in mmi_file:
    1329                          print("-------------")
    1330                          print('"%s" (ATC: %s / PZN: %s)' % (drug['name'], drug['atc'], drug['pzn']))
    1331                          for stoff in drug['wirkstoffe']:
    1332                                  print(" Wirkstoff:", stoff)
    1333                          print(drug)
    1334                  mmi_file.close()
    1335          #--------------------------------------------------------
    1336 -        def test_mmi_import_drugs():
    1337                  mmi = cGelbeListeWineInterface()
    1338                  mmi.import_drugs()
    1339          #--------------------------------------------------------
    1340 -        def test_mmi_interaction_check():
    1341                  mmi = cGelbeListeInterface()
    1342                  print(mmi)
    1343                  print("interface definition:", mmi.version)
    1344                  # Metoprolol + Hct vs Citalopram
    1345                  diclofenac = '7587712'
    1346                  phenprocoumon = '4421744'
    1347                  mmi.check_interactions(drug_ids_list = [diclofenac, phenprocoumon])
    1348          #--------------------------------------------------------
    1349          # FreeDiams
    1350          #--------------------------------------------------------
    1351 -        def test_fd_switch_to():
    1352                  gmPerson.set_active_patient(patient = gmPerson.cPerson(aPK_obj = 12))
    1353                  fd = cFreeDiamsInterface()
    1354                  fd.patient = gmPerson.gmCurrentPatient()
    1355  #               fd.switch_to_frontend(blocking = True)
    1356                  fd.import_fd2gm_file_as_drugs(filename = sys.argv[2])
    1357          #--------------------------------------------------------
    1358 -        def test_fd_show_interactions():
    1359                  gmPerson.set_active_patient(patient = gmPerson.cPerson(aPK_obj = 12))
    1360                  fd = cFreeDiamsInterface()
    1361                  fd.patient = gmPerson.gmCurrentPatient()
    1362                  fd.check_interactions(substances = fd.patient.emr.get_current_medications(include_unapproved = True))
    1363
    1364          #--------------------------------------------------------
    1365          # MMI/Gelbe Liste
    1366          #test_MMI_interface()
    1367          #test_MMI_file()
    1368          #test_mmi_switch_to()
    1369          #test_mmi_let_user_select_drugs()
    1370          #test_mmi_import_substances()
    1371          #test_mmi_import_drugs()
    1372
    1373          # FreeDiams
    1374          #test_fd_switch_to()
    1375          #test_fd_show_interactions()
    1376