Refactoring. Native support to ODF files added. A new control tag '{% tablerow_tag %}'. This tag replace the row internal XML with the control-flow a control tag near it. Used for table genation.

This commit is contained in:
Christopher Ramírez 2012-07-20 01:32:23 -06:00
parent 4973bb36ab
commit 8d5104be4c
2 changed files with 100 additions and 43 deletions

View file

@ -1,2 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<office:document-content xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rpt="http://openoffice.org/2005/report" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:css3t="http://www.w3.org/TR/css3-text/" office:version="1.2"><office:scripts/><office:font-face-decls><style:font-face style:name="Lohit Hindi1" svg:font-family="&apos;Lohit Hindi&apos;"/><style:font-face style:name="Liberation Serif" svg:font-family="&apos;Liberation Serif&apos;" style:font-family-generic="roman" style:font-pitch="variable"/><style:font-face style:name="Liberation Sans" svg:font-family="&apos;Liberation Sans&apos;" style:font-family-generic="swiss" style:font-pitch="variable"/><style:font-face style:name="Lohit Hindi" svg:font-family="&apos;Lohit Hindi&apos;" style:font-family-generic="system" style:font-pitch="variable"/><style:font-face style:name="WenQuanYi Micro Hei" svg:font-family="&apos;WenQuanYi Micro Hei&apos;" style:font-family-generic="system" style:font-pitch="variable"/></office:font-face-decls><office:automatic-styles/><office:body><office:text><text:sequence-decls><text:sequence-decl text:display-outline-level="0" text:name="Illustration"/><text:sequence-decl text:display-outline-level="0" text:name="Table"/><text:sequence-decl text:display-outline-level="0" text:name="Text"/><text:sequence-decl text:display-outline-level="0" text:name="Drawing"/></text:sequence-decls><text:p text:style-name="Standard">Hello {{ record.name }}</text:p><text:p text:style-name="Standard"/><text:p text:style-name="Standard">You are from {{ record.country }}</text:p><text:p text:style-name="Standard"/><text:p text:style-name="Standard">{% if record.age %}{{ record.age }}{% else %}Unknow age{% endif %}</text:p></office:text></office:body></office:document-content>

View file

@ -43,6 +43,10 @@ except ImportError:
PARAGRAPH_TAG = '{% paragraph_tag %}' PARAGRAPH_TAG = '{% paragraph_tag %}'
TABLEROW_TAG = '{% tablerow_tag %}'
OOO_PARAGRAPH_NODE = 'text:p'
OOO_TABLEROW_NODE = 'table:table-row'
class BaseRender(): class BaseRender():
""" """
@ -55,24 +59,24 @@ class BaseRender():
def __init__(self, xml_doc, **template_args): def __init__(self, xml_doc, **template_args):
self.template_vars = template_args self.template_vars = template_args
self.xml_document = xml.dom.minidom.parse(xml_doc) self.xml_document = xml.dom.minidom.parseString(xml_doc)
body = self.xml_document.getElementsByTagName('office:body') body = self.xml_document.getElementsByTagName('office:body')
self.content_body = body and body[0] self.content_body = body and body[0]
# ------------------------------------------------------------------------@ # ------------------------------------------------------------------------@
def get_paragraph_parent(self, node): def get_parent_of(self, node, parent_type):
""" """
Returns the first node's parent with name "text:p" Returns the first node's parent with name of parent_type
If parent "text:p" is not found, returns None. If parent "text:p" is not found, returns None.
""" """
if hasattr(node, 'parentNode'): if hasattr(node, 'parentNode'):
if node.parentNode.nodeName.lower() == 'text:p': if node.parentNode.nodeName.lower() == parent_type:
return node.parentNode return node.parentNode
else: else:
return get_paragraph_parent(node.parentNode) return self.get_parent_of(node.parentNode, parent_type)
else: else:
return None return None
@ -84,57 +88,66 @@ class BaseRender():
Once the XML have been prepared, this routine is called Once the XML have been prepared, this routine is called
to do the actual rendering. to do the actual rendering.
""" """
# print self.xml_document.toprettyxml()
template = TemplateEngine(self.xml_document.toxml()) template = TemplateEngine(self.xml_document.toxml())
return template.render(**self.template_vars) return template.render(**self.template_vars)
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
def scan_child_nodes(self, nodes): def prepare_document(self):
"""
Search in every paragraph node in the document.
"""
paragraphs = self.content_body.getElementsByTagName('text:p')
for paragraph in paragraphs:
self.scan_paragraph_child_nodes(paragraph)
def scan_paragraph_child_nodes(self, nodes):
""" """
""" """
if nodes.hasChildNodes(): if nodes.hasChildNodes():
child_nodes = nodes.childNodes child_nodes = nodes.childNodes
for node in child_nodes: for node in child_nodes:
if node.nodeType == node.TEXT_NODE: if node.nodeType == node.TEXT_NODE:
node_text = node.data.lower() self.handle_special_tags(node)
# replace a paragraph node with contained tags
# if tag PARAGRAPH_TAG is in paragraph content.
if node_text.find(PARAGRAPH_TAG) > -1:
# Get this node text:p parent
paragraph_node = self.get_paragraph_parent(node)
paragraph_parent = paragraph_node.parentNode
# Discar PARAGRAPH_TAG
pgraph_node_text = \
paragraph_node.toxml().replace(PARAGRAPH_TAG, '')
# replace text:p node's XML with its contained templates tags.
new_node_text = \
' '.join(re.findall('(\{.*?\})', pgraph_node_text))
new_node = xml_document.createTextNode(new_node_text)
paragraph_parent.replaceChild(new_node, paragraph_node)
else: else:
if node.hasChildNodes(): if node.hasChildNodes():
scan_child_nodes(node) self.scan_paragraph_child_nodes(node)
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
def handle_special_tags(self): def handle_special_tags(self, node):
""" """
""" """
paragraphs = self.content_body.getElementsByTagName('text:p') node_text = node.data.lower()
for paragraph in paragraphs: replace_node = None
self.scan_child_nodes(paragraph)
if node_text.find(PARAGRAPH_TAG) > -1:
replace_node = self.get_parent_of(node, OOO_PARAGRAPH_NODE)
note_text = replace_node.toxml().replace(PARAGRAPH_TAG, '')
elif node_text.find(TABLEROW_TAG) > -1:
replace_node = self.get_parent_of(node, OOO_TABLEROW_NODE)
note_text = replace_node.toxml().replace(TABLEROW_TAG, '')
if replace_node is not None:
paragraph_parent = replace_node.parentNode
new_node_text = \
' '.join(re.findall('(\{.*?\})', note_text))
new_node = self.xml_document.createTextNode(new_node_text)
paragraph_parent.replaceChild(new_node, replace_node)
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
@ -145,22 +158,68 @@ class BaseRender():
to parse template engine tags to parse template engine tags
""" """
self.handle_special_tags() self.prepare_document()
return self.render_with_engine() return self.render_with_engine()
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
if __name__ == "__main__": if __name__ == "__main__":
data = { from datetime import datetime
'name': u'Christopher Ramirez',
'country': 'Nicaragua' document = {
'datetime': datetime.now()
} }
render = BaseRender('content.xml', record=data) countries = [
print render.render() {'country': 'United States', 'capital': 'Washington'},
{'country': 'England', 'capital': 'London'},
{'country': 'Japan', 'capital': 'Tokio'},
{'country': 'Nicaragua', 'capital': 'Managua'},
{'country': 'Argentina', 'capital': 'Buenos aires'},
{'country': 'Chile', 'capital': 'Santiago'},
{'country': 'Mexico', 'capital': 'MExico City'},
]
# ODF is just a zipfile
input = zipfile.ZipFile( 'simple_template.odt', "r" )
# we cannot write directly to HttpResponse, so use StringIO
# text = StringIO.StringIO()
text = open('rendered.odt', 'wb')
# output document
output = zipfile.ZipFile( text, "w" )
# go through the files in source
for zi in input.filelist:
out = input.read( zi.filename )
if zi.filename == 'content.xml':
render = BaseRender(out, trademark={'owner':{}}, 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."
# output_file.open('rendered.odt', 'w')
# output_file.write(output)
# output_file.close()
# render = BaseRender('content.xml', record=data)
# print render.render()
# xml_document = xml.dom.minidom.parse('content.xml') # xml_document = xml.dom.minidom.parse('content.xml')
# doc_body = xml_document.getElementsByTagName('office:body') # doc_body = xml_document.getElementsByTagName('office:body')