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:
helmutm 2007-05-14 20:31:15 +00:00
parent ba101510cf
commit 954db76abe
10 changed files with 383 additions and 18 deletions

78
composer/README.txt Normal file
View 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
View file

@ -0,0 +1,4 @@
"""
$Id$
"""

55
composer/base.py Normal file
View 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
View 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
View 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.
"""

View file

@ -0,0 +1,4 @@
"""
$Id$
"""

22
composer/tests.py Executable file
View 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')

View file

@ -51,4 +51,4 @@ Let's create an address and assign it to a person:
Service Management
==================
>>> from cybertools.organize.service import ServiceInstance
>>> from cybertools.organize.service import Service

View file

@ -122,30 +122,29 @@ class ITask(Interface):
# services
class IService(Interface):
""" A general service definition or a group of service instances,
class IServiceGroup(Interface):
""" A group of related services or a general service definition,
e.g. a regular bus service or a series of trainings.
"""
class IServiceInstance(Interface):
""" A concrete service instance that clients may register with.
class IService(Interface):
""" 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(
title=_(u'Number of Seats'),
capacity = schema.Int(
title=_(u'Capacity'),
description=_(u'The capacity (maximum number of clients) '
'of this service; a negative number means: '
'no restriction, i.e. unlimited capacity.'),
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: '
'no restriction, i.e. unlimited capacity. '
'Read-only attribute')
'no restriction, i.e. unlimited capacity; '
'read-only')
serviceProviders = Attribute('A collection of one or more service providers.')
@ -156,12 +155,12 @@ class IServiceInstance(Interface):
def register(client):
""" Register a client with this service. Return an IRegistration
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):
""" A service instance that starts at a certain date/time and
class IScheduledService(IService):
""" A service that starts at a certain date/time and
usually ends a certain time later.
"""
@ -187,13 +186,12 @@ class IRegistration(Interface):
class IResource(Interface):
""" 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
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.
"""
class IServiceProvider(Interface):
""" An entity, e.g. a person or an institution, that is responsible
for providing a service or a service instance.
""" A party, that is responsible for providing a service.
"""

62
organize/service.py Normal file
View 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)