diff -r 000000000000 -r 2e8eeb919028 configurationengine/source/scripts/conesub_compare.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/configurationengine/source/scripts/conesub_compare.py Thu Mar 11 17:04:37 2010 +0200 @@ -0,0 +1,288 @@ +# +# 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 +import sys +import logging +from optparse import OptionParser, OptionGroup +import codecs + +import cone_common +import time + +from cone.public import api, plugin, utils, exceptions +from time import gmtime, strftime +import report_util +ROOT_PATH = os.path.dirname(os.path.abspath(__file__)) + +VERSION = '1.0' + +log = logging.getLogger('cone') + +REPORT_SHORTCUTS = { + 'api': report_util.ReportShortcut( + os.path.join(ROOT_PATH, 'compare_api_report_template.html'), + 'api_comparison.html', + 'Report changes in feature definitions'), + + 'data': report_util.ReportShortcut( + os.path.join(ROOT_PATH, 'compare_data_report_template.html'), + 'data_comparison.html', + 'Report changes in data values'), + + 'crml_dc': report_util.ReportShortcut( + os.path.join(ROOT_PATH, 'crml_dc_report_template.html'), + 'crml_dc_report.html', + 'Report CRML data compatibility issues'), + + 'crml_dc_csv': report_util.ReportShortcut( + os.path.join(ROOT_PATH, 'crml_dc_report_template.csv'), + 'crml_dc_report.csv', + 'Report CRML data compatibility issues (CSV format)'), +} +DEFAULT_SHORTCUT = 'data' + +def main(): + shortcut_container = report_util.ReportShortcutContainer(REPORT_SHORTCUTS, + DEFAULT_SHORTCUT) + + gset = cone_common.get_settings([os.path.join(ROOT_PATH,'conesub_compare.cfg')]) + + parser = OptionParser(version="%%prog %s" % VERSION) + + parser.add_options(cone_common.COMMON_OPTIONS) + + parser.add_option("-p", "--project",\ + dest="project",\ + help="defines the location of current project. Default is the current working directory.",\ + default=".",\ + metavar="STORAGE") + + group = OptionGroup(parser, 'Compare options', + 'The generate function will create target files from a specific configuration.'\ + 'The generate will always work with read-only mode of the project, so no changes are saved to project') + + group.add_option("-s", "--sourceconfiguration",\ + dest="sourceconfiguration",\ + help="defines the name of the sourceconfiguration for the compare action. "\ + "The configuration is expected to be located current storage.",\ + metavar="CONFIG") + + group.add_option("-t", "--targetconfiguration",\ + dest="targetconfiguration",\ + help="defines the name of the target configuration for the compare action. "\ + "The configuration can be located in the current storage or it the configuration"\ + "definition can contain a path to a storage. The storage definition is given as a path"\ + "before semicolon. e.g. x:\data\configproject;productx.confml, test.cpf;root.confml",\ + metavar="CONFIG") + +# group.add_option("--compare-dict",\ +# dest="compare_dict",\ +# action="store", +# type="string", +# help="Compare elements as a dictionary", +# metavar="DICT",\ +# default=None) + + group.add_option("--report",\ + dest="report_file",\ + action="store", + type="string", + help="The file where the comparison report is written."\ + "By default this value is determined by the used "\ + "report type. Example: --report report.html.", + metavar="FILE",\ + default=None) + + group.add_option("--template",\ + dest="template",\ + action="store", + type="string", + help="Template used in a report generation. By default "\ + "this value is determined by the used report type. "\ + "Example: --template report_template.html.", + metavar="FILE",\ + default=None) + + group.add_option("--report-type", + dest="report_type", + action="store", + type="string", + help="The type of the report to generate. This is a convenience "\ + "switch for setting the used template. "\ + "Possible values:\n "\ + + shortcut_container.get_shortcut_help_text(), + metavar="TYPE",\ + default=None) + + group.add_option("--impl-filter",\ + dest="impl_filter",\ + action="store", + type="string", + help="The pattern used for filtering implementations for the "\ + "comparison. See the switch --impl in action generate for "\ + "more info. ", + metavar="PATTERN",\ + default=None) + + start_time = time.time() + + parser.add_option_group(group) + (options, args) = parser.parse_args() + + cone_common.handle_common_options(options, settings=gset) + + if not options.sourceconfiguration: parser.error("sourceconfiguration must be given") + if not options.targetconfiguration: parser.error("targetconfiguration must be given") + if options.report_type and options.template: + parser.error("both --report-type and --template supplied; use only one of them") + if not shortcut_container.is_valid_shortcut(options.report_type): + parser.error("Invalid report type: %s" % options.report_type) + + template_file, report_file = shortcut_container.determine_template_and_report( + options.report_type, + options.template, + options.report_file, + 'comparison') + + action = CompareAction(options.project, + options.sourceconfiguration, + options.targetconfiguration, + template = template_file, + report_file = report_file, + impl_filter = options.impl_filter) + result = action.run_action() + + resmap = {True: 0, False: 1} + sys.exit(resmap[result]) + + + +class CompareAction(object): + + def __init__(self, current, sourceconfig, targetconfig, **kwargs): + self.current = current + self.sourceconfig = sourceconfig + self.targetconfig = targetconfig + self.reportfile = kwargs.get("report_file", 'compare.html') + self.reporttemplate = kwargs.get('template', '') + self.columns = kwargs.get('columns', None) + self.impl_filter = kwargs.get('impl_filter',) + + def run_action(self): + """ + Run the action. + @return: True if successful, False if not. + """ + + currentprj = api.Project(api.Storage.open(self.current,"r")) + targetprj = None + (targetproject,targetconf) = self.parse_target_configuration(self.targetconfig) + print "Compare %s <> %s in %s" % (self.sourceconfig, targetconf, targetproject) + if targetproject != '': + targetprj = api.Project(api.Storage.open(targetproject,"r")) + else: + # The comparison doesn't seem to work if the same project + # object is used + #targetprj = currentprj + targetprj = api.Project(api.Storage.open(self.current,"r")) + + source = currentprj.get_configuration(self.sourceconfig) + target = targetprj.get_configuration(targetconf) + + print "Writing report to %s" % self.reportfile + sourcedata = {} + targetdata = {} + sourcedata['name'] = self.sourceconfig + targetdata['name'] = self.targetconfig + sourcedata['features'] = self.get_feature_api_data(source) + targetdata['features'] = self.get_feature_api_data(target) + + impl_comp_data_proxy = ImplComparisonDataProxy(source, + target, + self.impl_filter) + + template_data = {'sourcedata': sourcedata, + 'targetdata': targetdata, + 'impl_data': impl_comp_data_proxy} + + result = report_util.generate_report(self.reporttemplate, + self.reportfile, + {'data': template_data}) + print "Done." + return result + + def parse_target_configuration(self,configpath): + """ + return tuple (storage, configpath) from storagepath;root.confml. + returns ('', 'root.confml') from root.confml + """ + elems = configpath.rsplit(';',2) + if len(elems) > 1: + return (elems[0],elems[1]) + else: + return ('',elems[0]) + + def get_feature_api_data(self,config): + # Traverse through all features in the api + # and construct the data rows + data = {} + for elem in config.get_default_view().get_features('**'): + data[elem.fqr] = elem._obj + return data + +class ImplComparisonDataProxy(object): + """ + Proxy object for loading implementation comparison data on demand. + """ + def __init__(self, sourceconfig, targetconfig, impl_filter): + self.sourceconfig = sourceconfig + self.targetconfig = targetconfig + if impl_filter is None: self.impl_filter = '.*' + else: self.impl_filter = impl_filter + + self._flat_data = None + + @property + def flat(self): + try: + if self._flat_data is None: + self._flat_data = self._get_flat_comparison_data() + return self._flat_data + except Exception, e: + utils.log_exception(log, 'Error retrieving ImplComparisonDataProxy.flat!') + raise + + def _get_flat_comparison_data(self): + log.debug("Loading implementations for comparison (impl filter = '%s')..." % (self.impl_filter)) + + try: + source_impls = plugin.get_impl_set(self.sourceconfig, self.impl_filter) + target_impls = plugin.get_impl_set(self.targetconfig, self.impl_filter) + except Exception, e: + utils.log_exception(log, 'Failed to load implementations!') + raise + + log.debug("%d impl(s) in source." % len(source_impls)) + log.debug("%d impl(s) in target." % len(target_impls)) + + log.debug("Generating flat comparison results...") + result = source_impls.flat_compare(target_impls) + log.debug("Generated %d result row(s)" % len(result)) + return result + +if __name__ == "__main__": + main()