Codebase list pyexcel / cde6c00
Import upstream version 0.6.6 Kali Janitor 3 years ago
156 changed file(s) with 3901 addition(s) and 1534 deletion(s). Raw diff Collapse all Expand all
00 # These are supported funding model platforms
11
2 patreon: pyexcel
2 github: chfw
3 patreon: chfw
0 With your PR, here is a check list:
1
2 - [ ] Has test cases written?
3 - [ ] Has all code lines tested?
4 - [ ] Has `make format` been run?
5 - [ ] Please update CHANGELOG.yml(not CHANGELOG.rst)
6 - [ ] Passes all Travis CI builds
7 - [ ] Has fair amount of documentation if your change is complex
8 - [ ] Agree on NEW BSD License for your contribution
0 on: [push]
1
2 jobs:
3 run_moban:
4 runs-on: ubuntu-latest
5 name: synchronize templates via moban
6 steps:
7 - uses: actions/checkout@v2
8 with:
9 ref: ${{ github.head_ref }}
10 - name: Set up Python
11 uses: actions/setup-python@v1
12 with:
13 python-version: '3.7'
14 - name: check changes
15 run: |
16 pip install moban gitfs2 pypifs moban-jinja2-github moban-ansible
17 moban
18 git status
19 git diff --exit-code
20 - name: Auto-commit
21 if: failure()
22 uses: docker://cdssnc/auto-commit-github-action
23 env:
24 GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
25 with:
26 args: >-
27 This is an auto-commit, updating project meta data,
28 such as changelog.rst, contributors.rst
0 name: Upload Python Package
1
2 on:
3 release:
4 types: [created]
5
6 jobs:
7 deploy:
8 runs-on: ubuntu-latest
9 steps:
10 - uses: actions/checkout@v1
11 - name: Set up Python
12 uses: actions/setup-python@v1
13 with:
14 python-version: '3.x'
15 - name: Install dependencies
16 run: |
17 python -m pip install --upgrade pip
18 pip install setuptools wheel twine
19 - name: Build and publish
20 env:
21 TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
22 TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
23 run: |
24 python setup.py sdist bdist_wheel
25 twine upload dist/*
2424 sdist/
2525 var/
2626 wheels/
27 pip-wheel-metadata/
2827 share/python-wheels/
2928 *.egg-info/
3029 .installed.cfg
5150 nosetests.xml
5251 coverage.xml
5352 *.cover
53 *.py,cover
5454 .hypothesis/
5555 .pytest_cache/
56 cover/
5657
5758 # Translations
5859 *.mo
6263 *.log
6364 local_settings.py
6465 db.sqlite3
66 db.sqlite3-journal
6567
6668 # Flask stuff:
6769 instance/
7476 docs/_build/
7577
7678 # PyBuilder
79 .pybuilder/
7780 target/
7881
7982 # Jupyter Notebook
8487 ipython_config.py
8588
8689 # pyenv
87 .python-version
90 # For a library or package, you might want to ignore these files since the code is
91 # intended to run in multiple environments; otherwise, check them in:
92 # .python-version
8893
8994 # pipenv
9095 # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
9196 # However, in case of collaboration, if having platform-specific dependencies or dependencies
92 # having no cross-platform support, pipenv may install dependencies that don’t work, or not
97 # having no cross-platform support, pipenv may install dependencies that don't work, or not
9398 # install all needed dependencies.
9499 #Pipfile.lock
95100
96 # celery beat schedule file
101 # PEP 582; used by e.g. github.com/David-OConnor/pyflow
102 __pypackages__/
103
104 # Celery stuff
97105 celerybeat-schedule
106 celerybeat.pid
98107
99108 # SageMath parsed files
100109 *.sage.py
125134
126135 # Pyre type checker
127136 .pyre/
137
138 # pytype static type analyzer
139 .pytype/
140
141 # Cython debug symbols
142 cython_debug/
128143
129144 # VirtualEnv rules
130145 # Virtualenv
158173 # Windows rules
159174 # Windows thumbnail cache files
160175 Thumbs.db
176 Thumbs.db:encryptable
161177 ehthumbs.db
162178 ehthumbs_vista.db
163179
263279 # Vim rules
264280 # Swap
265281 [._]*.s[a-v][a-z]
282 !*.svg # comment out if you don't need vector files
266283 [._]*.sw[a-p]
267284 [._]s[a-rt-v][a-z]
268285 [._]ss[a-gi-z]
270287
271288 # Session
272289 Session.vim
290 Sessionx.vim
273291
274292 # Temporary
275293 .netrwhist
280298 [._]*.un~
281299
282300 # JetBrains rules
283 # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
301 # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
284302 # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
285303
286304 # User-specific stuff
310328 # When using Gradle or Maven with auto-import, you should exclude module files,
311329 # since they will be recreated, and may cause churn. Uncomment if using
312330 # auto-import.
331 # .idea/artifacts
332 # .idea/compiler.xml
333 # .idea/jarRepositories.xml
313334 # .idea/modules.xml
314335 # .idea/*.iml
315336 # .idea/modules
337 # *.iml
338 # *.ipr
316339
317340 # CMake
318341 cmake-build-*/
362385
363386 # SFTP configuration file
364387 sftp-config.json
388 sftp-config-alt*.json
365389
366390 # Package control specific files
367391 Package Control.last-run
399423 !.vscode/tasks.json
400424 !.vscode/launch.json
401425 !.vscode/extensions.json
426 *.code-workspace
427
428 # Local History for Visual Studio Code
429 .history/
402430
403431 # Xcode rules
404432 # Xcode
425453 *.perspectivev3
426454 !default.perspectivev3
427455
456 ## Gcc Patch
457 /*.gcno
458
428459 # Eclipse rules
429460 .metadata
430461 bin/
476507
477508 # Annotation Processing
478509 .apt_generated/
510 .apt_generated_test/
479511
480512 # Scala IDE specific (Scala & Java development for Eclipse)
481513 .cache-main
482514 .scala_dependencies
483515 .worksheet
516
517 # Uncomment this line if you wish to ignore the project description file.
518 # Typically, this file would be tracked if it contains build/dependency configurations:
519 #.project
484520
485521 # TortoiseGit rules
486522 # Project-level settings
505541 cscope.po.out
506542
507543
544 # remove moban hash dictionary
545 .moban.hashes
546
508547 docs/build/
509548 commons
0 [settings]
1 line_length=79
2 known_first_party=
3 known_third_party=mock,nose,pyexcel_io
4 indent=' '
5 multi_line_output=3
6 length_sort=1
7 default_section=FIRSTPARTY
8 no_lines_before=LOCALFOLDER
9 sections=FUTURE,STDLIB,FIRSTPARTY,THIRDPARTY,LOCALFOLDER
+0
-160
.moban.d/README.rst less more
0 {%extends "BASIC-README.rst.jj2"%}
1
2 {%block features%}
3
4 Feature Highlights
5 ===================
6
7 1. One application programming interface(API) to handle multiple data sources:
8
9 * physical file
10 * memory file
11 * SQLAlchemy table
12 * Django Model
13 * Python data structures: dictionary, records and array
14 2. One API to read and write data in various excel file formats.
15 3. For large data sets, data streaming are supported. A genenerator can be returned to you. Checkout iget_records, iget_array, isave_as and isave_book_as.
16
17 {% endblock %}
18
19 {%block usage%}
20
21 Usage
22 ===============
23
24 Please note that you will have to use '.sortable.html' in order to replicate the example.
25
26 .. image:: https://github.com/pyexcel/pyexcel-sortable/raw/master/sortable.gif
27
28 .. code-block:: python
29
30 >>> # pip install pyexcel-text==0.2.7.1
31 >>> import pyexcel as p
32 >>> ccs_insight2 = p.Sheet()
33 >>> ccs_insight2.name = "Worldwide Mobile Phone Shipments (Billions), 2017-2021"
34 >>> ccs_insight2.ndjson = """
35 ... {"year": ["2017", "2018", "2019", "2020", "2021"]}
36 ... {"smart phones": [1.53, 1.64, 1.74, 1.82, 1.90]}
37 ... {"feature phones": [0.46, 0.38, 0.30, 0.23, 0.17]}
38 ... """.strip()
39 >>> ccs_insight2
40 pyexcel sheet:
41 +----------------+------+------+------+------+------+
42 | year | 2017 | 2018 | 2019 | 2020 | 2021 |
43 +----------------+------+------+------+------+------+
44 | smart phones | 1.53 | 1.64 | 1.74 | 1.82 | 1.9 |
45 +----------------+------+------+------+------+------+
46 | feature phones | 0.46 | 0.38 | 0.3 | 0.23 | 0.17 |
47 +----------------+------+------+------+------+------+
48
49
50
51 Suppose you have the following data in a dictionary:
52
53 ========= ====
54 Name Age
55 ========= ====
56 Adam 28
57 Beatrice 29
58 Ceri 30
59 Dean 26
60 ========= ====
61
62 you can easily save it into an excel file using the following code:
63
64 .. code-block:: python
65
66 >>> import pyexcel
67 >>> # make sure you had pyexcel-xls installed
68 >>> a_list_of_dictionaries = [
69 ... {
70 ... "Name": 'Adam',
71 ... "Age": 28
72 ... },
73 ... {
74 ... "Name": 'Beatrice',
75 ... "Age": 29
76 ... },
77 ... {
78 ... "Name": 'Ceri',
79 ... "Age": 30
80 ... },
81 ... {
82 ... "Name": 'Dean',
83 ... "Age": 26
84 ... }
85 ... ]
86 >>> pyexcel.save_as(records=a_list_of_dictionaries, dest_file_name="your_file.xls")
87
88
89 And here's how to obtain the records:
90
91 .. code-block:: python
92
93 >>> import pyexcel as p
94 >>> records = p.iget_records(file_name="your_file.xls")
95 >>> for record in records:
96 ... print("%s is aged at %d" % (record['Name'], record['Age']))
97 Adam is aged at 28
98 Beatrice is aged at 29
99 Ceri is aged at 30
100 Dean is aged at 26
101 >>> p.free_resources()
102
103
104 Advanced usage :fire:
105 ----------------------
106
107 If you are dealing with big data, please consider these usages:
108
109 >>> def increase_everyones_age(generator):
110 ... for row in generator:
111 ... row['Age'] += 1
112 ... yield row
113 >>> def duplicate_each_record(generator):
114 ... for row in generator:
115 ... yield row
116 ... yield row
117 >>> records = p.iget_records(file_name="your_file.xls")
118 >>> io=p.isave_as(records=duplicate_each_record(increase_everyones_age(records)),
119 ... dest_file_type='csv', dest_lineterminator='\n')
120 >>> print(io.getvalue())
121 Age,Name
122 29,Adam
123 29,Adam
124 30,Beatrice
125 30,Beatrice
126 31,Ceri
127 31,Ceri
128 27,Dean
129 27,Dean
130 <BLANKLINE>
131
132 Two advantages of above method:
133
134 #. Add as many wrapping functions as you want.
135 #. Constant memory consumption
136
137 Available Plugins
138 =================
139
140 {% include "plugins-list.rst.jj2"%}
141
142
143 Acknowledgement
144 ===============
145
146 All great work have been done by odf, ezodf, xlrd, xlwt, tabulate and other
147 individual developers. This library unites only the data access code.
148
149
150 .. testcode::
151 :hide:
152
153 >>> import os
154 >>> os.unlink("your_file.xls")
155
156 {%endblock%}
157
158 {%block development_guide%}
159 {%endblock%}
0 {% extends 'setup.py.jj2' %}
1
2 {% block additional_keywords %}
3 'tsv',
4 'tsvz'
5 'csv',
6 'csvz',
7 'xls',
8 'xlsx',
9 'ods'
10 {% endblock %}
11
12 {% block additional_classifiers %}
13 'Development Status :: 3 - Alpha',
14 {% endblock %}}
15
16 {%- block morefiles %}
17 "CONTRIBUTORS.rst",
18 {%- endblock %}
0 ================================================================================
1 Read partial data
2 ================================================================================
3
4
5 {% include "partial-data.rst.jj2" %}⏎
0 {% include 'docs/source/conf.py' %}
0 {% extends 'docs/source/conf.py.jj2'%}
11
2 master_doc = "index"⏎
2 {%block SPHINX_EXTENSIONS%}
3 'sphinx.ext.autosummary',
4 'sphinx.ext.napoleon',
5 'sphinxcontrib.excel'
6 {%endblock%}
7
8 {%block custom_doc_theme%}
9 html_theme = 'default'
10 def setup(app):
11 app.add_stylesheet('theme_overrides.css')
12
13
14 {%endblock%}
15
16 {%block additional_mapping%}
17 'xlrd': ('http://xlrd.readthedocs.io/en/latest/', None)
18 {%endblock%}
+0
-6
.moban.d/docs/source/guide.rst less more
0 Developer's guide
1 =================
2
3 {%include "developer_guide.rst.jj2" %}
4
5
0 Developer's guide
1 =================
2
3 {%include "developer_guide.rst.jj2" %}
4
5
2222
2323 {%include "installation.rst.jj2" %}
2424
25 Suppose you have the following data in a dictionary:
26
27 ========= ====
28 Name Age
29 ========= ====
30 Adam 28
31 Beatrice 29
32 Ceri 30
33 Dean 26
34 ========= ====
35
36 you can easily save it into an excel file using the following code:
37
38 .. code-block:: python
39
40 >>> import pyexcel
41 >>> # make sure you had pyexcel-xls installed
42 >>> a_list_of_dictionaries = [
43 ... {
44 ... "Name": 'Adam',
45 ... "Age": 28
46 ... },
47 ... {
48 ... "Name": 'Beatrice',
49 ... "Age": 29
50 ... },
51 ... {
52 ... "Name": 'Ceri',
53 ... "Age": 30
54 ... },
55 ... {
56 ... "Name": 'Dean',
57 ... "Age": 26
58 ... }
59 ... ]
60 >>> pyexcel.save_as(records=a_list_of_dictionaries, dest_file_name="your_file.xls")
61
62 And here's how to obtain the records:
63
64 .. code-block:: python
65
66 >>> import pyexcel as p
67 >>> records = p.iget_records(file_name="your_file.xls")
68 >>> for record in records:
69 ... print("%s is aged at %d" % (record['Name'], record['Age']))
70 Adam is aged at 28
71 Beatrice is aged at 29
72 Ceri is aged at 30
73 Dean is aged at 26
74 >>> p.free_resources()
75
76
77 Custom data rendering:
78
79 .. code-block:: python
80
81 >>> # pip install pyexcel-text==0.2.7.1
82 >>> import pyexcel as p
83 >>> ccs_insight2 = p.Sheet()
84 >>> ccs_insight2.name = "Worldwide Mobile Phone Shipments (Billions), 2017-2021"
85 >>> ccs_insight2.ndjson = """
86 ... {"year": ["2017", "2018", "2019", "2020", "2021"]}
87 ... {"smart phones": [1.53, 1.64, 1.74, 1.82, 1.90]}
88 ... {"feature phones": [0.46, 0.38, 0.30, 0.23, 0.17]}
89 ... """.strip()
90 >>> ccs_insight2
91 pyexcel sheet:
92 +----------------+------+------+------+------+------+
93 | year | 2017 | 2018 | 2019 | 2020 | 2021 |
94 +----------------+------+------+------+------+------+
95 | smart phones | 1.53 | 1.64 | 1.74 | 1.82 | 1.9 |
96 +----------------+------+------+------+------+------+
97 | feature phones | 0.46 | 0.38 | 0.3 | 0.23 | 0.17 |
98 +----------------+------+------+------+------+------+
99
100
101 Advanced usage :fire:
102 ----------------------
103
104 If you are dealing with big data, please consider these usages:
105
106 .. code-block:: python
107
108 >>> def increase_everyones_age(generator):
109 ... for row in generator:
110 ... row['Age'] += 1
111 ... yield row
112 >>> def duplicate_each_record(generator):
113 ... for row in generator:
114 ... yield row
115 ... yield row
116 >>> records = p.iget_records(file_name="your_file.xls")
117 >>> io=p.isave_as(records=duplicate_each_record(increase_everyones_age(records)),
118 ... dest_file_type='csv', dest_lineterminator='\n')
119 >>> print(io.getvalue())
120 Age,Name
121 29,Adam
122 29,Adam
123 30,Beatrice
124 30,Beatrice
125 31,Ceri
126 31,Ceri
127 27,Dean
128 27,Dean
129 <BLANKLINE>
130
131
132 Two advantages of above method:
133
134 #. Add as many wrapping functions as you want.
135 #. Constant memory consumption
136
137
138 .. testcode::
139 :hide:
140
141 >>> import os
142 >>> os.unlink("your_file.xls")
143
144
145
25146 For individual excel file formats, please install them as you wish:
26147
27148 {%include "plugins-list.rst.jj2"%}
33154 ======== ========== ============= ==================== ============= =============
34155 pyexcel pyexcel-io pyexcel-text pyexcel-handsontable pyexcel-pygal pyexcel-gantt
35156 ======== ========== ============= ==================== ============= =============
36 0.5.14+ 0.5.18+ 0.2.6+ 0.0.1+ 0.0.1 0.0.1
157 0.6.5+ 0.6.2+ 0.2.6+ 0.0.1+ 0.0.1 0.0.1
158 0.5.15+ 0.5.19+ 0.2.6+ 0.0.1+ 0.0.1 0.0.1
159 0.5.14 0.5.18 0.2.6+ 0.0.1+ 0.0.1 0.0.1
37160 0.5.10+ 0.5.11+ 0.2.6+ 0.0.1+ 0.0.1 0.0.1
38161 0.5.9.1+ 0.5.9.1+ 0.2.6+ 0.0.1 0.0.1 0.0.1
39162 0.5.4+ 0.5.1+ 0.2.6+ 0.0.1 0.0.1 0.0.1
140263 architecture
141264
142265 New tutorial
143 ----------
266 --------------
144267 .. toctree::
145268
146269 quickstart
153276 database
154277
155278 Old tutorial
156 ----------
279 --------------
157280 .. toctree::
158281
159282 tutorial_file
0 {% set sphinx=True %}
1
2 {% include "one-liners.rst.jj2" %}⏎
0 {% set sphinx=True %}
1
2 {% include "two-liners.rst.jj2" %}⏎
+0
-3
.moban.d/minimum_requirements.txt less more
0 {%include "min_requirements.txt.jj2" %}
1 pyexcel-xlsx==0.4.1
2 pyexcel-xls==0.4.1
0 {%include "min_requirements.txt.jj2" %}
1 pyexcel-xlsx==0.4.1
2 pyexcel-xls==0.4.1
0 One liners
1 ================================================================================
2
3 This section shows you how to get data from your excel files and how to
4 export data to excel files in **one line**
5
6 Read from the excel files
7 --------------------------------------------------------------------------------
8
9 Get a list of dictionaries
10 ********************************************************************************
11
12 {% if sphinx %}
13 .. testcode::
14 :hide:
15
16 >>> import os
17 >>> import pyexcel as p
18 >>> content="""
19 ... Coffees,Serving Size,Caffeine (mg)
20 ... Starbucks Coffee Blonde Roast,venti(20 oz),475
21 ... Dunkin' Donuts Coffee with Turbo Shot,large(20 oz.),398
22 ... Starbucks Coffee Pike Place Roast,grande(16 oz.),310
23 ... Panera Coffee Light Roast,regular(16 oz.),300
24 ... """.strip()
25 >>> sheet = p.get_sheet(file_content=content, file_type='csv')
26 >>> sheet.save_as("your_file.xls")
27
28 {% endif %}
29
30 Suppose you want to process the following coffee data (data source `coffee chart <https://cspinet.org/eating-healthy/ingredients-of-concern/caffeine-chart>`_ on the center for science in the public interest):
31
32 {% if sphinx %}
33
34 .. pyexcel-table::
35
36 ---pyexcel:Top 5 coffeine drinks---
37 Coffees,Serving Size,Caffeine (mg)
38 Starbucks Coffee Blonde Roast,venti(20 oz),475
39 Dunkin' Donuts Coffee with Turbo Shot,large(20 oz.),398
40 Starbucks Coffee Pike Place Roast,grande(16 oz.),310
41 Panera Coffee Light Roast,regular(16 oz.),300
42
43 {% else %}
44
45 Top 5 coffeine drinks:
46
47 ===================================== =============== =============
48 Coffees Serving Size Caffeine (mg)
49 Starbucks Coffee Blonde Roast venti(20 oz) 475
50 Dunkin' Donuts Coffee with Turbo Shot large(20 oz.) 398
51 Starbucks Coffee Pike Place Roast grande(16 oz.) 310
52 Panera Coffee Light Roast regular(16 oz.) 300
53 ===================================== =============== =============
54
55 {% endif %}
56
57 Let's get a list of dictionary out from the xls file:
58
59 .. code-block:: python
60
61 >>> records = p.get_records(file_name="your_file.xls")
62
63 And let's check what do we have:
64
65 .. code-block:: python
66
67 >>> for r in records:
68 ... print(f"{r['Serving Size']} of {r['Coffees']} has {r['Caffeine (mg)']} mg")
69 venti(20 oz) of Starbucks Coffee Blonde Roast has 475 mg
70 large(20 oz.) of Dunkin' Donuts Coffee with Turbo Shot has 398 mg
71 grande(16 oz.) of Starbucks Coffee Pike Place Roast has 310 mg
72 regular(16 oz.) of Panera Coffee Light Roast has 300 mg
73
74
75 Get two dimensional array
76 ********************************************************************************
77
78 Instead, what if you have to use `pyexcel.get_array` to do the same:
79
80 .. code-block:: python
81
82 >>> for row in p.get_array(file_name="your_file.xls", start_row=1):
83 ... print(f"{row[1]} of {row[0]} has {row[2]} mg")
84 venti(20 oz) of Starbucks Coffee Blonde Roast has 475 mg
85 large(20 oz.) of Dunkin' Donuts Coffee with Turbo Shot has 398 mg
86 grande(16 oz.) of Starbucks Coffee Pike Place Roast has 310 mg
87 regular(16 oz.) of Panera Coffee Light Roast has 300 mg
88
89
90 where `start_row` skips the header row.
91
92
93 Get a dictionary
94 ********************************************************************************
95
96 You can get a dictionary too:
97
98 Now let's get a dictionary out from the spreadsheet:
99
100 .. code-block:: python
101
102 >>> my_dict = p.get_dict(file_name="your_file.xls", name_columns_by_row=0)
103
104 And check what do we have:
105
106 .. code-block:: python
107
108 >>> from pyexcel._compact import OrderedDict
109 >>> isinstance(my_dict, OrderedDict)
110 True
111 >>> for key, values in my_dict.items():
112 ... print(key + " : " + ','.join([str(item) for item in values]))
113 Coffees : Starbucks Coffee Blonde Roast,Dunkin' Donuts Coffee with Turbo Shot,Starbucks Coffee Pike Place Roast,Panera Coffee Light Roast
114 Serving Size : venti(20 oz),large(20 oz.),grande(16 oz.),regular(16 oz.)
115 Caffeine (mg) : 475,398,310,300
116
117 Please note that my_dict is an OrderedDict.
118
119 Get a dictionary of two dimensional array
120 ********************************************************************************
121
122 {% if sphinx %}
123 .. testcode::
124 :hide:
125
126 >>> a_dictionary_of_two_dimensional_arrays = {
127 ... 'Sheet 1':
128 ... [
129 ... [1.0, 2.0, 3.0],
130 ... [4.0, 5.0, 6.0],
131 ... [7.0, 8.0, 9.0]
132 ... ],
133 ... 'Sheet 2':
134 ... [
135 ... ['X', 'Y', 'Z'],
136 ... [1.0, 2.0, 3.0],
137 ... [4.0, 5.0, 6.0]
138 ... ],
139 ... 'Sheet 3':
140 ... [
141 ... ['O', 'P', 'Q'],
142 ... [3.0, 2.0, 1.0],
143 ... [4.0, 3.0, 2.0]
144 ... ]
145 ... }
146 >>> data = OrderedDict()
147 >>> data.update({"Sheet 1": a_dictionary_of_two_dimensional_arrays['Sheet 1']})
148 >>> data.update({"Sheet 2": a_dictionary_of_two_dimensional_arrays['Sheet 2']})
149 >>> data.update({"Sheet 3": a_dictionary_of_two_dimensional_arrays['Sheet 3']})
150 >>> p.save_book_as(bookdict=data, dest_file_name="book.xls")
151
152 {% endif %}
153
154 Suppose you have a multiple sheet book as the following:
155
156 {% if sphinx %}
157
158 .. pyexcel-table::
159
160 ---pyexcel:Sheet 1---
161 1,2,3
162 4,5,6
163 7,8,9
164 ---pyexcel---
165 ---pyexcel:Sheet 2---
166 X,Y,Z
167 1,2,3
168 4,5,6
169 ---pyexcel---
170 ---pyexcel:Sheet 3---
171 O,P,Q
172 3,2,1
173 4,3,2
174
175 {% else %}
176
177 pyexcel:Sheet 1:
178
179 ===================== = =
180 1 2 3
181 4 5 6
182 7 8 9
183 ===================== = =
184
185 pyexcel:Sheet 2:
186
187 ===================== = =
188 X Y Z
189 1 2 3
190 4 5 6
191 ===================== = =
192
193 pyexcel:Sheet 3:
194
195 ===================== = =
196 O P Q
197 3 2 1
198 4 3 2
199 ===================== = =
200
201 {% endif %}
202
203 Here is the code to obtain those sheets as a single dictionary:
204
205 .. code-block:: python
206
207 >>> book_dict = p.get_book_dict(file_name="book.xls")
208
209 And check:
210
211 .. code-block:: python
212
213 >>> isinstance(book_dict, OrderedDict)
214 True
215 >>> import json
216 >>> for key, item in book_dict.items():
217 ... print(json.dumps({key: item}))
218 {"Sheet 1": [[1, 2, 3], [4, 5, 6], [7, 8, 9]]}
219 {"Sheet 2": [["X", "Y", "Z"], [1, 2, 3], [4, 5, 6]]}
220 {"Sheet 3": [["O", "P", "Q"], [3, 2, 1], [4, 3, 2]]}
221
222 {% if sphinx %}
223
224 .. testcode::
225 :hide:
226
227 >>> import os
228 >>> os.unlink("book.xls")
229
230 {% endif %}
231
232 Write data
233 ---------------------------------------------
234
235 Export an array
236 **********************
237
238 Suppose you have the following array:
239
240 .. code-block:: python
241
242 >>> data = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
243
244 And here is the code to save it as an excel file :
245
246 .. code-block:: python
247
248 >>> p.save_as(array=data, dest_file_name="example.xls")
249
250 Let's verify it:
251
252 .. code-block:: python
253
254 >>> p.get_sheet(file_name="example.xls")
255 pyexcel_sheet1:
256 +---+---+---+
257 | 1 | 2 | 3 |
258 +---+---+---+
259 | 4 | 5 | 6 |
260 +---+---+---+
261 | 7 | 8 | 9 |
262 +---+---+---+
263
264 {% if sphinx %}
265 .. testcode::
266 :hide:
267
268 >>> import os
269 >>> os.unlink("example.xls")
270 {% endif %}
271
272 And here is the code to save it as a csv file :
273
274 .. code-block:: python
275
276 >>> p.save_as(array=data,
277 ... dest_file_name="example.csv",
278 ... dest_delimiter=':')
279
280 Let's verify it:
281
282 .. code-block:: python
283
284 >>> with open("example.csv") as f:
285 ... for line in f.readlines():
286 ... print(line.rstrip())
287 ...
288 1:2:3
289 4:5:6
290 7:8:9
291
292 Export a list of dictionaries
293 **********************************
294
295 .. code-block:: python
296
297 >>> records = [
298 ... {"year": 1903, "country": "Germany", "speed": "206.7km/h"},
299 ... {"year": 1964, "country": "Japan", "speed": "210km/h"},
300 ... {"year": 2008, "country": "China", "speed": "350km/h"}
301 ... ]
302 >>> p.save_as(records=records, dest_file_name='high_speed_rail.xls')
303
304
305 Export a dictionary of single key value pair
306 ********************************************************************************
307
308 .. code-block:: python
309
310 >>> henley_on_thames_facts = {
311 ... "area": "5.58 square meters",
312 ... "population": "11,619",
313 ... "civial parish": "Henley-on-Thames",
314 ... "latitude": "51.536",
315 ... "longitude": "-0.898"
316 ... }
317 >>> p.save_as(adict=henley_on_thames_facts, dest_file_name='henley.xlsx')
318
319
320 Export a dictionary of single dimensonal array
321 ********************************************************************************
322
323 .. code-block:: python
324
325 >>> ccs_insights = {
326 ... "year": ["2017", "2018", "2019", "2020", "2021"],
327 ... "smart phones": [1.53, 1.64, 1.74, 1.82, 1.90],
328 ... "feature phones": [0.46, 0.38, 0.30, 0.23, 0.17]
329 ... }
330 >>> p.save_as(adict=ccs_insights, dest_file_name='ccs.csv')
331
332
333 Export a dictionary of two dimensional array as a book
334 ********************************************************************************
335
336 Suppose you want to save the below dictionary to an excel file :
337
338 .. code-block:: python
339
340 >>> a_dictionary_of_two_dimensional_arrays = {
341 ... 'Sheet 1':
342 ... [
343 ... [1.0, 2.0, 3.0],
344 ... [4.0, 5.0, 6.0],
345 ... [7.0, 8.0, 9.0]
346 ... ],
347 ... 'Sheet 2':
348 ... [
349 ... ['X', 'Y', 'Z'],
350 ... [1.0, 2.0, 3.0],
351 ... [4.0, 5.0, 6.0]
352 ... ],
353 ... 'Sheet 3':
354 ... [
355 ... ['O', 'P', 'Q'],
356 ... [3.0, 2.0, 1.0],
357 ... [4.0, 3.0, 2.0]
358 ... ]
359 ... }
360
361 Here is the code:
362
363 .. code-block:: python
364
365 >>> p.save_book_as(
366 ... bookdict=a_dictionary_of_two_dimensional_arrays,
367 ... dest_file_name="book.xls"
368 ... )
369
370 If you want to preserve the order of sheets in your dictionary, you have to
371 pass on an ordered dictionary to the function itself. For example:
372
373 .. code-block:: python
374
375 >>> data = OrderedDict()
376 >>> data.update({"Sheet 2": a_dictionary_of_two_dimensional_arrays['Sheet 2']})
377 >>> data.update({"Sheet 1": a_dictionary_of_two_dimensional_arrays['Sheet 1']})
378 >>> data.update({"Sheet 3": a_dictionary_of_two_dimensional_arrays['Sheet 3']})
379 >>> p.save_book_as(bookdict=data, dest_file_name="book.xls")
380
381 Let's verify its order:
382
383 .. code-block:: python
384
385 >>> book_dict = p.get_book_dict(file_name="book.xls")
386 >>> for key, item in book_dict.items():
387 ... print(json.dumps({key: item}))
388 {"Sheet 2": [["X", "Y", "Z"], [1, 2, 3], [4, 5, 6]]}
389 {"Sheet 1": [[1, 2, 3], [4, 5, 6], [7, 8, 9]]}
390 {"Sheet 3": [["O", "P", "Q"], [3, 2, 1], [4, 3, 2]]}
391
392 Please notice that "Sheet 2" is the first item in the *book_dict*, meaning the order of sheets are preserved.
393
394
395 Transcoding
396 -------------------------------------------
397
398 .. note::
399
400 Please note that `pyexcel-cli` can perform file transcoding at command line.
401 No need to open your editor, save the problem, then python run.
402
403 {% if sphinx %}
404 .. testcode::
405 :hide:
406
407 >>> import datetime
408 >>> data = [
409 ... ["name", "weight", "birth"],
410 ... ["Adam", 3.4, datetime.date(2015, 2, 3)],
411 ... ["Smith", 4.2, datetime.date(2014, 11, 12)]
412 ... ]
413 >>> p.save_as(array=data, dest_file_name="birth.xls")
414
415 {% endif %}
416
417 The following code does a simple file format transcoding from xls to csv:
418
419 .. code-block:: python
420
421 >>> p.save_as(file_name="birth.xls", dest_file_name="birth.csv")
422
423 Again it is really simple. Let's verify what we have gotten:
424
425 .. code-block:: python
426
427 >>> sheet = p.get_sheet(file_name="birth.csv")
428 >>> sheet
429 birth.csv:
430 +-------+--------+----------+
431 | name | weight | birth |
432 +-------+--------+----------+
433 | Adam | 3.4 | 03/02/15 |
434 +-------+--------+----------+
435 | Smith | 4.2 | 12/11/14 |
436 +-------+--------+----------+
437
438 .. NOTE::
439
440 Please note that csv(comma separate value) file is pure text file. Formula, charts, images and formatting in xls file will disappear no matter which transcoding tool you use. Hence, pyexcel is a quick alternative for this transcoding job.
441
442
443 Let use previous example and save it as xlsx instead
444
445 .. code-block:: python
446
447 >>> p.save_as(file_name="birth.xls",
448 ... dest_file_name="birth.xlsx") # change the file extension
449
450 Again let's verify what we have gotten:
451
452 .. code-block:: python
453
454 >>> sheet = p.get_sheet(file_name="birth.xlsx")
455 >>> sheet
456 pyexcel_sheet1:
457 +-------+--------+----------+
458 | name | weight | birth |
459 +-------+--------+----------+
460 | Adam | 3.4 | 03/02/15 |
461 +-------+--------+----------+
462 | Smith | 4.2 | 12/11/14 |
463 +-------+--------+----------+
464
465
466 Excel book merge and split operation in one line
467 --------------------------------------------------------------------------------
468
469 Merge all excel files in directory into a book where each file become a sheet
470 ********************************************************************************
471
472 The following code will merge every excel files into one file, say "output.xls":
473
474 .. code-block:: python
475
476 from pyexcel.cookbook import merge_all_to_a_book
477 import glob
478
479
480 merge_all_to_a_book(glob.glob("your_csv_directory\*.csv"), "output.xls")
481
482 You can mix and match with other excel formats: xls, xlsm and ods. For example, if you are sure you have only xls, xlsm, xlsx, ods and csv files in `your_excel_file_directory`, you can do the following:
483
484 .. code-block:: python
485
486 from pyexcel.cookbook import merge_all_to_a_book
487 import glob
488
489
490 merge_all_to_a_book(glob.glob("your_excel_file_directory\*.*"), "output.xls")
491
492 Split a book into single sheet files
493 ****************************************
494
495 {% if sphinx %}
496 .. testcode::
497 :hide:
498
499 >>> content = {
500 ... 'Sheet 1':
501 ... [
502 ... [1.0, 2.0, 3.0],
503 ... [4.0, 5.0, 6.0],
504 ... [7.0, 8.0, 9.0]
505 ... ],
506 ... 'Sheet 2':
507 ... [
508 ... ['X', 'Y', 'Z'],
509 ... [1.0, 2.0, 3.0],
510 ... [4.0, 5.0, 6.0]
511 ... ],
512 ... 'Sheet 3':
513 ... [
514 ... ['O', 'P', 'Q'],
515 ... [3.0, 2.0, 1.0],
516 ... [4.0, 3.0, 2.0]
517 ... ]
518 ... }
519 >>> book = p.Book(content)
520 >>> book.save_as("megabook.xls")
521
522 {% endif %}
523
524 Suppose you have many sheets in a work book and you would like to separate each into a single sheet excel file. You can easily do this:
525
526 .. code-block:: python
527
528 >>> from pyexcel.cookbook import split_a_book
529 >>> split_a_book("megabook.xls", "output.xls")
530 >>> import glob
531 >>> outputfiles = glob.glob("*_output.xls")
532 >>> for file in sorted(outputfiles):
533 ... print(file)
534 ...
535 Sheet 1_output.xls
536 Sheet 2_output.xls
537 Sheet 3_output.xls
538
539 for the output file, you can specify any of the supported formats
540
541 {% if sphinx %}
542 .. testcode::
543 :hide:
544
545 >>> os.unlink("Sheet 1_output.xls")
546 >>> os.unlink("Sheet 2_output.xls")
547 >>> os.unlink("Sheet 3_output.xls")
548 {% endif %}
549
550 Extract just one sheet from a book
551 *************************************
552
553
554 Suppose you just want to extract one sheet from many sheets that exists in a work book and you would like to separate it into a single sheet excel file. You can easily do this:
555
556 .. code-block:: python
557
558 >>> from pyexcel.cookbook import extract_a_sheet_from_a_book
559 >>> extract_a_sheet_from_a_book("megabook.xls", "Sheet 1", "output.xls")
560 >>> if os.path.exists("Sheet 1_output.xls"):
561 ... print("Sheet 1_output.xls exists")
562 ...
563 Sheet 1_output.xls exists
564
565 for the output file, you can specify any of the supported formats
566
567 {% if sphinx %}
568 .. testcode::
569 :hide:
570
571 >>> os.unlink("Sheet 1_output.xls")
572 >>> os.unlink("megabook.xls")
573 >>> os.unlink('birth.xls')
574 >>> os.unlink('birth.csv')
575 >>> os.unlink('birth.xlsx')
576 >>> os.unlink('high_speed_rail.xls')
577 >>> os.unlink('henley.xlsx')
578 >>> os.unlink('ccs.csv')
579 >>> os.unlink("book.xls")
580 >>> os.unlink("your_file.xls")
581 >>> os.unlink("example.csv")
582 {% endif %}⏎
0
1 When you are dealing with huge amount of data, e.g. 64GB, obviously you would not
2 like to fill up your memory with those data. What you may want to do is, record
3 data from Nth line, take M records and stop. And you only want to use your memory
4 for the M records, not for beginning part nor for the tail part.
5
6 Hence partial read feature is developed to read partial data into memory for
7 processing.
8
9 You can paginate by row, by column and by both, hence you dictate what portion of the
10 data to read back. But remember only row limit features help you save memory. Let's
11 you use this feature to record data from Nth column, take M number of columns and skip
12 the rest. You are not going to reduce your memory footprint.
13
14 Why did not I see above benefit?
15 --------------------------------------------------------------------------------
16
17 This feature depends heavily on the implementation details.
18
19 `pyexcel-xls`_ (xlrd), `pyexcel-xlsx`_ (openpyxl), `pyexcel-ods`_ (odfpy) and
20 `pyexcel-ods3`_ (pyexcel-ezodf) will read all data into memory. Because xls,
21 xlsx and ods file are effective a zipped folder, all four will unzip the folder
22 and read the content in xml format in **full**, so as to make sense of all details.
23
24 Hence, during the partial data is been returned, the memory consumption won't
25 differ from reading the whole data back. Only after the partial
26 data is returned, the memory comsumption curve shall jump the cliff. So pagination
27 code here only limits the data returned to your program.
28
29 With that said, `pyexcel-xlsxr`_, `pyexcel-odsr`_ and `pyexcel-htmlr`_ DOES read
30 partial data into memory. Those three are implemented in such a way that they
31 consume the xml(html) when needed. When they have read designated portion of the
32 data, they stop, even if they are half way through.
33
34 In addition, pyexcel's csv readers can read partial data into memory too.
35
36 {% if sphinx %}
37
38 .. testcode::
39 :hide:
40
41 >>> import sys
42 >>> if sys.version_info[0] < 3:
43 ... from StringIO import StringIO
44 ... else:
45 ... from io import StringIO
46 >>> from pyexcel_io._compact import OrderedDict
47
48 {% endif %}
49
50 Let's assume the following file is a huge csv file:
51
52 .. code-block:: python
53
54 >>> import datetime
55 >>> import pyexcel as pe
56 >>> data = [
57 ... [1, 21, 31],
58 ... [2, 22, 32],
59 ... [3, 23, 33],
60 ... [4, 24, 34],
61 ... [5, 25, 35],
62 ... [6, 26, 36]
63 ... ]
64 >>> pe.save_as(array=data, dest_file_name="your_file.csv")
65
66
67 And let's pretend to read partial data:
68
69
70 .. code-block:: python
71
72 >>> pe.get_sheet(file_name="your_file.csv", start_row=2, row_limit=3)
73 your_file.csv:
74 +---+----+----+
75 | 3 | 23 | 33 |
76 +---+----+----+
77 | 4 | 24 | 34 |
78 +---+----+----+
79 | 5 | 25 | 35 |
80 +---+----+----+
81
82 And you could as well do the same for columns:
83
84 .. code-block:: python
85
86 >>> pe.get_sheet(file_name="your_file.csv", start_column=1, column_limit=2)
87 your_file.csv:
88 +----+----+
89 | 21 | 31 |
90 +----+----+
91 | 22 | 32 |
92 +----+----+
93 | 23 | 33 |
94 +----+----+
95 | 24 | 34 |
96 +----+----+
97 | 25 | 35 |
98 +----+----+
99 | 26 | 36 |
100 +----+----+
101
102 Obvious, you could do both at the same time:
103
104 .. code-block:: python
105
106 >>> pe.get_sheet(file_name="your_file.csv",
107 ... start_row=2, row_limit=3,
108 ... start_column=1, column_limit=2)
109 your_file.csv:
110 +----+----+
111 | 23 | 33 |
112 +----+----+
113 | 24 | 34 |
114 +----+----+
115 | 25 | 35 |
116 +----+----+
117
118
119 The pagination support is available across all pyexcel plugins.
120
121 .. note::
122
123 No column pagination support for query sets as data source.
124
125
126 Formatting while transcoding a big data file
127 --------------------------------------------------------------------------------
128
129 If you are transcoding a big data set, conventional formatting method would not
130 help unless a on-demand free RAM is available. However, there is a way to minimize
131 the memory footprint of pyexcel while the formatting is performed.
132
133 Let's continue from previous example. Suppose we want to transcode "your_file.csv"
134 to "your_file.xls" but increase each element by 1.
135
136 What we can do is to define a row renderer function as the following:
137
138 .. code-block:: python
139
140 >>> def increment_by_one(row):
141 ... for element in row:
142 ... yield element + 1
143
144 Then pass it onto save_as function using row_renderer:
145
146 .. code-block:: python
147
148 >>> pe.isave_as(file_name="your_file.csv",
149 ... row_renderer=increment_by_one,
150 ... dest_file_name="your_file.xlsx")
151
152
153 .. note::
154
155 If the data content is from a generator, isave_as has to be used.
156
157 We can verify if it was done correctly:
158
159 .. code-block:: python
160
161 >>> pe.get_sheet(file_name="your_file.xlsx")
162 your_file.csv:
163 +---+----+----+
164 | 2 | 22 | 32 |
165 +---+----+----+
166 | 3 | 23 | 33 |
167 +---+----+----+
168 | 4 | 24 | 34 |
169 +---+----+----+
170 | 5 | 25 | 35 |
171 +---+----+----+
172 | 6 | 26 | 36 |
173 +---+----+----+
174 | 7 | 27 | 37 |
175 +---+----+----+
176
177 {% if sphinx %}
178
179 .. testcode::
180 :hide:
181
182 >>> import os
183 >>> os.unlink("your_file.csv")
184 >>> os.unlink("your_file.xlsx")
185
186 {% endif %}⏎
0 {%extends "BASIC-README.rst.jj2"%}
1
2 {%block features%}
3
4 Feature Highlights
5 ===================
6
7 .. image:: https://github.com/pyexcel/pyexcel/raw/dev/docs/source/_static/images/architecture.svg
8
9
10 1. One application programming interface(API) to handle multiple data sources:
11
12 * physical file
13 * memory file
14 * SQLAlchemy table
15 * Django Model
16 * Python data structures: dictionary, records and array
17
18 2. One API to read and write data in various excel file formats.
19 3. For large data sets, data streaming are supported. A genenerator can be returned to you. Checkout iget_records, iget_array, isave_as and isave_book_as.
20
21 {% endblock %}
22
23 {%block usage%}
24
25 {%include "one-liners.rst.jj2" %}
26
27 Hidden feature: partial read
28 ===============================================
29
30 Most pyexcel users do not know, but other library users were requesting `the similar features <https://github.com/jazzband/tablib/issues/467>`_
31
32 {%include "partial-data.rst.jj2" %}
33
34 {%include "two-liners.rst.jj2" %}
35
36 Available Plugins
37 =================
38
39 {% include "plugins-list.rst.jj2" %}
40
41
42 Acknowledgement
43 ===============
44
45 All great work have been done by odf, ezodf, xlrd, xlwt, tabulate and other
46 individual developers. This library unites only the data access code.
47
48
49 {%endblock%}
50
51 {%block development_guide%}
52 {%endblock%}
0 {% extends "travis.yml.jj2" %}
1 {%block extra_matrix %}
2 matrix:
3 include:
4 - python: 3.6
5 env: MINREQ=1
6 {%endblock%}
7 {%block custom_python_versions%}
8 python:
9 - 3.8
10 - 3.7
11 - 3.6
12 {%endblock%}
13 {%block pypi_deployment%}
14 {%endblock %}⏎
+0
-16
.moban.d/setup.py less more
0 {% extends 'setup.py.jj2' %}
1
2 {% block additional_keywords %}
3 'tsv',
4 'tsvz'
5 'csv',
6 'csvz',
7 'xls',
8 'xlsx',
9 'ods'
10 {% endblock %}
11
12 {% block additional_classifiers %}
13 'Development Status :: 3 - Alpha',
14 'Programming Language :: Python :: Implementation :: PyPy'
15 {% endblock %}}
+0
-8
.moban.d/test.bat less more
0 {% extends "test.script.jj2" %}
1
2 {%block flake8_options%}
3 --builtins=unicode,xrange,long
4 {%endblock%}
5
6
7
+0
-9
.moban.d/test.sh less more
0 {% extends "test.script.jj2" %}
1
2 {%block pretest %}
3 #/bin/bash
4 {%endblock %}
5
6 {%block flake8_options%}
7 --builtins=unicode,xrange,long
8 {%endblock%}
0 {% extends 'tests/requirements.txt.jj2' %}
1 {%block extras %}
2 SQLAlchemy
3 flask
4 lxml==4.0.0;platform_python_implementation=="PyPy"
5 pyexcel-xlsx>=0.4.1
6 pyexcel-xls>=0.4.1
7 pyexcel-text>=0.2.0
8 psutil
9 mock
10 moban
11 black;python_version>="3.6"
12 isort;python_version>="3.6"
13 {%endblock%}
+0
-14
.moban.d/tests/requirements.txt less more
0 {% extends 'tests/requirements.txt.jj2' %}
1 {%block extras %}
2 SQLAlchemy
3 flask
4 lxml==4.0.0;platform_python_implementation=="PyPy"
5 pyexcel-xlsx>=0.4.1
6 pyexcel-xls>=0.4.1
7 pyexcel-text>=0.2.0
8 psutil
9 mock
10 moban
11 black;python_version>="3.6"
12 isort;python_version>="3.6"
13 {%endblock%}
+0
-13
.moban.d/travis.yml less more
0 {% extends "travis.yml.jj2" %}
1 {%block extra_matrix %}
2 matrix:
3 include:
4 - python: 2.7
5 env: MINREQ=1
6 {%endblock%}
7 {%block custom_install%}
8 - if [[ $TRAVIS_PYTHON_VERSION == "pypy" ]]; then rm tests/test_examples.py; fi
9 {%endblock%}
10
11 {%block pypi_deployment%}
12 {%endblock %}⏎
0 Stream APIs for big file : A set of two liners
1 ================================================================================
2
3 When you are dealing with **BIG** excel files, you will want **pyexcel** to use
4 constant memory.
5
6 This section shows you how to get data from your **BIG** excel files and how to
7 export data to excel files in **two lines** at most, without eating all
8 your computer memory.
9
10
11 Two liners for get data from big excel files
12 --------------------------------------------------------------------------------
13
14 Get a list of dictionaries
15 ********************************************************************************
16
17 {% if sphinx %}
18 .. testcode::
19 :hide:
20
21 >>> import os
22 >>> import pyexcel as p
23 >>> content="""
24 ... Coffees,Serving Size,Caffeine (mg)
25 ... Starbucks Coffee Blonde Roast,venti(20 oz),475
26 ... Dunkin' Donuts Coffee with Turbo Shot,large(20 oz.),398
27 ... Starbucks Coffee Pike Place Roast,grande(16 oz.),310
28 ... Panera Coffee Light Roast,regular(16 oz.),300
29 ... """.strip()
30 >>> sheet = p.get_sheet(file_content=content, file_type='csv')
31 >>> sheet.save_as("your_file.xls")
32
33 {% endif %}
34
35
36 {% if sphinx %}
37 Suppose you want to process the following coffee data:
38
39 .. pyexcel-table::
40
41 ---pyexcel:Huge list of coffeine drinks---
42 Coffees,Serving Size,Caffeine (mg)
43 Starbucks Coffee Blonde Roast,venti(20 oz),475
44 Dunkin' Donuts Coffee with Turbo Shot,large(20 oz.),398
45 Starbucks Coffee Pike Place Roast,grande(16 oz.),310
46 Panera Coffee Light Roast,regular(16 oz.),300
47 {% else %}
48 Suppose you want to process the following coffee data again:
49
50 Top 5 coffeine drinks:
51
52 ===================================== =============== =============
53 Coffees Serving Size Caffeine (mg)
54 Starbucks Coffee Blonde Roast venti(20 oz) 475
55 Dunkin' Donuts Coffee with Turbo Shot large(20 oz.) 398
56 Starbucks Coffee Pike Place Roast grande(16 oz.) 310
57 Panera Coffee Light Roast regular(16 oz.) 300
58 ===================================== =============== =============
59
60 {% endif %}
61
62 Let's get a list of dictionary out from the xls file:
63
64 .. code-block:: python
65
66 >>> records = p.iget_records(file_name="your_file.xls")
67
68 And let's check what do we have:
69
70 .. code-block:: python
71
72 >>> for r in records:
73 ... print(f"{r['Serving Size']} of {r['Coffees']} has {r['Caffeine (mg)']} mg")
74 venti(20 oz) of Starbucks Coffee Blonde Roast has 475 mg
75 large(20 oz.) of Dunkin' Donuts Coffee with Turbo Shot has 398 mg
76 grande(16 oz.) of Starbucks Coffee Pike Place Roast has 310 mg
77 regular(16 oz.) of Panera Coffee Light Roast has 300 mg
78
79 Please do not forgot the second line to close the opened file handle:
80
81 .. code-block:: python
82
83 >>> p.free_resources()
84
85 Get two dimensional array
86 ********************************************************************************
87
88 Instead, what if you have to use `pyexcel.get_array` to do the same:
89
90 .. code-block:: python
91
92 >>> for row in p.iget_array(file_name="your_file.xls", start_row=1):
93 ... print(f"{row[1]} of {row[0]} has {row[2]} mg")
94 venti(20 oz) of Starbucks Coffee Blonde Roast has 475 mg
95 large(20 oz.) of Dunkin' Donuts Coffee with Turbo Shot has 398 mg
96 grande(16 oz.) of Starbucks Coffee Pike Place Roast has 310 mg
97 regular(16 oz.) of Panera Coffee Light Roast has 300 mg
98
99 Again, do not forgot the second line:
100
101 .. code-block:: python
102
103 >>> p.free_resources()
104
105 where `start_row` skips the header row.
106
107 Data export in one liners
108 ---------------------------------------------
109
110 Export an array
111 **********************
112
113 Suppose you have the following array:
114
115 .. code-block:: python
116
117 >>> data = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
118
119 And here is the code to save it as an excel file :
120
121 .. code-block:: python
122
123 >>> p.isave_as(array=data, dest_file_name="example.xls")
124
125 But the following line is not required because the data source
126 are not file sources:
127
128 .. code-block:: python
129
130 >>> # p.free_resources()
131
132 Let's verify it:
133
134 .. code-block:: python
135
136 >>> p.get_sheet(file_name="example.xls")
137 pyexcel_sheet1:
138 +---+---+---+
139 | 1 | 2 | 3 |
140 +---+---+---+
141 | 4 | 5 | 6 |
142 +---+---+---+
143 | 7 | 8 | 9 |
144 +---+---+---+
145
146 {% if sphinx %}
147
148 .. testcode::
149 :hide:
150
151 >>> import os
152 >>> os.unlink("example.xls")
153
154 {% endif %}
155
156 And here is the code to save it as a csv file :
157
158 .. code-block:: python
159
160 >>> p.isave_as(array=data,
161 ... dest_file_name="example.csv",
162 ... dest_delimiter=':')
163
164 Let's verify it:
165
166 .. code-block:: python
167
168 >>> with open("example.csv") as f:
169 ... for line in f.readlines():
170 ... print(line.rstrip())
171 ...
172 1:2:3
173 4:5:6
174 7:8:9
175
176 Export a list of dictionaries
177 **********************************
178
179 .. code-block:: python
180
181 >>> records = [
182 ... {"year": 1903, "country": "Germany", "speed": "206.7km/h"},
183 ... {"year": 1964, "country": "Japan", "speed": "210km/h"},
184 ... {"year": 2008, "country": "China", "speed": "350km/h"}
185 ... ]
186 >>> p.isave_as(records=records, dest_file_name='high_speed_rail.xls')
187
188 Export a dictionary of single key value pair
189 ********************************************************************************
190
191 .. code-block:: python
192
193 >>> henley_on_thames_facts = {
194 ... "area": "5.58 square meters",
195 ... "population": "11,619",
196 ... "civial parish": "Henley-on-Thames",
197 ... "latitude": "51.536",
198 ... "longitude": "-0.898"
199 ... }
200 >>> p.isave_as(adict=henley_on_thames_facts, dest_file_name='henley.xlsx')
201
202 Export a dictionary of single dimensonal array
203 ********************************************************************************
204
205 .. code-block:: python
206
207 >>> ccs_insights = {
208 ... "year": ["2017", "2018", "2019", "2020", "2021"],
209 ... "smart phones": [1.53, 1.64, 1.74, 1.82, 1.90],
210 ... "feature phones": [0.46, 0.38, 0.30, 0.23, 0.17]
211 ... }
212 >>> p.isave_as(adict=ccs_insights, dest_file_name='ccs.csv')
213 >>> p.free_resources()
214
215 Export a dictionary of two dimensional array as a book
216 ********************************************************************************
217
218 Suppose you want to save the below dictionary to an excel file :
219
220 .. code-block:: python
221
222 >>> a_dictionary_of_two_dimensional_arrays = {
223 ... 'Sheet 1':
224 ... [
225 ... [1.0, 2.0, 3.0],
226 ... [4.0, 5.0, 6.0],
227 ... [7.0, 8.0, 9.0]
228 ... ],
229 ... 'Sheet 2':
230 ... [
231 ... ['X', 'Y', 'Z'],
232 ... [1.0, 2.0, 3.0],
233 ... [4.0, 5.0, 6.0]
234 ... ],
235 ... 'Sheet 3':
236 ... [
237 ... ['O', 'P', 'Q'],
238 ... [3.0, 2.0, 1.0],
239 ... [4.0, 3.0, 2.0]
240 ... ]
241 ... }
242
243 Here is the code:
244
245 .. code-block:: python
246
247 >>> p.isave_book_as(
248 ... bookdict=a_dictionary_of_two_dimensional_arrays,
249 ... dest_file_name="book.xls"
250 ... )
251
252 If you want to preserve the order of sheets in your dictionary, you have to
253 pass on an ordered dictionary to the function itself. For example:
254
255 .. code-block:: python
256
257 >>> from pyexcel._compact import OrderedDict
258 >>> data = OrderedDict()
259 >>> data.update({"Sheet 2": a_dictionary_of_two_dimensional_arrays['Sheet 2']})
260 >>> data.update({"Sheet 1": a_dictionary_of_two_dimensional_arrays['Sheet 1']})
261 >>> data.update({"Sheet 3": a_dictionary_of_two_dimensional_arrays['Sheet 3']})
262 >>> p.isave_book_as(bookdict=data, dest_file_name="book.xls")
263 >>> p.free_resources()
264
265 Let's verify its order:
266
267 .. code-block:: python
268
269 >>> import json
270 >>> book_dict = p.get_book_dict(file_name="book.xls")
271 >>> for key, item in book_dict.items():
272 ... print(json.dumps({key: item}))
273 {"Sheet 2": [["X", "Y", "Z"], [1, 2, 3], [4, 5, 6]]}
274 {"Sheet 1": [[1, 2, 3], [4, 5, 6], [7, 8, 9]]}
275 {"Sheet 3": [["O", "P", "Q"], [3, 2, 1], [4, 3, 2]]}
276
277 Please notice that "Sheet 2" is the first item in the *book_dict*, meaning the order of sheets are preserved.
278
279
280 File format transcoding on one line
281 -------------------------------------------
282
283 .. note::
284
285 Please note that the following file transcoding could be with zero line. Please
286 install pyexcel-cli and you will do the transcode in one command. No need to
287 open your editor, save the problem, then python run.
288
289 {% if sphinx %}
290
291 .. testcode::
292 :hide:
293
294 >>> import datetime
295 >>> data = [
296 ... ["name", "weight", "birth"],
297 ... ["Adam", 3.4, datetime.date(2015, 2, 3)],
298 ... ["Smith", 4.2, datetime.date(2014, 11, 12)]
299 ... ]
300 >>> p.isave_as(array=data, dest_file_name="birth.xls")
301
302 {% endif %}
303
304 The following code does a simple file format transcoding from xls to csv:
305
306 .. code-block:: python
307
308 >>> import pyexcel
309 >>> p.save_as(file_name="birth.xls", dest_file_name="birth.csv")
310
311 Again it is really simple. Let's verify what we have gotten:
312
313 .. code-block:: python
314
315 >>> sheet = p.get_sheet(file_name="birth.csv")
316 >>> sheet
317 birth.csv:
318 +-------+--------+----------+
319 | name | weight | birth |
320 +-------+--------+----------+
321 | Adam | 3.4 | 03/02/15 |
322 +-------+--------+----------+
323 | Smith | 4.2 | 12/11/14 |
324 +-------+--------+----------+
325
326 .. note::
327
328 Please note that csv(comma separate value) file is pure text file. Formula, charts, images and formatting in xls file will disappear no matter which transcoding tool you use. Hence, pyexcel is a quick alternative for this transcoding job.
329
330
331 Let use previous example and save it as xlsx instead
332
333 .. code-block:: python
334
335 >>> import pyexcel
336 >>> p.isave_as(file_name="birth.xls",
337 ... dest_file_name="birth.xlsx") # change the file extension
338
339 Again let's verify what we have gotten:
340
341 .. code-block:: python
342
343 >>> sheet = p.get_sheet(file_name="birth.xlsx")
344 >>> sheet
345 pyexcel_sheet1:
346 +-------+--------+----------+
347 | name | weight | birth |
348 +-------+--------+----------+
349 | Adam | 3.4 | 03/02/15 |
350 +-------+--------+----------+
351 | Smith | 4.2 | 12/11/14 |
352 +-------+--------+----------+
353
354 {% if sphinx %}
355 .. testcode::
356 :hide:
357
358 >>> import os
359 >>> os.unlink('ccs.csv')
360 >>> os.unlink('book.xls')
361
362 {% endif %}
0 requires:
1 - type: git
2 url: https://github.com/moremoban/pypi-mobans
3 submodule: true
4 - https://github.com/pyexcel/pyexcel-mobans
0 overrides: "git://github.com/pyexcel/pyexcel-mobans!/mobanfile.yaml"
51 configuration:
6 configuration_dir: "pyexcel-mobans:config"
7 template_dir:
8 - "pyexcel-mobans:templates"
9 - "pypi-mobans:templates"
10 - ".moban.d"
112 configuration: pyexcel.yml
123 targets:
13 - setup.py: setup.py
4 - setup.py: custom_setup.py.jj2
145 - "docs/source/conf.py": "docs/source/custom_conf.py.jj2"
15 - .travis.yml: travis.yml
16 - requirements.txt: requirements.txt.jj2
17 - min_requirements.txt: minimum_requirements.txt
18 - "tests/requirements.txt": "tests/requirements.txt"
19 - LICENSE: NEW_BSD_LICENSE.jj2
6 - "docs/source/quickstart.rst": "docs/source/quickstart.rst.jj2"
7 - "docs/source/two-liners.rst": "docs/source/two-liners.rst.jj2"
8 - "docs/source/bigdata.rst": "docs/source/bigdata.rst.jj2"
9 - .travis.yml: pyexcel-travis.yml.jj2
2010 - MANIFEST.in: CUSTOM_MANIFEST.in.jj2
21 - README.rst: README.rst
22 - "docs/source/guide.rst": "docs/source/guide.rst"
23 - test.sh: test.sh
24 - test.bat: test.bat
11 - README.rst: pyexcel-README.rst.jj2
12 - "docs/source/guide.rst": "docs/source/guide.rst.jj2"
2513 - .gitignore: commons-gitignore.jj2
2614 - "pyexcel/__version__.py": version.txt
2715 - "docs/source/index.rst": "docs/source/pyexcel-index.rst.jj2"
28 - output: CHANGELOG.rst
29 configuration: changelog.yml
30 template: CHANGELOG.rst.jj2
31 - lint.sh: lint.script.jj2
16 - "tests/requirements.txt": "tests/custom_requirements.txt.jj2"
17 - "min_requirements.txt": "minimum_requirements.txt.jj2"
18
0
10 sudo: false
21 dist: xenial
32 language: python
43 notifications:
54 email: false
65 python:
7 - &pypy2 pypy2.7-6.0
8 - &pypy3 pypy3.5-6.0
6 - 3.8
97 - 3.7
108 - 3.6
11 - 3.5
12 - 2.7
139 matrix:
1410 include:
15 - python: 2.7
11 - python: 3.6
1612 env: MINREQ=1
1713
1814 stages:
15 - lint
1916 - test
20 - lint
2117
22 .disable_global: &disable_global
23 before_install: false
24 install: true
25 before_script: false
26 after_success: false
27 after_failure: false
2818
2919 .lint: &lint
30 <<: *disable_global
20 git:
21 submodules: false
3122 python: 3.6
23 env:
24 - MINREQ=0
3225 stage: lint
33 install: pip install flake8
3426 script: make lint
3527
3628 jobs:
3729 include:
30 - *moban
3831 - *lint
3932
4033 stage: test
4134
42 script: make test
43
4435 before_install:
45 - if [[ $TRAVIS_PYTHON_VERSION == "pypy" ]]; then rm tests/test_examples.py; fi
4636 - if [[ -f min_requirements.txt && "$MINREQ" -eq 1 ]]; then
4737 mv min_requirements.txt requirements.txt ;
4838 fi
49 - test ! -f rnd_requirements.txt || pip install --no-deps -r rnd_requirements.txt
39 - test ! -f rnd_requirements.txt ||
40 pip install --no-deps -r rnd_requirements.txt
5041 - test ! -f rnd_requirements.txt || pip install -r rnd_requirements.txt ;
5142 - pip install -r tests/requirements.txt
5243 script:
00 Change log
11 ================================================================================
22
3 0.6.6 - 14.11.2020
4 --------------------------------------------------------------------------------
5
6 **Updated**
7
8 #. `#233 <https://github.com/pyexcel/pyexcel/issues/233>`_: dynamically resize
9 the table matrix on set_value. sheet['AA1'] = 'test' will work in this
10 release.
11
12 0.6.5 - 8.10.2020
13 --------------------------------------------------------------------------------
14
15 **Updated**
16
17 #. update queryset source to work with pyexcel-io 0.6.0
18
19 0.6.4 - 18.08.2020
20 --------------------------------------------------------------------------------
21
22 **Updated**
23
24 #. `#219 <https://github.com/pyexcel/pyexcel/issues/219>`_: book created from
25 dict no longer discards order.
26
27 0.6.3 - 01.08.2020
28 --------------------------------------------------------------------------------
29
30 **fixed**
31
32 #. `#214 <https://github.com/pyexcel/pyexcel/issues/214>`_: remove leading and
33 trailing whitespace for column names
34
35 **removed**
36
37 #. python 2 compatibility have been permanently removed.
38
39 0.6.2 - 8.06.2020
40 --------------------------------------------------------------------------------
41
42 **fixed**
43
44 #. `#109 <https://github.com/pyexcel/pyexcel/issues/109>`_: Control the column
45 order when write the data output
46
47 0.6.1 - 02.05.2020
48 --------------------------------------------------------------------------------
49
50 **fixed**
51
52 #. `#203 <https://github.com/pyexcel/pyexcel/issues/203>`_: texttable was
53 dropped out in 0.6.0 as compulsary dependency. end user may experience it
54 when a sheet/table is printed in a shell. otherwise, new user of pyexcel
55 won't see it. As of release date, no issues were created
56
57 0.6.0 - 21.04.2020
58 --------------------------------------------------------------------------------
59
60 **updated**
61
62 #. `#199 <https://github.com/pyexcel/pyexcel/issues/199>`_: += in place; = +
63 shall return new instance
64 #. `#195 <https://github.com/pyexcel/pyexcel/issues/195>`_: documentation
65 update. however small is welcome
66
67 **removed**
68
69 #. Dropping the test support for python version lower than 3.6. v0.6.0 should
70 work with python 2.7 but is not guaranteed to work. Please upgrade to python
71 3.6+.
72
73 0.5.15 - 07.07.2019
74 --------------------------------------------------------------------------------
75
76 **updated**
77
78 #. `#185 <https://github.com/pyexcel/pyexcel/issues/185>`_: fix a bug with http
79 data source. The real fix lies in pyexcel-io v0.5.19. this release just put
80 the version requirement in.
81
382 0.5.14 - 12.06.2019
483 --------------------------------------------------------------------------------
584
6 updated
7 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
85 **updated**
886
987 #. `#182 <https://github.com/pyexcel/pyexcel/issues/182>`_: support
1088 dest_force_file_type on save_as and save_book_as
1290 0.5.13 - 12.03.2019
1391 --------------------------------------------------------------------------------
1492
15 updated
16 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
93 **updated**
1794
1895 #. `#176 <https://github.com/pyexcel/pyexcel/issues/176>`_: get_sheet
1996 {IndexError}list index out of range // XLSX can't be opened
2198 0.5.12 - 25.02.2019
2299 --------------------------------------------------------------------------------
23100
24 updated
25 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
101 **updated**
26102
27103 #. `#174 <https://github.com/pyexcel/pyexcel/issues/174>`_: include examples in
28104 tarbar
30106 0.5.11 - 22.02.2019
31107 --------------------------------------------------------------------------------
32108
33 updated
34 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
109 **updated**
35110
36111 #. `#169 <https://github.com/pyexcel/pyexcel/issues/169>`_: remove
37112 pyexcel-handsontalbe in test
40115 0.5.10 - 3.12.2018
41116 --------------------------------------------------------------------------------
42117
43 updated
44 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
118 **updated**
45119
46120 #. `#157 <https://github.com/pyexcel/pyexcel/issues/157>`_: Please use
47121 scan_plugins_regex, which lml 0.7 complains about
50124 0.5.9.1 - 30.08.2018
51125 --------------------------------------------------------------------------------
52126
53 updated
54 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
127 **updated**
55128
56129 #. to require pyexcel-io 0.5.9.1 and use lml at least version 0.0.2
57130
58131 0.5.9 - 30.08.2018
59132 --------------------------------------------------------------------------------
60133
61 added
62 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
134 **added**
63135
64136 #. support __len__. len(book) returns the number of sheets and len(sheet)
65137 returns the number of rows
71143 but with .blob file suffix.
72144 #. finally, pyexcel got import pyexcel.__version__
73145
74 updated
75 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
146 **updated**
76147
77148 #. Sheet.to_records() returns a generator now, saving memory
78149 #. `#115 <https://github.com/pyexcel/pyexcel/issues/115>`_, Fix set membership
80151 #. `#140 <https://github.com/pyexcel/pyexcel/issues/140>`_, Direct writes to
81152 cells yield weird results
82153
83 0.5.8 - unreleased
84 --------------------------------------------------------------------------------
85
86 added
87 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
154 0.5.8 - 26.03.2018
155 --------------------------------------------------------------------------------
156
157 **added**
88158
89159 #. `#125 <https://github.com/pyexcel/pyexcel/issues/125>`_, sort book sheets
90160
91 updated
92 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
161 **updated**
93162
94163 #. `#126 <https://github.com/pyexcel/pyexcel/issues/126>`_, dest_sheet_name in
95164 save_as will set the sheet name in the output
99168 0.5.7 - 11.01.2018
100169 --------------------------------------------------------------------------------
101170
102 added
103 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
171 **added**
104172
105173 #. `pyexcel-io#46 <https://github.com/pyexcel/pyexcel-io/issues/46>`_, expose
106174 `bulk_save` to developer.
108176 0.5.6 - 23.10.2017
109177 --------------------------------------------------------------------------------
110178
111 removed
112 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
179 **removed**
113180
114181 #. `#105 <https://github.com/pyexcel/pyexcel/issues/105>`_, remove gease from
115182 setup_requires, introduced by 0.5.5.
120187 0.5.5 - 20.10.2017
121188 --------------------------------------------------------------------------------
122189
123 removed
124 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
190 **removed**
125191
126192 #. `#105 <https://github.com/pyexcel/pyexcel/issues/105>`_, remove gease from
127193 setup_requires, introduced by 0.5.5.
132198 0.5.4 - 27.09.2017
133199 --------------------------------------------------------------------------------
134200
135 fixed
136 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
201 **fixed**
137202
138203 #. `#100 <https://github.com/pyexcel/pyexcel/issues/100>`_, Sheet.to_dict() gets
139204 out of range error because there is only one row.
140205
141 updated
142 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
206 **updated**
143207
144208 #. Updated the baseline of pyexcel-io to 0.5.1.
145209
146210 0.5.3 - 01-08-2017
147211 --------------------------------------------------------------------------------
148212
149 added
150 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
213 **added**
151214
152215 #. `#95 <https://github.com/pyexcel/pyexcel/issues/95>`_, respect the order of
153216 records in iget_records, isave_as and save_as.
157220 0.5.2 - 26-07-2017
158221 --------------------------------------------------------------------------------
159222
160 Updated
161 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
223 **Updated**
162224
163225 #. embeded the enabler for pyexcel-htmlr. http source does not support text/html
164226 as mime type.
166228 0.5.1 - 12.06.2017
167229 --------------------------------------------------------------------------------
168230
169 Updated
170 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
231 **Updated**
171232
172233 #. support saving SheetStream and BookStream to database targets. This is needed
173234 for pyexcel-webio and its downstream projects.
175236 0.5.0 - 19.06.2017
176237 --------------------------------------------------------------------------------
177238
178 Added
179 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
239 **Added**
180240
181241 #. Sheet.top() and Sheet.top_left() for data browsing
182242 #. add html as default rich display in Jupyter notebook when pyexcel-text and
194254 is enfored. free_resource is added and it should be called when iget_array,
195255 iget_records, isave_as and/or isave_book_as are used.
196256
197 Updated
198 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
257 **Updated**
199258
200259 #. array is passed to pyexcel.Sheet as reference. it means your array data will
201260 be modified.
202261
203 Removed
204 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
262 **Removed**
205263
206264 #. pyexcel.Writer and pyexcel.BookWriter were removed
207265 #. pyexcel.load_book_from_sql and pyexcel.load_from_sql were removed
213271 0.4.5 - 17.03.2017
214272 --------------------------------------------------------------------------------
215273
216 Updated
217 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
274 **Updated**
218275
219276 #. `#80 <https://github.com/pyexcel/pyexcel/issues/80>`_: remove pyexcel-chart
220277 import from v0.4.x
222279 0.4.4 - 06.02.2017
223280 --------------------------------------------------------------------------------
224281
225 Updated
226 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
282 **Updated**
227283
228284 #. `#68 <https://github.com/pyexcel/pyexcel/issues/68>`_: regression
229285 save_to_memory() should have returned a stream instance which has been reset
231287 #. `#74 <https://github.com/pyexcel/pyexcel/issues/74>`_: Not able to handle
232288 decimal.Decimal
233289
234 Removed
235 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
290 **Removed**
236291
237292 #. remove get_{{file_type}}_stream functions from pyexcel.Sheet and pyexcel.Book
238293 introduced since 0.4.3.
240295 0.4.3 - 26.01.2017
241296 --------------------------------------------------------------------------------
242297
243 Added
244 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
298 **Added**
245299
246300 #. '.stream' attribute are attached to `~pyexcel.Sheet` and `~pyexcel.Book` to
247301 get direct access the underneath stream in responding to file type
249303 world, for example, Sheet.stream.csv gives a text stream that contains csv
250304 formatted data. Book.stream.xls returns a xls format data in a byte stream.
251305
252 Updated
253 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
306 **Updated**
254307
255308 #. Better error reporting when an unknown parameters or unsupported file types
256309 were given to the signature functions.
258311 0.4.2 - 17.01.2017
259312 --------------------------------------------------------------------------------
260313
261 Updated
262 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
314 **Updated**
263315
264316 #. Raise exception if the incoming sheet does not have column names. In other
265317 words, only sheet with column names could be saved to database. sheet with
279331 0.4.1 - 23.12.2016
280332 --------------------------------------------------------------------------------
281333
282 Updated
283 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
334 **Updated**
284335
285336 #. `#68 <https://github.com/pyexcel/pyexcel/issues/68>`_: regression
286337 save_to_memory() should have returned a stream instance.
288339 0.4.0 - 22.12.2016
289340 --------------------------------------------------------------------------------
290341
291 Added
292 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
342 **Added**
293343
294344 #. `Flask-Excel#19 <https://github.com/pyexcel/Flask-Excel/issues/19>`_ allow
295345 sheet_name parameter
296346 #. `pyexcel-xls#11 <https://github.com/pyexcel/pyexcel-xls/issues/11>`_
297347 case-insensitive for file_type. `xls` and `XLS` are treated in the same way
298348
299 Updated
300 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
349 **Updated**
301350
302351 #. `#66 <https://github.com/pyexcel/pyexcel/issues/66>`_: `export_columns` is
303352 ignored
306355 0.3.3 - 07.11.2016
307356 --------------------------------------------------------------------------------
308357
309 Updated
310 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
358 **Updated**
311359
312360 #. `#63 <https://github.com/pyexcel/pyexcel/issues/63>`_: cannot display empty
313361 sheet(hence book with empty sheet) as texttable
315363 0.3.2 - 02.11.2016
316364 --------------------------------------------------------------------------------
317365
318 Updated
319 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
366 **Updated**
320367
321368 #. `#62 <https://github.com/pyexcel/pyexcel/issues/62>`_: optional module import
322369 error become visible.
324371 0.3.0 - 28.10.2016
325372 --------------------------------------------------------------------------------
326373
327 Added:
328 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
374 **Added:**
329375
330376 #. file type setters for Sheet and Book, and its documentation
331377 #. `iget_records` returns a generator for a list of records and should have
335381 files.
336382 #. Enable pagination support, and custom row renderer via pyexcel-io v0.2.3
337383
338 Updated
339 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
384 **Updated**
340385
341386 #. Take `isave_as` out from `save_as`. Hence two functions are there for save a
342387 sheet as
363408 actual content. No longer they will return a io object hence you cannot call
364409 getvalue() on them.
365410
366 Removed:
367 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
411 **Removed:**
368412
369413 #. `content` and `out_file` as function parameters to the signature functions
370414 are no longer supported.
414458 0.2.5 - 31.08.2016
415459 --------------------------------------------------------------------------------
416460
417 Updated:
418 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
461 **Updated:**
419462
420463 #. `#58 <https://github.com/pyexcel/pyexcel/issues/58>`_: texttable should have
421464 been made as compulsory requirement
423466 0.2.4 - 14.07.2016
424467 --------------------------------------------------------------------------------
425468
426 Updated:
427 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
469 **Updated:**
428470
429471 #. For python 2, writing to sys.stdout by pyexcel-cli raise IOError.
430472
431473 0.2.3 - 11.07.2016
432474 --------------------------------------------------------------------------------
433475
434 Updated:
435 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
476 **Updated:**
436477
437478 #. For python 3, do not seek 0 when saving to memory if sys.stdout is passed on.
438479 Hence, adding support for sys.stdin and sys.stdout.
440481 0.2.2 - 01.06.2016
441482 --------------------------------------------------------------------------------
442483
443 Updated:
444 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
484 **Updated:**
445485
446486 #. Explicit imports, no longer needed
447487 #. Depends on latest setuptools 18.0.1
452492 0.2.1 - 23.04.2016
453493 --------------------------------------------------------------------------------
454494
455 Added:
456 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
495 **Added:**
457496
458497 #. add pyexcel-text file types as attributes of pyexcel.Sheet and pyexcel.Book,
459498 related to `#31 <https://github.com/pyexcel/pyexcel/issues/31>`__
460499 #. auto import pyexcel-text if it is pip installed
461500
462 Updated:
463 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
501 **Updated:**
464502
465503 #. code refactoring done for easy addition of sources.
466504 #. bug fix `#29 <https://github.com/pyexcel/pyexcel/issues/29>`__, Even if the
468506 #. pyexcel-text is no longer a plugin to pyexcel-io but to pyexcel.sources, see
469507 `pyexcel-text#22 <https://github.com/pyexcel/pyexcel-text/issues/22>`__
470508
471 Removed:
472 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
509 **Removed:**
473510
474511 #. pyexcel.presentation is removed. No longer the internal decorate @outsource
475512 is used. related to `#31 <https://github.com/pyexcel/pyexcel/issues/31>`_
477514 0.2.0 - 17.01.2016
478515 --------------------------------------------------------------------------------
479516
480 Updated
481 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
517 **Updated**
482518
483519 #. adopt pyexcel-io yield key word to return generator as content
484520 #. pyexcel.save_as and pyexcel.save_book_as get performance improvements
486522 0.1.7 - 03.07.2015
487523 --------------------------------------------------------------------------------
488524
489 Added
490 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
525 **Added**
491526
492527 #. Support pyramid-excel which does the database commit on its own.
493528
494529 0.1.6 - 13.06.2015
495530 --------------------------------------------------------------------------------
496531
497 Added
498 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
532 **Added**
499533
500534 #. get excel data from a http url
501535
502536 0.0.13 - 07.02.2015
503537 --------------------------------------------------------------------------------
504538
505 Added
506 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
539 **Added**
507540
508541 #. Support django
509542 #. texttable as default renderer
511544 0.0.12 - 25.01.2015
512545 --------------------------------------------------------------------------------
513546
514 Added
515 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
547 **Added**
516548
517549 #. Added sqlalchemy support
518550
519551 0.0.10 - 15.12.2015
520552 --------------------------------------------------------------------------------
521553
522 Added
523 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
554 **Added**
524555
525556 #. added csvz and tsvz format
526557
527558 0.0.4 - 12.10.2014
528559 --------------------------------------------------------------------------------
529560
530 Updated
531 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
561 **Updated**
532562
533563 #. Support python 3
534564
535565 0.0.1 - 14.09.2014
536566 --------------------------------------------------------------------------------
537567
538 Features:
539 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
568 **Features:**
540569
541570 #. read and write csv, ods, xls, xlsx and xlsm files(which are referred later as
542571 excel files)
0
1
2 18 contributors
3 ================================================================================
4
5 In alphabetical order:
6
7 * `Akshaya Kumar Sharma <https://github.com/akshayakrsh>`_
8 * `Andre Almar <https://github.com/andrealmar>`_
9 * `Arunkumar Rajendran <https://github.com/arunkumar-ra>`_
10 * `Ayan Banerjee <https://github.com/ayan-b>`_
11 * `Chris Hill-Scott <https://github.com/quis>`_
12 * `Craig Anderson <https://github.com/craiga>`_
13 * `Daryl Yu <https://github.com/darylyu>`_
14 * `J Harley <https://github.com/julzhk>`_
15 * `Joel Nothman <https://github.com/jnothman>`_
16 * `John Vandenberg <https://github.com/jayvdb>`_
17 * `Linghui Zeng <https://github.com/mathsyouth>`_
18 * `nikolas <https://github.com/nikolas>`_
19 * `Rintze M. Zelle <https://github.com/rmzelle>`_
20 * `Simeon Visser <https://github.com/svisser>`_
21 * `Simon Allen <https://github.com/garfunkel>`_
22 * `simon klemenc <https://github.com/hiaselhans>`_
23 * `Tim Gates <https://github.com/timgates42>`_
24 * `William Jamir Silva <https://github.com/williamjamir>`_
0 Copyright (c) 2014-2019 by Onni Software Ltd. and its contributors
0 Copyright (c) 2014-2020 by Onni Software Ltd. and its contributors
11 All rights reserved.
22
33 Redistribution and use in source and binary forms of the software as well
1212 and/or other materials provided with the distribution.
1313
1414 * Neither the name of 'pyexcel' nor the names of the contributors
15 may be used to endorse or promote products derived from this software
15 may not be used to endorse or promote products derived from this software
1616 without specific prior written permission.
1717
1818 THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND
00 include README.rst
11 include LICENSE
22 include CHANGELOG.rst
3 include CONTRIBUTORS.rst
34 recursive-include tests *
45 recursive-include docs *
56 recursive-include examples *
00 all: test
11
2 test:
2 test: lint
33 bash test.sh
44
5 doc:
6 bash document.sh
5 install_test:
6 pip install -r tests/requirements.txt
77
8 uml:
9 plantuml -tsvg -o ../_static/images/ docs/source/uml/*.uml
8 lint:
9 bash lint.sh
1010
1111 format:
12 isort -y $(find pyexcel -name "*.py"|xargs echo) $(find tests -name "*.py"|xargs echo)
13 black -l 79 pyexcel
14 black -l 79 tests
15 black -l 79 setup.py
16 lint:
17 bash lint.sh
12 bash format.sh
13
14 git-diff-check:
15 git diff --exit-code
22 ================================================================================
33
44 .. 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
5 :target: https://www.patreon.com/chfw
6
7 .. image:: https://raw.githubusercontent.com/pyexcel/pyexcel-mobans/master/images/awesome-badge.svg
8 :target: https://awesome-python.com/#specific-formats-processing
99
1010 .. image:: https://travis-ci.org/pyexcel/pyexcel.svg?branch=master
1111 :target: http://travis-ci.org/pyexcel/pyexcel
1313 .. image:: https://codecov.io/gh/pyexcel/pyexcel/branch/master/graph/badge.svg
1414 :target: https://codecov.io/gh/pyexcel/pyexcel
1515
16 .. image:: https://badge.fury.io/py/pyexcel.svg
17 :target: https://pypi.org/project/pyexcel
18
19 .. image:: https://anaconda.org/conda-forge/pyexcel/badges/version.svg
20 :target: https://anaconda.org/conda-forge/pyexcel
21
22 .. image:: https://pepy.tech/badge/pyexcel/month
23 :target: https://pepy.tech/project/pyexcel
24
25 .. image:: https://anaconda.org/conda-forge/pyexcel/badges/downloads.svg
26 :target: https://anaconda.org/conda-forge/pyexcel
27
1628 .. image:: https://img.shields.io/gitter/room/gitterHQ/gitter.svg
1729 :target: https://gitter.im/pyexcel/Lobby
1830
31 .. image:: https://img.shields.io/static/v1?label=continuous%20templating&message=%E6%A8%A1%E7%89%88%E6%9B%B4%E6%96%B0&color=blue&style=flat-square
32 :target: https://moban.readthedocs.io/en/latest/#at-scale-continous-templating-for-open-source-projects
33
34 .. image:: https://img.shields.io/static/v1?label=coding%20style&message=black&color=black&style=flat-square
35 :target: https://github.com/psf/black
1936 .. image:: https://readthedocs.org/projects/pyexcel/badge/?version=latest
2037 :target: http://pyexcel.readthedocs.org/en/latest/
2138
2340 ================================================================================
2441
2542 If your company has embedded pyexcel and its components into a revenue generating
26 product, please support me on `patreon <https://www.patreon.com/bePatron?u=5537627>`_
43 product, please support me on github, `patreon <https://www.patreon.com/bePatron?u=5537627>`_
2744 or `bounty source <https://salt.bountysource.com/teams/chfw-pyexcel>`_ to maintain
2845 the project and develop it further.
2946
4259
4360 Fonts, colors and charts are not supported.
4461
62 Nor to read password protected xls, xlsx and ods files.
63
4564 Introduction
4665 ================================================================================
4766
4867 Feature Highlights
4968 ===================
69
70 .. image:: https://github.com/pyexcel/pyexcel/raw/dev/docs/source/_static/images/architecture.svg
71
5072
5173 1. One application programming interface(API) to handle multiple data sources:
5274
5577 * SQLAlchemy table
5678 * Django Model
5779 * Python data structures: dictionary, records and array
80
5881 2. One API to read and write data in various excel file formats.
59 3. For large data sets, data streaming are supported. A genenerator can be returned to you. Checkout iget_records, iget_array, isave_as and isave_book_as.
82 3. For large data sets, data streaming are supported. A genenerator can be returned to you. Checkout iget_records, iget_array, isave_as and isave_book_as.
6083
6184
6285
81104
82105
83106
84 Usage
85 ===============
86
87 Please note that you will have to use '.sortable.html' in order to replicate the example.
88
89 .. image:: https://github.com/pyexcel/pyexcel-sortable/raw/master/sortable.gif
90
91 .. code-block:: python
92
93 >>> # pip install pyexcel-text==0.2.7.1
94 >>> import pyexcel as p
95 >>> ccs_insight2 = p.Sheet()
96 >>> ccs_insight2.name = "Worldwide Mobile Phone Shipments (Billions), 2017-2021"
97 >>> ccs_insight2.ndjson = """
98 ... {"year": ["2017", "2018", "2019", "2020", "2021"]}
99 ... {"smart phones": [1.53, 1.64, 1.74, 1.82, 1.90]}
100 ... {"feature phones": [0.46, 0.38, 0.30, 0.23, 0.17]}
101 ... """.strip()
102 >>> ccs_insight2
103 pyexcel sheet:
104 +----------------+------+------+------+------+------+
105 | year | 2017 | 2018 | 2019 | 2020 | 2021 |
106 +----------------+------+------+------+------+------+
107 | smart phones | 1.53 | 1.64 | 1.74 | 1.82 | 1.9 |
108 +----------------+------+------+------+------+------+
109 | feature phones | 0.46 | 0.38 | 0.3 | 0.23 | 0.17 |
110 +----------------+------+------+------+------+------+
111
112
113
114 Suppose you have the following data in a dictionary:
115
116 ========= ====
117 Name Age
118 ========= ====
119 Adam 28
120 Beatrice 29
121 Ceri 30
122 Dean 26
123 ========= ====
124
125 you can easily save it into an excel file using the following code:
107 One liners
108 ================================================================================
109
110 This section shows you how to get data from your excel files and how to
111 export data to excel files in **one line**
112
113 Read from the excel files
114 --------------------------------------------------------------------------------
115
116 Get a list of dictionaries
117 ********************************************************************************
118
119
120 Suppose you want to process the following coffee data (data source `coffee chart <https://cspinet.org/eating-healthy/ingredients-of-concern/caffeine-chart>`_ on the center for science in the public interest):
121
122
123 Top 5 coffeine drinks:
124
125 ===================================== =============== =============
126 Coffees Serving Size Caffeine (mg)
127 Starbucks Coffee Blonde Roast venti(20 oz) 475
128 Dunkin' Donuts Coffee with Turbo Shot large(20 oz.) 398
129 Starbucks Coffee Pike Place Roast grande(16 oz.) 310
130 Panera Coffee Light Roast regular(16 oz.) 300
131 ===================================== =============== =============
132
133
134 Let's get a list of dictionary out from the xls file:
135
136 .. code-block:: python
137
138 >>> records = p.get_records(file_name="your_file.xls")
139
140 And let's check what do we have:
141
142 .. code-block:: python
143
144 >>> for r in records:
145 ... print(f"{r['Serving Size']} of {r['Coffees']} has {r['Caffeine (mg)']} mg")
146 venti(20 oz) of Starbucks Coffee Blonde Roast has 475 mg
147 large(20 oz.) of Dunkin' Donuts Coffee with Turbo Shot has 398 mg
148 grande(16 oz.) of Starbucks Coffee Pike Place Roast has 310 mg
149 regular(16 oz.) of Panera Coffee Light Roast has 300 mg
150
151
152 Get two dimensional array
153 ********************************************************************************
154
155 Instead, what if you have to use `pyexcel.get_array` to do the same:
156
157 .. code-block:: python
158
159 >>> for row in p.get_array(file_name="your_file.xls", start_row=1):
160 ... print(f"{row[1]} of {row[0]} has {row[2]} mg")
161 venti(20 oz) of Starbucks Coffee Blonde Roast has 475 mg
162 large(20 oz.) of Dunkin' Donuts Coffee with Turbo Shot has 398 mg
163 grande(16 oz.) of Starbucks Coffee Pike Place Roast has 310 mg
164 regular(16 oz.) of Panera Coffee Light Roast has 300 mg
165
166
167 where `start_row` skips the header row.
168
169
170 Get a dictionary
171 ********************************************************************************
172
173 You can get a dictionary too:
174
175 Now let's get a dictionary out from the spreadsheet:
176
177 .. code-block:: python
178
179 >>> my_dict = p.get_dict(file_name="your_file.xls", name_columns_by_row=0)
180
181 And check what do we have:
182
183 .. code-block:: python
184
185 >>> from pyexcel._compact import OrderedDict
186 >>> isinstance(my_dict, OrderedDict)
187 True
188 >>> for key, values in my_dict.items():
189 ... print(key + " : " + ','.join([str(item) for item in values]))
190 Coffees : Starbucks Coffee Blonde Roast,Dunkin' Donuts Coffee with Turbo Shot,Starbucks Coffee Pike Place Roast,Panera Coffee Light Roast
191 Serving Size : venti(20 oz),large(20 oz.),grande(16 oz.),regular(16 oz.)
192 Caffeine (mg) : 475,398,310,300
193
194 Please note that my_dict is an OrderedDict.
195
196 Get a dictionary of two dimensional array
197 ********************************************************************************
198
199
200 Suppose you have a multiple sheet book as the following:
201
202
203 pyexcel:Sheet 1:
204
205 ===================== = =
206 1 2 3
207 4 5 6
208 7 8 9
209 ===================== = =
210
211 pyexcel:Sheet 2:
212
213 ===================== = =
214 X Y Z
215 1 2 3
216 4 5 6
217 ===================== = =
218
219 pyexcel:Sheet 3:
220
221 ===================== = =
222 O P Q
223 3 2 1
224 4 3 2
225 ===================== = =
226
227
228 Here is the code to obtain those sheets as a single dictionary:
229
230 .. code-block:: python
231
232 >>> book_dict = p.get_book_dict(file_name="book.xls")
233
234 And check:
235
236 .. code-block:: python
237
238 >>> isinstance(book_dict, OrderedDict)
239 True
240 >>> import json
241 >>> for key, item in book_dict.items():
242 ... print(json.dumps({key: item}))
243 {"Sheet 1": [[1, 2, 3], [4, 5, 6], [7, 8, 9]]}
244 {"Sheet 2": [["X", "Y", "Z"], [1, 2, 3], [4, 5, 6]]}
245 {"Sheet 3": [["O", "P", "Q"], [3, 2, 1], [4, 3, 2]]}
246
247
248 Write data
249 ---------------------------------------------
250
251 Export an array
252 **********************
253
254 Suppose you have the following array:
255
256 .. code-block:: python
257
258 >>> data = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
259
260 And here is the code to save it as an excel file :
261
262 .. code-block:: python
263
264 >>> p.save_as(array=data, dest_file_name="example.xls")
265
266 Let's verify it:
267
268 .. code-block:: python
269
270 >>> p.get_sheet(file_name="example.xls")
271 pyexcel_sheet1:
272 +---+---+---+
273 | 1 | 2 | 3 |
274 +---+---+---+
275 | 4 | 5 | 6 |
276 +---+---+---+
277 | 7 | 8 | 9 |
278 +---+---+---+
279
280
281 And here is the code to save it as a csv file :
282
283 .. code-block:: python
284
285 >>> p.save_as(array=data,
286 ... dest_file_name="example.csv",
287 ... dest_delimiter=':')
288
289 Let's verify it:
290
291 .. code-block:: python
292
293 >>> with open("example.csv") as f:
294 ... for line in f.readlines():
295 ... print(line.rstrip())
296 ...
297 1:2:3
298 4:5:6
299 7:8:9
300
301 Export a list of dictionaries
302 **********************************
303
304 .. code-block:: python
305
306 >>> records = [
307 ... {"year": 1903, "country": "Germany", "speed": "206.7km/h"},
308 ... {"year": 1964, "country": "Japan", "speed": "210km/h"},
309 ... {"year": 2008, "country": "China", "speed": "350km/h"}
310 ... ]
311 >>> p.save_as(records=records, dest_file_name='high_speed_rail.xls')
312
313
314 Export a dictionary of single key value pair
315 ********************************************************************************
316
317 .. code-block:: python
318
319 >>> henley_on_thames_facts = {
320 ... "area": "5.58 square meters",
321 ... "population": "11,619",
322 ... "civial parish": "Henley-on-Thames",
323 ... "latitude": "51.536",
324 ... "longitude": "-0.898"
325 ... }
326 >>> p.save_as(adict=henley_on_thames_facts, dest_file_name='henley.xlsx')
327
328
329 Export a dictionary of single dimensonal array
330 ********************************************************************************
331
332 .. code-block:: python
333
334 >>> ccs_insights = {
335 ... "year": ["2017", "2018", "2019", "2020", "2021"],
336 ... "smart phones": [1.53, 1.64, 1.74, 1.82, 1.90],
337 ... "feature phones": [0.46, 0.38, 0.30, 0.23, 0.17]
338 ... }
339 >>> p.save_as(adict=ccs_insights, dest_file_name='ccs.csv')
340
341
342 Export a dictionary of two dimensional array as a book
343 ********************************************************************************
344
345 Suppose you want to save the below dictionary to an excel file :
346
347 .. code-block:: python
348
349 >>> a_dictionary_of_two_dimensional_arrays = {
350 ... 'Sheet 1':
351 ... [
352 ... [1.0, 2.0, 3.0],
353 ... [4.0, 5.0, 6.0],
354 ... [7.0, 8.0, 9.0]
355 ... ],
356 ... 'Sheet 2':
357 ... [
358 ... ['X', 'Y', 'Z'],
359 ... [1.0, 2.0, 3.0],
360 ... [4.0, 5.0, 6.0]
361 ... ],
362 ... 'Sheet 3':
363 ... [
364 ... ['O', 'P', 'Q'],
365 ... [3.0, 2.0, 1.0],
366 ... [4.0, 3.0, 2.0]
367 ... ]
368 ... }
369
370 Here is the code:
371
372 .. code-block:: python
373
374 >>> p.save_book_as(
375 ... bookdict=a_dictionary_of_two_dimensional_arrays,
376 ... dest_file_name="book.xls"
377 ... )
378
379 If you want to preserve the order of sheets in your dictionary, you have to
380 pass on an ordered dictionary to the function itself. For example:
381
382 .. code-block:: python
383
384 >>> data = OrderedDict()
385 >>> data.update({"Sheet 2": a_dictionary_of_two_dimensional_arrays['Sheet 2']})
386 >>> data.update({"Sheet 1": a_dictionary_of_two_dimensional_arrays['Sheet 1']})
387 >>> data.update({"Sheet 3": a_dictionary_of_two_dimensional_arrays['Sheet 3']})
388 >>> p.save_book_as(bookdict=data, dest_file_name="book.xls")
389
390 Let's verify its order:
391
392 .. code-block:: python
393
394 >>> book_dict = p.get_book_dict(file_name="book.xls")
395 >>> for key, item in book_dict.items():
396 ... print(json.dumps({key: item}))
397 {"Sheet 2": [["X", "Y", "Z"], [1, 2, 3], [4, 5, 6]]}
398 {"Sheet 1": [[1, 2, 3], [4, 5, 6], [7, 8, 9]]}
399 {"Sheet 3": [["O", "P", "Q"], [3, 2, 1], [4, 3, 2]]}
400
401 Please notice that "Sheet 2" is the first item in the *book_dict*, meaning the order of sheets are preserved.
402
403
404 Transcoding
405 -------------------------------------------
406
407 .. note::
408
409 Please note that `pyexcel-cli` can perform file transcoding at command line.
410 No need to open your editor, save the problem, then python run.
411
412
413 The following code does a simple file format transcoding from xls to csv:
414
415 .. code-block:: python
416
417 >>> p.save_as(file_name="birth.xls", dest_file_name="birth.csv")
418
419 Again it is really simple. Let's verify what we have gotten:
420
421 .. code-block:: python
422
423 >>> sheet = p.get_sheet(file_name="birth.csv")
424 >>> sheet
425 birth.csv:
426 +-------+--------+----------+
427 | name | weight | birth |
428 +-------+--------+----------+
429 | Adam | 3.4 | 03/02/15 |
430 +-------+--------+----------+
431 | Smith | 4.2 | 12/11/14 |
432 +-------+--------+----------+
433
434 .. NOTE::
435
436 Please note that csv(comma separate value) file is pure text file. Formula, charts, images and formatting in xls file will disappear no matter which transcoding tool you use. Hence, pyexcel is a quick alternative for this transcoding job.
437
438
439 Let use previous example and save it as xlsx instead
440
441 .. code-block:: python
442
443 >>> p.save_as(file_name="birth.xls",
444 ... dest_file_name="birth.xlsx") # change the file extension
445
446 Again let's verify what we have gotten:
447
448 .. code-block:: python
449
450 >>> sheet = p.get_sheet(file_name="birth.xlsx")
451 >>> sheet
452 pyexcel_sheet1:
453 +-------+--------+----------+
454 | name | weight | birth |
455 +-------+--------+----------+
456 | Adam | 3.4 | 03/02/15 |
457 +-------+--------+----------+
458 | Smith | 4.2 | 12/11/14 |
459 +-------+--------+----------+
460
461
462 Excel book merge and split operation in one line
463 --------------------------------------------------------------------------------
464
465 Merge all excel files in directory into a book where each file become a sheet
466 ********************************************************************************
467
468 The following code will merge every excel files into one file, say "output.xls":
469
470 .. code-block:: python
471
472 from pyexcel.cookbook import merge_all_to_a_book
473 import glob
474
475
476 merge_all_to_a_book(glob.glob("your_csv_directory\*.csv"), "output.xls")
477
478 You can mix and match with other excel formats: xls, xlsm and ods. For example, if you are sure you have only xls, xlsm, xlsx, ods and csv files in `your_excel_file_directory`, you can do the following:
479
480 .. code-block:: python
481
482 from pyexcel.cookbook import merge_all_to_a_book
483 import glob
484
485
486 merge_all_to_a_book(glob.glob("your_excel_file_directory\*.*"), "output.xls")
487
488 Split a book into single sheet files
489 ****************************************
490
491
492 Suppose you have many sheets in a work book and you would like to separate each into a single sheet excel file. You can easily do this:
493
494 .. code-block:: python
495
496 >>> from pyexcel.cookbook import split_a_book
497 >>> split_a_book("megabook.xls", "output.xls")
498 >>> import glob
499 >>> outputfiles = glob.glob("*_output.xls")
500 >>> for file in sorted(outputfiles):
501 ... print(file)
502 ...
503 Sheet 1_output.xls
504 Sheet 2_output.xls
505 Sheet 3_output.xls
506
507 for the output file, you can specify any of the supported formats
508
509
510 Extract just one sheet from a book
511 *************************************
512
513
514 Suppose you just want to extract one sheet from many sheets that exists in a work book and you would like to separate it into a single sheet excel file. You can easily do this:
515
516 .. code-block:: python
517
518 >>> from pyexcel.cookbook import extract_a_sheet_from_a_book
519 >>> extract_a_sheet_from_a_book("megabook.xls", "Sheet 1", "output.xls")
520 >>> if os.path.exists("Sheet 1_output.xls"):
521 ... print("Sheet 1_output.xls exists")
522 ...
523 Sheet 1_output.xls exists
524
525 for the output file, you can specify any of the supported formats
526
527
528 Hidden feature: partial read
529 ===============================================
530
531 Most pyexcel users do not know, but other library users were requesting `the similar features <https://github.com/jazzband/tablib/issues/467>`_
532
533
534 When you are dealing with huge amount of data, e.g. 64GB, obviously you would not
535 like to fill up your memory with those data. What you may want to do is, record
536 data from Nth line, take M records and stop. And you only want to use your memory
537 for the M records, not for beginning part nor for the tail part.
538
539 Hence partial read feature is developed to read partial data into memory for
540 processing.
541
542 You can paginate by row, by column and by both, hence you dictate what portion of the
543 data to read back. But remember only row limit features help you save memory. Let's
544 you use this feature to record data from Nth column, take M number of columns and skip
545 the rest. You are not going to reduce your memory footprint.
546
547 Why did not I see above benefit?
548 --------------------------------------------------------------------------------
549
550 This feature depends heavily on the implementation details.
551
552 `pyexcel-xls`_ (xlrd), `pyexcel-xlsx`_ (openpyxl), `pyexcel-ods`_ (odfpy) and
553 `pyexcel-ods3`_ (pyexcel-ezodf) will read all data into memory. Because xls,
554 xlsx and ods file are effective a zipped folder, all four will unzip the folder
555 and read the content in xml format in **full**, so as to make sense of all details.
556
557 Hence, during the partial data is been returned, the memory consumption won't
558 differ from reading the whole data back. Only after the partial
559 data is returned, the memory comsumption curve shall jump the cliff. So pagination
560 code here only limits the data returned to your program.
561
562 With that said, `pyexcel-xlsxr`_, `pyexcel-odsr`_ and `pyexcel-htmlr`_ DOES read
563 partial data into memory. Those three are implemented in such a way that they
564 consume the xml(html) when needed. When they have read designated portion of the
565 data, they stop, even if they are half way through.
566
567 In addition, pyexcel's csv readers can read partial data into memory too.
568
569
570 Let's assume the following file is a huge csv file:
571
572 .. code-block:: python
573
574 >>> import datetime
575 >>> import pyexcel as pe
576 >>> data = [
577 ... [1, 21, 31],
578 ... [2, 22, 32],
579 ... [3, 23, 33],
580 ... [4, 24, 34],
581 ... [5, 25, 35],
582 ... [6, 26, 36]
583 ... ]
584 >>> pe.save_as(array=data, dest_file_name="your_file.csv")
585
586
587 And let's pretend to read partial data:
588
589
590 .. code-block:: python
591
592 >>> pe.get_sheet(file_name="your_file.csv", start_row=2, row_limit=3)
593 your_file.csv:
594 +---+----+----+
595 | 3 | 23 | 33 |
596 +---+----+----+
597 | 4 | 24 | 34 |
598 +---+----+----+
599 | 5 | 25 | 35 |
600 +---+----+----+
601
602 And you could as well do the same for columns:
603
604 .. code-block:: python
605
606 >>> pe.get_sheet(file_name="your_file.csv", start_column=1, column_limit=2)
607 your_file.csv:
608 +----+----+
609 | 21 | 31 |
610 +----+----+
611 | 22 | 32 |
612 +----+----+
613 | 23 | 33 |
614 +----+----+
615 | 24 | 34 |
616 +----+----+
617 | 25 | 35 |
618 +----+----+
619 | 26 | 36 |
620 +----+----+
621
622 Obvious, you could do both at the same time:
623
624 .. code-block:: python
625
626 >>> pe.get_sheet(file_name="your_file.csv",
627 ... start_row=2, row_limit=3,
628 ... start_column=1, column_limit=2)
629 your_file.csv:
630 +----+----+
631 | 23 | 33 |
632 +----+----+
633 | 24 | 34 |
634 +----+----+
635 | 25 | 35 |
636 +----+----+
637
638
639 The pagination support is available across all pyexcel plugins.
640
641 .. note::
642
643 No column pagination support for query sets as data source.
644
645
646 Formatting while transcoding a big data file
647 --------------------------------------------------------------------------------
648
649 If you are transcoding a big data set, conventional formatting method would not
650 help unless a on-demand free RAM is available. However, there is a way to minimize
651 the memory footprint of pyexcel while the formatting is performed.
652
653 Let's continue from previous example. Suppose we want to transcode "your_file.csv"
654 to "your_file.xls" but increase each element by 1.
655
656 What we can do is to define a row renderer function as the following:
657
658 .. code-block:: python
659
660 >>> def increment_by_one(row):
661 ... for element in row:
662 ... yield element + 1
663
664 Then pass it onto save_as function using row_renderer:
665
666 .. code-block:: python
667
668 >>> pe.isave_as(file_name="your_file.csv",
669 ... row_renderer=increment_by_one,
670 ... dest_file_name="your_file.xlsx")
671
672
673 .. note::
674
675 If the data content is from a generator, isave_as has to be used.
676
677 We can verify if it was done correctly:
678
679 .. code-block:: python
680
681 >>> pe.get_sheet(file_name="your_file.xlsx")
682 your_file.csv:
683 +---+----+----+
684 | 2 | 22 | 32 |
685 +---+----+----+
686 | 3 | 23 | 33 |
687 +---+----+----+
688 | 4 | 24 | 34 |
689 +---+----+----+
690 | 5 | 25 | 35 |
691 +---+----+----+
692 | 6 | 26 | 36 |
693 +---+----+----+
694 | 7 | 27 | 37 |
695 +---+----+----+
696
697
698 Stream APIs for big file : A set of two liners
699 ================================================================================
700
701 When you are dealing with **BIG** excel files, you will want **pyexcel** to use
702 constant memory.
703
704 This section shows you how to get data from your **BIG** excel files and how to
705 export data to excel files in **two lines** at most, without eating all
706 your computer memory.
707
708
709 Two liners for get data from big excel files
710 --------------------------------------------------------------------------------
711
712 Get a list of dictionaries
713 ********************************************************************************
714
715
716
717 Suppose you want to process the following coffee data again:
718
719 Top 5 coffeine drinks:
720
721 ===================================== =============== =============
722 Coffees Serving Size Caffeine (mg)
723 Starbucks Coffee Blonde Roast venti(20 oz) 475
724 Dunkin' Donuts Coffee with Turbo Shot large(20 oz.) 398
725 Starbucks Coffee Pike Place Roast grande(16 oz.) 310
726 Panera Coffee Light Roast regular(16 oz.) 300
727 ===================================== =============== =============
728
729
730 Let's get a list of dictionary out from the xls file:
731
732 .. code-block:: python
733
734 >>> records = p.iget_records(file_name="your_file.xls")
735
736 And let's check what do we have:
737
738 .. code-block:: python
739
740 >>> for r in records:
741 ... print(f"{r['Serving Size']} of {r['Coffees']} has {r['Caffeine (mg)']} mg")
742 venti(20 oz) of Starbucks Coffee Blonde Roast has 475 mg
743 large(20 oz.) of Dunkin' Donuts Coffee with Turbo Shot has 398 mg
744 grande(16 oz.) of Starbucks Coffee Pike Place Roast has 310 mg
745 regular(16 oz.) of Panera Coffee Light Roast has 300 mg
746
747 Please do not forgot the second line to close the opened file handle:
748
749 .. code-block:: python
750
751 >>> p.free_resources()
752
753 Get two dimensional array
754 ********************************************************************************
755
756 Instead, what if you have to use `pyexcel.get_array` to do the same:
757
758 .. code-block:: python
759
760 >>> for row in p.iget_array(file_name="your_file.xls", start_row=1):
761 ... print(f"{row[1]} of {row[0]} has {row[2]} mg")
762 venti(20 oz) of Starbucks Coffee Blonde Roast has 475 mg
763 large(20 oz.) of Dunkin' Donuts Coffee with Turbo Shot has 398 mg
764 grande(16 oz.) of Starbucks Coffee Pike Place Roast has 310 mg
765 regular(16 oz.) of Panera Coffee Light Roast has 300 mg
766
767 Again, do not forgot the second line:
768
769 .. code-block:: python
770
771 >>> p.free_resources()
772
773 where `start_row` skips the header row.
774
775 Data export in one liners
776 ---------------------------------------------
777
778 Export an array
779 **********************
780
781 Suppose you have the following array:
782
783 .. code-block:: python
784
785 >>> data = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
786
787 And here is the code to save it as an excel file :
788
789 .. code-block:: python
790
791 >>> p.isave_as(array=data, dest_file_name="example.xls")
792
793 But the following line is not required because the data source
794 are not file sources:
795
796 .. code-block:: python
797
798 >>> # p.free_resources()
799
800 Let's verify it:
801
802 .. code-block:: python
803
804 >>> p.get_sheet(file_name="example.xls")
805 pyexcel_sheet1:
806 +---+---+---+
807 | 1 | 2 | 3 |
808 +---+---+---+
809 | 4 | 5 | 6 |
810 +---+---+---+
811 | 7 | 8 | 9 |
812 +---+---+---+
813
814
815 And here is the code to save it as a csv file :
816
817 .. code-block:: python
818
819 >>> p.isave_as(array=data,
820 ... dest_file_name="example.csv",
821 ... dest_delimiter=':')
822
823 Let's verify it:
824
825 .. code-block:: python
826
827 >>> with open("example.csv") as f:
828 ... for line in f.readlines():
829 ... print(line.rstrip())
830 ...
831 1:2:3
832 4:5:6
833 7:8:9
834
835 Export a list of dictionaries
836 **********************************
837
838 .. code-block:: python
839
840 >>> records = [
841 ... {"year": 1903, "country": "Germany", "speed": "206.7km/h"},
842 ... {"year": 1964, "country": "Japan", "speed": "210km/h"},
843 ... {"year": 2008, "country": "China", "speed": "350km/h"}
844 ... ]
845 >>> p.isave_as(records=records, dest_file_name='high_speed_rail.xls')
846
847 Export a dictionary of single key value pair
848 ********************************************************************************
849
850 .. code-block:: python
851
852 >>> henley_on_thames_facts = {
853 ... "area": "5.58 square meters",
854 ... "population": "11,619",
855 ... "civial parish": "Henley-on-Thames",
856 ... "latitude": "51.536",
857 ... "longitude": "-0.898"
858 ... }
859 >>> p.isave_as(adict=henley_on_thames_facts, dest_file_name='henley.xlsx')
860
861 Export a dictionary of single dimensonal array
862 ********************************************************************************
863
864 .. code-block:: python
865
866 >>> ccs_insights = {
867 ... "year": ["2017", "2018", "2019", "2020", "2021"],
868 ... "smart phones": [1.53, 1.64, 1.74, 1.82, 1.90],
869 ... "feature phones": [0.46, 0.38, 0.30, 0.23, 0.17]
870 ... }
871 >>> p.isave_as(adict=ccs_insights, dest_file_name='ccs.csv')
872 >>> p.free_resources()
873
874 Export a dictionary of two dimensional array as a book
875 ********************************************************************************
876
877 Suppose you want to save the below dictionary to an excel file :
878
879 .. code-block:: python
880
881 >>> a_dictionary_of_two_dimensional_arrays = {
882 ... 'Sheet 1':
883 ... [
884 ... [1.0, 2.0, 3.0],
885 ... [4.0, 5.0, 6.0],
886 ... [7.0, 8.0, 9.0]
887 ... ],
888 ... 'Sheet 2':
889 ... [
890 ... ['X', 'Y', 'Z'],
891 ... [1.0, 2.0, 3.0],
892 ... [4.0, 5.0, 6.0]
893 ... ],
894 ... 'Sheet 3':
895 ... [
896 ... ['O', 'P', 'Q'],
897 ... [3.0, 2.0, 1.0],
898 ... [4.0, 3.0, 2.0]
899 ... ]
900 ... }
901
902 Here is the code:
903
904 .. code-block:: python
905
906 >>> p.isave_book_as(
907 ... bookdict=a_dictionary_of_two_dimensional_arrays,
908 ... dest_file_name="book.xls"
909 ... )
910
911 If you want to preserve the order of sheets in your dictionary, you have to
912 pass on an ordered dictionary to the function itself. For example:
913
914 .. code-block:: python
915
916 >>> from pyexcel._compact import OrderedDict
917 >>> data = OrderedDict()
918 >>> data.update({"Sheet 2": a_dictionary_of_two_dimensional_arrays['Sheet 2']})
919 >>> data.update({"Sheet 1": a_dictionary_of_two_dimensional_arrays['Sheet 1']})
920 >>> data.update({"Sheet 3": a_dictionary_of_two_dimensional_arrays['Sheet 3']})
921 >>> p.isave_book_as(bookdict=data, dest_file_name="book.xls")
922 >>> p.free_resources()
923
924 Let's verify its order:
925
926 .. code-block:: python
927
928 >>> import json
929 >>> book_dict = p.get_book_dict(file_name="book.xls")
930 >>> for key, item in book_dict.items():
931 ... print(json.dumps({key: item}))
932 {"Sheet 2": [["X", "Y", "Z"], [1, 2, 3], [4, 5, 6]]}
933 {"Sheet 1": [[1, 2, 3], [4, 5, 6], [7, 8, 9]]}
934 {"Sheet 3": [["O", "P", "Q"], [3, 2, 1], [4, 3, 2]]}
935
936 Please notice that "Sheet 2" is the first item in the *book_dict*, meaning the order of sheets are preserved.
937
938
939 File format transcoding on one line
940 -------------------------------------------
941
942 .. note::
943
944 Please note that the following file transcoding could be with zero line. Please
945 install pyexcel-cli and you will do the transcode in one command. No need to
946 open your editor, save the problem, then python run.
947
948
949 The following code does a simple file format transcoding from xls to csv:
126950
127951 .. code-block:: python
128952
129953 >>> import pyexcel
130 >>> # make sure you had pyexcel-xls installed
131 >>> a_list_of_dictionaries = [
132 ... {
133 ... "Name": 'Adam',
134 ... "Age": 28
135 ... },
136 ... {
137 ... "Name": 'Beatrice',
138 ... "Age": 29
139 ... },
140 ... {
141 ... "Name": 'Ceri',
142 ... "Age": 30
143 ... },
144 ... {
145 ... "Name": 'Dean',
146 ... "Age": 26
147 ... }
148 ... ]
149 >>> pyexcel.save_as(records=a_list_of_dictionaries, dest_file_name="your_file.xls")
150
151
152 And here's how to obtain the records:
153
154 .. code-block:: python
155
156 >>> import pyexcel as p
157 >>> records = p.iget_records(file_name="your_file.xls")
158 >>> for record in records:
159 ... print("%s is aged at %d" % (record['Name'], record['Age']))
160 Adam is aged at 28
161 Beatrice is aged at 29
162 Ceri is aged at 30
163 Dean is aged at 26
164 >>> p.free_resources()
165
166
167 Advanced usage :fire:
168 ----------------------
169
170 If you are dealing with big data, please consider these usages:
171
172 >>> def increase_everyones_age(generator):
173 ... for row in generator:
174 ... row['Age'] += 1
175 ... yield row
176 >>> def duplicate_each_record(generator):
177 ... for row in generator:
178 ... yield row
179 ... yield row
180 >>> records = p.iget_records(file_name="your_file.xls")
181 >>> io=p.isave_as(records=duplicate_each_record(increase_everyones_age(records)),
182 ... dest_file_type='csv', dest_lineterminator='\n')
183 >>> print(io.getvalue())
184 Age,Name
185 29,Adam
186 29,Adam
187 30,Beatrice
188 30,Beatrice
189 31,Ceri
190 31,Ceri
191 27,Dean
192 27,Dean
193 <BLANKLINE>
194
195 Two advantages of above method:
196
197 #. Add as many wrapping functions as you want.
198 #. Constant memory consumption
954 >>> p.save_as(file_name="birth.xls", dest_file_name="birth.csv")
955
956 Again it is really simple. Let's verify what we have gotten:
957
958 .. code-block:: python
959
960 >>> sheet = p.get_sheet(file_name="birth.csv")
961 >>> sheet
962 birth.csv:
963 +-------+--------+----------+
964 | name | weight | birth |
965 +-------+--------+----------+
966 | Adam | 3.4 | 03/02/15 |
967 +-------+--------+----------+
968 | Smith | 4.2 | 12/11/14 |
969 +-------+--------+----------+
970
971 .. note::
972
973 Please note that csv(comma separate value) file is pure text file. Formula, charts, images and formatting in xls file will disappear no matter which transcoding tool you use. Hence, pyexcel is a quick alternative for this transcoding job.
974
975
976 Let use previous example and save it as xlsx instead
977
978 .. code-block:: python
979
980 >>> import pyexcel
981 >>> p.isave_as(file_name="birth.xls",
982 ... dest_file_name="birth.xlsx") # change the file extension
983
984 Again let's verify what we have gotten:
985
986 .. code-block:: python
987
988 >>> sheet = p.get_sheet(file_name="birth.xlsx")
989 >>> sheet
990 pyexcel_sheet1:
991 +-------+--------+----------+
992 | name | weight | birth |
993 +-------+--------+----------+
994 | Adam | 3.4 | 03/02/15 |
995 +-------+--------+----------+
996 | Smith | 4.2 | 12/11/14 |
997 +-------+--------+----------+
998
199999
2001000 Available Plugins
2011001 =================
2051005
2061006 .. table:: A list of file formats supported by external plugins
2071007
208 ======================== ======================= ================= ==================
209 Package name Supported file formats Dependencies Python versions
210 ======================== ======================= ================= ==================
211 `pyexcel-io`_ csv, csvz [#f1]_, tsv, 2.6, 2.7, 3.3,
212 tsvz [#f2]_ 3.4, 3.5, 3.6
213 pypy
214 `pyexcel-xls`_ xls, xlsx(read only), `xlrd`_, same as above
1008 ======================== ======================= =================
1009 Package name Supported file formats Dependencies
1010 ======================== ======================= =================
1011 `pyexcel-io`_ csv, csvz [#f1]_, tsv,
1012 tsvz [#f2]_
1013 `pyexcel-xls`_ xls, xlsx(read only), `xlrd`_,
2151014 xlsm(read only) `xlwt`_
216 `pyexcel-xlsx`_ xlsx `openpyxl`_ same as above
217 `pyexcel-ods3`_ ods `pyexcel-ezodf`_, 2.6, 2.7, 3.3, 3.4
218 lxml 3.5, 3.6
219 `pyexcel-ods`_ ods `odfpy`_ same as above
220 ======================== ======================= ================= ==================
1015 `pyexcel-xlsx`_ xlsx `openpyxl`_
1016 `pyexcel-ods3`_ ods `pyexcel-ezodf`_,
1017 lxml
1018 `pyexcel-ods`_ ods `odfpy`_
1019 ======================== ======================= =================
2211020
2221021 .. table:: Dedicated file reader and writers
2231022
224 ======================== ======================= ================= ==================
225 Package name Supported file formats Dependencies Python versions
226 ======================== ======================= ================= ==================
227 `pyexcel-xlsxw`_ xlsx(write only) `XlsxWriter`_ Python 2 and 3
228 `pyexcel-xlsxr`_ xlsx(read only) lxml same as above
229 `pyexcel-xlsbr`_ xlsx(read only) pyxlsb same as above
230 `pyexcel-odsr`_ read only for ods, fods lxml same as above
231 `pyexcel-odsw`_ write only for ods loxun same as above
232 `pyexcel-htmlr`_ html(read only) lxml,html5lib same as above
233 `pyexcel-pdfr`_ pdf(read only) pdftables Python 2 only.
234 ======================== ======================= ================= ==================
1023 ======================== ======================= =================
1024 Package name Supported file formats Dependencies
1025 ======================== ======================= =================
1026 `pyexcel-xlsxw`_ xlsx(write only) `XlsxWriter`_
1027 `pyexcel-libxlsxw`_ xlsx(write only) `libxlsxwriter`_
1028 `pyexcel-xlsxr`_ xlsx(read only) lxml
1029 `pyexcel-xlsbr`_ xlsb(read only) pyxlsb
1030 `pyexcel-odsr`_ read only for ods, fods lxml
1031 `pyexcel-odsw`_ write only for ods loxun
1032 `pyexcel-htmlr`_ html(read only) lxml,html5lib
1033 `pyexcel-pdfr`_ pdf(read only) camelot
1034 ======================== ======================= =================
1035
1036
1037 Plugin shopping guide
1038 ------------------------
1039
1040 Since 2020, all pyexcel-io plugins have dropped the support for python version
1041 lower than 3.6. If you want to use any python verions, please use pyexcel-io
1042 and its plugins version lower than 0.6.0.
1043
1044
1045 Except csv files, xls, xlsx and ods files are a zip of a folder containing a lot of
1046 xml files
1047
1048 The dedicated readers for excel files can stream read
1049
1050
1051 In order to manage the list of plugins installed, you need to use pip to add or remove
1052 a plugin. When you use virtualenv, you can have different plugins per virtual
1053 environment. In the situation where you have multiple plugins that does the same thing
1054 in your environment, you need to tell pyexcel which plugin to use per function call.
1055 For example, pyexcel-ods and pyexcel-odsr, and you want to get_array to use pyexcel-odsr.
1056 You need to append get_array(..., library='pyexcel-odsr').
1057
2351058
2361059
2371060 .. _pyexcel-io: https://github.com/pyexcel/pyexcel-io
2441067 .. _pyexcel-pdfr: https://github.com/pyexcel/pyexcel-pdfr
2451068
2461069 .. _pyexcel-xlsxw: https://github.com/pyexcel/pyexcel-xlsxw
1070 .. _pyexcel-libxlsxw: https://github.com/pyexcel/pyexcel-libxlsxw
2471071 .. _pyexcel-xlsxr: https://github.com/pyexcel/pyexcel-xlsxr
2481072 .. _pyexcel-xlsbr: https://github.com/pyexcel/pyexcel-xlsbr
2491073 .. _pyexcel-htmlr: https://github.com/pyexcel/pyexcel-htmlr
2541078 .. _XlsxWriter: https://github.com/jmcnamara/XlsxWriter
2551079 .. _pyexcel-ezodf: https://github.com/pyexcel/pyexcel-ezodf
2561080 .. _odfpy: https://github.com/eea/odfpy
1081 .. _libxlsxwriter: http://libxlsxwriter.github.io/getting_started.html
2571082
2581083 .. table:: Other data renderers
2591084
2871112 .. _pyexcel-gantt: https://github.com/pyexcel/pyexcel-gantt
2881113 .. _frappe-gantt: https://github.com/frappe/gantt
2891114
290 In order to manage the list of plugins installed, you need to use pip to add or remove
291 a plugin. When you use virtualenv, you can have different plugins per virtual
292 environment. In the situation where you have multiple plugins that does the same thing
293 in your environment, you need to tell pyexcel which plugin to use per function call.
294 For example, pyexcel-ods and pyexcel-odsr, and you want to get_array to use pyexcel-odsr.
295 You need to append get_array(..., library='pyexcel-odsr').
296
2971115 .. rubric:: Footnotes
2981116
2991117 .. [#f1] zipped csv file
3071125 individual developers. This library unites only the data access code.
3081126
3091127
310 .. testcode::
311 :hide:
312
313 >>> import os
314 >>> os.unlink("your_file.xls")
315
3161128
3171129
3181130 License
00 name: pyexcel
11 organisation: pyexcel
22 releases:
3 - changes:
4 - action: Updated
5 details:
6 - "`#233`: dynamically resize the table matrix on set_value. sheet['AA1'] = 'test' will work in this release."
7 version: 0.6.6
8 date: 14.11.2020
9 - changes:
10 - action: Updated
11 details:
12 - "update queryset source to work with pyexcel-io 0.6.0"
13 version: 0.6.5
14 date: 8.10.2020
15 - changes:
16 - action: Updated
17 details:
18 - "`#219`: book created from dict no longer discards order."
19 version: 0.6.4
20 date: 18.08.2020
21 - changes:
22 - action: fixed
23 details:
24 - "`#214`: remove leading and trailing whitespace for column names"
25 - action: removed
26 details:
27 - python 2 compatibility have been permanently removed.
28 version: 0.6.3
29 date: 01.08.2020
30 - changes:
31 - action: fixed
32 details:
33 - "`#109`: Control the column order when write the data output"
34 version: 0.6.2
35 date: 8.06.2020
36 - changes:
37 - action: fixed
38 details:
39 - "`#203`: texttable was dropped out in 0.6.0 as compulsary dependency. end user may experience it when a sheet/table is printed in a shell. otherwise, new user of pyexcel won't see it. As of release date, no issues were created"
40 version: 0.6.1
41 date: 02.05.2020
42 - changes:
43 - action: updated
44 details:
45 - "`#199`: += in place; = + shall return new instance"
46 - "`#195`: documentation update. however small is welcome"
47 - action: removed
48 details:
49 - "Dropping the test support for python version lower than 3.6. v0.6.0 should work with python 2.7 but is not guaranteed to work. Please upgrade to python 3.6+."
50
51 version: 0.6.0
52 date: 21.04.2020
53 - changes:
54 - action: updated
55 details:
56 - "`#185`: fix a bug with http data source. The real fix lies in pyexcel-io v0.5.19. this release just put the version requirement in."
57 version: 0.5.15
58 date: 07.07.2019
359 - changes:
460 - action: updated
561 details:
60116 details:
61117 - '`#126`, dest_sheet_name in save_as will set the sheet name in the output'
62118 - '`#115`, Fix set membership test to run faster in python2'
63 date: unreleased
119 date: 26.03.2018
64120 version: 0.5.8
65121 - changes:
66122 - action: added
0 <?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="836px" preserveAspectRatio="none" style="width:982px;height:836px;" version="1.1" viewBox="0 0 982 836" width="982px" zoomAndPan="magnify"><defs><filter height="300%" id="f1qgfhkuhkkxbe" width="300%" x="-1" y="-1"><feGaussianBlur result="blurOut" stdDeviation="2.0"/><feColorMatrix in="blurOut" result="blurOut2" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .4 0"/><feOffset dx="4.0" dy="4.0" in="blurOut2" result="blurOut3"/><feBlend in="SourceGraphic" in2="blurOut3" mode="normal"/></filter></defs><g><!--cluster pyexcel--><polygon fill="#FFFFFF" filter="url(#f1qgfhkuhkkxbe)" points="88,190,147,190,154,212.4883,676,212.4883,676,365,88,365,88,190" style="stroke: #000000; stroke-width: 2.0;"/><line style="stroke: #000000; stroke-width: 2.0;" x1="88" x2="154" y1="212.4883" y2="212.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="53" x="92" y="205.5352">pyexcel</text><!--cluster plugins--><polygon fill="#FFFFFF" filter="url(#f1qgfhkuhkkxbe)" points="96,286,156,286,163,308.4883,668,308.4883,668,357,96,357,96,286" style="stroke: #000000; stroke-width: 2.0;"/><line style="stroke: #000000; stroke-width: 2.0;" x1="96" x2="163" y1="308.4883" y2="308.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="54" x="100" y="301.5352">plugins</text><!--cluster pyexcel-io--><polygon fill="#FFFFFF" filter="url(#f1qgfhkuhkkxbe)" points="14,382,96,382,103,404.4883,373,404.4883,373,670,14,670,14,382" style="stroke: #000000; stroke-width: 2.0;"/><line style="stroke: #000000; stroke-width: 2.0;" x1="14" x2="103" y1="404.4883" y2="404.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="76" x="18" y="397.5352">pyexcel-io</text><!--cluster io plugins--><polygon fill="#FFFFFF" filter="url(#f1qgfhkuhkkxbe)" points="22,591,101,591,108,613.4883,365,613.4883,365,662,22,662,22,591" style="stroke: #000000; stroke-width: 2.0;"/><line style="stroke: #000000; stroke-width: 2.0;" x1="22" x2="108" y1="613.4883" y2="613.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="73" x="26" y="606.5352">io plugins</text><!--cluster ods--><path d="M387,598 C387,595 389,593 392,593 C395,593 397,595 397,598 C397,595 399,593 402,593 C405,593 407,595 407,598 C407,595 409,593 412,593 C415,593 417,595 417,598 C417,595 419,593 422,593 C425,593 427,595 427,598 C427,595 429,593 432,593 C435,593 437,595 437,598 C437,595 439,593 442,593 C445,593 447,595 447,598 C447,595 449,593 452,593 C455,593 457,595 457,598 C457,595 459,593 462,593 C465,593 467,595 467,598 C467,595 469,593 472,593 C475,593 477,595 477,598 C477,595 479,593 482,593 C485,593 487,595 487,598 C487,595 489,593 492,593 C495,593 497,595 497,598 C497,595 499,593 502,593 C505,593 507,595 507,598 C507,595 509,593 512,593 C515,593 517,595 517,598 C517,595 519,593 522,593 C525,593 527,595 527,598 C527,595 529,593 532,593 C535,593 537,595 537,598 C537,595 539,593 542,593 C545,593 547,595 547,598 C547,595 549,593 552,593 C555,593 557,595 557,598 C557,595 559,593 562,593 C565,593 567,595 567,598 C567,595 569,593 572,593 C575,593 577,595 577,598 C577,595 579,593 582,593 C585,593 587,595 587,598 C587,595 589,593 592,593 C595,593 597,595 597,598 C597,595 599,593 602,593 C605,593 607,595 607,598 C607,595 609,593 612,593 C615,593 617,595 617,598 C617,595 619,593 622,593 C625,593 627,595 627,598 C627,595 629,593 632,593 C635,593 637,595 637,598 C637,595 639,593 642,593 C645,593 647,595 647,598 C647,595 649,593 652,593 C655,593 657,595 657,598 C657,595 659,593 662,593 C665,593 667,595 667,598 C667,595 669,593 672,593 C675,593 677,595 677,598 C677,595 679,593 682,593 C685,593 687,595 687,598 C687,595 689,593 692,593 C695,593 697,595 697,598 C697,595 699,593 702,593 C705,593 707,595 707,598 C707,595 709,593 712,593 C715,593 717,595 717,598 C717,595 719,593 722,593 C725,593 727,595 727,598 C727,595 729,593 732,593 C735,593 737,595 737,598 C737,595 739,593 742,593 C745,593 747,595 747,598 C747,595 749,593 752,593 C755,593 757,595 757,598 C757,595 759,593 762,593 C765,593 767,595 767,598 C767,595 769,593 772,593 C775,593 777,595 777,598 C777,595 779,593 782,593 C785,593 787,595 787,598 C787,595 789,593 792,593 C795,593 797,595 797,598 C800,598 802,600 802,603 C802,606 800,608 797,608 C800,608 802,610 802,613 C802,616 800,618 797,618 C800,618 802,620 802,623 C802,626 800,628 797,628 C800,628 802,630 802,633 C802,636 800,638 797,638 C800,638 802,640 802,643 C802,646 800,648 797,648 C800,648 802,650 802,653 C802,656 800,658 797,658 C797,661 794,663 792,663 C789,663 787,661 787,658 C787,661 784,663 782,663 C779,663 777,661 777,658 C777,661 774,663 772,663 C769,663 767,661 767,658 C767,661 764,663 762,663 C759,663 757,661 757,658 C757,661 754,663 752,663 C749,663 747,661 747,658 C747,661 744,663 742,663 C739,663 737,661 737,658 C737,661 734,663 732,663 C729,663 727,661 727,658 C727,661 724,663 722,663 C719,663 717,661 717,658 C717,661 714,663 712,663 C709,663 707,661 707,658 C707,661 704,663 702,663 C699,663 697,661 697,658 C697,661 694,663 692,663 C689,663 687,661 687,658 C687,661 684,663 682,663 C679,663 677,661 677,658 C677,661 674,663 672,663 C669,663 667,661 667,658 C667,661 664,663 662,663 C659,663 657,661 657,658 C657,661 654,663 652,663 C649,663 647,661 647,658 C647,661 644,663 642,663 C639,663 637,661 637,658 C637,661 634,663 632,663 C629,663 627,661 627,658 C627,661 624,663 622,663 C619,663 617,661 617,658 C617,661 614,663 612,663 C609,663 607,661 607,658 C607,661 604,663 602,663 C599,663 597,661 597,658 C597,661 594,663 592,663 C589,663 587,661 587,658 C587,661 584,663 582,663 C579,663 577,661 577,658 C577,661 574,663 572,663 C569,663 567,661 567,658 C567,661 564,663 562,663 C559,663 557,661 557,658 C557,661 554,663 552,663 C549,663 547,661 547,658 C547,661 544,663 542,663 C539,663 537,661 537,658 C537,661 534,663 532,663 C529,663 527,661 527,658 C527,661 524,663 522,663 C519,663 517,661 517,658 C517,661 514,663 512,663 C509,663 507,661 507,658 C507,661 504,663 502,663 C499,663 497,661 497,658 C497,661 494,663 492,663 C489,663 487,661 487,658 C487,661 484,663 482,663 C479,663 477,661 477,658 C477,661 474,663 472,663 C469,663 467,661 467,658 C467,661 464,663 462,663 C459,663 457,661 457,658 C457,661 454,663 452,663 C449,663 447,661 447,658 C447,661 444,663 442,663 C439,663 437,661 437,658 C437,661 434,663 432,663 C429,663 427,661 427,658 C427,661 424,663 422,663 C419,663 417,661 417,658 C417,661 414,663 412,663 C409,663 407,661 407,658 C407,661 404,663 402,663 C399,663 397,661 397,658 C397,661 394,663 392,663 C389,663 387,661 387,658 C384,658 382,656 382,653 C382,650 384,648 387,648 C384,648 382,646 382,643 C382,640 384,638 387,638 C384,638 382,636 382,633 C382,630 384,628 387,628 C384,628 382,626 382,623 C382,620 384,618 387,618 C384,618 382,616 382,613 C382,610 384,608 387,608 C384,608 382,606 382,603 C382,600 384,598 387,598 " fill="#FFFFFF" filter="url(#f1qgfhkuhkkxbe)" style="stroke: #000000; stroke-width: 2.0;"/><text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="26" x="580" y="617.5352">ods</text><!--cluster xls, xlsx--><path d="M394,389 C394,386 396,384 399,384 C402,384 404,386 404,389 C404,386 406,384 409,384 C412,384 414,386 414,389 C414,386 416,384 419,384 C422,384 424,386 424,389 C424,386 426,384 429,384 C432,384 434,386 434,389 C434,386 436,384 439,384 C442,384 444,386 444,389 C444,386 446,384 449,384 C452,384 454,386 454,389 C454,386 456,384 459,384 C462,384 464,386 464,389 C464,386 466,384 469,384 C472,384 474,386 474,389 C474,386 476,384 479,384 C482,384 484,386 484,389 C484,386 486,384 489,384 C492,384 494,386 494,389 C494,386 496,384 499,384 C502,384 504,386 504,389 C504,386 506,384 509,384 C512,384 514,386 514,389 C514,386 516,384 519,384 C522,384 524,386 524,389 C524,386 526,384 529,384 C532,384 534,386 534,389 C534,386 536,384 539,384 C542,384 544,386 544,389 C544,386 546,384 549,384 C552,384 554,386 554,389 C554,386 556,384 559,384 C562,384 564,386 564,389 C564,386 566,384 569,384 C572,384 574,386 574,389 C574,386 576,384 579,384 C582,384 584,386 584,389 C584,386 586,384 589,384 C592,384 594,386 594,389 C594,386 596,384 599,384 C602,384 604,386 604,389 C604,386 606,384 609,384 C612,384 614,386 614,389 C614,386 616,384 619,384 C622,384 624,386 624,389 C624,386 626,384 629,384 C632,384 634,386 634,389 C634,386 636,384 639,384 C642,384 644,386 644,389 C644,386 646,384 649,384 C652,384 654,386 654,389 C654,386 656,384 659,384 C662,384 664,386 664,389 C664,386 666,384 669,384 C672,384 674,386 674,389 C674,386 676,384 679,384 C682,384 684,386 684,389 C684,386 686,384 689,384 C692,384 694,386 694,389 C694,386 696,384 699,384 C702,384 704,386 704,389 C704,386 706,384 709,384 C712,384 714,386 714,389 C714,386 716,384 719,384 C722,384 724,386 724,389 C724,386 726,384 729,384 C732,384 734,386 734,389 C734,386 736,384 739,384 C742,384 744,386 744,389 C744,386 746,384 749,384 C752,384 754,386 754,389 C754,386 756,384 759,384 C762,384 764,386 764,389 C764,386 766,384 769,384 C772,384 774,386 774,389 C774,386 776,384 779,384 C782,384 784,386 784,389 C784,386 786,384 789,384 C792,384 794,386 794,389 C794,386 796,384 799,384 C802,384 804,386 804,389 C807,389 809,391 809,394 C809,397 807,399 804,399 C807,399 809,401 809,404 C809,407 807,409 804,409 C807,409 809,411 809,414 C809,417 807,419 804,419 C807,419 809,421 809,424 C809,427 807,429 804,429 C807,429 809,431 809,434 C809,437 807,439 804,439 C807,439 809,441 809,444 C809,447 807,449 804,449 C804,452 801,454 799,454 C796,454 794,452 794,449 C794,452 791,454 789,454 C786,454 784,452 784,449 C784,452 781,454 779,454 C776,454 774,452 774,449 C774,452 771,454 769,454 C766,454 764,452 764,449 C764,452 761,454 759,454 C756,454 754,452 754,449 C754,452 751,454 749,454 C746,454 744,452 744,449 C744,452 741,454 739,454 C736,454 734,452 734,449 C734,452 731,454 729,454 C726,454 724,452 724,449 C724,452 721,454 719,454 C716,454 714,452 714,449 C714,452 711,454 709,454 C706,454 704,452 704,449 C704,452 701,454 699,454 C696,454 694,452 694,449 C694,452 691,454 689,454 C686,454 684,452 684,449 C684,452 681,454 679,454 C676,454 674,452 674,449 C674,452 671,454 669,454 C666,454 664,452 664,449 C664,452 661,454 659,454 C656,454 654,452 654,449 C654,452 651,454 649,454 C646,454 644,452 644,449 C644,452 641,454 639,454 C636,454 634,452 634,449 C634,452 631,454 629,454 C626,454 624,452 624,449 C624,452 621,454 619,454 C616,454 614,452 614,449 C614,452 611,454 609,454 C606,454 604,452 604,449 C604,452 601,454 599,454 C596,454 594,452 594,449 C594,452 591,454 589,454 C586,454 584,452 584,449 C584,452 581,454 579,454 C576,454 574,452 574,449 C574,452 571,454 569,454 C566,454 564,452 564,449 C564,452 561,454 559,454 C556,454 554,452 554,449 C554,452 551,454 549,454 C546,454 544,452 544,449 C544,452 541,454 539,454 C536,454 534,452 534,449 C534,452 531,454 529,454 C526,454 524,452 524,449 C524,452 521,454 519,454 C516,454 514,452 514,449 C514,452 511,454 509,454 C506,454 504,452 504,449 C504,452 501,454 499,454 C496,454 494,452 494,449 C494,452 491,454 489,454 C486,454 484,452 484,449 C484,452 481,454 479,454 C476,454 474,452 474,449 C474,452 471,454 469,454 C466,454 464,452 464,449 C464,452 461,454 459,454 C456,454 454,452 454,449 C454,452 451,454 449,454 C446,454 444,452 444,449 C444,452 441,454 439,454 C436,454 434,452 434,449 C434,452 431,454 429,454 C426,454 424,452 424,449 C424,452 421,454 419,454 C416,454 414,452 414,449 C414,452 411,454 409,454 C406,454 404,452 404,449 C404,452 401,454 399,454 C396,454 394,452 394,449 C391,449 389,447 389,444 C389,441 391,439 394,439 C391,439 389,437 389,434 C389,431 391,429 394,429 C391,429 389,427 389,424 C389,421 391,419 394,419 C391,419 389,417 389,414 C389,411 391,409 394,409 C391,409 389,407 389,404 C389,401 391,399 394,399 C391,399 389,397 389,394 C389,391 391,389 394,389 " fill="#FFFFFF" filter="url(#f1qgfhkuhkkxbe)" style="stroke: #000000; stroke-width: 2.0;"/><text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="58" x="570" y="408.5352">xls, xlsx</text><!--entity pyexcel public api--><rect fill="#FEFECE" filter="url(#f1qgfhkuhkkxbe)" height="36.4883" style="stroke: #A80036; stroke-width: 1.5;" width="143" x="96.5" y="217"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="91.5" y="222"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="91.5" y="243.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="123" x="106.5" y="240.5352">pyexcel public api</text><!--entity pyexcel internal api--><rect fill="#FEFECE" filter="url(#f1qgfhkuhkkxbe)" height="36.4883" style="stroke: #A80036; stroke-width: 1.5;" width="154" x="275" y="217"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="270" y="222"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="270" y="243.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="134" x="285" y="240.5352">pyexcel internal api</text><!--entity pyexcel plugin interfaces--><rect fill="#FEFECE" filter="url(#f1qgfhkuhkkxbe)" height="36.4883" style="stroke: #A80036; stroke-width: 1.5;" width="191" x="464.5" y="217"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="459.5" y="222"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="459.5" y="243.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="171" x="474.5" y="240.5352">pyexcel plugin interfaces</text><!--entity data source plugins--><rect fill="#FEFECE" filter="url(#f1qgfhkuhkkxbe)" height="36.4883" style="stroke: #A80036; stroke-width: 1.5;" width="155" x="504.5" y="313"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="499.5" y="318"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="499.5" y="339.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="135" x="514.5" y="336.5352">data source plugins</text><!--entity data renderer plugins--><rect fill="#FEFECE" filter="url(#f1qgfhkuhkkxbe)" height="36.4883" style="stroke: #A80036; stroke-width: 1.5;" width="169" x="300.5" y="313"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="295.5" y="318"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="295.5" y="339.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="149" x="310.5" y="336.5352">data renderer plugins</text><!--entity data parsing plugins--><rect fill="#FEFECE" filter="url(#f1qgfhkuhkkxbe)" height="36.4883" style="stroke: #A80036; stroke-width: 1.5;" width="161" x="104.5" y="313"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="99.5" y="318"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="99.5" y="339.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="141" x="114.5" y="336.5352">data parsing plugins</text><!--entity pyexcel-io public api--><rect fill="#FEFECE" filter="url(#f1qgfhkuhkkxbe)" height="36.4883" style="stroke: #A80036; stroke-width: 1.5;" width="164" x="177" y="409"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="172" y="414"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="172" y="435.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="144" x="187" y="432.5352">pyexcel-io public api</text><!--entity pyexcel-io plugin interfaces--><rect fill="#FEFECE" filter="url(#f1qgfhkuhkkxbe)" height="36.4883" style="stroke: #A80036; stroke-width: 1.5;" width="212" x="153" y="513.5"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="148" y="518.5"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="148" y="539.9883"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="192" x="163" y="537.0352">pyexcel-io plugin interfaces</text><!--entity csv, csvz, tsv, tsvz--><rect fill="#FEFECE" filter="url(#f1qgfhkuhkkxbe)" height="36.4883" style="stroke: #A80036; stroke-width: 1.5;" width="140" x="217" y="618"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="212" y="623"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="212" y="644.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="120" x="227" y="641.5352">csv, csvz, tsv, tsvz</text><!--entity django, sqlalchemy--><rect fill="#FEFECE" filter="url(#f1qgfhkuhkkxbe)" height="36.4883" style="stroke: #A80036; stroke-width: 1.5;" width="152" x="30" y="618"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="25" y="623"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="25" y="644.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="132" x="40" y="641.5352">django, sqlalchemy</text><!--entity pyexcel-ods--><rect fill="#FEFECE" filter="url(#f1qgfhkuhkkxbe)" height="36.4883" style="stroke: #A80036; stroke-width: 1.5;" width="105" x="392.5" y="618"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="387.5" y="623"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="387.5" y="644.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="85" x="402.5" y="641.5352">pyexcel-ods</text><!--entity pyexcel-ods3--><rect fill="#FEFECE" filter="url(#f1qgfhkuhkkxbe)" height="36.4883" style="stroke: #A80036; stroke-width: 1.5;" width="114" x="533" y="618"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="528" y="623"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="528" y="644.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="94" x="543" y="641.5352">pyexcel-ods3</text><!--entity pyexcel-odsr--><rect fill="#FEFECE" filter="url(#f1qgfhkuhkkxbe)" height="36.4883" style="stroke: #A80036; stroke-width: 1.5;" width="111" x="682.5" y="618"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="677.5" y="623"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="677.5" y="644.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="91" x="692.5" y="641.5352">pyexcel-odsr</text><!--entity pyexcel-xls--><rect fill="#FEFECE" filter="url(#f1qgfhkuhkkxbe)" height="36.4883" style="stroke: #A80036; stroke-width: 1.5;" width="100" x="554" y="409"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="549" y="414"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="549" y="435.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="80" x="564" y="432.5352">pyexcel-xls</text><!--entity pyexcel-xlsx--><rect fill="#FEFECE" filter="url(#f1qgfhkuhkkxbe)" height="36.4883" style="stroke: #A80036; stroke-width: 1.5;" width="109" x="689.5" y="409"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="684.5" y="414"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="684.5" y="435.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="89" x="699.5" y="432.5352">pyexcel-xlsx</text><!--entity pyexcel-xlsxw--><rect fill="#FEFECE" filter="url(#f1qgfhkuhkkxbe)" height="36.4883" style="stroke: #A80036; stroke-width: 1.5;" width="120" x="399" y="409"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="394" y="414"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="394" y="435.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="100" x="409" y="432.5352">pyexcel-xlsxw</text><!--entity tabulate--><rect fill="#FFFFFF" filter="url(#f1qgfhkuhkkxbe)" height="52.9766" style="stroke: #A80036; stroke-width: 1.5;" width="117" x="651.5" y="8"/><rect fill="#FFFFFF" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="646.5" y="13"/><rect fill="#FFFFFF" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="646.5" y="50.9766"/><text fill="#000000" font-family="sans-serif" font-size="14" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="97" x="661.5" y="31.5352">«dependency»</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="56" x="682" y="48.0234">tabulate</text><!--entity pygal--><rect fill="#FFFFFF" filter="url(#f1qgfhkuhkkxbe)" height="52.9766" style="stroke: #A80036; stroke-width: 1.5;" width="117" x="499.5" y="8"/><rect fill="#FFFFFF" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="494.5" y="13"/><rect fill="#FFFFFF" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="494.5" y="50.9766"/><text fill="#000000" font-family="sans-serif" font-size="14" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="97" x="509.5" y="31.5352">«dependency»</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="37" x="539.5" y="48.0234">pygal</text><!--entity handsontable--><rect fill="#FFFFFF" filter="url(#f1qgfhkuhkkxbe)" height="52.9766" style="stroke: #A80036; stroke-width: 1.5;" width="117" x="320.5" y="8"/><rect fill="#FFFFFF" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="315.5" y="13"/><rect fill="#FFFFFF" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="315.5" y="50.9766"/><text fill="#000000" font-family="sans-serif" font-size="14" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="97" x="330.5" y="31.5352">«dependency»</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="94" x="332" y="48.0234">handsontable</text><!--entity xlrd--><rect fill="#FFFFFF" filter="url(#f1qgfhkuhkkxbe)" height="52.9766" style="stroke: #A80036; stroke-width: 1.5;" width="117" x="552.5" y="505"/><rect fill="#FFFFFF" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="547.5" y="510"/><rect fill="#FFFFFF" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="547.5" y="547.9766"/><text fill="#000000" font-family="sans-serif" font-size="14" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="97" x="562.5" y="528.5352">«dependency»</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="28" x="597" y="545.0234">xlrd</text><!--entity xlwt--><rect fill="#FFFFFF" filter="url(#f1qgfhkuhkkxbe)" height="52.9766" style="stroke: #A80036; stroke-width: 1.5;" width="117" x="704.5" y="505"/><rect fill="#FFFFFF" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="699.5" y="510"/><rect fill="#FFFFFF" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="699.5" y="547.9766"/><text fill="#000000" font-family="sans-serif" font-size="14" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="97" x="714.5" y="528.5352">«dependency»</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="29" x="748.5" y="545.0234">xlwt</text><!--entity openpyxl--><rect fill="#FFFFFF" filter="url(#f1qgfhkuhkkxbe)" height="52.9766" style="stroke: #A80036; stroke-width: 1.5;" width="117" x="856.5" y="505"/><rect fill="#FFFFFF" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="851.5" y="510"/><rect fill="#FFFFFF" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="851.5" y="547.9766"/><text fill="#000000" font-family="sans-serif" font-size="14" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="97" x="866.5" y="528.5352">«dependency»</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="64" x="883" y="545.0234">openpyxl</text><!--entity xlsxwriter--><rect fill="#FFFFFF" filter="url(#f1qgfhkuhkkxbe)" height="52.9766" style="stroke: #A80036; stroke-width: 1.5;" width="117" x="400.5" y="505"/><rect fill="#FFFFFF" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="395.5" y="510"/><rect fill="#FFFFFF" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="395.5" y="547.9766"/><text fill="#000000" font-family="sans-serif" font-size="14" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="97" x="410.5" y="528.5352">«dependency»</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="69" x="424.5" y="545.0234">xlsxwriter</text><!--entity ezodf--><rect fill="#FFFFFF" filter="url(#f1qgfhkuhkkxbe)" height="52.9766" style="stroke: #A80036; stroke-width: 1.5;" width="117" x="535.5" y="775"/><rect fill="#FFFFFF" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="530.5" y="780"/><rect fill="#FFFFFF" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="530.5" y="817.9766"/><text fill="#000000" font-family="sans-serif" font-size="14" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="97" x="545.5" y="798.5352">«dependency»</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="39" x="574.5" y="815.0234">ezodf</text><!--entity odfpy--><rect fill="#FFFFFF" filter="url(#f1qgfhkuhkkxbe)" height="52.9766" style="stroke: #A80036; stroke-width: 1.5;" width="117" x="383.5" y="775"/><rect fill="#FFFFFF" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="378.5" y="780"/><rect fill="#FFFFFF" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="378.5" y="817.9766"/><text fill="#000000" font-family="sans-serif" font-size="14" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="97" x="393.5" y="798.5352">«dependency»</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="39" x="422.5" y="815.0234">odfpy</text><!--entity pyexcel-text--><rect fill="#FEFECE" filter="url(#f1qgfhkuhkkxbe)" height="36.4883" style="stroke: #A80036; stroke-width: 1.5;" width="107" x="655.5" y="121"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="650.5" y="126"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="650.5" y="147.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="87" x="665.5" y="144.5352">pyexcel-text</text><!--entity pyexcel-pygal--><rect fill="#FEFECE" filter="url(#f1qgfhkuhkkxbe)" height="36.4883" style="stroke: #A80036; stroke-width: 1.5;" width="117" x="501.5" y="121"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="496.5" y="126"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="496.5" y="147.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="97" x="511.5" y="144.5352">pyexcel-pygal</text><!--entity pyexcel-handsontable--><rect fill="#FEFECE" filter="url(#f1qgfhkuhkkxbe)" height="36.4883" style="stroke: #A80036; stroke-width: 1.5;" width="174" x="292" y="121"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="287" y="126"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="287" y="147.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="154" x="302" y="144.5352">pyexcel-handsontable</text><!--link pyexcel public api to pyexcel internal api--><path d="M239.5156,235 C249.4935,235 259.4713,235 269.4492,235 " fill="none" id="pyexcel public api-pyexcel internal api" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="274.667,235,265.667,231,269.667,235,265.667,239,274.667,235" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pyexcel internal api to pyexcel plugin interfaces--><path d="M429.1875,235 C439.1956,235 449.2038,235 459.2119,235 " fill="none" id="pyexcel internal api-pyexcel plugin interfaces" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="464.4456,235,455.4456,231,459.4456,235,455.4456,239,464.4456,235" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pyexcel-text to pyexcel plugin interfaces--><path d="M676.5042,159.9369 C650.1199,176.9362 613.2948,200.6624 588.153,216.8612 " fill="none" id="pyexcel-text-pyexcel plugin interfaces" style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 7.0,7.0;"/><polygon fill="#A80036" points="680.9742,157.0569,671.2421,158.569,676.7711,159.765,675.5751,165.294,680.9742,157.0569" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pyexcel-pygal to pyexcel plugin interfaces--><path d="M560,162.0611 C560,178.8106 560,201.1694 560,216.6977 " fill="none" id="pyexcel-pygal-pyexcel plugin interfaces" style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 7.0,7.0;"/><polygon fill="#A80036" points="560,157.0569,556,166.0569,560,162.0569,564,166.0569,560,157.0569" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pyexcel-handsontable to pyexcel plugin interfaces--><path d="M417.6827,159.5168 C449.7852,176.5435 495.0069,200.5285 525.8007,216.8612 " fill="none" id="pyexcel-handsontable-pyexcel plugin interfaces" style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 7.0,7.0;"/><polygon fill="#A80036" points="413.0447,157.0569,419.1213,164.8077,417.4618,159.3998,422.8698,157.7403,413.0447,157.0569" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pyexcel plugin interfaces to data source plugins--><path d="M564.138,253.0569 C567.68,268.5126 572.8023,290.8647 576.6542,307.6731 " fill="none" id="pyexcel plugin interfaces-data source plugins" style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 7.0,7.0;"/><polygon fill="#A80036" points="577.8057,312.6977,579.694,303.0316,576.6887,307.8241,571.8962,304.8187,577.8057,312.6977" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pyexcel plugin interfaces to data renderer plugins--><path d="M527.0838,253.0569 C497.3576,269.3638 453.6371,293.3476 422.5564,310.3976 " fill="none" id="pyexcel plugin interfaces-data renderer plugins" style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 7.0,7.0;"/><polygon fill="#A80036" points="418.0656,312.8612,427.8801,312.0394,422.4493,310.4564,424.0324,305.0255,418.0656,312.8612" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pyexcel plugin interfaces to data parsing plugins--><path d="M464.174,250.0484 C397.9962,260.9344 316.1607,275.5139 283,286 C262.9306,292.3464 241.5873,301.7978 223.9599,310.403 " fill="none" id="pyexcel plugin interfaces-data parsing plugins" style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 7.0,7.0;"/><polygon fill="#A80036" points="219.0908,312.8043,228.9318,312.4109,223.5751,310.5927,225.3933,305.236,219.0908,312.8043" style="stroke: #A80036; stroke-width: 1.0;"/><!--link data renderer plugins to pyexcel-io public api--><path d="M361.3004,349.0569 C340.2277,365.1122 309.3878,388.6093 287.083,405.6035 " fill="none" id="data renderer plugins-pyexcel-io public api" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="283.0218,408.6977,292.6049,406.4253,286.999,405.6676,287.7567,400.0617,283.0218,408.6977" style="stroke: #A80036; stroke-width: 1.0;"/><!--link data parsing plugins to pyexcel-io public api--><path d="M198.9188,349.0569 C211.0381,364.7791 228.6581,387.6375 241.6851,404.5375 " fill="none" id="data parsing plugins-pyexcel-io public api" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="244.892,408.6977,242.5654,399.1276,241.8394,404.7377,236.2293,404.0117,244.892,408.6977" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pyexcel-io public api to pyexcel-io plugin interfaces--><path d="M259,445.2025 C259,462.6276 259,489.0009 259,508.0045 " fill="none" id="pyexcel-io public api-pyexcel-io plugin interfaces" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="259,513.1555,263,504.1555,259,508.1555,255,504.1555,259,513.1555" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pyexcel-io plugin interfaces to csv, csvz, tsv, tsvz--><path d="M263.8772,549.7025 C268.5461,567.1276 275.6127,593.5009 280.7045,612.5045 " fill="none" id="pyexcel-io plugin interfaces-csv, csvz, tsv, tsvz" style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 7.0,7.0;"/><polygon fill="#A80036" points="282.0847,617.6555,283.619,607.9269,280.7906,612.8259,275.8916,609.9975,282.0847,617.6555" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pyexcel-io plugin interfaces to django, sqlalchemy--><path d="M232.3494,549.7025 C205.7746,567.8533 164.9845,595.7132 136.9817,614.8393 " fill="none" id="pyexcel-io plugin interfaces-django, sqlalchemy" style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 7.0,7.0;"/><polygon fill="#A80036" points="132.6014,617.8311,142.2893,616.058,136.7302,615.011,137.7772,609.4519,132.6014,617.8311" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pyexcel-xls to pyexcel-io plugin interfaces--><path d="M553.5505,446.8598 C547.6608,449.0087 541.7163,451.1014 536,453 C466.3876,476.1211 385.5607,498.5299 329.2517,513.4452 " fill="none" id="pyexcel-xls-pyexcel-io plugin interfaces" style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 7.0,7.0;"/><polygon fill="#A80036" points="558.4787,445.0447,548.6508,444.4019,553.7868,446.7728,551.4159,451.9088,558.4787,445.0447" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pyexcel-xlsx to pyexcel-io plugin interfaces--><path d="M692.7396,446.6976 C685.8249,449.0046 678.7788,451.1801 672,453 C545.9562,486.8386 511.0517,479.8037 383,505 C369.6082,507.6351 355.4298,510.5481 341.6314,513.45 " fill="none" id="pyexcel-xlsx-pyexcel-io plugin interfaces" style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 7.0,7.0;"/><polygon fill="#A80036" points="697.5007,445.0822,687.6927,444.1859,692.7658,446.6886,690.263,451.7617,697.5007,445.0822" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pyexcel-ods to pyexcel-io plugin interfaces--><path d="M412.8197,615.1442 C400.4927,607.3524 386.2173,598.5645 373,591 C347.4233,576.3619 317.9135,561.0002 295.3266,549.5528 " fill="none" id="pyexcel-ods-pyexcel-io plugin interfaces" style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 7.0,7.0;"/><polygon fill="#A80036" points="417.2674,617.9657,411.8101,609.767,413.0452,615.2874,407.5249,616.5225,417.2674,617.9657" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pyexcel-io plugin interfaces to pyexcel-ods3--><path d="M342.4549,549.5402 C410.8275,564.5457 498.8469,584.4658 515,591 C530.5558,597.2926 546.6572,606.5378 559.9343,615.014 " fill="none" id="pyexcel-io plugin interfaces-pyexcel-ods3" style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 7.0,7.0;"/><polygon fill="#A80036" points="564.3239,617.8537,558.94,609.6067,560.1258,615.1379,554.5946,616.3237,564.3239,617.8537" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pyexcel-io plugin interfaces to pyexcel-xlsxw--><path d="M293.7731,513.3311 C328.7742,495.043 382.6867,466.8737 419.3342,447.7254 " fill="none" id="pyexcel-io plugin interfaces-pyexcel-xlsxw" style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 7.0,7.0;"/><polygon fill="#A80036" points="424.1627,445.2025,414.3335,445.825,419.7311,447.5179,418.0382,452.9155,424.1627,445.2025" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pyexcel-io plugin interfaces to pyexcel-odsr--><path d="M337.8367,549.51 C352.7883,552.5954 368.3388,555.5811 383,558 C507.5053,578.5421 545.8036,549.578 665,591 C681.0463,596.5762 697.2905,606.1213 710.3686,614.9895 " fill="none" id="pyexcel-io plugin interfaces-pyexcel-odsr" style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 7.0,7.0;"/><polygon fill="#A80036" points="714.6775,617.9643,709.5436,609.5593,710.5628,615.1236,704.9985,616.1428,714.6775,617.9643" style="stroke: #A80036; stroke-width: 1.0;"/><!--link tabulate to pyexcel-text--><path d="M709.7451,61.1401 C709.5657,79.8859 709.3304,104.4746 709.1726,120.9587 " fill="none" id="tabulate-pyexcel-text" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pygal to pyexcel-pygal--><path d="M558.5099,61.1401 C558.8686,79.8859 559.3392,104.4746 559.6547,120.9587 " fill="none" id="pygal-pyexcel-pygal" style="stroke: #A80036; stroke-width: 1.0;"/><!--link handsontable to pyexcel-handsontable--><path d="M379,61.1401 C379,79.8859 379,104.4746 379,120.9587 " fill="none" id="handsontable-pyexcel-handsontable" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pyexcel-xls to xlrd--><path d="M605.2193,445.2025 C606.3239,461.6929 607.9654,486.1973 609.2172,504.8852 " fill="none" id="pyexcel-xls-xlrd" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pyexcel-xls to xlwt--><path d="M631.6957,445.2025 C656.7863,461.6929 694.0705,486.1973 722.5048,504.8852 " fill="none" id="pyexcel-xls-xlwt" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pyexcel-xlsx to openpyxl--><path d="M773.7859,445.2025 C800.7702,461.6929 840.8683,486.1973 871.4486,504.8852 " fill="none" id="pyexcel-xlsx-openpyxl" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pyexcel-xlsxw to xlsxwriter--><path d="M459,445.2025 C459,461.6929 459,486.1973 459,504.8852 " fill="none" id="pyexcel-xlsxw-xlsxwriter" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pyexcel-ods3 to ezodf--><path d="M590.4357,654.0265 C591.1403,683.1809 592.5337,740.8327 593.3541,774.7745 " fill="none" id="pyexcel-ods3-ezodf" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pyexcel-ods to odfpy--><path d="M444.6732,654.0265 C444.1448,683.1809 443.0997,740.8327 442.4844,774.7745 " fill="none" id="pyexcel-ods-odfpy" style="stroke: #A80036; stroke-width: 1.0;"/><!--
0 <?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="853px" preserveAspectRatio="none" style="width:1340px;height:853px;" version="1.1" viewBox="0 0 1340 853" width="1340px" zoomAndPan="magnify"><defs><filter height="300%" id="fiw5od00ir7id" width="300%" x="-1" y="-1"><feGaussianBlur result="blurOut" stdDeviation="2.0"/><feColorMatrix in="blurOut" result="blurOut2" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .4 0"/><feOffset dx="4.0" dy="4.0" in="blurOut2" result="blurOut3"/><feBlend in="SourceGraphic" in2="blurOut3" mode="normal"/></filter></defs><g><!--cluster pyexcel--><polygon fill="#FFFFFF" filter="url(#fiw5od00ir7id)" points="14,190,73,190,80,212.4883,602,212.4883,602,373.5,14,373.5,14,190" style="stroke: #000000; stroke-width: 2.0;"/><line style="stroke: #000000; stroke-width: 2.0;" x1="14" x2="80" y1="212.4883" y2="212.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="53" x="18" y="205.5352">pyexcel</text><!--cluster plugins--><polygon fill="#FFFFFF" filter="url(#fiw5od00ir7id)" points="22,294.5,82,294.5,89,316.9883,594,316.9883,594,365.5,22,365.5,22,294.5" style="stroke: #000000; stroke-width: 2.0;"/><line style="stroke: #000000; stroke-width: 2.0;" x1="22" x2="89" y1="316.9883" y2="316.9883"/><text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="54" x="26" y="310.0352">plugins</text><!--cluster pyexcel-io--><polygon fill="#FFFFFF" filter="url(#fiw5od00ir7id)" points="228,399,310,399,317,421.4883,587,421.4883,587,687,228,687,228,399" style="stroke: #000000; stroke-width: 2.0;"/><line style="stroke: #000000; stroke-width: 2.0;" x1="228" x2="317" y1="421.4883" y2="421.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="76" x="232" y="414.5352">pyexcel-io</text><!--cluster io plugins--><polygon fill="#FFFFFF" filter="url(#fiw5od00ir7id)" points="236,608,315,608,322,630.4883,579,630.4883,579,679,236,679,236,608" style="stroke: #000000; stroke-width: 2.0;"/><line style="stroke: #000000; stroke-width: 2.0;" x1="236" x2="322" y1="630.4883" y2="630.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="73" x="240" y="623.5352">io plugins</text><!--cluster ods--><path d="M601,615 C601,612 603,610 606,610 C609,610 611,612 611,615 C611,612 613,610 616,610 C619,610 621,612 621,615 C621,612 623,610 626,610 C629,610 631,612 631,615 C631,612 633,610 636,610 C639,610 641,612 641,615 C641,612 643,610 646,610 C649,610 651,612 651,615 C651,612 653,610 656,610 C659,610 661,612 661,615 C661,612 663,610 666,610 C669,610 671,612 671,615 C671,612 673,610 676,610 C679,610 681,612 681,615 C681,612 683,610 686,610 C689,610 691,612 691,615 C691,612 693,610 696,610 C699,610 701,612 701,615 C701,612 703,610 706,610 C709,610 711,612 711,615 C711,612 713,610 716,610 C719,610 721,612 721,615 C721,612 723,610 726,610 C729,610 731,612 731,615 C731,612 733,610 736,610 C739,610 741,612 741,615 C741,612 743,610 746,610 C749,610 751,612 751,615 C751,612 753,610 756,610 C759,610 761,612 761,615 C761,612 763,610 766,610 C769,610 771,612 771,615 C771,612 773,610 776,610 C779,610 781,612 781,615 C781,612 783,610 786,610 C789,610 791,612 791,615 C791,612 793,610 796,610 C799,610 801,612 801,615 C801,612 803,610 806,610 C809,610 811,612 811,615 C811,612 813,610 816,610 C819,610 821,612 821,615 C821,612 823,610 826,610 C829,610 831,612 831,615 C831,612 833,610 836,610 C839,610 841,612 841,615 C841,612 843,610 846,610 C849,610 851,612 851,615 C851,612 853,610 856,610 C859,610 861,612 861,615 C861,612 863,610 866,610 C869,610 871,612 871,615 C871,612 873,610 876,610 C879,610 881,612 881,615 C881,612 883,610 886,610 C889,610 891,612 891,615 C891,612 893,610 896,610 C899,610 901,612 901,615 C901,612 903,610 906,610 C909,610 911,612 911,615 C911,612 913,610 916,610 C919,610 921,612 921,615 C921,612 923,610 926,610 C929,610 931,612 931,615 C931,612 933,610 936,610 C939,610 941,612 941,615 C941,612 943,610 946,610 C949,610 951,612 951,615 C951,612 953,610 956,610 C959,610 961,612 961,615 C961,612 963,610 966,610 C969,610 971,612 971,615 C971,612 973,610 976,610 C979,610 981,612 981,615 C981,612 983,610 986,610 C989,610 991,612 991,615 C991,612 993,610 996,610 C999,610 1001,612 1001,615 C1001,612 1003,610 1006,610 C1009,610 1011,612 1011,615 C1014,615 1016,617 1016,620 C1016,623 1014,625 1011,625 C1014,625 1016,627 1016,630 C1016,633 1014,635 1011,635 C1014,635 1016,637 1016,640 C1016,643 1014,645 1011,645 C1014,645 1016,647 1016,650 C1016,653 1014,655 1011,655 C1014,655 1016,657 1016,660 C1016,663 1014,665 1011,665 C1014,665 1016,667 1016,670 C1016,673 1014,675 1011,675 C1011,678 1008,680 1006,680 C1003,680 1001,678 1001,675 C1001,678 998,680 996,680 C993,680 991,678 991,675 C991,678 988,680 986,680 C983,680 981,678 981,675 C981,678 978,680 976,680 C973,680 971,678 971,675 C971,678 968,680 966,680 C963,680 961,678 961,675 C961,678 958,680 956,680 C953,680 951,678 951,675 C951,678 948,680 946,680 C943,680 941,678 941,675 C941,678 938,680 936,680 C933,680 931,678 931,675 C931,678 928,680 926,680 C923,680 921,678 921,675 C921,678 918,680 916,680 C913,680 911,678 911,675 C911,678 908,680 906,680 C903,680 901,678 901,675 C901,678 898,680 896,680 C893,680 891,678 891,675 C891,678 888,680 886,680 C883,680 881,678 881,675 C881,678 878,680 876,680 C873,680 871,678 871,675 C871,678 868,680 866,680 C863,680 861,678 861,675 C861,678 858,680 856,680 C853,680 851,678 851,675 C851,678 848,680 846,680 C843,680 841,678 841,675 C841,678 838,680 836,680 C833,680 831,678 831,675 C831,678 828,680 826,680 C823,680 821,678 821,675 C821,678 818,680 816,680 C813,680 811,678 811,675 C811,678 808,680 806,680 C803,680 801,678 801,675 C801,678 798,680 796,680 C793,680 791,678 791,675 C791,678 788,680 786,680 C783,680 781,678 781,675 C781,678 778,680 776,680 C773,680 771,678 771,675 C771,678 768,680 766,680 C763,680 761,678 761,675 C761,678 758,680 756,680 C753,680 751,678 751,675 C751,678 748,680 746,680 C743,680 741,678 741,675 C741,678 738,680 736,680 C733,680 731,678 731,675 C731,678 728,680 726,680 C723,680 721,678 721,675 C721,678 718,680 716,680 C713,680 711,678 711,675 C711,678 708,680 706,680 C703,680 701,678 701,675 C701,678 698,680 696,680 C693,680 691,678 691,675 C691,678 688,680 686,680 C683,680 681,678 681,675 C681,678 678,680 676,680 C673,680 671,678 671,675 C671,678 668,680 666,680 C663,680 661,678 661,675 C661,678 658,680 656,680 C653,680 651,678 651,675 C651,678 648,680 646,680 C643,680 641,678 641,675 C641,678 638,680 636,680 C633,680 631,678 631,675 C631,678 628,680 626,680 C623,680 621,678 621,675 C621,678 618,680 616,680 C613,680 611,678 611,675 C611,678 608,680 606,680 C603,680 601,678 601,675 C598,675 596,673 596,670 C596,667 598,665 601,665 C598,665 596,663 596,660 C596,657 598,655 601,655 C598,655 596,653 596,650 C596,647 598,645 601,645 C598,645 596,643 596,640 C596,637 598,635 601,635 C598,635 596,633 596,630 C596,627 598,625 601,625 C598,625 596,623 596,620 C596,617 598,615 601,615 " fill="#FFFFFF" filter="url(#fiw5od00ir7id)" style="stroke: #000000; stroke-width: 2.0;"/><text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="26" x="794" y="634.5352">ods</text><!--cluster xls, xlsx--><path d="M598,406 C598,403 600,401 603,401 C606,401 608,403 608,406 C608,403 610,401 613,401 C616,401 618,403 618,406 C618,403 620,401 623,401 C626,401 628,403 628,406 C628,403 630,401 633,401 C636,401 638,403 638,406 C638,403 640,401 643,401 C646,401 648,403 648,406 C648,403 650,401 653,401 C656,401 658,403 658,406 C658,403 660,401 663,401 C666,401 668,403 668,406 C668,403 670,401 673,401 C676,401 678,403 678,406 C678,403 680,401 683,401 C686,401 688,403 688,406 C688,403 690,401 693,401 C696,401 698,403 698,406 C698,403 700,401 703,401 C706,401 708,403 708,406 C708,403 710,401 713,401 C716,401 718,403 718,406 C718,403 720,401 723,401 C726,401 728,403 728,406 C728,403 730,401 733,401 C736,401 738,403 738,406 C738,403 740,401 743,401 C746,401 748,403 748,406 C748,403 750,401 753,401 C756,401 758,403 758,406 C758,403 760,401 763,401 C766,401 768,403 768,406 C768,403 770,401 773,401 C776,401 778,403 778,406 C778,403 780,401 783,401 C786,401 788,403 788,406 C788,403 790,401 793,401 C796,401 798,403 798,406 C798,403 800,401 803,401 C806,401 808,403 808,406 C808,403 810,401 813,401 C816,401 818,403 818,406 C818,403 820,401 823,401 C826,401 828,403 828,406 C828,403 830,401 833,401 C836,401 838,403 838,406 C838,403 840,401 843,401 C846,401 848,403 848,406 C848,403 850,401 853,401 C856,401 858,403 858,406 C858,403 860,401 863,401 C866,401 868,403 868,406 C868,403 870,401 873,401 C876,401 878,403 878,406 C878,403 880,401 883,401 C886,401 888,403 888,406 C888,403 890,401 893,401 C896,401 898,403 898,406 C898,403 900,401 903,401 C906,401 908,403 908,406 C908,403 910,401 913,401 C916,401 918,403 918,406 C918,403 920,401 923,401 C926,401 928,403 928,406 C928,403 930,401 933,401 C936,401 938,403 938,406 C938,403 940,401 943,401 C946,401 948,403 948,406 C948,403 950,401 953,401 C956,401 958,403 958,406 C958,403 960,401 963,401 C966,401 968,403 968,406 C968,403 970,401 973,401 C976,401 978,403 978,406 C978,403 980,401 983,401 C986,401 988,403 988,406 C988,403 990,401 993,401 C996,401 998,403 998,406 C998,403 1000,401 1003,401 C1006,401 1008,403 1008,406 C1008,403 1010,401 1013,401 C1016,401 1018,403 1018,406 C1018,403 1020,401 1023,401 C1026,401 1028,403 1028,406 C1028,403 1030,401 1033,401 C1036,401 1038,403 1038,406 C1038,403 1040,401 1043,401 C1046,401 1048,403 1048,406 C1048,403 1050,401 1053,401 C1056,401 1058,403 1058,406 C1058,403 1060,401 1063,401 C1066,401 1068,403 1068,406 C1068,403 1070,401 1073,401 C1076,401 1078,403 1078,406 C1078,403 1080,401 1083,401 C1086,401 1088,403 1088,406 C1088,403 1090,401 1093,401 C1096,401 1098,403 1098,406 C1098,403 1100,401 1103,401 C1106,401 1108,403 1108,406 C1108,403 1110,401 1113,401 C1116,401 1118,403 1118,406 C1118,403 1120,401 1123,401 C1126,401 1128,403 1128,406 C1128,403 1130,401 1133,401 C1136,401 1138,403 1138,406 C1138,403 1140,401 1143,401 C1146,401 1148,403 1148,406 C1148,403 1150,401 1153,401 C1156,401 1158,403 1158,406 C1158,403 1160,401 1163,401 C1166,401 1168,403 1168,406 C1168,403 1170,401 1173,401 C1176,401 1178,403 1178,406 C1178,403 1180,401 1183,401 C1186,401 1188,403 1188,406 C1188,403 1190,401 1193,401 C1196,401 1198,403 1198,406 C1198,403 1200,401 1203,401 C1206,401 1208,403 1208,406 C1208,403 1210,401 1213,401 C1216,401 1218,403 1218,406 C1218,403 1220,401 1223,401 C1226,401 1228,403 1228,406 C1228,403 1230,401 1233,401 C1236,401 1238,403 1238,406 C1238,403 1240,401 1243,401 C1246,401 1248,403 1248,406 C1248,403 1250,401 1253,401 C1256,401 1258,403 1258,406 C1258,403 1260,401 1263,401 C1266,401 1268,403 1268,406 C1268,403 1270,401 1273,401 C1276,401 1278,403 1278,406 C1278,403 1280,401 1283,401 C1286,401 1288,403 1288,406 C1288,403 1290,401 1293,401 C1296,401 1298,403 1298,406 C1298,403 1300,401 1303,401 C1306,401 1308,403 1308,406 C1308,403 1310,401 1313,401 C1316,401 1318,403 1318,406 C1318,403 1320,401 1323,401 C1326,401 1328,403 1328,406 C1331,406 1333,408 1333,411 C1333,414 1331,416 1328,416 C1331,416 1333,418 1333,421 C1333,424 1331,426 1328,426 C1331,426 1333,428 1333,431 C1333,434 1331,436 1328,436 C1331,436 1333,438 1333,441 C1333,444 1331,446 1328,446 C1331,446 1333,448 1333,451 C1333,454 1331,456 1328,456 C1331,456 1333,458 1333,461 C1333,464 1331,466 1328,466 C1328,469 1325,471 1323,471 C1320,471 1318,469 1318,466 C1318,469 1315,471 1313,471 C1310,471 1308,469 1308,466 C1308,469 1305,471 1303,471 C1300,471 1298,469 1298,466 C1298,469 1295,471 1293,471 C1290,471 1288,469 1288,466 C1288,469 1285,471 1283,471 C1280,471 1278,469 1278,466 C1278,469 1275,471 1273,471 C1270,471 1268,469 1268,466 C1268,469 1265,471 1263,471 C1260,471 1258,469 1258,466 C1258,469 1255,471 1253,471 C1250,471 1248,469 1248,466 C1248,469 1245,471 1243,471 C1240,471 1238,469 1238,466 C1238,469 1235,471 1233,471 C1230,471 1228,469 1228,466 C1228,469 1225,471 1223,471 C1220,471 1218,469 1218,466 C1218,469 1215,471 1213,471 C1210,471 1208,469 1208,466 C1208,469 1205,471 1203,471 C1200,471 1198,469 1198,466 C1198,469 1195,471 1193,471 C1190,471 1188,469 1188,466 C1188,469 1185,471 1183,471 C1180,471 1178,469 1178,466 C1178,469 1175,471 1173,471 C1170,471 1168,469 1168,466 C1168,469 1165,471 1163,471 C1160,471 1158,469 1158,466 C1158,469 1155,471 1153,471 C1150,471 1148,469 1148,466 C1148,469 1145,471 1143,471 C1140,471 1138,469 1138,466 C1138,469 1135,471 1133,471 C1130,471 1128,469 1128,466 C1128,469 1125,471 1123,471 C1120,471 1118,469 1118,466 C1118,469 1115,471 1113,471 C1110,471 1108,469 1108,466 C1108,469 1105,471 1103,471 C1100,471 1098,469 1098,466 C1098,469 1095,471 1093,471 C1090,471 1088,469 1088,466 C1088,469 1085,471 1083,471 C1080,471 1078,469 1078,466 C1078,469 1075,471 1073,471 C1070,471 1068,469 1068,466 C1068,469 1065,471 1063,471 C1060,471 1058,469 1058,466 C1058,469 1055,471 1053,471 C1050,471 1048,469 1048,466 C1048,469 1045,471 1043,471 C1040,471 1038,469 1038,466 C1038,469 1035,471 1033,471 C1030,471 1028,469 1028,466 C1028,469 1025,471 1023,471 C1020,471 1018,469 1018,466 C1018,469 1015,471 1013,471 C1010,471 1008,469 1008,466 C1008,469 1005,471 1003,471 C1000,471 998,469 998,466 C998,469 995,471 993,471 C990,471 988,469 988,466 C988,469 985,471 983,471 C980,471 978,469 978,466 C978,469 975,471 973,471 C970,471 968,469 968,466 C968,469 965,471 963,471 C960,471 958,469 958,466 C958,469 955,471 953,471 C950,471 948,469 948,466 C948,469 945,471 943,471 C940,471 938,469 938,466 C938,469 935,471 933,471 C930,471 928,469 928,466 C928,469 925,471 923,471 C920,471 918,469 918,466 C918,469 915,471 913,471 C910,471 908,469 908,466 C908,469 905,471 903,471 C900,471 898,469 898,466 C898,469 895,471 893,471 C890,471 888,469 888,466 C888,469 885,471 883,471 C880,471 878,469 878,466 C878,469 875,471 873,471 C870,471 868,469 868,466 C868,469 865,471 863,471 C860,471 858,469 858,466 C858,469 855,471 853,471 C850,471 848,469 848,466 C848,469 845,471 843,471 C840,471 838,469 838,466 C838,469 835,471 833,471 C830,471 828,469 828,466 C828,469 825,471 823,471 C820,471 818,469 818,466 C818,469 815,471 813,471 C810,471 808,469 808,466 C808,469 805,471 803,471 C800,471 798,469 798,466 C798,469 795,471 793,471 C790,471 788,469 788,466 C788,469 785,471 783,471 C780,471 778,469 778,466 C778,469 775,471 773,471 C770,471 768,469 768,466 C768,469 765,471 763,471 C760,471 758,469 758,466 C758,469 755,471 753,471 C750,471 748,469 748,466 C748,469 745,471 743,471 C740,471 738,469 738,466 C738,469 735,471 733,471 C730,471 728,469 728,466 C728,469 725,471 723,471 C720,471 718,469 718,466 C718,469 715,471 713,471 C710,471 708,469 708,466 C708,469 705,471 703,471 C700,471 698,469 698,466 C698,469 695,471 693,471 C690,471 688,469 688,466 C688,469 685,471 683,471 C680,471 678,469 678,466 C678,469 675,471 673,471 C670,471 668,469 668,466 C668,469 665,471 663,471 C660,471 658,469 658,466 C658,469 655,471 653,471 C650,471 648,469 648,466 C648,469 645,471 643,471 C640,471 638,469 638,466 C638,469 635,471 633,471 C630,471 628,469 628,466 C628,469 625,471 623,471 C620,471 618,469 618,466 C618,469 615,471 613,471 C610,471 608,469 608,466 C608,469 605,471 603,471 C600,471 598,469 598,466 C595,466 593,464 593,461 C593,458 595,456 598,456 C595,456 593,454 593,451 C593,448 595,446 598,446 C595,446 593,444 593,441 C593,438 595,436 598,436 C595,436 593,434 593,431 C593,428 595,426 598,426 C595,426 593,424 593,421 C593,418 595,416 598,416 C595,416 593,414 593,411 C593,408 595,406 598,406 " fill="#FFFFFF" filter="url(#fiw5od00ir7id)" style="stroke: #000000; stroke-width: 2.0;"/><text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="58" x="935.5" y="425.5352">xls, xlsx</text><!--entity pyexcel public api--><rect fill="#FEFECE" filter="url(#fiw5od00ir7id)" height="36.4883" style="stroke: #A80036; stroke-width: 1.5;" width="143" x="22.5" y="217"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="17.5" y="222"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="17.5" y="243.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="123" x="32.5" y="240.5352">pyexcel public api</text><!--entity pyexcel internal api--><rect fill="#FEFECE" filter="url(#fiw5od00ir7id)" height="36.4883" style="stroke: #A80036; stroke-width: 1.5;" width="154" x="201" y="217"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="196" y="222"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="196" y="243.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="134" x="211" y="240.5352">pyexcel internal api</text><!--entity pyexcel plugin interfaces--><rect fill="#FEFECE" filter="url(#fiw5od00ir7id)" height="36.4883" style="stroke: #A80036; stroke-width: 1.5;" width="191" x="390.5" y="217"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="385.5" y="222"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="385.5" y="243.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="171" x="400.5" y="240.5352">pyexcel plugin interfaces</text><!--entity data source plugins--><rect fill="#FEFECE" filter="url(#fiw5od00ir7id)" height="36.4883" style="stroke: #A80036; stroke-width: 1.5;" width="155" x="430.5" y="321.5"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="425.5" y="326.5"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="425.5" y="347.9883"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="135" x="440.5" y="345.0352">data source plugins</text><!--entity data renderer plugins--><rect fill="#FEFECE" filter="url(#fiw5od00ir7id)" height="36.4883" style="stroke: #A80036; stroke-width: 1.5;" width="169" x="226.5" y="321.5"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="221.5" y="326.5"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="221.5" y="347.9883"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="149" x="236.5" y="345.0352">data renderer plugins</text><!--entity data parsing plugins--><rect fill="#FEFECE" filter="url(#fiw5od00ir7id)" height="36.4883" style="stroke: #A80036; stroke-width: 1.5;" width="161" x="30.5" y="321.5"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="25.5" y="326.5"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="25.5" y="347.9883"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="141" x="40.5" y="345.0352">data parsing plugins</text><!--entity pyexcel-io public api--><rect fill="#FEFECE" filter="url(#fiw5od00ir7id)" height="36.4883" style="stroke: #A80036; stroke-width: 1.5;" width="164" x="236" y="426"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="231" y="431"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="231" y="452.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="144" x="246" y="449.5352">pyexcel-io public api</text><!--entity pyexcel-io plugin interfaces--><rect fill="#FEFECE" filter="url(#fiw5od00ir7id)" height="36.4883" style="stroke: #A80036; stroke-width: 1.5;" width="212" x="367" y="530.5"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="362" y="535.5"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="362" y="556.9883"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="192" x="377" y="554.0352">pyexcel-io plugin interfaces</text><!--entity csv, csvz, tsv, tsvz--><rect fill="#FEFECE" filter="url(#fiw5od00ir7id)" height="36.4883" style="stroke: #A80036; stroke-width: 1.5;" width="140" x="431" y="635"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="426" y="640"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="426" y="661.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="120" x="441" y="658.5352">csv, csvz, tsv, tsvz</text><!--entity django, sqlalchemy--><rect fill="#FEFECE" filter="url(#fiw5od00ir7id)" height="36.4883" style="stroke: #A80036; stroke-width: 1.5;" width="152" x="244" y="635"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="239" y="640"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="239" y="661.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="132" x="254" y="658.5352">django, sqlalchemy</text><!--entity pyexcel-ods--><rect fill="#FEFECE" filter="url(#fiw5od00ir7id)" height="36.4883" style="stroke: #A80036; stroke-width: 1.5;" width="105" x="606.5" y="635"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="601.5" y="640"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="601.5" y="661.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="85" x="616.5" y="658.5352">pyexcel-ods</text><!--entity pyexcel-ods3--><rect fill="#FEFECE" filter="url(#fiw5od00ir7id)" height="36.4883" style="stroke: #A80036; stroke-width: 1.5;" width="114" x="747" y="635"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="742" y="640"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="742" y="661.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="94" x="757" y="658.5352">pyexcel-ods3</text><!--entity pyexcel-odsr--><rect fill="#FEFECE" filter="url(#fiw5od00ir7id)" height="36.4883" style="stroke: #A80036; stroke-width: 1.5;" width="111" x="896.5" y="635"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="891.5" y="640"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="891.5" y="661.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="91" x="906.5" y="658.5352">pyexcel-odsr</text><!--entity pyexcel-xls--><rect fill="#FEFECE" filter="url(#fiw5od00ir7id)" height="36.4883" style="stroke: #A80036; stroke-width: 1.5;" width="100" x="931" y="426"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="926" y="431"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="926" y="452.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="80" x="941" y="449.5352">pyexcel-xls</text><!--entity pyexcel-xlsx--><rect fill="#FEFECE" filter="url(#fiw5od00ir7id)" height="36.4883" style="stroke: #A80036; stroke-width: 1.5;" width="109" x="1066.5" y="426"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="1061.5" y="431"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="1061.5" y="452.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="89" x="1076.5" y="449.5352">pyexcel-xlsx</text><!--entity pyexcel-xlsxw--><rect fill="#FEFECE" filter="url(#fiw5od00ir7id)" height="36.4883" style="stroke: #A80036; stroke-width: 1.5;" width="120" x="776" y="426"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="771" y="431"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="771" y="452.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="100" x="786" y="449.5352">pyexcel-xlsxw</text><!--entity pyexcel-libxlsxw--><rect fill="#FEFECE" filter="url(#fiw5od00ir7id)" height="36.4883" style="stroke: #A80036; stroke-width: 1.5;" width="137" x="603.5" y="426"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="598.5" y="431"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="598.5" y="452.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="117" x="613.5" y="449.5352">pyexcel-libxlsxw</text><!--entity pyexcel-xlsxr--><rect fill="#FEFECE" filter="url(#fiw5od00ir7id)" height="36.4883" style="stroke: #A80036; stroke-width: 1.5;" width="115" x="1210.5" y="426"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="1205.5" y="431"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="1205.5" y="452.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="95" x="1220.5" y="449.5352">pyexcel-xlsxr</text><!--entity tabulate--><rect fill="#FFFFFF" filter="url(#fiw5od00ir7id)" height="52.9766" style="stroke: #A80036; stroke-width: 1.5;" width="117" x="251.5" y="8"/><rect fill="#FFFFFF" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="246.5" y="13"/><rect fill="#FFFFFF" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="246.5" y="50.9766"/><text fill="#000000" font-family="sans-serif" font-size="14" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="97" x="261.5" y="31.5352">«dependency»</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="56" x="282" y="48.0234">tabulate</text><!--entity pygal--><rect fill="#FFFFFF" filter="url(#fiw5od00ir7id)" height="52.9766" style="stroke: #A80036; stroke-width: 1.5;" width="117" x="608.5" y="8"/><rect fill="#FFFFFF" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="603.5" y="13"/><rect fill="#FFFFFF" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="603.5" y="50.9766"/><text fill="#000000" font-family="sans-serif" font-size="14" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="97" x="618.5" y="31.5352">«dependency»</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="37" x="648.5" y="48.0234">pygal</text><!--entity handsontable--><rect fill="#FFFFFF" filter="url(#fiw5od00ir7id)" height="52.9766" style="stroke: #A80036; stroke-width: 1.5;" width="117" x="427.5" y="8"/><rect fill="#FFFFFF" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="422.5" y="13"/><rect fill="#FFFFFF" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="422.5" y="50.9766"/><text fill="#000000" font-family="sans-serif" font-size="14" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="97" x="437.5" y="31.5352">«dependency»</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="94" x="439" y="48.0234">handsontable</text><!--entity xlrd--><rect fill="#FFFFFF" filter="url(#fiw5od00ir7id)" height="52.9766" style="stroke: #A80036; stroke-width: 1.5;" width="117" x="758.5" y="522"/><rect fill="#FFFFFF" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="753.5" y="527"/><rect fill="#FFFFFF" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="753.5" y="564.9766"/><text fill="#000000" font-family="sans-serif" font-size="14" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="97" x="768.5" y="545.5352">«dependency»</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="28" x="803" y="562.0234">xlrd</text><!--entity xlwt--><rect fill="#FFFFFF" filter="url(#fiw5od00ir7id)" height="52.9766" style="stroke: #A80036; stroke-width: 1.5;" width="117" x="910.5" y="522"/><rect fill="#FFFFFF" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="905.5" y="527"/><rect fill="#FFFFFF" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="905.5" y="564.9766"/><text fill="#000000" font-family="sans-serif" font-size="14" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="97" x="920.5" y="545.5352">«dependency»</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="29" x="954.5" y="562.0234">xlwt</text><!--entity openpyxl--><rect fill="#FFFFFF" filter="url(#fiw5od00ir7id)" height="52.9766" style="stroke: #A80036; stroke-width: 1.5;" width="117" x="1062.5" y="522"/><rect fill="#FFFFFF" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="1057.5" y="527"/><rect fill="#FFFFFF" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="1057.5" y="564.9766"/><text fill="#000000" font-family="sans-serif" font-size="14" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="97" x="1072.5" y="545.5352">«dependency»</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="64" x="1089" y="562.0234">openpyxl</text><!--entity xlsxwriter--><rect fill="#FFFFFF" filter="url(#fiw5od00ir7id)" height="52.9766" style="stroke: #A80036; stroke-width: 1.5;" width="117" x="924.5" y="313"/><rect fill="#FFFFFF" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="919.5" y="318"/><rect fill="#FFFFFF" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="919.5" y="355.9766"/><text fill="#000000" font-family="sans-serif" font-size="14" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="97" x="934.5" y="336.5352">«dependency»</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="69" x="948.5" y="353.0234">xlsxwriter</text><!--entity ezodf--><rect fill="#FFFFFF" filter="url(#fiw5od00ir7id)" height="52.9766" style="stroke: #A80036; stroke-width: 1.5;" width="117" x="749.5" y="792"/><rect fill="#FFFFFF" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="744.5" y="797"/><rect fill="#FFFFFF" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="744.5" y="834.9766"/><text fill="#000000" font-family="sans-serif" font-size="14" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="97" x="759.5" y="815.5352">«dependency»</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="39" x="788.5" y="832.0234">ezodf</text><!--entity odfpy--><rect fill="#FFFFFF" filter="url(#fiw5od00ir7id)" height="52.9766" style="stroke: #A80036; stroke-width: 1.5;" width="117" x="597.5" y="792"/><rect fill="#FFFFFF" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="592.5" y="797"/><rect fill="#FFFFFF" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="592.5" y="834.9766"/><text fill="#000000" font-family="sans-serif" font-size="14" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="97" x="607.5" y="815.5352">«dependency»</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="39" x="636.5" y="832.0234">odfpy</text><!--entity lxml--><rect fill="#FFFFFF" filter="url(#fiw5od00ir7id)" height="52.9766" style="stroke: #A80036; stroke-width: 1.5;" width="117" x="1214.5" y="522"/><rect fill="#FFFFFF" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="1209.5" y="527"/><rect fill="#FFFFFF" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="1209.5" y="564.9766"/><text fill="#000000" font-family="sans-serif" font-size="14" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="97" x="1224.5" y="545.5352">«dependency»</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="30" x="1258" y="562.0234">lxml</text><!--entity libxlsxwpy--><rect fill="#FFFFFF" filter="url(#fiw5od00ir7id)" height="52.9766" style="stroke: #A80036; stroke-width: 1.5;" width="117" x="620.5" y="313"/><rect fill="#FFFFFF" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="615.5" y="318"/><rect fill="#FFFFFF" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="615.5" y="355.9766"/><text fill="#000000" font-family="sans-serif" font-size="14" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="97" x="630.5" y="336.5352">«dependency»</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="73" x="642.5" y="353.0234">libxlsxwpy</text><!--entity libxlsx--><rect fill="#FFFFFF" filter="url(#fiw5od00ir7id)" height="52.9766" style="stroke: #A80036; stroke-width: 1.5;" width="117" x="772.5" y="313"/><rect fill="#FFFFFF" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="767.5" y="318"/><rect fill="#FFFFFF" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="767.5" y="355.9766"/><text fill="#000000" font-family="sans-serif" font-size="14" font-style="italic" lengthAdjust="spacingAndGlyphs" textLength="97" x="782.5" y="336.5352">«dependency»</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="46" x="808" y="353.0234">libxlsx</text><!--entity pyexcel-text--><rect fill="#FEFECE" filter="url(#fiw5od00ir7id)" height="36.4883" style="stroke: #A80036; stroke-width: 1.5;" width="107" x="256.5" y="121"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="251.5" y="126"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="251.5" y="147.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="87" x="266.5" y="144.5352">pyexcel-text</text><!--entity pyexcel-pygal--><rect fill="#FEFECE" filter="url(#fiw5od00ir7id)" height="36.4883" style="stroke: #A80036; stroke-width: 1.5;" width="117" x="608.5" y="121"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="603.5" y="126"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="603.5" y="147.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="97" x="618.5" y="144.5352">pyexcel-pygal</text><!--entity pyexcel-handsontable--><rect fill="#FEFECE" filter="url(#fiw5od00ir7id)" height="36.4883" style="stroke: #A80036; stroke-width: 1.5;" width="174" x="399" y="121"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="394" y="126"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="394" y="147.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="154" x="409" y="144.5352">pyexcel-handsontable</text><!--entity sphinxcontrib-excel--><rect fill="#FEFECE" filter="url(#fiw5od00ir7id)" height="36.4883" style="stroke: #A80036; stroke-width: 1.5;" width="160" x="61" y="121"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="56" y="126"/><rect fill="#FEFECE" height="5" style="stroke: #A80036; stroke-width: 1.5;" width="10" x="56" y="147.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="140" x="71" y="144.5352">sphinxcontrib-excel</text><!--link pyexcel public api to pyexcel internal api--><path d="M165.5156,235 C175.4935,235 185.4713,235 195.4492,235 " fill="none" id="pyexcel public api-pyexcel internal api" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="200.667,235,191.667,231,195.667,235,191.667,239,200.667,235" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pyexcel internal api to pyexcel plugin interfaces--><path d="M355.1875,235 C365.1956,235 375.2038,235 385.2119,235 " fill="none" id="pyexcel internal api-pyexcel plugin interfaces" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="390.4456,235,381.4456,231,385.4456,235,381.4456,239,390.4456,235" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pyexcel-text to pyexcel plugin interfaces--><path d="M347.6141,159.5168 C378.8298,176.5435 422.8023,200.5285 452.7455,216.8612 " fill="none" id="pyexcel-text-pyexcel plugin interfaces" style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 7.0,7.0;"/><polygon fill="#A80036" points="343.1043,157.0569,349.0897,164.8784,347.4937,159.4513,352.9207,157.8553,343.1043,157.0569" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pyexcel-pygal to pyexcel plugin interfaces--><path d="M628.3173,159.5168 C596.2148,176.5435 550.9931,200.5285 520.1993,216.8612 " fill="none" id="pyexcel-pygal-pyexcel plugin interfaces" style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 7.0,7.0;"/><polygon fill="#A80036" points="632.9553,157.0569,623.1302,157.7403,628.5382,159.3998,626.8787,164.8077,632.9553,157.0569" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pyexcel-handsontable to pyexcel plugin interfaces--><path d="M486,162.0611 C486,178.8106 486,201.1694 486,216.6977 " fill="none" id="pyexcel-handsontable-pyexcel plugin interfaces" style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 7.0,7.0;"/><polygon fill="#A80036" points="486,157.0569,482,166.0569,486,162.0569,490,166.0569,486,157.0569" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pyexcel plugin interfaces to data source plugins--><path d="M489.8321,253.2025 C493.5005,270.6276 499.0528,297.0009 503.0536,316.0045 " fill="none" id="pyexcel plugin interfaces-data source plugins" style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 7.0,7.0;"/><polygon fill="#A80036" points="504.138,321.1555,506.1982,311.5245,503.108,316.2627,498.3698,313.1725,504.138,321.1555" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pyexcel plugin interfaces to data renderer plugins--><path d="M455.5173,253.2025 C424.9962,271.428 378.0816,299.4427 346.042,318.5749 " fill="none" id="pyexcel plugin interfaces-data renderer plugins" style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 7.0,7.0;"/><polygon fill="#A80036" points="341.4264,321.3311,351.2043,320.1512,345.7193,318.7677,347.1028,313.2826,341.4264,321.3311" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pyexcel plugin interfaces to data parsing plugins--><path d="M390.3918,250.2007 C336.3714,260.108 268.043,274.8665 209,294.5 C189.1888,301.0878 168.0415,310.4751 150.4814,318.9729 " fill="none" id="pyexcel plugin interfaces-data parsing plugins" style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 7.0,7.0;"/><polygon fill="#A80036" points="145.6278,321.3431,155.4703,320.9883,150.1207,319.1491,151.9599,313.7996,145.6278,321.3431" style="stroke: #A80036; stroke-width: 1.0;"/><!--link data renderer plugins to pyexcel-io public api--><path d="M312.2193,357.7025 C313.3865,375.1276 315.1532,401.5009 316.4261,420.5045 " fill="none" id="data renderer plugins-pyexcel-io public api" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="316.7712,425.6555,320.1605,416.4082,316.4369,420.6667,312.1784,416.9431,316.7712,425.6555" style="stroke: #A80036; stroke-width: 1.0;"/><!--link data parsing plugins to pyexcel-io public api--><path d="M147.0566,357.7025 C183.4547,376.0774 239.563,404.4026 277.4787,423.5436 " fill="none" id="data parsing plugins-pyexcel-io public api" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="282.0099,425.8311,275.7785,418.2042,277.5465,423.5777,272.173,425.3457,282.0099,425.8311" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pyexcel-io public api to pyexcel-io plugin interfaces--><path d="M344.9989,462.2025 C371.9211,480.3533 413.2445,508.2132 441.6133,527.3393 " fill="none" id="pyexcel-io public api-pyexcel-io plugin interfaces" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="446.0509,530.3311,440.8244,521.9834,441.9051,527.5361,436.3524,528.6168,446.0509,530.3311" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pyexcel-io plugin interfaces to csv, csvz, tsv, tsvz--><path d="M477.8772,566.7025 C482.5461,584.1276 489.6127,610.5009 494.7045,629.5045 " fill="none" id="pyexcel-io plugin interfaces-csv, csvz, tsv, tsvz" style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 7.0,7.0;"/><polygon fill="#A80036" points="496.0847,634.6555,497.619,624.9269,494.7906,629.8259,489.8916,626.9975,496.0847,634.6555" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pyexcel-io plugin interfaces to django, sqlalchemy--><path d="M446.3494,566.7025 C419.7746,584.8533 378.9845,612.7132 350.9817,631.8393 " fill="none" id="pyexcel-io plugin interfaces-django, sqlalchemy" style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 7.0,7.0;"/><polygon fill="#A80036" points="346.6014,634.8311,356.2893,633.058,350.7302,632.011,351.7772,626.4519,346.6014,634.8311" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pyexcel-xls to pyexcel-io plugin interfaces--><path d="M933.2894,463.8058 C926.8692,466.0893 920.3188,468.2297 914,470 C801.6761,501.4696 669.5765,523.0116 579.4391,535.4557 " fill="none" id="pyexcel-xls-pyexcel-io plugin interfaces" style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 7.0,7.0;"/><polygon fill="#A80036" points="938.1675,462.0354,928.3428,461.3459,933.4675,463.7413,931.0722,468.8659,938.1675,462.0354" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pyexcel-xlsxw to pyexcel-io plugin interfaces--><path d="M778.4985,463.5491 C771.608,465.7785 764.6575,467.9751 758,470 C684.8397,492.2519 600.2959,515.1939 542.4034,530.4846 " fill="none" id="pyexcel-xlsxw-pyexcel-io plugin interfaces" style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 7.0,7.0;"/><polygon fill="#A80036" points="783.2549,462.0022,773.459,460.9822,778.5001,463.5488,775.9335,468.5898,783.2549,462.0022" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pyexcel-libxlsxw to pyexcel-io plugin interfaces--><path d="M632.5325,464.7254 C596.0682,483.8737 542.4253,512.043 507.5992,530.3311 " fill="none" id="pyexcel-libxlsxw-pyexcel-io plugin interfaces" style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 7.0,7.0;"/><polygon fill="#A80036" points="637.3368,462.2025,627.509,462.8456,632.9101,464.5272,631.2285,469.9283,637.3368,462.2025" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pyexcel-xlsx to pyexcel-io plugin interfaces--><path d="M1070.4488,463.7617 C1063.3191,466.1053 1056.0257,468.2733 1049,470 C962.3244,491.3024 720.4565,520.6417 579.2227,536.7432 " fill="none" id="pyexcel-xlsx-pyexcel-io plugin interfaces" style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 7.0,7.0;"/><polygon fill="#A80036" points="1075.3532,462.1144,1065.548,461.1881,1070.6134,463.7064,1068.0952,468.7718,1075.3532,462.1144" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pyexcel-xlsxr to pyexcel-io plugin interfaces--><path d="M1216.4492,463.5654 C1208.6725,466.0211 1200.6828,468.2727 1193,470 C1163.5188,476.6282 772.2616,517.5725 579.2254,537.5536 " fill="none" id="pyexcel-xlsxr-pyexcel-io plugin interfaces" style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 7.0,7.0;"/><polygon fill="#A80036" points="1221.2592,462.0129,1211.4656,460.9708,1216.5009,463.5488,1213.923,468.5841,1221.2592,462.0129" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pyexcel-ods to pyexcel-io plugin interfaces--><path d="M626.8197,632.1442 C614.4927,624.3524 600.2173,615.5645 587,608 C561.4233,593.3619 531.9135,578.0002 509.3266,566.5528 " fill="none" id="pyexcel-ods-pyexcel-io plugin interfaces" style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 7.0,7.0;"/><polygon fill="#A80036" points="631.2674,634.9657,625.8101,626.767,627.0452,632.2874,621.5249,633.5225,631.2674,634.9657" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pyexcel-io plugin interfaces to pyexcel-ods3--><path d="M579.2792,565.482 C626.2123,574.9288 681.4132,588.7504 729,608 C744.5558,614.2926 760.6572,623.5378 773.9343,632.014 " fill="none" id="pyexcel-io plugin interfaces-pyexcel-ods3" style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 7.0,7.0;"/><polygon fill="#A80036" points="778.3239,634.8537,772.94,626.6067,774.1258,632.1379,768.5946,633.3237,778.3239,634.8537" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pyexcel-io plugin interfaces to pyexcel-odsr--><path d="M579.3424,559.1569 C680.6927,570.2001 825.5275,588.5436 879,608 C894.838,613.7628 910.9433,623.2283 923.9826,631.995 " fill="none" id="pyexcel-io plugin interfaces-pyexcel-odsr" style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 7.0,7.0;"/><polygon fill="#A80036" points="928.2822,634.9349,923.1108,626.553,924.1549,632.1127,918.5952,633.1568,928.2822,634.9349" style="stroke: #A80036; stroke-width: 1.0;"/><!--link tabulate to pyexcel-text--><path d="M310,61.1401 C310,79.8859 310,104.4746 310,120.9587 " fill="none" id="tabulate-pyexcel-text" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pygal to pyexcel-pygal--><path d="M667,61.1401 C667,79.8859 667,104.4746 667,120.9587 " fill="none" id="pygal-pyexcel-pygal" style="stroke: #A80036; stroke-width: 1.0;"/><!--link handsontable to pyexcel-handsontable--><path d="M486,61.1401 C486,79.8859 486,104.4746 486,120.9587 " fill="none" id="handsontable-pyexcel-handsontable" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pyexcel-xls to xlrd--><path d="M952.4334,462.2025 C926.5538,478.6929 888.0971,503.1973 858.7686,521.8852 " fill="none" id="pyexcel-xls-xlrd" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pyexcel-xls to xlwt--><path d="M978.9098,462.2025 C977.0161,478.6929 974.2022,503.1973 972.0562,521.8852 " fill="none" id="pyexcel-xls-xlwt" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pyexcel-xlsx to openpyxl--><path d="M1121,462.2025 C1121,478.6929 1121,503.1973 1121,521.8852 " fill="none" id="pyexcel-xlsx-openpyxl" style="stroke: #A80036; stroke-width: 1.0;"/><!--link xlsxwriter to pyexcel-xlsxw--><path d="M945.5253,366.1401 C919.1557,384.8859 884.5669,409.4746 861.3787,425.9587 " fill="none" id="xlsxwriter-pyexcel-xlsxw" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pyexcel-ods3 to ezodf--><path d="M804.4357,671.0265 C805.1403,700.1809 806.5337,757.8327 807.3541,791.7745 " fill="none" id="pyexcel-ods3-ezodf" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pyexcel-ods to odfpy--><path d="M658.6732,671.0265 C658.1448,700.1809 657.0997,757.8327 656.4844,791.7745 " fill="none" id="pyexcel-ods-odfpy" style="stroke: #A80036; stroke-width: 1.0;"/><!--link lxml to pyexcel-odsr--><path d="M1214.1373,569.2022 C1208.3538,571.1828 1202.5675,573.1435 1197,575 C1132.2105,596.6045 1057.3524,620.2459 1007.6032,635.7725 " fill="none" id="lxml-pyexcel-odsr" style="stroke: #A80036; stroke-width: 1.0;"/><!--link pyexcel-xlsxr to lxml--><path d="M1268.8709,462.2025 C1269.6599,478.6929 1270.8324,503.1973 1271.7266,521.8852 " fill="none" id="pyexcel-xlsxr-lxml" style="stroke: #A80036; stroke-width: 1.0;"/><!--link sphinxcontrib-excel to pyexcel-handsontable--><path d="M174.5008,120.9336 C196.978,109.8672 227.6452,96.7571 256.5,91 C303.1364,81.6951 316.7711,82.1717 363.5,91 C392.0118,96.3866 422.3908,108.2464 445.7634,118.8289 " fill="none" id="sphinxcontrib-excel-pyexcel-handsontable" style="stroke: #A80036; stroke-width: 1.0;"/><polygon fill="#A80036" points="450.3419,120.926,443.8253,113.5413,445.7961,118.8437,440.4937,120.8146,450.3419,120.926" style="stroke: #A80036; stroke-width: 1.0;"/><!--link libxlsxwpy to pyexcel-libxlsxw--><path d="M677.2155,366.1401 C675.9598,384.8859 674.3127,409.4746 673.2085,425.9587 " fill="none" id="libxlsxwpy-pyexcel-libxlsxw" style="stroke: #A80036; stroke-width: 1.0;"/><!--link libxlsxwpy to libxlsx--><path d="M737.7813,339.5 C749.3122,339.5 760.8432,339.5 772.3741,339.5 " fill="none" id="libxlsxwpy-libxlsx" style="stroke: #A80036; stroke-width: 1.0;"/><!--
11 @startuml
22
33 package "pyexcel" {
3030 [pyexcel-xls]
3131 [pyexcel-xlsx]
3232 [pyexcel-xlsxw]
33 [pyexcel-libxlsxw]
34 [pyexcel-xlsxr]
3335 }
3436
3537 [tabulate] <<dependency>>
4143 [xlsxwriter] <<dependency>>
4244 [ezodf] <<dependency>>
4345 [odfpy] <<dependency>>
46 [lxml] <<dependency>>
47 [libxlsxwpy] <<dependency>>
48 [libxlsx] <<dependency>>
4449
4550 [pyexcel public api] -right-> [pyexcel internal api]
4651 [pyexcel internal api] -right-> [pyexcel plugin interfaces]
5863 [pyexcel-io plugin interfaces] ..> [csv, csvz, tsv, tsvz]
5964 [pyexcel-io plugin interfaces] ..> [django, sqlalchemy]
6065 [pyexcel-io plugin interfaces] .up.> [pyexcel-xls]
66 [pyexcel-io plugin interfaces] .up.> [pyexcel-xlsxw]
67 [pyexcel-io plugin interfaces] .up.> [pyexcel-libxlsxw]
6168 [pyexcel-io plugin interfaces] .up.> [pyexcel-xlsx]
69 [pyexcel-io plugin interfaces] .up.> [pyexcel-xlsxr]
6270 [pyexcel-io plugin interfaces] .left.> [pyexcel-ods]
6371 [pyexcel-io plugin interfaces] .right.> [pyexcel-ods3]
64 [pyexcel-io plugin interfaces] .right.> [pyexcel-xlsxw]
6572 [pyexcel-io plugin interfaces] .right.> [pyexcel-odsr]
73
6674
6775 [pyexcel-text] -up- [tabulate]
6876 [pyexcel-pygal] -up- [pygal]
7078 [pyexcel-xls] -right- [xlrd]
7179 [pyexcel-xls] -right- [xlwt]
7280 [pyexcel-xlsx] -right- [openpyxl]
73 [pyexcel-xlsxw] -down- [xlsxwriter]
81 [pyexcel-xlsxw] -up- [xlsxwriter]
7482 [pyexcel-ods3] - - - [ezodf]
7583 [pyexcel-ods] - - - [odfpy]
84 [pyexcel-odsr] -up- [lxml]
85 [pyexcel-xlsxr] -right- [lxml]
86 [sphinxcontrib-excel] -right-> [pyexcel-handsontable]
87 [pyexcel-libxlsxw] -up- [libxlsxwpy]
88 [libxlsxwpy] -right- [libxlsx]
7689
7790 skinparam component{
7891
88101 JVM: Java HotSpot(TM) 64-Bit Server VM
89102 Java Version: 1.8.0_131-b11
90103 Operating System: Mac OS X
91 OS Version: 10.11.6
104 OS Version: 10.14.6
92105 Default Encoding: UTF-8
93106 Language: en
94 Country: US
107 Country: GB
95108 --></g></svg>⏎
177177 Sheet.number_of_columns
178178 Sheet.row_range
179179 Sheet.column_range
180
181 Iteration
182 -----------------
183
184 .. autosummary::
185 :toctree: generated/
186
187 Sheet.rows
188 Sheet.rrows
189 Sheet.columns
190 Sheet.rcolumns
191 Sheet.enumerate
192 Sheet.reverse
193 Sheet.vertical
194 Sheet.rvertical
195180
196181
197182 Cell access
300285 .. autosummary::
301286 :toctree: generated/
302287
288 Sheet.project
303289 Sheet.transpose
304290 Sheet.map
305291 Sheet.region
6767 .. testcode::
6868 :hide:
6969
70 >>> from mock import patch, MagicMock
70 >>> from unittest.mock import patch, MagicMock
7171 >>> import os
7272 >>> patcher = patch('pyexcel._compact.request.urlopen')
7373 >>> fake_url_open = patcher.start()
253253 .. testcode::
254254 :hide:
255255
256 >>> from mock import patch, MagicMock
256 >>> from unittest.mock import patch, MagicMock
257257 >>> import os
258258 >>> patcher = patch('pyexcel._compact.request.urlopen')
259259 >>> fake_url_open = patcher.start()
11 Read partial data
22 ================================================================================
33
4 .. _get_an_array_from_an_excel_sheet:
5
6 Here the code to get an array of data from your excel file.
7
8 .. pyexcel-table::
9
10 ---pyexcel:data example---
11 1,2,3
12 4,5,6
13 7,8,9
14
15 .. testcode::
16 :hide:
17
18 >>> import pyexcel as pe
19 >>> data = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
20 >>> s = pe.Sheet(data)
21 >>> s.save_as("example.xls")
22
23 The following code will give you the data in json::
24
25 >>> import pyexcel
26 >>> # "example.csv","example.xlsx","example.xlsm"
27 >>> my_array = pyexcel.get_array(file_name="example.xls")
28 >>> my_array
29 [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
30
31 .. testcode::
32 :hide:
33
34 >>> import os
35 >>> os.unlink("example.xls")
364
375
38 Pagination
6 When you are dealing with huge amount of data, e.g. 64GB, obviously you would not
7 like to fill up your memory with those data. What you may want to do is, record
8 data from Nth line, take M records and stop. And you only want to use your memory
9 for the M records, not for beginning part nor for the tail part.
10
11 Hence partial read feature is developed to read partial data into memory for
12 processing.
13
14 You can paginate by row, by column and by both, hence you dictate what portion of the
15 data to read back. But remember only row limit features help you save memory. Let's
16 you use this feature to record data from Nth column, take M number of columns and skip
17 the rest. You are not going to reduce your memory footprint.
18
19 Why did not I see above benefit?
3920 --------------------------------------------------------------------------------
4021
41 When you are dealing with huge amount of data, e.g. 64GB, obviously you would not
42 like to fill up your memory with those data. Hence pagination feature is developed
43 to read partial data into memory for processing. You can paginate by row, by
44 column and by both.
22 This feature depends heavily on the implementation details.
4523
46 Please note that `pyexcel-xls`, `pyexcel-xlsx`, `pyexcel-ods` and `pyexcel-ods3`
47 will read all data into memory. And pagination code here only limits the data
48 returned to your program. With that said, csv readers, `pyexcel-xlsxr` and `pyexcel-odsr`
49 DOES read partial data into memory.
24 `pyexcel-xls`_ (xlrd), `pyexcel-xlsx`_ (openpyxl), `pyexcel-ods`_ (odfpy) and
25 `pyexcel-ods3`_ (pyexcel-ezodf) will read all data into memory. Because xls,
26 xlsx and ods file are effective a zipped folder, all four will unzip the folder
27 and read the content in xml format in **full**, so as to make sense of all details.
5028
29 Hence, during the partial data is been returned, the memory consumption won't
30 differ from reading the whole data back. Only after the partial
31 data is returned, the memory comsumption curve shall jump the cliff. So pagination
32 code here only limits the data returned to your program.
5133
52 .. testcode::
53 :hide:
34 With that said, `pyexcel-xlsxr`_, `pyexcel-odsr`_ and `pyexcel-htmlr`_ DOES read
35 partial data into memory. Those three are implemented in such a way that they
36 consume the xml(html) when needed. When they have read designated portion of the
37 data, they stop, even if they are half way through.
5438
55 >>> import sys
56 >>> if sys.version_info[0] < 3:
57 ... from StringIO import StringIO
58 ... else:
59 ... from io import StringIO
60 >>> from pyexcel_io._compact import OrderedDict
39 In addition, pyexcel's csv readers can read partial data into memory too.
40
6141
6242 Let's assume the following file is a huge csv file:
6343
7555 ... ]
7656 >>> pe.save_as(array=data, dest_file_name="your_file.csv")
7757
58
7859 And let's pretend to read partial data:
60
7961
8062 .. code-block:: python
8163
145127
146128 What we can do is to define a row renderer function as the following:
147129
130 .. code-block:: python
131
148132 >>> def increment_by_one(row):
149133 ... for element in row:
150134 ... yield element + 1
151135
152136 Then pass it onto save_as function using row_renderer:
137
138 .. code-block:: python
153139
154140 >>> pe.isave_as(file_name="your_file.csv",
155141 ... row_renderer=increment_by_one,
161147 If the data content is from a generator, isave_as has to be used.
162148
163149 We can verify if it was done correctly:
150
151 .. code-block:: python
164152
165153 >>> pe.get_sheet(file_name="your_file.xlsx")
166154 your_file.csv:
177165 +---+----+----+
178166 | 7 | 27 | 37 |
179167 +---+----+----+
180
181 .. testcode::
182 :hide:
183
184 >>> import os
185 >>> os.unlink("your_file.csv")
186 >>> os.unlink("your_file.xlsx")
6363 1
6464
6565 .. TIP::
66 With pyexcel, you can regard single sheet reader as an
67 two dimensional array and multi-sheet excel book reader
68 as a ordered dictionary of two dimensional arrays.
69
70 **Write multiple sheet excel file**
66 With pyexcel, you can regard single sheet as an
67 two dimensional array and multi-sheet excel book
68 as an ordered dictionary of two dimensional arrays.
69
70 **Write multiple sheet excel book**
7171
7272 Suppose you have previous data as a dictionary and you want to
7373 save it as multiple sheet excel file::
77 #
88 # This file only contains a selection of the most common options. For a full
99 # list see the documentation:
10 # http://www.sphinx-doc.org/en/master/config
10 # https://www.sphinx-doc.org/en/master/usage/configuration.html
1111
1212 # -- Path setup --------------------------------------------------------------
1313
2222 # -- Project information -----------------------------------------------------
2323
2424 project = 'pyexcel'
25 copyright = '2014-2019 Onni Software Ltd.'
26 author = 'C.W.'
25 copyright = '2014-2020 Onni Software Ltd.'
26 author = 'chfw'
2727 # The short X.Y version
28 version = '0.5.14'
28 version = '0.6.6'
2929 # The full version, including alpha/beta/rc tags
30 release = '0.5.14'
30 release = '0.6.6'
3131
3232 # -- General configuration ---------------------------------------------------
3333
3434 # Add any Sphinx extension module names here, as strings. They can be
3535 # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
3636 # ones.
37 extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.intersphinx', 'sphinx.ext.viewcode',]
37 extensions = [ 'sphinx.ext.autosummary', 'sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.intersphinx', 'sphinx.ext.viewcode', 'sphinxcontrib.excel',]
3838
3939 # Add any paths that contain templates here, relative to this directory.
4040 templates_path = ['_templates']
6868 # -- Options for intersphinx extension ---------------------------------------
6969
7070 # Example configuration for intersphinx: refer to the Python standard library.
71 intersphinx_mapping = {'https://docs.python.org/': None}
71 intersphinx_mapping = {'https://docs.python.org/3/': None}
7272 # TODO: html_theme not configurable upstream
7373 html_theme = 'default'
74
75
7674 def setup(app):
7775 app.add_stylesheet('theme_overrides.css')
7876
8987 intersphinx_mapping.update({
9088 'xlrd': ('http://xlrd.readthedocs.io/en/latest/', None)
9189 })
92
9390 master_doc = "index"
7676 :hide:
7777
7878 >>> session.close()
79 >>> os.unlink('birth.xls')
7980 >>> os.unlink('birth.db')
+0
-6
docs/source/generated/pyexcel.Sheet.columns.rst less more
0 pyexcel.Sheet.columns
1 =====================
2
3 .. currentmodule:: pyexcel
4
5 .. automethod:: Sheet.columns⏎
+0
-6
docs/source/generated/pyexcel.Sheet.enumerate.rst less more
0 pyexcel.Sheet.enumerate
1 =======================
2
3 .. currentmodule:: pyexcel
4
5 .. automethod:: Sheet.enumerate⏎
0 pyexcel.Sheet.project
1 =====================
2
3 .. currentmodule:: pyexcel
4
5 .. automethod:: Sheet.project⏎
+0
-6
docs/source/generated/pyexcel.Sheet.rcolumns.rst less more
0 pyexcel.Sheet.rcolumns
1 ======================
2
3 .. currentmodule:: pyexcel
4
5 .. automethod:: Sheet.rcolumns⏎
+0
-6
docs/source/generated/pyexcel.Sheet.reverse.rst less more
0 pyexcel.Sheet.reverse
1 =====================
2
3 .. currentmodule:: pyexcel
4
5 .. automethod:: Sheet.reverse⏎
+0
-6
docs/source/generated/pyexcel.Sheet.rows.rst less more
0 pyexcel.Sheet.rows
1 ==================
2
3 .. currentmodule:: pyexcel
4
5 .. automethod:: Sheet.rows⏎
+0
-6
docs/source/generated/pyexcel.Sheet.rrows.rst less more
0 pyexcel.Sheet.rrows
1 ===================
2
3 .. currentmodule:: pyexcel
4
5 .. automethod:: Sheet.rrows⏎
1414
1515 ~Sheet.__init__
1616 ~Sheet.cell_value
17 ~Sheet.clone
1718 ~Sheet.column_at
1819 ~Sheet.column_range
1920 ~Sheet.columns
7374 ~Sheet.number_of_rows
7475 ~Sheet.paste
7576 ~Sheet.plot
77 ~Sheet.project
7678 ~Sheet.rcolumns
7779 ~Sheet.region
7880 ~Sheet.register_input
+0
-6
docs/source/generated/pyexcel.Sheet.rvertical.rst less more
0 pyexcel.Sheet.rvertical
1 =======================
2
3 .. currentmodule:: pyexcel
4
5 .. automethod:: Sheet.rvertical⏎
+0
-6
docs/source/generated/pyexcel.Sheet.vertical.rst less more
0 pyexcel.Sheet.vertical
1 ======================
2
3 .. currentmodule:: pyexcel
4
5 .. automethod:: Sheet.vertical⏎
3939
4040 > test.bat
4141
42 How to update test environment and update documentation
43 ---------------------------------------------------------
4442
45 Additional steps are required:
43 Before you commit
44 ------------------------------
4645
47 #. pip install moban
48 #. git clone https://github.com/moremoban/setupmobans.git # generic setup
49 #. git clone https://github.com/pyexcel/pyexcel-commons.git commons
50 #. make your changes in `.moban.d` directory, then issue command `moban`
46 Please run::
5147
52 What is pyexcel-commons
53 ---------------------------------
48 $ make format
5449
55 Many information that are shared across pyexcel projects, such as: this developer guide, license info, etc. are stored in `pyexcel-commons` project.
56
57 What is .moban.d
58 ---------------------------------
59
60 `.moban.d` stores the specific meta data for the library.
61
62 Acceptance criteria
63 -------------------
64
65 #. Has Test cases written
66 #. Has all code lines tested
67 #. Passes all Travis CI builds
68 #. Has fair amount of documentation if your change is complex
69 #. run 'make format' so as to confirm the pyexcel organisation's coding style
70 #. Please update CHANGELOG.rst
71 #. Please add yourself to CONTRIBUTORS.rst
72 #. Agree on NEW BSD License for your contribution
50 so as to beautify your code otherwise travis-ci may fail your unit test.
1414
1515 ~Matrix.__init__
1616 ~Matrix.cell_value
17 ~Matrix.clone
1718 ~Matrix.column_at
1819 ~Matrix.column_range
1920 ~Matrix.columns
00 `pyexcel` - Let you focus on data, instead of file formats
11 ================================================================================
22
3 :Author: C.W.
3 :Author: chfw
44 :Source code: http://github.com/pyexcel/pyexcel.git
55 :Issues: http://github.com/pyexcel/pyexcel/issues
66 :License: New BSD License
2020
2121 The idea originated from the common usability problem: when an excel file
2222 driven web application is delivered for non-developer users (ie: team assistant,
23 human resource administrator etc). The fact is that not everyone knows (or cares) about the
24 differences between various excel formats: csv, xls, xlsx are all the same to them. Instead of training those users
25 about file formats, this library helps web developers to handle most of the excel file
26 formats by providing a common programming interface. To add a specific excel file format type
27 to you application, all you need is to install an extra pyexcel plugin. Hence no code changes
28 to your application and no issues with excel file formats any more. Looking at the
29 community, this library and its associated ones try to become a small and easy to
30 install alternative to Pandas.
23 human resource administrator etc). The fact is that not everyone knows (or cares)
24 about the differences between various excel formats: csv, xls, xlsx are all
25 the same to them. Instead of training those users about file formats, this
26 library helps web developers to handle most of the excel file
27 formats by providing a common programming interface. To add a specific excel
28 file format type to you application, all you need is to install an extra pyexcel
29 plugin. Hence no code changes to your application and no issues with excel file
30 formats any more. Looking at the community, this library and its associated ones
31 try to become a small and easy to install alternative to Pandas.
3132
3233
3334 Support the project
3435 ================================================================================
3536
3637 If your company has embedded pyexcel and its components into a revenue generating
37 product, please support me on `patreon <https://www.patreon.com/bePatron?u=5537627>`_
38 product, please support me on github, `patreon <https://www.patreon.com/bePatron?u=5537627>`_
3839 or `bounty source <https://salt.bountysource.com/teams/chfw-pyexcel>`_ to maintain
3940 the project and develop it further.
4041
6768 $ cd pyexcel
6869 $ python setup.py install
6970
71 Suppose you have the following data in a dictionary:
72
73 ========= ====
74 Name Age
75 ========= ====
76 Adam 28
77 Beatrice 29
78 Ceri 30
79 Dean 26
80 ========= ====
81
82 you can easily save it into an excel file using the following code:
83
84 .. code-block:: python
85
86 >>> import pyexcel
87 >>> # make sure you had pyexcel-xls installed
88 >>> a_list_of_dictionaries = [
89 ... {
90 ... "Name": 'Adam',
91 ... "Age": 28
92 ... },
93 ... {
94 ... "Name": 'Beatrice',
95 ... "Age": 29
96 ... },
97 ... {
98 ... "Name": 'Ceri',
99 ... "Age": 30
100 ... },
101 ... {
102 ... "Name": 'Dean',
103 ... "Age": 26
104 ... }
105 ... ]
106 >>> pyexcel.save_as(records=a_list_of_dictionaries, dest_file_name="your_file.xls")
107
108 And here's how to obtain the records:
109
110 .. code-block:: python
111
112 >>> import pyexcel as p
113 >>> records = p.iget_records(file_name="your_file.xls")
114 >>> for record in records:
115 ... print("%s is aged at %d" % (record['Name'], record['Age']))
116 Adam is aged at 28
117 Beatrice is aged at 29
118 Ceri is aged at 30
119 Dean is aged at 26
120 >>> p.free_resources()
121
122
123 Custom data rendering:
124
125 .. code-block:: python
126
127 >>> # pip install pyexcel-text==0.2.7.1
128 >>> import pyexcel as p
129 >>> ccs_insight2 = p.Sheet()
130 >>> ccs_insight2.name = "Worldwide Mobile Phone Shipments (Billions), 2017-2021"
131 >>> ccs_insight2.ndjson = """
132 ... {"year": ["2017", "2018", "2019", "2020", "2021"]}
133 ... {"smart phones": [1.53, 1.64, 1.74, 1.82, 1.90]}
134 ... {"feature phones": [0.46, 0.38, 0.30, 0.23, 0.17]}
135 ... """.strip()
136 >>> ccs_insight2
137 pyexcel sheet:
138 +----------------+------+------+------+------+------+
139 | year | 2017 | 2018 | 2019 | 2020 | 2021 |
140 +----------------+------+------+------+------+------+
141 | smart phones | 1.53 | 1.64 | 1.74 | 1.82 | 1.9 |
142 +----------------+------+------+------+------+------+
143 | feature phones | 0.46 | 0.38 | 0.3 | 0.23 | 0.17 |
144 +----------------+------+------+------+------+------+
145
146
147 Advanced usage :fire:
148 ----------------------
149
150 If you are dealing with big data, please consider these usages:
151
152 .. code-block:: python
153
154 >>> def increase_everyones_age(generator):
155 ... for row in generator:
156 ... row['Age'] += 1
157 ... yield row
158 >>> def duplicate_each_record(generator):
159 ... for row in generator:
160 ... yield row
161 ... yield row
162 >>> records = p.iget_records(file_name="your_file.xls")
163 >>> io=p.isave_as(records=duplicate_each_record(increase_everyones_age(records)),
164 ... dest_file_type='csv', dest_lineterminator='\n')
165 >>> print(io.getvalue())
166 Age,Name
167 29,Adam
168 29,Adam
169 30,Beatrice
170 30,Beatrice
171 31,Ceri
172 31,Ceri
173 27,Dean
174 27,Dean
175 <BLANKLINE>
176
177
178 Two advantages of above method:
179
180 #. Add as many wrapping functions as you want.
181 #. Constant memory consumption
182
183
184 .. testcode::
185 :hide:
186
187 >>> import os
188 >>> os.unlink("your_file.xls")
189
190
191
70192 For individual excel file formats, please install them as you wish:
71193
72194 .. _file-format-list:
74196
75197 .. table:: A list of file formats supported by external plugins
76198
77 ======================== ======================= ================= ==================
78 Package name Supported file formats Dependencies Python versions
79 ======================== ======================= ================= ==================
80 `pyexcel-io`_ csv, csvz [#f1]_, tsv, 2.6, 2.7, 3.3,
81 tsvz [#f2]_ 3.4, 3.5, 3.6
82 pypy
83 `pyexcel-xls`_ xls, xlsx(read only), `xlrd`_, same as above
199 ======================== ======================= =================
200 Package name Supported file formats Dependencies
201 ======================== ======================= =================
202 `pyexcel-io`_ csv, csvz [#f1]_, tsv,
203 tsvz [#f2]_
204 `pyexcel-xls`_ xls, xlsx(read only), `xlrd`_,
84205 xlsm(read only) `xlwt`_
85 `pyexcel-xlsx`_ xlsx `openpyxl`_ same as above
86 `pyexcel-ods3`_ ods `pyexcel-ezodf`_, 2.6, 2.7, 3.3, 3.4
87 lxml 3.5, 3.6
88 `pyexcel-ods`_ ods `odfpy`_ same as above
89 ======================== ======================= ================= ==================
206 `pyexcel-xlsx`_ xlsx `openpyxl`_
207 `pyexcel-ods3`_ ods `pyexcel-ezodf`_,
208 lxml
209 `pyexcel-ods`_ ods `odfpy`_
210 ======================== ======================= =================
90211
91212 .. table:: Dedicated file reader and writers
92213
93 ======================== ======================= ================= ==================
94 Package name Supported file formats Dependencies Python versions
95 ======================== ======================= ================= ==================
96 `pyexcel-xlsxw`_ xlsx(write only) `XlsxWriter`_ Python 2 and 3
97 `pyexcel-xlsxr`_ xlsx(read only) lxml same as above
98 `pyexcel-xlsbr`_ xlsx(read only) pyxlsb same as above
99 `pyexcel-odsr`_ read only for ods, fods lxml same as above
100 `pyexcel-odsw`_ write only for ods loxun same as above
101 `pyexcel-htmlr`_ html(read only) lxml,html5lib same as above
102 `pyexcel-pdfr`_ pdf(read only) pdftables Python 2 only.
103 ======================== ======================= ================= ==================
214 ======================== ======================= =================
215 Package name Supported file formats Dependencies
216 ======================== ======================= =================
217 `pyexcel-xlsxw`_ xlsx(write only) `XlsxWriter`_
218 `pyexcel-libxlsxw`_ xlsx(write only) `libxlsxwriter`_
219 `pyexcel-xlsxr`_ xlsx(read only) lxml
220 `pyexcel-xlsbr`_ xlsb(read only) pyxlsb
221 `pyexcel-odsr`_ read only for ods, fods lxml
222 `pyexcel-odsw`_ write only for ods loxun
223 `pyexcel-htmlr`_ html(read only) lxml,html5lib
224 `pyexcel-pdfr`_ pdf(read only) camelot
225 ======================== ======================= =================
226
227
228 Plugin shopping guide
229 ------------------------
230
231 Since 2020, all pyexcel-io plugins have dropped the support for python version
232 lower than 3.6. If you want to use any python verions, please use pyexcel-io
233 and its plugins version lower than 0.6.0.
234
235
236 Except csv files, xls, xlsx and ods files are a zip of a folder containing a lot of
237 xml files
238
239 The dedicated readers for excel files can stream read
240
241
242 In order to manage the list of plugins installed, you need to use pip to add or remove
243 a plugin. When you use virtualenv, you can have different plugins per virtual
244 environment. In the situation where you have multiple plugins that does the same thing
245 in your environment, you need to tell pyexcel which plugin to use per function call.
246 For example, pyexcel-ods and pyexcel-odsr, and you want to get_array to use pyexcel-odsr.
247 You need to append get_array(..., library='pyexcel-odsr').
248
104249
105250
106251 .. _pyexcel-io: https://github.com/pyexcel/pyexcel-io
113258 .. _pyexcel-pdfr: https://github.com/pyexcel/pyexcel-pdfr
114259
115260 .. _pyexcel-xlsxw: https://github.com/pyexcel/pyexcel-xlsxw
261 .. _pyexcel-libxlsxw: https://github.com/pyexcel/pyexcel-libxlsxw
116262 .. _pyexcel-xlsxr: https://github.com/pyexcel/pyexcel-xlsxr
117263 .. _pyexcel-xlsbr: https://github.com/pyexcel/pyexcel-xlsbr
118264 .. _pyexcel-htmlr: https://github.com/pyexcel/pyexcel-htmlr
123269 .. _XlsxWriter: https://github.com/jmcnamara/XlsxWriter
124270 .. _pyexcel-ezodf: https://github.com/pyexcel/pyexcel-ezodf
125271 .. _odfpy: https://github.com/eea/odfpy
272 .. _libxlsxwriter: http://libxlsxwriter.github.io/getting_started.html
126273
127274 .. table:: Other data renderers
128275
156303 .. _pyexcel-gantt: https://github.com/pyexcel/pyexcel-gantt
157304 .. _frappe-gantt: https://github.com/frappe/gantt
158305
159 In order to manage the list of plugins installed, you need to use pip to add or remove
160 a plugin. When you use virtualenv, you can have different plugins per virtual
161 environment. In the situation where you have multiple plugins that does the same thing
162 in your environment, you need to tell pyexcel which plugin to use per function call.
163 For example, pyexcel-ods and pyexcel-odsr, and you want to get_array to use pyexcel-odsr.
164 You need to append get_array(..., library='pyexcel-odsr').
165
166306 .. rubric:: Footnotes
167307
168308 .. [#f1] zipped csv file
175315 ======== ========== ============= ==================== ============= =============
176316 pyexcel pyexcel-io pyexcel-text pyexcel-handsontable pyexcel-pygal pyexcel-gantt
177317 ======== ========== ============= ==================== ============= =============
178 0.5.14+ 0.5.18+ 0.2.6+ 0.0.1+ 0.0.1 0.0.1
318 0.6.5+ 0.6.2+ 0.2.6+ 0.0.1+ 0.0.1 0.0.1
319 0.5.15+ 0.5.19+ 0.2.6+ 0.0.1+ 0.0.1 0.0.1
320 0.5.14 0.5.18 0.2.6+ 0.0.1+ 0.0.1 0.0.1
179321 0.5.10+ 0.5.11+ 0.2.6+ 0.0.1+ 0.0.1 0.0.1
180322 0.5.9.1+ 0.5.9.1+ 0.2.6+ 0.0.1 0.0.1 0.0.1
181323 0.5.4+ 0.5.1+ 0.2.6+ 0.0.1 0.0.1 0.0.1
282424 architecture
283425
284426 New tutorial
285 ----------
427 --------------
286428 .. toctree::
287429
288430 quickstart
295437 database
296438
297439 Old tutorial
298 ----------
440 --------------
299441 .. toctree::
300442
301443 tutorial_file
0 What's breaking in 0.6.0
1 ================================================================================
2
3 In the following statements::
4
5 sheet_a = sheet.row + rows
6 sheet_b = sheet.column + columns
7 book = sheet_a + sheet_b
8
9 `sheet_a` and `sheet_b` will no longer have access to the data of `sheet`. `book`
10 will no longer have access to the data of `sheet_a` and `sheet_b`.
11
12 Under Hyrum's Law, this enhancement in 0.6.0 will cause breakage otherwise.
13
014 What's breaking in 0.5.9
115 ================================================================================
216
0
01 One liners
12 ================================================================================
23
34 This section shows you how to get data from your excel files and how to
45 export data to excel files in **one line**
56
6 One liner to get data from the excel files
7 Read from the excel files
78 --------------------------------------------------------------------------------
89
910 Get a list of dictionaries
2425 >>> sheet = p.get_sheet(file_content=content, file_type='csv')
2526 >>> sheet.save_as("your_file.xls")
2627
27 Suppose you want to process the :download:`following coffee data <coffee.csv>` (data source `coffee chart <https://cspinet.org/eating-healthy/ingredients-of-concern/caffeine-chart>`_ on the center for science in the public interest):
28
29 Suppose you want to process the following coffee data (data source `coffee chart <https://cspinet.org/eating-healthy/ingredients-of-concern/caffeine-chart>`_ on the center for science in the public interest):
30
2831
2932 .. pyexcel-table::
3033
3538 Starbucks Coffee Pike Place Roast,grande(16 oz.),310
3639 Panera Coffee Light Roast,regular(16 oz.),300
3740
38 Let's get a list of dictionary out from the xls file::
39
41
42 Let's get a list of dictionary out from the xls file:
43
44 .. code-block:: python
45
4046 >>> records = p.get_records(file_name="your_file.xls")
4147
42 And let's check what do we have::
43
44 >>> for record in records:
45 ... print("%s of %s has %s mg" % (
46 ... record['Serving Size'],
47 ... record['Coffees'],
48 ... record['Caffeine (mg)']))
48 And let's check what do we have:
49
50 .. code-block:: python
51
52 >>> for r in records:
53 ... print(f"{r['Serving Size']} of {r['Coffees']} has {r['Caffeine (mg)']} mg")
4954 venti(20 oz) of Starbucks Coffee Blonde Roast has 475 mg
5055 large(20 oz.) of Dunkin' Donuts Coffee with Turbo Shot has 398 mg
5156 grande(16 oz.) of Starbucks Coffee Pike Place Roast has 310 mg
5560 Get two dimensional array
5661 ********************************************************************************
5762
58 Instead, what if you have to use :meth:`pyexcel.get_array` to do the same:
63 Instead, what if you have to use `pyexcel.get_array` to do the same:
64
65 .. code-block:: python
5966
6067 >>> for row in p.get_array(file_name="your_file.xls", start_row=1):
61 ... print("%s of %s has %s mg" % (
62 ... row[1],
63 ... row[0],
64 ... row[2]))
68 ... print(f"{row[1]} of {row[0]} has {row[2]} mg")
6569 venti(20 oz) of Starbucks Coffee Blonde Roast has 475 mg
6670 large(20 oz.) of Dunkin' Donuts Coffee with Turbo Shot has 398 mg
6771 grande(16 oz.) of Starbucks Coffee Pike Place Roast has 310 mg
6872 regular(16 oz.) of Panera Coffee Light Roast has 300 mg
6973
74
7075 where `start_row` skips the header row.
7176
7277
7883 Now let's get a dictionary out from the spreadsheet:
7984
8085 .. code-block:: python
81
86
8287 >>> my_dict = p.get_dict(file_name="your_file.xls", name_columns_by_row=0)
8388
84 And check what do we have::
89 And check what do we have:
90
91 .. code-block:: python
8592
8693 >>> from pyexcel._compact import OrderedDict
8794 >>> isinstance(my_dict, OrderedDict)
126133 >>> data.update({"Sheet 3": a_dictionary_of_two_dimensional_arrays['Sheet 3']})
127134 >>> p.save_book_as(bookdict=data, dest_file_name="book.xls")
128135
136
129137 Suppose you have a multiple sheet book as the following:
138
130139
131140 .. pyexcel-table::
132141
145154 3,2,1
146155 4,3,2
147156
148 Here is the code to obtain those sheets as a single dictionary::
157
158 Here is the code to obtain those sheets as a single dictionary:
159
160 .. code-block:: python
149161
150162 >>> book_dict = p.get_book_dict(file_name="book.xls")
151163
152 And check::
164 And check:
165
166 .. code-block:: python
167
153168 >>> isinstance(book_dict, OrderedDict)
154169 True
155170 >>> import json
159174 {"Sheet 2": [["X", "Y", "Z"], [1, 2, 3], [4, 5, 6]]}
160175 {"Sheet 3": [["O", "P", "Q"], [3, 2, 1], [4, 3, 2]]}
161176
177
162178 .. testcode::
163179 :hide:
164180
166182 >>> os.unlink("book.xls")
167183
168184
169 Data export in one line
185 Write data
170186 ---------------------------------------------
171187
172188 Export an array
173189 **********************
174190
175 Suppose you have the following array::
191 Suppose you have the following array:
192
193 .. code-block:: python
176194
177195 >>> data = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
178196
179 And here is the code to save it as an excel file ::
197 And here is the code to save it as an excel file :
198
199 .. code-block:: python
180200
181201 >>> p.save_as(array=data, dest_file_name="example.xls")
182202
183 Let's verify it::
203 Let's verify it:
204
205 .. code-block:: python
184206
185207 >>> p.get_sheet(file_name="example.xls")
186208 pyexcel_sheet1:
198220 >>> import os
199221 >>> os.unlink("example.xls")
200222
201
202 And here is the code to save it as a csv file ::
223 And here is the code to save it as a csv file :
224
225 .. code-block:: python
203226
204227 >>> p.save_as(array=data,
205228 ... dest_file_name="example.csv",
206229 ... dest_delimiter=':')
207230
208 Let's verify it::
231 Let's verify it:
232
233 .. code-block:: python
209234
210235 >>> with open("example.csv") as f:
211236 ... for line in f.readlines():
218243 Export a list of dictionaries
219244 **********************************
220245
221 ::
246 .. code-block:: python
222247
223248 >>> records = [
224249 ... {"year": 1903, "country": "Germany", "speed": "206.7km/h"},
231256 Export a dictionary of single key value pair
232257 ********************************************************************************
233258
234 ::
259 .. code-block:: python
235260
236261 >>> henley_on_thames_facts = {
237262 ... "area": "5.58 square meters",
246271 Export a dictionary of single dimensonal array
247272 ********************************************************************************
248273
274 .. code-block:: python
275
249276 >>> ccs_insights = {
250277 ... "year": ["2017", "2018", "2019", "2020", "2021"],
251278 ... "smart phones": [1.53, 1.64, 1.74, 1.82, 1.90],
257284 Export a dictionary of two dimensional array as a book
258285 ********************************************************************************
259286
260 Suppose you want to save the below dictionary to an excel file ::
261
287 Suppose you want to save the below dictionary to an excel file :
288
289 .. code-block:: python
290
262291 >>> a_dictionary_of_two_dimensional_arrays = {
263292 ... 'Sheet 1':
264293 ... [
280309 ... ]
281310 ... }
282311
283 Here is the code::
312 Here is the code:
313
314 .. code-block:: python
284315
285316 >>> p.save_book_as(
286317 ... bookdict=a_dictionary_of_two_dimensional_arrays,
288319 ... )
289320
290321 If you want to preserve the order of sheets in your dictionary, you have to
291 pass on an ordered dictionary to the function itself. For example::
322 pass on an ordered dictionary to the function itself. For example:
323
324 .. code-block:: python
292325
293326 >>> data = OrderedDict()
294327 >>> data.update({"Sheet 2": a_dictionary_of_two_dimensional_arrays['Sheet 2']})
296329 >>> data.update({"Sheet 3": a_dictionary_of_two_dimensional_arrays['Sheet 3']})
297330 >>> p.save_book_as(bookdict=data, dest_file_name="book.xls")
298331
299 Let's verify its order::
332 Let's verify its order:
333
334 .. code-block:: python
300335
301336 >>> book_dict = p.get_book_dict(file_name="book.xls")
302337 >>> for key, item in book_dict.items():
308343 Please notice that "Sheet 2" is the first item in the *book_dict*, meaning the order of sheets are preserved.
309344
310345
311 File format transcoding on one line
346 Transcoding
312347 -------------------------------------------
313348
314349 .. note::
315350
316 Please note that the following file transcoding could be with zero line. Please
317 install pyexcel-cli and you will do the transcode in one command. No need to
318 open your editor, save the problem, then python run.
319
351 Please note that `pyexcel-cli` can perform file transcoding at command line.
352 No need to open your editor, save the problem, then python run.
320353
321354 .. testcode::
322355 :hide:
329362 ... ]
330363 >>> p.save_as(array=data, dest_file_name="birth.xls")
331364
332 The following code does a simple file format transcoding from xls to csv::
365
366 The following code does a simple file format transcoding from xls to csv:
367
368 .. code-block:: python
333369
334370 >>> p.save_as(file_name="birth.xls", dest_file_name="birth.csv")
335371
336372 Again it is really simple. Let's verify what we have gotten:
373
374 .. code-block:: python
337375
338376 >>> sheet = p.get_sheet(file_name="birth.csv")
339377 >>> sheet
353391
354392 Let use previous example and save it as xlsx instead
355393
394 .. code-block:: python
395
356396 >>> p.save_as(file_name="birth.xls",
357397 ... dest_file_name="birth.xlsx") # change the file extension
358398
359399 Again let's verify what we have gotten:
400
401 .. code-block:: python
360402
361403 >>> sheet = p.get_sheet(file_name="birth.xlsx")
362404 >>> sheet
376418 Merge all excel files in directory into a book where each file become a sheet
377419 ********************************************************************************
378420
379 The following code will merge every excel files into one file, say "output.xls"::
421 The following code will merge every excel files into one file, say "output.xls":
422
423 .. code-block:: python
380424
381425 from pyexcel.cookbook import merge_all_to_a_book
382426 import glob
384428
385429 merge_all_to_a_book(glob.glob("your_csv_directory\*.csv"), "output.xls")
386430
387 You can mix and match with other excel formats: xls, xlsm and ods. For example, if you are sure you have only xls, xlsm, xlsx, ods and csv files in `your_excel_file_directory`, you can do the following::
431 You can mix and match with other excel formats: xls, xlsm and ods. For example, if you are sure you have only xls, xlsm, xlsx, ods and csv files in `your_excel_file_directory`, you can do the following:
432
433 .. code-block:: python
388434
389435 from pyexcel.cookbook import merge_all_to_a_book
390436 import glob
399445 :hide:
400446
401447 >>> content = {
402 ... 'Sheet 1':
448 ... 'Sheet 1':
403449 ... [
404 ... [1.0, 2.0, 3.0],
405 ... [4.0, 5.0, 6.0],
450 ... [1.0, 2.0, 3.0],
451 ... [4.0, 5.0, 6.0],
406452 ... [7.0, 8.0, 9.0]
407453 ... ],
408 ... 'Sheet 2':
454 ... 'Sheet 2':
409455 ... [
410 ... ['X', 'Y', 'Z'],
411 ... [1.0, 2.0, 3.0],
456 ... ['X', 'Y', 'Z'],
457 ... [1.0, 2.0, 3.0],
412458 ... [4.0, 5.0, 6.0]
413 ... ],
414 ... 'Sheet 3':
459 ... ],
460 ... 'Sheet 3':
415461 ... [
416 ... ['O', 'P', 'Q'],
417 ... [3.0, 2.0, 1.0],
462 ... ['O', 'P', 'Q'],
463 ... [3.0, 2.0, 1.0],
418464 ... [4.0, 3.0, 2.0]
419 ... ]
465 ... ]
420466 ... }
421467 >>> book = p.Book(content)
422468 >>> book.save_as("megabook.xls")
423469
424 Suppose you have many sheets in a work book and you would like to separate each into a single sheet excel file. You can easily do this::
470
471 Suppose you have many sheets in a work book and you would like to separate each into a single sheet excel file. You can easily do this:
472
473 .. code-block:: python
425474
426475 >>> from pyexcel.cookbook import split_a_book
427476 >>> split_a_book("megabook.xls", "output.xls")
447496 *************************************
448497
449498
450 Suppose you just want to extract one sheet from many sheets that exists in a work book and you would like to separate it into a single sheet excel file. You can easily do this::
499 Suppose you just want to extract one sheet from many sheets that exists in a work book and you would like to separate it into a single sheet excel file. You can easily do this:
500
501 .. code-block:: python
451502
452503 >>> from pyexcel.cookbook import extract_a_sheet_from_a_book
453504 >>> extract_a_sheet_from_a_book("megabook.xls", "Sheet 1", "output.xls")
2828 `pyexcel-echarts`_ draws 2D, 3D, geo charts from pyexcel data and has awesome animations too, but
2929 it is under development.
3030
31 `pyexcel-matplotlib`_ helps you with scentific charts and is under developmement.
31 `pyexcel-matplotlib`_ helps you with scientific charts and is under developmement.
3232
3333 Gantt chart visualization for your excel data
3434 -------------------------------------------------
4242 .. _pyexcel-echarts: https://github.com/pyexcel-renderers/pyexcel-echarts
4343 .. _pyexcel-matplotlib: https://github.com/pyexcel-renderers/pyexcel-matplotlib
4444 .. _sphinxcontrib-excel: https://github.com/pyexcel-renderers/sphinxcontrib-excel
45 .. _pyexcel-gantt: https://github.com/pyexcel-renderers/pyexcel-gantt
4546
47
00 Sheet
11 ==========
2
3 The sheet api here is much less powerful than pandas DataFrame when the array is of
4 significant size. To be honesty, pandas DataFrame is much more powerful and
5 provide rich data manipulation apis. When would you consider the sheet api here?
6 if your data manipulation steps are basic and your data volume is not high, you
7 can use them.
8
29
310 Random access
411 -----------------
6875 In order to set a value to a cell, please use
6976 sheet[row_index, column_index] = new_value
7077
78 or sheet['A1'] = new_value
79
7180
7281 **Random access to rows and columns**
7382
185194
186195 >>> list(sheet.rows())
187196 [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
188
189 You can get data from the bottom to the top one by
190 calling :meth:`~pyexcel.Sheet.rrows()`::
191
192 >>> list(sheet.rrows())
193 [[7, 8, 9], [4, 5, 6], [1, 2, 3]]
194
195 You might want the data arranged vertically. You can call
196 :meth:`~pyexcel.Sheet.columns()`::
197
198 >>> list(sheet.columns())
199 [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
200
201 You can get columns in reverse sequence as well by calling
202 :meth:`~pyexcel.Sheet.rcolumns()`::
203
204 >>> list(sheet.rcolumns())
205 [[3, 6, 9], [2, 5, 8], [1, 4, 7]]
206
207 Do you want to flatten the data? You can get the content in one
208 dimensional array. If you are interested in playing with one
209 dimensional enumeration, you can check out these functions
210 :meth:`~pyexcel.Sheet.enumerate`, :meth:`~pyexcel.Sheet.reverse`,
211 :meth:`~pyexcel.Sheet.vertical`, and :meth:`~pyexcel.Sheet.rvertical()`::
212
213 >>> list(sheet.enumerate())
214 [1, 2, 3, 4, 5, 6, 7, 8, 9]
215 >>> list(sheet.reverse())
216 [9, 8, 7, 6, 5, 4, 3, 2, 1]
217 >>> list(sheet.vertical())
218 [1, 4, 7, 2, 5, 8, 3, 6, 9]
219 >>> list(sheet.rvertical())
220 [9, 6, 3, 8, 5, 2, 7, 4, 1]
221197
222198
223199 **attributes**
397373 ... [12, 15]
398374 ... ]
399375 >>> sheet2 = pyexcel.Sheet(extra_data)
400 >>> sheet.column += sheet2
401 >>> sheet.column["Column 4"]
376 >>> sheet3 = sheet.column + sheet2
377 >>> sheet3.column["Column 4"]
402378 [10, 11, 12]
403 >>> sheet.column["Column 5"]
379 >>> sheet3.column["Column 5"]
404380 [13, 14, 15]
381
382 Please note above column plus statement will not update original `sheet` instance, as
383 pyexcel user demanded:
384
385 .. code-block:: python
386
387 >>> sheet
388 pyexcel sheet:
389 +----------+----------+
390 | Column 1 | Column 3 |
391 +==========+==========+
392 | 1 | 7 |
393 +----------+----------+
394 | 2 | 8 |
395 +----------+----------+
396 | 3 | 9 |
397 +----------+----------+
398
399 So, to change orginal `sheet` instance, you can elect to do:
400
401 .. code-block:: python
402
403 >>> sheet.column += sheet2
405404
406405 Here is what you will get:
407406
160160 .. testcode::
161161 :hide:
162162
163 >>> from mock import patch, MagicMock
163 >>> from unittest.mock import patch, MagicMock
164164 >>> import pyexcel as pe
165 >>> from pyexcel._compact import StringIO
165 >>> from pyexcel._compact import StringIO, PY2, BytesIO
166166 >>> patcher = patch('pyexcel._compact.request.urlopen')
167167 >>> urlopen = patcher.start()
168 >>> io = StringIO("1,2,3")
168 >>> io = StringIO("1,2,3") if PY2 else BytesIO("1,2,3".encode('utf-8'))
169169 >>> x = MagicMock()
170170 >>> x.type.return_value = "text/csv"
171171 >>> io.info = x
200200 .. testcode::
201201 :hide:
202202
203 >>> from mock import patch, MagicMock
203 >>> from unittest.mock import patch, MagicMock
204204 >>> import os
205205 >>> patcher = patch('pyexcel._compact.request.urlopen')
206206 >>> fake_url_open = patcher.start()
240240 .. testcode::
241241 :hide:
242242
243 >>> from mock import patch, MagicMock
243 >>> from unittest.mock import patch, MagicMock
244244 >>> import os
245245 >>> patcher = patch('pyexcel._compact.request.urlopen')
246246 >>> fake_url_open = patcher.start()
514514 >>> os.unlink("output.csv")
515515 >>> os.unlink("example.xls")
516516 >>> os.unlink("example_series.xls")
517 >>> os.unlink("tutorial.csv")
250250 :hide:
251251
252252 >>> import os
253 >>> import pyexcel
253 >>> import pyexcel as pe
254254 >>> data = [
255255 ... ["Row 1", 1, 2, 3],
256256 ... ["Row 2", 4, 5, 6],
257257 ... ["Row 3", 7, 8, 9],
258258 ... ]
259 >>> pyexcel.save_as(array=data, dest_file_name="row_example.xls")
259 >>> pe.save_as(array=data, dest_file_name="row_example.xls")
260260
261261 And then you want to update "Row 3" with for example::
262262
0
01 Stream APIs for big file : A set of two liners
12 ================================================================================
23
4 When you are dealing with **BIG** excel files, you will want **pyexcel** to use
5 constant memory.
6
37 This section shows you how to get data from your **BIG** excel files and how to
4 export data to excel files in **two lines** at most.
8 export data to excel files in **two lines** at most, without eating all
9 your computer memory.
510
611
712 Two liners for get data from big excel files
2530 >>> sheet = p.get_sheet(file_content=content, file_type='csv')
2631 >>> sheet.save_as("your_file.xls")
2732
33
34
2835 Suppose you want to process the following coffee data:
2936
3037 .. pyexcel-table::
3643 Starbucks Coffee Pike Place Roast,grande(16 oz.),310
3744 Panera Coffee Light Roast,regular(16 oz.),300
3845
39 Let's get a list of dictionary out from the xls file::
40
46 Let's get a list of dictionary out from the xls file:
47
48 .. code-block:: python
49
4150 >>> records = p.iget_records(file_name="your_file.xls")
4251
43 And let's check what do we have::
44
45 >>> for record in records:
46 ... print("%s of %s has %s mg" % (
47 ... record['Serving Size'],
48 ... record['Coffees'],
49 ... record['Caffeine (mg)']))
52 And let's check what do we have:
53
54 .. code-block:: python
55
56 >>> for r in records:
57 ... print(f"{r['Serving Size']} of {r['Coffees']} has {r['Caffeine (mg)']} mg")
5058 venti(20 oz) of Starbucks Coffee Blonde Roast has 475 mg
5159 large(20 oz.) of Dunkin' Donuts Coffee with Turbo Shot has 398 mg
5260 grande(16 oz.) of Starbucks Coffee Pike Place Roast has 310 mg
5361 regular(16 oz.) of Panera Coffee Light Roast has 300 mg
5462
55 Please do not forgot the second line::
63 Please do not forgot the second line to close the opened file handle:
64
65 .. code-block:: python
5666
5767 >>> p.free_resources()
5868
5969 Get two dimensional array
6070 ********************************************************************************
6171
62 Instead, what if you have to use :meth:`pyexcel.get_array` to do the same:
72 Instead, what if you have to use `pyexcel.get_array` to do the same:
73
74 .. code-block:: python
6375
6476 >>> for row in p.iget_array(file_name="your_file.xls", start_row=1):
65 ... print("%s of %s has %s mg" % (
66 ... row[1],
67 ... row[0],
68 ... row[2]))
77 ... print(f"{row[1]} of {row[0]} has {row[2]} mg")
6978 venti(20 oz) of Starbucks Coffee Blonde Roast has 475 mg
7079 large(20 oz.) of Dunkin' Donuts Coffee with Turbo Shot has 398 mg
7180 grande(16 oz.) of Starbucks Coffee Pike Place Roast has 310 mg
7281 regular(16 oz.) of Panera Coffee Light Roast has 300 mg
7382
74 Again, do not forgot the second line::
83 Again, do not forgot the second line:
84
85 .. code-block:: python
7586
7687 >>> p.free_resources()
77
88
7889 where `start_row` skips the header row.
7990
8091 Data export in one liners
8394 Export an array
8495 **********************
8596
86 Suppose you have the following array::
97 Suppose you have the following array:
98
99 .. code-block:: python
87100
88101 >>> data = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
89102
90 And here is the code to save it as an excel file ::
103 And here is the code to save it as an excel file :
104
105 .. code-block:: python
91106
92107 >>> p.isave_as(array=data, dest_file_name="example.xls")
93108
94109 But the following line is not required because the data source
95 are not file sources::
110 are not file sources:
111
112 .. code-block:: python
96113
97114 >>> # p.free_resources()
98115
99 Let's verify it::
116 Let's verify it:
117
118 .. code-block:: python
100119
101120 >>> p.get_sheet(file_name="example.xls")
102121 pyexcel_sheet1:
108127 | 7 | 8 | 9 |
109128 +---+---+---+
110129
130
111131 .. testcode::
112132 :hide:
113133
115135 >>> os.unlink("example.xls")
116136
117137
118 And here is the code to save it as a csv file ::
138 And here is the code to save it as a csv file :
139
140 .. code-block:: python
119141
120142 >>> p.isave_as(array=data,
121143 ... dest_file_name="example.csv",
122144 ... dest_delimiter=':')
123145
124 Let's verify it::
146 Let's verify it:
147
148 .. code-block:: python
125149
126150 >>> with open("example.csv") as f:
127151 ... for line in f.readlines():
134158 Export a list of dictionaries
135159 **********************************
136160
137 ::
161 .. code-block:: python
138162
139163 >>> records = [
140164 ... {"year": 1903, "country": "Germany", "speed": "206.7km/h"},
146170 Export a dictionary of single key value pair
147171 ********************************************************************************
148172
149 ::
173 .. code-block:: python
150174
151175 >>> henley_on_thames_facts = {
152176 ... "area": "5.58 square meters",
160184 Export a dictionary of single dimensonal array
161185 ********************************************************************************
162186
187 .. code-block:: python
188
163189 >>> ccs_insights = {
164190 ... "year": ["2017", "2018", "2019", "2020", "2021"],
165191 ... "smart phones": [1.53, 1.64, 1.74, 1.82, 1.90],
171197 Export a dictionary of two dimensional array as a book
172198 ********************************************************************************
173199
174 Suppose you want to save the below dictionary to an excel file ::
175
200 Suppose you want to save the below dictionary to an excel file :
201
202 .. code-block:: python
203
176204 >>> a_dictionary_of_two_dimensional_arrays = {
177205 ... 'Sheet 1':
178206 ... [
194222 ... ]
195223 ... }
196224
197 Here is the code::
225 Here is the code:
226
227 .. code-block:: python
198228
199229 >>> p.isave_book_as(
200230 ... bookdict=a_dictionary_of_two_dimensional_arrays,
202232 ... )
203233
204234 If you want to preserve the order of sheets in your dictionary, you have to
205 pass on an ordered dictionary to the function itself. For example::
235 pass on an ordered dictionary to the function itself. For example:
236
237 .. code-block:: python
206238
207239 >>> from pyexcel._compact import OrderedDict
208240 >>> data = OrderedDict()
212244 >>> p.isave_book_as(bookdict=data, dest_file_name="book.xls")
213245 >>> p.free_resources()
214246
215 Let's verify its order::
247 Let's verify its order:
248
249 .. code-block:: python
216250
217251 >>> import json
218252 >>> book_dict = p.get_book_dict(file_name="book.xls")
246280 ... ]
247281 >>> p.isave_as(array=data, dest_file_name="birth.xls")
248282
249 The following code does a simple file format transcoding from xls to csv::
283
284 The following code does a simple file format transcoding from xls to csv:
285
286 .. code-block:: python
250287
251288 >>> import pyexcel
252289 >>> p.save_as(file_name="birth.xls", dest_file_name="birth.csv")
253290
254291 Again it is really simple. Let's verify what we have gotten:
292
293 .. code-block:: python
255294
256295 >>> sheet = p.get_sheet(file_name="birth.csv")
257296 >>> sheet
264303 | Smith | 4.2 | 12/11/14 |
265304 +-------+--------+----------+
266305
267 .. NOTE::
306 .. note::
268307
269308 Please note that csv(comma separate value) file is pure text file. Formula, charts, images and formatting in xls file will disappear no matter which transcoding tool you use. Hence, pyexcel is a quick alternative for this transcoding job.
270309
271310
272311 Let use previous example and save it as xlsx instead
312
313 .. code-block:: python
273314
274315 >>> import pyexcel
275316 >>> p.isave_as(file_name="birth.xls",
277318
278319 Again let's verify what we have gotten:
279320
321 .. code-block:: python
322
280323 >>> sheet = p.get_sheet(file_name="birth.xlsx")
281324 >>> sheet
282325 pyexcel_sheet1:
287330 +-------+--------+----------+
288331 | Smith | 4.2 | 12/11/14 |
289332 +-------+--------+----------+
333
334 .. testcode::
335 :hide:
336
337 >>> import os
338 >>> os.unlink('ccs.csv')
339 >>> os.unlink('book.xls')
2929 [pyexcel-xls]
3030 [pyexcel-xlsx]
3131 [pyexcel-xlsxw]
32 [pyexcel-libxlsxw]
33 [pyexcel-xlsxr]
3234 }
3335
3436 [tabulate] <<dependency>>
4042 [xlsxwriter] <<dependency>>
4143 [ezodf] <<dependency>>
4244 [odfpy] <<dependency>>
45 [lxml] <<dependency>>
46 [libxlsxwpy] <<dependency>>
47 [libxlsx] <<dependency>>
4348
4449 [pyexcel public api] -right-> [pyexcel internal api]
4550 [pyexcel internal api] -right-> [pyexcel plugin interfaces]
5762 [pyexcel-io plugin interfaces] ..> [csv, csvz, tsv, tsvz]
5863 [pyexcel-io plugin interfaces] ..> [django, sqlalchemy]
5964 [pyexcel-io plugin interfaces] .up.> [pyexcel-xls]
65 [pyexcel-io plugin interfaces] .up.> [pyexcel-xlsxw]
66 [pyexcel-io plugin interfaces] .up.> [pyexcel-libxlsxw]
6067 [pyexcel-io plugin interfaces] .up.> [pyexcel-xlsx]
68 [pyexcel-io plugin interfaces] .up.> [pyexcel-xlsxr]
6169 [pyexcel-io plugin interfaces] .left.> [pyexcel-ods]
6270 [pyexcel-io plugin interfaces] .right.> [pyexcel-ods3]
63 [pyexcel-io plugin interfaces] .right.> [pyexcel-xlsxw]
6471 [pyexcel-io plugin interfaces] .right.> [pyexcel-odsr]
72
6573
6674 [pyexcel-text] -up- [tabulate]
6775 [pyexcel-pygal] -up- [pygal]
6977 [pyexcel-xls] -right- [xlrd]
7078 [pyexcel-xls] -right- [xlwt]
7179 [pyexcel-xlsx] -right- [openpyxl]
72 [pyexcel-xlsxw] -down- [xlsxwriter]
80 [pyexcel-xlsxw] -up- [xlsxwriter]
7381 [pyexcel-ods3] --- [ezodf]
7482 [pyexcel-ods] --- [odfpy]
83 [pyexcel-odsr] -up- [lxml]
84 [pyexcel-xlsxr] -right- [lxml]
85 [sphinxcontrib-excel] -right-> [pyexcel-handsontable]
86 [pyexcel-libxlsxw] -up- [libxlsxwpy]
87 [libxlsxwpy] -right- [libxlsx]
7588
7689 skinparam component{
7790
0 isort $(find pyexcel -name "*.py"|xargs echo) $(find tests -name "*.py"|xargs echo)
1 black -l 79 pyexcel
2 black -l 79 tests
0 flake8 . --exclude=.moban.d,docs --builtins=unicode,xrange,long⏎
0 pip install flake8
1 flake8 --exclude=.moban.d,docs,setup.py --builtins=unicode,xrange,long . && python setup.py checkdocs⏎
00 lml==0.0.4
1 pyexcel-io==0.5.18
2 texttable==0.8.3;python_version<"3"
3 texttable==0.8.3;python_version>="3"
4 ordereddict;python_version<"2.7"
5 weakrefset;python_version<"2.7"
6 lxml==3.4.4;platform_python_implementation=="PyPy"
1 pyexcel-io==0.6.2
2 texttable==0.8.3
73 pyexcel-xlsx==0.4.1
84 pyexcel-xls==0.4.1
0 __version__ = "0.5.14"
1 __author__ = "C.W."
0 __version__ = '0.6.6'
1 __author__ = 'chfw'
1616 # pylint: disable=ungrouped-imports
1717 import sys
1818 import warnings
19 from io import BytesIO, StringIO
20 from urllib import request as request
1921 from textwrap import dedent
22 from itertools import zip_longest
23 from collections import OrderedDict
2024
2125 PY2 = sys.version_info[0] == 2
2226 PY26 = PY2 and sys.version_info[1] < 7
2327 PY3_AND_ABOVE = sys.version_info[0] >= 3
2428
25 if PY26:
26 from ordereddict import OrderedDict
27 else:
28 from collections import OrderedDict
2929
30 if PY2:
31 from StringIO import StringIO
32 from StringIO import StringIO as BytesIO
33 from itertools import izip_longest as zip_longest
34 from itertools import izip as czip
35 import urllib2 as request
36
37 class Iterator(object):
38 """Python 2 iterator"""
39
40 def next(self):
41 """Iterator interface get next value"""
42 return type(self).__next__(self)
43
44 irange = xrange
45 else:
46 from io import StringIO, BytesIO
47 import urllib.request as request
48 from itertools import zip_longest
49
50 Iterator = object
51 irange = range
52 czip = zip
30 Iterator = object
31 irange = range
32 czip = zip
5333
5434
5535 def is_tuple_consists_of_strings(an_array):
66 :copyright: (c) 2014-2019 by Onni Software Ltd.
77 :license: New BSD License, see LICENSE for more details
88 """
9 import pyexcel._compact as compact
9 from pyexcel import _compact as compact
1010 from pyexcel.sheet import Sheet
1111 from pyexcel.internal.meta import BookMeta
1212 from pyexcel.internal.common import SheetIterator
3939 self.init(sheets=sheets, filename=filename, path=path)
4040
4141 def init(self, sheets=None, filename="memory", path=None):
42 """indpendent function so that it could be called multiple times
43 """
42 """indpendent function so that it could be called multiple times"""
4443 self.__path = path
4544 self.filename = filename
4645 self.load_from_sheets(sheets)
5554 if sheets is None:
5655 return
5756 keys = sheets.keys()
58 if not isinstance(sheets, compact.OrderedDict):
59 # if the end user does not care about the order
60 # we put alphatical order
61 keys = sorted(keys)
6257 for name in keys:
6358 value = sheets[name]
6459 if isinstance(value, Sheet):
221216
222217
223218 def to_book(bookstream):
224 """Convert a bookstream to Book
225 """
219 """Convert a bookstream to Book"""
226220 if isinstance(bookstream, Book):
227221 return bookstream
228222 else:
33
44 Constants appeared in pyexcel
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
99 # flake8: noqa
33
44 A list of pyexcel signature functions
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
99 import re
1010
11 import pyexcel.constants as constants
12 import pyexcel.docstrings as docs
13 import pyexcel.internal.core as sources
11 from pyexcel import constants as constants
12 from pyexcel import docstrings as docs
1413 from pyexcel.book import Book, to_book
1514 from pyexcel.sheet import Sheet
1615 from pyexcel._compact import OrderedDict, append_doc, zip_longest
17
18 import pyexcel_io.manager as manager
16 from pyexcel.internal import core as sources
17
18 from pyexcel_io import manager as manager
1919
2020 STARTS_WITH_DEST = "^dest_(.*)"
2121 SAVE_AS_EXCEPTION = (
33
44 List of apis that become deprecated but was kept for backward compatibility
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
99 import warnings
197197 message="Deprecated since v0.0.7! Please use class Book instead",
198198 )
199199 def BookReader(file_name, **keywords):
200 """For backward compatibility
201 """
200 """For backward compatibility"""
202201 return load_book(file_name, **keywords)
203202
204203
33
44 Reusible docstrings
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
99 # flake8: noqa
33
44 Reusible docstrings for pyexcel.core
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
99 from . import keywords
33
44 Reusible docstrings for keywords in signature functions
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
99 CSV_PARAMS = """
33
44 Reusible docstrings for pyexcel.internal.meta
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
99 from .keywords import CSV_PARAMS
33
44 Exceptions appeared in pyexcel
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
99
33
44 Deprecated module import
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
99 from ..deprecated import deprecated_pyexcel_ext
33
44 Deprecated module import
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
99 from ..deprecated import deprecated_pyexcel_ext
33
44 Deprecated module import
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
99 from ..deprecated import deprecated_pyexcel_ext
33
44 Deprecated module import
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
99 from ..deprecated import deprecated_pyexcel_ext
33
44 Deprecated module import
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
99 from ..deprecated import deprecated_pyexcel_ext
33
44 Pyexcel internals that subjected to change
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
9 from lml.loader import scan_plugins_regex
910 from pyexcel.internal.plugins import PARSER, RENDERER # noqa
1011 from pyexcel.internal.generators import BookStream, SheetStream # noqa
1112 from pyexcel.internal.source_plugin import SOURCE # noqa
12
13 from lml.loader import scan_plugins_regex
1413
1514 BLACK_LIST = [
1615 "pyexcel_io",
33
44 Book and sheet attributes
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
9 import pyexcel.constants as constants
9 from pyexcel import constants as constants
1010
1111 ATTRIBUTE_REGISTRY = {
1212 constants.SHEET: {
33
44 Defintion for the shared objects
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
99 NO_COLUMN_NAMES = "Only sheet with column names is accepted"
33
44 elementary functions to read and write generic excel content
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
99 from pyexcel._compact import PY2
33
44 Simple garbage collector
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
9 import pyexcel.docstrings as docs
9 from pyexcel import docstrings as docs
1010 from pyexcel._compact import append_doc
1111
1212 GARBAGE = []
33
44 Defintion for the sheet and book generators.
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
99 from pyexcel._compact import OrderedDict
33
44 Annotate sheet and book class' attributes
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
99 import sys
1010 from functools import partial
1111
12 import pyexcel.constants as constants
13 import pyexcel.docstrings as docs
12 from pyexcel import constants as constants
13 from pyexcel import docstrings as docs
1414 from pyexcel._compact import PY2, append_doc
1515 from pyexcel.internal import SOURCE
1616 from pyexcel.internal.core import save_book, save_sheet, get_sheet_stream
1818
1919
2020 def make_presenter(source_getter, attribute=None):
21 """make a custom presentation method for each file types
22 """
21 """make a custom presentation method for each file types"""
2322
2423 def custom_presenter(self, **keywords):
2524 """docstring is assigned a few lines down the line"""
4140
4241
4342 def sheet_presenter(attribute=None):
44 """make a custom presentation method for sheet
45 """
43 """make a custom presentation method for sheet"""
4644 source_getter = SOURCE.get_writable_source
4745 return make_presenter(source_getter, attribute)
4846
4947
5048 def book_presenter(attribute=None):
51 """make a custom presentation method for book
52 """
49 """make a custom presentation method for book"""
5350 source_getter = SOURCE.get_writable_book_source
5451 return make_presenter(source_getter, attribute)
5552
5653
5754 def importer(attribute=None):
58 """make a custom input method for sheet
59 """
55 """make a custom input method for sheet"""
6056
6157 def custom_importer1(self, content, **keywords):
6258 """docstring is assigned a few lines down the line"""
7874
7975
8076 def book_importer(attribute=None):
81 """make a custom input method for book
82 """
77 """make a custom input method for book"""
8378
8479 def custom_book_importer(self, content, **keywords):
8580 """docstring is assigned a few lines down the line"""
260255
261256 @append_doc(docs.SAVE_AS_OPTIONS)
262257 def save_as(self, filename, **keywords):
263 """Save the content to a named file
264 """
258 """Save the content to a named file"""
265259 return save_sheet(self, file_name=filename, **keywords)
266260
267261 def save_to_memory(self, file_type, stream=None, **keywords):
33
44 Renderer and parser plugin manager
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
99 from lml.plugin import PluginManager
1010
1111
1212 class IOPluginManager(PluginManager):
13 """Generic plugin manager for renderer and parser
14 """
13 """Generic plugin manager for renderer and parser"""
1514
1615 def __init__(self, name):
1716 PluginManager.__init__(self, name)
1817
1918 def get_a_plugin(self, key, library=None):
20 """get a plugin to handle the file type
21 """
19 """get a plugin to handle the file type"""
2220 __file_type = None
2321 if key:
2422 __file_type = key.lower()
2725 return plugin_cls(__file_type)
2826
2927 def get_all_file_types(self):
30 """get all supported file types
31 """
28 """get all supported file types"""
3229 file_types = list(self.registry.keys())
3330 return file_types
3431
33
44 Locally shared utility functions
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
99 import re
2727 raise NotImplementedError("Not implemented")
2828
2929 def __add__(self, other):
30 """Overload += sign
30 """Overload + sign
3131
3232 :return: self
3333 """
4646
4747
4848 def analyse_slice(aslice, upper_bound):
49 """An internal function to analyze a given slice
50 """
49 """An internal function to analyze a given slice"""
5150 if aslice.start is None:
5251 start = 0
5352 else:
33
44 Generic table column
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
9 import copy
910 import types
1011
11 import pyexcel._compact as compact
12 from pyexcel import _compact as compact
1213
1314 from . import _shared as utils
1415
249250 :return: self
250251 """
251252 if isinstance(other, compact.OrderedDict):
252 self._ref.extend_columns(other)
253 self._ref.extend_columns(copy.deepcopy(other))
253254 elif isinstance(other, list):
254 self._ref.extend_columns(other)
255 self._ref.extend_columns(copy.deepcopy(other))
255256 elif hasattr(other, "get_internal_array"):
256 self._ref.extend_columns_with_rows(other.get_internal_array())
257 self._ref.extend_columns_with_rows(
258 copy.deepcopy(other.get_internal_array())
259 )
257260 else:
258261 raise TypeError
259262
260263 return self
261264
262265 def __add__(self, other):
263 """Overload += sign
264
265 :return: self
266 """
267 self.__iadd__(other)
268 return self._ref
266 """Overload + sign
267 :return: new instance
268 """
269 new_instance = self._ref.clone()
270 if isinstance(other, compact.OrderedDict):
271 new_instance.extend_columns(copy.deepcopy(other))
272 elif isinstance(other, list):
273 new_instance.extend_columns(copy.deepcopy(other))
274 elif hasattr(other, "get_internal_array"):
275 new_instance.extend_columns_with_rows(
276 copy.deepcopy(other.get_internal_array())
277 )
278 else:
279 raise TypeError
280
281 return new_instance
269282
270283 def __getattr__(self, attr):
271284 """
280293 return self._ref.named_column_at(the_attr)
281294
282295 def format(self, column_index=None, formatter=None, format_specs=None):
283 """Format a column
284 """
296 """Format a column"""
285297 if column_index is not None:
286298 self._handle_one_formatter(column_index, formatter)
287299 elif format_specs:
1010 import datetime
1111 from decimal import Decimal
1212
13 import pyexcel.constants as constants
13 from pyexcel import constants as constants
1414 from pyexcel._compact import PY2
1515
1616
1212 from functools import partial
1313 from itertools import chain
1414
15 import pyexcel._compact as compact
16 import pyexcel.constants as constants
15 from pyexcel import _compact as compact
16 from pyexcel import constants as constants
1717 from pyexcel.internal.meta import SheetMeta
1818 from pyexcel.internal.sheets.row import Row
1919 from pyexcel.internal.sheets.column import Column
8181 :param int column: column index which starts from 0
8282 :param any new_value: new value if this is to set the value
8383 """
84 if row < self.number_of_rows() and column < self.number_of_columns():
85 if new_value is None:
86 # get
84 fit = row < self.number_of_rows() and column < self.number_of_columns()
85 if new_value is None:
86 if fit:
8787 return self.__array[row][column]
8888 else:
89 # set
90 self.__array[row][column] = new_value
91 else:
92 if new_value is None:
9389 raise IndexError("Index out of range")
94 else:
95 self.paste((row, column), [[new_value]])
90 else:
91 if not fit:
92 width, array = uniform(self.__array, row+1, column+1)
93 self.__width, self.__array = width, array
94
95 self.__array[row][column] = new_value
9696
9797 def row_at(self, index):
9898 """
110110 raise IndexError(constants.MESSAGE_INDEX_OUT_OF_RANGE)
111111
112112 def set_row_at(self, row_index, data_array):
113 """Update a row data range
114 """
113 """Update a row data range"""
115114 nrows = self.number_of_rows()
116115 if row_index < nrows:
117116 self.__array[row_index] = data_array
448447 self.__width, self.__array = uniform(self.__array)
449448
450449 def delete_columns(self, column_indices):
451 """Delete columns by specified list of indices
452 """
450 """Delete columns by specified list of indices"""
453451 if isinstance(column_indices, list) is False:
454452 raise TypeError(constants.MESSAGE_DATA_ERROR_DATA_TYPE_MISMATCH)
455453 if len(column_indices) > 0:
501499 self.__width, self.__array = uniform(self.__array)
502500
503501 def to_array(self):
504 """Get an array out
505 """
502 """Get an array out"""
506503 return self.__array
507504
508505 def __iter__(self):
761758 value = custom_function(value)
762759 self.cell_value(row, column, value)
763760
761 def __iadd__(self, other):
762 return _add(self.name, self.__array, other)
763
764764 def __add__(self, other):
765765 """Overload the + sign
766766
767767 :returns: a new book
768768 """
769 from pyexcel.book import Book, local_uuid
770
771 content = {}
772 content[self.name] = self.__array
773 if isinstance(other, Book):
774 other_in_dict = other.to_dict()
775 for key in other_in_dict.keys():
776 new_key = key
777 if len(other_in_dict.keys()) == 1:
778 new_key = other.filename
779 if new_key in content:
780 uid = local_uuid()
781 new_key = "%s_%s" % (key, uid)
782 content[new_key] = other_in_dict[key]
783 elif isinstance(other, Matrix):
784 new_key = other.name
785 if new_key in content:
786 uid = local_uuid()
787 new_key = "%s_%s" % (other.name, uid)
788 content[new_key] = other.get_internal_array()
789 else:
790 raise TypeError
791 new_book = Book()
792 new_book.load_from_sheets(content)
793 return new_book
769 return _add(self.name, copy.deepcopy(self.__array), other)
770
771 def clone(self):
772 return Matrix(copy.deepcopy(self.__array))
794773
795774
796775 def _unique(seq):
817796 return 0
818797
819798
820 def uniform(array):
799 def uniform(array, min_rows=0, min_columns=0):
821800 """Fill-in empty strings to empty cells to make it MxN
822801
823802 :param list in_array: a list of arrays
803 :param int row_no: desired minimum row count
804 :param int column_no: desired minimum column length
824805 """
825 width = longest_row_number(array)
806 width = max(min_columns, longest_row_number(array))
807 array_length = len(array)
808 height = max(array_length, min_rows)
809
826810 if width == 0:
827811 return 0, array
828812 else:
833817 row[index] = constants.DEFAULT_NA
834818 if row_length < width:
835819 row += [constants.DEFAULT_NA] * (width - row_length)
820 for _ in range(array_length, height):
821 row = [constants.DEFAULT_NA] * width
822 array.append(row)
836823 return width, array
837824
838825
860847 row_data.append(constants.DEFAULT_NA)
861848 new_array.append(row_data)
862849 return new_array
850
851
852 def _add(name, left, right):
853 from pyexcel.book import Book, local_uuid
854
855 content = {}
856 content[name] = left
857 if isinstance(right, Book):
858 right_in_dict = copy.deepcopy(right.to_dict())
859 for key in right_in_dict.keys():
860 new_key = key
861 if len(right_in_dict.keys()) == 1:
862 new_key = right.filename
863 if new_key in content:
864 uid = local_uuid()
865 new_key = "%s_%s" % (key, uid)
866 content[new_key] = right_in_dict[key]
867 elif isinstance(right, Matrix):
868 new_key = right.name
869 if new_key in content:
870 uid = local_uuid()
871 new_key = "%s_%s" % (right.name, uid)
872 content[new_key] = copy.deepcopy(right.get_internal_array())
873 else:
874 raise TypeError
875 new_book = Book()
876 new_book.load_from_sheets(content)
877 return new_book
33
44 Generic table row
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
9 import copy
910 import types
1011
11 import pyexcel._compact as compact
12 from pyexcel import _compact as compact
1213
1314 from . import _shared as utils
1415
209210 :return: self
210211 """
211212 if isinstance(other, compact.OrderedDict):
212 self._ref.extend_rows(other)
213 self._ref.extend_rows(copy.deepcopy(other))
213214 elif isinstance(other, list):
214 self._ref.extend_rows(other)
215 self._ref.extend_rows(copy.deepcopy(other))
215216 elif hasattr(other, "get_internal_array"):
216 self._ref.extend_rows(other.get_internal_array())
217 self._ref.extend_rows(copy.deepcopy(other.get_internal_array()))
217218 else:
218219 raise TypeError
219220 return self
220221
222 def __add__(self, other):
223 """Overload + sign
224
225 :return: new instance
226 """
227 new_instance = self._ref.clone()
228 if isinstance(other, compact.OrderedDict):
229 new_instance.extend_rows(copy.deepcopy(other))
230 elif isinstance(other, list):
231 new_instance.extend_rows(copy.deepcopy(other))
232 elif hasattr(other, "get_internal_array"):
233 new_instance.extend_rows(copy.deepcopy(other.get_internal_array()))
234 else:
235 raise TypeError
236 return new_instance
237
221238 def format(self, row_index=None, formatter=None, format_specs=None):
222 """Format a row
223 """
239 """Format a row"""
224240 if row_index is not None:
225241 self._handle_one_formatter(row_index, formatter)
226242 elif format_specs:
33
44 Second level abstraction
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
9 import pyexcel.constants as constants
10 import pyexcel.exceptions as exceptions
9 from pyexcel import constants as constants
10 from pyexcel import exceptions as exceptions
11 from lml.plugin import PluginManager
1112 from pyexcel.internal.attributes import (
1213 register_book_attribute,
1314 register_sheet_attribute,
1415 )
1516
16 import pyexcel_io.constants as io_constants
17 from lml.plugin import PluginManager
17 from pyexcel_io import constants as io_constants
1818
1919 REGISTRY_KEY_FORMAT = "%s-%s"
2020 # ignore the following attributes
5959 def get_a_plugin(
6060 self, target=None, action=None, source_library=None, **keywords
6161 ):
62 """obtain a source plugin for pyexcel signature functions"""
62 """obtain a source plugin for signature functions"""
6363 key = REGISTRY_KEY_FORMAT % (target, action)
6464 io_library = None
6565 # backward support pyexcel-io library parameter
7474 return source_instance
7575
7676 def get_source(self, **keywords):
77 """obtain a sheet read source plugin for pyexcel signature functions"""
77 """obtain a sheet read source plugin for signature functions"""
7878 return self.get_a_plugin(
7979 target=constants.SHEET, action=constants.READ_ACTION, **keywords
8080 )
8181
8282 def get_book_source(self, **keywords):
83 """obtain a book read source plugin for pyexcel signature functions"""
83 """obtain a book read source plugin for signature functions"""
8484 return self.get_a_plugin(
8585 target=constants.BOOK, action=constants.READ_ACTION, **keywords
8686 )
8787
8888 def get_writable_source(self, **keywords):
89 """obtain a sheet write source plugin for pyexcel signature functions
90 """
89 """obtain a sheet write source plugin for signature functions"""
9190 return self.get_a_plugin(
9291 target=constants.SHEET, action=constants.WRITE_ACTION, **keywords
9392 )
9493
9594 def get_writable_book_source(self, **keywords):
96 """obtain a book write source plugin for pyexcel signature functions"""
95 """obtain a book write source plugin for signature functions"""
9796 return self.get_a_plugin(
9897 target=constants.BOOK, action=constants.WRITE_ACTION, **keywords
9998 )
33
44 Extract tabular data from external file, stream or content
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
9 import pyexcel.internal.garbagecollector as gc
9 from pyexcel.internal import garbagecollector as gc
1010
1111
1212 class AbstractParser(object):
33
44 Public interface for plugins
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
99 import types
1010 from itertools import product
1111
12 import pyexcel.constants as constants
12 from pyexcel import constants as constants
13 from lml.plugin import PluginInfo, PluginInfoChain
1314 from pyexcel._compact import is_string
1415 from pyexcel.exceptions import FileTypeNotSupported
1516 from pyexcel.internal.plugins import PARSER, RENDERER
16
17 from lml.plugin import PluginInfo, PluginInfoChain
1817
1918
2019 class SourceInfo(PluginInfo):
132131
133132
134133 class PyexcelPluginChain(PluginInfoChain):
135 """It is used by pyexcel plugins
136 """
134 """It is used by pyexcel plugins"""
137135
138136 def add_a_source(self, relative_plugin_class_path=None, **keywords):
139137 """
33
44 A list of built-in parsers
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
99 from pyexcel.plugins import PyexcelPluginChain
33
44 Export data into database datables
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
99 from pyexcel.parser import DbParser
1010
11 import pyexcel_io.database.common as django
1211 from pyexcel_io import get_data, iget_data
12 from pyexcel_io.database import common as django
1313
1414
1515 class DjangoExporter(DbParser):
33
44 Parsing excel sources
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
99 from pyexcel.parser import AbstractParser
33
44 Export data into database datables
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
99 from pyexcel.parser import DbParser
1010
11 import pyexcel_io.database.common as sql
1211 from pyexcel_io import get_data, iget_data
12 from pyexcel_io.database import common as sql
1313
1414
1515 class SQLAlchemyExporter(DbParser):
33
44 A list of built-in renderers
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
99 from pyexcel.plugins import PyexcelPluginChain
44 Export data into texttable format. It also serves the default
55 presentation of pyexcel sheet and book.
66
7 :copyright: (c) 2015-2017 by Onni Software Ltd.
7 :copyright: (c) 2015-2020 by Onni Software Ltd.
88 :license: New BSD License
99 """
1010 from types import GeneratorType
1111
12 import pyexcel.constants as constants
12 from pyexcel import constants as constants
13 from texttable import Texttable
1314 from pyexcel.renderer import Renderer
1415 from pyexcel.internal.sheets.formatters import to_format
15
16 from texttable import Texttable
1716
1817
1918 class TextTableRenderer(Renderer):
33
44 Export data into django models
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
9 import pyexcel.internal.common as common
109 from pyexcel._compact import OrderedDict
10 from pyexcel.internal import common as common
1111 from pyexcel.renderer import DbRenderer
1212
13 import pyexcel_io.database.common as django
1413 from pyexcel_io import save_data
14 from pyexcel_io.database import common as django
1515
1616 NO_COLUMN_NAMES = "Only sheet with column names is accepted"
1717
33
44 Export data into excel files
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
99 from pyexcel.renderer import AbstractRenderer
1010 from pyexcel.constants import DEFAULT_SHEET_NAME
1111
12 import pyexcel_io.manager as manager
12 from pyexcel_io import manager as manager
1313 from pyexcel_io import save_data
1414
1515
33
44 Export data into database datables
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
9 import pyexcel.internal.common as common
109 from pyexcel._compact import OrderedDict
10 from pyexcel.internal import common as common
1111 from pyexcel.renderer import DbRenderer
1212
13 import pyexcel_io.database.common as sql
1413 from pyexcel_io import save_data
14 from pyexcel_io.database import common as sql
1515
1616
1717 class SQLAlchemyRenderer(DbRenderer):
33
44 A list of built-in sources
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
99 from pyexcel.plugins import PyexcelPluginChain
33
44 Generic database sources
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
99 from pyexcel.source import AbstractSource
33
44 Representation of django sources
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
99 from pyexcel_io.constants import DB_DJANGO
33
44 Representation of input file sources
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
99 import os
1414
1515 # pylint: disable=W0223
1616 class ReadExcelFromFile(AbstractSource):
17 """Pick up 'file_name' field and do single sheet based read and write
18 """
17 """Pick up 'file_name' field and do single sheet based read and write"""
1918
2019 def __init__(self, file_name=None, parser_library=None, **keywords):
2120 self.__file_name = file_name
33
44 Representation of output file sources
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
99 from pyexcel.source import AbstractSource
1313
1414 # pylint: disable=W0223
1515 class WriteSheetToFile(AbstractSource):
16 """Pick up 'file_name' field and do single sheet based read and write
17 """
16 """Pick up 'file_name' field and do single sheet based read and write"""
1817
1918 def __init__(self, file_name=None, renderer_library=None, **keywords):
2019 AbstractSource.__init__(self, **keywords)
3534
3635 # pylint: disable=W0223
3736 class WriteBookToFile(WriteSheetToFile):
38 """Pick up 'file_name' field and do multiple sheet based read and write
39 """
37 """Pick up 'file_name' field and do multiple sheet based read and write"""
4038
4139 def write_data(self, book):
4240 self._renderer.render_book_to_file(
33
44 Representation of http sources
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
9 import pyexcel.constants as constants
9 from pyexcel import constants as constants
1010 from pyexcel.source import AbstractSource
1111 from pyexcel._compact import PY2, request
1212 from pyexcel.internal import PARSER
5454 file_type = _get_file_type_from_url(self.__url)
5555 parser_library = self._keywords.get("parser_library", None)
5656 aparser = PARSER.get_a_plugin(file_type, parser_library)
57 sheets = aparser.parse_file_stream(connection, **self._keywords)
57 content = connection.read()
58 sheets = aparser.parse_file_content(content, **self._keywords)
5859 return sheets
5960
6061 def get_source_info(self):
33
44 Representation of input file sources
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
99 from pyexcel.source import AbstractSource
33
44 Representation of output file sources
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
99 from pyexcel.source import AbstractSource, MemorySourceMixin
33
44 Local magic workds
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
99 # keywords
33
44 Representation of array source
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
99 from pyexcel.source import AbstractSource, MemorySourceMixin
33
44 Representation of book dict source
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
99 from pyexcel.source import AbstractSource, MemorySourceMixin
33
44 Representation of array, dict, records and book dict sources
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
9 import pyexcel.constants as constants
9 from pyexcel import constants as constants
1010 from pyexcel._compact import PY2, OrderedDict, zip_longest
1111
1212 from pyexcel_io.sheet import SheetReader
33
44 Representation of dict sources
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
99 from pyexcel.source import AbstractSource, MemorySourceMixin
33
44 Representation of records source
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
99 from pyexcel.source import AbstractSource, MemorySourceMixin
33
44 Representation of querysets
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
9 import pyexcel.constants as constants
9 from pyexcel import constants as constants
1010 from pyexcel.source import AbstractSource
1111
12 from pyexcel_io.database.querysets import QuerysetsReader
12 from pyexcel_io import get_data
13 from pyexcel_io.constants import DB_QUERYSET
1314 from . import params
1415
1516
6667 )
6768 if self.__skip_row_func is not None:
6869 local_params["skip_row_func"] = self.__skip_row_func
69 reader = QuerysetsReader(
70 self.__query_sets, self.__column_names, **local_params
70
71 data = get_data(
72 self.__query_sets,
73 file_type=DB_QUERYSET,
74 column_names=self.__column_names,
75 **local_params
7176 )
72 data = reader.to_array()
73 return {self.__sheet_name: data}
77 return data
33
44 Representation of array source
55
6 :copyright: (c) 2015-2018 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
99 from pyexcel.source import AbstractSource, MemorySourceMixin
33
44 Representation of sqlalchemy sources
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
99 from pyexcel_io.constants import DB_SQL
33
44 Renders pyexcel.Book and pyexcel.Sheet to any format
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
9 import pyexcel._compact as compact
9 from pyexcel import _compact as compact
1010
1111
1212 class AbstractRenderer(object):
88 """
99 from collections import defaultdict
1010
11 import pyexcel._compact as compact
12 import pyexcel.constants as constants
11 from pyexcel import _compact as compact
12 from pyexcel import constants as constants
13 from pyexcel._compact import OrderedDict
1314 from pyexcel.internal.sheets.row import Row as NamedRow
1415 from pyexcel.internal.sheets.column import Column as NamedColumn
1516 from pyexcel.internal.sheets.matrix import Matrix
8081 """
8182 self.__column_names = []
8283 self.__row_names = []
83 self.__row_index = 0
84 self.__row_index = -1
85 self.__column_index = -1
8486 self.init(
8587 sheet=sheet,
8688 name=name,
160162 if transpose_after:
161163 self.transpose()
162164
165 def clone(self):
166 import copy
167
168 new_sheet = Sheet(
169 copy.deepcopy(self.get_internal_array()),
170 name_columns_by_row=self.__row_index,
171 name_rows_by_column=self.__column_index,
172 )
173 return new_sheet
174
163175 def transpose(self):
164176 self.__column_names, self.__row_names = (
165177 self.__row_names,
183195 The specified column will be deleted from the data
184196 :param column_index: the index of the column that has the row names
185197 """
198 self.__column_index = column_index
186199 self.__row_names = make_names_unique(self.column_at(column_index))
187200 del self.column[column_index]
188201
463476 else:
464477 raise ValueError(constants.MESSAGE_DATA_ERROR_NO_SERIES)
465478
479 def project(self, new_ordered_columns, exclusion=False):
480 """
481 Rearrange the sheet.
482
483 :ivar new_ordered_columns: new columns
484 :ivar exclusion: to exlucde named column or not. defaults to False
485
486 Example::
487
488 >>> sheet = Sheet(
489 ... [["A", "B", "C"], [1, 2, 3], [11, 22, 33], [111, 222, 333]],
490 ... name_columns_by_row=0)
491 >>> sheet.project(["B", "A", "C"])
492 pyexcel sheet:
493 +-----+-----+-----+
494 | B | A | C |
495 +=====+=====+=====+
496 | 2 | 1 | 3 |
497 +-----+-----+-----+
498 | 22 | 11 | 33 |
499 +-----+-----+-----+
500 | 222 | 111 | 333 |
501 +-----+-----+-----+
502 >>> sheet.project(["B", "C"])
503 pyexcel sheet:
504 +-----+-----+
505 | B | C |
506 +=====+=====+
507 | 2 | 3 |
508 +-----+-----+
509 | 22 | 33 |
510 +-----+-----+
511 | 222 | 333 |
512 +-----+-----+
513 >>> sheet.project(["B", "C"], exclusion=True)
514 pyexcel sheet:
515 +-----+
516 | A |
517 +=====+
518 | 1 |
519 +-----+
520 | 11 |
521 +-----+
522 | 111 |
523 +-----+
524
525 """
526 from pyexcel import get_array
527
528 the_dict = self.to_dict()
529 new_dict = OrderedDict()
530 if exclusion:
531 for column in the_dict.keys():
532 if column not in new_ordered_columns:
533 new_dict[column] = the_dict[column]
534 else:
535 for column in new_ordered_columns:
536 new_dict[column] = the_dict[column]
537
538 array = get_array(adict=new_dict)
539 return Sheet(array, name=self.name, name_columns_by_row=0)
540
466541 def to_dict(self, row=False):
467542 """Returns a dictionary"""
468543 the_dict = compact.OrderedDict()
556631 for item in alist:
557632 if not compact.is_string(type(item)):
558633 item = str(item)
634 item = item.strip()
559635 if item in duplicates:
560636 duplicates[item] = duplicates[item] + 1
561637 new_names.append("%s-%d" % (item, duplicates[item]))
33
44 Generic data source definition
55
6 :copyright: (c) 2015-2017 by Onni Software Ltd.
6 :copyright: (c) 2015-2020 by Onni Software Ltd.
77 :license: New BSD License
88 """
9 import pyexcel.constants as constants
9 from pyexcel import constants as constants
1010
1111
1212 class AbstractSource(object):
00 overrides: "pyexcel.yaml"
11 name: "pyexcel"
2 author: chfw
23 nick_name: pyexcel
3 version: 0.5.14
4 current_version: 0.5.14
5 release: 0.5.14
6 copyright_year: 2014-2019
4 version: 0.6.6
5 current_version: 0.6.6
6 release: 0.6.6
7 copyright_year: 2014-2020
78 branch: master
9 is_on_conda: true
10 setup_use_markers: true
11 sphinx_extensions:
12 - sphinx.ext.autosummary
13 - sphinx.ext.autodoc
14 - sphinx.ext.doctest
15 - sphinx.ext.intersphinx
16 - sphinx.ext.viewcode
17 - sphinxcontrib.excel
818 dependencies:
919 - lml>=0.0.4
10 - pyexcel-io>=0.5.18
11 - texttable>=0.8.1;python_version<"3"
12 - texttable>=0.8.2;python_version>="3"
13 - ordereddict;python_version<"2.7"
14 - weakrefset;python_version<"2.7"
15 - lxml>=3.4.4;platform_python_implementation=="PyPy"
20 - pyexcel-io>=0.6.2
21 - texttable>=0.8.2
1622 extra_dependencies:
1723 - xls:
18 - pyexcel-xls>=0.5.0
24 - pyexcel-xls>=0.6.0
1925 - xlsx:
20 - pyexcel-xlsx>=0.5.0
26 - pyexcel-xlsx>=0.6.0
2127 - ods:
22 - pyexcel-ods3>=0.5.0
28 - pyexcel-ods3>=0.6.0
29 test_dependencies:
30 - flask
31 - SQLAlchemy
32 - pyexcel-xlsx>=0.4.1
33 - pyexcel-xls>=0.4.1
34 - pyexcel-text>=0.2.0
35 - psutil
36 - pyexcel-pygal
2337 description: A wrapper library that provides one API to read, manipulate and write data in different excel formats
38 python_requires: ">=3.6"
39 min_python_version: "3.6"
40 skip_readme: true
41 moban_command: false
00 lml>=0.0.4
1 pyexcel-io>=0.5.18
2 texttable>=0.8.1;python_version<"3"
3 texttable>=0.8.2;python_version>="3"
4 ordereddict;python_version<"2.7"
5 weakrefset;python_version<"2.7"
6 lxml>=3.4.4;platform_python_implementation=="PyPy"
1 pyexcel-io>=0.6.2
2 texttable>=0.8.2
0 https://github.com/pyexcel-renderers/pyexcel-pygal/archive/master.zip
1 https://github.com/pyexcel/pyexcel-io/archive/master.zip
00 #!/usr/bin/env python3
1
2 """
3 Template by pypi-mobans
4 """
15
26 import os
37 import sys
4
5 # Template by pypi-mobans
68 import codecs
79 import locale
810 import platform
2830 locale.setlocale(locale.LC_ALL, "en_US.UTF-8")
2931
3032 NAME = "pyexcel"
31 AUTHOR = "C.W."
32 VERSION = "0.5.14"
33 AUTHOR = "chfw"
34 VERSION = "0.6.6"
3335 EMAIL = "[email protected]"
3436 LICENSE = "New BSD"
3537 DESCRIPTION = (
36 "A wrapper library that provides one API to read, manipulate and write"
37 + "data in different excel formats"
38 "A wrapper library that provides one API to read, manipulate and write" +
39 "data in different excel formats"
3840 )
3941 URL = "https://github.com/pyexcel/pyexcel"
40 DOWNLOAD_URL = "%s/archive/0.5.14.tar.gz" % URL
41 FILES = ["README.rst", "CHANGELOG.rst"]
42 KEYWORDS = ["python", "tsv", "tsvz" "csv", "csvz", "xls", "xlsx", "ods"]
42 DOWNLOAD_URL = "%s/archive/0.6.6.tar.gz" % URL
43 FILES = ["README.rst", "CONTRIBUTORS.rst", "CHANGELOG.rst"]
44 KEYWORDS = [
45 "python",
46 'tsv',
47 'tsvz'
48 'csv',
49 'csvz',
50 'xls',
51 'xlsx',
52 'ods'
53 ]
4354
4455 CLASSIFIERS = [
4556 "Topic :: Software Development :: Libraries",
4657 "Programming Language :: Python",
4758 "Intended Audience :: Developers",
48 "Programming Language :: Python :: 2.6",
49 "Programming Language :: Python :: 2.7",
50 "Programming Language :: Python :: 3.3",
51 "Programming Language :: Python :: 3.4",
52 "Programming Language :: Python :: 3.5",
59
60 "Programming Language :: Python :: 3 :: Only",
61
62
63
5364 "Programming Language :: Python :: 3.6",
5465 "Programming Language :: Python :: 3.7",
5566 "Programming Language :: Python :: 3.8",
56 "Development Status :: 3 - Alpha",
57 "Programming Language :: Python :: Implementation :: PyPy",
67
68 'Development Status :: 3 - Alpha',
5869 ]
5970
60 INSTALL_REQUIRES = ["lml>=0.0.4", "pyexcel-io>=0.5.18"]
71 PYTHON_REQUIRES = ">=3.6"
72
73 INSTALL_REQUIRES = [
74 "lml>=0.0.4",
75 "pyexcel-io>=0.6.2",
76 "texttable>=0.8.2",
77 ]
6178 SETUP_COMMANDS = {}
6279
63 if PY2:
64 INSTALL_REQUIRES.append("texttable>=0.8.1")
65 if not PY2:
66 INSTALL_REQUIRES.append("texttable>=0.8.2")
67 if PY26:
68 INSTALL_REQUIRES.append("ordereddict")
69 if PY26:
70 INSTALL_REQUIRES.append("weakrefset")
71 if platform.python_implementation == "PyPy":
72 INSTALL_REQUIRES.append("lxml>=3.4.4")
73
74 PACKAGES = find_packages(exclude=["ez_setup", "examples", "tests"])
80 PACKAGES = find_packages(exclude=["ez_setup", "examples", "tests", "tests.*"])
7581 EXTRAS_REQUIRE = {
76 "xls": ["pyexcel-xls>=0.5.0"],
77 "xlsx": ["pyexcel-xlsx>=0.5.0"],
78 "ods": ["pyexcel-ods3>=0.5.0"],
82 "xls": ['pyexcel-xls>=0.6.0'],
83 "xlsx": ['pyexcel-xlsx>=0.6.0'],
84 "ods": ['pyexcel-ods3>=0.6.0'],
7985 }
8086 # You do not need to read beyond this line
81 PUBLISH_COMMAND = "{0} setup.py sdist bdist_wheel upload -r pypi".format(
82 sys.executable
83 )
84 GS_COMMAND = (
85 "gs pyexcel v0.5.14 " + "Find 0.5.14 in changelog for more details"
86 )
87 NO_GS_MESSAGE = (
88 "Automatic github release is disabled. "
89 + "Please install gease to enable it."
90 )
87 PUBLISH_COMMAND = "{0} setup.py sdist bdist_wheel upload -r pypi".format(sys.executable)
88 HERE = os.path.abspath(os.path.dirname(__file__))
89
90 GS_COMMAND = ("gease pyexcel v0.6.6 " +
91 "Find 0.6.6 in changelog for more details")
92 NO_GS_MESSAGE = ("Automatic github release is disabled. " +
93 "Please install gease to enable it.")
9194 UPLOAD_FAILED_MSG = (
92 'Upload failed. please run "%s" yourself.' % PUBLISH_COMMAND
93 )
94 HERE = os.path.abspath(os.path.dirname(__file__))
95 'Upload failed. please run "%s" yourself.' % PUBLISH_COMMAND)
9596
9697
9798 class PublishCommand(Command):
128129 self.status(NO_GS_MESSAGE)
129130 if run_status:
130131 if os.system(PUBLISH_COMMAND) != 0:
131 self.status(UPLOAD_FAILED_MSG % PUBLISH_COMMAND)
132 self.status(UPLOAD_FAILED_MSG)
132133
133134 sys.exit()
134135
135136
136 SETUP_COMMANDS.update({"publish": PublishCommand})
137
137 SETUP_COMMANDS.update({
138 "publish": PublishCommand
139 })
138140
139141 def has_gease():
140142 """
144146 """
145147 try:
146148 import gease # noqa
147
148149 return True
149150 except ImportError:
150151 return False
205206 long_description=read_files(*FILES),
206207 license=LICENSE,
207208 keywords=KEYWORDS,
209 python_requires=PYTHON_REQUIRES,
208210 extras_require=EXTRAS_REQUIRE,
209211 tests_require=["nose"],
210212 install_requires=INSTALL_REQUIRES,
212214 include_package_data=True,
213215 zip_safe=False,
214216 classifiers=CLASSIFIERS,
215 cmdclass=SETUP_COMMANDS,
217 cmdclass=SETUP_COMMANDS
216218 )
00 pip freeze
1 nosetests --with-coverage --cover-package pyexcel --cover-package tests tests --with-doctest --doctest-extension=.rst README.rst docs/source pyexcel
1 nosetests --with-coverage --cover-package pyexcel --cover-package tests tests --with-doctest --doctest-extension=.rst docs/source pyexcel
00 #/bin/bash
11 pip freeze
2 nosetests --with-coverage --cover-package pyexcel --cover-package tests tests --with-doctest --doctest-extension=.rst README.rst docs/source pyexcel
2 nosetests --with-coverage --cover-package pyexcel --cover-package tests tests --with-doctest --doctest-extension=.rst docs/source pyexcel
22
33 import pyexcel as pe
44
5 import unittest
56 from nose.tools import eq_, raises
67
78
6061 table.append(array)
6162 io = pe.save_as(dest_file_type=file_type, array=table)
6263 return io
64
65
66 class PyexcelSheetBase(unittest.TestCase):
67 filename = "fixtures/non-uniform-rows.csv"
68
69 def setUp(self):
70 self.sheet = pe.Sheet([
71 [f"{row_no}_{col_no}" for col_no in range(10)]
72 for row_no in range(10)
73 ])
6374
6475
6576 class PyexcelBase:
22 codecov
33 coverage
44 flake8
5 black
6 isort
7 collective.checkdocs
8 pygments
9 moban
10 moban_jinja2_github
11 flask
512 SQLAlchemy
6 flask
7 lxml==4.0.0;platform_python_implementation=="PyPy"
813 pyexcel-xlsx>=0.4.1
914 pyexcel-xls>=0.4.1
1015 pyexcel-text>=0.2.0
1116 psutil
12 mock
13 moban
14 black;python_version>="3.6"
15 isort;python_version>="3.6"
17 pyexcel-pygal
1818
1919 book = p.Book(book_dict)
2020 eq_(len(book.test.array), 1)
21
22
23 def test_sheet_ordering():
24 test_data = [["a", "b"]]
25
26 book_dict = {"first": test_data, "middle": test_data, "last": test_data}
27
28 book = p.Book(book_dict)
29 eq_(book.sheet_names(), ["first", "middle", "last"])
44 from datetime import datetime
55 from textwrap import dedent
66
7 import psutil
78 import pyexcel as p
89 from _compact import StringIO, OrderedDict
910
10 import psutil
1111 from nose.tools import eq_
1212
1313
00 import warnings
1 from unittest.mock import patch
12
23 import pyexcel.ext.ods
34 import pyexcel.ext.xls
45 import pyexcel.ext.ods3
56 import pyexcel.ext.text
67 import pyexcel.ext.xlsx
7
8 from mock import patch
98
109 try:
1110 reload
00 import os
11 from textwrap import dedent
22
3 import pyexcel.constants as constants
43 from pyexcel import Book, Sheet, get_book
4 from pyexcel import constants as constants
55 from _compact import StringIO, OrderedDict
66 from pyexcel.source import AbstractSource, MemorySourceMixin
77 from pyexcel.plugins import SourceInfo
221221 save_as(dest_file_name=self.testfile, adict=self.data)
222222
223223 def test_general_usage(self):
224 """format a row
225 """
224 """format a row"""
226225 r = get_sheet(file_name=self.testfile)
227226 r.row.format(1, str)
228227 c1 = r.row_at(1)
230229 self.assertEqual(c1, c2)
231230
232231 def test_general_usage2(self):
233 """format a row
234 """
232 """format a row"""
235233 r = get_sheet(file_name=self.testfile)
236234 r.row.format(1, str)
237235 c1 = r.row_at(1)
239237 self.assertEqual(c1, c2)
240238
241239 def test_one_formatter_for_two_rows(self):
242 """format more than one row
243 """
240 """format more than one row"""
244241 r = get_sheet(file_name=self.testfile)
245242 r.row.format([1, 2], str)
246243 c1 = r.row_at(2)
00 import os
11
2 import pyexcel.internal.garbagecollector as gc
32 from pyexcel import iget_array
3 from pyexcel.internal import garbagecollector as gc
44
55 from nose.tools import eq_
66
00 from textwrap import dedent
11 from unittest import TestCase
2 from unittest.mock import MagicMock, patch
23
34 import pyexcel as pe
4 from pyexcel._compact import PY2, StringIO
5
6 from mock import MagicMock, patch
5 from pyexcel._compact import PY2, BytesIO, StringIO
76
87
98 class TestHttpBookSource(TestCase):
1110 self.patcher = patch("pyexcel._compact.request.urlopen")
1211 mock_open = self.patcher.start()
1312 self.mocked_info = MagicMock()
14 io = StringIO("1,2,3")
13 if PY2:
14 io = StringIO("1,2,3")
15 else:
16 io = BytesIO("1,2,3".encode("utf-8"))
1517 io.info = self.mocked_info
1618 mock_open.return_value = io
1719
0 from unittest.mock import MagicMock
1
02 from pyexcel._compact import PY2
13 from pyexcel.internal.core import _seek_at_zero
2
3 from mock import MagicMock
44
55
66 def test_seek_at_zero():
8181 m.extend_columns(1.1)
8282
8383 def test_iadd_list(self):
84 """Test in place add a list
85 """
84 """Test in place add a list"""
8685 m2 = Matrix(self.data)
8786 m2.column += self.data3
8887 eq_(self.result, m2.get_internal_array())
8988
9089 def test_add(self):
91 """Test operator add overload
92 """
90 """Test operator add overload"""
9391 # +
9492 m3 = Matrix(self.data)
9593 m4 = m3.column + self.data3
33 from base import PyexcelMultipleSheetBase, clean_up_files, create_sample_file1
44 from _compact import OrderedDict
55
6 from nose.tools import raises
6 from nose.tools import eq_, raises
77
88
99 class TestXlsNXlsmMultipleSheets(PyexcelMultipleSheetBase):
445445 """
446446 b1 = pe.BookReader(self.testfile)
447447 b3 = b1["Sheet1"] + b1["Sheet1"]
448 content = b3.dict
449 sheet_names = content.keys()
450 assert len(sheet_names) == 2
451 for name in sheet_names:
452 if "Sheet1" in name:
453 assert content[name] == self.content["Sheet1"]
448 b3["Sheet1"].row[0] = [3, 3, 3, 3]
449 eq_(b1["Sheet1"].row[0], [1, 1, 1, 1])
454450
455451 @raises(TypeError)
456452 def test_add_book_error(self):
0 from textwrap import dedent
1
2 from pyexcel import Sheet
3
4 from nose.tools import eq_
5
6
7 def test_project():
8 sheet = Sheet(
9 [["A", "B", "C"], [1, 2, 3], [11, 22, 33], [111, 222, 333]],
10 name_columns_by_row=0,
11 )
12 expected = dedent(
13 """
14 pyexcel sheet:
15 +-----+-----+-----+
16 | B | A | C |
17 +=====+=====+=====+
18 | 2 | 1 | 3 |
19 +-----+-----+-----+
20 | 22 | 11 | 33 |
21 +-----+-----+-----+
22 | 222 | 111 | 333 |
23 +-----+-----+-----+"""
24 ).strip()
25 sheet = sheet.project(["B", "A", "C"])
26 eq_(expected, str(sheet))
27
28
29 def test_project_for_less():
30 sheet = Sheet(
31 [["A", "B", "C"], [1, 2, 3], [11, 22, 33], [111, 222, 333]],
32 name_columns_by_row=0,
33 )
34 expected = dedent(
35 """
36 pyexcel sheet:
37 +-----+-----+
38 | B | C |
39 +=====+=====+
40 | 2 | 3 |
41 +-----+-----+
42 | 22 | 33 |
43 +-----+-----+
44 | 222 | 333 |
45 +-----+-----+"""
46 ).strip()
47 sheet = sheet.project(["B", "C"])
48 eq_(expected, str(sheet))
49
50
51 def test_project_for_exclusion():
52 sheet = Sheet(
53 [["A", "B", "C"], [1, 2, 3], [11, 22, 33], [111, 222, 333]],
54 name_columns_by_row=0,
55 )
56 expected = dedent(
57 """
58 pyexcel sheet:
59 +-----+
60 | A |
61 +=====+
62 | 1 |
63 +-----+
64 | 11 |
65 +-----+
66 | 111 |
67 +-----+"""
68 ).strip()
69 sheet = sheet.project(["B", "C"], exclusion=True)
70 eq_(expected, str(sheet))
0 import random
1
2 from base import PyexcelSheetBase
3
4
5 class TestSheetAccess(PyexcelSheetBase):
6 @staticmethod
7 def get_random_char():
8 i = random.randint(97, 122)
9 return chr(i)
10
11 def test_out_of_bounds_write(self):
12 value = self.get_random_char()
13 column = self.get_random_char() + self.get_random_char()
14 cell = column + str(random.randint(9, 30))
15 self.sheet[cell] = value
16
17 self.assertEqual(value, self.sheet[cell])
18
19 def test_out_of_column_bound_write(self):
20 value = self.get_random_char()
21 column = self.get_random_char() + self.get_random_char()
22 cell = 'A' + column + str(random.randint(9, 30))
23 self.sheet[cell] = value
24
25 self.assertEqual(value, self.sheet[cell])
26
27 def test_out_of_bounds_read(self):
28 with self.assertRaises(IndexError):
29 self.sheet[0, 20]
30
31 with self.assertRaises(IndexError):
32 self.sheet[20, 0]
33
34 def test_column_edge_case(self):
35 column = self.sheet.number_of_columns()-1
36
37 self.assertEqual(self.sheet[0, column], f"0_{column}")
38 with self.assertRaises(IndexError):
39 self.sheet[0, column+1]
40
41 def test_row_edge_case(self):
42 row = self.sheet.number_of_rows()-1
43
44 self.assertEqual(self.sheet[row, 0], f"{row}_0")
45 with self.assertRaises(IndexError):
46 self.sheet[row+1, 0]
11 import copy
22 from textwrap import dedent
33
4 from pyexcel.sheet import Sheet
4 from pyexcel.sheet import Sheet, make_names_unique
55 from pyexcel.internal.meta import PyexcelObject
66
77 from nose.tools import eq_, raises
129129 def test_pyexcel_object():
130130 obj = PyexcelObject()
131131 obj.save_to_memory("csv")
132
133
134 def test_make_names_unique():
135 alist = ["a", "a"]
136 new_names = make_names_unique(alist)
137 expected = ["a", "a-1"]
138 eq_(new_names, expected)
139
140
141 def test_make_names_unique_includes_leading_whitespace():
142 alist = ["a", " a"]
143 new_names = make_names_unique(alist)
144 expected = ["a", "a-1"]
145 eq_(new_names, expected)
146
147
148 def test_make_names_unique_includes_trailing_whitespace():
149 alist = ["a", "a "]
150 new_names = make_names_unique(alist)
151 expected = ["a", "a-1"]
152 eq_(new_names, expected)
153
154
155 def test_make_names_unique_includes_whitespace():
156 alist = ["a", " a ", " ", " "]
157 new_names = make_names_unique(alist)
158 expected = ["a", "a-1", "", "-1"]
159 eq_(new_names, expected)
00 from pyexcel import Sheet
11 from _compact import OrderedDict
22
3 from nose.tools import eq_, raises
3 from nose.tools import eq_, raises, assert_not_in
44
55
66 class TestSheetColumn:
3232 eq_(s.column["Column 1"], ["1", "4", "7"])
3333 eq_(s.column["Column 3"], ["3", "6", "9"])
3434
35 @raises(AttributeError)
3536 def test_add(self):
3637 s = Sheet(self.data, "test")
3738 s.name_columns_by_row(0)
3839 data = OrderedDict({"Column 4": [10, 11, 12]})
39 s = s.column + data
40 s1 = s.column + data
41 eq_(s1.column.Column_4, [10, 11, 12])
42 eq_(s.column.Column_4, [10, 11, 12])
43
44 def test_iadd(self):
45 s = Sheet(self.data, "test")
46 s.name_columns_by_row(0)
47 data = OrderedDict({"Column 4": [10, 11, 12]})
48 s.column += data
4049 eq_(s.column.Column_4, [10, 11, 12])
4150
4251 @raises(TypeError)
131140 s = Sheet(self.data, "test")
132141 s.name_columns_by_row(2)
133142 data = OrderedDict({"Column 4": [10, 11, 12]})
134 s = s.column + data
143 s1 = s.column + data
144 eq_(s1.column["Column 4"], [10, 11, 12])
145 assert_not_in("Column 4", s.column)
146
147 def test_iadd(self):
148 s = Sheet(self.data, "test")
149 s.name_columns_by_row(2)
150 data = OrderedDict({"Column 4": [10, 11, 12]})
151 s.column += data
135152 assert s.column["Column 4"] == [10, 11, 12]
136153
137154 def test_dot_notation(self):
00 from pyexcel import Sheet
11 from _compact import OrderedDict
22
3 from nose.tools import eq_, raises
3 from nose.tools import eq_, raises, assert_not_in
44
55
66 class TestSheetRow:
7171 s = Sheet(self.data, "test")
7272 s.name_rows_by_column(0)
7373 data = OrderedDict({"Row 5": [10, 11, 12]})
74 s = s.row + data
74 s1 = s.row + data
75 assert s1.row["Row 5"] == [10, 11, 12]
76 assert_not_in("Row 5", s.row)
77
78 def test_iadd(self):
79 s = Sheet(self.data, "test")
80 s.name_rows_by_column(0)
81 data = OrderedDict({"Row 5": [10, 11, 12]})
82 s.row += data
7583 assert s.row["Row 5"] == [10, 11, 12]
7684
7785 def test_dot_notation(self):
33 from pyexcel.internal.generators import SheetStream
44 from pyexcel.plugins.sources.output_to_memory import WriteSheetToMemory
55
6 import pyexcel_io.manager as manager
76 from nose.tools import eq_
7 from pyexcel_io import manager as manager
88
99
1010 def test_save_to():
00 import copy
11
22 from pyexcel import Sheet, load_from_dict, load_from_records
3 from _compact import OrderedDict
43
54 from nose.tools import eq_, raises
65
125124 s.paste((1, 2))
126125
127126
128 class TestSheetColumn:
129 def setUp(self):
130 self.data = [
131 ["Column 1", "Column 2", "Column 3"],
132 [1, 2, 3],
133 [4, 5, 6],
134 [7, 8, 9],
135 ]
136
137 def test_formatter_by_named_column(self):
138 """Test one named column"""
139 s = Sheet(self.data, "test")
140 s.name_columns_by_row(0)
141 s.column.format("Column 1", str)
142 assert s.column["Column 1"] == ["1", "4", "7"]
143
144 def test_formatter_by_named_columns(self):
145 """Test multiple named columns"""
146 s = Sheet(self.data, "test")
147 s.name_columns_by_row(0)
148 s.column.format(["Column 1", "Column 3"], str)
149 assert s.column["Column 1"] == ["1", "4", "7"]
150 assert s.column["Column 3"] == ["3", "6", "9"]
151
152 def test_add(self):
153 s = Sheet(self.data, "test")
154 s.name_columns_by_row(0)
155 data = OrderedDict({"Column 4": [10, 11, 12]})
156 s = s.column + data
157 assert s.column["Column 4"] == [10, 11, 12]
158
159 @raises(TypeError)
160 def test_add_wrong_type(self):
161 """Add string type"""
162 s = Sheet(self.data, "test")
163 s.name_columns_by_row(0)
164 s = s.column + "string type" # bang
165
166 @raises(ValueError)
167 def test_delete_named_column(self):
168 s = Sheet(self.data, "test")
169 s.name_columns_by_row(0)
170 del s.column["Column 2"]
171 assert s.number_of_columns() == 2
172 s.column["Column 2"] # bang
173
174 @raises(ValueError)
175 def test_delete_indexed_column1(self):
176 s = Sheet(self.data, "test")
177 s.name_columns_by_row(0)
178 del s.column[1]
179 assert s.number_of_columns() == 2
180 s.column["Column 2"] # access it after deletion, bang
181
182 @raises(ValueError)
183 def test_delete_indexed_column2(self):
184 s = Sheet(self.data, "test")
185 s.name_columns_by_row(0)
186 del s.column["Column 2"]
187 assert s.number_of_columns() == 2
188 s.column["Column 2"] # access it after deletion, bang
189
190 @raises(ValueError)
191 def test_delete_indexed_column(self):
192 s = Sheet(self.data, "test")
193 s.name_columns_by_row(0)
194 s.delete_named_column_at(1)
195 assert s.number_of_columns() == 2
196 s.column["Column 2"] # access it after deletion, bang
197
198 @raises(ValueError)
199 def test_delete_column(self):
200 s = Sheet(self.data, "test")
201 del s.column[1, 2]
202 assert s.number_of_columns() == 1
203 s.column["Column 2"] # access it after deletion, bang
204
205
206 class TestSheetColumn2:
207 def setUp(self):
208 self.data = [
209 [1, 2, 3],
210 [4, 5, 6],
211 ["Column 1", "Column 2", "Column 3"],
212 [7, 8, 9],
213 ]
214
215 def test_series(self):
216 s = Sheet(self.data, "test")
217 s.name_columns_by_row(2)
218 assert s.colnames == ["Column 1", "Column 2", "Column 3"]
219 custom_columns = ["C1", "C2", "C3"]
220 s.colnames = custom_columns
221 assert s.colnames == custom_columns
222
223 def test_series2(self):
224 custom_columns = ["C1", "C2", "C3"]
225 s = Sheet(self.data, "test", colnames=custom_columns)
226 assert s.colnames == custom_columns
227
228 @raises(NotImplementedError)
229 def test_series3(self):
230 custom_columns = ["C1", "C2", "C3"]
231 Sheet(
232 self.data, "test", colnames=custom_columns, name_columns_by_row=0
233 )
234
235 def test_formatter_by_named_column(self):
236 s = Sheet(self.data, "test")
237 s.name_columns_by_row(2)
238 s.column.format("Column 1", str)
239 assert s.column["Column 1"] == ["1", "4", "7"]
240
241 def test_formatter_by_named_column_2(self):
242 s = Sheet(self.data, "test")
243 s.name_columns_by_row(2)
244 s.column.format("Column 1", str)
245 assert s.column["Column 1"] == ["1", "4", "7"]
246
247 def test_add(self):
248 s = Sheet(self.data, "test")
249 s.name_columns_by_row(2)
250 data = OrderedDict({"Column 4": [10, 11, 12]})
251 s = s.column + data
252 assert s.column["Column 4"] == [10, 11, 12]
253
254 @raises(ValueError)
255 def test_delete_named_column(self):
256 s = Sheet(self.data, "test")
257 s.name_columns_by_row(2)
258 del s.column["Column 2"]
259 assert s.number_of_columns() == 2
260 s.column["Column 2"] # bang
261
262 def test_set_indexed_row(self):
263 s = Sheet(self.data, "test")
264 s.name_columns_by_row(2)
265 s.row[0] = [10000, 1, 11]
266 assert s.row[0] == [10000, 1, 11]
267
268
269 class TestSheetRow:
270 def setUp(self):
271 self.data = [
272 ["Row 0", -1, -2, -3],
273 ["Row 1", 1, 2, 3],
274 ["Row 2", 4, 5, 6],
275 ["Row 3", 7, 8, 9],
276 ]
277
278 def test_formatter_by_named_row(self):
279 s = Sheet(self.data, "test")
280 s.name_rows_by_column(0)
281 s.row.format("Row 1", str)
282 assert s.row["Row 1"] == ["1", "2", "3"]
283
284 def test_rownames(self):
285 s = Sheet(self.data, "test", name_rows_by_column=0)
286 assert s.rownames == ["Row 0", "Row 1", "Row 2", "Row 3"]
287 custom_rows = ["R0", "R1", "R2", "R3"]
288 s.rownames = custom_rows
289 assert s.rownames == custom_rows
290
291 def test_rownames2(self):
292 custom_rows = ["R0", "R1", "R2", "R3"]
293 s = Sheet(self.data, "test", rownames=custom_rows)
294 assert s.rownames == custom_rows
295
296 @raises(NotImplementedError)
297 def test_rownames3(self):
298 custom_rows = ["R0", "R1", "R2", "R3"]
299 Sheet(self.data, "test", name_rows_by_column=0, rownames=custom_rows)
300
301 def test_formatter_by_named_row_2(self):
302 s = Sheet(self.data, "test")
303 s.name_rows_by_column(0)
304 s.row.format("Row 1", str)
305 assert s.row["Row 1"] == ["1", "2", "3"]
306
307 def test_row_series_to_dict(self):
308 s = Sheet(self.data, "test")
309 s.name_rows_by_column(0)
310 content = s.to_dict(True)
311 keys = ["Row 0", "Row 1", "Row 2", "Row 3"]
312 assert keys == list(content.keys())
313
314 @raises(TypeError)
315 def test_extend_rows_using_wrong_data_type(self):
316 s = Sheet(self.data, "test")
317 s.name_rows_by_column(0)
318 s.extend_rows([1, 2])
319
320 def test_formatter_by_named_row2(self):
321 """Test a list of string as index"""
322 s = Sheet(self.data, "test")
323 s.name_rows_by_column(0)
324 s.row.format(["Row 1", "Row 2"], str)
325 assert s.row["Row 1"] == ["1", "2", "3"]
326 assert s.row["Row 2"] == ["4", "5", "6"]
327
328 def test_add(self):
329 s = Sheet(self.data, "test")
330 s.name_rows_by_column(0)
331 data = OrderedDict({"Row 5": [10, 11, 12]})
332 s = s.row + data
333 assert s.row["Row 5"] == [10, 11, 12]
334
335 @raises(TypeError)
336 def test_add_wrong_type(self):
337 s = Sheet(self.data, "test")
338 s.name_rows_by_column(0)
339 s = s.row + "string type" # bang
340
341 @raises(ValueError)
342 def test_delete_named_row(self):
343 s = Sheet(self.data, "test")
344 s.name_rows_by_column(0)
345 del s.row["Row 2"]
346 assert s.number_of_rows() == 3
347 s.row["Row 2"] # already deleted
348
349 @raises(ValueError)
350 def test_delete_indexed_row1(self):
351 s = Sheet(self.data, "test")
352 s.name_rows_by_column(0)
353 del s.row[2]
354 assert s.number_of_rows() == 3
355 s.row["Row 2"] # already deleted
356
357 @raises(ValueError)
358 def test_delete_indexed_row2(self):
359 s = Sheet(self.data, "test")
360 s.name_rows_by_column(0)
361 s.delete_named_row_at(2)
362 assert s.number_of_rows() == 3
363 s.row["Row 2"] # already deleted
364
365 @raises(ValueError)
366 def test_delete_indexed_row3(self):
367 s = Sheet(self.data, "test")
368 s.name_rows_by_column(0)
369 del s.row["Row 0", "Row 1"]
370 assert s.number_of_rows() == 2
371 s.row["Row 1"] # already deleted
372
373 @raises(ValueError)
374 def test_delete_row(self):
375 s = Sheet(self.data, "test")
376 del s.row[1, 2]
377 assert s.number_of_rows() == 2
378 s.row["Row 1"] # already deleted
379
380 def test_column_locator2(self):
381 """
382 Remove odd columns
383 """
384 sheet = Sheet(self.data)
385
386 def locator(index, _):
387 return index % 2 == 0
388
389 del sheet.row[locator]
390 assert sheet.number_of_rows() == 2
391
392 def test_set_named_row(self):
393 s = Sheet(self.data, "test")
394 s.name_rows_by_column(0)
395 s.row["Row 2"] = [11, 11, 11]
396 assert s.row["Row 2"] == [11, 11, 11]
397
398 def test_set_indexed_column(self):
399 s = Sheet(self.data, "test", name_rows_by_column=0)
400 s.column[0] = [12, 3, 4, 5]
401 assert s.column[0] == [12, 3, 4, 5]
402
403
404127 class TestUniquenessOfNames:
405128 def test_column_names(self):
406129 data = [
5252 eq_(result, list(actual))
5353
5454 def test_book_reader_to_records_custom(self):
55 """use custom header
56 """
55 """use custom header"""
5756 r = pe.SeriesReader(self.testfile)
5857 custom_headers = ["O", "P", "Q"]
5958 result = [