work in progress: process upload of resources from loops.agent via HTTP PUT
git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@1953 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
parent
c3ffee3437
commit
1d0ff2aed2
6 changed files with 196 additions and 17 deletions
|
@ -140,7 +140,10 @@ 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'
|
||||
|
@ -154,9 +157,14 @@ Uploading Resources with HTTP PUT Requests
|
|||
>>> request._traversal_stack = list(reversed(rrid.split('/')))
|
||||
|
||||
>>> traverser = ResourceManagerTraverser(resources, request)
|
||||
>>> traverser.publishTraverse(request, '.data')
|
||||
*** resources.PUT .data testing,data,file1.txt local user filesystem
|
||||
<...DummyWriteFile object ...>
|
||||
>>> 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
|
||||
=============
|
||||
|
|
|
@ -16,7 +16,21 @@
|
|||
set_schema="loops.integrator.interfaces.IExternalCollection" />
|
||||
</zope:class>
|
||||
|
||||
<zope:class class="loops.integrator.put.DummyWriteFile">
|
||||
<zope:adapter factory="loops.integrator.source.ExternalSourceInfo"
|
||||
trusted="True" />
|
||||
|
||||
<zope:class class="loops.integrator.source.ExternalSourceInfo">
|
||||
<require permission="zope.View"
|
||||
interface="loops.integrator.interfaces.IExternalSourceInfo" />
|
||||
<require permission="zope.ManageContent"
|
||||
set_schema="loops.integrator.interfaces.IExternalSourceInfo" />
|
||||
</zope:class>
|
||||
|
||||
<zope:class class="loops.integrator.put.MetadataProxy">
|
||||
<allow attributes="write" />
|
||||
</zope:class>
|
||||
|
||||
<zope:class class="loops.integrator.put.DummyResource">
|
||||
<allow attributes="write" />
|
||||
</zope:class>
|
||||
|
||||
|
|
|
@ -28,8 +28,22 @@ from zope import interface, component, schema
|
|||
from loops.util import _
|
||||
|
||||
|
||||
class IExternalSourceInfo(Interface):
|
||||
""" Provide additional information about the external source
|
||||
of an object.
|
||||
"""
|
||||
|
||||
externalIdentifier = Attribute('A string that allows to uniquely '
|
||||
'identify a resource or concept that is provided by an '
|
||||
'external system, e.g. a client-base loops agent. ')
|
||||
|
||||
|
||||
# external collections
|
||||
|
||||
class IExternalCollection(Interface):
|
||||
""" A collection of resources, to be used for a concept adapter.
|
||||
""" A concept representing a collection of resources that may be
|
||||
actively retrieved from an external system using the parameters
|
||||
given.
|
||||
"""
|
||||
|
||||
providerName = schema.TextLine(
|
||||
|
@ -84,6 +98,7 @@ class IExternalCollectionProvider(Interface):
|
|||
provided. Return the list of objects created.
|
||||
"""
|
||||
|
||||
# classification stuff
|
||||
|
||||
class IAutoClassifier(Interface):
|
||||
""" An adapter that more or less automagically assigns concepts to a
|
||||
|
|
|
@ -25,12 +25,21 @@ $Id$
|
|||
from zope import interface, component
|
||||
from zope.interface import implements
|
||||
from zope.component import adapts
|
||||
from zope.app.catalog.interfaces import ICatalog
|
||||
from zope.app.container.interfaces import INameChooser
|
||||
from zope.app.container.traversal import ContainerTraverser, ItemTraverser
|
||||
from zope.contenttype import guess_content_type
|
||||
from zope.cachedescriptors.property import Lazy
|
||||
from zope.event import notify
|
||||
from zope.filerepresentation.interfaces import IWriteFile
|
||||
from zope.lifecycleevent import ObjectCreatedEvent, ObjectModifiedEvent
|
||||
from zope.publisher.interfaces import IPublishTraverse
|
||||
|
||||
from cybertools.text import mimetypes
|
||||
from loops.common import adapted
|
||||
from loops.integrator.interfaces import IExternalSourceInfo
|
||||
from loops.interfaces import IResourceManager
|
||||
from loops.resource import Resource
|
||||
|
||||
|
||||
class ResourceManagerTraverser(ItemTraverser):
|
||||
|
@ -41,20 +50,94 @@ class ResourceManagerTraverser(ItemTraverser):
|
|||
def publishTraverse(self, request, name):
|
||||
if request.method == 'PUT':
|
||||
if name.startswith('.'):
|
||||
stack = request._traversal_stack
|
||||
#stack = request.getTraversalStack()
|
||||
#print '*** resources.PUT', name, '/'.join(stack)
|
||||
machine, user, app = stack.pop(), stack.pop(), stack.pop()
|
||||
path = '/'.join(reversed(stack))
|
||||
path = path.replace(',', ',,').replace('/', ',')
|
||||
for i in range(len(stack)):
|
||||
stack.pop()
|
||||
print '*** resources.PUT', name, path, machine, user, app
|
||||
return DummyWriteFile()
|
||||
return self.getOrCreateResource(request, name)
|
||||
return super(ResourceManagerTraverser, self).publishTraverse(request, name)
|
||||
|
||||
def getOrCreateResource(self, request, name):
|
||||
stack = request._traversal_stack
|
||||
#machine, user, app = stack.pop(), stack.pop(), stack.pop()
|
||||
path = '/'.join(reversed(stack))
|
||||
#path = path.replace(',', ',,').replace('/', ',')
|
||||
for i in range(len(stack)):
|
||||
# prevent further traversal
|
||||
stack.pop()
|
||||
#print '*** resources.PUT', name, path, machine, user, app
|
||||
print '*** resources.PUT', name, path
|
||||
resource = self.findResource(path)
|
||||
if resource is None:
|
||||
resource = self.createResource(path)
|
||||
notify(ObjectModifiedEvent(resource))
|
||||
#resource = DummyResource()
|
||||
if name == '.meta':
|
||||
return MetadataProxy(resource)
|
||||
return resource
|
||||
|
||||
class DummyWriteFile(object):
|
||||
def findResource(self, identifier):
|
||||
cat = component.getUtility(ICatalog)
|
||||
loopsRoot = self.context.getLoopsRoot()
|
||||
result = cat.searchResults(
|
||||
loops_externalidentifier=(identifier, identifier))
|
||||
result = [r for r in result if r.getLoopsRoot() == loopsRoot]
|
||||
if len(result) > 1:
|
||||
raise ValueError('More than one resource found for external '
|
||||
'identifier %s. Resources found: %s.' %
|
||||
(identifier, str([r.name for r in result])))
|
||||
if len(result) == 0:
|
||||
return None
|
||||
return result[0]
|
||||
|
||||
def createResource(self, identifier):
|
||||
name = self.generateName(identifier)
|
||||
title = self.generateTitle(identifier)
|
||||
contentType = guess_content_type(identifier,
|
||||
default='application/octet-stream')[0]
|
||||
resource = Resource()
|
||||
self.context[name] = resource
|
||||
cm = self.context.getLoopsRoot().getConceptManager()
|
||||
resource.resourceType = cm['extfile']
|
||||
obj = adapted(resource)
|
||||
obj.contentType = contentType
|
||||
obj.title = title
|
||||
#obj.data = data
|
||||
notify(ObjectModifiedEvent(resource))
|
||||
# TODO: provide basic concept assignments (collections)
|
||||
IExternalSourceInfo(resource).externalIdentifier == identifier
|
||||
return resource
|
||||
|
||||
def generateName(self, name):
|
||||
name = INameChooser(self.context).chooseName(name, None)
|
||||
return name
|
||||
|
||||
def generateTitle(self, name):
|
||||
separators = ('/', '\\')
|
||||
for sep in separators:
|
||||
if sep in name:
|
||||
name = name.rsplit(sep, 1)[-1]
|
||||
break
|
||||
if '.' in name:
|
||||
base, ext = name.rsplit('.', 1)
|
||||
if ext.lower() in mimetypes.extensions.values():
|
||||
name = base
|
||||
return name.decode('UTF-8')
|
||||
|
||||
|
||||
class MetadataProxy(object):
|
||||
""" Processes a metadata file for an associated resource.
|
||||
"""
|
||||
|
||||
def __init__(self, resource):
|
||||
self.resource = resource
|
||||
|
||||
implements(IWriteFile)
|
||||
|
||||
def write(self, text):
|
||||
# TODO: provide/change concept assignments based on metadata
|
||||
print '*** metadata', repr(text)
|
||||
|
||||
|
||||
class DummyResource(object):
|
||||
""" Just for testing
|
||||
"""
|
||||
|
||||
implements(IWriteFile)
|
||||
|
||||
|
|
53
integrator/source.py
Normal file
53
integrator/source.py
Normal file
|
@ -0,0 +1,53 @@
|
|||
#
|
||||
# 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
|
||||
#
|
||||
|
||||
"""
|
||||
Managing information form objects provided by external sources, e.g. loops.agent.
|
||||
|
||||
$Id$
|
||||
"""
|
||||
|
||||
from persistent.mapping import PersistentMapping
|
||||
from zope import interface, component
|
||||
from zope.interface import implements
|
||||
from zope.component import adapts
|
||||
|
||||
from loops.interfaces import ILoopsObject
|
||||
from loops.integrator.interfaces import IExternalSourceInfo
|
||||
|
||||
|
||||
sourceInfoAttrName = '__loops_integrator_sourceinfo__'
|
||||
|
||||
|
||||
class ExternalSourceInfo(object):
|
||||
|
||||
implements(IExternalSourceInfo)
|
||||
adapts(ILoopsObject)
|
||||
|
||||
def __init__(self, context):
|
||||
self.context = context
|
||||
|
||||
def getSourceInfo(self):
|
||||
return getattr(self.context, sourceInfoAttrName, PersistentMapping())
|
||||
|
||||
def getExternalIdentifier(self):
|
||||
return self.getSourceInfo().get('externalIdentifier')
|
||||
def setExternalIdentifier(self, value):
|
||||
self.getSourceInfo()['externalIdentifier'] = value
|
||||
externalIdentifier = property(getExternalIdentifier, setExternalIdentifier)
|
||||
|
|
@ -6,6 +6,8 @@ $Id$
|
|||
|
||||
import os
|
||||
from zope import component
|
||||
from zope.app.catalog.interfaces import ICatalog
|
||||
from zope.app.catalog.field import FieldIndex
|
||||
|
||||
from cybertools.storage.interfaces import IExternalStorage
|
||||
from cybertools.storage.filesystem import fullPathStorage
|
||||
|
@ -13,7 +15,7 @@ from loops import util
|
|||
from loops.interfaces import IFile, IExternalFile
|
||||
from loops.concept import Concept
|
||||
from loops.resource import Resource, FileAdapter, ExternalFileAdapter
|
||||
from loops.integrator.interfaces import IExternalCollection
|
||||
from loops.integrator.interfaces import IExternalSourceInfo, IExternalCollection
|
||||
from loops.knowledge.setup import SetupManager as KnowledgeSetupManager
|
||||
from loops.setup import SetupManager, addAndConfigureObject
|
||||
from loops.tests.setup import TestSite as BaseTestSite
|
||||
|
@ -35,6 +37,10 @@ class TestSite(BaseTestSite):
|
|||
|
||||
component.provideUtility(fullPathStorage(), IExternalStorage, name='fullpath')
|
||||
|
||||
catalog = component.getUtility(ICatalog)
|
||||
catalog['loops_externalidentifier'] = FieldIndex('externalIdentifier',
|
||||
IExternalSourceInfo, False)
|
||||
|
||||
tType = concepts.getTypeConcept()
|
||||
tExtFile = addAndConfigureObject(concepts, Concept, 'extfile',
|
||||
title=u'External File', conceptType=tType,
|
||||
|
|
Loading…
Add table
Reference in a new issue