Projet

Général

Profil

0001-correctly-export-numbers-to-ODS-fixes-28058.patch

Benjamin Dauvergne, 14 janvier 2019 12:05

Télécharger (5,63 ko)

Voir les différences:

Subject: [PATCH] correctly export numbers to ODS (fixes #28058)

 bijoe/visualization/ods.py   | 22 +++++++++++++++++---
 bijoe/visualization/utils.py |  2 +-
 tests/test_schema1.py        | 22 +++++++++++++++++++-
 tests/utils.py               | 39 ++++++++++++++++++++++++++++++++++++
 4 files changed, 80 insertions(+), 5 deletions(-)
bijoe/visualization/ods.py
16 16
# You should have received a copy of the GNU Affero General Public License
17 17
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 18

  
19
import sys
20

  
19 21
import zipfile
20 22
import xml.etree.ElementTree as ET
21 23

  
24
from django.utils.encoding import force_text
25

  
26

  
22 27
OFFICE_NS = 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'
23 28
TABLE_NS = 'urn:oasis:names:tc:opendocument:xmlns:table:1.0'
24 29
TEXT_NS = 'urn:oasis:names:tc:opendocument:xmlns:text:1.0'
25 30
XLINK_NS = 'http://www.w3.org/1999/xlink'
26 31

  
27 32

  
33
def is_number(x):
34
    if sys.version_info >= (3, 0):
35
        return isinstance(x, (int, float))
36
    else:
37
        return isinstance(x, (int, long, float))
38

  
39

  
28 40
class Workbook(object):
29 41
    def __init__(self, encoding='utf-8'):
30 42
        self.sheets = []
......
94 106

  
95 107
class WorkCell(object):
96 108
    def __init__(self, worksheet, value, hint=None):
109
        self.value_type = 'string'
110
        if is_number(value):
111
            self.value_type = 'float'
97 112
        if value is None:
98 113
            value = ''
99
        if type(value) is not unicode:
100
            value = unicode(value, 'utf-8')
114
        value = force_text(value)
101 115
        self.value = value
102 116
        self.worksheet = worksheet
103 117
        self.hint = hint
104 118

  
105 119
    def get_node(self):
106 120
        root = ET.Element('{%s}table-cell' % TABLE_NS)
107
        root.attrib['{%s}value-type' % OFFICE_NS] = 'string'
121
        root.attrib['{%s}value-type' % OFFICE_NS] = self.value_type
122
        if self.value_type == 'float':
123
            root.attrib['{%s}value' % OFFICE_NS] = self.value
108 124
        p = ET.SubElement(root, '{%s}p' % TEXT_NS)
109 125
        if self.hint == 'uri':
110 126
            base_filename = self.value.split('/')[-1]
bijoe/visualization/utils.py
253 253
            sheet.write(0, 0, full_title)
254 254
            for j, row in enumerate(table.table()):
255 255
                for i, value in enumerate(row):
256
                    sheet.write(j + 1, i, unicode(0 if value is None else value))
256
                    sheet.write(j + 1, i, 0 if value is None else value)
257 257
        return workbook
258 258

  
259 259
    def title(self):
tests/test_schema1.py
1 1
# -*- coding: utf-8 -*-
2 2

  
3
from utils import login, get_table
3
from utils import login, get_table, get_ods_table, get_ods_document
4

  
5
from bijoe.visualization.ods import OFFICE_NS, TABLE_NS
4 6

  
5 7

  
6 8
def test_simple(schema1, app, admin):
......
28 30
        ['mois (Date)', 'janvier', u'f\xe9vrier', 'mars', 'avril', 'mai', 'juin', 'juillet', u'ao\xfbt'],
29 31
        ['number of rows', '10', '1', '1', '1', '1', '1', '1', '1'],
30 32
    ]
33

  
34

  
35
def test_ods(schema1, app, admin):
36
    login(app, admin)
37
    response = app.get('/').follow()
38
    response = response.click('Facts 1')
39
    form = response.form
40
    form.set('representation', 'table')
41
    form.set('measure', 'simple_count')
42
    form.set('drilldown_x', 'innersubcategory')
43
    response = form.submit('visualize')
44
    assert 'big-msg-info' not in response
45
    ods_response = response.form.submit('ods')
46
    # skip first line of ODS table as it's a header not present in the HTML display
47
    assert get_table(response) == get_ods_table(ods_response)[1:]
48
    root = get_ods_document(ods_response)
49
    nodes = root.findall('.//{%s}table-cell' % TABLE_NS)
50
    assert len([node for node in nodes if node.attrib['{%s}value-type' % OFFICE_NS] == 'float']) == 2
tests/utils.py
1
import io
2
import zipfile
3
import xml.etree.ElementTree as ET
4

  
1 5
from django.conf import settings
2 6

  
3 7

  
......
29 33
            row.append(td.text.strip())
30 34
    return table
31 35

  
36

  
37
def xml_node_text_content(node):
38
    '''Extract text content from node and all its children. Equivalent to
39
       xmlNodeGetContent from libxml.'''
40

  
41
    if node is None:
42
        return ''
43

  
44
    def helper(node):
45
        s = []
46
        if node.text:
47
            s.append(node.text)
48
        for child in node:
49
            s.extend(helper(child))
50
            if child.tail:
51
                s.append(child.tail)
52
        return s
53
    return u''.join(helper(node))
54

  
55

  
56
def get_ods_document(response):
57
    return ET.fromstring(zipfile.ZipFile(io.BytesIO(response.content)).read('content.xml'))
58

  
59

  
60
def get_ods_table(response):
61
    from bijoe.visualization.ods import TABLE_NS
62

  
63
    root = get_ods_document(response)
64
    table = []
65
    for row_node in root.findall('.//{%s}table-row' % TABLE_NS):
66
        row = []
67
        table.append(row)
68
        for cell_node in row_node.findall('.//{%s}table-cell' % TABLE_NS):
69
            row.append(xml_node_text_content(cell_node))
70
    return table
32
-