From 7025d24fea20890c906c061b364b81d51979877d Mon Sep 17 00:00:00 2001 From: Jean-Paul Smets <jp@nexedi.com> Date: Tue, 23 Mar 2010 07:50:46 +0000 Subject: [PATCH] Updated to support grouping even in very complex cases related to production or in universal solver cases. git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@33979 20353a03-c40f-0410-a6d1-a30d3c3de9de --- product/ERP5/Document/SolverProcess.py | 116 +++++++++++------- .../ERP5/Document/SolverTypeInformation.py | 78 +++++++----- 2 files changed, 123 insertions(+), 71 deletions(-) diff --git a/product/ERP5/Document/SolverProcess.py b/product/ERP5/Document/SolverProcess.py index 4cd5a1ed34..3d6a9061ec 100644 --- a/product/ERP5/Document/SolverProcess.py +++ b/product/ERP5/Document/SolverProcess.py @@ -82,64 +82,94 @@ class SolverProcess(XMLObject, ActiveProcess): """ movement_dict = {} types_tool = self.portal_types + message_list = [] # First create a mapping between delivery movements and solvers # in order to know for each movements which solvers are needed # and which parameters with + # + # movement_dict[movement] = { + # solver : [((c1, v1), (c2, v2 )), + # ((c1, v1), (c2, v2 )), + # ], for decision in self.contentValues(portal_type="Solver Decision"): solver = decision.getSolverValue() # do nothing if solver is not yet set. if solver is None: continue - solver_type = solver.getId() # ex. Postpone Production Solver solver_conviguration_dict = decision.getConfigurationPropertyDict() - solver_conviguration_key = tuple(solver_conviguration_dict.items()) + configuration_mapping = solver_conviguration_dict.items() + configuration_mapping.sort() # Make sure the list is sorted in canonical way + configuration_mapping = tuple(configuration_mapping) for movement in decision.getDeliveryValueList(): # Detect incompatibilities - movement_solver_dict = movement_dict.setdefault(movement.getRelativeUrl(), {}) - movement_solver_configuration_list = movement_solver_dict.setdefault(solver_type, []) - if solver_conviguration_key not in movement_solver_configuration_list: - movement_solver_configuration_list.append(solver_conviguration_key) + movement_solver_dict = movement_dict.setdefault(movement, {}) + movement_solver_configuration_list = movement_solver_dict.setdefault(solver, []) + if configuration_mapping not in movement_solver_configuration_list: + movement_solver_configuration_list.append(configuration_mapping) - # Second, make sure solvers do not conflict and configuration is valid - # Build a movement and configuration structure per solver type + # Second, create a mapping between solvers and movements + # and their configuration + # + # solver_dict[solver] = { + # movement : [((c1, v1), (c2, v2 )), + # ((c1, v1), (c2, v2 )), + # ], + # } + # solver_dict = {} - for movement_url, movement_solver_dict in movement_dict.items(): - for solver_type, movement_solver_configuration_list in movement_solver_dict.items(): - solver_movement_dict = solver_dict.setdefault(solver_type, {}) - configuration_list = solver_movement_dict.setdefault(movement_url, []) - configuration_list.extend(movement_solver_configuration_list) # XXX-JPS WRONG - # Then start the grouping procedure - solver_dict = {} - for movement_url, movement_solver_dict in movement_dict.items(): - for solver_type, movement_solver_configuration_list in movement_solver_dict.items(): - solver = types_tool[solver_type] - for other_solver_type in movement_solver_dict.keys(): - if other_solver_type == solver_type: - continue - if solver.conflictsWithSolver(types_tool[other_solver_type]): - # XXX6PJS REDO HERE - raise ValueError, "Solver %s conflicts with solver %s on movement %s" % (solver_type, other_solver_type, movement_url) - # Make sure multiple configuration are possible - try: - # Solver key contains only those properties which differentiate - # solvers (ex. there should be only Production Reduction Solver) - solver_key = solver.getSolverProcessGroupingKey(movement_url, movement_solver_configuration_list, movement_solver_dict) - except: # Raise the exception generated by the solver in case of failure of grouping - raise - solver_key_dict = solver_dict.setdefault(solver_type, {}) - solver_movement_dict = solver_key_dict.setdefault(solver_key, {}) - solver_movement_dict[movement_url] = movement_solver_configuration_list - - # Third, build target solvers - for solver_type, solver_key_dict in solver_dict.items(): + for movement, movement_solver_dict in movement_dict.items(): + for solver, movement_solver_configuration_list in movement_solver_dict.items(): + solver_movement_dict = solver_dict.setdefault(solver, {}) + solver_movement_dict[movement] = movement_solver_configuration_list + + # Third, group solver configurations and make sure solvers do not conflict + # by creating a mapping between solvers and movement configuration grouped + # by a key which is used to aggregate multiple configurations + # + # grouped_solver_dict[solver] = { + # solver_key: { + # movement : [((c1, v1), (c2, v2 )), + # ((c1, v1), (c2, v2 )), + # ], + # } + # } + grouped_solver_dict = {} + for movement, movement_solver_dict in movement_dict.items(): + for solver, movement_solver_configuration_list in movement_solver_dict.items(): + for configuration_mapping in movement_solver_configuration_list: + solver_message_list = solver.getSolverConflictMessageList(movement, configuration_mapping, solver_dict) + if solver_message_list: + message_list.extend(solver_message_list) + continue # No need to keep on + # Make sure multiple configuration are possible + try: + # Solver key contains only those properties which differentiate + # solvers (ex. there should be only Production Reduction Solver) + solver_key = solver.getSolverProcessGroupingKey(movement, configuration_mapping, solver_dict) + except: # Raise the exception generated by the solver in case of failure of grouping + raise + solver_key_dict = grouped_solver_dict.setdefault(solver, {}) + solver_movement_dict = solver_key_dict.setdefault(solver_key, {}) + movement_solver_configuration_list = movement_solver_dict.setdefault(solver, []) + if configuration_mapping not in movement_solver_configuration_list: + movement_solver_configuration_list.append(configuration_mapping) + + # Return empty list of conflicts + if message_list: return message_list + + # Fourth, build target solvers + for solver, solver_key_dict in grouped_solver_dict.items(): for solver_key, solver_movement_dict in solver_key_dict.items(): - solver_instance = self.newContent(portal_type=solver_type) - solver_instance._setDeliveryList(solver_movement_dict.keys()) - for movement_url, configuration_list in solver_movement_dict.iteritems(): - for configuration_kw in configuration_list: - if len(configuration_kw): - solver_instance.updateConfiguration(**dict(configuration_kw)) + solver_instance = self.newContent(portal_type=solver.getId()) + solver_instance._setDeliveryValueList(solver_movement_dict.keys()) + for movement, configuration_list in solver_movement_dict.iteritems(): + for configuration_mapping in configuration_list: + if len(configuration_mapping): + solver_instance.updateConfiguration(**dict(configuration_mapping)) + + # Return empty list of conflicts + return [] # ISolver implementation # Solver Process Workflow Interface diff --git a/product/ERP5/Document/SolverTypeInformation.py b/product/ERP5/Document/SolverTypeInformation.py index 89b366bd25..dae03dc79b 100644 --- a/product/ERP5/Document/SolverTypeInformation.py +++ b/product/ERP5/Document/SolverTypeInformation.py @@ -52,31 +52,45 @@ class SolverTypeInformation(ERP5TypeInformation): , PropertySheet.Configurable ) - def conflictsWithSolver(self, movement, configuration_dict, other_configuration_list): + def getSolverConflictMessageList(self, movement, configuration_mapping, solver_dict, movement_dict): """ - Returns True if the solver conflicts with other_solver. False else. + Returns the list of conflictings messgaes if the solver and configuration_mapping + conflicts with another solver - movement -- a movement or a movement relative url + movement -- a movement + + configuration_mapping -- a mapping of configuration parameters sorted in + canonical way. ((c1, v1), (c2, v2 )) - configuration_dict -- a dictionary of configuration parameters to - solve the current movement with self - - other_configuration_list -- a list of solvers and their configuration - for the same movement + solver_dict -- a dictionary of configuration parameters for + each solver + solver_dict[solver] = { + movement : [((c1, v1), (c2, v2 )), + ((c1, v1), (c2, v2 )), + ],} + + movement_dict -- a dictionary of solver and configuration parameters for + each movement + movement_dict[movement] = { + solver : [((c1, v1), (c2, v2 )), + ((c1, v1), (c2, v2 )), + ],} """ - method = self._getTypeBasedMethod('conflictsWithSolver') + method = self._getTypeBasedMethod('getSolverConflictMessageList') if method is not None: - return method(movement, configuration_dict, other_configuration_list) + return method(movement, configuration_mapping, solver_dict, movement_dict) # Default Implementation (use categories and trivial case) - for solver_type, configuration_dict in other_configuration_list: - if solver.getTestedProperty() == self.getTestedProperty(): - return True + # this default implementation should be applicable to most + # solvers so that use of Type Based methods is very rare + for solver, configuration_list in movement_dict[movement].items(): + if solver is not self and solver.getTestedProperty() == self.getTestedProperty(): + return AppropriateUIMessage(whatever) # XXX-TODO - # Return False by Default - return False + # Return emtpty message list + return () - def getSolverProcessGroupingKey(self, movement, configuration_dict, other_configuration_list): + def getSolverProcessGroupingKey(self, movement, configuration_mapping, solver_dict, movement_dict): """ Returns a key which can be used to group solvers during the process to build Targer Solver instances from Solver Decisions. @@ -98,26 +112,34 @@ class SolverTypeInformation(ERP5TypeInformation): Adopt, Accept) which tested property is configurable, is the tested property itself. - movement -- a movement or a movement relative url - - configuration_dict -- a dictionary of configuration parameters + movement -- a movement - other_configuration_list -- a list of movements and their configuration - which are solved by the same solve type. - [(m1, c1), (m2, c2), ...] + configuration_mapping -- a mapping of configuration parameters sorted in + canonical way. ((c1, v1), (c2, v2 )) + + solver_dict -- a dictionary of configuration parameters for + each solver + solver_dict[solver] = { + movement : [((c1, v1), (c2, v2 )), + ((c1, v1), (c2, v2 )), + ],} + + movement_dict -- a dictionary of solver and configuration parameters for + each movement + movement_dict[movement] = { + solver : [((c1, v1), (c2, v2 )), + ((c1, v1), (c2, v2 )), + ],} """ method = self._getTypeBasedMethod('getSolverProcessGroupingKey') if method is not None: - return method(movement, configuration_dict, other_configuration_list) + return method(movement, configuration_mapping, solver_dict, movement_dict) - # Default Implementation (read properties and implement XXX) + # Default Implementation (read solver type properties and implement XXX-TODO) if self.isLineGroupable(): return () - if isinstance(movement, str): - return movement - else: - return movement.getRelativeUrl() + return movement.getRelativeUrl() def getDefaultConfigurationPropertyDict(self, configurable): """ -- 2.30.9