|
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 logging |
|
19 import pickle |
|
20 from optparse import OptionParser, OptionGroup |
|
21 import cone_common |
|
22 import generation_report |
|
23 from cone.public import api, plugin, utils, exceptions |
|
24 |
|
25 |
|
26 VERSION = '1.0' |
|
27 |
|
28 logger = logging.getLogger('cone') |
|
29 |
|
30 def main(): |
|
31 parser = OptionParser(version="%%prog %s" % VERSION) |
|
32 |
|
33 parser.add_options(cone_common.COMMON_OPTIONS) |
|
34 |
|
35 group = OptionGroup(parser, 'Report options', |
|
36 'The report function generates a report using previously generated '\ |
|
37 'intermediary report data as input.') |
|
38 |
|
39 group.add_option("-i", "--input-data",\ |
|
40 action="append",\ |
|
41 help="Defines an input file for report generation. "\ |
|
42 "If specified more than once, the data of all specified "\ |
|
43 "report data files is merged.", |
|
44 metavar="FILE", |
|
45 default=[]) |
|
46 |
|
47 group.add_option("-d", "--input-data-dir",\ |
|
48 help="Defines a directory containing the data files to use for "\ |
|
49 "generating the report. This is an alternative to specifying "\ |
|
50 "a number of --input-data files. The order of the data files "\ |
|
51 "is determined by the generation time stamps found in the data "\ |
|
52 "files.", |
|
53 metavar="DIR", |
|
54 default=None) |
|
55 |
|
56 group.add_option("-r", "--report",\ |
|
57 dest="report",\ |
|
58 action="store", |
|
59 type="string", |
|
60 help="Specifies the report file to create."\ |
|
61 "Example -r report.html.", |
|
62 metavar="FILE",\ |
|
63 default="report.html") |
|
64 |
|
65 group.add_option("-t", "--template",\ |
|
66 dest="template",\ |
|
67 action="store", |
|
68 type="string", |
|
69 help="Template used in report generation."\ |
|
70 "Example -t report_template.html.", |
|
71 metavar="FILE",\ |
|
72 default=None) |
|
73 |
|
74 parser.add_option_group(group) |
|
75 (options, args) = parser.parse_args() |
|
76 |
|
77 cone_common.handle_common_options(options) |
|
78 |
|
79 if len(options.input_data) == 0 and not options.input_data_dir: |
|
80 parser.error("Input data must be specified with either --input-data or --input-data-dir") |
|
81 if len(options.input_data) > 0 and options.input_data_dir: |
|
82 parser.error("Both --input-data and --input-data-dir specified, use one or the other.") |
|
83 if options.input_data_dir and not os.path.isdir(options.input_data_dir): |
|
84 parser.error('Given --input-data-dir does not exist or is not a directory.') |
|
85 |
|
86 if options.input_data: |
|
87 files = options.input_data |
|
88 else: |
|
89 files = get_input_data_files(options.input_data_dir) |
|
90 |
|
91 if len(files) == 0: |
|
92 parser.error("At least one input data file must be specified.") |
|
93 |
|
94 |
|
95 class DataEntry(object): |
|
96 def __init__(self, label, data): |
|
97 self.label = label |
|
98 self.data = data |
|
99 |
|
100 # Load all data files |
|
101 data_entries = [] |
|
102 for data_file in files: |
|
103 print "Loading data file '%s'" % data_file |
|
104 label = get_generation_run_label(data_file) |
|
105 data = generation_report.load_report_data(data_file) |
|
106 data_entries.append(DataEntry(label, data)) |
|
107 |
|
108 # Sort by time stamp |
|
109 data_entries.sort(key=lambda entry: entry.data.generation_timestamp) |
|
110 |
|
111 # Use the first data object as the main report data |
|
112 main_entry = data_entries[0] |
|
113 |
|
114 # Merge the rest of the data objects into the main data |
|
115 if len(data_entries) > 1: |
|
116 # Update the generation_runs attribute of all implementations |
|
117 # in the main data |
|
118 for line in main_entry.data.lines: |
|
119 for impl in line.impls: |
|
120 impl.generation_runs = [main_entry.label] |
|
121 |
|
122 # Load other report data files and merge them to the main data object |
|
123 for i in xrange(len(data_entries) - 1): |
|
124 entry = data_entries[i + 1] |
|
125 print "Merging data for '%s'" % entry.label |
|
126 merge_report_data(main_entry.data, entry.data, entry.label) |
|
127 |
|
128 # Generate the report |
|
129 main_entry.data.report_filename = options.report |
|
130 generation_report.generate_report(main_entry.data, options.report, options.template) |
|
131 |
|
132 print "Generated report to '%s'" % options.report |
|
133 |
|
134 def get_input_data_files(directory): |
|
135 files = [] |
|
136 for name in os.listdir(directory): |
|
137 path = os.path.join(directory, name) |
|
138 if os.path.isfile(path): |
|
139 files.append(path) |
|
140 return files |
|
141 |
|
142 |
|
143 def get_generation_run_label(datafile_path): |
|
144 filename = os.path.split(datafile_path)[1] |
|
145 filename_noext = os.path.splitext(filename)[0] |
|
146 return filename_noext |
|
147 |
|
148 def get_feature(rep_data, ref): |
|
149 for feat in rep_data.lines: |
|
150 if feat.ref == ref: |
|
151 return feat |
|
152 raise RuntimeError("Feature '%s' not found in refs with impl" % ref) |
|
153 |
|
154 def get_impl(rep_data, ref, impl_name): |
|
155 feat = get_feature(rep_data, ref) |
|
156 for impl in feat.impls: |
|
157 if impl.name == impl_name: |
|
158 return impl |
|
159 raise RuntimeError("Impl '%s' not found for feature '%s'" % (impl_name, ref)) |
|
160 |
|
161 def merge_report_data(data, data_to_merge, generation_run_label): |
|
162 impls_by_ref = {} |
|
163 for feat in data.lines: |
|
164 impls_dict = {} |
|
165 impls_by_ref[feat.ref] = impls_dict |
|
166 for impl in feat.impls: |
|
167 impls_dict[impl.name] = impl |
|
168 |
|
169 for feat in data_to_merge.lines: |
|
170 if feat.ref in impls_by_ref: |
|
171 # Feature has implementations in both report data objects |
|
172 # ------------------------------------------------------- |
|
173 impls_dict = impls_by_ref[feat.ref] |
|
174 |
|
175 for impl in feat.impls: |
|
176 if impl.name in impls_dict: |
|
177 # Same implementation in both: add the generation run to merge to the impl |
|
178 impl = get_impl(data, feat.ref, impl.name) |
|
179 impl.generation_runs.append(generation_run_label) |
|
180 else: |
|
181 # Implementation only in the data to merge: add to the main data |
|
182 impl = get_impl(data_to_merge, feat.ref, impl.name) |
|
183 impl.generation_runs = [generation_run_label] |
|
184 feat = get_feature(data, feat.ref) |
|
185 feat.impls.append(impl) |
|
186 feat.nbr_impls += 1 |
|
187 else: |
|
188 # Feature has implementations only in the data to merge |
|
189 # ----------------------------------------------------- |
|
190 |
|
191 # Add the feature and impls to the main data |
|
192 feat = get_feature(data_to_merge, feat.ref) |
|
193 for impl in feat.impls: |
|
194 impl.generation_runs = [generation_run_label] |
|
195 data.lines.append(feat) |
|
196 data.nbr_of_refs += 1 |
|
197 |
|
198 # Remove from features with no impl in the main data |
|
199 for i, noimpl_feat in enumerate(data.ref_noimpl): |
|
200 if feat.ref == noimpl_feat.ref: |
|
201 del data.ref_noimpl[i] |
|
202 data.nbr_of_refs_noimpl -= 1 |
|
203 break |
|
204 |
|
205 if __name__ == "__main__": |
|
206 main() |