summaryrefslogtreecommitdiff
path: root/bin/report/interface.py
diff options
context:
space:
mode:
authorpinky <>2006-12-07 13:41:40 +0000
committerpinky <>2006-12-07 13:41:40 +0000
commit004a0b996ff8f269451e07346f71a129a1f3fbaf (patch)
tree48f43c1fc2ca4c1f4d5648e7e73553d3f4ccae06 /bin/report/interface.py
New trunk
bzr revid: pinky-3f10ee12cea3c4c75cef44ab04ad33ef47432907
Diffstat (limited to 'bin/report/interface.py')
-rw-r--r--bin/report/interface.py195
1 files changed, 195 insertions, 0 deletions
diff --git a/bin/report/interface.py b/bin/report/interface.py
new file mode 100644
index 00000000000..53983771be0
--- /dev/null
+++ b/bin/report/interface.py
@@ -0,0 +1,195 @@
+##############################################################################
+#
+# Copyright (c) 2004-2006 TINY SPRL. (http://tiny.be) All Rights Reserved.
+#
+# $Id: interface.py 1304 2005-09-08 14:35:42Z nicoe $
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsability of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# garantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# This program is Free Software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+##############################################################################
+
+import os,re
+
+#Ged> Why do we use libxml2 here instead of xml.dom like in other places of the code?
+import libxml2
+import libxslt
+
+import netsvc
+import pooler
+
+import tools
+import print_xml
+import render
+
+#
+# encode a value to a string in utf8 and converts XML entities
+#
+def toxml(val):
+ if isinstance(val, str):
+ str_utf8 = val
+ elif isinstance(val, unicode):
+ str_utf8 = val.encode('utf-8')
+ else:
+ str_utf8 = str(val)
+ return str_utf8.replace('&', '&amp;').replace('<','&lt;').replace('>','&gt;')
+
+class report_int(netsvc.Service):
+ def __init__(self, name, audience='*'):
+ super(report_int, self).__init__(name, audience)
+ if name[0:7]<>'report.':
+ raise 'ConceptionError, bad report name'
+ self.name = name
+ self.id = 0
+ self.name2 = '.'.join(name.split('.')[1:])
+ self.joinGroup('report')
+ self.exportMethod(self.create)
+
+ def create(self, cr, uid, ids, datas, context={}):
+ return False
+
+"""
+ Class to automatically build a document using the transformation process:
+ XML -> DATAS -> RML -> PDF
+ -> HTML
+ using a XSL:RML transformation
+"""
+class report_rml(report_int):
+ def __init__(self, name, table, tmpl, xsl):
+ super(report_rml, self).__init__(name)
+ self.table = table
+ self.tmpl = tmpl
+ self.xsl = xsl
+ self.bin_datas = {}
+ self.generators = {'pdf': self.create_pdf, 'html': self.create_html, 'raw': self.create_raw}
+
+ def create(self, cr, uid, ids, datas, context):
+ xml = self.create_xml(cr, uid, ids, datas, context)
+# file('/tmp/terp.xml','wb+').write(xml)
+ if datas.get('report_type', 'pdf') == 'raw':
+ return xml
+ rml = self.create_rml(cr, xml, uid, context)
+# file('/tmp/terp.rml','wb+').write(rml)
+ report_type = datas.get('report_type', 'pdf')
+ create_doc = self.generators[report_type]
+ pdf = create_doc(rml)
+ return (pdf, report_type)
+
+ def create_xml(self, cr, uid, ids, datas, context={}):
+ doc = print_xml.document(cr, uid, datas, {})
+ self.bin_datas = doc.bin_datas
+ doc.parse(self.tmpl, ids, self.table, context)
+ xml = doc.xml_get()
+ doc.close()
+ return self.post_process_xml_data(cr, uid, xml, context)
+
+ def post_process_xml_data(self, cr, uid, xml, context={}):
+ # find the position of the 3rd tag
+ # (skip the <?xml ...?> and the "root" tag)
+ iter = re.finditer('<[^>]*>', xml)
+ i = iter.next()
+ i = iter.next()
+ pos_xml = i.end()
+
+ doc = print_xml.document(cr, uid, {}, {})
+ tmpl_path = os.path.join(tools.config['root_path'], 'addons/custom/corporate_defaults.xml')
+ doc.parse(tmpl_path, [uid], 'res.users', context)
+ corporate_header = doc.xml_get()
+ doc.close()
+
+ # find the position of the tag after the <?xml ...?> tag
+ iter = re.finditer('<[^>]*>', corporate_header)
+ i = iter.next()
+ pos_header = i.end()
+
+ return xml[:pos_xml] + corporate_header[pos_header:] + xml[pos_xml:]
+
+ #
+ # TODO: The translation doesn't work for "<tag t="1">textext<tag> tex</tag>text</tag>"
+ #
+ def create_rml(self, cr, xml, uid, context={}):
+ service = netsvc.LocalService("object_proxy")
+
+ # In some case we might not use xsl ...
+ if not self.xsl:
+ return xml
+
+ # load XSL (parse it to the XML level)
+ styledoc = libxml2.parseFile(os.path.join(tools.config['root_path'],self.xsl))
+
+ #TODO: get all the translation in one query. That means we have to:
+ # * build a list of items to translate,
+ # * issue the query to translate them,
+ # * (re)build/update the stylesheet with the translated items
+
+ # translate the XSL stylesheet
+ def look_down(child, lang):
+ while child is not None:
+ if (child.type == "element") and child.hasProp('t'):
+ #FIXME: use cursor
+ res = service.execute(cr.dbname, uid, 'ir.translation', '_get_source', self.name2, 'xsl', lang, child.content)
+ if res:
+ child.setContent(res)
+ look_down(child.children, lang)
+ child = child.next
+
+ if context.get('lang', False):
+ look_down(styledoc.children, context['lang'])
+
+ style = libxslt.parseStylesheetDoc(styledoc) # parse XSL
+
+ doc = libxml2.parseMemory(xml,len(xml)) # load XML (data)
+ result = style.applyStylesheet(doc, None) # create RML (apply XSL to XML data)
+ xml = style.saveResultToString(result) # save result to string
+
+ style.freeStylesheet()
+ doc.freeDoc()
+ result.freeDoc()
+ return xml
+
+ def create_pdf(self, xml):
+ obj = render.rml(xml, self.bin_datas, os.path.dirname(self.tmpl))
+ obj.render()
+ return obj.get()
+
+ def create_html(self, xml):
+ obj = render.rml2html(xml, self.bin_datas)
+ obj.render()
+ return obj.get()
+
+ def create_raw(self, xml):
+ return xml
+
+from report_sxw import report_sxw
+
+def register_all(db):
+ opj = os.path.join
+ #FIXME: multi-db, quoique... ca init le code donc ok. Enfin, du moins si les modules sont les memes.
+ cr = db.cursor()
+ cr.execute("SELECT * FROM ir_act_report_xml WHERE auto ORDER BY id")
+ result = cr.dictfetchall()
+ cr.close()
+ for r in result:
+ if r['report_rml']:
+ report_sxw('report.'+r['report_name'], r['model'], opj('addons',r['report_rml']))
+ if r['report_xsl']:
+ report_rml('report.'+r['report_name'], r['model'], opj('addons',r['report_xml']), r['report_xsl'] and opj('addons',r['report_xsl']))
+