Commit c528eb90 authored by Jérome Perrin's avatar Jérome Perrin

Inventory API: remove unecessary joins in the queries

We were joining catalog for section, node and resource, mainly in order to
expose _title and _relative_url in brains. We just need to select the
corresponding uids and let brains do another catalog search to get the object
when accessed.
Note that erp5_banking_core is not updated
parent 172a08bc
......@@ -12,13 +12,41 @@
#
##############################################################################
from Products.ZSQLCatalog.zsqlbrain import ZSQLBrain
from DateTime import DateTime
from Products.ERP5Type.TransactionalVariable import getTransactionalVariable
from ZTUtils import make_query
from Products.CMFCore.utils import getToolByName
from zLOG import LOG, PROBLEM
from Products.ERP5Type.Message import translateString
from ComputedAttribute import ComputedAttribute
class ComputedAttributeGetItemCompatibleMixin(ZSQLBrain):
"""A brain that supports accessing computed attributes using __getitem__
protocol.
"""
def __init__(self):
# __getitem__ returns the computed attribute directly, but if we access
# brain['node_title'] we expect to have the attribute after computation,
# not the ComputedAttribute attribue instance. Defining a __getitem__
# method on that class is not enough, because the brain class is not
# directly this class but a class created on the fly that also inherits
# from Record which already defines a __getitem__ method.
# We cannot patch the instance, because Record does not allow this kind of
# mutation, but as the class is created on the fly, for each query, it's
# safe to patch the class. See Shared/DC/ZRDB/Results.py for more detail.
# A Records holds a list of r instances, only the first __init__ needs to
# do this patching.
if not hasattr(self.__class__, '__super__getitem__'):
self.__class__.__super__getitem__ = self.__class__.__getitem__
self.__class__.__getitem__ =\
ComputedAttributeGetItemCompatibleMixin.__getitem__
# ComputedAttribute compatibility for __getitem__
def __getitem__(self, name):
item = self.__super__getitem__(name)
if isinstance(item, ComputedAttribute):
return item.__of__(self)
return item
class InventoryBrain(ZSQLBrain):
"""
Global analysis (all variations and categories)
......@@ -55,7 +83,6 @@ class InventoryBrain(ZSQLBrain):
list(self.getPortalCurrentInventoryStateList()))
def getAvailableInventory(self):
at_date=DateTime()
current = self.getCurrentInventory()
result = self.Resource_zGetInventory(
resource_uid=[self.resource_uid], ignore_variation=1,
......@@ -75,11 +102,11 @@ class InventoryBrain(ZSQLBrain):
if resource is not None:
return resource.getQuantityUnit()
class InventoryListBrain(ZSQLBrain):
class InventoryListBrain(ComputedAttributeGetItemCompatibleMixin):
"""
Lists each variation
"""
# Stock management
def getInventory(self, **kw):
simulation_tool = getToolByName(self, 'portal_simulation')
......@@ -110,13 +137,73 @@ class InventoryListBrain(ZSQLBrain):
resource_uid=self.resource_uid, **kw)
def getQuantityUnit(self, **kw):
resource = self.portal_catalog.getObject(self.resource_uid)
resource = self.getResourceValue()
if resource is not None:
return resource.getQuantityUnit()
def _getObjectByUid(self, uid):
uid_cache = getTransactionalVariable().setdefault(
'InventoryBrain.uid_cache', {None: None})
try:
return uid_cache[uid]
except KeyError:
result_list = self.portal_catalog(uid=uid, limit=1,
select_dict=dict(title=None, relative_url=None))
result = None
if result_list:
result = result_list[0]
uid_cache[uid] = result
return result
def getSectionValue(self):
return self._getObjectByUid(self.section_uid)
def getSectionTitle(self):
section = self.getSectionValue()
if section is not None:
return section.title
section_title = ComputedAttribute(getSectionTitle, 1)
def getSectionRelativeUrl(self):
section = self.getSectionValue()
if section is not None:
return section.relative_url
section_relative_url = ComputedAttribute(getSectionRelativeUrl, 1)
def getNodeValue(self):
return self._getObjectByUid(self.node_uid)
def getNodeTitle(self):
node = self.getNodeValue()
if node is not None:
return node.title
node_title = ComputedAttribute(getNodeTitle, 1)
def getNodeRelativeUrl(self):
node = self.getNodeValue()
if node is not None:
return node.relative_url
node_relative_url = ComputedAttribute(getNodeRelativeUrl, 1)
def getResourceValue(self):
return self._getObjectByUid(self.resource_uid)
def getResourceTitle(self):
resource = self.getResourceValue()
if resource is not None:
return resource.title
resource_title = ComputedAttribute(getResourceTitle, 1)
def getResourceRelativeUrl(self):
resource = self.getResourceValue()
if resource is not None:
return resource.relative_url
resource_relative_url = ComputedAttribute(getResourceRelativeUrl, 1)
def getListItemUrl(self, cname_id, selection_index, selection_name):
"""Returns the URL for column `cname_id`. Used by ListBox
"""
resource = self.getResourceValue()
if cname_id in ('getExplanationText', 'getExplanation', ):
o = self.getObject()
if o is not None:
......@@ -132,9 +219,8 @@ class InventoryListBrain(ZSQLBrain):
explanation.getRelativeUrl())
else:
return ''
elif getattr(self, 'resource_uid', None) is not None:
elif resource is not None:
# A resource is defined, so try to display the movement list
resource = self.portal_catalog.getObject(self.resource_uid)
form_name = 'Resource_viewMovementHistory'
query_kw = {
'variation_text': self.variation_text,
......@@ -291,7 +377,6 @@ class DeliveryListBrain(InventoryListBrain):
"""
Returns current inventory at current date
"""
at_date = DateTime()
current = self.getCurrentInventory()
result = self.Resource_zGetInventory(
resource_uid = [self.resource_uid],
......@@ -323,6 +408,7 @@ class MovementHistoryListBrain(InventoryListBrain):
"""Brain for getMovementHistoryList
"""
def __init__(self):
InventoryListBrain.__init__(self)
if not self.date:
return
# convert the date in the movement's original timezone.
......@@ -331,7 +417,7 @@ class MovementHistoryListBrain(InventoryListBrain):
obj = self.getObject()
if obj is not None:
timezone = None
if self.node_relative_url == obj.getSource():
if self.node_uid == obj.getSourceUid():
start_date = obj.getStartDate()
if start_date is not None:
timezone = start_date.timezone()
......
......@@ -715,8 +715,7 @@ class SimulationTool(BaseTool):
column_value_dict.set('funding_uid', funding_uid)
column_value_dict.set('payment_request_uid', payment_request_uid)
column_value_dict.set('function_uid', function_uid)
if column_value_dict.set('section_uid', section_uid):
sql_kw['section_filtered'] = 1
column_value_dict.set('section_uid', section_uid)
column_value_dict.set('node_uid', node_uid)
column_value_dict.set('mirror_node_uid', mirror_node_uid)
column_value_dict.set('mirror_section_uid', mirror_section_uid)
......@@ -731,8 +730,7 @@ class SimulationTool(BaseTool):
sql_kw['transformed_uid'] = self._generatePropertyUidList(transformed_resource)
if column_value_dict.setUIDList('section_uid', section):
sql_kw['section_filtered'] = 1
column_value_dict.setUIDList('section_uid', section)
column_value_dict.setUIDList('mirror_section_uid', mirror_section)
column_value_dict.setUIDList('variation_text', variation_text,
as_text=1)
......@@ -749,9 +747,7 @@ class SimulationTool(BaseTool):
related_key_dict.setUIDList('payment_request_category_uid', payment_request_category)
related_key_dict.setUIDList('function_category_uid', function_category)
related_key_dict.setUIDList('payment_category_uid', payment_category)
if related_key_dict.setUIDList('section_category_uid',
section_category):
sql_kw['section_filtered'] = 1
related_key_dict.setUIDList('section_category_uid', section_category)
related_key_dict.setUIDList('mirror_section_category_uid',
mirror_section_category)
# category strict membership
......@@ -769,9 +765,8 @@ class SimulationTool(BaseTool):
function_category_strict_membership)
related_key_dict.setUIDList('payment_category_strict_membership_uid',
payment_category_strict_membership)
if related_key_dict.setUIDList('section_category_strict_membership_uid',
section_category_strict_membership):
sql_kw['section_filtered'] = 1
related_key_dict.setUIDList('section_category_strict_membership_uid',
section_category_strict_membership)
related_key_dict.setUIDList(
'mirror_section_category_strict_membership_uid',
mirror_section_category_strict_membership)
......
......@@ -24,7 +24,6 @@ standardize\r\n
omit_simulation\r\n
omit_input\r\n
omit_output\r\n
section_filtered\r\n
input_simulation_state:list\r\n
output_simulation_state:list\r\n
group_by_expression\r\n
......@@ -78,12 +77,6 @@ SELECT\n
SUM(stock.quantity) AS total_quantity,\n
SUM(stock.total_price) AS total_price,\n
</dtml-if>\n
COUNT(DISTINCT node.title) AS node_title,\n
COUNT(DISTINCT node.relative_url) AS node_relative_url,\n
COUNT(DISTINCT section.title) AS section_title,\n
COUNT(DISTINCT section.relative_url) AS section_relative_url,\n
COUNT(DISTINCT resource.title) AS resource_title,\n
COUNT(DISTINCT resource.relative_url) AS resource_relative_url,\n
COUNT(DISTINCT stock.variation_text) AS variation_text,\n
MAX(stock.resource_uid) AS resource_uid,\n
COUNT(DISTINCT stock.uid) AS stock_uid,\n
......@@ -91,8 +84,6 @@ SELECT\n
\n
FROM\n
stock\n
<dtml-if section_filtered> INNER <dtml-else> LEFT </dtml-if>\n
JOIN catalog AS section ON (section.uid = stock.section_uid)\n
<dtml-in prefix="table" expr="from_table_list"> \n
<dtml-if expr="table_key != \'stock\'">\n
, <dtml-var table_item> AS <dtml-var table_key>\n
......@@ -102,16 +93,12 @@ FROM\n
<dtml-var "portal_selections.buildSQLJoinExpressionFromDomainSelection(selection_domain)"> </dtml-if>\n
<dtml-if selection_report>,\n
<dtml-var "portal_selections.buildSQLJoinExpressionFromDomainSelection(selection_report)"> </dtml-if>\n
, catalog as node, catalog as resource\n
\n
WHERE\n
1 = 1\n
<dtml-if where_expression>\n
AND <dtml-var where_expression>\n
</dtml-if>\n
\n
AND node.uid = stock.node_uid\n
AND resource.uid = stock.resource_uid\n
\n
<dtml-if omit_simulation>\n
AND catalog.portal_type != \'Simulation Movement\'\n
......
......@@ -33,7 +33,6 @@ ignore_variation\r\n
standardize\r\n
omit_simulation\r\n
only_accountable\r\n
section_filtered\r\n
omit_input\r\n
omit_output\r\n
input_simulation_state:list\r\n
......@@ -92,7 +91,7 @@ SELECT\n
SUM(ROUND(<dtml-var stock_table_id>.quantity\n
<dtml-if transformed_uid> * transformation.quantity</dtml-if>, <dtml-var precision>)) AS total_quantity,\n
<dtml-if convert_quantity_result>\n
SUM(ROUND(<dtml-var stock_table_id>.quantity * measure.quantity \n
SUM(ROUND(<dtml-var stock_table_id>.quantity * measure.quantity\n
<dtml-if quantity_unit_uid> / quantity_unit_conversion.quantity</dtml-if>\n
<dtml-if transformed_uid> * transformation.quantity</dtml-if>, <dtml-var precision>))\n
AS converted_quantity,\n
......@@ -110,20 +109,15 @@ SELECT\n
IFNULL(SUM(<dtml-var stock_table_id>.total_price), 0) AS total_price\n
</dtml-if>\n
<dtml-if inventory_list>\n
,node.title AS node_title,\n
node.uid AS node_uid,\n
node.relative_url AS node_relative_url,\n
section.title AS section_title,\n
section.uid AS section_uid,\n
section.relative_url AS section_relative_url,\n
resource.title AS resource_title,\n
resource.relative_url AS resource_relative_url,\n
<dtml-if transformed_uid>\n
transformed_resource.title AS transformed_resource_title,\n
transformed_resource.relative_url AS transformed_resource_relative_url,\n
transformation.transformed_uid AS transformed_resource_uid,\n
transformation.transformed_variation_text AS transformed_variation_text,\n
</dtml-if>\n
,\n
<dtml-var stock_table_id>.node_uid AS node_uid,\n
<dtml-var stock_table_id>.section_uid AS section_uid,\n
<dtml-if transformed_uid>\n
transformed_resource.title AS transformed_resource_title,\n
transformed_resource.relative_url AS transformed_resource_relative_url,\n
transformation.transformed_uid AS transformed_resource_uid,\n
transformation.transformed_variation_text AS transformed_variation_text,\n
</dtml-if>\n
<dtml-var stock_table_id>.resource_uid AS resource_uid,\n
<dtml-var stock_table_id>.variation_text AS variation_text,\n
<dtml-var stock_table_id>.sub_variation_text AS sub_variation_text,\n
......@@ -142,12 +136,6 @@ SELECT\n
</dtml-if>\n
<dtml-if statistic>\n
,\n
COUNT(DISTINCT node.title) AS node_title,\n
COUNT(DISTINCT node.relative_url) AS node_relative_url,\n
COUNT(DISTINCT section.title) AS section_title,\n
COUNT(DISTINCT section.relative_url) AS section_relative_url,\n
COUNT(DISTINCT resource.title) AS resource_title,\n
COUNT(DISTINCT resource.relative_url) AS resource_relative_url,\n
COUNT(DISTINCT <dtml-var stock_table_id>.variation_text) AS variation_text,\n
MAX(<dtml-var stock_table_id>.resource_uid) AS resource_uid,\n
COUNT(DISTINCT <dtml-var stock_table_id>.uid) AS stock_uid,\n
......@@ -157,8 +145,6 @@ SELECT\n
\n
FROM\n
catalog, <dtml-var stock_table_id>\n
<dtml-if section_filtered> INNER <dtml-else> LEFT </dtml-if> \n
JOIN catalog AS section ON (section.uid = <dtml-var stock_table_id>.section_uid)\n
<dtml-if quantity_unit_uid>\n
LEFT JOIN quantity_unit_conversion ON \n
(quantity_unit_conversion.resource_uid = <dtml-var stock_table_id>.resource_uid\n
......@@ -171,19 +157,16 @@ FROM\n
</dtml-in>\n
<dtml-if selection_domain>, <dtml-var "portal_selections.buildSQLJoinExpressionFromDomainSelection(selection_domain)"> </dtml-if>\n
<dtml-if selection_report>, <dtml-var "portal_selections.buildSQLJoinExpressionFromDomainSelection(selection_report)"> </dtml-if>\n
, catalog as node, catalog as resource <dtml-if transformed_uid>, transformation, catalog as transformed_resource</dtml-if>\n
<dtml-if transformed_uid>, transformation, catalog as transformed_resource</dtml-if>\n
\n
WHERE\n
<dtml-var stock_table_id>.uid = catalog.uid\n
<dtml-if where_expression>\n
AND <dtml-var where_expression>\n
</dtml-if>\n
\n
AND node.uid = <dtml-var stock_table_id>.node_uid\n
\n
<dtml-if transformed_uid>\n
AND transformation.uid = <dtml-var stock_table_id>.resource_uid\n
AND resource.uid = transformation.uid\n
AND <dtml-var stock_table_id>.variation_text = transformation.variation_text\n
\n
AND transformed_resource.uid = transformation.transformed_uid\n
......@@ -191,8 +174,6 @@ WHERE\n
<dtml-if transformed_variation_text>\n
AND <dtml-sqltest transformed_variation_text column="transformation.transformed_variation_text" type=string>\n
</dtml-if>\n
<dtml-else>\n
AND resource.uid = <dtml-var stock_table_id>.resource_uid\n
</dtml-if>\n
\n
<dtml-if omit_simulation>\n
......@@ -212,7 +193,6 @@ WHERE\n
AND concat(<dtml-var stock_table_id>.variation_text,\'\\n\') REGEXP measure.variation\n
</dtml-if>\n
\n
\n
<dtml-if group_by_expression>\n
GROUP BY\n
<dtml-if transformed_uid>transformation.transformed_uid,</dtml-if>\n
......
......@@ -432,7 +432,6 @@ omit_input\r\n
omit_output\r\n
omit_asset_increase\r\n
omit_asset_decrease\r\n
section_filtered\r\n
initial_running_total_quantity\r\n
initial_running_total_price\r\n
input_simulation_state:list\r\n
......@@ -510,16 +509,10 @@ SELECT\n
stock.project_uid AS project_uid,\n
stock.funding_uid AS funding_uid,\n
stock.payment_request_uid AS payment_request_uid,\n
node.uid AS node_uid,\n
node.title AS node_title,\n
node.relative_url AS node_relative_url,\n
section.uid AS section_uid,\n
section.title AS section_title,\n
section.relative_url AS section_relative_url\n
stock.node_uid AS node_uid,\n
stock.section_uid AS section_uid\n
FROM\n
stock\n
<dtml-if section_filtered> INNER <dtml-else> LEFT </dtml-if> \n
JOIN catalog AS section ON (section.uid = stock.section_uid)\n
<dtml-in prefix="table" expr="from_table_list"> \n
<dtml-if expr="table_key != \'stock\'">\n
, <dtml-var table_item> AS <dtml-var table_key>\n
......@@ -527,16 +520,12 @@ FROM\n
</dtml-in>\n
<dtml-if selection_domain>, <dtml-var "portal_selections.buildSQLJoinExpressionFromDomainSelection(selection_domain)"> </dtml-if>\n
<dtml-if selection_report>, <dtml-var "portal_selections.buildSQLJoinExpressionFromDomainSelection(selection_report)"> </dtml-if>\n
, catalog as node, catalog as resource\n
\n
WHERE\n
1 = 1\n
<dtml-if where_expression>\n
AND <dtml-var where_expression>\n
</dtml-if>\n
\n
AND node.uid = stock.node_uid\n
AND resource.uid = stock.resource_uid\n
\n
<dtml-if omit_simulation>\n
AND catalog.portal_type != \'Simulation Movement\'\n
......@@ -615,7 +604,7 @@ WHERE\n
\n
<dtml-if group_by_expression>\n
GROUP BY\n
<dtml-var group_by_expression>\n
<dtml-var group_by_expression>\n
</dtml-if>\n
\n
<dtml-if order_by_expression>\n
......
......@@ -54,7 +54,7 @@
<key> <string>src</string> </key>
<value> <string encoding="cdata"><![CDATA[
<dtml-var table_0>.resource_uid = resource.uid
<dtml-var table_0>.resource_uid = stock.resource_uid
]]></string> </value>
</item>
......
......@@ -1436,7 +1436,7 @@ class TestMovementHistoryList(InventoryAPITestCase):
"""Test attributes exposed on brains."""
getMovementHistoryList = self.getSimulationTool().getMovementHistoryList
self._makeMovement(quantity=100)
brain = getMovementHistoryList()[0]
brain = getMovementHistoryList(section_uid=self.section.getUid())[0]
self.assertTrue(hasattr(brain, 'node_uid'))
self.assertTrue(hasattr(brain, 'resource_uid'))
self.assertTrue(hasattr(brain, 'section_uid'))
......@@ -1448,6 +1448,36 @@ class TestMovementHistoryList(InventoryAPITestCase):
self.assertTrue(hasattr(brain, 'mirror_node_uid'))
self.assertTrue(hasattr(brain, 'mirror_section_uid'))
# compatiblity names
self.assertTrue(hasattr(brain, 'section_title'))
self.assertEquals(brain.section_title, self.section.getTitle())
self.assertTrue(hasattr(brain, 'section_relative_url'))
self.assertEquals(brain.section_relative_url, self.section.getRelativeUrl())
self.assertTrue(hasattr(brain, 'node_title'))
self.assertEquals(brain.node_title, self.node.getTitle())
self.assertTrue(hasattr(brain, 'node_relative_url'))
self.assertEquals(brain.node_relative_url, self.node.getRelativeUrl())
self.assertTrue(hasattr(brain, 'resource_title'))
self.assertEquals(brain.resource_title, self.resource.getTitle())
self.assertTrue(hasattr(brain, 'resource_relative_url'))
self.assertEquals(brain.resource_relative_url, self.resource.getRelativeUrl())
def testBrainGetItem(self):
"""Test __getitem__ interface on brains."""
getMovementHistoryList = self.getSimulationTool().getMovementHistoryList
self._makeMovement(quantity=100)
brain = getMovementHistoryList(section_uid=self.section.getUid())[0]
self.assertEquals(brain['node_uid'], self.node.getUid())
self.assertEquals(brain['node_relative_url'], self.node.getRelativeUrl())
self.assertEquals(brain['node_title'], self.node.getTitle())
self.assertEquals(brain['section_uid'], self.section.getUid())
self.assertEquals(brain['section_relative_url'], self.section.getRelativeUrl())
self.assertEquals(brain['section_title'], self.section.getTitle())
self.assertEquals(brain['resource_uid'], self.resource.getUid())
self.assertEquals(brain['resource_relative_url'], self.resource.getRelativeUrl())
self.assertEquals(brain['resource_title'], self.resource.getTitle())
def testSection(self):
getMovementHistoryList = self.getSimulationTool().getMovementHistoryList
mvt = self._makeMovement(quantity=100)
......
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