diff --git a/pyglance/glance/compare.py b/pyglance/glance/compare.py index cd36f6d03fd35191c38ea9b398388f7c5160afa2..590a84b648d707ce1e634fd35ebbf192d68c07f5 100644 --- a/pyglance/glance/compare.py +++ b/pyglance/glance/compare.py @@ -24,6 +24,7 @@ LOG = logging.getLogger(__name__) glance_lon_lat_defaults = {'longitude': 'pixel_longitude', 'latitude': 'pixel_latitude', + 'lon_lat_epsilon': 0.0, 'data_filter_function_lon_in_a': None, 'data_filter_function_lat_in_a': None, 'data_filter_function_lon_in_b': None, @@ -213,8 +214,8 @@ def _load_config_or_options(optionsSet, originalArgs) : runInfo = {} runInfo['shouldIncludeReport'] = True runInfo['shouldIncludeImages'] = False + runInfo['doFork'] = False # default runInfo.update(glance_lon_lat_defaults) # get the default lon/lat info - runInfo['lon_lat_epsilon'] = 0.0 runInfo['version'] = _get_glance_version_string() # by default, we don't have any particular variables to analyze @@ -254,7 +255,7 @@ def _load_config_or_options(optionsSet, originalArgs) : filePath, ('.py' , 'U', 1)) # get everything from the config file - runInfo['shouldIncludeImages'] = glanceRunConfig.shouldIncludeImages + runInfo.update(glanceRunConfig.settings) runInfo.update(glanceRunConfig.lat_lon_info) # get info on the lat/lon variables # get any requested names @@ -278,6 +279,7 @@ def _load_config_or_options(optionsSet, originalArgs) : # so get everything from the options directly runInfo['shouldIncludeReport'] = not optionsSet.imagesOnly runInfo['shouldIncludeImages'] = not optionsSet.htmlOnly + runInfo['doFork'] = optionsSet.doFork runInfo['latitude'] = optionsSet.latitudeVar or runInfo['latitude'] runInfo['longitude'] = optionsSet.longitudeVar or runInfo['longitude'] runInfo['lon_lat_epsilon'] = optionsSet.lonlatepsilon @@ -580,6 +582,8 @@ python -m glance help="set default epsilon for longitude and latitude comparsion") parser.add_option('-n', '--version', dest='version', action="store_true", default=False, help="view the glance version") + parser.add_option('-f', '--fork', dest='doFork', + action="store_true", default=False, help="start multiple processes to create images in parallel") options, args = parser.parse_args() @@ -935,8 +939,9 @@ python -m glance else : LOG.warn(explanationName + ' ' + 'could not be compared. This may be because the data for this variable does not match in shape ' + - 'between the two files or the data may not match the shape of the selected longitude and ' + - 'latitude variables.') + 'between the two files (file A data shape: ' + str(aData.shape) + '; file B data shape: ' + str(bData.shape) + + ') or the data may not match the shape of the selected longitude ' + str(longitudeCommon.shape) + ' and ' + + 'latitude ' + str(latitudeCommon.shape) + ' variables.') # from this point on, we will be forking to create child processes so we can parallelize our image and # report generation @@ -947,17 +952,6 @@ python -m glance # loop to create the images for all our variables if (runInfo['shouldIncludeImages']) : for name in variableAnalysisInfo : - """ TODO for the moment, I think this is resulting in too many processes, so only generate figs for one var at a time - # create a child to handle this variable's images - pid = os.fork() - isParent = not (pid is 0) - # only the child needs to make figures, the parent can move on - if (isParent) : - childPids.append(pid) - LOG.debug ("Started child process (pid: " + str(pid) + ") to create reports for variable " + name) - - else : - """ # create the images comparing that variable print("\tcreating figures for: " + variableAnalysisInfo[name]['exp_name']) plot.plot_and_save_figure_comparison(variableAnalysisInfo[name]['data']['A'], @@ -970,9 +964,9 @@ python -m glance latitudeCommon, longitudeCommon, spaciallyInvalidMaskA, spaciallyInvalidMaskB, - outputPath, True) + outputPath, True, + doFork=runInfo['doFork']) print("\tfinished creating figures for: " + variableAnalysisInfo[name]['exp_name']) - #sys.exit(0) # this child has successfully finished it's tasks # reports are fast, so the parent thread will just do this # generate our general report pages once we've looked at all the variables diff --git a/pyglance/glance/exconfig.py b/pyglance/glance/exconfig.py index 122f9bbbb92b1d3a2da696e2f565afd9429dbebd..3f1a6bf6f9e0130d072324fe3959a54019922b20 100644 --- a/pyglance/glance/exconfig.py +++ b/pyglance/glance/exconfig.py @@ -11,8 +11,16 @@ Created by Eva Schiffer Jun 2009. Copyright (c) 2009 University of Wisconsin SSEC. All rights reserved. """ +# various general settings to control how reports are created +settings = {} # whether or not images should be generated and shown in the report -shouldIncludeImages = True +settings['shouldIncludeImages'] = True +# should we create multiple processes to make more than one image at a time? +# turning on this option can cause glance to use a very large amount of system +# resources (if your data set is particularly large, your machine may not have +# enough), but will speed up image generation in cases where your data set is +# relatively small or your machine is very powerful +settings['doFork'] = True # the names of the latitude and longitude variables that will be used lat_lon_info = {} @@ -66,6 +74,10 @@ defaultValues = {'epsilon': 0.0, # the acceptable differe 'nonfinite_data_tolerance': None # the allowed fraction of non-finite data # None indicates that variables should not be tested # on amount of non-finite data +""" + 'data_filter_function_a': (insert lambda function here), + 'data_filter_function_b': (insert lambda function here) +""" } # a list of all the variables to analyze, all of the details are optional, diff --git a/pyglance/glance/filters.py b/pyglance/glance/filters.py new file mode 100644 index 0000000000000000000000000000000000000000..6c6a9e600ca9d5a0865b3183f473b6fcb0c42151 --- /dev/null +++ b/pyglance/glance/filters.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python +# encoding: utf-8 +""" +General filters that can be used to prefilter data before comparison (define in the config file). + +Created by Eva Schiffer August 2009. +Copyright (c) 2009 University of Wisconsin SSEC. All rights reserved. +""" + +import numpy as np + +def trim_off_of_top (data, num_elements_to_trim) : + """ + Remove num_elements_to_trim rows from the top of the data array + and return a copy of the remaining data + """ + assert(num_elements_to_trim >= 0) + + return data[num_elements_to_trim:, :].copy() + +def trim_off_of_bottom (data, num_elements_to_trim) : + """ + Remove num_elements_to_trim rows from the bottom of the data array + and return a copy of the remaining data + """ + assert(num_elements_to_trim >= 0) + + return data[:(-1 * num_elements_to_trim), :].copy() + +def trim_off_of_right (data, num_elements_to_trim) : + """ + Remove num_elements_to_trim columns from the right side of the data array + and return a copy of the remaining data + """ + assert(num_elements_to_trim >= 0) + + return data[:, :(-1 * num_elements_to_trim)].copy() + +def trim_off_of_left (data, num_elements_to_trim) : + """ + Remove num_elements_to_trim columns from the left side of the data array + and return a copy of the remaining data + """ + assert(num_elements_to_trim >= 0) + + return data[:, num_elements_to_trim:] + +def flatten_data_into_bins (data, ranges, new_values, missing_value, return_data_type) : + """ + Sort the data into the given ranges. Each range should correspond to a value + given in the list of new_values; this value will be used for all data points + that fall within that range. Ranges will be treated as a continuious spectrum + So please list them in increasing order. + + Note: Data values that fall directly between two ranges will be grouped with + the larger range. + + Also Note: If a data point is not found to fall within any of the ranges, + the missing_value will be filled into that spot instead. + """ + # make sure we have values to match each range, no more and no less + assert(len(ranges) == (len(new_values) + 1)) + + new_data = np.zeros(data.shape, dtype=return_data_type) + + # find the data in each range + filled_mask = np.zeros(data.shape, dtype=bool) + temp_mask = np.zeros(data.shape, dtype=bool) + for index in range(len(ranges) - 1) : + temp_mask = (data >= ranges[index]) & (data <= ranges[index + 1]) + new_data[temp_mask] = new_values[index] + filled_mask = filled_mask | temp_mask + + # clean up anything that didn't get filled + new_data[~filled_mask] = missing_value + + return new_data + +def extract_bit_from_packed_mask (data, index_of_bit_to_extract, + (truth_value, false_value), + return_data_type) : + """ + Extract a one bit boolean mask from a larger packed data set. The bit that + you wish to extract must be identified by it's index from the lower end of + the array of bits (with 0 being the bit that would represent a value of 1 + if the mask was an integer; so the 1 indexed bit would represent the integer + value of 2, the 2 indexed bit a value of 4, and so on). + Note: It is assumed that the endian-ness of your data was handled correctly + by whatever code loaded it from the file. + + The return data will be filled with the truth_value and false_value based + on whether the bit was 1 (true) or 0 (false) and will be of the requested + return_data_type. + + Note: If you wish to examine or compare more than one packed bit, you may + use this filter multiple times to extract each separately and then program + your own function to perform any comparisons. + """ + # we are only allowing positive indexing due to data typing complexity + assert(index_of_bit_to_extract >= 0) + + # make our mask + mask_value = 2**index_of_bit_to_extract + bit_mask = np.zeros(data.shape, dtype=int) + bit_mask = bit_mask + mask_value + + # get the data out and fill in the requested values + pure_extraction = np.bitwise_and(data, bit_mask) + new_data = np.zeros(data.shape, dtype=return_data_type) + new_data[pure_extraction > 0] = truth_value + new_data[pure_extraction <= 0] = false_value + + return new_data + +def select_slice_from_3D_last (data, slice_index) : + """ + Select a slice from a 3 dimensional data set. + slice_index indicates the index of the slice that you would like returned + by this function + + note: this assumes you wish to slice across the dimention that you would index + into last + """ + assert(slice_index >= 0) + assert(slice_index < data.shape[2]) + + return data[:, :, slice_index] + +if __name__=='__main__': + sys.exit(main()) \ No newline at end of file diff --git a/pyglance/glance/plot.py b/pyglance/glance/plot.py index 22470098a90e342e3ab7c9f488255759ddcb6aa5..14576fa0928c06504333df1cd85532ced4d6499c 100644 --- a/pyglance/glance/plot.py +++ b/pyglance/glance/plot.py @@ -403,6 +403,39 @@ def plot_and_save_spacial_trouble(longitude, latitude, return +def _handle_fig_creation_task(child_figure_function, log_message, + fullFigOutputNamePath, smallFigOutputNamePath, + shouldMakeSmall, doFork) : + """ + fork to do something. + the parent will return the child pid + the child will do it's work and then exit + """ + + pid = 0 + if (doFork) : + # do the fork + pid = os.fork() + + # figure out if we're the parent or child + isParent = not (pid is 0) + if (isParent) : + return pid + else : + figure = child_figure_function() + LOG.info(log_message) + figure.savefig(fullFigOutputNamePath, dpi=fullSizeDPI) + if (shouldMakeSmall) : + figure.savefig(smallFigOutputNamePath, dpi=thumbSizeDPI) + + # if we've reached this point and we did fork, + # then we're the child process and we should stop now + if (doFork) : + sys.exit(0) # the child is done now + + # if we didn't fork, return the 0 pid to indicate that + return pid + def plot_and_save_figure_comparison(aData, bData, variableRunInfo, fileAName, fileBName, @@ -413,7 +446,8 @@ def plot_and_save_figure_comparison(aData, bData, spaciallyInvalidMaskB, outputPath, makeSmall=False, - shortCircuitComparisons=False) : + shortCircuitComparisons=False, + doFork=False) : """ given two files, and information on what to compare, make comparison figures and save them to the given output graph. @@ -450,12 +484,6 @@ def plot_and_save_figure_comparison(aData, bData, (spaciallyInvalidMaskA, spaciallyInvalidMaskB) = delta.diff(aData, bData, variableRunInfo['epsilon'], (missing_value, missing_value_b), (spaciallyInvalidMaskA, spaciallyInvalidMaskB)) - ''' - rawDiffData, goodMask, troubleMask, (aNotFiniteMask, bNotFiniteMask), \ - (aMissingMask, bMissingMask), outsideEpsilonMask = delta.diff(aData, bData, variableRunInfo['epsilon'], - (missing_value, missing_value_b), - spaciallyInvalidMaskBoth) - ''' absDiffData = np.abs(rawDiffData) # we also want to show the distance between our two, rather than just which one's bigger/smaller # some more display info, pull it out for convenience @@ -473,158 +501,120 @@ def plot_and_save_figure_comparison(aData, bData, childPids = [] # the original data figures + + # the original A data LOG.info("\t\tcreating image of " + variableDisplayName + " in file a") - pid = os.fork() - isParent = not (pid is 0) - if (isParent) : + pid = _handle_fig_creation_task((lambda : _create_mapped_figure(aData, latitudeAData, longitudeAData, + (variableDisplayName + "\nin File A"), + invalidMask=(~goodInAMask), + dataRanges=dataRanges, + dataRangeNames=dataRangeNames)), + "\t\tsaving image of " + variableDisplayName + " for file a", + outputPath + "/" + variableName + ".A.png", + outputPath + "/" + variableName + ".A.small.png", + makeSmall, doFork) + if not (pid is 0) : childPids.append(pid) LOG.debug ("Started child process (pid: " + str(pid) + ") to create file a image for " + variableDisplayName) - else : - figureA = _create_mapped_figure(aData, latitudeAData, longitudeAData, - (variableDisplayName + "\nin File A"), - invalidMask=(~goodInAMask), - dataRanges=dataRanges, - dataRangeNames=dataRangeNames) - LOG.info("\t\tsaving image of " + variableDisplayName + " for file a") - figureA.savefig(outputPath + "/" + variableName + ".A.png", dpi=fullSizeDPI) - if (makeSmall) : - figureA.savefig(outputPath + "/" + variableName + ".A.small.png", dpi=thumbSizeDPI) - sys.exit(0) # the child is done now + # the original B data LOG.info("\t\tcreating image of " + variableDisplayName + " in file b") - pid = os.fork() - isParent = not (pid is 0) - if (isParent) : + pid = _handle_fig_creation_task((lambda : _create_mapped_figure(bData, latitudeBData, longitudeBData, + (variableDisplayName + "\nin File B"), + invalidMask=(~ goodInBMask), + dataRanges=dataRanges, + dataRangeNames=dataRangeNames)), + "\t\tsaving image of " + variableDisplayName + " for file b", + outputPath + "/" + variableName + ".B.png", + outputPath + "/" + variableName + ".B.small.png", + makeSmall, doFork) + if not (pid is 0) : childPids.append(pid) LOG.debug ("Started child process (pid: " + str(pid) + ") to create file b image for " + variableDisplayName) - else : - figureB = _create_mapped_figure(bData, latitudeBData, longitudeBData, - (variableDisplayName + "\nin File B"), - invalidMask=(~ goodInBMask), - dataRanges=dataRanges, - dataRangeNames=dataRangeNames) - LOG.info("\t\tsaving image of " + variableDisplayName + " in file b") - figureB.savefig(outputPath + "/" + variableName + ".B.png", dpi=fullSizeDPI) - if (makeSmall) : - figureB.savefig(outputPath + "/" + variableName + ".B.small.png", dpi=thumbSizeDPI) - sys.exit(0) # the child is done now # make the data comparison figures if not shortCircuitComparisons : + # the distance between the two data sets LOG.info("\t\tcreating image of the absolute value of difference in " + variableDisplayName) - pid = os.fork() - isParent = not (pid is 0) - if (isParent) : + pid = _handle_fig_creation_task((lambda : _create_mapped_figure(absDiffData, + latitudeCommonData, longitudeCommonData, + ("Absolute value of difference in\n" + variableDisplayName), + invalidMask=(~ goodMask))), + "\t\tsaving image of the absolute value of difference for " + variableDisplayName, + outputPath + "/" + variableName + ".AbsDiff.png", + outputPath + "/" + variableName + ".AbsDiff.small.png", + makeSmall, doFork) + if not (pid is 0) : childPids.append(pid) LOG.debug ("Started child process (pid: " + str(pid) + ") to create absolute value of difference image for " + variableDisplayName) - else : - figureAbsDiff = _create_mapped_figure(absDiffData, latitudeCommonData, longitudeCommonData, - ("Absolute value of difference in\n" + variableDisplayName), - invalidMask=(~ goodMask)) - LOG.info("\t\tsaving image of the absolute value of difference for " + variableDisplayName) - figureAbsDiff.savefig(outputPath + "/" + variableName + ".AbsDiff.png", dpi=fullSizeDPI) - if (makeSmall) : - figureAbsDiff.savefig(outputPath + "/" + variableName + ".AbsDiff.small.png", dpi=thumbSizeDPI) - sys.exit(0) # the child is done now + # the subtraction of one data set from the other LOG.info("\t\tcreating image of the difference in " + variableDisplayName) - pid = os.fork() - isParent = not (pid is 0) - if (isParent) : + pid = _handle_fig_creation_task((lambda : _create_mapped_figure(rawDiffData, latitudeCommonData, longitudeCommonData, + ("Value of (Data File B - Data File A) for\n" + variableDisplayName), + invalidMask=(~ goodMask))), + "\t\tsaving image of the difference in " + variableDisplayName, + outputPath + "/" + variableName + ".Diff.png", + outputPath + "/" + variableName + ".Diff.small.png", + makeSmall, doFork) + if not (pid is 0) : childPids.append(pid) LOG.debug ("Started child process (pid: " + str(pid) + ") to create difference image for " + variableDisplayName) - else : - figureDiff = _create_mapped_figure(rawDiffData, latitudeCommonData, longitudeCommonData, - ("Value of (Data File B - Data File A) for\n" + variableDisplayName), - invalidMask=(~ goodMask)) - LOG.info("\t\tsaving image of the difference in " + variableDisplayName) - figureDiff.savefig(outputPath + "/" + variableName + ".Diff.png", dpi=fullSizeDPI) - if (makeSmall) : - figureDiff.savefig(outputPath + "/" + variableName + ".Diff.small.png", dpi=thumbSizeDPI) - sys.exit(0) # the child is done now - # this figure is more complex because we want to mark the trouble points on it + # mark the trouble points LOG.info("\t\tcreating image marking trouble data in " + variableDisplayName) - pid = os.fork() - isParent = not (pid is 0) - if (isParent) : + # this is not an optimal solution, but we need to have at least somewhat valid data at any mismatched points so + # that our plot won't be totally destroyed by missing or non-finite data from B + bDataCopy = bData[:] + tempMask = goodInAMask & (~goodInBMask) + bDataCopy[tempMask] = aData[tempMask] + pid = _handle_fig_creation_task((lambda : _create_mapped_figure(bDataCopy, latitudeCommonData, longitudeCommonData, + ("Areas of trouble data in\n" + variableDisplayName), + (~(goodInAMask | goodInBMask)), + mediumGrayColorMap, troubleMask, + dataRanges=dataRanges, + dataRangeNames=dataRangeNames)), + "\t\tsaving image marking trouble data in " + variableDisplayName, + outputPath + "/" + variableName + ".Trouble.png", + outputPath + "/" + variableName + ".Trouble.small.png", + makeSmall, doFork) + if not (pid is 0) : childPids.append(pid) LOG.debug ("Started child process (pid: " + str(pid) + ") to create trouble image for " + variableDisplayName) - else : - # this is not an optimal solution, but we need to have at least somewhat valid data at any mismatched points so - # that our plot won't be totally destroyed by missing or non-finite data from B - bDataCopy = bData[:] - tempMask = goodInAMask & (~goodInBMask) - bDataCopy[tempMask] = aData[tempMask] - # create the figure marked with the trouble points on top of B's data in grayscale - figureBadDataInDiff = _create_mapped_figure(bDataCopy, latitudeCommonData, longitudeCommonData, - ("Areas of trouble data in\n" + variableDisplayName), - (~(goodInAMask | goodInBMask)), - mediumGrayColorMap, troubleMask, - dataRanges=dataRanges, - dataRangeNames=dataRangeNames) - LOG.info("\t\tsaving image marking trouble data in " + variableDisplayName) - figureBadDataInDiff.savefig(outputPath + "/" + variableName + ".Trouble.png", dpi=fullSizeDPI) - if (makeSmall) : - figureBadDataInDiff.savefig(outputPath + "/" + variableName + ".Trouble.small.png", dpi=thumbSizeDPI) - sys.exit(0) # the child is done now - # a histogram of the values of fileA - file B so that the distribution of error is visible (hopefully) + # a histogram of the values of fileA - file B LOG.info("\t\tcreating histogram of the amount of difference in " + variableDisplayName) - pid = os.fork() - isParent = not (pid is 0) - if (isParent) : + numBinsToUse = 50 + valuesForHist = rawDiffData[goodMask] + pid = _handle_fig_creation_task((lambda : _create_histogram(valuesForHist, numBinsToUse, + ("Difference in\n" + variableDisplayName), + ('Value of (Data File B - Data File A) at a Data Point'), + ('Number of Data Points with a Given Difference'), + True)), + "\t\tsaving histogram of the amount of difference in " + variableDisplayName, + outputPath + "/" + variableName + ".Hist.png", + outputPath + "/" + variableName + ".Hist.small.png", + makeSmall, doFork) + if not (pid is 0) : childPids.append(pid) LOG.debug ("Started child process (pid: " + str(pid) + ") to create difference histogram image for " + variableDisplayName) - else : - numBinsToUse = 50 - valuesForHist = rawDiffData[goodMask] - diffHistogramFigure = _create_histogram(valuesForHist, numBinsToUse, - ("Difference in\n" + variableDisplayName), - ('Value of (Data File B - Data File A) at a Data Point'), - ('Number of Data Points with a Given Difference'), - True) - LOG.info("\t\tsaving histogram of the amount of difference in " + variableDisplayName) - diffHistogramFigure.savefig(outputPath + "/" + variableName + ".Hist.png", dpi=fullSizeDPI) - if (makeSmall) : - diffHistogramFigure.savefig(outputPath + "/" + variableName + ".Hist.small.png", dpi=thumbSizeDPI) - sys.exit(0) # the child is done now - ''' TODO, is this actually useful? - # a histogram of the values of fileA - file B, excluding epsilon mismatched values (to show errors more clearly) - LOG.info("\t\tcreating histogram of the amount of difference for imperfect matches") - numBinsToUse = 50 - valuesForHist = rawDiffData[outsideEpsilonMask] # select only the imperfectly matched points - imperfectHistogramFigure = None - if (valuesForHist.size > 0) : - imperfectHistogramFigure = _create_histogram(valuesForHist, numBinsToUse, - ("Difference in " + variableDisplayName + "\nExcluding Epsilon Matches"), - ('Value of (Data File B - Data File A) at a Data Point'), - ('Number of Data Points with a Given Difference'), - True) - LOG.info("\t\tsaving histogram of the amount of difference for imperfect matches") - imperfectHistogramFigure.savefig(outputPath + "/" + variableName + ".ImpHist.png", dpi=fullSizeDPI) - if (makeSmall): - imperfectHistogramFigure.savefig(outputPath + "/" + variableName + ".ImpHist.small.png", dpi=thumbSizeDPI) - ''' - - # scatter plot of file a and b comparison + # scatter plot of file a vs file b values LOG.info("\t\tcreating scatter plot of file a values vs file b values for " + variableDisplayName) - pid = os.fork() - isParent = not (pid is 0) - if (isParent) : + + pid = _handle_fig_creation_task((lambda : _create_scatter_plot(aData[goodMask], bData[goodMask], + "Value in File A vs Value in File B", + "File A Value", "File B Value", + outsideEpsilonMask[goodMask], + variableRunInfo['epsilon'])), + "\t\tsaving scatter plot of file a values vs file b values in " + variableDisplayName, + outputPath + "/" + variableName + ".Scatter.png", + outputPath + "/" + variableName + ".Scatter.small.png", + makeSmall, doFork) + if not (pid is 0) : childPids.append(pid) LOG.debug ("Started child process (pid: " + str(pid) + ") to create scatter plot image for " + variableDisplayName) - else : - diffScatterPlot = _create_scatter_plot(aData[goodMask], bData[goodMask], - "Value in File A vs Value in File B", "File A Value", "File B Value", - outsideEpsilonMask[goodMask], variableRunInfo['epsilon']) - LOG.info("\t\tsaving scatter plot of file a values vs file b values in " + variableDisplayName) - diffScatterPlot.savefig(outputPath + "/" + variableName + ".Scatter.png", dpi=fullSizeDPI) - if (makeSmall): - diffScatterPlot.savefig(outputPath + "/" + variableName + ".Scatter.small.png", dpi=thumbSizeDPI) - sys.exit(0) # the child is done now # now we need to wait for all of our child processes to terminate before returning if (isParent) : # just in case diff --git a/pyglance/glance/report.py b/pyglance/glance/report.py index 474bce3a1ec20efe14a170c39679d8b055896fd9..a39bffe9d23ff5b6d70f5106f51df8351f55beaa 100644 --- a/pyglance/glance/report.py +++ b/pyglance/glance/report.py @@ -148,8 +148,9 @@ def generate_and_save_summary_report(files, shutil.copy(failFile, outputPath) # copy the original configuration file, TODO should I move this to a list input in the parameters? - originalConfigFile = runInfo['config_file_path'] - shutil.copy(originalConfigFile, outputPath) + if ('config_file_path' in runInfo) : + originalConfigFile = runInfo['config_file_path'] + shutil.copy(originalConfigFile, outputPath) return