Restructuration of code to a more extendable code.
This commit is contained in:
parent
39e96bc04c
commit
6019a75e87
3 changed files with 304 additions and 301 deletions
309
__init__.py
309
__init__.py
|
|
@ -24,10 +24,309 @@
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Secretary
|
Secretary
|
||||||
Take the power of Django or Jinja2 templates to OpenOffice and LibreOffice.
|
Take the power of Jinja2 templates to OpenOffice and LibreOffice.
|
||||||
|
|
||||||
Basic usage:
|
This file implements BaseRender. BaseRender prepares a XML which describes
|
||||||
from secretary.renders import BaseRender
|
ODT document content to be processed by jinja2 or Django template system.
|
||||||
|
|
||||||
to be added...
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import re
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import logging
|
||||||
|
import zipfile
|
||||||
|
import StringIO
|
||||||
|
import xml.dom.minidom
|
||||||
|
from os.path import isfile
|
||||||
|
from jinja2 import Environment, Undefined
|
||||||
|
|
||||||
|
|
||||||
|
PARAGRAPH_TAG = '{% control_paragraph %}'
|
||||||
|
TABLEROW_TAG = '{% control_tablerow %}'
|
||||||
|
TABLECELL_TAG = '{% control_tablecell %}'
|
||||||
|
|
||||||
|
ODF_PARAGRAPH_NODE = 'text:p'
|
||||||
|
ODF_TABLEROW_NODE = 'table:table-row'
|
||||||
|
ODF_TABLECELL_NODE = 'table:table-cell'
|
||||||
|
|
||||||
|
|
||||||
|
class UndefinedSilently(Undefined):
|
||||||
|
# Silently undefined,
|
||||||
|
# see http://stackoverflow.com/questions/6182498/jinja2-how-to-make-it-fail-silently-like-djangotemplate
|
||||||
|
def silently_undefined(*args, **kwargs):
|
||||||
|
return u''
|
||||||
|
|
||||||
|
return_new = lambda *args, **kwargs: UndefinedSilently()
|
||||||
|
|
||||||
|
__unicode__ = silently_undefined
|
||||||
|
__str__ = silently_undefined
|
||||||
|
__call__ = return_new
|
||||||
|
__getattr__ = return_new
|
||||||
|
|
||||||
|
# ************************************************
|
||||||
|
#
|
||||||
|
# SECRETARY FILTERS
|
||||||
|
#
|
||||||
|
# ************************************************
|
||||||
|
|
||||||
|
def pad_string(value, length=5):
|
||||||
|
value = str(value)
|
||||||
|
return value.zfill(length)
|
||||||
|
|
||||||
|
|
||||||
|
class Render():
|
||||||
|
"""
|
||||||
|
Prapares a XML string or file to be processed by a templating system.
|
||||||
|
|
||||||
|
Use example:
|
||||||
|
render = BaseRender('content/xml', var1, var2.. varN)
|
||||||
|
render.render
|
||||||
|
"""
|
||||||
|
|
||||||
|
_template = None
|
||||||
|
_environment = None
|
||||||
|
_working_template = None
|
||||||
|
_unpacked_template = None
|
||||||
|
_packed_template = None
|
||||||
|
_content_file = None
|
||||||
|
_style_file = None
|
||||||
|
_mimetype = ''
|
||||||
|
|
||||||
|
|
||||||
|
@property
|
||||||
|
def enviroment(self):
|
||||||
|
return self._environment
|
||||||
|
@enviroment.setter
|
||||||
|
def enviroment(self, value):
|
||||||
|
self._environment = value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def template(self):
|
||||||
|
return self._template
|
||||||
|
@template.setter
|
||||||
|
def template(self, value):
|
||||||
|
self._template = value
|
||||||
|
|
||||||
|
|
||||||
|
# def __init__(self, xml_doc, template_args):
|
||||||
|
def __init__(self, template, **kwargs):
|
||||||
|
"""
|
||||||
|
Builds a ODFRender instance
|
||||||
|
TODO: document
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.template = template
|
||||||
|
self._environment = Environment(undefined=UndefinedSilently)
|
||||||
|
self._environment.filters['pad'] = pad_string
|
||||||
|
|
||||||
|
|
||||||
|
def unpack_template(self):
|
||||||
|
"""
|
||||||
|
Loads the template into a ZIP file, allowing to make
|
||||||
|
CRUD operations into the ZIP archive.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if os.path.exists(self.template):
|
||||||
|
f = open(self.template, 'r')
|
||||||
|
self._unpacked_template = zipfile.ZipFile(f, "r" )
|
||||||
|
else:
|
||||||
|
self._unpacked_template = zipfile.ZipFile(self._template, "r" )
|
||||||
|
|
||||||
|
# go through the files in source
|
||||||
|
for zi in self._unpacked_template.filelist:
|
||||||
|
archive_file = self._unpacked_template.read( zi.filename )
|
||||||
|
|
||||||
|
if zi.filename == 'content.xml':
|
||||||
|
self._content_file = archive_file
|
||||||
|
elif zi.filename == 'styles.xml':
|
||||||
|
self._style_file = archive_file
|
||||||
|
elif zi.filename == 'mimetype':
|
||||||
|
self._mimetype = archive_file
|
||||||
|
|
||||||
|
def pack_document(self):
|
||||||
|
"""
|
||||||
|
Make an archive from _unpacked_template
|
||||||
|
"""
|
||||||
|
self.rendered = StringIO.StringIO()
|
||||||
|
self._packed_template = zipfile.ZipFile(self.rendered, 'a')
|
||||||
|
|
||||||
|
for zip_file in self._unpacked_template.filelist:
|
||||||
|
out = self._unpacked_template.read( zip_file.filename )
|
||||||
|
|
||||||
|
if zip_file.filename == 'mimetype':
|
||||||
|
# mimetype is stored within the ODF
|
||||||
|
mimetype = self._mimetype
|
||||||
|
|
||||||
|
if zip_file.filename == 'content.xml':
|
||||||
|
out = self._content_file
|
||||||
|
|
||||||
|
if zip_file.filename == 'styles.xml':
|
||||||
|
out = self._style_file
|
||||||
|
|
||||||
|
if sys.version_info >= (2, 7):
|
||||||
|
self._packed_template.writestr(zip_file.filename, out, zipfile.ZIP_DEFLATED)
|
||||||
|
else:
|
||||||
|
self._packed_template.writestr(zip_file.filename, out)
|
||||||
|
|
||||||
|
self._packed_template.close()
|
||||||
|
self._unpacked_template.close()
|
||||||
|
|
||||||
|
|
||||||
|
def render(self, **kwargs):
|
||||||
|
"""
|
||||||
|
Unpack and render the internal template
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.unpack_template()
|
||||||
|
self._template_vars = kwargs
|
||||||
|
|
||||||
|
# Load content.xml and style.xml file
|
||||||
|
self.content = xml.dom.minidom.parseString(self._content_file)
|
||||||
|
body = self.content.getElementsByTagName('office:body')
|
||||||
|
self.content_body = body and body[0]
|
||||||
|
|
||||||
|
self.styles = xml.dom.minidom.parseString(self._style_file)
|
||||||
|
body = self.styles.getElementsByTagName('office:master-styles')
|
||||||
|
self.headers = body and body[0]
|
||||||
|
|
||||||
|
# Render content.xml
|
||||||
|
self.prepare_template_tags(self.content_body)
|
||||||
|
template = self._environment.from_string(self.content.toxml())
|
||||||
|
result = template.render(**kwargs)
|
||||||
|
result = result.replace('\n', '<text:line-break/>')
|
||||||
|
self.content = xml.dom.minidom.parseString(result.encode('ascii', 'xmlcharrefreplace'))
|
||||||
|
self.content_body = self.content.getElementsByTagName('office:body')
|
||||||
|
|
||||||
|
# Render style.xml
|
||||||
|
self.prepare_template_tags(self.styles)
|
||||||
|
template = self._environment.from_string(self.styles.toxml())
|
||||||
|
result = template.render(**kwargs)
|
||||||
|
result = result.replace('\n', '<text:line-break/>')
|
||||||
|
self.styles = xml.dom.minidom.parseString(result.encode('ascii', 'xmlcharrefreplace'))
|
||||||
|
self.headers = None
|
||||||
|
|
||||||
|
# Save rendered content and headers
|
||||||
|
self._content_file = self.content.toxml().encode('ascii', 'xmlcharrefreplace')
|
||||||
|
|
||||||
|
self._style_file = (self.styles.toxml().encode('ascii', 'xmlcharrefreplace'))
|
||||||
|
|
||||||
|
self.pack_document()
|
||||||
|
return self.rendered
|
||||||
|
|
||||||
|
|
||||||
|
def node_parents(self, node, parent_type):
|
||||||
|
"""
|
||||||
|
Returns the first node's parent with name of parent_type
|
||||||
|
If parent "text:p" is not found, returns None.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if hasattr(node, 'parentNode'):
|
||||||
|
if node.parentNode.nodeName.lower() == parent_type:
|
||||||
|
return node.parentNode
|
||||||
|
else:
|
||||||
|
return self.node_parents(node.parentNode, parent_type)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def render_with_engine(self):
|
||||||
|
"""
|
||||||
|
Once the XML have been prepared, this routine is called
|
||||||
|
to do the actual rendering.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
template = environment.from_string(self.xml_document.toxml())
|
||||||
|
rendered = template.render(**self.template_vars)
|
||||||
|
|
||||||
|
# Replace all \n in field values with a ODT line break
|
||||||
|
rendered = rendered.replace('\n', '<text:line-break/>')
|
||||||
|
|
||||||
|
return rendered
|
||||||
|
|
||||||
|
|
||||||
|
def create_text_span_node(self, xml_document, content):
|
||||||
|
span = self.content.createElement('text:span')
|
||||||
|
text_node = self.create_text_node(self.content, content)
|
||||||
|
span.appendChild(text_node)
|
||||||
|
|
||||||
|
return span
|
||||||
|
|
||||||
|
def create_text_node(self, xml_document, text):
|
||||||
|
"""
|
||||||
|
Creates a text node
|
||||||
|
"""
|
||||||
|
return self.content.createTextNode(text)
|
||||||
|
|
||||||
|
|
||||||
|
def prepare_template_tags(self, xml_document):
|
||||||
|
"""
|
||||||
|
Search every field node in the inner template and
|
||||||
|
replace them with a <text:span> field. Flow tags are
|
||||||
|
replaced with a blank node and moved into the ancestor
|
||||||
|
tag defined in description field attribute.
|
||||||
|
"""
|
||||||
|
fields = xml_document.getElementsByTagName('text:text-input')
|
||||||
|
|
||||||
|
for field in fields:
|
||||||
|
if field.hasChildNodes():
|
||||||
|
field_content = field.childNodes[0].data.replace('\n', '')
|
||||||
|
|
||||||
|
jinja_tags = re.findall(r'(\{.*?\}*})', field_content)
|
||||||
|
if not jinja_tags:
|
||||||
|
# Field does not contains jinja template tags
|
||||||
|
continue
|
||||||
|
|
||||||
|
field_description = field.getAttribute('text:description')
|
||||||
|
|
||||||
|
if not field_description:
|
||||||
|
new_node = self.create_text_span_node(xml_document, field_content)
|
||||||
|
else:
|
||||||
|
if field_description in \
|
||||||
|
['text:p', 'table:table-row', 'table:table-cell']:
|
||||||
|
field = self.node_parents(field, field_description)
|
||||||
|
|
||||||
|
new_node = self.create_text_node(xml_document, field_content)
|
||||||
|
|
||||||
|
parent = field.parentNode
|
||||||
|
parent.insertBefore(new_node, field)
|
||||||
|
parent.removeChild(field)
|
||||||
|
|
||||||
|
|
||||||
|
def render_template(template, **kwargs):
|
||||||
|
"""
|
||||||
|
Render a ODF template file
|
||||||
|
"""
|
||||||
|
|
||||||
|
engine = Render(file)
|
||||||
|
return engine.render(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
from sys import argv
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
document = {
|
||||||
|
'datetime': datetime.now()
|
||||||
|
}
|
||||||
|
|
||||||
|
countries = [
|
||||||
|
{'country': 'United States', 'capital': 'Washington', 'cities': ['miami', 'new york', 'california', 'texas', 'atlanta']},
|
||||||
|
{'country': 'England', 'capital': 'London', 'cities': ['gales']},
|
||||||
|
{'country': 'Japan', 'capital': 'Tokio', 'cities': ['hiroshima', 'nagazaki']},
|
||||||
|
{'country': 'Nicaragua', 'capital': 'Managua', 'cities': [u'león', 'granada', 'masaya']},
|
||||||
|
{'country': 'Argentina', 'capital': 'Buenos aires'},
|
||||||
|
{'country': 'Chile', 'capital': 'Santiago'},
|
||||||
|
{'country': 'Mexico', 'capital': 'MExico City', 'cities': ['puebla', 'cancun']},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
render = Render('simple_template.odt')
|
||||||
|
result = render.render(countries=countries, document=document)
|
||||||
|
|
||||||
|
output = open('rendered.odt', 'w')
|
||||||
|
output.write(result.getvalue())
|
||||||
|
|
||||||
|
print "Template rendering finished! Check rendered.odt file."
|
||||||
296
renders.py
296
renders.py
|
|
@ -1,296 +0,0 @@
|
||||||
#!/usr/bin/python
|
|
||||||
# -*- encoding: utf-8 -*-
|
|
||||||
|
|
||||||
# * Copyright (c) 2012 Christopher Ramírez blindedbythedark [at} gmail (dot] com.
|
|
||||||
# * All rights reserved.
|
|
||||||
# *
|
|
||||||
# * Permission is hereby granted, free of charge, to any person obtaining a
|
|
||||||
# * copy of this software and associated documentation files (the "Software"),
|
|
||||||
# * to deal in the Software without restriction, including without limitation
|
|
||||||
# * the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
||||||
# * and/or sell copies of the Software, and to permit persons to whom the
|
|
||||||
# * Software is furnished to do so, subject to the following conditions:
|
|
||||||
# *
|
|
||||||
# * The above copyright notice and this permission notice shall be included in
|
|
||||||
# * all copies or substantial portions of the Software.
|
|
||||||
# *
|
|
||||||
# * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
# * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
# * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
# * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
# * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
# * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
||||||
# * DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
"""
|
|
||||||
Secretary
|
|
||||||
Take the power of Jinja2 templates to OpenOffice and LibreOffice.
|
|
||||||
|
|
||||||
This file implements BaseRender. BaseRender prepares a XML which describes
|
|
||||||
ODT document content to be processed by jinja2 or Django template system.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import re
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import logging
|
|
||||||
import zipfile
|
|
||||||
import StringIO
|
|
||||||
import xml.dom.minidom
|
|
||||||
from os.path import isfile
|
|
||||||
from jinja2 import Environment, Undefined
|
|
||||||
|
|
||||||
|
|
||||||
PARAGRAPH_TAG = '{% control_paragraph %}'
|
|
||||||
TABLEROW_TAG = '{% control_tablerow %}'
|
|
||||||
TABLECELL_TAG = '{% control_tablecell %}'
|
|
||||||
|
|
||||||
OOO_PARAGRAPH_NODE = 'text:p'
|
|
||||||
OOO_TABLEROW_NODE = 'table:table-row'
|
|
||||||
OOO_TABLECELL_NODE = 'table:table-cell'
|
|
||||||
|
|
||||||
|
|
||||||
# Silently undefined,
|
|
||||||
# see http://stackoverflow.com/questions/6182498/jinja2-how-to-make-it-fail-silently-like-djangotemplate
|
|
||||||
def silently_undefined(*args, **kwargs):
|
|
||||||
return u''
|
|
||||||
|
|
||||||
return_new = lambda *args, **kwargs: UndefinedSilently()
|
|
||||||
|
|
||||||
class UndefinedSilently(Undefined):
|
|
||||||
__unicode__ = silently_undefined
|
|
||||||
__str__ = silently_undefined
|
|
||||||
__call__ = return_new
|
|
||||||
__getattr__ = return_new
|
|
||||||
|
|
||||||
# ************************************************
|
|
||||||
#
|
|
||||||
# SECRETARY FILTERS
|
|
||||||
#
|
|
||||||
# ************************************************
|
|
||||||
|
|
||||||
def pad_string(value, length=5):
|
|
||||||
value = str(value)
|
|
||||||
return value.zfill(length)
|
|
||||||
|
|
||||||
|
|
||||||
class BaseRender():
|
|
||||||
"""
|
|
||||||
Prapares a XML string or file to be processed by a templating system.
|
|
||||||
|
|
||||||
Use example:
|
|
||||||
render = BaseRender('content/xml', var1, var2.. varN)
|
|
||||||
render.render
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, xml_doc, template_args):
|
|
||||||
self.template_vars = template_args
|
|
||||||
self.xml_document = xml.dom.minidom.parseString(xml_doc)
|
|
||||||
self.debug = template_args.get('debug', False)
|
|
||||||
|
|
||||||
body = self.xml_document.getElementsByTagName('office:body') or \
|
|
||||||
self.xml_document.getElementsByTagName('office:master-styles')
|
|
||||||
|
|
||||||
self.content_body = body and body[0]
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------@
|
|
||||||
|
|
||||||
|
|
||||||
def get_parent_of(self, node, parent_type):
|
|
||||||
"""
|
|
||||||
Returns the first node's parent with name of parent_type
|
|
||||||
If parent "text:p" is not found, returns None.
|
|
||||||
"""
|
|
||||||
|
|
||||||
if hasattr(node, 'parentNode'):
|
|
||||||
if node.parentNode.nodeName.lower() == parent_type:
|
|
||||||
return node.parentNode
|
|
||||||
else:
|
|
||||||
return self.get_parent_of(node.parentNode, parent_type)
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------@
|
|
||||||
|
|
||||||
|
|
||||||
def render_with_engine(self):
|
|
||||||
"""
|
|
||||||
Once the XML have been prepared, this routine is called
|
|
||||||
to do the actual rendering.
|
|
||||||
"""
|
|
||||||
|
|
||||||
environment = Environment(undefined=UndefinedSilently)
|
|
||||||
environment.filters['pad'] = pad_string
|
|
||||||
|
|
||||||
template = environment.from_string(self.xml_document.toxml())
|
|
||||||
rendered = template.render(**self.template_vars)
|
|
||||||
|
|
||||||
# Replace all \n in field values with a ODT line break
|
|
||||||
rendered = rendered.replace('\n', '<text:line-break/>')
|
|
||||||
|
|
||||||
# if self.debug:
|
|
||||||
# # Return a indented XML
|
|
||||||
# return xml.dom.minidom.parseString(
|
|
||||||
# rendered.encode('ascii', 'xmlcharrefreplace')).toprettyxml()
|
|
||||||
|
|
||||||
|
|
||||||
return rendered
|
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------
|
|
||||||
|
|
||||||
def create_text_span_node(self, content):
|
|
||||||
span = self.xml_document.createElement('text:span')
|
|
||||||
text_node = self.create_text_node(content)
|
|
||||||
span.appendChild(text_node)
|
|
||||||
|
|
||||||
return span
|
|
||||||
|
|
||||||
def create_text_node(self, text):
|
|
||||||
"""
|
|
||||||
Creates a text node
|
|
||||||
"""
|
|
||||||
return self.xml_document.createTextNode(text)
|
|
||||||
|
|
||||||
|
|
||||||
def prepare_document(self):
|
|
||||||
"""
|
|
||||||
Search in every field node in the document and
|
|
||||||
replace it with a <text:span> field
|
|
||||||
"""
|
|
||||||
fields = self.content_body.getElementsByTagName('text:text-input')
|
|
||||||
|
|
||||||
for field in fields:
|
|
||||||
if field.hasChildNodes():
|
|
||||||
field_content = field.childNodes[0].data
|
|
||||||
|
|
||||||
jinja_tags = re.findall(r'(\{.*?\}*})', field_content)
|
|
||||||
if not jinja_tags:
|
|
||||||
# Field does not contains jinja template tags
|
|
||||||
continue
|
|
||||||
|
|
||||||
field_description = field.getAttribute('text:description')
|
|
||||||
|
|
||||||
if not field_description:
|
|
||||||
new_node = self.create_text_span_node(field_content)
|
|
||||||
else:
|
|
||||||
if field_description in \
|
|
||||||
['text:p', 'table:table-row', 'table:table-cell']:
|
|
||||||
field = self.get_parent_of(field, field_description)
|
|
||||||
|
|
||||||
new_node = self.create_text_node(field_content)
|
|
||||||
|
|
||||||
parent = field.parentNode
|
|
||||||
parent.insertBefore(new_node, field)
|
|
||||||
parent.removeChild(field)
|
|
||||||
|
|
||||||
|
|
||||||
def render(self):
|
|
||||||
"""
|
|
||||||
render prepares the XML and the call render_with_engine
|
|
||||||
to parse template engine tags
|
|
||||||
"""
|
|
||||||
|
|
||||||
self.prepare_document()
|
|
||||||
return self.render_with_engine()
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
def render_template(template, **kwargs):
|
|
||||||
"""
|
|
||||||
Renders *template* file using *kwargs* variables.
|
|
||||||
Returns the ODF file generated.
|
|
||||||
"""
|
|
||||||
input_template = StringIO.StringIO(template)
|
|
||||||
input = zipfile.ZipFile(input_template, "r" )
|
|
||||||
rendered = StringIO.StringIO()
|
|
||||||
output = zipfile.ZipFile(rendered, 'a')
|
|
||||||
|
|
||||||
# go through the files in source
|
|
||||||
for zi in input.filelist:
|
|
||||||
out = input.read( zi.filename )
|
|
||||||
|
|
||||||
if zi.filename in ('content.xml', 'styles.xml'):
|
|
||||||
render = BaseRender(out, kwargs)
|
|
||||||
out = render.render().encode('ascii', 'xmlcharrefreplace')
|
|
||||||
|
|
||||||
elif zi.filename == 'mimetype':
|
|
||||||
# mimetype is stored within the ODF
|
|
||||||
mimetype = out
|
|
||||||
|
|
||||||
if sys.version_info >= (2, 7):
|
|
||||||
output.writestr(zi.filename, out, zipfile.ZIP_DEFLATED)
|
|
||||||
else:
|
|
||||||
output.writestr(zi.filename, out)
|
|
||||||
|
|
||||||
# close and finish
|
|
||||||
input.close()
|
|
||||||
output.close()
|
|
||||||
|
|
||||||
return rendered.getvalue()
|
|
||||||
|
|
||||||
|
|
||||||
def render_template_file(file, **kwargs):
|
|
||||||
"""
|
|
||||||
Render a ODF template file
|
|
||||||
"""
|
|
||||||
|
|
||||||
template_string = StringIO.StringIO()
|
|
||||||
template_string.write(file)
|
|
||||||
|
|
||||||
return render_template(template_string, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
from sys import argv
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
document = {
|
|
||||||
'datetime': datetime.now()
|
|
||||||
}
|
|
||||||
|
|
||||||
countries = [
|
|
||||||
{'country': 'United States', 'capital': 'Washington', 'cities': ['miami', 'new york', 'california', 'texas', 'atlanta']},
|
|
||||||
{'country': 'England', 'capital': 'London', 'cities': ['gales']},
|
|
||||||
{'country': 'Japan', 'capital': 'Tokio', 'cities': ['hiroshima', 'nagazaki']},
|
|
||||||
{'country': 'Nicaragua', 'capital': 'Managua', 'cities': [u'león', 'granada', 'masaya']},
|
|
||||||
{'country': 'Argentina', 'capital': 'Buenos aires'},
|
|
||||||
{'country': 'Chile', 'capital': 'Santiago'},
|
|
||||||
{'country': 'Mexico', 'capital': 'MExico City', 'cities': ['puebla', 'cancun']},
|
|
||||||
]
|
|
||||||
|
|
||||||
# ODF is just a zipfile
|
|
||||||
input = zipfile.ZipFile( 'simple_template.odt', "r" )
|
|
||||||
|
|
||||||
if len(argv) > 1:
|
|
||||||
if isfile(argv[1]):
|
|
||||||
input = zipfile.ZipFile(argv[1])
|
|
||||||
|
|
||||||
|
|
||||||
text = open('rendered.odt', 'wb')
|
|
||||||
output = zipfile.ZipFile( text, "w" )
|
|
||||||
|
|
||||||
# go through the files in input
|
|
||||||
for zi in input.filelist:
|
|
||||||
out = input.read( zi.filename )
|
|
||||||
|
|
||||||
if zi.filename == 'content.xml':
|
|
||||||
render = BaseRender(out, document=document, countries=countries)
|
|
||||||
out = render.render().encode('ascii', 'xmlcharrefreplace')
|
|
||||||
|
|
||||||
elif zi.filename == 'mimetype':
|
|
||||||
# mimetype is stored within the ODF
|
|
||||||
mimetype = out
|
|
||||||
|
|
||||||
output.writestr( zi.filename, out,
|
|
||||||
zipfile.ZIP_DEFLATED )
|
|
||||||
|
|
||||||
# close and finish
|
|
||||||
input.close()
|
|
||||||
output.close()
|
|
||||||
|
|
||||||
print "Template rendering finished! Check rendered.odt file."
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue