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
|
||||
==================
|
||||
|
||||
>>> from cybertools.organize.service import ServiceInstance
|
||||
>>> from cybertools.organize.service import Service
|
||||
|
|
|
@ -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
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