Commit a748c0ca authored by Tatuya Kamada's avatar Tatuya Kamada

* Fix:

  if listbox has no data line, must not have table rows

* Append:
  ReportSection and ImageField mapping features
  TODO: write their unit test



git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@26389 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 6eec15d5
...@@ -35,17 +35,20 @@ from Products.ERP5Form.FormBox import FormBox ...@@ -35,17 +35,20 @@ from Products.ERP5Form.FormBox import FormBox
from Products.ERP5Form.ImageField import ImageField from Products.ERP5Form.ImageField import ImageField
from Products.ERP5OOo.OOoUtils import OOoBuilder from Products.ERP5OOo.OOoUtils import OOoBuilder
from Acquisition import Implicit from Acquisition import Implicit, aq_base
from Globals import InitializeClass, DTMLFile, Persistent, get_request from Globals import InitializeClass, DTMLFile, Persistent, get_request
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
from AccessControl.Role import RoleManager from AccessControl.Role import RoleManager
from OFS.SimpleItem import Item, SimpleItem from OFS.SimpleItem import Item, SimpleItem
from urllib import quote from OFS.Image import File
from urllib import quote, quote_plus
from copy import deepcopy from copy import deepcopy
from lxml import etree from lxml import etree
from zLOG import LOG, DEBUG, INFO, WARNING from zLOG import LOG, DEBUG, INFO, WARNING
from mimetypes import guess_extension from mimetypes import guess_extension
from DateTime import DateTime from DateTime import DateTime
from decimal import Decimal
import re
try: try:
from webdav.Lockable import ResourceLockedError from webdav.Lockable import ResourceLockedError
...@@ -99,15 +102,16 @@ class FormPrintout(Implicit, Persistent, RoleManager, Item): ...@@ -99,15 +102,16 @@ class FormPrintout(Implicit, Persistent, RoleManager, Item):
a form as a ODF document content, and a template as a document layout. a form as a ODF document content, and a template as a document layout.
WARNING: The Form Printout currently supports only ODT format document. WARNING: The Form Printout currently supports only ODT format document.
And the functions only supports paragraphs and tables.
The functions status:
Fields <-> Paragraphs: supported
ListBox <-> Table: supported Fields -> Paragraphs: supported
Report Section <-> Tables: experimentally supported ListBox -> Table: supported
FormBox <-> Frame: experimentally supported Report Section -> Frames: experimentally supported
Photo <-> Image: not supported yet FormBox -> Frame: experimentally supported
Style group <-> styles.xml: supported ImageField -> Photo: supported
Meta group <-> meta.xml: not supported yet styles.xml: supported
meta.xml: not supported yet
""" """
meta_type = "ERP5 Form Printout" meta_type = "ERP5 Form Printout"
...@@ -211,6 +215,8 @@ InitializeClass(FormPrintout) ...@@ -211,6 +215,8 @@ InitializeClass(FormPrintout)
class ODFStrategy(Implicit): class ODFStrategy(Implicit):
"""ODFStrategy creates a ODF Document. """ """ODFStrategy creates a ODF Document. """
odf_existent_name_list = []
def render(self, extra_context={}): def render(self, extra_context={}):
"""Render a odf document, form as a content, template as a template. """Render a odf document, form as a content, template as a template.
...@@ -240,6 +246,7 @@ class ODFStrategy(Implicit): ...@@ -240,6 +246,7 @@ class ODFStrategy(Implicit):
# Create a new builder instance # Create a new builder instance
ooo_builder = OOoBuilder(ooo_document) ooo_builder = OOoBuilder(ooo_document)
self.odf_existent_name_list = ooo_builder.getNameList()
# content.xml # content.xml
ooo_builder = self._replaceContentXml(ooo_builder=ooo_builder, extra_context=extra_context) ooo_builder = self._replaceContentXml(ooo_builder=ooo_builder, extra_context=extra_context)
...@@ -264,10 +271,12 @@ class ODFStrategy(Implicit): ...@@ -264,10 +271,12 @@ class ODFStrategy(Implicit):
content_element_tree = self._replaceXmlByForm(element_tree=content_element_tree, content_element_tree = self._replaceXmlByForm(element_tree=content_element_tree,
form=form, form=form,
here=here, here=here,
extra_context=extra_context) extra_context=extra_context,
ooo_builder=ooo_builder)
# mapping ERP5Report report method to ODF # mapping ERP5Report report method to ODF
content_element_tree = self._replaceXmlByReportSection(element_tree=content_element_tree, content_element_tree = self._replaceXmlByReportSection(element_tree=content_element_tree,
extra_context=extra_context) extra_context=extra_context,
ooo_builder=ooo_builder)
content_xml = etree.tostring(content_element_tree, encoding='utf-8') content_xml = etree.tostring(content_element_tree, encoding='utf-8')
# Replace content.xml in master openoffice template # Replace content.xml in master openoffice template
...@@ -286,7 +295,8 @@ class ODFStrategy(Implicit): ...@@ -286,7 +295,8 @@ class ODFStrategy(Implicit):
styles_element_tree = self._replaceXmlByForm(element_tree=styles_element_tree, styles_element_tree = self._replaceXmlByForm(element_tree=styles_element_tree,
form=form, form=form,
here=here, here=here,
extra_context=extra_context) extra_context=extra_context,
ooo_builder=ooo_builder)
styles_xml = etree.tostring(styles_element_tree, encoding='utf-8') styles_xml = etree.tostring(styles_element_tree, encoding='utf-8')
ooo_builder.replace('styles.xml', styles_xml) ooo_builder.replace('styles.xml', styles_xml)
...@@ -306,7 +316,7 @@ class ODFStrategy(Implicit): ...@@ -306,7 +316,7 @@ class ODFStrategy(Implicit):
return ooo_builder return ooo_builder
def _replaceXmlByForm(self, element_tree=None, form=None, here=None, def _replaceXmlByForm(self, element_tree=None, form=None, here=None,
extra_context=None, render_prefix=None): extra_context=None, render_prefix=None, ooo_builder=None):
field_list = form.get_fields() field_list = form.get_fields()
REQUEST = get_request() REQUEST = get_request()
for (count, field) in enumerate(field_list): for (count, field) in enumerate(field_list):
...@@ -321,9 +331,10 @@ class ODFStrategy(Implicit): ...@@ -321,9 +331,10 @@ class ODFStrategy(Implicit):
field_id=field.id, field_id=field.id,
form = sub_form, form = sub_form,
REQUEST=REQUEST) REQUEST=REQUEST)
#elif isinstance(field, ImageField): elif isinstance(field, ImageField):
# element_tree = self._replaceXmlByImageField(element_tree=element_tree, element_tree = self._replaceXmlByImageField(element_tree=element_tree,
# image_field=field) image_field=field,
ooo_builder=ooo_builder)
else: else:
element_tree = self._replaceNodeViaReference(element_tree=element_tree, element_tree = self._replaceNodeViaReference(element_tree=element_tree,
field=field) field=field)
...@@ -377,7 +388,7 @@ class ODFStrategy(Implicit): ...@@ -377,7 +388,7 @@ class ODFStrategy(Implicit):
node.tail = '' node.tail = ''
return element_tree return element_tree
def _replaceXmlByReportSection(self, element_tree=None, extra_context=None): def _replaceXmlByReportSection(self, element_tree=None, extra_context=None, ooo_builder=None):
if not extra_context.has_key('report_method') or extra_context['report_method'] is None: if not extra_context.has_key('report_method') or extra_context['report_method'] is None:
return element_tree return element_tree
report_method = extra_context['report_method'] report_method = extra_context['report_method']
...@@ -386,6 +397,8 @@ class ODFStrategy(Implicit): ...@@ -386,6 +397,8 @@ class ODFStrategy(Implicit):
REQUEST = get_request() REQUEST = get_request()
request = extra_context.get('REQUEST', REQUEST) request = extra_context.get('REQUEST', REQUEST)
render_prefix = None render_prefix = None
frame_paragraph_index_dict = {}
for (index, report_item) in enumerate(report_section_list): for (index, report_item) in enumerate(report_section_list):
if index > 0: if index > 0:
render_prefix = 'x%s' % index render_prefix = 'x%s' % index
...@@ -393,14 +406,48 @@ class ODFStrategy(Implicit): ...@@ -393,14 +406,48 @@ class ODFStrategy(Implicit):
here = report_item.getObject(portal_object) here = report_item.getObject(portal_object)
form_id = report_item.getFormId() form_id = report_item.getFormId()
form = getattr(here, form_id) form = getattr(here, form_id)
element_tree = self._replaceXmlByForm(element_tree=element_tree,
report_section_frame_xpath = '//draw:frame[@draw:name="%s"]' % form_id
frame_list = element_tree.xpath(report_section_frame_xpath, namespaces=element_tree.nsmap)
if len(frame_list) is 0:
continue
frame = frame_list[0]
paragraph = frame.getparent()
parent = paragraph.getparent()
frame_paragraph_element_tree = deepcopy(paragraph)
if not form_id in frame_paragraph_index_dict:
frame_paragraph_index_dict[form_id] = parent.index(paragraph)
parent.remove(paragraph)
else:
self._setReportSectionFrameName(form_id=form_id,
frame_paragraph_index=frame_paragraph_index_dict[form_id],
frame_paragraph_element_tree=frame_paragraph_element_tree)
frame_paragraph_index = frame_paragraph_index_dict[form_id]
frame_paragraph_element_tree = self._replaceXmlByForm(element_tree=frame_paragraph_element_tree,
form=form, form=form,
here=here, here=here,
extra_context=extra_context, extra_context=extra_context,
render_prefix=render_prefix) ooo_builder=ooo_builder)
parent.insert(frame_paragraph_index, frame_paragraph_element_tree)
frame_paragraph_index_dict[form_id] = frame_paragraph_index + 1
report_item.popReport(portal_object, render_prefix = render_prefix) report_item.popReport(portal_object, render_prefix = render_prefix)
return element_tree return element_tree
def _setReportSectionFrameName(self,
form_id='',
frame_paragraph_index=0,
frame_paragraph_element_tree=None):
report_section_frame_name = "%s_%s" % (form_id, frame_paragraph_index)
draw_name_attribute = '{%s}name' % frame_paragraph_element_tree.nsmap['draw']
report_section_frame = frame_paragraph_element_tree.xpath('draw:frame[@draw:name="%s"]' % form_id,
namespaces=frame_paragraph_element_tree.nsmap)
if len(report_section_frame) is 0:
return
report_section_frame[0].set(draw_name_attribute, report_section_frame_name)
def _replaceXmlByFormbox(self, element_tree=None, field_id=None, form=None, REQUEST=None): def _replaceXmlByFormbox(self, element_tree=None, field_id=None, form=None, REQUEST=None):
draw_xpath = '//draw:frame[@draw:name="%s"]/draw:text-box/*' % field_id draw_xpath = '//draw:frame[@draw:name="%s"]/draw:text-box/*' % field_id
text_list = element_tree.xpath(draw_xpath, namespaces=element_tree.nsmap) text_list = element_tree.xpath(draw_xpath, namespaces=element_tree.nsmap)
...@@ -419,15 +466,68 @@ class ODFStrategy(Implicit): ...@@ -419,15 +466,68 @@ class ODFStrategy(Implicit):
parent.append(child) parent.append(child)
return element_tree return element_tree
def _replaceXmlByImageField(self, element_tree=None, image_field=None): def _replaceXmlByImageField(self, element_tree=None, image_field=None, ooo_builder=None):
alt = image_field.get_value('description') or image_field.get_value('title') alt = image_field.get_value('description') or image_field.get_value('title')
image_xpath = '//draw:frame[@draw:name="%s"]/*' % image_field.id image_xpath = '//draw:frame[@draw:name="%s"]/*' % image_field.id
image_list = element_tree.xpath(image_xpath, namespaces=element_tree.nsmap) image_list = element_tree.xpath(image_xpath, namespaces=element_tree.nsmap)
if len(image_list) > 0: if len(image_list) is 0:
image_list[0].set("{%s}href" % element_tree.nsmap['xlink'], image_field.absolute_url()) return element_tree
path = image_field.get_value('default')
picture = self.getPortalObject().unrestrictedTraverse(path)
picture_data = getattr(aq_base(picture), 'data', None)
picture_type = picture.getContentType()
picture_path = self._createOdfUniqueFileName(path=path, picture_type=picture_type)
ooo_builder.addFileEntry(picture_path, media_type=picture_type, content=picture_data)
image_node = image_list[0]
picture_size = self._getPictureSize(picture, image_node)
image_node.set('{%s}href' % element_tree.nsmap['xlink'], picture_path)
image_frame = image_node.getparent()
image_frame.set('{%s}width' % element_tree.nsmap['svg'], picture_size[0])
image_frame.set('{%s}height' % element_tree.nsmap['svg'], picture_size[1])
return element_tree return element_tree
def _createOdfUniqueFileName(self, path='', picture_type=''):
extension = guess_extension(picture_type)
picture_path = 'Pictures/%s%s' % (quote_plus(path), extension)
if picture_path not in self.odf_existent_name_list:
return picture_path
number = 0
while True:
picture_path = 'Pictures/%s_%s%s' % (path, number, extension)
if picture_path not in self.odf_existent_name_list:
return picture_path
number += 1
def _getPictureSize(self, picture=None, image_node=None):
if picture is None or image_node is None:
return ('0cm', '0cm')
draw_frame_node = image_node.getparent()
svg_width = draw_frame_node.attrib.get('{%s}width' % draw_frame_node.nsmap['svg'])
svg_height = draw_frame_node.attrib.get('{%s}height' % draw_frame_node.nsmap['svg'])
if svg_width is None or svg_height is None:
return ('0cm', '0cm')
# if not match causes exception
width_tuple = re.match("(\d[\d\.]*)(.*)", svg_width).groups()
height_tuple = re.match("(\d[\d\.]*)(.*)", svg_height).groups()
unit = width_tuple[1]
w = Decimal(width_tuple[0])
h = Decimal(height_tuple[0])
aspect_ratio = 1
try: # try image properties
aspect_ratio = Decimal(picture.width) / Decimal(picture.height)
except (TypeError, ZeroDivisionError):
try: # try ERP5.Document.Image API
height = Decimal(picture.getHeight())
if height:
aspect_ratio = Decimal(picture.getWidth()) / height
except AttributeError: # fallback to Photo API
height = float(picture.height())
if height:
aspect_ratio = Decimal(picture.width()) / height
w = h * aspect_ratio
return (str(w) + unit, str(h) + unit)
def _appendTableByListbox(self, def _appendTableByListbox(self,
element_tree=None, element_tree=None,
listbox=None, listbox=None,
...@@ -484,7 +584,7 @@ class ODFStrategy(Implicit): ...@@ -484,7 +584,7 @@ class ODFStrategy(Implicit):
row = self._updateColumnValue(row, listbox_column_list) row = self._updateColumnValue(row, listbox_column_list)
newtable.append(row) newtable.append(row)
is_top = False is_top = False
elif listboxline.isStatLine() or index is last_index: elif listboxline.isStatLine() or (index is last_index and listboxline.isDataLine()):
row = deepcopy(row_bottom) row = deepcopy(row_bottom)
row = self._updateColumnStatValue(row, listbox_column_list, row_middle) row = self._updateColumnStatValue(row, listbox_column_list, row_middle)
newtable.append(row) newtable.append(row)
......
...@@ -479,7 +479,6 @@ class TestFormPrintout(ERP5TypeTestCase): ...@@ -479,7 +479,6 @@ class TestFormPrintout(ERP5TypeTestCase):
REQUEST = request) REQUEST = request)
self.assertEqual(len(listboxline_list), 4) self.assertEqual(len(listboxline_list), 4)
self.assertTrue(listboxline_list[1].getColumnProperty('title') == "foo_title_7") self.assertTrue(listboxline_list[1].getColumnProperty('title') == "foo_title_7")
LOG('testFormPrintout start_date', INFO, listboxline_list[1].getColumnProperty('start_date'))
odf_document = foo_printout.index_html(REQUEST=request) odf_document = foo_printout.index_html(REQUEST=request)
#test_output = open("/tmp/test_02_07_Table.odf", "w") #test_output = open("/tmp/test_02_07_Table.odf", "w")
...@@ -493,7 +492,6 @@ class TestFormPrintout(ERP5TypeTestCase): ...@@ -493,7 +492,6 @@ class TestFormPrintout(ERP5TypeTestCase):
content = etree.XML(content_xml) content = etree.XML(content_xml)
table_row_xpath = '//table:table[@table:name="listbox"]/table:table-row' table_row_xpath = '//table:table[@table:name="listbox"]/table:table-row'
odf_table_rows = content.xpath(table_row_xpath, namespaces=content.nsmap) odf_table_rows = content.xpath(table_row_xpath, namespaces=content.nsmap)
LOG('testFormPrintout odf_table_rows', INFO, odf_table_rows)
self.assertEqual(len(odf_table_rows), 3) self.assertEqual(len(odf_table_rows), 3)
# to test ODF table cell number format # to test ODF table cell number format
first_row = odf_table_rows[0] first_row = odf_table_rows[0]
...@@ -503,6 +501,40 @@ class TestFormPrintout(ERP5TypeTestCase): ...@@ -503,6 +501,40 @@ class TestFormPrintout(ERP5TypeTestCase):
self.assertTrue(date_column.attrib.has_key(date_value_attrib)) self.assertTrue(date_column.attrib.has_key(date_value_attrib))
self.assertEqual(date_column.attrib[date_value_attrib], '2009-04-20') self.assertEqual(date_column.attrib[date_value_attrib], '2009-04-20')
def test_02_Table_08_Nodata(self, run=run_all_test):
"""7. Normal case: list box has no data"""
if not run: return
# test target
test1 = self.portal.foo_module.test1
foo_printout = test1.Foo_viewAsPrintout
foo_form = test1.Foo_view
listbox = foo_form.listbox
request = self.app.REQUEST
request['here'] = test1
test1.foo_1.setTitle('foo_title_8')
message = listbox.ListBox_setPropertyList(
field_columns = 'id|ID\ntitle|Title\nquantity|Quantity\nstart_date|Date',)
self.failUnless('Set Successfully' in message)
listboxline_list = listbox.get_value('default', render_format = 'list',
REQUEST = request)
# title line only
self.assertEqual(len(listboxline_list), 1)
odf_document = foo_printout.index_html(REQUEST=request)
#test_output = open("/tmp/test_02_08_Table.odf", "w")
#test_output.write(odf_document)
self.assertTrue(odf_document is not None)
builder = OOoBuilder(odf_document)
content_xml = builder.extract("content.xml")
content = etree.XML(content_xml)
table_row_xpath = '//table:table[@table:name="listbox"]/table:table-row'
odf_table_rows = content.xpath(table_row_xpath, namespaces=content.nsmap)
# no rows
self.assertEqual(len(odf_table_rows), 0)
def _test_03_Frame(self, run=run_all_test): def _test_03_Frame(self, run=run_all_test):
""" """
Frame not supported yet Frame not supported yet
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment