From 63f800a31619ec784b274b4edcb6f40cfe1bb118 Mon Sep 17 00:00:00 2001 From: Christophe Chauvet Date: Sun, 26 Apr 2009 11:04:44 +0200 Subject: [PATCH 01/19] [FIX] * restore with the user connexion to owner's object, intead of the owner who originaly create the database [IMP] * pg_dump command is launch with no owner defined bzr revid: christophe.chauvet@syleam.fr-20090426090444-glridpyq1llqlac0 --- bin/service/web_services.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/service/web_services.py b/bin/service/web_services.py index 4045b4120a2..f57137940d4 100644 --- a/bin/service/web_services.py +++ b/bin/service/web_services.py @@ -171,7 +171,7 @@ class db(netsvc.Service): security.check_super(password) logger = netsvc.Logger() - cmd = ['pg_dump', '--format=c'] + cmd = ['pg_dump', '--format=c', '--no-owner'] if tools.config['db_user']: cmd.append('--username=' + tools.config['db_user']) if tools.config['db_host']: @@ -210,7 +210,7 @@ class db(netsvc.Service): cr.close() sql_db.close_db('template1') - cmd = ['pg_restore'] + cmd = ['pg_restore', '--no-owner'] if tools.config['db_user']: cmd.append('--username=' + tools.config['db_user']) if tools.config['db_host']: From 740bb829f965dd787287cf0484a166551a8f4896 Mon Sep 17 00:00:00 2001 From: Naresh Choksy Date: Tue, 28 Apr 2009 15:54:21 +0530 Subject: [PATCH 02/19] added report engine bzr revid: nch@tinyerp.com-20090428102421-0bjwivab9it6bf2c --- bin/osv/orm.py | 6 +- bin/report/common.py | 45 +- bin/report/interface.py | 34 +- bin/report/preprocess.py | 71 +++ bin/report/printscreen/ps_form.py | 21 +- bin/report/printscreen/ps_list.py | 93 ++-- bin/report/render/__init__.py | 4 +- bin/report/render/odt2odt/__init__.py | 22 + bin/report/render/odt2odt/odt2odt.py | 67 +++ bin/report/render/rml.py | 30 +- bin/report/render/rml2html/rml2html.py | 206 ++++---- bin/report/render/rml2html/utils.py | 27 +- bin/report/render/rml2pdf/__init__.py | 23 +- bin/report/render/rml2pdf/trml2pdf.py | 640 ++++++++++++------------- bin/report/render/rml2pdf/utils.py | 87 +++- bin/report/report_sxw.py | 572 +++++++--------------- 16 files changed, 995 insertions(+), 953 deletions(-) create mode 100644 bin/report/preprocess.py create mode 100644 bin/report/render/odt2odt/__init__.py create mode 100644 bin/report/render/odt2odt/odt2odt.py diff --git a/bin/osv/orm.py b/bin/osv/orm.py index 2aadff3b509..b3b92dc00cc 100644 --- a/bin/osv/orm.py +++ b/bin/osv/orm.py @@ -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],)) diff --git a/bin/report/common.py b/bin/report/common.py index 0bea817787c..23fc057e03f 100644 --- a/bin/report/common.py +++ b/bin/report/common.py @@ -1,7 +1,7 @@ # -*- encoding: utf-8 -*- ############################################################################## # -# OpenERP, Open Source Management Solution +# OpenERP, Open Source Management Solution # Copyright (C) 2004-2009 Tiny SPRL (). 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: diff --git a/bin/report/interface.py b/bin/report/interface.py index bb71f201a8d..0aaf60889c8 100644 --- a/bin/report/interface.py +++ b/bin/report/interface.py @@ -1,7 +1,7 @@ # -*- encoding: utf-8 -*- ############################################################################## # -# OpenERP, Open Source Management Solution +# OpenERP, Open Source Management Solution # Copyright (C) 2004-2009 Tiny SPRL (). 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 diff --git a/bin/report/preprocess.py b/bin/report/preprocess.py new file mode 100644 index 00000000000..c51dc858770 --- /dev/null +++ b/bin/report/preprocess.py @@ -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(''' + This is a test[[ setTag('para','xpre') ]] + + + Row 1 [[ setTag('tr','tr',{'style':'TrLevel'+str(a['level']), 'paraStyle':('Level'+str(a['level']))}) ]] + Row 2 [[ True and removeParentNode('td') ]] + + Row 1 [[repeatIn(o.order_line,'o')]] + Row 2 + + +

This isa test

+
''') + a = report() + result = a.preprocess_rml(node) + print etree.tostring(result) + diff --git a/bin/report/printscreen/ps_form.py b/bin/report/printscreen/ps_form.py index 9fb85fd52cb..a294383f90f 100644 --- a/bin/report/printscreen/ps_form.py +++ b/bin/report/printscreen/ps_form.py @@ -1,7 +1,7 @@ # -*- encoding: utf-8 -*- ############################################################################## # -# OpenERP, Open Source Management Solution +# OpenERP, Open Source Management Solution # Copyright (C) 2004-2009 Tiny SPRL (). 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): diff --git a/bin/report/printscreen/ps_list.py b/bin/report/printscreen/ps_list.py index d75d2ecad20..c3dd6b341bd 100644 --- a/bin/report/printscreen/ps_list.py +++ b/bin/report/printscreen/ps_list.py @@ -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) diff --git a/bin/report/render/__init__.py b/bin/report/render/__init__.py index 80994b5990a..ebfa37bfa27 100644 --- a/bin/report/render/__init__.py +++ b/bin/report/render/__init__.py @@ -1,7 +1,7 @@ # -*- encoding: utf-8 -*- ############################################################################## # -# OpenERP, Open Source Management Solution +# OpenERP, Open Source Management Solution # Copyright (C) 2004-2009 Tiny SPRL (). 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 diff --git a/bin/report/render/odt2odt/__init__.py b/bin/report/render/odt2odt/__init__.py new file mode 100644 index 00000000000..9163b6eb222 --- /dev/null +++ b/bin/report/render/odt2odt/__init__.py @@ -0,0 +1,22 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2009 Tiny SPRL (). 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 . +# +############################################################################## +from odt2odt import parseNode \ No newline at end of file diff --git a/bin/report/render/odt2odt/odt2odt.py b/bin/report/render/odt2odt/odt2odt.py new file mode 100644 index 00000000000..4f4b7172ee1 --- /dev/null +++ b/bin/report/render/odt2odt/odt2odt.py @@ -0,0 +1,67 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2009 Tiny SPRL (). 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 . +# +############################################################################## + +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() + diff --git a/bin/report/render/rml.py b/bin/report/render/rml.py index c32896af640..faad2c4c0b1 100644 --- a/bin/report/render/rml.py +++ b/bin/report/render/rml.py @@ -1,7 +1,7 @@ # -*- encoding: utf-8 -*- ############################################################################## # -# OpenERP, Open Source Management Solution +# OpenERP, Open Source Management Solution # Copyright (C) 2004-2009 Tiny SPRL (). 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: diff --git a/bin/report/render/rml2html/rml2html.py b/bin/report/render/rml2html/rml2html.py index 1033c64d454..5e7bb5cf4e9 100644 --- a/bin/report/render/rml2html/rml2html.py +++ b/bin/report/render/rml2html/rml2html.py @@ -1,7 +1,7 @@ # -*- encoding: utf-8 -*- ############################################################################## # -# OpenERP, Open Source Management Solution +# OpenERP, Open Source Management Solution # Copyright (C) 2004-2009 Tiny SPRL (). 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 '
'*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 "
"*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('°','°') + 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+=''' ''' - - -# 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 = ''''''%(template.data) + else: + tag = '' self.result +=''' + } + + %s
%s -
+
- - '''%(list_story,list_story[0]) + + '''%(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' diff --git a/bin/report/render/rml2html/utils.py b/bin/report/render/rml2html/utils.py index 848dae463ee..110f361e058 100644 --- a/bin/report/render/rml2html/utils.py +++ b/bin/report/render/rml2html/utils.py @@ -1,7 +1,7 @@ # -*- encoding: utf-8 -*- ############################################################################## # -# OpenERP, Open Source Management Solution +# OpenERP, Open Source Management Solution # Copyright (C) 2004-2009 Tiny SPRL (). 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: diff --git a/bin/report/render/rml2pdf/__init__.py b/bin/report/render/rml2pdf/__init__.py index 6f068b7b8ca..cb473494f0a 100644 --- a/bin/report/render/rml2pdf/__init__.py +++ b/bin/report/render/rml2pdf/__init__.py @@ -1,7 +1,7 @@ # -*- encoding: utf-8 -*- ############################################################################## # -# OpenERP, Open Source Management Solution +# OpenERP, Open Source Management Solution # Copyright (C) 2004-2009 Tiny SPRL (). 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 diff --git a/bin/report/render/rml2pdf/trml2pdf.py b/bin/report/render/rml2pdf/trml2pdf.py index 5f8154044c2..efde722f0ed 100644 --- a/bin/report/render/rml2pdf/trml2pdf.py +++ b/bin/report/render/rml2pdf/trml2pdf.py @@ -16,34 +16,14 @@ # 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 . +# along with this program. If not, see . # ############################################################################## -# trml2pdf - An RML to PDF converter -# Copyright (C) 2003, Fabien Pinckaers, UCL, FSA -# Contributors -# Richard Waid -# -# 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 import sys from StringIO import StringIO -import xml.dom.minidom import copy - import reportlab import re from reportlab.pdfgen import canvas @@ -52,23 +32,12 @@ import cStringIO import utils import color import os +from lxml import etree +import base64 +from reportlab.platypus.doctemplate import ActionFlowable -# -# Change this to UTF-8 if you plan tu use Reportlab's UTF-8 support -# -# reportlab use "code page 1252" encoding by default. cfr reportlab user guide p.46 encoding = 'utf-8' -def str2xml(s): - return s.replace('&', '&').replace('<', '<').replace('>', '>') - -def _child_get(node, childs): - clds = [] - for n in node.childNodes: - if (n.nodeType==n.ELEMENT_NODE) and (n.localName==childs): - clds.append(n) - return clds - class PageCount(platypus.Flowable): def draw(self): self.canv.beginForm("pageCount") @@ -80,91 +49,91 @@ class PageReset(platypus.Flowable): def draw(self): self.canv._pageNumber = 0 -class _rml_styles(object): - def __init__(self, nodes): +class _rml_styles(object,): + def __init__(self, nodes, localcontext): + self.localcontext = localcontext self.styles = {} self.names = {} self.table_styles = {} for node in nodes: - for style in node.getElementsByTagName('blockTableStyle'): - self.table_styles[style.getAttribute('id')] = self._table_style_get(style) - for style in node.getElementsByTagName('paraStyle'): - self.styles[style.getAttribute('name')] = self._para_style_update(style) - for variable in node.getElementsByTagName('initialize'): - for name in variable.getElementsByTagName('name'): - self.names[ name.getAttribute('id')] = name.getAttribute('value') + for style in node.findall('blockTableStyle'): + self.table_styles[style.get('id')] = self._table_style_get(style) + for style in node.findall('paraStyle'): + self.styles[style.get('name')] = self._para_style_update(style) + for variable in node.findall('initialize'): + for name in variable.findall('name'): + self.names[ name.get('id')] = name.get('value') def _para_style_update(self, node): data = {} for attr in ['textColor', 'backColor', 'bulletColor', 'borderColor']: - if node.hasAttribute(attr): - data[attr] = color.get(node.getAttribute(attr)) + if node.get(attr): + data[attr] = color.get(node.get(attr)) for attr in ['fontName', 'bulletFontName', 'bulletText']: - if node.hasAttribute(attr): - data[attr] = node.getAttribute(attr) + if node.get(attr): + data[attr] = node.get(attr) for attr in ['fontSize', 'leftIndent', 'rightIndent', 'spaceBefore', 'spaceAfter', 'firstLineIndent', 'bulletIndent', 'bulletFontSize', 'leading', 'borderWidth','borderPadding','borderRadius']: - if node.hasAttribute(attr): - data[attr] = utils.unit_get(node.getAttribute(attr)) - if node.hasAttribute('alignment'): + if node.get(attr): + data[attr] = utils.unit_get(node.get(attr)) + if node.get('alignment'): align = { 'right':reportlab.lib.enums.TA_RIGHT, 'center':reportlab.lib.enums.TA_CENTER, 'justify':reportlab.lib.enums.TA_JUSTIFY } - data['alignment'] = align.get(node.getAttribute('alignment').lower(), reportlab.lib.enums.TA_LEFT) + data['alignment'] = align.get(node.get('alignment').lower(), reportlab.lib.enums.TA_LEFT) return data def _table_style_get(self, style_node): styles = [] - for node in style_node.childNodes: - if node.nodeType==node.ELEMENT_NODE: - start = utils.tuple_int_get(node, 'start', (0,0) ) - stop = utils.tuple_int_get(node, 'stop', (-1,-1) ) - if node.localName=='blockValign': - styles.append(('VALIGN', start, stop, str(node.getAttribute('value')))) - elif node.localName=='blockFont': - styles.append(('FONT', start, stop, str(node.getAttribute('name')))) - elif node.localName=='blockTextColor': - styles.append(('TEXTCOLOR', start, stop, color.get(str(node.getAttribute('colorName'))))) - elif node.localName=='blockLeading': - styles.append(('LEADING', start, stop, utils.unit_get(node.getAttribute('length')))) - elif node.localName=='blockAlignment': - styles.append(('ALIGNMENT', start, stop, str(node.getAttribute('value')))) - elif node.localName=='blockSpan': - styles.append(('SPAN', start, stop)) - elif node.localName=='blockLeftPadding': - styles.append(('LEFTPADDING', start, stop, utils.unit_get(node.getAttribute('length')))) - elif node.localName=='blockRightPadding': - styles.append(('RIGHTPADDING', start, stop, utils.unit_get(node.getAttribute('length')))) - elif node.localName=='blockTopPadding': - styles.append(('TOPPADDING', start, stop, utils.unit_get(node.getAttribute('length')))) - elif node.localName=='blockBottomPadding': - styles.append(('BOTTOMPADDING', start, stop, utils.unit_get(node.getAttribute('length')))) - elif node.localName=='blockBackground': - styles.append(('BACKGROUND', start, stop, color.get(node.getAttribute('colorName')))) - if node.hasAttribute('size'): - styles.append(('FONTSIZE', start, stop, utils.unit_get(node.getAttribute('size')))) - elif node.localName=='lineStyle': - kind = node.getAttribute('kind') - kind_list = [ 'GRID', 'BOX', 'OUTLINE', 'INNERGRID', 'LINEBELOW', 'LINEABOVE','LINEBEFORE', 'LINEAFTER' ] - assert kind in kind_list - thick = 1 - if node.hasAttribute('thickness'): - thick = float(node.getAttribute('thickness')) - styles.append((kind, start, stop, thick, color.get(node.getAttribute('colorName')))) + for node in style_node.getchildren(): + start = utils.tuple_int_get(node, 'start', (0,0) ) + stop = utils.tuple_int_get(node, 'stop', (-1,-1) ) + if node.tag=='blockValign': + styles.append(('VALIGN', start, stop, str(node.get('value')))) + elif node.tag=='blockFont': + styles.append(('FONT', start, stop, str(node.get('name')))) + elif node.tag=='blockTextColor': + styles.append(('TEXTCOLOR', start, stop, color.get(str(node.get('colorName'))))) + elif node.tag=='blockLeading': + styles.append(('LEADING', start, stop, utils.unit_get(node.get('length')))) + elif node.tag=='blockAlignment': + styles.append(('ALIGNMENT', start, stop, str(node.get('value')))) + elif node.tag=='blockSpan': + styles.append(('SPAN', start, stop)) + elif node.tag=='blockLeftPadding': + styles.append(('LEFTPADDING', start, stop, utils.unit_get(node.get('length')))) + elif node.tag=='blockRightPadding': + styles.append(('RIGHTPADDING', start, stop, utils.unit_get(node.get('length')))) + elif node.tag=='blockTopPadding': + styles.append(('TOPPADDING', start, stop, utils.unit_get(node.get('length')))) + elif node.tag=='blockBottomPadding': + styles.append(('BOTTOMPADDING', start, stop, utils.unit_get(node.get('length')))) + elif node.tag=='blockBackground': + styles.append(('BACKGROUND', start, stop, color.get(node.get('colorName')))) + if node.get('size'): + styles.append(('FONTSIZE', start, stop, utils.unit_get(node.get('size')))) + elif node.tag=='lineStyle': + kind = node.get('kind') + kind_list = [ 'GRID', 'BOX', 'OUTLINE', 'INNERGRID', 'LINEBELOW', 'LINEABOVE','LINEBEFORE', 'LINEAFTER' ] + assert kind in kind_list + thick = 1 + if node.get('thickness'): + thick = float(node.get('thickness')) + styles.append((kind, start, stop, thick, color.get(node.get('colorName')))) return platypus.tables.TableStyle(styles) def para_style_get(self, node): style = False - if node.hasAttribute('style'): - if node.getAttribute('style') in self.styles: + if node.get('style'): + if node.get('style') in self.styles: styles = reportlab.lib.styles.getSampleStyleSheet() - sname = node.getAttribute('style') + sname = node.get('style') style = reportlab.lib.styles.ParagraphStyle(sname, styles["Normal"], **self.styles[sname]) else: - sys.stderr.write('Warning: style not found, %s - setting default!\n' % (node.getAttribute('style'),) ) + sys.stderr.write('Warning: style not found, %s - setting default!\n' % (node.get('style'),) ) if not style: styles = reportlab.lib.styles.getSampleStyleSheet() style = copy.deepcopy(styles['Normal']) @@ -172,9 +141,10 @@ class _rml_styles(object): return style class _rml_doc(object): - def __init__(self, data, images={}, path='.', title=None): - self.dom = xml.dom.minidom.parseString(data) - self.filename = self.dom.documentElement.getAttribute('filename') + def __init__(self, node, localcontext, images={}, path='.', title=None): + self.localcontext = localcontext + self.etree = node + self.filename = self.etree.get('filename') self.images = images self.path = path self.title = title @@ -185,9 +155,9 @@ class _rml_doc(object): from reportlab.pdfbase.ttfonts import TTFont for node in els: - for font in node.getElementsByTagName('registerFont'): - name = font.getAttribute('fontName').encode('ascii') - fname = font.getAttribute('fontFile').encode('ascii') + for font in node.findall('registerFont'): + name = font.get('fontName').encode('ascii') + fname = font.get('fontFile').encode('ascii') pdfmetrics.registerFont(TTFont(name, fname )) addMapping(name, 0, 0, name) #normal addMapping(name, 0, 1, name) #italic @@ -195,46 +165,46 @@ class _rml_doc(object): addMapping(name, 1, 1, name) #italic and bold def _textual_image(self, node): - import base64 rc = '' - for n in node.childNodes: - if n.nodeType in (node.CDATA_SECTION_NODE, node.TEXT_NODE): - rc += n.data - return base64.decodestring(rc) + for n in node.getchildren(): + rc +=( etree.tostring(n) or '') + n.tail + return base64.decodestring(node.tostring()) def _images(self, el): result = {} - for node in el.getElementsByTagName('image'): - result[node.getAttribute('name')] = self._textual_image(node) + for node in el.findall('image'): + rc =( node.text or '') + result[node.get('name')] = base64.decodestring(rc) return result def render(self, out): - el = self.dom.documentElement.getElementsByTagName('docinit') + el = self.etree.findall('docinit') if el: self.docinit(el) - el = self.dom.documentElement.getElementsByTagName('stylesheet') - self.styles = _rml_styles(el) + el = self.etree.findall('stylesheet') + self.styles = _rml_styles(el,self.localcontext) - el = self.dom.documentElement.getElementsByTagName('images') + el = self.etree.findall('images') if el: self.images.update( self._images(el[0]) ) - - el = self.dom.documentElement.getElementsByTagName('template') + el = self.etree.findall('template') if len(el): - pt_obj = _rml_template(out, el[0], self, images=self.images, path=self.path, title=self.title) - pt_obj.render(self.dom.documentElement.getElementsByTagName('story')) + pt_obj = _rml_template(self.localcontext, out, el[0], self, images=self.images, path=self.path, title=self.title) + el = utils._child_get(self.etree, self, 'story') + pt_obj.render(el) else: self.canvas = canvas.Canvas(out) - pd = self.dom.documentElement.getElementsByTagName('pageDrawing')[0] - pd_obj = _rml_canvas(self.canvas, None, self, self.images, path=self.path, title=self.title) + pd = self.etree.find('pageDrawing')[0] + pd_obj = _rml_canvas(self.canvas, self.localcontext, None, self, self.images, path=self.path, title=self.title) pd_obj.render(pd) self.canvas.showPage() self.canvas.save() class _rml_canvas(object): - def __init__(self, canvas, doc_tmpl=None, doc=None, images={}, path='.', title=None): + def __init__(self, canvas,localcontext, doc_tmpl=None, doc=None, images={}, path='.', title=None): + self.localcontext = localcontext self.canvas = canvas self.styles = doc.styles self.doc_tmpl = doc_tmpl @@ -246,75 +216,80 @@ class _rml_canvas(object): self.canvas.setTitle(self.title) def _textual(self, node, x=0, y=0): - rc = '' - for n in node.childNodes: - if n.nodeType == n.ELEMENT_NODE: - if n.localName == 'seq': - from reportlab.lib.sequencer import getSequencer - seq = getSequencer() - rc += str(seq.next(n.getAttribute('id'))) - if n.localName == 'pageCount': - if x or y: - self.canvas.translate(x,y) - self.canvas.doForm('pageCount') - if x or y: - self.canvas.translate(-x,-y) - if n.localName == 'pageNumber': - rc += str(self.canvas.getPageNumber()) - elif n.nodeType in (node.CDATA_SECTION_NODE, node.TEXT_NODE): - # this doesn't need to be "entities" encoded like flowables need to - rc += n.data - return rc.encode(encoding, 'replace') + rc = utils._process_text(self, node.text or '') + for n in node: + if n.tag == 'seq': + from reportlab.lib.sequencer import getSequencer + seq = getSequencer() + rc += str(seq.next(n.get('id'))) + if n.tag == 'pageCount': + if x or y: + self.canvas.translate(x,y) + self.canvas.doForm('pageCount') + if x or y: + self.canvas.translate(-x,-y) + if n.tag == 'pageNumber': + rc += str(self.canvas.getPageNumber()) + rc += utils._process_text(self, n.tail) + return rc def _drawString(self, node): v = utils.attr_get(node, ['x','y']) - self.canvas.drawString(text=self._textual(node, **v), **v) + text=self._textual(node, **v) + self.canvas.drawString(text=text, **v) + def _drawCenteredString(self, node): v = utils.attr_get(node, ['x','y']) - self.canvas.drawCentredString(text=self._textual(node, **v), **v) + text=self._textual(node, **v) + self.canvas.drawCentredString(text=text, **v) + def _drawRightString(self, node): v = utils.attr_get(node, ['x','y']) - self.canvas.drawRightString(text=self._textual(node, **v), **v) + text=self._textual(node, **v) + self.canvas.drawRightString(text=text, **v) + def _rect(self, node): - if node.hasAttribute('round'): - self.canvas.roundRect(radius=utils.unit_get(node.getAttribute('round')), **utils.attr_get(node, ['x','y','width','height'], {'fill':'bool','stroke':'bool'})) + if node.get('round'): + self.canvas.roundRect(radius=utils.unit_get(node.get('round')), **utils.attr_get(node, ['x','y','width','height'], {'fill':'bool','stroke':'bool'})) else: self.canvas.rect(**utils.attr_get(node, ['x','y','width','height'], {'fill':'bool','stroke':'bool'})) def _ellipse(self, node): - x1 = utils.unit_get(node.getAttribute('x')) - x2 = utils.unit_get(node.getAttribute('width')) - y1 = utils.unit_get(node.getAttribute('y')) - y2 = utils.unit_get(node.getAttribute('height')) + x1 = utils.unit_get(node.get('x')) + x2 = utils.unit_get(node.get('width')) + y1 = utils.unit_get(node.get('y')) + y2 = utils.unit_get(node.get('height')) + self.canvas.ellipse(x1,y1,x2,y2, **utils.attr_get(node, [], {'fill':'bool','stroke':'bool'})) + def _curves(self, node): - line_str = utils.text_get(node).split() + line_str = node.text.split() lines = [] while len(line_str)>7: self.canvas.bezier(*[utils.unit_get(l) for l in line_str[0:8]]) line_str = line_str[8:] + def _lines(self, node): - line_str = utils.text_get(node).split() + line_str = node.text.split() lines = [] while len(line_str)>3: lines.append([utils.unit_get(l) for l in line_str[0:4]]) line_str = line_str[4:] self.canvas.lines(lines) + def _grid(self, node): - xlist = [utils.unit_get(s) for s in node.getAttribute('xs').split(',')] - ylist = [utils.unit_get(s) for s in node.getAttribute('ys').split(',')] + xlist = [utils.unit_get(s) for s in node.get('xs').split(',')] + ylist = [utils.unit_get(s) for s in node.get('ys').split(',')] + self.canvas.grid(xlist, ylist) + def _translate(self, node): - dx = 0 - dy = 0 - if node.hasAttribute('dx'): - dx = utils.unit_get(node.getAttribute('dx')) - if node.hasAttribute('dy'): - dy = utils.unit_get(node.getAttribute('dy')) + dx = utils.unit_get(node.get('dx')) or 0 + dy = utils.unit_get(node.get('dy')) or 0 self.canvas.translate(dx,dy) def _circle(self, node): - self.canvas.circle(x_cen=utils.unit_get(node.getAttribute('x')), y_cen=utils.unit_get(node.getAttribute('y')), r=utils.unit_get(node.getAttribute('radius')), **utils.attr_get(node, [], {'fill':'bool','stroke':'bool'})) + self.canvas.circle(x_cen=utils.unit_get(node.get('x')), y_cen=utils.unit_get(node.get('y')), r=utils.unit_get(node.get('radius')), **utils.attr_get(node, [], {'fill':'bool','stroke':'bool'})) def _place(self, node): flows = _rml_flowable(self.doc, images=self.images, path=self.path, title=self.title).render(node) @@ -333,53 +308,55 @@ class _rml_canvas(object): def _line_mode(self, node): ljoin = {'round':1, 'mitered':0, 'bevelled':2} lcap = {'default':0, 'round':1, 'square':2} - if node.hasAttribute('width'): - self.canvas.setLineWidth(utils.unit_get(node.getAttribute('width'))) - if node.hasAttribute('join'): - self.canvas.setLineJoin(ljoin[node.getAttribute('join')]) - if node.hasAttribute('cap'): - self.canvas.setLineCap(lcap[node.getAttribute('cap')]) - if node.hasAttribute('miterLimit'): - self.canvas.setDash(utils.unit_get(node.getAttribute('miterLimit'))) - if node.hasAttribute('dash'): - dashes = node.getAttribute('dash').split(',') + + if node.get('width'): + self.canvas.setLineWidth(utils.unit_get(node.get('width'))) + if node.get('join'): + self.canvas.setLineJoin(ljoin[node.get('join')]) + if node.get('cap'): + self.canvas.setLineCap(lcap[node.get('cap')]) + if node.get('miterLimit'): + self.canvas.setDash(utils.unit_get(node.get('miterLimit'))) + if node.get('dash'): + dashes = node.get('dash').split(',') for x in range(len(dashes)): dashes[x]=utils.unit_get(dashes[x]) - self.canvas.setDash(node.getAttribute('dash').split(',')) + self.canvas.setDash(node.get('dash').split(',')) def _image(self, node): import urllib from reportlab.lib.utils import ImageReader - -# s = StringIO() - if not node.hasAttribute('file'): - - if node.hasAttribute('name'): - image_data = self.images[node.getAttribute('name')] + if not node.get('file') : + if node.get('name'): + image_data = self.images[node.get('name')] s = cStringIO.StringIO(image_data) else: import base64 - image_data = base64.decodestring(node.firstChild.nodeValue) + if self.localcontext: + res = utils._regex.findall(node.text) + for key in res: + newtext = eval(key, {}, self.localcontext) + node.text = newtext + image_data = base64.decodestring(node.text) if not image_data: return False - s = cStringIO.StringIO(image_data) -# s.write(image_data) + s = cStringIO.StringIO(image_data) else: - if node.getAttribute('file') in self.images: - s = cStringIO.StringIO(self.images[node.getAttribute('file')]) -# s.write(self.images[node.getAttribute('file')]) + if node.get('file') in self.images: + s = cStringIO.StringIO(self.images[node.get('file')]) else: try: - u = urllib.urlopen(str(node.getAttribute('file'))) + u = urllib.urlopen(str(node.get('file'))) + s = cStringIO.StringIO(u.read()) except: - u = file(os.path.join(self.path,str(node.getAttribute('file'))), 'rb') - s = cStringIO.StringIO(u.read()) + u = file(os.path.join(self.path,str(node.get('file'))), 'rb') + s = cStringIO.StringIO(u.read()) img = ImageReader(s) (sx,sy) = img.getSize() args = {} for tag in ('width','height','x','y'): - if node.hasAttribute(tag): - args[tag] = utils.unit_get(node.getAttribute(tag)) + if node.get(tag): + args[tag] = utils.unit_get(node.get(tag)) if ('width' in args) and (not 'height' in args): args['height'] = sy * args['width'] / sx elif ('height' in args) and (not 'width' in args): @@ -394,25 +371,25 @@ class _rml_canvas(object): def _path(self, node): self.path = self.canvas.beginPath() self.path.moveTo(**utils.attr_get(node, ['x','y'])) - for n in node.childNodes: - if n.nodeType == node.ELEMENT_NODE: - if n.localName=='moveto': + for n in utils._child_get(node, self): + if not n.text : + if n.tag=='moveto': vals = utils.text_get(n).split() self.path.moveTo(utils.unit_get(vals[0]), utils.unit_get(vals[1])) - elif n.localName=='curvesto': + elif n.tag=='curvesto': vals = utils.text_get(n).split() while len(vals)>5: pos=[] while len(pos)<6: pos.append(utils.unit_get(vals.pop(0))) self.path.curveTo(*pos) - elif (n.nodeType == node.TEXT_NODE): - data = n.data.split() # Not sure if I must merge all TEXT_NODE ? + elif n.text: + data = n.text.split() # Not sure if I must merge all TEXT_NODE ? while len(data)>1: x = utils.unit_get(data.pop(0)) y = utils.unit_get(data.pop(0)) self.path.lineTo(x,y) - if (not node.hasAttribute('close')) or utils.bool_get(node.getAttribute('close')): + if (not node.get('close')) or utils.bool_get(node.get('close')): self.path.close() self.canvas.drawPath(self.path, **utils.attr_get(node, [], {'fill':'bool','stroke':'bool'})) @@ -426,26 +403,24 @@ class _rml_canvas(object): 'lines': self._lines, 'grid': self._grid, 'curves': self._curves, - 'fill': lambda node: self.canvas.setFillColor(color.get(node.getAttribute('color'))), - 'stroke': lambda node: self.canvas.setStrokeColor(color.get(node.getAttribute('color'))), - 'setFont': lambda node: self.canvas.setFont(node.getAttribute('name'), utils.unit_get(node.getAttribute('size'))), + 'fill': lambda node: self.canvas.setFillColor(color.get(node.get('color'))), + 'stroke': lambda node: self.canvas.setStrokeColor(color.get(node.get('color'))), + 'setFont': lambda node: self.canvas.setFont(node.get('name'), utils.unit_get(node.get('size'))), 'place': self._place, 'circle': self._circle, 'lineMode': self._line_mode, 'path': self._path, - 'rotate': lambda node: self.canvas.rotate(float(node.getAttribute('degrees'))), + 'rotate': lambda node: self.canvas.rotate(float(node.get('degrees'))), 'translate': self._translate, 'image': self._image } - for nd in node.childNodes: - if nd.nodeType==nd.ELEMENT_NODE: - for tag in tags: - if nd.localName==tag: - tags[tag](nd) - break + for n in utils._child_get(node, self): + if n.tag in tags: + tags[n.tag](n) class _rml_draw(object): - def __init__(self, node, styles, images={}, path='.', title=None): + def __init__(self, localcontext ,node, styles, images={}, path='.', title=None): + self.localcontext = localcontext self.node = node self.styles = styles self.canvas = None @@ -455,12 +430,13 @@ class _rml_draw(object): def render(self, canvas, doc): canvas.saveState() - cnv = _rml_canvas(canvas, doc, self.styles, images=self.images, path=self.path, title=self.canvas_title) + cnv = _rml_canvas(canvas,self.localcontext, doc, self.styles, images=self.images, path=self.path, title=self.canvas_title) cnv.render(self.node) canvas.restoreState() class _rml_flowable(object): - def __init__(self, doc, images={}, path='.', title=None): + def __init__(self,doc,localcontext, images={}, path='.', title=None): + self.localcontext = localcontext self.doc = doc self.styles = doc.styles self.images = images @@ -468,66 +444,58 @@ class _rml_flowable(object): self.title = title def _textual(self, node): - rc = '' - for n in node.childNodes: - if n.nodeType == node.ELEMENT_NODE: - if n.localName == 'getName': - newNode = self.doc.dom.createTextNode(self.styles.names.get(n.getAttribute('id'),'Unknown name')) - node.insertBefore(newNode, n) - node.removeChild(n) - n = newNode - elif n.localName == 'pageNumber': - rc += '' # TODO: change this ! - else: - #CHECKME: I wonder if this is useful since we don't stock the result. Maybe for the getName tag? - self._textual(n) - rc += n.toxml() - elif n.nodeType in (node.CDATA_SECTION_NODE, node.TEXT_NODE): - rc += str2xml(n.data) - return rc.encode(encoding, 'replace') + rc1 = utils._process_text(self, node.text or '') + for n in utils._child_get(node,self): + rc1 += '<'+n.tag+'>' + rc1 += self._textual(n) + rc1 += '' + rc1 += utils._process_text(self, node.tail or '') + return rc1 def _table(self, node): + childs = utils._child_get(node,self,'tr') + if not childs: + return None length = 0 colwidths = None rowheights = None data = [] - childs = _child_get(node,'tr') - if not childs: - return None - posy = 0 styles = [] + posy = 0 for tr in childs: paraStyle = None - if tr.hasAttribute('style'): - st = copy.deepcopy(self.styles.table_styles[tr.getAttribute('style')]) + if tr.get('style'): + st = copy.deepcopy(self.styles.table_styles[tr.get('style')]) for s in st._cmds: s[1][1] = posy s[2][1] = posy styles.append(st) - if tr.hasAttribute('paraStyle'): - paraStyle = self.styles.styles[tr.getAttribute('paraStyle')] - + if tr.get('paraStyle'): + paraStyle = self.styles.styles[tr.get('paraStyle')] data2 = [] posx = 0 - for td in _child_get(tr, 'td'): - if td.hasAttribute('style'): - st = copy.deepcopy(self.styles.table_styles[td.getAttribute('style')]) + for td in utils._child_get(tr, self,'td'): + if td.get('style'): + st = copy.deepcopy(self.styles.table_styles[td.get('style')]) for s in st._cmds: s[1][1] = posy s[2][1] = posy s[1][0] = posx s[2][0] = posx styles.append(st) - if td.hasAttribute('paraStyle'): + if td.get('paraStyle'): # TODO: merge styles - paraStyle = self.styles.styles[td.getAttribute('paraStyle')] + paraStyle = self.styles.styles[td.get('paraStyle')] posx += 1 flow = [] - for n in td.childNodes: - if n.nodeType==node.ELEMENT_NODE: - fl = self._flowable(n, extra_style=paraStyle) + for n in utils._child_get(td, self): + fl = self._flowable(n, extra_style=paraStyle) + if isinstance(fl,list): + flow += fl + else: flow.append( fl ) + if not len(flow): flow = self._textual(td) data2.append( flow ) @@ -540,51 +508,51 @@ class _rml_flowable(object): data2.append('') data.append( data2 ) posy += 1 - if node.hasAttribute('colWidths'): - assert length == len(node.getAttribute('colWidths').split(',')) - colwidths = [utils.unit_get(f.strip()) for f in node.getAttribute('colWidths').split(',')] - if node.hasAttribute('rowHeights'): - rowheights = [utils.unit_get(f.strip()) for f in node.getAttribute('rowHeights').split(',')] + + if node.get('colWidths'): + assert length == len(node.get('colWidths').split(',')) + colwidths = [utils.unit_get(f.strip()) for f in node.get('colWidths').split(',')] + if node.get('rowHeights'): + rowheights = [utils.unit_get(f.strip()) for f in node.get('rowHeights').split(',')] if len(rowheights) == 1: rowheights = rowheights[0] table = platypus.LongTable(data = data, colWidths=colwidths, rowHeights=rowheights, **(utils.attr_get(node, ['splitByRow'] ,{'repeatRows':'int','repeatCols':'int'}))) - if node.hasAttribute('style'): - table.setStyle(self.styles.table_styles[node.getAttribute('style')]) + if node.get('style'): + table.setStyle(self.styles.table_styles[node.get('style')]) for s in styles: table.setStyle(s) return table def _illustration(self, node): class Illustration(platypus.flowables.Flowable): - def __init__(self, node, styles, self2): + def __init__(self, node, localcontext, styles, self2): + self.localcontext = localcontext self.node = node self.styles = styles - self.width = utils.unit_get(node.getAttribute('width')) - self.height = utils.unit_get(node.getAttribute('height')) + self.width = utils.unit_get(node.get('width')) + self.height = utils.unit_get(node.get('height')) self.self2 = self2 def wrap(self, *args): return (self.width, self.height) def draw(self): canvas = self.canv - drw = _rml_draw(self.node, self.styles, images=self.self2.images, path=self.self2.path, title=self.self2.title) + drw = _rml_draw(self.localcontext ,self.node,self.styles, images=self.self2.images, path=self.self2.path, title=self.self2.title) drw.render(self.canv, None) - return Illustration(node, self.styles, self) + return Illustration(node, self.localcontext, self.styles, self) def _textual_image(self, node): - import base64 - rc = '' - for n in node.childNodes: - if n.nodeType in (node.CDATA_SECTION_NODE, node.TEXT_NODE): - rc += n.data - return base64.decodestring(rc) + return base64.decodestring(node.text) def _flowable(self, node, extra_style=None): - if node.localName=='para': + if node.tag=='para': style = self.styles.para_style_get(node) if extra_style: style.__dict__.update(extra_style) - return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str'}))) - elif node.localName=='barCode': + result = [] + for i in self._textual(node).split('\n'): + result.append(platypus.Paragraph(i, style, **(utils.attr_get(node, [], {'bulletText':'str'})))) + return result + elif node.tag=='barCode': try: from reportlab.graphics.barcode import code128 from reportlab.graphics.barcode import code39 @@ -593,7 +561,6 @@ class _rml_flowable(object): from reportlab.graphics.barcode import fourstate from reportlab.graphics.barcode import usps except Exception, e: - print 'Warning: Reportlab barcode extension not installed !' return None args = utils.attr_get(node, [], {'ratio':'float','xdim':'unit','height':'unit','checksum':'bool','quiet':'bool'}) codes = { @@ -610,54 +577,49 @@ class _rml_flowable(object): 'postnet': lambda x: usps.POSTNET(x, **args), } code = 'code128' - if node.hasAttribute('code'): - code = node.getAttribute('code').lower() + if node.get('code'): + code = node.get('code').lower() return codes[code](self._textual(node)) - elif node.localName=='name': - self.styles.names[ node.getAttribute('id')] = node.getAttribute('value') + elif node.tag=='name': + self.styles.names[ node.get('id')] = node.get('value') return None - elif node.localName=='xpre': + elif node.tag=='xpre': style = self.styles.para_style_get(node) return platypus.XPreformatted(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str','dedent':'int','frags':'int'}))) - elif node.localName=='pre': + elif node.tag=='pre': style = self.styles.para_style_get(node) return platypus.Preformatted(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str','dedent':'int'}))) - elif node.localName=='illustration': + elif node.tag=='illustration': return self._illustration(node) - elif node.localName=='blockTable': + elif node.tag=='blockTable': return self._table(node) - elif node.localName=='title': + elif node.tag=='title': styles = reportlab.lib.styles.getSampleStyleSheet() style = styles['Title'] return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str'}))) - elif re.match('^h([1-9]+[0-9]*)$', node.localName): + elif re.match('^h([1-9]+[0-9]*)$', (node.text or '')): styles = reportlab.lib.styles.getSampleStyleSheet() - style = styles['Heading'+str(node.localName[1:])] + style = styles['Heading'+str(node.get[1:])] return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str'}))) - elif node.localName=='image': - if not node.hasAttribute('file'): - if node.hasAttribute('name'): - image_data = self.doc.images[node.getAttribute('name')].read() + elif node.tag=='image': + if not node.get('file'): + if node.get('name'): + image_data = self.doc.images[node.get('name')].read() else: import base64 - image_data = base64.decodestring(node.firstChild.nodeValue) - - image = StringIO() - image.write(image_data) - image.seek(0) + image_data = base64.decodestring(node.text) + image = cStringIO.StringIO(image_data) return platypus.Image(image, mask=(250,255,250,255,250,255), **(utils.attr_get(node, ['width','height']))) else: - return platypus.Image(node.getAttribute('file'), mask=(250,255,250,255,250,255), **(utils.attr_get(node, ['width','height']))) - + return platypus.Image(node.get('file'), mask=(250,255,250,255,250,255), **(utils.attr_get(node, ['width','height']))) from reportlab.lib.utils import ImageReader - name = str(node.getAttribute('file')) + name = str(node.get('file')) img = ImageReader(name) (sx,sy) = img.getSize() - args = {} for tag in ('width','height'): - if node.hasAttribute(tag): - args[tag] = utils.unit_get(node.getAttribute(tag)) + if node.get(tag): + args[tag] = utils.unit_get(node.get(tag)) if ('width' in args) and (not 'height' in args): args['height'] = sy * args['width'] / sx elif ('height' in args) and (not 'width' in args): @@ -668,58 +630,56 @@ class _rml_flowable(object): else: args['height'] = sy * args['width'] / sx return platypus.Image(name, mask=(250,255,250,255,250,255), **args) - elif node.localName=='spacer': - if node.hasAttribute('width'): - width = utils.unit_get(node.getAttribute('width')) + elif node.tag=='spacer': + if node.get('width'): + width = utils.unit_get(node.get('width')) else: width = utils.unit_get('1cm') - length = utils.unit_get(node.getAttribute('length')) + length = utils.unit_get(node.get('length')) return platypus.Spacer(width=width, height=length) - elif node.localName=='section': + elif node.tag=='section': return self.render(node) - elif node.localName == 'pageNumberReset': + elif node.tag == 'pageNumberReset': return PageReset() - elif node.localName in ('pageBreak', 'nextPage'): + elif node.tag in ('pageBreak', 'nextPage'): return platypus.PageBreak() - elif node.localName=='condPageBreak': + elif node.tag=='condPageBreak': return platypus.CondPageBreak(**(utils.attr_get(node, ['height']))) - elif node.localName=='setNextTemplate': - return platypus.NextPageTemplate(str(node.getAttribute('name'))) - elif node.localName=='nextFrame': + elif node.tag=='setNextTemplate': + return platypus.NextPageTemplate(str(node.get('name'))) + elif node.tag=='nextFrame': return platypus.CondPageBreak(1000) # TODO: change the 1000 ! - elif node.localName == 'setNextFrame': + elif node.tag == 'setNextFrame': from reportlab.platypus.doctemplate import NextFrameFlowable - return NextFrameFlowable(str(node.getAttribute('name'))) - elif node.localName == 'currentFrame': + return NextFrameFlowable(str(node.get('name'))) + elif node.tag == 'currentFrame': from reportlab.platypus.doctemplate import CurrentFrameFlowable - return CurrentFrameFlowable(str(node.getAttribute('name'))) - elif node.localName == 'frameEnd': + return CurrentFrameFlowable(str(node.get('name'))) + elif node.tag == 'frameEnd': return EndFrameFlowable() - elif node.localName == 'hr': - width_hr=node.hasAttribute('width') and node.getAttribute('width') or '100%' - color_hr=node.hasAttribute('color') and node.getAttribute('color') or 'black' - thickness_hr=node.hasAttribute('thickness') and node.getAttribute('thickness') or 1 - lineCap_hr=node.hasAttribute('lineCap') and node.getAttribute('lineCap') or 'round' + elif node.tag == 'hr': + width_hr=node.get('width') or '100%' + color_hr=node.get('color') or 'black' + thickness_hr=node.get('thickness') or 1 + lineCap_hr=node.get('lineCap') or 'round' return platypus.flowables.HRFlowable(width=width_hr,color=color.get(color_hr),thickness=float(thickness_hr),lineCap=str(lineCap_hr)) else: - sys.stderr.write('Warning: flowable not yet implemented: %s !\n' % (node.localName,)) + sys.stderr.write('Warning: flowable not yet implemented: %s !\n' % (node.tag,)) return None def render(self, node_story): - story = [] - node = node_story.firstChild - while node: - if node.nodeType == node.ELEMENT_NODE: + def process_story(node_story): + sub_story = [] + for node in utils._child_get(node_story, self): flow = self._flowable(node) if flow: - if type(flow) == type([]): - story = story + flow + if isinstance(flow,list): + sub_story = sub_story + flow else: - story.append(flow) - node = node.nextSibling - return story + sub_story.append(flow) + return sub_story + return process_story(node_story) -from reportlab.platypus.doctemplate import ActionFlowable class EndFrameFlowable(ActionFlowable): def __init__(self,resume=0): @@ -733,7 +693,6 @@ class TinyDocTemplate(platypus.BaseDocTemplate): self.pageTemplate.onPage(self.canv,self) for f in self.pageTemplate.frames: f._reset() self.beforePage() - #keep a count of flowables added to this page. zero indicates bad stuff self._curPageFlowableCount = 0 if hasattr(self,'_nextFrameIndex'): del self._nextFrameIndex @@ -747,59 +706,66 @@ class TinyDocTemplate(platypus.BaseDocTemplate): self.canv._pageNumber = 0 class _rml_template(object): - def __init__(self, out, node, doc, images={}, path='.', title=None): + def __init__(self, localcontext, out, node, doc, images={}, path='.', title=None): + self.localcontext = localcontext self.images= images self.path = path self.title = title - if not node.hasAttribute('pageSize'): + if not node.get('pageSize'): pageSize = (utils.unit_get('21cm'), utils.unit_get('29.7cm')) else: - ps = map(lambda x:x.strip(), node.getAttribute('pageSize').replace(')', '').replace('(', '').split(',')) + ps = map(lambda x:x.strip(), node.get('pageSize').replace(')', '').replace('(', '').split(',')) pageSize = ( utils.unit_get(ps[0]),utils.unit_get(ps[1]) ) - cm = reportlab.lib.units.cm + self.doc_tmpl = TinyDocTemplate(out, pagesize=pageSize, **utils.attr_get(node, ['leftMargin','rightMargin','topMargin','bottomMargin'], {'allowSplitting':'int','showBoundary':'bool','title':'str','author':'str'})) self.page_templates = [] self.styles = doc.styles self.doc = doc - pts = node.getElementsByTagName('pageTemplate') + pts = node.findall('pageTemplate') for pt in pts: frames = [] - for frame_el in pt.getElementsByTagName('frame'): + for frame_el in pt.findall('frame'): frame = platypus.Frame( **(utils.attr_get(frame_el, ['x1','y1', 'width','height', 'leftPadding', 'rightPadding', 'bottomPadding', 'topPadding'], {'id':'str', 'showBoundary':'bool'})) ) if utils.attr_get(frame_el, ['last']): frame.lastFrame = True frames.append( frame ) - gr = pt.getElementsByTagName('pageGraphics') + try : + gr = pt.findall('pageGraphics') or pt.getchildren()[1].findall('pageGraphics') + except : + gr='' if len(gr): - drw = _rml_draw(gr[0], self.doc, images=images, path=self.path, title=self.title) + drw = _rml_draw(self.localcontext,gr[0], self.doc, images=images, path=self.path, title=self.title) self.page_templates.append( platypus.PageTemplate(frames=frames, onPage=drw.render, **utils.attr_get(pt, [], {'id':'str'}) )) else: - drw = _rml_draw(node,self.doc,title=self.title) + drw = _rml_draw(self.localcontext,node,self.doc,title=self.title) self.page_templates.append( platypus.PageTemplate(frames=frames,onPage=drw.render, **utils.attr_get(pt, [], {'id':'str'}) )) self.doc_tmpl.addPageTemplates(self.page_templates) def render(self, node_stories): fis = [] - r = _rml_flowable(self.doc,images=self.images, path=self.path, title=self.title) + r = _rml_flowable(self.doc,self.localcontext, images=self.images, path=self.path, title=self.title) for node_story in node_stories: fis += r.render(node_story) - if node_story==node_stories[-1]: - - fis.append(PageCount()) - fis.append(platypus.PageBreak()) - self.doc_tmpl.build(fis) -def parseString(data, fout=None, images={}, path='.',title=None): - r = _rml_doc(data, images, path, title=title) +def parseNode(rml, localcontext = {},fout=None, images={}, path='.',title=None): + node = etree.XML(rml) + r = _rml_doc(node, localcontext, images, path, title=title) + fp = cStringIO.StringIO() + r.render(fp) + return fp.getvalue() + +def parseString(rml, localcontext = {},fout=None, images={}, path='.',title=None): + node = etree.XML(rml) + r = _rml_doc(node, localcontext, images, path, title=title) if fout: fp = file(fout,'wb') r.render(fp) fp.close() return fout else: - fp = StringIO() + fp = cStringIO.StringIO() r.render(fp) return fp.getvalue() @@ -817,5 +783,3 @@ if __name__=="__main__": print 'Usage: trml2pdf input.rml >output.pdf' print 'Try \'trml2pdf --help\' for more information.' -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: - diff --git a/bin/report/render/rml2pdf/utils.py b/bin/report/render/rml2pdf/utils.py index 25573ed99cc..ea43d1a0d80 100644 --- a/bin/report/render/rml2pdf/utils.py +++ b/bin/report/render/rml2pdf/utils.py @@ -1,7 +1,7 @@ # -*- encoding: utf-8 -*- ############################################################################## # -# OpenERP, Open Source Management Solution +# OpenERP, Open Source Management Solution # Copyright (C) 2004-2009 Tiny SPRL (). 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: \ No newline at end of file diff --git a/bin/report/report_sxw.py b/bin/report/report_sxw.py index cd2fc78f3f0..04eea2da1e5 100644 --- a/bin/report/report_sxw.py +++ b/bin/report/report_sxw.py @@ -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('',''' [[company.logo]] ''') if not self.logo and rml_head.find('company.logo')>=0: rml_head = rml_head.replace('','') - - 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', "" + \ + odt) + sxw_z.writestr('meta.xml', "" + \ + 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',"" + \ + 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', "" + \ - rml2) - sxw_z.writestr('meta.xml', "" + \ - 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',"" + \ - 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) From 8e1ab8207cb1863214b4673ac309157b2a7dd2b1 Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Tue, 28 Apr 2009 15:50:45 +0200 Subject: [PATCH 03/19] [IMP] email sending: use OpenERP logger instead of logging... bzr revid: christophe@tinyerp.com-20090428135045-qh3h94cv6igneakp --- bin/tools/misc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/tools/misc.py b/bin/tools/misc.py index 4dd11c5a71c..8a348d3af0e 100644 --- a/bin/tools/misc.py +++ b/bin/tools/misc.py @@ -388,8 +388,8 @@ def email_send(email_from, email_to, subject, body, email_cc=None, email_bcc=Non ) s.quit() except Exception, e: - import logging - logging.getLogger().error(str(e)) + import netsvc + netsvc.Logger().notifyChannel('email_send', netsvc.LOG_ERROR, e) return False return True From 82b04743288ed9586830806b8bf6c5b1233c58ec Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Tue, 28 Apr 2009 16:10:07 +0200 Subject: [PATCH 04/19] [FIX] do not relay on _obj to know if the cursor has been closed lp bug: https://launchpad.net/bugs/330999 fixed bzr revid: christophe@tinyerp.com-20090428141007-td39oa95wk5h5dt7 --- bin/sql_db.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bin/sql_db.py b/bin/sql_db.py index 1e88f1b66c7..f3b8c86b64c 100644 --- a/bin/sql_db.py +++ b/bin/sql_db.py @@ -71,7 +71,7 @@ class Cursor(object): @wraps(f) def wrapper(self, *args, **kwargs): - if not hasattr(self, '_obj'): + if self.__closed: raise psycopg2.ProgrammingError('Unable to use the cursor after having closing it') return f(self, *args, **kwargs) return wrapper @@ -81,6 +81,7 @@ class Cursor(object): self._serialized = serialized self._cnx = pool.getconn() self._obj = self._cnx.cursor(cursor_factory=psycopg1cursor) + self.__closed = False self.autocommit(False) self.dbname = pool.dbname @@ -89,7 +90,7 @@ class Cursor(object): self.__caller = tuple(stack()[2][1:3]) def __del__(self): - if hasattr(self, '_obj'): + if not self.__closed: if tools.config['log_level'] in (netsvc.LOG_DEBUG, netsvc.LOG_DEBUG_RPC): # Oops. 'self' has not been closed explicitly. # The cursor will be deleted by the garbage collector, @@ -166,6 +167,7 @@ class Cursor(object): # collected as fast as they should). The problem is probably due in # part because browse records keep a reference to the cursor. del self._obj + self.__closed = True self._pool.putconn(self._cnx) @check From 022e7ff2cb9b6238618f0ce0ffc81fe748f02d6b Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Tue, 28 Apr 2009 16:11:24 +0200 Subject: [PATCH 05/19] [FIX] when loading the module graph, try to reload the modules that have been previously marked as uninstallable... bzr revid: christophe@tinyerp.com-20090428141124-e4dv73ldetlfg92f --- bin/addons/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bin/addons/__init__.py b/bin/addons/__init__.py index ce8117f9965..1258145f384 100644 --- a/bin/addons/__init__.py +++ b/bin/addons/__init__.py @@ -322,6 +322,7 @@ def upgrade_graph(graph, cr, module_list, force=None): raise if info.get('installable', True): packages.append((module, info.get('depends', []), info)) + dependencies = dict([(p, deps) for p, deps, data in packages]) current, later = set([p for p, dep, data in packages]), set() @@ -669,7 +670,8 @@ def load_modules(db, force_demo=False, status=None, update_module=False): pool = pooler.get_pool(cr.dbname) try: report = tools.assertion_report() - STATES_TO_LOAD = ['installed', 'to upgrade'] + # NOTE: Try to also load the modules that have been marked as uninstallable previously... + STATES_TO_LOAD = ['installed', 'to upgrade', 'uninstallable'] graph = create_graph(cr, ['base'], force) has_updates = False if update_module: From efc01f25aea7aa5ac44a3edc9b19327b6b5bc58c Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Tue, 28 Apr 2009 16:40:14 +0200 Subject: [PATCH 06/19] [FIX] allow to use server-side domains with widget='selection' fields [FIX] widget='selection' fields now handle correctly the required fields bzr revid: christophe@tinyerp.com-20090428144014-7wxt52bfy5odnf5r --- bin/osv/orm.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/bin/osv/orm.py b/bin/osv/orm.py index b3b92dc00cc..cfc311102eb 100644 --- a/bin/osv/orm.py +++ b/bin/osv/orm.py @@ -800,13 +800,14 @@ class orm_template(object): attrs = {} try: if node.getAttribute('name') in self._columns: - relation = self._columns[node.getAttribute('name')]._obj + column = self._columns[node.getAttribute('name')] else: - relation = self._inherit_fields[node.getAttribute('name')][2]._obj + column = self._inherit_fields[node.getAttribute('name')][2] except: - relation = False + column = False - if relation: + if column: + relation = column._obj childs = False views = {} for f in node.childNodes: @@ -821,9 +822,12 @@ class orm_template(object): } attrs = {'views': views} if node.hasAttribute('widget') and node.getAttribute('widget')=='selection': - # We can not use the domain has it is defined according to the record ! - attrs['selection'] = self.pool.get(relation).name_search(cr, user, '', context=context) - if not attrs.get('required',False): + # We can not use the 'string' domain has it is defined according to the record ! + dom = None + if column._domain and not isinstance(column._domain, (str, unicode)): + dom = column._domain + attrs['selection'] = self.pool.get(relation).name_search(cr, user, '', dom, context=context) + if (node.hasAttribute('required') and not int(node.getAttribute('required'))) or not column.required: attrs['selection'].append((False,'')) fields[node.getAttribute('name')] = attrs From 315dc765bf116955c46d5a6195f83aca752c34f1 Mon Sep 17 00:00:00 2001 From: Naresh Choksy Date: Wed, 29 Apr 2009 16:27:18 +0530 Subject: [PATCH 07/19] bugfix regular expression accepts string and not list bzr revid: nch@tinyerp.com-20090429105718-9nuzjh9foosowe8y --- bin/report/preprocess.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/report/preprocess.py b/bin/report/preprocess.py index c51dc858770..fe7f173af53 100644 --- a/bin/report/preprocess.py +++ b/bin/report/preprocess.py @@ -32,7 +32,7 @@ class report(object): return txt.group(0) def _sub1(txt): if len(txt.group(4)) > 1: - return [] + return " " match = rml_parents if type in ['odt','sxw']: match = sxw_parents @@ -44,7 +44,7 @@ class report(object): n.set('rml_loop', txt.group(2)) return '[['+txt.group(1)+"''"+txt.group(4)+']]' t = _regex1.sub(_sub1, node.text) - if t == []: + if t == " ": t = _regex11.sub(_sub1, node.text) t = _regex3.sub(_sub3, t) node.text = _regex2.sub(_sub2, t) From 20fdb10ae8a2466e57b876af1230fa35df49acd7 Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Wed, 29 Apr 2009 19:36:17 +0200 Subject: [PATCH 08/19] merge bzr revid: fp@tinyerp.com-20090429173617-44uf762027pjlzq6 --- addons/purchase/wizard/wizard_group.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/addons/purchase/wizard/wizard_group.py b/addons/purchase/wizard/wizard_group.py index a538a80bc23..8c0bc2dd319 100644 --- a/addons/purchase/wizard/wizard_group.py +++ b/addons/purchase/wizard/wizard_group.py @@ -62,7 +62,7 @@ def _mergeOrders(self, cr, uid, data, context): list_key.append((field, field_val)) list_key.sort() return tuple(list_key) - + # compute what the new orders should contain new_orders = {} for porder in [order for order in order_obj.browse(cr, uid, data['ids']) if order.state == 'draft']: @@ -120,7 +120,6 @@ def _mergeOrders(self, cr, uid, data, context): del value['uom_factor'] value.update(dict(key)) order_data['order_line'] = [(0, 0, value) for value in order_data['order_line'].itervalues()] - # create the new order neworder_id = order_obj.create(cr, uid, order_data) allorders.append(neworder_id) From a1072bed43b4612a810b9ac3dc5cb190ee487e1d Mon Sep 17 00:00:00 2001 From: "Jay (Open ERP)" Date: Fri, 1 May 2009 12:38:28 +0530 Subject: [PATCH 09/19] [FIX] Bug 364835 : corrected non-enlgish character problem for parsing report(ref:Ferdinand) bzr revid: jvo@tinyerp.com-20090501070828-rz7h97sk1a6vnqol --- bin/report/print_xml.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bin/report/print_xml.py b/bin/report/print_xml.py index cceecd14c33..03d22a70e18 100644 --- a/bin/report/print_xml.py +++ b/bin/report/print_xml.py @@ -262,7 +262,8 @@ class document(object): if not isinstance(datas[atr['value']], (str, unicode)): txt = self.doc.createTextNode(str(datas[atr['value']])) else: - txt = self.doc.createTextNode(datas[atr['value']].decode('utf-8')) +# txt = self.doc.createTextNode(datas[atr['value']].decode('utf-8')) + txt = self.doc.createTextNode(datas[atr['value']]) el.appendChild(txt) else: el_cld = node.firstChild From b313d793bc752072eb3529fcd2391bba72541ecb Mon Sep 17 00:00:00 2001 From: "Jay (Open ERP)" Date: Fri, 1 May 2009 17:39:09 +0530 Subject: [PATCH 10/19] [FIX] Corrected arguement passed to cursor's execute method(when params=[] bzr revid: jvo@tinyerp.com-20090501120909-4nyzibm3fapkv1rq --- bin/sql_db.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bin/sql_db.py b/bin/sql_db.py index f3b8c86b64c..598e8be5e25 100644 --- a/bin/sql_db.py +++ b/bin/sql_db.py @@ -108,7 +108,7 @@ class Cursor(object): self.count+=1 if '%d' in query or '%f' in query: log(query, netsvc.LOG_WARNING) - log("SQL queries mustn't containt %d or %f anymore. Use only %s", netsvc.LOG_WARNING) + log("SQL queries mustn't contain %d or %f anymore. Use only %s", netsvc.LOG_WARNING) if params: query = query.replace('%d', '%s').replace('%f', '%s') @@ -116,6 +116,7 @@ class Cursor(object): now = mdt.now() try: + params = params or None res = self._obj.execute(query, params) except Exception, e: log("bad query: %s" % self._obj.query) From db120a927e5098cfd4a023972cfc3a83bffd792d Mon Sep 17 00:00:00 2001 From: Naresh Choksy Date: Mon, 4 May 2009 11:44:01 +0530 Subject: [PATCH 11/19] bugfix bzr revid: nch@tinyerp.com-20090504061401-p7q0ihqpgogu666m --- bin/report/report_sxw.py | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/bin/report/report_sxw.py b/bin/report/report_sxw.py index 04eea2da1e5..118133a5be1 100644 --- a/bin/report/report_sxw.py +++ b/bin/report/report_sxw.py @@ -290,6 +290,17 @@ class rml_parse(object): found.getparent().replace(found,tag) return True + def set_context(self, objects, data, ids, report_type = None): + self.localcontext['data'] = data + self.localcontext['objects'] = objects + self.datas = data + self.ids = ids + self.objects = objects + if report_type: + if report_type=='odt' : + self.localcontext.update({'name_space' :common.odt_namespace}) + else: + self.localcontext.update({'name_space' :common.sxw_namespace}) class report_sxw(report_rml, preprocess.report): def __init__(self, name, table, rml=False, parser=rml_parse, header=True, store=False): @@ -299,19 +310,6 @@ class report_sxw(report_rml, preprocess.report): 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) @@ -402,7 +400,7 @@ class report_sxw(report_rml, preprocess.report): 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) + rml_parser.set_context(objs, data, ids, report_xml.report_type) processed_rml = self.preprocess_rml(etree.XML(rml),report_xml.report_type) if report_xml.header: rml_parser._add_header(processed_rml) @@ -426,7 +424,7 @@ class report_sxw(report_rml, preprocess.report): 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_parser.set_context(objs, data, ids,report_xml.report_type) rml_dom_meta = node = etree.XML(meta) elements = node.findall(rml_parser.localcontext['name_space']["meta"]+"user-defined") @@ -456,7 +454,7 @@ class report_sxw(report_rml, preprocess.report): 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_parser.set_context(objs, data, ids, report_xml.report_type) rml_dom = self.preprocess_rml(etree.XML(rml),report_type) create_doc = self.generators[report_type] odt = create_doc(rml_dom,rml_parser.localcontext) From 1f5a45028dbb9e9b367c553790273077b5015a22 Mon Sep 17 00:00:00 2001 From: Naresh Choksy Date: Mon, 4 May 2009 14:26:20 +0530 Subject: [PATCH 12/19] bugfix:repeatIn On tr when no record exists bzr revid: nch@tinyerp.com-20090504085620-09y8tchv0ap14jwz --- bin/report/render/rml2pdf/utils.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/bin/report/render/rml2pdf/utils.py b/bin/report/render/rml2pdf/utils.py index ea43d1a0d80..c92567d738f 100644 --- a/bin/report/render/rml2pdf/utils.py +++ b/bin/report/render/rml2pdf/utils.py @@ -44,12 +44,10 @@ 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): @@ -69,8 +67,6 @@ def _child_get(node, self=None, tagname=None): 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: From 968fe9815f99c0a397845348f943df13741e989d Mon Sep 17 00:00:00 2001 From: Naresh Choksy Date: Mon, 4 May 2009 14:42:26 +0530 Subject: [PATCH 13/19] bugfix:reports when no color style found bzr revid: nch@tinyerp.com-20090504091226-knu1wtdmttpi7f1b --- bin/report/render/rml2pdf/color.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bin/report/render/rml2pdf/color.py b/bin/report/render/rml2pdf/color.py index 3f3caf7fa43..237356f47ec 100644 --- a/bin/report/render/rml2pdf/color.py +++ b/bin/report/render/rml2pdf/color.py @@ -1,7 +1,7 @@ # -*- encoding: utf-8 -*- ############################################################################## # -# OpenERP, Open Source Management Solution +# OpenERP, Open Source Management Solution # Copyright (C) 2004-2009 Tiny SPRL (). All Rights Reserved # $Id$ # @@ -29,6 +29,8 @@ regex_t = re.compile('\(([0-9\.]*),([0-9\.]*),([0-9\.]*)\)') regex_h = re.compile('#([0-9a-zA-Z][0-9a-zA-Z])([0-9a-zA-Z][0-9a-zA-Z])([0-9a-zA-Z][0-9a-zA-Z])') def get(col_str): + if col_str == None: + col_str = '' global allcols if col_str in allcols.keys(): return allcols[col_str] From ce281db084fd42eda67a288daa3f745e01d24d30 Mon Sep 17 00:00:00 2001 From: Naresh Choksy Date: Mon, 4 May 2009 15:52:51 +0530 Subject: [PATCH 14/19] bugfix:translation of reports bzr revid: nch@tinyerp.com-20090504102251-lbhux5vbdvjp33qp --- bin/report/report_sxw.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/bin/report/report_sxw.py b/bin/report/report_sxw.py index 118133a5be1..aaa61cac0f0 100644 --- a/bin/report/report_sxw.py +++ b/bin/report/report_sxw.py @@ -179,6 +179,7 @@ class rml_parse(object): self.lang_dict = {} self.default_lang = {} self.lang_dict_called = False + self._transl_regex = re.compile('(\[\[.+?\]\])') def setTag(self, oldtag, newtag, attrs=None): return newtag, attrs @@ -264,10 +265,18 @@ class rml_parse(object): return ret_lst def _translate(self,text): - lang = self.localcontext.get('lang', False) - if lang and not text.isspace(): + lang = self.localcontext['lang'] + if lang and text 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 + 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) return text def _add_header(self, rml_dom, header=1): From 47a3a483eb2f15e94884310e3944830fc0075d89 Mon Sep 17 00:00:00 2001 From: Stephane Wirtel Date: Mon, 4 May 2009 14:35:21 +0200 Subject: [PATCH 15/19] [FIX] Add the context_lang field in the res_users table bzr revid: stephane@tinyerp.com-20090504123521-jqe9dyrd75kap5h3 --- bin/addons/base/base.sql | 1 + 1 file changed, 1 insertion(+) diff --git a/bin/addons/base/base.sql b/bin/addons/base/base.sql index 3585f1c4718..d38fc77b34c 100644 --- a/bin/addons/base/base.sql +++ b/bin/addons/base/base.sql @@ -144,6 +144,7 @@ CREATE TABLE res_users ( context_tz varchar(64) default null, signature text, -- action_id int references ir_act_window on delete set null, + context_lang varchar(64) default '', action_id int, primary key(id) ); From 61993e43fc3635a43480ae2a35d03e2a295c5382 Mon Sep 17 00:00:00 2001 From: Stephane Wirtel Date: Mon, 4 May 2009 17:09:04 +0200 Subject: [PATCH 16/19] [FIX] Add the odt2odt dependency in the list of the addons (patch from Dukai Gabor) lp bug: https://launchpad.net/bugs/371669 fixed bzr revid: stephane@tinyerp.com-20090504150904-1kg6xfk21ly0s2lw --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index e4d90b320ea..b9fe1799077 100755 --- a/setup.py +++ b/setup.py @@ -178,6 +178,7 @@ setup(name = name, 'openerp-server.report.render.rml2pdf', 'openerp-server.report.render.rml2html', 'openerp-server.wizard', + 'openerp-server.report.render.odt2odt', 'openerp-server.workflow'] + \ list(find_addons()), package_dir = {'openerp-server': 'bin'}, From e0ccd7abee1041cd14ae6e8da355653bd808863c Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Tue, 5 May 2009 08:44:05 +0200 Subject: [PATCH 17/19] bugifx_rng bzr revid: fp@tinyerp.com-20090505064405-s9a0aq08c55pxmo6 --- bin/addons/base/rng/view.rng | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/bin/addons/base/rng/view.rng b/bin/addons/base/rng/view.rng index f7ab0fe9477..d87a4cd8ffa 100644 --- a/bin/addons/base/rng/view.rng +++ b/bin/addons/base/rng/view.rng @@ -199,6 +199,28 @@ + + + + + + + + + + + + + + + + + + + + + + @@ -365,6 +387,7 @@ + From 0f9228494ca3ca864ebb84d3584255c9dab3d6cf Mon Sep 17 00:00:00 2001 From: "mra (Open ERP)" Date: Tue, 5 May 2009 16:30:23 +0530 Subject: [PATCH 18/19] bug fix: Bug #371253 : draft invoice and purchase order invoiced and paid (ref:pso) bzr revid: mra@tinyerp.com-20090505110023-au321rryj2qxgshk --- addons/purchase/purchase.py | 11 ++++++++++- addons/purchase/purchase_workflow.xml | 2 +- addons/purchase/stock.py | 7 +++---- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index 6df6d8911d5..e591269f9c3 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -135,6 +135,15 @@ class purchase_order(osv.osv): result[line.order_id.id] = True return result.keys() + def _invoiced(self, cursor, user, ids, name, arg, context=None): + res = {} + for purchase in self.browse(cursor, user, ids, context=context): + if purchase.invoice_id.reconciled: + res[purchase.id] = purchase.invoice_id.reconciled + else: + res[purchase.id] = False + return res + _columns = { 'name': fields.char('Order Reference', size=64, required=True, select=True), 'origin': fields.char('Origin', size=64, @@ -163,7 +172,7 @@ class purchase_order(osv.osv): 'picking_ids': fields.one2many('stock.picking', 'purchase_id', 'Picking List', readonly=True, help="This is the list of picking list that have been generated for this purchase"), 'shipped':fields.boolean('Received', readonly=True, select=True), 'shipped_rate': fields.function(_shipped_rate, method=True, string='Received', type='float'), - 'invoiced':fields.boolean('Invoiced & Paid', readonly=True, select=True), + 'invoiced': fields.function(_invoiced, method=True, string='Invoiced & Paid', type='boolean'), 'invoiced_rate': fields.function(_invoiced_rate, method=True, string='Invoiced', type='float'), 'invoice_method': fields.selection([('manual','Manual'),('order','From Order'),('picking','From Picking')], 'Invoicing Control', required=True, help="From Order: a draft invoice will be pre-generated based on the purchase order. The accountant " \ diff --git a/addons/purchase/purchase_workflow.xml b/addons/purchase/purchase_workflow.xml index 2a3884739ed..26f83d5a554 100644 --- a/addons/purchase/purchase_workflow.xml +++ b/addons/purchase/purchase_workflow.xml @@ -55,7 +55,7 @@ invoice_done - write({'invoiced':1,'state':'approved'}) + write({'state':'approved'}) function diff --git a/addons/purchase/stock.py b/addons/purchase/stock.py index df74b52036a..11383b66b19 100644 --- a/addons/purchase/stock.py +++ b/addons/purchase/stock.py @@ -1,7 +1,7 @@ # -*- encoding: utf-8 -*- ############################################################################## # -# OpenERP, Open Source Management Solution +# OpenERP, Open Source Management Solution # Copyright (C) 2004-2009 Tiny SPRL (). All Rights Reserved # $Id$ # @@ -46,7 +46,7 @@ class stock_picking(osv.osv): _defaults = { 'purchase_id': lambda *a: False, } - + def get_currency_id(self, cursor, user, picking): if picking.purchase_id: return picking.purchase_id.pricelist_id.currency_id.id @@ -93,8 +93,7 @@ class stock_picking(osv.osv): def _invoice_hook(self, cursor, user, picking, invoice_id): purchase_obj = self.pool.get('purchase.order') if picking.purchase_id: - purchase_obj.write(cursor, user, [picking.purchase_id.id], {'invoiced':True, - 'invoice_id': invoice_id, + purchase_obj.write(cursor, user, [picking.purchase_id.id], {'invoice_id': invoice_id, }) return super(stock_picking, self)._invoice_hook(cursor, user, picking, invoice_id) From 853789d0aaea956e677af869ad9336af0b7fcfc3 Mon Sep 17 00:00:00 2001 From: Fabien Pinckaers Date: Tue, 5 May 2009 23:14:47 +0200 Subject: [PATCH 19/19] bugfixes bzr revid: fp@tinyerp.com-20090505211447-1cbbmt92q52u0hcq --- addons/project/project.py | 28 +++++++++++----------------- addons/project/project_view.xml | 2 +- 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/addons/project/project.py b/addons/project/project.py index e180bf92a1b..e1af9e8293a 100644 --- a/addons/project/project.py +++ b/addons/project/project.py @@ -156,26 +156,20 @@ class project(osv.osv): return res def copy(self, cr, uid, id, default={},context={}): + proj = self.browse(cr, uid, id, context=context) default = default or {} - default['child_id'] = [] - return super(project, self).copy(cr, uid, id, default, context) + context['active_test'] = False + default['state'] = 'open' + default['name'] = proj.name+_(' (copy)') + res = super(project, self).copy(cr, uid, id, default, context) + ids = self.search(cr, uid, [('parent_id','child_of', [res])]) + cr.execute('update project_task set active=True where project_id in ('+','.join(map(str,ids))+')') + return res def duplicate_template(self, cr, uid, ids,context={}): - for proj in self.browse(cr, uid, ids): - parent_id=context.get('parent_id',False) - new_id=self.pool.get('project.project').copy(cr, uid, proj.id,default={'name':proj.name+_(' (copy)'),'state':'open','parent_id':parent_id}) - cr.execute('select id from project_task where project_id=%s', (proj.id,)) - res = cr.fetchall() - for (tasks_id,) in res: - self.pool.get('project.task').copy(cr, uid, tasks_id,default={'project_id':new_id,'active':True}, context=context) - cr.execute('select id from project_project where parent_id=%s', (proj.id,)) - res = cr.fetchall() - project_ids = [x[0] for x in res] - for child in project_ids: - self.duplicate_template(cr, uid, [child],context={'parent_id':new_id}) - - # TODO : Improve this to open the new project (using a wizard) - + default = {'parent_id': context.get('parent_id',False)} + for id in ids: + self.copy(cr, uid, id, default=default) cr.commit() raise osv.except_osv(_('Operation Done'), _('A new project has been created !\nWe suggest you to close this one and work on this new project.')) diff --git a/addons/project/project_view.xml b/addons/project/project_view.xml index 1e10a534ece..2784a0780bc 100644 --- a/addons/project/project_view.xml +++ b/addons/project/project_view.xml @@ -512,7 +512,7 @@ form tree,form,calendar,graph,gantt [('project_id', 'child_of', [active_id])] - {'project_id':active_id} + {'project_id':active_id, 'active_test':False}