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))
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
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
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
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