Codebase list pyexcel-ods / 5fee29e
New upstream version 0.5.6 Sophie Brun 4 years ago
48 changed file(s) with 3034 addition(s) and 1131 deletion(s). Raw diff Collapse all Expand all
+0
-6
.gitignore less more
0 *.pyc
1 *~
2 .coverage
3 pyexcel*-info
4 build
5 dist
+0
-16
.moban.d/README.rst less more
0 {%extends 'README.rst.jj2' %}
1
2 {%block description%}
3 **pyexcel-ods** is a tiny wrapper library to read, manipulate and write data in
4 ods fromat using python 2.6 and python 2.7. You are likely to use it with
5 `pyexcel <https://github.com/pyexcel/pyexcel>`_.
6 `pyexcel-ods3 <https://github.com/pyexcel/pyexcel-ods3>`_ is a sister library that
7 does the same thing but supports Python 3.3 and 3.4 and depends on lxml.
8 {%endblock%}
9
10 {%block extras %}
11 Credits
12 ================================================================================
13
14 ODSReader is originally written by `Marco Conti <https://github.com/marcoconti83/read-ods-with-odfpy>`_
15 {%endblock%}
+0
-3
.moban.d/requirements.txt less more
0 {% for dependency in dependencies: %}
1 {{dependency}}
2 {% endfor %}
+0
-7
.moban.d/setup.py less more
0 {% extends 'setup.py.jj2' %}
1
2 {%block additional_classifiers%}
3 'Programming Language :: Python :: 2.6',
4 'Programming Language :: Python :: 2.7'
5 {%endblock%}}
6
+0
-5
.moban.d/tests/requirements.txt less more
0 {% extends 'tests/requirements.txt.jj2' %}
1 {%block extras %}
2 pyexcel
3 pyexcel-xls
4 {%endblock%}
+0
-5
.moban.d/travis.yml less more
0 {% extends "travis.yml.jj2" %}
1
2 {%block test_other_python_versions%} - 2.6
3 {%endblock%}
4
+0
-17
.travis.yml less more
0 sudo: false
1 language: python
2 notifications:
3 email: false
4 env:
5 global:
6 python:
7 - 2.7
8 - 2.6
9 install:
10 - rm applymoban.py
11 - pip install -r requirements.txt
12 - pip install -r tests/requirements.txt
13 script:
14 make test
15 after_success:
16 codecov
0 Change log
1 ================================================================================
2
3 0.5.6 - 19.03.2019
4 --------------------------------------------------------------------------------
5
6 added
7 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
8
9 #. `#32 <https://github.com/pyexcel/pyexcel-ods/issues/32>`_, fix odfpy pinning
10 #. `#33 <https://github.com/pyexcel/pyexcel-ods/issues/33>`_, fix
11 IntegerAccuracyLossError on i586
12
13 0.5.5 - 16.03.2019
14 --------------------------------------------------------------------------------
15
16 added
17 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
18
19 #. `#32 <https://github.com/pyexcel/pyexcel-ods/issues/32>`_, fix odfpy pinning
20
21 0.5.4 - 27.11.2018
22 --------------------------------------------------------------------------------
23
24 added
25 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
26
27 #. `#30 <https://github.com/pyexcel/pyexcel-ods/issues/30>`_, long type will not
28 be written in ods. please use string type. And if the integer is equal or
29 greater than 10 to the power of 16, it will not be written either in ods. In
30 both situation, IntegerPrecisionLossError will be raised.
31
32 0.5.3 - unreleased
33 --------------------------------------------------------------------------------
34
35 added
36 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
37
38 #. `#24 <https://github.com/pyexcel/pyexcel-ods/issues/24>`_, ignore
39 comments(<office:comment>) in cell
40 #. `#27 <https://github.com/pyexcel/pyexcel-ods/issues/27>`_, exception raised
41 when currency type is missing
42 #. fix odfpy version on 1.3.5.
43
44 0.5.2 - 23.10.2017
45 --------------------------------------------------------------------------------
46
47 updated
48 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
49
50 #. pyexcel `pyexcel#105 <https://github.com/pyexcel/pyexcel/issues/105>`_,
51 remove gease from setup_requires, introduced by 0.5.1.
52 #. remove python2.6 test support
53
54 0.5.1 - 20.10.2017
55 --------------------------------------------------------------------------------
56
57 added
58 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
59
60 #. `pyexcel#103 <https://github.com/pyexcel/pyexcel/issues/103>`_, include
61 LICENSE file in MANIFEST.in, meaning LICENSE file will appear in the released
62 tar ball.
63
64 0.5.0 - 30.08.2017
65 --------------------------------------------------------------------------------
66
67 Updated
68 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
69
70 #. put dependency on pyexcel-io 0.5.0, which uses cStringIO instead of StringIO.
71 Hence, there will be performance boost in handling files in memory.
72
73 Relocated
74 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
75
76 #. All ods type conversion code lives in pyexcel_io.service module
77
78 0.4.1 - 25.08.2017
79 --------------------------------------------------------------------------------
80
81 Updated
82 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
83
84 #. `pyexcel#23 <https://github.com/pyexcel/pyexcel/issues/23>`_, handle
85 unseekable stream given by http response
86 #. PR `#22 <https://github.com/pyexcel/pyexcel-ods/pull/22>`_, hanle white
87 spaces in a cell.
88
89 0.4.0 - 19.06.2017
90 --------------------------------------------------------------------------------
91
92 Updated
93 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
94
95 #. `pyexcel#14 <https://github.com/pyexcel/pyexcel/issues/14>`_, close file
96 handle
97 #. pyexcel-io plugin interface now updated to use `lml
98 <https://github.com/chfw/lml>`_.
99
100 0.3.3 - 07.05.2017
101 --------------------------------------------------------------------------------
102
103 Updated
104 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
105
106 #. issue `pyexcel#19 <https://github.com/pyexcel/pyexcel/issues/19>`_, not all
107 texts in a multi-node cell were extracted.
108
109 0.3.2 - 13.04.2017
110 --------------------------------------------------------------------------------
111
112 Updated
113 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
114
115 #. issue `pyexcel#17 <https://github.com/pyexcel/pyexcel/issues/17>`_, empty new
116 line is ignored
117 #. issue `pyexcel#6 <https://github.com/pyexcel/pyexcel/issues/6>`_,
118 PT288H00M00S is valid duration
119
120 0.3.1 - 02.02.2017
121 --------------------------------------------------------------------------------
122
123 Added
124 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
125
126 #. Recognize currency type
127
128 0.3.0 - 22.12.2016
129 --------------------------------------------------------------------------------
130
131 Updated
132 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
133
134 #. Code refactoring with pyexcel-io v 0.3.0
135
136 0.2.2 - 24.10.2016
137 --------------------------------------------------------------------------------
138
139 Updated
140 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
141
142 #. issue `pyexcel#14 <https://github.com/pyexcel/pyexcel/issues/14>`_, index
143 error when reading a ods file that has non-uniform columns repeated property.
144
145 0.2.1 - 31.08.2016
146 --------------------------------------------------------------------------------
147
148 Added
149 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
150
151 #. support pagination. two pairs: start_row, row_limit and start_column,
152 column_limit help you deal with large files.
153 #. use odfpy 1.3.3 as compulsory package. offically support python 3
154
155 0.2.0 - 01.06.2016
156 --------------------------------------------------------------------------------
157
158 Added
159 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
160
161 #. By default, `float` will be converted to `int` where fits. `auto_detect_int`,
162 a flag to switch off the autoatic conversion from `float` to `int`.
163 #. 'library=pyexcel-ods' was added so as to inform pyexcel to use it instead of
164 other libraries, in the situation where multiple plugins were installed.
165
166 Updated
167 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
168
169 #. support the auto-import feature of pyexcel-io 0.2.0
170
171 0.1.1 - 30.01.2016
172 --------------------------------------------------------------------------------
173
174 Added
175 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
176
177 #. 'streaming' is an extra option given to get_data. Only when 'streaming' is
178 explicitly set to True, the data will be consisted of generators, hence will
179 break your existing code.
180 #. uses yield in to_array and returns a generator
181 #. support multi-line text cell #5
182 #. feature migration from pyexcel-ods3 pyexcel/pyexcel-ods3#5
183
184 Updated
185 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
186
187 #. compatibility with pyexcel-io 0.1.1
188
189 0.0.12 - 10.10.2015
190 --------------------------------------------------------------------------------
191
192 Updated
193 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
194
195 #. Bug fix: excessive trailing columns with empty values
196
197 0.0.11 - 26.09.2015
198 --------------------------------------------------------------------------------
199
200 Updated
201 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
202
203 #. Complete fix for libreoffice datetime field
204
205 0.0.10 - 15.09.2015
206 --------------------------------------------------------------------------------
207
208 Updated
209 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
210
211 #. Bug fix: date field could have datetime from libreoffice
212
213 0.0.9 - 21.08.2015
214 --------------------------------------------------------------------------------
215
216 Updated
217 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
218
219 #. Bug fix: utf-8 string throw unicode exceptions
220
221 0.0.8 - 28.06.2015
222 --------------------------------------------------------------------------------
223
224 Updated
225 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
226
227 #. Pin dependency odfpy 0.9.6 to avoid buggy odfpy 1.3.0
228
229 0.0.7 - 28.05.2015
230 --------------------------------------------------------------------------------
231
232 Updated
233 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
234
235 #. Bug fix: "number-columns-repeated" is now respected
236
237 0.0.6 - 21.05.2015
238 --------------------------------------------------------------------------------
239
240 Updated
241 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
242
243 #. get_data and save_data are seen across pyexcel-* extensions. remember them
244 once and use them across all extensions.
245
246 0.0.5 - 22.02.2015
247 --------------------------------------------------------------------------------
248
249 Added
250 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
251
252 #. Loads only one sheet from a multiple sheet book
253 #. Use New BSD License
254
255 0.0.4 - 14.12.2014
256 --------------------------------------------------------------------------------
257
258 Updated
259 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
260
261 #. IO interface update as pyexcel-io introduced keywords.
262 #. initial release
263
264 0.0.3 - 08.12.2014
265 --------------------------------------------------------------------------------
266
267 Updated
268 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
269
270 #. IO interface update as pyexcel-io introduced keywords.
271 #. initial release
0 Copyright (c) 2015-2016 by Onni Software Ltd. and its contributors
0 Copyright (c) 2015-2019 by Onni Software Ltd. and its contributors
11 All rights reserved.
22
33 Redistribution and use in source and binary forms of the software as well
1212 and/or other materials provided with the distribution.
1313
1414 * Neither the name of 'pyexcel-ods' nor the names of the contributors
15 may not be used to endorse or promote products derived from this software
15 may be used to endorse or promote products derived from this software
1616 without specific prior written permission.
1717
1818 THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND
2626 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
2727 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
2828 SOFTWARE AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
29 DAMAGE.
29 DAMAGE.
00 include README.rst
1 include LICENSE
2 include CHANGELOG.rst
3 recursive-include tests *
4 recursive-include docs *
+0
-5
Makefile less more
0 all: test
1
2 test:
3 bash test.sh
4
0 Metadata-Version: 1.1
1 Name: pyexcel-ods
2 Version: 0.5.6
3 Summary: A wrapper library to read, manipulate and write data in ods format
4 Home-page: https://github.com/pyexcel/pyexcel-ods
5 Author: C.W.
6 Author-email: [email protected]
7 License: New BSD
8 Download-URL: https://github.com/pyexcel/pyexcel-ods/archive/0.5.6.tar.gz
9 Description: ================================================================================
10 pyexcel-ods - Let you focus on data, instead of ods format
11 ================================================================================
12
13 .. image:: https://raw.githubusercontent.com/pyexcel/pyexcel.github.io/master/images/patreon.png
14 :target: https://www.patreon.com/pyexcel
15
16 .. image:: https://api.bountysource.com/badge/team?team_id=288537
17 :target: https://salt.bountysource.com/teams/chfw-pyexcel
18
19 .. image:: https://travis-ci.org/pyexcel/pyexcel-ods.svg?branch=master
20 :target: http://travis-ci.org/pyexcel/pyexcel-ods
21
22 .. image:: https://codecov.io/gh/pyexcel/pyexcel-ods/branch/master/graph/badge.svg
23 :target: https://codecov.io/gh/pyexcel/pyexcel-ods
24
25 .. image:: https://img.shields.io/gitter/room/gitterHQ/gitter.svg
26 :target: https://gitter.im/pyexcel/Lobby
27
28
29 **pyexcel-ods** is a tiny wrapper library to read, manipulate and write data in
30 ods format using python 2.6 and python 2.7. You are likely to use it with
31 `pyexcel <https://github.com/pyexcel/pyexcel>`_.
32 `pyexcel-ods3 <https://github.com/pyexcel/pyexcel-ods3>`_ is a sister library that
33 depends on ezodf and lxml. `pyexcel-odsr <https://github.com/pyexcel/pyexcel-odsr>`_
34 is the other sister library that has no external dependency but do ods reading only
35
36 Known constraints
37 ==================
38
39 Fonts, colors and charts are not supported.
40
41 Installation
42 ================================================================================
43
44
45 You can install pyexcel-ods via pip:
46
47 .. code-block:: bash
48
49 $ pip install pyexcel-ods
50
51
52 or clone it and install it:
53
54 .. code-block:: bash
55
56 $ git clone https://github.com/pyexcel/pyexcel-ods.git
57 $ cd pyexcel-ods
58 $ python setup.py install
59
60 Support the project
61 ================================================================================
62
63 If your company has embedded pyexcel and its components into a revenue generating
64 product, please support me on `patreon <https://www.patreon.com/bePatron?u=5537627>`_
65 or `bounty source <https://salt.bountysource.com/teams/chfw-pyexcel>`_ to maintain
66 the project and develop it further.
67
68 If you are an individual, you are welcome to support me too and for however long
69 you feel like. As my backer, you will receive
70 `early access to pyexcel related contents <https://www.patreon.com/pyexcel/posts>`_.
71
72 And your issues will get prioritized if you would like to become my patreon as `pyexcel pro user`.
73
74 With your financial support, I will be able to invest
75 a little bit more time in coding, documentation and writing interesting posts.
76
77
78 Usage
79 ================================================================================
80
81 As a standalone library
82 --------------------------------------------------------------------------------
83
84 Write to an ods file
85 ********************************************************************************
86
87
88
89 Here's the sample code to write a dictionary to an ods file:
90
91 .. code-block:: python
92
93 >>> from pyexcel_ods import save_data
94 >>> data = OrderedDict() # from collections import OrderedDict
95 >>> data.update({"Sheet 1": [[1, 2, 3], [4, 5, 6]]})
96 >>> data.update({"Sheet 2": [["row 1", "row 2", "row 3"]]})
97 >>> save_data("your_file.ods", data)
98
99
100 Read from an ods file
101 ********************************************************************************
102
103 Here's the sample code:
104
105 .. code-block:: python
106
107 >>> from pyexcel_ods import get_data
108 >>> data = get_data("your_file.ods")
109 >>> import json
110 >>> print(json.dumps(data))
111 {"Sheet 1": [[1, 2, 3], [4, 5, 6]], "Sheet 2": [["row 1", "row 2", "row 3"]]}
112
113
114 Write an ods to memory
115 ********************************************************************************
116
117 Here's the sample code to write a dictionary to an ods file:
118
119 .. code-block:: python
120
121 >>> from pyexcel_ods import save_data
122 >>> data = OrderedDict()
123 >>> data.update({"Sheet 1": [[1, 2, 3], [4, 5, 6]]})
124 >>> data.update({"Sheet 2": [[7, 8, 9], [10, 11, 12]]})
125 >>> io = StringIO()
126 >>> save_data(io, data)
127 >>> # do something with the io
128 >>> # In reality, you might give it to your http response
129 >>> # object for downloading
130
131
132
133
134 Read from an ods from memory
135 ********************************************************************************
136
137 Continue from previous example:
138
139 .. code-block:: python
140
141 >>> # This is just an illustration
142 >>> # In reality, you might deal with ods file upload
143 >>> # where you will read from requests.FILES['YOUR_ODS_FILE']
144 >>> data = get_data(io)
145 >>> print(json.dumps(data))
146 {"Sheet 1": [[1, 2, 3], [4, 5, 6]], "Sheet 2": [[7, 8, 9], [10, 11, 12]]}
147
148
149 Pagination feature
150 ********************************************************************************
151
152 Special notice 30/01/2017: due to the constraints of the underlying 3rd party
153 library, it will read the whole file before returning the paginated data. So
154 at the end of day, the only benefit is less data returned from the reading
155 function. No major performance improvement will be seen.
156
157 With that said, please install `pyexcel-odsr <https://github.com/pyexcel/pyexcel-odsr>`_
158 and it gives better performance in pagination.
159
160 Let's assume the following file is a huge ods file:
161
162 .. code-block:: python
163
164 >>> huge_data = [
165 ... [1, 21, 31],
166 ... [2, 22, 32],
167 ... [3, 23, 33],
168 ... [4, 24, 34],
169 ... [5, 25, 35],
170 ... [6, 26, 36]
171 ... ]
172 >>> sheetx = {
173 ... "huge": huge_data
174 ... }
175 >>> save_data("huge_file.ods", sheetx)
176
177 And let's pretend to read partial data:
178
179 .. code-block:: python
180
181 >>> partial_data = get_data("huge_file.ods", start_row=2, row_limit=3)
182 >>> print(json.dumps(partial_data))
183 {"huge": [[3, 23, 33], [4, 24, 34], [5, 25, 35]]}
184
185 And you could as well do the same for columns:
186
187 .. code-block:: python
188
189 >>> partial_data = get_data("huge_file.ods", start_column=1, column_limit=2)
190 >>> print(json.dumps(partial_data))
191 {"huge": [[21, 31], [22, 32], [23, 33], [24, 34], [25, 35], [26, 36]]}
192
193 Obvious, you could do both at the same time:
194
195 .. code-block:: python
196
197 >>> partial_data = get_data("huge_file.ods",
198 ... start_row=2, row_limit=3,
199 ... start_column=1, column_limit=2)
200 >>> print(json.dumps(partial_data))
201 {"huge": [[23, 33], [24, 34], [25, 35]]}
202
203 As a pyexcel plugin
204 --------------------------------------------------------------------------------
205
206 No longer, explicit import is needed since pyexcel version 0.2.2. Instead,
207 this library is auto-loaded. So if you want to read data in ods format,
208 installing it is enough.
209
210
211 Reading from an ods file
212 ********************************************************************************
213
214 Here is the sample code:
215
216 .. code-block:: python
217
218 >>> import pyexcel as pe
219 >>> sheet = pe.get_book(file_name="your_file.ods")
220 >>> sheet
221 Sheet 1:
222 +---+---+---+
223 | 1 | 2 | 3 |
224 +---+---+---+
225 | 4 | 5 | 6 |
226 +---+---+---+
227 Sheet 2:
228 +-------+-------+-------+
229 | row 1 | row 2 | row 3 |
230 +-------+-------+-------+
231
232
233 Writing to an ods file
234 ********************************************************************************
235
236 Here is the sample code:
237
238 .. code-block:: python
239
240 >>> sheet.save_as("another_file.ods")
241
242
243 Reading from a IO instance
244 ********************************************************************************
245
246 You got to wrap the binary content with stream to get ods working:
247
248 .. code-block:: python
249
250 >>> # This is just an illustration
251 >>> # In reality, you might deal with ods file upload
252 >>> # where you will read from requests.FILES['YOUR_ODS_FILE']
253 >>> odsfile = "another_file.ods"
254 >>> with open(odsfile, "rb") as f:
255 ... content = f.read()
256 ... r = pe.get_book(file_type="ods", file_content=content)
257 ... print(r)
258 ...
259 Sheet 1:
260 +---+---+---+
261 | 1 | 2 | 3 |
262 +---+---+---+
263 | 4 | 5 | 6 |
264 +---+---+---+
265 Sheet 2:
266 +-------+-------+-------+
267 | row 1 | row 2 | row 3 |
268 +-------+-------+-------+
269
270
271 Writing to a StringIO instance
272 ********************************************************************************
273
274 You need to pass a StringIO instance to Writer:
275
276 .. code-block:: python
277
278 >>> data = [
279 ... [1, 2, 3],
280 ... [4, 5, 6]
281 ... ]
282 >>> io = StringIO()
283 >>> sheet = pe.Sheet(data)
284 >>> io = sheet.save_to_memory("ods", io)
285 >>> # then do something with io
286 >>> # In reality, you might give it to your http response
287 >>> # object for downloading
288
289
290 License
291 ================================================================================
292
293 New BSD License
294
295 Developer guide
296 ==================
297
298 Development steps for code changes
299
300 #. git clone https://github.com/pyexcel/pyexcel-ods.git
301 #. cd pyexcel-ods
302
303 Upgrade your setup tools and pip. They are needed for development and testing only:
304
305 #. pip install --upgrade setuptools pip
306
307 Then install relevant development requirements:
308
309 #. pip install -r rnd_requirements.txt # if such a file exists
310 #. pip install -r requirements.txt
311 #. pip install -r tests/requirements.txt
312
313 Once you have finished your changes, please provide test case(s), relevant documentation
314 and update CHANGELOG.rst.
315
316 .. note::
317
318 As to rnd_requirements.txt, usually, it is created when a dependent
319 library is not released. Once the dependecy is installed
320 (will be released), the future
321 version of the dependency in the requirements.txt will be valid.
322
323
324 How to test your contribution
325 ------------------------------
326
327 Although `nose` and `doctest` are both used in code testing, it is adviable that unit tests are put in tests. `doctest` is incorporated only to make sure the code examples in documentation remain valid across different development releases.
328
329 On Linux/Unix systems, please launch your tests like this::
330
331 $ make
332
333 On Windows systems, please issue this command::
334
335 > test.bat
336
337 How to update test environment and update documentation
338 ---------------------------------------------------------
339
340 Additional steps are required:
341
342 #. pip install moban
343 #. git clone https://github.com/moremoban/setupmobans.git # generic setup
344 #. git clone https://github.com/pyexcel/pyexcel-commons.git commons
345 #. make your changes in `.moban.d` directory, then issue command `moban`
346
347 What is pyexcel-commons
348 ---------------------------------
349
350 Many information that are shared across pyexcel projects, such as: this developer guide, license info, etc. are stored in `pyexcel-commons` project.
351
352 What is .moban.d
353 ---------------------------------
354
355 `.moban.d` stores the specific meta data for the library.
356
357 Acceptance criteria
358 -------------------
359
360 #. Has Test cases written
361 #. Has all code lines tested
362 #. Passes all Travis CI builds
363 #. Has fair amount of documentation if your change is complex
364 #. run 'make format' so as to confirm the pyexcel organisation's coding style
365 #. Please update CHANGELOG.rst
366 #. Please add yourself to CONTRIBUTORS.rst
367 #. Agree on NEW BSD License for your contribution
368
369 Credits
370 ================================================================================
371
372 ODSReader is originally written by `Marco Conti <https://github.com/marcoconti83/read-ods-with-odfpy>`_
373
374
375 Change log
376 ================================================================================
377
378 0.5.6 - 19.03.2019
379 --------------------------------------------------------------------------------
380
381 added
382 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
383
384 #. `#32 <https://github.com/pyexcel/pyexcel-ods/issues/32>`_, fix odfpy pinning
385 #. `#33 <https://github.com/pyexcel/pyexcel-ods/issues/33>`_, fix
386 IntegerAccuracyLossError on i586
387
388 0.5.5 - 16.03.2019
389 --------------------------------------------------------------------------------
390
391 added
392 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
393
394 #. `#32 <https://github.com/pyexcel/pyexcel-ods/issues/32>`_, fix odfpy pinning
395
396 0.5.4 - 27.11.2018
397 --------------------------------------------------------------------------------
398
399 added
400 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
401
402 #. `#30 <https://github.com/pyexcel/pyexcel-ods/issues/30>`_, long type will not
403 be written in ods. please use string type. And if the integer is equal or
404 greater than 10 to the power of 16, it will not be written either in ods. In
405 both situation, IntegerPrecisionLossError will be raised.
406
407 0.5.3 - unreleased
408 --------------------------------------------------------------------------------
409
410 added
411 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
412
413 #. `#24 <https://github.com/pyexcel/pyexcel-ods/issues/24>`_, ignore
414 comments(<office:comment>) in cell
415 #. `#27 <https://github.com/pyexcel/pyexcel-ods/issues/27>`_, exception raised
416 when currency type is missing
417 #. fix odfpy version on 1.3.5.
418
419 0.5.2 - 23.10.2017
420 --------------------------------------------------------------------------------
421
422 updated
423 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
424
425 #. pyexcel `pyexcel#105 <https://github.com/pyexcel/pyexcel/issues/105>`_,
426 remove gease from setup_requires, introduced by 0.5.1.
427 #. remove python2.6 test support
428
429 0.5.1 - 20.10.2017
430 --------------------------------------------------------------------------------
431
432 added
433 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
434
435 #. `pyexcel#103 <https://github.com/pyexcel/pyexcel/issues/103>`_, include
436 LICENSE file in MANIFEST.in, meaning LICENSE file will appear in the released
437 tar ball.
438
439 0.5.0 - 30.08.2017
440 --------------------------------------------------------------------------------
441
442 Updated
443 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
444
445 #. put dependency on pyexcel-io 0.5.0, which uses cStringIO instead of StringIO.
446 Hence, there will be performance boost in handling files in memory.
447
448 Relocated
449 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
450
451 #. All ods type conversion code lives in pyexcel_io.service module
452
453 0.4.1 - 25.08.2017
454 --------------------------------------------------------------------------------
455
456 Updated
457 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
458
459 #. `pyexcel#23 <https://github.com/pyexcel/pyexcel/issues/23>`_, handle
460 unseekable stream given by http response
461 #. PR `#22 <https://github.com/pyexcel/pyexcel-ods/pull/22>`_, hanle white
462 spaces in a cell.
463
464 0.4.0 - 19.06.2017
465 --------------------------------------------------------------------------------
466
467 Updated
468 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
469
470 #. `pyexcel#14 <https://github.com/pyexcel/pyexcel/issues/14>`_, close file
471 handle
472 #. pyexcel-io plugin interface now updated to use `lml
473 <https://github.com/chfw/lml>`_.
474
475 0.3.3 - 07.05.2017
476 --------------------------------------------------------------------------------
477
478 Updated
479 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
480
481 #. issue `pyexcel#19 <https://github.com/pyexcel/pyexcel/issues/19>`_, not all
482 texts in a multi-node cell were extracted.
483
484 0.3.2 - 13.04.2017
485 --------------------------------------------------------------------------------
486
487 Updated
488 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
489
490 #. issue `pyexcel#17 <https://github.com/pyexcel/pyexcel/issues/17>`_, empty new
491 line is ignored
492 #. issue `pyexcel#6 <https://github.com/pyexcel/pyexcel/issues/6>`_,
493 PT288H00M00S is valid duration
494
495 0.3.1 - 02.02.2017
496 --------------------------------------------------------------------------------
497
498 Added
499 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
500
501 #. Recognize currency type
502
503 0.3.0 - 22.12.2016
504 --------------------------------------------------------------------------------
505
506 Updated
507 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
508
509 #. Code refactoring with pyexcel-io v 0.3.0
510
511 0.2.2 - 24.10.2016
512 --------------------------------------------------------------------------------
513
514 Updated
515 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
516
517 #. issue `pyexcel#14 <https://github.com/pyexcel/pyexcel/issues/14>`_, index
518 error when reading a ods file that has non-uniform columns repeated property.
519
520 0.2.1 - 31.08.2016
521 --------------------------------------------------------------------------------
522
523 Added
524 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
525
526 #. support pagination. two pairs: start_row, row_limit and start_column,
527 column_limit help you deal with large files.
528 #. use odfpy 1.3.3 as compulsory package. offically support python 3
529
530 0.2.0 - 01.06.2016
531 --------------------------------------------------------------------------------
532
533 Added
534 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
535
536 #. By default, `float` will be converted to `int` where fits. `auto_detect_int`,
537 a flag to switch off the autoatic conversion from `float` to `int`.
538 #. 'library=pyexcel-ods' was added so as to inform pyexcel to use it instead of
539 other libraries, in the situation where multiple plugins were installed.
540
541 Updated
542 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
543
544 #. support the auto-import feature of pyexcel-io 0.2.0
545
546 0.1.1 - 30.01.2016
547 --------------------------------------------------------------------------------
548
549 Added
550 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
551
552 #. 'streaming' is an extra option given to get_data. Only when 'streaming' is
553 explicitly set to True, the data will be consisted of generators, hence will
554 break your existing code.
555 #. uses yield in to_array and returns a generator
556 #. support multi-line text cell #5
557 #. feature migration from pyexcel-ods3 pyexcel/pyexcel-ods3#5
558
559 Updated
560 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
561
562 #. compatibility with pyexcel-io 0.1.1
563
564 0.0.12 - 10.10.2015
565 --------------------------------------------------------------------------------
566
567 Updated
568 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
569
570 #. Bug fix: excessive trailing columns with empty values
571
572 0.0.11 - 26.09.2015
573 --------------------------------------------------------------------------------
574
575 Updated
576 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
577
578 #. Complete fix for libreoffice datetime field
579
580 0.0.10 - 15.09.2015
581 --------------------------------------------------------------------------------
582
583 Updated
584 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
585
586 #. Bug fix: date field could have datetime from libreoffice
587
588 0.0.9 - 21.08.2015
589 --------------------------------------------------------------------------------
590
591 Updated
592 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
593
594 #. Bug fix: utf-8 string throw unicode exceptions
595
596 0.0.8 - 28.06.2015
597 --------------------------------------------------------------------------------
598
599 Updated
600 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
601
602 #. Pin dependency odfpy 0.9.6 to avoid buggy odfpy 1.3.0
603
604 0.0.7 - 28.05.2015
605 --------------------------------------------------------------------------------
606
607 Updated
608 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
609
610 #. Bug fix: "number-columns-repeated" is now respected
611
612 0.0.6 - 21.05.2015
613 --------------------------------------------------------------------------------
614
615 Updated
616 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
617
618 #. get_data and save_data are seen across pyexcel-* extensions. remember them
619 once and use them across all extensions.
620
621 0.0.5 - 22.02.2015
622 --------------------------------------------------------------------------------
623
624 Added
625 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
626
627 #. Loads only one sheet from a multiple sheet book
628 #. Use New BSD License
629
630 0.0.4 - 14.12.2014
631 --------------------------------------------------------------------------------
632
633 Updated
634 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
635
636 #. IO interface update as pyexcel-io introduced keywords.
637 #. initial release
638
639 0.0.3 - 08.12.2014
640 --------------------------------------------------------------------------------
641
642 Updated
643 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
644
645 #. IO interface update as pyexcel-io introduced keywords.
646 #. initial release
647
648
649 Keywords: python
650 Platform: UNKNOWN
651 Classifier: Topic :: Software Development :: Libraries
652 Classifier: Programming Language :: Python
653 Classifier: Intended Audience :: Developers
654 Classifier: Programming Language :: Python :: 2.6
655 Classifier: Programming Language :: Python :: 2.7
656 Classifier: Programming Language :: Python :: 3.3
657 Classifier: Programming Language :: Python :: 3.4
658 Classifier: Programming Language :: Python :: 3.5
659 Classifier: Programming Language :: Python :: 3.6
11 pyexcel-ods - Let you focus on data, instead of ods format
22 ================================================================================
33
4 .. image:: https://api.travis-ci.org/pyexcel/pyexcel-ods.png
5 :target: http://travis-ci.org/pyexcel/pyexcel-ods
6
7 .. image:: https://codecov.io/github/pyexcel/pyexcel-ods/coverage.png
8 :target: https://codecov.io/github/pyexcel/pyexcel-ods
4 .. image:: https://raw.githubusercontent.com/pyexcel/pyexcel.github.io/master/images/patreon.png
5 :target: https://www.patreon.com/pyexcel
6
7 .. image:: https://api.bountysource.com/badge/team?team_id=288537
8 :target: https://salt.bountysource.com/teams/chfw-pyexcel
9
10 .. image:: https://travis-ci.org/pyexcel/pyexcel-ods.svg?branch=master
11 :target: http://travis-ci.org/pyexcel/pyexcel-ods
12
13 .. image:: https://codecov.io/gh/pyexcel/pyexcel-ods/branch/master/graph/badge.svg
14 :target: https://codecov.io/gh/pyexcel/pyexcel-ods
15
16 .. image:: https://img.shields.io/gitter/room/gitterHQ/gitter.svg
17 :target: https://gitter.im/pyexcel/Lobby
18
919
1020 **pyexcel-ods** is a tiny wrapper library to read, manipulate and write data in
11 ods fromat using python 2.6 and python 2.7. You are likely to use it with
21 ods format using python 2.6 and python 2.7. You are likely to use it with
1222 `pyexcel <https://github.com/pyexcel/pyexcel>`_.
1323 `pyexcel-ods3 <https://github.com/pyexcel/pyexcel-ods3>`_ is a sister library that
14 does the same thing but supports Python 3.3 and 3.4 and depends on lxml.
24 depends on ezodf and lxml. `pyexcel-odsr <https://github.com/pyexcel/pyexcel-odsr>`_
25 is the other sister library that has no external dependency but do ods reading only
1526
1627 Known constraints
17 ================================================================================
18
19 Fonts, colors and charts are not supported.
28 ==================
29
30 Fonts, colors and charts are not supported.
2031
2132 Installation
2233 ================================================================================
2334
24 You can install it via pip:
35
36 You can install pyexcel-ods via pip:
2537
2638 .. code-block:: bash
2739
2840 $ pip install pyexcel-ods
2941
42
3043 or clone it and install it:
3144
3245 .. code-block:: bash
3346
34 $ git clone http://github.com/pyexcel/pyexcel-ods.git
47 $ git clone https://github.com/pyexcel/pyexcel-ods.git
3548 $ cd pyexcel-ods
3649 $ python setup.py install
3750
51 Support the project
52 ================================================================================
53
54 If your company has embedded pyexcel and its components into a revenue generating
55 product, please support me on `patreon <https://www.patreon.com/bePatron?u=5537627>`_
56 or `bounty source <https://salt.bountysource.com/teams/chfw-pyexcel>`_ to maintain
57 the project and develop it further.
58
59 If you are an individual, you are welcome to support me too and for however long
60 you feel like. As my backer, you will receive
61 `early access to pyexcel related contents <https://www.patreon.com/pyexcel/posts>`_.
62
63 And your issues will get prioritized if you would like to become my patreon as `pyexcel pro user`.
64
65 With your financial support, I will be able to invest
66 a little bit more time in coding, documentation and writing interesting posts.
67
68
3869 Usage
3970 ================================================================================
40
41 New feature
42 --------------------------------------------------------------------------------
43
44
45 1. Passing "streaming=True" to get_data, you will get the two dimensional array as a generator
46 2. Passing "data=your_generator" to save_data is acceptable too.
47
4871
4972 As a standalone library
5073 --------------------------------------------------------------------------------
5174
52 Write to an ods file
53 ********************************************************************************
54
5575 .. testcode::
5676 :hide:
5777
78 >>> import os
5879 >>> import sys
5980 >>> if sys.version_info[0] < 3:
6081 ... from StringIO import StringIO
6182 ... else:
6283 ... from io import BytesIO as StringIO
63 >>> from pyexcel_io import OrderedDict
84 >>> PY2 = sys.version_info[0] == 2
85 >>> if PY2 and sys.version_info[1] < 7:
86 ... from ordereddict import OrderedDict
87 ... else:
88 ... from collections import OrderedDict
89
90
91 Write to an ods file
92 ********************************************************************************
93
6494
6595
6696 Here's the sample code to write a dictionary to an ods file:
73103 >>> data.update({"Sheet 2": [["row 1", "row 2", "row 3"]]})
74104 >>> save_data("your_file.ods", data)
75105
106
76107 Read from an ods file
77108 ********************************************************************************
78109
84115 >>> data = get_data("your_file.ods")
85116 >>> import json
86117 >>> print(json.dumps(data))
87 {"Sheet 1": [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]], "Sheet 2": [["row 1", "row 2", "row 3"]]}
118 {"Sheet 1": [[1, 2, 3], [4, 5, 6]], "Sheet 2": [["row 1", "row 2", "row 3"]]}
119
88120
89121 Write an ods to memory
90122 ********************************************************************************
105137
106138
107139
140
108141 Read from an ods from memory
109142 ********************************************************************************
110143
117150 >>> # where you will read from requests.FILES['YOUR_ODS_FILE']
118151 >>> data = get_data(io)
119152 >>> print(json.dumps(data))
120 {"Sheet 1": [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]], "Sheet 2": [[7.0, 8.0, 9.0], [10.0, 11.0, 12.0]]}
153 {"Sheet 1": [[1, 2, 3], [4, 5, 6]], "Sheet 2": [[7, 8, 9], [10, 11, 12]]}
154
155
156 Pagination feature
157 ********************************************************************************
158
159 Special notice 30/01/2017: due to the constraints of the underlying 3rd party
160 library, it will read the whole file before returning the paginated data. So
161 at the end of day, the only benefit is less data returned from the reading
162 function. No major performance improvement will be seen.
163
164 With that said, please install `pyexcel-odsr <https://github.com/pyexcel/pyexcel-odsr>`_
165 and it gives better performance in pagination.
166
167 Let's assume the following file is a huge ods file:
168
169 .. code-block:: python
170
171 >>> huge_data = [
172 ... [1, 21, 31],
173 ... [2, 22, 32],
174 ... [3, 23, 33],
175 ... [4, 24, 34],
176 ... [5, 25, 35],
177 ... [6, 26, 36]
178 ... ]
179 >>> sheetx = {
180 ... "huge": huge_data
181 ... }
182 >>> save_data("huge_file.ods", sheetx)
183
184 And let's pretend to read partial data:
185
186 .. code-block:: python
187
188 >>> partial_data = get_data("huge_file.ods", start_row=2, row_limit=3)
189 >>> print(json.dumps(partial_data))
190 {"huge": [[3, 23, 33], [4, 24, 34], [5, 25, 35]]}
191
192 And you could as well do the same for columns:
193
194 .. code-block:: python
195
196 >>> partial_data = get_data("huge_file.ods", start_column=1, column_limit=2)
197 >>> print(json.dumps(partial_data))
198 {"huge": [[21, 31], [22, 32], [23, 33], [24, 34], [25, 35], [26, 36]]}
199
200 Obvious, you could do both at the same time:
201
202 .. code-block:: python
203
204 >>> partial_data = get_data("huge_file.ods",
205 ... start_row=2, row_limit=3,
206 ... start_column=1, column_limit=2)
207 >>> print(json.dumps(partial_data))
208 {"huge": [[23, 33], [24, 34], [25, 35]]}
209
210 .. testcode::
211 :hide:
212
213 >>> os.unlink("huge_file.ods")
121214
122215
123216 As a pyexcel plugin
124217 --------------------------------------------------------------------------------
125218
126 Import it in your file to enable this plugin:
127
128 .. code-block:: python
129
130 from pyexcel.ext import ods
131
132 Please note only pyexcel version 0.0.4+ support this.
219 No longer, explicit import is needed since pyexcel version 0.2.2. Instead,
220 this library is auto-loaded. So if you want to read data in ods format,
221 installing it is enough.
222
133223
134224 Reading from an ods file
135225 ********************************************************************************
139229 .. code-block:: python
140230
141231 >>> import pyexcel as pe
142 >>> from pyexcel.ext import ods
143232 >>> sheet = pe.get_book(file_name="your_file.ods")
144233 >>> sheet
145 Sheet Name: Sheet 1
234 Sheet 1:
146235 +---+---+---+
147236 | 1 | 2 | 3 |
148237 +---+---+---+
149238 | 4 | 5 | 6 |
150239 +---+---+---+
151 Sheet Name: Sheet 2
240 Sheet 2:
152241 +-------+-------+-------+
153242 | row 1 | row 2 | row 3 |
154243 +-------+-------+-------+
155244
245
156246 Writing to an ods file
157247 ********************************************************************************
158248
162252
163253 >>> sheet.save_as("another_file.ods")
164254
255
165256 Reading from a IO instance
166 ================================================================================
257 ********************************************************************************
167258
168259 You got to wrap the binary content with stream to get ods working:
169260
178269 ... r = pe.get_book(file_type="ods", file_content=content)
179270 ... print(r)
180271 ...
181 Sheet Name: Sheet 1
272 Sheet 1:
182273 +---+---+---+
183274 | 1 | 2 | 3 |
184275 +---+---+---+
185276 | 4 | 5 | 6 |
186277 +---+---+---+
187 Sheet Name: Sheet 2
278 Sheet 2:
188279 +-------+-------+-------+
189280 | row 1 | row 2 | row 3 |
190281 +-------+-------+-------+
191282
192283
193284 Writing to a StringIO instance
194 ================================================================================
285 ********************************************************************************
195286
196287 You need to pass a StringIO instance to Writer:
197288
203294 ... ]
204295 >>> io = StringIO()
205296 >>> sheet = pe.Sheet(data)
206 >>> sheet.save_to_memory("ods", io)
297 >>> io = sheet.save_to_memory("ods", io)
207298 >>> # then do something with io
208299 >>> # In reality, you might give it to your http response
209300 >>> # object for downloading
210301
302
211303 License
212304 ================================================================================
213305
214306 New BSD License
307
308 Developer guide
309 ==================
310
311 Development steps for code changes
312
313 #. git clone https://github.com/pyexcel/pyexcel-ods.git
314 #. cd pyexcel-ods
315
316 Upgrade your setup tools and pip. They are needed for development and testing only:
317
318 #. pip install --upgrade setuptools pip
319
320 Then install relevant development requirements:
321
322 #. pip install -r rnd_requirements.txt # if such a file exists
323 #. pip install -r requirements.txt
324 #. pip install -r tests/requirements.txt
325
326 Once you have finished your changes, please provide test case(s), relevant documentation
327 and update CHANGELOG.rst.
328
329 .. note::
330
331 As to rnd_requirements.txt, usually, it is created when a dependent
332 library is not released. Once the dependecy is installed
333 (will be released), the future
334 version of the dependency in the requirements.txt will be valid.
335
336
337 How to test your contribution
338 ------------------------------
339
340 Although `nose` and `doctest` are both used in code testing, it is adviable that unit tests are put in tests. `doctest` is incorporated only to make sure the code examples in documentation remain valid across different development releases.
341
342 On Linux/Unix systems, please launch your tests like this::
343
344 $ make
345
346 On Windows systems, please issue this command::
347
348 > test.bat
349
350 How to update test environment and update documentation
351 ---------------------------------------------------------
352
353 Additional steps are required:
354
355 #. pip install moban
356 #. git clone https://github.com/moremoban/setupmobans.git # generic setup
357 #. git clone https://github.com/pyexcel/pyexcel-commons.git commons
358 #. make your changes in `.moban.d` directory, then issue command `moban`
359
360 What is pyexcel-commons
361 ---------------------------------
362
363 Many information that are shared across pyexcel projects, such as: this developer guide, license info, etc. are stored in `pyexcel-commons` project.
364
365 What is .moban.d
366 ---------------------------------
367
368 `.moban.d` stores the specific meta data for the library.
369
370 Acceptance criteria
371 -------------------
372
373 #. Has Test cases written
374 #. Has all code lines tested
375 #. Passes all Travis CI builds
376 #. Has fair amount of documentation if your change is complex
377 #. run 'make format' so as to confirm the pyexcel organisation's coding style
378 #. Please update CHANGELOG.rst
379 #. Please add yourself to CONTRIBUTORS.rst
380 #. Agree on NEW BSD License for your contribution
215381
216382 Credits
217383 ================================================================================
223389
224390 >>> import os
225391 >>> os.unlink("your_file.ods")
226 >>> os.unlink("another_file.ods")
392 >>> os.unlink("another_file.ods")
+0
-15
applymoban.py less more
0 from os import path, system
1
2 config_dir = 'commons/config'
3 template_dir = 'commons/templates'
4
5 if not path.exists("commons"):
6 system("git clone https://github.com/pyexcel/pyexcel-commons.git commons")
7 system("moban -cd {0} -td {1} -t docs/source/conf.py.jj2 -o docs/source/conf.py -c moban.yaml".format(config_dir, template_dir))
8 system("moban -cd {0} -td {1} .moban.d -t README.rst -o README.rst -c moban.yaml".format(config_dir, template_dir))
9 system("moban -cd {0} -td {1} .moban.d -t setup.py -o setup.py -c moban.yaml".format(config_dir, template_dir))
10 system("moban -cd {0} -td {1} .moban.d -t travis.yml -o .travis.yml -c moban.yaml".format(config_dir, template_dir))
11 system("moban -cd {0} -td .moban.d -t requirements.txt -o requirements.txt -c moban.yaml".format(config_dir))
12 system("moban -cd {0} -td {1} -t LICENSE.jj2 -o LICENSE -c moban.yaml".format(config_dir, template_dir))
13 system("moban -cd {0} -td {1} .moban.d -t tests/requirements.txt -o tests/requirements.txt -c moban.yaml".format(config_dir, template_dir))
14 system("moban -cd {0} -td {1} .moban.d -t MANIFEST.in.jj2 -o MANIFEST.in -c moban.yaml".format(config_dir, template_dir))
00 # -*- coding: utf-8 -*-
1 import sys
2 import os
1 DESCRIPTION = (
2 'A wrapper library to read, manipulate and write data in ods format' +
3 ''
4 )
5 # Configuration file for the Sphinx documentation builder.
6 #
7 # This file only contains a selection of the most common options. For a full
8 # list see the documentation:
9 # http://www.sphinx-doc.org/en/master/config
10
11 # -- Path setup --------------------------------------------------------------
312
413 # If extensions (or modules to document with autodoc) are in another directory,
514 # add these directories to sys.path here. If the directory is relative to the
615 # documentation root, use os.path.abspath to make it absolute, like shown here.
7 #sys.path.insert(0, os.path.abspath('.'))
16 #
17 # import os
18 # import sys
19 # sys.path.insert(0, os.path.abspath('.'))
820
9 # -- General configuration ------------------------------------------------
21 # -- Project information -----------------------------------------------------
1022
11 # If your documentation needs a minimal Sphinx version, state it here.
12 #needs_sphinx = '1.0'
23 project = 'pyexcel'
24 copyright = 'copyright 2015-2019 Onni Software Ltd.'
25 author = 'Onni Software Ltd.'
26 # The short X.Y version
27 version = '0.5.6'
28 # The full version, including alpha/beta/rc tags
29 release = '0.5.6'
30
31 # -- General configuration ---------------------------------------------------
1332
1433 # Add any Sphinx extension module names here, as strings. They can be
1534 # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
1635 # ones.
17 extensions = [
18 'sphinx.ext.autodoc',
19 'sphinx.ext.doctest',
20 'sphinx.ext.intersphinx',
21 'sphinx.ext.viewcode',
22 ]
23
24 intersphinx_mapping = {'pyexcel': ('http://pyexcel.readthedocs.org/en/latest/', None)}
36 extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.intersphinx', 'sphinx.ext.viewcode',]
2537
2638 # Add any paths that contain templates here, relative to this directory.
2739 templates_path = ['_templates']
2840
29 # The suffix of source filenames.
30 source_suffix = '.rst'
31
32 # The encoding of source files.
33 #source_encoding = 'utf-8-sig'
34
35 # The master toctree document.
36 master_doc = 'index'
37
38 # General information about the project.
39 project = u'pyexcel-ods'
40 copyright = u'2015-2016 Onni Software Ltd.'
41
42 # The version info for the project you're documenting, acts as replacement for
43 # |version| and |release|, also used in various other places throughout the
44 # built documents.
45 #
46 # The short X.Y version.
47 version = '0.1.0'
48 # The full version, including alpha/beta/rc tags.
49 release = '0.1.0'
50
5141 # The language for content autogenerated by Sphinx. Refer to documentation
5242 # for a list of supported languages.
53 #language = None
54
55 # There are two options for replacing |today|: either, you set today to some
56 # non-false value, then it is used:
57 #today = ''
58 # Else, today_fmt is used as the format for a strftime call.
59 #today_fmt = '%B %d, %Y'
43 #
44 # This is also used if you do content translation via gettext catalogs.
45 # Usually you set "language" from the command line for these cases.
46 language = 'en'
6047
6148 # List of patterns, relative to source directory, that match files and
6249 # directories to ignore when looking for source files.
50 # This pattern also affects html_static_path and html_extra_path.
6351 exclude_patterns = []
6452
65 # The reST default role (used for this markup: `text`) to use for all
66 # documents.
67 #default_role = None
6853
69 # If true, '()' will be appended to :func: etc. cross-reference text.
70 #add_function_parentheses = True
71
72 # If true, the current module name will be prepended to all description
73 # unit titles (such as .. function::).
74 #add_module_names = True
75
76 # If true, sectionauthor and moduleauthor directives will be shown in the
77 # output. They are ignored by default.
78 #show_authors = False
79
80 # The name of the Pygments (syntax highlighting) style to use.
81 pygments_style = 'sphinx'
82
83 # A list of ignored prefixes for module index sorting.
84 #modindex_common_prefix = []
85
86 # If true, keep warnings as "system message" paragraphs in the built documents.
87 #keep_warnings = False
88
89
90 # -- Options for HTML output ----------------------------------------------
54 # -- Options for HTML output -------------------------------------------------
9155
9256 # The theme to use for HTML and HTML Help pages. See the documentation for
9357 # a list of builtin themes.
94 html_theme = 'default'
95
96 # Theme options are theme-specific and customize the look and feel of a theme
97 # further. For a list of options available for each theme, see the
98 # documentation.
99 #html_theme_options = {}
100 # Add any paths that contain custom themes here, relative to this directory.
101 #html_theme_path = []
102
103 # The name for this set of Sphinx documents. If None, it defaults to
104 # "<project> v<release> documentation".
105 #html_title = None
106
107 # A shorter title for the navigation bar. Default is the same as html_title.
108 #html_short_title = None
109
110 # The name of an image file (relative to this directory) to place at the top
111 # of the sidebar.
112 #html_logo = None
113
114 # The name of an image file (within the static path) to use as favicon of the
115 # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
116 # pixels large.
117 #html_favicon = None
58 #
59 html_theme = 'alabaster'
11860
11961 # Add any paths that contain custom static files (such as style sheets) here,
12062 # relative to this directory. They are copied after the builtin static files,
12163 # so a file named "default.css" will overwrite the builtin "default.css".
12264 html_static_path = ['_static']
12365
124 # Add any extra paths that contain custom files (such as robots.txt or
125 # .htaccess) here, relative to this directory. These files are copied
126 # directly to the root of the documentation.
127 #html_extra_path = []
66 # -- Extension configuration -------------------------------------------------
67 # -- Options for intersphinx extension ---------------------------------------
12868
129 # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
130 # using the given strftime format.
131 #html_last_updated_fmt = '%b %d, %Y'
69 # Example configuration for intersphinx: refer to the Python standard library.
70 intersphinx_mapping = {'https://docs.python.org/': None}
71 # TODO: html_theme not configurable upstream
72 html_theme = 'default'
13273
133 # If true, SmartyPants will be used to convert quotes and dashes to
134 # typographically correct entities.
135 #html_use_smartypants = True
136
137 # Custom sidebar templates, maps document names to template names.
138 #html_sidebars = {}
139
140 # Additional templates that should be rendered to pages, maps page names to
141 # template names.
142 #html_additional_pages = {}
143
144 # If false, no module index is generated.
145 #html_domain_indices = True
146
147 # If false, no index is generated.
148 #html_use_index = True
149
150 # If true, the index is split into individual pages for each letter.
151 #html_split_index = False
152
153 # If true, links to the reST sources are added to the pages.
154 #html_show_sourcelink = True
155
156 # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
157 #html_show_sphinx = True
158
159 # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
160 #html_show_copyright = True
161
162 # If true, an OpenSearch description file will be output, and all pages will
163 # contain a <link> tag referring to it. The value of this option must be the
164 # base URL from which the finished HTML is served.
165 #html_use_opensearch = ''
166
167 # This is the file name suffix for HTML files (e.g. ".xhtml").
168 #html_file_suffix = None
169
170 # Output file base name for HTML help builder.
171 htmlhelp_basename = 'pyexcel-odsdoc'
172
173
174 # -- Options for LaTeX output ---------------------------------------------
175
176 latex_elements = {
177 # The paper size ('letterpaper' or 'a4paper').
178 #'papersize': 'letterpaper',
179
180 # The font size ('10pt', '11pt' or '12pt').
181 #'pointsize': '10pt',
182
183 # Additional stuff for the LaTeX preamble.
184 #'preamble': '',
185 }
186
187 # Grouping the document tree into LaTeX files. List of tuples
188 # (source start file, target name, title,
189 # author, documentclass [howto, manual, or own class]).
190 latex_documents = [
191 ('index', 'pyexcel-ods.tex', u'pyexcel-ods Documentation',
192 u'Onni Software Ltd.', 'manual'),
74 # TODO: DESCRIPTION not configurable upstream
75 texinfo_documents = [
76 ('index', 'pyexcel-ods',
77 'pyexcel-ods Documentation',
78 'Onni Software Ltd.', 'pyexcel-ods',
79 DESCRIPTION,
80 'Miscellaneous'),
19381 ]
194
195 # The name of an image file (relative to this directory) to place at the top of
196 # the title page.
197 #latex_logo = None
198
199 # For "manual" documents, if this is true, then toplevel headings are parts,
200 # not chapters.
201 #latex_use_parts = False
202
203 # If true, show page references after internal links.
204 #latex_show_pagerefs = False
205
206 # If true, show URL addresses after external links.
207 #latex_show_urls = False
208
209 # Documents to append as an appendix to all manuals.
210 #latex_appendices = []
211
212 # If false, no module index is generated.
213 #latex_domain_indices = True
214
215
216 # -- Options for manual page output ---------------------------------------
217
218 # One entry per manual page. List of tuples
219 # (source start file, name, description, authors, manual section).
220 man_pages = [
221 ('index', 'pyexcel-ods', u'pyexcel-ods Documentation',
222 [u'Onni Software Ltd.'], 1)
223 ]
224
225 # If true, show URL addresses after external links.
226 #man_show_urls = False
227
228
229 # -- Options for Texinfo output -------------------------------------------
230
231 # Grouping the document tree into Texinfo files. List of tuples
232 # (source start file, target name, title, author,
233 # dir menu entry, description, category)
234 texinfo_documents = [
235 ('index', 'pyexcel-ods', u'pyexcel-ods Documentation',
236 u'Onni Software Ltd.', 'pyexcel-ods', 'One line description of project.',
237 'Miscellaneous'),
238 ]
239
240 # Documents to append as an appendix to all manuals.
241 #texinfo_appendices = []
242
243 # If false, no module index is generated.
244 #texinfo_domain_indices = True
245
246 # How to display URL addresses: 'footnote', 'no', or 'inline'.
247 #texinfo_show_urls = 'footnote'
248
249 # If true, do not generate a @detailmenu in the "Top" node's menu.
250 #texinfo_no_detailmenu = False
82 intersphinx_mapping.update({
83 'pyexcel': ('http://pyexcel.readthedocs.io/en/latest/', None),
84 })
+0
-12
moban.yaml less more
0 overrides: "pyexcel.yaml"
1 name: "pyexcel-ods"
2 nick_name: ods
3 version: 0.1.0
4 file_type: ods
5 keywords:
6 - 'ods'
7 dependencies:
8 - pyexcel-io>=0.1.0
9 - odfpy==0.9.6
10 description: |
11 A wrapper library to read, manipulate and write data in ods format
00 """
11 pyexcel_ods
22 ~~~~~~~~~~~~~~~~~~~
3 The lower level ods file format handler using odfpy
4 :copyright: (c) 2015-2017 by Onni Software Ltd & its contributors
5 :license: New BSD License
6 """
37
4 ODS format plugin for pyexcel
8 # flake8: noqa
9 from pyexcel_io.io import get_data as read_data
10 from pyexcel_io.io import isstream
11 from pyexcel_io.io import store_data as write_data
512
6 :copyright: (c) 2015-2016 by Onni Software Ltd.
7 :license: New BSD License, see LICENSE for further details
8 """
9 # Copyright 2011 Marco Conti
13 # this line has to be place above all else
14 # because of dynamic import
15 from pyexcel_io.plugins import IOPluginInfoChain
1016
11 # Licensed under the Apache License, Version 2.0 (the "License");
12 # you may not use this file except in compliance with the License.
13 # You may obtain a copy of the License at
14
15 # http://www.apache.org/licenses/LICENSE-2.0
16
17 # Unless required by applicable law or agreed to in writing, software
18 # distributed under the License is distributed on an "AS IS" BASIS,
19 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 # See the License for the specific language governing permissions and
21 # limitations under the License.
22
23 # Thanks to grt for the fixes
24 import datetime
25 from odf.table import TableRow, TableCell, Table
26 from odf.text import P
27 from odf.namespaces import OFFICENS
28 from odf.opendocument import OpenDocumentSpreadsheet, load
29 from pyexcel_io import (
30 SheetReaderBase,
31 BookReader,
32 SheetWriter,
33 BookWriter,
34 READERS,
35 WRITERS,
36 isstream,
37 get_data as read_data,
38 store_data as write_data
17 __FILE_TYPE__ = "ods"
18 IOPluginInfoChain(__name__).add_a_reader(
19 relative_plugin_class_path="odsr.ODSBook",
20 file_types=[__FILE_TYPE__],
21 stream_type="binary",
22 ).add_a_writer(
23 relative_plugin_class_path="odsw.ODSWriter",
24 file_types=[__FILE_TYPE__],
25 stream_type="binary",
3926 )
4027
4128
42 def float_value(value):
43 ret = float(value)
44 return ret
45
46
47 def date_value(value):
48 ret = "invalid"
49 try:
50 # catch strptime exceptions only
51 if len(value) == 10:
52 ret = datetime.datetime.strptime(
53 value,
54 "%Y-%m-%d")
55 ret = ret.date()
56 elif len(value) == 19:
57 ret = datetime.datetime.strptime(
58 value,
59 "%Y-%m-%dT%H:%M:%S")
60 elif len(value) > 19:
61 ret = datetime.datetime.strptime(
62 value[0:26],
63 "%Y-%m-%dT%H:%M:%S.%f")
64 except:
65 pass
66 if ret == "invalid":
67 raise Exception("Bad date value %s" % value)
68 return ret
69
70
71 def ods_date_value(value):
72 return value.strftime("%Y-%m-%d")
73
74
75 def time_value(value):
76 hour = int(value[2:4])
77 minute = int(value[5:7])
78 second = int(value[8:10])
79 if hour < 24:
80 ret = datetime.time(hour, minute, second)
81 else:
82 ret = datetime.timedelta(hours=hour, minutes=minute, seconds=second)
83 return ret
84
85
86 def ods_time_value(value):
87 return value.strftime("PT%HH%MM%SS")
88
89
90 def boolean_value(value):
91 if value == "true":
92 ret = True
93 else:
94 ret = False
95 return ret
96
97
98 def ods_bool_value(value):
99 if value is True:
100 return "true"
101 else:
102 return "false"
103
104
105 def ods_timedelta_value(cell):
106 hours = cell.days * 24 + cell.seconds // 3600
107 minutes = (cell.seconds // 60) % 60
108 seconds = cell.seconds % 60
109 return "PT%02dH%02dM%02dS" % (hours, minutes, seconds)
110
111
112 ODS_FORMAT_CONVERSION = {
113 "float": float,
114 "date": datetime.date,
115 "time": datetime.time,
116 'timedelta': datetime.timedelta,
117 "boolean": bool,
118 "percentage": float,
119 "currency": float
120 }
121
122
123 ODS_WRITE_FORMAT_COVERSION = {
124 float: "float",
125 int: "float",
126 str: "string",
127 datetime.date: "date",
128 datetime.time: "time",
129 datetime.timedelta: "timedelta",
130 bool: "boolean",
131 unicode: "string"
132 }
133
134
135 VALUE_CONVERTERS = {
136 "float": float_value,
137 "date": date_value,
138 "time": time_value,
139 "timedelta": time_value,
140 "boolean": boolean_value,
141 "percentage": float_value,
142 "currency": float_value
143 }
144
145 ODS_VALUE_CONVERTERS = {
146 "date": ods_date_value,
147 "time": ods_time_value,
148 "boolean": ods_bool_value,
149 "timedelta": ods_timedelta_value
150 }
151
152
153 VALUE_TOKEN = {
154 "float": "value",
155 "date": "date-value",
156 "time": "time-value",
157 "boolean": "boolean-value",
158 "percentage": "value",
159 "currency": "value",
160 "timedelta": "time-value"
161 }
162
163
164 class ODSSheet(SheetReaderBase):
165 @property
166 def name(self):
167 return self.native_sheet.getAttribute("name")
168
169 def to_array(self):
170 """reads a sheet in the sheet dictionary, storing each sheet
171 as an array (rows) of arrays (columns)"""
172 rows = self.native_sheet.getElementsByType(TableRow)
173 # for each row
174 for row in rows:
175 tmp_row = []
176 arr_cells = []
177 cells = row.getElementsByType(TableCell)
178
179 # for each cell
180 for cell in cells:
181 # repeated value?
182 repeat = cell.getAttribute("numbercolumnsrepeated")
183 cell_value = self._read_cell(cell)
184 if(not repeat):
185 tmp_row.append(cell_value)
186 else:
187 r = int(repeat)
188 for i in range(0, r):
189 tmp_row.append(cell_value)
190 if cell_value is not None and cell_value != '':
191 arr_cells += tmp_row
192 tmp_row = []
193 # if row contained something
194 yield arr_cells
195
196 def _read_text_cell(self, cell):
197 textContent = []
198 ps = cell.getElementsByType(P)
199 # for each text node
200 for p in ps:
201 for n in p.childNodes:
202 if (n.nodeType == 3):
203 textContent.append(unicode(n.data))
204 return '\n'.join(textContent)
205
206 def _read_cell(self, cell):
207 cell_type = cell.getAttrNS(OFFICENS, "value-type")
208 value_token = VALUE_TOKEN.get(cell_type, "value")
209 ret = None
210 if cell_type == "string":
211 textContent = self._read_text_cell(cell)
212 ret = textContent
213 else:
214 if cell_type in ODS_FORMAT_CONVERSION:
215 value = cell.getAttrNS(OFFICENS, value_token)
216 n_value = VALUE_CONVERTERS[cell_type](value)
217 ret = n_value
218 else:
219 textContent = self._read_text_cell(cell)
220 ret = textContent
221 return ret
222
223
224 class ODSBook(BookReader):
225
226 def get_sheet(self, native_sheet):
227 return ODSSheet(native_sheet)
228
229 def load_from_memory(self, file_content, **keywords):
230 return load(file_content)
231
232 def load_from_file(self, filename, **keywords):
233 return load(filename)
234
235 def sheet_iterator(self):
236 if self.sheet_name is not None:
237 tables = self.native_book.spreadsheet.getElementsByType(Table)
238 rets = [table for table in tables
239 if table.getAttribute('name') == self.sheet_name]
240 if len(rets) == 0:
241 raise ValueError("%s cannot be found" % self.sheet_name)
242 else:
243 return rets
244 elif self.sheet_index is not None:
245 tables = self.native_book.spreadsheet.getElementsByType(Table)
246 length = len(tables)
247 if self.sheet_index < length:
248 return [tables[self.sheet_index]]
249 else:
250 raise IndexError("Index %d of out bound %d" % (
251 self.sheet_index, length))
252 else:
253 return self.native_book.spreadsheet.getElementsByType(Table)
254
255
256 class ODSSheetWriter(SheetWriter):
257 """
258 ODS sheet writer
259 """
260 def set_sheet_name(self, name):
261 self.native_sheet = Table(name=name)
262
263 def set_size(self, size):
264 pass
265
266 def write_cell(self, row, cell):
267 tc = TableCell()
268 cell_type = type(cell)
269 cell_odf_type = ODS_WRITE_FORMAT_COVERSION.get(cell_type, "string")
270 tc.setAttrNS(OFFICENS, "value-type", cell_odf_type)
271 cell_odf_value_token = VALUE_TOKEN.get(cell_odf_type, "value")
272 converter = ODS_VALUE_CONVERTERS.get(cell_odf_type, None)
273 if converter:
274 cell = converter(cell)
275 if cell_odf_type != 'string':
276 tc.setAttrNS(OFFICENS, cell_odf_value_token, cell)
277 tc.addElement(P(text=cell))
278 else:
279 lines = cell.split('\n')
280 for line in lines:
281 tc.addElement(P(text=line))
282 row.addElement(tc)
283
284 def write_row(self, array):
285 """
286 write a row into the file
287 """
288 tr = TableRow()
289 self.native_sheet.addElement(tr)
290 for cell in array:
291 self.write_cell(tr, cell)
292
293 def close(self):
294 """
295 This call writes file
296
297 """
298 self.native_book.spreadsheet.addElement(self.native_sheet)
299
300
301 class ODSWriter(BookWriter):
302 """
303 open document spreadsheet writer
304
305 """
306 def __init__(self, file, **keywords):
307 BookWriter.__init__(self, file, **keywords)
308 self.native_book = OpenDocumentSpreadsheet()
309
310 def create_sheet(self, name):
311 """
312 write a row into the file
313 """
314 return ODSSheetWriter(self.native_book, None, name)
315
316 def close(self):
317 """
318 This call writes file
319
320 """
321 self.native_book.write(self.file)
322
323 # Register ods reader and writer
324 READERS["ods"] = ODSBook
325 WRITERS["ods"] = ODSWriter
326
327
32829 def save_data(afile, data, file_type=None, **keywords):
329 """
330 Save all data to a file
331 """
30 """standalone module function for writing module supported file type"""
33231 if isstream(afile) and file_type is None:
333 file_type = 'ods'
32 file_type = __FILE_TYPE__
33433 write_data(afile, data, file_type=file_type, **keywords)
33534
33635
33736 def get_data(afile, file_type=None, **keywords):
338 """
339 Retrieve all data from incoming file
340 """
37 """standalone module function for reading module supported file type"""
34138 if isstream(afile) and file_type is None:
342 file_type = 'ods'
39 file_type = __FILE_TYPE__
34340 return read_data(afile, file_type=file_type, **keywords)
344
0 """
1 pyexcel_ods.odsr
2 ~~~~~~~~~~~~~~~~~~~~~
3
4 ods reader
5
6 :copyright: (c) 2014-2017 by Onni Software Ltd.
7 :license: New BSD License, see LICENSE for more details
8 """
9 # Copyright 2011 Marco Conti
10
11 # Licensed under the Apache License, Version 2.0 (the "License");
12 # you may not use this file except in compliance with the License.
13 # You may obtain a copy of the License at
14
15 # http://www.apache.org/licenses/LICENSE-2.0
16
17 # Unless required by applicable law or agreed to in writing, software
18 # distributed under the License is distributed on an "AS IS" BASIS,
19 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 # See the License for the specific language governing permissions and
21 # limitations under the License.
22
23 import pyexcel_io.service as service
24 from pyexcel_io._compact import OrderedDict
25 from pyexcel_io.book import BookReader
26 from pyexcel_io.sheet import SheetReader
27
28 from odf.namespaces import OFFICENS
29 from odf.opendocument import load
30 from odf.table import Table, TableCell, TableRow
31
32 # Thanks to grt for the fixes
33 from odf.teletype import extractText
34 from odf.text import P
35
36
37 class ODSSheet(SheetReader):
38 """native ods sheet"""
39
40 def __init__(self, sheet, auto_detect_int=True, **keywords):
41 SheetReader.__init__(self, sheet, **keywords)
42 self.__auto_detect_int = auto_detect_int
43
44 @property
45 def name(self):
46 return self._native_sheet.getAttribute("name")
47
48 def row_iterator(self):
49 return self._native_sheet.getElementsByType(TableRow)
50
51 def column_iterator(self, row):
52 cells = row.getElementsByType(TableCell)
53 for cell in cells:
54 repeat = cell.getAttribute("numbercolumnsrepeated")
55 cell_value = self.__read_cell(cell)
56 if repeat:
57 number_of_repeat = int(repeat)
58 for i in range(number_of_repeat):
59 yield cell_value
60 else:
61 yield cell_value
62
63 def __read_cell(self, cell):
64 cell_type = cell.getAttrNS(OFFICENS, "value-type")
65 value_token = service.VALUE_TOKEN.get(cell_type, "value")
66 ret = None
67 if cell_type == "string":
68 text_content = self.__read_text_cell(cell)
69 ret = text_content
70 elif cell_type == "currency":
71 value = cell.getAttrNS(OFFICENS, value_token)
72 currency = cell.getAttrNS(OFFICENS, cell_type)
73 if currency:
74 ret = value + " " + currency
75 else:
76 ret = value
77 else:
78 if cell_type in service.VALUE_CONVERTERS:
79 value = cell.getAttrNS(OFFICENS, value_token)
80 n_value = service.VALUE_CONVERTERS[cell_type](value)
81 if cell_type == "float" and self.__auto_detect_int:
82 if service.has_no_digits_in_float(n_value):
83 n_value = int(n_value)
84 ret = n_value
85 else:
86 text_content = self.__read_text_cell(cell)
87 ret = text_content
88 return ret
89
90 def __read_text_cell(self, cell):
91 text_content = []
92 paragraphs = cell.getElementsByType(P)
93 # for each text node
94 for paragraph in paragraphs:
95 name_space, tag = paragraph.parentNode.qname
96 if tag != str("annotation"):
97 data = extractText(paragraph)
98 text_content.append(data)
99 return "\n".join(text_content)
100
101
102 class ODSBook(BookReader):
103 """read ods book"""
104
105 def open(self, file_name, **keywords):
106 """open ods file"""
107 BookReader.open(self, file_name, **keywords)
108 self._load_from_file()
109
110 def open_stream(self, file_stream, **keywords):
111 """open ods file stream"""
112 BookReader.open_stream(self, file_stream, **keywords)
113 self._load_from_memory()
114
115 def read_sheet_by_name(self, sheet_name):
116 """read a named sheet"""
117 tables = self._native_book.spreadsheet.getElementsByType(Table)
118 rets = [
119 table
120 for table in tables
121 if table.getAttribute("name") == sheet_name
122 ]
123 if len(rets) == 0:
124 raise ValueError("%s cannot be found" % sheet_name)
125 else:
126 return self.read_sheet(rets[0])
127
128 def read_sheet_by_index(self, sheet_index):
129 """read a sheet at a specified index"""
130 tables = self._native_book.spreadsheet.getElementsByType(Table)
131 length = len(tables)
132 if sheet_index < length:
133 return self.read_sheet(tables[sheet_index])
134 else:
135 raise IndexError(
136 "Index %d of out bound %d" % (sheet_index, length)
137 )
138
139 def read_all(self):
140 """read all sheets"""
141 result = OrderedDict()
142 for sheet in self._native_book.spreadsheet.getElementsByType(Table):
143 ods_sheet = ODSSheet(sheet, **self._keywords)
144 result[ods_sheet.name] = ods_sheet.to_array()
145
146 return result
147
148 def read_sheet(self, native_sheet):
149 """read one native sheet"""
150 sheet = ODSSheet(native_sheet, **self._keywords)
151 return {sheet.name: sheet.to_array()}
152
153 def close(self):
154 self._native_book = None
155
156 def _load_from_memory(self):
157 self._native_book = load(self._file_stream)
158
159 def _load_from_file(self):
160 self._native_book = load(self._file_name)
0 """
1 pyexcel_ods.odsw
2 ~~~~~~~~~~~~~~~~~~~~~
3
4 ods writer
5
6 :copyright: (c) 2014-2017 by Onni Software Ltd.
7 :license: New BSD License, see LICENSE for more details
8 """
9 import sys
10
11 import pyexcel_io.service as converter
12 from pyexcel_io.book import BookWriter
13 from pyexcel_io.sheet import SheetWriter
14
15 from odf.namespaces import OFFICENS
16 from odf.opendocument import OpenDocumentSpreadsheet
17 from odf.table import Table, TableCell, TableRow
18 from odf.text import P
19
20 PY2 = sys.version_info[0] == 2
21
22 PY27_BELOW = PY2 and sys.version_info[1] < 7
23
24
25 class ODSSheetWriter(SheetWriter):
26 """
27 ODS sheet writer
28 """
29
30 def set_sheet_name(self, name):
31 """initialize the native table"""
32 self._native_sheet = Table(name=name)
33
34 def set_size(self, size):
35 """not used in this class but used in ods3"""
36 pass
37
38 def write_cell(self, row, cell):
39 """write a native cell"""
40 cell_to_be_written = TableCell()
41 cell_type = type(cell)
42 cell_odf_type = converter.ODS_WRITE_FORMAT_COVERSION.get(
43 cell_type, "string"
44 )
45 cell_to_be_written.setAttrNS(OFFICENS, "value-type", cell_odf_type)
46 cell_odf_value_token = converter.VALUE_TOKEN.get(
47 cell_odf_type, "value"
48 )
49 converter_func = converter.ODS_VALUE_CONVERTERS.get(
50 cell_odf_type, None
51 )
52 if converter_func:
53 cell = converter_func(cell)
54 if cell_odf_type != "string":
55 cell_to_be_written.setAttrNS(OFFICENS, cell_odf_value_token, cell)
56 cell_to_be_written.addElement(P(text=cell))
57 else:
58 lines = cell.split("\n")
59 for line in lines:
60 cell_to_be_written.addElement(P(text=line))
61 row.addElement(cell_to_be_written)
62
63 def write_row(self, array):
64 """
65 write a row into the file
66 """
67 row = TableRow()
68 self._native_sheet.addElement(row)
69 for cell in array:
70 self.write_cell(row, cell)
71
72 def close(self):
73 """
74 This call writes file
75
76 """
77 self._native_book.spreadsheet.addElement(self._native_sheet)
78
79
80 class ODSWriter(BookWriter):
81 """
82 open document spreadsheet writer
83
84 """
85
86 def __init__(self):
87 BookWriter.__init__(self)
88 self._native_book = OpenDocumentSpreadsheet()
89
90 def create_sheet(self, name):
91 """
92 write a row into the file
93 """
94 return ODSSheetWriter(self._native_book, None, name)
95
96 def close(self):
97 """
98 This call writes file
99
100 """
101 self._native_book.write(self._file_alike_object)
102 self._native_book = None
0 Metadata-Version: 1.1
1 Name: pyexcel-ods
2 Version: 0.5.6
3 Summary: A wrapper library to read, manipulate and write data in ods format
4 Home-page: https://github.com/pyexcel/pyexcel-ods
5 Author: C.W.
6 Author-email: [email protected]
7 License: New BSD
8 Download-URL: https://github.com/pyexcel/pyexcel-ods/archive/0.5.6.tar.gz
9 Description: ================================================================================
10 pyexcel-ods - Let you focus on data, instead of ods format
11 ================================================================================
12
13 .. image:: https://raw.githubusercontent.com/pyexcel/pyexcel.github.io/master/images/patreon.png
14 :target: https://www.patreon.com/pyexcel
15
16 .. image:: https://api.bountysource.com/badge/team?team_id=288537
17 :target: https://salt.bountysource.com/teams/chfw-pyexcel
18
19 .. image:: https://travis-ci.org/pyexcel/pyexcel-ods.svg?branch=master
20 :target: http://travis-ci.org/pyexcel/pyexcel-ods
21
22 .. image:: https://codecov.io/gh/pyexcel/pyexcel-ods/branch/master/graph/badge.svg
23 :target: https://codecov.io/gh/pyexcel/pyexcel-ods
24
25 .. image:: https://img.shields.io/gitter/room/gitterHQ/gitter.svg
26 :target: https://gitter.im/pyexcel/Lobby
27
28
29 **pyexcel-ods** is a tiny wrapper library to read, manipulate and write data in
30 ods format using python 2.6 and python 2.7. You are likely to use it with
31 `pyexcel <https://github.com/pyexcel/pyexcel>`_.
32 `pyexcel-ods3 <https://github.com/pyexcel/pyexcel-ods3>`_ is a sister library that
33 depends on ezodf and lxml. `pyexcel-odsr <https://github.com/pyexcel/pyexcel-odsr>`_
34 is the other sister library that has no external dependency but do ods reading only
35
36 Known constraints
37 ==================
38
39 Fonts, colors and charts are not supported.
40
41 Installation
42 ================================================================================
43
44
45 You can install pyexcel-ods via pip:
46
47 .. code-block:: bash
48
49 $ pip install pyexcel-ods
50
51
52 or clone it and install it:
53
54 .. code-block:: bash
55
56 $ git clone https://github.com/pyexcel/pyexcel-ods.git
57 $ cd pyexcel-ods
58 $ python setup.py install
59
60 Support the project
61 ================================================================================
62
63 If your company has embedded pyexcel and its components into a revenue generating
64 product, please support me on `patreon <https://www.patreon.com/bePatron?u=5537627>`_
65 or `bounty source <https://salt.bountysource.com/teams/chfw-pyexcel>`_ to maintain
66 the project and develop it further.
67
68 If you are an individual, you are welcome to support me too and for however long
69 you feel like. As my backer, you will receive
70 `early access to pyexcel related contents <https://www.patreon.com/pyexcel/posts>`_.
71
72 And your issues will get prioritized if you would like to become my patreon as `pyexcel pro user`.
73
74 With your financial support, I will be able to invest
75 a little bit more time in coding, documentation and writing interesting posts.
76
77
78 Usage
79 ================================================================================
80
81 As a standalone library
82 --------------------------------------------------------------------------------
83
84 Write to an ods file
85 ********************************************************************************
86
87
88
89 Here's the sample code to write a dictionary to an ods file:
90
91 .. code-block:: python
92
93 >>> from pyexcel_ods import save_data
94 >>> data = OrderedDict() # from collections import OrderedDict
95 >>> data.update({"Sheet 1": [[1, 2, 3], [4, 5, 6]]})
96 >>> data.update({"Sheet 2": [["row 1", "row 2", "row 3"]]})
97 >>> save_data("your_file.ods", data)
98
99
100 Read from an ods file
101 ********************************************************************************
102
103 Here's the sample code:
104
105 .. code-block:: python
106
107 >>> from pyexcel_ods import get_data
108 >>> data = get_data("your_file.ods")
109 >>> import json
110 >>> print(json.dumps(data))
111 {"Sheet 1": [[1, 2, 3], [4, 5, 6]], "Sheet 2": [["row 1", "row 2", "row 3"]]}
112
113
114 Write an ods to memory
115 ********************************************************************************
116
117 Here's the sample code to write a dictionary to an ods file:
118
119 .. code-block:: python
120
121 >>> from pyexcel_ods import save_data
122 >>> data = OrderedDict()
123 >>> data.update({"Sheet 1": [[1, 2, 3], [4, 5, 6]]})
124 >>> data.update({"Sheet 2": [[7, 8, 9], [10, 11, 12]]})
125 >>> io = StringIO()
126 >>> save_data(io, data)
127 >>> # do something with the io
128 >>> # In reality, you might give it to your http response
129 >>> # object for downloading
130
131
132
133
134 Read from an ods from memory
135 ********************************************************************************
136
137 Continue from previous example:
138
139 .. code-block:: python
140
141 >>> # This is just an illustration
142 >>> # In reality, you might deal with ods file upload
143 >>> # where you will read from requests.FILES['YOUR_ODS_FILE']
144 >>> data = get_data(io)
145 >>> print(json.dumps(data))
146 {"Sheet 1": [[1, 2, 3], [4, 5, 6]], "Sheet 2": [[7, 8, 9], [10, 11, 12]]}
147
148
149 Pagination feature
150 ********************************************************************************
151
152 Special notice 30/01/2017: due to the constraints of the underlying 3rd party
153 library, it will read the whole file before returning the paginated data. So
154 at the end of day, the only benefit is less data returned from the reading
155 function. No major performance improvement will be seen.
156
157 With that said, please install `pyexcel-odsr <https://github.com/pyexcel/pyexcel-odsr>`_
158 and it gives better performance in pagination.
159
160 Let's assume the following file is a huge ods file:
161
162 .. code-block:: python
163
164 >>> huge_data = [
165 ... [1, 21, 31],
166 ... [2, 22, 32],
167 ... [3, 23, 33],
168 ... [4, 24, 34],
169 ... [5, 25, 35],
170 ... [6, 26, 36]
171 ... ]
172 >>> sheetx = {
173 ... "huge": huge_data
174 ... }
175 >>> save_data("huge_file.ods", sheetx)
176
177 And let's pretend to read partial data:
178
179 .. code-block:: python
180
181 >>> partial_data = get_data("huge_file.ods", start_row=2, row_limit=3)
182 >>> print(json.dumps(partial_data))
183 {"huge": [[3, 23, 33], [4, 24, 34], [5, 25, 35]]}
184
185 And you could as well do the same for columns:
186
187 .. code-block:: python
188
189 >>> partial_data = get_data("huge_file.ods", start_column=1, column_limit=2)
190 >>> print(json.dumps(partial_data))
191 {"huge": [[21, 31], [22, 32], [23, 33], [24, 34], [25, 35], [26, 36]]}
192
193 Obvious, you could do both at the same time:
194
195 .. code-block:: python
196
197 >>> partial_data = get_data("huge_file.ods",
198 ... start_row=2, row_limit=3,
199 ... start_column=1, column_limit=2)
200 >>> print(json.dumps(partial_data))
201 {"huge": [[23, 33], [24, 34], [25, 35]]}
202
203 As a pyexcel plugin
204 --------------------------------------------------------------------------------
205
206 No longer, explicit import is needed since pyexcel version 0.2.2. Instead,
207 this library is auto-loaded. So if you want to read data in ods format,
208 installing it is enough.
209
210
211 Reading from an ods file
212 ********************************************************************************
213
214 Here is the sample code:
215
216 .. code-block:: python
217
218 >>> import pyexcel as pe
219 >>> sheet = pe.get_book(file_name="your_file.ods")
220 >>> sheet
221 Sheet 1:
222 +---+---+---+
223 | 1 | 2 | 3 |
224 +---+---+---+
225 | 4 | 5 | 6 |
226 +---+---+---+
227 Sheet 2:
228 +-------+-------+-------+
229 | row 1 | row 2 | row 3 |
230 +-------+-------+-------+
231
232
233 Writing to an ods file
234 ********************************************************************************
235
236 Here is the sample code:
237
238 .. code-block:: python
239
240 >>> sheet.save_as("another_file.ods")
241
242
243 Reading from a IO instance
244 ********************************************************************************
245
246 You got to wrap the binary content with stream to get ods working:
247
248 .. code-block:: python
249
250 >>> # This is just an illustration
251 >>> # In reality, you might deal with ods file upload
252 >>> # where you will read from requests.FILES['YOUR_ODS_FILE']
253 >>> odsfile = "another_file.ods"
254 >>> with open(odsfile, "rb") as f:
255 ... content = f.read()
256 ... r = pe.get_book(file_type="ods", file_content=content)
257 ... print(r)
258 ...
259 Sheet 1:
260 +---+---+---+
261 | 1 | 2 | 3 |
262 +---+---+---+
263 | 4 | 5 | 6 |
264 +---+---+---+
265 Sheet 2:
266 +-------+-------+-------+
267 | row 1 | row 2 | row 3 |
268 +-------+-------+-------+
269
270
271 Writing to a StringIO instance
272 ********************************************************************************
273
274 You need to pass a StringIO instance to Writer:
275
276 .. code-block:: python
277
278 >>> data = [
279 ... [1, 2, 3],
280 ... [4, 5, 6]
281 ... ]
282 >>> io = StringIO()
283 >>> sheet = pe.Sheet(data)
284 >>> io = sheet.save_to_memory("ods", io)
285 >>> # then do something with io
286 >>> # In reality, you might give it to your http response
287 >>> # object for downloading
288
289
290 License
291 ================================================================================
292
293 New BSD License
294
295 Developer guide
296 ==================
297
298 Development steps for code changes
299
300 #. git clone https://github.com/pyexcel/pyexcel-ods.git
301 #. cd pyexcel-ods
302
303 Upgrade your setup tools and pip. They are needed for development and testing only:
304
305 #. pip install --upgrade setuptools pip
306
307 Then install relevant development requirements:
308
309 #. pip install -r rnd_requirements.txt # if such a file exists
310 #. pip install -r requirements.txt
311 #. pip install -r tests/requirements.txt
312
313 Once you have finished your changes, please provide test case(s), relevant documentation
314 and update CHANGELOG.rst.
315
316 .. note::
317
318 As to rnd_requirements.txt, usually, it is created when a dependent
319 library is not released. Once the dependecy is installed
320 (will be released), the future
321 version of the dependency in the requirements.txt will be valid.
322
323
324 How to test your contribution
325 ------------------------------
326
327 Although `nose` and `doctest` are both used in code testing, it is adviable that unit tests are put in tests. `doctest` is incorporated only to make sure the code examples in documentation remain valid across different development releases.
328
329 On Linux/Unix systems, please launch your tests like this::
330
331 $ make
332
333 On Windows systems, please issue this command::
334
335 > test.bat
336
337 How to update test environment and update documentation
338 ---------------------------------------------------------
339
340 Additional steps are required:
341
342 #. pip install moban
343 #. git clone https://github.com/moremoban/setupmobans.git # generic setup
344 #. git clone https://github.com/pyexcel/pyexcel-commons.git commons
345 #. make your changes in `.moban.d` directory, then issue command `moban`
346
347 What is pyexcel-commons
348 ---------------------------------
349
350 Many information that are shared across pyexcel projects, such as: this developer guide, license info, etc. are stored in `pyexcel-commons` project.
351
352 What is .moban.d
353 ---------------------------------
354
355 `.moban.d` stores the specific meta data for the library.
356
357 Acceptance criteria
358 -------------------
359
360 #. Has Test cases written
361 #. Has all code lines tested
362 #. Passes all Travis CI builds
363 #. Has fair amount of documentation if your change is complex
364 #. run 'make format' so as to confirm the pyexcel organisation's coding style
365 #. Please update CHANGELOG.rst
366 #. Please add yourself to CONTRIBUTORS.rst
367 #. Agree on NEW BSD License for your contribution
368
369 Credits
370 ================================================================================
371
372 ODSReader is originally written by `Marco Conti <https://github.com/marcoconti83/read-ods-with-odfpy>`_
373
374
375 Change log
376 ================================================================================
377
378 0.5.6 - 19.03.2019
379 --------------------------------------------------------------------------------
380
381 added
382 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
383
384 #. `#32 <https://github.com/pyexcel/pyexcel-ods/issues/32>`_, fix odfpy pinning
385 #. `#33 <https://github.com/pyexcel/pyexcel-ods/issues/33>`_, fix
386 IntegerAccuracyLossError on i586
387
388 0.5.5 - 16.03.2019
389 --------------------------------------------------------------------------------
390
391 added
392 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
393
394 #. `#32 <https://github.com/pyexcel/pyexcel-ods/issues/32>`_, fix odfpy pinning
395
396 0.5.4 - 27.11.2018
397 --------------------------------------------------------------------------------
398
399 added
400 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
401
402 #. `#30 <https://github.com/pyexcel/pyexcel-ods/issues/30>`_, long type will not
403 be written in ods. please use string type. And if the integer is equal or
404 greater than 10 to the power of 16, it will not be written either in ods. In
405 both situation, IntegerPrecisionLossError will be raised.
406
407 0.5.3 - unreleased
408 --------------------------------------------------------------------------------
409
410 added
411 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
412
413 #. `#24 <https://github.com/pyexcel/pyexcel-ods/issues/24>`_, ignore
414 comments(<office:comment>) in cell
415 #. `#27 <https://github.com/pyexcel/pyexcel-ods/issues/27>`_, exception raised
416 when currency type is missing
417 #. fix odfpy version on 1.3.5.
418
419 0.5.2 - 23.10.2017
420 --------------------------------------------------------------------------------
421
422 updated
423 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
424
425 #. pyexcel `pyexcel#105 <https://github.com/pyexcel/pyexcel/issues/105>`_,
426 remove gease from setup_requires, introduced by 0.5.1.
427 #. remove python2.6 test support
428
429 0.5.1 - 20.10.2017
430 --------------------------------------------------------------------------------
431
432 added
433 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
434
435 #. `pyexcel#103 <https://github.com/pyexcel/pyexcel/issues/103>`_, include
436 LICENSE file in MANIFEST.in, meaning LICENSE file will appear in the released
437 tar ball.
438
439 0.5.0 - 30.08.2017
440 --------------------------------------------------------------------------------
441
442 Updated
443 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
444
445 #. put dependency on pyexcel-io 0.5.0, which uses cStringIO instead of StringIO.
446 Hence, there will be performance boost in handling files in memory.
447
448 Relocated
449 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
450
451 #. All ods type conversion code lives in pyexcel_io.service module
452
453 0.4.1 - 25.08.2017
454 --------------------------------------------------------------------------------
455
456 Updated
457 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
458
459 #. `pyexcel#23 <https://github.com/pyexcel/pyexcel/issues/23>`_, handle
460 unseekable stream given by http response
461 #. PR `#22 <https://github.com/pyexcel/pyexcel-ods/pull/22>`_, hanle white
462 spaces in a cell.
463
464 0.4.0 - 19.06.2017
465 --------------------------------------------------------------------------------
466
467 Updated
468 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
469
470 #. `pyexcel#14 <https://github.com/pyexcel/pyexcel/issues/14>`_, close file
471 handle
472 #. pyexcel-io plugin interface now updated to use `lml
473 <https://github.com/chfw/lml>`_.
474
475 0.3.3 - 07.05.2017
476 --------------------------------------------------------------------------------
477
478 Updated
479 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
480
481 #. issue `pyexcel#19 <https://github.com/pyexcel/pyexcel/issues/19>`_, not all
482 texts in a multi-node cell were extracted.
483
484 0.3.2 - 13.04.2017
485 --------------------------------------------------------------------------------
486
487 Updated
488 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
489
490 #. issue `pyexcel#17 <https://github.com/pyexcel/pyexcel/issues/17>`_, empty new
491 line is ignored
492 #. issue `pyexcel#6 <https://github.com/pyexcel/pyexcel/issues/6>`_,
493 PT288H00M00S is valid duration
494
495 0.3.1 - 02.02.2017
496 --------------------------------------------------------------------------------
497
498 Added
499 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
500
501 #. Recognize currency type
502
503 0.3.0 - 22.12.2016
504 --------------------------------------------------------------------------------
505
506 Updated
507 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
508
509 #. Code refactoring with pyexcel-io v 0.3.0
510
511 0.2.2 - 24.10.2016
512 --------------------------------------------------------------------------------
513
514 Updated
515 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
516
517 #. issue `pyexcel#14 <https://github.com/pyexcel/pyexcel/issues/14>`_, index
518 error when reading a ods file that has non-uniform columns repeated property.
519
520 0.2.1 - 31.08.2016
521 --------------------------------------------------------------------------------
522
523 Added
524 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
525
526 #. support pagination. two pairs: start_row, row_limit and start_column,
527 column_limit help you deal with large files.
528 #. use odfpy 1.3.3 as compulsory package. offically support python 3
529
530 0.2.0 - 01.06.2016
531 --------------------------------------------------------------------------------
532
533 Added
534 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
535
536 #. By default, `float` will be converted to `int` where fits. `auto_detect_int`,
537 a flag to switch off the autoatic conversion from `float` to `int`.
538 #. 'library=pyexcel-ods' was added so as to inform pyexcel to use it instead of
539 other libraries, in the situation where multiple plugins were installed.
540
541 Updated
542 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
543
544 #. support the auto-import feature of pyexcel-io 0.2.0
545
546 0.1.1 - 30.01.2016
547 --------------------------------------------------------------------------------
548
549 Added
550 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
551
552 #. 'streaming' is an extra option given to get_data. Only when 'streaming' is
553 explicitly set to True, the data will be consisted of generators, hence will
554 break your existing code.
555 #. uses yield in to_array and returns a generator
556 #. support multi-line text cell #5
557 #. feature migration from pyexcel-ods3 pyexcel/pyexcel-ods3#5
558
559 Updated
560 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
561
562 #. compatibility with pyexcel-io 0.1.1
563
564 0.0.12 - 10.10.2015
565 --------------------------------------------------------------------------------
566
567 Updated
568 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
569
570 #. Bug fix: excessive trailing columns with empty values
571
572 0.0.11 - 26.09.2015
573 --------------------------------------------------------------------------------
574
575 Updated
576 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
577
578 #. Complete fix for libreoffice datetime field
579
580 0.0.10 - 15.09.2015
581 --------------------------------------------------------------------------------
582
583 Updated
584 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
585
586 #. Bug fix: date field could have datetime from libreoffice
587
588 0.0.9 - 21.08.2015
589 --------------------------------------------------------------------------------
590
591 Updated
592 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
593
594 #. Bug fix: utf-8 string throw unicode exceptions
595
596 0.0.8 - 28.06.2015
597 --------------------------------------------------------------------------------
598
599 Updated
600 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
601
602 #. Pin dependency odfpy 0.9.6 to avoid buggy odfpy 1.3.0
603
604 0.0.7 - 28.05.2015
605 --------------------------------------------------------------------------------
606
607 Updated
608 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
609
610 #. Bug fix: "number-columns-repeated" is now respected
611
612 0.0.6 - 21.05.2015
613 --------------------------------------------------------------------------------
614
615 Updated
616 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
617
618 #. get_data and save_data are seen across pyexcel-* extensions. remember them
619 once and use them across all extensions.
620
621 0.0.5 - 22.02.2015
622 --------------------------------------------------------------------------------
623
624 Added
625 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
626
627 #. Loads only one sheet from a multiple sheet book
628 #. Use New BSD License
629
630 0.0.4 - 14.12.2014
631 --------------------------------------------------------------------------------
632
633 Updated
634 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
635
636 #. IO interface update as pyexcel-io introduced keywords.
637 #. initial release
638
639 0.0.3 - 08.12.2014
640 --------------------------------------------------------------------------------
641
642 Updated
643 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
644
645 #. IO interface update as pyexcel-io introduced keywords.
646 #. initial release
647
648
649 Keywords: python
650 Platform: UNKNOWN
651 Classifier: Topic :: Software Development :: Libraries
652 Classifier: Programming Language :: Python
653 Classifier: Intended Audience :: Developers
654 Classifier: Programming Language :: Python :: 2.6
655 Classifier: Programming Language :: Python :: 2.7
656 Classifier: Programming Language :: Python :: 3.3
657 Classifier: Programming Language :: Python :: 3.4
658 Classifier: Programming Language :: Python :: 3.5
659 Classifier: Programming Language :: Python :: 3.6
0 CHANGELOG.rst
1 LICENSE
2 MANIFEST.in
3 README.rst
4 setup.cfg
5 setup.py
6 docs/Makefile
7 docs/make.bat
8 docs/source/conf.py
9 docs/source/index.rst
10 pyexcel_ods/__init__.py
11 pyexcel_ods/odsr.py
12 pyexcel_ods/odsw.py
13 pyexcel_ods.egg-info/PKG-INFO
14 pyexcel_ods.egg-info/SOURCES.txt
15 pyexcel_ods.egg-info/dependency_links.txt
16 pyexcel_ods.egg-info/not-zip-safe
17 pyexcel_ods.egg-info/requires.txt
18 pyexcel_ods.egg-info/top_level.txt
19 tests/base.py
20 tests/requirements.txt
21 tests/test_bug_fixes.py
22 tests/test_filter.py
23 tests/test_formatters.py
24 tests/test_multiline_feature.py
25 tests/test_multiple_sheets.py
26 tests/test_ods_reader.py
27 tests/test_stringio.py
28 tests/test_writer.py
29 tests/fixtures/12_day_as_time.ods
30 tests/fixtures/comment-in-cell.ods
31 tests/fixtures/file_with_an_empty_sheet.ods
32 tests/fixtures/issue_27.ods
33 tests/fixtures/issue_61.ods
34 tests/fixtures/multilineods.ods
35 tests/fixtures/ods_formats.ods
36 tests/fixtures/pyexcel_81_ods_19.ods
37 tests/fixtures/repeated.ods
38 tests/fixtures/white_space.ods
0 pyexcel-io>=0.5.16
1 odfpy>=1.3.5
+0
-2
requirements.txt less more
0 pyexcel-io>=0.1.0
1 odfpy==0.9.6
0 [metadata]
1 description-file = README.rst
2
3 [bdist_wheel]
4 universal = 1
5
6 [egg_info]
7 tag_build =
8 tag_date = 0
9
0 #!/usr/bin/env python3
1
2 import codecs
3 import locale
4 # Template by pypi-mobans
5 import os
6 import platform
7 import sys
8 from shutil import rmtree
9
10 from setuptools import Command, find_packages, setup
11
12 # Work around mbcs bug in distutils.
13 # http://bugs.python.org/issue10945
14 # This work around is only if a project supports Python < 3.4
15
16 # Work around for locale not being set
017 try:
1 from setuptools import setup, find_packages
2 except ImportError:
3 from ez_setup import use_setuptools
4 use_setuptools()
5 from setuptools import setup, find_packages
18 lc = locale.getlocale()
19 pf = platform.system()
20 if pf != 'Windows' and lc == (None, None):
21 locale.setlocale(locale.LC_ALL, 'C.UTF-8')
22 except (ValueError, UnicodeError, locale.Error):
23 locale.setlocale(locale.LC_ALL, 'en_US.UTF-8')
624
7 with open("README.rst", 'r') as readme:
8 README_txt = readme.read()
9
10 dependencies = [
11 'pyexcel-io>=0.1.0',
12 'odfpy==0.9.6',
25 NAME = 'pyexcel-ods'
26 AUTHOR = 'C.W.'
27 VERSION = '0.5.6'
28 EMAIL = '[email protected]'
29 LICENSE = 'New BSD'
30 DESCRIPTION = (
31 'A wrapper library to read, manipulate and write data in ods format'
32 )
33 URL = 'https://github.com/pyexcel/pyexcel-ods'
34 DOWNLOAD_URL = '%s/archive/0.5.6.tar.gz' % URL
35 FILES = ['README.rst', 'CHANGELOG.rst']
36 KEYWORDS = [
37 'python',
1338 ]
1439
15 extras = {}
40 CLASSIFIERS = [
41 'Topic :: Software Development :: Libraries',
42 'Programming Language :: Python',
43 'Intended Audience :: Developers',
44 'Programming Language :: Python :: 2.6',
45 'Programming Language :: Python :: 2.7',
46 'Programming Language :: Python :: 3.3',
47 'Programming Language :: Python :: 3.4',
48 'Programming Language :: Python :: 3.5',
49 'Programming Language :: Python :: 3.6',
50 ]
51
52 INSTALL_REQUIRES = [
53 'pyexcel-io>=0.5.16',
54 'odfpy>=1.3.5',
55 ]
56 SETUP_COMMANDS = {}
1657
1758
18 setup(
19 name='pyexcel-ods',
20 author='C. W.',
21 version='0.1.0',
22 author_email='wangc_2011 (at) hotmail.com',
23 url='https://github.com/pyexcel/pyexcel-ods',
24 description='A wrapper library to read, manipulate and write data in ods format',
25 install_requires=dependencies,
26 extras_require=extras,
27 packages=find_packages(exclude=['ez_setup', 'examples', 'tests']),
28 include_package_data=True,
29 long_description=README_txt,
30 zip_safe=False,
31 tests_require=['nose'],
32 keywords=[
33 'excel',
34 'python',
35 'pyexcel',
36 ],
37 license='New BSD',
38 classifiers=[
39 'Topic :: Office/Business',
40 'Topic :: Utilities',
41 'Topic :: Software Development :: Libraries',
42 'Programming Language :: Python',
43 'License :: OSI Approved :: BSD License',
44 'Intended Audience :: Developers',
45 'Programming Language :: Python :: 2.6',
46 'Programming Language :: Python :: 2.7'
47 ]
48 )
59 PACKAGES = find_packages(exclude=['ez_setup', 'examples', 'tests'])
60 EXTRAS_REQUIRE = {
61 }
62 # You do not need to read beyond this line
63 PUBLISH_COMMAND = '{0} setup.py sdist bdist_wheel upload -r pypi'.format(
64 sys.executable)
65 GS_COMMAND = ('gs pyexcel-ods v0.5.6 ' +
66 "Find 0.5.6 in changelog for more details")
67 NO_GS_MESSAGE = ('Automatic github release is disabled. ' +
68 'Please install gease to enable it.')
69 UPLOAD_FAILED_MSG = (
70 'Upload failed. please run "%s" yourself.' % PUBLISH_COMMAND)
71 HERE = os.path.abspath(os.path.dirname(__file__))
72
73
74 class PublishCommand(Command):
75 """Support setup.py upload."""
76
77 description = 'Build and publish the package on github and pypi'
78 user_options = []
79
80 @staticmethod
81 def status(s):
82 """Prints things in bold."""
83 print('\033[1m{0}\033[0m'.format(s))
84
85 def initialize_options(self):
86 pass
87
88 def finalize_options(self):
89 pass
90
91 def run(self):
92 try:
93 self.status('Removing previous builds...')
94 rmtree(os.path.join(HERE, 'dist'))
95 rmtree(os.path.join(HERE, 'build'))
96 rmtree(os.path.join(HERE, 'pyexcel_ods.egg-info'))
97 except OSError:
98 pass
99
100 self.status('Building Source and Wheel (universal) distribution...')
101 run_status = True
102 if has_gease():
103 run_status = os.system(GS_COMMAND) == 0
104 else:
105 self.status(NO_GS_MESSAGE)
106 if run_status:
107 if os.system(PUBLISH_COMMAND) != 0:
108 self.status(UPLOAD_FAILED_MSG % PUBLISH_COMMAND)
109
110 sys.exit()
111
112
113 SETUP_COMMANDS.update({
114 'publish': PublishCommand
115 })
116
117
118 def has_gease():
119 """
120 test if github release command is installed
121
122 visit http://github.com/moremoban/gease for more info
123 """
124 try:
125 import gease # noqa
126 return True
127 except ImportError:
128 return False
129
130
131 def read_files(*files):
132 """Read files into setup"""
133 text = ""
134 for single_file in files:
135 content = read(single_file)
136 text = text + content + "\n"
137 return text
138
139
140 def read(afile):
141 """Read a file into setup"""
142 the_relative_file = os.path.join(HERE, afile)
143 with codecs.open(the_relative_file, 'r', 'utf-8') as opened_file:
144 content = filter_out_test_code(opened_file)
145 content = "".join(list(content))
146 return content
147
148
149 def filter_out_test_code(file_handle):
150 found_test_code = False
151 for line in file_handle.readlines():
152 if line.startswith('.. testcode:'):
153 found_test_code = True
154 continue
155 if found_test_code is True:
156 if line.startswith(' '):
157 continue
158 else:
159 empty_line = line.strip()
160 if len(empty_line) == 0:
161 continue
162 else:
163 found_test_code = False
164 yield line
165 else:
166 for keyword in ['|version|', '|today|']:
167 if keyword in line:
168 break
169 else:
170 yield line
171
172
173 if __name__ == '__main__':
174 setup(
175 test_suite="tests",
176 name=NAME,
177 author=AUTHOR,
178 version=VERSION,
179 author_email=EMAIL,
180 description=DESCRIPTION,
181 url=URL,
182 download_url=DOWNLOAD_URL,
183 long_description=read_files(*FILES),
184 license=LICENSE,
185 keywords=KEYWORDS,
186 extras_require=EXTRAS_REQUIRE,
187 tests_require=['nose'],
188 install_requires=INSTALL_REQUIRES,
189 packages=PACKAGES,
190 include_package_data=True,
191 zip_safe=False,
192 classifiers=CLASSIFIERS,
193 cmdclass=SETUP_COMMANDS
194 )
+0
-1
test.bat less more
0 nosetests --with-cov --with-doctest --doctest-extension=.rst -I applymoban.py -I setup.py
+0
-1
test.sh less more
0 nosetests --rednose --with-cov --cov pyexcel_ods --cov tests --with-doctest --doctest-extension=.rst
0 import os
0 import datetime # noqa
1 import os # noqa
2
13 import pyexcel
2 from nose.tools import raises
3 import datetime
4 from nose.tools import eq_, raises # noqa
45
56
67 def create_sample_file1(file):
7 data=['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 1.1, 1]
8 data = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", 1.1, 1]
89 table = []
910 table.append(data[:4])
1011 table.append(data[4:8])
1617 """
1718 Abstract functional test for hat writers
1819 """
20
1921 content = {
20 "X": [1,2,3,4,5],
21 "Y": [6,7,8,9,10],
22 "Z": [11,12,13,14,15],
22 "X": [1, 2, 3, 4, 5],
23 "Y": [6, 7, 8, 9, 10],
24 "Z": [11, 12, 13, 14, 15],
2325 }
24
26
2527 def test_series_table(self):
2628 pyexcel.save_as(adict=self.content, dest_file_name=self.testfile)
2729 r = pyexcel.get_sheet(file_name=self.testfile, name_columns_by_row=0)
28 actual = pyexcel.utils.to_dict(r)
29 assert actual == self.content
30
30 eq_(r.dict, self.content)
31
3132
3233 class PyexcelWriterBase:
3334 """
3637 testfile and testfile2 have to be initialized before
3738 it is used for testing
3839 """
40
3941 content = [
40 [1,2,3,4,5],
41 [1,2,3,4,5],
42 [1,2,3,4,5],
43 [1,2,3,4,5]
42 [1, 2, 3, 4, 5],
43 [1, 2, 3, 4, 5],
44 [1, 2, 3, 4, 5],
45 [1, 2, 3, 4, 5],
4446 ]
4547
4648 def _create_a_file(self, file):
47 pyexcel.save_as(dest_file_name=file,array=self.content)
48
49 pyexcel.save_as(dest_file_name=file, array=self.content)
50
4951 def test_write_array(self):
5052 self._create_a_file(self.testfile)
5153 r = pyexcel.get_sheet(file_name=self.testfile)
52 actual = pyexcel.utils.to_array(r.rows())
54 actual = list(r.rows())
5355 assert actual == self.content
5456
5557
5658 class PyexcelMultipleSheetBase:
57
5859 def _write_test_file(self, filename):
5960 pyexcel.save_book_as(bookdict=self.content, dest_file_name=filename)
6061
6566 os.unlink(self.testfile)
6667
6768 def test_sheet_names(self):
68 r = pyexcel.BookReader( self.testfile)
69 expected = [ "Sheet1", "Sheet2", "Sheet3"]
69 r = pyexcel.BookReader(self.testfile)
70 expected = ["Sheet1", "Sheet2", "Sheet3"]
7071 sheet_names = r.sheet_names()
7172 for name in sheet_names:
7273 assert name in expected
7374
7475 def test_reading_through_sheets(self):
7576 b = pyexcel.BookReader(self.testfile)
76 data = pyexcel.utils.to_array(b["Sheet1"].rows())
77 data = list(b["Sheet1"].rows())
7778 expected = [[1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3]]
7879 assert data == expected
79 data = pyexcel.utils.to_array(b["Sheet2"].rows())
80 data = list(b["Sheet2"].rows())
8081 expected = [[4, 4, 4, 4], [5, 5, 5, 5], [6, 6, 6, 6]]
8182 assert data == expected
82 data = pyexcel.utils.to_array(b["Sheet3"].rows())
83 expected = [[u'X', u'Y', u'Z'], [1, 4, 7], [2, 5, 8], [3, 6, 9]]
83 data = list(b["Sheet3"].rows())
84 expected = [[u"X", u"Y", u"Z"], [1, 4, 7], [2, 5, 8], [3, 6, 9]]
8485 assert data == expected
8586 sheet3 = b["Sheet3"]
8687 sheet3.name_columns_by_row(0)
87 data = pyexcel.utils.to_array(b["Sheet3"].rows())
88 data = list(b["Sheet3"].rows())
8889 expected = [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
8990 assert data == expected
9091
91 def test_iterate_through_sheets(self):
92 b = pyexcel.BookReader(self.testfile)
93 for s in b:
94 data = pyexcel.utils.to_array(s)
95 assert self.content[s.name] == data
96 si = pyexcel.iterators.SheetIterator(b)
97 for s in si:
98 data = pyexcel.utils.to_array(s)
99 assert self.content[s.name] == data
100
101 def test_random_access_operator(self):
102 r = pyexcel.BookReader(self.testfile)
103 value = r["Sheet1"][0,1]
104 assert value == 1
105 value = r["Sheet3"][0,1]
106 assert value == 'Y'
107 r["Sheet3"].name_columns_by_row(0)
108 assert r["Sheet3"][0,1] == 4
109
11092
11193 class ODSCellTypes:
112
11394 def test_formats(self):
11495 # date formats
11596 date_format = "%d/%m/%Y"
116 assert self.data["Sheet1"][0][0] == "Date"
117 assert self.data["Sheet1"][1][0].strftime(date_format) == "11/11/2014"
118 assert self.data["Sheet1"][2][0].strftime(date_format) == "01/01/2001"
119 assert self.data["Sheet1"][3][0] == ""
97 eq_(self.data["Sheet1"][0][0], "Date")
98 eq_(self.data["Sheet1"][1][0].strftime(date_format), "11/11/2014")
99 eq_(self.data["Sheet1"][2][0].strftime(date_format), "01/01/2001")
100 eq_(self.data["Sheet1"][3][0], "")
120101 # time formats
121102 time_format = "%S:%M:%H"
122 assert self.data["Sheet1"][0][1] == "Time"
123 assert self.data["Sheet1"][1][1].strftime(time_format) == "12:12:11"
124 assert self.data["Sheet1"][2][1].strftime(time_format) == "12:00:00"
125 assert self.data["Sheet1"][3][1] == 0
126 assert self.data["Sheet1"][4][1] == datetime.timedelta(hours=27, minutes=17, seconds=54)
127 assert self.data["Sheet1"][5][1] == "Other"
103 eq_(self.data["Sheet1"][0][1], "Time")
104 eq_(self.data["Sheet1"][1][1].strftime(time_format), "12:12:11")
105 eq_(self.data["Sheet1"][2][1].strftime(time_format), "12:00:00")
106 eq_(self.data["Sheet1"][3][1], 0)
107 eq_(
108 self.data["Sheet1"][4][1],
109 datetime.timedelta(hours=27, minutes=17, seconds=54),
110 )
111 eq_(self.data["Sheet1"][5][1], "Other")
128112 # boolean
129 assert self.data["Sheet1"][0][2] == "Boolean"
130 assert self.data["Sheet1"][1][2] is True
131 assert self.data["Sheet1"][2][2] is False
113 eq_(self.data["Sheet1"][0][2], "Boolean")
114 eq_(self.data["Sheet1"][1][2], True)
115 eq_(self.data["Sheet1"][2][2], False)
132116 # Float
133 assert self.data["Sheet1"][0][3] == "Float"
134 assert self.data["Sheet1"][1][3] == 11.11
117 eq_(self.data["Sheet1"][0][3], "Float")
118 eq_(self.data["Sheet1"][1][3], 11.11)
135119 # Currency
136 assert self.data["Sheet1"][0][4] == "Currency"
137 assert self.data["Sheet1"][1][4] == 1
138 assert self.data["Sheet1"][2][4] == -10000
120 eq_(self.data["Sheet1"][0][4], "Currency")
121 eq_(self.data["Sheet1"][1][4], "1 GBP")
122 eq_(self.data["Sheet1"][2][4], "-10000 GBP")
139123 # Percentage
140 assert self.data["Sheet1"][0][5] == "Percentage"
141 assert self.data["Sheet1"][1][5] == 2
124 eq_(self.data["Sheet1"][0][5], "Percentage")
125 eq_(self.data["Sheet1"][1][5], 2)
142126 # int
143 assert self.data["Sheet1"][0][6] == "Int"
144 assert self.data["Sheet1"][1][6] == 3
145 assert self.data["Sheet1"][4][6] == 11
127 eq_(self.data["Sheet1"][0][6], "Int")
128 eq_(self.data["Sheet1"][1][6], 3)
129 eq_(self.data["Sheet1"][4][6], 11)
146130 # Scientifed not supported
147 assert self.data["Sheet1"][1][7] == 100000
131 eq_(self.data["Sheet1"][1][7], 100000)
148132 # Fraction
149 assert self.data["Sheet1"][1][8] == 1.25
133 eq_(self.data["Sheet1"][1][8], 1.25)
150134 # Text
151 assert self.data["Sheet1"][1][9] == "abc"
135 eq_(self.data["Sheet1"][1][9], "abc")
152136
153137 @raises(IndexError)
154138 def test_no_excessive_trailing_columns(self):
155 assert self.data["Sheet1"][2][6] == ""
139 eq_(self.data["Sheet1"][2][6], "")
00 nose
1 rednose
2 nose-cov
1 mock;python_version<"3"
32 codecov
43 coverage
4 flake8
5 moban
6 black;python_version>="3.6"
7 isort;python_version>="3.6"
8 psutil
59 pyexcel
610 pyexcel-xls
00 #!/usr/bin/python
11 # -*- encoding: utf-8 -*-
22 import os
3
4 import psutil
5 import pyexcel as pe
6 from nose import SkipTest
7 from nose.tools import eq_, raises
8 from pyexcel_io.exceptions import IntegerAccuracyLossError
9
310 from pyexcel_ods import get_data, save_data
4 from nose.tools import raises
11
12 IN_TRAVIS = "TRAVIS" in os.environ
513
614
715 def test_bug_fix_for_issue_1():
8 data = get_data(os.path.join("tests", "fixtures", "repeated.ods"))
9 assert data == [['repeated', 'repeated', 'repeated', 'repeated']]
10
16 data = get_data(get_fixtures("repeated.ods"))
17 eq_(data["Sheet1"], [["repeated", "repeated", "repeated", "repeated"]])
18
19
1120 def test_bug_fix_for_issue_2():
1221 data = {}
1322 data.update({"Sheet 1": [[1, 2, 3], [4, 5, 6]]})
1423 data.update({"Sheet 2": [[u"row 1", u"Héllô!", u"HolÁ!"]]})
1524 save_data("your_file.ods", data)
1625 new_data = get_data("your_file.ods")
17 assert new_data["Sheet 2"] == [[u'row 1', u'H\xe9ll\xf4!', u'Hol\xc1!']]
26 assert new_data["Sheet 2"] == [[u"row 1", u"H\xe9ll\xf4!", u"Hol\xc1!"]]
1827
19 def test_date_util_parse():
20 from pyexcel_ods import date_value
21 value = "2015-08-17T19:20:00"
22 d = date_value(value)
23 assert d.strftime("%Y-%m-%dT%H:%M:%S") == "2015-08-17T19:20:00"
24 value = "2015-08-17"
25 d = date_value(value)
26 assert d.strftime("%Y-%m-%d") == "2015-08-17"
27 value = "2015-08-17T19:20:59.999999"
28 d = date_value(value)
29 assert d.strftime("%Y-%m-%dT%H:%M:%S") == "2015-08-17T19:20:59"
30 value = "2015-08-17T19:20:59.99999"
31 d = date_value(value)
32 assert d.strftime("%Y-%m-%dT%H:%M:%S") == "2015-08-17T19:20:59"
33 value = "2015-08-17T19:20:59.999999999999999"
34 d = date_value(value)
35 assert d.strftime("%Y-%m-%dT%H:%M:%S") == "2015-08-17T19:20:59"
36
28
3729 @raises(Exception)
3830 def test_invalid_date():
39 from pyexcel_ods import date_value
31 from pyexcel_ods.ods import date_value
32
4033 value = "2015-08-"
4134 date_value(value)
4235
36
4337 @raises(Exception)
4438 def test_fake_date_time_10():
45 from pyexcel_ods import date_value
39 from pyexcel_ods.ods import date_value
40
4641 date_value("1234567890")
42
4743
4844 @raises(Exception)
4945 def test_fake_date_time_19():
50 from pyexcel_ods import date_value
46 from pyexcel_ods.ods import date_value
47
5148 date_value("1234567890123456789")
49
5250
5351 @raises(Exception)
5452 def test_fake_date_time_20():
55 from pyexcel_ods import date_value
53 from pyexcel_ods.ods import date_value
54
5655 date_value("12345678901234567890")
57
56
57
58 def test_issue_13():
59 test_file = "test_issue_13.ods"
60 data = [[1, 2], [], [], [], [3, 4]]
61 save_data(test_file, {test_file: data})
62 written_data = get_data(test_file, skip_empty_rows=False)
63 eq_(data, written_data[test_file])
64 os.unlink(test_file)
65
66
67 def test_issue_14():
68 # pyexcel issue 61
69 test_file = "issue_61.ods"
70 data = get_data(get_fixtures(test_file), skip_empty_rows=True)
71 eq_(data["S-LMC"], [[u"aaa"], [0]])
72
73
74 def test_issue_6():
75 test_file = "12_day_as_time.ods"
76 data = get_data(get_fixtures(test_file), skip_empty_rows=True)
77 eq_(data["Sheet1"][0][0].days, 12)
78
79
80 def test_issue_19():
81 test_file = "pyexcel_81_ods_19.ods"
82 data = get_data(get_fixtures(test_file), skip_empty_rows=True)
83 eq_(data["product.template"][1][1], "PRODUCT NAME PMP")
84
85
86 def test_issue_83_ods_file_handle():
87 # this proves that odfpy
88 # does not leave a file handle open at all
89 proc = psutil.Process()
90 test_file = get_fixtures("issue_61.ods")
91 open_files_l1 = proc.open_files()
92
93 # start with a csv file
94 data = pe.iget_array(file_name=test_file, library="pyexcel-ods")
95 open_files_l2 = proc.open_files()
96 delta = len(open_files_l2) - len(open_files_l1)
97 # cannot catch open file handle
98 assert delta == 0
99
100 # now the file handle get opened when we run through
101 # the generator
102 list(data)
103 open_files_l3 = proc.open_files()
104 delta = len(open_files_l3) - len(open_files_l1)
105 # cannot catch open file handle
106 assert delta == 0
107
108 # free the fish
109 pe.free_resources()
110 open_files_l4 = proc.open_files()
111 # this confirms that no more open file handle
112 eq_(open_files_l1, open_files_l4)
113
114
115 def test_pr_22():
116 test_file = get_fixtures("white_space.ods")
117 data = get_data(test_file)
118 eq_(data["Sheet1"][0][0], "paragraph with tab(\t), space, \nnew line")
119
120
121 def test_issue_23():
122 if not IN_TRAVIS:
123 raise SkipTest()
124 pe.get_book(
125 url=(
126 "https://github.com/pyexcel/pyexcel-ods/"
127 + "raw/master/tests/fixtures/white_space.ods"
128 )
129 )
130
131
132 def test_issue_24():
133 test_file = get_fixtures("comment-in-cell.ods")
134 data = get_data(test_file)
135 eq_(data["Sheet1"], [["test"]])
136
137
138 def test_issue_27():
139 test_file = get_fixtures("issue_27.ods")
140 data = get_data(test_file, skip_empty_rows=True)
141 eq_(data["VGPMX"], [["", "Cost Basis", "0"]])
142
143
144 def test_issue_30():
145 test_file = "issue_30.ods"
146 sheet = pe.Sheet()
147 sheet[0, 0] = 999999999999999
148 sheet.save_as(test_file)
149 sheet2 = pe.get_sheet(file_name=test_file)
150 eq_(sheet[0, 0], sheet2[0, 0])
151 os.unlink(test_file)
152
153
154 @raises(IntegerAccuracyLossError)
155 def test_issue_30_precision_loss():
156 test_file = "issue_30_2.ods"
157 sheet = pe.Sheet()
158 sheet[0, 0] = 9999999999999999
159 sheet.save_as(test_file)
160
161
162 def get_fixtures(filename):
163 return os.path.join("tests", "fixtures", filename)
0 import os
1
2 from nose.tools import eq_
3 from pyexcel_io import get_data, save_data
4
5
6 class TestFilter:
7 def setUp(self):
8 self.test_file = "test_filter.ods"
9 sample = [
10 [1, 21, 31],
11 [2, 22, 32],
12 [3, 23, 33],
13 [4, 24, 34],
14 [5, 25, 35],
15 [6, 26, 36],
16 ]
17 save_data(self.test_file, sample)
18 self.sheet_name = "pyexcel_sheet1"
19
20 def test_filter_row(self):
21 filtered_data = get_data(
22 self.test_file, start_row=3, library="pyexcel-ods"
23 )
24 expected = [[4, 24, 34], [5, 25, 35], [6, 26, 36]]
25 eq_(filtered_data[self.sheet_name], expected)
26
27 def test_filter_row_2(self):
28 filtered_data = get_data(
29 self.test_file, start_row=3, row_limit=1, library="pyexcel-ods"
30 )
31 expected = [[4, 24, 34]]
32 eq_(filtered_data[self.sheet_name], expected)
33
34 def test_filter_column(self):
35 filtered_data = get_data(
36 self.test_file, start_column=1, library="pyexcel-ods"
37 )
38 expected = [[21, 31], [22, 32], [23, 33], [24, 34], [25, 35], [26, 36]]
39 eq_(filtered_data[self.sheet_name], expected)
40
41 def test_filter_column_2(self):
42 filtered_data = get_data(
43 self.test_file,
44 start_column=1,
45 column_limit=1,
46 library="pyexcel-ods",
47 )
48 expected = [[21], [22], [23], [24], [25], [26]]
49 eq_(filtered_data[self.sheet_name], expected)
50
51 def test_filter_both_ways(self):
52 filtered_data = get_data(
53 self.test_file, start_column=1, start_row=3, library="pyexcel-ods"
54 )
55 expected = [[24, 34], [25, 35], [26, 36]]
56 eq_(filtered_data[self.sheet_name], expected)
57
58 def test_filter_both_ways_2(self):
59 filtered_data = get_data(
60 self.test_file,
61 start_column=1,
62 column_limit=1,
63 start_row=3,
64 row_limit=1,
65 library="pyexcel-ods",
66 )
67 expected = [[24]]
68 eq_(filtered_data[self.sheet_name], expected)
69
70 def tearDown(self):
71 os.unlink(self.test_file)
0 import os
1 from textwrap import dedent
2
3 import pyexcel as pe
4 from nose.tools import eq_
5
6
7 class TestAutoDetectInt:
8 def setUp(self):
9 self.content = [[1, 2, 3.1]]
10 self.test_file = "test_auto_detect_init.ods"
11 pe.save_as(array=self.content, dest_file_name=self.test_file)
12
13 def test_auto_detect_int(self):
14 sheet = pe.get_sheet(file_name=self.test_file, library="pyexcel-ods")
15 expected = dedent(
16 """
17 pyexcel_sheet1:
18 +---+---+-----+
19 | 1 | 2 | 3.1 |
20 +---+---+-----+"""
21 ).strip()
22 eq_(str(sheet), expected)
23
24 def test_get_book_auto_detect_int(self):
25 book = pe.get_book(file_name=self.test_file, library="pyexcel-ods")
26 expected = dedent(
27 """
28 pyexcel_sheet1:
29 +---+---+-----+
30 | 1 | 2 | 3.1 |
31 +---+---+-----+"""
32 ).strip()
33 eq_(str(book), expected)
34
35 def test_auto_detect_int_false(self):
36 sheet = pe.get_sheet(
37 file_name=self.test_file,
38 auto_detect_int=False,
39 library="pyexcel-ods",
40 )
41 expected = dedent(
42 """
43 pyexcel_sheet1:
44 +-----+-----+-----+
45 | 1.0 | 2.0 | 3.1 |
46 +-----+-----+-----+"""
47 ).strip()
48 eq_(str(sheet), expected)
49
50 def test_get_book_auto_detect_int_false(self):
51 book = pe.get_book(
52 file_name=self.test_file,
53 auto_detect_int=False,
54 library="pyexcel-ods",
55 )
56 expected = dedent(
57 """
58 pyexcel_sheet1:
59 +-----+-----+-----+
60 | 1.0 | 2.0 | 3.1 |
61 +-----+-----+-----+"""
62 ).strip()
63 eq_(str(book), expected)
64
65 def tearDown(self):
66 os.unlink(self.test_file)
00 import os
1 import pyexcel,pyexcel.ext.ods
1
2 import pyexcel
23
34
45 def test_reading_multiline_ods():
56 testfile = os.path.join("tests", "fixtures", "multilineods.ods")
67 sheet = pyexcel.get_sheet(file_name=testfile)
7 assert sheet[0,0] == '1\n2\n3\n4'
8 assert sheet[0, 0] == "1\n2\n3\n4"
9 assert sheet[1, 0] == "Line 1\n\nLine 2"
810
911
1012 def test_writing_multiline_ods():
11 content = "2\n3\n4\n993939\na"
13 content = "2\n3\n4\n993939\n\na"
1214 testfile = "writemultiline.ods"
13 array = [[content,"test"]]
15 array = [[content, "test"]]
1416 pyexcel.save_as(array=array, dest_file_name=testfile)
1517 sheet = pyexcel.get_sheet(file_name=testfile)
16 assert sheet[0,0] == content
18 assert sheet[0, 0] == content
1719 os.unlink(testfile)
0 import os
1 import sys
2
3 import pyexcel
4 from nose.tools import raises
5
6 from base import PyexcelMultipleSheetBase
7
8 if sys.version_info[0] == 2 and sys.version_info[1] < 7:
9 from ordereddict import OrderedDict
10 else:
11 from collections import OrderedDict
12
13
14 class TestOdsNxlsMultipleSheets(PyexcelMultipleSheetBase):
15 def setUp(self):
16 self.testfile = "multiple1.ods"
17 self.testfile2 = "multiple1.xls"
18 self.content = _produce_ordered_dict()
19 self._write_test_file(self.testfile)
20
21 def tearDown(self):
22 self._clean_up()
23
24
25 class TestXlsNOdsMultipleSheets(PyexcelMultipleSheetBase):
26 def setUp(self):
27 self.testfile = "multiple1.xls"
28 self.testfile2 = "multiple1.ods"
29 self.content = _produce_ordered_dict()
30 self._write_test_file(self.testfile)
31
32 def tearDown(self):
33 self._clean_up()
34
35
36 class TestAddBooks:
37 def _write_test_file(self, file):
38 """
39 Make a test file as:
40
41 1,1,1,1
42 2,2,2,2
43 3,3,3,3
44 """
45 self.rows = 3
46 pyexcel.save_book_as(bookdict=self.content, dest_file_name=file)
47
48 def setUp(self):
49 self.testfile = "multiple1.ods"
50 self.testfile2 = "multiple1.xls"
51 self.content = _produce_ordered_dict()
52 self._write_test_file(self.testfile)
53 self._write_test_file(self.testfile2)
54
55 def test_load_a_single_sheet(self):
56 b1 = pyexcel.get_book(file_name=self.testfile, sheet_name="Sheet1")
57 assert len(b1.sheet_names()) == 1
58 assert b1["Sheet1"].to_array() == self.content["Sheet1"]
59
60 def test_load_a_single_sheet2(self):
61 b1 = pyexcel.load_book(self.testfile, sheet_index=0)
62 assert len(b1.sheet_names()) == 1
63 assert b1["Sheet1"].to_array() == self.content["Sheet1"]
64
65 @raises(IndexError)
66 def test_load_a_single_sheet3(self):
67 pyexcel.get_book(file_name=self.testfile, sheet_index=10000)
68
69 @raises(ValueError)
70 def test_load_a_single_sheet4(self):
71 pyexcel.get_book(file_name=self.testfile, sheet_name="Not exist")
72
73 def test_delete_sheets(self):
74 b1 = pyexcel.load_book(self.testfile)
75 assert len(b1.sheet_names()) == 3
76 del b1["Sheet1"]
77 assert len(b1.sheet_names()) == 2
78 try:
79 del b1["Sheet1"]
80 assert 1 == 2
81 except KeyError:
82 assert 1 == 1
83 del b1[1]
84 assert len(b1.sheet_names()) == 1
85 try:
86 del b1[1]
87 assert 1 == 2
88 except IndexError:
89 assert 1 == 1
90
91 def test_delete_sheets2(self):
92 """repetitively delete first sheet"""
93 b1 = pyexcel.load_book(self.testfile)
94 del b1[0]
95 assert len(b1.sheet_names()) == 2
96 del b1[0]
97 assert len(b1.sheet_names()) == 1
98 del b1[0]
99 assert len(b1.sheet_names()) == 0
100
101 def test_add_book1(self):
102 """
103 test this scenario: book3 = book1 + book2
104 """
105 b1 = pyexcel.get_book(file_name=self.testfile)
106 b2 = pyexcel.get_book(file_name=self.testfile2)
107 b3 = b1 + b2
108 content = b3.dict
109 sheet_names = content.keys()
110 assert len(sheet_names) == 6
111 for name in sheet_names:
112 if "Sheet3" in name:
113 assert content[name] == self.content["Sheet3"]
114 elif "Sheet2" in name:
115 assert content[name] == self.content["Sheet2"]
116 elif "Sheet1" in name:
117 assert content[name] == self.content["Sheet1"]
118
119 def test_add_book1_in_place(self):
120 """
121 test this scenario: book1 += book2
122 """
123 b1 = pyexcel.BookReader(self.testfile)
124 b2 = pyexcel.BookReader(self.testfile2)
125 b1 += b2
126 content = b1.dict
127 sheet_names = content.keys()
128 assert len(sheet_names) == 6
129 for name in sheet_names:
130 if "Sheet3" in name:
131 assert content[name] == self.content["Sheet3"]
132 elif "Sheet2" in name:
133 assert content[name] == self.content["Sheet2"]
134 elif "Sheet1" in name:
135 assert content[name] == self.content["Sheet1"]
136
137 def test_add_book2(self):
138 """
139 test this scenario: book3 = book1 + sheet3
140 """
141 b1 = pyexcel.BookReader(self.testfile)
142 b2 = pyexcel.BookReader(self.testfile2)
143 b3 = b1 + b2["Sheet3"]
144 content = b3.dict
145 sheet_names = content.keys()
146 assert len(sheet_names) == 4
147 for name in sheet_names:
148 if "Sheet3" in name:
149 assert content[name] == self.content["Sheet3"]
150 elif "Sheet2" in name:
151 assert content[name] == self.content["Sheet2"]
152 elif "Sheet1" in name:
153 assert content[name] == self.content["Sheet1"]
154
155 def test_add_book2_in_place(self):
156 """
157 test this scenario: book3 = book1 + sheet3
158 """
159 b1 = pyexcel.BookReader(self.testfile)
160 b2 = pyexcel.BookReader(self.testfile2)
161 b1 += b2["Sheet3"]
162 content = b1.dict
163 sheet_names = content.keys()
164 assert len(sheet_names) == 4
165 for name in sheet_names:
166 if "Sheet3" in name:
167 assert content[name] == self.content["Sheet3"]
168 elif "Sheet2" in name:
169 assert content[name] == self.content["Sheet2"]
170 elif "Sheet1" in name:
171 assert content[name] == self.content["Sheet1"]
172
173 def test_add_book3(self):
174 """
175 test this scenario: book3 = sheet1 + sheet2
176 """
177 b1 = pyexcel.BookReader(self.testfile)
178 b2 = pyexcel.BookReader(self.testfile2)
179 b3 = b1["Sheet1"] + b2["Sheet3"]
180 content = b3.dict
181 sheet_names = content.keys()
182 assert len(sheet_names) == 2
183 assert content["Sheet3"] == self.content["Sheet3"]
184 assert content["Sheet1"] == self.content["Sheet1"]
185
186 def test_add_book4(self):
187 """
188 test this scenario: book3 = sheet1 + book
189 """
190 b1 = pyexcel.BookReader(self.testfile)
191 b2 = pyexcel.BookReader(self.testfile2)
192 b3 = b1["Sheet1"] + b2
193 content = b3.dict
194 sheet_names = content.keys()
195 assert len(sheet_names) == 4
196 for name in sheet_names:
197 if "Sheet3" in name:
198 assert content[name] == self.content["Sheet3"]
199 elif "Sheet2" in name:
200 assert content[name] == self.content["Sheet2"]
201 elif "Sheet1" in name:
202 assert content[name] == self.content["Sheet1"]
203
204 def test_add_book_error(self):
205 """
206 test this scenario: book3 = sheet1 + book
207 """
208 b1 = pyexcel.BookReader(self.testfile)
209 try:
210 b1 + 12
211 assert 1 == 2
212 except TypeError:
213 assert 1 == 1
214 try:
215 b1 += 12
216 assert 1 == 2
217 except TypeError:
218 assert 1 == 1
219
220 def tearDown(self):
221 if os.path.exists(self.testfile):
222 os.unlink(self.testfile)
223 if os.path.exists(self.testfile2):
224 os.unlink(self.testfile2)
225
226
227 class TestMultiSheetReader:
228 def setUp(self):
229 self.testfile = "file_with_an_empty_sheet.ods"
230
231 def test_reader_with_correct_sheets(self):
232 r = pyexcel.BookReader(
233 os.path.join("tests", "fixtures", self.testfile)
234 )
235 assert r.number_of_sheets() == 3
236
237
238 def _produce_ordered_dict():
239 data_dict = OrderedDict()
240 data_dict.update({"Sheet1": [[1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3]]})
241 data_dict.update({"Sheet2": [[4, 4, 4, 4], [5, 5, 5, 5], [6, 6, 6, 6]]})
242 data_dict.update(
243 {"Sheet3": [[u"X", u"Y", u"Z"], [1, 4, 7], [2, 5, 8], [3, 6, 9]]}
244 )
245 return data_dict
+0
-245
tests/test_mutliple_sheets.py less more
0 from base import PyexcelMultipleSheetBase
1 import pyexcel
2 import os
3 from pyexcel.ext import ods
4 from pyexcel.ext import xls
5 from nose.tools import raises
6 import sys
7
8 if sys.version_info[0] == 2 and sys.version_info[1] < 7:
9 from ordereddict import OrderedDict
10 else:
11 from collections import OrderedDict
12
13
14 class TestOdsNxlsMultipleSheets(PyexcelMultipleSheetBase):
15 def setUp(self):
16 self.testfile = "multiple1.ods"
17 self.testfile2 = "multiple1.xls"
18 self.content = {
19 "Sheet1": [[1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3]],
20 "Sheet2": [[4, 4, 4, 4], [5, 5, 5, 5], [6, 6, 6, 6]],
21 "Sheet3": [[u'X', u'Y', u'Z'], [1, 4, 7], [2, 5, 8], [3, 6, 9]]
22 }
23 self._write_test_file(self.testfile)
24
25 def tearDown(self):
26 self._clean_up()
27
28
29 class TestXlsNOdsMultipleSheets(PyexcelMultipleSheetBase):
30 def setUp(self):
31 self.testfile = "multiple1.xls"
32 self.testfile2 = "multiple1.ods"
33 self.content = {
34 "Sheet1": [[1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3]],
35 "Sheet2": [[4, 4, 4, 4], [5, 5, 5, 5], [6, 6, 6, 6]],
36 "Sheet3": [[u'X', u'Y', u'Z'], [1, 4, 7], [2, 5, 8], [3, 6, 9]]
37 }
38 self._write_test_file(self.testfile)
39
40 def tearDown(self):
41 self._clean_up()
42
43
44 class TestAddBooks:
45 def _write_test_file(self, file):
46 """
47 Make a test file as:
48
49 1,1,1,1
50 2,2,2,2
51 3,3,3,3
52 """
53 self.rows = 3
54 pyexcel.save_book_as(bookdict=self.content, dest_file_name=file)
55
56 def setUp(self):
57 self.testfile = "multiple1.ods"
58 self.testfile2 = "multiple1.xls"
59 self.content = OrderedDict()
60 self.content.update({"Sheet1": [[1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3]]})
61 self.content.update({"Sheet2": [[4, 4, 4, 4], [5, 5, 5, 5], [6, 6, 6, 6]]})
62 self.content.update({"Sheet3": [[u'X', u'Y', u'Z'], [1, 4, 7], [2, 5, 8], [3, 6, 9]]})
63 self._write_test_file(self.testfile)
64 self._write_test_file(self.testfile2)
65
66 def test_load_a_single_sheet(self):
67 b1 = pyexcel.load_book(self.testfile, sheet_name="Sheet1")
68 assert len(b1.sheet_names()) == 1
69 assert b1['Sheet1'].to_array() == self.content['Sheet1']
70
71 def test_load_a_single_sheet2(self):
72 b1 = pyexcel.load_book(self.testfile, sheet_index=0)
73 assert len(b1.sheet_names()) == 1
74 assert b1['Sheet1'].to_array() == self.content['Sheet1']
75
76 @raises(IndexError)
77 def test_load_a_single_sheet3(self):
78 pyexcel.load_book(self.testfile, sheet_index=10000)
79
80 @raises(ValueError)
81 def test_load_a_single_sheet4(self):
82 pyexcel.load_book(self.testfile, sheet_name="Not exist")
83
84 def test_delete_sheets(self):
85 b1 = pyexcel.load_book(self.testfile)
86 assert len(b1.sheet_names()) == 3
87 del b1["Sheet1"]
88 assert len(b1.sheet_names()) == 2
89 try:
90 del b1["Sheet1"]
91 assert 1==2
92 except KeyError:
93 assert 1==1
94 del b1[1]
95 assert len(b1.sheet_names()) == 1
96 try:
97 del b1[1]
98 assert 1==2
99 except IndexError:
100 assert 1==1
101
102 def test_delete_sheets2(self):
103 """repetitively delete first sheet"""
104 b1 = pyexcel.load_book(self.testfile)
105 del b1[0]
106 assert len(b1.sheet_names()) == 2
107 del b1[0]
108 assert len(b1.sheet_names()) == 1
109 del b1[0]
110 assert len(b1.sheet_names()) == 0
111
112 def test_add_book1(self):
113 """
114 test this scenario: book3 = book1 + book2
115 """
116 b1 = pyexcel.get_book(file_name=self.testfile)
117 b2 = pyexcel.get_book(file_name=self.testfile2)
118 b3 = b1 + b2
119 content = pyexcel.utils.to_dict(b3)
120 sheet_names = content.keys()
121 assert len(sheet_names) == 6
122 for name in sheet_names:
123 if "Sheet3" in name:
124 assert content[name] == self.content["Sheet3"]
125 elif "Sheet2" in name:
126 assert content[name] == self.content["Sheet2"]
127 elif "Sheet1" in name:
128 assert content[name] == self.content["Sheet1"]
129
130 def test_add_book1_in_place(self):
131 """
132 test this scenario book1 += book2
133 """
134 b1 = pyexcel.BookReader(self.testfile)
135 b2 = pyexcel.BookReader(self.testfile2)
136 b1 += b2
137 content = pyexcel.utils.to_dict(b1)
138 sheet_names = content.keys()
139 assert len(sheet_names) == 6
140 for name in sheet_names:
141 if "Sheet3" in name:
142 assert content[name] == self.content["Sheet3"]
143 elif "Sheet2" in name:
144 assert content[name] == self.content["Sheet2"]
145 elif "Sheet1" in name:
146 assert content[name] == self.content["Sheet1"]
147
148 def test_add_book2(self):
149 """
150 test this scenario book3 = book1 + sheet3
151 """
152 b1 = pyexcel.BookReader(self.testfile)
153 b2 = pyexcel.BookReader(self.testfile2)
154 b3 = b1 + b2["Sheet3"]
155 content = pyexcel.utils.to_dict(b3)
156 sheet_names = content.keys()
157 assert len(sheet_names) == 4
158 for name in sheet_names:
159 if "Sheet3" in name:
160 assert content[name] == self.content["Sheet3"]
161 elif "Sheet2" in name:
162 assert content[name] == self.content["Sheet2"]
163 elif "Sheet1" in name:
164 assert content[name] == self.content["Sheet1"]
165
166 def test_add_book2_in_place(self):
167 """
168 test this scenario book3 = book1 + sheet3
169 """
170 b1 = pyexcel.BookReader(self.testfile)
171 b2 = pyexcel.BookReader(self.testfile2)
172 b1 += b2["Sheet3"]
173 content = pyexcel.utils.to_dict(b1)
174 sheet_names = content.keys()
175 assert len(sheet_names) == 4
176 for name in sheet_names:
177 if "Sheet3" in name:
178 assert content[name] == self.content["Sheet3"]
179 elif "Sheet2" in name:
180 assert content[name] == self.content["Sheet2"]
181 elif "Sheet1" in name:
182 assert content[name] == self.content["Sheet1"]
183
184 def test_add_book3(self):
185 """
186 test this scenario book3 = sheet1 + sheet2
187 """
188 b1 = pyexcel.BookReader(self.testfile)
189 b2 = pyexcel.BookReader(self.testfile2)
190 b3 = b1["Sheet1"] + b2["Sheet3"]
191 content = pyexcel.utils.to_dict(b3)
192 sheet_names = content.keys()
193 assert len(sheet_names) == 2
194 assert content["Sheet3"] == self.content["Sheet3"]
195 assert content["Sheet1"] == self.content["Sheet1"]
196
197 def test_add_book4(self):
198 """
199 test this scenario book3 = sheet1 + book
200 """
201 b1 = pyexcel.BookReader(self.testfile)
202 b2 = pyexcel.BookReader(self.testfile2)
203 b3 = b1["Sheet1"] + b2
204 content = pyexcel.utils.to_dict(b3)
205 sheet_names = content.keys()
206 assert len(sheet_names) == 4
207 for name in sheet_names:
208 if "Sheet3" in name:
209 assert content[name] == self.content["Sheet3"]
210 elif "Sheet2" in name:
211 assert content[name] == self.content["Sheet2"]
212 elif "Sheet1" in name:
213 assert content[name] == self.content["Sheet1"]
214
215 def test_add_book_error(self):
216 """
217 test this scenario: book3 = sheet1 + book
218 """
219 b1 = pyexcel.BookReader(self.testfile)
220 try:
221 b1 + 12
222 assert 1==2
223 except TypeError:
224 assert 1==1
225 try:
226 b1 += 12
227 assert 1==2
228 except TypeError:
229 assert 1==1
230
231 def tearDown(self):
232 if os.path.exists(self.testfile):
233 os.unlink(self.testfile)
234 if os.path.exists(self.testfile2):
235 os.unlink(self.testfile2)
236
237
238 class TestMultiSheetReader:
239 def setUp(self):
240 self.testfile = "file_with_an_empty_sheet.ods"
241
242 def test_reader_with_correct_sheets(self):
243 r = pyexcel.BookReader(os.path.join("tests", "fixtures", self.testfile))
244 assert r.number_of_sheets() == 3
00 import os
1 from pyexcel.ext import ods
1
22 from base import ODSCellTypes
3 from pyexcel_ods.odsr import ODSBook
4 from pyexcel_ods.odsw import ODSWriter
35
46
57 class TestODSReader(ODSCellTypes):
68 def setUp(self):
7 r = ods.ODSBook(os.path.join("tests",
8 "fixtures",
9 "ods_formats.ods"))
10 self.data = r.sheets()
9 r = ODSBook()
10 r.open(os.path.join("tests", "fixtures", "ods_formats.ods"))
11 self.data = r.read_all()
1112 for key in self.data.keys():
1213 self.data[key] = list(self.data[key])
14 r.close()
1315
1416
1517 class TestODSWriter(ODSCellTypes):
1618 def setUp(self):
17 r = ods.ODSBook(os.path.join("tests",
18 "fixtures",
19 "ods_formats.ods"))
20 self.data1 = r.sheets()
19 r = ODSBook()
20 r.open(os.path.join("tests", "fixtures", "ods_formats.ods"))
21 self.data1 = r.read_all()
2122 self.testfile = "odswriter.ods"
22 w = ods.ODSWriter(self.testfile)
23 w = ODSWriter()
24 w.open(self.testfile)
2325 w.write(self.data1)
2426 w.close()
25 r2 = ods.ODSBook(self.testfile)
26 self.data = r2.sheets()
27 r2 = ODSBook()
28 r2.open(self.testfile)
29 self.data = r2.read_all()
2730 for key in self.data.keys():
2831 self.data[key] = list(self.data[key])
2932
00 import os
1
12 import pyexcel
2 from pyexcel.ext import ods
3 from StringIO import StringIO
3 from nose.tools import eq_
4
45 from base import create_sample_file1
56
67
78 class TestStringIO:
9 def test_ods_stringio(self):
10 testfile = "cute.ods"
11 create_sample_file1(testfile)
12 with open(testfile, "rb") as f:
13 content = f.read()
14 r = pyexcel.get_sheet(
15 file_type="ods", file_content=content, library="pyexcel-ods"
16 )
17 result = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", 1.1, 1]
18 actual = list(r.enumerate())
19 eq_(result, actual)
20 if os.path.exists(testfile):
21 os.unlink(testfile)
822
9 def test_ods_stringio(self):
10 odsfile = "cute.ods"
11 create_sample_file1(odsfile)
12 with open(odsfile, "rb") as f:
13 content = f.read()
14 r = pyexcel.Reader(("ods", content))
15 result=['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 1.1, 1]
16 actual = pyexcel.utils.to_array(r.enumerate())
17 assert result == actual
18 if os.path.exists(odsfile):
19 os.unlink(odsfile)
20
21
22 def test_xls_output_stringio(self):
23 data = [
24 [1, 2, 3],
25 [4, 5, 6]
26 ]
27 io = pyexcel.save_as(dest_file_type='ods', array=data)
28 r = pyexcel.Reader(("ods", io.getvalue()))
29 result=[1, 2, 3, 4, 5, 6]
30 actual = pyexcel.utils.to_array(r.enumerate())
31 assert result == actual
23 def test_ods_output_stringio(self):
24 data = [[1, 2, 3], [4, 5, 6]]
25 io = pyexcel.save_as(dest_file_type="ods", array=data)
26 r = pyexcel.get_sheet(
27 file_type="ods", file_content=io.getvalue(), library="pyexcel-ods"
28 )
29 result = [1, 2, 3, 4, 5, 6]
30 actual = list(r.enumerate())
31 eq_(result, actual)
00 import os
1 from pyexcel.ext import ods
2 from base import PyexcelWriterBase, PyexcelHatWriterBase
1
2 from base import PyexcelHatWriterBase, PyexcelWriterBase
3 from pyexcel_ods.odsr import ODSBook as Reader
4 from pyexcel_ods.odsw import ODSWriter as Writer
35
46
57 class TestNativeODSWriter:
79 self.content = {
810 "Sheet1": [[1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3]],
911 "Sheet2": [[4, 4, 4, 4], [5, 5, 5, 5], [6, 6, 6, 6]],
10 "Sheet3": [[u'X', u'Y', u'Z'], [1, 4, 7], [2, 5, 8], [3, 6, 9]]
12 "Sheet3": [[u"X", u"Y", u"Z"], [1, 4, 7], [2, 5, 8], [3, 6, 9]],
1113 }
12 self.testfile = "odswriter.ods"
13 writer = ods.ODSWriter(self.testfile)
14 self.testfile = "writer.ods"
15 writer = Writer()
16 writer.open(self.testfile)
1417 writer.write(self.content)
1518 writer.close()
16 reader = ods.ODSBook(self.testfile)
17 content = reader.sheets()
19 reader = Reader()
20 reader.open(self.testfile)
21 content = reader.read_all()
1822 for key in content.keys():
1923 content[key] = list(content[key])
2024 assert content == self.content
25 reader.close()
2126
2227 def tearDown(self):
2328 if os.path.exists(self.testfile):
2429 os.unlink(self.testfile)
2530
2631
27 class TestODSnCSVWriter(PyexcelWriterBase):
32 class TestodsnCSVWriter(PyexcelWriterBase):
2833 def setUp(self):
29 self.testfile="testods.ods"
30 self.testfile2="test.csv"
34 self.testfile = "test.ods"
35 self.testfile2 = "test.csv"
3136
3237 def tearDown(self):
3338 if os.path.exists(self.testfile):
3641 os.unlink(self.testfile2)
3742
3843
39 class TestODSHatWriter(PyexcelHatWriterBase):
44 class TestodsHatWriter(PyexcelHatWriterBase):
4045 def setUp(self):
41 self.testfile="testhat.ods"
46 self.testfile = "test.ods"
4247
4348 def tearDown(self):
4449 if os.path.exists(self.testfile):
4550 os.unlink(self.testfile)
46
47