[tryton-debian-vcs] relatorio branch debian updated. debian/0.7.1-1-4-ga40dc33
Mathias Behrle
tryton-debian-vcs at alioth.debian.org
Fri Jan 19 18:08:19 UTC 2018
The following commit has been merged in the debian branch:
https://alioth.debian.org/plugins/scmgit/cgi-bin/gitweb.cgi/?p=tryton/relatorio.git;a=commitdiff;h=debian/0.7.1-1-4-ga40dc33
commit a40dc33eb437bb34f0e21572e956c60c0fcb7073
Author: Mathias Behrle <mathiasb at m9s.biz>
Date: Fri Jan 19 18:42:27 2018 +0100
Releasing debian version 0.8.0-1~exp1.
Signed-off-by: Mathias Behrle <mathiasb at m9s.biz>
diff --git a/debian/changelog b/debian/changelog
index bb2894b..57c7b54 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,11 @@
+relatorio (0.8.0-1~exp1) experimental; urgency=medium
+
+ * Merging upstream version 0.8.0.
+ * Remove 01-add-pypi-magic.patch.
+ * Add python[3]-magic 2:0.4.15-1~exp1 to (Build)-Depends.
+
+ -- Mathias Behrle <mathiasb at m9s.biz> Fri, 19 Jan 2018 18:38:25 +0100
+
relatorio (0.7.1-1) unstable; urgency=medium
* Use the preferred https URL format in the copyright file.
commit 43a62306e008fd571432be0b8be1b5d658808573
Author: Mathias Behrle <mathiasb at m9s.biz>
Date: Fri Jan 19 18:38:09 2018 +0100
Add python[3]-magic 2:0.4.15-1~exp1 to (Build)-Depends.
diff --git a/debian/control b/debian/control
index 2a62a90..70e03c7 100644
--- a/debian/control
+++ b/debian/control
@@ -10,12 +10,14 @@ Build-Depends:
python-genshi,
python-lxml,
python-nose,
+ python-magic (>= 2:0.4.15-1~exp1),
python-pycha,
python-setuptools,
python-yaml,
python3,
python3-genshi,
python3-lxml,
+ python3-magic (>= 2:0.4.15-1~exp1),
python3-nose,
python3-setuptools,
python3-yaml,
@@ -30,6 +32,7 @@ Architecture: all
Depends:
python-genshi,
python-lxml,
+ python-magic (>= 2:0.4.15-1~exp1),
python-pkg-resources,
${misc:Depends},
${python:Depends},
@@ -46,6 +49,7 @@ Architecture: all
Depends:
python3-genshi,
python3-lxml,
+ python3-magic (>= 2:0.4.15-1~exp1),
python3-pkg-resources,
${misc:Depends},
${python3:Depends},
commit 13411dce45525ca5f4be35e24db9c3254f69dae5
Author: Mathias Behrle <mathiasb at m9s.biz>
Date: Fri Jan 19 18:30:44 2018 +0100
Remove 01-add-pypi-magic.patch.
python-magic from pypi with compatibility layer for file-magic has hit
experimental. Thanks to Adam Hupp and Christoph Biedl!
diff --git a/debian/patches/01-add-pypi-magic.patch b/debian/patches/01-add-pypi-magic.patch
deleted file mode 100644
index 804bdb9..0000000
--- a/debian/patches/01-add-pypi-magic.patch
+++ /dev/null
@@ -1,343 +0,0 @@
-Description: Add python-magic code copy from Pypi.
- Adding this code copy because of the (hopefully) ongoing merge
- of python-magic(file) and python-magic(pypi). The first being available
- in Debian as part of the file package, the latter is currently discussed
- to replace the former by adding a compatibility layer.
- This patch is subject to be removed, once python-magic from pypi (or an
- equivalent alternative) is available.
- Relevant discussions:
- https://lists.debian.org/debian-python/2017/09/msg00008.html
- https://lists.debian.org/debian-python/2017/09/msg00015.html
- https://lists.debian.org/debian-python/2017/10/msg00021.html
-Author: Mathias Behrle <mathiasb at m9s.biz>
-Bug-Debian: https://bugs.debian.org/877849
-Forwarded: not-needed
-Last-Update: 2017-11-02
-
---- /dev/null
-+++ b/thirdparty/magic/magic.py
-@@ -0,0 +1,296 @@
-+"""
-+magic is a wrapper around the libmagic file identification library.
-+
-+See README for more information.
-+
-+Usage:
-+
-+>>> import magic
-+>>> magic.from_file("testdata/test.pdf")
-+'PDF document, version 1.2'
-+>>> magic.from_file("testdata/test.pdf", mime=True)
-+'application/pdf'
-+>>> magic.from_buffer(open("testdata/test.pdf").read(1024))
-+'PDF document, version 1.2'
-+>>>
-+
-+
-+"""
-+
-+import sys
-+import glob
-+import os.path
-+import ctypes
-+import ctypes.util
-+import threading
-+
-+from ctypes import c_char_p, c_int, c_size_t, c_void_p
-+
-+
-+class MagicException(Exception):
-+ def __init__(self, message):
-+ super(MagicException, self).__init__(message)
-+ self.message = message
-+
-+
-+class Magic:
-+ """
-+ Magic is a wrapper around the libmagic C library.
-+
-+ """
-+
-+ def __init__(self, mime=False, magic_file=None, mime_encoding=False,
-+ keep_going=False, uncompress=False):
-+ """
-+ Create a new libmagic wrapper.
-+
-+ mime - if True, mimetypes are returned instead of textual descriptions
-+ mime_encoding - if True, codec is returned
-+ magic_file - use a mime database other than the system default
-+ keep_going - don't stop at the first match, keep going
-+ uncompress - Try to look inside compressed files.
-+ """
-+ self.flags = MAGIC_NONE
-+ if mime:
-+ self.flags |= MAGIC_MIME
-+ if mime_encoding:
-+ self.flags |= MAGIC_MIME_ENCODING
-+ if keep_going:
-+ self.flags |= MAGIC_CONTINUE
-+
-+ if uncompress:
-+ self.flags |= MAGIC_COMPRESS
-+
-+ self.cookie = magic_open(self.flags)
-+ self.lock = threading.Lock()
-+
-+ magic_load(self.cookie, magic_file)
-+
-+ def from_buffer(self, buf):
-+ """
-+ Identify the contents of `buf`
-+ """
-+ with self.lock:
-+ try:
-+ return maybe_decode(magic_buffer(self.cookie, buf))
-+ except MagicException as e:
-+ return self._handle509Bug(e)
-+
-+ def from_file(self, filename):
-+ # raise FileNotFoundException or IOError if the file does not exist
-+ with open(filename):
-+ pass
-+ with self.lock:
-+ try:
-+ return maybe_decode(magic_file(self.cookie, filename))
-+ except MagicException as e:
-+ return self._handle509Bug(e)
-+
-+ def _handle509Bug(self, e):
-+ # libmagic 5.09 has a bug where it might fail to identify the
-+ # mimetype of a file and returns null from magic_file (and
-+ # likely _buffer), but also does not return an error message.
-+ if e.message is None and (self.flags & MAGIC_MIME):
-+ return "application/octet-stream"
-+ else:
-+ raise e
-+
-+ def __del__(self):
-+ # no _thread_check here because there can be no other
-+ # references to this object at this point.
-+
-+ # during shutdown magic_close may have been cleared already so
-+ # make sure it exists before using it.
-+
-+ # the self.cookie check should be unnecessary and was an
-+ # incorrect fix for a threading problem, however I'm leaving
-+ # it in because it's harmless and I'm slightly afraid to
-+ # remove it.
-+ if self.cookie and magic_close:
-+ magic_close(self.cookie)
-+ self.cookie = None
-+
-+_instances = {}
-+
-+def _get_magic_type(mime):
-+ i = _instances.get(mime)
-+ if i is None:
-+ i = _instances[mime] = Magic(mime=mime)
-+ return i
-+
-+def from_file(filename, mime=False):
-+ """"
-+ Accepts a filename and returns the detected filetype. Return
-+ value is the mimetype if mime=True, otherwise a human readable
-+ name.
-+
-+ >>> magic.from_file("testdata/test.pdf", mime=True)
-+ 'application/pdf'
-+ """
-+ m = _get_magic_type(mime)
-+ return m.from_file(filename)
-+
-+def from_buffer(buffer, mime=False):
-+ """
-+ Accepts a binary string and returns the detected filetype. Return
-+ value is the mimetype if mime=True, otherwise a human readable
-+ name.
-+
-+ >>> magic.from_buffer(open("testdata/test.pdf").read(1024))
-+ 'PDF document, version 1.2'
-+ """
-+ m = _get_magic_type(mime)
-+ return m.from_buffer(buffer)
-+
-+
-+
-+
-+libmagic = None
-+# Let's try to find magic or magic1
-+dll = ctypes.util.find_library('magic') or ctypes.util.find_library('magic1') or ctypes.util.find_library('cygmagic-1')
-+
-+# This is necessary because find_library returns None if it doesn't find the library
-+if dll:
-+ libmagic = ctypes.CDLL(dll)
-+
-+if not libmagic or not libmagic._name:
-+ windows_dlls = ['magic1.dll','cygmagic-1.dll']
-+ platform_to_lib = {'darwin': ['/opt/local/lib/libmagic.dylib',
-+ '/usr/local/lib/libmagic.dylib'] +
-+ # Assumes there will only be one version installed
-+ glob.glob('/usr/local/Cellar/libmagic/*/lib/libmagic.dylib'),
-+ 'win32': windows_dlls,
-+ 'cygwin': windows_dlls,
-+ 'linux': ['libmagic.so.1'], # fallback for some Linuxes (e.g. Alpine) where library search does not work
-+ }
-+ platform = 'linux' if sys.platform.startswith('linux') else sys.platform
-+ for dll in platform_to_lib.get(platform, []):
-+ try:
-+ libmagic = ctypes.CDLL(dll)
-+ break
-+ except OSError:
-+ pass
-+
-+if not libmagic or not libmagic._name:
-+ # It is better to raise an ImportError since we are importing magic module
-+ raise ImportError('failed to find libmagic. Check your installation')
-+
-+magic_t = ctypes.c_void_p
-+
-+def errorcheck_null(result, func, args):
-+ if result is None:
-+ err = magic_error(args[0])
-+ raise MagicException(err)
-+ else:
-+ return result
-+
-+def errorcheck_negative_one(result, func, args):
-+ if result is -1:
-+ err = magic_error(args[0])
-+ raise MagicException(err)
-+ else:
-+ return result
-+
-+
-+# return str on python3. Don't want to unconditionally
-+# decode because that results in unicode on python2
-+def maybe_decode(s):
-+ if str == bytes:
-+ return s
-+ else:
-+ return s.decode('utf-8')
-+
-+def coerce_filename(filename):
-+ if filename is None:
-+ return None
-+
-+ # ctypes will implicitly convert unicode strings to bytes with
-+ # .encode('ascii'). If you use the filesystem encoding
-+ # then you'll get inconsistent behavior (crashes) depending on the user's
-+ # LANG environment variable
-+ is_unicode = (sys.version_info[0] <= 2 and
-+ isinstance(filename, unicode)) or \
-+ (sys.version_info[0] >= 3 and
-+ isinstance(filename, str))
-+ if is_unicode:
-+ return filename.encode('utf-8')
-+ else:
-+ return filename
-+
-+magic_open = libmagic.magic_open
-+magic_open.restype = magic_t
-+magic_open.argtypes = [c_int]
-+
-+magic_close = libmagic.magic_close
-+magic_close.restype = None
-+magic_close.argtypes = [magic_t]
-+
-+magic_error = libmagic.magic_error
-+magic_error.restype = c_char_p
-+magic_error.argtypes = [magic_t]
-+
-+magic_errno = libmagic.magic_errno
-+magic_errno.restype = c_int
-+magic_errno.argtypes = [magic_t]
-+
-+_magic_file = libmagic.magic_file
-+_magic_file.restype = c_char_p
-+_magic_file.argtypes = [magic_t, c_char_p]
-+_magic_file.errcheck = errorcheck_null
-+
-+def magic_file(cookie, filename):
-+ return _magic_file(cookie, coerce_filename(filename))
-+
-+_magic_buffer = libmagic.magic_buffer
-+_magic_buffer.restype = c_char_p
-+_magic_buffer.argtypes = [magic_t, c_void_p, c_size_t]
-+_magic_buffer.errcheck = errorcheck_null
-+
-+def magic_buffer(cookie, buf):
-+ return _magic_buffer(cookie, buf, len(buf))
-+
-+
-+_magic_load = libmagic.magic_load
-+_magic_load.restype = c_int
-+_magic_load.argtypes = [magic_t, c_char_p]
-+_magic_load.errcheck = errorcheck_negative_one
-+
-+def magic_load(cookie, filename):
-+ return _magic_load(cookie, coerce_filename(filename))
-+
-+magic_setflags = libmagic.magic_setflags
-+magic_setflags.restype = c_int
-+magic_setflags.argtypes = [magic_t, c_int]
-+
-+magic_check = libmagic.magic_check
-+magic_check.restype = c_int
-+magic_check.argtypes = [magic_t, c_char_p]
-+
-+magic_compile = libmagic.magic_compile
-+magic_compile.restype = c_int
-+magic_compile.argtypes = [magic_t, c_char_p]
-+
-+
-+
-+MAGIC_NONE = 0x000000 # No flags
-+MAGIC_DEBUG = 0x000001 # Turn on debugging
-+MAGIC_SYMLINK = 0x000002 # Follow symlinks
-+MAGIC_COMPRESS = 0x000004 # Check inside compressed files
-+MAGIC_DEVICES = 0x000008 # Look at the contents of devices
-+MAGIC_MIME = 0x000010 # Return a mime string
-+MAGIC_MIME_ENCODING = 0x000400 # Return the MIME encoding
-+MAGIC_CONTINUE = 0x000020 # Return all matches
-+MAGIC_CHECK = 0x000040 # Print warnings to stderr
-+MAGIC_PRESERVE_ATIME = 0x000080 # Restore access time on exit
-+MAGIC_RAW = 0x000100 # Don't translate unprintable chars
-+MAGIC_ERROR = 0x000200 # Handle ENOENT etc as real errors
-+
-+MAGIC_NO_CHECK_COMPRESS = 0x001000 # Don't check for compressed files
-+MAGIC_NO_CHECK_TAR = 0x002000 # Don't check for tar files
-+MAGIC_NO_CHECK_SOFT = 0x004000 # Don't check magic entries
-+MAGIC_NO_CHECK_APPTYPE = 0x008000 # Don't check application type
-+MAGIC_NO_CHECK_ELF = 0x010000 # Don't check for elf details
-+MAGIC_NO_CHECK_ASCII = 0x020000 # Don't check for ascii files
-+MAGIC_NO_CHECK_TROFF = 0x040000 # Don't check ascii/troff
-+MAGIC_NO_CHECK_FORTRAN = 0x080000 # Don't check ascii/fortran
-+MAGIC_NO_CHECK_TOKENS = 0x100000 # Don't check ascii/tokens
---- a/MANIFEST.in
-+++ b/MANIFEST.in
-@@ -6,3 +6,4 @@
- include relatorio/tests/*.odt
- include relatorio/tests/*.png
- include relatorio/tests/templates/*.tmpl
-+include thirdparty/magic/*
---- /dev/null
-+++ b/thirdparty/__init__.py
-@@ -0,0 +1 @@
-+# placeholder
-\ No newline at end of file
---- /dev/null
-+++ b/thirdparty/magic/__init__.py
-@@ -0,0 +1 @@
-+# placeholder
-\ No newline at end of file
---- a/relatorio/templates/opendocument.py
-+++ b/relatorio/templates/opendocument.py
-@@ -873,7 +873,7 @@
-
- def extract_images(child, namespaces, start=0):
- "Extract draw:image with binary-data and replace by href"
-- import magic
-+ from thirdparty.magic import magic
- images = []
- for i, image in enumerate(
- child.xpath('//draw:image', namespaces=namespaces), start):
diff --git a/debian/patches/series b/debian/patches/series
deleted file mode 100644
index 3fa1687..0000000
--- a/debian/patches/series
+++ /dev/null
@@ -1 +0,0 @@
-01-add-pypi-magic.patch
commit 7647e5c8cf62cafb8f8fb66409f3aadb77e9aa89
Author: Mathias Behrle <mathiasb at m9s.biz>
Date: Fri Jan 19 18:26:07 2018 +0100
Merging upstream version 0.8.0.
diff --git a/.drone.yml b/.drone.yml
new file mode 100644
index 0000000..494e28b
--- /dev/null
+++ b/.drone.yml
@@ -0,0 +1,27 @@
+clone:
+ hg:
+ image: plugins/hg
+
+pipeline:
+ tox:
+ image: ${IMAGE}
+ commands:
+ - pip install tox
+ - apt-get update
+ - apt-get install -y python-cairo
+ - tox -e "${TOXENV}"
+ volumes:
+ - cache:/root/.cache
+
+matrix:
+ include:
+ - IMAGE: python:2.7
+ TOXENV: py27
+ - IMAGE: python:3.3
+ TOXENV: py33
+ - IMAGE: python:3.4
+ TOXENV: py34
+ - IMAGE: python:3.5
+ TOXENV: py35
+ - IMAGE: python:3.6
+ TOXENV: py36
diff --git a/CHANGES b/CHANGES
index 4b6c809..1cc4da5 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,9 @@
+0.8.0 - 20171204
+* Do not guess_type on styled cell content
+* Remove type attributes when guessing type
+* Add support for Python 3.6
+* Remove soft-page-break
+
0.7.1 - 20171008
* Remove warning when import plugin fails
* Apply the guess type function on the correct node
diff --git a/PKG-INFO b/PKG-INFO
index 2c3578f..88e41da 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: relatorio
-Version: 0.7.1
+Version: 0.8.0
Summary: A templating library able to output odt and pdf files
Home-page: http://relatorio.tryton.org/
Author: Cedric Krier
@@ -21,14 +21,6 @@ Description: Relatorio
The documenation is provided at http://relatorio.readthedocs.org/
- Note on PyCha
- =============
-
- Since the 0.4.0 release, pycha upstream author included most of our patches. So
- it is not necessary anymore to use our friendly fork of the project.
-
- For SVG support, you need PyCha 0.4.2 or later.
-
Platform: UNKNOWN
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
diff --git a/README b/README
index 76e37f3..2be76db 100644
--- a/README
+++ b/README
@@ -12,11 +12,3 @@ Documenation
============
The documenation is provided at http://relatorio.readthedocs.org/
-
-Note on PyCha
-=============
-
-Since the 0.4.0 release, pycha upstream author included most of our patches. So
-it is not necessary anymore to use our friendly fork of the project.
-
-For SVG support, you need PyCha 0.4.2 or later.
diff --git a/doc/conf.py b/doc/conf.py
index 433badb..19ef52e 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -48,9 +48,9 @@ copyright = u'2015, Nicolas Évrard, CGaëtan de Menten, Cédric Krier'
# built documents.
#
# The short X.Y version.
-version = '0.7'
+version = '0.8'
# The full version, including alpha/beta/rc tags.
-release = '0.7.1'
+release = '0.8.0'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
diff --git a/examples/basic.odt b/examples/basic.odt
new file mode 100644
index 0000000..f82485e
Binary files /dev/null and b/examples/basic.odt differ
diff --git a/examples/basic.tex b/examples/basic.tex
new file mode 100644
index 0000000..5316a30
--- /dev/null
+++ b/examples/basic.tex
@@ -0,0 +1,21 @@
+\enableregime [utf-8]
+\mainlanguage [fr]
+
+\starttext
+
+Dear $o.customer.name,
+
+Here is the list of your purchases:
+
+\starttable[|l|l|l|l|l|]
+\HL
+\NC Name \VL Reference \VL Quantity \VL Unit Price \VL Amount \SR
+\HL
+{% for line in o.lines%} \
+\NC $line.item.name \VL $line.item.reference \VL $line.quantity
+\VL $line.item.price \VL $line.amount \SR
+\HL \
+{% end %}
+\stoptable
+
+\stoptext
diff --git a/examples/bouteille.png b/examples/bouteille.png
new file mode 100644
index 0000000..ce076c3
Binary files /dev/null and b/examples/bouteille.png differ
diff --git a/examples/columns.odt b/examples/columns.odt
new file mode 100644
index 0000000..4363581
Binary files /dev/null and b/examples/columns.odt differ
diff --git a/examples/common.py b/examples/common.py
new file mode 100644
index 0000000..1fd898f
--- /dev/null
+++ b/examples/common.py
@@ -0,0 +1,43 @@
+from os.path import join, dirname
+
+
+class Invoice(dict):
+
+ @property
+ def total(self):
+ return sum(l['amount'] for l in self['lines'])
+
+ @property
+ def vat(self):
+ return self.total * 0.21
+
+
+inv = Invoice(customer={'name': 'John Bonham',
+ 'address': {'street': 'Smirnov street',
+ 'zip': 1000,
+ 'city': 'Montreux'}},
+ lines=[{'item': {'name': 'Vodka 70cl',
+ 'reference': 'VDKA-001',
+ 'price': 10.34},
+ 'quantity': 7,
+ 'amount': 7 * 10.34},
+ {'item': {'name': 'Cognac 70cl',
+ 'reference': 'CGNC-067',
+ 'price': 13.46},
+ 'quantity': 12,
+ 'amount': 12 * 13.46},
+ {'item': {'name': 'Sparkling water 25cl',
+ 'reference': 'WATR-007',
+ 'price': 4},
+ 'quantity': 1,
+ 'amount': 4},
+ {'item': {'name': 'Good customer',
+ 'reference': 'BONM-001',
+ 'price': -20},
+ 'quantity': 1,
+ 'amount': -20},
+ ],
+ id='MZY-20080703',
+ status='late',
+ bottle=(open(join(dirname(__file__), 'bouteille.png'), 'rb'),
+ 'image/png'))
diff --git a/examples/complicated.odt b/examples/complicated.odt
new file mode 100644
index 0000000..c36ce6a
Binary files /dev/null and b/examples/complicated.odt differ
diff --git a/examples/demo_chart.py b/examples/demo_chart.py
new file mode 100644
index 0000000..f7fb567
--- /dev/null
+++ b/examples/demo_chart.py
@@ -0,0 +1,23 @@
+from os.path import abspath, join, dirname
+from relatorio import Report
+
+# test data
+from common import inv
+
+if __name__ == '__main__':
+ pie_report = Report(abspath(join(dirname(__file__), 'pie_chart')),
+ 'image/png')
+ open(join(dirname(__file__), 'pie.png'), 'wb').write(
+ pie_report(o=inv).render().getvalue())
+ hbar_report = Report(abspath(join(dirname(__file__), 'hbar_chart')),
+ 'image/svg')
+ open(join(dirname(__file__), 'hbar.svg'), 'wb').write(
+ hbar_report(o=inv).render().getvalue())
+ vbar_report = Report(abspath(join(dirname(__file__), 'vbar_chart')),
+ 'image/svg')
+ open(join(dirname(__file__), 'vbar.svg'), 'wb').write(
+ vbar_report(o=inv).render().getvalue())
+ line_report = Report(abspath(join(dirname(__file__), 'line_chart')),
+ 'image/png')
+ open(join(dirname(__file__), 'line.png'), 'wb').write(
+ line_report(o=inv).render().getvalue())
diff --git a/examples/demo_context.py b/examples/demo_context.py
new file mode 100644
index 0000000..5551532
--- /dev/null
+++ b/examples/demo_context.py
@@ -0,0 +1,14 @@
+from os.path import abspath, join, dirname
+from relatorio import Report
+
+# test data
+from common import inv
+
+# PDF
+if __name__ == '__main__':
+ print "generating output_basic.pdf... ",
+ report = Report(abspath(join(dirname(__file__), 'basic.tex')),
+ 'application/pdf')
+ content = report(o=inv).render().getvalue()
+ open(join(dirname(__file__), 'output_basic.pdf'), 'wb').write(content)
+ print "done"
diff --git a/examples/demo_odf.py b/examples/demo_odf.py
new file mode 100644
index 0000000..66c4286
--- /dev/null
+++ b/examples/demo_odf.py
@@ -0,0 +1,68 @@
+from os.path import abspath, join, dirname
+from relatorio import Report
+from relatorio.templates import opendocument
+
+# test data
+from common import inv
+
+ODT_MIME = 'application/vnd.oasis.opendocument.text'
+ODS_MIME = 'application/vnd.oasis.opendocument.spreadsheet'
+ODP_MIME = 'application/vnd.oasis.opendocument.presentation'
+
+if __name__ == '__main__':
+ pwd = dirname(__file__)
+ # ODT
+ print "generating output_basic.odt... ",
+ report = Report(abspath(join(dirname(__file__), 'basic.odt')), ODT_MIME)
+ content = report(o=inv).render().getvalue()
+ open(join(pwd, 'output_basic.odt'), 'wb').write(content)
+ print "done"
+
+ # we could also use an opendocument template directly
+ print "generating output_template_basic.odt... ",
+ template = opendocument.Template(source='',
+ filepath=abspath(join(pwd, 'basic.odt')))
+ content = template.generate(o=inv).render().getvalue()
+ open(join(pwd, 'output_template_basic.odt'), 'wb').write(content)
+ print "done"
+
+ print "generating output_complicated.odt... ",
+ # Add a chart to the invoice
+ inv['chart'] = (
+ Report(abspath(join(pwd, 'pie_chart')), 'image/png'), 'image/png')
+ report = Report(abspath(join(pwd, 'complicated.odt')), ODT_MIME)
+ try:
+ content = report(o=inv).render().getvalue()
+ except NotImplementedError:
+ print "skipped"
+ else:
+ open(join(pwd, 'output_complicated.odt'), 'wb').write(content)
+ print "done"
+
+ print "generating output_columns.odt... ",
+ report = Report(abspath(join(pwd, 'columns.odt')), ODT_MIME)
+ lst = [[], ['i'], ['a', 'b'], [1, 2, 3], ['I', 'II', 'III', 'IV']]
+ titles = ['first', 'second', 'third', 'fourth']
+ content = report(titles=titles, lst=lst).render().getvalue()
+ open(join(pwd, 'output_columns.odt'), 'wb').write(content)
+ print "done"
+
+ # ODS
+ print "generating output_pivot.ods... ",
+ report = Report(abspath(join(pwd, 'pivot.ods')), ODS_MIME)
+ content = report(o=inv).render().getvalue()
+ open(join(pwd, 'output_pivot.ods'), 'wb').write(content)
+ print "done"
+
+ print "generating output_sheets.ods... ",
+ report = Report(abspath(join(pwd, 'demo_sheets.ods')), ODS_MIME)
+ content = report(lst=lst).render().getvalue()
+ open(join(pwd, 'output_sheets.ods'), 'wb').write(content)
+ print "done"
+
+ # ODP
+ print "generating output_presentation.odp... ",
+ report = Report(abspath(join(pwd, 'presentation.odp')), ODP_MIME)
+ content = report(o=inv).render().getvalue()
+ open(join(pwd, 'output_presentation.odp'), 'wb').write(content)
+ print "done"
diff --git a/examples/demo_repository.py b/examples/demo_repository.py
new file mode 100644
index 0000000..cb1e35a
--- /dev/null
+++ b/examples/demo_repository.py
@@ -0,0 +1,32 @@
+import relatorio
+from common import Invoice, inv
+from os.path import join, dirname
+
+ODT_MIME = 'application/vnd.oasis.opendocument.text'
+ODS_MIME = 'application/vnd.oasis.opendocument.spreadsheet'
+ODP_MIME = 'application/vnd.oasis.opendocument.presentation'
+
+repository = relatorio.ReportRepository()
+repository.add_report(Invoice, ODT_MIME, 'basic.odt', report_name='basic')
+repository.add_report(Invoice, ODT_MIME,
+ 'complicated.odt', report_name='complicated')
+repository.add_report(Invoice, ODS_MIME, 'pivot.ods', report_name='pivot')
+repository.add_report(Invoice, ODP_MIME,
+ 'presentation.odp', report_name='presentation')
+repository.add_report(Invoice, 'image/png', 'pie_chart', report_name='pie')
+
+if __name__ == '__main__':
+ # Add a chart to the invoice
+ inv['chart'] = repository.by_id(Invoice, 'pie')[:2]
+
+ # Generate all reports on the invoice class
+ for report_name, ext in (('basic', '.odt'),
+ ('complicated', '.odt'),
+ ('pivot', '.ods'),
+ ('presentation', '.odp')):
+ filename = 'output_%s%s' % (report_name, ext)
+ print "generating '%s'..." % filename,
+ report, mimetype, desc = repository.by_id(Invoice, report_name)
+ data = report(o=inv).render().getvalue()
+ open(join(dirname(__file__), filename), 'wb').write(data)
+ print "done"
diff --git a/examples/demo_sheets.ods b/examples/demo_sheets.ods
new file mode 100644
index 0000000..5797de4
Binary files /dev/null and b/examples/demo_sheets.ods differ
diff --git a/examples/hbar_chart b/examples/hbar_chart
new file mode 100644
index 0000000..0f6607b
--- /dev/null
+++ b/examples/hbar_chart
@@ -0,0 +1,32 @@
+options:
+ width: 600
+ height: 500
+ legend:
+ hide: false
+ position:
+ right: 40
+ padding: {bottom: 70, left: 70, right: 10, top: 10}
+ axis:
+ y:
+ interval: 15
+ padding: 5
+ x:
+ ticks:
+ {% for idx, line in enumerate(o.lines) %}
+ - v: $idx
+ label: $line.item.name
+ {% end %}
+chart:
+ type: hbar
+ output_type: svg
+ dataset:
+ - - Sales
+ -
+ {% for idx, line in enumerate(o.lines) %}
+ - [$idx, $line.amount]
+ {% end %}
+ - - Absolute sales
+ -
+ {% for idx, line in enumerate(o.lines) %}
+ - [$idx, ${abs(line.amount)}]
+ {% end %}
diff --git a/examples/line_chart b/examples/line_chart
new file mode 100644
index 0000000..1457845
--- /dev/null
+++ b/examples/line_chart
@@ -0,0 +1,27 @@
+options:
+ width: 600
+ height: 500
+ legend:
+ hide: false
+ position:
+ right: 40
+ padding: {bottom: 70, left: 70, right: 10, top: 10}
+ axis:
+ y:
+ interval: 10
+ padding: 10
+ x:
+ ticks:
+ {% for idx, line in enumerate(o.lines) %}
+ - v: $idx
+ label: $line.item.name
+ {% end %}
+chart:
+ type: line
+ output_type: png
+ dataset:
+ - - Sales
+ -
+ {% for idx, line in enumerate(o.lines) %}
+ - [$idx, $line.amount]
+ {% end %}
diff --git a/examples/pie_chart b/examples/pie_chart
new file mode 100644
index 0000000..4b24054
--- /dev/null
+++ b/examples/pie_chart
@@ -0,0 +1,15 @@
+options:
+ width: 600
+ height: 400
+ background: {hide: true}
+ legend: {hide: true}
+ axis: {labelFontSize: 14}
+ padding: {bottom: 10, left: 10, right: 10, top: 10}
+chart:
+ type: pie
+ output_type: png
+ dataset:
+ {% for line in o.lines %}
+ - - ${line.item.name}
+ - - [0, $line.amount]
+ {% end %}
diff --git a/examples/pivot.ods b/examples/pivot.ods
new file mode 100644
index 0000000..0d0b40f
Binary files /dev/null and b/examples/pivot.ods differ
diff --git a/examples/presentation.odp b/examples/presentation.odp
new file mode 100644
index 0000000..7d08b92
Binary files /dev/null and b/examples/presentation.odp differ
diff --git a/examples/vbar_chart b/examples/vbar_chart
new file mode 100644
index 0000000..56c6790
--- /dev/null
+++ b/examples/vbar_chart
@@ -0,0 +1,32 @@
+options:
+ width: 600
+ height: 500
+ legend:
+ hide: false
+ position:
+ right: 40
+ padding: {bottom: 70, left: 70, right: 10, top: 10}
+ axis:
+ y:
+ interval: 20
+ padding: 5
+ x:
+ ticks:
+ {% for idx, line in enumerate(o.lines) %}
+ - v: $idx
+ label: $line.item.name
+ {% end %}
+chart:
+ type: vbar
+ output_type: svg
+ dataset:
+ - - Sales
+ -
+ {% for idx, line in enumerate(o.lines) %}
+ - [$idx, $line.amount]
+ {% end %}
+ - - Absolute sales
+ -
+ {% for idx, line in enumerate(o.lines) %}
+ - [$idx, ${abs(line.amount)}]
+ {% end %}
diff --git a/relatorio.egg-info/PKG-INFO b/relatorio.egg-info/PKG-INFO
index 2c3578f..88e41da 100644
--- a/relatorio.egg-info/PKG-INFO
+++ b/relatorio.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: relatorio
-Version: 0.7.1
+Version: 0.8.0
Summary: A templating library able to output odt and pdf files
Home-page: http://relatorio.tryton.org/
Author: Cedric Krier
@@ -21,14 +21,6 @@ Description: Relatorio
The documenation is provided at http://relatorio.readthedocs.org/
- Note on PyCha
- =============
-
- Since the 0.4.0 release, pycha upstream author included most of our patches. So
- it is not necessary anymore to use our friendly fork of the project.
-
- For SVG support, you need PyCha 0.4.2 or later.
-
Platform: UNKNOWN
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
diff --git a/relatorio.egg-info/SOURCES.txt b/relatorio.egg-info/SOURCES.txt
index 5d118bc..07d1fee 100644
--- a/relatorio.egg-info/SOURCES.txt
+++ b/relatorio.egg-info/SOURCES.txt
@@ -1,3 +1,6 @@
+.drone.yml
+.hgignore
+.hgtags
AUTHORS
CHANGES
LICENSE
@@ -5,6 +8,7 @@ MANIFEST.in
README
setup.cfg
setup.py
+tox.ini
doc/Makefile
doc/basic.png
doc/basic_generated.png
@@ -22,6 +26,23 @@ doc/pivot.png
doc/pivot_rendered.png
doc/quickexample.rst
doc/relatorio_basic.png
+examples/basic.odt
+examples/basic.tex
+examples/bouteille.png
+examples/columns.odt
+examples/common.py
+examples/complicated.odt
+examples/demo_chart.py
+examples/demo_context.py
+examples/demo_odf.py
+examples/demo_repository.py
+examples/demo_sheets.ods
+examples/hbar_chart
+examples/line_chart
+examples/pie_chart
+examples/pivot.ods
+examples/presentation.odp
+examples/vbar_chart
relatorio/__init__.py
relatorio/reporting.py
relatorio.egg-info/PKG-INFO
diff --git a/relatorio/__init__.py b/relatorio/__init__.py
index f2bb8da..4a9e345 100644
--- a/relatorio/__init__.py
+++ b/relatorio/__init__.py
@@ -12,5 +12,5 @@ and report together, find reports by mimetypes/name/python objects.
from .reporting import MIMETemplateLoader, ReportRepository, Report
from . import templates
-__version__ = '0.7.1'
+__version__ = '0.8.0'
__all__ = ['MIMETemplateLoader', 'ReportRepository', 'Report', 'templates']
diff --git a/relatorio/templates/opendocument.py b/relatorio/templates/opendocument.py
index b72a075..c25284a 100644
--- a/relatorio/templates/opendocument.py
+++ b/relatorio/templates/opendocument.py
@@ -211,6 +211,26 @@ def wrap_nodes_between(first, last, new_parent):
old_parent.remove(last)
+def remove_node_keeping_tail(node):
+ """Remove the node from the tree but keeping tail by appending to the
+ previous or parent node.
+ """
+ parent = node.getparent()
+ if node.tail:
+ previous = node.getprevious()
+ if previous is not None:
+ if not previous.tail:
+ previous.tail = node.tail
+ else:
+ previous.tail += node.tail
+ else:
+ if not parent.text:
+ parent.text = node.tail
+ else:
+ parent.text += node.tail
+ parent.remove(node)
+
+
def update_py_attrs(node, value):
"""An helper function to update py_attrs of a node.
"""
@@ -311,6 +331,7 @@ class Template(MarkupTemplate):
self.namespaces['py'] = GENSHI_URI
self.namespaces['relatorio'] = RELATORIO_URI
+ self._remove_soft_page_break(tree)
self._invert_style(tree)
self._handle_relatorio_tags(tree)
self._handle_images(tree)
@@ -318,6 +339,17 @@ class Template(MarkupTemplate):
self._escape_values(tree)
return BytesIO(lxml.etree.tostring(tree))
+ def _remove_soft_page_break(self, tree):
+ "remove soft-page-break tag and use-soft-page-break attribute"
+ xpath_expr = "//text:soft-page-break"
+ for node in tree.xpath(xpath_expr, namespaces=self.namespaces):
+ remove_node_keeping_tail(node)
+
+ xpath_expr = "//office:text[@text:use-soft-page-breaks]"
+ text_namespace = self.namespaces['text']
+ for node in tree.xpath(xpath_expr, namespaces=self.namespaces):
+ node.attrib.pop('{%s}use-soft-page-breaks' % text_namespace)
+
def _invert_style(self, tree):
"inverts the text:a and text:span"
xpath_expr = "//text:a[starts-with(@xlink:href, 'relatorio://')]" \
@@ -396,6 +428,16 @@ class Template(MarkupTemplate):
table_namespace = self.namespaces['table']
table_row_tag = '{%s}table-row' % table_namespace
table_cell_tag = '{%s}table-cell' % table_namespace
+ text_namespace = self.namespaces['text']
+ text_style_attributes = [s % text_namespace for s in [
+ '{%s}class-names', '{%s}cond-style-name', '{%s}style-name']]
+
+ office_value = '{%s}value' % self.namespaces['office']
+ office_valuetype = '{%s}value-type' % self.namespaces['office']
+ if 'calcext' in self.namespaces:
+ calcext_valuetype = '{%s}value-type' % self.namespaces['calcext']
+ else:
+ calcext_valuetype = None
py_replace = '{%s}replace' % GENSHI_URI
@@ -473,13 +515,17 @@ class Template(MarkupTemplate):
# remove the directive node
r_node.getparent().remove(r_node)
else:
+ def has_style(node):
+ return any(attr in node.attrib
+ for attr in text_style_attributes)
# It's not a genshi statement it's a python expression
parent = r_node.getparent()
grand_parent = parent.getparent()
# Guess type only if it is the only value in the cell
+ # and its parent has no style
if (grand_parent is None
or grand_parent.tag != table_cell_tag
- ) or len(parent) != 1:
+ ) or len(grand_parent) != 1 or has_style(parent):
r_node.attrib[py_replace] = expr
continue
@@ -491,6 +537,11 @@ class Template(MarkupTemplate):
dico = ('__relatorio_guess_type('
'__relatorio_store_cache(%s, %s))')
update_py_attrs(grand_parent, dico % (cache_id, expr))
+ for attr in [office_value,
+ office_valuetype,
+ calcext_valuetype]:
+ if attr:
+ grand_parent.attrib.pop(attr, None)
def _handle_column_loops(self, statement, ancestor, opening,
outer_o_node, outer_c_node):
diff --git a/relatorio/tests/test_odt.py b/relatorio/tests/test_odt.py
index 2b22087..ca5f5b4 100644
--- a/relatorio/tests/test_odt.py
+++ b/relatorio/tests/test_odt.py
@@ -23,7 +23,7 @@
import os
import unittest
-from io import StringIO
+from io import StringIO, BytesIO
import lxml.etree
from genshi.filters import Translator
@@ -31,7 +31,7 @@ from genshi.core import PI
from genshi.template.eval import UndefinedError
from relatorio.templates.opendocument import Template, GENSHI_EXPR,\
- GENSHI_URI, RELATORIO_URI, fod2od
+ GENSHI_URI, RELATORIO_URI, fod2od, remove_node_keeping_tail
OO_TABLE_NS = "urn:oasis:names:tc:opendocument:xmlns:table:1.0"
@@ -339,3 +339,76 @@ class TestOOTemplating(unittest.TestCase):
with open(filepath, mode='rb') as source:
oot = Template(source)
oot.generate(**self.data)
+
+
+class TestRemoveNodeKeepingTail(unittest.TestCase):
+
+ def test_without_tail(self):
+ "Testing remove_node_keeping_tail without tail"
+ xml = b'''<parent><target/></parent>'''
+ tree = lxml.etree.parse(BytesIO(xml))
+ target = tree.getroot()[0]
+
+ remove_node_keeping_tail(target)
+
+ self.assertEqual(lxml.etree.tostring(tree), b'''<parent/>''')
+
+ def test_with_tail(self):
+ "Testing remove_node_keeping_tail with tail"
+ xml = b'''<parent><target/>tail</parent>'''
+ tree = lxml.etree.parse(BytesIO(xml))
+ target = tree.getroot()[0]
+
+ remove_node_keeping_tail(target)
+
+ self.assertEqual(
+ lxml.etree.tostring(tree),
+ b'''<parent>tail</parent>''')
+
+ def test_with_tail_and_parent_text(self):
+ "Testing remove_node_keeping_tail with tail and parent text"
+ xml = b'''<parent>text<target/>tail</parent>'''
+ tree = lxml.etree.parse(BytesIO(xml))
+ target = tree.getroot()[0]
+
+ remove_node_keeping_tail(target)
+
+ self.assertEqual(
+ lxml.etree.tostring(tree),
+ b'''<parent>texttail</parent>''')
+
+ def test_without_tail_and_with_previous(self):
+ "Testing remove_node_keeping_tail without tail and with previous"
+ xml = b'''<parent><previous/><target/></parent>'''
+ tree = lxml.etree.parse(BytesIO(xml))
+ target = tree.getroot()[1]
+
+ remove_node_keeping_tail(target)
+
+ self.assertEqual(
+ lxml.etree.tostring(tree),
+ b'''<parent><previous/></parent>''')
+
+ def test_with_tail_and_previous(self):
+ "Testing remove_node_keeping_tail with tail and previous"
+ xml = b'''<parent><previous/><target/>tail</parent>'''
+ tree = lxml.etree.parse(BytesIO(xml))
+ target = tree.getroot()[1]
+
+ remove_node_keeping_tail(target)
+
+ self.assertEqual(
+ lxml.etree.tostring(tree),
+ b'''<parent><previous/>tail</parent>''')
+
+ def test_with_tail_and_previous_tail(self):
+ "Testing remove_node_keeping_tail with tail and previous tail"
+ xml = b'''<parent><previous/>tail<target/>tail</parent>'''
+ tree = lxml.etree.parse(BytesIO(xml))
+ target = tree.getroot()[1]
+
+ remove_node_keeping_tail(target)
+
+ self.assertEqual(
+ lxml.etree.tostring(tree),
+ b'''<parent><previous/>tailtail</parent>''')
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 0000000..792676f
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,13 @@
+# Tox (http://tox.testrun.org/) is a tool for running tests
+# in multiple virtualenvs. This configuration file will run the
+# test suite on all supported python versions. To use it, "pip install tox"
+# and then run "tox" from this directory.
+
+[tox]
+envlist = py26, py27, py32, py33, py34, py35, py36, pypy
+
+[testenv]
+commands = {envpython} setup.py test
+deps =
+ pyyaml
+ pycha
--
relatorio
More information about the tryton-debian-vcs
mailing list