Use relatorio for reporting

This commit is contained in:
Cédric Krier committed 2008-07-17 19:33:42 +02:00
1 parent a051287eef
commit 5480bd311d
1 file changed
+73 -232
+73 -232
View File
@@ -21,6 +21,11 @@ import datetime
import md5
from _strptime import LocaleTime
from base64 import decodestring
import relatorio
import tempfile
from genshi.filters import Translator
import traceback
from trytond.config import CONFIG
MODULE_LIST = []
MODULE_CLASS_LIST = {}
@@ -241,6 +246,34 @@ PARENTS = {
}
class ReportFactory:
def __call__(self, objects, **kwargs):
data = {}
data['objects'] = objects
data.update(kwargs)
return data
class TranslateFactory:
def __init__(self, cursor, report_name, language, translation):
self.cursor = cursor
self.report_name = report_name
self.language = language
self.translation = translation
def __call__(self, text):
res = self.translation._get_source(self.cursor,
self.report_name, 'odt', self.language, text)
if res:
return res
return text
def set_language(self, language):
self.language = language
class Report(object):
_name = ""
@@ -304,34 +337,33 @@ class Report(object):
def parse(self, cursor, user, report, objects, datas, context):
localcontext = {}
localcontext['datas'] = datas
localcontext['objects'] = objects
localcontext['user'] = self.pool.get('res.user').\
browse(cursor, user, user)
localcontext['_language_cache'] = {}
localcontext['formatLang'] = self.format_lang
localcontext['decodestring'] = decodestring
localcontext['time'] = time
localcontext['datetime'] = datetime
localcontext.update(context)
translate = TranslateFactory(cursor, self._name,
context.get('language', 'en_US'),
self.pool.get('ir.translation'))
localcontext['setLang'] = lambda language: translate.set_language(language)
if not report.report_content:
raise ExceptOSV('Error', 'Missing report file!')
#cStringIO difference:
#calling StringIO() with a string parameter creates a read-only object
content_io = StringIO.StringIO()
content_io.write(report.report_content)
content_z = zipfile.ZipFile(content_io, mode='r')
localcontext['content_z'] = content_z
content_xml = content_z.read('content.xml')
dom = xml.dom.minidom.parseString(content_xml)
node = dom.documentElement
self._parse_node(cursor, user, node, localcontext, context)
raise Exception('Error', 'Missing report file!')
style_z = zipfile.ZipFile(content_io, mode='r')
style_xml = content_z.read('styles.xml')
style_z.close()
dom_style = xml.dom.minidom.parseString(style_xml)
node_style = dom_style.documentElement
self._parse_node(cursor, user, node_style, localcontext, context)
content_z.close()
fd, path = tempfile.mkstemp(suffix='.odt', prefix='trytond')
os.write(fd, report.report_content)
os.close(fd)
pictures = []
if report.style_content:
pictures = []
content_z = zipfile.ZipFile(path, mode='r')
style_xml = content_z.read('styles.xml')
dom_style = xml.dom.minidom.parseString(style_xml)
node_style = dom_style.documentElement
#cStringIO difference:
#calling StringIO() with a string parameter creates a read-only object
style2_io = StringIO.StringIO()
@@ -346,7 +378,6 @@ class Report(object):
style2_io.close()
dom_style2 = xml.dom.minidom.parseString(style2_xml)
node_style2 = dom_style2.documentElement
self._parse_node(cursor, user, node_style2, localcontext, context)
style_header_node2 = self.find(node_style2, 'master-styles')
style_header_node = self.find(node_style, 'master-styles')
style_header_node.parentNode.replaceChild(style_header_node2,
@@ -356,27 +387,28 @@ class Report(object):
style_header_node.parentNode.replaceChild(style_header_node2,
style_header_node)
content_z = zipfile.ZipFile(content_io, mode='a')
content_z.writestr('content.xml',
'<?xml version="1.0" encoding="UTF-8"?>' + \
dom.documentElement.toxml('utf-8'))
content_z.writestr('styles.xml',
'<?xml version="1.0" encoding="UTF-8"?>' + \
dom_style.documentElement.toxml('utf-8'))
content_z = zipfile.ZipFile(path, mode='a')
if localcontext.get('pictures'):
pictures.extend(localcontext['pictures'])
for file, picture in pictures:
content_z.writestr(file, picture)
content_z.close()
data = content_io.getvalue()
content_io.close()
content_z.writestr('styles.xml',
'<?xml version="1.0" encoding="UTF-8"?>' + \
dom_style.documentElement.toxml('utf-8'))
for file, picture in pictures:
content_z.writestr(file, picture)
content_z.close()
translator = Translator(translate)
rel_report = relatorio.Report(path, 'application/vnd.oasis.opendocument.text',
ReportFactory(), relatorio.MIMETemplateLoader())
rel_report.filters.insert(0, translator)
data = rel_report(objects, **localcontext).render().getvalue()
os.remove(path)
output_format = report.output_format.format
if output_format == 'pdf':
data = self.convert_pdf(data)
return (output_format, data)
def convert_pdf(self, data):
"""
Convert report to PDF using OpenOffice.org.
@@ -428,174 +460,6 @@ class Report(object):
ExceptOSV('Error', 'Error converting to PDF')
return res_data
def _parse_node(self, cursor, user, node, localcontext, context,
node_context = None):
if node_context is None:
node_context = {}
while True:
if node.hasChildNodes():
node = node.firstChild
elif node.nextSibling:
node = node.nextSibling
else:
while node and not node.nextSibling:
node = node.parentNode
if not node:
break
node = node.nextSibling
if node in node_context:
localcontext.update(node_context[node])
if node.nodeType in (node.CDATA_SECTION_NODE, node.TEXT_NODE):
res = self._parse_text(cursor, user, node, localcontext, context,
node_context)
if isinstance(res, dom.Node):
node = res
if node.nodeType == node.ELEMENT_NODE:
if node.nodeName == "draw:frame":
self._parse_draw_frame(cursor, user, node, localcontext, context,)
def _parse_draw_frame(self, cursor, user, node, localcontext, context):
"""When a picture name in the template document contain
"replaceWith(content, extension)", this piture is replaced
with the content, a file name is generated automatically with
the given extension
"""
if "replaceWith" not in node.attributes["draw:name"].nodeValue:
return
localcontext.setdefault('pictures', [])
ctx = localcontext.copy()
ctx.update(context)
ctx["replaceWith"] = lambda x,y: (x,y)
try:
res = eval(node.attributes["draw:name"].nodeValue, ctx)
except:
Logger().notify_channel('report', LOG_ERROR,
'Error on eval "%s"' % node.nodeValue)
raise
if isinstance(res, tuple):
data = decodestring(res[0])
filename = 'Pictures/%s.%s'% (md5.new(data).hexdigest(), res[1])
localcontext['pictures'].append((filename,data))
for child_node in node.childNodes:
if child_node.nodeName == "draw:image":
child_node.attributes["xlink:href"].nodeValue = filename
continue
def _parse_text(self, cursor, user, node, localcontext, context,
node_context):
if node.parentNode.tagName == 'text:text-input':
ctx = localcontext.copy()
ctx.update(context)
ctx['RepeatIn'] = lambda lst, name, parents=False: \
self.repeat_in(lst, name, parents=parents,
tnode=node, node_context=node_context)
ctx['setTag'] = lambda oldtag, newtag, attrs=None: \
self.set_tag(oldtag, newtag, attrs=attrs, tnode=node)
ctx['removeParentNode'] = lambda tag='p': \
self.remove_parent_node(tag, tnode=node)
ctx['setLang'] = lambda lang: \
self.set_lang(lang, localcontext)
ctx['formatLang'] = lambda value, digits=2, date=False: \
self.format_lang(value, digits=digits, date=date,
localcontext=localcontext)
ctx['time'] = time
ctx['datetime'] = datetime
try:
res = eval(node.nodeValue, ctx)
except:
Logger().notify_channel('report', LOG_ERROR,
'Error on eval "%s"' % node.nodeValue)
raise
if isinstance(res, bool):
res = ''
if hasattr(res, '__str__'):
res = res.__str__()
if isinstance(res, basestring):
if '\n' in res:
parent2 = node.parentNode.parentNode
parent = node.parentNode
first = True
newnode = None
for val in res.decode('utf-8').split('\n'):
if first:
newnode = node
first = False
else:
newnode = node.cloneNode(1)
newnode.nodeValue = val
parent2.insertBefore(newnode, parent)
newnode = node.parentNode.cloneNode(1)
newnode.nodeType = newnode.ELEMENT_NODE
newnode.tagName = 'text:line-break'
newnode.firstChild.nodeValue = ''
if newnode.getAttribute('text:style-name'):
newnode.removeAttribute('text:style-name')
parent2.insertBefore(newnode, parent)
parent2.removeChild(parent)
if newnode:
parent2.removeChild(newnode)
else:
node.nodeValue = res.decode('utf-8')
node.parentNode.parentNode.replaceChild(node, node.parentNode)
return res
if 'language' in localcontext:
lang = localcontext['language']
text = node.nodeValue
if lang and text and not text.isspace():
translation_obj = self.pool.get('ir.translation')
new_text = translation_obj._get_source(cursor,
self._name, 'odt', lang, text)
if new_text:
node.nodeValue = new_text.decode('utf-8')
return None
def repeat_in(self, lst, name, parents=False, tnode=None, node_context=None):
node = self.find_parent(tnode, parents or PARENTS)
pnode = node.parentNode
nextnode = node.nextSibling
pnode.removeChild(node)
tnode.parentNode.parentNode.removeChild(tnode.parentNode)
if not lst:
return pnode
for i in range(len(lst)):
newnode = node.cloneNode(1)
if nextnode:
pnode.insertBefore(newnode, nextnode)
else:
pnode.appendChild(newnode)
node_context[newnode] = {name: lst[i]}
return pnode
def set_tag(self, oldtag, newtag, attrs=None, tnode=None):
if attrs is None:
attrs = {}
node = self.find_parent(tnode, [oldtag])
if node:
node.tagName = newtag
for key, val in attrs.items():
node.setAttribute(key, val)
return None
def remove_parent_node(self, tag='p', tnode=None):
node = self.find_parent(tnode, [tag])
if node:
pnode = node.parentNode
pnode.removeChild(node)
return pnode
def find_parent(self, node, parents):
while True:
if not node.parentNode:
return None
node = node.parentNode
if node.nodeType == node.ELEMENT_NODE \
and node.localName in parents:
break
return node
def find(self, tnode, tag):
for node in tnode.childNodes:
if node.nodeType == node.ELEMENT_NODE \
@@ -606,43 +470,20 @@ class Report(object):
return res
return None
def set_lang(self, lang, localcontext):
localcontext['language'] = lang
_language_cache = localcontext['_language_cache']
for obj in localcontext['objects']:
obj._context['language'] = lang
for table in obj._cache:
for obj_id in obj._cache[table]:
_language_cache.setdefault(
obj._context['language'], {}).setdefault(
table, {}).update(
obj._cache[table][obj_id])
if lang in _language_cache \
and table in _language_cache[lang] \
and obj_id in _language_cache[lang][table]:
obj._cache[table][obj_id] = \
_language_cache[lang][table][obj_id]
else:
obj._cache[table][obj_id] = {'id': obj_id}
return ''
def format_lang(self, value, digits=2, date=False, localcontext=None):
if localcontext is None:
localcontext = {}
def format_lang(self, value, digits=2, date=False, language='en_US'):
encoding = locale.getdefaultlocale()[1]
if encoding == 'utf':
encoding = 'UTF-8'
if encoding == 'cp1252':
encoding = '1252'
lang = localcontext.get('language', False) or 'en_US'
try:
if os.name == 'nt':
lang = _LOCALE2WIN32.get(lang, lang)
locale.setlocale(locale.LC_ALL, lang + '.' + encoding)
language = _LOCALE2WIN32.get(language, language)
locale.setlocale(locale.LC_ALL, language + '.' + encoding)
except Exception:
Logger().notify_channel('web-service', LOG_ERROR,
'Report %s: unable to set locale "%s"' % \
(self._name, lang + '.' + encoding))
(self._name, language + '.' + encoding))
if date:
if isinstance(value, time.struct_time):
locale_format = LocaleTime().LC_date.replace('%y', '%Y')