configurationengine/source/cone/report/report_util.py
changeset 3 e7e0ae78773e
equal deleted inserted replaced
2:87cfa131b535 3:e7e0ae78773e
       
     1 #
       
     2 # Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     3 # All rights reserved.
       
     4 # This component and the accompanying materials are made available
       
     5 # under the terms of "Eclipse Public License v1.0"
       
     6 # which accompanies this distribution, and is available
       
     7 # at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 #
       
     9 # Initial Contributors:
       
    10 # Nokia Corporation - initial contribution.
       
    11 #
       
    12 # Contributors:
       
    13 #
       
    14 # Description:
       
    15 #
       
    16 
       
    17 import os
       
    18 import urllib
       
    19 import logging
       
    20 from jinja2 import Environment, FileSystemLoader
       
    21 from cone.public import utils
       
    22 
       
    23 ROOT_PATH = os.path.abspath(os.path.dirname(__file__))
       
    24 
       
    25 log = logging.getLogger('cone.report_util')
       
    26 
       
    27 class ReportShortcut(object):
       
    28     def __init__(self, template_file, report_file, description):
       
    29         self.template_file = template_file
       
    30         self.report_file = report_file
       
    31         self.description = description
       
    32 
       
    33 class ReportShortcutContainer(object):
       
    34     """
       
    35     Container for report shortcuts.
       
    36     
       
    37     A report shortcut describes a pre-defined report option that
       
    38     has a default template, report file and description. The shortcut
       
    39     container holds a set of shortcuts and can be used to generate
       
    40     
       
    41     """
       
    42     def __init__(self, shortcuts, default_shortcut):
       
    43         """
       
    44         @param shortcuts: The shortcuts, a dictionary mapping shortcut
       
    45             names to ReportShortcut objects.
       
    46         @param default_shortcut: The default shortcut name to use
       
    47         """
       
    48         if not isinstance(shortcuts, dict):
       
    49             raise ValueError("'shortcuts' must a dictionary (%s given)!" % type(shortcuts))
       
    50         if default_shortcut is not None and default_shortcut not in shortcuts:
       
    51             raise ValueError("'default_shortcut' must be either None or exist ing 'shortcuts'!")
       
    52         self.shortcuts = shortcuts
       
    53         self.default_shortcut = default_shortcut
       
    54     
       
    55     def get_shortcut_help_text(self):
       
    56         """
       
    57         Create the text to append to the option description for the
       
    58         option used to specify the used shortcut.
       
    59         """
       
    60         shortcuts_text = []
       
    61         refs = sorted(self.shortcuts.keys())
       
    62         if refs:
       
    63             for ref in refs:
       
    64                 sc = self.shortcuts[ref]
       
    65                 text = "%s - %s" % (ref, sc.description)
       
    66                 COLUMN_WIDTH = (80 - 25)
       
    67                 space_count = COLUMN_WIDTH - (len(text) % COLUMN_WIDTH)
       
    68                 shortcuts_text.append(text + space_count * ' ')
       
    69         else:
       
    70             shortcuts_text.append("None")
       
    71         shortcuts_text = '\n'.join(shortcuts_text)
       
    72         return shortcuts_text
       
    73     
       
    74     def is_valid_shortcut(self, shortcut):
       
    75         """
       
    76         Return whether the given shortcut is valid within the context
       
    77         of this container.
       
    78         """
       
    79         return shortcut is None or shortcut in self.shortcuts
       
    80     
       
    81     def determine_template_and_report(self, shortcut, template_file, report_file, report_file_name_without_ext):
       
    82         """
       
    83         Determine the actual template and report files based on the shortcuts
       
    84         and given options.
       
    85         @param shortcut: The used shortcut or None.
       
    86         @param template_file: Explicitly given template file or None.
       
    87         @param report_file: Explicitly given report file or None.
       
    88         @param report_file_name_without_ext: Prefix used to determine the
       
    89             report file name if the template was explicitly given, but the
       
    90             report file was not. E.g. if this is 'test' and the explicitly
       
    91             given template file is 'my_template.html', the report file would
       
    92             be 'test.html'.
       
    93         @return: Tuple (template_file, report_file) specifying the actual
       
    94             template and report files.
       
    95         """
       
    96         actual_shortcut      = None
       
    97         actual_template_file = None
       
    98         actual_report_file   = None
       
    99         
       
   100         # Handle report shortcut (set to default or check the given one)
       
   101         if not shortcut:
       
   102             actual_shortcut = self.default_shortcut
       
   103         else:
       
   104             actual_shortcut = shortcut
       
   105         
       
   106         # Determine template file
       
   107         if template_file:
       
   108             actual_template_file = template_file
       
   109         else:
       
   110             actual_template_file = self.shortcuts[actual_shortcut].template_file
       
   111         
       
   112         # Determine report output file
       
   113         if report_file:
       
   114             actual_report_file = report_file
       
   115         else:
       
   116             if template_file:
       
   117                 # Determine report file name based on the template file name
       
   118                 # if the template was explicitly given
       
   119                 actual_report_file = report_file_name_without_ext + os.path.splitext(template_file)[1]
       
   120             else:
       
   121                 actual_report_file = self.shortcuts[actual_shortcut].report_file
       
   122         
       
   123         return actual_template_file, actual_report_file
       
   124 
       
   125 def generate_report(template_file, report_file, report_data, template_paths=[], extra_filters={}):
       
   126     """
       
   127     Generate a report based on the given template file, report file
       
   128     and data dictionary.
       
   129     @param template_file: Path to the template file to use.
       
   130     @param report_file: Path to the output report file.
       
   131     @param report_data: The report data dictionary used when rendering
       
   132         the report from the template.
       
   133     @param template_paths: the additional search paths for templates. The default location cone.report is 
       
   134     always included.   
       
   135     @return: True if successful, False if not.
       
   136     """
       
   137     template_paths.insert(0, ROOT_PATH)
       
   138     template_paths.insert(0, os.path.dirname(template_file))
       
   139     template_paths = utils.distinct_array(template_paths)
       
   140     log.debug('generate_report(template_file=%r, report_file=%r, <data>, template_paths=%s)' % (template_file, report_file, template_paths))
       
   141     if not isinstance(report_data, dict):
       
   142         raise ValueError("report_data must be a dictionary!")
       
   143     
       
   144     try:
       
   145         template_file = os.path.abspath(template_file)
       
   146         
       
   147         loader = FileSystemLoader(template_paths)
       
   148         env = Environment(loader=loader)
       
   149         set_filters(env,extra_filters)
       
   150         
       
   151         template = env.get_template(os.path.basename(template_file))
       
   152         file_string = template.render(report_data)
       
   153         
       
   154         # Create directories for the report
       
   155         report_dir = os.path.dirname(report_file)
       
   156         if report_dir != '' and not os.path.exists(report_dir):
       
   157             os.makedirs(report_dir)
       
   158         
       
   159         # Write the rendered report to file
       
   160         f = open(report_file, 'wb')
       
   161         try:        f.write(file_string.encode('utf-8'))
       
   162         finally:    f.close()
       
   163         
       
   164         print "Generated report to '%s'" % report_file
       
   165         return True
       
   166     except Exception, e:
       
   167         utils.log_exception(log, "Failed to generate report: %s %s" % (type(e), e))
       
   168         return False
       
   169 
       
   170 def _set_default_filters(env):
       
   171     """
       
   172     Set default filters to the given Jinja environment
       
   173     """
       
   174     env.filters['xml_charref_replace'] = lambda value: unicode(value).encode('ascii', 'xmlcharrefreplace')
       
   175     env.filters['pathname_to_url'] = lambda value: urllib.pathname2url(value)
       
   176     env.filters['csv_escape'] = _csv_escape
       
   177     env.filters['csv_escape_partial'] = lambda value: unicode(value).replace('"', '""')
       
   178 
       
   179 def set_filters(env, filters={}):
       
   180     """
       
   181     First set the default filters and then possible extra filters from filters dict
       
   182     @param filters: the filters dictionary where the key is the filter name and value the method pointer
       
   183      
       
   184     """
       
   185     _set_default_filters(env)
       
   186     for name, filter in filters.iteritems():
       
   187         env.filters[name]=filter
       
   188 
       
   189 def _csv_escape(value):
       
   190     """
       
   191     Escape a string value so that it can be used as a field in a CSV file.
       
   192     """
       
   193     value = unicode(value)
       
   194     
       
   195     needs_quoting = False
       
   196     for special_char in '",\n':
       
   197         if special_char in value:
       
   198             needs_quoting = True
       
   199     
       
   200     if needs_quoting:
       
   201         if '"' in value:
       
   202             value = value.replace('"', '""')
       
   203         value = '"' + value + '"'
       
   204     
       
   205     return value