configurationengine/source/scripts/conesub_compare.py
changeset 0 2e8eeb919028
child 3 e7e0ae78773e
equal deleted inserted replaced
-1:000000000000 0:2e8eeb919028
       
     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 sys
       
    19 import logging
       
    20 from optparse import OptionParser, OptionGroup
       
    21 import codecs
       
    22 
       
    23 import cone_common
       
    24 import time
       
    25 
       
    26 from cone.public import api, plugin, utils, exceptions
       
    27 from time import gmtime, strftime
       
    28 import report_util
       
    29 ROOT_PATH = os.path.dirname(os.path.abspath(__file__))
       
    30 
       
    31 VERSION = '1.0'
       
    32 
       
    33 log = logging.getLogger('cone')
       
    34 
       
    35 REPORT_SHORTCUTS = {
       
    36     'api': report_util.ReportShortcut(
       
    37         os.path.join(ROOT_PATH, 'compare_api_report_template.html'),
       
    38         'api_comparison.html',
       
    39         'Report changes in feature definitions'),
       
    40                        
       
    41     'data': report_util.ReportShortcut(
       
    42         os.path.join(ROOT_PATH, 'compare_data_report_template.html'),
       
    43         'data_comparison.html',
       
    44         'Report changes in data values'),
       
    45                        
       
    46     'crml_dc': report_util.ReportShortcut(
       
    47         os.path.join(ROOT_PATH, 'crml_dc_report_template.html'),
       
    48         'crml_dc_report.html',
       
    49         'Report CRML data compatibility issues'),
       
    50                        
       
    51     'crml_dc_csv': report_util.ReportShortcut(
       
    52         os.path.join(ROOT_PATH, 'crml_dc_report_template.csv'),
       
    53         'crml_dc_report.csv',
       
    54         'Report CRML data compatibility issues (CSV format)'),
       
    55 }
       
    56 DEFAULT_SHORTCUT = 'data'
       
    57 
       
    58 def main():
       
    59     shortcut_container = report_util.ReportShortcutContainer(REPORT_SHORTCUTS,
       
    60                                                              DEFAULT_SHORTCUT)
       
    61     
       
    62     gset = cone_common.get_settings([os.path.join(ROOT_PATH,'conesub_compare.cfg')])
       
    63 
       
    64     parser = OptionParser(version="%%prog %s" % VERSION)
       
    65     
       
    66     parser.add_options(cone_common.COMMON_OPTIONS)
       
    67     
       
    68     parser.add_option("-p", "--project",\
       
    69                        dest="project",\
       
    70                        help="defines the location of current project. Default is the current working directory.",\
       
    71                        default=".",\
       
    72                        metavar="STORAGE")
       
    73     
       
    74     group = OptionGroup(parser, 'Compare options',
       
    75                     'The generate function will create target files from a specific configuration.'\
       
    76                     'The generate will always work with read-only mode of the project, so no changes are saved to project')
       
    77     
       
    78     group.add_option("-s", "--sourceconfiguration",\
       
    79                         dest="sourceconfiguration",\
       
    80                         help="defines the name of the sourceconfiguration for the compare action. "\
       
    81                              "The configuration is expected to be located current storage.",\
       
    82                         metavar="CONFIG")
       
    83     
       
    84     group.add_option("-t", "--targetconfiguration",\
       
    85                         dest="targetconfiguration",\
       
    86                         help="defines the name of the target configuration for the compare action. "\
       
    87                              "The configuration can be located in the current storage or it the configuration"\
       
    88                              "definition can contain a path to a storage. The storage definition is given as a path"\
       
    89                              "before semicolon. e.g. x:\data\configproject;productx.confml, test.cpf;root.confml",\
       
    90                         metavar="CONFIG")
       
    91 
       
    92 #    group.add_option("--compare-dict",\
       
    93 #                   dest="compare_dict",\
       
    94 #                   action="store",
       
    95 #                   type="string",
       
    96 #                   help="Compare elements as a dictionary",
       
    97 #                   metavar="DICT",\
       
    98 #                   default=None)
       
    99 
       
   100     group.add_option("--report",\
       
   101                    dest="report_file",\
       
   102                    action="store",
       
   103                    type="string",
       
   104                    help="The file where the comparison report is written."\
       
   105                         "By default this value is determined by the used "\
       
   106                         "report type. Example: --report report.html.",
       
   107                    metavar="FILE",\
       
   108                    default=None)
       
   109 
       
   110     group.add_option("--template",\
       
   111                    dest="template",\
       
   112                    action="store",
       
   113                    type="string",
       
   114                    help="Template used in a report generation. By default "\
       
   115                         "this value is determined by the used report type. "\
       
   116                         "Example: --template report_template.html.",
       
   117                    metavar="FILE",\
       
   118                    default=None)
       
   119     
       
   120     group.add_option("--report-type",
       
   121                    dest="report_type",
       
   122                    action="store",
       
   123                    type="string",
       
   124                    help="The type of the report to generate. This is a convenience "\
       
   125                         "switch for setting the used template.                     "\
       
   126                         "Possible values:\n                                      "\
       
   127                         + shortcut_container.get_shortcut_help_text(),
       
   128                    metavar="TYPE",\
       
   129                    default=None)
       
   130     
       
   131     group.add_option("--impl-filter",\
       
   132                    dest="impl_filter",\
       
   133                    action="store",
       
   134                    type="string",
       
   135                    help="The pattern used for filtering implementations for the "\
       
   136                         "comparison. See the switch --impl in action generate for "\
       
   137                         "more info. ",
       
   138                    metavar="PATTERN",\
       
   139                    default=None)
       
   140     
       
   141     start_time = time.time()
       
   142     
       
   143     parser.add_option_group(group)
       
   144     (options, args) = parser.parse_args()
       
   145     
       
   146     cone_common.handle_common_options(options, settings=gset)
       
   147     
       
   148     if not options.sourceconfiguration: parser.error("sourceconfiguration must be given")
       
   149     if not options.targetconfiguration: parser.error("targetconfiguration must be given")
       
   150     if options.report_type and options.template:
       
   151         parser.error("both --report-type and --template supplied; use only one of them")
       
   152     if not shortcut_container.is_valid_shortcut(options.report_type):
       
   153         parser.error("Invalid report type: %s" % options.report_type)
       
   154     
       
   155     template_file, report_file = shortcut_container.determine_template_and_report(
       
   156         options.report_type,
       
   157         options.template,
       
   158         options.report_file,
       
   159         'comparison')
       
   160     
       
   161     action = CompareAction(options.project,
       
   162                            options.sourceconfiguration,
       
   163                            options.targetconfiguration,
       
   164                            template    = template_file,
       
   165                            report_file = report_file,
       
   166                            impl_filter = options.impl_filter)
       
   167     result = action.run_action()
       
   168     
       
   169     resmap = {True: 0, False: 1}
       
   170     sys.exit(resmap[result])
       
   171 
       
   172 
       
   173 
       
   174 class CompareAction(object):
       
   175     
       
   176     def __init__(self, current, sourceconfig, targetconfig, **kwargs):
       
   177         self.current = current
       
   178         self.sourceconfig = sourceconfig
       
   179         self.targetconfig = targetconfig
       
   180         self.reportfile = kwargs.get("report_file", 'compare.html')
       
   181         self.reporttemplate = kwargs.get('template', '')
       
   182         self.columns = kwargs.get('columns', None)
       
   183         self.impl_filter = kwargs.get('impl_filter',)
       
   184 
       
   185     def run_action(self):
       
   186         """
       
   187         Run the action.
       
   188         @return: True if successful, False if not.
       
   189         """
       
   190         
       
   191         currentprj = api.Project(api.Storage.open(self.current,"r"))
       
   192         targetprj = None
       
   193         (targetproject,targetconf) = self.parse_target_configuration(self.targetconfig)
       
   194         print "Compare %s <> %s in %s" % (self.sourceconfig, targetconf, targetproject)
       
   195         if targetproject != '':
       
   196             targetprj = api.Project(api.Storage.open(targetproject,"r"))
       
   197         else:
       
   198             # The comparison doesn't seem to work if the same project
       
   199             #    object is used
       
   200             #targetprj = currentprj
       
   201             targetprj = api.Project(api.Storage.open(self.current,"r"))
       
   202             
       
   203         source = currentprj.get_configuration(self.sourceconfig)
       
   204         target = targetprj.get_configuration(targetconf)
       
   205 
       
   206         print "Writing report to %s" % self.reportfile
       
   207         sourcedata = {}
       
   208         targetdata = {}
       
   209         sourcedata['name'] = self.sourceconfig
       
   210         targetdata['name'] = self.targetconfig
       
   211         sourcedata['features'] = self.get_feature_api_data(source)
       
   212         targetdata['features'] = self.get_feature_api_data(target)
       
   213         
       
   214         impl_comp_data_proxy = ImplComparisonDataProxy(source,
       
   215                                                        target,
       
   216                                                        self.impl_filter)
       
   217         
       
   218         template_data = {'sourcedata': sourcedata,
       
   219                          'targetdata': targetdata,
       
   220                          'impl_data':  impl_comp_data_proxy}
       
   221         
       
   222         result = report_util.generate_report(self.reporttemplate,
       
   223                                              self.reportfile,
       
   224                                              {'data': template_data})
       
   225         print "Done."
       
   226         return result
       
   227         
       
   228     def parse_target_configuration(self,configpath):
       
   229         """
       
   230         return tuple (storage, configpath) from storagepath;root.confml.
       
   231         returns ('', 'root.confml') from root.confml 
       
   232         """
       
   233         elems = configpath.rsplit(';',2)
       
   234         if len(elems) > 1:
       
   235             return (elems[0],elems[1])
       
   236         else:
       
   237             return ('',elems[0])
       
   238 
       
   239     def get_feature_api_data(self,config):
       
   240         # Traverse through all features in the api
       
   241         # and construct the data rows
       
   242         data = {}
       
   243         for elem in config.get_default_view().get_features('**'):
       
   244             data[elem.fqr] = elem._obj
       
   245         return data
       
   246 
       
   247 class ImplComparisonDataProxy(object):
       
   248     """
       
   249     Proxy object for loading implementation comparison data on demand.
       
   250     """
       
   251     def __init__(self, sourceconfig, targetconfig, impl_filter):
       
   252         self.sourceconfig = sourceconfig
       
   253         self.targetconfig = targetconfig
       
   254         if impl_filter is None: self.impl_filter = '.*'
       
   255         else:                   self.impl_filter = impl_filter
       
   256         
       
   257         self._flat_data = None
       
   258     
       
   259     @property
       
   260     def flat(self):
       
   261         try:
       
   262             if self._flat_data is None:
       
   263                 self._flat_data = self._get_flat_comparison_data()
       
   264             return self._flat_data
       
   265         except Exception, e:
       
   266             utils.log_exception(log, 'Error retrieving ImplComparisonDataProxy.flat!')
       
   267             raise
       
   268     
       
   269     def _get_flat_comparison_data(self):
       
   270         log.debug("Loading implementations for comparison (impl filter = '%s')..." % (self.impl_filter))
       
   271         
       
   272         try:
       
   273             source_impls = plugin.get_impl_set(self.sourceconfig, self.impl_filter)
       
   274             target_impls = plugin.get_impl_set(self.targetconfig, self.impl_filter)
       
   275         except Exception, e:
       
   276             utils.log_exception(log, 'Failed to load implementations!')
       
   277             raise
       
   278         
       
   279         log.debug("%d impl(s) in source." % len(source_impls))
       
   280         log.debug("%d impl(s) in target." % len(target_impls))
       
   281         
       
   282         log.debug("Generating flat comparison results...")
       
   283         result = source_impls.flat_compare(target_impls)
       
   284         log.debug("Generated %d result row(s)" % len(result))
       
   285         return result
       
   286 
       
   287 if __name__ == "__main__":
       
   288     main()