New upstream version 0.5.6
Sophie Brun
4 years ago
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 | {% 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 | {% extends 'tests/requirements.txt.jj2' %} | |
1 | {%block extras %} | |
2 | pyexcel | |
3 | pyexcel-xls | |
4 | {%endblock%} |
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 | |
1 | 1 | All rights reserved. |
2 | 2 | |
3 | 3 | Redistribution and use in source and binary forms of the software as well |
12 | 12 | and/or other materials provided with the distribution. |
13 | 13 | |
14 | 14 | * 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 | |
16 | 16 | without specific prior written permission. |
17 | 17 | |
18 | 18 | THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND |
26 | 26 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
27 | 27 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
28 | 28 | SOFTWARE AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
29 | DAMAGE.⏎ | |
29 | DAMAGE. |
0 | 0 | include README.rst |
1 | include LICENSE | |
2 | include CHANGELOG.rst | |
3 | recursive-include tests * | |
4 | recursive-include docs * |
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 |
1 | 1 | pyexcel-ods - Let you focus on data, instead of ods format |
2 | 2 | ================================================================================ |
3 | 3 | |
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 | ||
9 | 19 | |
10 | 20 | **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 | |
12 | 22 | `pyexcel <https://github.com/pyexcel/pyexcel>`_. |
13 | 23 | `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 | |
15 | 26 | |
16 | 27 | Known constraints |
17 | ================================================================================ | |
18 | ||
19 | Fonts, colors and charts are not supported. | |
28 | ================== | |
29 | ||
30 | Fonts, colors and charts are not supported. | |
20 | 31 | |
21 | 32 | Installation |
22 | 33 | ================================================================================ |
23 | 34 | |
24 | You can install it via pip: | |
35 | ||
36 | You can install pyexcel-ods via pip: | |
25 | 37 | |
26 | 38 | .. code-block:: bash |
27 | 39 | |
28 | 40 | $ pip install pyexcel-ods |
29 | 41 | |
42 | ||
30 | 43 | or clone it and install it: |
31 | 44 | |
32 | 45 | .. code-block:: bash |
33 | 46 | |
34 | $ git clone http://github.com/pyexcel/pyexcel-ods.git | |
47 | $ git clone https://github.com/pyexcel/pyexcel-ods.git | |
35 | 48 | $ cd pyexcel-ods |
36 | 49 | $ python setup.py install |
37 | 50 | |
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 | ||
38 | 69 | Usage |
39 | 70 | ================================================================================ |
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 | ||
48 | 71 | |
49 | 72 | As a standalone library |
50 | 73 | -------------------------------------------------------------------------------- |
51 | 74 | |
52 | Write to an ods file | |
53 | ******************************************************************************** | |
54 | ||
55 | 75 | .. testcode:: |
56 | 76 | :hide: |
57 | 77 | |
78 | >>> import os | |
58 | 79 | >>> import sys |
59 | 80 | >>> if sys.version_info[0] < 3: |
60 | 81 | ... from StringIO import StringIO |
61 | 82 | ... else: |
62 | 83 | ... 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 | ||
64 | 94 | |
65 | 95 | |
66 | 96 | Here's the sample code to write a dictionary to an ods file: |
73 | 103 | >>> data.update({"Sheet 2": [["row 1", "row 2", "row 3"]]}) |
74 | 104 | >>> save_data("your_file.ods", data) |
75 | 105 | |
106 | ||
76 | 107 | Read from an ods file |
77 | 108 | ******************************************************************************** |
78 | 109 | |
84 | 115 | >>> data = get_data("your_file.ods") |
85 | 116 | >>> import json |
86 | 117 | >>> 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 | ||
88 | 120 | |
89 | 121 | Write an ods to memory |
90 | 122 | ******************************************************************************** |
105 | 137 | |
106 | 138 | |
107 | 139 | |
140 | ||
108 | 141 | Read from an ods from memory |
109 | 142 | ******************************************************************************** |
110 | 143 | |
117 | 150 | >>> # where you will read from requests.FILES['YOUR_ODS_FILE'] |
118 | 151 | >>> data = get_data(io) |
119 | 152 | >>> 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") | |
121 | 214 | |
122 | 215 | |
123 | 216 | As a pyexcel plugin |
124 | 217 | -------------------------------------------------------------------------------- |
125 | 218 | |
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 | ||
133 | 223 | |
134 | 224 | Reading from an ods file |
135 | 225 | ******************************************************************************** |
139 | 229 | .. code-block:: python |
140 | 230 | |
141 | 231 | >>> import pyexcel as pe |
142 | >>> from pyexcel.ext import ods | |
143 | 232 | >>> sheet = pe.get_book(file_name="your_file.ods") |
144 | 233 | >>> sheet |
145 | Sheet Name: Sheet 1 | |
234 | Sheet 1: | |
146 | 235 | +---+---+---+ |
147 | 236 | | 1 | 2 | 3 | |
148 | 237 | +---+---+---+ |
149 | 238 | | 4 | 5 | 6 | |
150 | 239 | +---+---+---+ |
151 | Sheet Name: Sheet 2 | |
240 | Sheet 2: | |
152 | 241 | +-------+-------+-------+ |
153 | 242 | | row 1 | row 2 | row 3 | |
154 | 243 | +-------+-------+-------+ |
155 | 244 | |
245 | ||
156 | 246 | Writing to an ods file |
157 | 247 | ******************************************************************************** |
158 | 248 | |
162 | 252 | |
163 | 253 | >>> sheet.save_as("another_file.ods") |
164 | 254 | |
255 | ||
165 | 256 | Reading from a IO instance |
166 | ================================================================================ | |
257 | ******************************************************************************** | |
167 | 258 | |
168 | 259 | You got to wrap the binary content with stream to get ods working: |
169 | 260 | |
178 | 269 | ... r = pe.get_book(file_type="ods", file_content=content) |
179 | 270 | ... print(r) |
180 | 271 | ... |
181 | Sheet Name: Sheet 1 | |
272 | Sheet 1: | |
182 | 273 | +---+---+---+ |
183 | 274 | | 1 | 2 | 3 | |
184 | 275 | +---+---+---+ |
185 | 276 | | 4 | 5 | 6 | |
186 | 277 | +---+---+---+ |
187 | Sheet Name: Sheet 2 | |
278 | Sheet 2: | |
188 | 279 | +-------+-------+-------+ |
189 | 280 | | row 1 | row 2 | row 3 | |
190 | 281 | +-------+-------+-------+ |
191 | 282 | |
192 | 283 | |
193 | 284 | Writing to a StringIO instance |
194 | ================================================================================ | |
285 | ******************************************************************************** | |
195 | 286 | |
196 | 287 | You need to pass a StringIO instance to Writer: |
197 | 288 | |
203 | 294 | ... ] |
204 | 295 | >>> io = StringIO() |
205 | 296 | >>> sheet = pe.Sheet(data) |
206 | >>> sheet.save_to_memory("ods", io) | |
297 | >>> io = sheet.save_to_memory("ods", io) | |
207 | 298 | >>> # then do something with io |
208 | 299 | >>> # In reality, you might give it to your http response |
209 | 300 | >>> # object for downloading |
210 | 301 | |
302 | ||
211 | 303 | License |
212 | 304 | ================================================================================ |
213 | 305 | |
214 | 306 | 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 | |
215 | 381 | |
216 | 382 | Credits |
217 | 383 | ================================================================================ |
223 | 389 | |
224 | 390 | >>> import os |
225 | 391 | >>> os.unlink("your_file.ods") |
226 | >>> os.unlink("another_file.ods")⏎ | |
392 | >>> os.unlink("another_file.ods") |
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)) |
0 | 0 | # -*- 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 -------------------------------------------------------------- | |
3 | 12 | |
4 | 13 | # If extensions (or modules to document with autodoc) are in another directory, |
5 | 14 | # add these directories to sys.path here. If the directory is relative to the |
6 | 15 | # 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('.')) | |
8 | 20 | |
9 | # -- General configuration ------------------------------------------------ | |
21 | # -- Project information ----------------------------------------------------- | |
10 | 22 | |
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 --------------------------------------------------- | |
13 | 32 | |
14 | 33 | # Add any Sphinx extension module names here, as strings. They can be |
15 | 34 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom |
16 | 35 | # 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',] | |
25 | 37 | |
26 | 38 | # Add any paths that contain templates here, relative to this directory. |
27 | 39 | templates_path = ['_templates'] |
28 | 40 | |
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 | ||
51 | 41 | # The language for content autogenerated by Sphinx. Refer to documentation |
52 | 42 | # 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' | |
60 | 47 | |
61 | 48 | # List of patterns, relative to source directory, that match files and |
62 | 49 | # directories to ignore when looking for source files. |
50 | # This pattern also affects html_static_path and html_extra_path. | |
63 | 51 | exclude_patterns = [] |
64 | 52 | |
65 | # The reST default role (used for this markup: `text`) to use for all | |
66 | # documents. | |
67 | #default_role = None | |
68 | 53 | |
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 ------------------------------------------------- | |
91 | 55 | |
92 | 56 | # The theme to use for HTML and HTML Help pages. See the documentation for |
93 | 57 | # 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' | |
118 | 60 | |
119 | 61 | # Add any paths that contain custom static files (such as style sheets) here, |
120 | 62 | # relative to this directory. They are copied after the builtin static files, |
121 | 63 | # so a file named "default.css" will overwrite the builtin "default.css". |
122 | 64 | html_static_path = ['_static'] |
123 | 65 | |
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 --------------------------------------- | |
128 | 68 | |
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' | |
132 | 73 | |
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'), | |
193 | 81 | ] |
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 | 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⏎ |
0 | 0 | """ |
1 | 1 | pyexcel_ods |
2 | 2 | ~~~~~~~~~~~~~~~~~~~ |
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 | """ | |
3 | 7 | |
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 | |
5 | 12 | |
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 | |
10 | 16 | |
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", | |
39 | 26 | ) |
40 | 27 | |
41 | 28 | |
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 | ||
328 | 29 | 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""" | |
332 | 31 | if isstream(afile) and file_type is None: |
333 | file_type = 'ods' | |
32 | file_type = __FILE_TYPE__ | |
334 | 33 | write_data(afile, data, file_type=file_type, **keywords) |
335 | 34 | |
336 | 35 | |
337 | 36 | 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""" | |
341 | 38 | if isstream(afile) and file_type is None: |
342 | file_type = 'ods' | |
39 | file_type = __FILE_TYPE__ | |
343 | 40 | 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_ods |
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 | |
0 | 17 | 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') | |
6 | 24 | |
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', | |
13 | 38 | ] |
14 | 39 | |
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 = {} | |
16 | 57 | |
17 | 58 | |
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 | nosetests --with-cov --with-doctest --doctest-extension=.rst -I applymoban.py -I setup.py |
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 | ||
1 | 3 | import pyexcel |
2 | from nose.tools import raises | |
3 | import datetime | |
4 | from nose.tools import eq_, raises # noqa | |
4 | 5 | |
5 | 6 | |
6 | 7 | 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] | |
8 | 9 | table = [] |
9 | 10 | table.append(data[:4]) |
10 | 11 | table.append(data[4:8]) |
16 | 17 | """ |
17 | 18 | Abstract functional test for hat writers |
18 | 19 | """ |
20 | ||
19 | 21 | 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], | |
23 | 25 | } |
24 | ||
26 | ||
25 | 27 | def test_series_table(self): |
26 | 28 | pyexcel.save_as(adict=self.content, dest_file_name=self.testfile) |
27 | 29 | 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 | ||
31 | 32 | |
32 | 33 | class PyexcelWriterBase: |
33 | 34 | """ |
36 | 37 | testfile and testfile2 have to be initialized before |
37 | 38 | it is used for testing |
38 | 39 | """ |
40 | ||
39 | 41 | 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], | |
44 | 46 | ] |
45 | 47 | |
46 | 48 | 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 | ||
49 | 51 | def test_write_array(self): |
50 | 52 | self._create_a_file(self.testfile) |
51 | 53 | r = pyexcel.get_sheet(file_name=self.testfile) |
52 | actual = pyexcel.utils.to_array(r.rows()) | |
54 | actual = list(r.rows()) | |
53 | 55 | assert actual == self.content |
54 | 56 | |
55 | 57 | |
56 | 58 | class PyexcelMultipleSheetBase: |
57 | ||
58 | 59 | def _write_test_file(self, filename): |
59 | 60 | pyexcel.save_book_as(bookdict=self.content, dest_file_name=filename) |
60 | 61 | |
65 | 66 | os.unlink(self.testfile) |
66 | 67 | |
67 | 68 | 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"] | |
70 | 71 | sheet_names = r.sheet_names() |
71 | 72 | for name in sheet_names: |
72 | 73 | assert name in expected |
73 | 74 | |
74 | 75 | def test_reading_through_sheets(self): |
75 | 76 | b = pyexcel.BookReader(self.testfile) |
76 | data = pyexcel.utils.to_array(b["Sheet1"].rows()) | |
77 | data = list(b["Sheet1"].rows()) | |
77 | 78 | expected = [[1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3]] |
78 | 79 | assert data == expected |
79 | data = pyexcel.utils.to_array(b["Sheet2"].rows()) | |
80 | data = list(b["Sheet2"].rows()) | |
80 | 81 | expected = [[4, 4, 4, 4], [5, 5, 5, 5], [6, 6, 6, 6]] |
81 | 82 | 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]] | |
84 | 85 | assert data == expected |
85 | 86 | sheet3 = b["Sheet3"] |
86 | 87 | sheet3.name_columns_by_row(0) |
87 | data = pyexcel.utils.to_array(b["Sheet3"].rows()) | |
88 | data = list(b["Sheet3"].rows()) | |
88 | 89 | expected = [[1, 4, 7], [2, 5, 8], [3, 6, 9]] |
89 | 90 | assert data == expected |
90 | 91 | |
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 | ||
110 | 92 | |
111 | 93 | class ODSCellTypes: |
112 | ||
113 | 94 | def test_formats(self): |
114 | 95 | # date formats |
115 | 96 | 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], "") | |
120 | 101 | # time formats |
121 | 102 | 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") | |
128 | 112 | # 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) | |
132 | 116 | # 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) | |
135 | 119 | # 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") | |
139 | 123 | # 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) | |
142 | 126 | # 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) | |
146 | 130 | # Scientifed not supported |
147 | assert self.data["Sheet1"][1][7] == 100000 | |
131 | eq_(self.data["Sheet1"][1][7], 100000) | |
148 | 132 | # Fraction |
149 | assert self.data["Sheet1"][1][8] == 1.25 | |
133 | eq_(self.data["Sheet1"][1][8], 1.25) | |
150 | 134 | # Text |
151 | assert self.data["Sheet1"][1][9] == "abc" | |
135 | eq_(self.data["Sheet1"][1][9], "abc") | |
152 | 136 | |
153 | 137 | @raises(IndexError) |
154 | 138 | def test_no_excessive_trailing_columns(self): |
155 | assert self.data["Sheet1"][2][6] == "" | |
139 | eq_(self.data["Sheet1"][2][6], "") |
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
0 | 0 | nose |
1 | rednose | |
2 | nose-cov | |
1 | mock;python_version<"3" | |
3 | 2 | codecov |
4 | 3 | coverage |
4 | flake8 | |
5 | moban | |
6 | black;python_version>="3.6" | |
7 | isort;python_version>="3.6" | |
8 | psutil | |
5 | 9 | pyexcel |
6 | 10 | pyexcel-xls |
0 | 0 | #!/usr/bin/python |
1 | 1 | # -*- encoding: utf-8 -*- |
2 | 2 | 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 | ||
3 | 10 | from pyexcel_ods import get_data, save_data |
4 | from nose.tools import raises | |
11 | ||
12 | IN_TRAVIS = "TRAVIS" in os.environ | |
5 | 13 | |
6 | 14 | |
7 | 15 | 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 | ||
11 | 20 | def test_bug_fix_for_issue_2(): |
12 | 21 | data = {} |
13 | 22 | data.update({"Sheet 1": [[1, 2, 3], [4, 5, 6]]}) |
14 | 23 | data.update({"Sheet 2": [[u"row 1", u"Héllô!", u"HolÁ!"]]}) |
15 | 24 | save_data("your_file.ods", data) |
16 | 25 | 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!"]] | |
18 | 27 | |
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 | ||
37 | 29 | @raises(Exception) |
38 | 30 | def test_invalid_date(): |
39 | from pyexcel_ods import date_value | |
31 | from pyexcel_ods.ods import date_value | |
32 | ||
40 | 33 | value = "2015-08-" |
41 | 34 | date_value(value) |
42 | 35 | |
36 | ||
43 | 37 | @raises(Exception) |
44 | 38 | def test_fake_date_time_10(): |
45 | from pyexcel_ods import date_value | |
39 | from pyexcel_ods.ods import date_value | |
40 | ||
46 | 41 | date_value("1234567890") |
42 | ||
47 | 43 | |
48 | 44 | @raises(Exception) |
49 | 45 | def test_fake_date_time_19(): |
50 | from pyexcel_ods import date_value | |
46 | from pyexcel_ods.ods import date_value | |
47 | ||
51 | 48 | date_value("1234567890123456789") |
49 | ||
52 | 50 | |
53 | 51 | @raises(Exception) |
54 | 52 | def test_fake_date_time_20(): |
55 | from pyexcel_ods import date_value | |
53 | from pyexcel_ods.ods import date_value | |
54 | ||
56 | 55 | 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) |
0 | 0 | import os |
1 | import pyexcel,pyexcel.ext.ods | |
1 | ||
2 | import pyexcel | |
2 | 3 | |
3 | 4 | |
4 | 5 | def test_reading_multiline_ods(): |
5 | 6 | testfile = os.path.join("tests", "fixtures", "multilineods.ods") |
6 | 7 | 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" | |
8 | 10 | |
9 | 11 | |
10 | 12 | def test_writing_multiline_ods(): |
11 | content = "2\n3\n4\n993939\na" | |
13 | content = "2\n3\n4\n993939\n\na" | |
12 | 14 | testfile = "writemultiline.ods" |
13 | array = [[content,"test"]] | |
15 | array = [[content, "test"]] | |
14 | 16 | pyexcel.save_as(array=array, dest_file_name=testfile) |
15 | 17 | sheet = pyexcel.get_sheet(file_name=testfile) |
16 | assert sheet[0,0] == content | |
18 | assert sheet[0, 0] == content | |
17 | 19 | 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 | 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 |
0 | 0 | import os |
1 | from pyexcel.ext import ods | |
1 | ||
2 | 2 | from base import ODSCellTypes |
3 | from pyexcel_ods.odsr import ODSBook | |
4 | from pyexcel_ods.odsw import ODSWriter | |
3 | 5 | |
4 | 6 | |
5 | 7 | class TestODSReader(ODSCellTypes): |
6 | 8 | 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() | |
11 | 12 | for key in self.data.keys(): |
12 | 13 | self.data[key] = list(self.data[key]) |
14 | r.close() | |
13 | 15 | |
14 | 16 | |
15 | 17 | class TestODSWriter(ODSCellTypes): |
16 | 18 | 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() | |
21 | 22 | self.testfile = "odswriter.ods" |
22 | w = ods.ODSWriter(self.testfile) | |
23 | w = ODSWriter() | |
24 | w.open(self.testfile) | |
23 | 25 | w.write(self.data1) |
24 | 26 | 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() | |
27 | 30 | for key in self.data.keys(): |
28 | 31 | self.data[key] = list(self.data[key]) |
29 | 32 |
0 | 0 | import os |
1 | ||
1 | 2 | import pyexcel |
2 | from pyexcel.ext import ods | |
3 | from StringIO import StringIO | |
3 | from nose.tools import eq_ | |
4 | ||
4 | 5 | from base import create_sample_file1 |
5 | 6 | |
6 | 7 | |
7 | 8 | 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) | |
8 | 22 | |
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) |
0 | 0 | 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 | |
3 | 5 | |
4 | 6 | |
5 | 7 | class TestNativeODSWriter: |
7 | 9 | self.content = { |
8 | 10 | "Sheet1": [[1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3]], |
9 | 11 | "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]], | |
11 | 13 | } |
12 | self.testfile = "odswriter.ods" | |
13 | writer = ods.ODSWriter(self.testfile) | |
14 | self.testfile = "writer.ods" | |
15 | writer = Writer() | |
16 | writer.open(self.testfile) | |
14 | 17 | writer.write(self.content) |
15 | 18 | 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() | |
18 | 22 | for key in content.keys(): |
19 | 23 | content[key] = list(content[key]) |
20 | 24 | assert content == self.content |
25 | reader.close() | |
21 | 26 | |
22 | 27 | def tearDown(self): |
23 | 28 | if os.path.exists(self.testfile): |
24 | 29 | os.unlink(self.testfile) |
25 | 30 | |
26 | 31 | |
27 | class TestODSnCSVWriter(PyexcelWriterBase): | |
32 | class TestodsnCSVWriter(PyexcelWriterBase): | |
28 | 33 | def setUp(self): |
29 | self.testfile="testods.ods" | |
30 | self.testfile2="test.csv" | |
34 | self.testfile = "test.ods" | |
35 | self.testfile2 = "test.csv" | |
31 | 36 | |
32 | 37 | def tearDown(self): |
33 | 38 | if os.path.exists(self.testfile): |
36 | 41 | os.unlink(self.testfile2) |
37 | 42 | |
38 | 43 | |
39 | class TestODSHatWriter(PyexcelHatWriterBase): | |
44 | class TestodsHatWriter(PyexcelHatWriterBase): | |
40 | 45 | def setUp(self): |
41 | self.testfile="testhat.ods" | |
46 | self.testfile = "test.ods" | |
42 | 47 | |
43 | 48 | def tearDown(self): |
44 | 49 | if os.path.exists(self.testfile): |
45 | 50 | os.unlink(self.testfile) |
46 | ||
47 |