diff --git a/composer/base.py b/composer/base.py index fd255f5..d9151e7 100644 --- a/composer/base.py +++ b/composer/base.py @@ -54,7 +54,9 @@ class Template(object): implements(ITemplate) componentStorage = Jeep + components = None def __init__(self): - self.components = self.componentStorage() + if self.componentStorage is not None: + self.components = self.componentStorage() diff --git a/composer/instance.py b/composer/instance.py index 4a39c04..039f769 100644 --- a/composer/instance.py +++ b/composer/instance.py @@ -31,25 +31,25 @@ class Instance(object): implements(IInstance) - templateStorage = dict - templateAttributeName = '__ctc_templates__' + templateFactory = dict + templateAttributeName = '__ctc_template__' aspect = 'composer.default' def __init__(self, context): self.context = context - self.instances = [] - def setTemplate(self, template): - templates = getattr(self.context, - self.templateAttributeName, self.templateStorage()) - templates.setdefault(self.aspect, template) - setattr(self.context, self.templateAttributeName, templates) + def setTemplate(self, temp): + template = getattr(self.context, + self.templateAttributeName, + self.templateFactory()) + template.setdefault(self.aspect, temp) + setattr(self.context, self.templateAttributeName, template) def getTemplate(self): - templates = getattr(self.context, self.templateAttributeName, {}) - return templates.get(self.aspect, None) + template = getattr(self.context, self.templateAttributeName, {}) + return template.get(self.aspect, []) template = property(getTemplate, setTemplate) - def applyTemplate(self, *args, **kw): + def applyTemplates(self, *args, **kw): raise ValueError('To be implemented by subclass') diff --git a/composer/interfaces.py b/composer/interfaces.py index b9d26d2..028cbda 100644 --- a/composer/interfaces.py +++ b/composer/interfaces.py @@ -52,7 +52,7 @@ class ITemplate(Interface): """ components = Attribute('An ordered sequence of the components this ' - 'object is built upon') + 'object is built upon') # instances @@ -62,9 +62,9 @@ class IInstance(Interface): """ context = Attribute('Object this instance adapter has been created for') - template = Attribute('The template to be used for this instance') + template = Attribute('A template to be used for this instance') aspect = Attribute('A dotted name that helps to store and retrieve the ' - 'template.') + 'template.') def applyTemplate(*args, **kw): """ Apply the template using the instance's context. Note that this diff --git a/composer/schema/browser/__init__.py b/composer/schema/browser/__init__.py new file mode 100644 index 0000000..4bc90fb --- /dev/null +++ b/composer/schema/browser/__init__.py @@ -0,0 +1,4 @@ +""" +$Id$ +""" + diff --git a/composer/schema/browser/base.py b/composer/schema/browser/base.py new file mode 100644 index 0000000..442a152 --- /dev/null +++ b/composer/schema/browser/base.py @@ -0,0 +1,34 @@ +# +# 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 browser view classes for composer.schema. + +$Id$ +""" + +from zope.cachedescriptors.property import Lazy + +from cybertools.composer.instance import Instance + + +class SchemaView(object): + + @Lazy + def fields(self): + return self.context.fields diff --git a/composer/schema/browser/configure.zcml b/composer/schema/browser/configure.zcml new file mode 100644 index 0000000..b76d087 --- /dev/null +++ b/composer/schema/browser/configure.zcml @@ -0,0 +1,19 @@ + + + + + + + + + diff --git a/composer/schema/field.py b/composer/schema/field.py index f26527b..e405696 100644 --- a/composer/schema/field.py +++ b/composer/schema/field.py @@ -35,8 +35,15 @@ class Field(Component): def __init__(self, name, title=None, renderFactory=None, **kw): assert name - self.name = self.__name__ = name - title = title is None and name or title + self.__name__ = name + title = title or u'' self.renderFactory = renderFactory # use for rendering field content super(Field, self).__init__(title, __name__=name, **kw) + @property + def name(self): + return self.__name__ + + @property + def title(self): + return self.title or self.name diff --git a/composer/schema/interfaces.py b/composer/schema/interfaces.py index 7a1d7ea..4826ef9 100644 --- a/composer/schema/interfaces.py +++ b/composer/schema/interfaces.py @@ -38,6 +38,9 @@ class ISchema(ITemplate): fields = Attribute('The components the schema is built up of. ' 'Should be a sequence of IField objects.') + manager = Attribute('A manager object that may provide special ' + 'features, e.g. a factory of o') + class IField(IComponent): """ May be used for data entry or display. diff --git a/composer/schema/schema.py b/composer/schema/schema.py index 7526ce4..fe8ac59 100644 --- a/composer/schema/schema.py +++ b/composer/schema/schema.py @@ -33,7 +33,12 @@ class Schema(Template): implements(ISchema) - def __init__(self, *fields): + name = u'' + manager = None + + def __init__(self, *fields, **kw): + self.name = kw.get('name', u'') + self.manager = kw.get('manager', None) super(Schema, self).__init__() for f in fields: self.components.append(f) diff --git a/organize/interfaces.py b/organize/interfaces.py index 048fb6f..5ffda14 100644 --- a/organize/interfaces.py +++ b/organize/interfaces.py @@ -128,6 +128,18 @@ class IServiceManager(Interface): services = Attribute('A collection of services managed by this object.') + clients = Attribute('A collection of client objects (e.g. persons) ' + 'that have registered or want to register for ' + 'services managed by this service manager.') + + clientSchemas = Attribute('An optional collection of schema objects ' + 'that describe the data fields of the client ' + 'objects.') + + def addClient(client): + """ Add the client object given to the collection of clients. + """ + class IServiceGroup(Interface): """ A group of related services or a general service definition, @@ -183,6 +195,24 @@ class IScheduledService(IService): duration = Attribute('Time delta between start and end date/time.') +class IClient(Interface): + """ An fairly abstract interface for objects to be used as clients + for services. + """ + + manager = Attribute('The object that cares for this client.') + + +class IClientFactory(Interface): + """ Creates client objects. + """ + + def __call__(data): + """ Creates and returns a client object built from the + data set provided. + """ + + class IRegistration(Interface): """ Information about the registration of a client with a service. """ diff --git a/organize/service.py b/organize/service.py index 0ee99c5..19f90f5 100644 --- a/organize/service.py +++ b/organize/service.py @@ -22,9 +22,15 @@ Service management classes. $Id$ """ +from BTrees.OOBTree import OOBTree +from zope.cachedescriptors.property import Lazy +from zope.component import adapts from zope.interface import implements +from cybertools.composer.interfaces import IInstance +from cybertools.util.jeep import Jeep from cybertools.organize.interfaces import IServiceManager +from cybertools.organize.interfaces import IClient, IClientFactory from cybertools.organize.interfaces import IService, IScheduledService @@ -32,6 +38,30 @@ class ServiceManager(object): implements(IServiceManager) + servicesFactory = list + clientSchemasFactory = Jeep + clientsFactory = OOBTree + + clientNum = 0 + + def __init__(self): + if self.servicesFactory is not None: + self.services = self.servicesFactory() + if self.clientSchemasFactory is not None: + self.clientSchemas = self.clientSchemasFactory() + + @Lazy + def clients(self): + return self.clientsFactory() + + def addClient(self, client): + name = self.generateClientName(client) + self.clients[name] = client + + def generateClientName(self, client): + self.clientNum += 1 + return '%05i' % self.clientNum + class Service(object): @@ -60,6 +90,57 @@ class ScheduledService(Service): implements(IScheduledService) +class Client(object): + + implements(IClient) + + def __init__(self, manager): + self.manager = manager + + +class ClientFactory(object): + + implements(IClientFactory) + adapts(IServiceManager) + + def __init__(self, context): + self.context = context + + def __call__(self): + return Client(self.context) + + +class ClientInstanceAdapter(object): + + implements(IInstance) + adapts(IClient) + + baseAspect = 'service.client.' + schema = 'default' + + @property + def aspect(self): + return self.baseAspect + self.schema + + @property + def template(self): + return self.context.manager.clientSchemas.get(self.schema, None) + + def __init__(self, context): + self.context = context + + def applyTemplate(self, data={}, schema='default', **kw): + if getattr(self.context, 'attributes', None) is None: + self.context.attributes = OOBTree() + self.schema = schema + template = self.template + attributes = self.context.attributes.setdefault(self.aspect, OOBTree()) + if template is not None: + for c in template.components: + name = c.name + attributes[name] = data.get(name, u'') + + class Registration(object): def __init__(self, client): diff --git a/util/jeep.py b/util/jeep.py index 70d5419..0f57d3b 100644 --- a/util/jeep.py +++ b/util/jeep.py @@ -31,10 +31,10 @@ class Jeep(object): _attributes = ('_sequence',) def __init__(self, seq=[]): - self._sequence = [] - for item in seq: - attr, value = item - setattr(self, attr, value) + sequence = self._sequence = [] + for attr, value in seq: + sequence.append(attr) + object.__setattr__(self, attr, value) def __len__(self): return len(self._sequence) @@ -47,11 +47,11 @@ class Jeep(object): if not attr in self._attributes: if getattr(self, attr, _notfound) is _notfound: self._sequence.append(attr) - object.__setattr__(self, attr, value) + super(Jeep, self).__setattr__(attr, value) def __delattr__(self, attr): del self._sequence[self.index(attr)] - object.__delattr__(self, attr) + super(Jeep, self).__delattr__(attr) def __getitem__(self, key): if type(key) in (int, long): diff --git a/zutil/jeep.py b/zutil/jeep.py index c8b20cc..33604c8 100644 --- a/zutil/jeep.py +++ b/zutil/jeep.py @@ -21,26 +21,33 @@ A general purpose (thus 'Jeep') class that provides most of the interfaces of sequences and dictionaries and in addition allows attribute access to the dictionary entries. +This is the Zope-based persistent variant of the Jeep class. + $Id$ """ +from persistent import Persistent from persistent.list import PersistentList from BTrees.OOBTree import OOBTree +from zope.app.container.interfaces import IContainer +from zope.interface import implements _notfound = object() _nodefault = object() -class Jeep(object): +class Jeep(Persistent): + + implements(IContainer) _attributes = ('_sequence', '_mapping') def __init__(self, seq=[]): - self._sequence = PersistentList() - self._mapping = OOBTree() - for item in seq: - attr, value = item - setattr(self, attr, value) + sequence = self._sequence = PersistentList() + mapping = self._mapping = OOBTree() + for attr, value in seq: + sequence.append(attr) + mapping[attr] = value def __len__(self): return len(self._sequence) @@ -60,7 +67,7 @@ class Jeep(object): def __setattr__(self, attr, value): if attr in self._attributes: - object.__setattr__(self, attr, value) + super(Jeep, self).__setattr__(attr, value) else: if getattr(self, attr, _notfound) is _notfound: self._sequence.append(attr) @@ -87,6 +94,8 @@ class Jeep(object): def __contains__(self, key): return getattr(self, key, _notfound) is not _notfound + has_key = __contains__ + def keys(self): return [key for key in self._sequence]