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, logging, pickle |
|
18 import time |
|
19 from time import gmtime, strftime |
|
20 from jinja2 import Environment, PackageLoader, FileSystemLoader, Template |
|
21 from cone.public import api, exceptions, utils, plugin |
|
22 from cone.confml import model |
|
23 import report_util |
|
24 |
|
25 ROOT_PATH = os.path.dirname(os.path.abspath(__file__)) |
|
26 |
|
27 def save_report_data(rep_data, file_path): |
|
28 """ |
|
29 Save report data into an intermediary report data file. |
|
30 """ |
|
31 dir = os.path.dirname(file_path) |
|
32 if dir != '' and not os.path.exists(dir): |
|
33 os.makedirs(dir) |
|
34 |
|
35 pickle_data = pickle.dumps(rep_data) |
|
36 f = open(file_path, 'wb') |
|
37 try: f.write(pickle_data) |
|
38 finally: f.close() |
|
39 |
|
40 def load_report_data(file_path): |
|
41 """ |
|
42 Load report data from an intermediary report data file. |
|
43 """ |
|
44 f = open(file_path, "rb") |
|
45 try: data = f.read() |
|
46 finally: f.close() |
|
47 |
|
48 return pickle.loads(data) |
|
49 |
|
50 def _get_parent_sequence_or_self(feature): |
|
51 current = feature._parent |
|
52 while current is not None: |
|
53 if isinstance(current, api.FeatureSequence): |
|
54 return current |
|
55 current = current._parent |
|
56 return feature |
|
57 |
|
58 def collect_report_data(config, options, all_refs, impl_set, rule_exec_results): |
|
59 """ |
|
60 Collect data for report generation. |
|
61 """ |
|
62 impls = impl_set.get_all_implementations() |
|
63 impls.sort(key=lambda impl: (impl.ref, impl.index)) |
|
64 rep_data = ReportData() |
|
65 |
|
66 # Sort the rule results for unit testing purposes |
|
67 rep_data.rule_exec_results = sorted(rule_exec_results, key=lambda e: (e.source, e.index)) |
|
68 |
|
69 # Collect a dictionary that maps refs shown in the report to a list of |
|
70 # actual sub-refs used in the generation. |
|
71 # This is done because for sequence settings only an entry for the main |
|
72 # sequence setting is shown, but the actual refs used in generation point |
|
73 # to the individual sub-settings (and possibly even under them). |
|
74 # An example entry in the dictionary could be |
|
75 # 'MyFeature.MySequence' : ['MyFeature.MySequence.Name', |
|
76 # 'MyFeature.MySequence.File.localPath', |
|
77 # 'MyFeature.MySequence.File.targetPath'] |
|
78 dview = config.get_default_view() |
|
79 refs_dict = {} |
|
80 for ref in all_refs: |
|
81 try: |
|
82 feature = dview.get_feature(ref) |
|
83 except exceptions.NotFound: |
|
84 logging.getLogger('cone.generation_report').warning("Feature for data ref not found: %s" % ref) |
|
85 continue |
|
86 |
|
87 feature = _get_parent_sequence_or_self(feature._obj) |
|
88 ref_to_report = feature.fqr |
|
89 |
|
90 if ref_to_report not in refs_dict: |
|
91 refs_dict[ref_to_report] = [] |
|
92 refs_dict[ref_to_report].append(ref) |
|
93 |
|
94 # msg = [] |
|
95 # for ref, sub_refs in refs_dict.iteritems(): |
|
96 # msg.append("Ref: %s\nSub-refs:\n%s" % (ref, '\n'.join(sub_refs))) |
|
97 # logging.getLogger('cone').debug('\n--------------------------------------\n'.join(msg)) |
|
98 |
|
99 # Go through the refs and create report data entries |
|
100 for ref in sorted(refs_dict.iterkeys()): |
|
101 sub_refs = refs_dict[ref] |
|
102 |
|
103 #print "Ref: %s" % ref |
|
104 try: |
|
105 feat = dview.get_feature(ref) |
|
106 |
|
107 # Skip imaker-internal settings |
|
108 if isinstance(feat._obj, model.ConfmlSetting): |
|
109 try: |
|
110 prop = feat.get_property('cone-report-ignore') |
|
111 if prop.value.lower() in ('1', 'true'): |
|
112 continue |
|
113 except exceptions.NotFound: |
|
114 pass |
|
115 |
|
116 #print "Still %s" % ref |
|
117 |
|
118 found_output = False |
|
119 line = RefLine(ref, feat.get_type()) |
|
120 |
|
121 if plugin.is_temp_feature(feat): |
|
122 line.is_temp_feature = True |
|
123 |
|
124 if feat.get_type() == 'sequence': |
|
125 for f in feat.list_all_features(): |
|
126 line.add_sequence(feat.get_feature(f).get_name(), feat.get_feature(f).get_value()) |
|
127 else: |
|
128 if isinstance(feat.get_datas(), list): |
|
129 for d in feat.get_datas(): |
|
130 line.add_data(d.find_parent(type=api.Configuration).get_full_path(), d.get_value()) |
|
131 else: |
|
132 line.add_data(feat.get_data().find_parent(type=api.Configuration).get_full_path(), feat.get_value()) |
|
133 |
|
134 |
|
135 # Impl and output files |
|
136 has_impl = False |
|
137 for impl in impls: |
|
138 # Check for implementations using the actual sub-refs |
|
139 if impl.has_ref(sub_refs): |
|
140 has_impl = True |
|
141 line.add_impl(impl.ref, impl.IMPL_TYPE_ID, impl.list_output_files()) |
|
142 if has_impl: rep_data.add_line(line) |
|
143 else: rep_data.add_ref_noimpl(line) |
|
144 |
|
145 |
|
146 # For localPath and targetPath, the name should be the one from its parent file/folder setting |
|
147 if isinstance(feat._obj, (model.ConfmlLocalPath, model.ConfmlTargetPath)): |
|
148 name = feat._obj._parent.name |
|
149 else: |
|
150 name = feat.name |
|
151 |
|
152 line.set_feat_name(name) |
|
153 line.set_config_path( feat._obj.find_parent(type=api.Configuration).get_full_path()) |
|
154 |
|
155 except Exception, e: |
|
156 utils.log_exception(logging.getLogger('cone'), 'Failed to collect data for report. Exception: %s' % e) |
|
157 |
|
158 # create one list of not generated files |
|
159 for myline in rep_data.lines: |
|
160 for myimpl in myline.impls: |
|
161 for output_file in myimpl.outputfiles: |
|
162 if not output_file.exists and output_file not in rep_data.missing_output_files: |
|
163 rep_data.missing_output_files.append(output_file) |
|
164 |
|
165 rep_data.set_options(options) |
|
166 rep_data.set_output_dir(options.output) |
|
167 rep_data.update_nbr_of_refs() |
|
168 rep_data.update_nbr_of_refs_noimpl() |
|
169 |
|
170 return rep_data |
|
171 |
|
172 def generate_report(rep_data, report_file_path, template_file_path=None): |
|
173 """ |
|
174 Generate a generation report based on the given report data. |
|
175 @param rep_data: The report data. |
|
176 @param report_file_path: Path to the report file to generate. |
|
177 @param template_file_path: Path to the template file to use. |
|
178 If None, the default template is used. |
|
179 """ |
|
180 # Determine the template file and directory to use |
|
181 if template_file_path is None: |
|
182 template_file_path = os.path.join(ROOT_PATH, 'gen_report_template.html') |
|
183 |
|
184 report_util.generate_report(template_file_path, report_file_path, {'rep_data' : rep_data}) |
|
185 |
|
186 class ReportData(object): |
|
187 """ |
|
188 Data object that stores all information used in report generation. |
|
189 """ |
|
190 |
|
191 def __init__(self): |
|
192 self.generation_timestamp = time.time() |
|
193 self.generation_time = strftime("%d.%m.%Y %H:%M:%S") |
|
194 self.options = None |
|
195 self.lines = [] |
|
196 self.nbr_of_refs = 0 |
|
197 self.nbr_of_refs_noimpl = 0 |
|
198 self.cwd = os.getcwd() |
|
199 self.ref_noimpl = [] |
|
200 self.duration = 0 |
|
201 self.output_dir = os.getcwd() |
|
202 self.project_dir = '' |
|
203 self.rule_exec_results = [] |
|
204 self.missing_output_files = [] |
|
205 |
|
206 def set_output_dir(self, dir): |
|
207 self.output_dir = os.path.abspath(os.path.normpath(dir)) |
|
208 |
|
209 def add_line(self, line): |
|
210 self.lines.append(line) |
|
211 |
|
212 def set_duration(self, duration): |
|
213 self.duration = duration |
|
214 |
|
215 def set_options(self, options): |
|
216 self.options = options |
|
217 self.project_dir = os.path.abspath(options.project) |
|
218 |
|
219 def set_report_filename(self, filename): |
|
220 self.report_filename = filename |
|
221 |
|
222 def add_ref_noimpl(self, ref): |
|
223 self.ref_noimpl.append(ref) |
|
224 |
|
225 def update_nbr_of_refs(self): |
|
226 self.nbr_of_refs = len(self.lines) |
|
227 |
|
228 def update_nbr_of_refs_noimpl(self): |
|
229 self.nbr_of_refs_noimpl = len(self.ref_noimpl) |
|
230 |
|
231 def __repr__(self): |
|
232 return "ReportData(%s)" % [self.generation_timestamp, |
|
233 self.generation_time, |
|
234 self.options, |
|
235 self.lines, |
|
236 self.nbr_of_refs, |
|
237 self.nbr_of_refs_noimpl, |
|
238 self.cwd, |
|
239 self.ref_noimpl, |
|
240 self.duration, |
|
241 self.output_dir, |
|
242 self.project_dir, |
|
243 self.rule_exec_results, |
|
244 self.missing_output_files] |
|
245 |
|
246 class RefLine(object): |
|
247 """ |
|
248 Data object that stores information for one ref in report generation. |
|
249 """ |
|
250 |
|
251 def __init__(self, ref, type): |
|
252 self.ref = ref |
|
253 self.feat_type = type |
|
254 self.feat_name = None |
|
255 self.feat_value = None |
|
256 self.config_path = None |
|
257 self.impls = [] |
|
258 self.output = None |
|
259 self.nbr_impls = 0 |
|
260 self.nbr_outputfiles = 0 |
|
261 self.datas = [] |
|
262 self.nbr_of_datas = 0 |
|
263 self.nbr_of_rows = 0 |
|
264 self.seq_data = [] |
|
265 self.is_temp_feature = False |
|
266 |
|
267 def add_impl(self, impl_file, impl_type, outputfiles): |
|
268 self.impls.append(ImplLine(impl_file, impl_type, outputfiles)) |
|
269 self.nbr_impls = len(self.impls) |
|
270 self.nbr_outputfiles = len(outputfiles) + self.nbr_outputfiles |
|
271 |
|
272 def add_data(self, layer, value): |
|
273 self.datas.append(DataLine(layer,value)) |
|
274 self.nbr_of_datas = len(self.datas) |
|
275 |
|
276 def add_sequence(self, subsetting, values): |
|
277 self.seq_data.append([subsetting, values]) |
|
278 |
|
279 def set_feat_name(self, name): |
|
280 self.feat_name = name |
|
281 |
|
282 def set_feat_value(self, value): |
|
283 self.feat_value = value |
|
284 |
|
285 def set_config_path(self, filename): |
|
286 self.config_path = os.path.normpath(filename) |
|
287 |
|
288 class ImplLine(): |
|
289 def __init__(self, impl_file, impl_type, outputfiles, generation_runs=[]): |
|
290 self.name = os.path.normpath(impl_file) |
|
291 self.type = impl_type |
|
292 files = [] |
|
293 |
|
294 for outputfile in outputfiles: |
|
295 files.append(Outputfile(outputfile)) |
|
296 |
|
297 self.outputfiles = files |
|
298 self.generation_runs = generation_runs |
|
299 |
|
300 class Outputfile(): |
|
301 def __init__(self, filename): |
|
302 self.filename = os.path.normpath(filename) |
|
303 self.abs_filename = os.path.abspath(filename) |
|
304 self.exists = os.path.isfile(self.abs_filename) |
|
305 |
|
306 def __eq__(self, other): |
|
307 if type(self) is type(other): |
|
308 return self.filename == other.filename |
|
309 else: |
|
310 return False |
|
311 |
|
312 class DataLine(): |
|
313 def __init__(self, layer, value): |
|
314 self.layer = os.path.normpath(layer) |
|
315 self.value = value |
|