From c701a05d9e9936aee9a65e3dc734ff5da2e45b3a Mon Sep 17 00:00:00 2001 From: helmutm Date: Wed, 17 Oct 2007 18:18:06 +0000 Subject: [PATCH] initial set up for new composer.rule package git-svn-id: svn://svn.cy55.de/Zope3/src/cybertools/trunk@2118 fd906abe-77d9-0310-91a1-e0d9ade77398 --- composer/message/base.py | 6 +- composer/rule/README.txt | 28 +++++++ composer/rule/__init__.py | 4 + composer/rule/base.py | 149 +++++++++++++++++++++++++++++++++++ composer/rule/configure.zcml | 8 ++ composer/rule/instance.py | 43 ++++++++++ composer/rule/interfaces.py | 112 ++++++++++++++++++++++++++ composer/rule/mail.py | 33 ++++++++ composer/rule/tests.py | 22 ++++++ composer/schema/schema.py | 3 +- organize/service.py | 19 ++++- 11 files changed, 422 insertions(+), 5 deletions(-) create mode 100644 composer/rule/README.txt create mode 100644 composer/rule/__init__.py create mode 100644 composer/rule/base.py create mode 100644 composer/rule/configure.zcml create mode 100644 composer/rule/instance.py create mode 100644 composer/rule/interfaces.py create mode 100644 composer/rule/mail.py create mode 100755 composer/rule/tests.py diff --git a/composer/message/base.py b/composer/message/base.py index ce1e848..4d12259 100644 --- a/composer/message/base.py +++ b/composer/message/base.py @@ -37,11 +37,15 @@ class MessageManager(object): messagesFactory = Jeep messages = None + manager = None def __init__(self): if self.messagesFactory is not None: self.messages = self.messagesFactory() + def getManager(self): + return self.manager + class Message(Template): @@ -55,5 +59,3 @@ class Message(Template): for k, v in kw.items(): setattr(self, k, v) - - diff --git a/composer/rule/README.txt b/composer/rule/README.txt new file mode 100644 index 0000000..195a8b3 --- /dev/null +++ b/composer/rule/README.txt @@ -0,0 +1,28 @@ +=============================== +Rule-based Execution of Actions +=============================== + + ($Id$) + + >>> from zope import component + >>> from cybertools.composer.rule.base import RuleManager, Rule, Action + >>> from cybertools.composer.rule.base import EventType, Event + + >>> manager = RuleManager() + + >>> loginEvent = EventType('login') + >>> checkoutEvent = EventType('service.checkout') + + >>> checkoutRule = Rule('regcheckoutmail', manager=manager) + >>> checkoutRule.events.append(checkoutEvent) + >>> checkoutRule.actions.append(Action('message', + ... parameters = dict(messageName='confirmation_mail'))) + >>> checkoutRule.actions.append(Action('sendmail')) + >>> manager.rules.append(checkoutRule) + + >>> manager.handleEvent(Event(loginEvent)) + + >>> client = object() + + >>> manager.handleEvent(Event(checkoutEvent, client)) + diff --git a/composer/rule/__init__.py b/composer/rule/__init__.py new file mode 100644 index 0000000..4bc90fb --- /dev/null +++ b/composer/rule/__init__.py @@ -0,0 +1,4 @@ +""" +$Id$ +""" + diff --git a/composer/rule/base.py b/composer/rule/base.py new file mode 100644 index 0000000..aff0ffa --- /dev/null +++ b/composer/rule/base.py @@ -0,0 +1,149 @@ +# +# Copyright (c) 2007 Helmut Merz helmutm@cy55.de +# +# 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 +# + +""" +Basic classes for rules and actions. + +$Id$ +""" + +from zope.interface import implements + +from cybertools.composer.base import Component, Element, Compound +from cybertools.composer.base import Template +from cybertools.composer.interfaces import IInstance +from cybertools.composer.rule.interfaces import IRuleManager, IRule +from cybertools.composer.rule.interfaces import IEvent, ICondition +from cybertools.composer.rule.interfaces import IAction, IActionHandler +from cybertools.util.jeep import Jeep + + +# rules + +class RuleManager(object): + + implements(IRuleManager) + + rulesFactory = Jeep + rules = None + + def __init__(self): + if self.rulesFactory is not None: + self.rules = self.rulesFactory() + + def handleEvent(self, event): + pass + + +class Rule(Template): + + implements(IRule) + + name = title = description = u'' + manager = None + actions = None + events = None + conditions = None + + def __init__(self, name, **kw): + self.name = name + for k, v in kw.items(): + setattr(self, k, v) + self.events = [] + self.conditions = [] + self.actions = [] + + +class RuleInstance(object): + + implements(IInstance) + + template = None + + def applyTemplate(self): + pass + + +# events + +class EventType(object): + + def __init__(self, name, title=None): + self.name = name + self.title = title or name + + +class Event(object): + + implements(IEvent) + + def __init__(self, eventType, context=None): + self.eventType = eventType + self.name = eventType.name + self.title = eventType.title + self.context = context + + +# conditions + +class ConditionType(object): + + def __init__(self, name, title): + self.name = name + self.title = title + + +class Condition(object): + + implements(ICondition) + + def __init__(self, conditionType): + self.conditionType = conditionType + self.name = conditionType.name + self.title = conditionType.title + + def __call__(self, context, params): + return True + + +# actions + +class Action(object): + + implements(IAction) + + name = u'' + handlerName = u'' + parameters = None + rule = None + + def __init__(self, name, **kw): + for k, v in kw.items(): + setattr(self, k, v) + if self.parameters is None: + self.parameters = {} + if not self.handlerName: + self.handlerName = self.name + + +class ActionHandler(object): + + implements(IActionHandler) + + def __call__(self, data, params): + pass diff --git a/composer/rule/configure.zcml b/composer/rule/configure.zcml new file mode 100644 index 0000000..02b5d8b --- /dev/null +++ b/composer/rule/configure.zcml @@ -0,0 +1,8 @@ + + + + + diff --git a/composer/rule/instance.py b/composer/rule/instance.py new file mode 100644 index 0000000..19ae24c --- /dev/null +++ b/composer/rule/instance.py @@ -0,0 +1,43 @@ +# +# Copyright (c) 2007 Helmut Merz helmutm@cy55.de +# +# 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 +# + +""" +Rule instance and related classes. + +$Id$ +""" + +from string import Template +from zope import component +from zope.interface import implements + +from cybertools.composer.instance import Instance +from cybertools.composer.interfaces import IInstance + + +class RuleInstance(Instance): + + template = client = None + + def __init__(self, client, template): + self.client = client + self.template = template + + def applyTemplate(self, **kw): + pass + diff --git a/composer/rule/interfaces.py b/composer/rule/interfaces.py new file mode 100644 index 0000000..6c36d7b --- /dev/null +++ b/composer/rule/interfaces.py @@ -0,0 +1,112 @@ +# +# Copyright (c) 2007 Helmut Merz helmutm@cy55.de +# +# 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 +# + +""" +Message management. + +$Id$ +""" + +from zope.interface import Interface, Attribute +from zope.i18nmessageid import MessageFactory +from zope import schema + +from cybertools.composer.interfaces import ITemplate, IComponent + +_ = MessageFactory('zope') + + +class IRuleManager(Interface): + """ A manager (or container) for rules. + """ + + title = schema.TextLine( + title=_(u'Title'), + description=_(u'The title of the object.'), + required=True,) + + rules = Attribute('An ordered collection of rules managed by this object.') + + def handleEvent(event): + """ Handle the event given and apply the corresponding rules + to the client object. + """ + + +class IRule(ITemplate): + """ A rule that will be applied . + """ + + name = schema.ASCII( + title=_(u'Name'), + description=_(u'The internal name of the rule.'), + required=True,) + title = schema.TextLine( + title=_(u'Title'), + description=_(u'The title or label of the rule.'), + required=True,) + description = schema.Text( + title=_(u'Description'), + description=_(u'A brief description of the rule.'), + required=False,) + + manager = Attribute('The associated rule manager.') + events = Attribute('The events to be handled by this rule.') + conditions = Attribute('Conditions to be checked.' + 'This is typically a list of names of ICondition adapters.') + actions = Attribute('Sequence of actions to be carried out by this rule.') + + +class IEvent(Interface): + + name = Attribute('The name by which the event will be identified.') + title = Attribute('A human readable title or label.') + context = Attribute('An object that is associated with the event.') + + +class ICondition(Interface): + + def __call__(context, params): + """ Should return True if the condition should be fulfilled; + will allow the rule to call its actions. + """ + + +class IAction(IComponent): + """ Controls what will be done. + """ + + name = schema.ASCII( + title=_(u'Name'), + description=_(u'The name of the action.'), + required=True,) + + handlerName = Attribute('Name of the adapter that will carry out the action.') + parameters = Attribute('Mapping with additional preset informations that ' + 'will be supplied to the action handlers.') + rule = Attribute('The rule the action belongs to.') + + +class IActionHandler(Interface): + """ Does the real work. + """ + + def __call__(data, params): + """ Execute the action, using the data and parameters given (mappings). + """ + diff --git a/composer/rule/mail.py b/composer/rule/mail.py new file mode 100644 index 0000000..6d83932 --- /dev/null +++ b/composer/rule/mail.py @@ -0,0 +1,33 @@ +# +# Copyright (c) 2007 Helmut Merz helmutm@cy55.de +# +# 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 +# + +""" +Event definitions. + +$Id$ +""" + +from zope.interface import implements + +from cybertools.composer.rule.base import ActionHandler + + +class MailActionHandler(ActionHandler): + + pass + diff --git a/composer/rule/tests.py b/composer/rule/tests.py new file mode 100755 index 0000000..9886053 --- /dev/null +++ b/composer/rule/tests.py @@ -0,0 +1,22 @@ +# $Id$ + +import unittest, doctest +from zope.testing.doctestunit import DocFileSuite + + +class Test(unittest.TestCase): + "Basic tests." + + def testBasics(self): + pass + + +def test_suite(): + flags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS + return unittest.TestSuite(( + unittest.makeSuite(Test), + DocFileSuite('README.txt', optionflags=flags), + )) + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') diff --git a/composer/schema/schema.py b/composer/schema/schema.py index adab4ef..50e5333 100644 --- a/composer/schema/schema.py +++ b/composer/schema/schema.py @@ -17,7 +17,8 @@ # """ -Basic classes for a complex template structures. +Basic classes for schemas, i.e. sets of fields that may be used for creating +editing forms or display views for objects. $Id$ """ diff --git a/organize/service.py b/organize/service.py index 5d6b76b..260978a 100644 --- a/organize/service.py +++ b/organize/service.py @@ -31,6 +31,7 @@ from zope import component from zope.interface import implements, Interface from cybertools.composer.interfaces import IInstance +from cybertools.composer.rule.base import RuleManager, EventType from cybertools.composer.schema.interfaces import IClientManager, IClient from cybertools.stateful.definition import registerStatesDefinition from cybertools.stateful.definition import StatesDefinition @@ -43,16 +44,18 @@ from cybertools.organize.interfaces import IRegistration, IRegistrationTemplate from cybertools.organize.interfaces import IClientRegistrations -class ServiceManager(object): +class ServiceManager(RuleManager): implements(IServiceManager, IClientManager) servicesFactory = Jeep clientSchemasFactory = Jeep clientsFactory = OOBTree + rulesFactory = Jeep services = None clients = None + rules = None allowRegWithNumber = False allowDirectRegistration = True @@ -62,6 +65,8 @@ class ServiceManager(object): self.services = self.servicesFactory() if self.clientSchemasFactory is not None: self.clientSchemas = self.clientSchemasFactory() + if self.rulesFactory is not None: + self.rules = self.rulesFactory() def getServices(self, categories=[]): return self.services @@ -88,6 +93,9 @@ class ServiceManager(object): def checkClientName(self, name): return name not in self.getClients() + def getRules(self): + return self.rules + class Service(object): @@ -274,7 +282,14 @@ registerStatesDefinition( )) -# event handlers +# event types + +eventTypes = Jeep(( + EventType('service.checkout'), +)) + + +# Zope event handlers def clientRemoved(obj, event): """ Handle removal of a client object.