#
# Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
# All rights reserved.
# This component and the accompanying materials are made available
# under the terms of "Eclipse Public License v1.0"
# which accompanies this distribution, and is available
# at the URL "http://www.eclipse.org/legal/epl-v10.html".
#
# Initial Contributors:
# Nokia Corporation - initial contribution.
#
# Contributors:
#
# Description:
#
import os
import urllib
import logging
from jinja2 import Environment, FileSystemLoader
from cone.public import utils
ROOT_PATH = os.path.abspath(os.path.dirname(__file__))
log = logging.getLogger('cone.report_util')
class ReportShortcut(object):
def __init__(self, template_file, report_file, description):
self.template_file = template_file
self.report_file = report_file
self.description = description
class ReportShortcutContainer(object):
"""
Container for report shortcuts.
A report shortcut describes a pre-defined report option that
has a default template, report file and description. The shortcut
container holds a set of shortcuts and can be used to generate
"""
def __init__(self, shortcuts, default_shortcut):
"""
@param shortcuts: The shortcuts, a dictionary mapping shortcut
names to ReportShortcut objects.
@param default_shortcut: The default shortcut name to use
"""
if not isinstance(shortcuts, dict):
raise ValueError("'shortcuts' must a dictionary (%s given)!" % type(shortcuts))
if default_shortcut is not None and default_shortcut not in shortcuts:
raise ValueError("'default_shortcut' must be either None or exist ing 'shortcuts'!")
self.shortcuts = shortcuts
self.default_shortcut = default_shortcut
def get_shortcut_help_text(self):
"""
Create the text to append to the option description for the
option used to specify the used shortcut.
"""
shortcuts_text = []
refs = sorted(self.shortcuts.keys())
if refs:
for ref in refs:
sc = self.shortcuts[ref]
text = "%s - %s" % (ref, sc.description)
COLUMN_WIDTH = (80 - 25)
space_count = COLUMN_WIDTH - (len(text) % COLUMN_WIDTH)
shortcuts_text.append(text + space_count * ' ')
else:
shortcuts_text.append("None")
shortcuts_text = '\n'.join(shortcuts_text)
return shortcuts_text
def is_valid_shortcut(self, shortcut):
"""
Return whether the given shortcut is valid within the context
of this container.
"""
return shortcut is None or shortcut in self.shortcuts
def determine_template_and_report(self, shortcut, template_file, report_file, report_file_name_without_ext):
"""
Determine the actual template and report files based on the shortcuts
and given options.
@param shortcut: The used shortcut or None.
@param template_file: Explicitly given template file or None.
@param report_file: Explicitly given report file or None.
@param report_file_name_without_ext: Prefix used to determine the
report file name if the template was explicitly given, but the
report file was not. E.g. if this is 'test' and the explicitly
given template file is 'my_template.html', the report file would
be 'test.html'.
@return: Tuple (template_file, report_file) specifying the actual
template and report files.
"""
actual_shortcut = None
actual_template_file = None
actual_report_file = None
# Handle report shortcut (set to default or check the given one)
if not shortcut:
actual_shortcut = self.default_shortcut
else:
actual_shortcut = shortcut
# Determine template file
if template_file:
actual_template_file = template_file
else:
actual_template_file = self.shortcuts[actual_shortcut].template_file
# Determine report output file
if report_file:
actual_report_file = report_file
else:
if template_file:
# Determine report file name based on the template file name
# if the template was explicitly given
actual_report_file = report_file_name_without_ext + os.path.splitext(template_file)[1]
else:
actual_report_file = self.shortcuts[actual_shortcut].report_file
return actual_template_file, actual_report_file
def generate_report(template_file, report_file, report_data, template_paths=[], extra_filters={}):
"""
Generate a report based on the given template file, report file
and data dictionary.
@param template_file: Path to the template file to use.
@param report_file: Path to the output report file.
@param report_data: The report data dictionary used when rendering
the report from the template.
@param template_paths: the additional search paths for templates. The default location cone.report is
always included.
@return: True if successful, False if not.
"""
template_paths.insert(0, ROOT_PATH)
template_paths.insert(0, os.path.dirname(template_file))
template_paths = utils.distinct_array(template_paths)
log.debug('generate_report(template_file=%r, report_file=%r, <data>, template_paths=%s)' % (template_file, report_file, template_paths))
if not isinstance(report_data, dict):
raise ValueError("report_data must be a dictionary!")
try:
template_file = os.path.abspath(template_file)
loader = FileSystemLoader(template_paths)
env = Environment(loader=loader)
set_filters(env,extra_filters)
template = env.get_template(os.path.basename(template_file))
file_string = template.render(report_data)
# Create directories for the report
report_dir = os.path.dirname(report_file)
if report_dir != '' and not os.path.exists(report_dir):
os.makedirs(report_dir)
# Write the rendered report to file
f = open(report_file, 'wb')
try: f.write(file_string.encode('utf-8'))
finally: f.close()
print "Generated report to '%s'" % report_file
return True
except Exception, e:
utils.log_exception(log, "Failed to generate report: %s %s" % (type(e), e))
return False
def _set_default_filters(env):
"""
Set default filters to the given Jinja environment
"""
env.filters['xml_charref_replace'] = lambda value: unicode(value).encode('ascii', 'xmlcharrefreplace')
env.filters['pathname_to_url'] = lambda value: urllib.pathname2url(value)
env.filters['csv_escape'] = _csv_escape
env.filters['csv_escape_partial'] = lambda value: unicode(value).replace('"', '""')
def set_filters(env, filters={}):
"""
First set the default filters and then possible extra filters from filters dict
@param filters: the filters dictionary where the key is the filter name and value the method pointer
"""
_set_default_filters(env)
for name, filter in filters.iteritems():
env.filters[name]=filter
def _csv_escape(value):
"""
Escape a string value so that it can be used as a field in a CSV file.
"""
value = unicode(value)
needs_quoting = False
for special_char in '",\n':
if special_char in value:
needs_quoting = True
if needs_quoting:
if '"' in value:
value = value.replace('"', '""')
value = '"' + value + '"'
return value