From 0de607514de0384bbb56596d2ba1b4181325dfb0 Mon Sep 17 00:00:00 2001 From: helmutm Date: Thu, 12 Apr 2007 15:29:44 +0000 Subject: [PATCH] work in progress: loops.integrator with DirectoryCollectionProvider git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@1686 fd906abe-77d9-0310-91a1-e0d9ade77398 --- configure.zcml | 8 +- integrator/README.txt | 57 ++++++++++ integrator/collection.py | 99 ++++++++++++++++++ integrator/configure.zcml | 13 +++ integrator/interfaces.py | 85 ++++++++++++++- .../topics/programming/BeautifulProgram.pdf | Bin 0 -> 45918 bytes .../topics/programming/zope/zope3.txt | 5 + integrator/testsetup.py | 14 ++- organize/interfaces.py | 1 - resource.py | 14 ++- 10 files changed, 286 insertions(+), 10 deletions(-) create mode 100644 integrator/collection.py create mode 100644 integrator/testdata/topics/programming/BeautifulProgram.pdf create mode 100644 integrator/testdata/topics/programming/zope/zope3.txt diff --git a/configure.zcml b/configure.zcml index d482317..7a60e6a 100644 --- a/configure.zcml +++ b/configure.zcml @@ -417,8 +417,11 @@ + name="varsubdir" /> + + + diff --git a/integrator/README.txt b/integrator/README.txt index 2c931f1..662dc50 100644 --- a/integrator/README.txt +++ b/integrator/README.txt @@ -29,6 +29,63 @@ configuration): >>> len(concepts) + len(resources) 18 + +External Collections +==================== + +The basis of our work will be ExternalCollection objects, i.e. concepts +of the 'extcollection' type. We use an adapter for providing the attributes +and methods of the external collect object. + + >>> from loops.concept import Concept + >>> from loops.setup import addObject + >>> from loops.integrator.collection import ExternalCollectionAdapter + >>> tExternalCollection = concepts['extcollection'] + >>> coll01 = addObject(concepts, Concept, 'coll01', + ... title=u'Collection One', type=tExternalCollection) + >>> aColl01 = ExternalCollectionAdapter(coll01) + +An external collection carries a set of attributes that control the access +to the external system: + + >>> aColl01.providerName, aColl01.baseAddress, aColl01.address, aColl01.pattern + (None, None, None, None) + >>> from loops.integrator.testsetup import dataDir + >>> aColl01.baseAddress = dataDir + >>> aColl01.address = 'topics' + + +Directory Collection Provider +----------------------------- + +The DirectoryCollectionProvider collects files from a directory in the +file system. The parameters (directory paths) are provided by the calling +object, the external collection itself. + + >>> from loops.integrator.collection import DirectoryCollectionProvider + >>> dcp = DirectoryCollectionProvider() + + >>> sorted(dcp.collect(aColl01)) + ['programming/BeautifulProgram.pdf', 'programming/zope/zope3.txt'] + +If we provide a selective pattern we get only part of the files: + + >>> aColl01.pattern = r'.*\.txt' + >>> sorted(dcp.collect(aColl01)) + ['programming/zope/zope3.txt'] + +Let's now create the corresponding resource objects. + + >>> aColl01.pattern = '' + >>> addresses = dcp.collect(aColl01) + >>> res = list(dcp.createExtFileObjects(aColl01, addresses)) + >>> len(sorted(r.__name__ for r in res)) + 2 + >>> xf1 = res[0] + >>> xf1.__name__ + u'programming/BeautifulProgram.pdf' + + Fin de partie ============= diff --git a/integrator/collection.py b/integrator/collection.py new file mode 100644 index 0000000..b978706 --- /dev/null +++ b/integrator/collection.py @@ -0,0 +1,99 @@ +# +# 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 +# + +""" +Concept adapter(s) for external collections, e.g. a directory in the +file system. + +$Id$ +""" + +import os, re +from zope.component import adapts +from zope.interface import implements, Attribute +from zope.cachedescriptors.property import Lazy +from zope.schema.interfaces import IField +from zope.traversing.api import getName, getParent + +from cybertools.typology.interfaces import IType +from loops.common import AdapterBase +from loops.interfaces import IResource, IConcept +from loops.integrator.interfaces import IExternalCollection +from loops.integrator.interfaces import IExternalCollectionProvider +from loops.resource import Resource +from loops.setup import addObject +from loops.type import TypeInterfaceSourceList + + +TypeInterfaceSourceList.typeInterfaces += (IExternalCollection,) + + +class ExternalCollectionAdapter(AdapterBase): + """ A concept adapter for accessing an external collection. + May delegate access to a named utility. + """ + + implements(IExternalCollection) + adapts(IConcept) + + _adapterAttributes = ('context', '__parent__',) + _contextAttributes = list(IExternalCollection) + list(IConcept) + + def create(self): + pass + + def update(self): + pass + + +class DirectoryCollectionProvider(object): + """ A utility that provides access to files in a directory. + """ + + implements(IExternalCollectionProvider) + + def collect(self, client): + directory = self.getDirectory(client) + pattern = re.compile(client.pattern or '.*') + result = [] + for path, dirs, files in os.walk(directory): + if files: + result.extend(os.path.join(path[len(directory)+1:], f) + for f in files + if pattern.match(f)) + return result + + def createExtFileObjects(self, client, addresses, extFileType=None): + if extFileType is None: + extFileType = client.context.getLoopsRoot().getConceptManager()['extfile'] + rm = client.context.getLoopsRoot().getResourceManager() + directory = self.getDirectory(client) + for addr in addresses: + name = addr + obj = addObject(rm, Resource, name, + title=addr.decode('UTF-8'), type=extFileType, + externalAddress=addr, + storage='fullpath', + storageParams=dict(subdirectory=directory)) + yield obj + + def getDirectory(self, client): + baseAddress = client.baseAddress or '' + address = client.address or '' + return os.path.join(baseAddress, address) + diff --git a/integrator/configure.zcml b/integrator/configure.zcml index cc0be03..ad7799a 100644 --- a/integrator/configure.zcml +++ b/integrator/configure.zcml @@ -6,4 +6,17 @@ i18n_domain="zope" > + + + + + + + + + diff --git a/integrator/interfaces.py b/integrator/interfaces.py index 99e4816..a80e159 100644 --- a/integrator/interfaces.py +++ b/integrator/interfaces.py @@ -25,8 +25,91 @@ $Id$ from zope.interface import Interface, Attribute from zope import interface, component, schema +from loops.util import _ + class IExternalCollection(Interface): - """ A collection of resources. + """ A collection of resources, to be used for a concept adapter. """ + providerName = schema.TextLine( + title=_(u'Provider name'), + description=_(u'The name of a utility that provides the ' + 'external objects; default is a directory ' + 'collection provider'), + required=False) + baseAddress = schema.TextLine( + title=_(u'Base address'), + description=_(u'A base path or URL for accessing this collection ' + 'on the external system'), + required=False) + address = schema.TextLine( + title=_(u'Relative address'), + description=_(u'Optional second (local) part of the ' + 'collection\'s address'), + required=False) + pattern = schema.TextLine( + title=_(u'Selection pattern'), + description=_(u'A regular expression for selecting external objects ' + 'that should belong to this collection'), + required=False) + + def create(): + """ Select external objects that should belong to a collection + using all the informations in the attributes, + create a resource of type 'extfile' for each of them, + and associate them with this collection. + Fire appropriate events. + """ + + def update(): + """ Check for new, changed, or deleted external objects. + Create an 'extfile' resource for new ones, fire appropriate + events for new, changed, or deleted ones. + Resources for deleted objects are not removed but should + be empty; they also should receive some state change. + """ + + +class IExternalCollectionProvider(Interface): + """ A utility that provides access to an external collection of objects. + """ + + def collect(clientCollection): + """ Select objects that should belong to a collection, + return an iterable of local address parts of the selected external + objects. The object specified by the 'clientCollection' argument + is usually the caller of the method and should provide the + IExternalCollection interface. + """ + + def createExtFileObjects(clientCollection, addresses, extFileType=None): + """ Create a resource of type 'extFileType' (default is the + type with the name 'extfile') for each of the addresses + provided. Return the list of objects created. + """ + + +class IAutoClassifier(Interface): + """ An adapter that more or less automagically assigns concepts to a + resource using some sort of selection criteria for the concepts + that should be considered. + """ + + +class IOntologyExporter(Interface): + """ An adapter for creating an XML file with all appropriate informations + from the context and its children, selecting children via a + pattern or a set of selection criteria. + + This may then be used by an external tool for classifying + a set of external objects. + """ + + +class IClassificationImporter(Interface): + """ An Adapter for importing an XML file with classification + information for a collection of external objects." + """ + + diff --git a/integrator/testdata/topics/programming/BeautifulProgram.pdf b/integrator/testdata/topics/programming/BeautifulProgram.pdf new file mode 100644 index 0000000000000000000000000000000000000000..4ec732b163c3e6f02f5658da95c37a7e59eaac6f GIT binary patch literal 45918 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 Lxv0Dt0_^_-20F;U literal 0 HcmV?d00001 diff --git a/integrator/testdata/topics/programming/zope/zope3.txt b/integrator/testdata/topics/programming/zope/zope3.txt new file mode 100644 index 0000000..7f90f93 --- /dev/null +++ b/integrator/testdata/topics/programming/zope/zope3.txt @@ -0,0 +1,5 @@ +This file carries some information about Zope 3, the open-source +web application server. + +It is used as an example document for integrating external resources +into loops. diff --git a/integrator/testsetup.py b/integrator/testsetup.py index 1043316..4677b04 100644 --- a/integrator/testsetup.py +++ b/integrator/testsetup.py @@ -4,15 +4,20 @@ Set up a loops site for testing. $Id$ """ +import os from zope import component from loops import util +from loops.interfaces import IExternalFile from loops.concept import Concept from loops.resource import Resource from loops.integrator.interfaces import IExternalCollection from loops.knowledge.setup import SetupManager as KnowledgeSetupManager +from loops.setup import SetupManager, addObject from loops.tests.setup import TestSite as BaseTestSite +dataDir = os.path.join(os.path.dirname(__file__), 'testdata') + class TestSite(BaseTestSite): @@ -24,9 +29,12 @@ class TestSite(BaseTestSite): concepts, resources, views = self.baseSetup() tType = concepts.getTypeConcept() - - tExtFile = concepts['extfile'] = Concept(u'External File') - tExtCollection = concepts['extcollection'] = Concept(u'External Collection') + tExtFile = addObject(concepts, Concept, 'extfile', + title=u'External File', type=tType, + typeInterface=IExternalFile) + tExtCollection = addObject(concepts, Concept, 'extcollection', + title=u'External Collection', type=tType, + typeInterface=IExternalCollection) self.indexAll(concepts, resources) return concepts, resources, views diff --git a/organize/interfaces.py b/organize/interfaces.py index 02e9994..e7cd780 100644 --- a/organize/interfaces.py +++ b/organize/interfaces.py @@ -27,7 +27,6 @@ from zope import interface, component, schema from zope.app import zapi from zope.app.principalannotation import annotations from zope.app.security.interfaces import IAuthentication, PrincipalLookupError -from zope.i18nmessageid import MessageFactory from zope.security.proxy import removeSecurityProxy from cybertools.organize.interfaces import IPerson as IBasePerson diff --git a/resource.py b/resource.py index a9b4857..dbda046 100644 --- a/resource.py +++ b/resource.py @@ -315,12 +315,20 @@ class ExternalFileAdapter(FileAdapter): self.context._storageParams = value storageParams = property(getStorageParams, setStorageParams) - @Lazy - def externalAddress(self): + def getExternalAddress(self): + return getattr(self.context, '_externalAddress', self.context.__name__) + def setExternalAddress(self, addr): + # TODO (?) - use intId as default? + self.context._externalAddress = addr + externalAddress = property(getExternalAddress, setExternalAddress) + + #@Lazy + #def externalAddress(self): # or is this an editable attribute? # or some sort of subpath set during import? # anyway: an attribute of the context object. - return self.context.__name__ + # TODO: use intId and store in special attribute for later reuse + #return self.context.__name__ def setData(self, data): storageParams = self.storageParams