DomainTool.py 10.9 KB
Newer Older
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1 2
##############################################################################
#
Romain Courteaud's avatar
Romain Courteaud committed
3
# Copyright (c) 2002, 2005 Nexedi SARL and Contributors. All Rights Reserved.
Jean-Paul Smets's avatar
Jean-Paul Smets committed
4
#                    Jean-Paul Smets-Solanes <jp@nexedi.com>
5
#                    Sebastien Robin <seb@nexedi.com>
Jean-Paul Smets's avatar
Jean-Paul Smets committed
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
##############################################################################

from AccessControl import ClassSecurityInfo
from Globals import InitializeClass, DTMLFile
from Products.ERP5Type import Permissions
33
from Products.ERP5 import _dtmldir
Jean-Paul Smets's avatar
Jean-Paul Smets committed
34
from Products.ERP5Type.Tool.BaseTool import BaseTool
35
from zLOG import LOG
36
from DateTime import DateTime
Jean-Paul Smets's avatar
Jean-Paul Smets committed
37 38 39

class DomainTool(BaseTool):
    """
Jean-Paul Smets's avatar
Jean-Paul Smets committed
40 41
        A tool to define reusable ranges and subranges through
        predicate trees
Jean-Paul Smets's avatar
Jean-Paul Smets committed
42 43 44 45
    """
    id = 'portal_domains'
    meta_type = 'ERP5 Domain Tool'    
    portal_type     = 'Domain Tool'
Romain Courteaud's avatar
Romain Courteaud committed
46
    allowed_types   = ('ERP5 Domain', )
Jean-Paul Smets's avatar
Jean-Paul Smets committed
47 48 49 50

    # Declarative Security
    security = ClassSecurityInfo()

Romain Courteaud's avatar
Romain Courteaud committed
51 52
    security.declareProtected(Permissions.ManagePortal, 'manage_overview')
    manage_overview = DTMLFile('explainDomainTool', _dtmldir)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
53

Romain Courteaud's avatar
Romain Courteaud committed
54 55
    # XXX FIXME method should not be public 
    # (some users are not able to see resource's price)
56
    security.declarePublic('searchPredicateList')
Romain Courteaud's avatar
Romain Courteaud committed
57
    def searchPredicateList(self, context, test=1, sort_method=None,
58 59 60
                            ignored_category_list=None,
                            tested_base_category_list=None,
                            filter_method=None, acquired=1, **kw):
61
      """
Romain Courteaud's avatar
Romain Courteaud committed
62 63
      Search all predicates which corresponds to this particular 
      context.
64
      
Romain Courteaud's avatar
Romain Courteaud committed
65
      - The sort_method parameter allows to give a method which will be
66 67 68
        used in order to sort the list of predicates founds. The most
        important predicate is the first one in the list.

Romain Courteaud's avatar
Romain Courteaud committed
69
      - ignored_category_list:  this is the list of category that we do
70 71 72
        not want to test. For example, we might want to not test the 
        destination or the source of a predicate.

73 74 75 76
      - tested_base_category_list:  this is the list of category that we do
        want to test. For example, we might want to test only the 
        destination or the source of a predicate.

Romain Courteaud's avatar
Romain Courteaud committed
77
      - the acquired parameter allows to define if we want to use
78
        acquisition for categories. By default we want.
79 80 81 82 83 84 85
      """
      portal_catalog = context.portal_catalog
      portal_categories = context.portal_categories
      column_list = []
      expression_list = []
      checked_column_list = []
      sql_kw = {}
Romain Courteaud's avatar
Romain Courteaud committed
86
      # Search the columns of the predicate table
87 88
      for column in portal_catalog.getColumnIds():
        if column.startswith('predicate.'):
89
          column_list.append(column.split('.')[1])          
90
      for column in column_list:
Romain Courteaud's avatar
Romain Courteaud committed
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
        if column not in checked_column_list:
          range_property = 0
          if (column.endswith('_range_min')) or \
             (column.endswith('_range_max')):
            range_property = 1
            # XXX FIXME: what means property here ?
            property = column[-len('_range_min')]
          if ('%s_range_min' % column) in column_list:
            range_property = 1
            property = column
          if range_property:
            # We have to check a range property
            base_name = 'predicate.%s' % property
#             LOG('searchPredicateList, getPath', 0, context.getPath())
#             LOG('searchPredicateList, base_name', 0, base_name)
#             LOG('searchPredicateList, property', 0, property)
#             LOG('searchPredicateList, getProperty', 0,
#                 context.getProperty(property))
            value = context.getProperty(property)
            format_dict = {'base_name': base_name}
            expression = "(%(base_name)s is NULL) AND " \
                         "(%(base_name)s_range_min is NULL) AND " \
                         "(%(base_name)s_range_max is NULL)" % format_dict
            if value is not None:
              # Handle Mysql datetime correctly
              if isinstance(value, DateTime):
                value = value.ISO()
              format_dict['value'] = value
              # Generate expression
              expression += "OR (%(base_name)s = '%(value)s') " \
                          "OR (%(base_name)s_range_min <= '%(value)s') AND " \
                              "(%(base_name)s_range_max is NULL) " \
                          "OR (%(base_name)s_range_min is NULL) AND " \
                              "%(base_name)s_range_max > '%(value)s' " \
                          "OR (%(base_name)s_range_min <= '%(value)s') AND " \
                              "%(base_name)s_range_max > '%(value)s' " \
                            % format_dict
            expression = '( %s )' % expression
            expression_list.append(expression)
            checked_column_list.append('%s' % property)
            checked_column_list.append('%s_range_min' % property)
            checked_column_list.append('%s_range_max' % property)
133 134
      # Add predicate.uid for automatic join
      sql_kw['predicate.uid'] = '!=0'
135
      where_expression = ' AND \n'.join(expression_list)
136 137

      # Add category selection
138 139 140 141 142
      if tested_base_category_list is None:
        if acquired:
          category_list = context.getAcquiredCategoryList()
        else:
          category_list = context.getCategoryList()
143
      else:
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
        category_list = []
        for tested_base_category in tested_base_category_list:
          category_list.extend(
               context.getCategoryMembershipList(tested_base_category, base=1))

      if tested_base_category_list != []:
        if len(category_list)==0:
          category_list = ['NULL']
        category_expression = portal_categories.buildSQLSelector(
                                           category_list,
                                           query_table='predicate_category')
        if len(where_expression) > 0:
          where_expression = '(%s) AND \n(%s)' % \
                                          (where_expression,category_expression)
        else:
          where_expression = category_expression

161 162 163 164
      sql_kw['where_expression'] = where_expression
      # Add predicate_category.uid for automatic join
      sql_kw['predicate_category.uid'] = '!=0'
      kw.update(sql_kw)
165
#       LOG('searchPredicateList, kw',0,kw)
166 167

      sql_result_list = portal_catalog.searchResults(**kw)
Romain Courteaud's avatar
Romain Courteaud committed
168
      if kw.get('src__'):
Sebastien Robin's avatar
Sebastien Robin committed
169
        return sql_result_list
170
      result_list = []
Romain Courteaud's avatar
Romain Courteaud committed
171 172
#       LOG('searchPredicateList, result_list before test', 0,
#           [x.getObject() for x in sql_result_list])
173
      for predicate in [x.getObject() for x in sql_result_list]:
174 175 176
        if test==0 or predicate.test(
                       context, 
                       tested_base_category_list=tested_base_category_list):
177
          result_list.append(predicate)
Romain Courteaud's avatar
Romain Courteaud committed
178
#       LOG('searchPredicateList, result_list before sort', 0, result_list)
Sebastien Robin's avatar
Sebastien Robin committed
179 180
      if filter_method is not None:
        result_list = filter_method(result_list)
Sebastien Robin's avatar
Sebastien Robin committed
181 182
      if sort_method is not None:
        result_list.sort(sort_method)
Romain Courteaud's avatar
Romain Courteaud committed
183
#       LOG('searchPredicateList, result_list after sort', 0, result_list)
184 185
      return result_list

Romain Courteaud's avatar
Romain Courteaud committed
186 187
    # XXX FIXME method should not be public 
    # (some users are not able to see resource's price)
188
    security.declarePublic('generateMappedValue')
Romain Courteaud's avatar
Romain Courteaud committed
189
    def generateMappedValue(self, context, test=1, predicate_list=None, **kw):
190
      """
Romain Courteaud's avatar
Romain Courteaud committed
191 192 193
      We will generate a mapped value with the list of all predicates 
      founds. 
      Let's say we have 3 predicates (in the order we want) like this:
Sebastien Robin's avatar
Sebastien Robin committed
194 195 196
      Predicate 1   [ base_price1,           ,   ,   ,    ,    , ]
      Predicate 2   [ base_price2, quantity2 ,   ,   ,    ,    , ]
      Predicate 3   [ base_price3, quantity3 ,   ,   ,    ,    , ]
Romain Courteaud's avatar
Romain Courteaud committed
197 198 199 200
      Our MappedValue generated will have the base_price of the 
      predicate1, and the quantity of the Predicate2, because Predicate
      1 is the first one which defines a base_price and the Predicate2
      is the first one wich defines a quantity.
201
      """
Sebastien Robin's avatar
Sebastien Robin committed
202
      # First get the list of predicates
203
      if predicate_list is None:
Romain Courteaud's avatar
Romain Courteaud committed
204
        predicate_list = self.searchPredicateList(context, test=test, **kw)
205
      if len(predicate_list)==0:
Romain Courteaud's avatar
Romain Courteaud committed
206 207 208 209
        # No predicate, return None
        mapped_value = None
      else:
        # Generate tempDeliveryCell
210 211
        from Products.ERP5Type.Document import newTempSupplyCell
        mapped_value = newTempSupplyCell(self.getPortalObject(),
Romain Courteaud's avatar
Romain Courteaud committed
212 213 214 215 216 217 218 219 220 221 222 223
                                           'new_mapped_value')
        mapped_value_property_dict = {}
        # Look for each property the first predicate which defines the 
        # property
        for predicate in predicate_list:
          for mapped_value_property in predicate.getMappedValuePropertyList():
            if not mapped_value_property_dict.has_key(mapped_value_property):
              value = predicate.getProperty(mapped_value_property)
              if value is not None:
                mapped_value_property_dict[mapped_value_property] = value
        # Update mapped value
        mapped_value = mapped_value.asContext(**mapped_value_property_dict)
Sebastien Robin's avatar
Sebastien Robin committed
224
      return mapped_value
225

226 227


228
    def getChildDomainValueList(self, parent, **kw):
229 230 231 232 233 234
      """
      Return child domain objects already present adn thois generetaded dynamically
      """
      # get static domain
      object_list = list(parent.objectValues())
      # get dynamic object genretade from script
235
      object_list.extend(parent.getDomainGeneratorList(**kw))
236 237 238 239 240 241
      return object_list

    def getDomainByPath(self, path):
      """
      Return the domain object for a given path
      """
242 243 244 245 246 247
      path = path.split('/')
      base_domain_id = path[0]
      domain = self[base_domain_id]
      for depth, subdomain in enumerate(path[1:]):
        domain_list = self.getChildDomainValueList(domain, depth=depth)
        for d in domain_list:
248 249
          if d.getId() == subdomain:
            domain = d
250 251 252
            break
        else:
          raise KeyError, subdomain
253 254
      return domain
  
Jean-Paul Smets's avatar
Jean-Paul Smets committed
255
InitializeClass(DomainTool)