work in progress: read properties from Office documents and update version field if appropriate
git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@3926 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
parent
2fb16b5c24
commit
9091b56c69
3 changed files with 54 additions and 10 deletions
|
@ -214,9 +214,15 @@ Uploading Resources with HTTP PUT Requests
|
||||||
Extracting Document Properties from MS Office Files
|
Extracting Document Properties from MS Office Files
|
||||||
===================================================
|
===================================================
|
||||||
|
|
||||||
|
>>> import shutil
|
||||||
>>> from loops.resource import Resource
|
>>> from loops.resource import Resource
|
||||||
>>> tOfficeFile = concepts['officefile']
|
>>> tOfficeFile = concepts['officefile']
|
||||||
>>> path = os.path.join(dataDir, 'office')
|
>>> path = os.path.join(dataDir, 'office')
|
||||||
|
>>> fn = os.path.join(path, 'example.docx')
|
||||||
|
>>> shutil.copy(fn + '.sav', fn)
|
||||||
|
>>> os.path.getsize(fn)
|
||||||
|
195808L
|
||||||
|
|
||||||
>>> officeFile = addAndConfigureObject(resources, Resource, 'test.docx',
|
>>> officeFile = addAndConfigureObject(resources, Resource, 'test.docx',
|
||||||
... title=u'Example Word File', resourceType=tOfficeFile,
|
... title=u'Example Word File', resourceType=tOfficeFile,
|
||||||
... storageParams=dict(subdirectory=path))
|
... storageParams=dict(subdirectory=path))
|
||||||
|
@ -225,7 +231,7 @@ Extracting Document Properties from MS Office Files
|
||||||
|
|
||||||
>>> content = aOfficeFile.data
|
>>> content = aOfficeFile.data
|
||||||
>>> len(content)
|
>>> len(content)
|
||||||
195808
|
192925
|
||||||
|
|
||||||
|
|
||||||
Fin de partie
|
Fin de partie
|
||||||
|
|
|
@ -22,8 +22,9 @@ Resource adapter(s) for MS Office files.
|
||||||
$Id$
|
$Id$
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from xml.dom.minidom import parseString
|
from lxml import etree
|
||||||
from zipfile import ZipFile
|
from zipfile import ZipFile
|
||||||
|
import shutil
|
||||||
from zope.cachedescriptors.property import Lazy
|
from zope.cachedescriptors.property import Lazy
|
||||||
from zope import component
|
from zope import component
|
||||||
from zope.component import adapts
|
from zope.component import adapts
|
||||||
|
@ -36,6 +37,7 @@ from loops.integrator.interfaces import IOfficeFile
|
||||||
from loops.interfaces import IResource, IExternalFile
|
from loops.interfaces import IResource, IExternalFile
|
||||||
from loops.resource import ExternalFileAdapter
|
from loops.resource import ExternalFileAdapter
|
||||||
from loops.type import TypeInterfaceSourceList
|
from loops.type import TypeInterfaceSourceList
|
||||||
|
from loops.versioning.interfaces import IVersionable
|
||||||
|
|
||||||
|
|
||||||
TypeInterfaceSourceList.typeInterfaces += (IOfficeFile,)
|
TypeInterfaceSourceList.typeInterfaces += (IOfficeFile,)
|
||||||
|
@ -48,7 +50,8 @@ class OfficeFile(ExternalFileAdapter):
|
||||||
|
|
||||||
implements(IOfficeFile)
|
implements(IOfficeFile)
|
||||||
|
|
||||||
propertyMap = dict(version=u'Revision:')
|
propertyMap = {u'Revision:': 'version'}
|
||||||
|
propFileName = 'docProps/custom.xml'
|
||||||
|
|
||||||
def setExternalAddress(self, addr):
|
def setExternalAddress(self, addr):
|
||||||
super(OfficeFile, self).setExternalAddress(addr)
|
super(OfficeFile, self).setExternalAddress(addr)
|
||||||
|
@ -60,13 +63,46 @@ class OfficeFile(ExternalFileAdapter):
|
||||||
storage = component.getUtility(IExternalStorage, name=self.storageName)
|
storage = component.getUtility(IExternalStorage, name=self.storageName)
|
||||||
subDir = self.storageParams.get('subdirectory')
|
subDir = self.storageParams.get('subdirectory')
|
||||||
fn = storage.getDir(self.externalAddress, subDir)
|
fn = storage.getDir(self.externalAddress, subDir)
|
||||||
|
# TODO: check if suitable file type (.docx, .xlsm)
|
||||||
# open ZIP file, process properties, set version property in file
|
# open ZIP file, process properties, set version property in file
|
||||||
zf = ZipFile(fn, 'a')
|
zf = ZipFile(fn, 'r')
|
||||||
#print '***', zf.namelist()
|
#print '***', zf.namelist()
|
||||||
propsXml = zf.read('docProps/custom.xml')
|
propsXml = zf.read(self.propFileName)
|
||||||
dom = parseString(propsXml)
|
dom = etree.fromstring(propsXml)
|
||||||
props = dom.getElementsByTagName('property')
|
changed = False
|
||||||
for p in props:
|
docVersion = None
|
||||||
pass
|
version = IVersionable(self.context).versionId
|
||||||
#print '***', p.getAttribute('name'), p.childNodes[0].childNodes[0].data
|
strType = ('{http://schemas.openxmlformats.org/'
|
||||||
|
'officeDocument/2006/docPropsVTypes}lpwstr')
|
||||||
|
attributes = {}
|
||||||
|
for p in dom:
|
||||||
|
name = p.attrib.get('name')
|
||||||
|
value = p[0].text
|
||||||
|
#print '***', name, value, p[0].tag
|
||||||
|
attr = self.propertyMap.get(name)
|
||||||
|
if attr == 'version':
|
||||||
|
docVersion = value
|
||||||
|
if docVersion and docVersion != version:
|
||||||
|
# update XML
|
||||||
|
p[0] = etree.Element(strType)
|
||||||
|
p[0].text = version
|
||||||
|
changed = True
|
||||||
|
elif attr is not None:
|
||||||
|
attributes[attr] = value
|
||||||
zf.close()
|
zf.close()
|
||||||
|
if changed:
|
||||||
|
newFn = fn + '.new'
|
||||||
|
zf = ZipFile(fn, 'r')
|
||||||
|
newZf = ZipFile(newFn, 'w')
|
||||||
|
for info in zf.infolist():
|
||||||
|
name = info.filename
|
||||||
|
if name != self.propFileName:
|
||||||
|
newZf.writestr(info, zf.read(name))
|
||||||
|
newZf.writestr(self.propFileName, etree.tostring(dom))
|
||||||
|
newZf.close()
|
||||||
|
shutil.move(newFn, fn)
|
||||||
|
self.update(attributes)
|
||||||
|
|
||||||
|
def update(self, attributes):
|
||||||
|
# to be implemented by subclass
|
||||||
|
pass
|
||||||
|
|
|
@ -21,6 +21,7 @@ from loops.integrator.office.base import OfficeFile
|
||||||
from loops.knowledge.setup import SetupManager as KnowledgeSetupManager
|
from loops.knowledge.setup import SetupManager as KnowledgeSetupManager
|
||||||
from loops.setup import SetupManager, addAndConfigureObject
|
from loops.setup import SetupManager, addAndConfigureObject
|
||||||
from loops.tests.setup import TestSite as BaseTestSite
|
from loops.tests.setup import TestSite as BaseTestSite
|
||||||
|
from loops.versioning.versionable import VersionableResource
|
||||||
|
|
||||||
dataDir = os.path.join(os.path.dirname(__file__), 'testdata')
|
dataDir = os.path.join(os.path.dirname(__file__), 'testdata')
|
||||||
|
|
||||||
|
@ -37,6 +38,7 @@ class TestSite(BaseTestSite):
|
||||||
component.provideAdapter(FileAdapter, provides=IFile)
|
component.provideAdapter(FileAdapter, provides=IFile)
|
||||||
component.provideAdapter(ExternalFileAdapter, provides=IExternalFile)
|
component.provideAdapter(ExternalFileAdapter, provides=IExternalFile)
|
||||||
component.provideAdapter(OfficeFile, provides=IOfficeFile)
|
component.provideAdapter(OfficeFile, provides=IOfficeFile)
|
||||||
|
component.provideAdapter(VersionableResource)
|
||||||
|
|
||||||
component.provideUtility(fullPathStorage(), IExternalStorage, name='fullpath')
|
component.provideUtility(fullPathStorage(), IExternalStorage, name='fullpath')
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue