Commit 7025d24f authored by Jean-Paul Smets's avatar Jean-Paul Smets

Updated to support grouping even in very complex cases related to production...

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
parent 8a10405a
...@@ -82,64 +82,94 @@ class SolverProcess(XMLObject, ActiveProcess): ...@@ -82,64 +82,94 @@ class SolverProcess(XMLObject, ActiveProcess):
""" """
movement_dict = {} movement_dict = {}
types_tool = self.portal_types types_tool = self.portal_types
message_list = []
# First create a mapping between delivery movements and solvers # First create a mapping between delivery movements and solvers
# in order to know for each movements which solvers are needed # in order to know for each movements which solvers are needed
# and which parameters with # 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"): for decision in self.contentValues(portal_type="Solver Decision"):
solver = decision.getSolverValue() solver = decision.getSolverValue()
# do nothing if solver is not yet set. # do nothing if solver is not yet set.
if solver is None: if solver is None:
continue continue
solver_type = solver.getId() # ex. Postpone Production Solver
solver_conviguration_dict = decision.getConfigurationPropertyDict() 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(): for movement in decision.getDeliveryValueList():
# Detect incompatibilities # Detect incompatibilities
movement_solver_dict = movement_dict.setdefault(movement.getRelativeUrl(), {}) movement_solver_dict = movement_dict.setdefault(movement, {})
movement_solver_configuration_list = movement_solver_dict.setdefault(solver_type, []) movement_solver_configuration_list = movement_solver_dict.setdefault(solver, [])
if solver_conviguration_key not in movement_solver_configuration_list: if configuration_mapping not in movement_solver_configuration_list:
movement_solver_configuration_list.append(solver_conviguration_key) movement_solver_configuration_list.append(configuration_mapping)
# Second, make sure solvers do not conflict and configuration is valid # Second, create a mapping between solvers and movements
# Build a movement and configuration structure per solver type # and their configuration
#
# solver_dict[solver] = {
# movement : [((c1, v1), (c2, v2 )),
# ((c1, v1), (c2, v2 )),
# ],
# }
#
solver_dict = {} solver_dict = {}
for movement_url, movement_solver_dict in movement_dict.items(): for movement, movement_solver_dict in movement_dict.items():
for solver_type, movement_solver_configuration_list in movement_solver_dict.items(): for solver, movement_solver_configuration_list in movement_solver_dict.items():
solver_movement_dict = solver_dict.setdefault(solver_type, {}) solver_movement_dict = solver_dict.setdefault(solver, {})
configuration_list = solver_movement_dict.setdefault(movement_url, []) solver_movement_dict[movement] = movement_solver_configuration_list
configuration_list.extend(movement_solver_configuration_list) # XXX-JPS WRONG
# Then start the grouping procedure # Third, group solver configurations and make sure solvers do not conflict
solver_dict = {} # by creating a mapping between solvers and movement configuration grouped
for movement_url, movement_solver_dict in movement_dict.items(): # by a key which is used to aggregate multiple configurations
for solver_type, movement_solver_configuration_list in movement_solver_dict.items(): #
solver = types_tool[solver_type] # grouped_solver_dict[solver] = {
for other_solver_type in movement_solver_dict.keys(): # solver_key: {
if other_solver_type == solver_type: # movement : [((c1, v1), (c2, v2 )),
continue # ((c1, v1), (c2, v2 )),
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) # }
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 # Make sure multiple configuration are possible
try: try:
# Solver key contains only those properties which differentiate # Solver key contains only those properties which differentiate
# solvers (ex. there should be only Production Reduction Solver) # solvers (ex. there should be only Production Reduction Solver)
solver_key = solver.getSolverProcessGroupingKey(movement_url, movement_solver_configuration_list, movement_solver_dict) solver_key = solver.getSolverProcessGroupingKey(movement, configuration_mapping, solver_dict)
except: # Raise the exception generated by the solver in case of failure of grouping except: # Raise the exception generated by the solver in case of failure of grouping
raise raise
solver_key_dict = solver_dict.setdefault(solver_type, {}) solver_key_dict = grouped_solver_dict.setdefault(solver, {})
solver_movement_dict = solver_key_dict.setdefault(solver_key, {}) solver_movement_dict = solver_key_dict.setdefault(solver_key, {})
solver_movement_dict[movement_url] = movement_solver_configuration_list 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)
# Third, build target solvers # Return empty list of conflicts
for solver_type, solver_key_dict in solver_dict.items(): 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(): for solver_key, solver_movement_dict in solver_key_dict.items():
solver_instance = self.newContent(portal_type=solver_type) solver_instance = self.newContent(portal_type=solver.getId())
solver_instance._setDeliveryList(solver_movement_dict.keys()) solver_instance._setDeliveryValueList(solver_movement_dict.keys())
for movement_url, configuration_list in solver_movement_dict.iteritems(): for movement, configuration_list in solver_movement_dict.iteritems():
for configuration_kw in configuration_list: for configuration_mapping in configuration_list:
if len(configuration_kw): if len(configuration_mapping):
solver_instance.updateConfiguration(**dict(configuration_kw)) solver_instance.updateConfiguration(**dict(configuration_mapping))
# Return empty list of conflicts
return []
# ISolver implementation # ISolver implementation
# Solver Process Workflow Interface # Solver Process Workflow Interface
......
...@@ -52,31 +52,45 @@ class SolverTypeInformation(ERP5TypeInformation): ...@@ -52,31 +52,45 @@ class SolverTypeInformation(ERP5TypeInformation):
, PropertySheet.Configurable , 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_dict -- a dictionary of configuration parameters to
solve the current movement with self configuration_mapping -- a mapping of configuration parameters sorted in
canonical way. ((c1, v1), (c2, v2 ))
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: 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) # Default Implementation (use categories and trivial case)
for solver_type, configuration_dict in other_configuration_list: # this default implementation should be applicable to most
if solver.getTestedProperty() == self.getTestedProperty(): # solvers so that use of Type Based methods is very rare
return True 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 emtpty message list
return False 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 Returns a key which can be used to group solvers during the
process to build Targer Solver instances from Solver Decisions. process to build Targer Solver instances from Solver Decisions.
...@@ -98,25 +112,33 @@ class SolverTypeInformation(ERP5TypeInformation): ...@@ -98,25 +112,33 @@ class SolverTypeInformation(ERP5TypeInformation):
Adopt, Accept) which tested property is configurable, is the Adopt, Accept) which tested property is configurable, is the
tested property itself. tested property itself.
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 solver_dict -- a dictionary of configuration parameters for
each solver
solver_dict[solver] = {
movement : [((c1, v1), (c2, v2 )),
((c1, v1), (c2, v2 )),
],}
other_configuration_list -- a list of movements and their configuration movement_dict -- a dictionary of solver and configuration parameters for
which are solved by the same solve type. each movement
[(m1, c1), (m2, c2), ...] movement_dict[movement] = {
solver : [((c1, v1), (c2, v2 )),
((c1, v1), (c2, v2 )),
],}
""" """
method = self._getTypeBasedMethod('getSolverProcessGroupingKey') method = self._getTypeBasedMethod('getSolverProcessGroupingKey')
if method is not None: 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(): if self.isLineGroupable():
return () return ()
if isinstance(movement, str):
return movement
else:
return movement.getRelativeUrl() return movement.getRelativeUrl()
def getDefaultConfigurationPropertyDict(self, configurable): def getDefaultConfigurationPropertyDict(self, configurable):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment