SolverTypeInformation.py 10.1 KB
Newer Older
Jean-Paul Smets committed
1 2 3
# -*- coding: utf-8 -*-
##############################################################################
#
4
# Copyright (c) 2002-2010 Nexedi SARL and Contributors. All Rights Reserved.
Jean-Paul Smets committed
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
#
# 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
30
from Products.ERP5Type import Permissions, PropertySheet, interfaces
Jean-Paul Smets committed
31
from Products.ERP5Type.ERP5Type import ERP5TypeInformation
32
from Products.ERP5Type.Core.Predicate import Predicate
33
from Products.ERP5Type.UnrestrictedMethod import UnrestrictedMethod
Jean-Paul Smets committed
34

35
class SolverTypeInformation(Predicate, ERP5TypeInformation):
36
  """A Type Information class which implements all Solver related methods
Jean-Paul Smets committed
37 38 39 40 41 42 43
  """
  # CMF Type Definition
  meta_type = 'ERP5 Solver Type Information'
  portal_type = 'Solver Type'

  # Declarative security
  security = ClassSecurityInfo()
44 45 46 47
  security.declareObjectProtected(Permissions.AccessContentsInformation)

  # Default Properties
  property_sheets = ( PropertySheet.BaseType
48
                    , PropertySheet.SolverType
49 50
                    , PropertySheet.Configurable
                    )
51

52 53
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getSolverConflictMessageList')
54
  def getSolverConflictMessageList(self, movement, configuration_mapping, solver_dict, movement_dict):
55
    """
56 57
    Returns the list of conflictings messgaes if the solver and configuration_mapping
    conflicts with another solver
58

59 60 61 62
    movement -- a movement

    configuration_mapping -- a mapping of configuration parameters sorted in
                             canonical way. ((c1, v1), (c2, v2 ))
63 64

    solver_dict -- a dictionary of configuration parameters for
65 66 67 68 69 70
                   each solver
                      solver_dict[solver] = {
                         movement : [((c1, v1), (c2, v2 )),
                                     ((c1, v1), (c2, v2 )),
                                    ],}

71
    movement_dict -- a dictionary of solver and configuration parameters for
72 73 74 75 76
                     each movement
                       movement_dict[movement] = {
                                     solver : [((c1, v1), (c2, v2 )),
                                               ((c1, v1), (c2, v2 )),
                                              ],}
77
    """
78
    method = self._getTypeBasedMethod('getSolverConflictMessageList')
79
    if method is not None:
80
      return method(movement, configuration_mapping, solver_dict, movement_dict)
81 82

    # Default Implementation (use categories and trivial case)
83
    #  this default implementation should be applicable to most
84 85 86 87
    #  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
88

89 90
    # Return emtpty message list
    return ()
91

92 93
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getSolverProcessGroupingKey')
94
  def getSolverProcessGroupingKey(self, movement, configuration_mapping, solver_dict, movement_dict):
95
    """
96
    Returns a key which can be used to group solvers during the
97 98 99 100 101
    process to build Targer Solver instances from Solver Decisions.
    This key depends on the movement and on the configuration_dict.

    For example, the movement dependent key for a solver which reduces
    produced quantity is the releative URL of the production order which
102
    this movement depends from (if it depennds on a single production
103 104 105
    order). If the same movement relates to multiple production orders,
    then the movement dependent grouping key should be None, but this
    could generate a different group for movements which depend on
106
    a single production order and for movements which depend on
107 108 109 110 111 112
    multiple production orders. For this purpose, the grouping key
    can be decided by looking up other_movement_list, a dictionnary
    which provides for each movement solver by the same solver the
    configuration parameters.

    The configuration dependent key for a "universal" solver (ex.
113
    Adopt, Accept) which tested property is configurable, is the
114 115
    tested property itself.

116
    movement -- a movement
117

118 119
    configuration_mapping -- a mapping of configuration parameters sorted in
                             canonical way. ((c1, v1), (c2, v2 ))
120 121

    solver_dict -- a dictionary of configuration parameters for
122 123 124 125 126 127
                   each solver
                      solver_dict[solver] = {
                         movement : [((c1, v1), (c2, v2 )),
                                     ((c1, v1), (c2, v2 )),
                                    ],}

128
    movement_dict -- a dictionary of solver and configuration parameters for
129 130 131 132 133
                     each movement
                       movement_dict[movement] = {
                                     solver : [((c1, v1), (c2, v2 )),
                                               ((c1, v1), (c2, v2 )),
                                              ],}
134
    """
135 136
    method = self._getTypeBasedMethod('getSolverProcessGroupingKey')
    if method is not None:
137
      return method(movement, configuration_mapping, solver_dict, movement_dict)
138

139
    # Default Implementation (read solver type properties and implement XXX-TODO)
140 141 142
    if self.isLineGroupable():
      return ()

143
    return movement.getRelativeUrl()
144

145 146
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getDefaultConfigurationPropertyDict')
147 148 149 150 151 152 153 154 155
  def getDefaultConfigurationPropertyDict(self, configurable):
    """
    Returns a dictionary of default properties for specified
    configurable object
    (implementation)

    configurable -- a configurable document (Solver Decision
                    or Target Solver)
    """
156 157 158 159 160
    method_id = self.getDefaultConfigurationPropertyDictMethodId()
    if method_id:
      return self._callMethod(configurable, method_id, {})
    else:
      return {}
161

162 163
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getDefaultConfigurationProperty')
164
  def getDefaultConfigurationProperty(self, property, configurable):
165
    """
166
    Returns the default value for a given property
167 168 169 170
    (public API)

    configurable -- a configurable document (Solver Decision
                    or Target Solver)
171 172

    TODO: XXX-JPS unify with IConfigurable
173
    """
174
    return self.getDefaultConfigurationPropertyDict().get(property, None)
175

176 177
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getConfigurationPropertyListDict')
178
  def getConfigurationPropertyListDict(self, configurable):
179
    """
180 181 182
    Returns a dictionary of possible values for specified
    configurable object
    (implementation)
183 184 185

    configurable -- a configurable document (Solver Decision
                    or Target Solver)
186
    """
187 188 189 190 191
    method_id = self.getConfigurationPropertyListDictMethodId()
    if method_id:
      return self._callMethod(configurable, method_id, {})
    else:
      return {}
192

193 194
  security.declareProtected(Permissions.AccessContentsInformation,
                            'getConfigurationPropertyList')
195
  def getConfigurationPropertyList(self, property, configurable):
196
    """
197 198 199 200 201 202
    Returns a list of possible values for a given property
    (public API)

    configurable -- a configurable document (Solver Decision
                    or Target Solver)
    """
203
    return self.getConfigurationPropertyListDict().get(property, [])
204

205
  def _callMethod(self, configurable, method_id, default=None):
206 207
    # Implemented through type based method
    # and using read transaction cache
208
    from erp5.component.interface.ISolver import ISolver
209 210 211
    portal_type = configurable.getPortalType()
    if portal_type == 'Solver Decision':
      try:
212
        solver_portal_type = configurable.getSolverValue().getId()
213 214 215 216 217
        solver = configurable.getParentValue().newContent(
          portal_type=solver_portal_type,
          temp_object=True,
          delivery_list=configurable.getDeliveryList(),
          causality_value=configurable)
218
      except AttributeError:
219
        return default
220
    elif ISolver.providedBy(configurable):
221 222 223 224 225
      solver_portal_type = portal_type
      solver = configurable
    else:
      raise NotImplementedError, '%s is not supported for configurable argument' % portal_type

226
    method = getattr(solver, method_id)
227
    return method()
228

229 230
  security.declarePrivate('solve')
  @UnrestrictedMethod
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
  def solve(self, delivery_list=None, configuration_dict=None,
            activate_kw=None, **kw):
    if delivery_list is None:
      return
    if configuration_dict is None:
      configuration_dict = {}
    solver_process_tool = self.getPortalObject().portal_solver_processes
    solver_process = solver_process_tool.newContent(
      portal_type='Solver Process',
      temp_object=True)
    solver = solver_process.newContent(portal_type=self.getId(),
                                       delivery_list=delivery_list)
    solver.updateConfiguration(**configuration_dict)
    if self.getPortalObject().portal_workflow.isTransitionPossible(
      solver, 'start_solving'):
      solver.startSolving()
    solver.solve(activate_kw=activate_kw)