loops/integrator
2011-04-28 12:35:43 +02:00
..
agent added agent package as marker for further development: control cybertools.agent instance 2008-02-25 10:19:07 +00:00
content move QueryConcept stuff to loops.expert (keeping old query module for backward compatibility); work in progress: child-based queries with actions 2008-10-23 13:35:23 +00:00
mail fix send email feature: encode subject and message correctly 2010-02-02 10:08:45 +00:00
office add MS PowerPoint XML types 2011-01-07 10:38:51 +00:00
testdata make sure doc test does not leave a modified file behind. 2011-04-28 12:35:43 +02:00
__init__.py new loops subpackage 'integrator' for importing and integrating operating system files and other external objects 2007-04-10 14:56:48 +00:00
browser.py added integrator.content package: transparent access to filesystem directories and files 2008-03-12 13:02:34 +00:00
collection.py correctly identify master version when loading external collection 2010-11-01 11:28:54 +00:00
collection_macros.pt show field content for content manager only 2011-04-09 18:01:47 +00:00
configure.zcml work in progress: integration of IMAP folders 2009-09-04 16:11:39 +00:00
interfaces.py work in progress: office file - processing document properties 2010-07-08 17:26:08 +00:00
put.py upload of resources from loops.agent via HTTP PUT basically working 2007-08-23 16:27:03 +00:00
README.txt make sure doc test does not leave a modified file behind. 2011-04-28 12:35:43 +02:00
source.py bugfix for SourceInfo adapter: use context also for __parent__ to allow security checking 2007-08-27 17:31:46 +00:00
tests.py work in progress: office file - processing document properties 2010-07-08 17:26:08 +00:00
testsetup.py work in progress: read properties from Office documents and update version field if appropriate 2010-07-27 09:50:19 +00:00

===============================================================
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

  >>> loopsRoot = site['loops']
  >>> #loopsRoot.options = ['useVersioning:rev']
  >>> loopsRoot.options = ['useVersioning']


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, addAndConfigureObject
  >>> 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')


Mail Collections
================

  >>> tType = concepts['type']
  >>> from loops.integrator.mail.interfaces import IMailCollection, IMailResource
  >>> tMailCollection = addAndConfigureObject(concepts, Concept, 'mailcollection',
  ...                    title=u'Mail Collection', conceptType=tType,
  ...                    typeInterface=IMailCollection)
  >>> tMailResource = addAndConfigureObject(concepts, Concept, 'email',
  ...                    title=u'Mail Resource', conceptType=tType,
  ...                    typeInterface=IMailResource)

  >>> mailColl = addObject(concepts, Concept, 'mails.user1',
  ...                    title=u'My Mails (User1)', conceptType=tMailCollection)

  >>> from loops.integrator.mail.collection import MailCollectionAdapter
  >>> aMailColl = MailCollectionAdapter(mailColl)

An external collection carries a set of attributes that control the access
to the external system:

  >>> aMailColl.userName = u'jim'
  >>> (aMailColl.providerName, aMailColl.baseAddress, aMailColl.address,
  ...  aMailColl.pattern, aMailColl.userName)
  (u'imap', None, None, None, u'jim')

  >>> from loops.integrator.mail import testing

  >>> from loops.integrator.mail.imap import IMAPCollectionProvider
  >>> component.provideUtility(IMAPCollectionProvider(), name='imap')
  >>> from loops.integrator.mail.resource import MailResource
  >>> component.provideAdapter(MailResource, provides=IMailResource)

  >>> aMailColl.update()

  >>> aMail = adapted(mailColl.getResources()[0])

  >>> aMail.date, aMail.sender, aMail.receiver, aMail.title
  (datetime.datetime(...), 'ceo@cy55.de', 'ceo@example.org', 'Blogging from Munich')
  >>> aMail.data
  u'<p><b>Blogging from ...</b><br />\n'
  >>> aMail.externalAddress
  u'imap://jim@merz12/20081208171745.e4ce2xm96cco80cg@cy55.de'


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'


Extracting Document Properties from MS Office Files
===================================================

  >>> import shutil
  >>> from loops.resource import Resource
  >>> tOfficeFile = concepts['officefile']
  >>> path = os.path.join(dataDir, 'office')
  >>> fn = os.path.join(path, 'example.docx')
  >>> os.path.getsize(fn)
  20337L

  >>> officeFile = addAndConfigureObject(resources, Resource, 'test.docx',
  ...                    title=u'Example Word File', resourceType=tOfficeFile,
  ...                    storageParams=dict(subdirectory=path))
  >>> aOfficeFile = adapted(officeFile)
  >>> aOfficeFile.externalAddress = 'example.docx'

  >>> content = aOfficeFile.data
  >>> len(content)
  17409

  Clean up:
  >>> shutil.copy(fn + '.sav', fn)


Fin de partie
=============

  >>> placefulTearDown()