Ejemplo n.º 1
0
def checkEuroClassesValid(workBook, vehRowStarts, vehRowEnds, EuroClassNameColumns, Type=99, logger=None):
  """
  Check that all of the available euro classes are specified.
  """
  parentFrame = inspect.currentframe().f_back
  (filename, xa, xb, xc, xd) = inspect.getframeinfo(parentFrame)

  # Get the logging details.
  loggerM = getLogger(logger, 'checkEuroClassesValid')

  if Type == 1:
    logprint(loggerM, "Checking all motorcycle euro class names are understood.")
  elif Type == 2:
    logprint(loggerM, "Checking all hybrid bus euro class names are understood.")
  elif Type == 0:
    logprint(loggerM, "Checking all other euro class names are understood.")
  else:
    logprint(loggerM, "Checking all euro class names are understood.")

  ws_euro = workBook.Worksheets("UserEuro")
  for [vi, vehRowStart] in enumerate(vehRowStarts):
    vehRowEnd = vehRowEnds[vi]
    for [ci, euroNameCol] in enumerate(EuroClassNameColumns):
      euroClassRange = "{col}{rstart}:{col}{rend}".format(col=euroNameCol, rstart=vehRowStart, rend=vehRowEnd)
      euroClassesAvailable = ws_euro.Range(euroClassRange).Value
      for ecn in euroClassesAvailable:
        ecn = ecn[0]
        if ecn is None:
          continue
        if ecn not in euroClassNameVariationsAll:
          if ecn not in euroClassNameVariationsIgnore:
            raise ValueError('Unrecognized Euro Class Name: "{}".'.format(ecn))
Ejemplo n.º 2
0
def pasteDefaultEuroProportions(ws, details, logger=None):
  # Get the logging details.
  loggerM = getLogger(logger, 'pasteDefaultEuroProportions')
  logprint(loggerM, '               Pasting default euro proportions.', level='info')

  # Bus vs Coach
  DefaultRange = '{colstart}{rowstart}:{colend}{rowend}'.format(
                                           colstart=DefaultBusColumn[0],
                                           colend=DefaultBusMWColumn[0],
                                           rowstart=details['busCoachRow'][0],
                                           rowend=details['busCoachRow'][1])
  UserRange = '{colstart}{rowstart}:{colend}{rowend}'.format(
                                           colstart=UserDefinedBusColumn[0],
                                           colend=UserDefinedBusMWColumn[0],
                                           rowstart=details['busCoachRow'][0],
                                           rowend=details['busCoachRow'][1])
  logprint(loggerM, '{} - {}'.format(DefaultRange, UserRange), level='debug')
  ws.Range(UserRange).Value = ws.Range(DefaultRange).Value

  # LGV Fule Types
  if 'lgvFuelRows' in details.keys():
    DefaultRange = '{colstart}{rowstart}:{colend}{rowend}'.format(
                                            colstart=DefaultLGVFuelColumns[0],
                                            colend=DefaultLGVFuelColumns[-1],
                                            rowstart=details['lgvFuelRows'][0],
                                            rowend=details['lgvFuelRows'][-1])
    UserRange = '{colstart}{rowstart}:{colend}{rowend}'.format(
                                            colstart=UserDefinedLGVFuelColumns[0],
                                            colend=UserDefinedLGVFuelColumns[-1],
                                            rowstart=details['lgvFuelRows'][0],
                                            rowend=details['lgvFuelRows'][-1])
    logprint(loggerM, '{} - {}'.format(DefaultRange, UserRange), level='debug')
    ws.Range(UserRange).Value = ws.Range(DefaultRange).Value

  # Sizes
  for rs, re in zip(details['weightRowStarts'], details['weightRowEnds']):
    DefaultRange = '{col}{rowstart}:{col}{rowend}'.format(col=DefaultWeightColumn, rowstart=rs, rowend=re)
    UserRange = '{col}{rowstart}:{col}{rowend}'.format(col=UserDefinedWeightColumn, rowstart=rs, rowend=re)
    #print('{} - {}'.format(DefaultRange, UserRange))
    ws.Range(UserRange).Value = ws.Range(DefaultRange).Value

  # Sizes Buses
  for rs, re in zip(details['weightRowStartsBus'], details['weightRowEndsBus']):
    if rs == 512: # Grumble grumbe grumble
      cd = 'C'
    else:
      cd = DefaultWeightColumn
    DefaultRange = '{col}{rowstart}:{col}{rowend}'.format(col=cd, rowstart=rs, rowend=re)
    UserRange = '{col}{rowstart}:{col}{rowend}'.format(col=UserDefinedWeightColumn, rowstart=rs, rowend=re)
    logprint(loggerM, '{} - {}'.format(DefaultRange, UserRange), level='debug')
    ws.Range(UserRange).Value = ws.Range(DefaultRange).Value

  # Hybrids
  for rs, re in zip(details['vehRowStartsHB'], details['vehRowEndsHB']):
    for qq, (d, u) in enumerate(zip(DefaultEuroColumnsHB, UserDefinedEuroColumnsHB)):
      DefaultRange = '{col}{rowstart}:{col}{rowend}'.format(col=d, rowstart=rs+qq, rowend=re+qq)
      UserRange = '{col}{rowstart}:{col}{rowend}'.format(col=u, rowstart=rs+qq, rowend=re+qq)
      logprint(loggerM, '{} - {}'.format(DefaultRange, UserRange), level='debug')
      ws.Range(UserRange).Value = ws.Range(DefaultRange).Value

  # Motorcycles
  for rs, re in zip(details['vehRowStartsMC'], details['vehRowEndsMC']):
    for d, u in zip(DefaultEuroColumnsMC, UserDefinedEuroColumnsMC):
      DefaultRange = '{col}{rowstart}:{col}{rowend}'.format(col=d, rowstart=rs, rowend=re)
      UserRange = '{col}{rowstart}:{col}{rowend}'.format(col=u, rowstart=rs, rowend=re)
      logprint(loggerM, '{} - {}'.format(DefaultRange, UserRange), level='debug')
      ws.Range(UserRange).Value = ws.Range(DefaultRange).Value

  # Standard euro class
  for rs, re in zip(details['vehRowStarts'], details['vehRowEnds']):

    for d, u in zip(DefaultEuroColumns, UserDefinedEuroColumns):
      DefaultRange = '{col}{rowstart}:{col}{rowend}'.format(col=d, rowstart=rs, rowend=re)
      UserRange = '{col}{rowstart}:{col}{rowend}'.format(col=u, rowstart=rs, rowend=re)
      logprint(loggerM, '{} - {}'.format(DefaultRange, UserRange), level='debug')
      try: # Nested tries, messy but works.
        ws.Range(UserRange).Value = ws.Range(DefaultRange).Value
      except pywintypes.com_error:
        # Try skipping the last row. This is neccesary with a few vehicle types.
        logprint(loggerM, 'skip Last', level='debug')
        DefaultRange = '{col}{rowstart}:{col}{rowend}'.format(col=d, rowstart=rs, rowend=re-1)
        UserRange = '{col}{rowstart}:{col}{rowend}'.format(col=u, rowstart=rs, rowend=re-1)
        try:
          ws.Range(UserRange).Value = ws.Range(DefaultRange).Value
        except pywintypes.com_error:
          # Try skipping the first row.
          # This is neccesary for the NOx column for B100 Rigid HGVs
          logprint(loggerM, 'skip First', level='debug')
          DefaultRange = '{col}{rowstart}:{col}{rowend}'.format(col=d, rowstart=rs+1, rowend=re)
          UserRange = '{col}{rowstart}:{col}{rowend}'.format(col=u, rowstart=rs+1, rowend=re)
          try:
            ws.Range(UserRange).Value = ws.Range(DefaultRange).Value
          except pywintypes.com_error:
            # Try skipping the first two row.
            # This is neccesary for the PM column for biodeiesel buses.
            logprint(loggerM, 'skip First Two', level='debug')
            DefaultRange = '{col}{rowstart}:{col}{rowend}'.format(col=d, rowstart=rs+2, rowend=re)
            UserRange = '{col}{rowstart}:{col}{rowend}'.format(col=u, rowstart=rs+2, rowend=re)
            ws.Range(UserRange).Value = ws.Range(DefaultRange).Value
Ejemplo n.º 3
0
def prepareAndRun(fileName, vehSplit, details, location, year, euroClass,
                  ahk_exepath, ahk_ahkpathG, versionForOutPut, excel=None,
                  checkEuroClasses=False, DoMCycles=True, DoHybridBus=True, DoBusCoach=False,
                  inputData='prepare', busCoach='default', sizeRow=99, tech='All', vehiclesToSkip=[], logger=None):
  """
  Prepare the file for running the macro.
  euroClass of 99 will retain default euro breakdown.
  """
  closeExcel = False
  if excel is None:
    excel = win32.gencache.EnsureDispatch('Excel.Application')
    closeExcel = True

  # Get the logging details.
  loggerM = getLogger(logger, 'prepareAndRun')

  # Start off the autohotkey script as a (parallel) subprocess. This will
  # continually check until the compatibility warning appears, and then
  # close the warning.
  if os.path.isfile(ahk_exepath):
    subprocess.Popen([ahk_exepath, ahk_ahkpathG])

  # Open the document.
  wb = excel.Workbooks.Open(fileName)
  excel.Visible = True

  if checkEuroClasses:
    # Check that all of the euro class names within the document are as
    # we would expect. An error will be raised if there are any surprises
    # and this will mean that the global variables at the start of the
    # code will need to be edited.
    if DoMCycles:
      checkEuroClassesValid(wb, details['vehRowStartsMC'], details['vehRowEndsMC'], EuroClassNameColumnsMC, Type=1)
    if DoHybridBus:
      checkEuroClassesValid(wb, details['vehRowStartsHB'], details['vehRowEndsHB'], EuroClassNameColumnsMC, Type=2)
    checkEuroClassesValid(wb, details['vehRowStarts'], details['vehRowEnds'], EuroClassNameColumns, Type=0)

  # Set the default values in the Input Data sheet.
  ws_input = wb.Worksheets("Input Data")
  ws_input.Range("B4").Value = location
  ws_input.Range("B5").Value = year
  if ws_input.Range("B6").Value != vehSplit:
    # Don't change unless you have to.
    ws_input.Range("B6").Value = vehSplit


  if DoBusCoach:
    weightRowStarts = details['weightRowStartsBus']
    weightRowEnds = details['weightRowEndsBus']
    weightRowNames = details['weightRowNamesBus']
  else:
    weightRowStarts = details['weightRowStarts']
    weightRowEnds = details['weightRowEnds']
    weightRowNames = details['weightRowNames']

  if sizeRow != 99:
    # Set the designated size row.
    weightclassnames = {}
    for wri, wrs in enumerate(weightRowStarts):
      wre = weightRowEnds[wri]
      wrn = weightRowNames[wri]
      wrd = wrs + sizeRow
      if wrd > wre:
        # No more weight classes for this particular vehicle class.
        continue
      #Wally
      weightname = SpecifyWeight(wb, wrs, wre, wrd)
      weightclassnames[wrn] = weightname
  else:
    weightclassnames = {'Car': 'DefaultMix',
                        'LGV': 'DefaultMix',
                        'Rigid HGV': 'DefaultMix',
                        'Artic HGV': 'DefaultMix',
                        'Bus and Coach': 'DefaultMix',
                        'Bus': 'DefaultMix',
                        'Coach': 'DefaultMix',
                        'Motorcycle': 'DefaultMix'}

  logprint(loggerM, 'weightclassnames: {}'.format(weightclassnames), level='debug')
  vehsToInclude = []
  for veh, wn in weightclassnames.items():
    if (veh in techVehs[tech]) and (veh not in vehiclesToSkip):
      logprint(loggerM, '               Including weight "{}" for vehicles of class "{}."'.format(wn, veh))
      vehsToInclude.extend(in2outVeh[veh])
  for veh in vehiclesToSkip:
    if veh in vehsToInclude:
      vehsToInclude.remove(veh)

  if DoBusCoach:
    if busCoach == 'bus':
      if (sizeRow >= 3) and (sizeRow != 99):
        vehsToInclude = []
      else:
        vehsToInclude = ['Bus and Coach', 'B100 Bus', 'CNG Bus', 'Biomethane Bus',
                         'Biogas Bus', 'Hybrid Bus', 'FCEV Bus', 'B100 Coach']
    elif busCoach == 'coach':
      if (sizeRow >= 2) and (sizeRow != 99):
        vehsToInclude = []
      else:
        vehsToInclude = ['Bus and Coach', 'B100 Coach']

  if len(vehsToInclude) == 0:
    time.sleep(1) # To allow all systems to catch up.
    wb.Close(False)
    if closeExcel:
      excel.Quit()
      del(excelObj) # Make sure it's gone. Apparently some people have found this neccesary.
    return excel, None, None, None, None, None


  if type(inputData) is str:
    if inputData == 'prepare':
      # Prepare the input data.
      inputData = createEFTInput(vBreakdown=vehSplit, vehiclesToInclude=vehsToInclude, tech=tech, logger=logger)
      #inputData = inputData.as_matrix()
    else:
      raise ValueError("inputData '{}' is not understood.".format(inputData))
  if 'Motorcycle' not in vehsToInclude:
    DoMCycles = False
  if 'Hybrid Bus' not in vehsToInclude:
    DoHybridBus = False

  numRows, numCols = np.shape(inputData)
  inputData = tuple(map(tuple, inputData))
  ws_input.Range("A10:{}{}".format(numToLetter(numCols), numRows+9)).Value = inputData
  # Now we need to populate the UserEuro table with the defaults. Probably
  # only need to do this once per year, per area, but will do it every time
  # just in case.
  ws_euro = wb.Worksheets("UserEuro")
  ws_euro.Select()
  # There is a macro to do this, but for some reason it fails on versions 7.4
  # and 8.0 when run on my computer. So we must do it ourselves..
  pasteDefaultEuroProportions(ws_euro, details, logger=loggerM)
  #excel.Application.Run("PasteDefaultEuroProportions")

  # Now specify that we only want the specified euro class, by turning the
  # proportions for that class to 1, (or a weighted value if there are more
  # than one row for the particular euro class). This function also reads
  # the default proportions.
  if euroClass == 99:
    # Just stick with default euroclass.
    defaultProportions = 'NotMined'
    busCoachProportions = 'NotMined'
    gotTechs = None
    pass
  else:
    defaultProportions = pd.DataFrame(columns=['year', 'area', 'vehicle', 'euro', 'proportion'])
    # Motorcycles first
    if DoMCycles:
      logprint(loggerM, '               Assigning fleet euro proportions for motorcycles.')
      defaultProportionsMC_, gotTechsMC = specifyEuroProportions(euroClass, wb,
                                  details['vehRowStartsMC'], details['vehRowEndsMC'],
                                  EuroClassNameColumnsMC, DefaultEuroColumnsMC,
                                  UserDefinedEuroColumns, SubType='MC', tech=tech)
      for key, value in defaultProportionsMC_.items():
        defaultProportionsRow= pd.DataFrame([[year, location, key, euroClass, value]],
                                             columns=['year', 'area', 'vehicle', 'euro', 'proportion'])
        defaultProportions= defaultProportions.append(defaultProportionsRow)
    else:
      gotTechsMC = {}
    if DoHybridBus:
      logprint(loggerM, '               Assigning fleet euro proportions for hybrid buses.')
      defaultProportionsHB_, gotTechsHB = specifyEuroProportions(euroClass, wb,
                                  details['vehRowStartsHB'], details['vehRowEndsHB'],
                                  EuroClassNameColumnsHB, DefaultEuroColumnsHB,
                                  UserDefinedEuroColumns, SubType='HB', tech=tech)
      for key, value in defaultProportionsHB_.items():
        defaultProportionsRow= pd.DataFrame([[year, location, key, euroClass, value]],
                                             columns=['year', 'area', 'vehicle', 'euro', 'proportion'])
        defaultProportions= defaultProportions.append(defaultProportionsRow)
    else:
      gotTechsHB = {}
    logprint(loggerM, "               Assigning fleet euro proportions for all 'other' vehicle types.")
    # And all other vehicles
    defaultProportions_, gotTechs = specifyEuroProportions(euroClass, wb,
                             details['vehRowStarts'], details['vehRowEnds'],
                             EuroClassNameColumns, DefaultEuroColumns,
                             UserDefinedEuroColumns, tech=tech)

    gotTechs = {**gotTechs, **gotTechsMC, **gotTechsHB}
    for key, value in gotTechs.items():
      if any(value):
        gotTechs[key] = True
      else:
        gotTechs[key] = False
    # Organise the default proportions.
    for key, value in defaultProportions_.items():
      defaultProportionsRow = pd.DataFrame([[year, location, key, euroClass, value]],
                                           columns=['year', 'area', 'vehicle', 'euro', 'proportion'])
      defaultProportions = defaultProportions.append(defaultProportionsRow)
    defaultProportions['version'] = versionForOutPut

    busCoachProportions = 'NotMined'
    if DoBusCoach:
      # Set the bus - coach proportions.
      busCoachProportions = specifyBusCoach(wb, busCoach, details['busCoachRow'],
                                            UserDefinedBusColumn, UserDefinedBusMWColumn,
                                            DefaultBusColumn, DefaultBusMWColumn)

  # Now run the EFT tool.
  ws_input.Select() # Select the appropriate sheet, we can't run the macro
                    # from another sheet.
  logprint(loggerM, '               Running EFT routine.')

  excel.Application.Run("RunEfTRoutine")
  logprint(loggerM, '                 Complete.')
  time.sleep(0.5)

  # Save and Close. Saving as an xlsm, rather than a xlsb, file, so that it
  # can be opened by pandas.
  (FN, FE) =  os.path.splitext(fileName)
  if DoBusCoach:
    tempSaveName = fileName.replace(FE, '({}_{}_E{}_{}_{})'.format(location, year, euroClass, busCoach, sizeRow))
  else:
    tempSaveName = fileName.replace(FE, '({}_{}_E{}_{})'.format(location, year, euroClass, sizeRow))
  p = 1
  tempSaveName_ = tempSaveName
  while os.path.exists('{}.xlsm'.format(tempSaveName)):
    p += 1
    tempSaveName = '{}{}'.format(tempSaveName_, p)
  tempSaveName = '{}.xlsm'.format(tempSaveName)
  wb.SaveAs(tempSaveName, win32.constants.xlOpenXMLWorkbookMacroEnabled)
  wb.Close()

  time.sleep(1) # To allow all systems to catch up.
  if closeExcel:
    excel.Quit()
    del(excelObj) # Make sure it's gone. Apparently some people have found this neccesary.
  return excel, tempSaveName, defaultProportions, busCoachProportions, weightclassnames, gotTechs
Ejemplo n.º 4
0
def createEFTInput(vBreakdown='Detailed Option 2',
                   speeds=[5,6,7,8,9,10,12,14,16,18,20,25,30,35,40,
                           45,50,60,70,80,90,100,110,120,130,140],
                   roadTypes=availableRoadTypes,
                   vehiclesToSkip=['Taxi (black cab)'],
                   vehiclesToInclude=None,
                   tech='All',
                   logger=None):
  """
  vehiclesToInclude trumps (and overwrites) vehiclesToSkip
  """
  # Get the logging details.
    # Get the logging details.
  loggerM = getLogger(logger, 'createEFTInput')

  logprint(loggerM, 'Creating EFT input.', level='debug')
  logprint(loggerM, 'Initial vehiclesToSkip: {}'.format(', '.join(vehiclesToSkip)), level='debug')
  logprint(loggerM, 'Initial vehiclesToInclude: {}'.format(', '.join(vehiclesToInclude)), level='debug')
  VehSplit = VehSplits[vBreakdown]
  logprint(loggerM, 'VehSplit: {}'.format(', '.join(VehSplit)), level='debug')

  if vehiclesToInclude is not None:
    # Populate vehiclesToSkip with those vehicles that are not included.
    vehiclesToSkip = []
    for veh in VehSplit:
      if veh not in vehiclesToInclude:
        vehiclesToSkip.append(veh)
  logprint(loggerM, 'Intermediate vehiclesToSkip: {}'.format(', '.join(vehiclesToSkip)), level='debug')
  #RoadTypes = ['Urban (not London)', 'Rural (not London)', 'Motorway (not London)']
  if tech != 'All':
    # Add vehicles to vehiclesToSkip that are irrelevant for the chosen technology.
    for veh in VehSplit:
      if veh not in techVehs[tech]:
        vehiclesToSkip.append(veh)
  vehiclesToSkip = list(set(vehiclesToSkip))
  logprint(loggerM, 'Final vehiclesToSkip: {}'.format(', '.join(vehiclesToSkip)), level='debug')

  if type(roadTypes) is str:
    if roadTypes in ['all', 'All', 'ALL']:
      roadTypes = availableRoadTypes
    else:
      roadTypes = [roadTypes]

  if vBreakdown == 'Basic Split':
    numRows = 2*len(roadTypes)*len(speeds)
  else:
    numRows = len(roadTypes)*len(speeds)*(len(VehSplit)-len(vehiclesToSkip))
  numCols = 6 + len(VehSplit)

  inputDF = pd.DataFrame(index=range(numRows), columns=range(numCols))
  ri = -1

  for rT in roadTypes:
    logprint(loggerM, 'roadType - {}'.format(rT), level='debug')
    for sp in speeds:
      logprint(loggerM, '  speed - {}'.format(sp), level='debug')
      for veh in VehSplit:
        logprint(loggerM, '    vehicle - {}'.format(veh), level='debug')
        #print('    veh - {}'.format(veh))
        if vBreakdown == 'Basic Split':
          ri += 2
          #inputDF.set_value(ri-1, 0, 'S{} - LDV - {}'.format(sp, rT))
          inputDF.iat[ri-1, 0] = 'S{} - LDV - {}'.format(sp, rT)
          inputDF.iat[ri-1, 1] = rT
          inputDF.iat[ri-1, 2] = 1
          inputDF.iat[ri-1, 3] = 0
          inputDF.iat[ri-1, 4] = sp
          inputDF.iat[ri-1, 5] = 1
          inputDF.iat[ri-1, 6] = 1
          inputDF.iat[ri, 0] = 'S{} - HDV - {}'.format(sp, rT)
          inputDF.iat[ri, 1] = rT
          inputDF.iat[ri, 2] = 1
          inputDF.iat[ri, 3] = 100
          inputDF.iat[ri, 4] = sp
          inputDF.iat[ri, 5] = 1
          inputDF.iat[ri, 6] = 1
        else:
          if veh in vehiclesToSkip:
            logprint(loggerM, '      skipped', level='debug')
            pass
          else:
            logprint(loggerM, '      including', level='debug')
            ri += 1
            inputDF.iat[ri, 0] = 'S{} - {} - {}'.format(sp, veh, rT)
            inputDF.iat[ri, 1] = rT
            inputDF.iat[ri, 2] = 1
            for vehi, vehb in enumerate(VehSplit):
              if vehb == veh:
                inputDF.iat[ri, 3+vehi] = 100
              else:
                inputDF.iat[ri, 3+vehi] = 0
            inputDF.iat[ri, len(VehSplit)+3] = sp
            inputDF.iat[ri, len(VehSplit)+4] = 1 # 1 hour. Not neccesary for g/km output.
            inputDF.iat[ri, len(VehSplit)+5] = 1 # 1 km. Not neccesary either.
            logprint(loggerM, '        done', level='debug')

  inputData = inputDF.as_matrix()
  inputShape = np.shape(inputData)
  logprint(loggerM, 'input created with dimensions {} by {}.'.format(inputShape[0], inputShape[1]), level='debug')
  return inputData
Ejemplo n.º 5
0
def getProportions(ws, ColName, ColProp, ColUser, vehRowStarts,
                   vehRowEnds, mode='Most Vehicles', logger=None):

  # Get the logging details.
  loggerM = getLogger(logger, 'getProportions')

  # Start a pandas dateframe.
  df = pd.DataFrame(columns=['vehicle', 'euroname', 'euroclass', 'technology',
                             'proportion', 'sourceCell', 'userCell'])
  for vehi in range(len(vehRowStarts)):
    starow = vehRowStarts[vehi]
    endrow = vehRowEnds[vehi]
    if mode == 'Most Vehicles':
      vehName = ws.Range("{}{}".format(ColName, starow-1)).Value
      while vehName is None:
        vehName = ws.Range("{}{}".format(ColName, starow)).Value
        starow += 1
    elif mode == 'Motorcycles':
      stroke_ = ws.Range("A{}".format(starow)).Value
      weight_ = ws.Range("A{}".format(starow+1)).Value
      if stroke_ == '0-50cc':
        vehName = 'Motorcycle - 0-50cc'
      else:
        vehName = 'Motorcycle - {} - {}'.format(stroke_, weight_)
    elif mode == 'Hybrid Buses':
      decker_ = ws.Range("A{}".format(starow)).Value
      vehName = 'Hybrid Buses - {}'.format(decker_)
      starow += 1  # Grrrrr. Poor formatting in the EFT
      endrow += 1
    elif mode == 'Weights':
      vehName = ws.Range("{}{}".format(ColName, starow-1)).Value
    else:
      raise ValueError("mode '{}' is not recognised.".format(mode))
    for row in range(starow, endrow+1):
      euroName = ws.Range("{}{}".format(ColName, row)).Value
      if euroName is not None:
        sourceCell = "{}{}".format(ColProp, row)
        userCell = "{}{}".format(ColUser, row)
        proportion = ws.Range(sourceCell).Value
        if not isinstance(proportion, float):
          logprint(loggerM, 'Bad proportion value "{}" for veh {}, euro {}.'.format(proportion, vehName, euroName), level='info')
          sourceCell = "{}{}".format(ColUser, row)
          proportion = ws.Range(sourceCell).Value
          if not isinstance(proportion, float):
            #print(proportion)
            raise ValueError('Proportion must be a float.')
          else:
            logprint(loggerM, 'Fixed. Proportion value {}.'.format(proportion), level='info')
        logprint(loggerM, 'vehName: {}, euroName: {}, proportion: {}'.format(vehName, euroName, proportion), level='debug')
        got = False
        if mode == 'Weights':
          euroName = weightClassNameVariations[euroName]
          df1 = pd.DataFrame([[vehName, euroName, -99, '--', proportion, sourceCell, userCell]],
                               columns=['vehicle', 'euroname', 'euroclass',
                                        'technology', 'proportion', 'sourceCell', 'userCell'])
          df = df.append(df1, 1)
          continue
        for euroI, euronames in euroClassNameVariations.items():
          if euroI == 99:
            continue
          if euroName in euronames['All']:
            got = True
            tech = 'Standard'
            for techname, euronamestechs in euronames.items():
              if techname == 'All':
                continue
              if euroName in euronamestechs:
                tech = techname
                break
            df1 = pd.DataFrame([[vehName, euroName, euroI, tech, proportion, sourceCell, userCell]],
                               columns=['vehicle', 'euroname', 'euroclass',
                                        'technology', 'proportion', 'sourceCell', 'userCell'])
            df = df.append(df1, 1)

        if not got:
          raise ValueError("Can't identify euro class from {}.".format(euroName))
  if mode == 'Weights':
    df = df.rename(columns={'euroname': 'weightclass'})
    df = df.drop('euroclass', 1)
    df = df.drop('technology', 1)
  #print(df.head())
  return df