Newer
Older
#!/usr/bin/env python
# encoding: utf-8
"""
PDF/HTML report generation routines
Created by rayg Apr 2009.
Copyright (c) 2009 University of Wisconsin SSEC. All rights reserved.
"""
from pkg_resources import resource_string, resource_filename #, resource_stream
(no author)
committed
from mako.template import Template
(no author)
committed
from mako.lookup import TemplateLookup
import types as types
import numpy as np
import shutil as shutil
(no author)
committed
(no author)
committed
from glance.constants import *
# TODO, this should be overridable in the config file when there is one
Eva Schiffer
committed
floatFormat = '%#0.8g'
formattingSettings = {
types.FloatType: floatFormat,
np.float32: floatFormat,
np.float64: floatFormat
}
INT_TYPES = [
types.IntType,
np.int8,
np.int16,
np.int32,
np.int64,
]
INT_FMT = '%d'
for t in INT_TYPES: formattingSettings[t] = INT_FMT
(no author)
committed
# make and save an html page using a mako template, put all the data you need
# in the template into the kwargs
def _make_and_save_page (fullFilePath, templateFileNameToUse, **kwargs) :
(no author)
committed
tempFileName = resource_filename(__name__, ".")
tempLookup = TemplateLookup(directories=[tempFileName])
(no author)
committed
(no author)
committed
fileToWrite = open(fullFilePath, 'w')
(no author)
committed
tempTemplate = Template(resource_string(__name__, templateFileNameToUse), lookup=tempLookup)
(no author)
committed
fileToWrite.write(tempTemplate.render(**kwargs))
fileToWrite.close()
return
(no author)
committed
def make_formatted_display_string(displayData, customDisplayFormat=None) :
"""given a piece of data return a display string
"""
displayString = ''
(no author)
committed
formatStr = customDisplayFormat
# check to see if there is a format string to use
if type(displayData) in formattingSettings :
(no author)
committed
if formatStr is None :
formatStr = formattingSettings[type(displayData)]
displayString = locale.format_string(formatStr, displayData, grouping=True)
else :
displayString = str(displayData)
return displayString
(no author)
committed
def generate_and_save_summary_report(files,
(no author)
committed
outputPath, reportFileName,
(no author)
committed
runInfo,
(no author)
committed
variables,
(no author)
committed
spatial={},
varNames={},
globalAttrs={},) :
(no author)
committed
"""
given two files, and information about them, save a summary of their comparison
The summary report, in html format will be saved to the given outputPath/outputFile
(no author)
committed
Variables should be a dictionary keyed on the name of each compared variable and containing the
(no author)
committed
% of data values that were judged to be "similar enough" between file A and B (according
(no author)
committed
to the epsilon originally inputed for the comparison) and the epsilon used for the comparison
(no author)
committed
variables[var_name] = {
PASSED_EPSILON_PERCENT_KEY: percent "similar enough" according to epsilon,
VARIABLE_RUN_INFO_KEY: variable Run Info <- as defined for a variable report
(no author)
committed
}
(no author)
committed
more keys can be added in the future if more detailed data reporting is desired on the main report page
(no author)
committed
runInfo should be information about the run in the form
(no author)
committed
runInfo = {
MACHINE_INFO_KEY: currentMachine,
USER_INFO_KEY: currentUser,
TIME_INFO_KEY: currentTime,
GLANCE_VERSION_INFO_KEY: a version string describing glance,
LATITUDE_NAME_KEY: latitudeName,
LONGITUDE_NAME_KEY: longitudeName,
LAT_ALT_NAME_IN_B_KEY: latitudeNameInB, # optional, if not defined, B's using the normal latitude
LON_ALT_NAME_IN_B_KEY: longitudeNameInB, # optional, if not defined, B's using the normal longitude
DO_MAKE_IMAGES_KEY: shouldIncludeImages, # this key/value is optional, defaults to True
SHORT_CIRCUIT_DIFFS_KEY: if the diff related information should be shown
(no author)
committed
}
files is a dictionary in the form
(no author)
committed
files = {A_FILE_TITLE_KEY:
{
PATH_KEY: fileAName,
#DISPLAY_NAME_KEY: fileADisplayName, # TODO
LAST_MODIFIED_KEY: lastModifiedTimeA,
MD5SUM_KEY: aMD5SUM
},
B_FILE_TITLE_KEY:
{
PATH_KEY: fileBName,
#DISPLAY_NAME_KEY: fileADisplayName, # TODO
LAST_MODIFIED_KEY: lastModifiedTimeB,
MD5SUM_KEY: bMD5SUM
}
(no author)
committed
}
spatial is a dictionary in the form
spatial = {
(no author)
committed
A_FILE_TITLE_KEY:
{
NUMBER_INVALID_PTS_KEY: number of spatially invalid points only in A (and not corrspondingly in B),
PERCENT_INVALID_PTS_KEY: percent of spatially invalid points in A (out of all pts in A)
(no author)
committed
},
(no author)
committed
B_FILE_TITLE_KEY:
{
NUMBER_INVALID_PTS_KEY: number of spatially invalid points only in B (and not corrspondingly in A),
PERCENT_INVALID_PTS_KEY: percent of spatially invalid points in B (out of all pts in B)
(no author)
committed
},
(no author)
committed
PERCENT_INV_PTS_SHARED_KEY: the percent of points that are spatially invalid in at least one of the two files,
(no author)
committed
}
any of the first level of keys in spatial are optional,
(no author)
committed
although it is assumed that if you include an entry for A_FILE_TITLE_KEY or B_FILE_TITLE_KEY it
will have both of the expected keys in its dictionary
(ie. both NUMBER_INVALID_PTS_KEY and PERCENT_INVALID_PTS_KEY)
(no author)
committed
varNames should be information about the variables present in the files, in the form
(no author)
committed
varNames = {
VAR_NAMES_UNIQUE_TO_A_KEY: uniqueToAVars,
VAR_NAMES_UNIQUE_TO_B_KEY: uniqueToBVars,
SHARED_VARIABLE_NAMES_KEY: sharedVars
}
all entries in the varNames dictionary are optional.
globalAttrs should be a dictionary of global attributes for the file, in the form
globalAttrs = {
A_FILE_TITLE_KEY:
{
<AttrName>: attr value,
},
B_FILE_TITLE_KEY:
{
<AttrName>: attr value,
},
}
(no author)
committed
"""
(no author)
committed
# pack up all the data needed to build the summary report
(no author)
committed
varNamesToUse = {
VAR_NAMES_UNIQUE_TO_A_KEY: [ ],
VAR_NAMES_UNIQUE_TO_B_KEY: [ ],
SHARED_VARIABLE_NAMES_KEY: [ ]
}
(no author)
committed
varNamesToUse.update(varNames)
(no author)
committed
# build the full kwargs with all the info
(no author)
committed
kwargs = {
RUN_INFO_DICT_KEY: runInfo,
FILES_INFO_DICT_KEY: files,
SPATIAL_INFO_DICT_KEY: spatial,
VARIABLE_NAMES_DICT_KEY: varNamesToUse,
VARIABLE_RUN_INFO_DICT_KEY: variables,
ATTRS_INFO_DICT_KEY: globalAttrs,
(no author)
committed
}
(no author)
committed
_make_and_save_page((outputPath + "/" + reportFileName), 'mainreport.txt', **kwargs)
(no author)
committed
(no author)
committed
# copy the pass/fail images, TODO should I move this to a list input in the parameters?
(no author)
committed
passFile = resource_filename(__name__, 'pass.gif') # TODO, how can this be done without unzipping the egg?
failFile = resource_filename(__name__, 'fail.gif') # TODO, how can this be done without unzipping the egg?
shutil.copy(passFile, outputPath)
shutil.copy(failFile, outputPath)
(no author)
committed
# copy the original configuration file, TODO should I move this to a list input in the parameters?
(no author)
committed
if (CONFIG_FILE_PATH_KEY in runInfo) :
originalConfigFile = runInfo[CONFIG_FILE_PATH_KEY]
(no author)
committed
shutil.copy(originalConfigFile, outputPath)
(no author)
committed
(no author)
committed
return
(no author)
committed
def generate_and_save_doc_page(definitions, outputPath) :
"""
generate a page with all the statistics definitions for reference from the reports
"""
(no author)
committed
kwargs = {
DEFINITIONS_INFO_KEY: definitions
}
(no author)
committed
_make_and_save_page(outputPath + "/doc.html", 'doc.txt', **kwargs)
(no author)
committed
return
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
def group_stats_by_file(statGroups):
""" Group statistics by file
Where there are pairs of statistics in the form *_[ab] or [ab]_*, group
them together. So given
{ 'General Statistics': {
'min_a': -1,
'min_b': -10
'epsilon': 3
}
}
returns
{ 'General Statistics': {
'min': { 'a': -1. b: -10 },
'epsilon': { 'both':3 }
}
}
"""
def id_file(varname):
if varname[1] == "_":
return (varname[2:], varname[0])
if varname[-2] == "_":
return (varname[:-2], varname[-1])
return (varname, "both")
ret = {}
for groupname, group in statGroups.items():
ret[groupname] = {}
for var, val in group.items():
justvar, file = id_file(var)
#sys.stderr.write("{0} -> {1} {2}\n".format(var, justvar, file))
if justvar not in ret[groupname]:
ret[groupname][justvar] = { 'a':'', 'b':'', 'both':''}
ret[groupname][justvar][file] = val
return ret
(no author)
committed
def generate_and_save_variable_report(files,
variableRunInfo, # contains variable specific run information
generalRunInfo, # contains run information not related to the variable
statGroups,
imageNames,
outputPath, reportFileName,
variableAttrs={ },
(no author)
committed
) :
(no author)
committed
"""
given two files and information about the comparison of one of their variables,
generate an html report about that variable and store it the outputPath/reportFileName
provided
(no author)
committed
(no author)
committed
statGroups is a dictionary in the form
statGroups['stat group display name'] = {a dictionary of stats/values to show}
ie. there may be many different groups of stats that should each be displayed
(no author)
committed
generalRunInfo is a dictionary in the form
(no author)
committed
generalRunInfo = {
MACHINE_INFO_KEY: currentMachine,
USER_INFO_KEY: currentUser,
GLANCE_VERSION_INFO_KEY: a version string describing glance,
LATITUDE_NAME_KEY: latitudeName,
LONGITUDE_NAME_KEY: longitudeName,
LAT_ALT_NAME_IN_B_KEY: latitudeNameInB, # optional, if not defined, B's using the normal latitude
LON_ALT_NAME_IN_B_KEY: longitudeNameInB, # optional, if not defined, B's using the normal longitude
DO_MAKE_IMAGES_KEY: shouldIncludeImages,
SHORT_CIRCUIT_DIFFS_KEY: if the diff related information should be shown
}
(no author)
committed
variableRunInfo is a dictionary in the form
(no author)
committed
variableRunInfo = {
VARIABLE_TECH_NAME_KEY: variableName,
EPSILON_KEY: epsilon,
FILL_VALUE_KEY: missingDataValue,
DISPLAY_NAME_KEY: displayName
DID_VARIABLE_PASS_KEY: boolean value or None, # optional, boolean means it did or did not pass, None means it was
# not qualitatively tested against a set of tolerances
(no author)
committed
TIME_INFO_KEY: currentTime
(no author)
committed
}
(no author)
committed
files is a dictionary in the form
(no author)
committed
files = {A_FILE_TITLE_KEY:
{
PATH_KEY: fileAName,
#DISPLAY_NAME_KEY: fileADisplayName, # TODO
LAST_MODIFIED_KEY: lastModifiedTimeA,
MD5SUM_KEY: aMD5SUM
},
B_FILE_TITLE_KEY:
{
PATH_KEY: fileBName,
#DISPLAY_NAME_KEY: fileADisplayName, # TODO
LAST_MODIFIED_KEY: lastModifiedTimeB,
MD5SUM_KEY: bMD5SUM
(no author)
committed
}
}
spatial is a dictionary in the form
spatial = {
(no author)
committed
A_FILE_TITLE_KEY:
{
NUMBER_INVALID_PTS_KEY: number of spatially invalid points only in A (and not corrspondingly in B),
PERCENT_INVALID_PTS_KEY: percent of spatially invalid points in A (out of all pts in A)
},
B_FILE_TITLE_KEY:
{
NUMBER_INVALID_PTS_KEY: number of spatially invalid points only in B (and not corrspondingly in A),
PERCENT_INVALID_PTS_KEY: percent of spatially invalid points in B (out of all pts in B)
},
PERCENT_INV_PTS_SHARED_KEY: the percent of points that are spatially invalid in at least one of the two files,
imageNames is a dictionary in the form
imageNames = {
(no author)
committed
ORIGINAL_IMAGES_KEY : [list, of, file-names, of, original, images],
COMPARED_IMAGES_KEY : [list, of, file-names, of, compared, images]
}
note: the assumption will be made that smaller versions of these images exist
in the form small.filename
any of the first level of keys in spatial are optional,
(no author)
committed
although it is assumed that if you include an entry for A_FILE_TITLE_KEY or
B_FILE_TITLE_KEY it will have both of the expected keys in its dictionary
(ie. both NUMBER_INVALID_PTS_KEY and PERCENT_INVALID_PTS_KEY)
variableAttrs should be a dictionary of variable attributes for the files, in the form
variableAttrs = {
A_FILE_TITLE_KEY:
{
<AttrName>: attr value,
},
B_FILE_TITLE_KEY:
{
<AttrName>: attr value,
},
}
(no author)
committed
"""
(no author)
committed
# pack up all the data for a report on a particular variable
# information about the run in general
(no author)
committed
runInfo = generalRunInfo.copy()
runInfo.update(variableRunInfo)
groupedStats = group_stats_by_file(statGroups)
(no author)
committed
(no author)
committed
# put all the info together in the kwargs
(no author)
committed
kwargs = {
RUN_INFO_DICT_KEY: runInfo,
FILES_INFO_DICT_KEY : files,
STATS_INFO_DICT_KEY: statGroups,
GROUPED_STATS_INFO_DICT_KEY: groupedStats,
(no author)
committed
SPATIAL_INFO_DICT_KEY: spatial,
IMAGE_NAME_INFO_DICT_KEY: imageNames,
ATTRS_INFO_DICT_KEY: variableAttrs,
(no author)
committed
}
(no author)
committed
(no author)
committed
_make_and_save_page((outputPath + "/" + reportFileName), 'variablereport.txt', **kwargs)
(no author)
committed
return
def generate_and_save_inspect_variable_report(files,
variableRunInfo, # contains variable specific run information
generalRunInfo, # contains run information not related to the variable
statGroups,
spatial,
imageNames,
outputPath, reportFileName,
variableAttrs={ },
) :
"""
given a file and information about one of the variables in that file,
generate an html report about that variable and store it the outputPath/reportFileName provided
statGroups is a dictionary in the form
statGroups['stat group display name'] = {a dictionary of stats/values to show}
ie. there may be many different groups of stats that should each be displayed
generalRunInfo is a dictionary in the form
(no author)
committed
generalRunInfo = {
MACHINE_INFO_KEY: currentMachine,
USER_INFO_KEY: currentUser,
GLANCE_VERSION_INFO_KEY: a version string describing glance,
LATITUDE_NAME_KEY: latitudeName,
LONGITUDE_NAME_KEY: longitudeName,
DO_MAKE_IMAGES_KEY: shouldIncludeImages,
}
variableRunInfo is a dictionary in the form
(no author)
committed
variableRunInfo = { VARIABLE_TECH_NAME_KEY: variableName,
FILL_VALUE_KEY: missingDataValue,
DISPLAY_NAME_KEY: displayName,
TIME_INFO_KEY: currentTime
}
files is a dictionary in the form
(no author)
committed
files = {
A_FILE_TITLE_KEY:
{
PATH_KEY: fileAName,
#DISPLAY_NAME_KEY: fileADisplayName, # TODO
LAST_MODIFIED_KEY: lastModifiedTimeA,
MD5SUM_KEY: aMD5SUM
},
}
spatial is a dictionary in the form
spatial = {
(no author)
committed
NUMBER_INVALID_PTS_KEY: number of spatially invalid points only in A (and not corrspondingly in B),
PERCENT_INVALID_PTS_KEY: percent of spatially invalid points in A (out of all pts in A)
}
imageNames is a dictionary in the form
imageNames = {
(no author)
committed
ORIGINAL_IMAGES_KEY : [list, of, file-names, of, original, images],
COMPARED_IMAGES_KEY : [list, of, file-names, of, analyzed, images]
}
note: the assumption will be made that smaller versions of these images exist
in the form small.filename
variableAttrs should be a dictionary of variable attributes for the file, in the form
variableAttrs = {
A_FILE_TITLE_KEY:
{
<AttrName>: attr value,
},
}
"""
# pack up all the data for a report on a particular variable
# information about the run in general
runInfo = generalRunInfo.copy()
runInfo.update(variableRunInfo)
# put all the info together in the kwargs
(no author)
committed
kwargs = {
RUN_INFO_DICT_KEY: runInfo,
FILES_INFO_DICT_KEY : files,
STATS_INFO_DICT_KEY: statGroups,
SPATIAL_INFO_DICT_KEY: spatial,
IMAGE_NAME_INFO_DICT_KEY: imageNames,
ATTRS_INFO_DICT_KEY: variableAttrs,
}
_make_and_save_page((outputPath + "/" + reportFileName), 'inspectvariablereport.txt', **kwargs)
return
def generate_and_save_inspection_summary_report(files,
outputPath, reportFileName,
runInfo,
variables,
spatial={},
varNames={},
globalAttrs={},) :
given a file, and information about it, save a summary report about the file
The summary report, in html format will be saved to the given outputPath/outputFile
Variables should be a dictionary keyed on the name of each variable and containing the
% of data values that were judged to be "similar enough" between file A and B (according
to the epsilon originally inputed for the comparison) and the epsilon used for the comparison
(no author)
committed
variables[var_name] = {
PASSED_EPSILON_PERCENT_KEY: percent "similar enough" according to epsilon,
VARIABLE_RUN_INFO_KEY: variable Run Info <- as defined for a variable report
}
more keys can be added in the future if more detailed data reporting is desired on the main report page
runInfo should be information about the run in the form
(no author)
committed
runInfo = {
MACHINE_INFO_KEY: currentMachine,
USER_INFO_KEY: currentUser,
TIME_INFO_KEY: currentTime,
GLANCE_VERSION_INFO_KEY: a version string describing glance,
LATITUDE_NAME_KEY: latitudeName,
LONGITUDE_NAME_KEY: longitudeName,
DO_MAKE_IMAGES_KEY: shouldIncludeImages, # this key/value is optional, defaults to True
}
files is a dictionary in the form
(no author)
committed
files = {
A_FILE_TITLE_KEY:
{
PATH_KEY: fileAName,
#DISPLAY_NAME_KEY: fileADisplayName, # TODO
LAST_MODIFIED_KEY: lastModifiedTimeA,
MD5SUM_KEY: aMD5SUM
},
}
spatial is a dictionary in the form
spatial = {
(no author)
committed
NUMBER_INVALID_PTS_KEY: number of spatially invalid points only in A (and not corrspondingly in B),
PERCENT_INVALID_PTS_KEY: percent of spatially invalid points in A (out of all pts in A)
}
varNames should be information about the variables present in the file, in the form
more information may be added to this dictionary in the future
(no author)
committed
varNames = {
POSSIBLE_NAMES_KEY: listOfAllVarsInFile,
globalAttrs should be a dictionary of global attributes for the file, in the form
globalAttrs = {
A_FILE_TITLE_KEY:
{
<AttrName>: attr value,
},
}
"""
# pack up all the data needed to build the summary report
# TODO, more automated defaults
(no author)
committed
varNameDefaults = { POSSIBLE_NAMES_KEY: [ ],}
varNamesToUse = varNameDefaults
varNamesToUse.update(varNames)
# build the full kwargs with all the info
(no author)
committed
kwargs = {
RUN_INFO_DICT_KEY: runInfo,
FILES_INFO_DICT_KEY: files,
SPATIAL_INFO_DICT_KEY: spatial,
VARIABLE_NAMES_DICT_KEY: varNamesToUse,
VARIABLE_RUN_INFO_DICT_KEY: variables,
ATTRS_INFO_DICT_KEY: globalAttrs,
}
_make_and_save_page((outputPath + "/" + reportFileName), 'inspectmainreport.txt', **kwargs)
# copy the original configuration file, TODO should I move this to a list input in the parameters?
(no author)
committed
if (CONFIG_FILE_PATH_KEY in runInfo) :
originalConfigFile = runInfo[CONFIG_FILE_PATH_KEY]
shutil.copy(originalConfigFile, outputPath)
return