starting with organize.service; new package cybertools.composer
git-svn-id: svn://svn.cy55.de/Zope3/src/cybertools/trunk@1732 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
parent
ba101510cf
commit
954db76abe
10 changed files with 383 additions and 18 deletions
78
composer/README.txt
Normal file
78
composer/README.txt
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
================================================================
|
||||||
|
Composer - Building Complex Structures with Templates or Schemas
|
||||||
|
================================================================
|
||||||
|
|
||||||
|
($Id$)
|
||||||
|
|
||||||
|
>>> from cybertools.composer.base import Element, Compound, Template
|
||||||
|
>>> from cybertools.composer.client import Instance, Client
|
||||||
|
|
||||||
|
We set up a very simple demonstration system using a PC configurator.
|
||||||
|
We start with two classes denoting a configuration and a simple
|
||||||
|
component within this configuration.
|
||||||
|
|
||||||
|
>>> class Configuration(Template):
|
||||||
|
... def __init__(self, name):
|
||||||
|
... self.name = name
|
||||||
|
... super(Configuration, self).__init__()
|
||||||
|
|
||||||
|
>>> class BasicComponent(Element):
|
||||||
|
... def __init__(self, name):
|
||||||
|
... self.name = name
|
||||||
|
... def __repr__(self):
|
||||||
|
... return self.name
|
||||||
|
|
||||||
|
>>> desktop = Configuration('Desktop')
|
||||||
|
>>> desktop.components.append(BasicComponent('case'))
|
||||||
|
>>> desktop.components.append(BasicComponent('mainboard'))
|
||||||
|
>>> desktop.components.append(BasicComponent('cpu'))
|
||||||
|
>>> desktop.components.append(BasicComponent('harddisk'))
|
||||||
|
|
||||||
|
Now somebody wants to configure a desktop PC using this configuration.
|
||||||
|
We need another class denoting the product that will be created.
|
||||||
|
|
||||||
|
>>> class Product(object):
|
||||||
|
... def __init__(self, productId):
|
||||||
|
... self.productId = productId
|
||||||
|
... self.parts = {}
|
||||||
|
... def __repr__(self):
|
||||||
|
... return self.productId
|
||||||
|
|
||||||
|
>>> c001 = Product('c001')
|
||||||
|
|
||||||
|
The real stuff will be done by an instance that connects the product
|
||||||
|
(via the client) with the template.
|
||||||
|
|
||||||
|
>>> class ConfigurationInstance(Instance):
|
||||||
|
... def applyTemplate(self):
|
||||||
|
... for c in self.template.components:
|
||||||
|
... print c, self.parent.context.parts.get(c.name, '-')
|
||||||
|
|
||||||
|
In this case we can directly use the basic client adapter for setting up the
|
||||||
|
connection. As we have only one template we also associate only one
|
||||||
|
instance with the client.
|
||||||
|
|
||||||
|
>>> client = Client(c001)
|
||||||
|
>>> client.instances.append(ConfigurationInstance(client, desktop))
|
||||||
|
>>> client.applyTemplates()
|
||||||
|
case -
|
||||||
|
mainboard -
|
||||||
|
cpu -
|
||||||
|
harddisk -
|
||||||
|
|
||||||
|
If we have configured a CPU for our configuration this will be listed.
|
||||||
|
|
||||||
|
>>> c001.parts['cpu'] = Product('z80')
|
||||||
|
>>> client.applyTemplates()
|
||||||
|
case -
|
||||||
|
mainboard -
|
||||||
|
cpu z80
|
||||||
|
harddisk -
|
||||||
|
|
||||||
|
Note that the ConfigurationInstance's applyTemplate() method is fairly
|
||||||
|
primitive. In a real-world application there usually are a lot more methods
|
||||||
|
that do more stuff. In our PC configurator application there might be
|
||||||
|
methods that just list components (e.g. to provide a user interface),
|
||||||
|
retrieve candidate products (e.g. CPUs) to use in the
|
||||||
|
configuration and store the user's selection in the context object.
|
||||||
|
|
4
composer/__init__.py
Normal file
4
composer/__init__.py
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
"""
|
||||||
|
$Id$
|
||||||
|
"""
|
||||||
|
|
55
composer/base.py
Normal file
55
composer/base.py
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
#
|
||||||
|
# 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 a complex template structures.
|
||||||
|
|
||||||
|
$Id$
|
||||||
|
"""
|
||||||
|
|
||||||
|
from zope.interface import implements
|
||||||
|
|
||||||
|
from cybertools.composer.interfaces import IComponent, IElement, ICompound
|
||||||
|
from cybertools.composer.interfaces import ITemplate
|
||||||
|
|
||||||
|
|
||||||
|
class Component(object):
|
||||||
|
|
||||||
|
implements(IComponent)
|
||||||
|
|
||||||
|
|
||||||
|
class Element(Component):
|
||||||
|
|
||||||
|
implements(IElement)
|
||||||
|
|
||||||
|
|
||||||
|
class Compound(Component):
|
||||||
|
|
||||||
|
implements(ICompound)
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.parts = []
|
||||||
|
|
||||||
|
|
||||||
|
class Template(object):
|
||||||
|
|
||||||
|
implements(ITemplate)
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.components = []
|
||||||
|
|
56
composer/client.py
Normal file
56
composer/client.py
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
#
|
||||||
|
# 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
|
||||||
|
#
|
||||||
|
|
||||||
|
"""
|
||||||
|
Base classes to be used for client adapters.
|
||||||
|
|
||||||
|
$Id$
|
||||||
|
"""
|
||||||
|
|
||||||
|
from zope.interface import implements
|
||||||
|
|
||||||
|
from cybertools.composer.interfaces import IInstance, IClient
|
||||||
|
|
||||||
|
|
||||||
|
class Instance(object):
|
||||||
|
|
||||||
|
implements(IInstance)
|
||||||
|
|
||||||
|
parent = None
|
||||||
|
template = None
|
||||||
|
|
||||||
|
def __init__(self, parent, template):
|
||||||
|
self.parent = parent
|
||||||
|
self.template = template
|
||||||
|
|
||||||
|
def applyTemplate(self, *args, **kw):
|
||||||
|
raise ValueError('To be implemented by subclass')
|
||||||
|
|
||||||
|
|
||||||
|
class Client(object):
|
||||||
|
|
||||||
|
implements(IClient)
|
||||||
|
|
||||||
|
def __init__(self, context):
|
||||||
|
self.context = context
|
||||||
|
self.instances = []
|
||||||
|
|
||||||
|
def applyTemplates(self, *args, **kw):
|
||||||
|
for inst in self.instances:
|
||||||
|
inst.applyTemplate(*args, **kw)
|
||||||
|
|
86
composer/interfaces.py
Normal file
86
composer/interfaces.py
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
#
|
||||||
|
# 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
|
||||||
|
#
|
||||||
|
|
||||||
|
"""
|
||||||
|
Complex structures with templates/schemas.
|
||||||
|
|
||||||
|
$Id$
|
||||||
|
"""
|
||||||
|
|
||||||
|
from zope.interface import Interface, Attribute
|
||||||
|
|
||||||
|
|
||||||
|
# template side interfaces
|
||||||
|
|
||||||
|
class IComponent(Interface):
|
||||||
|
""" Basic building block. A component may be part of other components.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class IElement(IComponent):
|
||||||
|
""" A final or elementary component, i.e. one that does not consist
|
||||||
|
of other components.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class ICompound(IComponent):
|
||||||
|
""" A component that consists of other components.
|
||||||
|
"""
|
||||||
|
|
||||||
|
parts = Attribute('An ordered sequence of the components this '
|
||||||
|
'object consists of')
|
||||||
|
|
||||||
|
|
||||||
|
class ITemplate(Interface):
|
||||||
|
""" A structure, consisting of components, that may be used as a
|
||||||
|
template/schema/blueprint for client objects.
|
||||||
|
"""
|
||||||
|
|
||||||
|
components = Attribute('An ordered sequence of the components this '
|
||||||
|
'object is built upon')
|
||||||
|
|
||||||
|
|
||||||
|
# client side interfaces
|
||||||
|
|
||||||
|
class IInstance(Interface):
|
||||||
|
""" Represents an object that uses a template.
|
||||||
|
"""
|
||||||
|
|
||||||
|
parent = Attribute('The client this instance belongs to')
|
||||||
|
template = Attribute('Template this instance is associated with')
|
||||||
|
|
||||||
|
def applyTemplate(*args, **kw):
|
||||||
|
""" Apply the template (in the parent's context). Note that this
|
||||||
|
method is just an example - instance classes may define
|
||||||
|
other methods that provide more specific actions.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class IClient(Interface):
|
||||||
|
""" Represents an object that uses a set of templates via its instances.
|
||||||
|
"""
|
||||||
|
|
||||||
|
context = Attribute('Object this client adapter has been created for')
|
||||||
|
instances = Attribute('An ordered or unordered sequence of instance objects')
|
||||||
|
|
||||||
|
def applyTemplates(*args, **kw):
|
||||||
|
""" Apply the templates of all instances. Note that this
|
||||||
|
method is just an example - client classes may define
|
||||||
|
other methods that provide more specific actions.
|
||||||
|
"""
|
||||||
|
|
4
composer/schema/__init__.py
Normal file
4
composer/schema/__init__.py
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
"""
|
||||||
|
$Id$
|
||||||
|
"""
|
||||||
|
|
22
composer/tests.py
Executable file
22
composer/tests.py
Executable file
|
@ -0,0 +1,22 @@
|
||||||
|
# $Id$
|
||||||
|
|
||||||
|
import unittest, doctest
|
||||||
|
from zope.testing.doctestunit import DocFileSuite
|
||||||
|
|
||||||
|
|
||||||
|
class Test(unittest.TestCase):
|
||||||
|
"Basic tests for the cybertools.composer package."
|
||||||
|
|
||||||
|
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')
|
|
@ -51,4 +51,4 @@ Let's create an address and assign it to a person:
|
||||||
Service Management
|
Service Management
|
||||||
==================
|
==================
|
||||||
|
|
||||||
>>> from cybertools.organize.service import ServiceInstance
|
>>> from cybertools.organize.service import Service
|
||||||
|
|
|
@ -122,30 +122,29 @@ class ITask(Interface):
|
||||||
|
|
||||||
# services
|
# services
|
||||||
|
|
||||||
|
class IServiceGroup(Interface):
|
||||||
class IService(Interface):
|
""" A group of related services or a general service definition,
|
||||||
""" A general service definition or a group of service instances,
|
|
||||||
e.g. a regular bus service or a series of trainings.
|
e.g. a regular bus service or a series of trainings.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class IServiceInstance(Interface):
|
class IService(Interface):
|
||||||
""" A concrete service instance that clients may register with.
|
""" A service that clients may register with.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
service = Attribute('The service this object is an instance of.')
|
serviceGroup = Attribute('The service group this object is an instance of.')
|
||||||
|
|
||||||
seats = schema.Int(
|
capacity = schema.Int(
|
||||||
title=_(u'Number of Seats'),
|
title=_(u'Capacity'),
|
||||||
description=_(u'The capacity (maximum number of clients) '
|
description=_(u'The capacity (maximum number of clients) '
|
||||||
'of this service; a negative number means: '
|
'of this service; a negative number means: '
|
||||||
'no restriction, i.e. unlimited capacity.'),
|
'no restriction, i.e. unlimited capacity.'),
|
||||||
required=False,)
|
required=False,)
|
||||||
|
|
||||||
availableSeats = Attribute('Available capacity, i.e. number of seats '
|
availableCapacity = Attribute('Available capacity, i.e. number of seats '
|
||||||
'still available; a negative number means: '
|
'still available; a negative number means: '
|
||||||
'no restriction, i.e. unlimited capacity. '
|
'no restriction, i.e. unlimited capacity; '
|
||||||
'Read-only attribute')
|
'read-only')
|
||||||
|
|
||||||
serviceProviders = Attribute('A collection of one or more service providers.')
|
serviceProviders = Attribute('A collection of one or more service providers.')
|
||||||
|
|
||||||
|
@ -156,12 +155,12 @@ class IServiceInstance(Interface):
|
||||||
def register(client):
|
def register(client):
|
||||||
""" Register a client with this service. Return an IRegistration
|
""" Register a client with this service. Return an IRegistration
|
||||||
object if the registration is successful, otherwise
|
object if the registration is successful, otherwise
|
||||||
(e.g. if no seat is available) return None.
|
(e.g. if the service's capacity is exhausted) return None.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class IScheduledServiceInstance(IServiceInstance):
|
class IScheduledService(IService):
|
||||||
""" A service instance that starts at a certain date/time and
|
""" A service that starts at a certain date/time and
|
||||||
usually ends a certain time later.
|
usually ends a certain time later.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -187,13 +186,12 @@ class IRegistration(Interface):
|
||||||
class IResource(Interface):
|
class IResource(Interface):
|
||||||
""" A resource is needed by a service to be able to work, e.g. a
|
""" A resource is needed by a service to be able to work, e.g. a
|
||||||
room or a bus. A resource may have a limited capacity so that
|
room or a bus. A resource may have a limited capacity so that
|
||||||
at a certain time it may only be used by services to certain
|
at a certain time it may only be used by services to a certain
|
||||||
extent.
|
extent.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class IServiceProvider(Interface):
|
class IServiceProvider(Interface):
|
||||||
""" An entity, e.g. a person or an institution, that is responsible
|
""" A party, that is responsible for providing a service.
|
||||||
for providing a service or a service instance.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
62
organize/service.py
Normal file
62
organize/service.py
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
#
|
||||||
|
# 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
|
||||||
|
#
|
||||||
|
|
||||||
|
"""
|
||||||
|
Service instance classes.
|
||||||
|
|
||||||
|
$Id$
|
||||||
|
"""
|
||||||
|
|
||||||
|
from zope.interface import implements
|
||||||
|
|
||||||
|
from cybertools.organize.interfaces import IService
|
||||||
|
from cybertools.organize.interfaces import IScheduledService
|
||||||
|
|
||||||
|
|
||||||
|
class Registration(object):
|
||||||
|
|
||||||
|
def __init__(self, client):
|
||||||
|
self.client = client
|
||||||
|
|
||||||
|
|
||||||
|
class Service(object):
|
||||||
|
|
||||||
|
implements(IService)
|
||||||
|
|
||||||
|
def __init__(self, seats=-1):
|
||||||
|
self.capacity = capacity
|
||||||
|
self.registrations = []
|
||||||
|
|
||||||
|
@property
|
||||||
|
def availableCapacity(self):
|
||||||
|
if self.capacity >= 0 and len(self.registrations) >= self.capacity:
|
||||||
|
return 0
|
||||||
|
return self.capacity - len(self.registrations)
|
||||||
|
|
||||||
|
def register(self, client):
|
||||||
|
if self.availableCapacity:
|
||||||
|
reg = Registration(client)
|
||||||
|
self.registrations.append(reg)
|
||||||
|
return reg
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class ScheduledService(Service):
|
||||||
|
|
||||||
|
implements(IScheduledService)
|
||||||
|
|
Loading…
Add table
Reference in a new issue