Merge branch 'master' of ssh://git.cy55.de/home/git/cybertools
BIN
browser/icons/arrow_left.png
Normal file
After Width: | Height: | Size: 345 B |
BIN
browser/icons/arrow_right.png
Normal file
After Width: | Height: | Size: 349 B |
BIN
browser/icons/arrow_up.png
Normal file
After Width: | Height: | Size: 372 B |
BIN
browser/icons/book_next.png
Normal file
After Width: | Height: | Size: 702 B |
BIN
browser/icons/book_previous.png
Normal file
After Width: | Height: | Size: 680 B |
BIN
browser/icons/ledx.png
Executable file
After Width: | Height: | Size: 3.5 KiB |
|
@ -8,3 +8,52 @@ docgen - Document Generation from Result Sets and XML Structures
|
|||
>>> from cybertools.docgen.base import WordDocument
|
||||
>>> doc = WordDocument(None, TestRequest)
|
||||
|
||||
|
||||
Working with MHT Files
|
||||
======================
|
||||
|
||||
>>> import os
|
||||
>>> basePath = os.path.join(os.path.dirname(__file__), 'testing')
|
||||
|
||||
>>> path = os.path.join(basePath, 'test_doc.mht')
|
||||
>>> f = open(path, 'rt')
|
||||
>>> data = f.read()
|
||||
>>> f.close()
|
||||
|
||||
>>> xbody = '''<div class="WordSection1">
|
||||
... <v:shape id="Grafik_x0020_2" o:spid="_x0000_i1025" type="#_x0000_t75"
|
||||
... style="width:320pt;height:240pt;visibility:visible;mso-wrap-style:square">
|
||||
... <v:imagedata src="FB-Besprechungsprotokoll-Dateien/image002.jpg" o:title=""/>
|
||||
... </v:shape>
|
||||
... </div>
|
||||
... '''
|
||||
|
||||
>>> body = '''<div class="WordSection1">
|
||||
... <img src="files/test_image.jpg" />
|
||||
... </div>
|
||||
... '''
|
||||
|
||||
>>> from cybertools.docgen.mht import MHTFile
|
||||
>>> document = MHTFile(data, body)
|
||||
|
||||
>>> imageRefs = document.htmlDoc.getImageRefs()
|
||||
>>> for path in imageRefs:
|
||||
... imagePath = os.path.join(basePath, os.path.basename(path))
|
||||
... f = open(imagePath, 'rt')
|
||||
... imageData = f.read()
|
||||
... f.close()
|
||||
... document.addImage(imageData, path)
|
||||
|
||||
>>> document.insertBody()
|
||||
|
||||
>>> output = document.asString()
|
||||
>>> len(data), len(output)
|
||||
(294996, 336140)
|
||||
|
||||
>>> outPath = os.path.join(basePath, 'out_doc.mht')
|
||||
>>> #f = open(outPath, 'wt')
|
||||
>>> #f.write(document.asString())
|
||||
>>> #f.close()
|
||||
|
||||
>>> #os.unlink(outPath)
|
||||
|
||||
|
|
0
docgen/document.mht
Executable file → Normal file
131
docgen/mht.py
Normal file
|
@ -0,0 +1,131 @@
|
|||
#
|
||||
# Copyright (c) 2012 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
|
||||
#
|
||||
|
||||
"""
|
||||
Working with MHT Files.
|
||||
"""
|
||||
|
||||
import base64
|
||||
from cStringIO import StringIO
|
||||
import email
|
||||
import Image
|
||||
import mimetypes
|
||||
import os
|
||||
|
||||
from cybertools.text.lib.BeautifulSoup import BeautifulSoup, Tag
|
||||
|
||||
|
||||
class MHTFile(object):
|
||||
|
||||
#encoding = 'UTF-8'
|
||||
#encoding = 'ISO8859-15'
|
||||
encoding = 'Windows-1252'
|
||||
bodyMarker = 'lxdoc_body'
|
||||
foldernameSuffix = 'Dateien'
|
||||
indexes = dict(body=2, filelist=-2)
|
||||
|
||||
path = documentName = None
|
||||
|
||||
imageTemplate = ('\n'
|
||||
'Content-Location: %(path)s/%(docname)s-%(suffix)s/%(imgname)s\n'
|
||||
'Content-Transfer-Encoding: base64\n'
|
||||
'Content-Type: %(ctype)s\n\n%(imgdata)s\n\n')
|
||||
|
||||
filelistItemTemplate = ' <o:File HRef=3D"%s"/>\n'
|
||||
filelistPattern =' <o:File HRef=3D"filelist.xml"/>'
|
||||
|
||||
def __init__(self, data, body):
|
||||
self.data = data
|
||||
self.msg = email.message_from_string(data)
|
||||
self.boundary = self.msg.get_boundary()
|
||||
self.parts = data.split(self.boundary)
|
||||
self.body = body
|
||||
self.htmlDoc = HTMLDoc(body)
|
||||
self.lastImageNum = 0
|
||||
self.imageMappings = {}
|
||||
for idx, part in enumerate(self.msg.walk()):
|
||||
docPath = part['Content-Location']
|
||||
contentType = part.get_content_type()
|
||||
#print '***', idx, docPath, contentType
|
||||
if idx == self.indexes['body'] - 1:
|
||||
self.path, docname = os.path.split(docPath)
|
||||
self.documentName, ext = os.path.splitext(docname)
|
||||
if contentType.startswith('image/'):
|
||||
self.lastImageNum += 1
|
||||
#print '###', self.path, self.documentName, self.lastImageNum
|
||||
|
||||
def getImageRefs(self):
|
||||
return self.htmlDoc.getImageRefs()
|
||||
|
||||
def addImage(self, imageData, path):
|
||||
image = Image.open(StringIO(imageData))
|
||||
width, height = image.size
|
||||
contentType, enc = mimetypes.guess_type(path)
|
||||
bp, ext = os.path.splitext(path)
|
||||
self.lastImageNum += 1
|
||||
name = 'image%03i%s' % (self.lastImageNum, ext)
|
||||
self.imageMappings[path] = (name, width, height)
|
||||
flpos = self.indexes['filelist']
|
||||
vars = dict(path=self.path, docname=self.documentName,
|
||||
suffix=self.foldernameSuffix,
|
||||
imgname=name, ctype=contentType,
|
||||
imgdata=base64.encodestring(imageData))
|
||||
content = self. imageTemplate % vars
|
||||
self.parts.insert(flpos, str(content))
|
||||
filelistRep = (self.filelistItemTemplate % name) + self.filelistPattern
|
||||
filelist = self.parts[flpos]
|
||||
self.parts[flpos] = str(filelist.replace(self.filelistPattern, filelistRep))
|
||||
|
||||
|
||||
def insertBody(self):
|
||||
path = '-'.join((self.documentName, self.foldernameSuffix))
|
||||
self.htmlDoc.updateImageRefs(self.imageMappings, path)
|
||||
content = self.htmlDoc.doc.renderContents(self.encoding)
|
||||
bodyIndex = self.indexes['body']
|
||||
baseDocument = self.parts[bodyIndex]
|
||||
self.parts[bodyIndex] = baseDocument.replace(self.bodyMarker,
|
||||
self.quopri(content))
|
||||
|
||||
def asString(self):
|
||||
return self.boundary.join(self.parts)
|
||||
|
||||
def quopri(self, s):
|
||||
return s.replace('="', '=3D"')
|
||||
|
||||
|
||||
class HTMLDoc(object):
|
||||
|
||||
def __init__(self, data):
|
||||
self.data = data
|
||||
self.doc = BeautifulSoup(data)
|
||||
|
||||
def getImageRefs(self):
|
||||
return [img['src'] for img in self.doc('img')]
|
||||
|
||||
def updateImageRefs(self, mappings, path=''):
|
||||
for img in self.doc('img'):
|
||||
name, width, height = mappings[img['src']]
|
||||
imgdata = Tag(self.doc, 'v:imagedata')
|
||||
imgdata['src'] = '/'.join((path, name))
|
||||
imgdata.isSelfClosing = True
|
||||
img.append(imgdata)
|
||||
del img['src']
|
||||
img['style'] = 'width:%spt;height:%spt' % (width, height)
|
||||
img.isSelfClosing = False
|
||||
img.name='v:shape'
|
||||
|
4399
docgen/testing/test_doc.mht
Normal file
BIN
docgen/testing/test_image.jpg
Normal file
After Width: | Height: | Size: 30 KiB |
|
@ -1,5 +1,5 @@
|
|||
#
|
||||
# Copyright (c) 2008 Helmut Merz helmutm@cy55.de
|
||||
# Copyright (c) 2012 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
|
||||
|
@ -18,8 +18,6 @@
|
|||
|
||||
"""
|
||||
Access to objects in the file system.
|
||||
|
||||
$Id$
|
||||
"""
|
||||
|
||||
import os, stat
|
||||
|
@ -40,7 +38,7 @@ class ReadContainer(ReadContainer):
|
|||
|
||||
factoryName = 'filesystem'
|
||||
|
||||
@Lazy
|
||||
@property
|
||||
def filenames(self):
|
||||
return os.listdir(self.address)
|
||||
|
||||
|
@ -86,7 +84,7 @@ class File(File):
|
|||
data = None
|
||||
|
||||
def getData(self, num=-1):
|
||||
f = open(self.address, 'r')
|
||||
f = open(self.address, 'rb')
|
||||
data = f.read(num)
|
||||
f.close()
|
||||
return data
|
||||
|
@ -115,10 +113,13 @@ class ContainerFactory(ContainerFactory):
|
|||
|
||||
class FileFactory(FileFactory):
|
||||
|
||||
proxyClass = File
|
||||
imageClass = Image
|
||||
|
||||
def __call__(self, address, **kw):
|
||||
contentType = kw.pop('contentType', None)
|
||||
width = height = 0
|
||||
obj = File(address, contentType, **kw)
|
||||
obj = self.proxyClass(address, contentType, **kw)
|
||||
if not contentType:
|
||||
data = obj.getData(50)
|
||||
contentType, width, height = getImageInfo(data)
|
||||
|
@ -126,7 +127,7 @@ class FileFactory(FileFactory):
|
|||
name = os.path.basename(address)
|
||||
contentType, encoding = guess_content_type(name, data, '')
|
||||
if contentType.startswith('image/'):
|
||||
return Image(address, contentType=contentType,
|
||||
return self.imageClass(address, contentType=contentType,
|
||||
width=width, height=height, **kw)
|
||||
else:
|
||||
obj.contentType = contentType
|
||||
|
|
|
@ -80,6 +80,7 @@ def workItemStates():
|
|||
State('planned_x', 'planned', (), color='red'),
|
||||
State('accepted_x', 'accepted', (), color='yellow'),
|
||||
State('done_x', 'done', (), color='lightgreen'),
|
||||
State('finished_x', 'finished', (), color='green'),
|
||||
# transitions:
|
||||
Transition('plan', 'plan', 'planned'),
|
||||
Transition('accept', 'accept', 'accepted'),
|
||||
|
@ -131,13 +132,16 @@ class WorkItemType(object):
|
|||
"""
|
||||
|
||||
def __init__(self, name, title, description=u'',
|
||||
actions=None, fields=None, indicator=None):
|
||||
actions=None, fields=None, indicator=None,
|
||||
delegatedState='delegated', prefillDate=True):
|
||||
self.name = name
|
||||
self.title = title
|
||||
self.description = description
|
||||
self.actions = actions or list(editingRules)
|
||||
self.fields = fields or ('deadline', 'start-end', 'duration-effort')
|
||||
self.indicator = indicator
|
||||
self.delegatedState = delegatedState
|
||||
self.prefillDate = prefillDate
|
||||
|
||||
workItemTypes = Jeep((
|
||||
WorkItemType('work', u'Unit of Work', indicator='work_work'),
|
||||
|
@ -150,7 +154,13 @@ workItemTypes = Jeep((
|
|||
actions=('plan', 'accept', 'finish', 'cancel',
|
||||
'modify', 'delegate', 'move', 'close', 'reopen'),
|
||||
fields =('deadline',),
|
||||
indicator='work_deadline')
|
||||
indicator='work_deadline'),
|
||||
WorkItemType('checkup', u'Check-up',
|
||||
actions=('accept', 'finish', 'cancel',
|
||||
'modify', 'delegate', 'close', 'reopen'),
|
||||
fields =('deadline', 'start-end',),
|
||||
indicator='work_checkup',
|
||||
delegatedState='closed', prefillDate=False),
|
||||
))
|
||||
|
||||
|
||||
|
@ -182,7 +192,7 @@ class WorkItem(Stateful, Track):
|
|||
|
||||
def getWorkItemType(self):
|
||||
name = self.workItemType
|
||||
return name and workItemTypes[name] or None
|
||||
return name and workItemTypes[name] or workItemTypes['work']
|
||||
|
||||
@property
|
||||
def party(self):
|
||||
|
@ -259,7 +269,7 @@ class WorkItem(Stateful, Track):
|
|||
xkw = dict(kw)
|
||||
xkw.pop('party', None)
|
||||
delegated = self.createNew('delegate', userName, **xkw)
|
||||
delegated.state = 'delegated'
|
||||
delegated.state = self.getWorkItemType().delegatedState
|
||||
delegated.reindex('state')
|
||||
new = delegated.createNew('plan', userName, runId=0, **kw)
|
||||
new.data['source'] = delegated.name
|
||||
|
@ -284,7 +294,8 @@ class WorkItem(Stateful, Track):
|
|||
new.state = self.state
|
||||
new.reindex()
|
||||
moved.data['target'] = new.name
|
||||
if self.state in ('planned', 'accepted', 'delegated', 'moved', 'done'):
|
||||
if self.state in ('planned', 'accepted', 'delegated', 'moved',
|
||||
'done', 'finished'):
|
||||
self.state = self.state + '_x'
|
||||
self.reindex('state')
|
||||
return new
|
||||
|
|