added report engine

bzr revid: nch@tinyerp.com-20090428102421-0bjwivab9it6bf2c
This commit is contained in:
Naresh Choksy 2009-04-28 15:54:21 +05:30
parent 3ed36fbdb3
commit 740bb829f9
16 changed files with 995 additions and 953 deletions

View File

@ -181,12 +181,14 @@ class browse_record(object):
fffields = map(lambda x: x[0], ffields)
datas = self._table.read(self._cr, self._uid, ids, fffields, context=self._context, load="_classic_write")
if self._fields_process:
lang = self._context.get('lang', 'en_US') or 'en_US'
lang_obj = self.pool.get('res.lang').browse(self._cr, self._uid,self.pool.get('res.lang').search(self._cr, self._uid,[('code','=',lang)])[0])
for n, f in ffields:
if f._type in self._fields_process:
for d in datas:
d[n] = self._fields_process[f._type](d[n])
if d[n]:
d[n].set_value(self._cr, self._uid, d[n], self, f)
d[n].set_value(self._cr, self._uid, d[n], self, f, lang_obj)
# create browse records for 'remote' objects
@ -2303,7 +2305,7 @@ class orm(orm_template):
# It's the first node of the parent: position = parent_left+1
if not position:
if not vals[self._parent_name]:
if not vals[self._parent_name]:
position = 1
else:
cr.execute('select parent_left from '+self._table+' where id=%s', (vals[self._parent_name],))

View File

@ -1,7 +1,7 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
# $Id$
#
@ -25,5 +25,48 @@ pageSize = {
'A5': (148.5,105)
}
odt_namespace = {
"office":"{urn:oasis:names:tc:opendocument:xmlns:office:1.0}",
"style":"{urn:oasis:names:tc:opendocument:xmlns:style:1.0}",
"text":"{urn:oasis:names:tc:opendocument:xmlns:text:1.0}",
"table":"{urn:oasis:names:tc:opendocument:xmlns:table:1.0}",
"draw":"{urn:oasis:names:tc:opendocument:xmlns:drawing:1.0}",
"fo":"{urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0}",
"xlink":"{http://www.w3.org/1999/xlink}",
"dc":"{http://purl.org/dc/elements/1.1/}",
"meta":"{urn:oasis:names:tc:opendocument:xmlns:meta:1.0}",
"number":"{urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0}",
"svg":"{urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0}",
"chart":"{urn:oasis:names:tc:opendocument:xmlns:chart:1.0}",
"dr3d":"{urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0}",
"math":"{http://www.w3.org/1998/Math/MathML}",
"form":"{urn:oasis:names:tc:opendocument:xmlns:form:1.0}",
"script":"{urn:oasis:names:tc:opendocument:xmlns:script:1.0}",
"ooo":"{http://openoffice.org/2004/office}",
"ooow":"{http://openoffice.org/2004/writer}",
"oooc":"{http://openoffice.org/2004/calc}",
"dom":"{http://www.w3.org/2001/xml-events}" }
sxw_namespace = {
"office":"{http://openoffice.org/2000/office}",
"style":"{http://openoffice.org/2000/style}",
"text":"{http://openoffice.org/2000/text}",
"table":"{http://openoffice.org/2000/table}",
"draw":"{http://openoffice.org/2000/drawing}",
"fo":"{http://www.w3.org/1999/XSL/Format}",
"xlink":"{http://www.w3.org/1999/xlink}",
"dc":"{http://purl.org/dc/elements/1.1/}",
"meta":"{http://openoffice.org/2000/meta}",
"number":"{http://openoffice.org/2000/datastyle}",
"svg":"{http://www.w3.org/2000/svg}",
"chart":"{http://openoffice.org/2000/chart}",
"dr3d":"{http://openoffice.org/2000/dr3d}",
"math":"{http://www.w3.org/1998/Math/MathML}",
"form":"{http://openoffice.org/2000/form}",
"script":"{http://openoffice.org/2000/script}",
"ooo":"{http://openoffice.org/2004/office}",
"ooow":"{http://openoffice.org/2004/writer}",
"oooc":"{http://openoffice.org/2004/calc}",
"dom":"{http://www.w3.org/2001/xml-events}"}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -1,7 +1,7 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
# $Id$
#
@ -23,10 +23,9 @@
import os
import re
#Ged> Why do we use libxml2 here instead of xml.dom like in other places of the code?
import libxml2
import libxslt
from lxml import etree
import netsvc
import pooler
@ -189,30 +188,35 @@ class report_rml(report_int):
result.freeDoc()
return xml
def create_pdf(self, xml, logo=None, title=None):
def create_pdf(self, rml, localcontext = None, logo=None, title=None):
if logo:
self.bin_datas['logo'] = logo
else:
if 'logo' in self.bin_datas:
del self.bin_datas['logo']
obj = render.rml(xml, self.bin_datas, tools.config['root_path'],title)
obj = render.rml(rml, localcontext, self.bin_datas, tools.config['root_path'],title)
obj.render()
return obj.get()
def create_html(self, xml, logo=None, title=None):
obj = render.rml2html(xml, self.bin_datas)
def create_html(self, rml, localcontext = None, logo=None, title=None):
obj = render.rml2html(rml, localcontext, self.bin_datas)
obj.render()
return obj.get()
def create_raw(self, xml, logo=None, title=None):
return xml
def create_raw(self,rml, localcontext = None, logo=None, title=None):
obj = render.odt2odt(etree.XML(rml),localcontext)
obj.render()
return etree.tostring(obj.get())
def create_sxw(self, path, logo=None, title=None):
return path
def create_odt(self, data, logo=None, title=None):
return data
def create_sxw(self,rml,localcontext = None):
obj = render.odt2odt(rml,localcontext)
obj.render()
return obj.get()
def create_odt(self,rml,localcontext = None):
obj = render.odt2odt(rml,localcontext)
obj.render()
return obj.get()
from report_sxw import report_sxw

71
bin/report/preprocess.py Normal file
View File

@ -0,0 +1,71 @@
from lxml import etree
import re
rml_parents = ['tr','story','section']
sxw_parents = ['{http://openoffice.org/2000/table}table-row','{http://openoffice.org/2000/office}body','{http://openoffice.org/2000/text}section']
class report(object):
def preprocess_rml(self, root_node,type='pdf'):
_regex1 = re.compile("\[\[(.*?)(repeatIn\(.*?\s*,\s*[\'\"].*?[\'\"]\s*(?:,\s*(.*?)\s*)?\s*\))(.*?)\]\]")
_regex11= re.compile("\[\[(.*?)(repeatIn\(.*?\s*\(.*?\s*[\'\"].*?[\'\"]\s*\),[\'\"].*?[\'\"](?:,\s*(.*?)\s*)?\s*\))(.*?)\]\]")
_regex2 = re.compile("\[\[(.*?)(removeParentNode\(\s*(?:['\"](.*?)['\"])\s*\))(.*?)\]\]")
_regex3 = re.compile("\[\[\s*(.*?setTag\(\s*['\"](.*?)['\"]\s*,\s*['\"].*?['\"]\s*(?:,.*?)?\).*?)\s*\]\]")
for node in root_node:
if node.text:
def _sub3(txt):
n = node
while n.tag != txt.group(2):
n = n.getparent()
n.set('rml_tag', txt.group(1))
return "[[ '' ]]"
def _sub2(txt):
if txt.group(3):
n = node
try:
while n.tag != txt.group(3):
n = n.getparent()
except:
n = node
else:
n = node.getparent()
n.set('rml_except', txt.group(0)[2:-2])
return txt.group(0)
def _sub1(txt):
if len(txt.group(4)) > 1:
return []
match = rml_parents
if type in ['odt','sxw']:
match = sxw_parents
if txt.group(3):
match = [txt.group(3)]
n = node
while n.tag not in match:
n = n.getparent()
n.set('rml_loop', txt.group(2))
return '[['+txt.group(1)+"''"+txt.group(4)+']]'
t = _regex1.sub(_sub1, node.text)
if t == []:
t = _regex11.sub(_sub1, node.text)
t = _regex3.sub(_sub3, t)
node.text = _regex2.sub(_sub2, t)
self.preprocess_rml(node,type)
return root_node
if __name__=='__main__':
node = etree.XML('''<story>
<para>This is a test[[ setTag('para','xpre') ]]</para>
<blockTable>
<tr>
<td><para>Row 1 [[ setTag('tr','tr',{'style':'TrLevel'+str(a['level']), 'paraStyle':('Level'+str(a['level']))}) ]] </para></td>
<td>Row 2 [[ True and removeParentNode('td') ]] </td>
</tr><tr>
<td>Row 1 [[repeatIn(o.order_line,'o')]] </td>
<td>Row 2</td>
</tr>
</blockTable>
<p>This isa test</p>
</story>''')
a = report()
result = a.preprocess_rml(node)
print etree.tostring(result)

View File

@ -1,7 +1,7 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
# $Id$
#
@ -25,7 +25,7 @@ import pooler
import tools
from report import render
from lxml import etree
from xml.dom import minidom
import libxml2
import libxslt
@ -38,20 +38,25 @@ class report_printscreen_list(report_int):
def _parse_node(self, root_node):
result = []
for node in root_node.childNodes:
if node.localName == 'field':
attrsa = node.attributes
for node in root_node.getchildren():
if node.tag == 'field':
#attrsa = node.attributes
attrsa = node.attrib
print "typppppp",type(attrsa),dir(attrsa)
attrs = {}
if not attrsa is None:
for i in range(attrsa.length):
attrs[attrsa.item(i).localName] = attrsa.item(i).nodeValue
for key,val in attrsa.items():
attrs[key] = val
#for i in range(attrsa.length):
# attrs[attrsa.item(i).localName] = attrsa.item(i).nodeValue
result.append(attrs['name'])
else:
result.extend(self._parse_node(node))
return result
def _parse_string(self, view):
dom = minidom.parseString(view)
#dom = minidom.parseString(view)
dom = etree.XML(view)
return self._parse_node(dom)
def create(self, cr, uid, ids, datas, context=None):

View File

@ -23,10 +23,8 @@
from report.interface import report_int
import pooler
import tools
from lxml import etree
from report import render
from xml.dom import minidom
import libxml2
import libxslt
@ -38,20 +36,20 @@ class report_printscreen_list(report_int):
def _parse_node(self, root_node):
result = []
for node in root_node.childNodes:
if node.localName == 'field':
attrsa = node.attributes
for node in root_node.getchildren():
if node.tag == 'field':
attrsa = node.attrib
attrs = {}
if not attrsa is None:
for i in range(attrsa.length):
attrs[attrsa.item(i).localName] = attrsa.item(i).nodeValue
for key,val in attrsa.items():
attrs[key] = val
result.append(attrs['name'])
else:
result.extend(self._parse_node(node))
return result
def _parse_string(self, view):
dom = minidom.parseString(unicode(view, 'utf-8').encode('utf-8'))
dom = etree.XML(unicode(view, 'utf-8').encode('utf-8'))
return self._parse_node(dom)
def create(self, cr, uid, ids, datas, context=None):
@ -79,18 +77,15 @@ class report_printscreen_list(report_int):
def _create_table(self, uid, ids, fields, fields_order, results, context, title=''):
pageSize=[297.0, 210.0]
impl = minidom.getDOMImplementation()
new_doc = impl.createDocument(None, "report", None)
new_doc = etree.Element("report")
config = etree.Element("config")
# build header
config = new_doc.createElement("config")
def _append_node(name, text):
n = new_doc.createElement(name)
t = new_doc.createTextNode(text)
n.appendChild(t)
config.appendChild(n)
n = etree.Element(name)
n.text = text
config.append(n)
_append_node('date', time.strftime('%d/%m/%Y'))
_append_node('PageSize', '%.2fmm,%.2fmm' % tuple(pageSize))
@ -126,19 +121,17 @@ class report_printscreen_list(report_int):
l[pos] = strmax * s / t
_append_node('tableSize', ','.join(map(str,l)) )
new_doc.childNodes[0].appendChild(config)
header = new_doc.createElement("header")
new_doc.append(config)
header=etree.Element("header")
for f in fields_order:
field = new_doc.createElement("field")
field_txt = new_doc.createTextNode(fields[f]['string'] or '')
field.appendChild(field_txt)
header.appendChild(field)
field = etree.Element("field")
field.text = fields[f]['string'] or ''
header.append(field)
new_doc.childNodes[0].appendChild(header)
lines = new_doc.createElement("lines")
new_doc.append(header)
lines = etree.Element("lines")
tsum = []
count = len(fields_order)
@ -147,7 +140,7 @@ class report_printscreen_list(report_int):
for line in results:
node_line = new_doc.createElement("row")
node_line = etree.Element("row")
count = -1
for f in fields_order:
@ -169,48 +162,46 @@ class report_printscreen_list(report_int):
precision=(('digits' in fields[f]) and fields[f]['digits'][1]) or 2
line[f]='%.2f'%(line[f])
col = new_doc.createElement("col")
col.setAttribute('para','yes')
col.setAttribute('tree','no')
col = etree.Element("col")
col.set('para','yes')
col.set('tree','no')
if line[f] != None:
txt = new_doc.createTextNode(tools.ustr(line[f] or ''))
col.text = tools.ustr(line[f] or '')
if temp[count] == 1:
tsum[count] = float(tsum[count]) + float(line[f]);
else:
txt = new_doc.createTextNode('/')
col.appendChild(txt)
node_line.appendChild(col)
lines.appendChild(node_line)
node_line = new_doc.createElement("row")
lines.appendChild(node_line)
node_line = new_doc.createElement("row")
col.text = '/'
node_line.append(col)
lines.append(node_line)
node_line = etree.Element("row")
lines.append(node_line)
for f in range(0,count+1):
col = new_doc.createElement("col")
col.setAttribute('para','yes')
col.setAttribute('tree','no')
col = etree.Element("col")
col.set('para','yes')
col.set('tree','no')
if tsum[f] != None:
if tsum[f] >= 0.01 :
total = '%.2f'%(tsum[f])
txt = new_doc.createTextNode(str(total or ''))
txt = str(total or '')
else :
txt = new_doc.createTextNode(str(tsum[f] or ''))
txt = str(tsum[f] or '')
else:
txt = new_doc.createTextNode('/')
txt = '/'
if f == 0:
txt = new_doc.createTextNode('Total')
txt ='Total'
col.appendChild(txt)
node_line.appendChild(col)
col.text = txt
node_line.append(col)
lines.appendChild(node_line)
lines.append(node_line)
new_doc.childNodes[0].appendChild(lines)
new_doc.append(lines)
styledoc = libxml2.parseFile(os.path.join(tools.config['root_path'],'addons/base/report/custom_new.xsl'))
style = libxslt.parseStylesheetDoc(styledoc)
doc = libxml2.parseDoc(new_doc.toxml(encoding='utf-8'))
doc = libxml2.parseDoc(etree.tostring(new_doc))
rml_obj = style.applyStylesheet(doc, None)
rml = style.saveResultToString(rml_obj)
self.obj = render.rml(rml, title=self.title)

View File

@ -1,7 +1,7 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
# $Id$
#
@ -21,7 +21,7 @@
##############################################################################
from simple import simple
from rml import rml, rml2html
from rml import rml, rml2html, odt2odt
from render import render

View File

@ -0,0 +1,22 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
# $Id$
#
# 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 3 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, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from odt2odt import parseNode

View File

@ -0,0 +1,67 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
# $Id$
#
# 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 3 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, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from report.render.rml2pdf import utils
from lxml import etree
import copy
class odt2odt(object):
def __init__(self, odt, localcontext):
self.localcontext = localcontext
self.etree = odt
self._node = None
def render(self):
def process_text(node,new_node):
if new_node.tag in ['story','tr','section']:
new_node.attrib.clear()
for child in utils._child_get(node, self):
new_child = copy.deepcopy(child)
new_node.append(new_child)
if len(child):
for n in new_child:
new_child.remove(n)
process_text(child, new_child)
else:
new_child.text = utils._process_text(self, child.text)
self._node = copy.deepcopy(self.etree)
for n in self._node:
self._node.remove(n)
process_text(self.etree, self._node)
return self._node
def parseNode(node, localcontext = {}):
body = node.getchildren()[-1]
elements = body.findall(localcontext['name_space']["text"]+"p")
for pe in elements:
e = pe.findall(localcontext['name_space']["text"]+"drop-down")
for de in e:
pp=de.getparent()
for cnd in de.getchildren():
if cnd.text:
pe.append(cnd)
pp.remove(de)
r = odt2odt(node, localcontext)
return r.render()

View File

@ -1,7 +1,7 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
# $Id$
#
@ -23,26 +23,42 @@
import render
import rml2pdf
import rml2html as htmlizer
import odt2odt as odt
class rml(render.render):
def __init__(self, xml, datas={}, path='.',title=None):
def __init__(self, rml, localcontext = None, datas={}, path='.', title=None):
render.render.__init__(self, datas)
self.xml = xml
self.localcontext = localcontext
self.rml = rml
self.output_type = 'pdf'
self.path = path
self.title=title
def _render(self):
return rml2pdf.parseString(self.xml, images=self.bin_datas, path=self.path,title=self.title)
return rml2pdf.parseNode(self.rml, self.localcontext, images=self.bin_datas, path=self.path,title=self.title)
class rml2html(render.render):
def __init__(self, xml, datas={}):
def __init__(self, rml,localcontext = None, datas = {}):
super(rml2html, self).__init__(datas)
self.xml = xml
self.rml = rml
self.localcontext = localcontext
self.output_type = 'html'
def _render(self):
return htmlizer.parseString(self.xml)
return htmlizer.parseString(self.rml,self.localcontext)
class odt2odt(render.render):
def __init__(self, rml, localcontext = None, datas = {}):
render.render.__init__(self, datas)
self.rml_dom = rml
self.localcontext = localcontext
self.output_type = 'odt'
def _render(self):
return odt.parseNode(self.rml_dom,self.localcontext)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -1,7 +1,7 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
# $Id$
#
@ -38,17 +38,18 @@
import sys
import cStringIO
import xml.dom.minidom
from lxml import etree
import copy
import utils
from report.render.rml2pdf import utils
class _flowable(object):
def __init__(self, template, doc):
def __init__(self, template, doc, localcontext = None):
self._tags = {
'title': self._tag_title,
'spacer': self._tag_spacer,
'para': self._tag_para,
'section':self._section,
'nextFrame': self._tag_next_frame,
'blockTable': self._tag_table,
'pageBreak': self._tag_page_break,
@ -56,6 +57,8 @@ class _flowable(object):
}
self.template = template
self.doc = doc
self.localcontext = localcontext
self._cache = {}
def _tag_page_break(self, node):
return '<br/>'*3
@ -70,43 +73,72 @@ class _flowable(object):
return result
def _tag_title(self, node):
node.tagName='h1'
return node.toxml()
node.tag='h1'
return etree.tostring(node)
def _tag_spacer(self, node):
length = 1+int(utils.unit_get(node.getAttribute('length')))/35
length = 1+int(utils.unit_get(node.get('length')))/35
return "<br/>"*length
def _tag_table(self, node):
node.tagName='table'
if node.hasAttribute('colWidths'):
sizes = map(lambda x: utils.unit_get(x), node.getAttribute('colWidths').split(','))
tr = self.doc.createElement('tr')
new_node = copy.deepcopy(node)
for child in new_node:
new_node.remove(child)
new_node.tag = 'table'
def process(node,new_node):
for child in utils._child_get(node,self):
new_child = copy.deepcopy(child)
new_node.append(new_child)
if len(child):
for n in new_child:
new_child.remove(n)
process(child, new_child)
else:
new_child.text = utils._process_text(self, child.text)
new_child.tag = 'p'
try:
if new_child.get('style').find('terp_tblheader')!= -1:
new_node.tag = 'th'
except:
pass
process(node,new_node)
if new_node.get('colWidths',False):
sizes = map(lambda x: utils.unit_get(x), new_node.get('colWidths').split(','))
tr = etree.Element('tr')
for s in sizes:
td = self.doc.createElement('td')
td.setAttribute("width", str(s))
tr.appendChild(td)
node.appendChild(tr)
return node.toxml()
td = etree.Element('td')
td.set("width", str(s))
tr.append(td)
new_node.append(tr)
return etree.tostring(new_node)
def _tag_para(self, node):
node.tagName='p'
if node.hasAttribute('style'):
node.setAttribute('class', node.getAttribute('style'))
return node.toxml()
new_node = copy.deepcopy(node)
new_node.tag = 'p'
if new_node.attrib.get('style',False):
new_node.set('class', new_node.get('style'))
new_node.text = utils._process_text(self, node.text)
return etree.tostring(new_node)
def _section(self, node):
result = ''
for child in utils._child_get(node, self):
if child.tag in self._tags:
result += self._tags[child.tag](child)
return result
def render(self, node):
result = self.template.start()
result += self.template.frame_start()
for n in node.childNodes:
if n.nodeType==node.ELEMENT_NODE:
if n.localName in self._tags:
result += self._tags[n.localName](n)
else:
pass
#print 'tag', n.localName, 'not yet implemented!'
for n in utils._child_get(node, self):
if n.tag in self._tags:
result += self._tags[n.tag](n)
else:
pass
result += self.template.frame_stop()
result += self.template.end()
return result.encode('utf-8').replace('"',"\'").replace('°','&deg;')
class _rml_tmpl_tag(object):
def __init__(self, *args):
pass
@ -132,21 +164,22 @@ class _rml_tmpl_frame(_rml_tmpl_tag):
def tag_mergeable(self):
return False
# An awfull workaround since I don't really understand the semantic behind merge.
def merge(self, frame):
pass
class _rml_tmpl_draw_string(_rml_tmpl_tag):
def __init__(self, node, style):
self.posx = utils.unit_get(node.getAttribute('x'))
self.posy = utils.unit_get(node.getAttribute('y'))
def __init__(self, node, style,localcontext = {}):
self.localcontext = localcontext
self.posx = utils.unit_get(node.get('x'))
self.posy = utils.unit_get(node.get('y'))
aligns = {
'drawString': 'left',
'drawRightString': 'right',
'drawCentredString': 'center'
}
align = aligns[node.localName]
self.pos = [(self.posx, self.posy, align, utils.text_get(node), style.get('td'), style.font_size_get('td'))]
align = aligns[node.tag]
self.pos = [(self.posx, self.posy, align, utils._process_text(self, node.text), style.get('td'), style.font_size_get('td'))]
def tag_start(self):
self.pos.sort()
@ -171,8 +204,9 @@ class _rml_tmpl_draw_string(_rml_tmpl_tag):
self.pos+=ds.pos
class _rml_tmpl_draw_lines(_rml_tmpl_tag):
def __init__(self, node, style):
coord = [utils.unit_get(x) for x in utils.text_get(node).split(' ')]
def __init__(self, node, style, localcontext = {}):
self.localcontext = localcontext
coord = [utils.unit_get(x) for x in utils._process_text(self, node.text).split(' ')]
self.ok = False
self.posx = coord[0]
self.posy = coord[1]
@ -188,20 +222,20 @@ class _rml_tmpl_draw_lines(_rml_tmpl_tag):
return ''
class _rml_stylesheet(object):
def __init__(self, stylesheet, doc):
def __init__(self, localcontext, stylesheet, doc):
self.doc = doc
self.localcontext = localcontext
self.attrs = {}
self._tags = {
'fontSize': lambda x: ('font-size',str(utils.unit_get(x))+'px'),
'fontSize': lambda x: ('font-size',str(utils.unit_get(x)+5.0)+'px'),
'alignment': lambda x: ('text-align',str(x))
}
result = ''
for ps in stylesheet.getElementsByTagName('paraStyle'):
for ps in stylesheet.findall('paraStyle'):
attr = {}
attrs = ps.attributes
for i in range(attrs.length):
name = attrs.item(i).localName
attr[name] = ps.getAttribute(name)
attrs = ps.attrib
for key, val in attrs.items():
attr[key] = val
attrs = []
for a in attr:
if a in self._tags:
@ -217,13 +251,13 @@ class _rml_draw_style(object):
def __init__(self):
self.style = {}
self._styles = {
'fill': lambda x: {'td': {'color':x.getAttribute('color')}},
'setFont': lambda x: {'td': {'font-size':x.getAttribute('size')+'px'}},
'stroke': lambda x: {'hr': {'color':x.getAttribute('color')}},
'fill': lambda x: {'td': {'color':x.get('color')}},
'setFont': lambda x: {'td': {'font-size':x.get('size')+'px'}},
'stroke': lambda x: {'hr': {'color':x.get('color')}},
}
def update(self, node):
if node.localName in self._styles:
result = self._styles[node.localName](node)
if node.tag in self._styles:
result = self._styles[node.tag](node)
for key in result:
if key in self.style:
self.style[key].update(result[key])
@ -239,8 +273,9 @@ class _rml_draw_style(object):
return ';'.join(['%s:%s' % (x[0],x[1]) for x in self.style[tag].items()])
class _rml_template(object):
def __init__(self, template):
def __init__(self, template, localcontext=None):
self.frame_pos = -1
self.localcontext = localcontext
self.frames = []
self.template_order = []
self.page_template = {}
@ -252,20 +287,23 @@ class _rml_template(object):
'lines': _rml_tmpl_draw_lines
}
self.style = _rml_draw_style()
for pt in template.getElementsByTagName('pageTemplate'):
rc = 'data:image/png;base64,'
self.data = ''
for pt in template.findall('pageTemplate'):
frames = {}
id = pt.getAttribute('id')
id = pt.get('id')
self.template_order.append(id)
for tmpl in pt.getElementsByTagName('frame'):
posy = int(utils.unit_get(tmpl.getAttribute('y1'))) #+utils.unit_get(tmpl.getAttribute('height')))
posx = int(utils.unit_get(tmpl.getAttribute('x1')))
frames[(posy,posx,tmpl.getAttribute('id'))] = _rml_tmpl_frame(posx, utils.unit_get(tmpl.getAttribute('width')))
for tmpl in template.getElementsByTagName('pageGraphics'):
for n in tmpl.childNodes:
if n.nodeType==n.ELEMENT_NODE:
if n.localName in self._tags:
t = self._tags[n.localName](n, self.style)
frames[(t.posy,t.posx,n.localName)] = t
for tmpl in pt.findall('frame'):
posy = int(utils.unit_get(tmpl.get('y1')))
posx = int(utils.unit_get(tmpl.get('x1')))
frames[(posy,posx,tmpl.get('id'))] = _rml_tmpl_frame(posx, utils.unit_get(tmpl.get('width')))
for tmpl in pt.findall('pageGraphics'):
for n in tmpl.getchildren():
if n.tag == 'image':
self.data = rc + utils._process_text(self, n.text)
if n.tag in self._tags:
t = self._tags[n.tag](n, self.style,self.localcontext)
frames[(t.posy,t.posx,n.tag)] = t
else:
self.style.update(n)
keys = frames.keys()
@ -318,7 +356,7 @@ class _rml_template(object):
def start(self):
return ''
def end(self):
result = ''
while not self.loop:
@ -327,9 +365,10 @@ class _rml_template(object):
return result
class _rml_doc(object):
def __init__(self, data):
self.dom = xml.dom.minidom.parseString(data)
self.filename = self.dom.documentElement.getAttribute('filename')
def __init__(self, data, localcontext):
self.dom = etree.XML(data)
self.localcontext = localcontext
self.filename = self.dom.get('filename')
self.result = ''
def render(self, out):
@ -341,28 +380,28 @@ class _rml_doc(object):
p {margin:0px; font-size:12px;}
td {font-size:14px;}
'''
style = self.dom.documentElement.getElementsByTagName('stylesheet')[0]
s = _rml_stylesheet(style, self.dom)
style = self.dom.findall('stylesheet')[0]
s = _rml_stylesheet(self.localcontext, style, self.dom)
self.result += s.render()
self.result+='''
</style>
'''
# f = _flowable(template, self.dom)
list_story =[]
storys= self.dom.documentElement.getElementsByTagName('story')
for story in storys :
template = _rml_template(self.dom.documentElement.getElementsByTagName('template')[0])
f = _flowable(template, self.dom)
for story in utils._child_get(self.dom, self, 'story'):
template = _rml_template(self.dom.findall('template')[0], self.localcontext)
f = _flowable(template, self.dom, localcontext = self.localcontext)
story_text = f.render(story)
list_story.append(story_text)
del f
if template.data:
tag = '''<img src = '%s' width=80 height=72/>'''%(template.data)
else:
tag = ''
self.result +='''
<script type="text/javascript">
var indexer = 0;
var aryTest = %s ;
var aryTest = %s ;
function nextData()
{
if(indexer < aryTest.length -1)
@ -378,22 +417,23 @@ class _rml_doc(object):
indexer -= 1;
document.getElementById("tiny_data").innerHTML=aryTest[indexer];
}
}
</script>
}
</script>
</head>
<body>
%s
<div id="tiny_data">
%s
</div>
</div>
<br>
<input type="button" value="next" onclick="nextData();">
<input type="button" value="prev" onclick="prevData();">
</body></html>'''%(list_story,list_story[0])
</body></html>'''%(list_story,tag,list_story[0])
out.write( self.result)
def parseString(data, fout=None):
r = _rml_doc(data)
def parseString(data,localcontext = {}, fout=None):
r = _rml_doc(data, localcontext)
if fout:
fp = file(fout,'wb')
r.render(fp)
@ -404,7 +444,7 @@ def parseString(data, fout=None):
r.render(fp)
return fp.getvalue()
def trml2pdf_help():
def rml2html_help():
print 'Usage: rml2html input.rml >output.html'
print 'Render the standard input (RML) and output an HTML file'
sys.exit(0)
@ -412,7 +452,7 @@ def trml2pdf_help():
if __name__=="__main__":
if len(sys.argv)>1:
if sys.argv[1]=='--help':
trml2pdf_help()
rml2html_help()
print parseString(file(sys.argv[1], 'r').read()),
else:
print 'Usage: trml2pdf input.rml >output.pdf'

View File

@ -1,7 +1,7 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
# $Id$
#
@ -24,16 +24,9 @@ import re
import reportlab
import reportlab.lib.units
def text_get(node):
rc = ''
for node in node.childNodes:
if node.nodeType == node.TEXT_NODE:
rc = rc + node.data
return rc
units = [
(re.compile('^(-?[0-9\.]+)\s*in$'), reportlab.lib.units.inch),
(re.compile('^(-?[0-9\.]+)\s*cm$'), reportlab.lib.units.cm),
(re.compile('^(-?[0-9\.]+)\s*cm$'), reportlab.lib.units.cm),
(re.compile('^(-?[0-9\.]+)\s*mm$'), reportlab.lib.units.mm),
(re.compile('^(-?[0-9\.]+)\s*px$'), 0.7),
(re.compile('^(-?[0-9\.]+)\s*$'), 1)
@ -48,9 +41,9 @@ def unit_get(size):
return False
def tuple_int_get(node, attr_name, default=None):
if not node.hasAttribute(attr_name):
if not node.get(attr_name):
return default
res = [int(x) for x in node.getAttribute(attr_name).split(',')]
res = [int(x) for x in node.get(attr_name).split(',')]
return res
def bool_get(value):
@ -59,16 +52,16 @@ def bool_get(value):
def attr_get(node, attrs, dict={}):
res = {}
for name in attrs:
if node.hasAttribute(name):
res[name] = unit_get(node.getAttribute(name))
if node.get(name):
res[name] = unit_get(node.get(name))
for key in dict:
if node.hasAttribute(key):
if node.get(key):
if dict[key]=='str':
res[key] = str(node.getAttribute(key))
res[key] = str(node.get(key))
elif dict[key]=='bool':
res[key] = bool_get(node.getAttribute(key))
res[key] = bool_get(node.get(key))
elif dict[key]=='int':
res[key] = int(node.getAttribute(key))
res[key] = int(node.get(key))
return res
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -1,7 +1,7 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
# $Id$
#
@ -20,24 +20,5 @@
#
##############################################################################
# trml2pdf - An RML to PDF converter
# Copyright (C) 2003, Fabien Pinckaers, UCL, FSA
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
from trml2pdf import parseString
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
from trml2pdf import parseString, parseNode

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
# $Id$
#
@ -39,12 +39,70 @@
import re
import reportlab
from lxml import etree
_regex = re.compile('\[\[(.+?)\]\]')
def _child_get(node, self=None, tagname=None):
tr_flag = True
for n in node:
if self and self.localcontext and n.get('rml_loop', False):
oldctx = self.localcontext
for ctx in eval(n.get('rml_loop'),{}, self.localcontext):
tr_flag = False
self.localcontext.update(ctx)
if (tagname is None) or (n.tag==tagname):
if n.get('rml_except', False):
try:
eval(n.get('rml_except'), {}, self.localcontext)
except:
continue
if n.get('rml_tag'):
try:
(tag,attr) = eval(n.get('rml_tag'),{}, self.localcontext)
n2 = copy.copy(n)
n2.tag = tag
n2.attrib.update(attr)
yield n2
except:
yield n
else:
yield n
self.localcontext = oldctx
if n.tag == 'tr' and tr_flag:
yield n
continue
if self and self.localcontext and n.get('rml_except', False):
try:
eval(n.get('rml_except'), {}, self.localcontext)
except:
continue
if (tagname is None) or (n.tag==tagname):
yield n
def _process_text(self, txt):
if not self.localcontext:
return txt
if not txt:
return ''
result = ''
sps = _regex.split(txt)
while sps:
# This is a simple text to translate
result += self.localcontext.get('translate', lambda x:x)(sps.pop(0))
if sps:
try:
txt2 = eval(sps.pop(0),self.localcontext)
except:
txt2 = ''
if type(txt2)==type('') or type(txt2)==type(u''):
result += txt2
return result
def text_get(node):
rc = ''
for node in node.childNodes:
if node.nodeType == node.TEXT_NODE:
rc = rc + node.data
for node in node.getchildren():
rc = rc + node.text
return rc
units = [
@ -63,9 +121,9 @@ def unit_get(size):
return False
def tuple_int_get(node, attr_name, default=None):
if not node.hasAttribute(attr_name):
if not node.get(attr_name):
return default
res = [int(x) for x in node.getAttribute(attr_name).split(',')]
res = [int(x) for x in node.get(attr_name).split(',')]
return res
def bool_get(value):
@ -74,19 +132,18 @@ def bool_get(value):
def attr_get(node, attrs, dict={}):
res = {}
for name in attrs:
if node.hasAttribute(name):
res[name] = unit_get(node.getAttribute(name))
if node.get(name):
res[name] = unit_get(node.get(name))
for key in dict:
if node.hasAttribute(key):
if node.get(key):
if dict[key]=='str':
res[key] = str(node.getAttribute(key))
res[key] = str(node.get(key))
elif dict[key]=='bool':
res[key] = bool_get(node.getAttribute(key))
res[key] = bool_get(node.get(key))
elif dict[key]=='int':
res[key] = int(node.getAttribute(key))
res[key] = int(node.get(key))
elif dict[key]=='unit':
res[key] = unit_get(node.getAttribute(key))
res[key] = unit_get(node.get(key))
return res
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -20,23 +20,26 @@
#
##############################################################################
from interface import report_rml
from lxml import etree
import StringIO
import cStringIO
import base64
import copy
import ir
import locale
import mx.DateTime
import netsvc
import os
import osv
import pooler
import re
import time
from interface import report_rml
import preprocess
import ir
import netsvc
import osv
import pooler
import tools
import warnings
import xml.dom.minidom
import zipfile
import common
DT_FORMAT = '%Y-%m-%d'
DHM_FORMAT = '%Y-%m-%d %H:%M:%S'
@ -64,19 +67,14 @@ rml2sxw = {
'para': 'p',
}
class _format(object):
def set_value(self, cr, uid, name, object, field):
#super(_date_format, self).__init__(self)
pool_lang = pooler.get_pool(cr.dbname).get('res.lang')
def set_value(self, cr, uid, name, object, field, lang_obj):
self.object = object
self._field = field
self.name = name
lang = self.object._context.get('lang', 'en_US') or 'en_US'
lids = pool_lang.search(cr, uid,[('code','=',lang)])
if lids:
self.lang_obj = pool_lang.browse(cr, uid,lids[0])
else:
self.lang_obj = False
self.lang_obj = lang_obj
class _float_format(float, _format):
@ -88,10 +86,7 @@ class _float_format(float, _format):
digits = 2
if hasattr(self,'_field') and hasattr(self._field, 'digits') and self._field.digits:
digits = self._field.digits[1]
if self.lang_obj:
return self.lang_obj.format('%.' + str(digits) + 'f', self.name, True)
else:
return str(self.name)
return self.lang_obj.format('%.' + str(digits) + 'f', self.name, True)
return self.val
class _int_format(int, _format):
@ -101,13 +96,9 @@ class _int_format(int, _format):
def __str__(self):
if hasattr(self,'lang_obj'):
if self.lang_obj:
return self.lang_obj.format('%.d', self.name, True)
else:
return str(self.name)
return self.lang_obj.format('%.d', self.name, True)
return self.val
class _date_format(str, _format):
def __init__(self,value):
super(_date_format, self).__init__()
@ -117,10 +108,7 @@ class _date_format(str, _format):
if self.val:
if hasattr(self,'name') and (self.name):
date = mx.DateTime.strptime(self.name,DT_FORMAT)
if self.lang_obj:
return date.strftime(self.lang_obj.date_format)
else:
return date.strftime('%m/%d/%Y')
return date.strftime(self.lang_obj.date_format)
return self.val
class _dttime_format(str, _format):
@ -132,10 +120,7 @@ class _dttime_format(str, _format):
if self.val:
if hasattr(self,'name') and self.name:
datetime = mx.DateTime.strptime(self.name,DHM_FORMAT)
if self.lang_obj:
return datetime.strftime(self.lang_obj.date_format+ " " + self.lang_obj.time_format)
else:
return datetime.strftime('%m/%d/%Y %H:%M:%S')
return datetime.strftime(self.lang_obj.date_format+ " " + self.lang_obj.time_format)
return self.val
@ -161,31 +146,6 @@ class browse_record_list(list):
def __str__(self):
return "browse_record_list("+str(len(self))+")"
def repeatIn(self, name):
warnings.warn('Use repeatIn(object_list, \'variable\')', DeprecationWarning)
node = self.context['_node']
parents = self.context.get('parents', rml_parents)
node.data = ''
while True:
if not node.parentNode:
break
node = node.parentNode
if node.nodeType == node.ELEMENT_NODE and node.localName in parents:
break
parent_node = node
if not len(self):
return None
nodes = [(0,node)]
for i in range(1,len(self)):
newnode = parent_node.cloneNode(1)
n = parent_node.parentNode
n.insertBefore(newnode, parent_node)
nodes.append((i,newnode))
for i,node in nodes:
self.context[name] = self[i]
self.context['_self']._parse_node(node)
return None
class rml_parse(object):
def __init__(self, cr, uid, name, parents=rml_parents, tag=rml_tag, context=None):
if not context:
@ -193,7 +153,7 @@ class rml_parse(object):
self.cr = cr
self.uid = uid
self.pool = pooler.get_pool(cr.dbname)
user = self.pool.get('res.users').browse(cr, uid, uid, fields_process=_fields_process)
user = self.pool.get('res.users').browse(cr, uid, uid)
self.localcontext = {
'user': user,
'company': user.company_id,
@ -205,69 +165,44 @@ class rml_parse(object):
'formatLang': self.formatLang,
'logo' : user.company_id.logo,
'lang' : user.company_id.partner_id.lang,
'translate' : self._translate,
}
self.localcontext.update(context)
self.rml_header = user.company_id.rml_header
self.rml_header2 = user.company_id.rml_header2
self.logo = user.company_id.logo
self.name = name
self._regex = re.compile('\[\[(.+?)\]\]')
self._transl_regex = re.compile('(\[\[.+?\]\])')
self._node = None
self.parents = parents
self.tag = tag
self._lang_cache = {}
# self.already = {}
self.lang_dict = {}
self.default_lang = {}
self.lang_dict_called = False
def setTag(self, oldtag, newtag, attrs=None):
if not attrs:
attrs={}
node = self._find_parent(self._node, [oldtag])
if node:
node.tagName = newtag
for key, val in attrs.items():
node.setAttribute(key, val)
return None
return newtag, attrs
def format(self, text, oldtag=None):
if not oldtag:
oldtag = self.tag
self._node.data = ''
node = self._find_parent(self._node, [oldtag])
ns = None
if node:
pp = node.parentNode
ns = node.nextSibling
pp.removeChild(node)
self._node = pp
lst = tools.ustr(text).split('\n')
if not (text and lst):
return None
nodes = []
for i in range(len(lst)):
newnode = node.cloneNode(1)
newnode.tagName=rml_tag
newnode.childNodes[0].data = lst[i]
if ns:
pp.insertBefore(newnode, ns)
else:
pp.appendChild(newnode)
nodes.append((i, newnode))
return text
def removeParentNode(self, tag=None):
if not tag:
tag = self.tag
if self.tag == sxw_tag and rml2sxw.get(tag, False):
tag = rml2sxw[tag]
node = self._find_parent(self._node, [tag])
if node:
parentNode = node.parentNode
parentNode.removeChild(node)
self._node = parentNode
raise Exception('Skip')
def setLang(self, lang):
if not lang or self.default_lang.has_key(lang):
if not lang:
key = 'en_US'
self.lang_dict_called = False
self.localcontext['lang'] = lang
elif self.default_lang.has_key(lang):
key = lang
if self.default_lang.get(key,False):
self.lang_dict = self.default_lang.get(key,False).copy()
self.lang_dict_called = True
return True
self.localcontext['lang'] = lang
self.lang_dict_called = False
for obj in self.objects:
obj._context['lang'] = lang
for table in obj._cache:
@ -281,264 +216,115 @@ class rml_parse(object):
else:
obj._cache[table][id] = {'id': id}
def _get_lang_dict(self):
pool_lang = self.pool.get('res.lang')
lang = self.localcontext.get('lang', 'en_US') or 'en_US'
lang_ids = pool_lang.search(self.cr,self.uid,[('code','=',lang)])[0]
lang_obj = pool_lang.browse(self.cr,self.uid,lang_ids)
self.lang_dict.update({'lang_obj':lang_obj,'date_format':lang_obj.date_format,'time_format':lang_obj.time_format})
self.default_lang[lang] = self.lang_dict.copy()
return True
def formatLang(self, value, digits=2, date=False,date_time=False, grouping=True, monetary=False, currency=None):
if isinstance(value, (str, unicode)) and not value:
return ''
pool_lang = self.pool.get('res.lang')
lang = self.localcontext.get('lang', 'en_US') or 'en_US'
lids = pool_lang.search(self.cr,self.uid,[('code','=',lang)])
if not lids: return str(value)
lang_obj = pool_lang.browse(self.cr,self.uid,lids[0])
if not self.lang_dict_called:
self._get_lang_dict()
self.lang_dict_called = True
if date or date_time:
if not str(value):
return ''
date_format = lang_obj.date_format
date_format = self.lang_dict['date_format']
parse_format = DT_FORMAT
if date_time:
date_format = lang_obj.date_format + " " + lang_obj.time_format
parse_format = date_format
date_format = date_format + " " + self.lang_dict['time_format']
parse_format = DHM_FORMAT
# filtering time.strftime('%Y-%m-%d')
if type(value) == type(''):
parse_format = DHM_FORMAT
if (not date_time):
return value
return str(value)
if not isinstance(value, time.struct_time):
# assume string, parse it
# if len(str(value)) == 10:
# # length of date like 2001-01-01 is ten
# # assume format '%Y-%m-%d'
# date = mx.DateTime.strptime(value,DT_FORMAT)
# else:
# # assume format '%Y-%m-%d %H:%M:%S'
# value = str(value)[:19]
# date = mx.DateTime.strptime(str(value),DHM_FORMAT)
date = mx.DateTime.strptime(str(value),parse_format)
try:
date = mx.DateTime.strptime(str(value),parse_format)
except:# sometimes it takes converted values into value, so we dont need conversion.
return str(value)
else:
date = mx.DateTime.DateTime(*(value.timetuple()[:6]))
return date.strftime(date_format)
return lang_obj.format('%.' + str(digits) + 'f', value, grouping=grouping, monetary=monetary)
return self.lang_dict['lang_obj'].format('%.' + str(digits) + 'f', value, grouping=grouping, monetary=monetary)
def repeatIn(self, lst, name, nodes_parent=False):
self._node.data = ''
node = self._find_parent(self._node, nodes_parent or self.parents)
def repeatIn(self, lst, name,nodes_parent=False):
ret_lst = []
for id in lst:
ret_lst.append({name:id})
return ret_lst
pp = node.parentNode
ns = node.nextSibling
pp.removeChild(node)
self._node = pp
if not len(lst):
return None
nodes = []
for i in range(len(lst)):
newnode = node.cloneNode(1)
if ns:
pp.insertBefore(newnode, ns)
else:
pp.appendChild(newnode)
nodes.append((i, newnode))
for i, node in nodes:
self.node_context[node] = {name: lst[i]}
return None
def _eval(self, expr):
try:
res = eval(expr, self.localcontext)
if (res is None) or (res=='') or (res is False):
res = ''
except Exception,e:
import traceback, sys
tb_s = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback))
netsvc.Logger().notifyChannel('report', netsvc.LOG_ERROR,
'report %s:\n%s\n%s\nexpr: %s' % (self.name, tb_s, str(e),
expr.encode('utf-8')))
res = ''
return res
def _find_parent(self, node, parents):
while True:
if not node.parentNode:
return False
node = node.parentNode
if node.nodeType == node.ELEMENT_NODE and node.localName in parents:
break
return node
def _parse_text(self, text, level=None):
if not level:
level=[]
res = self._regex.findall(text)
todo = []
# translate the text
# the "split [[]] if not match [[]]" is not very nice, but I
# don't see how I could do it better...
# what I'd like to do is a re.sub(NOT pattern, func, string)
# but I don't know how to do that...
# translate the RML file
if 'lang' in self.localcontext:
lang = self.localcontext['lang']
if lang and text and not text.isspace():
transl_obj = self.pool.get('ir.translation')
piece_list = self._transl_regex.split(text)
for pn in range(len(piece_list)):
if not self._transl_regex.match(piece_list[pn]):
source_string = piece_list[pn].replace('\n', ' ').strip()
if len(source_string):
translated_string = transl_obj._get_source(self.cr, self.uid, self.name, 'rml', lang, source_string)
if translated_string:
piece_list[pn] = piece_list[pn].replace(source_string, translated_string)
text = ''.join(piece_list)
for key in res:
newtext = self._eval(key)
for i in range(len(level)):
if isinstance(newtext, list):
newtext = newtext[level[i]]
if isinstance(newtext, list):
todo.append((key, newtext))
else:
# if there are two [[]] blocks the same, it will replace both
# but it's ok because it should evaluate to the same thing
# anyway
newtext = tools.ustr(newtext)
text = text.replace('[['+key+']]', newtext)
self._node.data = text
if len(todo):
for key, newtext in todo:
parent_node = self._find_parent(self._node, parents)
assert parents.get(parent_node.localName, False), 'No parent node found !'
nodes = [parent_node]
for i in range(len(newtext) - 1):
newnode = parent_node.cloneNode(1)
if parents.get(parent_node.localName, False):
n = parent_node.parentNode
parent_node.parentNode.insertAfter(newnode, parent_node)
nodes.append(newnode)
return False
def _translate(self,text):
lang = self.localcontext.get('lang', False)
if lang and not text.isspace():
transl_obj = self.pool.get('ir.translation')
return transl_obj._get_source(self.cr, self.uid, self.name, 'rml', lang, text.replace('\n',' ').strip()) or text
return text
def _parse_node(self):
level = []
while True:
if self._node.nodeType==self._node.ELEMENT_NODE:
if self._node.hasAttribute('expr'):
newattrs = self._eval(self._node.getAttribute('expr'))
for key,val in newattrs.items():
self._node.setAttribute(key,val)
if self._node.hasChildNodes():
self._node = self._node.firstChild
elif self._node.nextSibling:
self._node = self._node.nextSibling
else:
while self._node and not self._node.nextSibling:
self._node = self._node.parentNode
if not self._node:
break
self._node = self._node.nextSibling
if self._node in self.node_context:
self.localcontext.update(self.node_context[self._node])
if self._node.nodeType in (self._node.CDATA_SECTION_NODE, self._node.TEXT_NODE):
# if self._node in self.already:
# self.already[self._node] += 1
# print "second pass!", self.already[self._node], '---%s---' % self._node.data
# else:
# self.already[self._node] = 0
self._parse_text(self._node.data, level)
return True
def _find_node(self, node, localname):
if node.localName==localname:
return node
for tag in node.childNodes:
if tag.nodeType==tag.ELEMENT_NODE:
found = self._find_node(tag, localname)
if found:
return found
return False
def _add_header(self, node, header=1):
def _add_header(self, rml_dom, header=1):
if header==2:
rml_head = self.rml_header2
else:
rml_head = self.rml_header
# Refactor this patch, to use the minidom interface
if self.logo and (rml_head.find('company.logo')<0 or rml_head.find('<image')<0) and rml_head.find('<!--image')<0:
rml_head = rml_head.replace('<pageGraphics>','''<pageGraphics> <image x="10" y="26cm" height="70" width="90" >[[company.logo]] </image> ''')
if not self.logo and rml_head.find('company.logo')>=0:
rml_head = rml_head.replace('<image','<!--image')
rml_head = rml_head.replace('</image>','</image-->')
head_dom = xml.dom.minidom.parseString(rml_head.encode('utf-8'))
#for frame in head_dom.getElementsByTagName('frame'):
# frame.parentNode.removeChild(frame)
node2 = head_dom.documentElement
for tag in node2.childNodes:
if tag.nodeType==tag.ELEMENT_NODE:
found = self._find_node(node, tag.localName)
# rml_frames = found.getElementsByTagName('frame')
if found:
if tag.hasAttribute('position') and (tag.getAttribute('position')=='inside'):
found.appendChild(tag)
else:
found.parentNode.replaceChild(tag, found)
# for frame in rml_frames:
# tag.appendChild(frame)
head_dom = etree.XML(rml_head)
for tag in head_dom.getchildren():
found = rml_dom.find('.//'+tag.tag)
if found:
if tag.get('position'):
found.append(tag)
else :
found.getparent().replace(found,tag)
return True
def preprocess(self, objects, data, ids):
self.localcontext['data'] = data
self.localcontext['objects'] = objects
self.datas = data
self.ids = ids
self.objects = objects
def _parse(self, rml_dom, objects, data, header=0):
self.node_context = {}
self.dom = rml_dom
self._node = self.dom.documentElement
if header:
self._add_header(self._node, header)
self._parse_node()
res = self.dom.documentElement.toxml('utf-8')
return res
class report_sxw(report_rml):
def __init__(self, name, table, rml, parser=rml_parse, header=True, store=False):
class report_sxw(report_rml, preprocess.report):
def __init__(self, name, table, rml=False, parser=rml_parse, header=True, store=False):
report_rml.__init__(self, name, table, rml, '')
self.name = name
self.parser = parser
self.header = header
self.store = store
def set_context(self, objects, data, ids, rml_parser, report_xml = None):
rml_parser.localcontext['data'] = data
rml_parser.localcontext['objects'] = objects
rml_parser.datas = data
rml_parser.ids = ids
rml_parser.objects = objects
if report_xml:
if report_xml.report_type=='odt' :
rml_parser.localcontext.update({'name_space' :common.odt_namespace})
else:
rml_parser.localcontext.update({'name_space' :common.sxw_namespace})
def getObjects(self, cr, uid, ids, context):
table_obj = pooler.get_pool(cr.dbname).get(self.table)
return table_obj.browse(cr, uid, ids, list_class=browse_record_list, context=context,
fields_process=_fields_process)
return table_obj.browse(cr, uid, ids, list_class=browse_record_list, context=context, fields_process=_fields_process)
def create(self, cr, uid, ids, data, context=None):
if not context:
context={}
pool = pooler.get_pool(cr.dbname)
ir_obj = pool.get('ir.actions.report.xml')
report_xml_ids = ir_obj.search(cr, uid,
[('report_name', '=', self.name[7:])], context=context)
if report_xml_ids:
report_xml = ir_obj.browse(cr, uid, report_xml_ids[0],
context=context)
attach = report_xml.attachment
report_xml = ir_obj.browse(cr, uid, report_xml_ids[0], context=context)
else:
ir_menu_report_obj = pool.get('ir.ui.menu')
report_menu_ids = ir_menu_report_obj.search(cr, uid,
[('id', 'in', ids)], context=context)
title = ''
if report_menu_ids:
report_name = ir_menu_report_obj.browse(cr, uid, report_menu_ids[0],
context=context)
title = report_name.name
rml = tools.file_open(self.tmpl, subdir=None).read()
report_type= data.get('report_type', 'pdf')
class a(object):
@ -546,8 +332,23 @@ class report_sxw(report_rml):
for key,arg in argv.items():
setattr(self, key, arg)
report_xml = a(title=title, report_type=report_type, report_rml_content=rml, name=title, attachment=False, header=self.header)
attach = False
report_type = report_xml.report_type
if report_type in ['sxw','odt']:
fnct = self.create_source_odt
elif report_type in ['pdf','html','raw']:
fnct = self.create_source_pdf
else:
raise 'Unknown Report Type'
return fnct(cr, uid, ids, data, report_xml, context)
def create_source_odt(self, cr, uid, ids, data, report_xml, context=None):
return self.create_single_odt(cr, uid, ids, data, report_xml, context or {})
def create_source_pdf(self, cr, uid, ids, data, report_xml, context=None):
if not context:
context={}
pool = pooler.get_pool(cr.dbname)
attach = report_xml.attachment
if attach:
objs = self.getObjects(cr, uid, ids, context)
results = []
@ -563,7 +364,7 @@ class report_sxw(report_rml):
d = base64.decodestring(brow_rec.datas)
results.append((d,'pdf'))
continue
result = self.create_single(cr, uid, [obj.id], data, report_xml, context)
result = self.create_single_pdf(cr, uid, [obj.id], data, report_xml, context)
try:
if aname:
name = aname+'.'+result[1]
@ -584,7 +385,6 @@ class report_sxw(report_rml):
if results:
if results[0][1]=='pdf':
from pyPdf import PdfFileWriter, PdfFileReader
import cStringIO
output = PdfFileWriter()
for r in results:
reader = PdfFileReader(cStringIO.StringIO(r[0]))
@ -593,95 +393,81 @@ class report_sxw(report_rml):
s = cStringIO.StringIO()
output.write(s)
return s.getvalue(), results[0][1]
return self.create_single(cr, uid, ids, data, report_xml, context)
return self.create_single_pdf(cr, uid, ids, data, report_xml, context)
def create_single(self, cr, uid, ids, data, report_xml, context={}):
def create_single_pdf(self, cr, uid, ids, data, report_xml, context={}):
logo = None
context = context.copy()
pool = pooler.get_pool(cr.dbname)
want_header = self.header
title = report_xml.name
attach = report_xml.attachment
report_type = report_xml.report_type
want_header = report_xml.header
rml = report_xml.report_rml_content
rml_parser = self.parser(cr, uid, self.name2, context)
objs = self.getObjects(cr, uid, ids, context)
self.set_context(objs, data, ids, rml_parser,report_xml)
processed_rml = self.preprocess_rml(etree.XML(rml),report_xml.report_type)
if report_xml.header:
rml_parser._add_header(processed_rml)
if rml_parser.logo:
logo = base64.decodestring(rml_parser.logo)
create_doc = self.generators[report_xml.report_type]
pdf = create_doc(etree.tostring(processed_rml),rml_parser.localcontext,logo,title.encode('utf8'))
return (pdf, report_xml.report_type)
if report_type in ['sxw','odt']:
context['parents'] = sxw_parents
sxw_io = StringIO.StringIO(report_xml.report_sxw_content)
sxw_z = zipfile.ZipFile(sxw_io, mode='r')
rml = sxw_z.read('content.xml')
meta = sxw_z.read('meta.xml')
sxw_z.close()
def create_single_odt(self, cr, uid, ids, data, report_xml, context={}):
context = context.copy()
report_type = report_xml.report_type
context['parents'] = sxw_parents
sxw_io = StringIO.StringIO(report_xml.report_sxw_content)
sxw_z = zipfile.ZipFile(sxw_io, mode='r')
rml = sxw_z.read('content.xml')
meta = sxw_z.read('meta.xml')
sxw_z.close()
rml_parser = self.parser(cr, uid, self.name2, context)
rml_parser.parents = sxw_parents
rml_parser.tag = sxw_tag
objs = self.getObjects(cr, uid, ids, context)
self.set_context(objs, data, ids,rml_parser,report_xml)
rml_dom_meta = node = etree.XML(meta)
elements = node.findall(rml_parser.localcontext['name_space']["meta"]+"user-defined")
for pe in elements:
if pe.get(rml_parser.localcontext['name_space']["meta"]+"name"):
if pe.get(rml_parser.localcontext['name_space']["meta"]+"name") == "Info 3":
pe.getchildren()[0].text=data['id']
if pe.get(rml_parser.localcontext['name_space']["meta"]+"name") == "Info 4":
pe.getchildren()[0].text=data['model']
meta = etree.tostring(rml_dom_meta)
rml_dom = etree.XML(rml)
rml_dom = self.preprocess_rml(rml_dom,report_type)
create_doc = self.generators[report_type]
odt = etree.tostring(create_doc(rml_dom, rml_parser.localcontext))
sxw_z = zipfile.ZipFile(sxw_io, mode='a')
sxw_z.writestr('content.xml', "<?xml version='1.0' encoding='UTF-8'?>" + \
odt)
sxw_z.writestr('meta.xml', "<?xml version='1.0' encoding='UTF-8'?>" + \
meta)
if report_xml.header:
#Add corporate header/footer
rml = tools.file_open(os.path.join('base', 'report', 'corporate_%s_header.xml' % report_type)).read()
rml_parser = self.parser(cr, uid, self.name2, context)
rml_parser.parents = sxw_parents
rml_parser.tag = sxw_tag
objs = self.getObjects(cr, uid, ids, context)
rml_parser.preprocess(objs, data, ids)
rml_dom = xml.dom.minidom.parseString(rml)
self.set_context(objs, data, ids, rml_parser,report_xml)
rml_dom = self.preprocess_rml(etree.XML(rml),report_type)
create_doc = self.generators[report_type]
odt = create_doc(rml_dom,rml_parser.localcontext)
if report_xml.header:
rml_parser._add_header(odt)
odt = etree.tostring(odt)
sxw_z.writestr('styles.xml',"<?xml version='1.0' encoding='UTF-8'?>" + \
odt)
sxw_z.close()
final_op = sxw_io.getvalue()
sxw_io.close()
return (final_op, report_type)
node = rml_dom.documentElement
elements = node.getElementsByTagName("text:p")
for pe in elements:
e = pe.getElementsByTagName("text:drop-down")
for de in e:
pp=de.parentNode
for cnd in de.childNodes:
if cnd.nodeType in (cnd.CDATA_SECTION_NODE, cnd.TEXT_NODE):
pe.appendChild(cnd)
pp.removeChild(de)
# Add Information : Resource ID and Model
rml_dom_meta = xml.dom.minidom.parseString(meta)
node = rml_dom_meta.documentElement
elements = node.getElementsByTagName("meta:user-defined")
for pe in elements:
if pe.hasAttribute("meta:name"):
if pe.getAttribute("meta:name") == "Info 3":
pe.childNodes[0].data=data['id']
if pe.getAttribute("meta:name") == "Info 4":
pe.childNodes[0].data=data['model']
meta = rml_dom_meta.documentElement.toxml('utf-8')
rml2 = rml_parser._parse(rml_dom, objs, data, header=want_header)
sxw_z = zipfile.ZipFile(sxw_io, mode='a')
sxw_z.writestr('content.xml', "<?xml version='1.0' encoding='UTF-8'?>" + \
rml2)
sxw_z.writestr('meta.xml', "<?xml version='1.0' encoding='UTF-8'?>" + \
meta)
if want_header:
#Add corporate header/footer
if report_type in ('odt', 'sxw'):
rml = tools.file_open(os.path.join('base', 'report', 'corporate_%s_header.xml' % report_type)).read()
rml_parser = self.parser(cr, uid, self.name2, context)
rml_parser.parents = sxw_parents
rml_parser.tag = sxw_tag
objs = self.getObjects(cr, uid, ids, context)
rml_parser.preprocess(objs, data, ids)
rml_dom = xml.dom.minidom.parseString(rml)
rml2 = rml_parser._parse(rml_dom, objs, data, header=want_header)
sxw_z.writestr('styles.xml',"<?xml version='1.0' encoding='UTF-8'?>" + \
rml2)
sxw_z.close()
rml2 = sxw_io.getvalue()
sxw_io.close()
else:
rml = report_xml.report_rml_content
context['parents'] = rml_parents
rml_parser = self.parser(cr, uid, self.name2, context)
rml_parser.parents = rml_parents
rml_parser.tag = rml_tag
objs = self.getObjects(cr, uid, ids, context)
rml_parser.preprocess(objs, data, ids)
rml_dom = xml.dom.minidom.parseString(rml)
rml2 = rml_parser._parse(rml_dom, objs, data, header=want_header)
if rml_parser.logo:
logo = base64.decodestring(rml_parser.logo)
create_doc = self.generators[report_type]
pdf = create_doc(rml2, logo, title.encode('utf8'))
return (pdf, report_type)