mirror of
https://github.com/kennethreitz/tablib.git
synced 2026-06-05 15:00:19 +00:00
Compare commits
280 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bb0abc863e | |||
| 58f6eefe01 | |||
| e4726cb85c | |||
| 412e690289 | |||
| 44e797d70e | |||
| 34c14aca18 | |||
| 7c318adde4 | |||
| 5dd74c0104 | |||
| a50ff92ff2 | |||
| 383d4b9c4e | |||
| 00e2ffa2ef | |||
| a3cd2c9cff | |||
| d89d243a30 | |||
| 69abfc3ada | |||
| 05bd0d1d42 | |||
| 62807734bd | |||
| c5c2dffe42 | |||
| 46102d4be7 | |||
| 44e9e24fec | |||
| 0ca5520bbc | |||
| e66eb4a189 | |||
| 0e720d78ca | |||
| 6afe716d64 | |||
| 76cbf9fadf | |||
| a93f93a458 | |||
| 3d44bdec40 | |||
| 319505817a | |||
| 6cb9a69746 | |||
| bb1354b61f | |||
| ddc4bd30f2 | |||
| 52e547daf9 | |||
| 7f0b7a0a22 | |||
| ddac443732 | |||
| e13f4d0aba | |||
| 54f9041f2c | |||
| 91d3299280 | |||
| f59abe84be | |||
| cf23f2344f | |||
| e16bb38c48 | |||
| 71ca275dd1 | |||
| 75bbfbbaf4 | |||
| b35d505621 | |||
| cd491c062c | |||
| 9fdb72cc5c | |||
| a5b1f7987e | |||
| 8cf6770a76 | |||
| 5fa3d2f886 | |||
| d4c66c7a4e | |||
| af17586581 | |||
| 23d21f00f3 | |||
| 7ee924b5a6 | |||
| d720beadac | |||
| ee9666a146 | |||
| 77a9e25795 | |||
| d515724817 | |||
| 2814fbc381 | |||
| 9ca1d4ec54 | |||
| abbb4e32d8 | |||
| f6e757d569 | |||
| 9ba0451843 | |||
| d99db57d75 | |||
| 2299c00883 | |||
| 5ba6f5d91a | |||
| bbdf5f11ab | |||
| 851ba25702 | |||
| 039272b274 | |||
| d6a7832e60 | |||
| e51c4faec7 | |||
| f7fc3244ee | |||
| 53d69bd3ea | |||
| fcc9700d11 | |||
| 1ec9c18a66 | |||
| 99c28fa560 | |||
| fa7fb579fd | |||
| be24de19dc | |||
| 1d4f4b68ca | |||
| 8debeb26ac | |||
| 38e1ee6c3d | |||
| a774789252 | |||
| 995eabad37 | |||
| d90358bf69 | |||
| c5920249de | |||
| 9b6a73c97c | |||
| 679bd115b6 | |||
| 32cbc36fc1 | |||
| 8bded88559 | |||
| f8f57a467e | |||
| a11a993955 | |||
| 25894f2948 | |||
| 591b89693e | |||
| 85d9c2497e | |||
| eaf52b691e | |||
| 6f53c5d2b9 | |||
| 90ee799576 | |||
| c02a21ccd2 | |||
| fa045ca114 | |||
| 65703550c3 | |||
| 1fcb98f9ae | |||
| e2d45ecff7 | |||
| 47d92277cc | |||
| fdd74b5b0c | |||
| de052f0fac | |||
| 2f3acf5af4 | |||
| c4e8755cd2 | |||
| 79dc4524a0 | |||
| a785d77901 | |||
| b3485ec942 | |||
| 28b358c9da | |||
| 24657520e9 | |||
| 66d9e50984 | |||
| 541fba6786 | |||
| bc6398ffb0 | |||
| dca7bc9a7d | |||
| 2fbda0f43d | |||
| e350f9428b | |||
| 68dba0a77d | |||
| 028be03c2c | |||
| e1d65ba3c8 | |||
| e4cb3bcd9b | |||
| bf9510e0c7 | |||
| 82ae3ca507 | |||
| 5fbdd56fba | |||
| f187cef5f4 | |||
| 87892d7266 | |||
| 20e2ce5ba0 | |||
| 48e576954d | |||
| a21f8187f8 | |||
| 8479df725e | |||
| 333deb2311 | |||
| 0b714f21e1 | |||
| ae730b00b1 | |||
| 84e8b0384f | |||
| 7a2842a8af | |||
| 954bbdccf3 | |||
| 7acaa8460d | |||
| 84e7e251ae | |||
| dc868eff31 | |||
| 43356e908c | |||
| f7acc19523 | |||
| c5972db8f0 | |||
| 1cc051f3e8 | |||
| 3da155ce0d | |||
| 9a34cf0980 | |||
| 434f66b4eb | |||
| d056916c53 | |||
| cf5239f097 | |||
| 49d8cb816f | |||
| fbd277ff2e | |||
| 6f4572fa56 | |||
| 453fc8614c | |||
| 01cf58e431 | |||
| f6cd89c76c | |||
| 1e0f30e8a6 | |||
| 569d35bfca | |||
| d40cdfbcd0 | |||
| 86bbaf9bea | |||
| 0ed01d85b9 | |||
| fc4cc7fa14 | |||
| 70716fdd21 | |||
| 1146ec2341 | |||
| 1a7d597745 | |||
| 56b627a561 | |||
| 98e182bed2 | |||
| c8a5563309 | |||
| c225a64d68 | |||
| d611d5a14f | |||
| 45121ddd65 | |||
| c74357cb20 | |||
| 939b0af551 | |||
| 9c2018653f | |||
| 2bc6122ee8 | |||
| 7f0748aac9 | |||
| 41a5c67159 | |||
| 3efefcc8da | |||
| d19de6025b | |||
| 65ba937c0d | |||
| 79a2bb888f | |||
| 25eacaf6f0 | |||
| c2a9af7fb3 | |||
| 3b06f3760d | |||
| e7ee3195a7 | |||
| 5bd2e3df52 | |||
| 837b3f83e6 | |||
| ff8f23edd5 | |||
| 5ffcfd56f2 | |||
| 955c24c974 | |||
| 192a5efabb | |||
| 1aafc7e2f4 | |||
| 9e45b95d12 | |||
| d8f0a018ae | |||
| 7545f3726e | |||
| 85e2bd73fc | |||
| 37033903c5 | |||
| 02c38c2520 | |||
| 26748deb9f | |||
| 63f6cea132 | |||
| 1b035f9774 | |||
| 2c14486c33 | |||
| 8bc69c9d85 | |||
| d36a2cbd42 | |||
| 1ab0eb3fae | |||
| cd71e1a5b1 | |||
| 47f79a7ca1 | |||
| 9f38efe413 | |||
| 5d98239a7e | |||
| a3f0d02633 | |||
| b29007a0df | |||
| e75c3c1a66 | |||
| 47cebbc328 | |||
| e4c39524f7 | |||
| c88c794314 | |||
| 752443f077 | |||
| 7c0507bcce | |||
| 652ac85549 | |||
| 05ea3c35fc | |||
| d5fada7e1d | |||
| 511c58d4e1 | |||
| c469360a0e | |||
| 97b4401b18 | |||
| 40e0f41b4c | |||
| 39435727ba | |||
| eda9d5af03 | |||
| 15435047c6 | |||
| a3781e3c89 | |||
| 6a825a8a39 | |||
| 6a449d497a | |||
| d807c60346 | |||
| 71603662b1 | |||
| 21c11b9911 | |||
| e8c923d712 | |||
| bc581c08df | |||
| 4f9c9d09ec | |||
| 63e8a7172d | |||
| 45e0af9f0e | |||
| fa6f5b3af3 | |||
| 0528e0a500 | |||
| 8e83734985 | |||
| 783eccc67d | |||
| 7236415f42 | |||
| c0a3c3ea1e | |||
| 14bd964fb1 | |||
| 6bfc6634ba | |||
| 54affad292 | |||
| 7c963a0f4d | |||
| 02f27f15c5 | |||
| 9c65515e7a | |||
| c87a954a9e | |||
| 42e40ed0ab | |||
| 23ab6c4724 | |||
| 32a09ccd6a | |||
| 81a7f79b3d | |||
| 05c9b33003 | |||
| ec7273d02d | |||
| 19ee1997b5 | |||
| f01d65c2e9 | |||
| 9778a96351 | |||
| 906138b138 | |||
| 43c68b396f | |||
| d611233c80 | |||
| 3d02b866ce | |||
| 887ee2fbac | |||
| bfd211854a | |||
| bc75911500 | |||
| a2b4e4c6ba | |||
| fde6f11763 | |||
| 33a83316df | |||
| f6d7888d9e | |||
| c19e2f2c5b | |||
| eaa2b9b8ea | |||
| 2f8083bda6 | |||
| 2c5a9af76e | |||
| e74a8f41cc | |||
| cd5aa4fc06 | |||
| 1d460bac40 | |||
| 4a3fde37a3 | |||
| 62ad123ad8 | |||
| fefc7b4d1f | |||
| 6313437a27 | |||
| 23a5bb1443 | |||
| 864f29cc4b |
+7
-1
@@ -22,4 +22,10 @@ coverage.xml
|
||||
nosetests.xml
|
||||
junit-py25.xml
|
||||
junit-py26.xml
|
||||
junit-py27.xml
|
||||
junit-py27.xml
|
||||
|
||||
# tox noise
|
||||
.tox
|
||||
|
||||
# pyenv noise
|
||||
.python-version
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
language: python
|
||||
python:
|
||||
- 2.7
|
||||
- 3.3
|
||||
- 3.4
|
||||
- 3.5
|
||||
- 3.6
|
||||
install:
|
||||
- python setup.py install
|
||||
script: python test_tablib.py
|
||||
@@ -4,9 +4,14 @@ various contributors:
|
||||
Development Lead
|
||||
````````````````
|
||||
|
||||
- Kenneth Reitz <_@kennethreitz.com>
|
||||
- Kenneth Reitz <me@kennethreitz.org>
|
||||
|
||||
|
||||
Core Contributors
|
||||
`````````````````
|
||||
|
||||
- Iuri de Silvio <iurisilvio@gmail.com>
|
||||
|
||||
Patches and Suggestions
|
||||
```````````````````````
|
||||
|
||||
@@ -16,4 +21,16 @@ Patches and Suggestions
|
||||
- Benjamin Wohlwend
|
||||
- Erik Youngren
|
||||
- Mark Rogers
|
||||
- Mark Walling
|
||||
- Mark Walling
|
||||
- Mike Waldner
|
||||
- Joel Friedly
|
||||
- Jakub Janoszek
|
||||
- Marc Abramowitz
|
||||
- Alex Gaynor
|
||||
- James Douglass
|
||||
- Tommy Anthony
|
||||
- Rabin Nankhwa
|
||||
- Marco Dallagiacoma
|
||||
- Mathias Loesch
|
||||
- Tushar Makkar
|
||||
- Andrii Soldatenko
|
||||
|
||||
+72
-1
@@ -1,6 +1,77 @@
|
||||
History
|
||||
-------
|
||||
|
||||
0.11.5 (2017-06-13)
|
||||
+++++++++++++++++++
|
||||
|
||||
- Use ``yaml.safe_load`` for importing yaml.
|
||||
|
||||
0.11.4 (2017-01-23)
|
||||
+++++++++++++++++++
|
||||
|
||||
- Use built-in `json` package if available
|
||||
- Support Python 3.5+ in classifiers
|
||||
|
||||
** Bugfixes **
|
||||
|
||||
- Fixed textual representation for Dataset with no headers
|
||||
- Handle decimal types
|
||||
|
||||
0.11.3 (2016-02-16)
|
||||
+++++++++++++++++++
|
||||
|
||||
- Release fix.
|
||||
|
||||
0.11.2 (2016-02-16)
|
||||
+++++++++++++++++++
|
||||
|
||||
**Bugfixes**
|
||||
|
||||
- Fix export only formats.
|
||||
- Fix for xlsx output.
|
||||
|
||||
0.11.1 (2016-02-07)
|
||||
+++++++++++++++++++
|
||||
|
||||
**Bugfixes**
|
||||
|
||||
- Fixed packaging error on Python 3.
|
||||
|
||||
|
||||
0.11.0 (2016-02-07)
|
||||
+++++++++++++++++++
|
||||
|
||||
**New Formats!**
|
||||
|
||||
- Added LaTeX table export format (``Dataset.latex``).
|
||||
- Support for dBase (DBF) files (``Dataset.dbf``).
|
||||
|
||||
**Improvements**
|
||||
|
||||
- New import/export interface (``Dataset.export()``, ``Dataset.load()``).
|
||||
- CSV custom delimiter support (``Dataset.export('csv', delimiter='$')``).
|
||||
- Adding ability to remove duplicates to all rows in a dataset (``Dataset.remove_duplicates()``).
|
||||
- Added a mechanism to avoid ``datetime.datetime`` issues when serializing data.
|
||||
- New ``detect_format()`` function (mostly for internal use).
|
||||
- Update the vendored unicodecsv to fix ``None`` handling.
|
||||
- Only freeze the headers row, not the headers columns (xls).
|
||||
|
||||
**Breaking Changes**
|
||||
|
||||
- ``detect()`` function removed.
|
||||
|
||||
**Bugfixes**
|
||||
|
||||
- Fix XLSX import.
|
||||
- Bugfix for ``Dataset.transpose().transpose()``.
|
||||
|
||||
|
||||
0.10.0 (2014-05-27)
|
||||
+++++++++++++++++++
|
||||
|
||||
* Unicode Column Headers
|
||||
* ALL the bugfixes!
|
||||
|
||||
0.9.11 (2011-06-30)
|
||||
+++++++++++++++++++
|
||||
|
||||
@@ -16,7 +87,7 @@ History
|
||||
|
||||
* Dataset API Changes
|
||||
* ``stack_rows`` => ``stack``, ``stack_columns`` => ``stack_cols``
|
||||
* column operations have their own methods now (``apend_col``, ``insert_col``)
|
||||
* column operations have their own methods now (``append_col``, ``insert_col``)
|
||||
* List-style ``pop()``
|
||||
* Redis-style ``rpush``, ``lpush``, ``rpop``, ``lpop``, ``rpush_col``, and ``lpush_col``
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright 2011 Kenneth Reitz
|
||||
Copyright 2016 Kenneth Reitz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
include HISTORY.rst README.rst LICENSE AUTHORS
|
||||
include HISTORY.rst README.rst LICENSE AUTHORS NOTICE test_tablib.py
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
test:
|
||||
python test_tablib.py
|
||||
publish:
|
||||
python setup.py register
|
||||
python setup.py sdist upload
|
||||
python setup.py bdist_wheel --universal upload
|
||||
@@ -1,5 +1,4 @@
|
||||
Tablib includes some vendorized python libraries: ordereddict, pyyaml,
|
||||
simplejson, unicodecsv, and xlwt.
|
||||
Tablib includes some vendorized python libraries: ordereddict, markup.
|
||||
|
||||
Markup License
|
||||
==============
|
||||
@@ -7,7 +6,6 @@ Markup License
|
||||
Markup is in the public domain.
|
||||
|
||||
|
||||
|
||||
OrderedDict License
|
||||
===================
|
||||
|
||||
@@ -32,144 +30,3 @@ subject to the following conditions:
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
|
||||
PyYAML License
|
||||
==============
|
||||
|
||||
Copyright (c) 2006 Kirill Simonov
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
|
||||
UnicodeCSV License
|
||||
==================
|
||||
|
||||
Copyright 2010 Jeremy Dunck. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are
|
||||
permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
of conditions and the following disclaimer in the documentation and/or other materials
|
||||
provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY JEREMY DUNCK ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JEREMY DUNCK OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
The views and conclusions contained in the software and documentation are those of the
|
||||
authors and should not be interpreted as representing official policies, either expressed
|
||||
or implied, of Jeremy Dunck.
|
||||
|
||||
|
||||
|
||||
|
||||
XLWT License
|
||||
============
|
||||
|
||||
Portions copyright © 2007, Stephen John Machin, Lingfo Pty Ltd
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. None of the names of Stephen John Machin, Lingfo Pty Ltd and any
|
||||
contributors may be used to endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
|
||||
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
THE POSSIBILITY OF SUCH DAMAGE.
|
||||
"""
|
||||
|
||||
"""
|
||||
Copyright (C) 2005 Roman V. Kiseliov
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
3. All advertising materials mentioning features or use of this
|
||||
software must display the following acknowledgment:
|
||||
"This product includes software developed by
|
||||
Roman V. Kiseliov <roman@kiseliov.ru>."
|
||||
|
||||
4. Redistributions of any form whatsoever must retain the following
|
||||
acknowledgment:
|
||||
"This product includes software developed by
|
||||
Roman V. Kiseliov <roman@kiseliov.ru>."
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY Roman V. Kiseliov ``AS IS'' AND ANY
|
||||
EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Roman V. Kiseliov OR
|
||||
ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Roman V. Kiseliov
|
||||
Russia
|
||||
Kursk
|
||||
Libknecht St., 4
|
||||
|
||||
+7(0712)56-09-83
|
||||
|
||||
<roman@kiseliov.ru>
|
||||
Subject: pyExcelerator
|
||||
+32
-10
@@ -1,6 +1,9 @@
|
||||
Tablib: format-agnostic tabular dataset library
|
||||
===============================================
|
||||
|
||||
.. image:: https://travis-ci.org/kennethreitz/tablib.svg?branch=master
|
||||
:target: https://travis-ci.org/kennethreitz/tablib
|
||||
|
||||
::
|
||||
|
||||
_____ ______ ___________ ______
|
||||
@@ -18,9 +21,12 @@ Output formats supported:
|
||||
- Excel (Sets + Books)
|
||||
- JSON (Sets + Books)
|
||||
- YAML (Sets + Books)
|
||||
- Pandas DataFrames (Sets)
|
||||
- HTML (Sets)
|
||||
- TSV (Sets)
|
||||
- OSD (Sets)
|
||||
- CSV (Sets)
|
||||
- DBF (Sets)
|
||||
|
||||
Note that tablib *purposefully* excludes XML support. It always will. (Note: This is a joke. Pull requests are welcome.)
|
||||
|
||||
@@ -28,7 +34,7 @@ Overview
|
||||
--------
|
||||
|
||||
`tablib.Dataset()`
|
||||
A Dataset is a table of tabular data. It may or may not have a header row. They can be build and manipulated as raw Python datatypes (Lists of tuples|dictionaries). Datasets can be imported from JSON, YAML, and CSV; they can be exported to XLSX, XLS, ODS, JSON, YAML, CSV, TSV, and HTML.
|
||||
A Dataset is a table of tabular data. It may or may not have a header row. They can be build and manipulated as raw Python datatypes (Lists of tuples|dictionaries). Datasets can be imported from JSON, YAML, DBF, and CSV; they can be exported to XLSX, XLS, ODS, JSON, YAML, DBF, CSV, TSV, and HTML.
|
||||
|
||||
`tablib.Databook()`
|
||||
A Databook is a set of Datasets. The most common form of a Databook is an Excel file with multiple spreadsheets. Databooks can be imported from JSON and YAML; they can be exported to XLSX, XLS, ODS, JSON, and YAML.
|
||||
@@ -55,17 +61,17 @@ Intelligently add new rows: ::
|
||||
|
||||
Intelligently add new columns: ::
|
||||
|
||||
>>> data.append(col=(90, 67, 83), header='age')
|
||||
>>> data.append_col((90, 67, 83), header='age')
|
||||
|
||||
Slice rows: ::
|
||||
|
||||
>>> print data[:2]
|
||||
>>> print(data[:2])
|
||||
[('John', 'Adams', 90), ('George', 'Washington', 67)]
|
||||
|
||||
|
||||
Slice columns by header: ::
|
||||
|
||||
>>> print data['first_name']
|
||||
>>> print(data['first_name'])
|
||||
['John', 'George', 'Henry']
|
||||
|
||||
Easily delete rows: ::
|
||||
@@ -81,7 +87,7 @@ JSON!
|
||||
+++++
|
||||
::
|
||||
|
||||
>>> print data.json
|
||||
>>> print(data.json)
|
||||
[
|
||||
{
|
||||
"last_name": "Adams",
|
||||
@@ -100,7 +106,7 @@ YAML!
|
||||
+++++
|
||||
::
|
||||
|
||||
>>> print data.yaml
|
||||
>>> print(data.yaml)
|
||||
- {age: 90, first_name: John, last_name: Adams}
|
||||
- {age: 83, first_name: Henry, last_name: Ford}
|
||||
|
||||
@@ -108,7 +114,7 @@ CSV...
|
||||
++++++
|
||||
::
|
||||
|
||||
>>> print data.csv
|
||||
>>> print(data.csv)
|
||||
first_name,last_name,age
|
||||
John,Adams,90
|
||||
Henry,Ford,83
|
||||
@@ -117,7 +123,24 @@ EXCEL!
|
||||
++++++
|
||||
::
|
||||
|
||||
>>> open('people.xls', 'wb').write(data.xls)
|
||||
>>> with open('people.xls', 'wb') as f:
|
||||
... f.write(data.xls)
|
||||
|
||||
DBF!
|
||||
++++
|
||||
::
|
||||
|
||||
>>> with open('people.dbf', 'wb') as f:
|
||||
... f.write(data.dbf)
|
||||
|
||||
Pandas DataFrame!
|
||||
+++++++++++++++++
|
||||
::
|
||||
|
||||
>>> print(data.df):
|
||||
first_name last_name age
|
||||
0 John Adams 90
|
||||
1 Henry Ford 83
|
||||
|
||||
It's that easy.
|
||||
|
||||
@@ -129,9 +152,8 @@ To install tablib, simply: ::
|
||||
|
||||
$ pip install tablib
|
||||
|
||||
Or, if you absolutely must: ::
|
||||
Make sure to check out `Tablib on PyPi <https://pypi.python.org/pypi/tablib/>`_!
|
||||
|
||||
$ easy_install tablib
|
||||
|
||||
Contribute
|
||||
----------
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
* Hooks System
|
||||
- pre/post-append
|
||||
- pre/post-import
|
||||
- pre/post-export
|
||||
* Add Tablib.ext namespace
|
||||
* Width detection for XLS output
|
||||
* Documentation Improvements
|
||||
Vendored
+2
-2
@@ -1,4 +1,4 @@
|
||||
<h3><a href="http://tablib.org">About Tablib</a></h3>
|
||||
<h3><a href="http://docs.python-tablib.org">About Tablib</a></h3>
|
||||
<p>
|
||||
Tablib is an MIT Licensed format-agnostic tabular dataset library, written in Python. It allows you to import, export, and manipulate tabular data sets. Advanced features include, segregation, dynamic columns, tags & filtering, and seamless format import & export.
|
||||
</p>
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
<h3>Useful Links</h3>
|
||||
<ul>
|
||||
<li><a href="http://tablib.org/">The Tablib Website</a></li>
|
||||
<li><a href="http://docs.python-tablib.org/">The Tablib Website</a></li>
|
||||
<li><a href="http://pypi.python.org/pypi/tablib">Tablib @ PyPI</a></li>
|
||||
<li><a href="http://github.com/kennethreitz/tablib">Tablib @ GitHub</a></li>
|
||||
<li><a href="http://github.com/kennethreitz/tablib/issues">Issue Tracker</a></li>
|
||||
|
||||
Vendored
+1
-1
@@ -1,4 +1,4 @@
|
||||
<h3><a href="http://tablib.org">About Tablib</a></h3>
|
||||
<h3><a href="http://docs.python-tablib.org/">About Tablib</a></h3>
|
||||
<p>
|
||||
Tablib is an MIT Licensed format-agnostic tabular dataset library, written in Python. It allows you to import, export, and manipulate tabular data sets. Advanced features include, segregation, dynamic columns, tags & filtering, and seamless format import & export.
|
||||
</p>
|
||||
Vendored
+7
-1
@@ -13,8 +13,14 @@
|
||||
© Copyright {{ copyright }}.
|
||||
</div>
|
||||
<a href="https://github.com/kennethreitz/tablib">
|
||||
<img style="position: absolute; top: 0; right: 0; border: 0;" src="http://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub" />
|
||||
<img style="position: absolute; top: 0; right: 0; border: 0;" src="//s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub" />
|
||||
</a>
|
||||
|
||||
<script type="text/javascript" src="//www.hellobar.com/hellobar.js"></script>
|
||||
<script type="text/javascript">
|
||||
new HelloBar(36402,48802);
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
var _gaq = _gaq || [];
|
||||
|
||||
Vendored
+2
-2
@@ -14,8 +14,8 @@
|
||||
{% block relbar1 %}{% endblock %}
|
||||
{% block relbar2 %}
|
||||
{% if theme_github_fork %}
|
||||
<a href="http://github.com/{{ theme_github_fork }}"><img style="position: fixed; top: 0; right: 0; border: 0;"
|
||||
src="http://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub" /></a>
|
||||
<a href="https://github.com/{{ theme_github_fork }}"><img style="position: fixed; top: 0; right: 0; border: 0;"
|
||||
src="//s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub" /></a>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% block sidebar1 %}{% endblock %}
|
||||
|
||||
+1
-1
@@ -41,7 +41,7 @@ master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'Tablib'
|
||||
copyright = u'2011. A <a href="http://kennethreitz.com/pages/open-projects.html">Kenneth Reitz</a> Project'
|
||||
copyright = u'2016. A <a href="http://kennethreitz.org/">Kenneth Reitz</a> Project'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
|
||||
+8
-47
@@ -8,11 +8,6 @@ Tablib is under active development, and contributors are welcome.
|
||||
If you have a feature request, suggestion, or bug report, please open a new
|
||||
issue on GitHub_. To submit patches, please send a pull request on GitHub_.
|
||||
|
||||
If you'd like to contribute, there's plenty to do. Here's a short todo list.
|
||||
|
||||
.. include:: ../TODO.rst
|
||||
|
||||
|
||||
.. _GitHub: http://github.com/kennethreitz/tablib/
|
||||
|
||||
|
||||
@@ -46,7 +41,7 @@ Source Control
|
||||
Tablib source is controlled with Git_, the lean, mean, distributed source
|
||||
control machine.
|
||||
|
||||
The repository is publicly accessable.
|
||||
The repository is publicly accessible.
|
||||
|
||||
``git clone git://github.com/kennethreitz/tablib.git``
|
||||
|
||||
@@ -66,12 +61,10 @@ Feature / Hotfix / Release branches follow a `Successful Git Branching Model`_ .
|
||||
The "next release" branch. Likely unstable.
|
||||
``master``
|
||||
Current production release (|version|) on PyPi.
|
||||
``gh-pages``
|
||||
Current release of http://tablib.org.
|
||||
|
||||
Each release is tagged.
|
||||
|
||||
When submitting patches, please place your feature/change in its own branch prior to opening a pull reqeust on GitHub_.
|
||||
When submitting patches, please place your feature/change in its own branch prior to opening a pull request on GitHub_.
|
||||
|
||||
|
||||
.. _Git: http://git-scm.org
|
||||
@@ -87,9 +80,7 @@ Adding New Formats
|
||||
|
||||
Tablib welcomes new format additions! Format suggestions include:
|
||||
|
||||
* Tab Separated Values
|
||||
* MySQL Dump
|
||||
* HTML Table
|
||||
|
||||
|
||||
Coding by Convention
|
||||
@@ -132,7 +123,7 @@ Tablib features a micro-framework for adding format support. The easiest way to
|
||||
|
||||
2.
|
||||
|
||||
Add your new format module to the :class:`tablib.formats.avalable` tuple.
|
||||
Add your new format module to the :class:`tablib.formats.available` tuple.
|
||||
|
||||
3.
|
||||
Add a mock property to the :class:`Dataset <tablib.Dataset>` class with verbose `reStructured Text`_ docstring. This alleviates IDE confusion, and allows for pretty auto-generated Sphinx_ documentation.
|
||||
@@ -174,20 +165,15 @@ This will generate a **nosetests.xml** file, which can then be analyzed.
|
||||
Continuous Integration
|
||||
----------------------
|
||||
|
||||
Every commit made to the **develop** branch is automatically tested and inspected upon receipt with `Jenkins CI`_. If you have access to the main repository and broke the build, you will receive an email accordingly.
|
||||
Every commit made to the **develop** branch is automatically tested and inspected upon receipt with `Travis CI`_. If you have access to the main repository and broke the build, you will receive an email accordingly.
|
||||
|
||||
Anyone may view the build status and history at any time.
|
||||
|
||||
http://ci.kennethreitz.com/
|
||||
|
||||
|
||||
If you are trustworthy and plan to contribute to tablib on a regular basis, please contact `Kenneth Reitz`_ to get an account on the Jenkins Server.
|
||||
|
||||
https://travis-ci.org/kennethreitz/tablib
|
||||
|
||||
Additional reports will also be included here in the future, including :pep:`8` checks and stress reports for extremely large datasets.
|
||||
|
||||
.. _`Jenkins CI`: http://jenkins-ci.org/
|
||||
.. _`Kenneth Reitz`: http://kennethreitz.com/contact-me/
|
||||
.. _`Jenkins CI`: https://travis-ci.org/
|
||||
|
||||
|
||||
.. _docs:
|
||||
@@ -210,37 +196,12 @@ Then, to build an HTML version of the docs, simply run the following from the **
|
||||
|
||||
Your ``docs/_build/html`` directory will then contain an HTML representation of the documentation, ready for publication on most web servers.
|
||||
|
||||
You can also generate the documentation in **ebpub**, **latex**, **json**, *&c* similarly.
|
||||
|
||||
.. admonition:: GitHub Pages
|
||||
|
||||
To push the documentation up to `GitHub Pages`_, you will first need to run `sphinx-to-github`_ against your ``docs/_build/html`` directory.
|
||||
|
||||
GitHub Pages are powered by an HTML generation system called Jeckyl_, which is configured to ignore files and folders that begin with "``_``" (*ie.* **_static**).
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
and `sphinx-to-github`_. ::
|
||||
|
||||
Installing sphinx-to-github is simple. ::
|
||||
|
||||
$ pip install sphinx-to-github
|
||||
|
||||
Running it against the docs is even simpler. ::
|
||||
|
||||
$ sphinx-to-github _build/html
|
||||
|
||||
Move the resulting files to the **gh-pages** branch of your repository, and push it up to GitHub.
|
||||
You can also generate the documentation in **epub**, **latex**, **json**, *&c* similarly.
|
||||
|
||||
.. _`reStructured Text`: http://docutils.sourceforge.net/rst.html
|
||||
.. _Sphinx: http://sphinx.pocoo.org
|
||||
.. _`GitHub Pages`: http://pages.github.com
|
||||
.. _Jeckyl: http://github.com/mojombo/jekyll
|
||||
.. _`sphinx-to-github`: http://github.com/michaeljones/sphinx-to-github
|
||||
|
||||
----------
|
||||
|
||||
Make sure to check out the :ref:`API Documentation <api>`.
|
||||
Make sure to check out the :ref:`API Documentation <api>`.
|
||||
|
||||
+12
-5
@@ -27,18 +27,25 @@ Tablib is an :ref:`MIT Licensed <mit>` format-agnostic tabular dataset library,
|
||||
::
|
||||
|
||||
>>> data = tablib.Dataset(headers=['First Name', 'Last Name', 'Age'])
|
||||
>>> map(data.append, [('Kenneth', 'Reitz', 22), ('Bessie', 'Monke', 21)])
|
||||
>>> for i in [('Kenneth', 'Reitz', 22), ('Bessie', 'Monke', 21)]:
|
||||
... data.append(i)
|
||||
|
||||
>>> data.json
|
||||
|
||||
>>> print(data.json)
|
||||
[{"Last Name": "Reitz", "First Name": "Kenneth", "Age": 22}, {"Last Name": "Monke", "First Name": "Bessie", "Age": 21}]
|
||||
|
||||
>>> data.yaml
|
||||
>>> print(data.yaml)
|
||||
- {Age: 22, First Name: Kenneth, Last Name: Reitz}
|
||||
- {Age: 21, First Name: Bessie, Last Name: Monke}
|
||||
|
||||
>>> data.xlsx
|
||||
<censored binary data>
|
||||
|
||||
>>> data.df
|
||||
First Name Last Name Age
|
||||
0 Kenneth Reitz 22
|
||||
1 Bessie Monke 21
|
||||
|
||||
|
||||
Testimonials
|
||||
------------
|
||||
@@ -55,7 +62,7 @@ and `The Sunlight Foundation <http://sunlightfoundation.com/>`_ use Tablib inter
|
||||
Tablib by @kennethreitz saved my life. I had to consolidate like 5 huge poorly maintained lists of domains and data. It was a breeze!
|
||||
|
||||
**Dave Coutts**
|
||||
It's turning into one of my most used modules of 2010. You really hit a sweat spot for managing tabular data with a minimal amount of code and effort.
|
||||
It's turning into one of my most used modules of 2010. You really hit a sweet spot for managing tabular data with a minimal amount of code and effort.
|
||||
|
||||
**Joshua Ourisman**
|
||||
Tablib has made it so much easier to deal with the inevitable 'I want an Excel file!' requests from clients...
|
||||
@@ -99,4 +106,4 @@ method, this part of the documentation is for you.
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
api
|
||||
api
|
||||
|
||||
+8
-33
@@ -2,7 +2,7 @@
|
||||
Installation
|
||||
============
|
||||
|
||||
This part of the documentation covers the installation of Tablib. The first step to using any software package is getting it properly installed. Please read this section carefully, or you may miss out on some nice :ref:`speed enhancements <peed-extentions>`.
|
||||
This part of the documentation covers the installation of Tablib. The first step to using any software package is getting it properly installed.
|
||||
|
||||
|
||||
.. _installing:
|
||||
@@ -14,27 +14,10 @@ Installing Tablib
|
||||
Distribute & Pip
|
||||
----------------
|
||||
|
||||
Installing Tablib is simple with `pip <http://www.pip-installer.org/>`_::
|
||||
Of course, the recommended way to install Tablib is with `pip <http://www.pip-installer.org/>`_::
|
||||
|
||||
$ pip install tablib
|
||||
|
||||
or, with `easy_install <http://pypi.python.org/pypi/setuptools>`_::
|
||||
|
||||
$ easy_install tablib
|
||||
|
||||
But, you really `shouldn't do that <http://www.pip-installer.org/en/latest/index.html#pip-compared-to-easy-install>`_.
|
||||
|
||||
|
||||
|
||||
Cheeseshop Mirror
|
||||
-----------------
|
||||
|
||||
If the Cheeseshop is down, you can also install Requests from Kenneth Reitz's personal `Cheeseshop mirror <pip.kreitz.co/>`_::
|
||||
|
||||
$ pip install -i http://pip.kreitz.co/simple tablib
|
||||
|
||||
|
||||
|
||||
|
||||
-------------------
|
||||
Download the Source
|
||||
@@ -57,22 +40,14 @@ To download the full source history from Git, see :ref:`Source Control <scm>`.
|
||||
.. _zipball: http://github.com/kennethreitz/tablib/zipball/master
|
||||
|
||||
|
||||
.. _speed-extentions:
|
||||
Speed Extentions
|
||||
.. _speed-extensions:
|
||||
Speed Extensions
|
||||
----------------
|
||||
|
||||
.. versionadded:: 0.8.5
|
||||
|
||||
Tablib is partially dependent on the **pyyaml**, **simplejson**, and **xlwt** modules. To reduce installation issues, fully integrated versions of all required libraries are included in Tablib.
|
||||
|
||||
However, if performance is important to you (and it should be), you can install **pyyaml** with C extentions from PyPi. ::
|
||||
|
||||
$ pip install PyYAML
|
||||
|
||||
If you're using Python 2.5, you should also install the **simplejson** module (pip will do this for you). If you're using Python 2.6+, the built-in **json** module is already optimized and in use. ::
|
||||
|
||||
$ pip install simplejson
|
||||
You can gain some speed improvement by optionally installing the ujson_ library.
|
||||
Tablib will fallback to the standard `json` module if it doesn't find ``ujson``.
|
||||
|
||||
.. _ujson: https://pypi.python.org/pypi/ujson
|
||||
|
||||
|
||||
.. _updates:
|
||||
@@ -89,4 +64,4 @@ When a new version is available, upgrading is simple::
|
||||
$ pip install tablib --upgrade
|
||||
|
||||
|
||||
Now, go get a :ref:`Quick Start <quickstart>`.
|
||||
Now, go get a :ref:`Quick Start <quickstart>`.
|
||||
|
||||
+7
-6
@@ -10,7 +10,7 @@ Advanced features include, segregation, dynamic columns, tags / filtering, and
|
||||
seamless format import/export.
|
||||
|
||||
|
||||
Philosphy
|
||||
Philosophy
|
||||
---------
|
||||
|
||||
Tablib was developed with a few :pep:`20` idioms in mind.
|
||||
@@ -49,7 +49,7 @@ Tablib is released under terms of `The MIT License`_.
|
||||
Tablib License
|
||||
--------------
|
||||
|
||||
Copyright 2011 Kenneth Reitz
|
||||
Copyright 2016 Kenneth Reitz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -77,11 +77,12 @@ Pythons Supported
|
||||
|
||||
At this time, the following Python platforms are officially supported:
|
||||
|
||||
* cPython 2.5
|
||||
* cPython 2.6
|
||||
* cPython 2.7
|
||||
* cPython 3.1
|
||||
* cPython 3.2
|
||||
* cPython 3.3
|
||||
* cPython 3.4
|
||||
* cPython 3.5
|
||||
* cPython 3.6
|
||||
* PyPy-c 1.4
|
||||
* PyPy-c 1.5
|
||||
|
||||
@@ -90,4 +91,4 @@ Support for other Pythons will be rolled out soon.
|
||||
|
||||
|
||||
|
||||
Now, go :ref:`Install Tablib <install>`.
|
||||
Now, go :ref:`Install Tablib <install>`.
|
||||
|
||||
+20
-3
@@ -39,6 +39,7 @@ You can now start filling this :class:`Dataset <tablib.Dataset>` object with dat
|
||||
|
||||
|
||||
|
||||
|
||||
-----------
|
||||
Adding Rows
|
||||
-----------
|
||||
@@ -68,7 +69,7 @@ Adding Headers
|
||||
--------------
|
||||
|
||||
|
||||
It's time enhance our :class:`Dataset` by giving our columns some titles. To do so, set :class:`Dataset.headers`. ::
|
||||
It's time to enhance our :class:`Dataset` by giving our columns some titles. To do so, set :class:`Dataset.headers`. ::
|
||||
|
||||
data.headers = ['First Name', 'Last Name']
|
||||
|
||||
@@ -97,6 +98,15 @@ Let's view the data now. ::
|
||||
It's that easy.
|
||||
|
||||
|
||||
--------------
|
||||
Importing Data
|
||||
--------------
|
||||
Creating a :class:`tablib.Dataset` object by importing a pre-existing file is simple. ::
|
||||
|
||||
imported_data = Dataset().load(open('data.csv').read())
|
||||
|
||||
This detects what sort of data is being passed in, and uses an appropriate formatter to do the import. So you can import from a variety of different file types.
|
||||
|
||||
--------------
|
||||
Exporting Data
|
||||
--------------
|
||||
@@ -146,6 +156,13 @@ To do so, we access the :class:`Dataset` as if it were a standard Python diction
|
||||
>>> data['First Name']
|
||||
['Kenneth', 'Bessie']
|
||||
|
||||
You can also access the column using its index. ::
|
||||
|
||||
>>> data.headers
|
||||
['Last Name', 'First Name', 'Age']
|
||||
>>> data.get_col(1)
|
||||
['Kenneth', 'Bessie']
|
||||
|
||||
Let's find the average age. ::
|
||||
|
||||
>>> ages = data['Age']
|
||||
@@ -244,7 +261,7 @@ Filtering Datasets with Tags
|
||||
|
||||
|
||||
When constructing a :class:`Dataset` object, you can add tags to rows by specifying the ``tags`` parameter.
|
||||
This allows you to filter your :class:`Dataset` later. This can be useful so separate rows of data based on
|
||||
This allows you to filter your :class:`Dataset` later. This can be useful to separate rows of data based on
|
||||
arbitrary criteria (*e.g.* origin) that you don't want to include in your :class:`Dataset`.
|
||||
|
||||
Let's tag some students. ::
|
||||
@@ -256,7 +273,7 @@ Let's tag some students. ::
|
||||
students.rpush(['Kenneth', 'Reitz'], tags=['male', 'technical'])
|
||||
students.rpush(['Bessie', 'Monke'], tags=['female', 'creative'])
|
||||
|
||||
Now that we have extra meta-data on our rows, we can use easily filter our :class:`Dataset`. Let's just see Male students. ::
|
||||
Now that we have extra meta-data on our rows, we can easily filter our :class:`Dataset`. Let's just see Male students. ::
|
||||
|
||||
|
||||
>>> students.filter(['male']).yaml
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
certifi==2017.7.27.1
|
||||
chardet==3.0.4
|
||||
et-xmlfile==1.0.1
|
||||
idna==2.6
|
||||
jdcal==1.3
|
||||
numpy==1.13.1
|
||||
odfpy==1.3.5
|
||||
openpyxl==2.4.8
|
||||
pandas==0.20.3
|
||||
pkginfo==1.4.1
|
||||
python-dateutil==2.6.1
|
||||
pytz==2017.2
|
||||
PyYAML==3.12
|
||||
requests==2.18.4
|
||||
requests-toolbelt==0.8.0
|
||||
six==1.10.0
|
||||
tqdm==4.15.0
|
||||
unicodecsv==0.14.1
|
||||
urllib3==1.22
|
||||
xlrd==1.1.0
|
||||
xlwt==1.3.0
|
||||
@@ -2,22 +2,13 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
import tablib
|
||||
|
||||
try:
|
||||
from setuptools import setup, find_packages
|
||||
from setuptools import setup
|
||||
except ImportError:
|
||||
from distutils.core import setup, find_packages
|
||||
|
||||
|
||||
packages = find_packages(exclude=('docs',))
|
||||
|
||||
if sys.version_info[:2] < (3,0):
|
||||
packages = [p for p in packages if '3' not in p]
|
||||
else:
|
||||
packages = [p for p in packages if '2' not in p]
|
||||
from distutils.core import setup
|
||||
|
||||
if sys.argv[-1] == 'publish':
|
||||
os.system("python setup.py sdist upload")
|
||||
@@ -30,7 +21,7 @@ if sys.argv[-1] == 'speedups':
|
||||
print('Pip required.')
|
||||
sys.exit(1)
|
||||
|
||||
os.system('pip install ujson pyyaml')
|
||||
os.system('pip install ujson')
|
||||
sys.exit()
|
||||
|
||||
if sys.argv[-1] == 'test':
|
||||
@@ -40,31 +31,54 @@ if sys.argv[-1] == 'test':
|
||||
print('py.test required.')
|
||||
sys.exit(1)
|
||||
|
||||
os.system('pytest test_tablib.py')
|
||||
sys.exit()
|
||||
errors = os.system('py.test test_tablib.py')
|
||||
sys.exit(bool(errors))
|
||||
|
||||
packages = [
|
||||
'tablib', 'tablib.formats',
|
||||
'tablib.packages',
|
||||
'tablib.packages.dbfpy',
|
||||
'tablib.packages.dbfpy3'
|
||||
]
|
||||
|
||||
install = [
|
||||
'odfpy',
|
||||
'openpyxl',
|
||||
'unicodecsv',
|
||||
'xlrd',
|
||||
'xlwt',
|
||||
'pyyaml',
|
||||
'pandas'
|
||||
]
|
||||
|
||||
|
||||
with open('tablib/core.py', 'r') as fd:
|
||||
version = re.search(r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]',
|
||||
fd.read(), re.MULTILINE).group(1)
|
||||
|
||||
setup(
|
||||
name='tablib',
|
||||
version=tablib.__version__,
|
||||
version=version,
|
||||
description='Format agnostic tabular data library (XLS, JSON, YAML, CSV)',
|
||||
long_description=(open('README.rst').read() + '\n\n' +
|
||||
open('HISTORY.rst').read()),
|
||||
author='Kenneth Reitz',
|
||||
author_email='me@kennethreitz.com',
|
||||
url='http://tablib.org',
|
||||
author_email='me@kennethreitz.org',
|
||||
url='http://python-tablib.org',
|
||||
packages=packages,
|
||||
license='MIT',
|
||||
classifiers=(
|
||||
classifiers=[
|
||||
'Development Status :: 5 - Production/Stable',
|
||||
'Intended Audience :: Developers',
|
||||
'Natural Language :: English',
|
||||
'License :: OSI Approved :: MIT License',
|
||||
'Programming Language :: Python',
|
||||
'Programming Language :: Python :: 2.5',
|
||||
'Programming Language :: Python :: 2.6',
|
||||
'Programming Language :: Python :: 2.7',
|
||||
'Programming Language :: Python :: 3.0',
|
||||
'Programming Language :: Python :: 3.1',
|
||||
'Programming Language :: Python :: 3.2',
|
||||
),
|
||||
'Programming Language :: Python :: 3.3',
|
||||
'Programming Language :: Python :: 3.4',
|
||||
'Programming Language :: Python :: 3.5',
|
||||
'Programming Language :: Python :: 3.6',
|
||||
],
|
||||
tests_require=['pytest'],
|
||||
install_requires=install,
|
||||
)
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
""" Tablib. """
|
||||
|
||||
from tablib.core import (
|
||||
Databook, Dataset, detect, import_set,
|
||||
Databook, Dataset, detect_format, import_set, import_book,
|
||||
InvalidDatasetType, InvalidDimensions, UnsupportedFormat,
|
||||
__version__
|
||||
)
|
||||
|
||||
+7
-8
@@ -22,28 +22,27 @@ except ImportError:
|
||||
|
||||
if is_py3:
|
||||
from io import BytesIO
|
||||
import tablib.packages.xlwt3 as xlwt
|
||||
from tablib.packages import markup3 as markup
|
||||
from tablib.packages import openpyxl3 as openpyxl
|
||||
from tablib.packages.odf3 import opendocument, style, text, table
|
||||
import tablib.packages.dbfpy3 as dbfpy
|
||||
|
||||
import csv
|
||||
from io import StringIO
|
||||
# py3 mappings
|
||||
|
||||
ifilter = filter
|
||||
unicode = str
|
||||
bytes = bytes
|
||||
basestring = str
|
||||
xrange = range
|
||||
|
||||
else:
|
||||
from cStringIO import StringIO as BytesIO
|
||||
from cStringIO import StringIO
|
||||
import tablib.packages.xlwt as xlwt
|
||||
from tablib.packages import markup
|
||||
from itertools import ifilter
|
||||
from tablib.packages import openpyxl
|
||||
from tablib.packages.odf import opendocument, style, text, table
|
||||
|
||||
from tablib.packages import unicodecsv as csv
|
||||
import unicodecsv as csv
|
||||
import tablib.packages.dbfpy as dbfpy
|
||||
|
||||
unicode = unicode
|
||||
unicode = unicode
|
||||
xrange = xrange
|
||||
|
||||
+300
-76
@@ -5,7 +5,7 @@
|
||||
|
||||
This module implements the central Tablib objects.
|
||||
|
||||
:copyright: (c) 2011 by Kenneth Reitz.
|
||||
:copyright: (c) 2016 by Kenneth Reitz.
|
||||
:license: MIT, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
@@ -14,22 +14,22 @@ from operator import itemgetter
|
||||
|
||||
from tablib import formats
|
||||
|
||||
from tablib.compat import OrderedDict
|
||||
from tablib.compat import OrderedDict, unicode
|
||||
|
||||
|
||||
__title__ = 'tablib'
|
||||
__version__ = '0.9.11'
|
||||
__build__ = 0x000911
|
||||
__version__ = '0.12.0'
|
||||
__build__ = 0x001200
|
||||
__author__ = 'Kenneth Reitz'
|
||||
__license__ = 'MIT'
|
||||
__copyright__ = 'Copyright 2011 Kenneth Reitz'
|
||||
__copyright__ = 'Copyright 2017 Kenneth Reitz'
|
||||
__docformat__ = 'restructuredtext'
|
||||
|
||||
|
||||
class Row(object):
|
||||
"""Internal Row object. Mainly used for filtering."""
|
||||
|
||||
__slots__ = ['tuple', '_row', 'tags']
|
||||
__slots__ = ['_row', 'tags']
|
||||
|
||||
def __init__(self, row=list(), tags=list()):
|
||||
self._row = list(row)
|
||||
@@ -45,7 +45,7 @@ class Row(object):
|
||||
return repr(self._row)
|
||||
|
||||
def __getslice__(self, i, j):
|
||||
return self._row[i,j]
|
||||
return self._row[i:j]
|
||||
|
||||
def __getitem__(self, i):
|
||||
return self._row[i]
|
||||
@@ -57,7 +57,14 @@ class Row(object):
|
||||
del self._row[i]
|
||||
|
||||
def __getstate__(self):
|
||||
return {'slot': [getattr(self, slot) for slot in self.__slots__]}
|
||||
|
||||
slots = dict()
|
||||
|
||||
for slot in self.__slots__:
|
||||
attribute = getattr(self, slot)
|
||||
slots[slot] = attribute
|
||||
|
||||
return slots
|
||||
|
||||
def __setstate__(self, state):
|
||||
for (k, v) in list(state.items()): setattr(self, k, v)
|
||||
@@ -105,7 +112,7 @@ class Dataset(object):
|
||||
functionality.
|
||||
|
||||
Usually you create a :class:`Dataset` instance in your main module, and append
|
||||
rows and columns as you collect data. ::
|
||||
rows as you collect data. ::
|
||||
|
||||
data = tablib.Dataset()
|
||||
data.headers = ('name', 'age')
|
||||
@@ -113,17 +120,30 @@ class Dataset(object):
|
||||
for (name, age) in some_collector():
|
||||
data.append((name, age))
|
||||
|
||||
You can also set rows and headers upon instantiation. This is useful if dealing
|
||||
with dozens or hundres of :class:`Dataset` objects. ::
|
||||
|
||||
Setting columns is similar. The column data length must equal the
|
||||
current height of the data and headers must be set ::
|
||||
|
||||
data = tablib.Dataset()
|
||||
data.headers = ('first_name', 'last_name')
|
||||
|
||||
data.append(('John', 'Adams'))
|
||||
data.append(('George', 'Washington'))
|
||||
|
||||
data.append_col((90, 67), header='age')
|
||||
|
||||
|
||||
You can also set rows and headers upon instantiation. This is useful if
|
||||
dealing with dozens or hundreds of :class:`Dataset` objects. ::
|
||||
|
||||
headers = ('first_name', 'last_name')
|
||||
data = [('John', 'Adams'), ('George', 'Washington')]
|
||||
|
||||
data = tablib.Dataset(*data, headers=headers)
|
||||
|
||||
|
||||
:param \*args: (optional) list of rows to populate Dataset
|
||||
:param headers: (optional) list strings for Dataset header row
|
||||
:param title: (optional) string to use as title of the Dataset
|
||||
|
||||
|
||||
.. admonition:: Format Attributes Definition
|
||||
@@ -134,6 +154,8 @@ class Dataset(object):
|
||||
|
||||
"""
|
||||
|
||||
_formats = {}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self._data = list(Row(arg) for arg in args)
|
||||
self.__headers = None
|
||||
@@ -144,15 +166,9 @@ class Dataset(object):
|
||||
# (column, callback) tuples
|
||||
self._formatters = []
|
||||
|
||||
try:
|
||||
self.headers = kwargs['headers']
|
||||
except KeyError:
|
||||
self.headers = None
|
||||
self.headers = kwargs.get('headers')
|
||||
|
||||
try:
|
||||
self.title = kwargs['title']
|
||||
except KeyError:
|
||||
self.title = None
|
||||
self.title = kwargs.get('title')
|
||||
|
||||
self._register_formats()
|
||||
|
||||
@@ -162,7 +178,7 @@ class Dataset(object):
|
||||
|
||||
|
||||
def __getitem__(self, key):
|
||||
if isinstance(key, str):
|
||||
if isinstance(key, str) or isinstance(key, unicode):
|
||||
if key in self.headers:
|
||||
pos = self.headers.index(key) # get 'key' index from each data
|
||||
return [row[pos] for row in self._data]
|
||||
@@ -175,14 +191,13 @@ class Dataset(object):
|
||||
else:
|
||||
return [result.tuple for result in _results]
|
||||
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self._validate(value)
|
||||
self._data[key] = Row(value)
|
||||
|
||||
|
||||
def __delitem__(self, key):
|
||||
if isinstance(key, str):
|
||||
if isinstance(key, str) or isinstance(key, unicode):
|
||||
|
||||
if key in self.headers:
|
||||
|
||||
@@ -205,6 +220,29 @@ class Dataset(object):
|
||||
except AttributeError:
|
||||
return '<dataset object>'
|
||||
|
||||
def __unicode__(self):
|
||||
result = []
|
||||
|
||||
# Add unicode representation of headers.
|
||||
if self.__headers:
|
||||
result.append([unicode(h) for h in self.__headers])
|
||||
|
||||
# Add unicode representation of rows.
|
||||
result.extend(list(map(unicode, row)) for row in self._data)
|
||||
|
||||
lens = [list(map(len, row)) for row in result]
|
||||
field_lens = list(map(max, zip(*lens)))
|
||||
|
||||
# delimiter between header and data
|
||||
if self.__headers:
|
||||
result.insert(1, ['-' * length for length in field_lens])
|
||||
|
||||
format_string = '|'.join('{%s:%s}' % item for item in enumerate(field_lens))
|
||||
|
||||
return '\n'.join(format_string.format(*row) for row in result)
|
||||
|
||||
def __str__(self):
|
||||
return self.__unicode__()
|
||||
|
||||
# ---------
|
||||
# Internals
|
||||
@@ -217,11 +255,16 @@ class Dataset(object):
|
||||
try:
|
||||
try:
|
||||
setattr(cls, fmt.title, property(fmt.export_set, fmt.import_set))
|
||||
setattr(cls, 'get_%s' % fmt.title, fmt.export_set)
|
||||
setattr(cls, 'set_%s' % fmt.title, fmt.import_set)
|
||||
cls._formats[fmt.title] = (fmt.export_set, fmt.import_set)
|
||||
except AttributeError:
|
||||
setattr(cls, fmt.title, property(fmt.export_set))
|
||||
setattr(cls, 'get_%s' % fmt.title, fmt.export_set)
|
||||
cls._formats[fmt.title] = (fmt.export_set, None)
|
||||
|
||||
except AttributeError:
|
||||
pass
|
||||
cls._formats[fmt.title] = (None, None)
|
||||
|
||||
|
||||
def _validate(self, row=None, col=None, safety=False):
|
||||
@@ -312,7 +355,7 @@ class Dataset(object):
|
||||
A dataset object can also be imported by setting the `Dataset.dict` attribute: ::
|
||||
|
||||
data = tablib.Dataset()
|
||||
data.json = '[{"last_name": "Adams","age": 90,"first_name": "John"}]'
|
||||
data.dict = [{'age': 90, 'first_name': 'Kenneth', 'last_name': 'Reitz'}]
|
||||
|
||||
"""
|
||||
return self._package()
|
||||
@@ -392,11 +435,41 @@ class Dataset(object):
|
||||
return 0
|
||||
|
||||
|
||||
def load(self, in_stream, format=None, **kwargs):
|
||||
"""
|
||||
Import `in_stream` to the :class:`Dataset` object using the `format`.
|
||||
|
||||
:param \*\*kwargs: (optional) custom configuration to the format `import_set`.
|
||||
"""
|
||||
|
||||
if not format:
|
||||
format = detect_format(in_stream)
|
||||
|
||||
export_set, import_set = self._formats.get(format, (None, None))
|
||||
if not import_set:
|
||||
raise UnsupportedFormat('Format {0} cannot be imported.'.format(format))
|
||||
|
||||
import_set(self, in_stream, **kwargs)
|
||||
return self
|
||||
|
||||
|
||||
|
||||
def export(self, format, **kwargs):
|
||||
"""
|
||||
Export :class:`Dataset` object to `format`.
|
||||
|
||||
:param \*\*kwargs: (optional) custom configuration to the format `export_set`.
|
||||
"""
|
||||
export_set, import_set = self._formats.get(format, (None, None))
|
||||
if not export_set:
|
||||
raise UnsupportedFormat('Format {0} cannot be exported.'.format(format))
|
||||
|
||||
return export_set(self, **kwargs)
|
||||
|
||||
# -------
|
||||
# Formats
|
||||
# -------
|
||||
|
||||
|
||||
@property
|
||||
def xls():
|
||||
"""A Legacy Excel Spreadsheet representation of the :class:`Dataset` object, with :ref:`separators`. Cannot be set.
|
||||
@@ -410,7 +483,7 @@ class Dataset(object):
|
||||
:class:`Dataset.xls` contains binary data, so make sure to write in binary mode::
|
||||
|
||||
with open('output.xls', 'wb') as f:
|
||||
f.write(data.xls)'
|
||||
f.write(data.xls)
|
||||
"""
|
||||
pass
|
||||
|
||||
@@ -423,7 +496,7 @@ class Dataset(object):
|
||||
:class:`Dataset.xlsx` contains binary data, so make sure to write in binary mode::
|
||||
|
||||
with open('output.xlsx', 'wb') as f:
|
||||
f.write(data.xlsx)'
|
||||
f.write(data.xlsx)
|
||||
"""
|
||||
pass
|
||||
|
||||
@@ -433,10 +506,10 @@ class Dataset(object):
|
||||
|
||||
.. admonition:: Binary Warning
|
||||
|
||||
:class:`Dataset.xlsx` contains binary data, so make sure to write in binary mode::
|
||||
:class:`Dataset.ods` contains binary data, so make sure to write in binary mode::
|
||||
|
||||
with open('output.ods', 'wb') as f:
|
||||
f.write(data.ods)'
|
||||
f.write(data.ods)
|
||||
"""
|
||||
pass
|
||||
|
||||
@@ -452,6 +525,17 @@ class Dataset(object):
|
||||
data.csv = 'age, first_name, last_name\\n90, John, Adams'
|
||||
|
||||
Import assumes (for now) that headers exist.
|
||||
|
||||
.. admonition:: Binary Warning
|
||||
|
||||
:class:`Dataset.csv` uses \\r\\n line endings by default, so make
|
||||
sure to write in binary mode::
|
||||
|
||||
with open('output.csv', 'wb') as f:
|
||||
f.write(data.csv)
|
||||
|
||||
If you do not do this, and you export the file on Windows, your
|
||||
CSV file will open in Excel with a blank line between each row.
|
||||
"""
|
||||
pass
|
||||
|
||||
@@ -477,7 +561,7 @@ class Dataset(object):
|
||||
set, a YAML list of objects will be returned. If no headers have
|
||||
been set, a YAML list of lists (rows) will be returned instead.
|
||||
|
||||
A dataset object can also be imported by setting the :class:`Dataset.json` attribute: ::
|
||||
A dataset object can also be imported by setting the :class:`Dataset.yaml` attribute: ::
|
||||
|
||||
data = tablib.Dataset()
|
||||
data.yaml = '- {age: 90, first_name: John, last_name: Adams}'
|
||||
@@ -486,6 +570,18 @@ class Dataset(object):
|
||||
"""
|
||||
pass
|
||||
|
||||
@property
|
||||
def df():
|
||||
"""A DataFrame representation of the :class:`Dataset` object.
|
||||
|
||||
A dataset object can also be imported by setting the :class:`Dataset.df` attribute: ::
|
||||
|
||||
data = tablib.Dataset()
|
||||
data.df = DataFrame(np.random.randn(6,4))
|
||||
|
||||
Import assumes (for now) that headers exist.
|
||||
"""
|
||||
pass
|
||||
|
||||
@property
|
||||
def json():
|
||||
@@ -496,10 +592,11 @@ class Dataset(object):
|
||||
A dataset object can also be imported by setting the :class:`Dataset.json` attribute: ::
|
||||
|
||||
data = tablib.Dataset()
|
||||
data.json = '[{age: 90, first_name: "John", liast_name: "Adams"}]'
|
||||
data.json = '[{"age": 90, "first_name": "John", "last_name": "Adams"}]'
|
||||
|
||||
Import assumes (for now) that headers exist.
|
||||
"""
|
||||
pass
|
||||
|
||||
@property
|
||||
def html():
|
||||
@@ -510,6 +607,40 @@ class Dataset(object):
|
||||
"""
|
||||
pass
|
||||
|
||||
@property
|
||||
def dbf():
|
||||
"""A dBASE representation of the :class:`Dataset` object.
|
||||
|
||||
A dataset object can also be imported by setting the
|
||||
:class:`Dataset.dbf` attribute. ::
|
||||
|
||||
# To import data from an existing DBF file:
|
||||
data = tablib.Dataset()
|
||||
data.dbf = open('existing_table.dbf').read()
|
||||
|
||||
# to import data from an ASCII-encoded bytestring:
|
||||
data = tablib.Dataset()
|
||||
data.dbf = '<bytestring of tabular data>'
|
||||
|
||||
.. admonition:: Binary Warning
|
||||
|
||||
:class:`Dataset.dbf` contains binary data, so make sure to write in binary mode::
|
||||
|
||||
with open('output.dbf', 'wb') as f:
|
||||
f.write(data.dbf)
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
@property
|
||||
def latex():
|
||||
"""A LaTeX booktabs representation of the :class:`Dataset` object. If a
|
||||
title has been set, it will be exported as the table caption.
|
||||
|
||||
.. note:: This method can be used for export only.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
# ----
|
||||
# Rows
|
||||
@@ -518,30 +649,11 @@ class Dataset(object):
|
||||
def insert(self, index, row, tags=list()):
|
||||
"""Inserts a row to the :class:`Dataset` at the given index.
|
||||
|
||||
Rows and columns inserted must be the correct size (height or width).
|
||||
Rows inserted must be the correct size (height or width).
|
||||
|
||||
The default behaviour is to insert the given row to the :class:`Dataset`
|
||||
object at the given index. If the ``col`` parameter is given, however,
|
||||
a new column will be insert to the :class:`Dataset` object instead.
|
||||
|
||||
You can also insert a column of a single callable object, which will
|
||||
add a new column with the return values of the callable each as an
|
||||
item in the column. ::
|
||||
|
||||
data.append(col=random.randint)
|
||||
|
||||
See :ref:`dyncols` for an in-depth example.
|
||||
|
||||
.. versionchanged:: 0.9.0
|
||||
If inserting a column, and :class:`Dataset.headers` is set, the
|
||||
header attribute must be set, and will be considered the header for
|
||||
that row.
|
||||
|
||||
.. versionadded:: 0.9.0
|
||||
If inserting a row, you can add :ref:`tags <tags>` to the row you are inserting.
|
||||
This gives you the ability to :class:`filter <Dataset.filter>` your
|
||||
:class:`Dataset` later.
|
||||
"""
|
||||
object at the given index.
|
||||
"""
|
||||
|
||||
self._validate(row)
|
||||
self._data.insert(index, Row(row, tags=tags))
|
||||
@@ -570,6 +682,14 @@ class Dataset(object):
|
||||
|
||||
self.rpush(row, tags)
|
||||
|
||||
def extend(self, rows, tags=list()):
|
||||
"""Adds a list of rows to the :class:`Dataset` using
|
||||
:class:`Dataset.append`
|
||||
"""
|
||||
|
||||
for row in rows:
|
||||
self.append(row, tags)
|
||||
|
||||
|
||||
def lpop(self):
|
||||
"""Removes and returns the first row of the :class:`Dataset`."""
|
||||
@@ -615,8 +735,22 @@ class Dataset(object):
|
||||
that row.
|
||||
|
||||
See :ref:`dyncols` for an in-depth example.
|
||||
|
||||
.. versionchanged:: 0.9.0
|
||||
If inserting a column, and :class:`Dataset.headers` is set, the
|
||||
header attribute must be set, and will be considered the header for
|
||||
that row.
|
||||
|
||||
.. versionadded:: 0.9.0
|
||||
If inserting a row, you can add :ref:`tags <tags>` to the row you are inserting.
|
||||
This gives you the ability to :class:`filter <Dataset.filter>` your
|
||||
:class:`Dataset` later.
|
||||
|
||||
"""
|
||||
|
||||
if col is None:
|
||||
col = []
|
||||
|
||||
# Callable Columns...
|
||||
if hasattr(col, '__call__'):
|
||||
col = list(map(col, self._data))
|
||||
@@ -628,8 +762,14 @@ class Dataset(object):
|
||||
# pop the first item off, add to headers
|
||||
if not header:
|
||||
raise HeadersNeeded()
|
||||
|
||||
# corner case - if header is set without data
|
||||
elif header and self.height == 0 and len(col):
|
||||
raise InvalidDimensions
|
||||
|
||||
self.headers.insert(index, header)
|
||||
|
||||
|
||||
if self.height and self.width:
|
||||
|
||||
for i, row in enumerate(self._data):
|
||||
@@ -684,6 +824,12 @@ class Dataset(object):
|
||||
self.rpush_col(col, header)
|
||||
|
||||
|
||||
def get_col(self, index):
|
||||
"""Returns the column from the :class:`Dataset` at the given index."""
|
||||
|
||||
return [row[index] for row in self._data]
|
||||
|
||||
|
||||
# ----
|
||||
# Misc
|
||||
# ----
|
||||
@@ -730,13 +876,13 @@ class Dataset(object):
|
||||
sorted.
|
||||
"""
|
||||
|
||||
if isinstance(col, str):
|
||||
if isinstance(col, str) or isinstance(col, unicode):
|
||||
|
||||
if not self.headers:
|
||||
raise HeadersNeeded
|
||||
|
||||
_sorted = sorted(self.dict, key=itemgetter(col), reverse=reverse)
|
||||
_dset = Dataset(headers=self.headers)
|
||||
_dset = Dataset(headers=self.headers, title=self.title)
|
||||
|
||||
for item in _sorted:
|
||||
row = [item[key] for key in self.headers]
|
||||
@@ -747,7 +893,7 @@ class Dataset(object):
|
||||
col = self.headers[col]
|
||||
|
||||
_sorted = sorted(self.dict, key=itemgetter(col), reverse=reverse)
|
||||
_dset = Dataset(headers=self.headers)
|
||||
_dset = Dataset(headers=self.headers, title=self.title)
|
||||
|
||||
for item in _sorted:
|
||||
if self.headers:
|
||||
@@ -775,17 +921,17 @@ class Dataset(object):
|
||||
new_headers = [self.headers[0]] + self[self.headers[0]]
|
||||
|
||||
_dset.headers = new_headers
|
||||
for column in self.headers:
|
||||
for index, column in enumerate(self.headers):
|
||||
|
||||
if column == self.headers[0]:
|
||||
# It's in the headers, so skip it
|
||||
continue
|
||||
|
||||
# Adding the column name as now they're a regular column
|
||||
row_data = [column] + self[column]
|
||||
# Use `get_col(index)` in case there are repeated values
|
||||
row_data = [column] + self.get_col(index)
|
||||
row_data = Row(row_data)
|
||||
_dset.append(row=row_data)
|
||||
|
||||
return _dset
|
||||
|
||||
|
||||
@@ -846,17 +992,66 @@ class Dataset(object):
|
||||
return _dset
|
||||
|
||||
|
||||
def remove_duplicates(self):
|
||||
"""Removes all duplicate rows from the :class:`Dataset` object
|
||||
while maintaining the original order."""
|
||||
seen = set()
|
||||
self._data[:] = [row for row in self._data if not (tuple(row) in seen or seen.add(tuple(row)))]
|
||||
|
||||
|
||||
def wipe(self):
|
||||
"""Removes all content and headers from the :class:`Dataset` object."""
|
||||
self._data = list()
|
||||
self.__headers = None
|
||||
|
||||
|
||||
def subset(self, rows=None, cols=None):
|
||||
"""Returns a new instance of the :class:`Dataset`,
|
||||
including only specified rows and columns.
|
||||
"""
|
||||
|
||||
# Don't return if no data
|
||||
if not self:
|
||||
return
|
||||
|
||||
if rows is None:
|
||||
rows = list(range(self.height))
|
||||
|
||||
if cols is None:
|
||||
cols = list(self.headers)
|
||||
|
||||
#filter out impossible rows and columns
|
||||
rows = [row for row in rows if row in range(self.height)]
|
||||
cols = [header for header in cols if header in self.headers]
|
||||
|
||||
_dset = Dataset()
|
||||
|
||||
#filtering rows and columns
|
||||
_dset.headers = list(cols)
|
||||
|
||||
_dset._data = []
|
||||
for row_no, row in enumerate(self._data):
|
||||
data_row = []
|
||||
for key in _dset.headers:
|
||||
if key in self.headers:
|
||||
pos = self.headers.index(key)
|
||||
data_row.append(row[pos])
|
||||
else:
|
||||
raise KeyError
|
||||
|
||||
if row_no in rows:
|
||||
_dset.append(row=Row(data_row))
|
||||
|
||||
return _dset
|
||||
|
||||
|
||||
|
||||
class Databook(object):
|
||||
"""A book of :class:`Dataset` objects.
|
||||
"""
|
||||
|
||||
_formats = {}
|
||||
|
||||
def __init__(self, sets=None):
|
||||
|
||||
if sets is None:
|
||||
@@ -872,7 +1067,6 @@ class Databook(object):
|
||||
except AttributeError:
|
||||
return '<databook object>'
|
||||
|
||||
|
||||
def wipe(self):
|
||||
"""Removes all :class:`Dataset` objects from the :class:`Databook`."""
|
||||
self._datasets = []
|
||||
@@ -885,16 +1079,20 @@ class Databook(object):
|
||||
try:
|
||||
try:
|
||||
setattr(cls, fmt.title, property(fmt.export_book, fmt.import_book))
|
||||
cls._formats[fmt.title] = (fmt.export_book, fmt.import_book)
|
||||
except AttributeError:
|
||||
setattr(cls, fmt.title, property(fmt.export_book))
|
||||
cls._formats[fmt.title] = (fmt.export_book, None)
|
||||
|
||||
except AttributeError:
|
||||
pass
|
||||
cls._formats[fmt.title] = (None, None)
|
||||
|
||||
def sheets(self):
|
||||
return self._datasets
|
||||
|
||||
def add_sheet(self, dataset):
|
||||
"""Adds given :class:`Dataset` to the :class:`Databook`."""
|
||||
if type(dataset) is Dataset:
|
||||
if isinstance(dataset, Dataset):
|
||||
self._datasets.append(dataset)
|
||||
else:
|
||||
raise InvalidDatasetType
|
||||
@@ -922,29 +1120,55 @@ class Databook(object):
|
||||
"""The number of the :class:`Dataset` objects within :class:`Databook`."""
|
||||
return len(self._datasets)
|
||||
|
||||
def load(self, format, in_stream, **kwargs):
|
||||
"""
|
||||
Import `in_stream` to the :class:`Databook` object using the `format`.
|
||||
|
||||
def detect(stream):
|
||||
"""Return (format, stream) of given stream."""
|
||||
:param \*\*kwargs: (optional) custom configuration to the format `import_book`.
|
||||
"""
|
||||
|
||||
if not format:
|
||||
format = detect_format(in_stream)
|
||||
|
||||
export_book, import_book = self._formats.get(format, (None, None))
|
||||
if not import_book:
|
||||
raise UnsupportedFormat('Format {0} cannot be loaded.'.format(format))
|
||||
|
||||
import_book(self, in_stream, **kwargs)
|
||||
return self
|
||||
|
||||
def export(self, format, **kwargs):
|
||||
"""
|
||||
Export :class:`Databook` object to `format`.
|
||||
|
||||
:param \*\*kwargs: (optional) custom configuration to the format `export_book`.
|
||||
"""
|
||||
export_book, import_book = self._formats.get(format, (None, None))
|
||||
if not export_book:
|
||||
raise UnsupportedFormat('Format {0} cannot be exported.'.format(format))
|
||||
|
||||
return export_book(self, **kwargs)
|
||||
|
||||
|
||||
def detect_format(stream):
|
||||
"""Return format name of given stream."""
|
||||
for fmt in formats.available:
|
||||
try:
|
||||
if fmt.detect(stream):
|
||||
return (fmt, stream)
|
||||
return fmt.title
|
||||
except AttributeError:
|
||||
pass
|
||||
return (None, stream)
|
||||
|
||||
|
||||
def import_set(stream):
|
||||
def import_set(stream, format=None, **kwargs):
|
||||
"""Return dataset of given stream."""
|
||||
(format, stream) = detect(stream)
|
||||
|
||||
try:
|
||||
data = Dataset()
|
||||
format.import_set(data, stream)
|
||||
return data
|
||||
return Dataset().load(stream, format, **kwargs)
|
||||
|
||||
except AttributeError:
|
||||
return None
|
||||
|
||||
def import_book(stream, format=None, **kwargs):
|
||||
"""Return dataset of given stream."""
|
||||
|
||||
return Databook().load(stream, format, **kwargs)
|
||||
|
||||
|
||||
class InvalidDatasetType(Exception):
|
||||
|
||||
@@ -11,5 +11,8 @@ from . import _tsv as tsv
|
||||
from . import _html as html
|
||||
from . import _xlsx as xlsx
|
||||
from . import _ods as ods
|
||||
from . import _dbf as dbf
|
||||
from . import _latex as latex
|
||||
from . import _df as df
|
||||
|
||||
available = (json, xls, yaml, csv, tsv, html, xlsx, ods)
|
||||
available = (json, xls, yaml, csv, dbf, tsv, html, latex, xlsx, ods, df)
|
||||
|
||||
+19
-17
@@ -1,27 +1,28 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
""" Tablib - CSV Support.
|
||||
""" Tablib - *SV Support.
|
||||
"""
|
||||
|
||||
from tablib.compat import is_py3, csv, StringIO
|
||||
|
||||
|
||||
title = 'csv'
|
||||
extentions = ('csv',)
|
||||
extensions = ('csv',)
|
||||
|
||||
|
||||
DEFAULT_ENCODING = 'utf-8'
|
||||
DEFAULT_DELIMITER = ','
|
||||
|
||||
|
||||
|
||||
def export_set(dataset):
|
||||
def export_set(dataset, **kwargs):
|
||||
"""Returns CSV representation of Dataset."""
|
||||
stream = StringIO()
|
||||
|
||||
if is_py3:
|
||||
_csv = csv.writer(stream)
|
||||
else:
|
||||
_csv = csv.writer(stream, encoding=DEFAULT_ENCODING)
|
||||
kwargs.setdefault('delimiter', DEFAULT_DELIMITER)
|
||||
if not is_py3:
|
||||
kwargs.setdefault('encoding', DEFAULT_ENCODING)
|
||||
|
||||
_csv = csv.writer(stream, **kwargs)
|
||||
|
||||
for row in dataset._package(dicts=False):
|
||||
_csv.writerow(row)
|
||||
@@ -29,15 +30,16 @@ def export_set(dataset):
|
||||
return stream.getvalue()
|
||||
|
||||
|
||||
def import_set(dset, in_stream, headers=True):
|
||||
def import_set(dset, in_stream, headers=True, **kwargs):
|
||||
"""Returns dataset from CSV stream."""
|
||||
|
||||
dset.wipe()
|
||||
|
||||
if is_py3:
|
||||
rows = csv.reader(in_stream.splitlines())
|
||||
else:
|
||||
rows = csv.reader(in_stream.splitlines(), encoding=DEFAULT_ENCODING)
|
||||
kwargs.setdefault('delimiter', DEFAULT_DELIMITER)
|
||||
if not is_py3:
|
||||
kwargs.setdefault('encoding', DEFAULT_ENCODING)
|
||||
|
||||
rows = csv.reader(StringIO(in_stream), **kwargs)
|
||||
for i, row in enumerate(rows):
|
||||
|
||||
if (i == 0) and (headers):
|
||||
@@ -46,10 +48,10 @@ def import_set(dset, in_stream, headers=True):
|
||||
dset.append(row)
|
||||
|
||||
|
||||
def detect(stream):
|
||||
def detect(stream, delimiter=DEFAULT_DELIMITER):
|
||||
"""Returns True if given stream is valid CSV."""
|
||||
try:
|
||||
csv.Sniffer().sniff(stream)
|
||||
csv.Sniffer().sniff(stream, delimiters=delimiter)
|
||||
return True
|
||||
except csv.Error:
|
||||
return False
|
||||
except (csv.Error, TypeError):
|
||||
return False
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
""" Tablib - DBF Support.
|
||||
"""
|
||||
import tempfile
|
||||
import struct
|
||||
import os
|
||||
|
||||
from tablib.compat import StringIO
|
||||
from tablib.compat import dbfpy
|
||||
from tablib.compat import is_py3
|
||||
|
||||
if is_py3:
|
||||
from tablib.packages.dbfpy3 import dbf
|
||||
from tablib.packages.dbfpy3 import dbfnew
|
||||
from tablib.packages.dbfpy3 import record as dbfrecord
|
||||
import io
|
||||
else:
|
||||
from tablib.packages.dbfpy import dbf
|
||||
from tablib.packages.dbfpy import dbfnew
|
||||
from tablib.packages.dbfpy import record as dbfrecord
|
||||
|
||||
|
||||
title = 'dbf'
|
||||
extensions = ('csv',)
|
||||
|
||||
DEFAULT_ENCODING = 'utf-8'
|
||||
|
||||
def export_set(dataset):
|
||||
"""Returns DBF representation of a Dataset"""
|
||||
new_dbf = dbfnew.dbf_new()
|
||||
temp_file, temp_uri = tempfile.mkstemp()
|
||||
|
||||
# create the appropriate fields based on the contents of the first row
|
||||
first_row = dataset[0]
|
||||
for fieldname, field_value in zip(dataset.headers, first_row):
|
||||
if type(field_value) in [int, float]:
|
||||
new_dbf.add_field(fieldname, 'N', 10, 8)
|
||||
else:
|
||||
new_dbf.add_field(fieldname, 'C', 80)
|
||||
|
||||
new_dbf.write(temp_uri)
|
||||
|
||||
dbf_file = dbf.Dbf(temp_uri, readOnly=0)
|
||||
for row in dataset:
|
||||
record = dbfrecord.DbfRecord(dbf_file)
|
||||
for fieldname, field_value in zip(dataset.headers, row):
|
||||
record[fieldname] = field_value
|
||||
record.store()
|
||||
|
||||
dbf_file.close()
|
||||
dbf_stream = open(temp_uri, 'rb')
|
||||
if is_py3:
|
||||
stream = io.BytesIO(dbf_stream.read())
|
||||
else:
|
||||
stream = StringIO(dbf_stream.read())
|
||||
dbf_stream.close()
|
||||
os.close(temp_file)
|
||||
os.remove(temp_uri)
|
||||
return stream.getvalue()
|
||||
|
||||
def import_set(dset, in_stream, headers=True):
|
||||
"""Returns a dataset from a DBF stream."""
|
||||
|
||||
dset.wipe()
|
||||
if is_py3:
|
||||
_dbf = dbf.Dbf(io.BytesIO(in_stream))
|
||||
else:
|
||||
_dbf = dbf.Dbf(StringIO(in_stream))
|
||||
dset.headers = _dbf.fieldNames
|
||||
for record in range(_dbf.recordCount):
|
||||
row = [_dbf[record][f] for f in _dbf.fieldNames]
|
||||
dset.append(row)
|
||||
|
||||
def detect(stream):
|
||||
"""Returns True if the given stream is valid DBF"""
|
||||
#_dbf = dbf.Table(StringIO(stream))
|
||||
try:
|
||||
if is_py3:
|
||||
if type(stream) is not bytes:
|
||||
stream = bytes(stream, 'utf-8')
|
||||
_dbf = dbf.Dbf(io.BytesIO(stream), readOnly=True)
|
||||
else:
|
||||
_dbf = dbf.Dbf(StringIO(stream), readOnly=True)
|
||||
return True
|
||||
except (ValueError, struct.error):
|
||||
# When we try to open up a file that's not a DBF, dbfpy raises a
|
||||
# ValueError.
|
||||
# When unpacking a string argument with less than 8 chars, struct.error is
|
||||
# raised.
|
||||
return False
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
""" Tablib - DataFrame Support.
|
||||
"""
|
||||
|
||||
|
||||
import sys
|
||||
|
||||
|
||||
if sys.version_info[0] > 2:
|
||||
from io import BytesIO
|
||||
else:
|
||||
from cStringIO import StringIO as BytesIO
|
||||
|
||||
from pandas import DataFrame
|
||||
|
||||
import tablib
|
||||
|
||||
from tablib.compat import unicode
|
||||
|
||||
title = 'df'
|
||||
extensions = ('df', )
|
||||
|
||||
def detect(stream):
|
||||
"""Returns True if given stream is a DataFrame."""
|
||||
try:
|
||||
DataFrame(stream)
|
||||
return True
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
|
||||
def export_set(dset, index=None):
|
||||
"""Returns DataFrame representation of DataBook."""
|
||||
dataframe = DataFrame(dset.dict, columns=dset.headers)
|
||||
return dataframe
|
||||
|
||||
|
||||
def import_set(dset, in_stream):
|
||||
"""Returns dataset from DataFrame."""
|
||||
dset.wipe()
|
||||
dset.dict = in_stream.to_dict(orient='records')
|
||||
+36
-26
@@ -5,56 +5,66 @@
|
||||
|
||||
import sys
|
||||
|
||||
|
||||
if sys.version_info[0] > 2:
|
||||
from io import StringIO
|
||||
from io import BytesIO as StringIO
|
||||
from tablib.packages import markup3 as markup
|
||||
else:
|
||||
from cStringIO import StringIO
|
||||
from tablib.packages import markup
|
||||
|
||||
import tablib
|
||||
from tablib.compat import unicode
|
||||
import codecs
|
||||
|
||||
BOOK_ENDINGS = 'h3'
|
||||
|
||||
title = 'html'
|
||||
extentions = ('html', )
|
||||
extensions = ('html', )
|
||||
|
||||
|
||||
def export_set(dataset):
|
||||
"""HTML representation of a Dataset."""
|
||||
"""HTML representation of a Dataset."""
|
||||
|
||||
stream = StringIO()
|
||||
stream = StringIO()
|
||||
|
||||
page = markup.page()
|
||||
page.table.open()
|
||||
page = markup.page()
|
||||
page.table.open()
|
||||
|
||||
if dataset.headers is not None:
|
||||
page.thead.open()
|
||||
headers = markup.oneliner.th(dataset.headers)
|
||||
page.tr(headers)
|
||||
page.thead.close()
|
||||
if dataset.headers is not None:
|
||||
new_header = [item if item is not None else '' for item in dataset.headers]
|
||||
|
||||
for row in dataset:
|
||||
html_row = markup.oneliner.td(row)
|
||||
page.tr(html_row)
|
||||
page.thead.open()
|
||||
headers = markup.oneliner.th(new_header)
|
||||
page.tr(headers)
|
||||
page.thead.close()
|
||||
|
||||
page.table.close()
|
||||
for row in dataset:
|
||||
new_row = [item if item is not None else '' for item in row]
|
||||
|
||||
stream.writelines(str(page))
|
||||
html_row = markup.oneliner.td(new_row)
|
||||
page.tr(html_row)
|
||||
|
||||
return stream.getvalue()
|
||||
page.table.close()
|
||||
|
||||
# Allow unicode characters in output
|
||||
wrapper = codecs.getwriter("utf8")(stream)
|
||||
wrapper.writelines(unicode(page))
|
||||
|
||||
return stream.getvalue().decode('utf-8')
|
||||
|
||||
|
||||
def export_book(databook):
|
||||
"""HTML representation of a Databook."""
|
||||
"""HTML representation of a Databook."""
|
||||
|
||||
stream = StringIO()
|
||||
stream = StringIO()
|
||||
|
||||
for i, dset in enumerate(databook._datasets):
|
||||
title = (dset.title if dset.title else 'Set %s' % (i))
|
||||
stream.write('<%s>%s</%s>\n' % (BOOK_ENDINGS, title, BOOK_ENDINGS))
|
||||
stream.write(dset.html)
|
||||
stream.write('\n')
|
||||
# Allow unicode characters in output
|
||||
wrapper = codecs.getwriter("utf8")(stream)
|
||||
|
||||
return stream.getvalue()
|
||||
for i, dset in enumerate(databook._datasets):
|
||||
title = (dset.title if dset.title else 'Set %s' % (i))
|
||||
wrapper.write('<%s>%s</%s>\n' % (BOOK_ENDINGS, title, BOOK_ENDINGS))
|
||||
wrapper.write(dset.html)
|
||||
wrapper.write('\n')
|
||||
|
||||
return stream.getvalue().decode('utf-8')
|
||||
|
||||
+18
-6
@@ -2,25 +2,37 @@
|
||||
|
||||
""" Tablib - JSON Support
|
||||
"""
|
||||
import decimal
|
||||
|
||||
import tablib
|
||||
|
||||
import sys
|
||||
from tablib.packages import omnijson as json
|
||||
|
||||
try:
|
||||
import ujson as json
|
||||
except ImportError:
|
||||
import json
|
||||
|
||||
title = 'json'
|
||||
extentions = ('json', 'jsn')
|
||||
extensions = ('json', 'jsn')
|
||||
|
||||
|
||||
def date_handler(obj):
|
||||
if isinstance(obj, decimal.Decimal):
|
||||
return str(obj)
|
||||
elif hasattr(obj, 'isoformat'):
|
||||
return obj.isoformat()
|
||||
else:
|
||||
return obj
|
||||
# return obj.isoformat() if hasattr(obj, 'isoformat') else obj
|
||||
|
||||
|
||||
def export_set(dataset):
|
||||
"""Returns JSON representation of Dataset."""
|
||||
return json.dumps(dataset.dict)
|
||||
return json.dumps(dataset.dict, default=date_handler)
|
||||
|
||||
|
||||
def export_book(databook):
|
||||
"""Returns JSON representation of Databook."""
|
||||
return json.dumps(databook._package())
|
||||
return json.dumps(databook._package(), default=date_handler)
|
||||
|
||||
|
||||
def import_set(dset, in_stream):
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""Tablib - LaTeX table export support.
|
||||
|
||||
Generates a LaTeX booktabs-style table from the dataset.
|
||||
"""
|
||||
import re
|
||||
|
||||
from tablib.compat import unicode
|
||||
|
||||
title = 'latex'
|
||||
extensions = ('tex',)
|
||||
|
||||
TABLE_TEMPLATE = """\
|
||||
%% Note: add \\usepackage{booktabs} to your preamble
|
||||
%%
|
||||
\\begin{table}[!htbp]
|
||||
\\centering
|
||||
%(CAPTION)s
|
||||
\\begin{tabular}{%(COLSPEC)s}
|
||||
\\toprule
|
||||
%(HEADER)s
|
||||
%(MIDRULE)s
|
||||
%(BODY)s
|
||||
\\bottomrule
|
||||
\\end{tabular}
|
||||
\\end{table}
|
||||
"""
|
||||
|
||||
TEX_RESERVED_SYMBOLS_MAP = dict([
|
||||
('\\', '\\textbackslash{}'),
|
||||
('{', '\\{'),
|
||||
('}', '\\}'),
|
||||
('$', '\\$'),
|
||||
('&', '\\&'),
|
||||
('#', '\\#'),
|
||||
('^', '\\textasciicircum{}'),
|
||||
('_', '\\_'),
|
||||
('~', '\\textasciitilde{}'),
|
||||
('%', '\\%'),
|
||||
])
|
||||
|
||||
TEX_RESERVED_SYMBOLS_RE = re.compile(
|
||||
'(%s)' % '|'.join(map(re.escape, TEX_RESERVED_SYMBOLS_MAP.keys())))
|
||||
|
||||
|
||||
def export_set(dataset):
|
||||
"""Returns LaTeX representation of dataset
|
||||
|
||||
:param dataset: dataset to serialize
|
||||
:type dataset: tablib.core.Dataset
|
||||
"""
|
||||
|
||||
caption = '\\caption{%s}' % dataset.title if dataset.title else '%'
|
||||
colspec = _colspec(dataset.width)
|
||||
header = _serialize_row(dataset.headers) if dataset.headers else ''
|
||||
midrule = _midrule(dataset.width)
|
||||
body = '\n'.join([_serialize_row(row) for row in dataset])
|
||||
return TABLE_TEMPLATE % dict(CAPTION=caption, COLSPEC=colspec,
|
||||
HEADER=header, MIDRULE=midrule, BODY=body)
|
||||
|
||||
|
||||
def _colspec(dataset_width):
|
||||
"""Generates the column specification for the LaTeX `tabular` environment
|
||||
based on the dataset width.
|
||||
|
||||
The first column is justified to the left, all further columns are aligned
|
||||
to the right.
|
||||
|
||||
.. note:: This is only a heuristic and most probably has to be fine-tuned
|
||||
post export. Column alignment should depend on the data type, e.g., textual
|
||||
content should usually be aligned to the left while numeric content almost
|
||||
always should be aligned to the right.
|
||||
|
||||
:param dataset_width: width of the dataset
|
||||
"""
|
||||
|
||||
spec = 'l'
|
||||
for _ in range(1, dataset_width):
|
||||
spec += 'r'
|
||||
return spec
|
||||
|
||||
|
||||
def _midrule(dataset_width):
|
||||
"""Generates the table `midrule`, which may be composed of several
|
||||
`cmidrules`.
|
||||
|
||||
:param dataset_width: width of the dataset to serialize
|
||||
"""
|
||||
|
||||
if not dataset_width or dataset_width == 1:
|
||||
return '\\midrule'
|
||||
return ' '.join([_cmidrule(colindex, dataset_width) for colindex in
|
||||
range(1, dataset_width + 1)])
|
||||
|
||||
|
||||
def _cmidrule(colindex, dataset_width):
|
||||
"""Generates the `cmidrule` for a single column with appropriate trimming
|
||||
based on the column position.
|
||||
|
||||
:param colindex: Column index
|
||||
:param dataset_width: width of the dataset
|
||||
"""
|
||||
|
||||
rule = '\\cmidrule(%s){%d-%d}'
|
||||
if colindex == 1:
|
||||
# Rule of first column is trimmed on the right
|
||||
return rule % ('r', colindex, colindex)
|
||||
if colindex == dataset_width:
|
||||
# Rule of last column is trimmed on the left
|
||||
return rule % ('l', colindex, colindex)
|
||||
# Inner columns are trimmed on the left and right
|
||||
return rule % ('lr', colindex, colindex)
|
||||
|
||||
|
||||
def _serialize_row(row):
|
||||
"""Returns string representation of a single row.
|
||||
|
||||
:param row: single dataset row
|
||||
"""
|
||||
|
||||
new_row = [_escape_tex_reserved_symbols(unicode(item)) if item else '' for
|
||||
item in row]
|
||||
return 6 * ' ' + ' & '.join(new_row) + ' \\\\'
|
||||
|
||||
|
||||
def _escape_tex_reserved_symbols(input):
|
||||
"""Escapes all TeX reserved symbols ('_', '~', etc.) in a string.
|
||||
|
||||
:param input: String to escape
|
||||
"""
|
||||
def replace(match):
|
||||
return TEX_RESERVED_SYMBOLS_MAP[match.group()]
|
||||
return TEX_RESERVED_SYMBOLS_RE.sub(replace, input)
|
||||
+4
-11
@@ -3,18 +3,11 @@
|
||||
""" Tablib - ODF Support.
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
|
||||
if sys.version_info[0] > 2:
|
||||
from io import BytesIO
|
||||
else:
|
||||
from cStringIO import StringIO as BytesIO
|
||||
|
||||
from tablib.compat import opendocument, style, table, text, unicode
|
||||
from odf import opendocument, style, table, text
|
||||
from tablib.compat import BytesIO, unicode
|
||||
|
||||
title = 'ods'
|
||||
extentions = ('ods',)
|
||||
extensions = ('ods',)
|
||||
|
||||
bold = style.Style(name="bold", family="paragraph")
|
||||
bold.addElement(style.TextProperties(fontweight="bold", fontweightasian="bold", fontweightcomplex="bold"))
|
||||
@@ -97,4 +90,4 @@ def dset_sheet(dataset, ws):
|
||||
ws.addElement(odf_row)
|
||||
cell = table.TableCell()
|
||||
cell.addElement(text.P(text=col))
|
||||
odf_row.addElement(cell)
|
||||
odf_row.addElement(cell)
|
||||
|
||||
+11
-40
@@ -3,57 +3,28 @@
|
||||
""" Tablib - TSV (Tab Separated Values) Support.
|
||||
"""
|
||||
|
||||
from tablib.compat import is_py3, csv, StringIO
|
||||
|
||||
|
||||
from tablib.formats._csv import (
|
||||
export_set as export_set_wrapper,
|
||||
import_set as import_set_wrapper,
|
||||
detect as detect_wrapper,
|
||||
)
|
||||
|
||||
title = 'tsv'
|
||||
extentions = ('tsv',)
|
||||
extensions = ('tsv',)
|
||||
|
||||
DEFAULT_ENCODING = 'utf-8'
|
||||
DELIMITER = '\t'
|
||||
|
||||
def export_set(dataset):
|
||||
"""Returns a TSV representation of Dataset."""
|
||||
|
||||
stream = StringIO()
|
||||
|
||||
if is_py3:
|
||||
_tsv = csv.writer(stream, delimiter='\t')
|
||||
else:
|
||||
_tsv = csv.writer(stream, encoding=DEFAULT_ENCODING, delimiter='\t')
|
||||
|
||||
for row in dataset._package(dicts=False):
|
||||
_tsv.writerow(row)
|
||||
|
||||
return stream.getvalue()
|
||||
"""Returns TSV representation of Dataset."""
|
||||
return export_set_wrapper(dataset, delimiter=DELIMITER)
|
||||
|
||||
|
||||
def import_set(dset, in_stream, headers=True):
|
||||
"""Returns dataset from TSV stream."""
|
||||
|
||||
dset.wipe()
|
||||
|
||||
if is_py3:
|
||||
rows = csv.reader(in_stream.split('\r\n'), delimiter='\t')
|
||||
else:
|
||||
rows = csv.reader(in_stream.split('\r\n'), delimiter='\t',
|
||||
encoding=DEFAULT_ENCODING)
|
||||
|
||||
for i, row in enumerate(rows):
|
||||
# Skip empty rows
|
||||
if not row:
|
||||
continue
|
||||
|
||||
if (i == 0) and (headers):
|
||||
dset.headers = row
|
||||
else:
|
||||
dset.append(row)
|
||||
return import_set_wrapper(dset, in_stream, headers=headers, delimiter=DELIMITER)
|
||||
|
||||
|
||||
def detect(stream):
|
||||
"""Returns True if given stream is valid TSV."""
|
||||
try:
|
||||
csv.Sniffer().sniff(stream, delimiters='\t')
|
||||
return True
|
||||
except csv.Error:
|
||||
return False
|
||||
return detect_wrapper(stream, delimiter=DELIMITER)
|
||||
|
||||
+61
-5
@@ -5,17 +5,39 @@
|
||||
|
||||
import sys
|
||||
|
||||
from tablib.compat import BytesIO, xlwt
|
||||
|
||||
from tablib.compat import BytesIO, xrange
|
||||
import tablib
|
||||
import xlrd
|
||||
import xlwt
|
||||
from xlrd.biffh import XLRDError
|
||||
|
||||
title = 'xls'
|
||||
extentions = ('xls',)
|
||||
extensions = ('xls',)
|
||||
|
||||
# special styles
|
||||
wrap = xlwt.easyxf("alignment: wrap on")
|
||||
bold = xlwt.easyxf("font: bold on")
|
||||
|
||||
|
||||
def detect(stream):
|
||||
"""Returns True if given stream is a readable excel file."""
|
||||
try:
|
||||
xlrd.open_workbook(file_contents=stream)
|
||||
return True
|
||||
except (TypeError, XLRDError):
|
||||
pass
|
||||
try:
|
||||
xlrd.open_workbook(file_contents=stream.read())
|
||||
return True
|
||||
except (AttributeError, XLRDError):
|
||||
pass
|
||||
try:
|
||||
xlrd.open_workbook(filename=stream)
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
|
||||
def export_set(dataset):
|
||||
"""Returns XLS representation of Dataset."""
|
||||
|
||||
@@ -45,6 +67,42 @@ def export_book(databook):
|
||||
return stream.getvalue()
|
||||
|
||||
|
||||
def import_set(dset, in_stream, headers=True):
|
||||
"""Returns databook from XLS stream."""
|
||||
|
||||
dset.wipe()
|
||||
|
||||
xls_book = xlrd.open_workbook(file_contents=in_stream)
|
||||
sheet = xls_book.sheet_by_index(0)
|
||||
|
||||
dset.title = sheet.name
|
||||
|
||||
for i in xrange(sheet.nrows):
|
||||
if (i == 0) and (headers):
|
||||
dset.headers = sheet.row_values(0)
|
||||
else:
|
||||
dset.append(sheet.row_values(i))
|
||||
|
||||
def import_book(dbook, in_stream, headers=True):
|
||||
"""Returns databook from XLS stream."""
|
||||
|
||||
dbook.wipe()
|
||||
|
||||
xls_book = xlrd.open_workbook(file_contents=in_stream)
|
||||
|
||||
for sheet in xls_book.sheets():
|
||||
data = tablib.Dataset()
|
||||
data.title = sheet.name
|
||||
|
||||
for i in xrange(sheet.nrows):
|
||||
if (i == 0) and (headers):
|
||||
data.headers = sheet.row_values(0)
|
||||
else:
|
||||
data.append(sheet.row_values(i))
|
||||
|
||||
dbook.add_sheet(data)
|
||||
|
||||
|
||||
def dset_sheet(dataset, ws):
|
||||
"""Completes given worksheet from given Dataset."""
|
||||
_package = dataset._package(dicts=False)
|
||||
@@ -78,5 +136,3 @@ def dset_sheet(dataset, ws):
|
||||
ws.write(i, j, col)
|
||||
except TypeError:
|
||||
ws.write(i, j, col)
|
||||
|
||||
|
||||
|
||||
+77
-29
@@ -11,50 +11,100 @@ if sys.version_info[0] > 2:
|
||||
else:
|
||||
from cStringIO import StringIO as BytesIO
|
||||
|
||||
from tablib.compat import openpyxl
|
||||
import openpyxl
|
||||
import tablib
|
||||
|
||||
Workbook = openpyxl.workbook.Workbook
|
||||
ExcelWriter = openpyxl.writer.excel.ExcelWriter
|
||||
get_column_letter = openpyxl.cell.get_column_letter
|
||||
get_column_letter = openpyxl.utils.get_column_letter
|
||||
|
||||
from tablib.compat import unicode
|
||||
|
||||
|
||||
title = 'xlsx'
|
||||
extentions = ('xlsx',)
|
||||
extensions = ('xlsx',)
|
||||
|
||||
def export_set(dataset):
|
||||
|
||||
def detect(stream):
|
||||
"""Returns True if given stream is a readable excel file."""
|
||||
try:
|
||||
openpyxl.reader.excel.load_workbook(stream)
|
||||
return True
|
||||
except openpyxl.shared.exc.InvalidFileException:
|
||||
pass
|
||||
|
||||
def export_set(dataset, freeze_panes=True):
|
||||
"""Returns XLSX representation of Dataset."""
|
||||
|
||||
wb = Workbook()
|
||||
ws = wb.worksheets[0]
|
||||
ws.title = dataset.title if dataset.title else 'Tablib Dataset'
|
||||
|
||||
dset_sheet(dataset, ws)
|
||||
dset_sheet(dataset, ws, freeze_panes=freeze_panes)
|
||||
|
||||
stream = BytesIO()
|
||||
wb.save(stream)
|
||||
return stream.getvalue()
|
||||
|
||||
|
||||
def export_book(databook):
|
||||
def export_book(databook, freeze_panes=True):
|
||||
"""Returns XLSX representation of DataBook."""
|
||||
|
||||
wb = Workbook()
|
||||
ew = ExcelWriter(workbook = wb)
|
||||
for sheet in wb.worksheets:
|
||||
wb.remove_sheet(sheet)
|
||||
for i, dset in enumerate(databook._datasets):
|
||||
ws = wb.create_sheet()
|
||||
ws.title = dset.title if dset.title else 'Sheet%s' % (i)
|
||||
|
||||
dset_sheet(dset, ws)
|
||||
dset_sheet(dset, ws, freeze_panes=freeze_panes)
|
||||
|
||||
|
||||
stream = BytesIO()
|
||||
ew.save(stream)
|
||||
wb.save(stream)
|
||||
return stream.getvalue()
|
||||
|
||||
|
||||
def dset_sheet(dataset, ws):
|
||||
def import_set(dset, in_stream, headers=True):
|
||||
"""Returns databook from XLS stream."""
|
||||
|
||||
dset.wipe()
|
||||
|
||||
xls_book = openpyxl.reader.excel.load_workbook(BytesIO(in_stream))
|
||||
sheet = xls_book.get_active_sheet()
|
||||
|
||||
dset.title = sheet.title
|
||||
|
||||
for i, row in enumerate(sheet.rows):
|
||||
row_vals = [c.value for c in row]
|
||||
if (i == 0) and (headers):
|
||||
dset.headers = row_vals
|
||||
else:
|
||||
dset.append(row_vals)
|
||||
|
||||
|
||||
def import_book(dbook, in_stream, headers=True):
|
||||
"""Returns databook from XLS stream."""
|
||||
|
||||
dbook.wipe()
|
||||
|
||||
xls_book = openpyxl.reader.excel.load_workbook(BytesIO(in_stream))
|
||||
|
||||
for sheet in xls_book.worksheets:
|
||||
data = tablib.Dataset()
|
||||
data.title = sheet.title
|
||||
|
||||
for i, row in enumerate(sheet.rows):
|
||||
row_vals = [c.value for c in row]
|
||||
if (i == 0) and (headers):
|
||||
data.headers = row_vals
|
||||
else:
|
||||
data.append(row_vals)
|
||||
|
||||
dbook.add_sheet(data)
|
||||
|
||||
|
||||
def dset_sheet(dataset, ws, freeze_panes=True):
|
||||
"""Completes given worksheet from given Dataset."""
|
||||
_package = dataset._package(dicts=False)
|
||||
|
||||
@@ -62,40 +112,38 @@ def dset_sheet(dataset, ws):
|
||||
_offset = i
|
||||
_package.insert((sep[0] + _offset), (sep[1],))
|
||||
|
||||
bold = openpyxl.styles.Font(bold=True)
|
||||
wrap_text = openpyxl.styles.Alignment(wrap_text=True)
|
||||
|
||||
for i, row in enumerate(_package):
|
||||
row_number = i + 1
|
||||
for j, col in enumerate(row):
|
||||
col_idx = get_column_letter(j + 1)
|
||||
cell = ws.cell('%s%s' % (col_idx, row_number))
|
||||
|
||||
# bold headers
|
||||
if (row_number == 1) and dataset.headers:
|
||||
# ws.cell('%s%s'%(col_idx, row_number)).value = unicode(
|
||||
# '%s' % col, errors='ignore')
|
||||
ws.cell('%s%s'%(col_idx, row_number)).value = unicode(col)
|
||||
style = ws.get_style('%s%s' % (col_idx, row_number))
|
||||
style.font.bold = True
|
||||
ws.freeze_panes = '%s%s' % (col_idx, row_number)
|
||||
|
||||
|
||||
# cell.value = unicode('%s' % col, errors='ignore')
|
||||
cell.value = unicode(col)
|
||||
cell.font = bold
|
||||
if freeze_panes:
|
||||
# Export Freeze only after first Line
|
||||
ws.freeze_panes = 'A2'
|
||||
|
||||
# bold separators
|
||||
elif len(row) < dataset.width:
|
||||
ws.cell('%s%s'%(col_idx, row_number)).value = unicode(
|
||||
'%s' % col, errors='ignore')
|
||||
style = ws.get_style('%s%s' % (col_idx, row_number))
|
||||
style.font.bold = True
|
||||
cell.value = unicode('%s' % col, errors='ignore')
|
||||
cell.font = bold
|
||||
|
||||
# wrap the rest
|
||||
else:
|
||||
try:
|
||||
if '\n' in col:
|
||||
ws.cell('%s%s'%(col_idx, row_number)).value = unicode(
|
||||
'%s' % col, errors='ignore')
|
||||
style = ws.get_style('%s%s' % (col_idx, row_number))
|
||||
style.alignment.wrap_text
|
||||
cell.value = unicode('%s' % col, errors='ignore')
|
||||
cell.alignment = wrap_text
|
||||
else:
|
||||
ws.cell('%s%s'%(col_idx, row_number)).value = unicode(
|
||||
'%s' % col, errors='ignore')
|
||||
cell.value = unicode('%s' % col, errors='ignore')
|
||||
except TypeError:
|
||||
ws.cell('%s%s'%(col_idx, row_number)).value = unicode(col)
|
||||
cell.value = unicode(col)
|
||||
|
||||
|
||||
|
||||
+11
-22
@@ -3,42 +3,29 @@
|
||||
""" Tablib - YAML Support.
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
try:
|
||||
import yaml
|
||||
except ImportError:
|
||||
if sys.version_info[0] > 2:
|
||||
import tablib.packages.yaml3 as yaml
|
||||
else:
|
||||
import tablib.packages.yaml as yaml
|
||||
|
||||
|
||||
import tablib
|
||||
|
||||
|
||||
import yaml
|
||||
|
||||
title = 'yaml'
|
||||
extentions = ('yaml', 'yml')
|
||||
|
||||
extensions = ('yaml', 'yml')
|
||||
|
||||
|
||||
def export_set(dataset):
|
||||
"""Returns YAML representation of Dataset."""
|
||||
|
||||
return yaml.dump(dataset._package(ordered=False))
|
||||
return yaml.safe_dump(dataset._package(ordered=False))
|
||||
|
||||
|
||||
def export_book(databook):
|
||||
"""Returns YAML representation of Databook."""
|
||||
return yaml.dump(databook._package())
|
||||
return yaml.safe_dump(databook._package(ordered=False))
|
||||
|
||||
|
||||
def import_set(dset, in_stream):
|
||||
"""Returns dataset from YAML stream."""
|
||||
|
||||
dset.wipe()
|
||||
dset.dict = yaml.load(in_stream)
|
||||
dset.dict = yaml.safe_load(in_stream)
|
||||
|
||||
|
||||
def import_book(dbook, in_stream):
|
||||
@@ -46,19 +33,21 @@ def import_book(dbook, in_stream):
|
||||
|
||||
dbook.wipe()
|
||||
|
||||
for sheet in yaml.load(in_stream):
|
||||
for sheet in yaml.safe_load(in_stream):
|
||||
data = tablib.Dataset()
|
||||
data.title = sheet['title']
|
||||
data.dict = sheet['data']
|
||||
dbook.add_sheet(data)
|
||||
|
||||
|
||||
def detect(stream):
|
||||
"""Returns True if given stream is valid YAML."""
|
||||
try:
|
||||
_yaml = yaml.load(stream)
|
||||
_yaml = yaml.safe_load(stream)
|
||||
if isinstance(_yaml, (list, tuple, dict)):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
except yaml.parser.ParserError:
|
||||
return False
|
||||
except (yaml.parser.ParserError, yaml.reader.ReaderError,
|
||||
yaml.scanner.ScannerError):
|
||||
return False
|
||||
|
||||
@@ -0,0 +1,297 @@
|
||||
#! /usr/bin/env python
|
||||
"""DBF accessing helpers.
|
||||
|
||||
FIXME: more documentation needed
|
||||
|
||||
Examples:
|
||||
|
||||
Create new table, setup structure, add records:
|
||||
|
||||
dbf = Dbf(filename, new=True)
|
||||
dbf.addField(
|
||||
("NAME", "C", 15),
|
||||
("SURNAME", "C", 25),
|
||||
("INITIALS", "C", 10),
|
||||
("BIRTHDATE", "D"),
|
||||
)
|
||||
for (n, s, i, b) in (
|
||||
("John", "Miller", "YC", (1980, 10, 11)),
|
||||
("Andy", "Larkin", "", (1980, 4, 11)),
|
||||
):
|
||||
rec = dbf.newRecord()
|
||||
rec["NAME"] = n
|
||||
rec["SURNAME"] = s
|
||||
rec["INITIALS"] = i
|
||||
rec["BIRTHDATE"] = b
|
||||
rec.store()
|
||||
dbf.close()
|
||||
|
||||
Open existed dbf, read some data:
|
||||
|
||||
dbf = Dbf(filename, True)
|
||||
for rec in dbf:
|
||||
for fldName in dbf.fieldNames:
|
||||
print '%s:\t %s (%s)' % (fldName, rec[fldName],
|
||||
type(rec[fldName]))
|
||||
print
|
||||
dbf.close()
|
||||
|
||||
"""
|
||||
"""History (most recent first):
|
||||
11-feb-2007 [als] export INVALID_VALUE;
|
||||
Dbf: added .ignoreErrors, .INVALID_VALUE
|
||||
04-jul-2006 [als] added export declaration
|
||||
20-dec-2005 [yc] removed fromStream and newDbf methods:
|
||||
use argument of __init__ call must be used instead;
|
||||
added class fields pointing to the header and
|
||||
record classes.
|
||||
17-dec-2005 [yc] split to several modules; reimplemented
|
||||
13-dec-2005 [yc] adapted to the changes of the `strutil` module.
|
||||
13-sep-2002 [als] support FoxPro Timestamp datatype
|
||||
15-nov-1999 [jjk] documentation updates, add demo
|
||||
24-aug-1998 [jjk] add some encodeValue methods (not tested), other tweaks
|
||||
08-jun-1998 [jjk] fix problems, add more features
|
||||
20-feb-1998 [jjk] fix problems, add more features
|
||||
19-feb-1998 [jjk] add create/write capabilities
|
||||
18-feb-1998 [jjk] from dbfload.py
|
||||
"""
|
||||
|
||||
__version__ = "$Revision: 1.7 $"[11:-2]
|
||||
__date__ = "$Date: 2007/02/11 09:23:13 $"[7:-2]
|
||||
__author__ = "Jeff Kunce <kuncej@mail.conservation.state.mo.us>"
|
||||
|
||||
__all__ = ["Dbf"]
|
||||
|
||||
from . import header
|
||||
from . import record
|
||||
from utils import INVALID_VALUE
|
||||
|
||||
|
||||
class Dbf(object):
|
||||
"""DBF accessor.
|
||||
|
||||
FIXME:
|
||||
docs and examples needed (dont' forget to tell
|
||||
about problems adding new fields on the fly)
|
||||
|
||||
Implementation notes:
|
||||
``_new`` field is used to indicate whether this is
|
||||
a new data table. `addField` could be used only for
|
||||
the new tables! If at least one record was appended
|
||||
to the table it's structure couldn't be changed.
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ("name", "header", "stream",
|
||||
"_changed", "_new", "_ignore_errors")
|
||||
|
||||
HeaderClass = header.DbfHeader
|
||||
RecordClass = record.DbfRecord
|
||||
INVALID_VALUE = INVALID_VALUE
|
||||
|
||||
# initialization and creation helpers
|
||||
|
||||
def __init__(self, f, readOnly=False, new=False, ignoreErrors=False):
|
||||
"""Initialize instance.
|
||||
|
||||
Arguments:
|
||||
f:
|
||||
Filename or file-like object.
|
||||
new:
|
||||
True if new data table must be created. Assume
|
||||
data table exists if this argument is False.
|
||||
readOnly:
|
||||
if ``f`` argument is a string file will
|
||||
be opend in read-only mode; in other cases
|
||||
this argument is ignored. This argument is ignored
|
||||
even if ``new`` argument is True.
|
||||
headerObj:
|
||||
`header.DbfHeader` instance or None. If this argument
|
||||
is None, new empty header will be used with the
|
||||
all fields set by default.
|
||||
ignoreErrors:
|
||||
if set, failing field value conversion will return
|
||||
``INVALID_VALUE`` instead of raising conversion error.
|
||||
|
||||
"""
|
||||
if isinstance(f, basestring):
|
||||
# a filename
|
||||
self.name = f
|
||||
if new:
|
||||
# new table (table file must be
|
||||
# created or opened and truncated)
|
||||
self.stream = file(f, "w+b")
|
||||
else:
|
||||
# tabe file must exist
|
||||
self.stream = file(f, ("r+b", "rb")[bool(readOnly)])
|
||||
else:
|
||||
# a stream
|
||||
self.name = getattr(f, "name", "")
|
||||
self.stream = f
|
||||
if new:
|
||||
# if this is a new table, header will be empty
|
||||
self.header = self.HeaderClass()
|
||||
else:
|
||||
# or instantiated using stream
|
||||
self.header = self.HeaderClass.fromStream(self.stream)
|
||||
self.ignoreErrors = ignoreErrors
|
||||
self._new = bool(new)
|
||||
self._changed = False
|
||||
|
||||
# properties
|
||||
|
||||
closed = property(lambda self: self.stream.closed)
|
||||
recordCount = property(lambda self: self.header.recordCount)
|
||||
fieldNames = property(
|
||||
lambda self: [_fld.name for _fld in self.header.fields])
|
||||
fieldDefs = property(lambda self: self.header.fields)
|
||||
changed = property(lambda self: self._changed or self.header.changed)
|
||||
|
||||
def ignoreErrors(self, value):
|
||||
"""Update `ignoreErrors` flag on the header object and self"""
|
||||
self.header.ignoreErrors = self._ignore_errors = bool(value)
|
||||
|
||||
ignoreErrors = property(
|
||||
lambda self: self._ignore_errors,
|
||||
ignoreErrors,
|
||||
doc="""Error processing mode for DBF field value conversion
|
||||
|
||||
if set, failing field value conversion will return
|
||||
``INVALID_VALUE`` instead of raising conversion error.
|
||||
|
||||
""")
|
||||
|
||||
# protected methods
|
||||
|
||||
def _fixIndex(self, index):
|
||||
"""Return fixed index.
|
||||
|
||||
This method fails if index isn't a numeric object
|
||||
(long or int). Or index isn't in a valid range
|
||||
(less or equal to the number of records in the db).
|
||||
|
||||
If ``index`` is a negative number, it will be
|
||||
treated as a negative indexes for list objects.
|
||||
|
||||
Return:
|
||||
Return value is numeric object maning valid index.
|
||||
|
||||
"""
|
||||
if not isinstance(index, (int, long)):
|
||||
raise TypeError("Index must be a numeric object")
|
||||
if index < 0:
|
||||
# index from the right side
|
||||
# fix it to the left-side index
|
||||
index += len(self) + 1
|
||||
if index >= len(self):
|
||||
raise IndexError("Record index out of range")
|
||||
return index
|
||||
|
||||
# iterface methods
|
||||
|
||||
def close(self):
|
||||
self.flush()
|
||||
self.stream.close()
|
||||
|
||||
def flush(self):
|
||||
"""Flush data to the associated stream."""
|
||||
if self.changed:
|
||||
self.header.setCurrentDate()
|
||||
self.header.write(self.stream)
|
||||
self.stream.flush()
|
||||
self._changed = False
|
||||
|
||||
def indexOfFieldName(self, name):
|
||||
"""Index of field named ``name``."""
|
||||
# FIXME: move this to header class
|
||||
return self.header.fields.index(name)
|
||||
|
||||
def newRecord(self):
|
||||
"""Return new record, which belong to this table."""
|
||||
return self.RecordClass(self)
|
||||
|
||||
def append(self, record):
|
||||
"""Append ``record`` to the database."""
|
||||
record.index = self.header.recordCount
|
||||
record._write()
|
||||
self.header.recordCount += 1
|
||||
self._changed = True
|
||||
self._new = False
|
||||
|
||||
def addField(self, *defs):
|
||||
"""Add field definitions.
|
||||
|
||||
For more information see `header.DbfHeader.addField`.
|
||||
|
||||
"""
|
||||
if self._new:
|
||||
self.header.addField(*defs)
|
||||
else:
|
||||
raise TypeError("At least one record was added, "
|
||||
"structure can't be changed")
|
||||
|
||||
# 'magic' methods (representation and sequence interface)
|
||||
|
||||
def __repr__(self):
|
||||
return "Dbf stream '%s'\n" % self.stream + repr(self.header)
|
||||
|
||||
def __len__(self):
|
||||
"""Return number of records."""
|
||||
return self.recordCount
|
||||
|
||||
def __getitem__(self, index):
|
||||
"""Return `DbfRecord` instance."""
|
||||
return self.RecordClass.fromStream(self, self._fixIndex(index))
|
||||
|
||||
def __setitem__(self, index, record):
|
||||
"""Write `DbfRecord` instance to the stream."""
|
||||
record.index = self._fixIndex(index)
|
||||
record._write()
|
||||
self._changed = True
|
||||
self._new = False
|
||||
|
||||
# def __del__(self):
|
||||
# """Flush stream upon deletion of the object."""
|
||||
# self.flush()
|
||||
|
||||
|
||||
def demo_read(filename):
|
||||
_dbf = Dbf(filename, True)
|
||||
for _rec in _dbf:
|
||||
print
|
||||
print(repr(_rec))
|
||||
_dbf.close()
|
||||
|
||||
|
||||
def demo_create(filename):
|
||||
_dbf = Dbf(filename, new=True)
|
||||
_dbf.addField(
|
||||
("NAME", "C", 15),
|
||||
("SURNAME", "C", 25),
|
||||
("INITIALS", "C", 10),
|
||||
("BIRTHDATE", "D"),
|
||||
)
|
||||
for (_n, _s, _i, _b) in (
|
||||
("John", "Miller", "YC", (1981, 1, 2)),
|
||||
("Andy", "Larkin", "AL", (1982, 3, 4)),
|
||||
("Bill", "Clinth", "", (1983, 5, 6)),
|
||||
("Bobb", "McNail", "", (1984, 7, 8)),
|
||||
):
|
||||
_rec = _dbf.newRecord()
|
||||
_rec["NAME"] = _n
|
||||
_rec["SURNAME"] = _s
|
||||
_rec["INITIALS"] = _i
|
||||
_rec["BIRTHDATE"] = _b
|
||||
_rec.store()
|
||||
print(repr(_dbf))
|
||||
_dbf.close()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
|
||||
_name = len(sys.argv) > 1 and sys.argv[1] or "county.dbf"
|
||||
demo_create(_name)
|
||||
demo_read(_name)
|
||||
|
||||
# vim: set et sw=4 sts=4 :
|
||||
@@ -0,0 +1,189 @@
|
||||
#!/usr/bin/python
|
||||
""".DBF creation helpers.
|
||||
|
||||
Note: this is a legacy interface. New code should use Dbf class
|
||||
for table creation (see examples in dbf.py)
|
||||
|
||||
TODO:
|
||||
- handle Memo fields.
|
||||
- check length of the fields accoring to the
|
||||
`http://www.clicketyclick.dk/databases/xbase/format/data_types.html`
|
||||
|
||||
"""
|
||||
"""History (most recent first)
|
||||
04-jul-2006 [als] added export declaration;
|
||||
updated for dbfpy 2.0
|
||||
15-dec-2005 [yc] define dbf_new.__slots__
|
||||
14-dec-2005 [yc] added vim modeline; retab'd; added doc-strings;
|
||||
dbf_new now is a new class (inherited from object)
|
||||
??-jun-2000 [--] added by Hans Fiby
|
||||
"""
|
||||
|
||||
__version__ = "$Revision: 1.4 $"[11:-2]
|
||||
__date__ = "$Date: 2006/07/04 08:18:18 $"[7:-2]
|
||||
|
||||
__all__ = ["dbf_new"]
|
||||
|
||||
from dbf import *
|
||||
from fields import *
|
||||
from header import *
|
||||
from record import *
|
||||
|
||||
|
||||
class _FieldDefinition(object):
|
||||
"""Field definition.
|
||||
|
||||
This is a simple structure, which contains ``name``, ``type``,
|
||||
``len``, ``dec`` and ``cls`` fields.
|
||||
|
||||
Objects also implement get/setitem magic functions, so fields
|
||||
could be accessed via sequence iterface, where 'name' has
|
||||
index 0, 'type' index 1, 'len' index 2, 'dec' index 3 and
|
||||
'cls' could be located at index 4.
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = "name", "type", "len", "dec", "cls"
|
||||
|
||||
# WARNING: be attentive - dictionaries are mutable!
|
||||
FLD_TYPES = {
|
||||
# type: (cls, len)
|
||||
"C": (DbfCharacterFieldDef, None),
|
||||
"N": (DbfNumericFieldDef, None),
|
||||
"L": (DbfLogicalFieldDef, 1),
|
||||
# FIXME: support memos
|
||||
# "M": (DbfMemoFieldDef),
|
||||
"D": (DbfDateFieldDef, 8),
|
||||
# FIXME: I'm not sure length should be 14 characters!
|
||||
# but temporary I use it, cuz date is 8 characters
|
||||
# and time 6 (hhmmss)
|
||||
"T": (DbfDateTimeFieldDef, 14),
|
||||
}
|
||||
|
||||
def __init__(self, name, type, len=None, dec=0):
|
||||
_cls, _len = self.FLD_TYPES[type]
|
||||
if _len is None:
|
||||
if len is None:
|
||||
raise ValueError("Field length must be defined")
|
||||
_len = len
|
||||
self.name = name
|
||||
self.type = type
|
||||
self.len = _len
|
||||
self.dec = dec
|
||||
self.cls = _cls
|
||||
|
||||
def getDbfField(self):
|
||||
"Return `DbfFieldDef` instance from the current definition."
|
||||
return self.cls(self.name, self.len, self.dec)
|
||||
|
||||
def appendToHeader(self, dbfh):
|
||||
"""Create a `DbfFieldDef` instance and append it to the dbf header.
|
||||
|
||||
Arguments:
|
||||
dbfh: `DbfHeader` instance.
|
||||
|
||||
"""
|
||||
_dbff = self.getDbfField()
|
||||
dbfh.addField(_dbff)
|
||||
|
||||
|
||||
class dbf_new(object):
|
||||
"""New .DBF creation helper.
|
||||
|
||||
Example Usage:
|
||||
|
||||
dbfn = dbf_new()
|
||||
dbfn.add_field("name",'C',80)
|
||||
dbfn.add_field("price",'N',10,2)
|
||||
dbfn.add_field("date",'D',8)
|
||||
dbfn.write("tst.dbf")
|
||||
|
||||
Note:
|
||||
This module cannot handle Memo-fields,
|
||||
they are special.
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ("fields",)
|
||||
|
||||
FieldDefinitionClass = _FieldDefinition
|
||||
|
||||
def __init__(self):
|
||||
self.fields = []
|
||||
|
||||
def add_field(self, name, typ, len, dec=0):
|
||||
"""Add field definition.
|
||||
|
||||
Arguments:
|
||||
name:
|
||||
field name (str object). field name must not
|
||||
contain ASCII NULs and it's length shouldn't
|
||||
exceed 10 characters.
|
||||
typ:
|
||||
type of the field. this must be a single character
|
||||
from the "CNLMDT" set meaning character, numeric,
|
||||
logical, memo, date and date/time respectively.
|
||||
len:
|
||||
length of the field. this argument is used only for
|
||||
the character and numeric fields. all other fields
|
||||
have fixed length.
|
||||
FIXME: use None as a default for this argument?
|
||||
dec:
|
||||
decimal precision. used only for the numric fields.
|
||||
|
||||
"""
|
||||
self.fields.append(self.FieldDefinitionClass(name, typ, len, dec))
|
||||
|
||||
def write(self, filename):
|
||||
"""Create empty .DBF file using current structure."""
|
||||
_dbfh = DbfHeader()
|
||||
_dbfh.setCurrentDate()
|
||||
for _fldDef in self.fields:
|
||||
_fldDef.appendToHeader(_dbfh)
|
||||
_dbfStream = file(filename, "wb")
|
||||
_dbfh.write(_dbfStream)
|
||||
_dbfStream.close()
|
||||
|
||||
def write_stream(self, stream):
|
||||
_dbfh = DbfHeader()
|
||||
_dbfh.setCurrentDate()
|
||||
for _fldDef in self.fields:
|
||||
_fldDef.appendToHeader(_dbfh)
|
||||
_dbfh.write(stream)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# create a new DBF-File
|
||||
dbfn = dbf_new()
|
||||
dbfn.add_field("name", 'C', 80)
|
||||
dbfn.add_field("price", 'N', 10, 2)
|
||||
dbfn.add_field("date", 'D', 8)
|
||||
dbfn.write("tst.dbf")
|
||||
# test new dbf
|
||||
print "*** created tst.dbf: ***"
|
||||
dbft = Dbf('tst.dbf', readOnly=0)
|
||||
print repr(dbft)
|
||||
# add a record
|
||||
rec = DbfRecord(dbft)
|
||||
rec['name'] = 'something'
|
||||
rec['price'] = 10.5
|
||||
rec['date'] = (2000, 1, 12)
|
||||
rec.store()
|
||||
# add another record
|
||||
rec = DbfRecord(dbft)
|
||||
rec['name'] = 'foo and bar'
|
||||
rec['price'] = 12234
|
||||
rec['date'] = (1992, 7, 15)
|
||||
rec.store()
|
||||
|
||||
# show the records
|
||||
print "*** inserted 2 records into tst.dbf: ***"
|
||||
print repr(dbft)
|
||||
for i1 in range(len(dbft)):
|
||||
rec = dbft[i1]
|
||||
for fldName in dbft.fieldNames:
|
||||
print '%s:\t %s' % (fldName, rec[fldName])
|
||||
print
|
||||
dbft.close()
|
||||
|
||||
# vim: set et sts=4 sw=4 :
|
||||
@@ -0,0 +1,466 @@
|
||||
"""DBF fields definitions.
|
||||
|
||||
TODO:
|
||||
- make memos work
|
||||
"""
|
||||
"""History (most recent first):
|
||||
26-may-2009 [als] DbfNumericFieldDef.decodeValue: strip zero bytes
|
||||
05-feb-2009 [als] DbfDateFieldDef.encodeValue: empty arg produces empty date
|
||||
16-sep-2008 [als] DbfNumericFieldDef decoding looks for decimal point
|
||||
in the value to select float or integer return type
|
||||
13-mar-2008 [als] check field name length in constructor
|
||||
11-feb-2007 [als] handle value conversion errors
|
||||
10-feb-2007 [als] DbfFieldDef: added .rawFromRecord()
|
||||
01-dec-2006 [als] Timestamp columns use None for empty values
|
||||
31-oct-2006 [als] support field types 'F' (float), 'I' (integer)
|
||||
and 'Y' (currency);
|
||||
automate export and registration of field classes
|
||||
04-jul-2006 [als] added export declaration
|
||||
10-mar-2006 [als] decode empty values for Date and Logical fields;
|
||||
show field name in errors
|
||||
10-mar-2006 [als] fix Numeric value decoding: according to spec,
|
||||
value always is string representation of the number;
|
||||
ensure that encoded Numeric value fits into the field
|
||||
20-dec-2005 [yc] use field names in upper case
|
||||
15-dec-2005 [yc] field definitions moved from `dbf`.
|
||||
"""
|
||||
|
||||
__version__ = "$Revision: 1.14 $"[11:-2]
|
||||
__date__ = "$Date: 2009/05/26 05:16:51 $"[7:-2]
|
||||
|
||||
__all__ = ["lookupFor",] # field classes added at the end of the module
|
||||
|
||||
import datetime
|
||||
import struct
|
||||
import sys
|
||||
|
||||
from . import utils
|
||||
|
||||
## abstract definitions
|
||||
|
||||
class DbfFieldDef(object):
|
||||
"""Abstract field definition.
|
||||
|
||||
Child classes must override ``type`` class attribute to provide datatype
|
||||
infromation of the field definition. For more info about types visit
|
||||
`http://www.clicketyclick.dk/databases/xbase/format/data_types.html`
|
||||
|
||||
Also child classes must override ``defaultValue`` field to provide
|
||||
default value for the field value.
|
||||
|
||||
If child class has fixed length ``length`` class attribute must be
|
||||
overriden and set to the valid value. None value means, that field
|
||||
isn't of fixed length.
|
||||
|
||||
Note: ``name`` field must not be changed after instantiation.
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ("name", "length", "decimalCount",
|
||||
"start", "end", "ignoreErrors")
|
||||
|
||||
# length of the field, None in case of variable-length field,
|
||||
# or a number if this field is a fixed-length field
|
||||
length = None
|
||||
|
||||
# field type. for more information about fields types visit
|
||||
# `http://www.clicketyclick.dk/databases/xbase/format/data_types.html`
|
||||
# must be overriden in child classes
|
||||
typeCode = None
|
||||
|
||||
# default value for the field. this field must be
|
||||
# overriden in child classes
|
||||
defaultValue = None
|
||||
|
||||
def __init__(self, name, length=None, decimalCount=None,
|
||||
start=None, stop=None, ignoreErrors=False,
|
||||
):
|
||||
"""Initialize instance."""
|
||||
assert self.typeCode is not None, "Type code must be overriden"
|
||||
assert self.defaultValue is not None, "Default value must be overriden"
|
||||
## fix arguments
|
||||
if len(name) >10:
|
||||
raise ValueError("Field name \"%s\" is too long" % name)
|
||||
name = str(name).upper()
|
||||
if self.__class__.length is None:
|
||||
if length is None:
|
||||
raise ValueError("[%s] Length isn't specified" % name)
|
||||
length = int(length)
|
||||
if length <= 0:
|
||||
raise ValueError("[%s] Length must be a positive integer"
|
||||
% name)
|
||||
else:
|
||||
length = self.length
|
||||
if decimalCount is None:
|
||||
decimalCount = 0
|
||||
## set fields
|
||||
self.name = name
|
||||
# FIXME: validate length according to the specification at
|
||||
# http://www.clicketyclick.dk/databases/xbase/format/data_types.html
|
||||
self.length = length
|
||||
self.decimalCount = decimalCount
|
||||
self.ignoreErrors = ignoreErrors
|
||||
self.start = start
|
||||
self.end = stop
|
||||
|
||||
def __cmp__(self, other):
|
||||
return cmp(self.name, str(other).upper())
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.name)
|
||||
|
||||
def fromString(cls, string, start, ignoreErrors=False):
|
||||
"""Decode dbf field definition from the string data.
|
||||
|
||||
Arguments:
|
||||
string:
|
||||
a string, dbf definition is decoded from. length of
|
||||
the string must be 32 bytes.
|
||||
start:
|
||||
position in the database file.
|
||||
ignoreErrors:
|
||||
initial error processing mode for the new field (boolean)
|
||||
|
||||
"""
|
||||
assert len(string) == 32
|
||||
_length = ord(string[16])
|
||||
return cls(utils.unzfill(string)[:11], _length, ord(string[17]),
|
||||
start, start + _length, ignoreErrors=ignoreErrors)
|
||||
fromString = classmethod(fromString)
|
||||
|
||||
def toString(self):
|
||||
"""Return encoded field definition.
|
||||
|
||||
Return:
|
||||
Return value is a string object containing encoded
|
||||
definition of this field.
|
||||
|
||||
"""
|
||||
if sys.version_info < (2, 4):
|
||||
# earlier versions did not support padding character
|
||||
_name = self.name[:11] + "\0" * (11 - len(self.name))
|
||||
else:
|
||||
_name = self.name.ljust(11, '\0')
|
||||
return (
|
||||
_name +
|
||||
self.typeCode +
|
||||
#data address
|
||||
chr(0) * 4 +
|
||||
chr(self.length) +
|
||||
chr(self.decimalCount) +
|
||||
chr(0) * 14
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return "%-10s %1s %3d %3d" % self.fieldInfo()
|
||||
|
||||
def fieldInfo(self):
|
||||
"""Return field information.
|
||||
|
||||
Return:
|
||||
Return value is a (name, type, length, decimals) tuple.
|
||||
|
||||
"""
|
||||
return (self.name, self.typeCode, self.length, self.decimalCount)
|
||||
|
||||
def rawFromRecord(self, record):
|
||||
"""Return a "raw" field value from the record string."""
|
||||
return record[self.start:self.end]
|
||||
|
||||
def decodeFromRecord(self, record):
|
||||
"""Return decoded field value from the record string."""
|
||||
try:
|
||||
return self.decodeValue(self.rawFromRecord(record))
|
||||
except:
|
||||
if self.ignoreErrors:
|
||||
return utils.INVALID_VALUE
|
||||
else:
|
||||
raise
|
||||
|
||||
def decodeValue(self, value):
|
||||
"""Return decoded value from string value.
|
||||
|
||||
This method shouldn't be used publicly. It's called from the
|
||||
`decodeFromRecord` method.
|
||||
|
||||
This is an abstract method and it must be overridden in child classes.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def encodeValue(self, value):
|
||||
"""Return str object containing encoded field value.
|
||||
|
||||
This is an abstract method and it must be overriden in child classes.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
## real classes
|
||||
|
||||
class DbfCharacterFieldDef(DbfFieldDef):
|
||||
"""Definition of the character field."""
|
||||
|
||||
typeCode = "C"
|
||||
defaultValue = ""
|
||||
|
||||
def decodeValue(self, value):
|
||||
"""Return string object.
|
||||
|
||||
Return value is a ``value`` argument with stripped right spaces.
|
||||
|
||||
"""
|
||||
return value.rstrip(" ")
|
||||
|
||||
def encodeValue(self, value):
|
||||
"""Return raw data string encoded from a ``value``."""
|
||||
return str(value)[:self.length].ljust(self.length)
|
||||
|
||||
|
||||
class DbfNumericFieldDef(DbfFieldDef):
|
||||
"""Definition of the numeric field."""
|
||||
|
||||
typeCode = "N"
|
||||
# XXX: now I'm not sure it was a good idea to make a class field
|
||||
# `defaultValue` instead of a generic method as it was implemented
|
||||
# previously -- it's ok with all types except number, cuz
|
||||
# if self.decimalCount is 0, we should return 0 and 0.0 otherwise.
|
||||
defaultValue = 0
|
||||
|
||||
def decodeValue(self, value):
|
||||
"""Return a number decoded from ``value``.
|
||||
|
||||
If decimals is zero, value will be decoded as an integer;
|
||||
or as a float otherwise.
|
||||
|
||||
Return:
|
||||
Return value is a int (long) or float instance.
|
||||
|
||||
"""
|
||||
value = value.strip(" \0")
|
||||
if "." in value:
|
||||
# a float (has decimal separator)
|
||||
return float(value)
|
||||
elif value:
|
||||
# must be an integer
|
||||
return int(value)
|
||||
else:
|
||||
return 0
|
||||
|
||||
def encodeValue(self, value):
|
||||
"""Return string containing encoded ``value``."""
|
||||
_rv = ("%*.*f" % (self.length, self.decimalCount, value))
|
||||
if len(_rv) > self.length:
|
||||
_ppos = _rv.find(".")
|
||||
if 0 <= _ppos <= self.length:
|
||||
_rv = _rv[:self.length]
|
||||
else:
|
||||
raise ValueError("[%s] Numeric overflow: %s (field width: %i)"
|
||||
% (self.name, _rv, self.length))
|
||||
return _rv
|
||||
|
||||
class DbfFloatFieldDef(DbfNumericFieldDef):
|
||||
"""Definition of the float field - same as numeric."""
|
||||
|
||||
typeCode = "F"
|
||||
|
||||
class DbfIntegerFieldDef(DbfFieldDef):
|
||||
"""Definition of the integer field."""
|
||||
|
||||
typeCode = "I"
|
||||
length = 4
|
||||
defaultValue = 0
|
||||
|
||||
def decodeValue(self, value):
|
||||
"""Return an integer number decoded from ``value``."""
|
||||
return struct.unpack("<i", value)[0]
|
||||
|
||||
def encodeValue(self, value):
|
||||
"""Return string containing encoded ``value``."""
|
||||
return struct.pack("<i", int(value))
|
||||
|
||||
class DbfCurrencyFieldDef(DbfFieldDef):
|
||||
"""Definition of the currency field."""
|
||||
|
||||
typeCode = "Y"
|
||||
length = 8
|
||||
defaultValue = 0.0
|
||||
|
||||
def decodeValue(self, value):
|
||||
"""Return float number decoded from ``value``."""
|
||||
return struct.unpack("<q", value)[0] / 10000.
|
||||
|
||||
def encodeValue(self, value):
|
||||
"""Return string containing encoded ``value``."""
|
||||
return struct.pack("<q", round(value * 10000))
|
||||
|
||||
class DbfLogicalFieldDef(DbfFieldDef):
|
||||
"""Definition of the logical field."""
|
||||
|
||||
typeCode = "L"
|
||||
defaultValue = -1
|
||||
length = 1
|
||||
|
||||
def decodeValue(self, value):
|
||||
"""Return True, False or -1 decoded from ``value``."""
|
||||
# Note: value always is 1-char string
|
||||
if value == "?":
|
||||
return -1
|
||||
if value in "NnFf ":
|
||||
return False
|
||||
if value in "YyTt":
|
||||
return True
|
||||
raise ValueError("[%s] Invalid logical value %r" % (self.name, value))
|
||||
|
||||
def encodeValue(self, value):
|
||||
"""Return a character from the "TF?" set.
|
||||
|
||||
Return:
|
||||
Return value is "T" if ``value`` is True
|
||||
"?" if value is -1 or False otherwise.
|
||||
|
||||
"""
|
||||
if value is True:
|
||||
return "T"
|
||||
if value == -1:
|
||||
return "?"
|
||||
return "F"
|
||||
|
||||
|
||||
class DbfMemoFieldDef(DbfFieldDef):
|
||||
"""Definition of the memo field.
|
||||
|
||||
Note: memos aren't currenly completely supported.
|
||||
|
||||
"""
|
||||
|
||||
typeCode = "M"
|
||||
defaultValue = " " * 10
|
||||
length = 10
|
||||
|
||||
def decodeValue(self, value):
|
||||
"""Return int .dbt block number decoded from the string object."""
|
||||
#return int(value)
|
||||
raise NotImplementedError
|
||||
|
||||
def encodeValue(self, value):
|
||||
"""Return raw data string encoded from a ``value``.
|
||||
|
||||
Note: this is an internal method.
|
||||
|
||||
"""
|
||||
#return str(value)[:self.length].ljust(self.length)
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class DbfDateFieldDef(DbfFieldDef):
|
||||
"""Definition of the date field."""
|
||||
|
||||
typeCode = "D"
|
||||
defaultValue = utils.classproperty(lambda cls: datetime.date.today())
|
||||
# "yyyymmdd" gives us 8 characters
|
||||
length = 8
|
||||
|
||||
def decodeValue(self, value):
|
||||
"""Return a ``datetime.date`` instance decoded from ``value``."""
|
||||
if value.strip():
|
||||
return utils.getDate(value)
|
||||
else:
|
||||
return None
|
||||
|
||||
def encodeValue(self, value):
|
||||
"""Return a string-encoded value.
|
||||
|
||||
``value`` argument should be a value suitable for the
|
||||
`utils.getDate` call.
|
||||
|
||||
Return:
|
||||
Return value is a string in format "yyyymmdd".
|
||||
|
||||
"""
|
||||
if value:
|
||||
return utils.getDate(value).strftime("%Y%m%d")
|
||||
else:
|
||||
return " " * self.length
|
||||
|
||||
|
||||
class DbfDateTimeFieldDef(DbfFieldDef):
|
||||
"""Definition of the timestamp field."""
|
||||
|
||||
# a difference between JDN (Julian Day Number)
|
||||
# and GDN (Gregorian Day Number). note, that GDN < JDN
|
||||
JDN_GDN_DIFF = 1721425
|
||||
typeCode = "T"
|
||||
defaultValue = utils.classproperty(lambda cls: datetime.datetime.now())
|
||||
# two 32-bits integers representing JDN and amount of
|
||||
# milliseconds respectively gives us 8 bytes.
|
||||
# note, that values must be encoded in LE byteorder.
|
||||
length = 8
|
||||
|
||||
def decodeValue(self, value):
|
||||
"""Return a `datetime.datetime` instance."""
|
||||
assert len(value) == self.length
|
||||
# LE byteorder
|
||||
_jdn, _msecs = struct.unpack("<2I", value)
|
||||
if _jdn >= 1:
|
||||
_rv = datetime.datetime.fromordinal(_jdn - self.JDN_GDN_DIFF)
|
||||
_rv += datetime.timedelta(0, _msecs / 1000.0)
|
||||
else:
|
||||
# empty date
|
||||
_rv = None
|
||||
return _rv
|
||||
|
||||
def encodeValue(self, value):
|
||||
"""Return a string-encoded ``value``."""
|
||||
if value:
|
||||
value = utils.getDateTime(value)
|
||||
# LE byteorder
|
||||
_rv = struct.pack("<2I", value.toordinal() + self.JDN_GDN_DIFF,
|
||||
(value.hour * 3600 + value.minute * 60 + value.second) * 1000)
|
||||
else:
|
||||
_rv = "\0" * self.length
|
||||
assert len(_rv) == self.length
|
||||
return _rv
|
||||
|
||||
|
||||
_fieldsRegistry = {}
|
||||
|
||||
def registerField(fieldCls):
|
||||
"""Register field definition class.
|
||||
|
||||
``fieldCls`` should be subclass of the `DbfFieldDef`.
|
||||
|
||||
Use `lookupFor` to retrieve field definition class
|
||||
by the type code.
|
||||
|
||||
"""
|
||||
assert fieldCls.typeCode is not None, "Type code isn't defined"
|
||||
# XXX: use fieldCls.typeCode.upper()? in case of any decign
|
||||
# don't forget to look to the same comment in ``lookupFor`` method
|
||||
_fieldsRegistry[fieldCls.typeCode] = fieldCls
|
||||
|
||||
|
||||
def lookupFor(typeCode):
|
||||
"""Return field definition class for the given type code.
|
||||
|
||||
``typeCode`` must be a single character. That type should be
|
||||
previously registered.
|
||||
|
||||
Use `registerField` to register new field class.
|
||||
|
||||
Return:
|
||||
Return value is a subclass of the `DbfFieldDef`.
|
||||
|
||||
"""
|
||||
# XXX: use typeCode.upper()? in case of any decign don't
|
||||
# forget to look to the same comment in ``registerField``
|
||||
return _fieldsRegistry[typeCode]
|
||||
|
||||
## register generic types
|
||||
|
||||
for (_name, _val) in globals().items():
|
||||
if isinstance(_val, type) and issubclass(_val, DbfFieldDef) \
|
||||
and (_name != "DbfFieldDef"):
|
||||
__all__.append(_name)
|
||||
registerField(_val)
|
||||
del _name, _val
|
||||
|
||||
# vim: et sts=4 sw=4 :
|
||||
@@ -0,0 +1,275 @@
|
||||
"""DBF header definition.
|
||||
|
||||
TODO:
|
||||
- handle encoding of the character fields
|
||||
(encoding information stored in the DBF header)
|
||||
|
||||
"""
|
||||
"""History (most recent first):
|
||||
16-sep-2010 [als] fromStream: fix century of the last update field
|
||||
11-feb-2007 [als] added .ignoreErrors
|
||||
10-feb-2007 [als] added __getitem__: return field definitions
|
||||
by field name or field number (zero-based)
|
||||
04-jul-2006 [als] added export declaration
|
||||
15-dec-2005 [yc] created
|
||||
"""
|
||||
|
||||
__version__ = "$Revision: 1.6 $"[11:-2]
|
||||
__date__ = "$Date: 2010/09/16 05:06:39 $"[7:-2]
|
||||
|
||||
__all__ = ["DbfHeader"]
|
||||
|
||||
try:
|
||||
import cStringIO
|
||||
except ImportError:
|
||||
# when we're in python3, we cStringIO has been replaced by io.StringIO
|
||||
import io as cStringIO
|
||||
import datetime
|
||||
import struct
|
||||
import time
|
||||
|
||||
from . import fields
|
||||
from . import utils
|
||||
|
||||
|
||||
class DbfHeader(object):
|
||||
"""Dbf header definition.
|
||||
|
||||
For more information about dbf header format visit
|
||||
`http://www.clicketyclick.dk/databases/xbase/format/dbf.html#DBF_STRUCT`
|
||||
|
||||
Examples:
|
||||
Create an empty dbf header and add some field definitions:
|
||||
dbfh = DbfHeader()
|
||||
dbfh.addField(("name", "C", 10))
|
||||
dbfh.addField(("date", "D"))
|
||||
dbfh.addField(DbfNumericFieldDef("price", 5, 2))
|
||||
Create a dbf header with field definitions:
|
||||
dbfh = DbfHeader([
|
||||
("name", "C", 10),
|
||||
("date", "D"),
|
||||
DbfNumericFieldDef("price", 5, 2),
|
||||
])
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ("signature", "fields", "lastUpdate", "recordLength",
|
||||
"recordCount", "headerLength", "changed", "_ignore_errors")
|
||||
|
||||
## instance construction and initialization methods
|
||||
|
||||
def __init__(self, fields=None, headerLength=0, recordLength=0,
|
||||
recordCount=0, signature=0x03, lastUpdate=None, ignoreErrors=False,
|
||||
):
|
||||
"""Initialize instance.
|
||||
|
||||
Arguments:
|
||||
fields:
|
||||
a list of field definitions;
|
||||
recordLength:
|
||||
size of the records;
|
||||
headerLength:
|
||||
size of the header;
|
||||
recordCount:
|
||||
number of records stored in DBF;
|
||||
signature:
|
||||
version number (aka signature). using 0x03 as a default meaning
|
||||
"File without DBT". for more information about this field visit
|
||||
``http://www.clicketyclick.dk/databases/xbase/format/dbf.html#DBF_NOTE_1_TARGET``
|
||||
lastUpdate:
|
||||
date of the DBF's update. this could be a string ('yymmdd' or
|
||||
'yyyymmdd'), timestamp (int or float), datetime/date value,
|
||||
a sequence (assuming (yyyy, mm, dd, ...)) or an object having
|
||||
callable ``ticks`` field.
|
||||
ignoreErrors:
|
||||
error processing mode for DBF fields (boolean)
|
||||
|
||||
"""
|
||||
self.signature = signature
|
||||
if fields is None:
|
||||
self.fields = []
|
||||
else:
|
||||
self.fields = list(fields)
|
||||
self.lastUpdate = utils.getDate(lastUpdate)
|
||||
self.recordLength = recordLength
|
||||
self.headerLength = headerLength
|
||||
self.recordCount = recordCount
|
||||
self.ignoreErrors = ignoreErrors
|
||||
# XXX: I'm not sure this is safe to
|
||||
# initialize `self.changed` in this way
|
||||
self.changed = bool(self.fields)
|
||||
|
||||
# @classmethod
|
||||
def fromString(cls, string):
|
||||
"""Return header instance from the string object."""
|
||||
return cls.fromStream(cStringIO.StringIO(str(string)))
|
||||
fromString = classmethod(fromString)
|
||||
|
||||
# @classmethod
|
||||
def fromStream(cls, stream):
|
||||
"""Return header object from the stream."""
|
||||
stream.seek(0)
|
||||
_data = stream.read(32)
|
||||
(_cnt, _hdrLen, _recLen) = struct.unpack("<I2H", _data[4:12])
|
||||
#reserved = _data[12:32]
|
||||
_year = ord(_data[1])
|
||||
if _year < 80:
|
||||
# dBase II started at 1980. It is quite unlikely
|
||||
# that actual last update date is before that year.
|
||||
_year += 2000
|
||||
else:
|
||||
_year += 1900
|
||||
## create header object
|
||||
_obj = cls(None, _hdrLen, _recLen, _cnt, ord(_data[0]),
|
||||
(_year, ord(_data[2]), ord(_data[3])))
|
||||
## append field definitions
|
||||
# position 0 is for the deletion flag
|
||||
_pos = 1
|
||||
_data = stream.read(1)
|
||||
|
||||
# The field definitions are ended either by \x0D OR a newline
|
||||
# character, so we need to handle both when reading from a stream.
|
||||
# When writing, dbfpy appears to write newlines instead of \x0D.
|
||||
while _data[0] not in ["\x0D", "\n"]:
|
||||
_data += stream.read(31)
|
||||
_fld = fields.lookupFor(_data[11]).fromString(_data, _pos)
|
||||
_obj._addField(_fld)
|
||||
_pos = _fld.end
|
||||
_data = stream.read(1)
|
||||
return _obj
|
||||
fromStream = classmethod(fromStream)
|
||||
|
||||
## properties
|
||||
|
||||
year = property(lambda self: self.lastUpdate.year)
|
||||
month = property(lambda self: self.lastUpdate.month)
|
||||
day = property(lambda self: self.lastUpdate.day)
|
||||
|
||||
def ignoreErrors(self, value):
|
||||
"""Update `ignoreErrors` flag on self and all fields"""
|
||||
self._ignore_errors = value = bool(value)
|
||||
for _field in self.fields:
|
||||
_field.ignoreErrors = value
|
||||
ignoreErrors = property(
|
||||
lambda self: self._ignore_errors,
|
||||
ignoreErrors,
|
||||
doc="""Error processing mode for DBF field value conversion
|
||||
|
||||
if set, failing field value conversion will return
|
||||
``INVALID_VALUE`` instead of raising conversion error.
|
||||
|
||||
""")
|
||||
|
||||
## object representation
|
||||
|
||||
def __repr__(self):
|
||||
_rv = """\
|
||||
Version (signature): 0x%02x
|
||||
Last update: %s
|
||||
Header length: %d
|
||||
Record length: %d
|
||||
Record count: %d
|
||||
FieldName Type Len Dec
|
||||
""" % (self.signature, self.lastUpdate, self.headerLength,
|
||||
self.recordLength, self.recordCount)
|
||||
_rv += "\n".join(
|
||||
["%10s %4s %3s %3s" % _fld.fieldInfo() for _fld in self.fields]
|
||||
)
|
||||
return _rv
|
||||
|
||||
## internal methods
|
||||
|
||||
def _addField(self, *defs):
|
||||
"""Internal variant of the `addField` method.
|
||||
|
||||
This method doesn't set `self.changed` field to True.
|
||||
|
||||
Return value is a length of the appended records.
|
||||
Note: this method doesn't modify ``recordLength`` and
|
||||
``headerLength`` fields. Use `addField` instead of this
|
||||
method if you don't exactly know what you're doing.
|
||||
|
||||
"""
|
||||
# insure we have dbf.DbfFieldDef instances first (instantiation
|
||||
# from the tuple could raise an error, in such a case I don't
|
||||
# wanna add any of the definitions -- all will be ignored)
|
||||
_defs = []
|
||||
_recordLength = 0
|
||||
for _def in defs:
|
||||
if isinstance(_def, fields.DbfFieldDef):
|
||||
_obj = _def
|
||||
else:
|
||||
(_name, _type, _len, _dec) = (tuple(_def) + (None,) * 4)[:4]
|
||||
_cls = fields.lookupFor(_type)
|
||||
_obj = _cls(_name, _len, _dec,
|
||||
ignoreErrors=self._ignore_errors)
|
||||
_recordLength += _obj.length
|
||||
_defs.append(_obj)
|
||||
# and now extend field definitions and
|
||||
# update record length
|
||||
self.fields += _defs
|
||||
return _recordLength
|
||||
|
||||
## interface methods
|
||||
|
||||
def addField(self, *defs):
|
||||
"""Add field definition to the header.
|
||||
|
||||
Examples:
|
||||
dbfh.addField(
|
||||
("name", "C", 20),
|
||||
dbf.DbfCharacterFieldDef("surname", 20),
|
||||
dbf.DbfDateFieldDef("birthdate"),
|
||||
("member", "L"),
|
||||
)
|
||||
dbfh.addField(("price", "N", 5, 2))
|
||||
dbfh.addField(dbf.DbfNumericFieldDef("origprice", 5, 2))
|
||||
|
||||
"""
|
||||
_oldLen = self.recordLength
|
||||
self.recordLength += self._addField(*defs)
|
||||
if not _oldLen:
|
||||
self.recordLength += 1
|
||||
# XXX: may be just use:
|
||||
# self.recordeLength += self._addField(*defs) + bool(not _oldLen)
|
||||
# recalculate headerLength
|
||||
self.headerLength = 32 + (32 * len(self.fields)) + 1
|
||||
self.changed = True
|
||||
|
||||
def write(self, stream):
|
||||
"""Encode and write header to the stream."""
|
||||
stream.seek(0)
|
||||
stream.write(self.toString())
|
||||
stream.write("".join([_fld.toString() for _fld in self.fields]))
|
||||
stream.write(chr(0x0D)) # cr at end of all hdr data
|
||||
self.changed = False
|
||||
|
||||
def toString(self):
|
||||
"""Returned 32 chars length string with encoded header."""
|
||||
return struct.pack("<4BI2H",
|
||||
self.signature,
|
||||
self.year - 1900,
|
||||
self.month,
|
||||
self.day,
|
||||
self.recordCount,
|
||||
self.headerLength,
|
||||
self.recordLength) + "\0" * 20
|
||||
|
||||
def setCurrentDate(self):
|
||||
"""Update ``self.lastUpdate`` field with current date value."""
|
||||
self.lastUpdate = datetime.date.today()
|
||||
|
||||
def __getitem__(self, item):
|
||||
"""Return a field definition by numeric index or name string"""
|
||||
if isinstance(item, basestring):
|
||||
_name = item.upper()
|
||||
for _field in self.fields:
|
||||
if _field.name == _name:
|
||||
return _field
|
||||
else:
|
||||
raise KeyError(item)
|
||||
else:
|
||||
# item must be field index
|
||||
return self.fields[item]
|
||||
|
||||
# vim: et sts=4 sw=4 :
|
||||
@@ -0,0 +1,262 @@
|
||||
"""DBF record definition.
|
||||
|
||||
"""
|
||||
"""History (most recent first):
|
||||
11-feb-2007 [als] __repr__: added special case for invalid field values
|
||||
10-feb-2007 [als] added .rawFromStream()
|
||||
30-oct-2006 [als] fix record length in .fromStream()
|
||||
04-jul-2006 [als] added export declaration
|
||||
20-dec-2005 [yc] DbfRecord.write() -> DbfRecord._write();
|
||||
added delete() method.
|
||||
16-dec-2005 [yc] record definition moved from `dbf`.
|
||||
"""
|
||||
|
||||
__version__ = "$Revision: 1.7 $"[11:-2]
|
||||
__date__ = "$Date: 2007/02/11 09:05:49 $"[7:-2]
|
||||
|
||||
__all__ = ["DbfRecord"]
|
||||
|
||||
from itertools import izip
|
||||
|
||||
import utils
|
||||
|
||||
class DbfRecord(object):
|
||||
"""DBF record.
|
||||
|
||||
Instances of this class shouldn't be created manualy,
|
||||
use `dbf.Dbf.newRecord` instead.
|
||||
|
||||
Class implements mapping/sequence interface, so
|
||||
fields could be accessed via their names or indexes
|
||||
(names is a preffered way to access fields).
|
||||
|
||||
Hint:
|
||||
Use `store` method to save modified record.
|
||||
|
||||
Examples:
|
||||
Add new record to the database:
|
||||
db = Dbf(filename)
|
||||
rec = db.newRecord()
|
||||
rec["FIELD1"] = value1
|
||||
rec["FIELD2"] = value2
|
||||
rec.store()
|
||||
Or the same, but modify existed
|
||||
(second in this case) record:
|
||||
db = Dbf(filename)
|
||||
rec = db[2]
|
||||
rec["FIELD1"] = value1
|
||||
rec["FIELD2"] = value2
|
||||
rec.store()
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = "dbf", "index", "deleted", "fieldData"
|
||||
|
||||
## creation and initialization
|
||||
|
||||
def __init__(self, dbf, index=None, deleted=False, data=None):
|
||||
"""Instance initialiation.
|
||||
|
||||
Arguments:
|
||||
dbf:
|
||||
A `Dbf.Dbf` instance this record belonogs to.
|
||||
index:
|
||||
An integer record index or None. If this value is
|
||||
None, record will be appended to the DBF.
|
||||
deleted:
|
||||
Boolean flag indicating whether this record
|
||||
is a deleted record.
|
||||
data:
|
||||
A sequence or None. This is a data of the fields.
|
||||
If this argument is None, default values will be used.
|
||||
|
||||
"""
|
||||
self.dbf = dbf
|
||||
# XXX: I'm not sure ``index`` is necessary
|
||||
self.index = index
|
||||
self.deleted = deleted
|
||||
if data is None:
|
||||
self.fieldData = [_fd.defaultValue for _fd in dbf.header.fields]
|
||||
else:
|
||||
self.fieldData = list(data)
|
||||
|
||||
# XXX: validate self.index before calculating position?
|
||||
position = property(lambda self: self.dbf.header.headerLength + \
|
||||
self.index * self.dbf.header.recordLength)
|
||||
|
||||
def rawFromStream(cls, dbf, index):
|
||||
"""Return raw record contents read from the stream.
|
||||
|
||||
Arguments:
|
||||
dbf:
|
||||
A `Dbf.Dbf` instance containing the record.
|
||||
index:
|
||||
Index of the record in the records' container.
|
||||
This argument can't be None in this call.
|
||||
|
||||
Return value is a string containing record data in DBF format.
|
||||
|
||||
"""
|
||||
# XXX: may be write smth assuming, that current stream
|
||||
# position is the required one? it could save some
|
||||
# time required to calculate where to seek in the file
|
||||
dbf.stream.seek(dbf.header.headerLength +
|
||||
index * dbf.header.recordLength)
|
||||
return dbf.stream.read(dbf.header.recordLength)
|
||||
rawFromStream = classmethod(rawFromStream)
|
||||
|
||||
def fromStream(cls, dbf, index):
|
||||
"""Return a record read from the stream.
|
||||
|
||||
Arguments:
|
||||
dbf:
|
||||
A `Dbf.Dbf` instance new record should belong to.
|
||||
index:
|
||||
Index of the record in the records' container.
|
||||
This argument can't be None in this call.
|
||||
|
||||
Return value is an instance of the current class.
|
||||
|
||||
"""
|
||||
return cls.fromString(dbf, cls.rawFromStream(dbf, index), index)
|
||||
fromStream = classmethod(fromStream)
|
||||
|
||||
def fromString(cls, dbf, string, index=None):
|
||||
"""Return record read from the string object.
|
||||
|
||||
Arguments:
|
||||
dbf:
|
||||
A `Dbf.Dbf` instance new record should belong to.
|
||||
string:
|
||||
A string new record should be created from.
|
||||
index:
|
||||
Index of the record in the container. If this
|
||||
argument is None, record will be appended.
|
||||
|
||||
Return value is an instance of the current class.
|
||||
|
||||
"""
|
||||
return cls(dbf, index, string[0]=="*",
|
||||
[_fd.decodeFromRecord(string) for _fd in dbf.header.fields])
|
||||
fromString = classmethod(fromString)
|
||||
|
||||
## object representation
|
||||
|
||||
def __repr__(self):
|
||||
_template = "%%%ds: %%s (%%s)" % max([len(_fld)
|
||||
for _fld in self.dbf.fieldNames])
|
||||
_rv = []
|
||||
for _fld in self.dbf.fieldNames:
|
||||
_val = self[_fld]
|
||||
if _val is utils.INVALID_VALUE:
|
||||
_rv.append(_template %
|
||||
(_fld, "None", "value cannot be decoded"))
|
||||
else:
|
||||
_rv.append(_template % (_fld, _val, type(_val)))
|
||||
return "\n".join(_rv)
|
||||
|
||||
## protected methods
|
||||
|
||||
def _write(self):
|
||||
"""Write data to the dbf stream.
|
||||
|
||||
Note:
|
||||
This isn't a public method, it's better to
|
||||
use 'store' instead publically.
|
||||
Be design ``_write`` method should be called
|
||||
only from the `Dbf` instance.
|
||||
|
||||
|
||||
"""
|
||||
self._validateIndex(False)
|
||||
self.dbf.stream.seek(self.position)
|
||||
self.dbf.stream.write(self.toString())
|
||||
# FIXME: may be move this write somewhere else?
|
||||
# why we should check this condition for each record?
|
||||
if self.index == len(self.dbf):
|
||||
# this is the last record,
|
||||
# we should write SUB (ASCII 26)
|
||||
self.dbf.stream.write("\x1A")
|
||||
|
||||
## utility methods
|
||||
|
||||
def _validateIndex(self, allowUndefined=True, checkRange=False):
|
||||
"""Valid ``self.index`` value.
|
||||
|
||||
If ``allowUndefined`` argument is True functions does nothing
|
||||
in case of ``self.index`` pointing to None object.
|
||||
|
||||
"""
|
||||
if self.index is None:
|
||||
if not allowUndefined:
|
||||
raise ValueError("Index is undefined")
|
||||
elif self.index < 0:
|
||||
raise ValueError("Index can't be negative (%s)" % self.index)
|
||||
elif checkRange and self.index <= self.dbf.header.recordCount:
|
||||
raise ValueError("There are only %d records in the DBF" %
|
||||
self.dbf.header.recordCount)
|
||||
|
||||
## interface methods
|
||||
|
||||
def store(self):
|
||||
"""Store current record in the DBF.
|
||||
|
||||
If ``self.index`` is None, this record will be appended to the
|
||||
records of the DBF this records belongs to; or replaced otherwise.
|
||||
|
||||
"""
|
||||
self._validateIndex()
|
||||
if self.index is None:
|
||||
self.index = len(self.dbf)
|
||||
self.dbf.append(self)
|
||||
else:
|
||||
self.dbf[self.index] = self
|
||||
|
||||
def delete(self):
|
||||
"""Mark method as deleted."""
|
||||
self.deleted = True
|
||||
|
||||
def toString(self):
|
||||
"""Return string packed record values."""
|
||||
return "".join([" *"[self.deleted]] + [
|
||||
_def.encodeValue(_dat)
|
||||
for (_def, _dat) in izip(self.dbf.header.fields, self.fieldData)
|
||||
])
|
||||
|
||||
def asList(self):
|
||||
"""Return a flat list of fields.
|
||||
|
||||
Note:
|
||||
Change of the list's values won't change
|
||||
real values stored in this object.
|
||||
|
||||
"""
|
||||
return self.fieldData[:]
|
||||
|
||||
def asDict(self):
|
||||
"""Return a dictionary of fields.
|
||||
|
||||
Note:
|
||||
Change of the dicts's values won't change
|
||||
real values stored in this object.
|
||||
|
||||
"""
|
||||
return dict([_i for _i in izip(self.dbf.fieldNames, self.fieldData)])
|
||||
|
||||
def __getitem__(self, key):
|
||||
"""Return value by field name or field index."""
|
||||
if isinstance(key, (long, int)):
|
||||
# integer index of the field
|
||||
return self.fieldData[key]
|
||||
# assuming string field name
|
||||
return self.fieldData[self.dbf.indexOfFieldName(key)]
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
"""Set field value by integer index of the field or string name."""
|
||||
if isinstance(key, (int, long)):
|
||||
# integer index of the field
|
||||
return self.fieldData[key]
|
||||
# assuming string field name
|
||||
self.fieldData[self.dbf.indexOfFieldName(key)] = value
|
||||
|
||||
# vim: et sts=4 sw=4 :
|
||||
@@ -0,0 +1,170 @@
|
||||
"""String utilities.
|
||||
|
||||
TODO:
|
||||
- allow strings in getDateTime routine;
|
||||
"""
|
||||
"""History (most recent first):
|
||||
11-feb-2007 [als] added INVALID_VALUE
|
||||
10-feb-2007 [als] allow date strings padded with spaces instead of zeroes
|
||||
20-dec-2005 [yc] handle long objects in getDate/getDateTime
|
||||
16-dec-2005 [yc] created from ``strutil`` module.
|
||||
"""
|
||||
|
||||
__version__ = "$Revision: 1.4 $"[11:-2]
|
||||
__date__ = "$Date: 2007/02/11 08:57:17 $"[7:-2]
|
||||
|
||||
import datetime
|
||||
import time
|
||||
|
||||
|
||||
def unzfill(str):
|
||||
"""Return a string without ASCII NULs.
|
||||
|
||||
This function searchers for the first NUL (ASCII 0) occurance
|
||||
and truncates string till that position.
|
||||
|
||||
"""
|
||||
try:
|
||||
return str[:str.index('\0')]
|
||||
except ValueError:
|
||||
return str
|
||||
|
||||
|
||||
def getDate(date=None):
|
||||
"""Return `datetime.date` instance.
|
||||
|
||||
Type of the ``date`` argument could be one of the following:
|
||||
None:
|
||||
use current date value;
|
||||
datetime.date:
|
||||
this value will be returned;
|
||||
datetime.datetime:
|
||||
the result of the date.date() will be returned;
|
||||
string:
|
||||
assuming "%Y%m%d" or "%y%m%dd" format;
|
||||
number:
|
||||
assuming it's a timestamp (returned for example
|
||||
by the time.time() call;
|
||||
sequence:
|
||||
assuming (year, month, day, ...) sequence;
|
||||
|
||||
Additionaly, if ``date`` has callable ``ticks`` attribute,
|
||||
it will be used and result of the called would be treated
|
||||
as a timestamp value.
|
||||
|
||||
"""
|
||||
if date is None:
|
||||
# use current value
|
||||
return datetime.date.today()
|
||||
if isinstance(date, datetime.date):
|
||||
return date
|
||||
if isinstance(date, datetime.datetime):
|
||||
return date.date()
|
||||
if isinstance(date, (int, long, float)):
|
||||
# date is a timestamp
|
||||
return datetime.date.fromtimestamp(date)
|
||||
if isinstance(date, basestring):
|
||||
date = date.replace(" ", "0")
|
||||
if len(date) == 6:
|
||||
# yymmdd
|
||||
return datetime.date(*time.strptime(date, "%y%m%d")[:3])
|
||||
# yyyymmdd
|
||||
return datetime.date(*time.strptime(date, "%Y%m%d")[:3])
|
||||
if hasattr(date, "__getitem__"):
|
||||
# a sequence (assuming date/time tuple)
|
||||
return datetime.date(*date[:3])
|
||||
return datetime.date.fromtimestamp(date.ticks())
|
||||
|
||||
|
||||
def getDateTime(value=None):
|
||||
"""Return `datetime.datetime` instance.
|
||||
|
||||
Type of the ``value`` argument could be one of the following:
|
||||
None:
|
||||
use current date value;
|
||||
datetime.date:
|
||||
result will be converted to the `datetime.datetime` instance
|
||||
using midnight;
|
||||
datetime.datetime:
|
||||
``value`` will be returned as is;
|
||||
string:
|
||||
*** CURRENTLY NOT SUPPORTED ***;
|
||||
number:
|
||||
assuming it's a timestamp (returned for example
|
||||
by the time.time() call;
|
||||
sequence:
|
||||
assuming (year, month, day, ...) sequence;
|
||||
|
||||
Additionaly, if ``value`` has callable ``ticks`` attribute,
|
||||
it will be used and result of the called would be treated
|
||||
as a timestamp value.
|
||||
|
||||
"""
|
||||
if value is None:
|
||||
# use current value
|
||||
return datetime.datetime.today()
|
||||
if isinstance(value, datetime.datetime):
|
||||
return value
|
||||
if isinstance(value, datetime.date):
|
||||
return datetime.datetime.fromordinal(value.toordinal())
|
||||
if isinstance(value, (int, long, float)):
|
||||
# value is a timestamp
|
||||
return datetime.datetime.fromtimestamp(value)
|
||||
if isinstance(value, basestring):
|
||||
raise NotImplementedError("Strings aren't currently implemented")
|
||||
if hasattr(value, "__getitem__"):
|
||||
# a sequence (assuming date/time tuple)
|
||||
return datetime.datetime(*tuple(value)[:6])
|
||||
return datetime.datetime.fromtimestamp(value.ticks())
|
||||
|
||||
|
||||
class classproperty(property):
|
||||
"""Works in the same way as a ``property``, but for the classes."""
|
||||
|
||||
def __get__(self, obj, cls):
|
||||
return self.fget(cls)
|
||||
|
||||
|
||||
class _InvalidValue(object):
|
||||
|
||||
"""Value returned from DBF records when field validation fails
|
||||
|
||||
The value is not equal to anything except for itself
|
||||
and equal to all empty values: None, 0, empty string etc.
|
||||
In other words, invalid value is equal to None and not equal
|
||||
to None at the same time.
|
||||
|
||||
This value yields zero upon explicit conversion to a number type,
|
||||
empty string for string types, and False for boolean.
|
||||
|
||||
"""
|
||||
|
||||
def __eq__(self, other):
|
||||
return not other
|
||||
|
||||
def __ne__(self, other):
|
||||
return not (other is self)
|
||||
|
||||
def __nonzero__(self):
|
||||
return False
|
||||
|
||||
def __int__(self):
|
||||
return 0
|
||||
__long__ = __int__
|
||||
|
||||
def __float__(self):
|
||||
return 0.0
|
||||
|
||||
def __str__(self):
|
||||
return ""
|
||||
|
||||
def __unicode__(self):
|
||||
return u""
|
||||
|
||||
def __repr__(self):
|
||||
return "<INVALID>"
|
||||
|
||||
# invalid value is a constant singleton
|
||||
INVALID_VALUE = _InvalidValue()
|
||||
|
||||
# vim: set et sts=4 sw=4 :
|
||||
@@ -0,0 +1,298 @@
|
||||
#! /usr/bin/env python
|
||||
"""DBF accessing helpers.
|
||||
|
||||
FIXME: more documentation needed
|
||||
|
||||
Examples:
|
||||
|
||||
Create new table, setup structure, add records:
|
||||
|
||||
dbf = Dbf(filename, new=True)
|
||||
dbf.addField(
|
||||
("NAME", "C", 15),
|
||||
("SURNAME", "C", 25),
|
||||
("INITIALS", "C", 10),
|
||||
("BIRTHDATE", "D"),
|
||||
)
|
||||
for (n, s, i, b) in (
|
||||
("John", "Miller", "YC", (1980, 10, 11)),
|
||||
("Andy", "Larkin", "", (1980, 4, 11)),
|
||||
):
|
||||
rec = dbf.newRecord()
|
||||
rec["NAME"] = n
|
||||
rec["SURNAME"] = s
|
||||
rec["INITIALS"] = i
|
||||
rec["BIRTHDATE"] = b
|
||||
rec.store()
|
||||
dbf.close()
|
||||
|
||||
Open existed dbf, read some data:
|
||||
|
||||
dbf = Dbf(filename, True)
|
||||
for rec in dbf:
|
||||
for fldName in dbf.fieldNames:
|
||||
print '%s:\t %s (%s)' % (fldName, rec[fldName],
|
||||
type(rec[fldName]))
|
||||
print
|
||||
dbf.close()
|
||||
|
||||
"""
|
||||
"""History (most recent first):
|
||||
11-feb-2007 [als] export INVALID_VALUE;
|
||||
Dbf: added .ignoreErrors, .INVALID_VALUE
|
||||
04-jul-2006 [als] added export declaration
|
||||
20-dec-2005 [yc] removed fromStream and newDbf methods:
|
||||
use argument of __init__ call must be used instead;
|
||||
added class fields pointing to the header and
|
||||
record classes.
|
||||
17-dec-2005 [yc] split to several modules; reimplemented
|
||||
13-dec-2005 [yc] adapted to the changes of the `strutil` module.
|
||||
13-sep-2002 [als] support FoxPro Timestamp datatype
|
||||
15-nov-1999 [jjk] documentation updates, add demo
|
||||
24-aug-1998 [jjk] add some encodeValue methods (not tested), other tweaks
|
||||
08-jun-1998 [jjk] fix problems, add more features
|
||||
20-feb-1998 [jjk] fix problems, add more features
|
||||
19-feb-1998 [jjk] add create/write capabilities
|
||||
18-feb-1998 [jjk] from dbfload.py
|
||||
"""
|
||||
|
||||
__version__ = "$Revision: 1.7 $"[11:-2]
|
||||
__date__ = "$Date: 2007/02/11 09:23:13 $"[7:-2]
|
||||
__author__ = "Jeff Kunce <kuncej@mail.conservation.state.mo.us>"
|
||||
|
||||
__all__ = ["Dbf"]
|
||||
|
||||
from . import header
|
||||
from . import record
|
||||
from .utils import INVALID_VALUE
|
||||
|
||||
|
||||
class Dbf(object):
|
||||
"""DBF accessor.
|
||||
|
||||
FIXME:
|
||||
docs and examples needed (dont' forget to tell
|
||||
about problems adding new fields on the fly)
|
||||
|
||||
Implementation notes:
|
||||
``_new`` field is used to indicate whether this is
|
||||
a new data table. `addField` could be used only for
|
||||
the new tables! If at least one record was appended
|
||||
to the table it's structure couldn't be changed.
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ("name", "header", "stream",
|
||||
"_changed", "_new", "_ignore_errors")
|
||||
|
||||
HeaderClass = header.DbfHeader
|
||||
RecordClass = record.DbfRecord
|
||||
INVALID_VALUE = INVALID_VALUE
|
||||
|
||||
# initialization and creation helpers
|
||||
|
||||
def __init__(self, f, readOnly=False, new=False, ignoreErrors=False):
|
||||
"""Initialize instance.
|
||||
|
||||
Arguments:
|
||||
f:
|
||||
Filename or file-like object.
|
||||
new:
|
||||
True if new data table must be created. Assume
|
||||
data table exists if this argument is False.
|
||||
readOnly:
|
||||
if ``f`` argument is a string file will
|
||||
be opend in read-only mode; in other cases
|
||||
this argument is ignored. This argument is ignored
|
||||
even if ``new`` argument is True.
|
||||
headerObj:
|
||||
`header.DbfHeader` instance or None. If this argument
|
||||
is None, new empty header will be used with the
|
||||
all fields set by default.
|
||||
ignoreErrors:
|
||||
if set, failing field value conversion will return
|
||||
``INVALID_VALUE`` instead of raising conversion error.
|
||||
|
||||
"""
|
||||
if isinstance(f, str):
|
||||
# a filename
|
||||
self.name = f
|
||||
if new:
|
||||
# new table (table file must be
|
||||
# created or opened and truncated)
|
||||
self.stream = open(f, "w+b")
|
||||
else:
|
||||
# tabe file must exist
|
||||
self.stream = open(f, ("r+b", "rb")[bool(readOnly)])
|
||||
else:
|
||||
# a stream
|
||||
self.name = getattr(f, "name", "")
|
||||
self.stream = f
|
||||
if new:
|
||||
# if this is a new table, header will be empty
|
||||
self.header = self.HeaderClass()
|
||||
else:
|
||||
# or instantiated using stream
|
||||
self.header = self.HeaderClass.fromStream(self.stream)
|
||||
self.ignoreErrors = ignoreErrors
|
||||
self._new = bool(new)
|
||||
self._changed = False
|
||||
|
||||
# properties
|
||||
|
||||
closed = property(lambda self: self.stream.closed)
|
||||
recordCount = property(lambda self: self.header.recordCount)
|
||||
fieldNames = property(
|
||||
lambda self: [_fld.name for _fld in self.header.fields])
|
||||
fieldDefs = property(lambda self: self.header.fields)
|
||||
changed = property(lambda self: self._changed or self.header.changed)
|
||||
|
||||
def ignoreErrors(self, value):
|
||||
"""Update `ignoreErrors` flag on the header object and self"""
|
||||
self.header.ignoreErrors = self._ignore_errors = bool(value)
|
||||
|
||||
ignoreErrors = property(
|
||||
lambda self: self._ignore_errors,
|
||||
ignoreErrors,
|
||||
doc="""Error processing mode for DBF field value conversion
|
||||
|
||||
if set, failing field value conversion will return
|
||||
``INVALID_VALUE`` instead of raising conversion error.
|
||||
|
||||
""")
|
||||
|
||||
# protected methods
|
||||
|
||||
def _fixIndex(self, index):
|
||||
"""Return fixed index.
|
||||
|
||||
This method fails if index isn't a numeric object
|
||||
(long or int). Or index isn't in a valid range
|
||||
(less or equal to the number of records in the db).
|
||||
|
||||
If ``index`` is a negative number, it will be
|
||||
treated as a negative indexes for list objects.
|
||||
|
||||
Return:
|
||||
Return value is numeric object maning valid index.
|
||||
|
||||
"""
|
||||
if not isinstance(index, int):
|
||||
raise TypeError("Index must be a numeric object")
|
||||
if index < 0:
|
||||
# index from the right side
|
||||
# fix it to the left-side index
|
||||
index += len(self) + 1
|
||||
if index >= len(self):
|
||||
raise IndexError("Record index out of range")
|
||||
return index
|
||||
|
||||
# iterface methods
|
||||
|
||||
def close(self):
|
||||
self.flush()
|
||||
self.stream.close()
|
||||
|
||||
def flush(self):
|
||||
"""Flush data to the associated stream."""
|
||||
if self.changed:
|
||||
self.header.setCurrentDate()
|
||||
self.header.write(self.stream)
|
||||
self.stream.flush()
|
||||
self._changed = False
|
||||
|
||||
def indexOfFieldName(self, name):
|
||||
"""Index of field named ``name``."""
|
||||
# FIXME: move this to header class
|
||||
names = [f.name for f in self.header.fields]
|
||||
return names.index(name.upper())
|
||||
|
||||
def newRecord(self):
|
||||
"""Return new record, which belong to this table."""
|
||||
return self.RecordClass(self)
|
||||
|
||||
def append(self, record):
|
||||
"""Append ``record`` to the database."""
|
||||
record.index = self.header.recordCount
|
||||
record._write()
|
||||
self.header.recordCount += 1
|
||||
self._changed = True
|
||||
self._new = False
|
||||
|
||||
def addField(self, *defs):
|
||||
"""Add field definitions.
|
||||
|
||||
For more information see `header.DbfHeader.addField`.
|
||||
|
||||
"""
|
||||
if self._new:
|
||||
self.header.addField(*defs)
|
||||
else:
|
||||
raise TypeError("At least one record was added, "
|
||||
"structure can't be changed")
|
||||
|
||||
# 'magic' methods (representation and sequence interface)
|
||||
|
||||
def __repr__(self):
|
||||
return "Dbf stream '%s'\n" % self.stream + repr(self.header)
|
||||
|
||||
def __len__(self):
|
||||
"""Return number of records."""
|
||||
return self.recordCount
|
||||
|
||||
def __getitem__(self, index):
|
||||
"""Return `DbfRecord` instance."""
|
||||
return self.RecordClass.fromStream(self, self._fixIndex(index))
|
||||
|
||||
def __setitem__(self, index, record):
|
||||
"""Write `DbfRecord` instance to the stream."""
|
||||
record.index = self._fixIndex(index)
|
||||
record._write()
|
||||
self._changed = True
|
||||
self._new = False
|
||||
|
||||
# def __del__(self):
|
||||
# """Flush stream upon deletion of the object."""
|
||||
# self.flush()
|
||||
|
||||
|
||||
def demo_read(filename):
|
||||
_dbf = Dbf(filename, True)
|
||||
for _rec in _dbf:
|
||||
print()
|
||||
print(repr(_rec))
|
||||
_dbf.close()
|
||||
|
||||
|
||||
def demo_create(filename):
|
||||
_dbf = Dbf(filename, new=True)
|
||||
_dbf.addField(
|
||||
("NAME", "C", 15),
|
||||
("SURNAME", "C", 25),
|
||||
("INITIALS", "C", 10),
|
||||
("BIRTHDATE", "D"),
|
||||
)
|
||||
for (_n, _s, _i, _b) in (
|
||||
("John", "Miller", "YC", (1981, 1, 2)),
|
||||
("Andy", "Larkin", "AL", (1982, 3, 4)),
|
||||
("Bill", "Clinth", "", (1983, 5, 6)),
|
||||
("Bobb", "McNail", "", (1984, 7, 8)),
|
||||
):
|
||||
_rec = _dbf.newRecord()
|
||||
_rec["NAME"] = _n
|
||||
_rec["SURNAME"] = _s
|
||||
_rec["INITIALS"] = _i
|
||||
_rec["BIRTHDATE"] = _b
|
||||
_rec.store()
|
||||
print(repr(_dbf))
|
||||
_dbf.close()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
|
||||
_name = len(sys.argv) > 1 and sys.argv[1] or "county.dbf"
|
||||
demo_create(_name)
|
||||
demo_read(_name)
|
||||
|
||||
# vim: set et sw=4 sts=4 :
|
||||
@@ -0,0 +1,183 @@
|
||||
#!/usr/bin/python
|
||||
""".DBF creation helpers.
|
||||
|
||||
Note: this is a legacy interface. New code should use Dbf class
|
||||
for table creation (see examples in dbf.py)
|
||||
|
||||
TODO:
|
||||
- handle Memo fields.
|
||||
- check length of the fields accoring to the
|
||||
`http://www.clicketyclick.dk/databases/xbase/format/data_types.html`
|
||||
|
||||
"""
|
||||
"""History (most recent first)
|
||||
04-jul-2006 [als] added export declaration;
|
||||
updated for dbfpy 2.0
|
||||
15-dec-2005 [yc] define dbf_new.__slots__
|
||||
14-dec-2005 [yc] added vim modeline; retab'd; added doc-strings;
|
||||
dbf_new now is a new class (inherited from object)
|
||||
??-jun-2000 [--] added by Hans Fiby
|
||||
"""
|
||||
|
||||
__version__ = "$Revision: 1.4 $"[11:-2]
|
||||
__date__ = "$Date: 2006/07/04 08:18:18 $"[7:-2]
|
||||
|
||||
__all__ = ["dbf_new"]
|
||||
|
||||
from .dbf import *
|
||||
from .fields import *
|
||||
from .header import *
|
||||
from .record import *
|
||||
|
||||
|
||||
class _FieldDefinition(object):
|
||||
"""Field definition.
|
||||
|
||||
This is a simple structure, which contains ``name``, ``type``,
|
||||
``len``, ``dec`` and ``cls`` fields.
|
||||
|
||||
Objects also implement get/setitem magic functions, so fields
|
||||
could be accessed via sequence iterface, where 'name' has
|
||||
index 0, 'type' index 1, 'len' index 2, 'dec' index 3 and
|
||||
'cls' could be located at index 4.
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = "name", "type", "len", "dec", "cls"
|
||||
|
||||
# WARNING: be attentive - dictionaries are mutable!
|
||||
FLD_TYPES = {
|
||||
# type: (cls, len)
|
||||
"C": (DbfCharacterFieldDef, None),
|
||||
"N": (DbfNumericFieldDef, None),
|
||||
"L": (DbfLogicalFieldDef, 1),
|
||||
# FIXME: support memos
|
||||
# "M": (DbfMemoFieldDef),
|
||||
"D": (DbfDateFieldDef, 8),
|
||||
# FIXME: I'm not sure length should be 14 characters!
|
||||
# but temporary I use it, cuz date is 8 characters
|
||||
# and time 6 (hhmmss)
|
||||
"T": (DbfDateTimeFieldDef, 14),
|
||||
}
|
||||
|
||||
def __init__(self, name, type, len=None, dec=0):
|
||||
_cls, _len = self.FLD_TYPES[type]
|
||||
if _len is None:
|
||||
if len is None:
|
||||
raise ValueError("Field length must be defined")
|
||||
_len = len
|
||||
self.name = name
|
||||
self.type = type
|
||||
self.len = _len
|
||||
self.dec = dec
|
||||
self.cls = _cls
|
||||
|
||||
def getDbfField(self):
|
||||
"Return `DbfFieldDef` instance from the current definition."
|
||||
return self.cls(self.name, self.len, self.dec)
|
||||
|
||||
def appendToHeader(self, dbfh):
|
||||
"""Create a `DbfFieldDef` instance and append it to the dbf header.
|
||||
|
||||
Arguments:
|
||||
dbfh: `DbfHeader` instance.
|
||||
|
||||
"""
|
||||
_dbff = self.getDbfField()
|
||||
dbfh.addField(_dbff)
|
||||
|
||||
|
||||
class dbf_new(object):
|
||||
"""New .DBF creation helper.
|
||||
|
||||
Example Usage:
|
||||
|
||||
dbfn = dbf_new()
|
||||
dbfn.add_field("name",'C',80)
|
||||
dbfn.add_field("price",'N',10,2)
|
||||
dbfn.add_field("date",'D',8)
|
||||
dbfn.write("tst.dbf")
|
||||
|
||||
Note:
|
||||
This module cannot handle Memo-fields,
|
||||
they are special.
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ("fields",)
|
||||
|
||||
FieldDefinitionClass = _FieldDefinition
|
||||
|
||||
def __init__(self):
|
||||
self.fields = []
|
||||
|
||||
def add_field(self, name, typ, len, dec=0):
|
||||
"""Add field definition.
|
||||
|
||||
Arguments:
|
||||
name:
|
||||
field name (str object). field name must not
|
||||
contain ASCII NULs and it's length shouldn't
|
||||
exceed 10 characters.
|
||||
typ:
|
||||
type of the field. this must be a single character
|
||||
from the "CNLMDT" set meaning character, numeric,
|
||||
logical, memo, date and date/time respectively.
|
||||
len:
|
||||
length of the field. this argument is used only for
|
||||
the character and numeric fields. all other fields
|
||||
have fixed length.
|
||||
FIXME: use None as a default for this argument?
|
||||
dec:
|
||||
decimal precision. used only for the numric fields.
|
||||
|
||||
"""
|
||||
self.fields.append(self.FieldDefinitionClass(name, typ, len, dec))
|
||||
|
||||
def write(self, filename):
|
||||
"""Create empty .DBF file using current structure."""
|
||||
_dbfh = DbfHeader()
|
||||
_dbfh.setCurrentDate()
|
||||
for _fldDef in self.fields:
|
||||
_fldDef.appendToHeader(_dbfh)
|
||||
|
||||
_dbfStream = open(filename, "wb")
|
||||
_dbfh.write(_dbfStream)
|
||||
_dbfStream.close()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# create a new DBF-File
|
||||
dbfn = dbf_new()
|
||||
dbfn.add_field("name", 'C', 80)
|
||||
dbfn.add_field("price", 'N', 10, 2)
|
||||
dbfn.add_field("date", 'D', 8)
|
||||
dbfn.write("tst.dbf")
|
||||
# test new dbf
|
||||
print("*** created tst.dbf: ***")
|
||||
dbft = Dbf('tst.dbf', readOnly=0)
|
||||
print(repr(dbft))
|
||||
# add a record
|
||||
rec = DbfRecord(dbft)
|
||||
rec['name'] = 'something'
|
||||
rec['price'] = 10.5
|
||||
rec['date'] = (2000, 1, 12)
|
||||
rec.store()
|
||||
# add another record
|
||||
rec = DbfRecord(dbft)
|
||||
rec['name'] = 'foo and bar'
|
||||
rec['price'] = 12234
|
||||
rec['date'] = (1992, 7, 15)
|
||||
rec.store()
|
||||
|
||||
# show the records
|
||||
print("*** inserted 2 records into tst.dbf: ***")
|
||||
print(repr(dbft))
|
||||
for i1 in range(len(dbft)):
|
||||
rec = dbft[i1]
|
||||
for fldName in dbft.fieldNames:
|
||||
print('%s:\t %s' % (fldName, rec[fldName]))
|
||||
print()
|
||||
dbft.close()
|
||||
|
||||
# vim: set et sts=4 sw=4 :
|
||||
@@ -0,0 +1,467 @@
|
||||
"""DBF fields definitions.
|
||||
|
||||
TODO:
|
||||
- make memos work
|
||||
"""
|
||||
"""History (most recent first):
|
||||
26-may-2009 [als] DbfNumericFieldDef.decodeValue: strip zero bytes
|
||||
05-feb-2009 [als] DbfDateFieldDef.encodeValue: empty arg produces empty date
|
||||
16-sep-2008 [als] DbfNumericFieldDef decoding looks for decimal point
|
||||
in the value to select float or integer return type
|
||||
13-mar-2008 [als] check field name length in constructor
|
||||
11-feb-2007 [als] handle value conversion errors
|
||||
10-feb-2007 [als] DbfFieldDef: added .rawFromRecord()
|
||||
01-dec-2006 [als] Timestamp columns use None for empty values
|
||||
31-oct-2006 [als] support field types 'F' (float), 'I' (integer)
|
||||
and 'Y' (currency);
|
||||
automate export and registration of field classes
|
||||
04-jul-2006 [als] added export declaration
|
||||
10-mar-2006 [als] decode empty values for Date and Logical fields;
|
||||
show field name in errors
|
||||
10-mar-2006 [als] fix Numeric value decoding: according to spec,
|
||||
value always is string representation of the number;
|
||||
ensure that encoded Numeric value fits into the field
|
||||
20-dec-2005 [yc] use field names in upper case
|
||||
15-dec-2005 [yc] field definitions moved from `dbf`.
|
||||
"""
|
||||
|
||||
__version__ = "$Revision: 1.14 $"[11:-2]
|
||||
__date__ = "$Date: 2009/05/26 05:16:51 $"[7:-2]
|
||||
|
||||
__all__ = ["lookupFor",] # field classes added at the end of the module
|
||||
|
||||
import datetime
|
||||
import struct
|
||||
import sys
|
||||
|
||||
from . import utils
|
||||
|
||||
## abstract definitions
|
||||
|
||||
class DbfFieldDef(object):
|
||||
"""Abstract field definition.
|
||||
|
||||
Child classes must override ``type`` class attribute to provide datatype
|
||||
infromation of the field definition. For more info about types visit
|
||||
`http://www.clicketyclick.dk/databases/xbase/format/data_types.html`
|
||||
|
||||
Also child classes must override ``defaultValue`` field to provide
|
||||
default value for the field value.
|
||||
|
||||
If child class has fixed length ``length`` class attribute must be
|
||||
overriden and set to the valid value. None value means, that field
|
||||
isn't of fixed length.
|
||||
|
||||
Note: ``name`` field must not be changed after instantiation.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
__slots__ = ("name", "decimalCount",
|
||||
"start", "end", "ignoreErrors")
|
||||
|
||||
# length of the field, None in case of variable-length field,
|
||||
# or a number if this field is a fixed-length field
|
||||
length = None
|
||||
|
||||
# field type. for more information about fields types visit
|
||||
# `http://www.clicketyclick.dk/databases/xbase/format/data_types.html`
|
||||
# must be overriden in child classes
|
||||
typeCode = None
|
||||
|
||||
# default value for the field. this field must be
|
||||
# overriden in child classes
|
||||
defaultValue = None
|
||||
|
||||
def __init__(self, name, length=None, decimalCount=None,
|
||||
start=None, stop=None, ignoreErrors=False,
|
||||
):
|
||||
"""Initialize instance."""
|
||||
assert self.typeCode is not None, "Type code must be overriden"
|
||||
assert self.defaultValue is not None, "Default value must be overriden"
|
||||
## fix arguments
|
||||
if len(name) >10:
|
||||
raise ValueError("Field name \"%s\" is too long" % name)
|
||||
name = str(name).upper()
|
||||
if self.__class__.length is None:
|
||||
if length is None:
|
||||
raise ValueError("[%s] Length isn't specified" % name)
|
||||
length = int(length)
|
||||
if length <= 0:
|
||||
raise ValueError("[%s] Length must be a positive integer"
|
||||
% name)
|
||||
else:
|
||||
length = self.length
|
||||
if decimalCount is None:
|
||||
decimalCount = 0
|
||||
## set fields
|
||||
self.name = name
|
||||
# FIXME: validate length according to the specification at
|
||||
# http://www.clicketyclick.dk/databases/xbase/format/data_types.html
|
||||
self.length = length
|
||||
self.decimalCount = decimalCount
|
||||
self.ignoreErrors = ignoreErrors
|
||||
self.start = start
|
||||
self.end = stop
|
||||
|
||||
def __cmp__(self, other):
|
||||
return cmp(self.name, str(other).upper())
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.name)
|
||||
|
||||
def fromString(cls, string, start, ignoreErrors=False):
|
||||
"""Decode dbf field definition from the string data.
|
||||
|
||||
Arguments:
|
||||
string:
|
||||
a string, dbf definition is decoded from. length of
|
||||
the string must be 32 bytes.
|
||||
start:
|
||||
position in the database file.
|
||||
ignoreErrors:
|
||||
initial error processing mode for the new field (boolean)
|
||||
|
||||
"""
|
||||
assert len(string) == 32
|
||||
_length = string[16]
|
||||
return cls(utils.unzfill(string)[:11].decode('utf-8'), _length,
|
||||
string[17], start, start + _length, ignoreErrors=ignoreErrors)
|
||||
fromString = classmethod(fromString)
|
||||
|
||||
def toString(self):
|
||||
"""Return encoded field definition.
|
||||
|
||||
Return:
|
||||
Return value is a string object containing encoded
|
||||
definition of this field.
|
||||
|
||||
"""
|
||||
if sys.version_info < (2, 4):
|
||||
# earlier versions did not support padding character
|
||||
_name = self.name[:11] + "\0" * (11 - len(self.name))
|
||||
else:
|
||||
_name = self.name.ljust(11, '\0')
|
||||
return (
|
||||
_name +
|
||||
self.typeCode +
|
||||
#data address
|
||||
chr(0) * 4 +
|
||||
chr(self.length) +
|
||||
chr(self.decimalCount) +
|
||||
chr(0) * 14
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return "%-10s %1s %3d %3d" % self.fieldInfo()
|
||||
|
||||
def fieldInfo(self):
|
||||
"""Return field information.
|
||||
|
||||
Return:
|
||||
Return value is a (name, type, length, decimals) tuple.
|
||||
|
||||
"""
|
||||
return (self.name, self.typeCode, self.length, self.decimalCount)
|
||||
|
||||
def rawFromRecord(self, record):
|
||||
"""Return a "raw" field value from the record string."""
|
||||
return record[self.start:self.end]
|
||||
|
||||
def decodeFromRecord(self, record):
|
||||
"""Return decoded field value from the record string."""
|
||||
try:
|
||||
return self.decodeValue(self.rawFromRecord(record))
|
||||
except:
|
||||
if self.ignoreErrors:
|
||||
return utils.INVALID_VALUE
|
||||
else:
|
||||
raise
|
||||
|
||||
def decodeValue(self, value):
|
||||
"""Return decoded value from string value.
|
||||
|
||||
This method shouldn't be used publicly. It's called from the
|
||||
`decodeFromRecord` method.
|
||||
|
||||
This is an abstract method and it must be overridden in child classes.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def encodeValue(self, value):
|
||||
"""Return str object containing encoded field value.
|
||||
|
||||
This is an abstract method and it must be overriden in child classes.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
## real classes
|
||||
|
||||
class DbfCharacterFieldDef(DbfFieldDef):
|
||||
"""Definition of the character field."""
|
||||
|
||||
typeCode = "C"
|
||||
defaultValue = b''
|
||||
|
||||
def decodeValue(self, value):
|
||||
"""Return string object.
|
||||
|
||||
Return value is a ``value`` argument with stripped right spaces.
|
||||
|
||||
"""
|
||||
return value.rstrip(b' ').decode('utf-8')
|
||||
|
||||
def encodeValue(self, value):
|
||||
"""Return raw data string encoded from a ``value``."""
|
||||
return str(value)[:self.length].ljust(self.length)
|
||||
|
||||
|
||||
class DbfNumericFieldDef(DbfFieldDef):
|
||||
"""Definition of the numeric field."""
|
||||
|
||||
typeCode = "N"
|
||||
# XXX: now I'm not sure it was a good idea to make a class field
|
||||
# `defaultValue` instead of a generic method as it was implemented
|
||||
# previously -- it's ok with all types except number, cuz
|
||||
# if self.decimalCount is 0, we should return 0 and 0.0 otherwise.
|
||||
defaultValue = 0
|
||||
|
||||
def decodeValue(self, value):
|
||||
"""Return a number decoded from ``value``.
|
||||
|
||||
If decimals is zero, value will be decoded as an integer;
|
||||
or as a float otherwise.
|
||||
|
||||
Return:
|
||||
Return value is a int (long) or float instance.
|
||||
|
||||
"""
|
||||
value = value.strip(b' \0')
|
||||
if b'.' in value:
|
||||
# a float (has decimal separator)
|
||||
return float(value)
|
||||
elif value:
|
||||
# must be an integer
|
||||
return int(value)
|
||||
else:
|
||||
return 0
|
||||
|
||||
def encodeValue(self, value):
|
||||
"""Return string containing encoded ``value``."""
|
||||
_rv = ("%*.*f" % (self.length, self.decimalCount, value))
|
||||
if len(_rv) > self.length:
|
||||
_ppos = _rv.find(".")
|
||||
if 0 <= _ppos <= self.length:
|
||||
_rv = _rv[:self.length]
|
||||
else:
|
||||
raise ValueError("[%s] Numeric overflow: %s (field width: %i)"
|
||||
% (self.name, _rv, self.length))
|
||||
return _rv
|
||||
|
||||
class DbfFloatFieldDef(DbfNumericFieldDef):
|
||||
"""Definition of the float field - same as numeric."""
|
||||
|
||||
typeCode = "F"
|
||||
|
||||
class DbfIntegerFieldDef(DbfFieldDef):
|
||||
"""Definition of the integer field."""
|
||||
|
||||
typeCode = "I"
|
||||
length = 4
|
||||
defaultValue = 0
|
||||
|
||||
def decodeValue(self, value):
|
||||
"""Return an integer number decoded from ``value``."""
|
||||
return struct.unpack("<i", value)[0]
|
||||
|
||||
def encodeValue(self, value):
|
||||
"""Return string containing encoded ``value``."""
|
||||
return struct.pack("<i", int(value))
|
||||
|
||||
class DbfCurrencyFieldDef(DbfFieldDef):
|
||||
"""Definition of the currency field."""
|
||||
|
||||
typeCode = "Y"
|
||||
length = 8
|
||||
defaultValue = 0.0
|
||||
|
||||
def decodeValue(self, value):
|
||||
"""Return float number decoded from ``value``."""
|
||||
return struct.unpack("<q", value)[0] / 10000.
|
||||
|
||||
def encodeValue(self, value):
|
||||
"""Return string containing encoded ``value``."""
|
||||
return struct.pack("<q", round(value * 10000))
|
||||
|
||||
class DbfLogicalFieldDef(DbfFieldDef):
|
||||
"""Definition of the logical field."""
|
||||
|
||||
typeCode = "L"
|
||||
defaultValue = -1
|
||||
length = 1
|
||||
|
||||
def decodeValue(self, value):
|
||||
"""Return True, False or -1 decoded from ``value``."""
|
||||
# Note: value always is 1-char string
|
||||
if value == "?":
|
||||
return -1
|
||||
if value in "NnFf ":
|
||||
return False
|
||||
if value in "YyTt":
|
||||
return True
|
||||
raise ValueError("[%s] Invalid logical value %r" % (self.name, value))
|
||||
|
||||
def encodeValue(self, value):
|
||||
"""Return a character from the "TF?" set.
|
||||
|
||||
Return:
|
||||
Return value is "T" if ``value`` is True
|
||||
"?" if value is -1 or False otherwise.
|
||||
|
||||
"""
|
||||
if value is True:
|
||||
return "T"
|
||||
if value == -1:
|
||||
return "?"
|
||||
return "F"
|
||||
|
||||
|
||||
class DbfMemoFieldDef(DbfFieldDef):
|
||||
"""Definition of the memo field.
|
||||
|
||||
Note: memos aren't currenly completely supported.
|
||||
|
||||
"""
|
||||
|
||||
typeCode = "M"
|
||||
defaultValue = " " * 10
|
||||
length = 10
|
||||
|
||||
def decodeValue(self, value):
|
||||
"""Return int .dbt block number decoded from the string object."""
|
||||
#return int(value)
|
||||
raise NotImplementedError
|
||||
|
||||
def encodeValue(self, value):
|
||||
"""Return raw data string encoded from a ``value``.
|
||||
|
||||
Note: this is an internal method.
|
||||
|
||||
"""
|
||||
#return str(value)[:self.length].ljust(self.length)
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class DbfDateFieldDef(DbfFieldDef):
|
||||
"""Definition of the date field."""
|
||||
|
||||
typeCode = "D"
|
||||
defaultValue = utils.classproperty(lambda cls: datetime.date.today())
|
||||
# "yyyymmdd" gives us 8 characters
|
||||
length = 8
|
||||
|
||||
def decodeValue(self, value):
|
||||
"""Return a ``datetime.date`` instance decoded from ``value``."""
|
||||
if value.strip():
|
||||
return utils.getDate(value)
|
||||
else:
|
||||
return None
|
||||
|
||||
def encodeValue(self, value):
|
||||
"""Return a string-encoded value.
|
||||
|
||||
``value`` argument should be a value suitable for the
|
||||
`utils.getDate` call.
|
||||
|
||||
Return:
|
||||
Return value is a string in format "yyyymmdd".
|
||||
|
||||
"""
|
||||
if value:
|
||||
return utils.getDate(value).strftime("%Y%m%d")
|
||||
else:
|
||||
return " " * self.length
|
||||
|
||||
|
||||
class DbfDateTimeFieldDef(DbfFieldDef):
|
||||
"""Definition of the timestamp field."""
|
||||
|
||||
# a difference between JDN (Julian Day Number)
|
||||
# and GDN (Gregorian Day Number). note, that GDN < JDN
|
||||
JDN_GDN_DIFF = 1721425
|
||||
typeCode = "T"
|
||||
defaultValue = utils.classproperty(lambda cls: datetime.datetime.now())
|
||||
# two 32-bits integers representing JDN and amount of
|
||||
# milliseconds respectively gives us 8 bytes.
|
||||
# note, that values must be encoded in LE byteorder.
|
||||
length = 8
|
||||
|
||||
def decodeValue(self, value):
|
||||
"""Return a `datetime.datetime` instance."""
|
||||
assert len(value) == self.length
|
||||
# LE byteorder
|
||||
_jdn, _msecs = struct.unpack("<2I", value)
|
||||
if _jdn >= 1:
|
||||
_rv = datetime.datetime.fromordinal(_jdn - self.JDN_GDN_DIFF)
|
||||
_rv += datetime.timedelta(0, _msecs / 1000.0)
|
||||
else:
|
||||
# empty date
|
||||
_rv = None
|
||||
return _rv
|
||||
|
||||
def encodeValue(self, value):
|
||||
"""Return a string-encoded ``value``."""
|
||||
if value:
|
||||
value = utils.getDateTime(value)
|
||||
# LE byteorder
|
||||
_rv = struct.pack("<2I", value.toordinal() + self.JDN_GDN_DIFF,
|
||||
(value.hour * 3600 + value.minute * 60 + value.second) * 1000)
|
||||
else:
|
||||
_rv = "\0" * self.length
|
||||
assert len(_rv) == self.length
|
||||
return _rv
|
||||
|
||||
|
||||
_fieldsRegistry = {}
|
||||
|
||||
def registerField(fieldCls):
|
||||
"""Register field definition class.
|
||||
|
||||
``fieldCls`` should be subclass of the `DbfFieldDef`.
|
||||
|
||||
Use `lookupFor` to retrieve field definition class
|
||||
by the type code.
|
||||
|
||||
"""
|
||||
assert fieldCls.typeCode is not None, "Type code isn't defined"
|
||||
# XXX: use fieldCls.typeCode.upper()? in case of any decign
|
||||
# don't forget to look to the same comment in ``lookupFor`` method
|
||||
_fieldsRegistry[fieldCls.typeCode] = fieldCls
|
||||
|
||||
|
||||
def lookupFor(typeCode):
|
||||
"""Return field definition class for the given type code.
|
||||
|
||||
``typeCode`` must be a single character. That type should be
|
||||
previously registered.
|
||||
|
||||
Use `registerField` to register new field class.
|
||||
|
||||
Return:
|
||||
Return value is a subclass of the `DbfFieldDef`.
|
||||
|
||||
"""
|
||||
# XXX: use typeCode.upper()? in case of any decign don't
|
||||
# forget to look to the same comment in ``registerField``
|
||||
return _fieldsRegistry[chr(typeCode)]
|
||||
|
||||
## register generic types
|
||||
|
||||
for (_name, _val) in list(globals().items()):
|
||||
if isinstance(_val, type) and issubclass(_val, DbfFieldDef) \
|
||||
and (_name != "DbfFieldDef"):
|
||||
__all__.append(_name)
|
||||
registerField(_val)
|
||||
del _name, _val
|
||||
|
||||
# vim: et sts=4 sw=4 :
|
||||
@@ -0,0 +1,273 @@
|
||||
"""DBF header definition.
|
||||
|
||||
TODO:
|
||||
- handle encoding of the character fields
|
||||
(encoding information stored in the DBF header)
|
||||
|
||||
"""
|
||||
"""History (most recent first):
|
||||
16-sep-2010 [als] fromStream: fix century of the last update field
|
||||
11-feb-2007 [als] added .ignoreErrors
|
||||
10-feb-2007 [als] added __getitem__: return field definitions
|
||||
by field name or field number (zero-based)
|
||||
04-jul-2006 [als] added export declaration
|
||||
15-dec-2005 [yc] created
|
||||
"""
|
||||
|
||||
__version__ = "$Revision: 1.6 $"[11:-2]
|
||||
__date__ = "$Date: 2010/09/16 05:06:39 $"[7:-2]
|
||||
|
||||
__all__ = ["DbfHeader"]
|
||||
|
||||
import io
|
||||
import datetime
|
||||
import struct
|
||||
import time
|
||||
import sys
|
||||
|
||||
from . import fields
|
||||
from .utils import getDate
|
||||
|
||||
|
||||
class DbfHeader(object):
|
||||
"""Dbf header definition.
|
||||
|
||||
For more information about dbf header format visit
|
||||
`http://www.clicketyclick.dk/databases/xbase/format/dbf.html#DBF_STRUCT`
|
||||
|
||||
Examples:
|
||||
Create an empty dbf header and add some field definitions:
|
||||
dbfh = DbfHeader()
|
||||
dbfh.addField(("name", "C", 10))
|
||||
dbfh.addField(("date", "D"))
|
||||
dbfh.addField(DbfNumericFieldDef("price", 5, 2))
|
||||
Create a dbf header with field definitions:
|
||||
dbfh = DbfHeader([
|
||||
("name", "C", 10),
|
||||
("date", "D"),
|
||||
DbfNumericFieldDef("price", 5, 2),
|
||||
])
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ("signature", "fields", "lastUpdate", "recordLength",
|
||||
"recordCount", "headerLength", "changed", "_ignore_errors")
|
||||
|
||||
## instance construction and initialization methods
|
||||
|
||||
def __init__(self, fields=None, headerLength=0, recordLength=0,
|
||||
recordCount=0, signature=0x03, lastUpdate=None, ignoreErrors=False,
|
||||
):
|
||||
"""Initialize instance.
|
||||
|
||||
Arguments:
|
||||
fields:
|
||||
a list of field definitions;
|
||||
recordLength:
|
||||
size of the records;
|
||||
headerLength:
|
||||
size of the header;
|
||||
recordCount:
|
||||
number of records stored in DBF;
|
||||
signature:
|
||||
version number (aka signature). using 0x03 as a default meaning
|
||||
"File without DBT". for more information about this field visit
|
||||
``http://www.clicketyclick.dk/databases/xbase/format/dbf.html#DBF_NOTE_1_TARGET``
|
||||
lastUpdate:
|
||||
date of the DBF's update. this could be a string ('yymmdd' or
|
||||
'yyyymmdd'), timestamp (int or float), datetime/date value,
|
||||
a sequence (assuming (yyyy, mm, dd, ...)) or an object having
|
||||
callable ``ticks`` field.
|
||||
ignoreErrors:
|
||||
error processing mode for DBF fields (boolean)
|
||||
|
||||
"""
|
||||
self.signature = signature
|
||||
if fields is None:
|
||||
self.fields = []
|
||||
else:
|
||||
self.fields = list(fields)
|
||||
self.lastUpdate = getDate(lastUpdate)
|
||||
self.recordLength = recordLength
|
||||
self.headerLength = headerLength
|
||||
self.recordCount = recordCount
|
||||
self.ignoreErrors = ignoreErrors
|
||||
# XXX: I'm not sure this is safe to
|
||||
# initialize `self.changed` in this way
|
||||
self.changed = bool(self.fields)
|
||||
|
||||
# @classmethod
|
||||
def fromString(cls, string):
|
||||
"""Return header instance from the string object."""
|
||||
return cls.fromStream(io.StringIO(str(string)))
|
||||
fromString = classmethod(fromString)
|
||||
|
||||
# @classmethod
|
||||
def fromStream(cls, stream):
|
||||
"""Return header object from the stream."""
|
||||
stream.seek(0)
|
||||
first_32 = stream.read(32)
|
||||
if type(first_32) != bytes:
|
||||
_data = bytes(first_32, sys.getfilesystemencoding())
|
||||
_data = first_32
|
||||
(_cnt, _hdrLen, _recLen) = struct.unpack("<I2H", _data[4:12])
|
||||
#reserved = _data[12:32]
|
||||
_year = _data[1]
|
||||
if _year < 80:
|
||||
# dBase II started at 1980. It is quite unlikely
|
||||
# that actual last update date is before that year.
|
||||
_year += 2000
|
||||
else:
|
||||
_year += 1900
|
||||
## create header object
|
||||
_obj = cls(None, _hdrLen, _recLen, _cnt, _data[0],
|
||||
(_year, _data[2], _data[3]))
|
||||
## append field definitions
|
||||
# position 0 is for the deletion flag
|
||||
_pos = 1
|
||||
_data = stream.read(1)
|
||||
while _data != b'\r':
|
||||
_data += stream.read(31)
|
||||
_fld = fields.lookupFor(_data[11]).fromString(_data, _pos)
|
||||
_obj._addField(_fld)
|
||||
_pos = _fld.end
|
||||
_data = stream.read(1)
|
||||
return _obj
|
||||
fromStream = classmethod(fromStream)
|
||||
|
||||
## properties
|
||||
|
||||
year = property(lambda self: self.lastUpdate.year)
|
||||
month = property(lambda self: self.lastUpdate.month)
|
||||
day = property(lambda self: self.lastUpdate.day)
|
||||
|
||||
def ignoreErrors(self, value):
|
||||
"""Update `ignoreErrors` flag on self and all fields"""
|
||||
self._ignore_errors = value = bool(value)
|
||||
for _field in self.fields:
|
||||
_field.ignoreErrors = value
|
||||
ignoreErrors = property(
|
||||
lambda self: self._ignore_errors,
|
||||
ignoreErrors,
|
||||
doc="""Error processing mode for DBF field value conversion
|
||||
|
||||
if set, failing field value conversion will return
|
||||
``INVALID_VALUE`` instead of raising conversion error.
|
||||
|
||||
""")
|
||||
|
||||
## object representation
|
||||
|
||||
def __repr__(self):
|
||||
_rv = """\
|
||||
Version (signature): 0x%02x
|
||||
Last update: %s
|
||||
Header length: %d
|
||||
Record length: %d
|
||||
Record count: %d
|
||||
FieldName Type Len Dec
|
||||
""" % (self.signature, self.lastUpdate, self.headerLength,
|
||||
self.recordLength, self.recordCount)
|
||||
_rv += "\n".join(
|
||||
["%10s %4s %3s %3s" % _fld.fieldInfo() for _fld in self.fields]
|
||||
)
|
||||
return _rv
|
||||
|
||||
## internal methods
|
||||
|
||||
def _addField(self, *defs):
|
||||
"""Internal variant of the `addField` method.
|
||||
|
||||
This method doesn't set `self.changed` field to True.
|
||||
|
||||
Return value is a length of the appended records.
|
||||
Note: this method doesn't modify ``recordLength`` and
|
||||
``headerLength`` fields. Use `addField` instead of this
|
||||
method if you don't exactly know what you're doing.
|
||||
|
||||
"""
|
||||
# insure we have dbf.DbfFieldDef instances first (instantiation
|
||||
# from the tuple could raise an error, in such a case I don't
|
||||
# wanna add any of the definitions -- all will be ignored)
|
||||
_defs = []
|
||||
_recordLength = 0
|
||||
for _def in defs:
|
||||
if isinstance(_def, fields.DbfFieldDef):
|
||||
_obj = _def
|
||||
else:
|
||||
(_name, _type, _len, _dec) = (tuple(_def) + (None,) * 4)[:4]
|
||||
_cls = fields.lookupFor(_type)
|
||||
_obj = _cls(_name, _len, _dec,
|
||||
ignoreErrors=self._ignore_errors)
|
||||
_recordLength += _obj.length
|
||||
_defs.append(_obj)
|
||||
# and now extend field definitions and
|
||||
# update record length
|
||||
self.fields += _defs
|
||||
return _recordLength
|
||||
|
||||
## interface methods
|
||||
|
||||
def addField(self, *defs):
|
||||
"""Add field definition to the header.
|
||||
|
||||
Examples:
|
||||
dbfh.addField(
|
||||
("name", "C", 20),
|
||||
dbf.DbfCharacterFieldDef("surname", 20),
|
||||
dbf.DbfDateFieldDef("birthdate"),
|
||||
("member", "L"),
|
||||
)
|
||||
dbfh.addField(("price", "N", 5, 2))
|
||||
dbfh.addField(dbf.DbfNumericFieldDef("origprice", 5, 2))
|
||||
|
||||
"""
|
||||
_oldLen = self.recordLength
|
||||
self.recordLength += self._addField(*defs)
|
||||
if not _oldLen:
|
||||
self.recordLength += 1
|
||||
# XXX: may be just use:
|
||||
# self.recordeLength += self._addField(*defs) + bool(not _oldLen)
|
||||
# recalculate headerLength
|
||||
self.headerLength = 32 + (32 * len(self.fields)) + 1
|
||||
self.changed = True
|
||||
|
||||
def write(self, stream):
|
||||
"""Encode and write header to the stream."""
|
||||
stream.seek(0)
|
||||
stream.write(self.toString())
|
||||
fields = [_fld.toString() for _fld in self.fields]
|
||||
stream.write(''.join(fields).encode(sys.getfilesystemencoding()))
|
||||
stream.write(b'\x0D') # cr at end of all header data
|
||||
self.changed = False
|
||||
|
||||
def toString(self):
|
||||
"""Returned 32 chars length string with encoded header."""
|
||||
return struct.pack("<4BI2H",
|
||||
self.signature,
|
||||
self.year - 1900,
|
||||
self.month,
|
||||
self.day,
|
||||
self.recordCount,
|
||||
self.headerLength,
|
||||
self.recordLength) + (b'\x00' * 20)
|
||||
#TODO: figure out if bytes(utf-8) is correct here.
|
||||
|
||||
def setCurrentDate(self):
|
||||
"""Update ``self.lastUpdate`` field with current date value."""
|
||||
self.lastUpdate = datetime.date.today()
|
||||
|
||||
def __getitem__(self, item):
|
||||
"""Return a field definition by numeric index or name string"""
|
||||
if isinstance(item, str):
|
||||
_name = item.upper()
|
||||
for _field in self.fields:
|
||||
if _field.name == _name:
|
||||
return _field
|
||||
else:
|
||||
raise KeyError(item)
|
||||
else:
|
||||
# item must be field index
|
||||
return self.fields[item]
|
||||
|
||||
# vim: et sts=4 sw=4 :
|
||||
@@ -0,0 +1,266 @@
|
||||
"""DBF record definition.
|
||||
|
||||
"""
|
||||
"""History (most recent first):
|
||||
11-feb-2007 [als] __repr__: added special case for invalid field values
|
||||
10-feb-2007 [als] added .rawFromStream()
|
||||
30-oct-2006 [als] fix record length in .fromStream()
|
||||
04-jul-2006 [als] added export declaration
|
||||
20-dec-2005 [yc] DbfRecord.write() -> DbfRecord._write();
|
||||
added delete() method.
|
||||
16-dec-2005 [yc] record definition moved from `dbf`.
|
||||
"""
|
||||
|
||||
__version__ = "$Revision: 1.7 $"[11:-2]
|
||||
__date__ = "$Date: 2007/02/11 09:05:49 $"[7:-2]
|
||||
|
||||
__all__ = ["DbfRecord"]
|
||||
|
||||
import sys
|
||||
|
||||
from . import utils
|
||||
|
||||
class DbfRecord(object):
|
||||
"""DBF record.
|
||||
|
||||
Instances of this class shouldn't be created manualy,
|
||||
use `dbf.Dbf.newRecord` instead.
|
||||
|
||||
Class implements mapping/sequence interface, so
|
||||
fields could be accessed via their names or indexes
|
||||
(names is a preffered way to access fields).
|
||||
|
||||
Hint:
|
||||
Use `store` method to save modified record.
|
||||
|
||||
Examples:
|
||||
Add new record to the database:
|
||||
db = Dbf(filename)
|
||||
rec = db.newRecord()
|
||||
rec["FIELD1"] = value1
|
||||
rec["FIELD2"] = value2
|
||||
rec.store()
|
||||
Or the same, but modify existed
|
||||
(second in this case) record:
|
||||
db = Dbf(filename)
|
||||
rec = db[2]
|
||||
rec["FIELD1"] = value1
|
||||
rec["FIELD2"] = value2
|
||||
rec.store()
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = "dbf", "index", "deleted", "fieldData"
|
||||
|
||||
## creation and initialization
|
||||
|
||||
def __init__(self, dbf, index=None, deleted=False, data=None):
|
||||
"""Instance initialiation.
|
||||
|
||||
Arguments:
|
||||
dbf:
|
||||
A `Dbf.Dbf` instance this record belonogs to.
|
||||
index:
|
||||
An integer record index or None. If this value is
|
||||
None, record will be appended to the DBF.
|
||||
deleted:
|
||||
Boolean flag indicating whether this record
|
||||
is a deleted record.
|
||||
data:
|
||||
A sequence or None. This is a data of the fields.
|
||||
If this argument is None, default values will be used.
|
||||
|
||||
"""
|
||||
self.dbf = dbf
|
||||
# XXX: I'm not sure ``index`` is necessary
|
||||
self.index = index
|
||||
self.deleted = deleted
|
||||
if data is None:
|
||||
self.fieldData = [_fd.defaultValue for _fd in dbf.header.fields]
|
||||
else:
|
||||
self.fieldData = list(data)
|
||||
|
||||
# XXX: validate self.index before calculating position?
|
||||
position = property(lambda self: self.dbf.header.headerLength + \
|
||||
self.index * self.dbf.header.recordLength)
|
||||
|
||||
def rawFromStream(cls, dbf, index):
|
||||
"""Return raw record contents read from the stream.
|
||||
|
||||
Arguments:
|
||||
dbf:
|
||||
A `Dbf.Dbf` instance containing the record.
|
||||
index:
|
||||
Index of the record in the records' container.
|
||||
This argument can't be None in this call.
|
||||
|
||||
Return value is a string containing record data in DBF format.
|
||||
|
||||
"""
|
||||
# XXX: may be write smth assuming, that current stream
|
||||
# position is the required one? it could save some
|
||||
# time required to calculate where to seek in the file
|
||||
dbf.stream.seek(dbf.header.headerLength +
|
||||
index * dbf.header.recordLength)
|
||||
return dbf.stream.read(dbf.header.recordLength)
|
||||
rawFromStream = classmethod(rawFromStream)
|
||||
|
||||
def fromStream(cls, dbf, index):
|
||||
"""Return a record read from the stream.
|
||||
|
||||
Arguments:
|
||||
dbf:
|
||||
A `Dbf.Dbf` instance new record should belong to.
|
||||
index:
|
||||
Index of the record in the records' container.
|
||||
This argument can't be None in this call.
|
||||
|
||||
Return value is an instance of the current class.
|
||||
|
||||
"""
|
||||
return cls.fromString(dbf, cls.rawFromStream(dbf, index), index)
|
||||
fromStream = classmethod(fromStream)
|
||||
|
||||
def fromString(cls, dbf, string, index=None):
|
||||
"""Return record read from the string object.
|
||||
|
||||
Arguments:
|
||||
dbf:
|
||||
A `Dbf.Dbf` instance new record should belong to.
|
||||
string:
|
||||
A string new record should be created from.
|
||||
index:
|
||||
Index of the record in the container. If this
|
||||
argument is None, record will be appended.
|
||||
|
||||
Return value is an instance of the current class.
|
||||
|
||||
"""
|
||||
return cls(dbf, index, string[0]=="*",
|
||||
[_fd.decodeFromRecord(string) for _fd in dbf.header.fields])
|
||||
fromString = classmethod(fromString)
|
||||
|
||||
## object representation
|
||||
|
||||
def __repr__(self):
|
||||
_template = "%%%ds: %%s (%%s)" % max([len(_fld)
|
||||
for _fld in self.dbf.fieldNames])
|
||||
_rv = []
|
||||
for _fld in self.dbf.fieldNames:
|
||||
_val = self[_fld]
|
||||
if _val is utils.INVALID_VALUE:
|
||||
_rv.append(_template %
|
||||
(_fld, "None", "value cannot be decoded"))
|
||||
else:
|
||||
_rv.append(_template % (_fld, _val, type(_val)))
|
||||
return "\n".join(_rv)
|
||||
|
||||
## protected methods
|
||||
|
||||
def _write(self):
|
||||
"""Write data to the dbf stream.
|
||||
|
||||
Note:
|
||||
This isn't a public method, it's better to
|
||||
use 'store' instead publically.
|
||||
Be design ``_write`` method should be called
|
||||
only from the `Dbf` instance.
|
||||
|
||||
|
||||
"""
|
||||
self._validateIndex(False)
|
||||
self.dbf.stream.seek(self.position)
|
||||
self.dbf.stream.write(bytes(self.toString(),
|
||||
sys.getfilesystemencoding()))
|
||||
# FIXME: may be move this write somewhere else?
|
||||
# why we should check this condition for each record?
|
||||
if self.index == len(self.dbf):
|
||||
# this is the last record,
|
||||
# we should write SUB (ASCII 26)
|
||||
self.dbf.stream.write(b"\x1A")
|
||||
|
||||
## utility methods
|
||||
|
||||
def _validateIndex(self, allowUndefined=True, checkRange=False):
|
||||
"""Valid ``self.index`` value.
|
||||
|
||||
If ``allowUndefined`` argument is True functions does nothing
|
||||
in case of ``self.index`` pointing to None object.
|
||||
|
||||
"""
|
||||
if self.index is None:
|
||||
if not allowUndefined:
|
||||
raise ValueError("Index is undefined")
|
||||
elif self.index < 0:
|
||||
raise ValueError("Index can't be negative (%s)" % self.index)
|
||||
elif checkRange and self.index <= self.dbf.header.recordCount:
|
||||
raise ValueError("There are only %d records in the DBF" %
|
||||
self.dbf.header.recordCount)
|
||||
|
||||
## interface methods
|
||||
|
||||
def store(self):
|
||||
"""Store current record in the DBF.
|
||||
|
||||
If ``self.index`` is None, this record will be appended to the
|
||||
records of the DBF this records belongs to; or replaced otherwise.
|
||||
|
||||
"""
|
||||
self._validateIndex()
|
||||
if self.index is None:
|
||||
self.index = len(self.dbf)
|
||||
self.dbf.append(self)
|
||||
else:
|
||||
self.dbf[self.index] = self
|
||||
|
||||
def delete(self):
|
||||
"""Mark method as deleted."""
|
||||
self.deleted = True
|
||||
|
||||
def toString(self):
|
||||
"""Return string packed record values."""
|
||||
# for (_def, _dat) in zip(self.dbf.header.fields, self.fieldData):
|
||||
#
|
||||
|
||||
return "".join([" *"[self.deleted]] + [
|
||||
_def.encodeValue(_dat)
|
||||
for (_def, _dat) in zip(self.dbf.header.fields, self.fieldData)
|
||||
])
|
||||
|
||||
def asList(self):
|
||||
"""Return a flat list of fields.
|
||||
|
||||
Note:
|
||||
Change of the list's values won't change
|
||||
real values stored in this object.
|
||||
|
||||
"""
|
||||
return self.fieldData[:]
|
||||
|
||||
def asDict(self):
|
||||
"""Return a dictionary of fields.
|
||||
|
||||
Note:
|
||||
Change of the dicts's values won't change
|
||||
real values stored in this object.
|
||||
|
||||
"""
|
||||
return dict([_i for _i in zip(self.dbf.fieldNames, self.fieldData)])
|
||||
|
||||
def __getitem__(self, key):
|
||||
"""Return value by field name or field index."""
|
||||
if isinstance(key, int):
|
||||
# integer index of the field
|
||||
return self.fieldData[key]
|
||||
# assuming string field name
|
||||
return self.fieldData[self.dbf.indexOfFieldName(key)]
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
"""Set field value by integer index of the field or string name."""
|
||||
if isinstance(key, int):
|
||||
# integer index of the field
|
||||
return self.fieldData[key]
|
||||
# assuming string field name
|
||||
self.fieldData[self.dbf.indexOfFieldName(key)] = value
|
||||
|
||||
# vim: et sts=4 sw=4 :
|
||||
@@ -0,0 +1,170 @@
|
||||
"""String utilities.
|
||||
|
||||
TODO:
|
||||
- allow strings in getDateTime routine;
|
||||
"""
|
||||
"""History (most recent first):
|
||||
11-feb-2007 [als] added INVALID_VALUE
|
||||
10-feb-2007 [als] allow date strings padded with spaces instead of zeroes
|
||||
20-dec-2005 [yc] handle long objects in getDate/getDateTime
|
||||
16-dec-2005 [yc] created from ``strutil`` module.
|
||||
"""
|
||||
|
||||
__version__ = "$Revision: 1.4 $"[11:-2]
|
||||
__date__ = "$Date: 2007/02/11 08:57:17 $"[7:-2]
|
||||
|
||||
import datetime
|
||||
import time
|
||||
|
||||
|
||||
def unzfill(str):
|
||||
"""Return a string without ASCII NULs.
|
||||
|
||||
This function searchers for the first NUL (ASCII 0) occurance
|
||||
and truncates string till that position.
|
||||
|
||||
"""
|
||||
try:
|
||||
return str[:str.index(b'\0')]
|
||||
except ValueError:
|
||||
return str
|
||||
|
||||
|
||||
def getDate(date=None):
|
||||
"""Return `datetime.date` instance.
|
||||
|
||||
Type of the ``date`` argument could be one of the following:
|
||||
None:
|
||||
use current date value;
|
||||
datetime.date:
|
||||
this value will be returned;
|
||||
datetime.datetime:
|
||||
the result of the date.date() will be returned;
|
||||
string:
|
||||
assuming "%Y%m%d" or "%y%m%dd" format;
|
||||
number:
|
||||
assuming it's a timestamp (returned for example
|
||||
by the time.time() call;
|
||||
sequence:
|
||||
assuming (year, month, day, ...) sequence;
|
||||
|
||||
Additionaly, if ``date`` has callable ``ticks`` attribute,
|
||||
it will be used and result of the called would be treated
|
||||
as a timestamp value.
|
||||
|
||||
"""
|
||||
if date is None:
|
||||
# use current value
|
||||
return datetime.date.today()
|
||||
if isinstance(date, datetime.date):
|
||||
return date
|
||||
if isinstance(date, datetime.datetime):
|
||||
return date.date()
|
||||
if isinstance(date, (int, float)):
|
||||
# date is a timestamp
|
||||
return datetime.date.fromtimestamp(date)
|
||||
if isinstance(date, str):
|
||||
date = date.replace(" ", "0")
|
||||
if len(date) == 6:
|
||||
# yymmdd
|
||||
return datetime.date(*time.strptime(date, "%y%m%d")[:3])
|
||||
# yyyymmdd
|
||||
return datetime.date(*time.strptime(date, "%Y%m%d")[:3])
|
||||
if hasattr(date, "__getitem__"):
|
||||
# a sequence (assuming date/time tuple)
|
||||
return datetime.date(*date[:3])
|
||||
return datetime.date.fromtimestamp(date.ticks())
|
||||
|
||||
|
||||
def getDateTime(value=None):
|
||||
"""Return `datetime.datetime` instance.
|
||||
|
||||
Type of the ``value`` argument could be one of the following:
|
||||
None:
|
||||
use current date value;
|
||||
datetime.date:
|
||||
result will be converted to the `datetime.datetime` instance
|
||||
using midnight;
|
||||
datetime.datetime:
|
||||
``value`` will be returned as is;
|
||||
string:
|
||||
*** CURRENTLY NOT SUPPORTED ***;
|
||||
number:
|
||||
assuming it's a timestamp (returned for example
|
||||
by the time.time() call;
|
||||
sequence:
|
||||
assuming (year, month, day, ...) sequence;
|
||||
|
||||
Additionaly, if ``value`` has callable ``ticks`` attribute,
|
||||
it will be used and result of the called would be treated
|
||||
as a timestamp value.
|
||||
|
||||
"""
|
||||
if value is None:
|
||||
# use current value
|
||||
return datetime.datetime.today()
|
||||
if isinstance(value, datetime.datetime):
|
||||
return value
|
||||
if isinstance(value, datetime.date):
|
||||
return datetime.datetime.fromordinal(value.toordinal())
|
||||
if isinstance(value, (int, float)):
|
||||
# value is a timestamp
|
||||
return datetime.datetime.fromtimestamp(value)
|
||||
if isinstance(value, str):
|
||||
raise NotImplementedError("Strings aren't currently implemented")
|
||||
if hasattr(value, "__getitem__"):
|
||||
# a sequence (assuming date/time tuple)
|
||||
return datetime.datetime(*tuple(value)[:6])
|
||||
return datetime.datetime.fromtimestamp(value.ticks())
|
||||
|
||||
|
||||
class classproperty(property):
|
||||
"""Works in the same way as a ``property``, but for the classes."""
|
||||
|
||||
def __get__(self, obj, cls):
|
||||
return self.fget(cls)
|
||||
|
||||
|
||||
class _InvalidValue(object):
|
||||
|
||||
"""Value returned from DBF records when field validation fails
|
||||
|
||||
The value is not equal to anything except for itself
|
||||
and equal to all empty values: None, 0, empty string etc.
|
||||
In other words, invalid value is equal to None and not equal
|
||||
to None at the same time.
|
||||
|
||||
This value yields zero upon explicit conversion to a number type,
|
||||
empty string for string types, and False for boolean.
|
||||
|
||||
"""
|
||||
|
||||
def __eq__(self, other):
|
||||
return not other
|
||||
|
||||
def __ne__(self, other):
|
||||
return not (other is self)
|
||||
|
||||
def __bool__(self):
|
||||
return False
|
||||
|
||||
def __int__(self):
|
||||
return 0
|
||||
__long__ = __int__
|
||||
|
||||
def __float__(self):
|
||||
return 0.0
|
||||
|
||||
def __str__(self):
|
||||
return ""
|
||||
|
||||
def __unicode__(self):
|
||||
return ""
|
||||
|
||||
def __repr__(self):
|
||||
return "<INVALID>"
|
||||
|
||||
# invalid value is a constant singleton
|
||||
INVALID_VALUE = _InvalidValue()
|
||||
|
||||
# vim: set et sts=4 sw=4 :
|
||||
+32
-34
@@ -33,7 +33,7 @@ class element:
|
||||
self.tag = tag.lower( )
|
||||
else:
|
||||
self.tag = tag.upper( )
|
||||
|
||||
|
||||
def __call__( self, *args, **kwargs ):
|
||||
if len( args ) > 1:
|
||||
raise ArgumentError( self.tag )
|
||||
@@ -42,14 +42,14 @@ class element:
|
||||
if self.parent is not None and self.parent.class_ is not None:
|
||||
if 'class_' not in kwargs:
|
||||
kwargs['class_'] = self.parent.class_
|
||||
|
||||
|
||||
if self.parent is None and len( args ) == 1:
|
||||
x = [ self.render( self.tag, False, myarg, mydict ) for myarg, mydict in _argsdicts( args, kwargs ) ]
|
||||
return '\n'.join( x )
|
||||
elif self.parent is None and len( args ) == 0:
|
||||
x = [ self.render( self.tag, True, myarg, mydict ) for myarg, mydict in _argsdicts( args, kwargs ) ]
|
||||
return '\n'.join( x )
|
||||
|
||||
|
||||
if self.tag in self.parent.twotags:
|
||||
for myarg, mydict in _argsdicts( args, kwargs ):
|
||||
self.render( self.tag, False, myarg, mydict )
|
||||
@@ -63,33 +63,31 @@ class element:
|
||||
raise DeprecationError( self.tag )
|
||||
else:
|
||||
raise InvalidElementError( self.tag, self.parent.mode )
|
||||
|
||||
|
||||
def render( self, tag, single, between, kwargs ):
|
||||
"""Append the actual tags to content."""
|
||||
|
||||
out = "<%s" % tag
|
||||
out = u"<%s" % tag
|
||||
for key, value in kwargs.iteritems( ):
|
||||
if value is not None: # when value is None that means stuff like <... checked>
|
||||
key = key.strip('_') # strip this so class_ will mean class, etc.
|
||||
if key == 'http_equiv': # special cases, maybe change _ to - overall?
|
||||
key = 'http-equiv'
|
||||
elif key == 'accept_charset':
|
||||
key = 'accept-charset'
|
||||
out = "%s %s=\"%s\"" % ( out, key, escape( value ) )
|
||||
if key in ['http_equiv', 'accept_charset']:
|
||||
key.replace('_','-')
|
||||
out = u"%s %s=\"%s\"" % ( out, key, escape( value ) )
|
||||
else:
|
||||
out = "%s %s" % ( out, key )
|
||||
out = u"%s %s" % ( out, key )
|
||||
if between is not None:
|
||||
out = "%s>%s</%s>" % ( out, between, tag )
|
||||
out = u"%s>%s</%s>" % ( out, between, tag )
|
||||
else:
|
||||
if single:
|
||||
out = "%s />" % out
|
||||
out = u"%s />" % out
|
||||
else:
|
||||
out = "%s>" % out
|
||||
out = u"%s>" % out
|
||||
if self.parent is not None:
|
||||
self.parent.content.append( out )
|
||||
else:
|
||||
return out
|
||||
|
||||
|
||||
def close( self ):
|
||||
"""Append a closing tag unless element has only opening tag."""
|
||||
|
||||
@@ -128,11 +126,11 @@ class page:
|
||||
these two keyword arguments may be used to select
|
||||
the set of valid elements in 'xml' mode
|
||||
invalid elements will raise appropriate exceptions
|
||||
|
||||
|
||||
separator -- string to place between added elements, defaults to newline
|
||||
|
||||
|
||||
class_ -- a class that will be added to every element if defined"""
|
||||
|
||||
|
||||
valid_onetags = [ "AREA", "BASE", "BR", "COL", "FRAME", "HR", "IMG", "INPUT", "LINK", "META", "PARAM" ]
|
||||
valid_twotags = [ "A", "ABBR", "ACRONYM", "ADDRESS", "B", "BDO", "BIG", "BLOCKQUOTE", "BODY", "BUTTON",
|
||||
"CAPTION", "CITE", "CODE", "COLGROUP", "DD", "DEL", "DFN", "DIV", "DL", "DT", "EM", "FIELDSET",
|
||||
@@ -163,7 +161,7 @@ class page:
|
||||
self.deptags += map( string.lower, self.deptags )
|
||||
self.mode = 'strict_html'
|
||||
elif mode == 'loose_html':
|
||||
self.onetags = valid_onetags + deprecated_onetags
|
||||
self.onetags = valid_onetags + deprecated_onetags
|
||||
self.onetags += map( string.lower, self.onetags )
|
||||
self.twotags = valid_twotags + deprecated_twotags
|
||||
self.twotags += map( string.lower, self.twotags )
|
||||
@@ -183,16 +181,16 @@ class page:
|
||||
|
||||
def __getattr__( self, attr ):
|
||||
if attr.startswith("__") and attr.endswith("__"):
|
||||
raise AttributeError, attr
|
||||
raise AttributeError(attr)
|
||||
return element( attr, case=self.case, parent=self )
|
||||
|
||||
def __str__( self ):
|
||||
|
||||
|
||||
if self._full and ( self.mode == 'strict_html' or self.mode == 'loose_html' ):
|
||||
end = [ '</body>', '</html>' ]
|
||||
else:
|
||||
end = [ ]
|
||||
|
||||
|
||||
return self.separator.join( self.header + self.content + self.footer + end )
|
||||
|
||||
def __call__( self, escape=False ):
|
||||
@@ -232,7 +230,7 @@ class page:
|
||||
|
||||
lang -- language, usually a two character string, will appear
|
||||
as <html lang='en'> in html mode (ignored in xml mode)
|
||||
|
||||
|
||||
css -- Cascading Style Sheet filename as a string or a list of
|
||||
strings for multiple css files (ignored in xml mode)
|
||||
|
||||
@@ -306,7 +304,7 @@ class page:
|
||||
def css( self, filelist ):
|
||||
"""This convenience function is only useful for html.
|
||||
It adds css stylesheet(s) to the document via the <link> element."""
|
||||
|
||||
|
||||
if isinstance( filelist, basestring ):
|
||||
self.link( href=filelist, rel='stylesheet', type='text/css', media='all' )
|
||||
else:
|
||||
@@ -322,7 +320,7 @@ class page:
|
||||
for name, content in mydict.iteritems( ):
|
||||
self.meta( name=name, content=content )
|
||||
else:
|
||||
raise TypeError, "Metainfo should be called with a dictionary argument of name:content pairs."
|
||||
raise TypeError ("Metainfo should be called with a dictionary argument of name:content pairs.")
|
||||
|
||||
def scripts( self, mydict ):
|
||||
"""Only useful in html, mydict is dictionary of src:type pairs will
|
||||
@@ -332,20 +330,20 @@ class page:
|
||||
for src, type in mydict.iteritems( ):
|
||||
self.script( '', src=src, type='text/%s' % type )
|
||||
else:
|
||||
raise TypeError, "Script should be given a dictionary of src:type pairs."
|
||||
raise TypeError ("Script should be given a dictionary of src:type pairs.")
|
||||
|
||||
|
||||
class _oneliner:
|
||||
"""An instance of oneliner returns a string corresponding to one element.
|
||||
This class can be used to write 'oneliners' that return a string
|
||||
immediately so there is no need to instantiate the page class."""
|
||||
|
||||
|
||||
def __init__( self, case='lower' ):
|
||||
self.case = case
|
||||
|
||||
|
||||
def __getattr__( self, attr ):
|
||||
if attr.startswith("__") and attr.endswith("__"):
|
||||
raise AttributeError, attr
|
||||
raise AttributeError(attr)
|
||||
return element( attr, case=self.case, parent=None )
|
||||
|
||||
oneliner = _oneliner( case='lower' )
|
||||
@@ -353,13 +351,13 @@ upper_oneliner = _oneliner( case='upper' )
|
||||
|
||||
def _argsdicts( args, mydict ):
|
||||
"""A utility generator that pads argument list and dictionary values, will only be called with len( args ) = 0, 1."""
|
||||
|
||||
|
||||
if len( args ) == 0:
|
||||
args = None,
|
||||
args = None,
|
||||
elif len( args ) == 1:
|
||||
args = _totuple( args[0] )
|
||||
else:
|
||||
raise Exception, "We should have never gotten here."
|
||||
raise Exception("We should have never gotten here.")
|
||||
|
||||
mykeys = mydict.keys( )
|
||||
myvalues = map( _totuple, mydict.values( ) )
|
||||
@@ -418,7 +416,7 @@ _escape = escape
|
||||
|
||||
def unescape( text ):
|
||||
"""Inverse of escape."""
|
||||
|
||||
|
||||
if isinstance( text, basestring ):
|
||||
if '&' in text:
|
||||
text = text.replace( '&', '&' )
|
||||
@@ -481,4 +479,4 @@ class CustomizationError( MarkupError ):
|
||||
self.message = "If you customize the allowed elements, you must define both types 'onetags' and 'twotags'."
|
||||
|
||||
if __name__ == '__main__':
|
||||
print __doc__
|
||||
print (__doc__)
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
|
||||
from namespaces import ANIMNS
|
||||
from element import Element
|
||||
|
||||
|
||||
# Autogenerated
|
||||
def Animate(**args):
|
||||
return Element(qname = (ANIMNS,'animate'), **args)
|
||||
|
||||
def Animatecolor(**args):
|
||||
return Element(qname = (ANIMNS,'animateColor'), **args)
|
||||
|
||||
def Animatemotion(**args):
|
||||
return Element(qname = (ANIMNS,'animateMotion'), **args)
|
||||
|
||||
def Animatetransform(**args):
|
||||
return Element(qname = (ANIMNS,'animateTransform'), **args)
|
||||
|
||||
def Audio(**args):
|
||||
return Element(qname = (ANIMNS,'audio'), **args)
|
||||
|
||||
def Command(**args):
|
||||
return Element(qname = (ANIMNS,'command'), **args)
|
||||
|
||||
def Iterate(**args):
|
||||
return Element(qname = (ANIMNS,'iterate'), **args)
|
||||
|
||||
def Par(**args):
|
||||
return Element(qname = (ANIMNS,'par'), **args)
|
||||
|
||||
def Param(**args):
|
||||
return Element(qname = (ANIMNS,'param'), **args)
|
||||
|
||||
def Seq(**args):
|
||||
return Element(qname = (ANIMNS,'seq'), **args)
|
||||
|
||||
def Set(**args):
|
||||
return Element(qname = (ANIMNS,'set'), **args)
|
||||
|
||||
def Transitionfilter(**args):
|
||||
return Element(qname = (ANIMNS,'transitionFilter'), **args)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,87 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
|
||||
from namespaces import CHARTNS
|
||||
from element import Element
|
||||
|
||||
# Autogenerated
|
||||
def Axis(**args):
|
||||
return Element(qname = (CHARTNS,'axis'), **args)
|
||||
|
||||
def Categories(**args):
|
||||
return Element(qname = (CHARTNS,'categories'), **args)
|
||||
|
||||
def Chart(**args):
|
||||
return Element(qname = (CHARTNS,'chart'), **args)
|
||||
|
||||
def DataPoint(**args):
|
||||
return Element(qname = (CHARTNS,'data-point'), **args)
|
||||
|
||||
def Domain(**args):
|
||||
return Element(qname = (CHARTNS,'domain'), **args)
|
||||
|
||||
def ErrorIndicator(**args):
|
||||
return Element(qname = (CHARTNS,'error-indicator'), **args)
|
||||
|
||||
def Floor(**args):
|
||||
return Element(qname = (CHARTNS,'floor'), **args)
|
||||
|
||||
def Footer(**args):
|
||||
return Element(qname = (CHARTNS,'footer'), **args)
|
||||
|
||||
def Grid(**args):
|
||||
return Element(qname = (CHARTNS,'grid'), **args)
|
||||
|
||||
def Legend(**args):
|
||||
return Element(qname = (CHARTNS,'legend'), **args)
|
||||
|
||||
def MeanValue(**args):
|
||||
return Element(qname = (CHARTNS,'mean-value'), **args)
|
||||
|
||||
def PlotArea(**args):
|
||||
return Element(qname = (CHARTNS,'plot-area'), **args)
|
||||
|
||||
def RegressionCurve(**args):
|
||||
return Element(qname = (CHARTNS,'regression-curve'), **args)
|
||||
|
||||
def Series(**args):
|
||||
return Element(qname = (CHARTNS,'series'), **args)
|
||||
|
||||
def StockGainMarker(**args):
|
||||
return Element(qname = (CHARTNS,'stock-gain-marker'), **args)
|
||||
|
||||
def StockLossMarker(**args):
|
||||
return Element(qname = (CHARTNS,'stock-loss-marker'), **args)
|
||||
|
||||
def StockRangeLine(**args):
|
||||
return Element(qname = (CHARTNS,'stock-range-line'), **args)
|
||||
|
||||
def Subtitle(**args):
|
||||
return Element(qname = (CHARTNS,'subtitle'), **args)
|
||||
|
||||
def SymbolImage(**args):
|
||||
return Element(qname = (CHARTNS,'symbol-image'), **args)
|
||||
|
||||
def Title(**args):
|
||||
return Element(qname = (CHARTNS,'title'), **args)
|
||||
|
||||
def Wall(**args):
|
||||
return Element(qname = (CHARTNS,'wall'), **args)
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
|
||||
from namespaces import CONFIGNS
|
||||
from element import Element
|
||||
|
||||
# Autogenerated
|
||||
def ConfigItem(**args):
|
||||
return Element(qname = (CONFIGNS, 'config-item'), **args)
|
||||
|
||||
def ConfigItemMapEntry(**args):
|
||||
return Element(qname = (CONFIGNS,'config-item-map-entry'), **args)
|
||||
|
||||
def ConfigItemMapIndexed(**args):
|
||||
return Element(qname = (CONFIGNS,'config-item-map-indexed'), **args)
|
||||
|
||||
def ConfigItemMapNamed(**args):
|
||||
return Element(qname = (CONFIGNS,'config-item-map-named'), **args)
|
||||
|
||||
def ConfigItemSet(**args):
|
||||
return Element(qname = (CONFIGNS, 'config-item-set'), **args)
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
|
||||
from namespaces import DCNS
|
||||
from element import Element
|
||||
|
||||
# Autogenerated
|
||||
def Creator(**args):
|
||||
return Element(qname = (DCNS,'creator'), **args)
|
||||
|
||||
def Date(**args):
|
||||
return Element(qname = (DCNS,'date'), **args)
|
||||
|
||||
def Description(**args):
|
||||
return Element(qname = (DCNS,'description'), **args)
|
||||
|
||||
def Language(**args):
|
||||
return Element(qname = (DCNS,'language'), **args)
|
||||
|
||||
def Subject(**args):
|
||||
return Element(qname = (DCNS,'subject'), **args)
|
||||
|
||||
def Title(**args):
|
||||
return Element(qname = (DCNS,'title'), **args)
|
||||
|
||||
# The following complete the Dublin Core elements, but there is no
|
||||
# guarantee a compliant implementation of OpenDocument will preserve
|
||||
# these elements
|
||||
|
||||
#def Contributor(**args):
|
||||
# return Element(qname = (DCNS,'contributor'), **args)
|
||||
|
||||
#def Coverage(**args):
|
||||
# return Element(qname = (DCNS,'coverage'), **args)
|
||||
|
||||
#def Format(**args):
|
||||
# return Element(qname = (DCNS,'format'), **args)
|
||||
|
||||
#def Identifier(**args):
|
||||
# return Element(qname = (DCNS,'identifier'), **args)
|
||||
|
||||
#def Publisher(**args):
|
||||
# return Element(qname = (DCNS,'publisher'), **args)
|
||||
|
||||
#def Relation(**args):
|
||||
# return Element(qname = (DCNS,'relation'), **args)
|
||||
|
||||
#def Rights(**args):
|
||||
# return Element(qname = (DCNS,'rights'), **args)
|
||||
|
||||
#def Source(**args):
|
||||
# return Element(qname = (DCNS,'source'), **args)
|
||||
|
||||
#def Type(**args):
|
||||
# return Element(qname = (DCNS,'type'), **args)
|
||||
@@ -1,43 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
|
||||
from namespaces import DR3DNS
|
||||
from element import Element
|
||||
from draw import StyleRefElement
|
||||
|
||||
# Autogenerated
|
||||
def Cube(**args):
|
||||
return StyleRefElement(qname = (DR3DNS,'cube'), **args)
|
||||
|
||||
def Extrude(**args):
|
||||
return StyleRefElement(qname = (DR3DNS,'extrude'), **args)
|
||||
|
||||
def Light(Element):
|
||||
return StyleRefElement(qname = (DR3DNS,'light'), **args)
|
||||
|
||||
def Rotate(**args):
|
||||
return StyleRefElement(qname = (DR3DNS,'rotate'), **args)
|
||||
|
||||
def Scene(**args):
|
||||
return StyleRefElement(qname = (DR3DNS,'scene'), **args)
|
||||
|
||||
def Sphere(**args):
|
||||
return StyleRefElement(qname = (DR3DNS,'sphere'), **args)
|
||||
|
||||
@@ -1,182 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
|
||||
from namespaces import DRAWNS, STYLENS, PRESENTATIONNS
|
||||
from element import Element
|
||||
|
||||
def StyleRefElement(stylename=None, classnames=None, **args):
|
||||
qattrs = {}
|
||||
if stylename is not None:
|
||||
f = stylename.getAttrNS(STYLENS, 'family')
|
||||
if f == 'graphic':
|
||||
qattrs[(DRAWNS,u'style-name')]= stylename
|
||||
elif f == 'presentation':
|
||||
qattrs[(PRESENTATIONNS,u'style-name')]= stylename
|
||||
else:
|
||||
raise ValueError, "Style's family must be either 'graphic' or 'presentation'"
|
||||
if classnames is not None:
|
||||
f = classnames[0].getAttrNS(STYLENS, 'family')
|
||||
if f == 'graphic':
|
||||
qattrs[(DRAWNS,u'class-names')]= classnames
|
||||
elif f == 'presentation':
|
||||
qattrs[(PRESENTATIONNS,u'class-names')]= classnames
|
||||
else:
|
||||
raise ValueError, "Style's family must be either 'graphic' or 'presentation'"
|
||||
return Element(qattributes=qattrs, **args)
|
||||
|
||||
def DrawElement(name=None, **args):
|
||||
e = Element(name=name, **args)
|
||||
if not args.has_key('displayname'):
|
||||
e.setAttrNS(DRAWNS,'display-name', name)
|
||||
return e
|
||||
|
||||
# Autogenerated
|
||||
def A(**args):
|
||||
return Element(qname = (DRAWNS,'a'), **args)
|
||||
|
||||
def Applet(**args):
|
||||
return Element(qname = (DRAWNS,'applet'), **args)
|
||||
|
||||
def AreaCircle(**args):
|
||||
return Element(qname = (DRAWNS,'area-circle'), **args)
|
||||
|
||||
def AreaPolygon(**args):
|
||||
return Element(qname = (DRAWNS,'area-polygon'), **args)
|
||||
|
||||
def AreaRectangle(**args):
|
||||
return Element(qname = (DRAWNS,'area-rectangle'), **args)
|
||||
|
||||
def Caption(**args):
|
||||
return StyleRefElement(qname = (DRAWNS,'caption'), **args)
|
||||
|
||||
def Circle(**args):
|
||||
return StyleRefElement(qname = (DRAWNS,'circle'), **args)
|
||||
|
||||
def Connector(**args):
|
||||
return StyleRefElement(qname = (DRAWNS,'connector'), **args)
|
||||
|
||||
def ContourPath(**args):
|
||||
return Element(qname = (DRAWNS,'contour-path'), **args)
|
||||
|
||||
def ContourPolygon(**args):
|
||||
return Element(qname = (DRAWNS,'contour-polygon'), **args)
|
||||
|
||||
def Control(**args):
|
||||
return StyleRefElement(qname = (DRAWNS,'control'), **args)
|
||||
|
||||
def CustomShape(**args):
|
||||
return StyleRefElement(qname = (DRAWNS,'custom-shape'), **args)
|
||||
|
||||
def Ellipse(**args):
|
||||
return StyleRefElement(qname = (DRAWNS,'ellipse'), **args)
|
||||
|
||||
def EnhancedGeometry(**args):
|
||||
return Element(qname = (DRAWNS,'enhanced-geometry'), **args)
|
||||
|
||||
def Equation(**args):
|
||||
return Element(qname = (DRAWNS,'equation'), **args)
|
||||
|
||||
def FillImage(**args):
|
||||
return DrawElement(qname = (DRAWNS,'fill-image'), **args)
|
||||
|
||||
def FloatingFrame(**args):
|
||||
return Element(qname = (DRAWNS,'floating-frame'), **args)
|
||||
|
||||
def Frame(**args):
|
||||
return StyleRefElement(qname = (DRAWNS,'frame'), **args)
|
||||
|
||||
def G(**args):
|
||||
return StyleRefElement(qname = (DRAWNS,'g'), **args)
|
||||
|
||||
def GluePoint(**args):
|
||||
return Element(qname = (DRAWNS,'glue-point'), **args)
|
||||
|
||||
def Gradient(**args):
|
||||
return DrawElement(qname = (DRAWNS,'gradient'), **args)
|
||||
|
||||
def Handle(**args):
|
||||
return Element(qname = (DRAWNS,'handle'), **args)
|
||||
|
||||
def Hatch(**args):
|
||||
return DrawElement(qname = (DRAWNS,'hatch'), **args)
|
||||
|
||||
def Image(**args):
|
||||
return Element(qname = (DRAWNS,'image'), **args)
|
||||
|
||||
def ImageMap(**args):
|
||||
return Element(qname = (DRAWNS,'image-map'), **args)
|
||||
|
||||
def Layer(**args):
|
||||
return Element(qname = (DRAWNS,'layer'), **args)
|
||||
|
||||
def LayerSet(**args):
|
||||
return Element(qname = (DRAWNS,'layer-set'), **args)
|
||||
|
||||
def Line(**args):
|
||||
return StyleRefElement(qname = (DRAWNS,'line'), **args)
|
||||
|
||||
def Marker(**args):
|
||||
return DrawElement(qname = (DRAWNS,'marker'), **args)
|
||||
|
||||
def Measure(**args):
|
||||
return StyleRefElement(qname = (DRAWNS,'measure'), **args)
|
||||
|
||||
def Object(**args):
|
||||
return Element(qname = (DRAWNS,'object'), **args)
|
||||
|
||||
def ObjectOle(**args):
|
||||
return Element(qname = (DRAWNS,'object-ole'), **args)
|
||||
|
||||
def Opacity(**args):
|
||||
return DrawElement(qname = (DRAWNS,'opacity'), **args)
|
||||
|
||||
def Page(**args):
|
||||
return Element(qname = (DRAWNS,'page'), **args)
|
||||
|
||||
def PageThumbnail(**args):
|
||||
return StyleRefElement(qname = (DRAWNS,'page-thumbnail'), **args)
|
||||
|
||||
def Param(**args):
|
||||
return Element(qname = (DRAWNS,'param'), **args)
|
||||
|
||||
def Path(**args):
|
||||
return StyleRefElement(qname = (DRAWNS,'path'), **args)
|
||||
|
||||
def Plugin(**args):
|
||||
return Element(qname = (DRAWNS,'plugin'), **args)
|
||||
|
||||
def Polygon(**args):
|
||||
return StyleRefElement(qname = (DRAWNS,'polygon'), **args)
|
||||
|
||||
def Polyline(**args):
|
||||
return StyleRefElement(qname = (DRAWNS,'polyline'), **args)
|
||||
|
||||
def Rect(**args):
|
||||
return StyleRefElement(qname = (DRAWNS,'rect'), **args)
|
||||
|
||||
def RegularPolygon(**args):
|
||||
return StyleRefElement(qname = (DRAWNS,'regular-polygon'), **args)
|
||||
|
||||
def StrokeDash(**args):
|
||||
return DrawElement(qname = (DRAWNS,'stroke-dash'), **args)
|
||||
|
||||
def TextBox(**args):
|
||||
return Element(qname = (DRAWNS,'text-box'), **args)
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Create a <text:list-style> element from a text string.
|
||||
# Copyright (C) 2008 J. David Eisenberg
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
|
||||
import re
|
||||
from style import Style, TextProperties, ListLevelProperties
|
||||
from text import ListStyle,ListLevelStyleNumber,ListLevelStyleBullet
|
||||
|
||||
"""
|
||||
Create a <text:list-style> element from a string or array.
|
||||
|
||||
List styles require a lot of code to create one level at a time.
|
||||
These routines take a string and delimiter, or a list of
|
||||
strings, and creates a <text:list-style> element for you.
|
||||
Each item in the string (or array) represents a list level
|
||||
* style for levels 1-10.</p>
|
||||
*
|
||||
* <p>If an item contains <code>1</code>, <code>I</code>,
|
||||
* <code>i</code>, <code>A</code>, or <code>a</code>, then it is presumed
|
||||
* to be a numbering style; otherwise it is a bulleted style.</p>
|
||||
"""
|
||||
|
||||
_MAX_LIST_LEVEL = 10
|
||||
SHOW_ALL_LEVELS = True
|
||||
SHOW_ONE_LEVEL = False
|
||||
|
||||
def styleFromString(name, specifiers, delim, spacing, showAllLevels):
|
||||
specArray = specifiers.split(delim)
|
||||
return styleFromList( name, specArray, spacing, showAllLevels )
|
||||
|
||||
def styleFromList( styleName, specArray, spacing, showAllLevels):
|
||||
bullet = ""
|
||||
numPrefix = ""
|
||||
numSuffix = ""
|
||||
numberFormat = ""
|
||||
cssLengthNum = 0
|
||||
cssLengthUnits = ""
|
||||
numbered = False
|
||||
displayLevels = 0
|
||||
listStyle = ListStyle(name=styleName)
|
||||
numFormatPattern = re.compile("([1IiAa])")
|
||||
cssLengthPattern = re.compile("([^a-z]+)\\s*([a-z]+)?")
|
||||
m = cssLengthPattern.search( spacing )
|
||||
if (m != None):
|
||||
cssLengthNum = float(m.group(1))
|
||||
if (m.lastindex == 2):
|
||||
cssLengthUnits = m.group(2)
|
||||
i = 0
|
||||
while i < len(specArray):
|
||||
specification = specArray[i]
|
||||
m = numFormatPattern.search(specification)
|
||||
if (m != None):
|
||||
numberFormat = m.group(1)
|
||||
numPrefix = specification[0:m.start(1)]
|
||||
numSuffix = specification[m.end(1):]
|
||||
bullet = ""
|
||||
numbered = True
|
||||
if (showAllLevels):
|
||||
displayLevels = i + 1
|
||||
else:
|
||||
displayLevels = 1
|
||||
else: # it's a bullet style
|
||||
bullet = specification
|
||||
numPrefix = ""
|
||||
numSuffix = ""
|
||||
numberFormat = ""
|
||||
displayLevels = 1
|
||||
numbered = False
|
||||
if (numbered):
|
||||
lls = ListLevelStyleNumber(level=(i+1))
|
||||
if (numPrefix != ''):
|
||||
lls.setAttribute('numprefix', numPrefix)
|
||||
if (numSuffix != ''):
|
||||
lls.setAttribute('numsuffix', numSuffix)
|
||||
lls.setAttribute('displaylevels', displayLevels)
|
||||
else:
|
||||
lls = ListLevelStyleBullet(level=(i+1),bulletchar=bullet[0])
|
||||
llp = ListLevelProperties()
|
||||
llp.setAttribute('spacebefore', str(cssLengthNum * (i+1)) + cssLengthUnits)
|
||||
llp.setAttribute('minlabelwidth', str(cssLengthNum) + cssLengthUnits)
|
||||
lls.addElement( llp )
|
||||
listStyle.addElement(lls)
|
||||
i += 1
|
||||
return listStyle
|
||||
|
||||
# vim: set expandtab sw=4 :
|
||||
@@ -1,513 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2007-2010 Søren Roug, European Environment Agency
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
|
||||
# Note: This script has copied a lot of text from xml.dom.minidom.
|
||||
# Whatever license applies to that file also applies to this file.
|
||||
#
|
||||
import xml.dom
|
||||
from xml.dom.minicompat import *
|
||||
from namespaces import nsdict
|
||||
import grammar
|
||||
from attrconverters import AttrConverters
|
||||
|
||||
# The following code is pasted form xml.sax.saxutils
|
||||
# Tt makes it possible to run the code without the xml sax package installed
|
||||
# To make it possible to have <rubbish> in your text elements, it is necessary to escape the texts
|
||||
def _escape(data, entities={}):
|
||||
""" Escape &, <, and > in a string of data.
|
||||
|
||||
You can escape other strings of data by passing a dictionary as
|
||||
the optional entities parameter. The keys and values must all be
|
||||
strings; each key will be replaced with its corresponding value.
|
||||
"""
|
||||
data = data.replace("&", "&")
|
||||
data = data.replace("<", "<")
|
||||
data = data.replace(">", ">")
|
||||
for chars, entity in entities.items():
|
||||
data = data.replace(chars, entity)
|
||||
return data
|
||||
|
||||
def _quoteattr(data, entities={}):
|
||||
""" Escape and quote an attribute value.
|
||||
|
||||
Escape &, <, and > in a string of data, then quote it for use as
|
||||
an attribute value. The \" character will be escaped as well, if
|
||||
necessary.
|
||||
|
||||
You can escape other strings of data by passing a dictionary as
|
||||
the optional entities parameter. The keys and values must all be
|
||||
strings; each key will be replaced with its corresponding value.
|
||||
"""
|
||||
entities['\n']=' '
|
||||
entities['\r']=''
|
||||
data = _escape(data, entities)
|
||||
if '"' in data:
|
||||
if "'" in data:
|
||||
data = '"%s"' % data.replace('"', """)
|
||||
else:
|
||||
data = "'%s'" % data
|
||||
else:
|
||||
data = '"%s"' % data
|
||||
return data
|
||||
|
||||
def _nssplit(qualifiedName):
|
||||
""" Split a qualified name into namespace part and local part. """
|
||||
fields = qualifiedName.split(':', 1)
|
||||
if len(fields) == 2:
|
||||
return fields
|
||||
else:
|
||||
return (None, fields[0])
|
||||
|
||||
def _nsassign(namespace):
|
||||
return nsdict.setdefault(namespace,"ns" + str(len(nsdict)))
|
||||
|
||||
# Exceptions
|
||||
class IllegalChild(StandardError):
|
||||
""" Complains if you add an element to a parent where it is not allowed """
|
||||
class IllegalText(StandardError):
|
||||
""" Complains if you add text or cdata to an element where it is not allowed """
|
||||
|
||||
class Node(xml.dom.Node):
|
||||
""" super class for more specific nodes """
|
||||
parentNode = None
|
||||
nextSibling = None
|
||||
previousSibling = None
|
||||
|
||||
def hasChildNodes(self):
|
||||
""" Tells whether this element has any children; text nodes,
|
||||
subelements, whatever.
|
||||
"""
|
||||
if self.childNodes:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def _get_childNodes(self):
|
||||
return self.childNodes
|
||||
|
||||
def _get_firstChild(self):
|
||||
if self.childNodes:
|
||||
return self.childNodes[0]
|
||||
|
||||
def _get_lastChild(self):
|
||||
if self.childNodes:
|
||||
return self.childNodes[-1]
|
||||
|
||||
def insertBefore(self, newChild, refChild):
|
||||
""" Inserts the node newChild before the existing child node refChild.
|
||||
If refChild is null, insert newChild at the end of the list of children.
|
||||
"""
|
||||
if newChild.nodeType not in self._child_node_types:
|
||||
raise IllegalChild, "%s cannot be child of %s" % (newChild.tagName, self.tagName)
|
||||
if newChild.parentNode is not None:
|
||||
newChild.parentNode.removeChild(newChild)
|
||||
if refChild is None:
|
||||
self.appendChild(newChild)
|
||||
else:
|
||||
try:
|
||||
index = self.childNodes.index(refChild)
|
||||
except ValueError:
|
||||
raise xml.dom.NotFoundErr()
|
||||
self.childNodes.insert(index, newChild)
|
||||
newChild.nextSibling = refChild
|
||||
refChild.previousSibling = newChild
|
||||
if index:
|
||||
node = self.childNodes[index-1]
|
||||
node.nextSibling = newChild
|
||||
newChild.previousSibling = node
|
||||
else:
|
||||
newChild.previousSibling = None
|
||||
newChild.parentNode = self
|
||||
return newChild
|
||||
|
||||
def appendChild(self, newChild):
|
||||
""" Adds the node newChild to the end of the list of children of this node.
|
||||
If the newChild is already in the tree, it is first removed.
|
||||
"""
|
||||
if newChild.nodeType == self.DOCUMENT_FRAGMENT_NODE:
|
||||
for c in tuple(newChild.childNodes):
|
||||
self.appendChild(c)
|
||||
### The DOM does not clearly specify what to return in this case
|
||||
return newChild
|
||||
if newChild.nodeType not in self._child_node_types:
|
||||
raise IllegalChild, "<%s> is not allowed in %s" % ( newChild.tagName, self.tagName)
|
||||
if newChild.parentNode is not None:
|
||||
newChild.parentNode.removeChild(newChild)
|
||||
_append_child(self, newChild)
|
||||
newChild.nextSibling = None
|
||||
return newChild
|
||||
|
||||
def removeChild(self, oldChild):
|
||||
""" Removes the child node indicated by oldChild from the list of children, and returns it.
|
||||
"""
|
||||
#FIXME: update ownerDocument.element_dict or find other solution
|
||||
try:
|
||||
self.childNodes.remove(oldChild)
|
||||
except ValueError:
|
||||
raise xml.dom.NotFoundErr()
|
||||
if oldChild.nextSibling is not None:
|
||||
oldChild.nextSibling.previousSibling = oldChild.previousSibling
|
||||
if oldChild.previousSibling is not None:
|
||||
oldChild.previousSibling.nextSibling = oldChild.nextSibling
|
||||
oldChild.nextSibling = oldChild.previousSibling = None
|
||||
if self.ownerDocument:
|
||||
self.ownerDocument.clear_caches()
|
||||
oldChild.parentNode = None
|
||||
return oldChild
|
||||
|
||||
def __str__(self):
|
||||
val = []
|
||||
for c in self.childNodes:
|
||||
val.append(str(c))
|
||||
return ''.join(val)
|
||||
|
||||
def __unicode__(self):
|
||||
val = []
|
||||
for c in self.childNodes:
|
||||
val.append(unicode(c))
|
||||
return u''.join(val)
|
||||
|
||||
defproperty(Node, "firstChild", doc="First child node, or None.")
|
||||
defproperty(Node, "lastChild", doc="Last child node, or None.")
|
||||
|
||||
def _append_child(self, node):
|
||||
# fast path with less checks; usable by DOM builders if careful
|
||||
childNodes = self.childNodes
|
||||
if childNodes:
|
||||
last = childNodes[-1]
|
||||
node.__dict__["previousSibling"] = last
|
||||
last.__dict__["nextSibling"] = node
|
||||
childNodes.append(node)
|
||||
node.__dict__["parentNode"] = self
|
||||
|
||||
class Childless:
|
||||
""" Mixin that makes childless-ness easy to implement and avoids
|
||||
the complexity of the Node methods that deal with children.
|
||||
"""
|
||||
|
||||
attributes = None
|
||||
childNodes = EmptyNodeList()
|
||||
firstChild = None
|
||||
lastChild = None
|
||||
|
||||
def _get_firstChild(self):
|
||||
return None
|
||||
|
||||
def _get_lastChild(self):
|
||||
return None
|
||||
|
||||
def appendChild(self, node):
|
||||
""" Raises an error """
|
||||
raise xml.dom.HierarchyRequestErr(
|
||||
self.tagName + " nodes cannot have children")
|
||||
|
||||
def hasChildNodes(self):
|
||||
return False
|
||||
|
||||
def insertBefore(self, newChild, refChild):
|
||||
""" Raises an error """
|
||||
raise xml.dom.HierarchyRequestErr(
|
||||
self.tagName + " nodes do not have children")
|
||||
|
||||
def removeChild(self, oldChild):
|
||||
""" Raises an error """
|
||||
raise xml.dom.NotFoundErr(
|
||||
self.tagName + " nodes do not have children")
|
||||
|
||||
def replaceChild(self, newChild, oldChild):
|
||||
""" Raises an error """
|
||||
raise xml.dom.HierarchyRequestErr(
|
||||
self.tagName + " nodes do not have children")
|
||||
|
||||
class Text(Childless, Node):
|
||||
nodeType = Node.TEXT_NODE
|
||||
tagName = "Text"
|
||||
|
||||
def __init__(self, data):
|
||||
self.data = data
|
||||
|
||||
def __str__(self):
|
||||
return self.data.encode()
|
||||
|
||||
def __unicode__(self):
|
||||
return self.data
|
||||
|
||||
def toXml(self,level,f):
|
||||
""" Write XML in UTF-8 """
|
||||
if self.data:
|
||||
f.write(_escape(unicode(self.data).encode('utf-8')))
|
||||
|
||||
class CDATASection(Childless, Text):
|
||||
nodeType = Node.CDATA_SECTION_NODE
|
||||
|
||||
def toXml(self,level,f):
|
||||
""" Generate XML output of the node. If the text contains "]]>", then
|
||||
escape it by going out of CDATA mode (]]>), then write the string
|
||||
and then go into CDATA mode again. (<![CDATA[)
|
||||
"""
|
||||
if self.data:
|
||||
f.write('<![CDATA[%s]]>' % self.data.replace(']]>',']]>]]><![CDATA['))
|
||||
|
||||
class Element(Node):
|
||||
""" Creates a arbitrary element and is intended to be subclassed not used on its own.
|
||||
This element is the base of every element it defines a class which resembles
|
||||
a xml-element. The main advantage of this kind of implementation is that you don't
|
||||
have to create a toXML method for every different object. Every element
|
||||
consists of an attribute, optional subelements, optional text and optional cdata.
|
||||
"""
|
||||
|
||||
nodeType = Node.ELEMENT_NODE
|
||||
namespaces = {} # Due to shallow copy this is a static variable
|
||||
|
||||
_child_node_types = (Node.ELEMENT_NODE,
|
||||
Node.PROCESSING_INSTRUCTION_NODE,
|
||||
Node.COMMENT_NODE,
|
||||
Node.TEXT_NODE,
|
||||
Node.CDATA_SECTION_NODE,
|
||||
Node.ENTITY_REFERENCE_NODE)
|
||||
|
||||
def __init__(self, attributes=None, text=None, cdata=None, qname=None, qattributes=None, check_grammar=True, **args):
|
||||
if qname is not None:
|
||||
self.qname = qname
|
||||
assert(hasattr(self, 'qname'))
|
||||
self.ownerDocument = None
|
||||
self.childNodes=[]
|
||||
self.allowed_children = grammar.allowed_children.get(self.qname)
|
||||
prefix = self.get_nsprefix(self.qname[0])
|
||||
self.tagName = prefix + ":" + self.qname[1]
|
||||
if text is not None:
|
||||
self.addText(text)
|
||||
if cdata is not None:
|
||||
self.addCDATA(cdata)
|
||||
|
||||
allowed_attrs = self.allowed_attributes()
|
||||
if allowed_attrs is not None:
|
||||
allowed_args = [ a[1].lower().replace('-','') for a in allowed_attrs]
|
||||
self.attributes={}
|
||||
# Load the attributes from the 'attributes' argument
|
||||
if attributes:
|
||||
for attr, value in attributes.items():
|
||||
self.setAttribute(attr, value)
|
||||
# Load the qualified attributes
|
||||
if qattributes:
|
||||
for attr, value in qattributes.items():
|
||||
self.setAttrNS(attr[0], attr[1], value)
|
||||
if allowed_attrs is not None:
|
||||
# Load the attributes from the 'args' argument
|
||||
for arg in args.keys():
|
||||
self.setAttribute(arg, args[arg])
|
||||
else:
|
||||
for arg in args.keys(): # If any attribute is allowed
|
||||
self.attributes[arg]=args[arg]
|
||||
if not check_grammar:
|
||||
return
|
||||
# Test that all mandatory attributes have been added.
|
||||
required = grammar.required_attributes.get(self.qname)
|
||||
if required:
|
||||
for r in required:
|
||||
if self.getAttrNS(r[0],r[1]) is None:
|
||||
raise AttributeError, "Required attribute missing: %s in <%s>" % (r[1].lower().replace('-',''), self.tagName)
|
||||
|
||||
def get_knownns(self, prefix):
|
||||
""" Odfpy maintains a list of known namespaces. In some cases a prefix is used, and
|
||||
we need to know which namespace it resolves to.
|
||||
"""
|
||||
global nsdict
|
||||
for ns,p in nsdict.items():
|
||||
if p == prefix: return ns
|
||||
return None
|
||||
|
||||
def get_nsprefix(self, namespace):
|
||||
""" Odfpy maintains a list of known namespaces. In some cases we have a namespace URL,
|
||||
and needs to look up or assign the prefix for it.
|
||||
"""
|
||||
if namespace is None: namespace = ""
|
||||
prefix = _nsassign(namespace)
|
||||
if not self.namespaces.has_key(namespace):
|
||||
self.namespaces[namespace] = prefix
|
||||
return prefix
|
||||
|
||||
def allowed_attributes(self):
|
||||
return grammar.allowed_attributes.get(self.qname)
|
||||
|
||||
def _setOwnerDoc(self, element):
|
||||
element.ownerDocument = self.ownerDocument
|
||||
for child in element.childNodes:
|
||||
self._setOwnerDoc(child)
|
||||
|
||||
def addElement(self, element, check_grammar=True):
|
||||
""" adds an element to an Element
|
||||
|
||||
Element.addElement(Element)
|
||||
"""
|
||||
if check_grammar and self.allowed_children is not None:
|
||||
if element.qname not in self.allowed_children:
|
||||
raise IllegalChild, "<%s> is not allowed in <%s>" % ( element.tagName, self.tagName)
|
||||
self.appendChild(element)
|
||||
self._setOwnerDoc(element)
|
||||
if self.ownerDocument:
|
||||
self.ownerDocument.rebuild_caches(element)
|
||||
|
||||
def addText(self, text, check_grammar=True):
|
||||
""" Adds text to an element
|
||||
Setting check_grammar=False turns off grammar checking
|
||||
"""
|
||||
if check_grammar and self.qname not in grammar.allows_text:
|
||||
raise IllegalText, "The <%s> element does not allow text" % self.tagName
|
||||
else:
|
||||
if text != '':
|
||||
self.appendChild(Text(text))
|
||||
|
||||
def addCDATA(self, cdata, check_grammar=True):
|
||||
""" Adds CDATA to an element
|
||||
Setting check_grammar=False turns off grammar checking
|
||||
"""
|
||||
if check_grammar and self.qname not in grammar.allows_text:
|
||||
raise IllegalText, "The <%s> element does not allow text" % self.tagName
|
||||
else:
|
||||
self.appendChild(CDATASection(cdata))
|
||||
|
||||
def removeAttribute(self, attr, check_grammar=True):
|
||||
""" Removes an attribute by name. """
|
||||
allowed_attrs = self.allowed_attributes()
|
||||
if allowed_attrs is None:
|
||||
if type(attr) == type(()):
|
||||
prefix, localname = attr
|
||||
self.removeAttrNS(prefix, localname)
|
||||
else:
|
||||
raise AttributeError, "Unable to add simple attribute - use (namespace, localpart)"
|
||||
else:
|
||||
# Construct a list of allowed arguments
|
||||
allowed_args = [ a[1].lower().replace('-','') for a in allowed_attrs]
|
||||
if check_grammar and attr not in allowed_args:
|
||||
raise AttributeError, "Attribute %s is not allowed in <%s>" % ( attr, self.tagName)
|
||||
i = allowed_args.index(attr)
|
||||
self.removeAttrNS(allowed_attrs[i][0], allowed_attrs[i][1])
|
||||
|
||||
def setAttribute(self, attr, value, check_grammar=True):
|
||||
""" Add an attribute to the element
|
||||
This is sort of a convenience method. All attributes in ODF have
|
||||
namespaces. The library knows what attributes are legal and then allows
|
||||
the user to provide the attribute as a keyword argument and the
|
||||
library will add the correct namespace.
|
||||
Must overwrite, If attribute already exists.
|
||||
"""
|
||||
allowed_attrs = self.allowed_attributes()
|
||||
if allowed_attrs is None:
|
||||
if type(attr) == type(()):
|
||||
prefix, localname = attr
|
||||
self.setAttrNS(prefix, localname, value)
|
||||
else:
|
||||
raise AttributeError, "Unable to add simple attribute - use (namespace, localpart)"
|
||||
else:
|
||||
# Construct a list of allowed arguments
|
||||
allowed_args = [ a[1].lower().replace('-','') for a in allowed_attrs]
|
||||
if check_grammar and attr not in allowed_args:
|
||||
raise AttributeError, "Attribute %s is not allowed in <%s>" % ( attr, self.tagName)
|
||||
i = allowed_args.index(attr)
|
||||
self.setAttrNS(allowed_attrs[i][0], allowed_attrs[i][1], value)
|
||||
|
||||
def setAttrNS(self, namespace, localpart, value):
|
||||
""" Add an attribute to the element
|
||||
In case you need to add an attribute the library doesn't know about
|
||||
then you must provide the full qualified name
|
||||
It will not check that the attribute is legal according to the schema.
|
||||
Must overwrite, If attribute already exists.
|
||||
"""
|
||||
allowed_attrs = self.allowed_attributes()
|
||||
prefix = self.get_nsprefix(namespace)
|
||||
# if allowed_attrs and (namespace, localpart) not in allowed_attrs:
|
||||
# raise AttributeError, "Attribute %s:%s is not allowed in element <%s>" % ( prefix, localpart, self.tagName)
|
||||
c = AttrConverters()
|
||||
self.attributes[(namespace, localpart)] = c.convert((namespace, localpart), value, self)
|
||||
|
||||
def getAttrNS(self, namespace, localpart):
|
||||
prefix = self.get_nsprefix(namespace)
|
||||
return self.attributes.get((namespace, localpart))
|
||||
|
||||
def removeAttrNS(self, namespace, localpart):
|
||||
del self.attributes[(namespace, localpart)]
|
||||
|
||||
def getAttribute(self, attr):
|
||||
""" Get an attribute value. The method knows which namespace the attribute is in
|
||||
"""
|
||||
allowed_attrs = self.allowed_attributes()
|
||||
if allowed_attrs is None:
|
||||
if type(attr) == type(()):
|
||||
prefix, localname = attr
|
||||
return self.getAttrNS(prefix, localname)
|
||||
else:
|
||||
raise AttributeError, "Unable to get simple attribute - use (namespace, localpart)"
|
||||
else:
|
||||
# Construct a list of allowed arguments
|
||||
allowed_args = [ a[1].lower().replace('-','') for a in allowed_attrs]
|
||||
i = allowed_args.index(attr)
|
||||
return self.getAttrNS(allowed_attrs[i][0], allowed_attrs[i][1])
|
||||
|
||||
def write_open_tag(self, level, f):
|
||||
f.write('<'+self.tagName)
|
||||
if level == 0:
|
||||
for namespace, prefix in self.namespaces.items():
|
||||
f.write(' xmlns:' + prefix + '="'+ _escape(str(namespace))+'"')
|
||||
for qname in self.attributes.keys():
|
||||
prefix = self.get_nsprefix(qname[0])
|
||||
f.write(' '+_escape(str(prefix+':'+qname[1]))+'='+_quoteattr(unicode(self.attributes[qname]).encode('utf-8')))
|
||||
f.write('>')
|
||||
|
||||
def write_close_tag(self, level, f):
|
||||
f.write('</'+self.tagName+'>')
|
||||
|
||||
def toXml(self, level, f):
|
||||
""" Generate XML stream out of the tree structure """
|
||||
f.write('<'+self.tagName)
|
||||
if level == 0:
|
||||
for namespace, prefix in self.namespaces.items():
|
||||
f.write(' xmlns:' + prefix + '="'+ _escape(str(namespace))+'"')
|
||||
for qname in self.attributes.keys():
|
||||
prefix = self.get_nsprefix(qname[0])
|
||||
f.write(' '+_escape(str(prefix+':'+qname[1]))+'='+_quoteattr(unicode(self.attributes[qname]).encode('utf-8')))
|
||||
if self.childNodes:
|
||||
f.write('>')
|
||||
for element in self.childNodes:
|
||||
element.toXml(level+1,f)
|
||||
f.write('</'+self.tagName+'>')
|
||||
else:
|
||||
f.write('/>')
|
||||
|
||||
def _getElementsByObj(self, obj, accumulator):
|
||||
if self.qname == obj.qname:
|
||||
accumulator.append(self)
|
||||
for e in self.childNodes:
|
||||
if e.nodeType == Node.ELEMENT_NODE:
|
||||
accumulator = e._getElementsByObj(obj, accumulator)
|
||||
return accumulator
|
||||
|
||||
def getElementsByType(self, element):
|
||||
""" Gets elements based on the type, which is function from text.py, draw.py etc. """
|
||||
obj = element(check_grammar=False)
|
||||
return self._getElementsByObj(obj,[])
|
||||
|
||||
def isInstanceOf(self, element):
|
||||
""" This is a check to see if the object is an instance of a type """
|
||||
obj = element(check_grammar=False)
|
||||
return self.qname == obj.qname
|
||||
|
||||
|
||||
@@ -1,325 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2008 Søren Roug, European Environment Agency
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
|
||||
from namespaces import *
|
||||
|
||||
# Inline element don't cause a box
|
||||
# They are analogous to the HTML elements SPAN, B, I etc.
|
||||
inline_elements = (
|
||||
(TEXTNS,u'a'),
|
||||
(TEXTNS,u'author-initials'),
|
||||
(TEXTNS,u'author-name'),
|
||||
(TEXTNS,u'bibliography-mark'),
|
||||
(TEXTNS,u'bookmark-ref'),
|
||||
(TEXTNS,u'chapter'),
|
||||
(TEXTNS,u'character-count'),
|
||||
(TEXTNS,u'conditional-text'),
|
||||
(TEXTNS,u'creation-date'),
|
||||
(TEXTNS,u'creation-time'),
|
||||
(TEXTNS,u'creator'),
|
||||
(TEXTNS,u'database-display'),
|
||||
(TEXTNS,u'database-name'),
|
||||
(TEXTNS,u'database-next'),
|
||||
(TEXTNS,u'database-row-number'),
|
||||
(TEXTNS,u'database-row-select'),
|
||||
(TEXTNS,u'date'),
|
||||
(TEXTNS,u'dde-connection'),
|
||||
(TEXTNS,u'description'),
|
||||
(TEXTNS,u'editing-cycles'),
|
||||
(TEXTNS,u'editing-duration'),
|
||||
(TEXTNS,u'execute-macro'),
|
||||
(TEXTNS,u'expression'),
|
||||
(TEXTNS,u'file-name'),
|
||||
(TEXTNS,u'hidden-paragraph'),
|
||||
(TEXTNS,u'hidden-text'),
|
||||
(TEXTNS,u'image-count'),
|
||||
(TEXTNS,u'initial-creator'),
|
||||
(TEXTNS,u'keywords'),
|
||||
(TEXTNS,u'measure'),
|
||||
(TEXTNS,u'modification-date'),
|
||||
(TEXTNS,u'modification-time'),
|
||||
(TEXTNS,u'note-ref'),
|
||||
(TEXTNS,u'object-count'),
|
||||
(TEXTNS,u'page-continuation'),
|
||||
(TEXTNS,u'page-count'),
|
||||
(TEXTNS,u'page-number'),
|
||||
(TEXTNS,u'page-variable-get'),
|
||||
(TEXTNS,u'page-variable-set'),
|
||||
(TEXTNS,u'paragraph-count'),
|
||||
(TEXTNS,u'placeholder'),
|
||||
(TEXTNS,u'print-date'),
|
||||
(TEXTNS,u'printed-by'),
|
||||
(TEXTNS,u'print-time'),
|
||||
(TEXTNS,u'reference-ref'),
|
||||
(TEXTNS,u'ruby'),
|
||||
(TEXTNS,u'ruby-base'),
|
||||
(TEXTNS,u'ruby-text'),
|
||||
(TEXTNS,u'script'),
|
||||
(TEXTNS,u'sender-city'),
|
||||
(TEXTNS,u'sender-company'),
|
||||
(TEXTNS,u'sender-country'),
|
||||
(TEXTNS,u'sender-email'),
|
||||
(TEXTNS,u'sender-fax'),
|
||||
(TEXTNS,u'sender-firstname'),
|
||||
(TEXTNS,u'sender-initials'),
|
||||
(TEXTNS,u'sender-lastname'),
|
||||
(TEXTNS,u'sender-phone-private'),
|
||||
(TEXTNS,u'sender-phone-work'),
|
||||
(TEXTNS,u'sender-position'),
|
||||
(TEXTNS,u'sender-postal-code'),
|
||||
(TEXTNS,u'sender-state-or-province'),
|
||||
(TEXTNS,u'sender-street'),
|
||||
(TEXTNS,u'sender-title'),
|
||||
(TEXTNS,u'sequence'),
|
||||
(TEXTNS,u'sequence-ref'),
|
||||
(TEXTNS,u'sheet-name'),
|
||||
(TEXTNS,u'span'),
|
||||
(TEXTNS,u'subject'),
|
||||
(TEXTNS,u'table-count'),
|
||||
(TEXTNS,u'table-formula'),
|
||||
(TEXTNS,u'template-name'),
|
||||
(TEXTNS,u'text-input'),
|
||||
(TEXTNS,u'time'),
|
||||
(TEXTNS,u'title'),
|
||||
(TEXTNS,u'user-defined'),
|
||||
(TEXTNS,u'user-field-get'),
|
||||
(TEXTNS,u'user-field-input'),
|
||||
(TEXTNS,u'variable-get'),
|
||||
(TEXTNS,u'variable-input'),
|
||||
(TEXTNS,u'variable-set'),
|
||||
(TEXTNS,u'word-count'),
|
||||
)
|
||||
|
||||
|
||||
# It is almost impossible to determine what elements are block elements.
|
||||
# There are so many that don't fit the form
|
||||
block_elements = (
|
||||
(TEXTNS,u'h'),
|
||||
(TEXTNS,u'p'),
|
||||
(TEXTNS,u'list'),
|
||||
(TEXTNS,u'list-item'),
|
||||
(TEXTNS,u'section'),
|
||||
)
|
||||
|
||||
declarative_elements = (
|
||||
(OFFICENS,u'font-face-decls'),
|
||||
(PRESENTATIONNS,u'date-time-decl'),
|
||||
(PRESENTATIONNS,u'footer-decl'),
|
||||
(PRESENTATIONNS,u'header-decl'),
|
||||
(TABLENS,u'table-template'),
|
||||
(TEXTNS,u'alphabetical-index-entry-template'),
|
||||
(TEXTNS,u'alphabetical-index-source'),
|
||||
(TEXTNS,u'bibliography-entry-template'),
|
||||
(TEXTNS,u'bibliography-source'),
|
||||
(TEXTNS,u'dde-connection-decls'),
|
||||
(TEXTNS,u'illustration-index-entry-template'),
|
||||
(TEXTNS,u'illustration-index-source'),
|
||||
(TEXTNS,u'index-source-styles'),
|
||||
(TEXTNS,u'index-title-template'),
|
||||
(TEXTNS,u'note-continuation-notice-backward'),
|
||||
(TEXTNS,u'note-continuation-notice-forward'),
|
||||
(TEXTNS,u'notes-configuration'),
|
||||
(TEXTNS,u'object-index-entry-template'),
|
||||
(TEXTNS,u'object-index-source'),
|
||||
(TEXTNS,u'sequence-decls'),
|
||||
(TEXTNS,u'table-index-entry-template'),
|
||||
(TEXTNS,u'table-index-source'),
|
||||
(TEXTNS,u'table-of-content-entry-template'),
|
||||
(TEXTNS,u'table-of-content-source'),
|
||||
(TEXTNS,u'user-field-decls'),
|
||||
(TEXTNS,u'user-index-entry-template'),
|
||||
(TEXTNS,u'user-index-source'),
|
||||
(TEXTNS,u'variable-decls'),
|
||||
)
|
||||
|
||||
empty_elements = (
|
||||
(ANIMNS,u'animate'),
|
||||
(ANIMNS,u'animateColor'),
|
||||
(ANIMNS,u'animateMotion'),
|
||||
(ANIMNS,u'animateTransform'),
|
||||
(ANIMNS,u'audio'),
|
||||
(ANIMNS,u'param'),
|
||||
(ANIMNS,u'set'),
|
||||
(ANIMNS,u'transitionFilter'),
|
||||
(CHARTNS,u'categories'),
|
||||
(CHARTNS,u'data-point'),
|
||||
(CHARTNS,u'domain'),
|
||||
(CHARTNS,u'error-indicator'),
|
||||
(CHARTNS,u'floor'),
|
||||
(CHARTNS,u'grid'),
|
||||
(CHARTNS,u'legend'),
|
||||
(CHARTNS,u'mean-value'),
|
||||
(CHARTNS,u'regression-curve'),
|
||||
(CHARTNS,u'stock-gain-marker'),
|
||||
(CHARTNS,u'stock-loss-marker'),
|
||||
(CHARTNS,u'stock-range-line'),
|
||||
(CHARTNS,u'symbol-image'),
|
||||
(CHARTNS,u'wall'),
|
||||
(DR3DNS,u'cube'),
|
||||
(DR3DNS,u'extrude'),
|
||||
(DR3DNS,u'light'),
|
||||
(DR3DNS,u'rotate'),
|
||||
(DR3DNS,u'sphere'),
|
||||
(DRAWNS,u'contour-path'),
|
||||
(DRAWNS,u'contour-polygon'),
|
||||
(DRAWNS,u'equation'),
|
||||
(DRAWNS,u'fill-image'),
|
||||
(DRAWNS,u'floating-frame'),
|
||||
(DRAWNS,u'glue-point'),
|
||||
(DRAWNS,u'gradient'),
|
||||
(DRAWNS,u'handle'),
|
||||
(DRAWNS,u'hatch'),
|
||||
(DRAWNS,u'layer'),
|
||||
(DRAWNS,u'marker'),
|
||||
(DRAWNS,u'opacity'),
|
||||
(DRAWNS,u'page-thumbnail'),
|
||||
(DRAWNS,u'param'),
|
||||
(DRAWNS,u'stroke-dash'),
|
||||
(FORMNS,u'connection-resource'),
|
||||
(FORMNS,u'list-value'),
|
||||
(FORMNS,u'property'),
|
||||
(MANIFESTNS,u'algorithm'),
|
||||
(MANIFESTNS,u'key-derivation'),
|
||||
(METANS,u'auto-reload'),
|
||||
(METANS,u'document-statistic'),
|
||||
(METANS,u'hyperlink-behaviour'),
|
||||
(METANS,u'template'),
|
||||
(NUMBERNS,u'am-pm'),
|
||||
(NUMBERNS,u'boolean'),
|
||||
(NUMBERNS,u'day'),
|
||||
(NUMBERNS,u'day-of-week'),
|
||||
(NUMBERNS,u'era'),
|
||||
(NUMBERNS,u'fraction'),
|
||||
(NUMBERNS,u'hours'),
|
||||
(NUMBERNS,u'minutes'),
|
||||
(NUMBERNS,u'month'),
|
||||
(NUMBERNS,u'quarter'),
|
||||
(NUMBERNS,u'scientific-number'),
|
||||
(NUMBERNS,u'seconds'),
|
||||
(NUMBERNS,u'text-content'),
|
||||
(NUMBERNS,u'week-of-year'),
|
||||
(NUMBERNS,u'year'),
|
||||
(OFFICENS,u'dde-source'),
|
||||
(PRESENTATIONNS,u'date-time'),
|
||||
(PRESENTATIONNS,u'footer'),
|
||||
(PRESENTATIONNS,u'header'),
|
||||
(PRESENTATIONNS,u'placeholder'),
|
||||
(PRESENTATIONNS,u'play'),
|
||||
(PRESENTATIONNS,u'show'),
|
||||
(PRESENTATIONNS,u'sound'),
|
||||
(SCRIPTNS,u'event-listener'),
|
||||
(STYLENS,u'column'),
|
||||
(STYLENS,u'column-sep'),
|
||||
(STYLENS,u'drop-cap'),
|
||||
(STYLENS,u'footnote-sep'),
|
||||
(STYLENS,u'list-level-properties'),
|
||||
(STYLENS,u'map'),
|
||||
(STYLENS,u'ruby-properties'),
|
||||
(STYLENS,u'table-column-properties'),
|
||||
(STYLENS,u'tab-stop'),
|
||||
(STYLENS,u'text-properties'),
|
||||
(SVGNS,u'definition-src'),
|
||||
(SVGNS,u'font-face-format'),
|
||||
(SVGNS,u'font-face-name'),
|
||||
(SVGNS,u'stop'),
|
||||
(TABLENS,u'body'),
|
||||
(TABLENS,u'cell-address'),
|
||||
(TABLENS,u'cell-range-source'),
|
||||
(TABLENS,u'change-deletion'),
|
||||
(TABLENS,u'consolidation'),
|
||||
(TABLENS,u'database-source-query'),
|
||||
(TABLENS,u'database-source-sql'),
|
||||
(TABLENS,u'database-source-table'),
|
||||
(TABLENS,u'data-pilot-display-info'),
|
||||
(TABLENS,u'data-pilot-field-reference'),
|
||||
(TABLENS,u'data-pilot-group-member'),
|
||||
(TABLENS,u'data-pilot-layout-info'),
|
||||
(TABLENS,u'data-pilot-member'),
|
||||
(TABLENS,u'data-pilot-sort-info'),
|
||||
(TABLENS,u'data-pilot-subtotal'),
|
||||
(TABLENS,u'dependency'),
|
||||
(TABLENS,u'error-macro'),
|
||||
(TABLENS,u'even-columns'),
|
||||
(TABLENS,u'even-rows'),
|
||||
(TABLENS,u'filter-condition'),
|
||||
(TABLENS,u'first-column'),
|
||||
(TABLENS,u'first-row'),
|
||||
(TABLENS,u'highlighted-range'),
|
||||
(TABLENS,u'insertion-cut-off'),
|
||||
(TABLENS,u'iteration'),
|
||||
(TABLENS,u'label-range'),
|
||||
(TABLENS,u'last-column'),
|
||||
(TABLENS,u'last-row'),
|
||||
(TABLENS,u'movement-cut-off'),
|
||||
(TABLENS,u'named-expression'),
|
||||
(TABLENS,u'named-range'),
|
||||
(TABLENS,u'null-date'),
|
||||
(TABLENS,u'odd-columns'),
|
||||
(TABLENS,u'odd-rows'),
|
||||
(TABLENS,u'operation'),
|
||||
(TABLENS,u'scenario'),
|
||||
(TABLENS,u'sort-by'),
|
||||
(TABLENS,u'sort-groups'),
|
||||
(TABLENS,u'source-range-address'),
|
||||
(TABLENS,u'source-service'),
|
||||
(TABLENS,u'subtotal-field'),
|
||||
(TABLENS,u'table-column'),
|
||||
(TABLENS,u'table-source'),
|
||||
(TABLENS,u'target-range-address'),
|
||||
(TEXTNS,u'alphabetical-index-auto-mark-file'),
|
||||
(TEXTNS,u'alphabetical-index-mark'),
|
||||
(TEXTNS,u'alphabetical-index-mark-end'),
|
||||
(TEXTNS,u'alphabetical-index-mark-start'),
|
||||
(TEXTNS,u'bookmark'),
|
||||
(TEXTNS,u'bookmark-end'),
|
||||
(TEXTNS,u'bookmark-start'),
|
||||
(TEXTNS,u'change'),
|
||||
(TEXTNS,u'change-end'),
|
||||
(TEXTNS,u'change-start'),
|
||||
(TEXTNS,u'dde-connection-decl'),
|
||||
(TEXTNS,u'index-entry-bibliography'),
|
||||
(TEXTNS,u'index-entry-chapter'),
|
||||
(TEXTNS,u'index-entry-link-end'),
|
||||
(TEXTNS,u'index-entry-link-start'),
|
||||
(TEXTNS,u'index-entry-page-number'),
|
||||
(TEXTNS,u'index-entry-tab-stop'),
|
||||
(TEXTNS,u'index-entry-text'),
|
||||
(TEXTNS,u'index-source-style'),
|
||||
(TEXTNS,u'line-break'),
|
||||
(TEXTNS,u'page'),
|
||||
(TEXTNS,u'reference-mark'),
|
||||
(TEXTNS,u'reference-mark-end'),
|
||||
(TEXTNS,u'reference-mark-start'),
|
||||
(TEXTNS,u's'),
|
||||
(TEXTNS,u'section-source'),
|
||||
(TEXTNS,u'sequence-decl'),
|
||||
(TEXTNS,u'soft-page-break'),
|
||||
(TEXTNS,u'sort-key'),
|
||||
(TEXTNS,u'tab'),
|
||||
(TEXTNS,u'toc-mark'),
|
||||
(TEXTNS,u'toc-mark-end'),
|
||||
(TEXTNS,u'toc-mark-start'),
|
||||
(TEXTNS,u'user-field-decl'),
|
||||
(TEXTNS,u'user-index-mark'),
|
||||
(TEXTNS,u'user-index-mark-end'),
|
||||
(TEXTNS,u'user-index-mark-start'),
|
||||
(TEXTNS,u'variable-decl')
|
||||
)
|
||||
@@ -1,115 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
|
||||
from namespaces import FORMNS
|
||||
from element import Element
|
||||
|
||||
|
||||
# Autogenerated
|
||||
def Button(**args):
|
||||
return Element(qname = (FORMNS,'button'), **args)
|
||||
|
||||
def Checkbox(**args):
|
||||
return Element(qname = (FORMNS,'checkbox'), **args)
|
||||
|
||||
def Column(**args):
|
||||
return Element(qname = (FORMNS,'column'), **args)
|
||||
|
||||
def Combobox(**args):
|
||||
return Element(qname = (FORMNS,'combobox'), **args)
|
||||
|
||||
def ConnectionResource(**args):
|
||||
return Element(qname = (FORMNS,'connection-resource'), **args)
|
||||
|
||||
def Date(**args):
|
||||
return Element(qname = (FORMNS,'date'), **args)
|
||||
|
||||
def File(**args):
|
||||
return Element(qname = (FORMNS,'file'), **args)
|
||||
|
||||
def FixedText(**args):
|
||||
return Element(qname = (FORMNS,'fixed-text'), **args)
|
||||
|
||||
def Form(**args):
|
||||
return Element(qname = (FORMNS,'form'), **args)
|
||||
|
||||
def FormattedText(**args):
|
||||
return Element(qname = (FORMNS,'formatted-text'), **args)
|
||||
|
||||
def Frame(**args):
|
||||
return Element(qname = (FORMNS,'frame'), **args)
|
||||
|
||||
def GenericControl(**args):
|
||||
return Element(qname = (FORMNS,'generic-control'), **args)
|
||||
|
||||
def Grid(**args):
|
||||
return Element(qname = (FORMNS,'grid'), **args)
|
||||
|
||||
def Hidden(**args):
|
||||
return Element(qname = (FORMNS,'hidden'), **args)
|
||||
|
||||
def Image(**args):
|
||||
return Element(qname = (FORMNS,'image'), **args)
|
||||
|
||||
def ImageFrame(**args):
|
||||
return Element(qname = (FORMNS,'image-frame'), **args)
|
||||
|
||||
def Item(**args):
|
||||
return Element(qname = (FORMNS,'item'), **args)
|
||||
|
||||
def ListProperty(**args):
|
||||
return Element(qname = (FORMNS,'list-property'), **args)
|
||||
|
||||
def ListValue(**args):
|
||||
return Element(qname = (FORMNS,'list-value'), **args)
|
||||
|
||||
def Listbox(**args):
|
||||
return Element(qname = (FORMNS,'listbox'), **args)
|
||||
|
||||
def Number(**args):
|
||||
return Element(qname = (FORMNS,'number'), **args)
|
||||
|
||||
def Option(**args):
|
||||
return Element(qname = (FORMNS,'option'), **args)
|
||||
|
||||
def Password(**args):
|
||||
return Element(qname = (FORMNS,'password'), **args)
|
||||
|
||||
def Properties(**args):
|
||||
return Element(qname = (FORMNS,'properties'), **args)
|
||||
|
||||
def Property(**args):
|
||||
return Element(qname = (FORMNS,'property'), **args)
|
||||
|
||||
def Radio(**args):
|
||||
return Element(qname = (FORMNS,'radio'), **args)
|
||||
|
||||
def Text(**args):
|
||||
return Element(qname = (FORMNS,'text'), **args)
|
||||
|
||||
def Textarea(**args):
|
||||
return Element(qname = (FORMNS,'textarea'), **args)
|
||||
|
||||
def Time(**args):
|
||||
return Element(qname = (FORMNS,'time'), **args)
|
||||
|
||||
def ValueRange(**args):
|
||||
return Element(qname = (FORMNS,'value-range'), **args)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,112 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2007-2008 Søren Roug, European Environment Agency
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
|
||||
# This script is to be embedded in opendocument.py later
|
||||
# The purpose is to read an ODT/ODP/ODS file and create the datastructure
|
||||
# in memory. The user should then be able to make operations and then save
|
||||
# the structure again.
|
||||
|
||||
from xml.sax import make_parser,handler
|
||||
from xml.sax.xmlreader import InputSource
|
||||
import xml.sax.saxutils
|
||||
from element import Element
|
||||
from namespaces import OFFICENS
|
||||
from cStringIO import StringIO
|
||||
|
||||
#
|
||||
# Parse the XML files
|
||||
#
|
||||
class LoadParser(handler.ContentHandler):
|
||||
""" Extract headings from content.xml of an ODT file """
|
||||
triggers = (
|
||||
(OFFICENS, 'automatic-styles'), (OFFICENS, 'body'),
|
||||
(OFFICENS, 'font-face-decls'), (OFFICENS, 'master-styles'),
|
||||
(OFFICENS, 'meta'), (OFFICENS, 'scripts'),
|
||||
(OFFICENS, 'settings'), (OFFICENS, 'styles') )
|
||||
|
||||
def __init__(self, document):
|
||||
self.doc = document
|
||||
self.data = []
|
||||
self.level = 0
|
||||
self.parse = False
|
||||
|
||||
def characters(self, data):
|
||||
if self.parse == False:
|
||||
return
|
||||
self.data.append(data)
|
||||
|
||||
def startElementNS(self, tag, qname, attrs):
|
||||
if tag in self.triggers:
|
||||
self.parse = True
|
||||
if self.doc._parsing != "styles.xml" and tag == (OFFICENS, 'font-face-decls'):
|
||||
self.parse = False
|
||||
if self.parse == False:
|
||||
return
|
||||
|
||||
self.level = self.level + 1
|
||||
# Add any accumulated text content
|
||||
content = ''.join(self.data)
|
||||
if len(content.strip()) > 0:
|
||||
self.parent.addText(content, check_grammar=False)
|
||||
self.data = []
|
||||
# Create the element
|
||||
attrdict = {}
|
||||
for (att,value) in attrs.items():
|
||||
attrdict[att] = value
|
||||
try:
|
||||
e = Element(qname = tag, qattributes=attrdict, check_grammar=False)
|
||||
self.curr = e
|
||||
except AttributeError, v:
|
||||
print "Error: %s" % v
|
||||
|
||||
if tag == (OFFICENS, 'automatic-styles'):
|
||||
e = self.doc.automaticstyles
|
||||
elif tag == (OFFICENS, 'body'):
|
||||
e = self.doc.body
|
||||
elif tag == (OFFICENS, 'master-styles'):
|
||||
e = self.doc.masterstyles
|
||||
elif tag == (OFFICENS, 'meta'):
|
||||
e = self.doc.meta
|
||||
elif tag == (OFFICENS,'scripts'):
|
||||
e = self.doc.scripts
|
||||
elif tag == (OFFICENS,'settings'):
|
||||
e = self.doc.settings
|
||||
elif tag == (OFFICENS,'styles'):
|
||||
e = self.doc.styles
|
||||
elif self.doc._parsing == "styles.xml" and tag == (OFFICENS, 'font-face-decls'):
|
||||
e = self.doc.fontfacedecls
|
||||
elif hasattr(self,'parent'):
|
||||
self.parent.addElement(e, check_grammar=False)
|
||||
self.parent = e
|
||||
|
||||
|
||||
def endElementNS(self, tag, qname):
|
||||
if self.parse == False:
|
||||
return
|
||||
self.level = self.level - 1
|
||||
str = ''.join(self.data)
|
||||
if len(str.strip()) > 0:
|
||||
self.curr.addText(str, check_grammar=False)
|
||||
self.data = []
|
||||
self.curr = self.curr.parentNode
|
||||
self.parent = self.curr
|
||||
if tag in self.triggers:
|
||||
self.parse = False
|
||||
@@ -1,41 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
#
|
||||
|
||||
from namespaces import MANIFESTNS
|
||||
from element import Element
|
||||
|
||||
# Autogenerated
|
||||
def Manifest(**args):
|
||||
return Element(qname = (MANIFESTNS,'manifest'), **args)
|
||||
|
||||
def FileEntry(**args):
|
||||
return Element(qname = (MANIFESTNS,'file-entry'), **args)
|
||||
|
||||
def EncryptionData(**args):
|
||||
return Element(qname = (MANIFESTNS,'encryption-data'), **args)
|
||||
|
||||
def Algorithm(**args):
|
||||
return Element(qname = (MANIFESTNS,'algorithm'), **args)
|
||||
|
||||
def KeyDerivation(**args):
|
||||
return Element(qname = (MANIFESTNS,'key-derivation'), **args)
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
|
||||
from namespaces import MATHNS
|
||||
from element import Element
|
||||
|
||||
# ODF 1.0 section 12.5
|
||||
# Mathematical content is represented by MathML 2.0
|
||||
|
||||
# Autogenerated
|
||||
def Math(**args):
|
||||
return Element(qname = (MATHNS,'math'), **args)
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
|
||||
from namespaces import METANS
|
||||
from element import Element
|
||||
|
||||
# Autogenerated
|
||||
def AutoReload(**args):
|
||||
return Element(qname = (METANS,'auto-reload'), **args)
|
||||
|
||||
def CreationDate(**args):
|
||||
return Element(qname = (METANS,'creation-date'), **args)
|
||||
|
||||
def DateString(**args):
|
||||
return Element(qname = (METANS,'date-string'), **args)
|
||||
|
||||
def DocumentStatistic(**args):
|
||||
return Element(qname = (METANS,'document-statistic'), **args)
|
||||
|
||||
def EditingCycles(**args):
|
||||
return Element(qname = (METANS,'editing-cycles'), **args)
|
||||
|
||||
def EditingDuration(**args):
|
||||
return Element(qname = (METANS,'editing-duration'), **args)
|
||||
|
||||
def Generator(**args):
|
||||
return Element(qname = (METANS,'generator'), **args)
|
||||
|
||||
def HyperlinkBehaviour(**args):
|
||||
return Element(qname = (METANS,'hyperlink-behaviour'), **args)
|
||||
|
||||
def InitialCreator(**args):
|
||||
return Element(qname = (METANS,'initial-creator'), **args)
|
||||
|
||||
def Keyword(**args):
|
||||
return Element(qname = (METANS,'keyword'), **args)
|
||||
|
||||
def PrintDate(**args):
|
||||
return Element(qname = (METANS,'print-date'), **args)
|
||||
|
||||
def PrintedBy(**args):
|
||||
return Element(qname = (METANS,'printed-by'), **args)
|
||||
|
||||
def Template(**args):
|
||||
return Element(qname = (METANS,'template'), **args)
|
||||
|
||||
def UserDefined(**args):
|
||||
return Element(qname = (METANS,'user-defined'), **args)
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2006-2010 Søren Roug, European Environment Agency
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
TOOLSVERSION = u"ODFPY/0.9.3"
|
||||
|
||||
ANIMNS = u"urn:oasis:names:tc:opendocument:xmlns:animation:1.0"
|
||||
DBNS = u"urn:oasis:names:tc:opendocument:xmlns:database:1.0"
|
||||
CHARTNS = u"urn:oasis:names:tc:opendocument:xmlns:chart:1.0"
|
||||
CONFIGNS = u"urn:oasis:names:tc:opendocument:xmlns:config:1.0"
|
||||
#DBNS = u"http://openoffice.org/2004/database"
|
||||
DCNS = u"http://purl.org/dc/elements/1.1/"
|
||||
DOMNS = u"http://www.w3.org/2001/xml-events"
|
||||
DR3DNS = u"urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0"
|
||||
DRAWNS = u"urn:oasis:names:tc:opendocument:xmlns:drawing:1.0"
|
||||
FIELDNS = u"urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0"
|
||||
FONS = u"urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0"
|
||||
FORMNS = u"urn:oasis:names:tc:opendocument:xmlns:form:1.0"
|
||||
GRDDLNS = u"http://www.w3.org/2003/g/data-view#"
|
||||
KOFFICENS = u"http://www.koffice.org/2005/"
|
||||
MANIFESTNS = u"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"
|
||||
MATHNS = u"http://www.w3.org/1998/Math/MathML"
|
||||
METANS = u"urn:oasis:names:tc:opendocument:xmlns:meta:1.0"
|
||||
NUMBERNS = u"urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0"
|
||||
OFFICENS = u"urn:oasis:names:tc:opendocument:xmlns:office:1.0"
|
||||
OFNS = u"urn:oasis:names:tc:opendocument:xmlns:of:1.2"
|
||||
OOONS = u"http://openoffice.org/2004/office"
|
||||
OOOWNS = u"http://openoffice.org/2004/writer"
|
||||
OOOCNS = u"http://openoffice.org/2004/calc"
|
||||
PRESENTATIONNS = u"urn:oasis:names:tc:opendocument:xmlns:presentation:1.0"
|
||||
RDFANS = u"http://docs.oasis-open.org/opendocument/meta/rdfa#"
|
||||
RPTNS = u"http://openoffice.org/2005/report"
|
||||
SCRIPTNS = u"urn:oasis:names:tc:opendocument:xmlns:script:1.0"
|
||||
SMILNS = u"urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0"
|
||||
STYLENS = u"urn:oasis:names:tc:opendocument:xmlns:style:1.0"
|
||||
SVGNS = u"urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0"
|
||||
TABLENS = u"urn:oasis:names:tc:opendocument:xmlns:table:1.0"
|
||||
TEXTNS = u"urn:oasis:names:tc:opendocument:xmlns:text:1.0"
|
||||
XFORMSNS = u"http://www.w3.org/2002/xforms"
|
||||
XLINKNS = u"http://www.w3.org/1999/xlink"
|
||||
XMLNS = u"http://www.w3.org/XML/1998/namespace"
|
||||
XSDNS = u"http://www.w3.org/2001/XMLSchema"
|
||||
XSINS = u"http://www.w3.org/2001/XMLSchema-instance"
|
||||
|
||||
nsdict = {
|
||||
ANIMNS: u'anim',
|
||||
CHARTNS: u'chart',
|
||||
CONFIGNS: u'config',
|
||||
DBNS: u'db',
|
||||
DCNS: u'dc',
|
||||
DOMNS: u'dom',
|
||||
DR3DNS: u'dr3d',
|
||||
DRAWNS: u'draw',
|
||||
FIELDNS: u'field',
|
||||
FONS: u'fo',
|
||||
FORMNS: u'form',
|
||||
GRDDLNS: u'grddl',
|
||||
KOFFICENS: u'koffice',
|
||||
MANIFESTNS: u'manifest',
|
||||
MATHNS: u'math',
|
||||
METANS: u'meta',
|
||||
NUMBERNS: u'number',
|
||||
OFFICENS: u'office',
|
||||
OFNS: u'of',
|
||||
OOONS: u'ooo',
|
||||
OOOWNS: u'ooow',
|
||||
OOOCNS: u'oooc',
|
||||
PRESENTATIONNS: u'presentation',
|
||||
RDFANS: u'rdfa',
|
||||
RPTNS: u'rpt',
|
||||
SCRIPTNS: u'script',
|
||||
SMILNS: u'smil',
|
||||
STYLENS: u'style',
|
||||
SVGNS: u'svg',
|
||||
TABLENS: u'table',
|
||||
TEXTNS: u'text',
|
||||
XFORMSNS: u'xforms',
|
||||
XLINKNS: u'xlink',
|
||||
XMLNS: u'xml',
|
||||
XSDNS: u'xsd',
|
||||
XSINS: u'xsi',
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
|
||||
from namespaces import NUMBERNS
|
||||
from element import Element
|
||||
from style import StyleElement
|
||||
|
||||
|
||||
# Autogenerated
|
||||
def AmPm(**args):
|
||||
return Element(qname = (NUMBERNS,'am-pm'), **args)
|
||||
|
||||
def Boolean(**args):
|
||||
return Element(qname = (NUMBERNS,'boolean'), **args)
|
||||
|
||||
def BooleanStyle(**args):
|
||||
return StyleElement(qname = (NUMBERNS,'boolean-style'), **args)
|
||||
|
||||
def CurrencyStyle(**args):
|
||||
return StyleElement(qname = (NUMBERNS,'currency-style'), **args)
|
||||
|
||||
def CurrencySymbol(**args):
|
||||
return Element(qname = (NUMBERNS,'currency-symbol'), **args)
|
||||
|
||||
def DateStyle(**args):
|
||||
return StyleElement(qname = (NUMBERNS,'date-style'), **args)
|
||||
|
||||
def Day(**args):
|
||||
return Element(qname = (NUMBERNS,'day'), **args)
|
||||
|
||||
def DayOfWeek(**args):
|
||||
return Element(qname = (NUMBERNS,'day-of-week'), **args)
|
||||
|
||||
def EmbeddedText(**args):
|
||||
return Element(qname = (NUMBERNS,'embedded-text'), **args)
|
||||
|
||||
def Era(**args):
|
||||
return Element(qname = (NUMBERNS,'era'), **args)
|
||||
|
||||
def Fraction(**args):
|
||||
return Element(qname = (NUMBERNS,'fraction'), **args)
|
||||
|
||||
def Hours(**args):
|
||||
return Element(qname = (NUMBERNS,'hours'), **args)
|
||||
|
||||
def Minutes(**args):
|
||||
return Element(qname = (NUMBERNS,'minutes'), **args)
|
||||
|
||||
def Month(**args):
|
||||
return Element(qname = (NUMBERNS,'month'), **args)
|
||||
|
||||
def Number(**args):
|
||||
return Element(qname = (NUMBERNS,'number'), **args)
|
||||
|
||||
def NumberStyle(**args):
|
||||
return StyleElement(qname = (NUMBERNS,'number-style'), **args)
|
||||
|
||||
def PercentageStyle(**args):
|
||||
return StyleElement(qname = (NUMBERNS,'percentage-style'), **args)
|
||||
|
||||
def Quarter(**args):
|
||||
return Element(qname = (NUMBERNS,'quarter'), **args)
|
||||
|
||||
def ScientificNumber(**args):
|
||||
return Element(qname = (NUMBERNS,'scientific-number'), **args)
|
||||
|
||||
def Seconds(**args):
|
||||
return Element(qname = (NUMBERNS,'seconds'), **args)
|
||||
|
||||
def Text(**args):
|
||||
return Element(qname = (NUMBERNS,'text'), **args)
|
||||
|
||||
def TextContent(**args):
|
||||
return Element(qname = (NUMBERNS,'text-content'), **args)
|
||||
|
||||
def TextStyle(**args):
|
||||
return StyleElement(qname = (NUMBERNS,'text-style'), **args)
|
||||
|
||||
def TimeStyle(**args):
|
||||
return StyleElement(qname = (NUMBERNS,'time-style'), **args)
|
||||
|
||||
def WeekOfYear(**args):
|
||||
return Element(qname = (NUMBERNS,'week-of-year'), **args)
|
||||
|
||||
def Year(**args):
|
||||
return Element(qname = (NUMBERNS,'year'), **args)
|
||||
|
||||
@@ -1,579 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2006-2008 Søren Roug, European Environment Agency
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# See http://trac.edgewall.org/wiki/WikiFormatting
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
|
||||
import sys, zipfile, xml.dom.minidom
|
||||
from namespaces import nsdict
|
||||
from elementtypes import *
|
||||
|
||||
IGNORED_TAGS = [
|
||||
'draw:a'
|
||||
'draw:g',
|
||||
'draw:line',
|
||||
'draw:object-ole',
|
||||
'office:annotation',
|
||||
'presentation:notes',
|
||||
'svg:desc',
|
||||
] + [ nsdict[item[0]]+":"+item[1] for item in empty_elements]
|
||||
|
||||
INLINE_TAGS = [ nsdict[item[0]]+":"+item[1] for item in inline_elements]
|
||||
|
||||
|
||||
class TextProps:
|
||||
""" Holds properties for a text style. """
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self.italic = False
|
||||
self.bold = False
|
||||
self.fixed = False
|
||||
self.underlined = False
|
||||
self.strikethrough = False
|
||||
self.superscript = False
|
||||
self.subscript = False
|
||||
|
||||
def setItalic(self, value):
|
||||
if value == "italic":
|
||||
self.italic = True
|
||||
elif value == "normal":
|
||||
self.italic = False
|
||||
|
||||
def setBold(self, value):
|
||||
if value == "bold":
|
||||
self.bold = True
|
||||
elif value == "normal":
|
||||
self.bold = False
|
||||
|
||||
def setFixed(self, value):
|
||||
self.fixed = value
|
||||
|
||||
def setUnderlined(self, value):
|
||||
if value and value != "none":
|
||||
self.underlined = True
|
||||
|
||||
def setStrikethrough(self, value):
|
||||
if value and value != "none":
|
||||
self.strikethrough = True
|
||||
|
||||
def setPosition(self, value):
|
||||
if value is None or value == '':
|
||||
return
|
||||
posisize = value.split(' ')
|
||||
textpos = posisize[0]
|
||||
if textpos.find('%') == -1:
|
||||
if textpos == "sub":
|
||||
self.superscript = False
|
||||
self.subscript = True
|
||||
elif textpos == "super":
|
||||
self.superscript = True
|
||||
self.subscript = False
|
||||
else:
|
||||
itextpos = int(textpos[:textpos.find('%')])
|
||||
if itextpos > 10:
|
||||
self.superscript = False
|
||||
self.subscript = True
|
||||
elif itextpos < -10:
|
||||
self.superscript = True
|
||||
self.subscript = False
|
||||
|
||||
def __str__(self):
|
||||
|
||||
return "[italic=%s, bold=i%s, fixed=%s]" % (str(self.italic),
|
||||
str(self.bold),
|
||||
str(self.fixed))
|
||||
|
||||
class ParagraphProps:
|
||||
""" Holds properties of a paragraph style. """
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self.blockquote = False
|
||||
self.headingLevel = 0
|
||||
self.code = False
|
||||
self.title = False
|
||||
self.indented = 0
|
||||
|
||||
def setIndented(self, value):
|
||||
self.indented = value
|
||||
|
||||
def setHeading(self, level):
|
||||
self.headingLevel = level
|
||||
|
||||
def setTitle(self, value):
|
||||
self.title = value
|
||||
|
||||
def setCode(self, value):
|
||||
self.code = value
|
||||
|
||||
|
||||
def __str__(self):
|
||||
|
||||
return "[bq=%s, h=%d, code=%s]" % (str(self.blockquote),
|
||||
self.headingLevel,
|
||||
str(self.code))
|
||||
|
||||
|
||||
class ListProperties:
|
||||
""" Holds properties for a list style. """
|
||||
|
||||
def __init__(self):
|
||||
self.ordered = False
|
||||
|
||||
def setOrdered(self, value):
|
||||
self.ordered = value
|
||||
|
||||
|
||||
|
||||
class ODF2MoinMoin(object):
|
||||
|
||||
|
||||
def __init__(self, filepath):
|
||||
self.footnotes = []
|
||||
self.footnoteCounter = 0
|
||||
self.textStyles = {"Standard": TextProps()}
|
||||
self.paragraphStyles = {"Standard": ParagraphProps()}
|
||||
self.listStyles = {}
|
||||
self.fixedFonts = []
|
||||
self.hasTitle = 0
|
||||
self.lastsegment = None
|
||||
|
||||
# Tags
|
||||
self.elements = {
|
||||
'draw:page': self.textToString,
|
||||
'draw:frame': self.textToString,
|
||||
'draw:image': self.draw_image,
|
||||
'draw:text-box': self.textToString,
|
||||
'text:a': self.text_a,
|
||||
'text:note': self.text_note,
|
||||
}
|
||||
for tag in IGNORED_TAGS:
|
||||
self.elements[tag] = self.do_nothing
|
||||
|
||||
for tag in INLINE_TAGS:
|
||||
self.elements[tag] = self.inline_markup
|
||||
self.elements['text:line-break'] = self.text_line_break
|
||||
self.elements['text:s'] = self.text_s
|
||||
self.elements['text:tab'] = self.text_tab
|
||||
|
||||
self.load(filepath)
|
||||
|
||||
def processFontDeclarations(self, fontDecl):
|
||||
""" Extracts necessary font information from a font-declaration
|
||||
element.
|
||||
"""
|
||||
for fontFace in fontDecl.getElementsByTagName("style:font-face"):
|
||||
if fontFace.getAttribute("style:font-pitch") == "fixed":
|
||||
self.fixedFonts.append(fontFace.getAttribute("style:name"))
|
||||
|
||||
|
||||
|
||||
def extractTextProperties(self, style, parent=None):
|
||||
""" Extracts text properties from a style element. """
|
||||
|
||||
textProps = TextProps()
|
||||
|
||||
if parent:
|
||||
parentProp = self.textStyles.get(parent, None)
|
||||
if parentProp:
|
||||
textProp = parentProp
|
||||
|
||||
textPropEl = style.getElementsByTagName("style:text-properties")
|
||||
if not textPropEl: return textProps
|
||||
|
||||
textPropEl = textPropEl[0]
|
||||
|
||||
textProps.setItalic(textPropEl.getAttribute("fo:font-style"))
|
||||
textProps.setBold(textPropEl.getAttribute("fo:font-weight"))
|
||||
textProps.setUnderlined(textPropEl.getAttribute("style:text-underline-style"))
|
||||
textProps.setStrikethrough(textPropEl.getAttribute("style:text-line-through-style"))
|
||||
textProps.setPosition(textPropEl.getAttribute("style:text-position"))
|
||||
|
||||
if textPropEl.getAttribute("style:font-name") in self.fixedFonts:
|
||||
textProps.setFixed(True)
|
||||
|
||||
return textProps
|
||||
|
||||
def extractParagraphProperties(self, style, parent=None):
|
||||
""" Extracts paragraph properties from a style element. """
|
||||
|
||||
paraProps = ParagraphProps()
|
||||
|
||||
name = style.getAttribute("style:name")
|
||||
|
||||
if name.startswith("Heading_20_"):
|
||||
level = name[11:]
|
||||
try:
|
||||
level = int(level)
|
||||
paraProps.setHeading(level)
|
||||
except:
|
||||
level = 0
|
||||
|
||||
if name == "Title":
|
||||
paraProps.setTitle(True)
|
||||
|
||||
paraPropEl = style.getElementsByTagName("style:paragraph-properties")
|
||||
if paraPropEl:
|
||||
paraPropEl = paraPropEl[0]
|
||||
leftMargin = paraPropEl.getAttribute("fo:margin-left")
|
||||
if leftMargin:
|
||||
try:
|
||||
leftMargin = float(leftMargin[:-2])
|
||||
if leftMargin > 0.01:
|
||||
paraProps.setIndented(True)
|
||||
except:
|
||||
pass
|
||||
|
||||
textProps = self.extractTextProperties(style)
|
||||
if textProps.fixed:
|
||||
paraProps.setCode(True)
|
||||
|
||||
return paraProps
|
||||
|
||||
|
||||
def processStyles(self, styleElements):
|
||||
""" Runs through "style" elements extracting necessary information.
|
||||
"""
|
||||
|
||||
for style in styleElements:
|
||||
|
||||
name = style.getAttribute("style:name")
|
||||
|
||||
if name == "Standard": continue
|
||||
|
||||
family = style.getAttribute("style:family")
|
||||
parent = style.getAttribute("style:parent-style-name")
|
||||
|
||||
if family == "text":
|
||||
self.textStyles[name] = self.extractTextProperties(style, parent)
|
||||
|
||||
elif family == "paragraph":
|
||||
self.paragraphStyles[name] = \
|
||||
self.extractParagraphProperties(style, parent)
|
||||
self.textStyles[name] = self.extractTextProperties(style, parent)
|
||||
|
||||
def processListStyles(self, listStyleElements):
|
||||
|
||||
for style in listStyleElements:
|
||||
name = style.getAttribute("style:name")
|
||||
|
||||
prop = ListProperties()
|
||||
if style.hasChildNodes():
|
||||
subitems = [el for el in style.childNodes
|
||||
if el.nodeType == xml.dom.Node.ELEMENT_NODE
|
||||
and el.tagName == "text:list-level-style-number"]
|
||||
if len(subitems) > 0:
|
||||
prop.setOrdered(True)
|
||||
|
||||
self.listStyles[name] = prop
|
||||
|
||||
|
||||
def load(self, filepath):
|
||||
""" Loads an ODT file. """
|
||||
|
||||
zip = zipfile.ZipFile(filepath)
|
||||
|
||||
styles_doc = xml.dom.minidom.parseString(zip.read("styles.xml"))
|
||||
fontfacedecls = styles_doc.getElementsByTagName("office:font-face-decls")
|
||||
if fontfacedecls:
|
||||
self.processFontDeclarations(fontfacedecls[0])
|
||||
self.processStyles(styles_doc.getElementsByTagName("style:style"))
|
||||
self.processListStyles(styles_doc.getElementsByTagName("text:list-style"))
|
||||
|
||||
self.content = xml.dom.minidom.parseString(zip.read("content.xml"))
|
||||
fontfacedecls = self.content.getElementsByTagName("office:font-face-decls")
|
||||
if fontfacedecls:
|
||||
self.processFontDeclarations(fontfacedecls[0])
|
||||
|
||||
self.processStyles(self.content.getElementsByTagName("style:style"))
|
||||
self.processListStyles(self.content.getElementsByTagName("text:list-style"))
|
||||
|
||||
def compressCodeBlocks(self, text):
|
||||
""" Removes extra blank lines from code blocks. """
|
||||
|
||||
return text
|
||||
lines = text.split("\n")
|
||||
buffer = []
|
||||
numLines = len(lines)
|
||||
for i in range(numLines):
|
||||
|
||||
if (lines[i].strip() or i == numLines-1 or i == 0 or
|
||||
not ( lines[i-1].startswith(" ")
|
||||
and lines[i+1].startswith(" ") ) ):
|
||||
buffer.append("\n" + lines[i])
|
||||
|
||||
return ''.join(buffer)
|
||||
|
||||
#-----------------------------------
|
||||
def do_nothing(self, node):
|
||||
return ''
|
||||
|
||||
def draw_image(self, node):
|
||||
"""
|
||||
"""
|
||||
|
||||
link = node.getAttribute("xlink:href")
|
||||
if link and link[:2] == './': # Indicates a sub-object, which isn't supported
|
||||
return "%s\n" % link
|
||||
if link and link[:9] == 'Pictures/':
|
||||
link = link[9:]
|
||||
return "[[Image(%s)]]\n" % link
|
||||
|
||||
def text_a(self, node):
|
||||
text = self.textToString(node)
|
||||
link = node.getAttribute("xlink:href")
|
||||
if link.strip() == text.strip():
|
||||
return "[%s] " % link.strip()
|
||||
else:
|
||||
return "[%s %s] " % (link.strip(), text.strip())
|
||||
|
||||
|
||||
def text_line_break(self, node):
|
||||
return "[[BR]]"
|
||||
|
||||
def text_note(self, node):
|
||||
cite = (node.getElementsByTagName("text:note-citation")[0]
|
||||
.childNodes[0].nodeValue)
|
||||
body = (node.getElementsByTagName("text:note-body")[0]
|
||||
.childNodes[0])
|
||||
self.footnotes.append((cite, self.textToString(body)))
|
||||
return "^%s^" % cite
|
||||
|
||||
def text_s(self, node):
|
||||
try:
|
||||
num = int(node.getAttribute("text:c"))
|
||||
return " "*num
|
||||
except:
|
||||
return " "
|
||||
|
||||
def text_tab(self, node):
|
||||
return " "
|
||||
|
||||
def inline_markup(self, node):
|
||||
text = self.textToString(node)
|
||||
|
||||
if not text.strip():
|
||||
return '' # don't apply styles to white space
|
||||
|
||||
styleName = node.getAttribute("text:style-name")
|
||||
style = self.textStyles.get(styleName, TextProps())
|
||||
|
||||
if style.fixed:
|
||||
return "`" + text + "`"
|
||||
|
||||
mark = []
|
||||
if style:
|
||||
if style.italic:
|
||||
mark.append("''")
|
||||
if style.bold:
|
||||
mark.append("'''")
|
||||
if style.underlined:
|
||||
mark.append("__")
|
||||
if style.strikethrough:
|
||||
mark.append("~~")
|
||||
if style.superscript:
|
||||
mark.append("^")
|
||||
if style.subscript:
|
||||
mark.append(",,")
|
||||
revmark = mark[:]
|
||||
revmark.reverse()
|
||||
return "%s%s%s" % (''.join(mark), text, ''.join(revmark))
|
||||
|
||||
#-----------------------------------
|
||||
def listToString(self, listElement, indent = 0):
|
||||
|
||||
self.lastsegment = listElement.tagName
|
||||
buffer = []
|
||||
|
||||
styleName = listElement.getAttribute("text:style-name")
|
||||
props = self.listStyles.get(styleName, ListProperties())
|
||||
|
||||
i = 0
|
||||
for item in listElement.childNodes:
|
||||
buffer.append(" "*indent)
|
||||
i += 1
|
||||
if props.ordered:
|
||||
number = str(i)
|
||||
number = " " + number + ". "
|
||||
buffer.append(" 1. ")
|
||||
else:
|
||||
buffer.append(" * ")
|
||||
subitems = [el for el in item.childNodes
|
||||
if el.tagName in ["text:p", "text:h", "text:list"]]
|
||||
for subitem in subitems:
|
||||
if subitem.tagName == "text:list":
|
||||
buffer.append("\n")
|
||||
buffer.append(self.listToString(subitem, indent+3))
|
||||
else:
|
||||
buffer.append(self.paragraphToString(subitem, indent+3))
|
||||
self.lastsegment = subitem.tagName
|
||||
self.lastsegment = item.tagName
|
||||
buffer.append("\n")
|
||||
|
||||
return ''.join(buffer)
|
||||
|
||||
def tableToString(self, tableElement):
|
||||
""" MoinMoin uses || to delimit table cells
|
||||
"""
|
||||
|
||||
self.lastsegment = tableElement.tagName
|
||||
buffer = []
|
||||
|
||||
for item in tableElement.childNodes:
|
||||
self.lastsegment = item.tagName
|
||||
if item.tagName == "table:table-header-rows":
|
||||
buffer.append(self.tableToString(item))
|
||||
if item.tagName == "table:table-row":
|
||||
buffer.append("\n||")
|
||||
for cell in item.childNodes:
|
||||
buffer.append(self.inline_markup(cell))
|
||||
buffer.append("||")
|
||||
self.lastsegment = cell.tagName
|
||||
return ''.join(buffer)
|
||||
|
||||
|
||||
def toString(self):
|
||||
""" Converts the document to a string.
|
||||
FIXME: Result from second call differs from first call
|
||||
"""
|
||||
body = self.content.getElementsByTagName("office:body")[0]
|
||||
text = body.childNodes[0]
|
||||
|
||||
buffer = []
|
||||
|
||||
paragraphs = [el for el in text.childNodes
|
||||
if el.tagName in ["draw:page", "text:p", "text:h","text:section",
|
||||
"text:list", "table:table"]]
|
||||
|
||||
for paragraph in paragraphs:
|
||||
if paragraph.tagName == "text:list":
|
||||
text = self.listToString(paragraph)
|
||||
elif paragraph.tagName == "text:section":
|
||||
text = self.textToString(paragraph)
|
||||
elif paragraph.tagName == "table:table":
|
||||
text = self.tableToString(paragraph)
|
||||
else:
|
||||
text = self.paragraphToString(paragraph)
|
||||
if text:
|
||||
buffer.append(text)
|
||||
|
||||
if self.footnotes:
|
||||
|
||||
buffer.append("----")
|
||||
for cite, body in self.footnotes:
|
||||
buffer.append("%s: %s" % (cite, body))
|
||||
|
||||
|
||||
buffer.append("")
|
||||
return self.compressCodeBlocks('\n'.join(buffer))
|
||||
|
||||
|
||||
def textToString(self, element):
|
||||
|
||||
buffer = []
|
||||
|
||||
for node in element.childNodes:
|
||||
|
||||
if node.nodeType == xml.dom.Node.TEXT_NODE:
|
||||
buffer.append(node.nodeValue)
|
||||
|
||||
elif node.nodeType == xml.dom.Node.ELEMENT_NODE:
|
||||
tag = node.tagName
|
||||
|
||||
if tag in ("draw:text-box", "draw:frame"):
|
||||
buffer.append(self.textToString(node))
|
||||
|
||||
elif tag in ("text:p", "text:h"):
|
||||
text = self.paragraphToString(node)
|
||||
if text:
|
||||
buffer.append(text)
|
||||
elif tag == "text:list":
|
||||
buffer.append(self.listToString(node))
|
||||
else:
|
||||
method = self.elements.get(tag)
|
||||
if method:
|
||||
buffer.append(method(node))
|
||||
else:
|
||||
buffer.append(" {" + tag + "} ")
|
||||
|
||||
return ''.join(buffer)
|
||||
|
||||
def paragraphToString(self, paragraph, indent = 0):
|
||||
|
||||
dummyParaProps = ParagraphProps()
|
||||
|
||||
style_name = paragraph.getAttribute("text:style-name")
|
||||
paraProps = self.paragraphStyles.get(style_name, dummyParaProps)
|
||||
text = self.inline_markup(paragraph)
|
||||
|
||||
if paraProps and not paraProps.code:
|
||||
text = text.strip()
|
||||
|
||||
if paragraph.tagName == "text:p" and self.lastsegment == "text:p":
|
||||
text = "\n" + text
|
||||
|
||||
self.lastsegment = paragraph.tagName
|
||||
|
||||
if paraProps.title:
|
||||
self.hasTitle = 1
|
||||
return "= " + text + " =\n"
|
||||
|
||||
outlinelevel = paragraph.getAttribute("text:outline-level")
|
||||
if outlinelevel:
|
||||
|
||||
level = int(outlinelevel)
|
||||
if self.hasTitle: level += 1
|
||||
|
||||
if level >= 1:
|
||||
return "=" * level + " " + text + " " + "=" * level + "\n"
|
||||
|
||||
elif paraProps.code:
|
||||
return "{{{\n" + text + "\n}}}\n"
|
||||
|
||||
if paraProps.indented:
|
||||
return self.wrapParagraph(text, indent = indent, blockquote = True)
|
||||
|
||||
else:
|
||||
return self.wrapParagraph(text, indent = indent)
|
||||
|
||||
|
||||
def wrapParagraph(self, text, indent = 0, blockquote=False):
|
||||
|
||||
counter = 0
|
||||
buffer = []
|
||||
LIMIT = 50
|
||||
|
||||
if blockquote:
|
||||
buffer.append(" ")
|
||||
|
||||
return ''.join(buffer) + text
|
||||
# Unused from here
|
||||
for token in text.split():
|
||||
|
||||
if counter > LIMIT - indent:
|
||||
buffer.append("\n" + " "*indent)
|
||||
if blockquote:
|
||||
buffer.append(" ")
|
||||
counter = 0
|
||||
|
||||
buffer.append(token + " ")
|
||||
counter += len(token)
|
||||
|
||||
return ''.join(buffer)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,115 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
|
||||
# This script lists the content of the manifest.xml file
|
||||
import zipfile
|
||||
from xml.sax import make_parser,handler
|
||||
from xml.sax.xmlreader import InputSource
|
||||
import xml.sax.saxutils
|
||||
from cStringIO import StringIO
|
||||
|
||||
MANIFESTNS="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
#
|
||||
# ODFMANIFESTHANDLER
|
||||
#
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
class ODFManifestHandler(handler.ContentHandler):
|
||||
""" The ODFManifestHandler parses a manifest file and produces a list of
|
||||
content """
|
||||
|
||||
def __init__(self):
|
||||
self.manifest = {}
|
||||
|
||||
# Tags
|
||||
# FIXME: Also handle encryption data
|
||||
self.elements = {
|
||||
(MANIFESTNS, 'file-entry'): (self.s_file_entry, self.donothing),
|
||||
}
|
||||
|
||||
def handle_starttag(self, tag, method, attrs):
|
||||
method(tag,attrs)
|
||||
|
||||
def handle_endtag(self, tag, method):
|
||||
method(tag)
|
||||
|
||||
def startElementNS(self, tag, qname, attrs):
|
||||
method = self.elements.get(tag, (None, None))[0]
|
||||
if method:
|
||||
self.handle_starttag(tag, method, attrs)
|
||||
else:
|
||||
self.unknown_starttag(tag,attrs)
|
||||
|
||||
def endElementNS(self, tag, qname):
|
||||
method = self.elements.get(tag, (None, None))[1]
|
||||
if method:
|
||||
self.handle_endtag(tag, method)
|
||||
else:
|
||||
self.unknown_endtag(tag)
|
||||
|
||||
def unknown_starttag(self, tag, attrs):
|
||||
pass
|
||||
|
||||
def unknown_endtag(self, tag):
|
||||
pass
|
||||
|
||||
def donothing(self, tag, attrs=None):
|
||||
pass
|
||||
|
||||
def s_file_entry(self, tag, attrs):
|
||||
m = attrs.get((MANIFESTNS, 'media-type'),"application/octet-stream")
|
||||
p = attrs.get((MANIFESTNS, 'full-path'))
|
||||
self.manifest[p] = { 'media-type':m, 'full-path':p }
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
#
|
||||
# Reading the file
|
||||
#
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
def manifestlist(manifestxml):
|
||||
odhandler = ODFManifestHandler()
|
||||
parser = make_parser()
|
||||
parser.setFeature(handler.feature_namespaces, 1)
|
||||
parser.setContentHandler(odhandler)
|
||||
parser.setErrorHandler(handler.ErrorHandler())
|
||||
|
||||
inpsrc = InputSource()
|
||||
inpsrc.setByteStream(StringIO(manifestxml))
|
||||
parser.parse(inpsrc)
|
||||
|
||||
return odhandler.manifest
|
||||
|
||||
def odfmanifest(odtfile):
|
||||
z = zipfile.ZipFile(odtfile)
|
||||
manifest = z.read('META-INF/manifest.xml')
|
||||
z.close()
|
||||
return manifestlist(manifest)
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
result = odfmanifest(sys.argv[1])
|
||||
for file in result.values():
|
||||
print "%-40s %-40s" % (file['media-type'], file['full-path'])
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
|
||||
from namespaces import OFFICENS
|
||||
from element import Element
|
||||
from draw import StyleRefElement
|
||||
|
||||
# Autogenerated
|
||||
def Annotation(**args):
|
||||
return StyleRefElement(qname = (OFFICENS,'annotation'), **args)
|
||||
|
||||
def AutomaticStyles(**args):
|
||||
return Element(qname = (OFFICENS, 'automatic-styles'), **args)
|
||||
|
||||
def BinaryData(**args):
|
||||
return Element(qname = (OFFICENS,'binary-data'), **args)
|
||||
|
||||
def Body(**args):
|
||||
return Element(qname = (OFFICENS, 'body'), **args)
|
||||
|
||||
def ChangeInfo(**args):
|
||||
return Element(qname = (OFFICENS,'change-info'), **args)
|
||||
|
||||
def Chart(**args):
|
||||
return Element(qname = (OFFICENS,'chart'), **args)
|
||||
|
||||
def DdeSource(**args):
|
||||
return Element(qname = (OFFICENS,'dde-source'), **args)
|
||||
|
||||
def Document(version="1.1", **args):
|
||||
return Element(qname = (OFFICENS,'document'), version=version, **args)
|
||||
|
||||
def DocumentContent(version="1.1", **args):
|
||||
return Element(qname = (OFFICENS, 'document-content'), version=version, **args)
|
||||
|
||||
def DocumentMeta(version="1.1", **args):
|
||||
return Element(qname = (OFFICENS, 'document-meta'), version=version, **args)
|
||||
|
||||
def DocumentSettings(version="1.1", **args):
|
||||
return Element(qname = (OFFICENS, 'document-settings'), version=version, **args)
|
||||
|
||||
def DocumentStyles(version="1.1", **args):
|
||||
return Element(qname = (OFFICENS, 'document-styles'), version=version, **args)
|
||||
|
||||
def Drawing(**args):
|
||||
return Element(qname = (OFFICENS,'drawing'), **args)
|
||||
|
||||
def EventListeners(**args):
|
||||
return Element(qname = (OFFICENS,'event-listeners'), **args)
|
||||
|
||||
def FontFaceDecls(**args):
|
||||
return Element(qname = (OFFICENS, 'font-face-decls'), **args)
|
||||
|
||||
def Forms(**args):
|
||||
return Element(qname = (OFFICENS,'forms'), **args)
|
||||
|
||||
def Image(**args):
|
||||
return Element(qname = (OFFICENS,'image'), **args)
|
||||
|
||||
def MasterStyles(**args):
|
||||
return Element(qname = (OFFICENS, 'master-styles'), **args)
|
||||
|
||||
def Meta(**args):
|
||||
return Element(qname = (OFFICENS, 'meta'), **args)
|
||||
|
||||
def Presentation(**args):
|
||||
return Element(qname = (OFFICENS,'presentation'), **args)
|
||||
|
||||
def Script(**args):
|
||||
return Element(qname = (OFFICENS, 'script'), **args)
|
||||
|
||||
def Scripts(**args):
|
||||
return Element(qname = (OFFICENS, 'scripts'), **args)
|
||||
|
||||
def Settings(**args):
|
||||
return Element(qname = (OFFICENS, 'settings'), **args)
|
||||
|
||||
def Spreadsheet(**args):
|
||||
return Element(qname = (OFFICENS, 'spreadsheet'), **args)
|
||||
|
||||
def Styles(**args):
|
||||
return Element(qname = (OFFICENS, 'styles'), **args)
|
||||
|
||||
def Text(**args):
|
||||
return Element(qname = (OFFICENS, 'text'), **args)
|
||||
|
||||
# Autogenerated end
|
||||
@@ -1,654 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2006-2010 Søren Roug, European Environment Agency
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
|
||||
__doc__="""Use OpenDocument to generate your documents."""
|
||||
|
||||
import zipfile, time, sys, mimetypes, copy
|
||||
from cStringIO import StringIO
|
||||
from namespaces import *
|
||||
import manifest, meta
|
||||
from office import *
|
||||
import element
|
||||
from attrconverters import make_NCName
|
||||
from xml.sax.xmlreader import InputSource
|
||||
from odfmanifest import manifestlist
|
||||
|
||||
__version__= TOOLSVERSION
|
||||
|
||||
_XMLPROLOGUE = u"<?xml version='1.0' encoding='UTF-8'?>\n"
|
||||
|
||||
UNIXPERMS = 0100644 << 16L # -rw-r--r--
|
||||
|
||||
IS_FILENAME = 0
|
||||
IS_IMAGE = 1
|
||||
# We need at least Python 2.2
|
||||
assert sys.version_info[0]>=2 and sys.version_info[1] >= 2
|
||||
|
||||
#sys.setrecursionlimit(100)
|
||||
#The recursion limit is set conservative so mistakes like
|
||||
# s=content() s.addElement(s) won't eat up too much processor time.
|
||||
|
||||
odmimetypes = {
|
||||
'application/vnd.oasis.opendocument.text': '.odt',
|
||||
'application/vnd.oasis.opendocument.text-template': '.ott',
|
||||
'application/vnd.oasis.opendocument.graphics': '.odg',
|
||||
'application/vnd.oasis.opendocument.graphics-template': '.otg',
|
||||
'application/vnd.oasis.opendocument.presentation': '.odp',
|
||||
'application/vnd.oasis.opendocument.presentation-template': '.otp',
|
||||
'application/vnd.oasis.opendocument.spreadsheet': '.ods',
|
||||
'application/vnd.oasis.opendocument.spreadsheet-template': '.ots',
|
||||
'application/vnd.oasis.opendocument.chart': '.odc',
|
||||
'application/vnd.oasis.opendocument.chart-template': '.otc',
|
||||
'application/vnd.oasis.opendocument.image': '.odi',
|
||||
'application/vnd.oasis.opendocument.image-template': '.oti',
|
||||
'application/vnd.oasis.opendocument.formula': '.odf',
|
||||
'application/vnd.oasis.opendocument.formula-template': '.otf',
|
||||
'application/vnd.oasis.opendocument.text-master': '.odm',
|
||||
'application/vnd.oasis.opendocument.text-web': '.oth',
|
||||
}
|
||||
|
||||
class OpaqueObject:
|
||||
def __init__(self, filename, mediatype, content=None):
|
||||
self.mediatype = mediatype
|
||||
self.filename = filename
|
||||
self.content = content
|
||||
|
||||
class OpenDocument:
|
||||
""" A class to hold the content of an OpenDocument document
|
||||
Use the xml method to write the XML
|
||||
source to the screen or to a file
|
||||
d = OpenDocument(mimetype)
|
||||
fd.write(d.xml())
|
||||
"""
|
||||
thumbnail = None
|
||||
|
||||
def __init__(self, mimetype, add_generator=True):
|
||||
self.mimetype = mimetype
|
||||
self.childobjects = []
|
||||
self._extra = []
|
||||
self.folder = "" # Always empty for toplevel documents
|
||||
self.topnode = Document(mimetype=self.mimetype)
|
||||
self.topnode.ownerDocument = self
|
||||
|
||||
self.clear_caches()
|
||||
|
||||
self.Pictures = {}
|
||||
self.meta = Meta()
|
||||
self.topnode.addElement(self.meta)
|
||||
if add_generator:
|
||||
self.meta.addElement(meta.Generator(text=TOOLSVERSION))
|
||||
self.scripts = Scripts()
|
||||
self.topnode.addElement(self.scripts)
|
||||
self.fontfacedecls = FontFaceDecls()
|
||||
self.topnode.addElement(self.fontfacedecls)
|
||||
self.settings = Settings()
|
||||
self.topnode.addElement(self.settings)
|
||||
self.styles = Styles()
|
||||
self.topnode.addElement(self.styles)
|
||||
self.automaticstyles = AutomaticStyles()
|
||||
self.topnode.addElement(self.automaticstyles)
|
||||
self.masterstyles = MasterStyles()
|
||||
self.topnode.addElement(self.masterstyles)
|
||||
self.body = Body()
|
||||
self.topnode.addElement(self.body)
|
||||
|
||||
def rebuild_caches(self, node=None):
|
||||
if node is None: node = self.topnode
|
||||
self.build_caches(node)
|
||||
for e in node.childNodes:
|
||||
if e.nodeType == element.Node.ELEMENT_NODE:
|
||||
self.rebuild_caches(e)
|
||||
|
||||
def clear_caches(self):
|
||||
self.element_dict = {}
|
||||
self._styles_dict = {}
|
||||
self._styles_ooo_fix = {}
|
||||
|
||||
def build_caches(self, element):
|
||||
""" Called from element.py
|
||||
"""
|
||||
if not self.element_dict.has_key(element.qname):
|
||||
self.element_dict[element.qname] = []
|
||||
self.element_dict[element.qname].append(element)
|
||||
if element.qname == (STYLENS, u'style'):
|
||||
self.__register_stylename(element) # Add to style dictionary
|
||||
styleref = element.getAttrNS(TEXTNS,u'style-name')
|
||||
if styleref is not None and self._styles_ooo_fix.has_key(styleref):
|
||||
element.setAttrNS(TEXTNS,u'style-name', self._styles_ooo_fix[styleref])
|
||||
|
||||
def __register_stylename(self, element):
|
||||
''' Register a style. But there are three style dictionaries:
|
||||
office:styles, office:automatic-styles and office:master-styles
|
||||
Chapter 14
|
||||
'''
|
||||
name = element.getAttrNS(STYLENS, u'name')
|
||||
if name is None:
|
||||
return
|
||||
if element.parentNode.qname in ((OFFICENS,u'styles'), (OFFICENS,u'automatic-styles')):
|
||||
if self._styles_dict.has_key(name):
|
||||
newname = 'M'+name # Rename style
|
||||
self._styles_ooo_fix[name] = newname
|
||||
# From here on all references to the old name will refer to the new one
|
||||
name = newname
|
||||
element.setAttrNS(STYLENS, u'name', name)
|
||||
self._styles_dict[name] = element
|
||||
|
||||
def toXml(self, filename=''):
|
||||
xml=StringIO()
|
||||
xml.write(_XMLPROLOGUE)
|
||||
self.body.toXml(0, xml)
|
||||
if not filename:
|
||||
return xml.getvalue()
|
||||
else:
|
||||
f=file(filename,'w')
|
||||
f.write(xml.getvalue())
|
||||
f.close()
|
||||
|
||||
def xml(self):
|
||||
""" Generates the full document as an XML file
|
||||
Always written as a bytestream in UTF-8 encoding
|
||||
"""
|
||||
self.__replaceGenerator()
|
||||
xml=StringIO()
|
||||
xml.write(_XMLPROLOGUE)
|
||||
self.topnode.toXml(0, xml)
|
||||
return xml.getvalue()
|
||||
|
||||
|
||||
def contentxml(self):
|
||||
""" Generates the content.xml file
|
||||
Always written as a bytestream in UTF-8 encoding
|
||||
"""
|
||||
xml=StringIO()
|
||||
xml.write(_XMLPROLOGUE)
|
||||
x = DocumentContent()
|
||||
x.write_open_tag(0, xml)
|
||||
if self.scripts.hasChildNodes():
|
||||
self.scripts.toXml(1, xml)
|
||||
if self.fontfacedecls.hasChildNodes():
|
||||
self.fontfacedecls.toXml(1, xml)
|
||||
a = AutomaticStyles()
|
||||
stylelist = self._used_auto_styles([self.styles, self.automaticstyles, self.body])
|
||||
if len(stylelist) > 0:
|
||||
a.write_open_tag(1, xml)
|
||||
for s in stylelist:
|
||||
s.toXml(2, xml)
|
||||
a.write_close_tag(1, xml)
|
||||
else:
|
||||
a.toXml(1, xml)
|
||||
self.body.toXml(1, xml)
|
||||
x.write_close_tag(0, xml)
|
||||
return xml.getvalue()
|
||||
|
||||
def __manifestxml(self):
|
||||
""" Generates the manifest.xml file
|
||||
The self.manifest isn't avaible unless the document is being saved
|
||||
"""
|
||||
xml=StringIO()
|
||||
xml.write(_XMLPROLOGUE)
|
||||
self.manifest.toXml(0,xml)
|
||||
return xml.getvalue()
|
||||
|
||||
def metaxml(self):
|
||||
""" Generates the meta.xml file """
|
||||
self.__replaceGenerator()
|
||||
x = DocumentMeta()
|
||||
x.addElement(self.meta)
|
||||
xml=StringIO()
|
||||
xml.write(_XMLPROLOGUE)
|
||||
x.toXml(0,xml)
|
||||
return xml.getvalue()
|
||||
|
||||
def settingsxml(self):
|
||||
""" Generates the settings.xml file """
|
||||
x = DocumentSettings()
|
||||
x.addElement(self.settings)
|
||||
xml=StringIO()
|
||||
xml.write(_XMLPROLOGUE)
|
||||
x.toXml(0,xml)
|
||||
return xml.getvalue()
|
||||
|
||||
def _parseoneelement(self, top, stylenamelist):
|
||||
""" Finds references to style objects in master-styles
|
||||
and add the style name to the style list if not already there.
|
||||
Recursive
|
||||
"""
|
||||
for e in top.childNodes:
|
||||
if e.nodeType == element.Node.ELEMENT_NODE:
|
||||
for styleref in ( (DRAWNS,u'style-name'),
|
||||
(DRAWNS,u'text-style-name'),
|
||||
(PRESENTATIONNS,u'style-name'),
|
||||
(STYLENS,u'data-style-name'),
|
||||
(STYLENS,u'list-style-name'),
|
||||
(STYLENS,u'page-layout-name'),
|
||||
(STYLENS,u'style-name'),
|
||||
(TABLENS,u'default-cell-style-name'),
|
||||
(TABLENS,u'style-name'),
|
||||
(TEXTNS,u'style-name') ):
|
||||
if e.getAttrNS(styleref[0],styleref[1]):
|
||||
stylename = e.getAttrNS(styleref[0],styleref[1])
|
||||
if stylename not in stylenamelist:
|
||||
stylenamelist.append(stylename)
|
||||
stylenamelist = self._parseoneelement(e, stylenamelist)
|
||||
return stylenamelist
|
||||
|
||||
def _used_auto_styles(self, segments):
|
||||
""" Loop through the masterstyles elements, and find the automatic
|
||||
styles that are used. These will be added to the automatic-styles
|
||||
element in styles.xml
|
||||
"""
|
||||
stylenamelist = []
|
||||
for top in segments:
|
||||
stylenamelist = self._parseoneelement(top, stylenamelist)
|
||||
stylelist = []
|
||||
for e in self.automaticstyles.childNodes:
|
||||
if e.getAttrNS(STYLENS,u'name') in stylenamelist:
|
||||
stylelist.append(e)
|
||||
return stylelist
|
||||
|
||||
def stylesxml(self):
|
||||
""" Generates the styles.xml file """
|
||||
xml=StringIO()
|
||||
xml.write(_XMLPROLOGUE)
|
||||
x = DocumentStyles()
|
||||
x.write_open_tag(0, xml)
|
||||
if self.fontfacedecls.hasChildNodes():
|
||||
self.fontfacedecls.toXml(1, xml)
|
||||
self.styles.toXml(1, xml)
|
||||
a = AutomaticStyles()
|
||||
a.write_open_tag(1, xml)
|
||||
for s in self._used_auto_styles([self.masterstyles]):
|
||||
s.toXml(2, xml)
|
||||
a.write_close_tag(1, xml)
|
||||
if self.masterstyles.hasChildNodes():
|
||||
self.masterstyles.toXml(1, xml)
|
||||
x.write_close_tag(0, xml)
|
||||
return xml.getvalue()
|
||||
|
||||
def addPicture(self, filename, mediatype=None, content=None):
|
||||
""" Add a picture
|
||||
It uses the same convention as OOo, in that it saves the picture in
|
||||
the zipfile in the subdirectory 'Pictures'
|
||||
If passed a file ptr, mediatype must be set
|
||||
"""
|
||||
if content is None:
|
||||
if mediatype is None:
|
||||
mediatype, encoding = mimetypes.guess_type(filename)
|
||||
if mediatype is None:
|
||||
mediatype = ''
|
||||
try: ext = filename[filename.rindex('.'):]
|
||||
except: ext=''
|
||||
else:
|
||||
ext = mimetypes.guess_extension(mediatype)
|
||||
manifestfn = "Pictures/%0.0f%s" % ((time.time()*10000000000), ext)
|
||||
self.Pictures[manifestfn] = (IS_FILENAME, filename, mediatype)
|
||||
else:
|
||||
manifestfn = filename
|
||||
self.Pictures[manifestfn] = (IS_IMAGE, content, mediatype)
|
||||
return manifestfn
|
||||
|
||||
def addPictureFromFile(self, filename, mediatype=None):
|
||||
""" Add a picture
|
||||
It uses the same convention as OOo, in that it saves the picture in
|
||||
the zipfile in the subdirectory 'Pictures'.
|
||||
If mediatype is not given, it will be guessed from the filename
|
||||
extension.
|
||||
"""
|
||||
if mediatype is None:
|
||||
mediatype, encoding = mimetypes.guess_type(filename)
|
||||
if mediatype is None:
|
||||
mediatype = ''
|
||||
try: ext = filename[filename.rindex('.'):]
|
||||
except ValueError: ext=''
|
||||
else:
|
||||
ext = mimetypes.guess_extension(mediatype)
|
||||
manifestfn = "Pictures/%0.0f%s" % ((time.time()*10000000000), ext)
|
||||
self.Pictures[manifestfn] = (IS_FILENAME, filename, mediatype)
|
||||
return manifestfn
|
||||
|
||||
def addPictureFromString(self, content, mediatype):
|
||||
""" Add a picture
|
||||
It uses the same convention as OOo, in that it saves the picture in
|
||||
the zipfile in the subdirectory 'Pictures'. The content variable
|
||||
is a string that contains the binary image data. The mediatype
|
||||
indicates the image format.
|
||||
"""
|
||||
ext = mimetypes.guess_extension(mediatype)
|
||||
manifestfn = "Pictures/%0.0f%s" % ((time.time()*10000000000), ext)
|
||||
self.Pictures[manifestfn] = (IS_IMAGE, content, mediatype)
|
||||
return manifestfn
|
||||
|
||||
def addThumbnail(self, filecontent=None):
|
||||
""" Add a fixed thumbnail
|
||||
The thumbnail in the library is big, so this is pretty useless.
|
||||
"""
|
||||
if filecontent is None:
|
||||
import thumbnail
|
||||
self.thumbnail = thumbnail.thumbnail()
|
||||
else:
|
||||
self.thumbnail = filecontent
|
||||
|
||||
def addObject(self, document, objectname=None):
|
||||
""" Adds an object (subdocument). The object must be an OpenDocument class
|
||||
The return value will be the folder in the zipfile the object is stored in
|
||||
"""
|
||||
self.childobjects.append(document)
|
||||
if objectname is None:
|
||||
document.folder = "%s/Object %d" % (self.folder, len(self.childobjects))
|
||||
else:
|
||||
document.folder = objectname
|
||||
return ".%s" % document.folder
|
||||
|
||||
def _savePictures(self, object, folder):
|
||||
hasPictures = False
|
||||
for arcname, picturerec in object.Pictures.items():
|
||||
what_it_is, fileobj, mediatype = picturerec
|
||||
self.manifest.addElement(manifest.FileEntry(fullpath="%s%s" % ( folder ,arcname), mediatype=mediatype))
|
||||
hasPictures = True
|
||||
if what_it_is == IS_FILENAME:
|
||||
self._z.write(fileobj, arcname, zipfile.ZIP_STORED)
|
||||
else:
|
||||
zi = zipfile.ZipInfo(str(arcname), self._now)
|
||||
zi.compress_type = zipfile.ZIP_STORED
|
||||
zi.external_attr = UNIXPERMS
|
||||
self._z.writestr(zi, fileobj)
|
||||
# According to section 17.7.3 in ODF 1.1, the pictures folder should not have a manifest entry
|
||||
# if hasPictures:
|
||||
# self.manifest.addElement(manifest.FileEntry(fullpath="%sPictures/" % folder, mediatype=""))
|
||||
# Look in subobjects
|
||||
subobjectnum = 1
|
||||
for subobject in object.childobjects:
|
||||
self._savePictures(subobject,'%sObject %d/' % (folder, subobjectnum))
|
||||
subobjectnum += 1
|
||||
|
||||
def __replaceGenerator(self):
|
||||
""" Section 3.1.1: The application MUST NOT export the original identifier
|
||||
belonging to the application that created the document.
|
||||
"""
|
||||
for m in self.meta.childNodes[:]:
|
||||
if m.qname == (METANS, u'generator'):
|
||||
self.meta.removeChild(m)
|
||||
self.meta.addElement(meta.Generator(text=TOOLSVERSION))
|
||||
|
||||
def save(self, outputfile, addsuffix=False):
|
||||
""" Save the document under the filename.
|
||||
If the filename is '-' then save to stdout
|
||||
"""
|
||||
if outputfile == '-':
|
||||
outputfp = zipfile.ZipFile(sys.stdout,"w")
|
||||
else:
|
||||
if addsuffix:
|
||||
outputfile = outputfile + odmimetypes.get(self.mimetype,'.xxx')
|
||||
outputfp = zipfile.ZipFile(outputfile, "w")
|
||||
self.__zipwrite(outputfp)
|
||||
outputfp.close()
|
||||
|
||||
def write(self, outputfp):
|
||||
""" User API to write the ODF file to an open file descriptor
|
||||
Writes the ZIP format
|
||||
"""
|
||||
zipoutputfp = zipfile.ZipFile(outputfp,"w")
|
||||
self.__zipwrite(zipoutputfp)
|
||||
|
||||
def __zipwrite(self, outputfp):
|
||||
""" Write the document to an open file pointer
|
||||
This is where the real work is done
|
||||
"""
|
||||
self._z = outputfp
|
||||
self._now = time.localtime()[:6]
|
||||
self.manifest = manifest.Manifest()
|
||||
|
||||
# Write mimetype
|
||||
zi = zipfile.ZipInfo('mimetype', self._now)
|
||||
zi.compress_type = zipfile.ZIP_STORED
|
||||
zi.external_attr = UNIXPERMS
|
||||
self._z.writestr(zi, self.mimetype)
|
||||
|
||||
self._saveXmlObjects(self,"")
|
||||
|
||||
# Write pictures
|
||||
self._savePictures(self,"")
|
||||
|
||||
# Write the thumbnail
|
||||
if self.thumbnail is not None:
|
||||
self.manifest.addElement(manifest.FileEntry(fullpath="Thumbnails/", mediatype=''))
|
||||
self.manifest.addElement(manifest.FileEntry(fullpath="Thumbnails/thumbnail.png", mediatype=''))
|
||||
zi = zipfile.ZipInfo("Thumbnails/thumbnail.png", self._now)
|
||||
zi.compress_type = zipfile.ZIP_DEFLATED
|
||||
zi.external_attr = UNIXPERMS
|
||||
self._z.writestr(zi, self.thumbnail)
|
||||
|
||||
# Write any extra files
|
||||
for op in self._extra:
|
||||
if op.filename == "META-INF/documentsignatures.xml": continue # Don't save signatures
|
||||
self.manifest.addElement(manifest.FileEntry(fullpath=op.filename, mediatype=op.mediatype))
|
||||
zi = zipfile.ZipInfo(op.filename.encode('utf-8'), self._now)
|
||||
zi.compress_type = zipfile.ZIP_DEFLATED
|
||||
zi.external_attr = UNIXPERMS
|
||||
if op.content is not None:
|
||||
self._z.writestr(zi, op.content)
|
||||
# Write manifest
|
||||
zi = zipfile.ZipInfo("META-INF/manifest.xml", self._now)
|
||||
zi.compress_type = zipfile.ZIP_DEFLATED
|
||||
zi.external_attr = UNIXPERMS
|
||||
self._z.writestr(zi, self.__manifestxml() )
|
||||
del self._z
|
||||
del self._now
|
||||
del self.manifest
|
||||
|
||||
|
||||
def _saveXmlObjects(self, object, folder):
|
||||
if self == object:
|
||||
self.manifest.addElement(manifest.FileEntry(fullpath="/", mediatype=object.mimetype))
|
||||
else:
|
||||
self.manifest.addElement(manifest.FileEntry(fullpath=folder, mediatype=object.mimetype))
|
||||
# Write styles
|
||||
self.manifest.addElement(manifest.FileEntry(fullpath="%sstyles.xml" % folder, mediatype="text/xml"))
|
||||
zi = zipfile.ZipInfo("%sstyles.xml" % folder, self._now)
|
||||
zi.compress_type = zipfile.ZIP_DEFLATED
|
||||
zi.external_attr = UNIXPERMS
|
||||
self._z.writestr(zi, object.stylesxml() )
|
||||
|
||||
# Write content
|
||||
self.manifest.addElement(manifest.FileEntry(fullpath="%scontent.xml" % folder, mediatype="text/xml"))
|
||||
zi = zipfile.ZipInfo("%scontent.xml" % folder, self._now)
|
||||
zi.compress_type = zipfile.ZIP_DEFLATED
|
||||
zi.external_attr = UNIXPERMS
|
||||
self._z.writestr(zi, object.contentxml() )
|
||||
|
||||
# Write settings
|
||||
if object.settings.hasChildNodes():
|
||||
self.manifest.addElement(manifest.FileEntry(fullpath="%ssettings.xml" % folder, mediatype="text/xml"))
|
||||
zi = zipfile.ZipInfo("%ssettings.xml" % folder, self._now)
|
||||
zi.compress_type = zipfile.ZIP_DEFLATED
|
||||
zi.external_attr = UNIXPERMS
|
||||
self._z.writestr(zi, object.settingsxml() )
|
||||
|
||||
# Write meta
|
||||
if self == object:
|
||||
self.manifest.addElement(manifest.FileEntry(fullpath="meta.xml", mediatype="text/xml"))
|
||||
zi = zipfile.ZipInfo("meta.xml", self._now)
|
||||
zi.compress_type = zipfile.ZIP_DEFLATED
|
||||
zi.external_attr = UNIXPERMS
|
||||
self._z.writestr(zi, object.metaxml() )
|
||||
|
||||
# Write subobjects
|
||||
subobjectnum = 1
|
||||
for subobject in object.childobjects:
|
||||
self._saveXmlObjects(subobject, '%sObject %d/' % (folder, subobjectnum))
|
||||
subobjectnum += 1
|
||||
|
||||
# Document's DOM methods
|
||||
def createElement(self, element):
|
||||
""" Inconvenient interface to create an element, but follows XML-DOM.
|
||||
Does not allow attributes as argument, therefore can't check grammar.
|
||||
"""
|
||||
return element(check_grammar=False)
|
||||
|
||||
def createTextNode(self, data):
|
||||
""" Method to create a text node """
|
||||
return element.Text(data)
|
||||
|
||||
def createCDATASection(self, data):
|
||||
""" Method to create a CDATA section """
|
||||
return element.CDATASection(cdata)
|
||||
|
||||
def getMediaType(self):
|
||||
""" Returns the media type """
|
||||
return self.mimetype
|
||||
|
||||
def getStyleByName(self, name):
|
||||
""" Finds a style object based on the name """
|
||||
ncname = make_NCName(name)
|
||||
if self._styles_dict == {}:
|
||||
self.rebuild_caches()
|
||||
return self._styles_dict.get(ncname, None)
|
||||
|
||||
def getElementsByType(self, element):
|
||||
""" Gets elements based on the type, which is function from text.py, draw.py etc. """
|
||||
obj = element(check_grammar=False)
|
||||
if self.element_dict == {}:
|
||||
self.rebuild_caches()
|
||||
return self.element_dict.get(obj.qname, [])
|
||||
|
||||
# Convenience functions
|
||||
def OpenDocumentChart():
|
||||
""" Creates a chart document """
|
||||
doc = OpenDocument('application/vnd.oasis.opendocument.chart')
|
||||
doc.chart = Chart()
|
||||
doc.body.addElement(doc.chart)
|
||||
return doc
|
||||
|
||||
def OpenDocumentDrawing():
|
||||
""" Creates a drawing document """
|
||||
doc = OpenDocument('application/vnd.oasis.opendocument.graphics')
|
||||
doc.drawing = Drawing()
|
||||
doc.body.addElement(doc.drawing)
|
||||
return doc
|
||||
|
||||
def OpenDocumentImage():
|
||||
""" Creates an image document """
|
||||
doc = OpenDocument('application/vnd.oasis.opendocument.image')
|
||||
doc.image = Image()
|
||||
doc.body.addElement(doc.image)
|
||||
return doc
|
||||
|
||||
def OpenDocumentPresentation():
|
||||
""" Creates a presentation document """
|
||||
doc = OpenDocument('application/vnd.oasis.opendocument.presentation')
|
||||
doc.presentation = Presentation()
|
||||
doc.body.addElement(doc.presentation)
|
||||
return doc
|
||||
|
||||
def OpenDocumentSpreadsheet():
|
||||
""" Creates a spreadsheet document """
|
||||
doc = OpenDocument('application/vnd.oasis.opendocument.spreadsheet')
|
||||
doc.spreadsheet = Spreadsheet()
|
||||
doc.body.addElement(doc.spreadsheet)
|
||||
return doc
|
||||
|
||||
def OpenDocumentText():
|
||||
""" Creates a text document """
|
||||
doc = OpenDocument('application/vnd.oasis.opendocument.text')
|
||||
doc.text = Text()
|
||||
doc.body.addElement(doc.text)
|
||||
return doc
|
||||
|
||||
def OpenDocumentTextMaster():
|
||||
""" Creates a text master document """
|
||||
doc = OpenDocument('application/vnd.oasis.opendocument.text-master')
|
||||
doc.text = Text()
|
||||
doc.body.addElement(doc.text)
|
||||
return doc
|
||||
|
||||
def __loadxmlparts(z, manifest, doc, objectpath):
|
||||
from load import LoadParser
|
||||
from xml.sax import make_parser, handler
|
||||
|
||||
for xmlfile in (objectpath+'settings.xml', objectpath+'meta.xml', objectpath+'content.xml', objectpath+'styles.xml'):
|
||||
if not manifest.has_key(xmlfile):
|
||||
continue
|
||||
try:
|
||||
xmlpart = z.read(xmlfile)
|
||||
doc._parsing = xmlfile
|
||||
|
||||
parser = make_parser()
|
||||
parser.setFeature(handler.feature_namespaces, 1)
|
||||
parser.setContentHandler(LoadParser(doc))
|
||||
parser.setErrorHandler(handler.ErrorHandler())
|
||||
|
||||
inpsrc = InputSource()
|
||||
inpsrc.setByteStream(StringIO(xmlpart))
|
||||
parser.parse(inpsrc)
|
||||
del doc._parsing
|
||||
except KeyError, v: pass
|
||||
|
||||
def load(odffile):
|
||||
""" Load an ODF file into memory
|
||||
Returns a reference to the structure
|
||||
"""
|
||||
z = zipfile.ZipFile(odffile)
|
||||
mimetype = z.read('mimetype')
|
||||
doc = OpenDocument(mimetype, add_generator=False)
|
||||
|
||||
# Look in the manifest file to see if which of the four files there are
|
||||
manifestpart = z.read('META-INF/manifest.xml')
|
||||
manifest = manifestlist(manifestpart)
|
||||
__loadxmlparts(z, manifest, doc, '')
|
||||
for mentry,mvalue in manifest.items():
|
||||
if mentry[:9] == "Pictures/" and len(mentry) > 9:
|
||||
doc.addPicture(mvalue['full-path'], mvalue['media-type'], z.read(mentry))
|
||||
elif mentry == "Thumbnails/thumbnail.png":
|
||||
doc.addThumbnail(z.read(mentry))
|
||||
elif mentry in ('settings.xml', 'meta.xml', 'content.xml', 'styles.xml'):
|
||||
pass
|
||||
# Load subobjects into structure
|
||||
elif mentry[:7] == "Object " and len(mentry) < 11 and mentry[-1] == "/":
|
||||
subdoc = OpenDocument(mvalue['media-type'], add_generator=False)
|
||||
doc.addObject(subdoc, "/" + mentry[:-1])
|
||||
__loadxmlparts(z, manifest, subdoc, mentry)
|
||||
elif mentry[:7] == "Object ":
|
||||
pass # Don't load subobjects as opaque objects
|
||||
else:
|
||||
if mvalue['full-path'][-1] == '/':
|
||||
doc._extra.append(OpaqueObject(mvalue['full-path'], mvalue['media-type'], None))
|
||||
else:
|
||||
doc._extra.append(OpaqueObject(mvalue['full-path'], mvalue['media-type'], z.read(mentry)))
|
||||
# Add the SUN junk here to the struct somewhere
|
||||
# It is cached data, so it can be out-of-date
|
||||
z.close()
|
||||
b = doc.getElementsByType(Body)
|
||||
if mimetype[:39] == 'application/vnd.oasis.opendocument.text':
|
||||
doc.text = b[0].firstChild
|
||||
elif mimetype[:43] == 'application/vnd.oasis.opendocument.graphics':
|
||||
doc.graphics = b[0].firstChild
|
||||
elif mimetype[:47] == 'application/vnd.oasis.opendocument.presentation':
|
||||
doc.presentation = b[0].firstChild
|
||||
elif mimetype[:46] == 'application/vnd.oasis.opendocument.spreadsheet':
|
||||
doc.spreadsheet = b[0].firstChild
|
||||
elif mimetype[:40] == 'application/vnd.oasis.opendocument.chart':
|
||||
doc.chart = b[0].firstChild
|
||||
elif mimetype[:40] == 'application/vnd.oasis.opendocument.image':
|
||||
doc.image = b[0].firstChild
|
||||
elif mimetype[:42] == 'application/vnd.oasis.opendocument.formula':
|
||||
doc.formula = b[0].firstChild
|
||||
return doc
|
||||
|
||||
# vim: set expandtab sw=4 :
|
||||
@@ -1,85 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
|
||||
from namespaces import PRESENTATIONNS
|
||||
from element import Element
|
||||
|
||||
# ODF 1.0 section 9.6 and 9.7
|
||||
# Autogenerated
|
||||
def AnimationGroup(**args):
|
||||
return Element(qname = (PRESENTATIONNS,'animation-group'), **args)
|
||||
|
||||
def Animations(**args):
|
||||
return Element(qname = (PRESENTATIONNS,'animations'), **args)
|
||||
|
||||
def DateTime(**args):
|
||||
return Element(qname = (PRESENTATIONNS,'date-time'), **args)
|
||||
|
||||
def DateTimeDecl(**args):
|
||||
return Element(qname = (PRESENTATIONNS,'date-time-decl'), **args)
|
||||
|
||||
def Dim(**args):
|
||||
return Element(qname = (PRESENTATIONNS,'dim'), **args)
|
||||
|
||||
def EventListener(**args):
|
||||
return Element(qname = (PRESENTATIONNS,'event-listener'), **args)
|
||||
|
||||
def Footer(**args):
|
||||
return Element(qname = (PRESENTATIONNS,'footer'), **args)
|
||||
|
||||
def FooterDecl(**args):
|
||||
return Element(qname = (PRESENTATIONNS,'footer-decl'), **args)
|
||||
|
||||
def Header(**args):
|
||||
return Element(qname = (PRESENTATIONNS,'header'), **args)
|
||||
|
||||
def HeaderDecl(**args):
|
||||
return Element(qname = (PRESENTATIONNS,'header-decl'), **args)
|
||||
|
||||
def HideShape(**args):
|
||||
return Element(qname = (PRESENTATIONNS,'hide-shape'), **args)
|
||||
|
||||
def HideText(**args):
|
||||
return Element(qname = (PRESENTATIONNS,'hide-text'), **args)
|
||||
|
||||
def Notes(**args):
|
||||
return Element(qname = (PRESENTATIONNS,'notes'), **args)
|
||||
|
||||
def Placeholder(**args):
|
||||
return Element(qname = (PRESENTATIONNS,'placeholder'), **args)
|
||||
|
||||
def Play(**args):
|
||||
return Element(qname = (PRESENTATIONNS,'play'), **args)
|
||||
|
||||
def Settings(**args):
|
||||
return Element(qname = (PRESENTATIONNS,'settings'), **args)
|
||||
|
||||
def Show(**args):
|
||||
return Element(qname = (PRESENTATIONNS,'show'), **args)
|
||||
|
||||
def ShowShape(**args):
|
||||
return Element(qname = (PRESENTATIONNS,'show-shape'), **args)
|
||||
|
||||
def ShowText(**args):
|
||||
return Element(qname = (PRESENTATIONNS,'show-text'), **args)
|
||||
|
||||
def Sound(**args):
|
||||
return Element(qname = (PRESENTATIONNS,'sound'), **args)
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
|
||||
from namespaces import SCRIPTNS
|
||||
from element import Element
|
||||
|
||||
# ODF 1.0 section 12.4.1
|
||||
# The <script:event-listener> element binds an event to a macro.
|
||||
|
||||
# Autogenerated
|
||||
def EventListener(**args):
|
||||
return Element(qname = (SCRIPTNS,'event-listener'), **args)
|
||||
|
||||
@@ -1,148 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
|
||||
from namespaces import STYLENS
|
||||
from element import Element
|
||||
|
||||
def StyleElement(**args):
|
||||
e = Element(**args)
|
||||
if args.get('check_grammar', True) == True:
|
||||
if not args.has_key('displayname'):
|
||||
e.setAttrNS(STYLENS,'display-name', args.get('name'))
|
||||
return e
|
||||
|
||||
# Autogenerated
|
||||
def BackgroundImage(**args):
|
||||
return Element(qname = (STYLENS,'background-image'), **args)
|
||||
|
||||
def ChartProperties(**args):
|
||||
return Element(qname = (STYLENS,'chart-properties'), **args)
|
||||
|
||||
def Column(**args):
|
||||
return Element(qname = (STYLENS,'column'), **args)
|
||||
|
||||
def ColumnSep(**args):
|
||||
return Element(qname = (STYLENS,'column-sep'), **args)
|
||||
|
||||
def Columns(**args):
|
||||
return Element(qname = (STYLENS,'columns'), **args)
|
||||
|
||||
def DefaultStyle(**args):
|
||||
return Element(qname = (STYLENS,'default-style'), **args)
|
||||
|
||||
def DrawingPageProperties(**args):
|
||||
return Element(qname = (STYLENS,'drawing-page-properties'), **args)
|
||||
|
||||
def DropCap(**args):
|
||||
return Element(qname = (STYLENS,'drop-cap'), **args)
|
||||
|
||||
def FontFace(**args):
|
||||
return Element(qname = (STYLENS,'font-face'), **args)
|
||||
|
||||
def Footer(**args):
|
||||
return Element(qname = (STYLENS,'footer'), **args)
|
||||
|
||||
def FooterLeft(**args):
|
||||
return Element(qname = (STYLENS,'footer-left'), **args)
|
||||
|
||||
def FooterStyle(**args):
|
||||
return Element(qname = (STYLENS,'footer-style'), **args)
|
||||
|
||||
def FootnoteSep(**args):
|
||||
return Element(qname = (STYLENS,'footnote-sep'), **args)
|
||||
|
||||
def GraphicProperties(**args):
|
||||
return Element(qname = (STYLENS,'graphic-properties'), **args)
|
||||
|
||||
def HandoutMaster(**args):
|
||||
return Element(qname = (STYLENS,'handout-master'), **args)
|
||||
|
||||
def Header(**args):
|
||||
return Element(qname = (STYLENS,'header'), **args)
|
||||
|
||||
def HeaderFooterProperties(**args):
|
||||
return Element(qname = (STYLENS,'header-footer-properties'), **args)
|
||||
|
||||
def HeaderLeft(**args):
|
||||
return Element(qname = (STYLENS,'header-left'), **args)
|
||||
|
||||
def HeaderStyle(**args):
|
||||
return Element(qname = (STYLENS,'header-style'), **args)
|
||||
|
||||
def ListLevelProperties(**args):
|
||||
return Element(qname = (STYLENS,'list-level-properties'), **args)
|
||||
|
||||
def Map(**args):
|
||||
return Element(qname = (STYLENS,'map'), **args)
|
||||
|
||||
def MasterPage(**args):
|
||||
return StyleElement(qname = (STYLENS,'master-page'), **args)
|
||||
|
||||
def PageLayout(**args):
|
||||
return Element(qname = (STYLENS,'page-layout'), **args)
|
||||
|
||||
def PageLayoutProperties(**args):
|
||||
return Element(qname = (STYLENS,'page-layout-properties'), **args)
|
||||
|
||||
def ParagraphProperties(**args):
|
||||
return Element(qname = (STYLENS,'paragraph-properties'), **args)
|
||||
|
||||
def PresentationPageLayout(**args):
|
||||
return StyleElement(qname = (STYLENS,'presentation-page-layout'), **args)
|
||||
|
||||
def RegionCenter(**args):
|
||||
return Element(qname = (STYLENS,'region-center'), **args)
|
||||
|
||||
def RegionLeft(**args):
|
||||
return Element(qname = (STYLENS,'region-left'), **args)
|
||||
|
||||
def RegionRight(**args):
|
||||
return Element(qname = (STYLENS,'region-right'), **args)
|
||||
|
||||
def RubyProperties(**args):
|
||||
return Element(qname = (STYLENS,'ruby-properties'), **args)
|
||||
|
||||
def SectionProperties(**args):
|
||||
return Element(qname = (STYLENS,'section-properties'), **args)
|
||||
|
||||
def Style(**args):
|
||||
return StyleElement(qname = (STYLENS,'style'), **args)
|
||||
|
||||
def TabStop(**args):
|
||||
return Element(qname = (STYLENS,'tab-stop'), **args)
|
||||
|
||||
def TabStops(**args):
|
||||
return Element(qname = (STYLENS,'tab-stops'), **args)
|
||||
|
||||
def TableCellProperties(**args):
|
||||
return Element(qname = (STYLENS,'table-cell-properties'), **args)
|
||||
|
||||
def TableColumnProperties(**args):
|
||||
return Element(qname = (STYLENS,'table-column-properties'), **args)
|
||||
|
||||
def TableProperties(**args):
|
||||
return Element(qname = (STYLENS,'table-properties'), **args)
|
||||
|
||||
def TableRowProperties(**args):
|
||||
return Element(qname = (STYLENS,'table-row-properties'), **args)
|
||||
|
||||
def TextProperties(**args):
|
||||
return Element(qname = (STYLENS,'text-properties'), **args)
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
|
||||
from namespaces import SVGNS
|
||||
from element import Element
|
||||
from draw import DrawElement
|
||||
|
||||
# Autogenerated
|
||||
def DefinitionSrc(**args):
|
||||
return Element(qname = (SVGNS,'definition-src'), **args)
|
||||
|
||||
def Desc(**args):
|
||||
return Element(qname = (SVGNS,'desc'), **args)
|
||||
|
||||
def FontFaceFormat(**args):
|
||||
return Element(qname = (SVGNS,'font-face-format'), **args)
|
||||
|
||||
def FontFaceName(**args):
|
||||
return Element(qname = (SVGNS,'font-face-name'), **args)
|
||||
|
||||
def FontFaceSrc(**args):
|
||||
return Element(qname = (SVGNS,'font-face-src'), **args)
|
||||
|
||||
def FontFaceUri(**args):
|
||||
return Element(qname = (SVGNS,'font-face-uri'), **args)
|
||||
|
||||
def Lineargradient(**args):
|
||||
return DrawElement(qname = (SVGNS,'linearGradient'), **args)
|
||||
|
||||
def Radialgradient(**args):
|
||||
return DrawElement(qname = (SVGNS,'radialGradient'), **args)
|
||||
|
||||
def Stop(**args):
|
||||
return Element(qname = (SVGNS,'stop'), **args)
|
||||
|
||||
def Title(**args):
|
||||
return Element(qname = (SVGNS,'title'), **args)
|
||||
@@ -1,307 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
|
||||
from namespaces import TABLENS
|
||||
from element import Element
|
||||
|
||||
|
||||
# Autogenerated
|
||||
def Body(**args):
|
||||
return Element(qname = (TABLENS,'body'), **args)
|
||||
|
||||
def CalculationSettings(**args):
|
||||
return Element(qname = (TABLENS,'calculation-settings'), **args)
|
||||
|
||||
def CellAddress(**args):
|
||||
return Element(qname = (TABLENS,'cell-address'), **args)
|
||||
|
||||
def CellContentChange(**args):
|
||||
return Element(qname = (TABLENS,'cell-content-change'), **args)
|
||||
|
||||
def CellContentDeletion(**args):
|
||||
return Element(qname = (TABLENS,'cell-content-deletion'), **args)
|
||||
|
||||
def CellRangeSource(**args):
|
||||
return Element(qname = (TABLENS,'cell-range-source'), **args)
|
||||
|
||||
def ChangeDeletion(**args):
|
||||
return Element(qname = (TABLENS,'change-deletion'), **args)
|
||||
|
||||
def ChangeTrackTableCell(**args):
|
||||
return Element(qname = (TABLENS,'change-track-table-cell'), **args)
|
||||
|
||||
def Consolidation(**args):
|
||||
return Element(qname = (TABLENS,'consolidation'), **args)
|
||||
|
||||
def ContentValidation(**args):
|
||||
return Element(qname = (TABLENS,'content-validation'), **args)
|
||||
|
||||
def ContentValidations(**args):
|
||||
return Element(qname = (TABLENS,'content-validations'), **args)
|
||||
|
||||
def CoveredTableCell(**args):
|
||||
return Element(qname = (TABLENS,'covered-table-cell'), **args)
|
||||
|
||||
def CutOffs(**args):
|
||||
return Element(qname = (TABLENS,'cut-offs'), **args)
|
||||
|
||||
def DataPilotDisplayInfo(**args):
|
||||
return Element(qname = (TABLENS,'data-pilot-display-info'), **args)
|
||||
|
||||
def DataPilotField(**args):
|
||||
return Element(qname = (TABLENS,'data-pilot-field'), **args)
|
||||
|
||||
def DataPilotFieldReference(**args):
|
||||
return Element(qname = (TABLENS,'data-pilot-field-reference'), **args)
|
||||
|
||||
def DataPilotGroup(**args):
|
||||
return Element(qname = (TABLENS,'data-pilot-group'), **args)
|
||||
|
||||
def DataPilotGroupMember(**args):
|
||||
return Element(qname = (TABLENS,'data-pilot-group-member'), **args)
|
||||
|
||||
def DataPilotGroups(**args):
|
||||
return Element(qname = (TABLENS,'data-pilot-groups'), **args)
|
||||
|
||||
def DataPilotLayoutInfo(**args):
|
||||
return Element(qname = (TABLENS,'data-pilot-layout-info'), **args)
|
||||
|
||||
def DataPilotLevel(**args):
|
||||
return Element(qname = (TABLENS,'data-pilot-level'), **args)
|
||||
|
||||
def DataPilotMember(**args):
|
||||
return Element(qname = (TABLENS,'data-pilot-member'), **args)
|
||||
|
||||
def DataPilotMembers(**args):
|
||||
return Element(qname = (TABLENS,'data-pilot-members'), **args)
|
||||
|
||||
def DataPilotSortInfo(**args):
|
||||
return Element(qname = (TABLENS,'data-pilot-sort-info'), **args)
|
||||
|
||||
def DataPilotSubtotal(**args):
|
||||
return Element(qname = (TABLENS,'data-pilot-subtotal'), **args)
|
||||
|
||||
def DataPilotSubtotals(**args):
|
||||
return Element(qname = (TABLENS,'data-pilot-subtotals'), **args)
|
||||
|
||||
def DataPilotTable(**args):
|
||||
return Element(qname = (TABLENS,'data-pilot-table'), **args)
|
||||
|
||||
def DataPilotTables(**args):
|
||||
return Element(qname = (TABLENS,'data-pilot-tables'), **args)
|
||||
|
||||
def DatabaseRange(**args):
|
||||
return Element(qname = (TABLENS,'database-range'), **args)
|
||||
|
||||
def DatabaseRanges(**args):
|
||||
return Element(qname = (TABLENS,'database-ranges'), **args)
|
||||
|
||||
def DatabaseSourceQuery(**args):
|
||||
return Element(qname = (TABLENS,'database-source-query'), **args)
|
||||
|
||||
def DatabaseSourceSql(**args):
|
||||
return Element(qname = (TABLENS,'database-source-sql'), **args)
|
||||
|
||||
def DatabaseSourceTable(**args):
|
||||
return Element(qname = (TABLENS,'database-source-table'), **args)
|
||||
|
||||
def DdeLink(**args):
|
||||
return Element(qname = (TABLENS,'dde-link'), **args)
|
||||
|
||||
def DdeLinks(**args):
|
||||
return Element(qname = (TABLENS,'dde-links'), **args)
|
||||
|
||||
def Deletion(**args):
|
||||
return Element(qname = (TABLENS,'deletion'), **args)
|
||||
|
||||
def Deletions(**args):
|
||||
return Element(qname = (TABLENS,'deletions'), **args)
|
||||
|
||||
def Dependencies(**args):
|
||||
return Element(qname = (TABLENS,'dependencies'), **args)
|
||||
|
||||
def Dependency(**args):
|
||||
return Element(qname = (TABLENS,'dependency'), **args)
|
||||
|
||||
def Detective(**args):
|
||||
return Element(qname = (TABLENS,'detective'), **args)
|
||||
|
||||
def ErrorMacro(**args):
|
||||
return Element(qname = (TABLENS,'error-macro'), **args)
|
||||
|
||||
def ErrorMessage(**args):
|
||||
return Element(qname = (TABLENS,'error-message'), **args)
|
||||
|
||||
def EvenColumns(**args):
|
||||
return Element(qname = (TABLENS,'even-columns'), **args)
|
||||
|
||||
def EvenRows(**args):
|
||||
return Element(qname = (TABLENS,'even-rows'), **args)
|
||||
|
||||
def Filter(**args):
|
||||
return Element(qname = (TABLENS,'filter'), **args)
|
||||
|
||||
def FilterAnd(**args):
|
||||
return Element(qname = (TABLENS,'filter-and'), **args)
|
||||
|
||||
def FilterCondition(**args):
|
||||
return Element(qname = (TABLENS,'filter-condition'), **args)
|
||||
|
||||
def FilterOr(**args):
|
||||
return Element(qname = (TABLENS,'filter-or'), **args)
|
||||
|
||||
def FirstColumn(**args):
|
||||
return Element(qname = (TABLENS,'first-column'), **args)
|
||||
|
||||
def FirstRow(**args):
|
||||
return Element(qname = (TABLENS,'first-row'), **args)
|
||||
|
||||
def HelpMessage(**args):
|
||||
return Element(qname = (TABLENS,'help-message'), **args)
|
||||
|
||||
def HighlightedRange(**args):
|
||||
return Element(qname = (TABLENS,'highlighted-range'), **args)
|
||||
|
||||
def Insertion(**args):
|
||||
return Element(qname = (TABLENS,'insertion'), **args)
|
||||
|
||||
def InsertionCutOff(**args):
|
||||
return Element(qname = (TABLENS,'insertion-cut-off'), **args)
|
||||
|
||||
def Iteration(**args):
|
||||
return Element(qname = (TABLENS,'iteration'), **args)
|
||||
|
||||
def LabelRange(**args):
|
||||
return Element(qname = (TABLENS,'label-range'), **args)
|
||||
|
||||
def LabelRanges(**args):
|
||||
return Element(qname = (TABLENS,'label-ranges'), **args)
|
||||
|
||||
def LastColumn(**args):
|
||||
return Element(qname = (TABLENS,'last-column'), **args)
|
||||
|
||||
def LastRow(**args):
|
||||
return Element(qname = (TABLENS,'last-row'), **args)
|
||||
|
||||
def Movement(**args):
|
||||
return Element(qname = (TABLENS,'movement'), **args)
|
||||
|
||||
def MovementCutOff(**args):
|
||||
return Element(qname = (TABLENS,'movement-cut-off'), **args)
|
||||
|
||||
def NamedExpression(**args):
|
||||
return Element(qname = (TABLENS,'named-expression'), **args)
|
||||
|
||||
def NamedExpressions(**args):
|
||||
return Element(qname = (TABLENS,'named-expressions'), **args)
|
||||
|
||||
def NamedRange(**args):
|
||||
return Element(qname = (TABLENS,'named-range'), **args)
|
||||
|
||||
def NullDate(**args):
|
||||
return Element(qname = (TABLENS,'null-date'), **args)
|
||||
|
||||
def OddColumns(**args):
|
||||
return Element(qname = (TABLENS,'odd-columns'), **args)
|
||||
|
||||
def OddRows(**args):
|
||||
return Element(qname = (TABLENS,'odd-rows'), **args)
|
||||
|
||||
def Operation(**args):
|
||||
return Element(qname = (TABLENS,'operation'), **args)
|
||||
|
||||
def Previous(**args):
|
||||
return Element(qname = (TABLENS,'previous'), **args)
|
||||
|
||||
def Scenario(**args):
|
||||
return Element(qname = (TABLENS,'scenario'), **args)
|
||||
|
||||
def Shapes(**args):
|
||||
return Element(qname = (TABLENS,'shapes'), **args)
|
||||
|
||||
def Sort(**args):
|
||||
return Element(qname = (TABLENS,'sort'), **args)
|
||||
|
||||
def SortBy(**args):
|
||||
return Element(qname = (TABLENS,'sort-by'), **args)
|
||||
|
||||
def SortGroups(**args):
|
||||
return Element(qname = (TABLENS,'sort-groups'), **args)
|
||||
|
||||
def SourceCellRange(**args):
|
||||
return Element(qname = (TABLENS,'source-cell-range'), **args)
|
||||
|
||||
def SourceRangeAddress(**args):
|
||||
return Element(qname = (TABLENS,'source-range-address'), **args)
|
||||
|
||||
def SourceService(**args):
|
||||
return Element(qname = (TABLENS,'source-service'), **args)
|
||||
|
||||
def SubtotalField(**args):
|
||||
return Element(qname = (TABLENS,'subtotal-field'), **args)
|
||||
|
||||
def SubtotalRule(**args):
|
||||
return Element(qname = (TABLENS,'subtotal-rule'), **args)
|
||||
|
||||
def SubtotalRules(**args):
|
||||
return Element(qname = (TABLENS,'subtotal-rules'), **args)
|
||||
|
||||
def Table(**args):
|
||||
return Element(qname = (TABLENS,'table'), **args)
|
||||
|
||||
def TableCell(**args):
|
||||
return Element(qname = (TABLENS,'table-cell'), **args)
|
||||
|
||||
def TableColumn(**args):
|
||||
return Element(qname = (TABLENS,'table-column'), **args)
|
||||
|
||||
def TableColumnGroup(**args):
|
||||
return Element(qname = (TABLENS,'table-column-group'), **args)
|
||||
|
||||
def TableColumns(**args):
|
||||
return Element(qname = (TABLENS,'table-columns'), **args)
|
||||
|
||||
def TableHeaderColumns(**args):
|
||||
return Element(qname = (TABLENS,'table-header-columns'), **args)
|
||||
|
||||
def TableHeaderRows(**args):
|
||||
return Element(qname = (TABLENS,'table-header-rows'), **args)
|
||||
|
||||
def TableRow(**args):
|
||||
return Element(qname = (TABLENS,'table-row'), **args)
|
||||
|
||||
def TableRowGroup(**args):
|
||||
return Element(qname = (TABLENS,'table-row-group'), **args)
|
||||
|
||||
def TableRows(**args):
|
||||
return Element(qname = (TABLENS,'table-rows'), **args)
|
||||
|
||||
def TableSource(**args):
|
||||
return Element(qname = (TABLENS,'table-source'), **args)
|
||||
|
||||
def TableTemplate(**args):
|
||||
return Element(qname = (TABLENS,'table-template'), **args)
|
||||
|
||||
def TargetRangeAddress(**args):
|
||||
return Element(qname = (TABLENS,'target-range-address'), **args)
|
||||
|
||||
def TrackedChanges(**args):
|
||||
return Element(qname = (TABLENS,'tracked-changes'), **args)
|
||||
|
||||
@@ -1,137 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Create and extract text from ODF, handling whitespace correctly.
|
||||
# Copyright (C) 2008 J. David Eisenberg
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
|
||||
"""
|
||||
Class for handling whitespace properly in OpenDocument.
|
||||
|
||||
While it is possible to use getTextContent() and setTextContent()
|
||||
to extract or create ODF content, these won't extract or create
|
||||
the appropriate <text:s>, <text:tab>, or <text:line-break>
|
||||
elements. This module takes care of that problem.
|
||||
"""
|
||||
|
||||
from odf.element import Node
|
||||
import odf.opendocument
|
||||
from odf.text import S,LineBreak,Tab
|
||||
|
||||
class WhitespaceText(object):
|
||||
|
||||
def __init__(self):
|
||||
self.textBuffer = []
|
||||
self.spaceCount = 0
|
||||
|
||||
def addTextToElement(self, odfElement, s):
|
||||
""" Process an input string, inserting
|
||||
<text:tab> elements for '\t',
|
||||
<text:line-break> elements for '\n', and
|
||||
<text:s> elements for runs of more than one blank.
|
||||
These will be added to the given element.
|
||||
"""
|
||||
i = 0
|
||||
ch = ' '
|
||||
|
||||
# When we encounter a tab or newline, we can immediately
|
||||
# dump any accumulated text and then emit the appropriate
|
||||
# ODF element.
|
||||
#
|
||||
# When we encounter a space, we add it to the text buffer,
|
||||
# and then collect more spaces. If there are more spaces
|
||||
# after the first one, we dump the text buffer and then
|
||||
# then emit the appropriate <text:s> element.
|
||||
|
||||
while i < len(s):
|
||||
ch = s[i]
|
||||
if ch == '\t':
|
||||
self._emitTextBuffer(odfElement)
|
||||
odfElement.addElement(Tab())
|
||||
i += 1
|
||||
elif ch == '\n':
|
||||
self._emitTextBuffer(odfElement);
|
||||
odfElement.addElement(LineBreak())
|
||||
i += 1
|
||||
elif ch == ' ':
|
||||
self.textBuffer.append(' ')
|
||||
i += 1
|
||||
self.spaceCount = 0
|
||||
while i < len(s) and (s[i] == ' '):
|
||||
self.spaceCount += 1
|
||||
i += 1
|
||||
if self.spaceCount > 0:
|
||||
self._emitTextBuffer(odfElement)
|
||||
self._emitSpaces(odfElement)
|
||||
else:
|
||||
self.textBuffer.append(ch)
|
||||
i += 1
|
||||
|
||||
self._emitTextBuffer(odfElement)
|
||||
|
||||
def _emitTextBuffer(self, odfElement):
|
||||
""" Creates a Text Node whose contents are the current textBuffer.
|
||||
Side effect: clears the text buffer.
|
||||
"""
|
||||
if len(self.textBuffer) > 0:
|
||||
odfElement.addText(''.join(self.textBuffer))
|
||||
self.textBuffer = []
|
||||
|
||||
|
||||
def _emitSpaces(self, odfElement):
|
||||
""" Creates a <text:s> element for the current spaceCount.
|
||||
Side effect: sets spaceCount back to zero
|
||||
"""
|
||||
if self.spaceCount > 0:
|
||||
spaceElement = S(c=self.spaceCount)
|
||||
odfElement.addElement(spaceElement)
|
||||
self.spaceCount = 0
|
||||
|
||||
def addTextToElement(odfElement, s):
|
||||
wst = WhitespaceText()
|
||||
wst.addTextToElement(odfElement, s)
|
||||
|
||||
def extractText(odfElement):
|
||||
""" Extract text content from an Element, with whitespace represented
|
||||
properly. Returns the text, with tabs, spaces, and newlines
|
||||
correctly evaluated. This method recursively descends through the
|
||||
children of the given element, accumulating text and "unwrapping"
|
||||
<text:s>, <text:tab>, and <text:line-break> elements along the way.
|
||||
"""
|
||||
result = [];
|
||||
|
||||
if len(odfElement.childNodes) != 0:
|
||||
for child in odfElement.childNodes:
|
||||
if child.nodeType == Node.TEXT_NODE:
|
||||
result.append(child.data)
|
||||
elif child.nodeType == Node.ELEMENT_NODE:
|
||||
subElement = child
|
||||
tagName = subElement.qname;
|
||||
if tagName == (u"urn:oasis:names:tc:opendocument:xmlns:text:1.0", u"line-break"):
|
||||
result.append("\n")
|
||||
elif tagName == (u"urn:oasis:names:tc:opendocument:xmlns:text:1.0", u"tab"):
|
||||
result.append("\t")
|
||||
elif tagName == (u"urn:oasis:names:tc:opendocument:xmlns:text:1.0", u"s"):
|
||||
c = subElement.getAttribute('c')
|
||||
if c:
|
||||
spaceCount = int(c)
|
||||
else:
|
||||
spaceCount = 1
|
||||
|
||||
result.append(" " * spaceCount)
|
||||
else:
|
||||
result.append(extractText(subElement))
|
||||
return ''.join(result)
|
||||
@@ -1,562 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
|
||||
from namespaces import TEXTNS
|
||||
from element import Element
|
||||
from style import StyleElement
|
||||
|
||||
# Autogenerated
|
||||
def A(**args):
|
||||
return Element(qname = (TEXTNS,'a'), **args)
|
||||
|
||||
def AlphabeticalIndex(**args):
|
||||
return Element(qname = (TEXTNS,'alphabetical-index'), **args)
|
||||
|
||||
def AlphabeticalIndexAutoMarkFile(**args):
|
||||
return Element(qname = (TEXTNS,'alphabetical-index-auto-mark-file'), **args)
|
||||
|
||||
def AlphabeticalIndexEntryTemplate(**args):
|
||||
return Element(qname = (TEXTNS,'alphabetical-index-entry-template'), **args)
|
||||
|
||||
def AlphabeticalIndexMark(**args):
|
||||
return Element(qname = (TEXTNS,'alphabetical-index-mark'), **args)
|
||||
|
||||
def AlphabeticalIndexMarkEnd(**args):
|
||||
return Element(qname = (TEXTNS,'alphabetical-index-mark-end'), **args)
|
||||
|
||||
def AlphabeticalIndexMarkStart(**args):
|
||||
return Element(qname = (TEXTNS,'alphabetical-index-mark-start'), **args)
|
||||
|
||||
def AlphabeticalIndexSource(**args):
|
||||
return Element(qname = (TEXTNS,'alphabetical-index-source'), **args)
|
||||
|
||||
def AuthorInitials(**args):
|
||||
return Element(qname = (TEXTNS,'author-initials'), **args)
|
||||
|
||||
def AuthorName(**args):
|
||||
return Element(qname = (TEXTNS,'author-name'), **args)
|
||||
|
||||
def Bibliography(**args):
|
||||
return Element(qname = (TEXTNS,'bibliography'), **args)
|
||||
|
||||
def BibliographyConfiguration(**args):
|
||||
return Element(qname = (TEXTNS,'bibliography-configuration'), **args)
|
||||
|
||||
def BibliographyEntryTemplate(**args):
|
||||
return Element(qname = (TEXTNS,'bibliography-entry-template'), **args)
|
||||
|
||||
def BibliographyMark(**args):
|
||||
return Element(qname = (TEXTNS,'bibliography-mark'), **args)
|
||||
|
||||
def BibliographySource(**args):
|
||||
return Element(qname = (TEXTNS,'bibliography-source'), **args)
|
||||
|
||||
def Bookmark(**args):
|
||||
return Element(qname = (TEXTNS,'bookmark'), **args)
|
||||
|
||||
def BookmarkEnd(**args):
|
||||
return Element(qname = (TEXTNS,'bookmark-end'), **args)
|
||||
|
||||
def BookmarkRef(**args):
|
||||
return Element(qname = (TEXTNS,'bookmark-ref'), **args)
|
||||
|
||||
def BookmarkStart(**args):
|
||||
return Element(qname = (TEXTNS,'bookmark-start'), **args)
|
||||
|
||||
def Change(**args):
|
||||
return Element(qname = (TEXTNS,'change'), **args)
|
||||
|
||||
def ChangeEnd(**args):
|
||||
return Element(qname = (TEXTNS,'change-end'), **args)
|
||||
|
||||
def ChangeStart(**args):
|
||||
return Element(qname = (TEXTNS,'change-start'), **args)
|
||||
|
||||
def ChangedRegion(**args):
|
||||
return Element(qname = (TEXTNS,'changed-region'), **args)
|
||||
|
||||
def Chapter(**args):
|
||||
return Element(qname = (TEXTNS,'chapter'), **args)
|
||||
|
||||
def CharacterCount(**args):
|
||||
return Element(qname = (TEXTNS,'character-count'), **args)
|
||||
|
||||
def ConditionalText(**args):
|
||||
return Element(qname = (TEXTNS,'conditional-text'), **args)
|
||||
|
||||
def CreationDate(**args):
|
||||
return Element(qname = (TEXTNS,'creation-date'), **args)
|
||||
|
||||
def CreationTime(**args):
|
||||
return Element(qname = (TEXTNS,'creation-time'), **args)
|
||||
|
||||
def Creator(**args):
|
||||
return Element(qname = (TEXTNS,'creator'), **args)
|
||||
|
||||
def DatabaseDisplay(**args):
|
||||
return Element(qname = (TEXTNS,'database-display'), **args)
|
||||
|
||||
def DatabaseName(**args):
|
||||
return Element(qname = (TEXTNS,'database-name'), **args)
|
||||
|
||||
def DatabaseNext(**args):
|
||||
return Element(qname = (TEXTNS,'database-next'), **args)
|
||||
|
||||
def DatabaseRowNumber(**args):
|
||||
return Element(qname = (TEXTNS,'database-row-number'), **args)
|
||||
|
||||
def DatabaseRowSelect(**args):
|
||||
return Element(qname = (TEXTNS,'database-row-select'), **args)
|
||||
|
||||
def Date(**args):
|
||||
return Element(qname = (TEXTNS,'date'), **args)
|
||||
|
||||
def DdeConnection(**args):
|
||||
return Element(qname = (TEXTNS,'dde-connection'), **args)
|
||||
|
||||
def DdeConnectionDecl(**args):
|
||||
return Element(qname = (TEXTNS,'dde-connection-decl'), **args)
|
||||
|
||||
def DdeConnectionDecls(**args):
|
||||
return Element(qname = (TEXTNS,'dde-connection-decls'), **args)
|
||||
|
||||
def Deletion(**args):
|
||||
return Element(qname = (TEXTNS,'deletion'), **args)
|
||||
|
||||
def Description(**args):
|
||||
return Element(qname = (TEXTNS,'description'), **args)
|
||||
|
||||
def EditingCycles(**args):
|
||||
return Element(qname = (TEXTNS,'editing-cycles'), **args)
|
||||
|
||||
def EditingDuration(**args):
|
||||
return Element(qname = (TEXTNS,'editing-duration'), **args)
|
||||
|
||||
def ExecuteMacro(**args):
|
||||
return Element(qname = (TEXTNS,'execute-macro'), **args)
|
||||
|
||||
def Expression(**args):
|
||||
return Element(qname = (TEXTNS,'expression'), **args)
|
||||
|
||||
def FileName(**args):
|
||||
return Element(qname = (TEXTNS,'file-name'), **args)
|
||||
|
||||
def FormatChange(**args):
|
||||
return Element(qname = (TEXTNS,'format-change'), **args)
|
||||
|
||||
def H(**args):
|
||||
return Element(qname = (TEXTNS, 'h'), **args)
|
||||
|
||||
def HiddenParagraph(**args):
|
||||
return Element(qname = (TEXTNS,'hidden-paragraph'), **args)
|
||||
|
||||
def HiddenText(**args):
|
||||
return Element(qname = (TEXTNS,'hidden-text'), **args)
|
||||
|
||||
def IllustrationIndex(**args):
|
||||
return Element(qname = (TEXTNS,'illustration-index'), **args)
|
||||
|
||||
def IllustrationIndexEntryTemplate(**args):
|
||||
return Element(qname = (TEXTNS,'illustration-index-entry-template'), **args)
|
||||
|
||||
def IllustrationIndexSource(**args):
|
||||
return Element(qname = (TEXTNS,'illustration-index-source'), **args)
|
||||
|
||||
def ImageCount(**args):
|
||||
return Element(qname = (TEXTNS,'image-count'), **args)
|
||||
|
||||
def IndexBody(**args):
|
||||
return Element(qname = (TEXTNS,'index-body'), **args)
|
||||
|
||||
def IndexEntryBibliography(**args):
|
||||
return Element(qname = (TEXTNS,'index-entry-bibliography'), **args)
|
||||
|
||||
def IndexEntryChapter(**args):
|
||||
return Element(qname = (TEXTNS,'index-entry-chapter'), **args)
|
||||
|
||||
def IndexEntryLinkEnd(**args):
|
||||
return Element(qname = (TEXTNS,'index-entry-link-end'), **args)
|
||||
|
||||
def IndexEntryLinkStart(**args):
|
||||
return Element(qname = (TEXTNS,'index-entry-link-start'), **args)
|
||||
|
||||
def IndexEntryPageNumber(**args):
|
||||
return Element(qname = (TEXTNS,'index-entry-page-number'), **args)
|
||||
|
||||
def IndexEntrySpan(**args):
|
||||
return Element(qname = (TEXTNS,'index-entry-span'), **args)
|
||||
|
||||
def IndexEntryTabStop(**args):
|
||||
return Element(qname = (TEXTNS,'index-entry-tab-stop'), **args)
|
||||
|
||||
def IndexEntryText(**args):
|
||||
return Element(qname = (TEXTNS,'index-entry-text'), **args)
|
||||
|
||||
def IndexSourceStyle(**args):
|
||||
return Element(qname = (TEXTNS,'index-source-style'), **args)
|
||||
|
||||
def IndexSourceStyles(**args):
|
||||
return Element(qname = (TEXTNS,'index-source-styles'), **args)
|
||||
|
||||
def IndexTitle(**args):
|
||||
return Element(qname = (TEXTNS,'index-title'), **args)
|
||||
|
||||
def IndexTitleTemplate(**args):
|
||||
return Element(qname = (TEXTNS,'index-title-template'), **args)
|
||||
|
||||
def InitialCreator(**args):
|
||||
return Element(qname = (TEXTNS,'initial-creator'), **args)
|
||||
|
||||
def Insertion(**args):
|
||||
return Element(qname = (TEXTNS,'insertion'), **args)
|
||||
|
||||
def Keywords(**args):
|
||||
return Element(qname = (TEXTNS,'keywords'), **args)
|
||||
|
||||
def LineBreak(**args):
|
||||
return Element(qname = (TEXTNS,'line-break'), **args)
|
||||
|
||||
def LinenumberingConfiguration(**args):
|
||||
return Element(qname = (TEXTNS,'linenumbering-configuration'), **args)
|
||||
|
||||
def LinenumberingSeparator(**args):
|
||||
return Element(qname = (TEXTNS,'linenumbering-separator'), **args)
|
||||
|
||||
def List(**args):
|
||||
return Element(qname = (TEXTNS,'list'), **args)
|
||||
|
||||
def ListHeader(**args):
|
||||
return Element(qname = (TEXTNS,'list-header'), **args)
|
||||
|
||||
def ListItem(**args):
|
||||
return Element(qname = (TEXTNS,'list-item'), **args)
|
||||
|
||||
def ListLevelStyleBullet(**args):
|
||||
return Element(qname = (TEXTNS,'list-level-style-bullet'), **args)
|
||||
|
||||
def ListLevelStyleImage(**args):
|
||||
return Element(qname = (TEXTNS,'list-level-style-image'), **args)
|
||||
|
||||
def ListLevelStyleNumber(**args):
|
||||
return Element(qname = (TEXTNS,'list-level-style-number'), **args)
|
||||
|
||||
def ListStyle(**args):
|
||||
return StyleElement(qname = (TEXTNS,'list-style'), **args)
|
||||
|
||||
def Measure(**args):
|
||||
return Element(qname = (TEXTNS,'measure'), **args)
|
||||
|
||||
def ModificationDate(**args):
|
||||
return Element(qname = (TEXTNS,'modification-date'), **args)
|
||||
|
||||
def ModificationTime(**args):
|
||||
return Element(qname = (TEXTNS,'modification-time'), **args)
|
||||
|
||||
def Note(**args):
|
||||
return Element(qname = (TEXTNS,'note'), **args)
|
||||
|
||||
def NoteBody(**args):
|
||||
return Element(qname = (TEXTNS,'note-body'), **args)
|
||||
|
||||
def NoteCitation(**args):
|
||||
return Element(qname = (TEXTNS,'note-citation'), **args)
|
||||
|
||||
def NoteContinuationNoticeBackward(**args):
|
||||
return Element(qname = (TEXTNS,'note-continuation-notice-backward'), **args)
|
||||
|
||||
def NoteContinuationNoticeForward(**args):
|
||||
return Element(qname = (TEXTNS,'note-continuation-notice-forward'), **args)
|
||||
|
||||
def NoteRef(**args):
|
||||
return Element(qname = (TEXTNS,'note-ref'), **args)
|
||||
|
||||
def NotesConfiguration(**args):
|
||||
return Element(qname = (TEXTNS,'notes-configuration'), **args)
|
||||
|
||||
def Number(**args):
|
||||
return Element(qname = (TEXTNS,'number'), **args)
|
||||
|
||||
def NumberedParagraph(**args):
|
||||
return Element(qname = (TEXTNS,'numbered-paragraph'), **args)
|
||||
|
||||
def ObjectCount(**args):
|
||||
return Element(qname = (TEXTNS,'object-count'), **args)
|
||||
|
||||
def ObjectIndex(**args):
|
||||
return Element(qname = (TEXTNS,'object-index'), **args)
|
||||
|
||||
def ObjectIndexEntryTemplate(**args):
|
||||
return Element(qname = (TEXTNS,'object-index-entry-template'), **args)
|
||||
|
||||
def ObjectIndexSource(**args):
|
||||
return Element(qname = (TEXTNS,'object-index-source'), **args)
|
||||
|
||||
def OutlineLevelStyle(**args):
|
||||
return Element(qname = (TEXTNS,'outline-level-style'), **args)
|
||||
|
||||
def OutlineStyle(**args):
|
||||
return Element(qname = (TEXTNS,'outline-style'), **args)
|
||||
|
||||
def P(**args):
|
||||
return Element(qname = (TEXTNS, 'p'), **args)
|
||||
|
||||
def Page(**args):
|
||||
return Element(qname = (TEXTNS,'page'), **args)
|
||||
|
||||
def PageContinuation(**args):
|
||||
return Element(qname = (TEXTNS,'page-continuation'), **args)
|
||||
|
||||
def PageCount(**args):
|
||||
return Element(qname = (TEXTNS,'page-count'), **args)
|
||||
|
||||
def PageNumber(**args):
|
||||
return Element(qname = (TEXTNS,'page-number'), **args)
|
||||
|
||||
def PageSequence(**args):
|
||||
return Element(qname = (TEXTNS,'page-sequence'), **args)
|
||||
|
||||
def PageVariableGet(**args):
|
||||
return Element(qname = (TEXTNS,'page-variable-get'), **args)
|
||||
|
||||
def PageVariableSet(**args):
|
||||
return Element(qname = (TEXTNS,'page-variable-set'), **args)
|
||||
|
||||
def ParagraphCount(**args):
|
||||
return Element(qname = (TEXTNS,'paragraph-count'), **args)
|
||||
|
||||
def Placeholder(**args):
|
||||
return Element(qname = (TEXTNS,'placeholder'), **args)
|
||||
|
||||
def PrintDate(**args):
|
||||
return Element(qname = (TEXTNS,'print-date'), **args)
|
||||
|
||||
def PrintTime(**args):
|
||||
return Element(qname = (TEXTNS,'print-time'), **args)
|
||||
|
||||
def PrintedBy(**args):
|
||||
return Element(qname = (TEXTNS,'printed-by'), **args)
|
||||
|
||||
def ReferenceMark(**args):
|
||||
return Element(qname = (TEXTNS,'reference-mark'), **args)
|
||||
|
||||
def ReferenceMarkEnd(**args):
|
||||
return Element(qname = (TEXTNS,'reference-mark-end'), **args)
|
||||
|
||||
def ReferenceMarkStart(**args):
|
||||
return Element(qname = (TEXTNS,'reference-mark-start'), **args)
|
||||
|
||||
def ReferenceRef(**args):
|
||||
return Element(qname = (TEXTNS,'reference-ref'), **args)
|
||||
|
||||
def Ruby(**args):
|
||||
return Element(qname = (TEXTNS,'ruby'), **args)
|
||||
|
||||
def RubyBase(**args):
|
||||
return Element(qname = (TEXTNS,'ruby-base'), **args)
|
||||
|
||||
def RubyText(**args):
|
||||
return Element(qname = (TEXTNS,'ruby-text'), **args)
|
||||
|
||||
def S(**args):
|
||||
return Element(qname = (TEXTNS,'s'), **args)
|
||||
|
||||
def Script(**args):
|
||||
return Element(qname = (TEXTNS,'script'), **args)
|
||||
|
||||
def Section(**args):
|
||||
return Element(qname = (TEXTNS,'section'), **args)
|
||||
|
||||
def SectionSource(**args):
|
||||
return Element(qname = (TEXTNS,'section-source'), **args)
|
||||
|
||||
def SenderCity(**args):
|
||||
return Element(qname = (TEXTNS,'sender-city'), **args)
|
||||
|
||||
def SenderCompany(**args):
|
||||
return Element(qname = (TEXTNS,'sender-company'), **args)
|
||||
|
||||
def SenderCountry(**args):
|
||||
return Element(qname = (TEXTNS,'sender-country'), **args)
|
||||
|
||||
def SenderEmail(**args):
|
||||
return Element(qname = (TEXTNS,'sender-email'), **args)
|
||||
|
||||
def SenderFax(**args):
|
||||
return Element(qname = (TEXTNS,'sender-fax'), **args)
|
||||
|
||||
def SenderFirstname(**args):
|
||||
return Element(qname = (TEXTNS,'sender-firstname'), **args)
|
||||
|
||||
def SenderInitials(**args):
|
||||
return Element(qname = (TEXTNS,'sender-initials'), **args)
|
||||
|
||||
def SenderLastname(**args):
|
||||
return Element(qname = (TEXTNS,'sender-lastname'), **args)
|
||||
|
||||
def SenderPhonePrivate(**args):
|
||||
return Element(qname = (TEXTNS,'sender-phone-private'), **args)
|
||||
|
||||
def SenderPhoneWork(**args):
|
||||
return Element(qname = (TEXTNS,'sender-phone-work'), **args)
|
||||
|
||||
def SenderPosition(**args):
|
||||
return Element(qname = (TEXTNS,'sender-position'), **args)
|
||||
|
||||
def SenderPostalCode(**args):
|
||||
return Element(qname = (TEXTNS,'sender-postal-code'), **args)
|
||||
|
||||
def SenderStateOrProvince(**args):
|
||||
return Element(qname = (TEXTNS,'sender-state-or-province'), **args)
|
||||
|
||||
def SenderStreet(**args):
|
||||
return Element(qname = (TEXTNS,'sender-street'), **args)
|
||||
|
||||
def SenderTitle(**args):
|
||||
return Element(qname = (TEXTNS,'sender-title'), **args)
|
||||
|
||||
def Sequence(**args):
|
||||
return Element(qname = (TEXTNS,'sequence'), **args)
|
||||
|
||||
def SequenceDecl(**args):
|
||||
return Element(qname = (TEXTNS,'sequence-decl'), **args)
|
||||
|
||||
def SequenceDecls(**args):
|
||||
return Element(qname = (TEXTNS,'sequence-decls'), **args)
|
||||
|
||||
def SequenceRef(**args):
|
||||
return Element(qname = (TEXTNS,'sequence-ref'), **args)
|
||||
|
||||
def SheetName(**args):
|
||||
return Element(qname = (TEXTNS,'sheet-name'), **args)
|
||||
|
||||
def SoftPageBreak(**args):
|
||||
return Element(qname = (TEXTNS,'soft-page-break'), **args)
|
||||
|
||||
def SortKey(**args):
|
||||
return Element(qname = (TEXTNS,'sort-key'), **args)
|
||||
|
||||
def Span(**args):
|
||||
return Element(qname = (TEXTNS,'span'), **args)
|
||||
|
||||
def Subject(**args):
|
||||
return Element(qname = (TEXTNS,'subject'), **args)
|
||||
|
||||
def Tab(**args):
|
||||
return Element(qname = (TEXTNS,'tab'), **args)
|
||||
|
||||
def TableCount(**args):
|
||||
return Element(qname = (TEXTNS,'table-count'), **args)
|
||||
|
||||
def TableFormula(**args):
|
||||
return Element(qname = (TEXTNS,'table-formula'), **args)
|
||||
|
||||
def TableIndex(**args):
|
||||
return Element(qname = (TEXTNS,'table-index'), **args)
|
||||
|
||||
def TableIndexEntryTemplate(**args):
|
||||
return Element(qname = (TEXTNS,'table-index-entry-template'), **args)
|
||||
|
||||
def TableIndexSource(**args):
|
||||
return Element(qname = (TEXTNS,'table-index-source'), **args)
|
||||
|
||||
def TableOfContent(**args):
|
||||
return Element(qname = (TEXTNS,'table-of-content'), **args)
|
||||
|
||||
def TableOfContentEntryTemplate(**args):
|
||||
return Element(qname = (TEXTNS,'table-of-content-entry-template'), **args)
|
||||
|
||||
def TableOfContentSource(**args):
|
||||
return Element(qname = (TEXTNS,'table-of-content-source'), **args)
|
||||
|
||||
def TemplateName(**args):
|
||||
return Element(qname = (TEXTNS,'template-name'), **args)
|
||||
|
||||
def TextInput(**args):
|
||||
return Element(qname = (TEXTNS,'text-input'), **args)
|
||||
|
||||
def Time(**args):
|
||||
return Element(qname = (TEXTNS,'time'), **args)
|
||||
|
||||
def Title(**args):
|
||||
return Element(qname = (TEXTNS,'title'), **args)
|
||||
|
||||
def TocMark(**args):
|
||||
return Element(qname = (TEXTNS,'toc-mark'), **args)
|
||||
|
||||
def TocMarkEnd(**args):
|
||||
return Element(qname = (TEXTNS,'toc-mark-end'), **args)
|
||||
|
||||
def TocMarkStart(**args):
|
||||
return Element(qname = (TEXTNS,'toc-mark-start'), **args)
|
||||
|
||||
def TrackedChanges(**args):
|
||||
return Element(qname = (TEXTNS,'tracked-changes'), **args)
|
||||
|
||||
def UserDefined(**args):
|
||||
return Element(qname = (TEXTNS,'user-defined'), **args)
|
||||
|
||||
def UserFieldDecl(**args):
|
||||
return Element(qname = (TEXTNS,'user-field-decl'), **args)
|
||||
|
||||
def UserFieldDecls(**args):
|
||||
return Element(qname = (TEXTNS,'user-field-decls'), **args)
|
||||
|
||||
def UserFieldGet(**args):
|
||||
return Element(qname = (TEXTNS,'user-field-get'), **args)
|
||||
|
||||
def UserFieldInput(**args):
|
||||
return Element(qname = (TEXTNS,'user-field-input'), **args)
|
||||
|
||||
def UserIndex(**args):
|
||||
return Element(qname = (TEXTNS,'user-index'), **args)
|
||||
|
||||
def UserIndexEntryTemplate(**args):
|
||||
return Element(qname = (TEXTNS,'user-index-entry-template'), **args)
|
||||
|
||||
def UserIndexMark(**args):
|
||||
return Element(qname = (TEXTNS,'user-index-mark'), **args)
|
||||
|
||||
def UserIndexMarkEnd(**args):
|
||||
return Element(qname = (TEXTNS,'user-index-mark-end'), **args)
|
||||
|
||||
def UserIndexMarkStart(**args):
|
||||
return Element(qname = (TEXTNS,'user-index-mark-start'), **args)
|
||||
|
||||
def UserIndexSource(**args):
|
||||
return Element(qname = (TEXTNS,'user-index-source'), **args)
|
||||
|
||||
def VariableDecl(**args):
|
||||
return Element(qname = (TEXTNS,'variable-decl'), **args)
|
||||
|
||||
def VariableDecls(**args):
|
||||
return Element(qname = (TEXTNS,'variable-decls'), **args)
|
||||
|
||||
def VariableGet(**args):
|
||||
return Element(qname = (TEXTNS,'variable-get'), **args)
|
||||
|
||||
def VariableInput(**args):
|
||||
return Element(qname = (TEXTNS,'variable-input'), **args)
|
||||
|
||||
def VariableSet(**args):
|
||||
return Element(qname = (TEXTNS,'variable-set'), **args)
|
||||
|
||||
def WordCount(**args):
|
||||
return Element(qname = (TEXTNS,'word-count'), **args)
|
||||
|
||||
@@ -1,427 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# This contains a 128x128 px thumbnail in PNG format
|
||||
# Taken from http://www.zwahlendesign.ch/en/node/20
|
||||
# openoffice_icons/openoffice_icons_linux/openoffice11.png
|
||||
# License: Freeware
|
||||
import base64
|
||||
|
||||
iconstr = """\
|
||||
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
|
||||
N38FAAAAAElFTkSuQmCC\
|
||||
"""
|
||||
|
||||
def thumbnail():
|
||||
icon = base64.decodestring(iconstr)
|
||||
return icon
|
||||
|
||||
if __name__ == "__main__":
|
||||
icon = thumbnail()
|
||||
f = file("thumbnail.png","wb")
|
||||
f.write(icon)
|
||||
f.close()
|
||||
@@ -1,169 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2006-2009 Søren Roug, European Environment Agency
|
||||
#
|
||||
# This is free software. You may redistribute it under the terms
|
||||
# of the Apache license and the GNU General Public License Version
|
||||
# 2 or at your option any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public
|
||||
# License along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# Contributor(s): Michael Howitz, gocept gmbh & co. kg
|
||||
#
|
||||
# $Id: userfield.py 447 2008-07-10 20:01:30Z roug $
|
||||
|
||||
"""Class to show and manipulate user fields in odf documents."""
|
||||
|
||||
import sys
|
||||
import zipfile
|
||||
|
||||
from odf.text import UserFieldDecl
|
||||
from odf.namespaces import OFFICENS
|
||||
from odf.opendocument import load
|
||||
|
||||
OUTENCODING = "utf-8"
|
||||
|
||||
|
||||
# OpenDocument v.1.0 section 6.7.1
|
||||
VALUE_TYPES = {
|
||||
'float': (OFFICENS, u'value'),
|
||||
'percentage': (OFFICENS, u'value'),
|
||||
'currency': (OFFICENS, u'value'),
|
||||
'date': (OFFICENS, u'date-value'),
|
||||
'time': (OFFICENS, u'time-value'),
|
||||
'boolean': (OFFICENS, u'boolean-value'),
|
||||
'string': (OFFICENS, u'string-value'),
|
||||
}
|
||||
|
||||
|
||||
class UserFields(object):
|
||||
"""List, view and manipulate user fields."""
|
||||
|
||||
# these attributes can be a filename or a file like object
|
||||
src_file = None
|
||||
dest_file = None
|
||||
|
||||
def __init__(self, src=None, dest=None):
|
||||
"""Constructor
|
||||
|
||||
src ... source document name, file like object or None for stdin
|
||||
dest ... destination document name, file like object or None for stdout
|
||||
|
||||
"""
|
||||
self.src_file = src
|
||||
self.dest_file = dest
|
||||
self.document = None
|
||||
|
||||
def loaddoc(self):
|
||||
if isinstance(self.src_file, basestring):
|
||||
# src_file is a filename, check if it is a zip-file
|
||||
if not zipfile.is_zipfile(self.src_file):
|
||||
raise TypeError("%s is no odt file." % self.src_file)
|
||||
elif self.src_file is None:
|
||||
# use stdin if no file given
|
||||
self.src_file = sys.stdin
|
||||
|
||||
self.document = load(self.src_file)
|
||||
|
||||
def savedoc(self):
|
||||
# write output
|
||||
if self.dest_file is None:
|
||||
# use stdout if no filename given
|
||||
self.document.save('-')
|
||||
else:
|
||||
self.document.save(self.dest_file)
|
||||
|
||||
def list_fields(self):
|
||||
"""List (extract) all known user-fields.
|
||||
|
||||
Returns list of user-field names.
|
||||
|
||||
"""
|
||||
return [x[0] for x in self.list_fields_and_values()]
|
||||
|
||||
def list_fields_and_values(self, field_names=None):
|
||||
"""List (extract) user-fields with type and value.
|
||||
|
||||
field_names ... list of field names to show or None for all.
|
||||
|
||||
Returns list of tuples (<field name>, <field type>, <value>).
|
||||
|
||||
"""
|
||||
self.loaddoc()
|
||||
found_fields = []
|
||||
all_fields = self.document.getElementsByType(UserFieldDecl)
|
||||
for f in all_fields:
|
||||
value_type = f.getAttribute('valuetype')
|
||||
if value_type == 'string':
|
||||
value = f.getAttribute('stringvalue')
|
||||
else:
|
||||
value = f.getAttribute('value')
|
||||
field_name = f.getAttribute('name')
|
||||
|
||||
if field_names is None or field_name in field_names:
|
||||
found_fields.append((field_name.encode(OUTENCODING),
|
||||
value_type.encode(OUTENCODING),
|
||||
value.encode(OUTENCODING)))
|
||||
return found_fields
|
||||
|
||||
def list_values(self, field_names):
|
||||
"""Extract the contents of given field names from the file.
|
||||
|
||||
field_names ... list of field names
|
||||
|
||||
Returns list of field values.
|
||||
|
||||
"""
|
||||
return [x[2] for x in self.list_fields_and_values(field_names)]
|
||||
|
||||
def get(self, field_name):
|
||||
"""Extract the contents of this field from the file.
|
||||
|
||||
Returns field value or None if field does not exist.
|
||||
|
||||
"""
|
||||
values = self.list_values([field_name])
|
||||
if not values:
|
||||
return None
|
||||
return values[0]
|
||||
|
||||
def get_type_and_value(self, field_name):
|
||||
"""Extract the type and contents of this field from the file.
|
||||
|
||||
Returns tuple (<type>, <field-value>) or None if field does not exist.
|
||||
|
||||
"""
|
||||
fields = self.list_fields_and_values([field_name])
|
||||
if not fields:
|
||||
return None
|
||||
field_name, value_type, value = fields[0]
|
||||
return value_type, value
|
||||
|
||||
def update(self, data):
|
||||
"""Set the value of user fields. The field types will be the same.
|
||||
|
||||
data ... dict, with field name as key, field value as value
|
||||
|
||||
Returns None
|
||||
|
||||
"""
|
||||
self.loaddoc()
|
||||
all_fields = self.document.getElementsByType(UserFieldDecl)
|
||||
for f in all_fields:
|
||||
field_name = f.getAttribute('name')
|
||||
if data.has_key(field_name):
|
||||
value_type = f.getAttribute('valuetype')
|
||||
value = data.get(field_name)
|
||||
if value_type == 'string':
|
||||
f.setAttribute('stringvalue', value)
|
||||
else:
|
||||
f.setAttribute('value', value)
|
||||
self.savedoc()
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
|
||||
from namespaces import XFORMSNS
|
||||
from element import Element
|
||||
|
||||
# ODF 1.0 section 11.2
|
||||
# XForms is designed to be embedded in another XML format.
|
||||
# Autogenerated
|
||||
def Model(**args):
|
||||
return Element(qname = (XFORMSNS,'model'), **args)
|
||||
|
||||
def Instance(**args):
|
||||
return Element(qname = (XFORMSNS,'instance'), **args)
|
||||
|
||||
def Bind(**args):
|
||||
return Element(qname = (XFORMSNS,'bind'), **args)
|
||||
@@ -1,61 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
|
||||
from .namespaces import ANIMNS
|
||||
from .element import Element
|
||||
|
||||
|
||||
# Autogenerated
|
||||
def Animate(**args):
|
||||
return Element(qname = (ANIMNS,'animate'), **args)
|
||||
|
||||
def Animatecolor(**args):
|
||||
return Element(qname = (ANIMNS,'animateColor'), **args)
|
||||
|
||||
def Animatemotion(**args):
|
||||
return Element(qname = (ANIMNS,'animateMotion'), **args)
|
||||
|
||||
def Animatetransform(**args):
|
||||
return Element(qname = (ANIMNS,'animateTransform'), **args)
|
||||
|
||||
def Audio(**args):
|
||||
return Element(qname = (ANIMNS,'audio'), **args)
|
||||
|
||||
def Command(**args):
|
||||
return Element(qname = (ANIMNS,'command'), **args)
|
||||
|
||||
def Iterate(**args):
|
||||
return Element(qname = (ANIMNS,'iterate'), **args)
|
||||
|
||||
def Par(**args):
|
||||
return Element(qname = (ANIMNS,'par'), **args)
|
||||
|
||||
def Param(**args):
|
||||
return Element(qname = (ANIMNS,'param'), **args)
|
||||
|
||||
def Seq(**args):
|
||||
return Element(qname = (ANIMNS,'seq'), **args)
|
||||
|
||||
def Set(**args):
|
||||
return Element(qname = (ANIMNS,'set'), **args)
|
||||
|
||||
def Transitionfilter(**args):
|
||||
return Element(qname = (ANIMNS,'transitionFilter'), **args)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,87 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
|
||||
from .namespaces import CHARTNS
|
||||
from .element import Element
|
||||
|
||||
# Autogenerated
|
||||
def Axis(**args):
|
||||
return Element(qname = (CHARTNS,'axis'), **args)
|
||||
|
||||
def Categories(**args):
|
||||
return Element(qname = (CHARTNS,'categories'), **args)
|
||||
|
||||
def Chart(**args):
|
||||
return Element(qname = (CHARTNS,'chart'), **args)
|
||||
|
||||
def DataPoint(**args):
|
||||
return Element(qname = (CHARTNS,'data-point'), **args)
|
||||
|
||||
def Domain(**args):
|
||||
return Element(qname = (CHARTNS,'domain'), **args)
|
||||
|
||||
def ErrorIndicator(**args):
|
||||
return Element(qname = (CHARTNS,'error-indicator'), **args)
|
||||
|
||||
def Floor(**args):
|
||||
return Element(qname = (CHARTNS,'floor'), **args)
|
||||
|
||||
def Footer(**args):
|
||||
return Element(qname = (CHARTNS,'footer'), **args)
|
||||
|
||||
def Grid(**args):
|
||||
return Element(qname = (CHARTNS,'grid'), **args)
|
||||
|
||||
def Legend(**args):
|
||||
return Element(qname = (CHARTNS,'legend'), **args)
|
||||
|
||||
def MeanValue(**args):
|
||||
return Element(qname = (CHARTNS,'mean-value'), **args)
|
||||
|
||||
def PlotArea(**args):
|
||||
return Element(qname = (CHARTNS,'plot-area'), **args)
|
||||
|
||||
def RegressionCurve(**args):
|
||||
return Element(qname = (CHARTNS,'regression-curve'), **args)
|
||||
|
||||
def Series(**args):
|
||||
return Element(qname = (CHARTNS,'series'), **args)
|
||||
|
||||
def StockGainMarker(**args):
|
||||
return Element(qname = (CHARTNS,'stock-gain-marker'), **args)
|
||||
|
||||
def StockLossMarker(**args):
|
||||
return Element(qname = (CHARTNS,'stock-loss-marker'), **args)
|
||||
|
||||
def StockRangeLine(**args):
|
||||
return Element(qname = (CHARTNS,'stock-range-line'), **args)
|
||||
|
||||
def Subtitle(**args):
|
||||
return Element(qname = (CHARTNS,'subtitle'), **args)
|
||||
|
||||
def SymbolImage(**args):
|
||||
return Element(qname = (CHARTNS,'symbol-image'), **args)
|
||||
|
||||
def Title(**args):
|
||||
return Element(qname = (CHARTNS,'title'), **args)
|
||||
|
||||
def Wall(**args):
|
||||
return Element(qname = (CHARTNS,'wall'), **args)
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
|
||||
from .namespaces import CONFIGNS
|
||||
from .element import Element
|
||||
|
||||
# Autogenerated
|
||||
def ConfigItem(**args):
|
||||
return Element(qname = (CONFIGNS, 'config-item'), **args)
|
||||
|
||||
def ConfigItemMapEntry(**args):
|
||||
return Element(qname = (CONFIGNS,'config-item-map-entry'), **args)
|
||||
|
||||
def ConfigItemMapIndexed(**args):
|
||||
return Element(qname = (CONFIGNS,'config-item-map-indexed'), **args)
|
||||
|
||||
def ConfigItemMapNamed(**args):
|
||||
return Element(qname = (CONFIGNS,'config-item-map-named'), **args)
|
||||
|
||||
def ConfigItemSet(**args):
|
||||
return Element(qname = (CONFIGNS, 'config-item-set'), **args)
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
|
||||
from .namespaces import DCNS
|
||||
from .element import Element
|
||||
|
||||
# Autogenerated
|
||||
def Creator(**args):
|
||||
return Element(qname = (DCNS,'creator'), **args)
|
||||
|
||||
def Date(**args):
|
||||
return Element(qname = (DCNS,'date'), **args)
|
||||
|
||||
def Description(**args):
|
||||
return Element(qname = (DCNS,'description'), **args)
|
||||
|
||||
def Language(**args):
|
||||
return Element(qname = (DCNS,'language'), **args)
|
||||
|
||||
def Subject(**args):
|
||||
return Element(qname = (DCNS,'subject'), **args)
|
||||
|
||||
def Title(**args):
|
||||
return Element(qname = (DCNS,'title'), **args)
|
||||
|
||||
# The following complete the Dublin Core elements, but there is no
|
||||
# guarantee a compliant implementation of OpenDocument will preserve
|
||||
# these elements
|
||||
|
||||
#def Contributor(**args):
|
||||
# return Element(qname = (DCNS,'contributor'), **args)
|
||||
|
||||
#def Coverage(**args):
|
||||
# return Element(qname = (DCNS,'coverage'), **args)
|
||||
|
||||
#def Format(**args):
|
||||
# return Element(qname = (DCNS,'format'), **args)
|
||||
|
||||
#def Identifier(**args):
|
||||
# return Element(qname = (DCNS,'identifier'), **args)
|
||||
|
||||
#def Publisher(**args):
|
||||
# return Element(qname = (DCNS,'publisher'), **args)
|
||||
|
||||
#def Relation(**args):
|
||||
# return Element(qname = (DCNS,'relation'), **args)
|
||||
|
||||
#def Rights(**args):
|
||||
# return Element(qname = (DCNS,'rights'), **args)
|
||||
|
||||
#def Source(**args):
|
||||
# return Element(qname = (DCNS,'source'), **args)
|
||||
|
||||
#def Type(**args):
|
||||
# return Element(qname = (DCNS,'type'), **args)
|
||||
@@ -1,43 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
|
||||
from .namespaces import DR3DNS
|
||||
from .element import Element
|
||||
from .draw import StyleRefElement
|
||||
|
||||
# Autogenerated
|
||||
def Cube(**args):
|
||||
return StyleRefElement(qname = (DR3DNS,'cube'), **args)
|
||||
|
||||
def Extrude(**args):
|
||||
return StyleRefElement(qname = (DR3DNS,'extrude'), **args)
|
||||
|
||||
def Light(Element):
|
||||
return StyleRefElement(qname = (DR3DNS,'light'), **args)
|
||||
|
||||
def Rotate(**args):
|
||||
return StyleRefElement(qname = (DR3DNS,'rotate'), **args)
|
||||
|
||||
def Scene(**args):
|
||||
return StyleRefElement(qname = (DR3DNS,'scene'), **args)
|
||||
|
||||
def Sphere(**args):
|
||||
return StyleRefElement(qname = (DR3DNS,'sphere'), **args)
|
||||
|
||||
@@ -1,182 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
|
||||
from .namespaces import DRAWNS, STYLENS, PRESENTATIONNS
|
||||
from .element import Element
|
||||
|
||||
def StyleRefElement(stylename=None, classnames=None, **args):
|
||||
qattrs = {}
|
||||
if stylename is not None:
|
||||
f = stylename.getAttrNS(STYLENS, 'family')
|
||||
if f == 'graphic':
|
||||
qattrs[(DRAWNS,'style-name')]= stylename
|
||||
elif f == 'presentation':
|
||||
qattrs[(PRESENTATIONNS,'style-name')]= stylename
|
||||
else:
|
||||
raise ValueError("Style's family must be either 'graphic' or 'presentation'")
|
||||
if classnames is not None:
|
||||
f = classnames[0].getAttrNS(STYLENS, 'family')
|
||||
if f == 'graphic':
|
||||
qattrs[(DRAWNS,'class-names')]= classnames
|
||||
elif f == 'presentation':
|
||||
qattrs[(PRESENTATIONNS,'class-names')]= classnames
|
||||
else:
|
||||
raise ValueError("Style's family must be either 'graphic' or 'presentation'")
|
||||
return Element(qattributes=qattrs, **args)
|
||||
|
||||
def DrawElement(name=None, **args):
|
||||
e = Element(name=name, **args)
|
||||
if 'displayname' not in args:
|
||||
e.setAttrNS(DRAWNS,'display-name', name)
|
||||
return e
|
||||
|
||||
# Autogenerated
|
||||
def A(**args):
|
||||
return Element(qname = (DRAWNS,'a'), **args)
|
||||
|
||||
def Applet(**args):
|
||||
return Element(qname = (DRAWNS,'applet'), **args)
|
||||
|
||||
def AreaCircle(**args):
|
||||
return Element(qname = (DRAWNS,'area-circle'), **args)
|
||||
|
||||
def AreaPolygon(**args):
|
||||
return Element(qname = (DRAWNS,'area-polygon'), **args)
|
||||
|
||||
def AreaRectangle(**args):
|
||||
return Element(qname = (DRAWNS,'area-rectangle'), **args)
|
||||
|
||||
def Caption(**args):
|
||||
return StyleRefElement(qname = (DRAWNS,'caption'), **args)
|
||||
|
||||
def Circle(**args):
|
||||
return StyleRefElement(qname = (DRAWNS,'circle'), **args)
|
||||
|
||||
def Connector(**args):
|
||||
return StyleRefElement(qname = (DRAWNS,'connector'), **args)
|
||||
|
||||
def ContourPath(**args):
|
||||
return Element(qname = (DRAWNS,'contour-path'), **args)
|
||||
|
||||
def ContourPolygon(**args):
|
||||
return Element(qname = (DRAWNS,'contour-polygon'), **args)
|
||||
|
||||
def Control(**args):
|
||||
return StyleRefElement(qname = (DRAWNS,'control'), **args)
|
||||
|
||||
def CustomShape(**args):
|
||||
return StyleRefElement(qname = (DRAWNS,'custom-shape'), **args)
|
||||
|
||||
def Ellipse(**args):
|
||||
return StyleRefElement(qname = (DRAWNS,'ellipse'), **args)
|
||||
|
||||
def EnhancedGeometry(**args):
|
||||
return Element(qname = (DRAWNS,'enhanced-geometry'), **args)
|
||||
|
||||
def Equation(**args):
|
||||
return Element(qname = (DRAWNS,'equation'), **args)
|
||||
|
||||
def FillImage(**args):
|
||||
return DrawElement(qname = (DRAWNS,'fill-image'), **args)
|
||||
|
||||
def FloatingFrame(**args):
|
||||
return Element(qname = (DRAWNS,'floating-frame'), **args)
|
||||
|
||||
def Frame(**args):
|
||||
return StyleRefElement(qname = (DRAWNS,'frame'), **args)
|
||||
|
||||
def G(**args):
|
||||
return StyleRefElement(qname = (DRAWNS,'g'), **args)
|
||||
|
||||
def GluePoint(**args):
|
||||
return Element(qname = (DRAWNS,'glue-point'), **args)
|
||||
|
||||
def Gradient(**args):
|
||||
return DrawElement(qname = (DRAWNS,'gradient'), **args)
|
||||
|
||||
def Handle(**args):
|
||||
return Element(qname = (DRAWNS,'handle'), **args)
|
||||
|
||||
def Hatch(**args):
|
||||
return DrawElement(qname = (DRAWNS,'hatch'), **args)
|
||||
|
||||
def Image(**args):
|
||||
return Element(qname = (DRAWNS,'image'), **args)
|
||||
|
||||
def ImageMap(**args):
|
||||
return Element(qname = (DRAWNS,'image-map'), **args)
|
||||
|
||||
def Layer(**args):
|
||||
return Element(qname = (DRAWNS,'layer'), **args)
|
||||
|
||||
def LayerSet(**args):
|
||||
return Element(qname = (DRAWNS,'layer-set'), **args)
|
||||
|
||||
def Line(**args):
|
||||
return StyleRefElement(qname = (DRAWNS,'line'), **args)
|
||||
|
||||
def Marker(**args):
|
||||
return DrawElement(qname = (DRAWNS,'marker'), **args)
|
||||
|
||||
def Measure(**args):
|
||||
return StyleRefElement(qname = (DRAWNS,'measure'), **args)
|
||||
|
||||
def Object(**args):
|
||||
return Element(qname = (DRAWNS,'object'), **args)
|
||||
|
||||
def ObjectOle(**args):
|
||||
return Element(qname = (DRAWNS,'object-ole'), **args)
|
||||
|
||||
def Opacity(**args):
|
||||
return DrawElement(qname = (DRAWNS,'opacity'), **args)
|
||||
|
||||
def Page(**args):
|
||||
return Element(qname = (DRAWNS,'page'), **args)
|
||||
|
||||
def PageThumbnail(**args):
|
||||
return StyleRefElement(qname = (DRAWNS,'page-thumbnail'), **args)
|
||||
|
||||
def Param(**args):
|
||||
return Element(qname = (DRAWNS,'param'), **args)
|
||||
|
||||
def Path(**args):
|
||||
return StyleRefElement(qname = (DRAWNS,'path'), **args)
|
||||
|
||||
def Plugin(**args):
|
||||
return Element(qname = (DRAWNS,'plugin'), **args)
|
||||
|
||||
def Polygon(**args):
|
||||
return StyleRefElement(qname = (DRAWNS,'polygon'), **args)
|
||||
|
||||
def Polyline(**args):
|
||||
return StyleRefElement(qname = (DRAWNS,'polyline'), **args)
|
||||
|
||||
def Rect(**args):
|
||||
return StyleRefElement(qname = (DRAWNS,'rect'), **args)
|
||||
|
||||
def RegularPolygon(**args):
|
||||
return StyleRefElement(qname = (DRAWNS,'regular-polygon'), **args)
|
||||
|
||||
def StrokeDash(**args):
|
||||
return DrawElement(qname = (DRAWNS,'stroke-dash'), **args)
|
||||
|
||||
def TextBox(**args):
|
||||
return Element(qname = (DRAWNS,'text-box'), **args)
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Create a <text:list-style> element from a text string.
|
||||
# Copyright (C) 2008 J. David Eisenberg
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
|
||||
import re
|
||||
from .style import Style, TextProperties, ListLevelProperties
|
||||
from .text import ListStyle,ListLevelStyleNumber,ListLevelStyleBullet
|
||||
|
||||
"""
|
||||
Create a <text:list-style> element from a string or array.
|
||||
|
||||
List styles require a lot of code to create one level at a time.
|
||||
These routines take a string and delimiter, or a list of
|
||||
strings, and creates a <text:list-style> element for you.
|
||||
Each item in the string (or array) represents a list level
|
||||
* style for levels 1-10.</p>
|
||||
*
|
||||
* <p>If an item contains <code>1</code>, <code>I</code>,
|
||||
* <code>i</code>, <code>A</code>, or <code>a</code>, then it is presumed
|
||||
* to be a numbering style; otherwise it is a bulleted style.</p>
|
||||
"""
|
||||
|
||||
_MAX_LIST_LEVEL = 10
|
||||
SHOW_ALL_LEVELS = True
|
||||
SHOW_ONE_LEVEL = False
|
||||
|
||||
def styleFromString(name, specifiers, delim, spacing, showAllLevels):
|
||||
specArray = specifiers.split(delim)
|
||||
return styleFromList( name, specArray, spacing, showAllLevels )
|
||||
|
||||
def styleFromList( styleName, specArray, spacing, showAllLevels):
|
||||
bullet = ""
|
||||
numPrefix = ""
|
||||
numSuffix = ""
|
||||
numberFormat = ""
|
||||
cssLengthNum = 0
|
||||
cssLengthUnits = ""
|
||||
numbered = False
|
||||
displayLevels = 0
|
||||
listStyle = ListStyle(name=styleName)
|
||||
numFormatPattern = re.compile("([1IiAa])")
|
||||
cssLengthPattern = re.compile("([^a-z]+)\\s*([a-z]+)?")
|
||||
m = cssLengthPattern.search( spacing )
|
||||
if (m != None):
|
||||
cssLengthNum = float(m.group(1))
|
||||
if (m.lastindex == 2):
|
||||
cssLengthUnits = m.group(2)
|
||||
i = 0
|
||||
while i < len(specArray):
|
||||
specification = specArray[i]
|
||||
m = numFormatPattern.search(specification)
|
||||
if (m != None):
|
||||
numberFormat = m.group(1)
|
||||
numPrefix = specification[0:m.start(1)]
|
||||
numSuffix = specification[m.end(1):]
|
||||
bullet = ""
|
||||
numbered = True
|
||||
if (showAllLevels):
|
||||
displayLevels = i + 1
|
||||
else:
|
||||
displayLevels = 1
|
||||
else: # it's a bullet style
|
||||
bullet = specification
|
||||
numPrefix = ""
|
||||
numSuffix = ""
|
||||
numberFormat = ""
|
||||
displayLevels = 1
|
||||
numbered = False
|
||||
if (numbered):
|
||||
lls = ListLevelStyleNumber(level=(i+1))
|
||||
if (numPrefix != ''):
|
||||
lls.setAttribute('numprefix', numPrefix)
|
||||
if (numSuffix != ''):
|
||||
lls.setAttribute('numsuffix', numSuffix)
|
||||
lls.setAttribute('displaylevels', displayLevels)
|
||||
else:
|
||||
lls = ListLevelStyleBullet(level=(i+1),bulletchar=bullet[0])
|
||||
llp = ListLevelProperties()
|
||||
llp.setAttribute('spacebefore', str(cssLengthNum * (i+1)) + cssLengthUnits)
|
||||
llp.setAttribute('minlabelwidth', str(cssLengthNum) + cssLengthUnits)
|
||||
lls.addElement( llp )
|
||||
listStyle.addElement(lls)
|
||||
i += 1
|
||||
return listStyle
|
||||
|
||||
# vim: set expandtab sw=4 :
|
||||
@@ -1,519 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2007-2010 Søren Roug, European Environment Agency
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
|
||||
# Note: This script has copied a lot of text from xml.dom.minidom.
|
||||
# Whatever license applies to that file also applies to this file.
|
||||
#
|
||||
import xml.dom
|
||||
from xml.dom.minicompat import *
|
||||
from .namespaces import nsdict
|
||||
from . import grammar
|
||||
from .attrconverters import AttrConverters
|
||||
|
||||
# The following code is pasted form xml.sax.saxutils
|
||||
# Tt makes it possible to run the code without the xml sax package installed
|
||||
# To make it possible to have <rubbish> in your text elements, it is necessary to escape the texts
|
||||
def _escape(data, entities={}):
|
||||
""" Escape &, <, and > in a string of data.
|
||||
|
||||
You can escape other strings of data by passing a dictionary as
|
||||
the optional entities parameter. The keys and values must all be
|
||||
strings; each key will be replaced with its corresponding value.
|
||||
"""
|
||||
try:
|
||||
data = data.decode('utf-8')
|
||||
except (TypeError, AttributeError):
|
||||
## Make sure our stream is a string
|
||||
## If it comes through as bytes it fails
|
||||
pass
|
||||
data = data.replace("&", "&")
|
||||
data = data.replace("<", "<")
|
||||
data = data.replace(">", ">")
|
||||
for chars, entity in list(entities.items()):
|
||||
data = data.replace(chars, entity)
|
||||
return data
|
||||
|
||||
def _quoteattr(data, entities={}):
|
||||
""" Escape and quote an attribute value.
|
||||
|
||||
Escape &, <, and > in a string of data, then quote it for use as
|
||||
an attribute value. The \" character will be escaped as well, if
|
||||
necessary.
|
||||
|
||||
You can escape other strings of data by passing a dictionary as
|
||||
the optional entities parameter. The keys and values must all be
|
||||
strings; each key will be replaced with its corresponding value.
|
||||
"""
|
||||
entities['\n']=' '
|
||||
entities['\r']=''
|
||||
data = _escape(data, entities)
|
||||
if '"' in data:
|
||||
if "'" in data:
|
||||
data = '"%s"' % data.replace('"', """)
|
||||
else:
|
||||
data = "'%s'" % data
|
||||
else:
|
||||
data = '"%s"' % data
|
||||
return data
|
||||
|
||||
def _nssplit(qualifiedName):
|
||||
""" Split a qualified name into namespace part and local part. """
|
||||
fields = qualifiedName.split(':', 1)
|
||||
if len(fields) == 2:
|
||||
return fields
|
||||
else:
|
||||
return (None, fields[0])
|
||||
|
||||
def _nsassign(namespace):
|
||||
return nsdict.setdefault(namespace,"ns" + str(len(nsdict)))
|
||||
|
||||
# Exceptions
|
||||
class IllegalChild(Exception):
|
||||
""" Complains if you add an element to a parent where it is not allowed """
|
||||
class IllegalText(Exception):
|
||||
""" Complains if you add text or cdata to an element where it is not allowed """
|
||||
|
||||
class Node(xml.dom.Node):
|
||||
""" super class for more specific nodes """
|
||||
parentNode = None
|
||||
nextSibling = None
|
||||
previousSibling = None
|
||||
|
||||
def hasChildNodes(self):
|
||||
""" Tells whether this element has any children; text nodes,
|
||||
subelements, whatever.
|
||||
"""
|
||||
if self.childNodes:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def _get_childNodes(self):
|
||||
return self.childNodes
|
||||
|
||||
def _get_firstChild(self):
|
||||
if self.childNodes:
|
||||
return self.childNodes[0]
|
||||
|
||||
def _get_lastChild(self):
|
||||
if self.childNodes:
|
||||
return self.childNodes[-1]
|
||||
|
||||
def insertBefore(self, newChild, refChild):
|
||||
""" Inserts the node newChild before the existing child node refChild.
|
||||
If refChild is null, insert newChild at the end of the list of children.
|
||||
"""
|
||||
if newChild.nodeType not in self._child_node_types:
|
||||
raise IllegalChild("%s cannot be child of %s" % (newChild.tagName, self.tagName))
|
||||
if newChild.parentNode is not None:
|
||||
newChild.parentNode.removeChild(newChild)
|
||||
if refChild is None:
|
||||
self.appendChild(newChild)
|
||||
else:
|
||||
try:
|
||||
index = self.childNodes.index(refChild)
|
||||
except ValueError:
|
||||
raise xml.dom.NotFoundErr()
|
||||
self.childNodes.insert(index, newChild)
|
||||
newChild.nextSibling = refChild
|
||||
refChild.previousSibling = newChild
|
||||
if index:
|
||||
node = self.childNodes[index-1]
|
||||
node.nextSibling = newChild
|
||||
newChild.previousSibling = node
|
||||
else:
|
||||
newChild.previousSibling = None
|
||||
newChild.parentNode = self
|
||||
return newChild
|
||||
|
||||
def appendChild(self, newChild):
|
||||
""" Adds the node newChild to the end of the list of children of this node.
|
||||
If the newChild is already in the tree, it is first removed.
|
||||
"""
|
||||
if newChild.nodeType == self.DOCUMENT_FRAGMENT_NODE:
|
||||
for c in tuple(newChild.childNodes):
|
||||
self.appendChild(c)
|
||||
### The DOM does not clearly specify what to return in this case
|
||||
return newChild
|
||||
if newChild.nodeType not in self._child_node_types:
|
||||
raise IllegalChild("<%s> is not allowed in %s" % ( newChild.tagName, self.tagName))
|
||||
if newChild.parentNode is not None:
|
||||
newChild.parentNode.removeChild(newChild)
|
||||
_append_child(self, newChild)
|
||||
newChild.nextSibling = None
|
||||
return newChild
|
||||
|
||||
def removeChild(self, oldChild):
|
||||
""" Removes the child node indicated by oldChild from the list of children, and returns it.
|
||||
"""
|
||||
#FIXME: update ownerDocument.element_dict or find other solution
|
||||
try:
|
||||
self.childNodes.remove(oldChild)
|
||||
except ValueError:
|
||||
raise xml.dom.NotFoundErr()
|
||||
if oldChild.nextSibling is not None:
|
||||
oldChild.nextSibling.previousSibling = oldChild.previousSibling
|
||||
if oldChild.previousSibling is not None:
|
||||
oldChild.previousSibling.nextSibling = oldChild.nextSibling
|
||||
oldChild.nextSibling = oldChild.previousSibling = None
|
||||
if self.ownerDocument:
|
||||
self.ownerDocument.clear_caches()
|
||||
oldChild.parentNode = None
|
||||
return oldChild
|
||||
|
||||
def __str__(self):
|
||||
val = []
|
||||
for c in self.childNodes:
|
||||
val.append(str(c))
|
||||
return ''.join(val)
|
||||
|
||||
def __unicode__(self):
|
||||
val = []
|
||||
for c in self.childNodes:
|
||||
val.append(str(c))
|
||||
return ''.join(val)
|
||||
|
||||
defproperty(Node, "firstChild", doc="First child node, or None.")
|
||||
defproperty(Node, "lastChild", doc="Last child node, or None.")
|
||||
|
||||
def _append_child(self, node):
|
||||
# fast path with less checks; usable by DOM builders if careful
|
||||
childNodes = self.childNodes
|
||||
if childNodes:
|
||||
last = childNodes[-1]
|
||||
node.__dict__["previousSibling"] = last
|
||||
last.__dict__["nextSibling"] = node
|
||||
childNodes.append(node)
|
||||
node.__dict__["parentNode"] = self
|
||||
|
||||
class Childless(object):
|
||||
""" Mixin that makes childless-ness easy to implement and avoids
|
||||
the complexity of the Node methods that deal with children.
|
||||
"""
|
||||
|
||||
attributes = None
|
||||
childNodes = EmptyNodeList()
|
||||
firstChild = None
|
||||
lastChild = None
|
||||
|
||||
def _get_firstChild(self):
|
||||
return None
|
||||
|
||||
def _get_lastChild(self):
|
||||
return None
|
||||
|
||||
def appendChild(self, node):
|
||||
""" Raises an error """
|
||||
raise xml.dom.HierarchyRequestErr(
|
||||
self.tagName + " nodes cannot have children")
|
||||
|
||||
def hasChildNodes(self):
|
||||
return False
|
||||
|
||||
def insertBefore(self, newChild, refChild):
|
||||
""" Raises an error """
|
||||
raise xml.dom.HierarchyRequestErr(
|
||||
self.tagName + " nodes do not have children")
|
||||
|
||||
def removeChild(self, oldChild):
|
||||
""" Raises an error """
|
||||
raise xml.dom.NotFoundErr(
|
||||
self.tagName + " nodes do not have children")
|
||||
|
||||
def replaceChild(self, newChild, oldChild):
|
||||
""" Raises an error """
|
||||
raise xml.dom.HierarchyRequestErr(
|
||||
self.tagName + " nodes do not have children")
|
||||
|
||||
class Text(Childless, Node):
|
||||
nodeType = Node.TEXT_NODE
|
||||
tagName = "Text"
|
||||
|
||||
def __init__(self, data):
|
||||
self.data = data
|
||||
|
||||
def __str__(self):
|
||||
return self.data.encode()
|
||||
|
||||
def __unicode__(self):
|
||||
return self.data
|
||||
|
||||
def toXml(self,level,f):
|
||||
""" Write XML in UTF-8 """
|
||||
if self.data:
|
||||
f.write(_escape(str(self.data).encode('utf-8')))
|
||||
|
||||
class CDATASection(Text, Childless):
|
||||
nodeType = Node.CDATA_SECTION_NODE
|
||||
|
||||
def toXml(self,level,f):
|
||||
""" Generate XML output of the node. If the text contains "]]>", then
|
||||
escape it by going out of CDATA mode (]]>), then write the string
|
||||
and then go into CDATA mode again. (<![CDATA[)
|
||||
"""
|
||||
if self.data:
|
||||
f.write('<![CDATA[%s]]>' % self.data.replace(']]>',']]>]]><![CDATA['))
|
||||
|
||||
class Element(Node):
|
||||
""" Creates a arbitrary element and is intended to be subclassed not used on its own.
|
||||
This element is the base of every element it defines a class which resembles
|
||||
a xml-element. The main advantage of this kind of implementation is that you don't
|
||||
have to create a toXML method for every different object. Every element
|
||||
consists of an attribute, optional subelements, optional text and optional cdata.
|
||||
"""
|
||||
|
||||
nodeType = Node.ELEMENT_NODE
|
||||
namespaces = {} # Due to shallow copy this is a static variable
|
||||
|
||||
_child_node_types = (Node.ELEMENT_NODE,
|
||||
Node.PROCESSING_INSTRUCTION_NODE,
|
||||
Node.COMMENT_NODE,
|
||||
Node.TEXT_NODE,
|
||||
Node.CDATA_SECTION_NODE,
|
||||
Node.ENTITY_REFERENCE_NODE)
|
||||
|
||||
def __init__(self, attributes=None, text=None, cdata=None, qname=None, qattributes=None, check_grammar=True, **args):
|
||||
if qname is not None:
|
||||
self.qname = qname
|
||||
assert(hasattr(self, 'qname'))
|
||||
self.ownerDocument = None
|
||||
self.childNodes=[]
|
||||
self.allowed_children = grammar.allowed_children.get(self.qname)
|
||||
prefix = self.get_nsprefix(self.qname[0])
|
||||
self.tagName = prefix + ":" + self.qname[1]
|
||||
if text is not None:
|
||||
self.addText(text)
|
||||
if cdata is not None:
|
||||
self.addCDATA(cdata)
|
||||
|
||||
allowed_attrs = self.allowed_attributes()
|
||||
if allowed_attrs is not None:
|
||||
allowed_args = [ a[1].lower().replace('-','') for a in allowed_attrs]
|
||||
self.attributes={}
|
||||
# Load the attributes from the 'attributes' argument
|
||||
if attributes:
|
||||
for attr, value in list(attributes.items()):
|
||||
self.setAttribute(attr, value)
|
||||
# Load the qualified attributes
|
||||
if qattributes:
|
||||
for attr, value in list(qattributes.items()):
|
||||
self.setAttrNS(attr[0], attr[1], value)
|
||||
if allowed_attrs is not None:
|
||||
# Load the attributes from the 'args' argument
|
||||
for arg in list(args.keys()):
|
||||
self.setAttribute(arg, args[arg])
|
||||
else:
|
||||
for arg in list(args.keys()): # If any attribute is allowed
|
||||
self.attributes[arg]=args[arg]
|
||||
if not check_grammar:
|
||||
return
|
||||
# Test that all mandatory attributes have been added.
|
||||
required = grammar.required_attributes.get(self.qname)
|
||||
if required:
|
||||
for r in required:
|
||||
if self.getAttrNS(r[0],r[1]) is None:
|
||||
raise AttributeError("Required attribute missing: %s in <%s>" % (r[1].lower().replace('-',''), self.tagName))
|
||||
|
||||
def get_knownns(self, prefix):
|
||||
""" Odfpy maintains a list of known namespaces. In some cases a prefix is used, and
|
||||
we need to know which namespace it resolves to.
|
||||
"""
|
||||
global nsdict
|
||||
for ns,p in list(nsdict.items()):
|
||||
if p == prefix: return ns
|
||||
return None
|
||||
|
||||
def get_nsprefix(self, namespace):
|
||||
""" Odfpy maintains a list of known namespaces. In some cases we have a namespace URL,
|
||||
and needs to look up or assign the prefix for it.
|
||||
"""
|
||||
if namespace is None: namespace = ""
|
||||
prefix = _nsassign(namespace)
|
||||
if namespace not in self.namespaces:
|
||||
self.namespaces[namespace] = prefix
|
||||
return prefix
|
||||
|
||||
def allowed_attributes(self):
|
||||
return grammar.allowed_attributes.get(self.qname)
|
||||
|
||||
def _setOwnerDoc(self, element):
|
||||
element.ownerDocument = self.ownerDocument
|
||||
for child in element.childNodes:
|
||||
self._setOwnerDoc(child)
|
||||
|
||||
def addElement(self, element, check_grammar=True):
|
||||
""" adds an element to an Element
|
||||
|
||||
Element.addElement(Element)
|
||||
"""
|
||||
if check_grammar and self.allowed_children is not None:
|
||||
if element.qname not in self.allowed_children:
|
||||
raise IllegalChild("<%s> is not allowed in <%s>" % ( element.tagName, self.tagName))
|
||||
self.appendChild(element)
|
||||
self._setOwnerDoc(element)
|
||||
if self.ownerDocument:
|
||||
self.ownerDocument.rebuild_caches(element)
|
||||
|
||||
def addText(self, text, check_grammar=True):
|
||||
""" Adds text to an element
|
||||
Setting check_grammar=False turns off grammar checking
|
||||
"""
|
||||
if check_grammar and self.qname not in grammar.allows_text:
|
||||
raise IllegalText("The <%s> element does not allow text" % self.tagName)
|
||||
else:
|
||||
if text != '':
|
||||
self.appendChild(Text(text))
|
||||
|
||||
def addCDATA(self, cdata, check_grammar=True):
|
||||
""" Adds CDATA to an element
|
||||
Setting check_grammar=False turns off grammar checking
|
||||
"""
|
||||
if check_grammar and self.qname not in grammar.allows_text:
|
||||
raise IllegalText("The <%s> element does not allow text" % self.tagName)
|
||||
else:
|
||||
self.appendChild(CDATASection(cdata))
|
||||
|
||||
def removeAttribute(self, attr, check_grammar=True):
|
||||
""" Removes an attribute by name. """
|
||||
allowed_attrs = self.allowed_attributes()
|
||||
if allowed_attrs is None:
|
||||
if type(attr) == type(()):
|
||||
prefix, localname = attr
|
||||
self.removeAttrNS(prefix, localname)
|
||||
else:
|
||||
raise AttributeError("Unable to add simple attribute - use (namespace, localpart)")
|
||||
else:
|
||||
# Construct a list of allowed arguments
|
||||
allowed_args = [ a[1].lower().replace('-','') for a in allowed_attrs]
|
||||
if check_grammar and attr not in allowed_args:
|
||||
raise AttributeError("Attribute %s is not allowed in <%s>" % ( attr, self.tagName))
|
||||
i = allowed_args.index(attr)
|
||||
self.removeAttrNS(allowed_attrs[i][0], allowed_attrs[i][1])
|
||||
|
||||
def setAttribute(self, attr, value, check_grammar=True):
|
||||
""" Add an attribute to the element
|
||||
This is sort of a convenience method. All attributes in ODF have
|
||||
namespaces. The library knows what attributes are legal and then allows
|
||||
the user to provide the attribute as a keyword argument and the
|
||||
library will add the correct namespace.
|
||||
Must overwrite, If attribute already exists.
|
||||
"""
|
||||
allowed_attrs = self.allowed_attributes()
|
||||
if allowed_attrs is None:
|
||||
if type(attr) == type(()):
|
||||
prefix, localname = attr
|
||||
self.setAttrNS(prefix, localname, value)
|
||||
else:
|
||||
raise AttributeError("Unable to add simple attribute - use (namespace, localpart)")
|
||||
else:
|
||||
# Construct a list of allowed arguments
|
||||
allowed_args = [ a[1].lower().replace('-','') for a in allowed_attrs]
|
||||
if check_grammar and attr not in allowed_args:
|
||||
raise AttributeError("Attribute %s is not allowed in <%s>" % ( attr, self.tagName))
|
||||
i = allowed_args.index(attr)
|
||||
self.setAttrNS(allowed_attrs[i][0], allowed_attrs[i][1], value)
|
||||
|
||||
def setAttrNS(self, namespace, localpart, value):
|
||||
""" Add an attribute to the element
|
||||
In case you need to add an attribute the library doesn't know about
|
||||
then you must provide the full qualified name
|
||||
It will not check that the attribute is legal according to the schema.
|
||||
Must overwrite, If attribute already exists.
|
||||
"""
|
||||
allowed_attrs = self.allowed_attributes()
|
||||
prefix = self.get_nsprefix(namespace)
|
||||
# if allowed_attrs and (namespace, localpart) not in allowed_attrs:
|
||||
# raise AttributeError, "Attribute %s:%s is not allowed in element <%s>" % ( prefix, localpart, self.tagName)
|
||||
c = AttrConverters()
|
||||
self.attributes[(namespace, localpart)] = c.convert((namespace, localpart), value, self)
|
||||
|
||||
def getAttrNS(self, namespace, localpart):
|
||||
prefix = self.get_nsprefix(namespace)
|
||||
return self.attributes.get((namespace, localpart))
|
||||
|
||||
def removeAttrNS(self, namespace, localpart):
|
||||
del self.attributes[(namespace, localpart)]
|
||||
|
||||
def getAttribute(self, attr):
|
||||
""" Get an attribute value. The method knows which namespace the attribute is in
|
||||
"""
|
||||
allowed_attrs = self.allowed_attributes()
|
||||
if allowed_attrs is None:
|
||||
if type(attr) == type(()):
|
||||
prefix, localname = attr
|
||||
return self.getAttrNS(prefix, localname)
|
||||
else:
|
||||
raise AttributeError("Unable to get simple attribute - use (namespace, localpart)")
|
||||
else:
|
||||
# Construct a list of allowed arguments
|
||||
allowed_args = [ a[1].lower().replace('-','') for a in allowed_attrs]
|
||||
i = allowed_args.index(attr)
|
||||
return self.getAttrNS(allowed_attrs[i][0], allowed_attrs[i][1])
|
||||
|
||||
def write_open_tag(self, level, f):
|
||||
f.write('<'+self.tagName)
|
||||
if level == 0:
|
||||
for namespace, prefix in list(self.namespaces.items()):
|
||||
f.write(' xmlns:' + prefix + '="'+ _escape(str(namespace))+'"')
|
||||
for qname in list(self.attributes.keys()):
|
||||
prefix = self.get_nsprefix(qname[0])
|
||||
f.write(' '+_escape(str(prefix+':'+qname[1]))+'='+_quoteattr(str(self.attributes[qname]).encode('utf-8')))
|
||||
f.write('>')
|
||||
|
||||
def write_close_tag(self, level, f):
|
||||
f.write('</'+self.tagName+'>')
|
||||
|
||||
def toXml(self, level, f):
|
||||
""" Generate XML stream out of the tree structure """
|
||||
f.write('<'+self.tagName)
|
||||
if level == 0:
|
||||
for namespace, prefix in list(self.namespaces.items()):
|
||||
f.write(' xmlns:' + prefix + '="'+ _escape(str(namespace))+'"')
|
||||
for qname in list(self.attributes.keys()):
|
||||
prefix = self.get_nsprefix(qname[0])
|
||||
f.write(' '+_escape(str(prefix+':'+qname[1]))+'='+_quoteattr(str(self.attributes[qname]).encode('utf-8')))
|
||||
if self.childNodes:
|
||||
f.write('>')
|
||||
for element in self.childNodes:
|
||||
element.toXml(level+1,f)
|
||||
f.write('</'+self.tagName+'>')
|
||||
else:
|
||||
f.write('/>')
|
||||
|
||||
def _getElementsByObj(self, obj, accumulator):
|
||||
if self.qname == obj.qname:
|
||||
accumulator.append(self)
|
||||
for e in self.childNodes:
|
||||
if e.nodeType == Node.ELEMENT_NODE:
|
||||
accumulator = e._getElementsByObj(obj, accumulator)
|
||||
return accumulator
|
||||
|
||||
def getElementsByType(self, element):
|
||||
""" Gets elements based on the type, which is function from text.py, draw.py etc. """
|
||||
obj = element(check_grammar=False)
|
||||
return self._getElementsByObj(obj,[])
|
||||
|
||||
def isInstanceOf(self, element):
|
||||
""" This is a check to see if the object is an instance of a type """
|
||||
obj = element(check_grammar=False)
|
||||
return self.qname == obj.qname
|
||||
|
||||
|
||||
@@ -1,325 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2008 Søren Roug, European Environment Agency
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
|
||||
from .namespaces import *
|
||||
|
||||
# Inline element don't cause a box
|
||||
# They are analogous to the HTML elements SPAN, B, I etc.
|
||||
inline_elements = (
|
||||
(TEXTNS,'a'),
|
||||
(TEXTNS,'author-initials'),
|
||||
(TEXTNS,'author-name'),
|
||||
(TEXTNS,'bibliography-mark'),
|
||||
(TEXTNS,'bookmark-ref'),
|
||||
(TEXTNS,'chapter'),
|
||||
(TEXTNS,'character-count'),
|
||||
(TEXTNS,'conditional-text'),
|
||||
(TEXTNS,'creation-date'),
|
||||
(TEXTNS,'creation-time'),
|
||||
(TEXTNS,'creator'),
|
||||
(TEXTNS,'database-display'),
|
||||
(TEXTNS,'database-name'),
|
||||
(TEXTNS,'database-next'),
|
||||
(TEXTNS,'database-row-number'),
|
||||
(TEXTNS,'database-row-select'),
|
||||
(TEXTNS,'date'),
|
||||
(TEXTNS,'dde-connection'),
|
||||
(TEXTNS,'description'),
|
||||
(TEXTNS,'editing-cycles'),
|
||||
(TEXTNS,'editing-duration'),
|
||||
(TEXTNS,'execute-macro'),
|
||||
(TEXTNS,'expression'),
|
||||
(TEXTNS,'file-name'),
|
||||
(TEXTNS,'hidden-paragraph'),
|
||||
(TEXTNS,'hidden-text'),
|
||||
(TEXTNS,'image-count'),
|
||||
(TEXTNS,'initial-creator'),
|
||||
(TEXTNS,'keywords'),
|
||||
(TEXTNS,'measure'),
|
||||
(TEXTNS,'modification-date'),
|
||||
(TEXTNS,'modification-time'),
|
||||
(TEXTNS,'note-ref'),
|
||||
(TEXTNS,'object-count'),
|
||||
(TEXTNS,'page-continuation'),
|
||||
(TEXTNS,'page-count'),
|
||||
(TEXTNS,'page-number'),
|
||||
(TEXTNS,'page-variable-get'),
|
||||
(TEXTNS,'page-variable-set'),
|
||||
(TEXTNS,'paragraph-count'),
|
||||
(TEXTNS,'placeholder'),
|
||||
(TEXTNS,'print-date'),
|
||||
(TEXTNS,'printed-by'),
|
||||
(TEXTNS,'print-time'),
|
||||
(TEXTNS,'reference-ref'),
|
||||
(TEXTNS,'ruby'),
|
||||
(TEXTNS,'ruby-base'),
|
||||
(TEXTNS,'ruby-text'),
|
||||
(TEXTNS,'script'),
|
||||
(TEXTNS,'sender-city'),
|
||||
(TEXTNS,'sender-company'),
|
||||
(TEXTNS,'sender-country'),
|
||||
(TEXTNS,'sender-email'),
|
||||
(TEXTNS,'sender-fax'),
|
||||
(TEXTNS,'sender-firstname'),
|
||||
(TEXTNS,'sender-initials'),
|
||||
(TEXTNS,'sender-lastname'),
|
||||
(TEXTNS,'sender-phone-private'),
|
||||
(TEXTNS,'sender-phone-work'),
|
||||
(TEXTNS,'sender-position'),
|
||||
(TEXTNS,'sender-postal-code'),
|
||||
(TEXTNS,'sender-state-or-province'),
|
||||
(TEXTNS,'sender-street'),
|
||||
(TEXTNS,'sender-title'),
|
||||
(TEXTNS,'sequence'),
|
||||
(TEXTNS,'sequence-ref'),
|
||||
(TEXTNS,'sheet-name'),
|
||||
(TEXTNS,'span'),
|
||||
(TEXTNS,'subject'),
|
||||
(TEXTNS,'table-count'),
|
||||
(TEXTNS,'table-formula'),
|
||||
(TEXTNS,'template-name'),
|
||||
(TEXTNS,'text-input'),
|
||||
(TEXTNS,'time'),
|
||||
(TEXTNS,'title'),
|
||||
(TEXTNS,'user-defined'),
|
||||
(TEXTNS,'user-field-get'),
|
||||
(TEXTNS,'user-field-input'),
|
||||
(TEXTNS,'variable-get'),
|
||||
(TEXTNS,'variable-input'),
|
||||
(TEXTNS,'variable-set'),
|
||||
(TEXTNS,'word-count'),
|
||||
)
|
||||
|
||||
|
||||
# It is almost impossible to determine what elements are block elements.
|
||||
# There are so many that don't fit the form
|
||||
block_elements = (
|
||||
(TEXTNS,'h'),
|
||||
(TEXTNS,'p'),
|
||||
(TEXTNS,'list'),
|
||||
(TEXTNS,'list-item'),
|
||||
(TEXTNS,'section'),
|
||||
)
|
||||
|
||||
declarative_elements = (
|
||||
(OFFICENS,'font-face-decls'),
|
||||
(PRESENTATIONNS,'date-time-decl'),
|
||||
(PRESENTATIONNS,'footer-decl'),
|
||||
(PRESENTATIONNS,'header-decl'),
|
||||
(TABLENS,'table-template'),
|
||||
(TEXTNS,'alphabetical-index-entry-template'),
|
||||
(TEXTNS,'alphabetical-index-source'),
|
||||
(TEXTNS,'bibliography-entry-template'),
|
||||
(TEXTNS,'bibliography-source'),
|
||||
(TEXTNS,'dde-connection-decls'),
|
||||
(TEXTNS,'illustration-index-entry-template'),
|
||||
(TEXTNS,'illustration-index-source'),
|
||||
(TEXTNS,'index-source-styles'),
|
||||
(TEXTNS,'index-title-template'),
|
||||
(TEXTNS,'note-continuation-notice-backward'),
|
||||
(TEXTNS,'note-continuation-notice-forward'),
|
||||
(TEXTNS,'notes-configuration'),
|
||||
(TEXTNS,'object-index-entry-template'),
|
||||
(TEXTNS,'object-index-source'),
|
||||
(TEXTNS,'sequence-decls'),
|
||||
(TEXTNS,'table-index-entry-template'),
|
||||
(TEXTNS,'table-index-source'),
|
||||
(TEXTNS,'table-of-content-entry-template'),
|
||||
(TEXTNS,'table-of-content-source'),
|
||||
(TEXTNS,'user-field-decls'),
|
||||
(TEXTNS,'user-index-entry-template'),
|
||||
(TEXTNS,'user-index-source'),
|
||||
(TEXTNS,'variable-decls'),
|
||||
)
|
||||
|
||||
empty_elements = (
|
||||
(ANIMNS,'animate'),
|
||||
(ANIMNS,'animateColor'),
|
||||
(ANIMNS,'animateMotion'),
|
||||
(ANIMNS,'animateTransform'),
|
||||
(ANIMNS,'audio'),
|
||||
(ANIMNS,'param'),
|
||||
(ANIMNS,'set'),
|
||||
(ANIMNS,'transitionFilter'),
|
||||
(CHARTNS,'categories'),
|
||||
(CHARTNS,'data-point'),
|
||||
(CHARTNS,'domain'),
|
||||
(CHARTNS,'error-indicator'),
|
||||
(CHARTNS,'floor'),
|
||||
(CHARTNS,'grid'),
|
||||
(CHARTNS,'legend'),
|
||||
(CHARTNS,'mean-value'),
|
||||
(CHARTNS,'regression-curve'),
|
||||
(CHARTNS,'stock-gain-marker'),
|
||||
(CHARTNS,'stock-loss-marker'),
|
||||
(CHARTNS,'stock-range-line'),
|
||||
(CHARTNS,'symbol-image'),
|
||||
(CHARTNS,'wall'),
|
||||
(DR3DNS,'cube'),
|
||||
(DR3DNS,'extrude'),
|
||||
(DR3DNS,'light'),
|
||||
(DR3DNS,'rotate'),
|
||||
(DR3DNS,'sphere'),
|
||||
(DRAWNS,'contour-path'),
|
||||
(DRAWNS,'contour-polygon'),
|
||||
(DRAWNS,'equation'),
|
||||
(DRAWNS,'fill-image'),
|
||||
(DRAWNS,'floating-frame'),
|
||||
(DRAWNS,'glue-point'),
|
||||
(DRAWNS,'gradient'),
|
||||
(DRAWNS,'handle'),
|
||||
(DRAWNS,'hatch'),
|
||||
(DRAWNS,'layer'),
|
||||
(DRAWNS,'marker'),
|
||||
(DRAWNS,'opacity'),
|
||||
(DRAWNS,'page-thumbnail'),
|
||||
(DRAWNS,'param'),
|
||||
(DRAWNS,'stroke-dash'),
|
||||
(FORMNS,'connection-resource'),
|
||||
(FORMNS,'list-value'),
|
||||
(FORMNS,'property'),
|
||||
(MANIFESTNS,'algorithm'),
|
||||
(MANIFESTNS,'key-derivation'),
|
||||
(METANS,'auto-reload'),
|
||||
(METANS,'document-statistic'),
|
||||
(METANS,'hyperlink-behaviour'),
|
||||
(METANS,'template'),
|
||||
(NUMBERNS,'am-pm'),
|
||||
(NUMBERNS,'boolean'),
|
||||
(NUMBERNS,'day'),
|
||||
(NUMBERNS,'day-of-week'),
|
||||
(NUMBERNS,'era'),
|
||||
(NUMBERNS,'fraction'),
|
||||
(NUMBERNS,'hours'),
|
||||
(NUMBERNS,'minutes'),
|
||||
(NUMBERNS,'month'),
|
||||
(NUMBERNS,'quarter'),
|
||||
(NUMBERNS,'scientific-number'),
|
||||
(NUMBERNS,'seconds'),
|
||||
(NUMBERNS,'text-content'),
|
||||
(NUMBERNS,'week-of-year'),
|
||||
(NUMBERNS,'year'),
|
||||
(OFFICENS,'dde-source'),
|
||||
(PRESENTATIONNS,'date-time'),
|
||||
(PRESENTATIONNS,'footer'),
|
||||
(PRESENTATIONNS,'header'),
|
||||
(PRESENTATIONNS,'placeholder'),
|
||||
(PRESENTATIONNS,'play'),
|
||||
(PRESENTATIONNS,'show'),
|
||||
(PRESENTATIONNS,'sound'),
|
||||
(SCRIPTNS,'event-listener'),
|
||||
(STYLENS,'column'),
|
||||
(STYLENS,'column-sep'),
|
||||
(STYLENS,'drop-cap'),
|
||||
(STYLENS,'footnote-sep'),
|
||||
(STYLENS,'list-level-properties'),
|
||||
(STYLENS,'map'),
|
||||
(STYLENS,'ruby-properties'),
|
||||
(STYLENS,'table-column-properties'),
|
||||
(STYLENS,'tab-stop'),
|
||||
(STYLENS,'text-properties'),
|
||||
(SVGNS,'definition-src'),
|
||||
(SVGNS,'font-face-format'),
|
||||
(SVGNS,'font-face-name'),
|
||||
(SVGNS,'stop'),
|
||||
(TABLENS,'body'),
|
||||
(TABLENS,'cell-address'),
|
||||
(TABLENS,'cell-range-source'),
|
||||
(TABLENS,'change-deletion'),
|
||||
(TABLENS,'consolidation'),
|
||||
(TABLENS,'database-source-query'),
|
||||
(TABLENS,'database-source-sql'),
|
||||
(TABLENS,'database-source-table'),
|
||||
(TABLENS,'data-pilot-display-info'),
|
||||
(TABLENS,'data-pilot-field-reference'),
|
||||
(TABLENS,'data-pilot-group-member'),
|
||||
(TABLENS,'data-pilot-layout-info'),
|
||||
(TABLENS,'data-pilot-member'),
|
||||
(TABLENS,'data-pilot-sort-info'),
|
||||
(TABLENS,'data-pilot-subtotal'),
|
||||
(TABLENS,'dependency'),
|
||||
(TABLENS,'error-macro'),
|
||||
(TABLENS,'even-columns'),
|
||||
(TABLENS,'even-rows'),
|
||||
(TABLENS,'filter-condition'),
|
||||
(TABLENS,'first-column'),
|
||||
(TABLENS,'first-row'),
|
||||
(TABLENS,'highlighted-range'),
|
||||
(TABLENS,'insertion-cut-off'),
|
||||
(TABLENS,'iteration'),
|
||||
(TABLENS,'label-range'),
|
||||
(TABLENS,'last-column'),
|
||||
(TABLENS,'last-row'),
|
||||
(TABLENS,'movement-cut-off'),
|
||||
(TABLENS,'named-expression'),
|
||||
(TABLENS,'named-range'),
|
||||
(TABLENS,'null-date'),
|
||||
(TABLENS,'odd-columns'),
|
||||
(TABLENS,'odd-rows'),
|
||||
(TABLENS,'operation'),
|
||||
(TABLENS,'scenario'),
|
||||
(TABLENS,'sort-by'),
|
||||
(TABLENS,'sort-groups'),
|
||||
(TABLENS,'source-range-address'),
|
||||
(TABLENS,'source-service'),
|
||||
(TABLENS,'subtotal-field'),
|
||||
(TABLENS,'table-column'),
|
||||
(TABLENS,'table-source'),
|
||||
(TABLENS,'target-range-address'),
|
||||
(TEXTNS,'alphabetical-index-auto-mark-file'),
|
||||
(TEXTNS,'alphabetical-index-mark'),
|
||||
(TEXTNS,'alphabetical-index-mark-end'),
|
||||
(TEXTNS,'alphabetical-index-mark-start'),
|
||||
(TEXTNS,'bookmark'),
|
||||
(TEXTNS,'bookmark-end'),
|
||||
(TEXTNS,'bookmark-start'),
|
||||
(TEXTNS,'change'),
|
||||
(TEXTNS,'change-end'),
|
||||
(TEXTNS,'change-start'),
|
||||
(TEXTNS,'dde-connection-decl'),
|
||||
(TEXTNS,'index-entry-bibliography'),
|
||||
(TEXTNS,'index-entry-chapter'),
|
||||
(TEXTNS,'index-entry-link-end'),
|
||||
(TEXTNS,'index-entry-link-start'),
|
||||
(TEXTNS,'index-entry-page-number'),
|
||||
(TEXTNS,'index-entry-tab-stop'),
|
||||
(TEXTNS,'index-entry-text'),
|
||||
(TEXTNS,'index-source-style'),
|
||||
(TEXTNS,'line-break'),
|
||||
(TEXTNS,'page'),
|
||||
(TEXTNS,'reference-mark'),
|
||||
(TEXTNS,'reference-mark-end'),
|
||||
(TEXTNS,'reference-mark-start'),
|
||||
(TEXTNS,'s'),
|
||||
(TEXTNS,'section-source'),
|
||||
(TEXTNS,'sequence-decl'),
|
||||
(TEXTNS,'soft-page-break'),
|
||||
(TEXTNS,'sort-key'),
|
||||
(TEXTNS,'tab'),
|
||||
(TEXTNS,'toc-mark'),
|
||||
(TEXTNS,'toc-mark-end'),
|
||||
(TEXTNS,'toc-mark-start'),
|
||||
(TEXTNS,'user-field-decl'),
|
||||
(TEXTNS,'user-index-mark'),
|
||||
(TEXTNS,'user-index-mark-end'),
|
||||
(TEXTNS,'user-index-mark-start'),
|
||||
(TEXTNS,'variable-decl')
|
||||
)
|
||||
@@ -1,115 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
|
||||
from .namespaces import FORMNS
|
||||
from .element import Element
|
||||
|
||||
|
||||
# Autogenerated
|
||||
def Button(**args):
|
||||
return Element(qname = (FORMNS,'button'), **args)
|
||||
|
||||
def Checkbox(**args):
|
||||
return Element(qname = (FORMNS,'checkbox'), **args)
|
||||
|
||||
def Column(**args):
|
||||
return Element(qname = (FORMNS,'column'), **args)
|
||||
|
||||
def Combobox(**args):
|
||||
return Element(qname = (FORMNS,'combobox'), **args)
|
||||
|
||||
def ConnectionResource(**args):
|
||||
return Element(qname = (FORMNS,'connection-resource'), **args)
|
||||
|
||||
def Date(**args):
|
||||
return Element(qname = (FORMNS,'date'), **args)
|
||||
|
||||
def File(**args):
|
||||
return Element(qname = (FORMNS,'file'), **args)
|
||||
|
||||
def FixedText(**args):
|
||||
return Element(qname = (FORMNS,'fixed-text'), **args)
|
||||
|
||||
def Form(**args):
|
||||
return Element(qname = (FORMNS,'form'), **args)
|
||||
|
||||
def FormattedText(**args):
|
||||
return Element(qname = (FORMNS,'formatted-text'), **args)
|
||||
|
||||
def Frame(**args):
|
||||
return Element(qname = (FORMNS,'frame'), **args)
|
||||
|
||||
def GenericControl(**args):
|
||||
return Element(qname = (FORMNS,'generic-control'), **args)
|
||||
|
||||
def Grid(**args):
|
||||
return Element(qname = (FORMNS,'grid'), **args)
|
||||
|
||||
def Hidden(**args):
|
||||
return Element(qname = (FORMNS,'hidden'), **args)
|
||||
|
||||
def Image(**args):
|
||||
return Element(qname = (FORMNS,'image'), **args)
|
||||
|
||||
def ImageFrame(**args):
|
||||
return Element(qname = (FORMNS,'image-frame'), **args)
|
||||
|
||||
def Item(**args):
|
||||
return Element(qname = (FORMNS,'item'), **args)
|
||||
|
||||
def ListProperty(**args):
|
||||
return Element(qname = (FORMNS,'list-property'), **args)
|
||||
|
||||
def ListValue(**args):
|
||||
return Element(qname = (FORMNS,'list-value'), **args)
|
||||
|
||||
def Listbox(**args):
|
||||
return Element(qname = (FORMNS,'listbox'), **args)
|
||||
|
||||
def Number(**args):
|
||||
return Element(qname = (FORMNS,'number'), **args)
|
||||
|
||||
def Option(**args):
|
||||
return Element(qname = (FORMNS,'option'), **args)
|
||||
|
||||
def Password(**args):
|
||||
return Element(qname = (FORMNS,'password'), **args)
|
||||
|
||||
def Properties(**args):
|
||||
return Element(qname = (FORMNS,'properties'), **args)
|
||||
|
||||
def Property(**args):
|
||||
return Element(qname = (FORMNS,'property'), **args)
|
||||
|
||||
def Radio(**args):
|
||||
return Element(qname = (FORMNS,'radio'), **args)
|
||||
|
||||
def Text(**args):
|
||||
return Element(qname = (FORMNS,'text'), **args)
|
||||
|
||||
def Textarea(**args):
|
||||
return Element(qname = (FORMNS,'textarea'), **args)
|
||||
|
||||
def Time(**args):
|
||||
return Element(qname = (FORMNS,'time'), **args)
|
||||
|
||||
def ValueRange(**args):
|
||||
return Element(qname = (FORMNS,'value-range'), **args)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,112 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2007-2008 Søren Roug, European Environment Agency
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
|
||||
# This script is to be embedded in opendocument.py later
|
||||
# The purpose is to read an ODT/ODP/ODS file and create the datastructure
|
||||
# in memory. The user should then be able to make operations and then save
|
||||
# the structure again.
|
||||
|
||||
from xml.sax import make_parser,handler
|
||||
from xml.sax.xmlreader import InputSource
|
||||
import xml.sax.saxutils
|
||||
from .element import Element
|
||||
from .namespaces import OFFICENS
|
||||
from io import StringIO
|
||||
|
||||
#
|
||||
# Parse the XML files
|
||||
#
|
||||
class LoadParser(handler.ContentHandler):
|
||||
""" Extract headings from content.xml of an ODT file """
|
||||
triggers = (
|
||||
(OFFICENS, 'automatic-styles'), (OFFICENS, 'body'),
|
||||
(OFFICENS, 'font-face-decls'), (OFFICENS, 'master-styles'),
|
||||
(OFFICENS, 'meta'), (OFFICENS, 'scripts'),
|
||||
(OFFICENS, 'settings'), (OFFICENS, 'styles') )
|
||||
|
||||
def __init__(self, document):
|
||||
self.doc = document
|
||||
self.data = []
|
||||
self.level = 0
|
||||
self.parse = False
|
||||
|
||||
def characters(self, data):
|
||||
if self.parse == False:
|
||||
return
|
||||
self.data.append(data)
|
||||
|
||||
def startElementNS(self, tag, qname, attrs):
|
||||
if tag in self.triggers:
|
||||
self.parse = True
|
||||
if self.doc._parsing != "styles.xml" and tag == (OFFICENS, 'font-face-decls'):
|
||||
self.parse = False
|
||||
if self.parse == False:
|
||||
return
|
||||
|
||||
self.level = self.level + 1
|
||||
# Add any accumulated text content
|
||||
content = ''.join(self.data)
|
||||
if len(content.strip()) > 0:
|
||||
self.parent.addText(content, check_grammar=False)
|
||||
self.data = []
|
||||
# Create the element
|
||||
attrdict = {}
|
||||
for (att,value) in list(attrs.items()):
|
||||
attrdict[att] = value
|
||||
try:
|
||||
e = Element(qname = tag, qattributes=attrdict, check_grammar=False)
|
||||
self.curr = e
|
||||
except AttributeError as v:
|
||||
print("Error: %s" % v)
|
||||
|
||||
if tag == (OFFICENS, 'automatic-styles'):
|
||||
e = self.doc.automaticstyles
|
||||
elif tag == (OFFICENS, 'body'):
|
||||
e = self.doc.body
|
||||
elif tag == (OFFICENS, 'master-styles'):
|
||||
e = self.doc.masterstyles
|
||||
elif tag == (OFFICENS, 'meta'):
|
||||
e = self.doc.meta
|
||||
elif tag == (OFFICENS,'scripts'):
|
||||
e = self.doc.scripts
|
||||
elif tag == (OFFICENS,'settings'):
|
||||
e = self.doc.settings
|
||||
elif tag == (OFFICENS,'styles'):
|
||||
e = self.doc.styles
|
||||
elif self.doc._parsing == "styles.xml" and tag == (OFFICENS, 'font-face-decls'):
|
||||
e = self.doc.fontfacedecls
|
||||
elif hasattr(self,'parent'):
|
||||
self.parent.addElement(e, check_grammar=False)
|
||||
self.parent = e
|
||||
|
||||
|
||||
def endElementNS(self, tag, qname):
|
||||
if self.parse == False:
|
||||
return
|
||||
self.level = self.level - 1
|
||||
str = ''.join(self.data)
|
||||
if len(str.strip()) > 0:
|
||||
self.curr.addText(str, check_grammar=False)
|
||||
self.data = []
|
||||
self.curr = self.curr.parentNode
|
||||
self.parent = self.curr
|
||||
if tag in self.triggers:
|
||||
self.parse = False
|
||||
@@ -1,41 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
#
|
||||
|
||||
from .namespaces import MANIFESTNS
|
||||
from .element import Element
|
||||
|
||||
# Autogenerated
|
||||
def Manifest(**args):
|
||||
return Element(qname = (MANIFESTNS,'manifest'), **args)
|
||||
|
||||
def FileEntry(**args):
|
||||
return Element(qname = (MANIFESTNS,'file-entry'), **args)
|
||||
|
||||
def EncryptionData(**args):
|
||||
return Element(qname = (MANIFESTNS,'encryption-data'), **args)
|
||||
|
||||
def Algorithm(**args):
|
||||
return Element(qname = (MANIFESTNS,'algorithm'), **args)
|
||||
|
||||
def KeyDerivation(**args):
|
||||
return Element(qname = (MANIFESTNS,'key-derivation'), **args)
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2006-2007 Søren Roug, European Environment Agency
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
|
||||
from .namespaces import MATHNS
|
||||
from .element import Element
|
||||
|
||||
# ODF 1.0 section 12.5
|
||||
# Mathematical content is represented by MathML 2.0
|
||||
|
||||
# Autogenerated
|
||||
def Math(**args):
|
||||
return Element(qname = (MATHNS,'math'), **args)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user