diff -r 000000000000 -r 2e8eeb919028 configurationengine/source/scripts/generation_report.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/configurationengine/source/scripts/generation_report.py Thu Mar 11 17:04:37 2010 +0200 @@ -0,0 +1,315 @@ +# +# 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, logging, pickle +import time +from time import gmtime, strftime +from jinja2 import Environment, PackageLoader, FileSystemLoader, Template +from cone.public import api, exceptions, utils, plugin +from cone.confml import model +import report_util + +ROOT_PATH = os.path.dirname(os.path.abspath(__file__)) + +def save_report_data(rep_data, file_path): + """ + Save report data into an intermediary report data file. + """ + dir = os.path.dirname(file_path) + if dir != '' and not os.path.exists(dir): + os.makedirs(dir) + + pickle_data = pickle.dumps(rep_data) + f = open(file_path, 'wb') + try: f.write(pickle_data) + finally: f.close() + +def load_report_data(file_path): + """ + Load report data from an intermediary report data file. + """ + f = open(file_path, "rb") + try: data = f.read() + finally: f.close() + + return pickle.loads(data) + +def _get_parent_sequence_or_self(feature): + current = feature._parent + while current is not None: + if isinstance(current, api.FeatureSequence): + return current + current = current._parent + return feature + +def collect_report_data(config, options, all_refs, impl_set, rule_exec_results): + """ + Collect data for report generation. + """ + impls = impl_set.get_all_implementations() + impls.sort(key=lambda impl: (impl.ref, impl.index)) + rep_data = ReportData() + + # Sort the rule results for unit testing purposes + rep_data.rule_exec_results = sorted(rule_exec_results, key=lambda e: (e.source, e.index)) + + # Collect a dictionary that maps refs shown in the report to a list of + # actual sub-refs used in the generation. + # This is done because for sequence settings only an entry for the main + # sequence setting is shown, but the actual refs used in generation point + # to the individual sub-settings (and possibly even under them). + # An example entry in the dictionary could be + # 'MyFeature.MySequence' : ['MyFeature.MySequence.Name', + # 'MyFeature.MySequence.File.localPath', + # 'MyFeature.MySequence.File.targetPath'] + dview = config.get_default_view() + refs_dict = {} + for ref in all_refs: + try: + feature = dview.get_feature(ref) + except exceptions.NotFound: + logging.getLogger('cone.generation_report').warning("Feature for data ref not found: %s" % ref) + continue + + feature = _get_parent_sequence_or_self(feature._obj) + ref_to_report = feature.fqr + + if ref_to_report not in refs_dict: + refs_dict[ref_to_report] = [] + refs_dict[ref_to_report].append(ref) + +# msg = [] +# for ref, sub_refs in refs_dict.iteritems(): +# msg.append("Ref: %s\nSub-refs:\n%s" % (ref, '\n'.join(sub_refs))) +# logging.getLogger('cone').debug('\n--------------------------------------\n'.join(msg)) + + # Go through the refs and create report data entries + for ref in sorted(refs_dict.iterkeys()): + sub_refs = refs_dict[ref] + + #print "Ref: %s" % ref + try: + feat = dview.get_feature(ref) + + # Skip imaker-internal settings + if isinstance(feat._obj, model.ConfmlSetting): + try: + prop = feat.get_property('cone-report-ignore') + if prop.value.lower() in ('1', 'true'): + continue + except exceptions.NotFound: + pass + + #print "Still %s" % ref + + found_output = False + line = RefLine(ref, feat.get_type()) + + if plugin.is_temp_feature(feat): + line.is_temp_feature = True + + if feat.get_type() == 'sequence': + for f in feat.list_all_features(): + line.add_sequence(feat.get_feature(f).get_name(), feat.get_feature(f).get_value()) + else: + if isinstance(feat.get_datas(), list): + for d in feat.get_datas(): + line.add_data(d.find_parent(type=api.Configuration).get_full_path(), d.get_value()) + else: + line.add_data(feat.get_data().find_parent(type=api.Configuration).get_full_path(), feat.get_value()) + + + # Impl and output files + has_impl = False + for impl in impls: + # Check for implementations using the actual sub-refs + if impl.has_ref(sub_refs): + has_impl = True + line.add_impl(impl.ref, impl.IMPL_TYPE_ID, impl.list_output_files()) + if has_impl: rep_data.add_line(line) + else: rep_data.add_ref_noimpl(line) + + + # For localPath and targetPath, the name should be the one from its parent file/folder setting + if isinstance(feat._obj, (model.ConfmlLocalPath, model.ConfmlTargetPath)): + name = feat._obj._parent.name + else: + name = feat.name + + line.set_feat_name(name) + line.set_config_path( feat._obj.find_parent(type=api.Configuration).get_full_path()) + + except Exception, e: + utils.log_exception(logging.getLogger('cone'), 'Failed to collect data for report. Exception: %s' % e) + + # create one list of not generated files + for myline in rep_data.lines: + for myimpl in myline.impls: + for output_file in myimpl.outputfiles: + if not output_file.exists and output_file not in rep_data.missing_output_files: + rep_data.missing_output_files.append(output_file) + + rep_data.set_options(options) + rep_data.set_output_dir(options.output) + rep_data.update_nbr_of_refs() + rep_data.update_nbr_of_refs_noimpl() + + return rep_data + +def generate_report(rep_data, report_file_path, template_file_path=None): + """ + Generate a generation report based on the given report data. + @param rep_data: The report data. + @param report_file_path: Path to the report file to generate. + @param template_file_path: Path to the template file to use. + If None, the default template is used. + """ + # Determine the template file and directory to use + if template_file_path is None: + template_file_path = os.path.join(ROOT_PATH, 'gen_report_template.html') + + report_util.generate_report(template_file_path, report_file_path, {'rep_data' : rep_data}) + +class ReportData(object): + """ + Data object that stores all information used in report generation. + """ + + def __init__(self): + self.generation_timestamp = time.time() + self.generation_time = strftime("%d.%m.%Y %H:%M:%S") + self.options = None + self.lines = [] + self.nbr_of_refs = 0 + self.nbr_of_refs_noimpl = 0 + self.cwd = os.getcwd() + self.ref_noimpl = [] + self.duration = 0 + self.output_dir = os.getcwd() + self.project_dir = '' + self.rule_exec_results = [] + self.missing_output_files = [] + + def set_output_dir(self, dir): + self.output_dir = os.path.abspath(os.path.normpath(dir)) + + def add_line(self, line): + self.lines.append(line) + + def set_duration(self, duration): + self.duration = duration + + def set_options(self, options): + self.options = options + self.project_dir = os.path.abspath(options.project) + + def set_report_filename(self, filename): + self.report_filename = filename + + def add_ref_noimpl(self, ref): + self.ref_noimpl.append(ref) + + def update_nbr_of_refs(self): + self.nbr_of_refs = len(self.lines) + + def update_nbr_of_refs_noimpl(self): + self.nbr_of_refs_noimpl = len(self.ref_noimpl) + + def __repr__(self): + return "ReportData(%s)" % [self.generation_timestamp, + self.generation_time, + self.options, + self.lines, + self.nbr_of_refs, + self.nbr_of_refs_noimpl, + self.cwd, + self.ref_noimpl, + self.duration, + self.output_dir, + self.project_dir, + self.rule_exec_results, + self.missing_output_files] + +class RefLine(object): + """ + Data object that stores information for one ref in report generation. + """ + + def __init__(self, ref, type): + self.ref = ref + self.feat_type = type + self.feat_name = None + self.feat_value = None + self.config_path = None + self.impls = [] + self.output = None + self.nbr_impls = 0 + self.nbr_outputfiles = 0 + self.datas = [] + self.nbr_of_datas = 0 + self.nbr_of_rows = 0 + self.seq_data = [] + self.is_temp_feature = False + + def add_impl(self, impl_file, impl_type, outputfiles): + self.impls.append(ImplLine(impl_file, impl_type, outputfiles)) + self.nbr_impls = len(self.impls) + self.nbr_outputfiles = len(outputfiles) + self.nbr_outputfiles + + def add_data(self, layer, value): + self.datas.append(DataLine(layer,value)) + self.nbr_of_datas = len(self.datas) + + def add_sequence(self, subsetting, values): + self.seq_data.append([subsetting, values]) + + def set_feat_name(self, name): + self.feat_name = name + + def set_feat_value(self, value): + self.feat_value = value + + def set_config_path(self, filename): + self.config_path = os.path.normpath(filename) + +class ImplLine(): + def __init__(self, impl_file, impl_type, outputfiles, generation_runs=[]): + self.name = os.path.normpath(impl_file) + self.type = impl_type + files = [] + + for outputfile in outputfiles: + files.append(Outputfile(outputfile)) + + self.outputfiles = files + self.generation_runs = generation_runs + +class Outputfile(): + def __init__(self, filename): + self.filename = os.path.normpath(filename) + self.abs_filename = os.path.abspath(filename) + self.exists = os.path.isfile(self.abs_filename) + + def __eq__(self, other): + if type(self) is type(other): + return self.filename == other.filename + else: + return False + +class DataLine(): + def __init__(self, layer, value): + self.layer = os.path.normpath(layer) + self.value = value \ No newline at end of file