Commit c9662b1e authored by Alexandre Boeglin's avatar Alexandre Boeglin

* _isTreeDelivered method moved from Rule to AppliedRule and

  SimulationMovement
* added transactional variable based cache to _isTreeDelivered
* activate and flush _isTreeDelivered cache in SimulationMovement.expand and
  AppliedRule.expand (so that it's only active when calling expand, and
  flushed afterwards, to prevent issues if we call expand in the same
  transaction as a builder)
* modified SimulationMovement.expand behaviour, so that it removes applied
  rules that no longer test(), and add new ones, if no movment in the applied
  rule sub tree is linked to a delivery


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@17756 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 20d7370b
...@@ -31,9 +31,13 @@ from Products.CMFCore.utils import getToolByName ...@@ -31,9 +31,13 @@ from Products.CMFCore.utils import getToolByName
from Products.ERP5Type import Permissions, PropertySheet, Constraint, Interface from Products.ERP5Type import Permissions, PropertySheet, Constraint, Interface
from Products.ERP5Type.XMLObject import XMLObject from Products.ERP5Type.XMLObject import XMLObject
from Products.ERP5Type.PsycoWrapper import psyco from Products.ERP5Type.PsycoWrapper import psyco
from Products.ERP5Type.TransactionalVariable import getTransactionalVariable
from zLOG import LOG from zLOG import LOG
TREE_DELIVERED_CACHE_KEY = 'AppliedRule._isTreeDelivered_cache'
TREE_DELIVERED_CACHE_ENABLED = 'TREE_DELIVERED_CACHE_ENABLED'
class AppliedRule(XMLObject): class AppliedRule(XMLObject):
""" """
An applied rule holds a list of simulation movements. An applied rule holds a list of simulation movements.
...@@ -89,6 +93,14 @@ class AppliedRule(XMLObject): ...@@ -89,6 +93,14 @@ class AppliedRule(XMLObject):
An applied rule can be expanded only if its parent movement An applied rule can be expanded only if its parent movement
is expanded. is expanded.
""" """
tv = getTransactionalVariable(self)
cache = tv.setdefault(TREE_DELIVERED_CACHE_KEY, {})
cache_enabled = cache.get(TREE_DELIVERED_CACHE_ENABLED, 0)
# enable cache
if not cache_enabled:
cache[TREE_DELIVERED_CACHE_ENABLED] = 1
rule = self.getSpecialiseValue() rule = self.getSpecialiseValue()
if rule is not None: if rule is not None:
if self.isRootAppliedRule(): if self.isRootAppliedRule():
...@@ -102,6 +114,13 @@ class AppliedRule(XMLObject): ...@@ -102,6 +114,13 @@ class AppliedRule(XMLObject):
# "recursiveImmediateReindexObject"]).\ # "recursiveImmediateReindexObject"]).\
# notifySimulationChange(rule._v_notify_dict) # notifySimulationChange(rule._v_notify_dict)
# disable and clear cache
if not cache_enabled:
try:
del tv[TREE_DELIVERED_CACHE_KEY]
except KeyError:
pass
security.declareProtected(Permissions.ModifyPortalContent, 'solve') security.declareProtected(Permissions.ModifyPortalContent, 'solve')
def solve(self, solution_list): def solve(self, solution_list):
""" """
...@@ -193,3 +212,33 @@ class AppliedRule(XMLObject): ...@@ -193,3 +212,33 @@ class AppliedRule(XMLObject):
delivery_url) delivery_url)
else: else:
delivery_value.notifySimulationChange() delivery_value.notifySimulationChange()
def _isTreeDelivered(self):
"""
Checks if submovements of this applied rule (going down the complete
simulation tree) have a delivery relation.
Returns True if at least one is delivered, False if none of them are.
see SimulationMovement._isTreeDelivered
"""
tv = getTransactionalVariable(self)
cache = tv.setdefault(TREE_DELIVERED_CACHE_KEY, {})
cache_enabled = cache.get(TREE_DELIVERED_CACHE_ENABLED, 0)
def getTreeDelivered(applied_rule):
for movement in applied_rule.objectValues():
if movement._isTreeDelivered():
return True
return False
rule_key = self.getRelativeUrl()
if cache_enabled:
try:
return cache[rule_key]
except:
result = getTreeDelivered(self)
cache[rule_key] = result
return result
else:
return getTreeDelivered(self)
...@@ -85,7 +85,7 @@ class DeliveryRule(Rule): ...@@ -85,7 +85,7 @@ class DeliveryRule(Rule):
(delivery.getPortalDraftOrderStateList() + \ (delivery.getPortalDraftOrderStateList() + \
delivery.getPortalPlannedOrderStateList()): delivery.getPortalPlannedOrderStateList()):
movement_delivery = movement.getDeliveryValue() movement_delivery = movement.getDeliveryValue()
if not self._isTreeDelivered([movement], ignore_first=1) and \ if not movement._isTreeDelivered(ignore_first=1) and \
movement_delivery not in delivery_movement_list: movement_delivery not in delivery_movement_list:
applied_rule._delObject(movement.getId()) applied_rule._delObject(movement.getId())
else: else:
...@@ -103,7 +103,7 @@ class DeliveryRule(Rule): ...@@ -103,7 +103,7 @@ class DeliveryRule(Rule):
# We are on a line # We are on a line
new_id = deliv_mvt.getId() new_id = deliv_mvt.getId()
else: else:
# Weare on a cell # We are on a cell
new_id = "%s_%s" % (deliv_mvt.getParentId(), deliv_mvt.getId()) new_id = "%s_%s" % (deliv_mvt.getParentId(), deliv_mvt.getId())
# Generate the simulation deliv_mvt # Generate the simulation deliv_mvt
# XXX Hardcoded value # XXX Hardcoded value
......
...@@ -86,7 +86,7 @@ class OrderRule(DeliveryRule): ...@@ -86,7 +86,7 @@ class OrderRule(DeliveryRule):
order.getPortalReservedInventoryStateList() and order.getPortalReservedInventoryStateList() and
not movement.getLastExpandSimulationState() in not movement.getLastExpandSimulationState() in
order.getPortalCurrentInventoryStateList()) and \ order.getPortalCurrentInventoryStateList()) and \
not self._isTreeDelivered([movement]): not movement._isTreeDelivered():
movement_order = movement.getOrderValue() movement_order = movement.getOrderValue()
if movement_order in order_movement_list: if movement_order in order_movement_list:
......
...@@ -220,21 +220,6 @@ class Rule(Predicate, XMLObject): ...@@ -220,21 +220,6 @@ class Rule(Predicate, XMLObject):
return 1 return 1
#### Helpers #### Helpers
def _isTreeDelivered(self, movement_list, ignore_first=0):
"""
returns 1 if the movement or any of its child is linked to a delivery
"""
child_movement_list = []
for movement in movement_list:
if not ignore_first and len(movement.getDeliveryList()) > 0:
return 1
else:
for applied_rule in movement.objectValues():
child_movement_list = applied_rule.objectValues()
if len(child_movement_list) == 0:
return 0
return self._isTreeDelivered(child_movement_list)
def _getCurrentMovementList(self, applied_rule, **kw): def _getCurrentMovementList(self, applied_rule, **kw):
""" """
Returns the list of current children of the applied rule, sorted in 3 Returns the list of current children of the applied rule, sorted in 3
...@@ -263,7 +248,7 @@ class Rule(Predicate, XMLObject): ...@@ -263,7 +248,7 @@ class Rule(Predicate, XMLObject):
if movement.isFrozen(): if movement.isFrozen():
immutable_movement_list.append(movement) immutable_movement_list.append(movement)
else: else:
if self._isTreeDelivered([movement]): if movement._isTreeDelivered():
mutable_movement_list.append(movement) mutable_movement_list.append(movement)
else: else:
deletable_movement_list.append(movement) deletable_movement_list.append(movement)
......
...@@ -31,6 +31,7 @@ from AccessControl import ClassSecurityInfo ...@@ -31,6 +31,7 @@ from AccessControl import ClassSecurityInfo
from Products.CMFCore.utils import getToolByName from Products.CMFCore.utils import getToolByName
from Products.ERP5Type import Permissions, PropertySheet, Constraint, Interface from Products.ERP5Type import Permissions, PropertySheet, Constraint, Interface
from Products.ERP5Type.TransactionalVariable import getTransactionalVariable
from Products.ERP5.Document.Movement import Movement from Products.ERP5.Document.Movement import Movement
...@@ -38,6 +39,8 @@ from zLOG import LOG ...@@ -38,6 +39,8 @@ from zLOG import LOG
from Acquisition import aq_base from Acquisition import aq_base
from AppliedRule import TREE_DELIVERED_CACHE_KEY, TREE_DELIVERED_CACHE_ENABLED
# XXX Do we need to create groups ? (ie. confirm group include confirmed, getting_ready and ready # XXX Do we need to create groups ? (ie. confirm group include confirmed, getting_ready and ready
parent_to_movement_simulation_state = { parent_to_movement_simulation_state = {
...@@ -209,36 +212,57 @@ class SimulationMovement(Movement): ...@@ -209,36 +212,57 @@ class SimulationMovement(Movement):
security.declareProtected(Permissions.ModifyPortalContent, 'expand') security.declareProtected(Permissions.ModifyPortalContent, 'expand')
def expand(self, force=0, **kw): def expand(self, force=0, **kw):
""" """
Parses all existing applied rules and make sure they apply. Checks all existing applied rules and make sure they still apply.
Checks other possible rules and starts expansion process Checks for other possible rules and starts expansion process (instanciates
(instanciates rule and calls expand on rule) applied rules and calls expand on them).
Only movements which applied rule parent is expanded can First get all applicable rules,
be expanded. then, delete all applied rules that no longer match and are not linked to
""" a delivery,
# XXX Default behaviour is not to expand if it has already been finally, apply new rules if no rule with the same type is already applied.
# expanded, but some rules are configuration rules and need to be """
# reexpanded each time, because the rule apply only if predicates
# are true, then this kind of rule must always be tested. Currently,
# we know that invoicing rule acts like this, and that it comes after
# invoice or invoicing_rule, so we if we come from invoince rule or
# invoicing rule, we always expand regardless of the causality state.
if ((self.getParentValue().getSpecialiseReference() not in
('default_invoicing_rule', 'default_invoice_rule')
and self.getCausalityState() == 'expanded' ) or \
len(self.objectIds()) != 0):
# Reexpand
for my_applied_rule in self.objectValues():
my_applied_rule.expand(force=force,**kw)
else:
portal_rules = getToolByName(self, 'portal_rules') portal_rules = getToolByName(self, 'portal_rules')
# Parse each rule and test if it applies
for rule in portal_rules.searchRuleList(self): tv = getTransactionalVariable(self)
rule.constructNewAppliedRule(self, **kw) cache = tv.setdefault(TREE_DELIVERED_CACHE_KEY, {})
for my_applied_rule in self.objectValues() : cache_enabled = cache.get(TREE_DELIVERED_CACHE_ENABLED, 0)
my_applied_rule.expand(force=force,**kw)
# Set to expanded # enable cache
if not cache_enabled:
cache[TREE_DELIVERED_CACHE_ENABLED] = 1
applied_rule_dict = {}
applicable_rule_dict = {}
for rule in portal_rules.searchRuleList(self, sort_on='version',
sort_order='descending'):
ref = rule.getReference()
if ref and ref not in applicable_rule_dict.iterkeys():
applicable_rule_dict[ref] = rule
for applied_rule in self.objectValues():
rule = applied_rule.getSpecialiseValue()
if not applied_rule._isTreeDelivered() and not rule.test(self):
self._delObject(applied_rule.getId())
else:
applied_rule_dict[rule.getPortalType()] = applied_rule
for rule in applicable_rule_dict.itervalues():
rule_type = rule.getPortalType()
if rule_type not in applied_rule_dict.iterkeys():
applied_rule = rule.constructNewAppliedRule(self, **kw)
applied_rule_dict[rule_type] = applied_rule
self.setCausalityState('expanded') self.setCausalityState('expanded')
# expand
for applied_rule in applied_rule_dict.itervalues():
applied_rule.expand(force=force, **kw)
# disable and clear cache
if not cache_enabled:
try:
del tv[TREE_DELIVERED_CACHE_KEY]
except KeyError:
pass
security.declareProtected(Permissions.ModifyPortalContent, 'diverge') security.declareProtected(Permissions.ModifyPortalContent, 'diverge')
def diverge(self): def diverge(self):
...@@ -480,3 +504,38 @@ class SimulationMovement(Movement): ...@@ -480,3 +504,38 @@ class SimulationMovement(Movement):
# 'recursiveImmediateReindexObject'])) # 'recursiveImmediateReindexObject']))
# activity.edit() # activity.edit()
def _isTreeDelivered(self, ignore_first=0):
"""
checks if subapplied rules of this movement (going down the complete
simulation tree) have a child with a delivery relation.
Returns True if at least one is delivered, False if none of them are.
see AppliedRule._isTreeDelivered
"""
tv = getTransactionalVariable(self)
cache = tv.setdefault(TREE_DELIVERED_CACHE_KEY, {})
cache_enabled = cache.get(TREE_DELIVERED_CACHE_ENABLED, 0)
def getTreeDelivered(movement, ignore_first=0):
if ignore_first:
if len(movement.getDeliveryList()) > 0:
return True
for applied_rule in movement.objectValues():
if applied_rule._isTreeDelivered():
return True
return False
if ignore_first:
rule_key = (self.getRelativeUrl(), 1)
else:
rule_key = self.getRelativeUrl()
if cache_enabled:
try:
return cache[rule_key]
except:
result = getTreeDelivered(self, ignore_first=ignore_first)
cache[rule_key] = result
return result
else:
return getTreeDelivered(self, ignore_first=ignore_first)
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