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 |   >>> from cybertools.docgen.base import WordDocument | ||||||
|   >>> doc = WordDocument(None, TestRequest) |   >>> 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 | #  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 | #  it under the terms of the GNU General Public License as published by | ||||||
|  | @ -18,8 +18,6 @@ | ||||||
| 
 | 
 | ||||||
| """ | """ | ||||||
| Access to objects in the file system. | Access to objects in the file system. | ||||||
| 
 |  | ||||||
| $Id$ |  | ||||||
| """ | """ | ||||||
| 
 | 
 | ||||||
| import os, stat | import os, stat | ||||||
|  | @ -40,7 +38,7 @@ class ReadContainer(ReadContainer): | ||||||
| 
 | 
 | ||||||
|     factoryName = 'filesystem' |     factoryName = 'filesystem' | ||||||
| 
 | 
 | ||||||
|     @Lazy |     @property | ||||||
|     def filenames(self): |     def filenames(self): | ||||||
|         return os.listdir(self.address) |         return os.listdir(self.address) | ||||||
| 
 | 
 | ||||||
|  | @ -86,7 +84,7 @@ class File(File): | ||||||
|     data = None |     data = None | ||||||
| 
 | 
 | ||||||
|     def getData(self, num=-1): |     def getData(self, num=-1): | ||||||
|         f = open(self.address, 'r') |         f = open(self.address, 'rb') | ||||||
|         data = f.read(num) |         data = f.read(num) | ||||||
|         f.close() |         f.close() | ||||||
|         return data |         return data | ||||||
|  | @ -115,10 +113,13 @@ class ContainerFactory(ContainerFactory): | ||||||
| 
 | 
 | ||||||
| class FileFactory(FileFactory): | class FileFactory(FileFactory): | ||||||
| 
 | 
 | ||||||
|  |     proxyClass = File | ||||||
|  |     imageClass = Image | ||||||
|  | 
 | ||||||
|     def __call__(self, address, **kw): |     def __call__(self, address, **kw): | ||||||
|         contentType = kw.pop('contentType', None) |         contentType = kw.pop('contentType', None) | ||||||
|         width = height = 0 |         width = height = 0 | ||||||
|         obj = File(address, contentType, **kw) |         obj = self.proxyClass(address, contentType, **kw) | ||||||
|         if not contentType: |         if not contentType: | ||||||
|             data = obj.getData(50) |             data = obj.getData(50) | ||||||
|             contentType, width, height = getImageInfo(data) |             contentType, width, height = getImageInfo(data) | ||||||
|  | @ -126,7 +127,7 @@ class FileFactory(FileFactory): | ||||||
|             name = os.path.basename(address) |             name = os.path.basename(address) | ||||||
|             contentType, encoding = guess_content_type(name, data, '') |             contentType, encoding = guess_content_type(name, data, '') | ||||||
|         if contentType.startswith('image/'): |         if contentType.startswith('image/'): | ||||||
|             return Image(address, contentType=contentType, |             return self.imageClass(address, contentType=contentType, | ||||||
|                          width=width, height=height, **kw) |                          width=width, height=height, **kw) | ||||||
|         else: |         else: | ||||||
|             obj.contentType = contentType |             obj.contentType = contentType | ||||||
|  |  | ||||||
|  | @ -80,6 +80,7 @@ def workItemStates(): | ||||||
|         State('planned_x', 'planned', (), color='red'), |         State('planned_x', 'planned', (), color='red'), | ||||||
|         State('accepted_x', 'accepted', (), color='yellow'), |         State('accepted_x', 'accepted', (), color='yellow'), | ||||||
|         State('done_x', 'done', (), color='lightgreen'), |         State('done_x', 'done', (), color='lightgreen'), | ||||||
|  |         State('finished_x', 'finished', (), color='green'), | ||||||
|         # transitions: |         # transitions: | ||||||
|         Transition('plan', 'plan', 'planned'), |         Transition('plan', 'plan', 'planned'), | ||||||
|         Transition('accept', 'accept', 'accepted'), |         Transition('accept', 'accept', 'accepted'), | ||||||
|  | @ -131,13 +132,16 @@ class WorkItemType(object): | ||||||
|     """ |     """ | ||||||
| 
 | 
 | ||||||
|     def __init__(self, name, title, description=u'',  |     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.name = name | ||||||
|         self.title = title |         self.title = title | ||||||
|         self.description = description |         self.description = description | ||||||
|         self.actions = actions or list(editingRules) |         self.actions = actions or list(editingRules) | ||||||
|         self.fields = fields or ('deadline', 'start-end', 'duration-effort') |         self.fields = fields or ('deadline', 'start-end', 'duration-effort') | ||||||
|         self.indicator = indicator |         self.indicator = indicator | ||||||
|  |         self.delegatedState = delegatedState | ||||||
|  |         self.prefillDate = prefillDate | ||||||
| 
 | 
 | ||||||
| workItemTypes = Jeep(( | workItemTypes = Jeep(( | ||||||
|     WorkItemType('work', u'Unit of Work', indicator='work_work'), |     WorkItemType('work', u'Unit of Work', indicator='work_work'), | ||||||
|  | @ -150,7 +154,13 @@ workItemTypes = Jeep(( | ||||||
|         actions=('plan', 'accept', 'finish', 'cancel',  |         actions=('plan', 'accept', 'finish', 'cancel',  | ||||||
|                  'modify', 'delegate', 'move', 'close', 'reopen'), |                  'modify', 'delegate', 'move', 'close', 'reopen'), | ||||||
|         fields =('deadline',), |         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): |     def getWorkItemType(self): | ||||||
|         name = self.workItemType |         name = self.workItemType | ||||||
|         return name and workItemTypes[name] or None |         return name and workItemTypes[name] or workItemTypes['work'] | ||||||
| 
 | 
 | ||||||
|     @property |     @property | ||||||
|     def party(self): |     def party(self): | ||||||
|  | @ -259,7 +269,7 @@ class WorkItem(Stateful, Track): | ||||||
|             xkw = dict(kw) |             xkw = dict(kw) | ||||||
|             xkw.pop('party', None) |             xkw.pop('party', None) | ||||||
|             delegated = self.createNew('delegate', userName, **xkw) |             delegated = self.createNew('delegate', userName, **xkw) | ||||||
|         delegated.state = 'delegated' |         delegated.state = self.getWorkItemType().delegatedState | ||||||
|         delegated.reindex('state') |         delegated.reindex('state') | ||||||
|         new = delegated.createNew('plan', userName, runId=0, **kw) |         new = delegated.createNew('plan', userName, runId=0, **kw) | ||||||
|         new.data['source'] = delegated.name |         new.data['source'] = delegated.name | ||||||
|  | @ -284,7 +294,8 @@ class WorkItem(Stateful, Track): | ||||||
|         new.state = self.state |         new.state = self.state | ||||||
|         new.reindex() |         new.reindex() | ||||||
|         moved.data['target'] = new.name |         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.state = self.state + '_x' | ||||||
|             self.reindex('state') |             self.reindex('state') | ||||||
|         return new |         return new | ||||||
|  |  | ||||||
 hplattner
						hplattner