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, Template |
|
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): |
|
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 @return: True if successful, False if not. |
|
134 """ |
|
135 log.debug('generate_report(template_file=%r, report_file=%r, <data>)' % (template_file, report_file)) |
|
136 if not isinstance(report_data, dict): |
|
137 raise ValueError("report_data must be a dictionary!") |
|
138 |
|
139 try: |
|
140 template_file = os.path.abspath(template_file) |
|
141 |
|
142 loader = FileSystemLoader([os.path.dirname(template_file), ROOT_PATH]) |
|
143 env = Environment(loader=loader) |
|
144 _set_default_filters(env) |
|
145 |
|
146 template = env.get_template(os.path.basename(template_file)) |
|
147 file_string = template.render(report_data) |
|
148 |
|
149 # Create directories for the report |
|
150 report_dir = os.path.dirname(report_file) |
|
151 if report_dir != '' and not os.path.exists(report_dir): |
|
152 os.makedirs(report_dir) |
|
153 |
|
154 # Write the rendered report to file |
|
155 f = open(report_file, 'wb') |
|
156 try: f.write(file_string.encode('utf-8')) |
|
157 finally: f.close() |
|
158 |
|
159 print "Generated report to '%s'" % report_file |
|
160 return True |
|
161 except Exception, e: |
|
162 utils.log_exception(log, "Failed to generate report: %s %s" % (type(e), e)) |
|
163 return False |
|
164 |
|
165 def _set_default_filters(env): |
|
166 """ |
|
167 Set default filters to the given Jinja environment |
|
168 """ |
|
169 env.filters['xml_charref_replace'] = lambda value: unicode(value).encode('ascii', 'xmlcharrefreplace') |
|
170 env.filters['pathname_to_url'] = lambda value: urllib.pathname2url(value) |
|
171 env.filters['csv_escape'] = _csv_escape |
|
172 env.filters['csv_escape_partial'] = lambda value: unicode(value).replace('"', '""') |
|
173 |
|
174 def _csv_escape(value): |
|
175 """ |
|
176 Escape a string value so that it can be used as a field in a CSV file. |
|
177 """ |
|
178 value = unicode(value) |
|
179 |
|
180 needs_quoting = False |
|
181 for special_char in '",\n': |
|
182 if special_char in value: |
|
183 needs_quoting = True |
|
184 |
|
185 if needs_quoting: |
|
186 if '"' in value: |
|
187 value = value.replace('"', '""') |
|
188 value = '"' + value + '"' |
|
189 |
|
190 return value |
|