=============================================================== loops - Linked Objects for Organization and Processing Services =============================================================== Integration of external sources. ($Id$) Setting up a loops Site and Utilities ===================================== Let's do some basic set up >>> from zope import component, interface >>> from zope.traversing.api import getName >>> from zope.app.testing.setup import placefulSetUp, placefulTearDown >>> site = placefulSetUp(True) and build a simple loops site with a concept manager and some concepts (with a relation registry, a catalog, and all the type machinery - what in real life is done via standard ZCML setup or via local utility configuration): >>> from loops.integrator.testsetup import TestSite >>> t = TestSite(site) >>> concepts, resources, views = t.setup() >>> 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', conceptType=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', datetime.datetime(...)), ('programming/zope/zope3.txt', datetime.datetime(...))] If we provide a more selective pattern we get only part of the files: >>> aColl01.pattern = r'.*\.txt' >>> sorted(dcp.collect(aColl01)) [('programming/zope/zope3.txt', datetime.datetime(...))] Let's now create the corresponding resource objects. >>> aColl01.pattern = '' >>> addresses = [e[0] for e in 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' >>> xf1.title u'BeautifulProgram' >>> xf1.contentType 'application/pdf' >>> from loops.common import adapted >>> aXf1 = adapted(xf1) >>> aXf1.storageName 'fullpath' >>> aXf1.storageParams {'subdirectory': '...topics'} >>> for r in res: del resources[r.__name__] Working with the External Collection ------------------------------------ >>> component.provideUtility(DirectoryCollectionProvider()) >>> aColl01.update() >>> res = coll01.getResources() >>> len(res) 2 >>> sorted((r.__name__, r.title, r._storageName) for r in res) [(u'programming_beautifulprogram.pdf', u'BeautifulProgram', 'fullpath'), (u'programming_zope_zope3.txt', u'zope3', 'fullpath')] We may update the collection after having changed the storage params. This should also change the settings for existing objects if they still can be found. >>> import os >>> aColl01.address = os.path.join('topics', 'programming') >>> aColl01.update() >>> res = sorted(coll01.getResources(), key=lambda x: getName(x)) >>> len(res) 2 >>> aXf1 = adapted(res[0]) >>> aXf1.storageName, aXf1.storageParams, aXf1.externalAddress ('fullpath', {'subdirectory': '...programming'}, 'BeautifulProgram.pdf') But if one of the referenced objects is not found any more it will be deleted. >>> aColl01.address = os.path.join('topics', 'programming', 'zope') >>> aColl01.update() >>> res = sorted(coll01.getResources(), key=lambda x: getName(x)) >>> len(res) 1 >>> aXf1 = adapted(res[0]) >>> aXf1.storageName, aXf1.storageParams, aXf1.externalAddress ('fullpath', {'subdirectory': '...zope'}, 'zope3.txt') Uploading Resources with HTTP PUT Requests ========================================== >>> from zope.publisher.browser import TestRequest >>> from zope.traversing.api import getName >>> from loops.integrator.put import ResourceManagerTraverser >>> from loops.integrator.source import ExternalSourceInfo >>> component.provideAdapter(ExternalSourceInfo) >>> rrinfo = 'local/user/filesystem' >>> rrpath = 'testing/data/file1.txt' >>> rrid = '/'.join((rrinfo, rrpath)) >>> baseUrl = 'http://127.0.0.1/loops/resources' >>> url = '/'.join((baseUrl, '.data', rrid)) >>> request = TestRequest(url) >>> request.method = 'PUT' >>> request._traversal_stack = list(reversed(rrid.split('/'))) >>> traverser = ResourceManagerTraverser(resources, request) >>> resource = traverser.publishTraverse(request, '.data') *** resources.PUT .data local/user/filesystem/testing/data/file1.txt >>> getName(resource) u'local_user_filesystem_testing_data_file1.txt' >>> resource.title u'file1' Fin de partie ============= >>> placefulTearDown()