diff --git a/.venv/Lib/site-packages/et_xmlfile-1.1.0.dist-info/AUTHORS.txt b/.venv/Lib/site-packages/et_xmlfile-1.1.0.dist-info/AUTHORS.txt new file mode 100644 index 00000000..f75816c8 --- /dev/null +++ b/.venv/Lib/site-packages/et_xmlfile-1.1.0.dist-info/AUTHORS.txt @@ -0,0 +1,4 @@ +The authors in alphabetical order + +* Charlie Clark +* Elias Rabel \ No newline at end of file diff --git a/.venv/Lib/site-packages/et_xmlfile-1.1.0.dist-info/INSTALLER b/.venv/Lib/site-packages/et_xmlfile-1.1.0.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/.venv/Lib/site-packages/et_xmlfile-1.1.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/Lib/site-packages/et_xmlfile-1.1.0.dist-info/LICENCE.rst b/.venv/Lib/site-packages/et_xmlfile-1.1.0.dist-info/LICENCE.rst new file mode 100644 index 00000000..d7cb633e --- /dev/null +++ b/.venv/Lib/site-packages/et_xmlfile-1.1.0.dist-info/LICENCE.rst @@ -0,0 +1,34 @@ +This software is under the MIT Licence +====================================== + +Copyright (c) 2010 openpyxl + +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. + +Odict implementation in openpyxl/writer/odict.py uses the following licence: + +Copyright (c) 2001-2011 Python Software Foundation + 2011 Raymond Hettinger +License: PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 + See http://www.opensource.org/licenses/Python-2.0 for full terms +Note: backport changes by Raymond were originally distributed under MIT + license, but since the original license for Python is more + restrictive than MIT, code cannot be released under its terms and + still adheres to the limitations of Python license. diff --git a/.venv/Lib/site-packages/et_xmlfile-1.1.0.dist-info/METADATA b/.venv/Lib/site-packages/et_xmlfile-1.1.0.dist-info/METADATA new file mode 100644 index 00000000..dbbcb92a --- /dev/null +++ b/.venv/Lib/site-packages/et_xmlfile-1.1.0.dist-info/METADATA @@ -0,0 +1,37 @@ +Metadata-Version: 2.1 +Name: et-xmlfile +Version: 1.1.0 +Summary: An implementation of lxml.xmlfile for the standard library +Home-page: https://foss.heptapod.net/openpyxl/et_xmlfile +Author: See ATUHORS.txt +Author-email: charlie.clark@clark-consulting.eu +License: MIT +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Operating System :: MacOS :: MacOS X +Classifier: Operating System :: Microsoft :: Windows +Classifier: Operating System :: POSIX +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Requires-Python: >=3.6 + +et_xmfile +========= + +et_xmlfile is a low memory library for creating large XML files. + +It is based upon the `xmlfile module from lxml `_ with the aim of allowing code to be developed that will work with both libraries. It was developed initially for the openpyxl project but is now a standalone module. + +The code was written by Elias Rabel as part of the `Python Düsseldorf `_ openpyxl sprint in September 2014. + + +Note on performance +------------------- + +The code was not developed with performance in mind but turned out to be faster than the existing SAX-based implementation but is significantly slower than lxml's xmlfile. There is one area where an optimisation for lxml will negatively affect the performance of et_xmfile and that is when using the `.element()` method on an xmlfile context manager. It is, therefore, recommended not to use this, though the method is provided for code compatibility. + + diff --git a/.venv/Lib/site-packages/et_xmlfile-1.1.0.dist-info/RECORD b/.venv/Lib/site-packages/et_xmlfile-1.1.0.dist-info/RECORD new file mode 100644 index 00000000..71c6f869 --- /dev/null +++ b/.venv/Lib/site-packages/et_xmlfile-1.1.0.dist-info/RECORD @@ -0,0 +1,11 @@ +et_xmlfile-1.1.0.dist-info/AUTHORS.txt,sha256=Y6mQLe0ywXMVP7WVFrZgEW3CqhIv-plM1CaOtdtBuXs,64 +et_xmlfile-1.1.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +et_xmlfile-1.1.0.dist-info/LICENCE.rst,sha256=r-YrNgzcqB-43m7kt2ENodhsORd3qAx6y20RRVxTxCk,1694 +et_xmlfile-1.1.0.dist-info/METADATA,sha256=B5hV5UW4GqmWGgwAW44C2ofZZEmV0MRnTEU98kzcUGw,1775 +et_xmlfile-1.1.0.dist-info/RECORD,, +et_xmlfile-1.1.0.dist-info/WHEEL,sha256=OqRkF0eY5GHssMorFjlbTIq072vpHpF60fIQA6lS9xA,92 +et_xmlfile-1.1.0.dist-info/top_level.txt,sha256=34-74d5NNARgTsPxCMta5o28XpBNmSN0iCZhtmx2Fk8,11 +et_xmlfile/__init__.py,sha256=-oTKwE6upIG2gOmnOc4KLgV-pKbBwy-zhQfizbmCruQ,269 +et_xmlfile/__pycache__/__init__.cpython-310.pyc,, +et_xmlfile/__pycache__/xmlfile.cpython-310.pyc,, +et_xmlfile/xmlfile.py,sha256=_h20RRb3ptDZ6xXoxMU_Wrx8rG6UZsg0TS7HEOrotzg,3204 diff --git a/.venv/Lib/site-packages/et_xmlfile-1.1.0.dist-info/WHEEL b/.venv/Lib/site-packages/et_xmlfile-1.1.0.dist-info/WHEEL new file mode 100644 index 00000000..385faab0 --- /dev/null +++ b/.venv/Lib/site-packages/et_xmlfile-1.1.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.36.2) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.venv/Lib/site-packages/et_xmlfile-1.1.0.dist-info/top_level.txt b/.venv/Lib/site-packages/et_xmlfile-1.1.0.dist-info/top_level.txt new file mode 100644 index 00000000..f573c275 --- /dev/null +++ b/.venv/Lib/site-packages/et_xmlfile-1.1.0.dist-info/top_level.txt @@ -0,0 +1 @@ +et_xmlfile diff --git a/.venv/Lib/site-packages/et_xmlfile/__init__.py b/.venv/Lib/site-packages/et_xmlfile/__init__.py new file mode 100644 index 00000000..11483588 --- /dev/null +++ b/.venv/Lib/site-packages/et_xmlfile/__init__.py @@ -0,0 +1,11 @@ +from __future__ import absolute_import + +from .xmlfile import xmlfile + +# constants +__version__ = '1.1.0' + +__author__ = 'See ATUHORS.txt' +__license__ = 'MIT' +__author_email__ = 'charlie.clark@clark-consulting.eu' +__url__ = 'https://foss.heptapod.net/openpyxl/et_xmlfile' diff --git a/.venv/Lib/site-packages/et_xmlfile/xmlfile.py b/.venv/Lib/site-packages/et_xmlfile/xmlfile.py new file mode 100644 index 00000000..09ec5555 --- /dev/null +++ b/.venv/Lib/site-packages/et_xmlfile/xmlfile.py @@ -0,0 +1,104 @@ +from __future__ import absolute_import +# Copyright (c) 2010-2015 openpyxl + +"""Implements the lxml.etree.xmlfile API using the standard library xml.etree""" + + +from contextlib import contextmanager + +from xml.etree.ElementTree import Element, tostring + + +class LxmlSyntaxError(Exception): + pass + + +class _FakeIncrementalFileWriter(object): + """Replacement for _IncrementalFileWriter of lxml. + Uses ElementTree to build xml in memory.""" + def __init__(self, output_file): + self._element_stack = [] + self._top_element = None + self._file = output_file + self._have_root = False + + @contextmanager + def element(self, tag, attrib=None, nsmap=None, **_extra): + """Create a new xml element using a context manager. + The elements are written when the top level context is left. + + This is for code compatibility only as it is quite slow. + """ + + # __enter__ part + self._have_root = True + if attrib is None: + attrib = {} + self._top_element = Element(tag, attrib=attrib, **_extra) + self._top_element.text = '' + self._top_element.tail = '' + self._element_stack.append(self._top_element) + yield + + # __exit__ part + el = self._element_stack.pop() + if self._element_stack: + parent = self._element_stack[-1] + parent.append(self._top_element) + self._top_element = parent + else: + self._write_element(el) + self._top_element = None + + def write(self, arg): + """Write a string or subelement.""" + + if isinstance(arg, str): + # it is not allowed to write a string outside of an element + if self._top_element is None: + raise LxmlSyntaxError() + + if len(self._top_element) == 0: + # element has no children: add string to text + self._top_element.text += arg + else: + # element has children: add string to tail of last child + self._top_element[-1].tail += arg + + else: + if self._top_element is not None: + self._top_element.append(arg) + elif not self._have_root: + self._write_element(arg) + else: + raise LxmlSyntaxError() + + def _write_element(self, element): + xml = tostring(element) + self._file.write(xml) + + def __enter__(self): + pass + + def __exit__(self, type, value, traceback): + # without root the xml document is incomplete + if not self._have_root: + raise LxmlSyntaxError() + + +class xmlfile(object): + """Context manager that can replace lxml.etree.xmlfile.""" + def __init__(self, output_file, buffered=False, encoding=None, close=False): + if isinstance(output_file, str): + self._file = open(output_file, 'wb') + self._close = True + else: + self._file = output_file + self._close = close + + def __enter__(self): + return _FakeIncrementalFileWriter(self._file) + + def __exit__(self, type, value, traceback): + if self._close == True: + self._file.close() diff --git a/.venv/Lib/site-packages/openpyxl-3.0.10.dist-info/INSTALLER b/.venv/Lib/site-packages/openpyxl-3.0.10.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl-3.0.10.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/Lib/site-packages/openpyxl-3.0.10.dist-info/LICENCE.rst b/.venv/Lib/site-packages/openpyxl-3.0.10.dist-info/LICENCE.rst new file mode 100644 index 00000000..82213c59 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl-3.0.10.dist-info/LICENCE.rst @@ -0,0 +1,23 @@ +This software is under the MIT Licence +====================================== + +Copyright (c) 2010 openpyxl + +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. diff --git a/.venv/Lib/site-packages/openpyxl-3.0.10.dist-info/METADATA b/.venv/Lib/site-packages/openpyxl-3.0.10.dist-info/METADATA new file mode 100644 index 00000000..36ccb9e9 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl-3.0.10.dist-info/METADATA @@ -0,0 +1,87 @@ +Metadata-Version: 2.1 +Name: openpyxl +Version: 3.0.10 +Summary: A Python library to read/write Excel 2010 xlsx/xlsm files +Home-page: https://openpyxl.readthedocs.io +Author: See AUTHORS +Author-email: charlie.clark@clark-consulting.eu +License: MIT +Project-URL: Documentation, https://openpyxl.readthedocs.io/en/stable/ +Project-URL: Source, https://foss.heptapod.net/openpyxl/openpyxl +Project-URL: Tracker, https://foss.heptapod.net/openpyxl/openpyxl/-/issues +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Operating System :: MacOS :: MacOS X +Classifier: Operating System :: Microsoft :: Windows +Classifier: Operating System :: POSIX +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Requires-Python: >=3.6 +License-File: LICENCE.rst +Requires-Dist: et-xmlfile + +.. image:: https://coveralls.io/repos/bitbucket/openpyxl/openpyxl/badge.svg?branch=default + :target: https://coveralls.io/bitbucket/openpyxl/openpyxl?branch=default + :alt: coverage status + +Introduction +------------ + +openpyxl is a Python library to read/write Excel 2010 xlsx/xlsm/xltx/xltm files. + +It was born from lack of existing library to read/write natively from Python +the Office Open XML format. + +All kudos to the PHPExcel team as openpyxl was initially based on PHPExcel. + + +Security +-------- + +By default openpyxl does not guard against quadratic blowup or billion laughs +xml attacks. To guard against these attacks install defusedxml. + +Mailing List +------------ + +The user list can be found on http://groups.google.com/group/openpyxl-users + + +Sample code:: + + from openpyxl import Workbook + wb = Workbook() + + # grab the active worksheet + ws = wb.active + + # Data can be assigned directly to cells + ws['A1'] = 42 + + # Rows can also be appended + ws.append([1, 2, 3]) + + # Python types will automatically be converted + import datetime + ws['A2'] = datetime.datetime.now() + + # Save the file + wb.save("sample.xlsx") + + +Documentation +------------- + +The documentation is at: https://openpyxl.readthedocs.io + +* installation methods +* code examples +* instructions for contributing + +Release notes: https://openpyxl.readthedocs.io/en/stable/changes.html + + diff --git a/.venv/Lib/site-packages/openpyxl-3.0.10.dist-info/RECORD b/.venv/Lib/site-packages/openpyxl-3.0.10.dist-info/RECORD new file mode 100644 index 00000000..46460186 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl-3.0.10.dist-info/RECORD @@ -0,0 +1,377 @@ +openpyxl-3.0.10.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +openpyxl-3.0.10.dist-info/LICENCE.rst,sha256=DIS7QvXTZ-Xr-fwt3jWxYUHfXuD9wYklCFi8bFVg9p4,1131 +openpyxl-3.0.10.dist-info/METADATA,sha256=r6YDI7oMq_RBCWv1t7hoLUfCndfSKMvZ3YgSNOKfk4U,2429 +openpyxl-3.0.10.dist-info/RECORD,, +openpyxl-3.0.10.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +openpyxl-3.0.10.dist-info/WHEEL,sha256=WzZ8cwjh8l0jtULNjYq1Hpr-WCqCRgPr--TX4P5I1Wo,110 +openpyxl-3.0.10.dist-info/top_level.txt,sha256=mKJO5QFAsUEDtJ_c97F-IbmVtHYEDymqD7d5X0ULkVs,9 +openpyxl/__init__.py,sha256=VtMPdsxka8GIDNczecdDTUIHQOIVHeDp3DYSbgFOZfA,589 +openpyxl/__pycache__/__init__.cpython-310.pyc,, +openpyxl/__pycache__/_constants.cpython-310.pyc,, +openpyxl/_constants.py,sha256=R63KSSz5_YgnXh6ecWuwUGaoKR9deHy1QkMcKheNfIQ,307 +openpyxl/cell/__init__.py,sha256=eEjbyTZfWrVERGpzukPo03WgevOvDJqpacyA4Whx5n8,122 +openpyxl/cell/__pycache__/__init__.cpython-310.pyc,, +openpyxl/cell/__pycache__/_writer.cpython-310.pyc,, +openpyxl/cell/__pycache__/cell.cpython-310.pyc,, +openpyxl/cell/__pycache__/read_only.cpython-310.pyc,, +openpyxl/cell/__pycache__/text.cpython-310.pyc,, +openpyxl/cell/_writer.py,sha256=SF2utlBS0iyswJpuboFX6Y_wXgsxs5ARgQCox_sFCxg,3186 +openpyxl/cell/cell.py,sha256=xQHDiR8yjBpPyN1JbJdLXEe-HxtTbDLMuXr7hzi6j5o,8670 +openpyxl/cell/read_only.py,sha256=rNEragjLK2TYU8lHRivO8_4lp-a4mJtZJO0-Pdxl4gk,3113 +openpyxl/cell/text.py,sha256=pHtvucnpW8pe2qU8ekDdActJu5UnUmwos0WCR_pxG8M,4367 +openpyxl/chart/_3d.py,sha256=mBWQGZ1qgwprx7sTzIVxkHsGoqX4kLtscbgKXPll5f4,3104 +openpyxl/chart/__init__.py,sha256=sitthdKhd--R4C3S7hBWghxhgCC-KVnORglOVs5doIQ,564 +openpyxl/chart/__pycache__/_3d.cpython-310.pyc,, +openpyxl/chart/__pycache__/__init__.cpython-310.pyc,, +openpyxl/chart/__pycache__/_chart.cpython-310.pyc,, +openpyxl/chart/__pycache__/area_chart.cpython-310.pyc,, +openpyxl/chart/__pycache__/axis.cpython-310.pyc,, +openpyxl/chart/__pycache__/bar_chart.cpython-310.pyc,, +openpyxl/chart/__pycache__/bubble_chart.cpython-310.pyc,, +openpyxl/chart/__pycache__/chartspace.cpython-310.pyc,, +openpyxl/chart/__pycache__/data_source.cpython-310.pyc,, +openpyxl/chart/__pycache__/descriptors.cpython-310.pyc,, +openpyxl/chart/__pycache__/error_bar.cpython-310.pyc,, +openpyxl/chart/__pycache__/label.cpython-310.pyc,, +openpyxl/chart/__pycache__/layout.cpython-310.pyc,, +openpyxl/chart/__pycache__/legend.cpython-310.pyc,, +openpyxl/chart/__pycache__/line_chart.cpython-310.pyc,, +openpyxl/chart/__pycache__/marker.cpython-310.pyc,, +openpyxl/chart/__pycache__/picture.cpython-310.pyc,, +openpyxl/chart/__pycache__/pie_chart.cpython-310.pyc,, +openpyxl/chart/__pycache__/pivot.cpython-310.pyc,, +openpyxl/chart/__pycache__/plotarea.cpython-310.pyc,, +openpyxl/chart/__pycache__/print_settings.cpython-310.pyc,, +openpyxl/chart/__pycache__/radar_chart.cpython-310.pyc,, +openpyxl/chart/__pycache__/reader.cpython-310.pyc,, +openpyxl/chart/__pycache__/reference.cpython-310.pyc,, +openpyxl/chart/__pycache__/scatter_chart.cpython-310.pyc,, +openpyxl/chart/__pycache__/series.cpython-310.pyc,, +openpyxl/chart/__pycache__/series_factory.cpython-310.pyc,, +openpyxl/chart/__pycache__/shapes.cpython-310.pyc,, +openpyxl/chart/__pycache__/stock_chart.cpython-310.pyc,, +openpyxl/chart/__pycache__/surface_chart.cpython-310.pyc,, +openpyxl/chart/__pycache__/text.cpython-310.pyc,, +openpyxl/chart/__pycache__/title.cpython-310.pyc,, +openpyxl/chart/__pycache__/trendline.cpython-310.pyc,, +openpyxl/chart/__pycache__/updown_bars.cpython-310.pyc,, +openpyxl/chart/_chart.py,sha256=5p29QQtdXmwrMWveTVbQmxAqcxNMaLo5_TYHmWpIXhM,5583 +openpyxl/chart/area_chart.py,sha256=_yWbDwamxVaXeNLjDFjfmYQj2pNKmWwJ7t8-zmj2RFs,2925 +openpyxl/chart/axis.py,sha256=ZhS5lHCRpAFLjO-LSBvPqmo3JFoStJURNpjerOvLry0,12657 +openpyxl/chart/bar_chart.py,sha256=gVUIwK7QMHejTqxkrRUMHK4g_euyGo9snvR9UiU3OeM,4175 +openpyxl/chart/bubble_chart.py,sha256=oQFIoXmFbck665Xrha8tTJkgaWYzXpnSaIWRaN20_FU,2021 +openpyxl/chart/chartspace.py,sha256=21asc8NG4Lzvu72-4fJafe1MV_PCXdEOiHRZkkBkaGI,6084 +openpyxl/chart/data_source.py,sha256=ZGFU04_tol5aj99Cy_z90A0TOySo-zsdgLXXLm98ZnU,5809 +openpyxl/chart/descriptors.py,sha256=n2z3gt3wajcZlxETyNx6i2q94Xhdt0xkMj8ezGg4fCo,764 +openpyxl/chart/error_bar.py,sha256=P6cMiz7SoxrsAZPYr3uNtBgKKFt_42pRsOjwgGGmeYQ,1832 +openpyxl/chart/label.py,sha256=hX6jerhmoqUgv60nW8LGkFjrxbmlYsh-9I2UdcZsxEY,4167 +openpyxl/chart/layout.py,sha256=nOob9eaim8AbQKEWt9dR5UaJ0i8HLb4TuHYz8yrTMlY,2040 +openpyxl/chart/legend.py,sha256=7Rx9q9w1DlXCZKQFQLcwtwIbDdqTjaRepIHmaeSnKp4,2040 +openpyxl/chart/line_chart.py,sha256=CiVznoifZD8fBclgJXqXl5GgoC3B-GXF8FyQA48GMQI,3986 +openpyxl/chart/marker.py,sha256=nfzOuh2L6xng8d2L1o4GbRdZUC0iNzxVzyor6pasJZQ,2600 +openpyxl/chart/picture.py,sha256=-uC50EG_Edye3FzQqSXgjMdc9JQ2IiZ04o9UqD5_cmc,1156 +openpyxl/chart/pie_chart.py,sha256=gyX0Wx4qJs4J7UFfyG1aPd1Dmp-lGyssK--TwsZNuZ8,4868 +openpyxl/chart/pivot.py,sha256=eqGfjudiANL1se3pbQVJVMs0asFH53vkuSU5_aYrq0w,1741 +openpyxl/chart/plotarea.py,sha256=ZaPQ9zrw7ZsvUXxI4z0jtueLaxEvPaclM1JiG6BXMg4,5832 +openpyxl/chart/print_settings.py,sha256=9h7hqB_bOOWQcwv-nXxUJx9LF8ldZ7uXgy8kpaSEAps,1454 +openpyxl/chart/radar_chart.py,sha256=5BY3nHnrbrMzCg3lhmFseKF7aTkRTQBqQf9jCQMVryQ,1537 +openpyxl/chart/reader.py,sha256=Lw_sTWhbK4OyG6TDyCvc1YxwsA-O8MtKWaxTVP5bgcg,719 +openpyxl/chart/reference.py,sha256=4VmUSvnq2PK1RhXppAoMnsgbM7xkfV3y6VNQXm1wI88,3098 +openpyxl/chart/scatter_chart.py,sha256=6ZGA8us3mn9ML1SNznMbnDoZGa3eEKhlQfJy0eBxEP4,1559 +openpyxl/chart/series.py,sha256=lacE1JDETHpw6wt-gQQJpXA8TohBw6j33-Pq5ln24NI,5908 +openpyxl/chart/series_factory.py,sha256=KI1cyin3vRjolNKZnJU3fVJL2lAA1EDFVJOIGpb7Ovs,1368 +openpyxl/chart/shapes.py,sha256=LuuMGPi8TpFKSiEACdRaqSM1UfrRs4OHl6bO97MFM-U,2815 +openpyxl/chart/stock_chart.py,sha256=PaySnFES8SkmEurl8l1ozqNNs9jbcdDKM1yGrUaIFMY,1620 +openpyxl/chart/surface_chart.py,sha256=tVr-y9U3yqVtiwAMdI1FVnoPvdowpe7Q0jOLsx6LLN8,2955 +openpyxl/chart/text.py,sha256=XWCcUSIkEeNDPL2i6Vd5DIQrXYdj0gMZBA04eYwlms0,1857 +openpyxl/chart/title.py,sha256=jCj0KKTV8jfZEJJW9jRZIC4658oSqP5T7R_7X6a4FCo,1973 +openpyxl/chart/trendline.py,sha256=dfykzMNDQADooCk3eGDtJsb-HtS5IjLPXUSpJj_F3Es,3053 +openpyxl/chart/updown_bars.py,sha256=EEHh1masZYcwuBVCBEVuYaHp1sYGM9NuRXBu8ZeqGvI,897 +openpyxl/chartsheet/__init__.py,sha256=LWRt_ng8kVyFPaPlkQcz09a4lmhMKuW1zNg2wViWi_k,71 +openpyxl/chartsheet/__pycache__/__init__.cpython-310.pyc,, +openpyxl/chartsheet/__pycache__/chartsheet.cpython-310.pyc,, +openpyxl/chartsheet/__pycache__/custom.cpython-310.pyc,, +openpyxl/chartsheet/__pycache__/properties.cpython-310.pyc,, +openpyxl/chartsheet/__pycache__/protection.cpython-310.pyc,, +openpyxl/chartsheet/__pycache__/publish.cpython-310.pyc,, +openpyxl/chartsheet/__pycache__/relation.cpython-310.pyc,, +openpyxl/chartsheet/__pycache__/views.cpython-310.pyc,, +openpyxl/chartsheet/chartsheet.py,sha256=m10kVu5nVE2-pdEMXrPlmMj5EdJHYPaSZ3BfEToYpo4,4042 +openpyxl/chartsheet/custom.py,sha256=adJoF6ZTWT6lNAK-VAqVXDZ4Js_Hpa4mqm1goaMO9oc,1691 +openpyxl/chartsheet/properties.py,sha256=Sj6qcgdkmMDUEYwS4ugXAzXMhj5_9w3SoN6b2pxTijU,679 +openpyxl/chartsheet/protection.py,sha256=eJixEBmdoTDO2_0h6g51sdSdfSdCaP8UUNsbEqHds6U,1265 +openpyxl/chartsheet/publish.py,sha256=0zOyFw5eIp2J9-WaNiuFcbLyHwr5f7rbkI7Ux6CbB1s,1587 +openpyxl/chartsheet/relation.py,sha256=UX6_M0VziJSXnrWGtKo_Ztp7ax5XGFHCL08emEmq2zs,2731 +openpyxl/chartsheet/views.py,sha256=WDbrdA7FYybD9f-iohh9J09qOcPpWdlkU3aJrROn2K8,1341 +openpyxl/comments/__init__.py,sha256=vS5MXYD5sbLGpWXb7q-GwN9SvdvlXoMM6voVZJMrfEI,67 +openpyxl/comments/__pycache__/__init__.cpython-310.pyc,, +openpyxl/comments/__pycache__/author.cpython-310.pyc,, +openpyxl/comments/__pycache__/comment_sheet.cpython-310.pyc,, +openpyxl/comments/__pycache__/comments.cpython-310.pyc,, +openpyxl/comments/__pycache__/shape_writer.cpython-310.pyc,, +openpyxl/comments/author.py,sha256=EKZq4Bvg0Uq_Vi3_OUhU-z8TrmQb9cWyTaOUl2CBeUI,388 +openpyxl/comments/comment_sheet.py,sha256=vpvJO1QaRJ2QvgFy-a59TWizvRke2eIyQJytWhgPcrA,5874 +openpyxl/comments/comments.py,sha256=LiOlDmCknMa_Ab_pUvulaj804CvsjGpBqi0ypXH4fxM,1474 +openpyxl/comments/shape_writer.py,sha256=0gYUfez5cAU5rLHTpuPX0hOah-8EwpY5SCzzM3ER_vc,3868 +openpyxl/compat/__init__.py,sha256=Nl-SeWQbkNn10RtizWEAgnk4glYoLRmUV4mwXQDi7OQ,1592 +openpyxl/compat/__pycache__/__init__.cpython-310.pyc,, +openpyxl/compat/__pycache__/abc.cpython-310.pyc,, +openpyxl/compat/__pycache__/numbers.cpython-310.pyc,, +openpyxl/compat/__pycache__/product.cpython-310.pyc,, +openpyxl/compat/__pycache__/singleton.cpython-310.pyc,, +openpyxl/compat/__pycache__/strings.cpython-310.pyc,, +openpyxl/compat/abc.py,sha256=9en931ecvRZKxR7qmcllR2p33HF4ZlcljII1H2QGNEk,155 +openpyxl/compat/numbers.py,sha256=6dSz7rLWsS9Wrs_VC5Qx2e4fbOZexwV5eEXeXv8SWPY,1617 +openpyxl/compat/product.py,sha256=PckDpAMWi2xcIuQpz9un6YtBK1aMDb17sDIEZeEHKBg,264 +openpyxl/compat/singleton.py,sha256=_odbcfJhL_P25QjrNYfCpUbRveis7uDiTtfUWvnFha4,1083 +openpyxl/compat/strings.py,sha256=zJb2-t-ezKuAqv9b68ZOguRUDdwEQepca1Zxg5srKj8,604 +openpyxl/descriptors/__init__.py,sha256=p_J_95x3LzJ-WAlkmClVOfibMi3PIwzgFu_9Ce3F-r8,1816 +openpyxl/descriptors/__pycache__/__init__.cpython-310.pyc,, +openpyxl/descriptors/__pycache__/base.cpython-310.pyc,, +openpyxl/descriptors/__pycache__/excel.cpython-310.pyc,, +openpyxl/descriptors/__pycache__/namespace.cpython-310.pyc,, +openpyxl/descriptors/__pycache__/nested.cpython-310.pyc,, +openpyxl/descriptors/__pycache__/sequence.cpython-310.pyc,, +openpyxl/descriptors/__pycache__/serialisable.cpython-310.pyc,, +openpyxl/descriptors/__pycache__/slots.cpython-310.pyc,, +openpyxl/descriptors/base.py,sha256=AEnaMxOZ0EA-vjaqVdoN9SSmyAfBz3hiOJ8JcwR3Geo,7110 +openpyxl/descriptors/excel.py,sha256=TPYzzf0x5uyCyXC3USjO8AZhzD83J0T08OaUOfqCyhs,2438 +openpyxl/descriptors/namespace.py,sha256=ZvQcYT_aLJIsdc5LjlmRPZQ7UZp-1OdWkPrD7hG2Jmc,309 +openpyxl/descriptors/nested.py,sha256=lkmVD1zG_VPAbukFpk8jnj5MjPkDaVRzVKKJfBRCWqc,2651 +openpyxl/descriptors/sequence.py,sha256=DJjJ8RyFaiuPSfNvEVevMii5uzUoZe2hjOESaS4kHig,3324 +openpyxl/descriptors/serialisable.py,sha256=cfRFFSAMbTM0GCPY5Lqy_UrmJABbKf-Klesw8pitTRo,7343 +openpyxl/descriptors/slots.py,sha256=xNj5vLWWoounpYqbP2JDnnhlTiTLRn-uTfQxncpFfn0,824 +openpyxl/drawing/__init__.py,sha256=4PhbbB73RtQ15UUWzzA12nN7gLRaG7D8rXNPY9OCWXU,66 +openpyxl/drawing/__pycache__/__init__.cpython-310.pyc,, +openpyxl/drawing/__pycache__/colors.cpython-310.pyc,, +openpyxl/drawing/__pycache__/connector.cpython-310.pyc,, +openpyxl/drawing/__pycache__/drawing.cpython-310.pyc,, +openpyxl/drawing/__pycache__/effect.cpython-310.pyc,, +openpyxl/drawing/__pycache__/fill.cpython-310.pyc,, +openpyxl/drawing/__pycache__/geometry.cpython-310.pyc,, +openpyxl/drawing/__pycache__/graphic.cpython-310.pyc,, +openpyxl/drawing/__pycache__/image.cpython-310.pyc,, +openpyxl/drawing/__pycache__/line.cpython-310.pyc,, +openpyxl/drawing/__pycache__/picture.cpython-310.pyc,, +openpyxl/drawing/__pycache__/properties.cpython-310.pyc,, +openpyxl/drawing/__pycache__/relation.cpython-310.pyc,, +openpyxl/drawing/__pycache__/spreadsheet_drawing.cpython-310.pyc,, +openpyxl/drawing/__pycache__/text.cpython-310.pyc,, +openpyxl/drawing/__pycache__/xdr.cpython-310.pyc,, +openpyxl/drawing/colors.py,sha256=DmY3f50XNemqt4gqK-HhEhMpY9mXa-H_gDGAJycjwpM,15278 +openpyxl/drawing/connector.py,sha256=BLeLHvxh--N5J0cscc45kSu4gwwclrtAhkCAjroyHg0,3863 +openpyxl/drawing/drawing.py,sha256=TVgnUo510WG9mgqbBEMZ-T-S4SXga_n2AQIYvHWfYrs,2785 +openpyxl/drawing/effect.py,sha256=iws9anMvqN_0zEdo8wCwsKmvjdn5u3_OVNIdh2aJL7A,9529 +openpyxl/drawing/fill.py,sha256=_k7ONkSM4pzDlcEcQs9tgIgKmg-SJ48NvSN0ro-1ov0,12701 +openpyxl/drawing/geometry.py,sha256=jCJit07H5zNv4HyTI18mZzFO2eJNgP8KQaCJ4btrdoI,17733 +openpyxl/drawing/graphic.py,sha256=GNE7VhmHE2FxlBzAAHNJAG9EYOeP04yL4n-LHXuE7nY,5127 +openpyxl/drawing/image.py,sha256=PbIRz5136GqdLf9738twuOThoF1LPoIVpIwuj8-F9Ks,1463 +openpyxl/drawing/line.py,sha256=pYrMRa7O0OzNu56-CmcWNV5x_bmKcJp4T3vRO9pE4uM,4105 +openpyxl/drawing/picture.py,sha256=lqf4WezgKKIRjT6ZU1hsfZNyQF8OP7ngAfIlcSC9zmY,4287 +openpyxl/drawing/properties.py,sha256=UH9e7NlPuSs13oM2IC4kMJURg6oWcBbXpxeLJ-gR6Gk,4948 +openpyxl/drawing/relation.py,sha256=igkBJ_OKZ16-IcdO-asvIw2hGub-mKzvH3k4K_ShmRQ,344 +openpyxl/drawing/spreadsheet_drawing.py,sha256=e2dJHU2Sp6OgKFH-egxrd8CqbRbPjjjuoWeePNHRn9o,10762 +openpyxl/drawing/text.py,sha256=vjOY-mIIwQII4GoiKQdQE_zqof8CLFNfyttIT3R90eo,22345 +openpyxl/drawing/xdr.py,sha256=xjSzolAxpxb4CWa4rbDTEX3iBw-uBsIOQeFRkzStgEA,626 +openpyxl/formatting/__init__.py,sha256=JQyMJU_rnfUyXM_Tk4Hz_dUM1uUqbz0UlIK0i5_rEZQ,59 +openpyxl/formatting/__pycache__/__init__.cpython-310.pyc,, +openpyxl/formatting/__pycache__/formatting.cpython-310.pyc,, +openpyxl/formatting/__pycache__/rule.cpython-310.pyc,, +openpyxl/formatting/formatting.py,sha256=ZLflOZbG5sfSGe_oTv_ghOZkz-XGAstUK_0hSCuvLGs,2805 +openpyxl/formatting/rule.py,sha256=0CcEqFpu9PSyGn5lbSFg-CeM49l7oHBBvRmJwuoCHMY,9308 +openpyxl/formula/__init__.py,sha256=KFbd8A3fcY4abjI58_pHkZ_4ba3OJx8RpzTUuixD3uQ,69 +openpyxl/formula/__pycache__/__init__.cpython-310.pyc,, +openpyxl/formula/__pycache__/tokenizer.cpython-310.pyc,, +openpyxl/formula/__pycache__/translate.cpython-310.pyc,, +openpyxl/formula/tokenizer.py,sha256=LYD7rjTds1kbKo_EeL22H4QtPgvzD04uAAh_XX_XQV0,15104 +openpyxl/formula/translate.py,sha256=3yduwyIg71VUHquJFbFKYorfpfwSdC4QNXdM3HyqGug,6661 +openpyxl/packaging/__init__.py,sha256=KcNtO2zoYizOgG-iZzayZffSL1WeZR98i1Q8QYTRhfI,90 +openpyxl/packaging/__pycache__/__init__.cpython-310.pyc,, +openpyxl/packaging/__pycache__/core.cpython-310.pyc,, +openpyxl/packaging/__pycache__/extended.cpython-310.pyc,, +openpyxl/packaging/__pycache__/interface.cpython-310.pyc,, +openpyxl/packaging/__pycache__/manifest.cpython-310.pyc,, +openpyxl/packaging/__pycache__/relationship.cpython-310.pyc,, +openpyxl/packaging/__pycache__/workbook.cpython-310.pyc,, +openpyxl/packaging/core.py,sha256=RZxejREIjo3zmsh2TYYT1D1w2wmIhNCJApZHvSnVhWA,4011 +openpyxl/packaging/extended.py,sha256=xvC-SBAqkE5GMV_oInfpxIcUzLpTcSNrCcMvk_nM7CM,4755 +openpyxl/packaging/interface.py,sha256=TAzyUJG3uFLY3uf_-bxTadTw7Thlq9TXC6JNbsSMlBg,920 +openpyxl/packaging/manifest.py,sha256=DMpq2jbB0LFzCRYUgprPfS7ncorSx0hATpL7AOJcMaY,5643 +openpyxl/packaging/relationship.py,sha256=k_7v8v0VTyJiYGkO9Izp8HskfCFrKcllx17tvUb1Jyc,4356 +openpyxl/packaging/workbook.py,sha256=kfqU5svwVYz_TDf23UM5c2CBvOUaBd08Khgst2xrlC4,7024 +openpyxl/pivot/__init__.py,sha256=vy0Tqjyg0JU0hFQkaKwsNNXId2uWxw94S4fNgk1JwmY,35 +openpyxl/pivot/__pycache__/__init__.cpython-310.pyc,, +openpyxl/pivot/__pycache__/cache.cpython-310.pyc,, +openpyxl/pivot/__pycache__/fields.cpython-310.pyc,, +openpyxl/pivot/__pycache__/record.cpython-310.pyc,, +openpyxl/pivot/__pycache__/table.cpython-310.pyc,, +openpyxl/pivot/cache.py,sha256=_1-6g7SOPTOBHq7wJHMBkvKMuVg9YyNbZ5nIapjdOGs,30587 +openpyxl/pivot/fields.py,sha256=yd2Iz2EIti0nJkMgSAhft0l7XHuNxnazvavpTStZrZY,6984 +openpyxl/pivot/record.py,sha256=y0s27ZvcvetQFsG3BpLFdUniC2cuQc09NzOY4zoagTY,2687 +openpyxl/pivot/table.py,sha256=cmMEh5s7zIcBWICWwuUd5E9FNAslX6V8Q2G2H-by5eg,37786 +openpyxl/reader/__init__.py,sha256=vy0Tqjyg0JU0hFQkaKwsNNXId2uWxw94S4fNgk1JwmY,35 +openpyxl/reader/__pycache__/__init__.cpython-310.pyc,, +openpyxl/reader/__pycache__/drawings.cpython-310.pyc,, +openpyxl/reader/__pycache__/excel.cpython-310.pyc,, +openpyxl/reader/__pycache__/strings.cpython-310.pyc,, +openpyxl/reader/__pycache__/workbook.cpython-310.pyc,, +openpyxl/reader/drawings.py,sha256=x0P-Hdauvp5pOfWg-PxTqNKnoVwE1m-ssolF99rA_fo,2052 +openpyxl/reader/excel.py,sha256=w1LMHWMmpbNhC73fc_LNFjfih8T700kKYLB85Y-FkNI,10978 +openpyxl/reader/strings.py,sha256=Vr23VuVcmGASy1m2Zv_q9WXafVQmkGUvh8vGTtbN30I,565 +openpyxl/reader/workbook.py,sha256=GZZLNq3ttKU-zHOcEApgEscMRIdFY928ndXyhBw5PRw,3921 +openpyxl/styles/__init__.py,sha256=6vl70dr1Z0LPjhf5GxPXXGC5625kSLrzwlHILYqs3Lo,363 +openpyxl/styles/__pycache__/__init__.cpython-310.pyc,, +openpyxl/styles/__pycache__/alignment.cpython-310.pyc,, +openpyxl/styles/__pycache__/borders.cpython-310.pyc,, +openpyxl/styles/__pycache__/builtins.cpython-310.pyc,, +openpyxl/styles/__pycache__/cell_style.cpython-310.pyc,, +openpyxl/styles/__pycache__/colors.cpython-310.pyc,, +openpyxl/styles/__pycache__/differential.cpython-310.pyc,, +openpyxl/styles/__pycache__/fills.cpython-310.pyc,, +openpyxl/styles/__pycache__/fonts.cpython-310.pyc,, +openpyxl/styles/__pycache__/named_styles.cpython-310.pyc,, +openpyxl/styles/__pycache__/numbers.cpython-310.pyc,, +openpyxl/styles/__pycache__/protection.cpython-310.pyc,, +openpyxl/styles/__pycache__/proxy.cpython-310.pyc,, +openpyxl/styles/__pycache__/styleable.cpython-310.pyc,, +openpyxl/styles/__pycache__/stylesheet.cpython-310.pyc,, +openpyxl/styles/__pycache__/table.cpython-310.pyc,, +openpyxl/styles/alignment.py,sha256=UcDakgcFWDwOlLtcZNC_3HLdBWzRsG7SmkSQA4u9KeY,2512 +openpyxl/styles/borders.py,sha256=dqzcD9b-l_HidrhiP0B6JmkXANOBv0Y_wegqKrRQkSo,3594 +openpyxl/styles/builtins.py,sha256=tvDp4fl300bR3cwZNEykgEUI8b4n-LcjdvXXwrUtqNU,31182 +openpyxl/styles/cell_style.py,sha256=ErOjpxrayeOPNl9NWCyF9h2eH4Qj2cstaUIKOs6Bg24,5304 +openpyxl/styles/colors.py,sha256=nHtEQyXtdvIyw1ktoGy3Du0qNdqqkPdvBdNJjOaYzSM,4653 +openpyxl/styles/differential.py,sha256=bB7H_6HraiFkK-Y1jNYkjZkTAlyA74OvLPa4f8PFyM4,2267 +openpyxl/styles/fills.py,sha256=1xVb5ES4IoHowYnoWMIwvRJTWFvOW3PvHkZlwm73RDY,6443 +openpyxl/styles/fonts.py,sha256=ke5GmJ3S-AKiUZFJzXXFBEugRmBYTZhLbdyoyOk96Rw,3525 +openpyxl/styles/named_styles.py,sha256=y62QW2bkzKU4SOOkS2wmpV6_QNwjpVow5mtJf9Olz5w,7424 +openpyxl/styles/numbers.py,sha256=iJjorVr1VFPH95d4UQpjyrwa8EkLIKqDMyw6oA8SwLY,5120 +openpyxl/styles/protection.py,sha256=VCR5HcpppNmTAqoUbIa7rQwdb0dp8517wWRVQXASBa0,394 +openpyxl/styles/proxy.py,sha256=1b351V-qVfG89G6KRhJpxByOZCq-MXLNShqKUi3LCco,1456 +openpyxl/styles/styleable.py,sha256=tJPWC8g8i0CEH5HHU00ArDetakYw6IrzsM3SOlNYXxI,4565 +openpyxl/styles/stylesheet.py,sha256=cvSuL72cnf2GRiIS5Fiky9fvoylx_x5PI6aXFl_7tLo,8535 +openpyxl/styles/table.py,sha256=Z9N_EpJ7ooDbIlWS0j9oYmSoL0R6NqhZc9irS58Z0LY,2801 +openpyxl/utils/__init__.py,sha256=JihKdC-kPyYYDj57UNbEXXn_hj707nWkWMX0SliR1H4,324 +openpyxl/utils/__pycache__/__init__.cpython-310.pyc,, +openpyxl/utils/__pycache__/bound_dictionary.cpython-310.pyc,, +openpyxl/utils/__pycache__/cell.cpython-310.pyc,, +openpyxl/utils/__pycache__/dataframe.cpython-310.pyc,, +openpyxl/utils/__pycache__/datetime.cpython-310.pyc,, +openpyxl/utils/__pycache__/escape.cpython-310.pyc,, +openpyxl/utils/__pycache__/exceptions.cpython-310.pyc,, +openpyxl/utils/__pycache__/formulas.cpython-310.pyc,, +openpyxl/utils/__pycache__/indexed_list.cpython-310.pyc,, +openpyxl/utils/__pycache__/inference.cpython-310.pyc,, +openpyxl/utils/__pycache__/protection.cpython-310.pyc,, +openpyxl/utils/__pycache__/units.cpython-310.pyc,, +openpyxl/utils/bound_dictionary.py,sha256=S8A5avfYgj8C9qY02VqPVPlao65n5qA4LZyww62LxNI,759 +openpyxl/utils/cell.py,sha256=0sDS5epgU4k9Hr-Vj2RUha9A6dpodiJ4Wd39KsZNPhI,6535 +openpyxl/utils/dataframe.py,sha256=4DnrL0HZYK9rWdwj1tQO00upaU108vfFjIjH26XGjhU,2596 +openpyxl/utils/datetime.py,sha256=Bf0YnwbYG_uZaTacGOY5LGRayX9MWPS3wGtz7Fuy9is,4529 +openpyxl/utils/escape.py,sha256=Bdfcwl5IzIqMD3r7BHxYLZ9azAwtN43pDRNjLzpWVzI,790 +openpyxl/utils/exceptions.py,sha256=-iVaVgQk_Xq65ItCh4aIJCQ6oLHIlTMR7KjcrVv8kZw,889 +openpyxl/utils/formulas.py,sha256=06fJ7kMfYD_bUemPc8MTyauJdh3UsYZVmnk2MDBN1Ag,3733 +openpyxl/utils/indexed_list.py,sha256=8XDEm8hAbp_XqpuPCeSBMBZPVaMw1q3irC4-A-sVLzc,1257 +openpyxl/utils/inference.py,sha256=iBefUcthoRSIO-htrh7fGr_P1_Lx0GU9pWlkIydFs08,1582 +openpyxl/utils/protection.py,sha256=sGpCIoMOW-V2f91x7R5uCg1P4lscvSJgS-E_Y_s06FM,830 +openpyxl/utils/units.py,sha256=tXAag0lQFkhWtAOARrrNBAJ69WTBNl48QrPR5VuMBrc,2674 +openpyxl/workbook/__init__.py,sha256=3YwXULCwZZlhWImVB_07V-IjyrHgziAgiitErnmP6nI,68 +openpyxl/workbook/__pycache__/__init__.cpython-310.pyc,, +openpyxl/workbook/__pycache__/_writer.cpython-310.pyc,, +openpyxl/workbook/__pycache__/child.cpython-310.pyc,, +openpyxl/workbook/__pycache__/defined_name.cpython-310.pyc,, +openpyxl/workbook/__pycache__/external_reference.cpython-310.pyc,, +openpyxl/workbook/__pycache__/function_group.cpython-310.pyc,, +openpyxl/workbook/__pycache__/properties.cpython-310.pyc,, +openpyxl/workbook/__pycache__/protection.cpython-310.pyc,, +openpyxl/workbook/__pycache__/smart_tags.cpython-310.pyc,, +openpyxl/workbook/__pycache__/views.cpython-310.pyc,, +openpyxl/workbook/__pycache__/web.cpython-310.pyc,, +openpyxl/workbook/__pycache__/workbook.cpython-310.pyc,, +openpyxl/workbook/_writer.py,sha256=jz0Ljx8Vl73sb0FYwub2pF7k9Zwk95k1Um15Jk4hQAg,6537 +openpyxl/workbook/child.py,sha256=hLBNKhlBF19O3ZnYuCwdmDI0nb_1P5udbDqkgkIQt_s,4060 +openpyxl/workbook/defined_name.py,sha256=84mftP70VemW4t0q1gMAd8jqpc_DX8rt0jcusLk1Z8I,7444 +openpyxl/workbook/external_link/__init__.py,sha256=p42iTw7McXeftIWoyIBsG1_FkZQjUcJ_iL-dYkKyglw,71 +openpyxl/workbook/external_link/__pycache__/__init__.cpython-310.pyc,, +openpyxl/workbook/external_link/__pycache__/external.cpython-310.pyc,, +openpyxl/workbook/external_link/external.py,sha256=Hb98DAk1BgyyLdYXqInjz9uYhX6VywKpHC9qotYHJPg,4555 +openpyxl/workbook/external_reference.py,sha256=swiOV5kB_rUAWVZwrZA7yFGS5FtaqqjBknJNntJCICM,348 +openpyxl/workbook/function_group.py,sha256=6dxHCm08V3A_XbhufMrSMtfN2wxetnK9H5gztR7mDu0,803 +openpyxl/workbook/properties.py,sha256=QXjwXTdN0c3lOs1Cjf-B8U9jvlmam6ds1yva6O2t5hk,5261 +openpyxl/workbook/protection.py,sha256=N_-4oJ9ST1DY5y1QUSPJj3rD9wrcBJxsJ-X8Gmwojb0,6031 +openpyxl/workbook/smart_tags.py,sha256=svv9SAsEpJucdtSLDr8vIm8ykGAfCZfuH64WaqZFfu0,1181 +openpyxl/workbook/views.py,sha256=LpjPsFmOA7Xz97bXe7L9DA63gsfQDzgwNBQZOyXlKyc,5214 +openpyxl/workbook/web.py,sha256=PhcttYhzh35m8jkwEkw5Q0wHnbJGjD2yvd_H2YldN2s,2642 +openpyxl/workbook/workbook.py,sha256=u0CU87LakDscmiazQQPfFpvBQn412Tc-8KMW_tjJi4c,13948 +openpyxl/worksheet/__init__.py,sha256=vy0Tqjyg0JU0hFQkaKwsNNXId2uWxw94S4fNgk1JwmY,35 +openpyxl/worksheet/__pycache__/__init__.cpython-310.pyc,, +openpyxl/worksheet/__pycache__/_read_only.cpython-310.pyc,, +openpyxl/worksheet/__pycache__/_reader.cpython-310.pyc,, +openpyxl/worksheet/__pycache__/_write_only.cpython-310.pyc,, +openpyxl/worksheet/__pycache__/_writer.cpython-310.pyc,, +openpyxl/worksheet/__pycache__/cell_range.cpython-310.pyc,, +openpyxl/worksheet/__pycache__/cell_watch.cpython-310.pyc,, +openpyxl/worksheet/__pycache__/controls.cpython-310.pyc,, +openpyxl/worksheet/__pycache__/copier.cpython-310.pyc,, +openpyxl/worksheet/__pycache__/custom.cpython-310.pyc,, +openpyxl/worksheet/__pycache__/datavalidation.cpython-310.pyc,, +openpyxl/worksheet/__pycache__/dimensions.cpython-310.pyc,, +openpyxl/worksheet/__pycache__/drawing.cpython-310.pyc,, +openpyxl/worksheet/__pycache__/errors.cpython-310.pyc,, +openpyxl/worksheet/__pycache__/filters.cpython-310.pyc,, +openpyxl/worksheet/__pycache__/header_footer.cpython-310.pyc,, +openpyxl/worksheet/__pycache__/hyperlink.cpython-310.pyc,, +openpyxl/worksheet/__pycache__/merge.cpython-310.pyc,, +openpyxl/worksheet/__pycache__/ole.cpython-310.pyc,, +openpyxl/worksheet/__pycache__/page.cpython-310.pyc,, +openpyxl/worksheet/__pycache__/pagebreak.cpython-310.pyc,, +openpyxl/worksheet/__pycache__/picture.cpython-310.pyc,, +openpyxl/worksheet/__pycache__/properties.cpython-310.pyc,, +openpyxl/worksheet/__pycache__/protection.cpython-310.pyc,, +openpyxl/worksheet/__pycache__/related.cpython-310.pyc,, +openpyxl/worksheet/__pycache__/scenario.cpython-310.pyc,, +openpyxl/worksheet/__pycache__/smart_tag.cpython-310.pyc,, +openpyxl/worksheet/__pycache__/table.cpython-310.pyc,, +openpyxl/worksheet/__pycache__/views.cpython-310.pyc,, +openpyxl/worksheet/__pycache__/worksheet.cpython-310.pyc,, +openpyxl/worksheet/_read_only.py,sha256=N0OBX8AWDtUXGn04B7KvdBKVAq1WeU5WtXSi9cwm-MU,5439 +openpyxl/worksheet/_reader.py,sha256=3fwxRovWHrz5YvQTu6MyDMOFqKJV9Qdu6q0l-yhNZBw,15856 +openpyxl/worksheet/_write_only.py,sha256=tSJQmFedR2J8n8M4cCL74a2DwaTLhFw7-cAfQM7_jMw,4256 +openpyxl/worksheet/_writer.py,sha256=ucDCufJ6SpFX58FebvGXarkVWS2RRX9_JUSIwjEYGik,10318 +openpyxl/worksheet/cell_range.py,sha256=JAmAMh9SDh-7j-0WP3uc-lgtyg3AKiamadOyqBD34HM,14642 +openpyxl/worksheet/cell_watch.py,sha256=LdxGcTmXbZ4sxm6inasFgZPld1ijdL5_ODSUvvz13DU,608 +openpyxl/worksheet/controls.py,sha256=N3cx3jYJ5HQHxYTT95HC9Bk77CG4zAvp1jZlKWRT8gE,2735 +openpyxl/worksheet/copier.py,sha256=yy3tV3Fvafb6KhwbIE7qKM-8jwRLADv_yfgf6ubJCZU,2327 +openpyxl/worksheet/custom.py,sha256=CRlQ98GwqqKmEDkv8gPUCa0ApNM2Vz-BLs_-RMu3jLA,639 +openpyxl/worksheet/datavalidation.py,sha256=PZo_Ojre6BAndAltZSyhHahDikaptLgLWCQHYzgJEn8,6136 +openpyxl/worksheet/dimensions.py,sha256=J9PiC1Uj1VNJoJqgph4BTHB_HMwfuKyJbN38YC_olwc,8877 +openpyxl/worksheet/drawing.py,sha256=OxtZKz-iGEdBe8cAIiNE0_dJmtxy740-ilvK4eshcW8,275 +openpyxl/worksheet/errors.py,sha256=KkFC4bnckvCp74XsVXA7JUCi4MIimEFu3uAddcQpjo0,2435 +openpyxl/worksheet/filters.py,sha256=DOcn_Eiyvf38X4FBEups5aunWmyZzttduAVI3mnL-Zg,10854 +openpyxl/worksheet/header_footer.py,sha256=AQ7BLJ_xNQ1FZ5E7ObZ4vAxHiGEacajqNss6jVhO6y4,7886 +openpyxl/worksheet/hyperlink.py,sha256=j4D98tw6TDEB-KsYRxlnCevaQg-zcNYDsCL8eRX11KY,1391 +openpyxl/worksheet/merge.py,sha256=LEV-etpiaD8ROQY-ppfi9DmSyIbkqIt4f5ssley-O_k,4140 +openpyxl/worksheet/ole.py,sha256=VDINt__P9yPRcwQnFhGSuuSdaFZXc5OrNXS2aXCX_Wc,3530 +openpyxl/worksheet/page.py,sha256=1uZMPI_218SXoR_41SfS2_mra7YMq2-VACupPUl8E4M,4920 +openpyxl/worksheet/pagebreak.py,sha256=SmJWoXhYn5ajLSVKT3dcwP4dFET9cbXNgEBGOeQohGE,1811 +openpyxl/worksheet/picture.py,sha256=72TctCxzk2JU8uFfjiEbTBufEe5eQxIieSPBRhU6m1Q,185 +openpyxl/worksheet/properties.py,sha256=xGM6ULgtLMCGxzPOGM4V0kOsvWEFYG9lLsfZYgF6IYk,3087 +openpyxl/worksheet/protection.py,sha256=kKanq7Tkmjw37nwm1q0VY9B579zLKWF2sFRLu849vX4,3787 +openpyxl/worksheet/related.py,sha256=dZcMFcmW8jXi6dTAomvZPLODjj9mPDVZZKz22TMfQts,348 +openpyxl/worksheet/scenario.py,sha256=ny0BiVQhzUKxi-ubTkbTF6v4AWGy7ZnV2jE3Rt8_um8,2401 +openpyxl/worksheet/smart_tag.py,sha256=nLbt04IqeJllk7TmNS1eTNdb7On5jMf3llfyy3otDSk,1608 +openpyxl/worksheet/table.py,sha256=HJgFVvv57qamZmlZf53qgZ3QfKrxuwSR-7EbUcr-ZNE,11716 +openpyxl/worksheet/views.py,sha256=jeuym07ghdbNft8P3eXZ3q1b0YWl72bMQD58v1cFWxc,4632 +openpyxl/worksheet/worksheet.py,sha256=qugIcPJtyLu_fqIhdIbzVi_JpRN3TaHn4Yxi1RisQZw,27473 +openpyxl/writer/__init__.py,sha256=vy0Tqjyg0JU0hFQkaKwsNNXId2uWxw94S4fNgk1JwmY,35 +openpyxl/writer/__pycache__/__init__.cpython-310.pyc,, +openpyxl/writer/__pycache__/excel.cpython-310.pyc,, +openpyxl/writer/__pycache__/theme.cpython-310.pyc,, +openpyxl/writer/excel.py,sha256=vsSn-sCmOxUCzPrH0PX6MBTnT6vMC5v7htGBbv71E3U,9854 +openpyxl/writer/theme.py,sha256=eRW0tETAWL8HyE6zyt6lLPnzWX9ul5JPXX3VHkZnw84,10320 +openpyxl/xml/__init__.py,sha256=f28-m53hDspYqdZxg2h-cMBOCjBCH7tzcsMLDZ7ax3c,1016 +openpyxl/xml/__pycache__/__init__.cpython-310.pyc,, +openpyxl/xml/__pycache__/constants.cpython-310.pyc,, +openpyxl/xml/__pycache__/functions.cpython-310.pyc,, +openpyxl/xml/constants.py,sha256=EmxmaCPuoGbzKAQovJXcjq6QHDUhAxaU1bt-Lf7mCko,4546 +openpyxl/xml/functions.py,sha256=EcldyNqNvSjLgJA7qpZ7Xp2fChlMr4H-zq0pIu4XA4Q,1929 diff --git a/.venv/Lib/site-packages/openpyxl-3.0.10.dist-info/REQUESTED b/.venv/Lib/site-packages/openpyxl-3.0.10.dist-info/REQUESTED new file mode 100644 index 00000000..e69de29b diff --git a/.venv/Lib/site-packages/openpyxl-3.0.10.dist-info/WHEEL b/.venv/Lib/site-packages/openpyxl-3.0.10.dist-info/WHEEL new file mode 100644 index 00000000..b733a60d --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl-3.0.10.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.0) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/.venv/Lib/site-packages/openpyxl-3.0.10.dist-info/top_level.txt b/.venv/Lib/site-packages/openpyxl-3.0.10.dist-info/top_level.txt new file mode 100644 index 00000000..794cc3d3 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl-3.0.10.dist-info/top_level.txt @@ -0,0 +1 @@ +openpyxl diff --git a/.venv/Lib/site-packages/openpyxl/__init__.py b/.venv/Lib/site-packages/openpyxl/__init__.py new file mode 100644 index 00000000..d8e31f45 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/__init__.py @@ -0,0 +1,18 @@ +# Copyright (c) 2010-2022 openpyxl + + +from openpyxl.compat.numbers import NUMPY +from openpyxl.xml import DEFUSEDXML, LXML +from openpyxl.workbook import Workbook +from openpyxl.reader.excel import load_workbook as open +from openpyxl.reader.excel import load_workbook +import openpyxl._constants as constants + +# Expose constants especially the version number + +__author__ = constants.__author__ +__author_email__ = constants.__author_email__ +__license__ = constants.__license__ +__maintainer_email__ = constants.__maintainer_email__ +__url__ = constants.__url__ +__version__ = constants.__version__ diff --git a/.venv/Lib/site-packages/openpyxl/_constants.py b/.venv/Lib/site-packages/openpyxl/_constants.py new file mode 100644 index 00000000..53e9bc99 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/_constants.py @@ -0,0 +1,13 @@ +# Copyright (c) 2010-2022 openpyxl + +""" +Package metadata +""" + +__author__ = "See AUTHORS" +__author_email__ = "charlie.clark@clark-consulting.eu" +__license__ = "MIT" +__maintainer_email__ = "openpyxl-users@googlegroups.com" +__url__ = "https://openpyxl.readthedocs.io" +__version__ = "3.0.10" +__python__ = "3.6" diff --git a/.venv/Lib/site-packages/openpyxl/cell/__init__.py b/.venv/Lib/site-packages/openpyxl/cell/__init__.py new file mode 100644 index 00000000..97e04dcd --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/cell/__init__.py @@ -0,0 +1,4 @@ +# Copyright (c) 2010-2022 openpyxl + +from .cell import Cell, WriteOnlyCell, MergedCell +from .read_only import ReadOnlyCell diff --git a/.venv/Lib/site-packages/openpyxl/cell/_writer.py b/.venv/Lib/site-packages/openpyxl/cell/_writer.py new file mode 100644 index 00000000..7208c138 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/cell/_writer.py @@ -0,0 +1,108 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.compat import safe_string +from openpyxl.xml.functions import Element, SubElement, whitespace, XML_NS, REL_NS +from openpyxl import LXML +from openpyxl.utils.datetime import to_excel, to_ISO8601 +from datetime import timedelta + + +def _set_attributes(cell, styled=None): + """ + Set coordinate and datatype + """ + coordinate = cell.coordinate + attrs = {'r': coordinate} + if styled: + attrs['s'] = f"{cell.style_id}" + + if cell.data_type == "s": + attrs['t'] = "inlineStr" + elif cell.data_type != 'f': + attrs['t'] = cell.data_type + + value = cell._value + + if cell.data_type == "d": + if hasattr(value, "tzinfo") and value.tzinfo is not None: + raise TypeError("Excel does not support timezones in datetimes. " + "The tzinfo in the datetime/time object must be set to None.") + + if cell.parent.parent.iso_dates and not isinstance(value, timedelta): + value = to_ISO8601(value) + else: + attrs['t'] = "n" + value = to_excel(value, cell.parent.parent.epoch) + + if cell.hyperlink: + cell.parent._hyperlinks.append(cell.hyperlink) + + return value, attrs + + +def etree_write_cell(xf, worksheet, cell, styled=None): + + value, attributes = _set_attributes(cell, styled) + + el = Element("c", attributes) + if value is None or value == "": + xf.write(el) + return + + if cell.data_type == 'f': + shared_formula = worksheet.formula_attributes.get(cell.coordinate, {}) + formula = SubElement(el, 'f', shared_formula) + if value is not None: + formula.text = value[1:] + value = None + + if cell.data_type == 's': + inline_string = SubElement(el, 'is') + text = SubElement(inline_string, 't') + text.text = value + whitespace(text) + + + else: + cell_content = SubElement(el, 'v') + if value is not None: + cell_content.text = safe_string(value) + + xf.write(el) + + +def lxml_write_cell(xf, worksheet, cell, styled=False): + value, attributes = _set_attributes(cell, styled) + + if value == '' or value is None: + with xf.element("c", attributes): + return + + with xf.element('c', attributes): + if cell.data_type == 'f': + shared_formula = worksheet.formula_attributes.get(cell.coordinate, {}) + with xf.element('f', shared_formula): + if value is not None: + xf.write(value[1:]) + value = None + + if cell.data_type == 's': + with xf.element("is"): + attrs = {} + if value != value.strip(): + attrs["{%s}space" % XML_NS] = "preserve" + el = Element("t", attrs) # lxml can't handle xml-ns + el.text = value + xf.write(el) + #with xf.element("t", attrs): + #xf.write(value) + else: + with xf.element("v"): + if value is not None: + xf.write(safe_string(value)) + + +if LXML: + write_cell = lxml_write_cell +else: + write_cell = etree_write_cell diff --git a/.venv/Lib/site-packages/openpyxl/cell/cell.py b/.venv/Lib/site-packages/openpyxl/cell/cell.py new file mode 100644 index 00000000..1bfbdaca --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/cell/cell.py @@ -0,0 +1,329 @@ +# Copyright (c) 2010-2022 openpyxl + +"""Manage individual cells in a spreadsheet. + +The Cell class is required to know its value and type, display options, +and any other features of an Excel cell. Utilities for referencing +cells using Excel's 'A1' column/row nomenclature are also provided. + +""" + +__docformat__ = "restructuredtext en" + +# Python stdlib imports +from copy import copy +import datetime +import re + + +from openpyxl.compat import ( + NUMERIC_TYPES, + deprecated, +) + +from openpyxl.utils.exceptions import IllegalCharacterError + +from openpyxl.utils import get_column_letter +from openpyxl.styles import numbers, is_date_format +from openpyxl.styles.styleable import StyleableObject +from openpyxl.worksheet.hyperlink import Hyperlink + +# constants + +TIME_TYPES = (datetime.datetime, datetime.date, datetime.time, datetime.timedelta) +TIME_FORMATS = { + datetime.datetime:numbers.FORMAT_DATE_DATETIME, + datetime.date:numbers.FORMAT_DATE_YYYYMMDD2, + datetime.time:numbers.FORMAT_DATE_TIME6, + datetime.timedelta:numbers.FORMAT_DATE_TIMEDELTA, + } + +STRING_TYPES = (str, bytes) +KNOWN_TYPES = NUMERIC_TYPES + TIME_TYPES + STRING_TYPES + (bool, type(None)) + +ILLEGAL_CHARACTERS_RE = re.compile(r'[\000-\010]|[\013-\014]|[\016-\037]') +ERROR_CODES = ('#NULL!', '#DIV/0!', '#VALUE!', '#REF!', '#NAME?', '#NUM!', + '#N/A') + +TYPE_STRING = 's' +TYPE_FORMULA = 'f' +TYPE_NUMERIC = 'n' +TYPE_BOOL = 'b' +TYPE_NULL = 'n' +TYPE_INLINE = 'inlineStr' +TYPE_ERROR = 'e' +TYPE_FORMULA_CACHE_STRING = 'str' + +VALID_TYPES = (TYPE_STRING, TYPE_FORMULA, TYPE_NUMERIC, TYPE_BOOL, + TYPE_NULL, TYPE_INLINE, TYPE_ERROR, TYPE_FORMULA_CACHE_STRING) + + +_TYPES = {int:'n', float:'n', str:'s', bool:'b'} + + +def get_type(t, value): + if isinstance(value, NUMERIC_TYPES): + dt = 'n' + elif isinstance(value, STRING_TYPES): + dt = 's' + elif isinstance(value, TIME_TYPES): + dt = 'd' + else: + return + _TYPES[t] = dt + return dt + + +def get_time_format(t): + value = TIME_FORMATS.get(t) + if value: + return value + for base in t.mro()[1:]: + value = TIME_FORMATS.get(base) + if value: + TIME_FORMATS[t] = value + return value + raise ValueError("Could not get time format for {0!r}".format(value)) + + +class Cell(StyleableObject): + """Describes cell associated properties. + + Properties of interest include style, type, value, and address. + + """ + __slots__ = ( + 'row', + 'column', + '_value', + 'data_type', + 'parent', + '_hyperlink', + '_comment', + ) + + def __init__(self, worksheet, row=None, column=None, value=None, style_array=None): + super(Cell, self).__init__(worksheet, style_array) + self.row = row + """Row number of this cell (1-based)""" + self.column = column + """Column number of this cell (1-based)""" + # _value is the stored value, while value is the displayed value + self._value = None + self._hyperlink = None + self.data_type = 'n' + if value is not None: + self.value = value + self._comment = None + + + @property + def coordinate(self): + """This cell's coordinate (ex. 'A5')""" + col = get_column_letter(self.column) + return f"{col}{self.row}" + + + @property + def col_idx(self): + """The numerical index of the column""" + return self.column + + + @property + def column_letter(self): + return get_column_letter(self.column) + + + @property + def encoding(self): + return self.parent.encoding + + @property + def base_date(self): + return self.parent.parent.epoch + + + def __repr__(self): + return "".format(self.parent.title, self.coordinate) + + def check_string(self, value): + """Check string coding, length, and line break character""" + if value is None: + return + # convert to str string + if not isinstance(value, str): + value = str(value, self.encoding) + value = str(value) + # string must never be longer than 32,767 characters + # truncate if necessary + value = value[:32767] + if next(ILLEGAL_CHARACTERS_RE.finditer(value), None): + raise IllegalCharacterError + return value + + def check_error(self, value): + """Tries to convert Error" else N/A""" + try: + return str(value) + except UnicodeDecodeError: + return u'#N/A' + + + def _bind_value(self, value): + """Given a value, infer the correct data type""" + + self.data_type = "n" + t = type(value) + try: + dt = _TYPES[t] + except KeyError: + dt = get_type(t, value) + + if dt is None and value is not None: + raise ValueError("Cannot convert {0!r} to Excel".format(value)) + + if dt: + self.data_type = dt + + if dt == 'd': + if not is_date_format(self.number_format): + self.number_format = get_time_format(t) + + elif dt == "s": + value = self.check_string(value) + if len(value) > 1 and value.startswith("="): + self.data_type = 'f' + elif value in ERROR_CODES: + self.data_type = 'e' + + self._value = value + + + @property + def value(self): + """Get or set the value held in the cell. + + :type: depends on the value (string, float, int or + :class:`datetime.datetime`) + """ + return self._value + + @value.setter + def value(self, value): + """Set the value and infer type and display options.""" + self._bind_value(value) + + @property + def internal_value(self): + """Always returns the value for excel.""" + return self._value + + @property + def hyperlink(self): + """Return the hyperlink target or an empty string""" + return self._hyperlink + + + @hyperlink.setter + def hyperlink(self, val): + """Set value and display for hyperlinks in a cell. + Automatically sets the `value` of the cell with link text, + but you can modify it afterwards by setting the `value` + property, and the hyperlink will remain. + Hyperlink is removed if set to ``None``.""" + if val is None: + self._hyperlink = None + else: + if not isinstance(val, Hyperlink): + val = Hyperlink(ref="", target=val) + val.ref = self.coordinate + self._hyperlink = val + if self._value is None: + self.value = val.target or val.location + + + @property + def is_date(self): + """True if the value is formatted as a date + + :type: bool + """ + return self.data_type == 'd' or ( + self.data_type == 'n' and is_date_format(self.number_format) + ) + + + def offset(self, row=0, column=0): + """Returns a cell location relative to this cell. + + :param row: number of rows to offset + :type row: int + + :param column: number of columns to offset + :type column: int + + :rtype: :class:`openpyxl.cell.Cell` + """ + offset_column = self.col_idx + column + offset_row = self.row + row + return self.parent.cell(column=offset_column, row=offset_row) + + + @property + def comment(self): + """ Returns the comment associated with this cell + + :type: :class:`openpyxl.comments.Comment` + """ + return self._comment + + + @comment.setter + def comment(self, value): + """ + Assign a comment to a cell + """ + + if value is not None: + if value.parent: + value = copy(value) + value.bind(self) + elif value is None and self._comment: + self._comment.unbind() + self._comment = value + + +class MergedCell(StyleableObject): + + """ + Describes the properties of a cell in a merged cell and helps to + display the borders of the merged cell. + + The value of a MergedCell is always None. + """ + + __slots__ = ('row', 'column') + + _value = None + data_type = "n" + comment = None + hyperlink = None + + + def __init__(self, worksheet, row=None, column=None): + super(MergedCell, self).__init__(worksheet) + self.row = row + self.column = column + + + def __repr__(self): + return "".format(self.parent.title, self.coordinate) + + coordinate = Cell.coordinate + _comment = comment + value = _value + + +def WriteOnlyCell(ws=None, value=None): + return Cell(worksheet=ws, column=1, row=1, value=value) diff --git a/.venv/Lib/site-packages/openpyxl/cell/read_only.py b/.venv/Lib/site-packages/openpyxl/cell/read_only.py new file mode 100644 index 00000000..52635fa9 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/cell/read_only.py @@ -0,0 +1,136 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.cell import Cell +from openpyxl.utils import get_column_letter +from openpyxl.utils.datetime import from_excel +from openpyxl.styles import is_date_format +from openpyxl.styles.numbers import BUILTIN_FORMATS, BUILTIN_FORMATS_MAX_SIZE + + +class ReadOnlyCell(object): + + __slots__ = ('parent', 'row', 'column', '_value', 'data_type', '_style_id') + + def __init__(self, sheet, row, column, value, data_type='n', style_id=0): + self.parent = sheet + self._value = None + self.row = row + self.column = column + self.data_type = data_type + self.value = value + self._style_id = style_id + + + def __eq__(self, other): + for a in self.__slots__: + if getattr(self, a) != getattr(other, a): + return + return True + + def __ne__(self, other): + return not self.__eq__(other) + + + def __repr__(self): + return "".format(self.parent.title, self.coordinate) + + + @property + def coordinate(self): + column = get_column_letter(self.column) + return "{1}{0}".format(self.row, column) + + + @property + def coordinate(self): + return Cell.coordinate.__get__(self) + + + @property + def column_letter(self): + return Cell.column_letter.__get__(self) + + + @property + def style_array(self): + return self.parent.parent._cell_styles[self._style_id] + + + @property + def has_style(self): + return self._style_id != 0 + + + @property + def number_format(self): + _id = self.style_array.numFmtId + if _id < BUILTIN_FORMATS_MAX_SIZE: + return BUILTIN_FORMATS.get(_id, "General") + else: + return self.parent.parent._number_formats[ + _id - BUILTIN_FORMATS_MAX_SIZE] + + @property + def font(self): + _id = self.style_array.fontId + return self.parent.parent._fonts[_id] + + @property + def fill(self): + _id = self.style_array.fillId + return self.parent.parent._fills[_id] + + @property + def border(self): + _id = self.style_array.borderId + return self.parent.parent._borders[_id] + + @property + def alignment(self): + _id = self.style_array.alignmentId + return self.parent.parent._alignments[_id] + + @property + def protection(self): + _id = self.style_array.protectionId + return self.parent.parent._protections[_id] + + + @property + def is_date(self): + return Cell.is_date.__get__(self) + + + @property + def internal_value(self): + return self._value + + @property + def value(self): + return self._value + + @value.setter + def value(self, value): + if self._value is not None: + raise AttributeError("Cell is read only") + self._value = value + + +class EmptyCell(object): + + __slots__ = () + + value = None + is_date = False + font = None + border = None + fill = None + number_format = None + alignment = None + data_type = 'n' + + + def __repr__(self): + return "" + +EMPTY_CELL = EmptyCell() diff --git a/.venv/Lib/site-packages/openpyxl/cell/text.py b/.venv/Lib/site-packages/openpyxl/cell/text.py new file mode 100644 index 00000000..16fb825f --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/cell/text.py @@ -0,0 +1,184 @@ +# Copyright (c) 2010-2022 openpyxl + +""" +Richtext definition +""" + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Alias, + Typed, + Integer, + Set, + NoneSet, + Bool, + String, + Sequence, +) +from openpyxl.descriptors.nested import ( + NestedBool, + NestedInteger, + NestedString, + NestedText, +) +from openpyxl.styles.fonts import Font + + +class PhoneticProperties(Serialisable): + + tagname = "phoneticPr" + + fontId = Integer() + type = NoneSet(values=(['halfwidthKatakana', 'fullwidthKatakana', + 'Hiragana', 'noConversion'])) + alignment = NoneSet(values=(['noControl', 'left', 'center', 'distributed'])) + + def __init__(self, + fontId=None, + type=None, + alignment=None, + ): + self.fontId = fontId + self.type = type + self.alignment = alignment + + +class PhoneticText(Serialisable): + + tagname = "rPh" + + sb = Integer() + eb = Integer() + t = NestedText(expected_type=str) + text = Alias('t') + + def __init__(self, + sb=None, + eb=None, + t=None, + ): + self.sb = sb + self.eb = eb + self.t = t + + +class InlineFont(Font): + + """ + Font for inline text because, yes what you need are different objects with the same elements but different constraints. + """ + + tagname = "RPrElt" + + rFont = NestedString(allow_none=True) + charset = Font.charset + family = Font.family + b =Font.b + i = Font.i + strike = Font.strike + outline = Font.outline + shadow = Font.shadow + condense = Font.condense + extend = Font.extend + color = Font.color + sz = Font.sz + u = Font.u + vertAlign = Font.vertAlign + scheme = Font.scheme + + __elements__ = ('rFont', 'charset', 'family', 'b', 'i', 'strike', + 'outline', 'shadow', 'condense', 'extend', 'color', 'sz', 'u', + 'vertAlign', 'scheme') + + def __init__(self, + rFont=None, + charset=None, + family=None, + b=None, + i=None, + strike=None, + outline=None, + shadow=None, + condense=None, + extend=None, + color=None, + sz=None, + u=None, + vertAlign=None, + scheme=None, + ): + self.rFont = rFont + self.charset = charset + self.family = family + self.b = b + self.i = i + self.strike = strike + self.outline = outline + self.shadow = shadow + self.condense = condense + self.extend = extend + self.color = color + self.sz = sz + self.u = u + self.vertAlign = vertAlign + self.scheme = scheme + + +class RichText(Serialisable): + + tagname = "RElt" + + rPr = Typed(expected_type=InlineFont, allow_none=True) + font = Alias("rPr") + t = NestedText(expected_type=str, allow_none=True) + text = Alias("t") + + __elements__ = ('rPr', 't') + + def __init__(self, + rPr=None, + t=None, + ): + self.rPr = rPr + self.t = t + + +class Text(Serialisable): + + tagname = "text" + + t = NestedText(allow_none=True, expected_type=str) + plain = Alias("t") + r = Sequence(expected_type=RichText, allow_none=True) + formatted = Alias("r") + rPh = Sequence(expected_type=PhoneticText, allow_none=True) + phonetic = Alias("rPh") + phoneticPr = Typed(expected_type=PhoneticProperties, allow_none=True) + PhoneticProperties = Alias("phoneticPr") + + __elements__ = ('t', 'r', 'rPh', 'phoneticPr') + + def __init__(self, + t=None, + r=(), + rPh=(), + phoneticPr=None, + ): + self.t = t + self.r = r + self.rPh = rPh + self.phoneticPr = phoneticPr + + + @property + def content(self): + """ + Text stripped of all formatting + """ + snippets = [] + if self.plain is not None: + snippets.append(self.plain) + for block in self.formatted: + if block.t is not None: + snippets.append(block.t) + return u"".join(snippets) diff --git a/.venv/Lib/site-packages/openpyxl/chart/_3d.py b/.venv/Lib/site-packages/openpyxl/chart/_3d.py new file mode 100644 index 00000000..391e628b --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/chart/_3d.py @@ -0,0 +1,105 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.descriptors import Typed, Alias +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors.nested import ( + NestedBool, + NestedInteger, + NestedMinMax, +) +from openpyxl.descriptors.excel import ExtensionList +from .marker import PictureOptions +from .shapes import GraphicalProperties + + +class View3D(Serialisable): + + tagname = "view3D" + + rotX = NestedMinMax(min=-90, max=90, allow_none=True) + x_rotation = Alias('rotX') + hPercent = NestedMinMax(min=5, max=500, allow_none=True) + height_percent = Alias('hPercent') + rotY = NestedInteger(min=-90, max=90, allow_none=True) + y_rotation = Alias('rotY') + depthPercent = NestedInteger(allow_none=True) + rAngAx = NestedBool(allow_none=True) + right_angle_axes = Alias('rAngAx') + perspective = NestedInteger(allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('rotX', 'hPercent', 'rotY', 'depthPercent', 'rAngAx', + 'perspective',) + + def __init__(self, + rotX=15, + hPercent=None, + rotY=20, + depthPercent=None, + rAngAx=True, + perspective=None, + extLst=None, + ): + self.rotX = rotX + self.hPercent = hPercent + self.rotY = rotY + self.depthPercent = depthPercent + self.rAngAx = rAngAx + self.perspective = perspective + + +class Surface(Serialisable): + + tagname = "surface" + + thickness = NestedInteger(allow_none=True) + spPr = Typed(expected_type=GraphicalProperties, allow_none=True) + graphicalProperties = Alias('spPr') + pictureOptions = Typed(expected_type=PictureOptions, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('thickness', 'spPr', 'pictureOptions',) + + def __init__(self, + thickness=None, + spPr=None, + pictureOptions=None, + extLst=None, + ): + self.thickness = thickness + self.spPr = spPr + self.pictureOptions = pictureOptions + + +class _3DBase(Serialisable): + + """ + Base class for 3D charts + """ + + tagname = "ChartBase" + + view3D = Typed(expected_type=View3D, allow_none=True) + floor = Typed(expected_type=Surface, allow_none=True) + sideWall = Typed(expected_type=Surface, allow_none=True) + backWall = Typed(expected_type=Surface, allow_none=True) + + def __init__(self, + view3D=None, + floor=None, + sideWall=None, + backWall=None, + ): + if view3D is None: + view3D = View3D() + self.view3D = view3D + if floor is None: + floor = Surface() + self.floor = floor + if sideWall is None: + sideWall = Surface() + self.sideWall = sideWall + if backWall is None: + backWall = Surface() + self.backWall = backWall + super(_3DBase, self).__init__() diff --git a/.venv/Lib/site-packages/openpyxl/chart/__init__.py b/.venv/Lib/site-packages/openpyxl/chart/__init__.py new file mode 100644 index 00000000..73b5f0e9 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/chart/__init__.py @@ -0,0 +1,19 @@ +# Copyright (c) 2010-2022 openpyxl + +from .area_chart import AreaChart, AreaChart3D +from .bar_chart import BarChart, BarChart3D +from .bubble_chart import BubbleChart +from .line_chart import LineChart, LineChart3D +from .pie_chart import ( + PieChart, + PieChart3D, + DoughnutChart, + ProjectedPieChart +) +from .radar_chart import RadarChart +from .scatter_chart import ScatterChart +from .stock_chart import StockChart +from .surface_chart import SurfaceChart, SurfaceChart3D + +from .series_factory import SeriesFactory as Series +from .reference import Reference diff --git a/.venv/Lib/site-packages/openpyxl/chart/_chart.py b/.venv/Lib/site-packages/openpyxl/chart/_chart.py new file mode 100644 index 00000000..6946aaf6 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/chart/_chart.py @@ -0,0 +1,196 @@ +# Copyright (c) 2010-2022 openpyxl + +from collections import OrderedDict +from operator import attrgetter + +from openpyxl.descriptors import ( + Typed, + Integer, + Alias, + MinMax, + Bool, + Set, +) +from openpyxl.descriptors.sequence import ValueSequence +from openpyxl.descriptors.serialisable import Serialisable + +from ._3d import _3DBase +from .data_source import AxDataSource, NumRef +from .layout import Layout +from .legend import Legend +from .reference import Reference +from .series_factory import SeriesFactory +from .series import attribute_mapping +from .shapes import GraphicalProperties +from .title import TitleDescriptor + +class AxId(Serialisable): + + val = Integer() + + def __init__(self, val): + self.val = val + + +def PlotArea(): + from .chartspace import PlotArea + return PlotArea() + + +class ChartBase(Serialisable): + + """ + Base class for all charts + """ + + legend = Typed(expected_type=Legend, allow_none=True) + layout = Typed(expected_type=Layout, allow_none=True) + roundedCorners = Bool(allow_none=True) + axId = ValueSequence(expected_type=int) + visible_cells_only = Bool(allow_none=True) + display_blanks = Set(values=['span', 'gap', 'zero']) + + _series_type = "" + ser = () + series = Alias('ser') + title = TitleDescriptor() + anchor = "E15" # default anchor position + width = 15 # in cm, approx 5 rows + height = 7.5 # in cm, approx 14 rows + _id = 1 + _path = "/xl/charts/chart{0}.xml" + style = MinMax(allow_none=True, min=1, max=48) + mime_type = "application/vnd.openxmlformats-officedocument.drawingml.chart+xml" + graphical_properties = Typed(expected_type=GraphicalProperties, allow_none=True) + + __elements__ = () + + + def __init__(self, axId=(), **kw): + self._charts = [self] + self.title = None + self.layout = None + self.roundedCorners = None + self.legend = Legend() + self.graphical_properties = None + self.style = None + self.plot_area = PlotArea() + self.axId = axId + self.display_blanks = 'gap' + self.pivotSource = None + self.pivotFormats = () + self.visible_cells_only = True + self.idx_base = 0 + super(ChartBase, self).__init__() + + + def __hash__(self): + """ + Just need to check for identity + """ + return id(self) + + def __iadd__(self, other): + """ + Combine the chart with another one + """ + if not isinstance(other, ChartBase): + raise TypeError("Only other charts can be added") + self._charts.append(other) + return self + + + def to_tree(self, namespace=None, tagname=None, idx=None): + self.axId = [id for id in self._axes] + if self.ser is not None: + for s in self.ser: + s.__elements__ = attribute_mapping[self._series_type] + return super(ChartBase, self).to_tree(tagname, idx) + + + def _reindex(self): + """ + Normalise and rebase series: sort by order and then rebase order + + """ + # sort data series in order and rebase + ds = sorted(self.series, key=attrgetter("order")) + for idx, s in enumerate(ds): + s.order = idx + self.series = ds + + + def _write(self): + from .chartspace import ChartSpace, ChartContainer + self.plot_area.layout = self.layout + + idx_base = self.idx_base + for chart in self._charts: + if chart not in self.plot_area._charts: + chart.idx_base = idx_base + idx_base += len(chart.series) + self.plot_area._charts = self._charts + + container = ChartContainer(plotArea=self.plot_area, legend=self.legend, title=self.title) + if isinstance(chart, _3DBase): + container.view3D = chart.view3D + container.floor = chart.floor + container.sideWall = chart.sideWall + container.backWall = chart.backWall + container.plotVisOnly = self.visible_cells_only + container.dispBlanksAs = self.display_blanks + container.pivotFmts = self.pivotFormats + cs = ChartSpace(chart=container) + cs.style = self.style + cs.roundedCorners = self.roundedCorners + cs.pivotSource = self.pivotSource + return cs.to_tree() + + + @property + def _axes(self): + x = getattr(self, "x_axis", None) + y = getattr(self, "y_axis", None) + z = getattr(self, "z_axis", None) + return OrderedDict([(axis.axId, axis) for axis in (x, y, z) if axis]) + + + def set_categories(self, labels): + """ + Set the categories / x-axis values + """ + if not isinstance(labels, Reference): + labels = Reference(range_string=labels) + for s in self.ser: + s.cat = AxDataSource(numRef=NumRef(f=labels)) + + + def add_data(self, data, from_rows=False, titles_from_data=False): + """ + Add a range of data in a single pass. + The default is to treat each column as a data series. + """ + if not isinstance(data, Reference): + data = Reference(range_string=data) + + if from_rows: + values = data.rows + + else: + values = data.cols + + for ref in values: + series = SeriesFactory(ref, title_from_data=titles_from_data) + self.series.append(series) + + + def append(self, value): + """Append a data series to the chart""" + l = self.series[:] + l.append(value) + self.series = l + + + @property + def path(self): + return self._path.format(self._id) diff --git a/.venv/Lib/site-packages/openpyxl/chart/area_chart.py b/.venv/Lib/site-packages/openpyxl/chart/area_chart.py new file mode 100644 index 00000000..63a5275c --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/chart/area_chart.py @@ -0,0 +1,106 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Set, + Bool, + Integer, + Sequence, + Alias, +) + +from openpyxl.descriptors.excel import ExtensionList +from openpyxl.descriptors.nested import ( + NestedMinMax, + NestedSet, + NestedBool, +) + +from ._chart import ChartBase +from .descriptors import NestedGapAmount +from .axis import TextAxis, NumericAxis, SeriesAxis, ChartLines +from .label import DataLabelList +from .series import Series + + +class _AreaChartBase(ChartBase): + + grouping = NestedSet(values=(['percentStacked', 'standard', 'stacked'])) + varyColors = NestedBool(nested=True, allow_none=True) + ser = Sequence(expected_type=Series, allow_none=True) + dLbls = Typed(expected_type=DataLabelList, allow_none=True) + dataLabels = Alias("dLbls") + dropLines = Typed(expected_type=ChartLines, allow_none=True) + + _series_type = "area" + + __elements__ = ('grouping', 'varyColors', 'ser', 'dLbls', 'dropLines') + + def __init__(self, + grouping="standard", + varyColors=None, + ser=(), + dLbls=None, + dropLines=None, + ): + self.grouping = grouping + self.varyColors = varyColors + self.ser = ser + self.dLbls = dLbls + self.dropLines = dropLines + super(_AreaChartBase, self).__init__() + + +class AreaChart(_AreaChartBase): + + tagname = "areaChart" + + grouping = _AreaChartBase.grouping + varyColors = _AreaChartBase.varyColors + ser = _AreaChartBase.ser + dLbls = _AreaChartBase.dLbls + dropLines = _AreaChartBase.dropLines + + # chart properties actually used by containing classes + x_axis = Typed(expected_type=TextAxis) + y_axis = Typed(expected_type=NumericAxis) + + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = _AreaChartBase.__elements__ + ('axId',) + + def __init__(self, + axId=None, + extLst=None, + **kw + ): + self.x_axis = TextAxis() + self.y_axis = NumericAxis() + super(AreaChart, self).__init__(**kw) + + +class AreaChart3D(AreaChart): + + tagname = "area3DChart" + + grouping = _AreaChartBase.grouping + varyColors = _AreaChartBase.varyColors + ser = _AreaChartBase.ser + dLbls = _AreaChartBase.dLbls + dropLines = _AreaChartBase.dropLines + + gapDepth = NestedGapAmount() + + x_axis = Typed(expected_type=TextAxis) + y_axis = Typed(expected_type=NumericAxis) + z_axis = Typed(expected_type=SeriesAxis, allow_none=True) + + __elements__ = AreaChart.__elements__ + ('gapDepth', ) + + def __init__(self, gapDepth=None, **kw): + self.gapDepth = gapDepth + super(AreaChart3D, self).__init__(**kw) + self.x_axis = TextAxis() + self.y_axis = NumericAxis() + self.z_axis = SeriesAxis() diff --git a/.venv/Lib/site-packages/openpyxl/chart/axis.py b/.venv/Lib/site-packages/openpyxl/chart/axis.py new file mode 100644 index 00000000..d98853a3 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/chart/axis.py @@ -0,0 +1,401 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Float, + NoneSet, + Bool, + Integer, + MinMax, + NoneSet, + Set, + String, + Alias, +) + +from openpyxl.descriptors.excel import ( + ExtensionList, + Percentage, + _explicit_none, +) +from openpyxl.descriptors.nested import ( + NestedValue, + NestedSet, + NestedBool, + NestedNoneSet, + NestedFloat, + NestedInteger, + NestedMinMax, +) +from openpyxl.xml.constants import CHART_NS + +from .descriptors import NumberFormatDescriptor +from .layout import Layout +from .text import Text, RichText +from .shapes import GraphicalProperties +from .title import Title, TitleDescriptor + + +class ChartLines(Serialisable): + + tagname = "chartLines" + + spPr = Typed(expected_type=GraphicalProperties, allow_none=True) + graphicalProperties = Alias('spPr') + + def __init__(self, spPr=None): + self.spPr = spPr + + +class Scaling(Serialisable): + + tagname = "scaling" + + logBase = NestedFloat(allow_none=True) + orientation = NestedSet(values=(['maxMin', 'minMax'])) + max = NestedFloat(allow_none=True) + min = NestedFloat(allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('logBase', 'orientation', 'max', 'min',) + + def __init__(self, + logBase=None, + orientation="minMax", + max=None, + min=None, + extLst=None, + ): + self.logBase = logBase + self.orientation = orientation + self.max = max + self.min = min + + +class _BaseAxis(Serialisable): + + axId = NestedInteger(expected_type=int) + scaling = Typed(expected_type=Scaling) + delete = NestedBool(allow_none=True) + axPos = NestedSet(values=(['b', 'l', 'r', 't'])) + majorGridlines = Typed(expected_type=ChartLines, allow_none=True) + minorGridlines = Typed(expected_type=ChartLines, allow_none=True) + title = TitleDescriptor() + numFmt = NumberFormatDescriptor() + number_format = Alias("numFmt") + majorTickMark = NestedNoneSet(values=(['cross', 'in', 'out']), to_tree=_explicit_none) + minorTickMark = NestedNoneSet(values=(['cross', 'in', 'out']), to_tree=_explicit_none) + tickLblPos = NestedNoneSet(values=(['high', 'low', 'nextTo'])) + spPr = Typed(expected_type=GraphicalProperties, allow_none=True) + graphicalProperties = Alias('spPr') + txPr = Typed(expected_type=RichText, allow_none=True) + textProperties = Alias('txPr') + crossAx = NestedInteger(expected_type=int) # references other axis + crosses = NestedNoneSet(values=(['autoZero', 'max', 'min'])) + crossesAt = NestedFloat(allow_none=True) + + # crosses & crossesAt are mutually exclusive + + __elements__ = ('axId', 'scaling', 'delete', 'axPos', 'majorGridlines', + 'minorGridlines', 'title', 'numFmt', 'majorTickMark', 'minorTickMark', + 'tickLblPos', 'spPr', 'txPr', 'crossAx', 'crosses', 'crossesAt') + + def __init__(self, + axId=None, + scaling=None, + delete=None, + axPos='l', + majorGridlines=None, + minorGridlines=None, + title=None, + numFmt=None, + majorTickMark=None, + minorTickMark=None, + tickLblPos=None, + spPr=None, + txPr= None, + crossAx=None, + crosses=None, + crossesAt=None, + ): + self.axId = axId + if scaling is None: + scaling = Scaling() + self.scaling = scaling + self.delete = delete + self.axPos = axPos + self.majorGridlines = majorGridlines + self.minorGridlines = minorGridlines + self.title = title + self.numFmt = numFmt + self.majorTickMark = majorTickMark + self.minorTickMark = minorTickMark + self.tickLblPos = tickLblPos + self.spPr = spPr + self.txPr = txPr + self.crossAx = crossAx + self.crosses = crosses + self.crossesAt = crossesAt + + +class DisplayUnitsLabel(Serialisable): + + tagname = "dispUnitsLbl" + + layout = Typed(expected_type=Layout, allow_none=True) + tx = Typed(expected_type=Text, allow_none=True) + text = Alias("tx") + spPr = Typed(expected_type=GraphicalProperties, allow_none=True) + graphicalProperties = Alias("spPr") + txPr = Typed(expected_type=RichText, allow_none=True) + textPropertes = Alias("txPr") + + __elements__ = ('layout', 'tx', 'spPr', 'txPr') + + def __init__(self, + layout=None, + tx=None, + spPr=None, + txPr=None, + ): + self.layout = layout + self.tx = tx + self.spPr = spPr + self.txPr = txPr + + +class DisplayUnitsLabelList(Serialisable): + + tagname = "dispUnits" + + custUnit = NestedFloat(allow_none=True) + builtInUnit = NestedNoneSet(values=(['hundreds', 'thousands', + 'tenThousands', 'hundredThousands', 'millions', 'tenMillions', + 'hundredMillions', 'billions', 'trillions'])) + dispUnitsLbl = Typed(expected_type=DisplayUnitsLabel, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('custUnit', 'builtInUnit', 'dispUnitsLbl',) + + def __init__(self, + custUnit=None, + builtInUnit=None, + dispUnitsLbl=None, + extLst=None, + ): + self.custUnit = custUnit + self.builtInUnit = builtInUnit + self.dispUnitsLbl = dispUnitsLbl + + +class NumericAxis(_BaseAxis): + + tagname = "valAx" + + axId = _BaseAxis.axId + scaling = _BaseAxis.scaling + delete = _BaseAxis.delete + axPos = _BaseAxis.axPos + majorGridlines = _BaseAxis.majorGridlines + minorGridlines = _BaseAxis.minorGridlines + title = _BaseAxis.title + numFmt = _BaseAxis.numFmt + majorTickMark = _BaseAxis.majorTickMark + minorTickMark = _BaseAxis.minorTickMark + tickLblPos = _BaseAxis.tickLblPos + spPr = _BaseAxis.spPr + txPr = _BaseAxis.txPr + crossAx = _BaseAxis.crossAx + crosses = _BaseAxis.crosses + crossesAt = _BaseAxis.crossesAt + + crossBetween = NestedNoneSet(values=(['between', 'midCat'])) + majorUnit = NestedFloat(allow_none=True) + minorUnit = NestedFloat(allow_none=True) + dispUnits = Typed(expected_type=DisplayUnitsLabelList, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = _BaseAxis.__elements__ + ('crossBetween', 'majorUnit', + 'minorUnit', 'dispUnits',) + + + def __init__(self, + crossBetween=None, + majorUnit=None, + minorUnit=None, + dispUnits=None, + extLst=None, + **kw + ): + self.crossBetween = crossBetween + self.majorUnit = majorUnit + self.minorUnit = minorUnit + self.dispUnits = dispUnits + kw.setdefault('majorGridlines', ChartLines()) + kw.setdefault('axId', 100) + kw.setdefault('crossAx', 10) + super(NumericAxis, self).__init__(**kw) + + + @classmethod + def from_tree(cls, node): + """ + Special case value axes with no gridlines + """ + self = super(NumericAxis, cls).from_tree(node) + gridlines = node.find("{%s}majorGridlines" % CHART_NS) + if gridlines is None: + self.majorGridlines = None + return self + + + +class TextAxis(_BaseAxis): + + tagname = "catAx" + + axId = _BaseAxis.axId + scaling = _BaseAxis.scaling + delete = _BaseAxis.delete + axPos = _BaseAxis.axPos + majorGridlines = _BaseAxis.majorGridlines + minorGridlines = _BaseAxis.minorGridlines + title = _BaseAxis.title + numFmt = _BaseAxis.numFmt + majorTickMark = _BaseAxis.majorTickMark + minorTickMark = _BaseAxis.minorTickMark + tickLblPos = _BaseAxis.tickLblPos + spPr = _BaseAxis.spPr + txPr = _BaseAxis.txPr + crossAx = _BaseAxis.crossAx + crosses = _BaseAxis.crosses + crossesAt = _BaseAxis.crossesAt + + auto = NestedBool(allow_none=True) + lblAlgn = NestedNoneSet(values=(['ctr', 'l', 'r'])) + lblOffset = NestedMinMax(min=0, max=1000) + tickLblSkip = NestedInteger(allow_none=True) + tickMarkSkip = NestedInteger(allow_none=True) + noMultiLvlLbl = NestedBool(allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = _BaseAxis.__elements__ + ('auto', 'lblAlgn', 'lblOffset', + 'tickLblSkip', 'tickMarkSkip', 'noMultiLvlLbl') + + def __init__(self, + auto=None, + lblAlgn=None, + lblOffset=100, + tickLblSkip=None, + tickMarkSkip=None, + noMultiLvlLbl=None, + extLst=None, + **kw + ): + self.auto = auto + self.lblAlgn = lblAlgn + self.lblOffset = lblOffset + self.tickLblSkip = tickLblSkip + self.tickMarkSkip = tickMarkSkip + self.noMultiLvlLbl = noMultiLvlLbl + kw.setdefault('axId', 10) + kw.setdefault('crossAx', 100) + super(TextAxis, self).__init__(**kw) + + +class DateAxis(TextAxis): + + tagname = "dateAx" + + axId = _BaseAxis.axId + scaling = _BaseAxis.scaling + delete = _BaseAxis.delete + axPos = _BaseAxis.axPos + majorGridlines = _BaseAxis.majorGridlines + minorGridlines = _BaseAxis.minorGridlines + title = _BaseAxis.title + numFmt = _BaseAxis.numFmt + majorTickMark = _BaseAxis.majorTickMark + minorTickMark = _BaseAxis.minorTickMark + tickLblPos = _BaseAxis.tickLblPos + spPr = _BaseAxis.spPr + txPr = _BaseAxis.txPr + crossAx = _BaseAxis.crossAx + crosses = _BaseAxis.crosses + crossesAt = _BaseAxis.crossesAt + + auto = NestedBool(allow_none=True) + lblOffset = NestedInteger(allow_none=True) + baseTimeUnit = NestedNoneSet(values=(['days', 'months', 'years'])) + majorUnit = NestedFloat(allow_none=True) + majorTimeUnit = NestedNoneSet(values=(['days', 'months', 'years'])) + minorUnit = NestedFloat(allow_none=True) + minorTimeUnit = NestedNoneSet(values=(['days', 'months', 'years'])) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = _BaseAxis.__elements__ + ('auto', 'lblOffset', + 'baseTimeUnit', 'majorUnit', 'majorTimeUnit', 'minorUnit', + 'minorTimeUnit') + + def __init__(self, + auto=None, + lblOffset=None, + baseTimeUnit=None, + majorUnit=None, + majorTimeUnit=None, + minorUnit=None, + minorTimeUnit=None, + extLst=None, + **kw + ): + self.auto = auto + self.lblOffset = lblOffset + self.baseTimeUnit = baseTimeUnit + self.majorUnit = majorUnit + self.majorTimeUnit = majorTimeUnit + self.minorUnit = minorUnit + self.minorTimeUnit = minorTimeUnit + kw.setdefault('axId', 500) + kw.setdefault('lblOffset', lblOffset) + super(DateAxis, self).__init__(**kw) + + +class SeriesAxis(_BaseAxis): + + tagname = "serAx" + + axId = _BaseAxis.axId + scaling = _BaseAxis.scaling + delete = _BaseAxis.delete + axPos = _BaseAxis.axPos + majorGridlines = _BaseAxis.majorGridlines + minorGridlines = _BaseAxis.minorGridlines + title = _BaseAxis.title + numFmt = _BaseAxis.numFmt + majorTickMark = _BaseAxis.majorTickMark + minorTickMark = _BaseAxis.minorTickMark + tickLblPos = _BaseAxis.tickLblPos + spPr = _BaseAxis.spPr + txPr = _BaseAxis.txPr + crossAx = _BaseAxis.crossAx + crosses = _BaseAxis.crosses + crossesAt = _BaseAxis.crossesAt + + tickLblSkip = NestedInteger(allow_none=True) + tickMarkSkip = NestedInteger(allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = _BaseAxis.__elements__ + ('tickLblSkip', 'tickMarkSkip') + + def __init__(self, + tickLblSkip=None, + tickMarkSkip=None, + extLst=None, + **kw + ): + self.tickLblSkip = tickLblSkip + self.tickMarkSkip = tickMarkSkip + kw.setdefault('axId', 1000) + kw.setdefault('crossAx', 10) + super(SeriesAxis, self).__init__(**kw) diff --git a/.venv/Lib/site-packages/openpyxl/chart/bar_chart.py b/.venv/Lib/site-packages/openpyxl/chart/bar_chart.py new file mode 100644 index 00000000..c3ac9f2c --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/chart/bar_chart.py @@ -0,0 +1,144 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Bool, + Integer, + Sequence, + Alias, +) +from openpyxl.descriptors.excel import ExtensionList +from openpyxl.descriptors.nested import ( + NestedNoneSet, + NestedSet, + NestedBool, + NestedInteger, + NestedMinMax, +) + +from .descriptors import ( + NestedGapAmount, + NestedOverlap, +) +from ._chart import ChartBase +from ._3d import _3DBase +from .axis import TextAxis, NumericAxis, SeriesAxis, ChartLines +from .shapes import GraphicalProperties +from .series import Series +from .legend import Legend +from .label import DataLabelList + + +class _BarChartBase(ChartBase): + + barDir = NestedSet(values=(['bar', 'col'])) + type = Alias("barDir") + grouping = NestedSet(values=(['percentStacked', 'clustered', 'standard', + 'stacked'])) + varyColors = NestedBool(nested=True, allow_none=True) + ser = Sequence(expected_type=Series, allow_none=True) + dLbls = Typed(expected_type=DataLabelList, allow_none=True) + dataLabels = Alias("dLbls") + + __elements__ = ('barDir', 'grouping', 'varyColors', 'ser', 'dLbls') + + _series_type = "bar" + + def __init__(self, + barDir="col", + grouping="clustered", + varyColors=None, + ser=(), + dLbls=None, + **kw + ): + self.barDir = barDir + self.grouping = grouping + self.varyColors = varyColors + self.ser = ser + self.dLbls = dLbls + super(_BarChartBase, self).__init__(**kw) + + +class BarChart(_BarChartBase): + + tagname = "barChart" + + barDir = _BarChartBase.barDir + grouping = _BarChartBase.grouping + varyColors = _BarChartBase.varyColors + ser = _BarChartBase.ser + dLbls = _BarChartBase.dLbls + + gapWidth = NestedGapAmount() + overlap = NestedOverlap() + serLines = Typed(expected_type=ChartLines, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + # chart properties actually used by containing classes + x_axis = Typed(expected_type=TextAxis) + y_axis = Typed(expected_type=NumericAxis) + + __elements__ = _BarChartBase.__elements__ + ('gapWidth', 'overlap', 'serLines', 'axId') + + def __init__(self, + gapWidth=150, + overlap=None, + serLines=None, + extLst=None, + **kw + ): + self.gapWidth = gapWidth + self.overlap = overlap + self.serLines = serLines + self.x_axis = TextAxis() + self.y_axis = NumericAxis() + self.legend = Legend() + super(BarChart, self).__init__(**kw) + + +class BarChart3D(_BarChartBase, _3DBase): + + tagname = "bar3DChart" + + barDir = _BarChartBase.barDir + grouping = _BarChartBase.grouping + varyColors = _BarChartBase.varyColors + ser = _BarChartBase.ser + dLbls = _BarChartBase.dLbls + + view3D = _3DBase.view3D + floor = _3DBase.floor + sideWall = _3DBase.sideWall + backWall = _3DBase.backWall + + gapWidth = NestedGapAmount() + gapDepth = NestedGapAmount() + shape = NestedNoneSet(values=(['cone', 'coneToMax', 'box', 'cylinder', 'pyramid', 'pyramidToMax'])) + serLines = Typed(expected_type=ChartLines, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + x_axis = Typed(expected_type=TextAxis) + y_axis = Typed(expected_type=NumericAxis) + z_axis = Typed(expected_type=SeriesAxis, allow_none=True) + + __elements__ = _BarChartBase.__elements__ + ('gapWidth', 'gapDepth', 'shape', 'serLines', 'axId') + + def __init__(self, + gapWidth=150, + gapDepth=150, + shape=None, + serLines=None, + extLst=None, + **kw + ): + self.gapWidth = gapWidth + self.gapDepth = gapDepth + self.shape = shape + self.serLines = serLines + self.x_axis = TextAxis() + self.y_axis = NumericAxis() + self.z_axis = SeriesAxis() + + super(BarChart3D, self).__init__(**kw) diff --git a/.venv/Lib/site-packages/openpyxl/chart/bubble_chart.py b/.venv/Lib/site-packages/openpyxl/chart/bubble_chart.py new file mode 100644 index 00000000..763dffc8 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/chart/bubble_chart.py @@ -0,0 +1,67 @@ +#Autogenerated schema +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Set, + MinMax, + Bool, + Integer, + Alias, + Sequence, +) +from openpyxl.descriptors.excel import ExtensionList +from openpyxl.descriptors.nested import ( + NestedNoneSet, + NestedMinMax, + NestedBool, +) + +from ._chart import ChartBase +from .axis import TextAxis, NumericAxis +from .series import XYSeries +from .label import DataLabelList + + +class BubbleChart(ChartBase): + + tagname = "bubbleChart" + + varyColors = NestedBool(allow_none=True) + ser = Sequence(expected_type=XYSeries, allow_none=True) + dLbls = Typed(expected_type=DataLabelList, allow_none=True) + dataLabels = Alias("dLbls") + bubble3D = NestedBool(allow_none=True) + bubbleScale = NestedMinMax(min=0, max=300, allow_none=True) + showNegBubbles = NestedBool(allow_none=True) + sizeRepresents = NestedNoneSet(values=(['area', 'w'])) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + x_axis = Typed(expected_type=NumericAxis) + y_axis = Typed(expected_type=NumericAxis) + + _series_type = "bubble" + + __elements__ = ('varyColors', 'ser', 'dLbls', 'bubble3D', 'bubbleScale', + 'showNegBubbles', 'sizeRepresents', 'axId') + + def __init__(self, + varyColors=None, + ser=(), + dLbls=None, + bubble3D=None, + bubbleScale=None, + showNegBubbles=None, + sizeRepresents=None, + extLst=None, + **kw + ): + self.varyColors = varyColors + self.ser = ser + self.dLbls = dLbls + self.bubble3D = bubble3D + self.bubbleScale = bubbleScale + self.showNegBubbles = showNegBubbles + self.sizeRepresents = sizeRepresents + self.x_axis = NumericAxis(axId=10, crossAx=20) + self.y_axis = NumericAxis(axId=20, crossAx=10) + super(BubbleChart, self).__init__(**kw) diff --git a/.venv/Lib/site-packages/openpyxl/chart/chartspace.py b/.venv/Lib/site-packages/openpyxl/chart/chartspace.py new file mode 100644 index 00000000..b1e8f81d --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/chart/chartspace.py @@ -0,0 +1,195 @@ + +# Copyright (c) 2010-2022 openpyxl + +""" +Enclosing chart object. The various chart types are actually child objects. +Will probably need to call this indirectly +""" + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + String, + Alias, +) +from openpyxl.descriptors.excel import ( + ExtensionList, + Relation +) +from openpyxl.descriptors.nested import ( + NestedBool, + NestedNoneSet, + NestedString, + NestedMinMax, +) +from openpyxl.descriptors.sequence import NestedSequence +from openpyxl.xml.constants import CHART_NS + +from openpyxl.drawing.colors import ColorMapping +from .text import RichText +from .shapes import GraphicalProperties +from .legend import Legend +from ._3d import _3DBase +from .plotarea import PlotArea +from .title import Title +from .pivot import ( + PivotFormat, + PivotSource, +) +from .print_settings import PrintSettings + + +class ChartContainer(Serialisable): + + tagname = "chart" + + title = Typed(expected_type=Title, allow_none=True) + autoTitleDeleted = NestedBool(allow_none=True) + pivotFmts = NestedSequence(expected_type=PivotFormat) + view3D = _3DBase.view3D + floor = _3DBase.floor + sideWall = _3DBase.sideWall + backWall = _3DBase.backWall + plotArea = Typed(expected_type=PlotArea, ) + legend = Typed(expected_type=Legend, allow_none=True) + plotVisOnly = NestedBool() + dispBlanksAs = NestedNoneSet(values=(['span', 'gap', 'zero'])) + showDLblsOverMax = NestedBool(allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('title', 'autoTitleDeleted', 'pivotFmts', 'view3D', + 'floor', 'sideWall', 'backWall', 'plotArea', 'legend', 'plotVisOnly', + 'dispBlanksAs', 'showDLblsOverMax') + + def __init__(self, + title=None, + autoTitleDeleted=None, + pivotFmts=(), + view3D=None, + floor=None, + sideWall=None, + backWall=None, + plotArea=None, + legend=None, + plotVisOnly=True, + dispBlanksAs="gap", + showDLblsOverMax=None, + extLst=None, + ): + self.title = title + self.autoTitleDeleted = autoTitleDeleted + self.pivotFmts = pivotFmts + self.view3D = view3D + self.floor = floor + self.sideWall = sideWall + self.backWall = backWall + if plotArea is None: + plotArea = PlotArea() + self.plotArea = plotArea + self.legend = legend + self.plotVisOnly = plotVisOnly + self.dispBlanksAs = dispBlanksAs + self.showDLblsOverMax = showDLblsOverMax + + +class Protection(Serialisable): + + tagname = "protection" + + chartObject = NestedBool(allow_none=True) + data = NestedBool(allow_none=True) + formatting = NestedBool(allow_none=True) + selection = NestedBool(allow_none=True) + userInterface = NestedBool(allow_none=True) + + __elements__ = ("chartObject", "data", "formatting", "selection", "userInterface") + + def __init__(self, + chartObject=None, + data=None, + formatting=None, + selection=None, + userInterface=None, + ): + self.chartObject = chartObject + self.data = data + self.formatting = formatting + self.selection = selection + self.userInterface = userInterface + + +class ExternalData(Serialisable): + + tagname = "externalData" + + autoUpdate = NestedBool(allow_none=True) + id = String() # Needs namespace + + def __init__(self, + autoUpdate=None, + id=None + ): + self.autoUpdate = autoUpdate + self.id = id + + +class ChartSpace(Serialisable): + + tagname = "chartSpace" + + date1904 = NestedBool(allow_none=True) + lang = NestedString(allow_none=True) + roundedCorners = NestedBool(allow_none=True) + style = NestedMinMax(allow_none=True, min=1, max=48) + clrMapOvr = Typed(expected_type=ColorMapping, allow_none=True) + pivotSource = Typed(expected_type=PivotSource, allow_none=True) + protection = Typed(expected_type=Protection, allow_none=True) + chart = Typed(expected_type=ChartContainer) + spPr = Typed(expected_type=GraphicalProperties, allow_none=True) + graphicalProperties = Alias("spPr") + txPr = Typed(expected_type=RichText, allow_none=True) + textProperties = Alias("txPr") + externalData = Typed(expected_type=ExternalData, allow_none=True) + printSettings = Typed(expected_type=PrintSettings, allow_none=True) + userShapes = Relation() + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('date1904', 'lang', 'roundedCorners', 'style', + 'clrMapOvr', 'pivotSource', 'protection', 'chart', 'spPr', 'txPr', + 'externalData', 'printSettings', 'userShapes') + + def __init__(self, + date1904=None, + lang=None, + roundedCorners=None, + style=None, + clrMapOvr=None, + pivotSource=None, + protection=None, + chart=None, + spPr=None, + txPr=None, + externalData=None, + printSettings=None, + userShapes=None, + extLst=None, + ): + self.date1904 = date1904 + self.lang = lang + self.roundedCorners = roundedCorners + self.style = style + self.clrMapOvr = clrMapOvr + self.pivotSource = pivotSource + self.protection = protection + self.chart = chart + self.spPr = spPr + self.txPr = txPr + self.externalData = externalData + self.printSettings = printSettings + self.userShapes = userShapes + + + def to_tree(self, tagname=None, idx=None, namespace=None): + tree = super(ChartSpace, self).to_tree() + tree.set("xmlns", CHART_NS) + return tree diff --git a/.venv/Lib/site-packages/openpyxl/chart/data_source.py b/.venv/Lib/site-packages/openpyxl/chart/data_source.py new file mode 100644 index 00000000..9a788d91 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/chart/data_source.py @@ -0,0 +1,246 @@ +""" +Collection of utility primitives for charts. +""" + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Bool, + Typed, + Alias, + String, + Integer, + Sequence, +) +from openpyxl.descriptors.excel import ExtensionList +from openpyxl.descriptors.nested import ( + NestedString, + NestedText, + NestedInteger, +) + + +class NumFmt(Serialisable): + + formatCode = String() + sourceLinked = Bool() + + def __init__(self, + formatCode=None, + sourceLinked=False + ): + self.formatCode = formatCode + self.sourceLinked = sourceLinked + + +class NumberValueDescriptor(NestedText): + """ + Data should be numerical but isn't always :-/ + """ + + allow_none = True + + def __set__(self, instance, value): + if value == "#N/A": + self.expected_type = str + else: + self.expected_type = float + super(NumberValueDescriptor, self).__set__(instance, value) + + +class NumVal(Serialisable): + + idx = Integer() + formatCode = NestedText(allow_none=True, expected_type=str) + v = NumberValueDescriptor() + + def __init__(self, + idx=None, + formatCode=None, + v=None, + ): + self.idx = idx + self.formatCode = formatCode + self.v = v + + +class NumData(Serialisable): + + formatCode = NestedText(expected_type=str, allow_none=True) + ptCount = NestedInteger(allow_none=True) + pt = Sequence(expected_type=NumVal) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('formatCode', 'ptCount', 'pt') + + def __init__(self, + formatCode=None, + ptCount=None, + pt=(), + extLst=None, + ): + self.formatCode = formatCode + self.ptCount = ptCount + self.pt = pt + + +class NumRef(Serialisable): + + f = NestedText(expected_type=str) + ref = Alias('f') + numCache = Typed(expected_type=NumData, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('f', 'numCache') + + def __init__(self, + f=None, + numCache=None, + extLst=None, + ): + self.f = f + self.numCache = numCache + + +class StrVal(Serialisable): + + tagname = "strVal" + + idx = Integer() + v = NestedText(expected_type=str) + + def __init__(self, + idx=0, + v=None, + ): + self.idx = idx + self.v = v + + +class StrData(Serialisable): + + tagname = "strData" + + ptCount = NestedInteger(allow_none=True) + pt = Sequence(expected_type=StrVal) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('ptCount', 'pt') + + def __init__(self, + ptCount=None, + pt=(), + extLst=None, + ): + self.ptCount = ptCount + self.pt = pt + + +class StrRef(Serialisable): + + tagname = "strRef" + + f = NestedText(expected_type=str, allow_none=True) + strCache = Typed(expected_type=StrData, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('f', 'strCache') + + def __init__(self, + f=None, + strCache=None, + extLst=None, + ): + self.f = f + self.strCache = strCache + + +class NumDataSource(Serialisable): + + numRef = Typed(expected_type=NumRef, allow_none=True) + numLit = Typed(expected_type=NumData, allow_none=True) + + + def __init__(self, + numRef=None, + numLit=None, + ): + self.numRef = numRef + self.numLit = numLit + + +class Level(Serialisable): + + tagname = "lvl" + + pt = Sequence(expected_type=StrVal) + + __elements__ = ('pt',) + + def __init__(self, + pt=(), + ): + self.pt = pt + + +class MultiLevelStrData(Serialisable): + + tagname = "multiLvlStrData" + + ptCount = Integer(allow_none=True) + lvl = Sequence(expected_type=Level) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('ptCount', 'lvl',) + + def __init__(self, + ptCount=None, + lvl=(), + extLst=None, + ): + self.ptCount = ptCount + self.lvl = lvl + + +class MultiLevelStrRef(Serialisable): + + tagname = "multiLvlStrRef" + + f = NestedText(expected_type=str) + multiLvlStrCache = Typed(expected_type=MultiLevelStrData, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('multiLvlStrCache', 'f') + + def __init__(self, + f=None, + multiLvlStrCache=None, + extLst=None, + ): + self.f = f + self.multiLvlStrCache = multiLvlStrCache + + +class AxDataSource(Serialisable): + + tagname = "cat" + + numRef = Typed(expected_type=NumRef, allow_none=True) + numLit = Typed(expected_type=NumData, allow_none=True) + strRef = Typed(expected_type=StrRef, allow_none=True) + strLit = Typed(expected_type=StrData, allow_none=True) + multiLvlStrRef = Typed(expected_type=MultiLevelStrRef, allow_none=True) + + def __init__(self, + numRef=None, + numLit=None, + strRef=None, + strLit=None, + multiLvlStrRef=None, + ): + if not any([numLit, numRef, strRef, strLit, multiLvlStrRef]): + raise TypeError("A data source must be provided") + self.numRef = numRef + self.numLit = numLit + self.strRef = strRef + self.strLit = strLit + self.multiLvlStrRef = multiLvlStrRef diff --git a/.venv/Lib/site-packages/openpyxl/chart/descriptors.py b/.venv/Lib/site-packages/openpyxl/chart/descriptors.py new file mode 100644 index 00000000..43d0ba1a --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/chart/descriptors.py @@ -0,0 +1,43 @@ +# Copyright (c) 2010-2022 openpyxl + + + +from openpyxl.descriptors.nested import ( + NestedMinMax + ) + +from openpyxl.descriptors import Typed + +from .data_source import NumFmt + +""" +Utility descriptors for the chart module. +For convenience but also clarity. +""" + +class NestedGapAmount(NestedMinMax): + + allow_none = True + min = 0 + max = 500 + + +class NestedOverlap(NestedMinMax): + + allow_none = True + min = -100 + max = 100 + + +class NumberFormatDescriptor(Typed): + """ + Allow direct assignment of format code + """ + + expected_type = NumFmt + allow_none = True + + def __set__(self, instance, value): + if isinstance(value, str): + value = NumFmt(value) + super(NumberFormatDescriptor, self).__set__(instance, value) diff --git a/.venv/Lib/site-packages/openpyxl/chart/error_bar.py b/.venv/Lib/site-packages/openpyxl/chart/error_bar.py new file mode 100644 index 00000000..8ef4e95e --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/chart/error_bar.py @@ -0,0 +1,62 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Float, + Set, + Alias +) + +from openpyxl.descriptors.excel import ExtensionList +from openpyxl.descriptors.nested import ( + NestedNoneSet, + NestedSet, + NestedBool, + NestedFloat, +) + +from .data_source import NumDataSource +from .shapes import GraphicalProperties + + +class ErrorBars(Serialisable): + + tagname = "errBars" + + errDir = NestedNoneSet(values=(['x', 'y'])) + direction = Alias("errDir") + errBarType = NestedSet(values=(['both', 'minus', 'plus'])) + style = Alias("errBarType") + errValType = NestedSet(values=(['cust', 'fixedVal', 'percentage', 'stdDev', 'stdErr'])) + size = Alias("errValType") + noEndCap = NestedBool(nested=True, allow_none=True) + plus = Typed(expected_type=NumDataSource, allow_none=True) + minus = Typed(expected_type=NumDataSource, allow_none=True) + val = NestedFloat(allow_none=True) + spPr = Typed(expected_type=GraphicalProperties, allow_none=True) + graphicalProperties = Alias("spPr") + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('errDir','errBarType', 'errValType', 'noEndCap','minus', 'plus', 'val', 'spPr') + + + def __init__(self, + errDir=None, + errBarType="both", + errValType="fixedVal", + noEndCap=None, + plus=None, + minus=None, + val=None, + spPr=None, + extLst=None, + ): + self.errDir = errDir + self.errBarType = errBarType + self.errValType = errValType + self.noEndCap = noEndCap + self.plus = plus + self.minus = minus + self.val = val + self.spPr = spPr diff --git a/.venv/Lib/site-packages/openpyxl/chart/label.py b/.venv/Lib/site-packages/openpyxl/chart/label.py new file mode 100644 index 00000000..9597da88 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/chart/label.py @@ -0,0 +1,127 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Sequence, + Alias, + Typed +) +from openpyxl.descriptors.excel import ExtensionList +from openpyxl.descriptors.nested import ( + NestedNoneSet, + NestedBool, + NestedString, + NestedInteger, + ) + +from .shapes import GraphicalProperties +from .text import RichText + + +class _DataLabelBase(Serialisable): + + numFmt = NestedString(allow_none=True, attribute="formatCode") + spPr = Typed(expected_type=GraphicalProperties, allow_none=True) + graphicalProperties = Alias('spPr') + txPr = Typed(expected_type=RichText, allow_none=True) + textProperties = Alias('txPr') + dLblPos = NestedNoneSet(values=['bestFit', 'b', 'ctr', 'inBase', 'inEnd', + 'l', 'outEnd', 'r', 't']) + position = Alias('dLblPos') + showLegendKey = NestedBool(allow_none=True) + showVal = NestedBool(allow_none=True) + showCatName = NestedBool(allow_none=True) + showSerName = NestedBool(allow_none=True) + showPercent = NestedBool(allow_none=True) + showBubbleSize = NestedBool(allow_none=True) + showLeaderLines = NestedBool(allow_none=True) + separator = NestedString(allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ("numFmt", "spPr", "txPr", "dLblPos", "showLegendKey", + "showVal", "showCatName", "showSerName", "showPercent", "showBubbleSize", + "showLeaderLines", "separator") + + def __init__(self, + numFmt=None, + spPr=None, + txPr=None, + dLblPos=None, + showLegendKey=None, + showVal=None, + showCatName=None, + showSerName=None, + showPercent=None, + showBubbleSize=None, + showLeaderLines=None, + separator=None, + extLst=None, + ): + self.numFmt = numFmt + self.spPr = spPr + self.txPr = txPr + self.dLblPos = dLblPos + self.showLegendKey = showLegendKey + self.showVal = showVal + self.showCatName = showCatName + self.showSerName = showSerName + self.showPercent = showPercent + self.showBubbleSize = showBubbleSize + self.showLeaderLines = showLeaderLines + self.separator = separator + + +class DataLabel(_DataLabelBase): + + tagname = "dLbl" + + idx = NestedInteger() + + numFmt = _DataLabelBase.numFmt + spPr = _DataLabelBase.spPr + txPr = _DataLabelBase.txPr + dLblPos = _DataLabelBase.dLblPos + showLegendKey = _DataLabelBase.showLegendKey + showVal = _DataLabelBase.showVal + showCatName = _DataLabelBase.showCatName + showSerName = _DataLabelBase.showSerName + showPercent = _DataLabelBase.showPercent + showBubbleSize = _DataLabelBase.showBubbleSize + showLeaderLines = _DataLabelBase.showLeaderLines + separator = _DataLabelBase.separator + extLst = _DataLabelBase.extLst + + __elements__ = ("idx",) + _DataLabelBase.__elements__ + + def __init__(self, idx=0, **kw ): + self.idx = idx + super(DataLabel, self).__init__(**kw) + + +class DataLabelList(_DataLabelBase): + + tagname = "dLbls" + + dLbl = Sequence(expected_type=DataLabel, allow_none=True) + + delete = NestedBool(allow_none=True) + numFmt = _DataLabelBase.numFmt + spPr = _DataLabelBase.spPr + txPr = _DataLabelBase.txPr + dLblPos = _DataLabelBase.dLblPos + showLegendKey = _DataLabelBase.showLegendKey + showVal = _DataLabelBase.showVal + showCatName = _DataLabelBase.showCatName + showSerName = _DataLabelBase.showSerName + showPercent = _DataLabelBase.showPercent + showBubbleSize = _DataLabelBase.showBubbleSize + showLeaderLines = _DataLabelBase.showLeaderLines + separator = _DataLabelBase.separator + extLst = _DataLabelBase.extLst + + __elements__ = ("delete", "dLbl",) + _DataLabelBase.__elements__ + + def __init__(self, dLbl=(), delete=None, **kw): + self.dLbl = dLbl + self.delete = delete + super(DataLabelList, self).__init__(**kw) diff --git a/.venv/Lib/site-packages/openpyxl/chart/layout.py b/.venv/Lib/site-packages/openpyxl/chart/layout.py new file mode 100644 index 00000000..384ad1df --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/chart/layout.py @@ -0,0 +1,74 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + NoneSet, + Float, + Typed, + Alias, +) + +from openpyxl.descriptors.excel import ExtensionList +from openpyxl.descriptors.nested import ( + NestedNoneSet, + NestedSet, + NestedMinMax, +) + +class ManualLayout(Serialisable): + + tagname = "manualLayout" + + layoutTarget = NestedNoneSet(values=(['inner', 'outer'])) + xMode = NestedNoneSet(values=(['edge', 'factor'])) + yMode = NestedNoneSet(values=(['edge', 'factor'])) + wMode = NestedSet(values=(['edge', 'factor'])) + hMode = NestedSet(values=(['edge', 'factor'])) + x = NestedMinMax(min=-1, max=1, allow_none=True) + y = NestedMinMax(min=-1, max=1, allow_none=True) + w = NestedMinMax(min=0, max=1, allow_none=True) + width = Alias('w') + h = NestedMinMax(min=0, max=1, allow_none=True) + height = Alias('h') + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('layoutTarget', 'xMode', 'yMode', 'wMode', 'hMode', 'x', + 'y', 'w', 'h') + + def __init__(self, + layoutTarget=None, + xMode=None, + yMode=None, + wMode="factor", + hMode="factor", + x=None, + y=None, + w=None, + h=None, + extLst=None, + ): + self.layoutTarget = layoutTarget + self.xMode = xMode + self.yMode = yMode + self.wMode = wMode + self.hMode = hMode + self.x = x + self.y = y + self.w = w + self.h = h + + +class Layout(Serialisable): + + tagname = "layout" + + manualLayout = Typed(expected_type=ManualLayout, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('manualLayout',) + + def __init__(self, + manualLayout=None, + extLst=None, + ): + self.manualLayout = manualLayout diff --git a/.venv/Lib/site-packages/openpyxl/chart/legend.py b/.venv/Lib/site-packages/openpyxl/chart/legend.py new file mode 100644 index 00000000..da0fa052 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/chart/legend.py @@ -0,0 +1,75 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Integer, + Alias, + Sequence, +) +from openpyxl.descriptors.excel import ExtensionList +from openpyxl.descriptors.nested import ( + NestedBool, + NestedSet, + NestedInteger +) + +from .layout import Layout +from .shapes import GraphicalProperties +from .text import RichText + + +class LegendEntry(Serialisable): + + tagname = "legendEntry" + + idx = NestedInteger() + delete = NestedBool() + txPr = Typed(expected_type=RichText, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('idx', 'delete', 'txPr') + + def __init__(self, + idx=0, + delete=False, + txPr=None, + extLst=None, + ): + self.idx = idx + self.delete = delete + self.txPr = txPr + + +class Legend(Serialisable): + + tagname = "legend" + + legendPos = NestedSet(values=(['b', 'tr', 'l', 'r', 't'])) + position = Alias('legendPos') + legendEntry = Sequence(expected_type=LegendEntry) + layout = Typed(expected_type=Layout, allow_none=True) + overlay = NestedBool(allow_none=True) + spPr = Typed(expected_type=GraphicalProperties, allow_none=True) + graphicalProperties = Alias('spPr') + txPr = Typed(expected_type=RichText, allow_none=True) + textProperties = Alias('txPr') + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('legendPos', 'legendEntry', 'layout', 'overlay', 'spPr', 'txPr',) + + def __init__(self, + legendPos="r", + legendEntry=(), + layout=None, + overlay=None, + spPr=None, + txPr=None, + extLst=None, + ): + self.legendPos = legendPos + self.legendEntry = legendEntry + self.layout = layout + self.overlay = overlay + self.spPr = spPr + self.txPr = txPr diff --git a/.venv/Lib/site-packages/openpyxl/chart/line_chart.py b/.venv/Lib/site-packages/openpyxl/chart/line_chart.py new file mode 100644 index 00000000..d8569e80 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/chart/line_chart.py @@ -0,0 +1,129 @@ +#Autogenerated schema +from openpyxl.descriptors import ( + Typed, + Sequence, + Alias, + ) +from openpyxl.descriptors.excel import ExtensionList +from openpyxl.descriptors.nested import ( + NestedSet, + NestedBool, +) + +from ._chart import ChartBase +from .updown_bars import UpDownBars +from .descriptors import NestedGapAmount +from .axis import TextAxis, NumericAxis, SeriesAxis, ChartLines, _BaseAxis +from .label import DataLabelList +from .series import Series + + +class _LineChartBase(ChartBase): + + grouping = NestedSet(values=(['percentStacked', 'standard', 'stacked'])) + varyColors = NestedBool(allow_none=True) + ser = Sequence(expected_type=Series, allow_none=True) + dLbls = Typed(expected_type=DataLabelList, allow_none=True) + dataLabels = Alias("dLbls") + dropLines = Typed(expected_type=ChartLines, allow_none=True) + + _series_type = "line" + + __elements__ = ('grouping', 'varyColors', 'ser', 'dLbls', 'dropLines') + + def __init__(self, + grouping="standard", + varyColors=None, + ser=(), + dLbls=None, + dropLines=None, + **kw + ): + self.grouping = grouping + self.varyColors = varyColors + self.ser = ser + self.dLbls = dLbls + self.dropLines = dropLines + super(_LineChartBase, self).__init__(**kw) + + +class LineChart(_LineChartBase): + + tagname = "lineChart" + + grouping = _LineChartBase.grouping + varyColors = _LineChartBase.varyColors + ser = _LineChartBase.ser + dLbls = _LineChartBase.dLbls + dropLines =_LineChartBase.dropLines + + hiLowLines = Typed(expected_type=ChartLines, allow_none=True) + upDownBars = Typed(expected_type=UpDownBars, allow_none=True) + marker = NestedBool(allow_none=True) + smooth = NestedBool(allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + x_axis = Typed(expected_type=_BaseAxis) + y_axis = Typed(expected_type=NumericAxis) + + __elements__ = _LineChartBase.__elements__ + ('hiLowLines', 'upDownBars', 'marker', 'smooth', 'axId') + + def __init__(self, + hiLowLines=None, + upDownBars=None, + marker=None, + smooth=None, + extLst=None, + **kw + ): + self.hiLowLines = hiLowLines + self.upDownBars = upDownBars + self.marker = marker + self.smooth = smooth + self.x_axis = TextAxis() + self.y_axis = NumericAxis() + + super(LineChart, self).__init__(**kw) + + +class LineChart3D(_LineChartBase): + + tagname = "line3DChart" + + grouping = _LineChartBase.grouping + varyColors = _LineChartBase.varyColors + ser = _LineChartBase.ser + dLbls = _LineChartBase.dLbls + dropLines =_LineChartBase.dropLines + + gapDepth = NestedGapAmount() + hiLowLines = Typed(expected_type=ChartLines, allow_none=True) + upDownBars = Typed(expected_type=UpDownBars, allow_none=True) + marker = NestedBool(allow_none=True) + smooth = NestedBool(allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + x_axis = Typed(expected_type=TextAxis) + y_axis = Typed(expected_type=NumericAxis) + z_axis = Typed(expected_type=SeriesAxis) + + __elements__ = _LineChartBase.__elements__ + ('gapDepth', 'hiLowLines', + 'upDownBars', 'marker', 'smooth', 'axId') + + def __init__(self, + gapDepth=None, + hiLowLines=None, + upDownBars=None, + marker=None, + smooth=None, + **kw + ): + self.gapDepth = gapDepth + self.hiLowLines = hiLowLines + self.upDownBars = upDownBars + self.marker = marker + self.smooth = smooth + self.x_axis = TextAxis() + self.y_axis = NumericAxis() + self.z_axis = SeriesAxis() + super(LineChart3D, self).__init__(**kw) diff --git a/.venv/Lib/site-packages/openpyxl/chart/marker.py b/.venv/Lib/site-packages/openpyxl/chart/marker.py new file mode 100644 index 00000000..468af0ff --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/chart/marker.py @@ -0,0 +1,90 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Alias, +) + +from openpyxl.descriptors.excel import( + ExtensionList, + _explicit_none, +) + +from openpyxl.descriptors.nested import ( + NestedBool, + NestedInteger, + NestedMinMax, + NestedNoneSet, +) + +from .layout import Layout +from .picture import PictureOptions +from .shapes import * +from .text import * +from .error_bar import * + + +class Marker(Serialisable): + + tagname = "marker" + + symbol = NestedNoneSet(values=(['circle', 'dash', 'diamond', 'dot', 'picture', + 'plus', 'square', 'star', 'triangle', 'x', 'auto']), + to_tree=_explicit_none) + size = NestedMinMax(min=2, max=72, allow_none=True) + spPr = Typed(expected_type=GraphicalProperties, allow_none=True) + graphicalProperties = Alias('spPr') + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('symbol', 'size', 'spPr') + + def __init__(self, + symbol=None, + size=None, + spPr=None, + extLst=None, + ): + self.symbol = symbol + self.size = size + if spPr is None: + spPr = GraphicalProperties() + self.spPr = spPr + + +class DataPoint(Serialisable): + + tagname = "dPt" + + idx = NestedInteger() + invertIfNegative = NestedBool(allow_none=True) + marker = Typed(expected_type=Marker, allow_none=True) + bubble3D = NestedBool(allow_none=True) + explosion = NestedInteger(allow_none=True) + spPr = Typed(expected_type=GraphicalProperties, allow_none=True) + graphicalProperties = Alias('spPr') + pictureOptions = Typed(expected_type=PictureOptions, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('idx', 'invertIfNegative', 'marker', 'bubble3D', + 'explosion', 'spPr', 'pictureOptions') + + def __init__(self, + idx=None, + invertIfNegative=None, + marker=None, + bubble3D=None, + explosion=None, + spPr=None, + pictureOptions=None, + extLst=None, + ): + self.idx = idx + self.invertIfNegative = invertIfNegative + self.marker = marker + self.bubble3D = bubble3D + self.explosion = explosion + if spPr is None: + spPr = GraphicalProperties() + self.spPr = spPr + self.pictureOptions = pictureOptions diff --git a/.venv/Lib/site-packages/openpyxl/chart/picture.py b/.venv/Lib/site-packages/openpyxl/chart/picture.py new file mode 100644 index 00000000..fb6626d7 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/chart/picture.py @@ -0,0 +1,35 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable + +from openpyxl.descriptors.nested import ( + NestedBool, + NestedFloat, + NestedMinMax, + NestedNoneSet, +) + +class PictureOptions(Serialisable): + + tagname = "pictureOptions" + + applyToFront = NestedBool(allow_none=True, nested=True) + applyToSides = NestedBool(allow_none=True, nested=True) + applyToEnd = NestedBool(allow_none=True, nested=True) + pictureFormat = NestedNoneSet(values=(['stretch', 'stack', 'stackScale']), nested=True) + pictureStackUnit = NestedFloat(allow_none=True, nested=True) + + __elements__ = ('applyToFront', 'applyToSides', 'applyToEnd', 'pictureFormat', 'pictureStackUnit') + + def __init__(self, + applyToFront=None, + applyToSides=None, + applyToEnd=None, + pictureFormat=None, + pictureStackUnit=None, + ): + self.applyToFront = applyToFront + self.applyToSides = applyToSides + self.applyToEnd = applyToEnd + self.pictureFormat = pictureFormat + self.pictureStackUnit = pictureStackUnit diff --git a/.venv/Lib/site-packages/openpyxl/chart/pie_chart.py b/.venv/Lib/site-packages/openpyxl/chart/pie_chart.py new file mode 100644 index 00000000..379a0ae6 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/chart/pie_chart.py @@ -0,0 +1,177 @@ +#Autogenerated schema +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Bool, + MinMax, + Integer, + NoneSet, + Float, + Alias, + Sequence, +) +from openpyxl.descriptors.excel import ExtensionList, Percentage +from openpyxl.descriptors.nested import ( + NestedBool, + NestedMinMax, + NestedInteger, + NestedFloat, + NestedNoneSet, + NestedSet, +) +from openpyxl.descriptors.sequence import ValueSequence + +from ._chart import ChartBase +from .axis import ChartLines +from .descriptors import NestedGapAmount +from .series import Series +from .label import DataLabelList + + +class _PieChartBase(ChartBase): + + varyColors = NestedBool(allow_none=True) + ser = Sequence(expected_type=Series, allow_none=True) + dLbls = Typed(expected_type=DataLabelList, allow_none=True) + dataLabels = Alias("dLbls") + + _series_type = "pie" + + __elements__ = ('varyColors', 'ser', 'dLbls') + + def __init__(self, + varyColors=True, + ser=(), + dLbls=None, + ): + self.varyColors = varyColors + self.ser = ser + self.dLbls = dLbls + super(_PieChartBase, self).__init__() + + + +class PieChart(_PieChartBase): + + tagname = "pieChart" + + varyColors = _PieChartBase.varyColors + ser = _PieChartBase.ser + dLbls = _PieChartBase.dLbls + + firstSliceAng = NestedMinMax(min=0, max=360) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = _PieChartBase.__elements__ + ('firstSliceAng', ) + + def __init__(self, + firstSliceAng=0, + extLst=None, + **kw + ): + self.firstSliceAng = firstSliceAng + super(PieChart, self).__init__(**kw) + + +class PieChart3D(_PieChartBase): + + tagname = "pie3DChart" + + varyColors = _PieChartBase.varyColors + ser = _PieChartBase.ser + dLbls = _PieChartBase.dLbls + + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = _PieChartBase.__elements__ + + +class DoughnutChart(_PieChartBase): + + tagname = "doughnutChart" + + varyColors = _PieChartBase.varyColors + ser = _PieChartBase.ser + dLbls = _PieChartBase.dLbls + + firstSliceAng = NestedMinMax(min=0, max=360) + holeSize = NestedMinMax(min=1, max=90, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = _PieChartBase.__elements__ + ('firstSliceAng', 'holeSize') + + def __init__(self, + firstSliceAng=0, + holeSize=10, + extLst=None, + **kw + ): + self.firstSliceAng = firstSliceAng + self.holeSize = holeSize + super(DoughnutChart, self).__init__(**kw) + + +class CustomSplit(Serialisable): + + tagname = "custSplit" + + secondPiePt = ValueSequence(expected_type=int) + + __elements__ = ('secondPiePt',) + + def __init__(self, + secondPiePt=(), + ): + self.secondPiePt = secondPiePt + + +class ProjectedPieChart(_PieChartBase): + + """ + From the spec 21.2.2.126 + + This element contains the pie of pie or bar of pie series on this + chart. Only the first series shall be displayed. The splitType element + shall determine whether the splitPos and custSplit elements apply. + """ + + tagname = "ofPieChart" + + varyColors = _PieChartBase.varyColors + ser = _PieChartBase.ser + dLbls = _PieChartBase.dLbls + + ofPieType = NestedSet(values=(['pie', 'bar'])) + type = Alias('ofPieType') + gapWidth = NestedGapAmount() + splitType = NestedNoneSet(values=(['auto', 'cust', 'percent', 'pos', 'val'])) + splitPos = NestedFloat(allow_none=True) + custSplit = Typed(expected_type=CustomSplit, allow_none=True) + secondPieSize = NestedMinMax(min=5, max=200, allow_none=True) + serLines = Typed(expected_type=ChartLines, allow_none=True) + join_lines = Alias('serLines') + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = _PieChartBase.__elements__ + ('ofPieType', 'gapWidth', + 'splitType', 'splitPos', 'custSplit', 'secondPieSize', 'serLines') + + def __init__(self, + ofPieType="pie", + gapWidth=None, + splitType="auto", + splitPos=None, + custSplit=None, + secondPieSize=75, + serLines=None, + extLst=None, + **kw + ): + self.ofPieType = ofPieType + self.gapWidth = gapWidth + self.splitType = splitType + self.splitPos = splitPos + self.custSplit = custSplit + self.secondPieSize = secondPieSize + if serLines is None: + self.serLines = ChartLines() + super(ProjectedPieChart, self).__init__(**kw) diff --git a/.venv/Lib/site-packages/openpyxl/chart/pivot.py b/.venv/Lib/site-packages/openpyxl/chart/pivot.py new file mode 100644 index 00000000..09fde3a9 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/chart/pivot.py @@ -0,0 +1,65 @@ + +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Alias, + Typed, +) +from openpyxl.descriptors.nested import NestedInteger, NestedText +from openpyxl.descriptors.excel import ExtensionList + +from .label import DataLabel +from .marker import Marker +from .shapes import GraphicalProperties +from .text import RichText + + +class PivotSource(Serialisable): + + tagname = "pivotSource" + + name = NestedText(expected_type=str) + fmtId = NestedInteger(expected_type=int) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('name', 'fmtId') + + def __init__(self, + name=None, + fmtId=None, + extLst=None, + ): + self.name = name + self.fmtId = fmtId + + +class PivotFormat(Serialisable): + + tagname = "pivotFmt" + + idx = NestedInteger(nested=True) + spPr = Typed(expected_type=GraphicalProperties, allow_none=True) + graphicalProperties = Alias("spPr") + txPr = Typed(expected_type=RichText, allow_none=True) + TextBody = Alias("txPr") + marker = Typed(expected_type=Marker, allow_none=True) + dLbl = Typed(expected_type=DataLabel, allow_none=True) + DataLabel = Alias("dLbl") + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('idx', 'spPr', 'txPr', 'marker', 'dLbl') + + def __init__(self, + idx=0, + spPr=None, + txPr=None, + marker=None, + dLbl=None, + extLst=None, + ): + self.idx = idx + self.spPr = spPr + self.txPr = txPr + self.marker = marker + self.dLbl = dLbl diff --git a/.venv/Lib/site-packages/openpyxl/chart/plotarea.py b/.venv/Lib/site-packages/openpyxl/chart/plotarea.py new file mode 100644 index 00000000..8eaadc2a --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/chart/plotarea.py @@ -0,0 +1,162 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Alias, +) +from openpyxl.descriptors.excel import ( + ExtensionList, +) +from openpyxl.descriptors.sequence import ( + MultiSequence, + MultiSequencePart, +) +from openpyxl.descriptors.nested import ( + NestedBool, +) + +from ._3d import _3DBase +from .area_chart import AreaChart, AreaChart3D +from .bar_chart import BarChart, BarChart3D +from .bubble_chart import BubbleChart +from .line_chart import LineChart, LineChart3D +from .pie_chart import PieChart, PieChart3D, ProjectedPieChart, DoughnutChart +from .radar_chart import RadarChart +from .scatter_chart import ScatterChart +from .stock_chart import StockChart +from .surface_chart import SurfaceChart, SurfaceChart3D +from .layout import Layout +from .shapes import GraphicalProperties +from .text import RichText + +from .axis import ( + NumericAxis, + TextAxis, + SeriesAxis, + DateAxis, +) + + +class DataTable(Serialisable): + + tagname = "dTable" + + showHorzBorder = NestedBool(allow_none=True) + showVertBorder = NestedBool(allow_none=True) + showOutline = NestedBool(allow_none=True) + showKeys = NestedBool(allow_none=True) + spPr = Typed(expected_type=GraphicalProperties, allow_none=True) + graphicalProperties = Alias('spPr') + txPr = Typed(expected_type=RichText, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('showHorzBorder', 'showVertBorder', 'showOutline', + 'showKeys', 'spPr', 'txPr') + + def __init__(self, + showHorzBorder=None, + showVertBorder=None, + showOutline=None, + showKeys=None, + spPr=None, + txPr=None, + extLst=None, + ): + self.showHorzBorder = showHorzBorder + self.showVertBorder = showVertBorder + self.showOutline = showOutline + self.showKeys = showKeys + self.spPr = spPr + self.txPr = txPr + + +class PlotArea(Serialisable): + + tagname = "plotArea" + + layout = Typed(expected_type=Layout, allow_none=True) + dTable = Typed(expected_type=DataTable, allow_none=True) + spPr = Typed(expected_type=GraphicalProperties, allow_none=True) + graphicalProperties = Alias("spPr") + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + # at least one chart + _charts = MultiSequence() + areaChart = MultiSequencePart(expected_type=AreaChart, store="_charts") + area3DChart = MultiSequencePart(expected_type=AreaChart3D, store="_charts") + lineChart = MultiSequencePart(expected_type=LineChart, store="_charts") + line3DChart = MultiSequencePart(expected_type=LineChart3D, store="_charts") + stockChart = MultiSequencePart(expected_type=StockChart, store="_charts") + radarChart = MultiSequencePart(expected_type=RadarChart, store="_charts") + scatterChart = MultiSequencePart(expected_type=ScatterChart, store="_charts") + pieChart = MultiSequencePart(expected_type=PieChart, store="_charts") + pie3DChart = MultiSequencePart(expected_type=PieChart3D, store="_charts") + doughnutChart = MultiSequencePart(expected_type=DoughnutChart, store="_charts") + barChart = MultiSequencePart(expected_type=BarChart, store="_charts") + bar3DChart = MultiSequencePart(expected_type=BarChart3D, store="_charts") + ofPieChart = MultiSequencePart(expected_type=ProjectedPieChart, store="_charts") + surfaceChart = MultiSequencePart(expected_type=SurfaceChart, store="_charts") + surface3DChart = MultiSequencePart(expected_type=SurfaceChart3D, store="_charts") + bubbleChart = MultiSequencePart(expected_type=BubbleChart, store="_charts") + + # axes + _axes = MultiSequence() + valAx = MultiSequencePart(expected_type=NumericAxis, store="_axes") + catAx = MultiSequencePart(expected_type=TextAxis, store="_axes") + dateAx = MultiSequencePart(expected_type=DateAxis, store="_axes") + serAx = MultiSequencePart(expected_type=SeriesAxis, store="_axes") + + __elements__ = ('layout', '_charts', '_axes', 'dTable', 'spPr') + + def __init__(self, + layout=None, + dTable=None, + spPr=None, + _charts=(), + _axes=(), + extLst=None, + ): + self.layout = layout + self.dTable = dTable + self.spPr = spPr + self._charts = _charts + self._axes = _axes + + + def to_tree(self, tagname=None, idx=None, namespace=None): + axIds = {ax.axId for ax in self._axes} + for chart in self._charts: + for id, axis in chart._axes.items(): + if id not in axIds: + setattr(self, axis.tagname, axis) + axIds.add(id) + + return super(PlotArea, self).to_tree(tagname) + + + @classmethod + def from_tree(cls, node): + self = super(PlotArea, cls).from_tree(node) + axes = dict((axis.axId, axis) for axis in self._axes) + for chart in self._charts: + if isinstance(chart, (ScatterChart, BubbleChart)): + x, y = (axes[axId] for axId in chart.axId) + chart.x_axis = x + chart.y_axis = y + continue + + for axId in chart.axId: + axis = axes.get(axId) + if axis is None and isinstance(chart, _3DBase): + # Series Axis can be optional + chart.z_axis = None + continue + if axis.tagname in ("catAx", "dateAx"): + chart.x_axis = axis + elif axis.tagname == "valAx": + chart.y_axis = axis + elif axis.tagname == "serAx": + chart.z_axis = axis + + return self diff --git a/.venv/Lib/site-packages/openpyxl/chart/print_settings.py b/.venv/Lib/site-packages/openpyxl/chart/print_settings.py new file mode 100644 index 00000000..6a8fc2fa --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/chart/print_settings.py @@ -0,0 +1,57 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Float, + Typed, + Alias, +) + +from openpyxl.worksheet.page import PrintPageSetup +from openpyxl.worksheet.header_footer import HeaderFooter + + +class PageMargins(Serialisable): + """ + Identical to openpyxl.worksheet.page.Pagemargins but element names are different :-/ + """ + tagname = "pageMargins" + + l = Float() + left = Alias('l') + r = Float() + right = Alias('r') + t = Float() + top = Alias('t') + b = Float() + bottom = Alias('b') + header = Float() + footer = Float() + + def __init__(self, l=0.75, r=0.75, t=1, b=1, header=0.5, footer=0.5): + self.l = l + self.r = r + self.t = t + self.b = b + self.header = header + self.footer = footer + + +class PrintSettings(Serialisable): + + tagname = "printSettings" + + headerFooter = Typed(expected_type=HeaderFooter, allow_none=True) + pageMargins = Typed(expected_type=PageMargins, allow_none=True) + pageSetup = Typed(expected_type=PrintPageSetup, allow_none=True) + + __elements__ = ("headerFooter", "pageMargins", "pageMargins") + + def __init__(self, + headerFooter=None, + pageMargins=None, + pageSetup=None, + ): + self.headerFooter = headerFooter + self.pageMargins = pageMargins + self.pageSetup = pageSetup diff --git a/.venv/Lib/site-packages/openpyxl/chart/radar_chart.py b/.venv/Lib/site-packages/openpyxl/chart/radar_chart.py new file mode 100644 index 00000000..40b32944 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/chart/radar_chart.py @@ -0,0 +1,55 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Sequence, + Typed, + Alias, +) +from openpyxl.descriptors.excel import ExtensionList +from openpyxl.descriptors.nested import ( + NestedBool, + NestedInteger, + NestedSet +) + +from ._chart import ChartBase +from .axis import TextAxis, NumericAxis +from .series import Series +from .label import DataLabelList + + +class RadarChart(ChartBase): + + tagname = "radarChart" + + radarStyle = NestedSet(values=(['standard', 'marker', 'filled'])) + type = Alias("radarStyle") + varyColors = NestedBool(nested=True, allow_none=True) + ser = Sequence(expected_type=Series, allow_none=True) + dLbls = Typed(expected_type=DataLabelList, allow_none=True) + dataLabels = Alias("dLbls") + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + _series_type = "radar" + + x_axis = Typed(expected_type=TextAxis) + y_axis = Typed(expected_type=NumericAxis) + + __elements__ = ('radarStyle', 'varyColors', 'ser', 'dLbls', 'axId') + + def __init__(self, + radarStyle="standard", + varyColors=None, + ser=(), + dLbls=None, + extLst=None, + **kw + ): + self.radarStyle = radarStyle + self.varyColors = varyColors + self.ser = ser + self.dLbls = dLbls + self.x_axis = TextAxis() + self.y_axis = NumericAxis() + super(RadarChart, self).__init__(**kw) diff --git a/.venv/Lib/site-packages/openpyxl/chart/reader.py b/.venv/Lib/site-packages/openpyxl/chart/reader.py new file mode 100644 index 00000000..832e3126 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/chart/reader.py @@ -0,0 +1,29 @@ +# Copyright (c) 2010-2022 openpyxl + +""" +Read a chart +""" + +def read_chart(chartspace): + cs = chartspace + plot = cs.chart.plotArea + + chart = plot._charts[0] + chart._charts = plot._charts + + chart.title = cs.chart.title + chart.display_blanks = cs.chart.dispBlanksAs + chart.visible_cells_only = cs.chart.plotVisOnly + chart.layout = plot.layout + chart.legend = cs.chart.legend + + # 3d attributes + chart.floor = cs.chart.floor + chart.sideWall = cs.chart.sideWall + chart.backWall = cs.chart.backWall + chart.pivotSource = cs.pivotSource + chart.pivotFormats = cs.chart.pivotFmts + chart.idx_base = min((s.idx for s in chart.series), default=0) + chart._reindex() + + return chart diff --git a/.venv/Lib/site-packages/openpyxl/chart/reference.py b/.venv/Lib/site-packages/openpyxl/chart/reference.py new file mode 100644 index 00000000..f96938f8 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/chart/reference.py @@ -0,0 +1,124 @@ +# Copyright (c) 2010-2022 openpyxl + +from itertools import chain + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + MinMax, + Typed, + String, + Strict, +) +from openpyxl.worksheet.worksheet import Worksheet +from openpyxl.utils import ( + get_column_letter, + range_to_tuple, + quote_sheetname +) + + +class DummyWorksheet: + + + def __init__(self, title): + self.title = title + + +class Reference(Strict): + + """ + Normalise cell range references + """ + + min_row = MinMax(min=1, max=1000000, expected_type=int) + max_row = MinMax(min=1, max=1000000, expected_type=int) + min_col = MinMax(min=1, max=16384, expected_type=int) + max_col = MinMax(min=1, max=16384, expected_type=int) + range_string = String(allow_none=True) + + def __init__(self, + worksheet=None, + min_col=None, + min_row=None, + max_col=None, + max_row=None, + range_string=None + ): + if range_string is not None: + sheetname, boundaries = range_to_tuple(range_string) + min_col, min_row, max_col, max_row = boundaries + worksheet = DummyWorksheet(sheetname) + + self.worksheet = worksheet + self.min_col = min_col + self.min_row = min_row + if max_col is None: + max_col = min_col + self.max_col = max_col + if max_row is None: + max_row = min_row + self.max_row = max_row + + + def __repr__(self): + return str(self) + + + def __str__(self): + fmt = u"{0}!${1}${2}:${3}${4}" + if (self.min_col == self.max_col + and self.min_row == self.max_row): + fmt = u"{0}!${1}${2}" + return fmt.format(self.sheetname, + get_column_letter(self.min_col), self.min_row, + get_column_letter(self.max_col), self.max_row + ) + + + __str__ = __str__ + + + + def __len__(self): + if self.min_row == self.max_row: + return 1 + self.max_col - self.min_col + return 1 + self.max_row - self.min_row + + + def __eq__(self, other): + return str(self) == str(other) + + + @property + def rows(self): + """ + Return all rows in the range + """ + for row in range(self.min_row, self.max_row+1): + yield Reference(self.worksheet, self.min_col, row, self.max_col, row) + + + @property + def cols(self): + """ + Return all columns in the range + """ + for col in range(self.min_col, self.max_col+1): + yield Reference(self.worksheet, col, self.min_row, col, self.max_row) + + + def pop(self): + """ + Return and remove the first cell + """ + cell = "{0}{1}".format(get_column_letter(self.min_col), self.min_row) + if self.min_row == self.max_row: + self.min_col += 1 + else: + self.min_row += 1 + return cell + + + @property + def sheetname(self): + return quote_sheetname(self.worksheet.title) diff --git a/.venv/Lib/site-packages/openpyxl/chart/scatter_chart.py b/.venv/Lib/site-packages/openpyxl/chart/scatter_chart.py new file mode 100644 index 00000000..25e9b972 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/chart/scatter_chart.py @@ -0,0 +1,53 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Sequence, + Alias +) +from openpyxl.descriptors.excel import ExtensionList +from openpyxl.descriptors.nested import ( + NestedNoneSet, + NestedBool, +) + +from ._chart import ChartBase +from .axis import NumericAxis +from .series import XYSeries +from .label import DataLabelList + + +class ScatterChart(ChartBase): + + tagname = "scatterChart" + + scatterStyle = NestedNoneSet(values=(['line', 'lineMarker', 'marker', 'smooth', 'smoothMarker'])) + varyColors = NestedBool(allow_none=True) + ser = Sequence(expected_type=XYSeries, allow_none=True) + dLbls = Typed(expected_type=DataLabelList, allow_none=True) + dataLabels = Alias("dLbls") + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + x_axis = Typed(expected_type=NumericAxis) + y_axis = Typed(expected_type=NumericAxis) + + _series_type = "scatter" + + __elements__ = ('scatterStyle', 'varyColors', 'ser', 'dLbls', 'axId',) + + def __init__(self, + scatterStyle=None, + varyColors=None, + ser=(), + dLbls=None, + extLst=None, + **kw + ): + self.scatterStyle = scatterStyle + self.varyColors = varyColors + self.ser = ser + self.dLbls = dLbls + self.x_axis = NumericAxis(axId=10, crossAx=20) + self.y_axis = NumericAxis(axId=20, crossAx=10) + super(ScatterChart, self).__init__(**kw) diff --git a/.venv/Lib/site-packages/openpyxl/chart/series.py b/.venv/Lib/site-packages/openpyxl/chart/series.py new file mode 100644 index 00000000..02751916 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/chart/series.py @@ -0,0 +1,197 @@ +# Copyright (c) 2010-2022 openpyxl + + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + String, + Integer, + Bool, + Alias, + Sequence, +) +from openpyxl.descriptors.excel import ExtensionList +from openpyxl.descriptors.nested import ( + NestedInteger, + NestedBool, + NestedNoneSet, + NestedText, +) + +from .shapes import GraphicalProperties +from .data_source import ( + AxDataSource, + NumDataSource, + NumRef, + StrRef, +) +from .error_bar import ErrorBars +from .label import DataLabelList +from .marker import DataPoint, PictureOptions, Marker +from .trendline import Trendline + +attribute_mapping = { + 'area': ('idx', 'order', 'tx', 'spPr', 'pictureOptions', 'dPt', 'dLbls', 'errBars', + 'trendline', 'cat', 'val',), + 'bar':('idx', 'order','tx', 'spPr', 'invertIfNegative', 'pictureOptions', 'dPt', + 'dLbls', 'trendline', 'errBars', 'cat', 'val', 'shape'), + 'bubble':('idx','order', 'tx', 'spPr', 'invertIfNegative', 'dPt', 'dLbls', + 'trendline', 'errBars', 'xVal', 'yVal', 'bubbleSize', 'bubble3D'), + 'line':('idx', 'order', 'tx', 'spPr', 'marker', 'dPt', 'dLbls', 'trendline', + 'errBars', 'cat', 'val', 'smooth'), + 'pie':('idx', 'order', 'tx', 'spPr', 'explosion', 'dPt', 'dLbls', 'cat', 'val'), + 'radar':('idx', 'order', 'tx', 'spPr', 'marker', 'dPt', 'dLbls', 'cat', 'val'), + 'scatter':('idx', 'order', 'tx', 'spPr', 'marker', 'dPt', 'dLbls', 'trendline', + 'errBars', 'xVal', 'yVal', 'smooth'), + 'surface':('idx', 'order', 'tx', 'spPr', 'cat', 'val'), + } + + +class SeriesLabel(Serialisable): + + tagname = "tx" + + strRef = Typed(expected_type=StrRef, allow_none=True) + v = NestedText(expected_type=str, allow_none=True) + value = Alias('v') + + __elements__ = ('strRef', 'v') + + def __init__(self, + strRef=None, + v=None): + self.strRef = strRef + self.v = v + + +class Series(Serialisable): + + """ + Generic series object. Should not be instantiated directly. + User the chart.Series factory instead. + """ + + tagname = "ser" + + idx = NestedInteger() + order = NestedInteger() + tx = Typed(expected_type=SeriesLabel, allow_none=True) + title = Alias('tx') + spPr = Typed(expected_type=GraphicalProperties, allow_none=True) + graphicalProperties = Alias('spPr') + + # area chart + pictureOptions = Typed(expected_type=PictureOptions, allow_none=True) + dPt = Sequence(expected_type=DataPoint, allow_none=True) + data_points = Alias("dPt") + dLbls = Typed(expected_type=DataLabelList, allow_none=True) + labels = Alias("dLbls") + trendline = Typed(expected_type=Trendline, allow_none=True) + errBars = Typed(expected_type=ErrorBars, allow_none=True) + cat = Typed(expected_type=AxDataSource, allow_none=True) + identifiers = Alias("cat") + val = Typed(expected_type=NumDataSource, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + #bar chart + invertIfNegative = NestedBool(allow_none=True) + shape = NestedNoneSet(values=(['cone', 'coneToMax', 'box', 'cylinder', 'pyramid', 'pyramidToMax'])) + + #bubble chart + xVal = Typed(expected_type=AxDataSource, allow_none=True) + yVal = Typed(expected_type=NumDataSource, allow_none=True) + bubbleSize = Typed(expected_type=NumDataSource, allow_none=True) + zVal = Alias("bubbleSize") + bubble3D = NestedBool(allow_none=True) + + #line chart + marker = Typed(expected_type=Marker, allow_none=True) + smooth = NestedBool(allow_none=True) + + #pie chart + explosion = NestedInteger(allow_none=True) + + __elements__ = () + + + def __init__(self, + idx=0, + order=0, + tx=None, + spPr=None, + pictureOptions=None, + dPt=(), + dLbls=None, + trendline=None, + errBars=None, + cat=None, + val=None, + invertIfNegative=None, + shape=None, + xVal=None, + yVal=None, + bubbleSize=None, + bubble3D=None, + marker=None, + smooth=None, + explosion=None, + extLst=None, + ): + self.idx = idx + self.order = order + self.tx = tx + if spPr is None: + spPr = GraphicalProperties() + self.spPr = spPr + self.pictureOptions = pictureOptions + self.dPt = dPt + self.dLbls = dLbls + self.trendline = trendline + self.errBars = errBars + self.cat = cat + self.val = val + self.invertIfNegative = invertIfNegative + self.shape = shape + self.xVal = xVal + self.yVal = yVal + self.bubbleSize = bubbleSize + self.bubble3D = bubble3D + if marker is None: + marker = Marker() + self.marker = marker + self.smooth = smooth + self.explosion = explosion + + + def to_tree(self, tagname=None, idx=None): + """The index can need rebasing""" + if idx is not None: + if self.order == self.idx: + self.order = idx # rebase the order if the index has been rebased + self.idx = idx + return super(Series, self).to_tree(tagname) + + +class XYSeries(Series): + + """Dedicated series for charts that have x and y series""" + + idx = Series.idx + order = Series.order + tx = Series.tx + spPr = Series.spPr + + dPt = Series.dPt + dLbls = Series.dLbls + trendline = Series.trendline + errBars = Series.errBars + xVal = Series.xVal + yVal = Series.yVal + + invertIfNegative = Series.invertIfNegative + + bubbleSize = Series.bubbleSize + bubble3D = Series.bubble3D + + marker = Series.marker + smooth = Series.smooth diff --git a/.venv/Lib/site-packages/openpyxl/chart/series_factory.py b/.venv/Lib/site-packages/openpyxl/chart/series_factory.py new file mode 100644 index 00000000..56e10538 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/chart/series_factory.py @@ -0,0 +1,41 @@ +# Copyright (c) 2010-2022 openpyxl + +from .data_source import NumDataSource, NumRef, AxDataSource +from .reference import Reference +from .series import Series, XYSeries, SeriesLabel, StrRef +from openpyxl.utils import rows_from_range, quote_sheetname + + +def SeriesFactory(values, xvalues=None, zvalues=None, title=None, title_from_data=False): + """ + Convenience Factory for creating chart data series. + """ + + if not isinstance(values, Reference): + values = Reference(range_string=values) + + if title_from_data: + cell = values.pop() + title = u"{0}!{1}".format(values.sheetname, cell) + title = SeriesLabel(strRef=StrRef(title)) + elif title is not None: + title = SeriesLabel(v=title) + + source = NumDataSource(numRef=NumRef(f=values)) + if xvalues is not None: + if not isinstance(xvalues, Reference): + xvalues = Reference(range_string=xvalues) + series = XYSeries() + series.yVal = source + series.xVal = AxDataSource(numRef=NumRef(f=xvalues)) + if zvalues is not None: + if not isinstance(zvalues, Reference): + zvalues = Reference(range_string=zvalues) + series.zVal = NumDataSource(NumRef(f=zvalues)) + else: + series = Series() + series.val = source + + if title is not None: + series.title = title + return series diff --git a/.venv/Lib/site-packages/openpyxl/chart/shapes.py b/.venv/Lib/site-packages/openpyxl/chart/shapes.py new file mode 100644 index 00000000..f9367ee7 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/chart/shapes.py @@ -0,0 +1,89 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Alias +) +from openpyxl.descriptors.nested import ( + EmptyTag +) +from openpyxl.drawing.colors import ColorChoiceDescriptor +from openpyxl.drawing.fill import * +from openpyxl.drawing.line import LineProperties +from openpyxl.drawing.geometry import ( + Shape3D, + Scene3D, + Transform2D, + CustomGeometry2D, + PresetGeometry2D, +) + + +class GraphicalProperties(Serialisable): + + """ + Somewhat vaguely 21.2.2.197 says this: + + This element specifies the formatting for the parent chart element. The + custGeom, prstGeom, scene3d, and xfrm elements are not supported. The + bwMode attribute is not supported. + + This doesn't leave much. And the element is used in different places. + """ + + tagname = "spPr" + + bwMode = NoneSet(values=(['clr', 'auto', 'gray', 'ltGray', 'invGray', + 'grayWhite', 'blackGray', 'blackWhite', 'black', 'white', 'hidden'] + ) + ) + + xfrm = Typed(expected_type=Transform2D, allow_none=True) + transform = Alias('xfrm') + custGeom = Typed(expected_type=CustomGeometry2D, allow_none=True) # either or + prstGeom = Typed(expected_type=PresetGeometry2D, allow_none=True) + + # fills one of + noFill = EmptyTag(namespace=DRAWING_NS) + solidFill = ColorChoiceDescriptor() + gradFill = Typed(expected_type=GradientFillProperties, allow_none=True) + pattFill = Typed(expected_type=PatternFillProperties, allow_none=True) + + ln = Typed(expected_type=LineProperties, allow_none=True) + line = Alias('ln') + scene3d = Typed(expected_type=Scene3D, allow_none=True) + sp3d = Typed(expected_type=Shape3D, allow_none=True) + shape3D = Alias('sp3d') + extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True) + + __elements__ = ('xfrm', 'prstGeom', 'noFill', 'solidFill', 'gradFill', 'pattFill', + 'ln', 'scene3d', 'sp3d') + + def __init__(self, + bwMode=None, + xfrm=None, + noFill=None, + solidFill=None, + gradFill=None, + pattFill=None, + ln=None, + scene3d=None, + custGeom=None, + prstGeom=None, + sp3d=None, + extLst=None, + ): + self.bwMode = bwMode + self.xfrm = xfrm + self.noFill = noFill + self.solidFill = solidFill + self.gradFill = gradFill + self.pattFill = pattFill + if ln is None: + ln = LineProperties() + self.ln = ln + self.custGeom = custGeom + self.prstGeom = prstGeom + self.scene3d = scene3d + self.sp3d = sp3d diff --git a/.venv/Lib/site-packages/openpyxl/chart/stock_chart.py b/.venv/Lib/site-packages/openpyxl/chart/stock_chart.py new file mode 100644 index 00000000..36f1195f --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/chart/stock_chart.py @@ -0,0 +1,54 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Sequence, + Alias, +) +from openpyxl.descriptors.excel import ExtensionList + +from ._chart import ChartBase +from .axis import TextAxis, NumericAxis, ChartLines +from .updown_bars import UpDownBars +from .label import DataLabelList +from .series import Series + + +class StockChart(ChartBase): + + tagname = "stockChart" + + ser = Sequence(expected_type=Series) #min 3, max4 + dLbls = Typed(expected_type=DataLabelList, allow_none=True) + dataLabels = Alias('dLbls') + dropLines = Typed(expected_type=ChartLines, allow_none=True) + hiLowLines = Typed(expected_type=ChartLines, allow_none=True) + upDownBars = Typed(expected_type=UpDownBars, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + x_axis = Typed(expected_type=TextAxis) + y_axis = Typed(expected_type=NumericAxis) + + _series_type = "line" + + __elements__ = ('ser', 'dLbls', 'dropLines', 'hiLowLines', 'upDownBars', + 'axId') + + def __init__(self, + ser=(), + dLbls=None, + dropLines=None, + hiLowLines=None, + upDownBars=None, + extLst=None, + **kw + ): + self.ser = ser + self.dLbls = dLbls + self.dropLines = dropLines + self.hiLowLines = hiLowLines + self.upDownBars = upDownBars + self.x_axis = TextAxis() + self.y_axis = NumericAxis() + super(StockChart, self).__init__(**kw) diff --git a/.venv/Lib/site-packages/openpyxl/chart/surface_chart.py b/.venv/Lib/site-packages/openpyxl/chart/surface_chart.py new file mode 100644 index 00000000..dc058d20 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/chart/surface_chart.py @@ -0,0 +1,119 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Integer, + Bool, + Alias, + Sequence, +) +from openpyxl.descriptors.excel import ExtensionList +from openpyxl.descriptors.nested import ( + NestedInteger, + NestedBool, +) + +from ._chart import ChartBase +from ._3d import _3DBase +from .axis import TextAxis, NumericAxis, SeriesAxis +from .shapes import GraphicalProperties +from .series import Series + + +class BandFormat(Serialisable): + + tagname = "bandFmt" + + idx = NestedInteger() + spPr = Typed(expected_type=GraphicalProperties, allow_none=True) + graphicalProperties = Alias("spPr") + + __elements__ = ('idx', 'spPr') + + def __init__(self, + idx=0, + spPr=None, + ): + self.idx = idx + self.spPr = spPr + + +class BandFormatList(Serialisable): + + tagname = "bandFmts" + + bandFmt = Sequence(expected_type=BandFormat, allow_none=True) + + __elements__ = ('bandFmt',) + + def __init__(self, + bandFmt=(), + ): + self.bandFmt = bandFmt + + +class _SurfaceChartBase(ChartBase): + + wireframe = NestedBool(allow_none=True) + ser = Sequence(expected_type=Series, allow_none=True) + bandFmts = Typed(expected_type=BandFormatList, allow_none=True) + + _series_type = "surface" + + __elements__ = ('wireframe', 'ser', 'bandFmts') + + def __init__(self, + wireframe=None, + ser=(), + bandFmts=None, + **kw + ): + self.wireframe = wireframe + self.ser = ser + self.bandFmts = bandFmts + super(_SurfaceChartBase, self).__init__(**kw) + + +class SurfaceChart3D(_SurfaceChartBase, _3DBase): + + tagname = "surface3DChart" + + wireframe = _SurfaceChartBase.wireframe + ser = _SurfaceChartBase.ser + bandFmts = _SurfaceChartBase.bandFmts + + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + x_axis = Typed(expected_type=TextAxis) + y_axis = Typed(expected_type=NumericAxis) + z_axis = Typed(expected_type=SeriesAxis) + + __elements__ = _SurfaceChartBase.__elements__ + ('axId',) + + def __init__(self, **kw): + self.x_axis = TextAxis() + self.y_axis = NumericAxis() + self.z_axis = SeriesAxis() + super(SurfaceChart3D, self).__init__(**kw) + + +class SurfaceChart(SurfaceChart3D): + + tagname = "surfaceChart" + + wireframe = _SurfaceChartBase.wireframe + ser = _SurfaceChartBase.ser + bandFmts = _SurfaceChartBase.bandFmts + + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = SurfaceChart3D.__elements__ + + def __init__(self, **kw): + super(SurfaceChart, self).__init__(**kw) + self.y_axis.delete = True + self.view3D.x_rotation = 90 + self.view3D.y_rotation = 0 + self.view3D.perspective = False + self.view3D.right_angle_axes = False diff --git a/.venv/Lib/site-packages/openpyxl/chart/text.py b/.venv/Lib/site-packages/openpyxl/chart/text.py new file mode 100644 index 00000000..cd820738 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/chart/text.py @@ -0,0 +1,78 @@ +# Copyright (c) 2010-2022 openpyxl +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Alias, + Sequence, +) + + +from openpyxl.drawing.text import ( + RichTextProperties, + ListStyle, + Paragraph, +) + +from .data_source import StrRef + + +class RichText(Serialisable): + + """ + From the specification: 21.2.2.216 + + This element specifies text formatting. The lstStyle element is not supported. + """ + + tagname = "rich" + + bodyPr = Typed(expected_type=RichTextProperties) + properties = Alias("bodyPr") + lstStyle = Typed(expected_type=ListStyle, allow_none=True) + p = Sequence(expected_type=Paragraph) + paragraphs = Alias('p') + + __elements__ = ("bodyPr", "lstStyle", "p") + + def __init__(self, + bodyPr=None, + lstStyle=None, + p=None, + ): + if bodyPr is None: + bodyPr = RichTextProperties() + self.bodyPr = bodyPr + self.lstStyle = lstStyle + if p is None: + p = [Paragraph()] + self.p = p + + +class Text(Serialisable): + + """ + The value can be either a cell reference or a text element + If both are present then the reference will be used. + """ + + tagname = "tx" + + strRef = Typed(expected_type=StrRef, allow_none=True) + rich = Typed(expected_type=RichText, allow_none=True) + + __elements__ = ("strRef", "rich") + + def __init__(self, + strRef=None, + rich=None + ): + self.strRef = strRef + if rich is None: + rich = RichText() + self.rich = rich + + + def to_tree(self, tagname=None, idx=None, namespace=None): + if self.strRef and self.rich: + self.rich = None # can only have one + return super(Text, self).to_tree(tagname, idx, namespace) diff --git a/.venv/Lib/site-packages/openpyxl/chart/title.py b/.venv/Lib/site-packages/openpyxl/chart/title.py new file mode 100644 index 00000000..86fe8f99 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/chart/title.py @@ -0,0 +1,76 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Alias, +) + +from openpyxl.descriptors.excel import ExtensionList +from openpyxl.descriptors.nested import NestedBool + +from .text import Text, RichText +from .layout import Layout +from .shapes import GraphicalProperties + +from openpyxl.drawing.text import ( + Paragraph, + RegularTextRun, + LineBreak, + ParagraphProperties, + CharacterProperties, +) + + +class Title(Serialisable): + tagname = "title" + + tx = Typed(expected_type=Text, allow_none=True) + text = Alias('tx') + layout = Typed(expected_type=Layout, allow_none=True) + overlay = NestedBool(allow_none=True) + spPr = Typed(expected_type=GraphicalProperties, allow_none=True) + graphicalProperties = Alias('spPr') + txPr = Typed(expected_type=RichText, allow_none=True) + body = Alias('txPr') + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('tx', 'layout', 'overlay', 'spPr', 'txPr') + + def __init__(self, + tx=None, + layout=None, + overlay=None, + spPr=None, + txPr=None, + extLst=None, + ): + if tx is None: + tx = Text() + self.tx = tx + self.layout = layout + self.overlay = overlay + self.spPr = spPr + self.txPr = txPr + + + +def title_maker(text): + title = Title() + paraprops = ParagraphProperties() + paraprops.defRPr = CharacterProperties() + paras = [Paragraph(r=[RegularTextRun(t=s)], pPr=paraprops) for s in text.split("\n")] + + title.tx.rich.paragraphs = paras + return title + + +class TitleDescriptor(Typed): + + expected_type = Title + allow_none = True + + def __set__(self, instance, value): + if isinstance(value, str): + value = title_maker(value) + super(TitleDescriptor, self).__set__(instance, value) diff --git a/.venv/Lib/site-packages/openpyxl/chart/trendline.py b/.venv/Lib/site-packages/openpyxl/chart/trendline.py new file mode 100644 index 00000000..f3781a43 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/chart/trendline.py @@ -0,0 +1,98 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + String, + Alias +) +from openpyxl.descriptors.excel import ExtensionList +from openpyxl.descriptors.nested import ( + NestedBool, + NestedInteger, + NestedFloat, + NestedSet +) + +from .data_source import NumFmt +from .shapes import GraphicalProperties +from .text import RichText, Text +from .layout import Layout + + +class TrendlineLabel(Serialisable): + + tagname = "trendlineLbl" + + layout = Typed(expected_type=Layout, allow_none=True) + tx = Typed(expected_type=Text, allow_none=True) + numFmt = Typed(expected_type=NumFmt, allow_none=True) + spPr = Typed(expected_type=GraphicalProperties, allow_none=True) + graphicalProperties = Alias("spPr") + txPr = Typed(expected_type=RichText, allow_none=True) + textProperties = Alias("txPr") + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('layout', 'tx', 'numFmt', 'spPr', 'txPr') + + def __init__(self, + layout=None, + tx=None, + numFmt=None, + spPr=None, + txPr=None, + extLst=None, + ): + self.layout = layout + self.tx = tx + self.numFmt = numFmt + self.spPr = spPr + self.txPr = txPr + + +class Trendline(Serialisable): + + tagname = "trendline" + + name = String(allow_none=True) + spPr = Typed(expected_type=GraphicalProperties, allow_none=True) + graphicalProperties = Alias('spPr') + trendlineType = NestedSet(values=(['exp', 'linear', 'log', 'movingAvg', 'poly', 'power'])) + order = NestedInteger(allow_none=True) + period = NestedInteger(allow_none=True) + forward = NestedFloat(allow_none=True) + backward = NestedFloat(allow_none=True) + intercept = NestedFloat(allow_none=True) + dispRSqr = NestedBool(allow_none=True) + dispEq = NestedBool(allow_none=True) + trendlineLbl = Typed(expected_type=TrendlineLabel, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('name', 'spPr', 'trendlineType', 'order', 'period', + 'forward', 'backward', 'intercept', 'dispRSqr', 'dispEq', 'trendlineLbl') + + def __init__(self, + name=None, + spPr=None, + trendlineType='linear', + order=None, + period=None, + forward=None, + backward=None, + intercept=None, + dispRSqr=None, + dispEq=None, + trendlineLbl=None, + extLst=None, + ): + self.name = name + self.spPr = spPr + self.trendlineType = trendlineType + self.order = order + self.period = period + self.forward = forward + self.backward = backward + self.intercept = intercept + self.dispRSqr = dispRSqr + self.dispEq = dispEq + self.trendlineLbl = trendlineLbl diff --git a/.venv/Lib/site-packages/openpyxl/chart/updown_bars.py b/.venv/Lib/site-packages/openpyxl/chart/updown_bars.py new file mode 100644 index 00000000..6a6e1620 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/chart/updown_bars.py @@ -0,0 +1,31 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import Typed +from openpyxl.descriptors.excel import ExtensionList + +from .shapes import GraphicalProperties +from .axis import ChartLines +from .descriptors import NestedGapAmount + + +class UpDownBars(Serialisable): + + tagname = "upbars" + + gapWidth = NestedGapAmount() + upBars = Typed(expected_type=ChartLines, allow_none=True) + downBars = Typed(expected_type=ChartLines, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('gapWidth', 'upBars', 'downBars') + + def __init__(self, + gapWidth=150, + upBars=None, + downBars=None, + extLst=None, + ): + self.gapWidth = gapWidth + self.upBars = upBars + self.downBars = downBars diff --git a/.venv/Lib/site-packages/openpyxl/chartsheet/__init__.py b/.venv/Lib/site-packages/openpyxl/chartsheet/__init__.py new file mode 100644 index 00000000..08a391b4 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/chartsheet/__init__.py @@ -0,0 +1,3 @@ +# Copyright (c) 2010-2022 openpyxl + +from .chartsheet import Chartsheet diff --git a/.venv/Lib/site-packages/openpyxl/chartsheet/chartsheet.py b/.venv/Lib/site-packages/openpyxl/chartsheet/chartsheet.py new file mode 100644 index 00000000..a3395a49 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/chartsheet/chartsheet.py @@ -0,0 +1,109 @@ +# Copyright (c) 2010-2022 openpyxl + +from weakref import ref + +from openpyxl.descriptors import Typed, Set, Alias +from openpyxl.descriptors.excel import ExtensionList +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.drawing.spreadsheet_drawing import ( + AbsoluteAnchor, + SpreadsheetDrawing, +) +from openpyxl.worksheet.page import ( + PageMargins, + PrintPageSetup +) +from openpyxl.packaging.relationship import Relationship, RelationshipList +from openpyxl.worksheet.drawing import Drawing +from openpyxl.worksheet.header_footer import HeaderFooter +from openpyxl.workbook.child import _WorkbookChild +from openpyxl.xml.constants import SHEET_MAIN_NS, REL_NS + +from .relation import DrawingHF, SheetBackgroundPicture +from .properties import ChartsheetProperties +from .protection import ChartsheetProtection +from .views import ChartsheetViewList +from .custom import CustomChartsheetViews +from .publish import WebPublishItems + + +class Chartsheet(_WorkbookChild, Serialisable): + + tagname = "chartsheet" + _default_title = "Chart" + _rel_type = "chartsheet" + _path = "/xl/chartsheets/sheet{0}.xml" + mime_type = "application/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml" + + sheetPr = Typed(expected_type=ChartsheetProperties, allow_none=True) + sheetViews = Typed(expected_type=ChartsheetViewList) + sheetProtection = Typed(expected_type=ChartsheetProtection, allow_none=True) + customSheetViews = Typed(expected_type=CustomChartsheetViews, allow_none=True) + pageMargins = Typed(expected_type=PageMargins, allow_none=True) + pageSetup = Typed(expected_type=PrintPageSetup, allow_none=True) + drawing = Typed(expected_type=Drawing, allow_none=True) + drawingHF = Typed(expected_type=DrawingHF, allow_none=True) + picture = Typed(expected_type=SheetBackgroundPicture, allow_none=True) + webPublishItems = Typed(expected_type=WebPublishItems, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + sheet_state = Set(values=('visible', 'hidden', 'veryHidden')) + headerFooter = Typed(expected_type=HeaderFooter) + HeaderFooter = Alias('headerFooter') + + __elements__ = ( + 'sheetPr', 'sheetViews', 'sheetProtection', 'customSheetViews', + 'pageMargins', 'pageSetup', 'headerFooter', 'drawing', 'drawingHF', + 'picture', 'webPublishItems') + + __attrs__ = () + + def __init__(self, + sheetPr=None, + sheetViews=None, + sheetProtection=None, + customSheetViews=None, + pageMargins=None, + pageSetup=None, + headerFooter=None, + drawing=None, + drawingHF=None, + picture=None, + webPublishItems=None, + extLst=None, + parent=None, + title="", + sheet_state='visible', + ): + super(Chartsheet, self).__init__(parent, title) + self._charts = [] + self.sheetPr = sheetPr + if sheetViews is None: + sheetViews = ChartsheetViewList() + self.sheetViews = sheetViews + self.sheetProtection = sheetProtection + self.customSheetViews = customSheetViews + self.pageMargins = pageMargins + self.pageSetup = pageSetup + if headerFooter is not None: + self.headerFooter = headerFooter + self.drawing = Drawing("rId1") + self.drawingHF = drawingHF + self.picture = picture + self.webPublishItems = webPublishItems + self.sheet_state = sheet_state + + + def add_chart(self, chart): + chart.anchor = AbsoluteAnchor() + self._charts.append(chart) + + + def to_tree(self): + self._drawing = SpreadsheetDrawing() + self._drawing.charts = self._charts + tree = super(Chartsheet, self).to_tree() + if not self.headerFooter: + el = tree.find('headerFooter') + tree.remove(el) + tree.set("xmlns", SHEET_MAIN_NS) + return tree diff --git a/.venv/Lib/site-packages/openpyxl/chartsheet/custom.py b/.venv/Lib/site-packages/openpyxl/chartsheet/custom.py new file mode 100644 index 00000000..92003e79 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/chartsheet/custom.py @@ -0,0 +1,61 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.worksheet.header_footer import HeaderFooter + +from openpyxl.descriptors import ( + Bool, + Integer, + Set, + Typed, + Sequence +) +from openpyxl.descriptors.excel import Guid +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.worksheet.page import ( + PageMargins, + PrintPageSetup +) + + +class CustomChartsheetView(Serialisable): + tagname = "customSheetView" + + guid = Guid() + scale = Integer() + state = Set(values=(['visible', 'hidden', 'veryHidden'])) + zoomToFit = Bool(allow_none=True) + pageMargins = Typed(expected_type=PageMargins, allow_none=True) + pageSetup = Typed(expected_type=PrintPageSetup, allow_none=True) + headerFooter = Typed(expected_type=HeaderFooter, allow_none=True) + + __elements__ = ('pageMargins', 'pageSetup', 'headerFooter') + + def __init__(self, + guid=None, + scale=None, + state='visible', + zoomToFit=None, + pageMargins=None, + pageSetup=None, + headerFooter=None, + ): + self.guid = guid + self.scale = scale + self.state = state + self.zoomToFit = zoomToFit + self.pageMargins = pageMargins + self.pageSetup = pageSetup + self.headerFooter = headerFooter + + +class CustomChartsheetViews(Serialisable): + tagname = "customSheetViews" + + customSheetView = Sequence(expected_type=CustomChartsheetView, allow_none=True) + + __elements__ = ('customSheetView',) + + def __init__(self, + customSheetView=None, + ): + self.customSheetView = customSheetView diff --git a/.venv/Lib/site-packages/openpyxl/chartsheet/properties.py b/.venv/Lib/site-packages/openpyxl/chartsheet/properties.py new file mode 100644 index 00000000..78c92687 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/chartsheet/properties.py @@ -0,0 +1,28 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.descriptors import ( + Bool, + String, + Typed +) +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.styles import Color + + +class ChartsheetProperties(Serialisable): + tagname = "sheetPr" + + published = Bool(allow_none=True) + codeName = String(allow_none=True) + tabColor = Typed(expected_type=Color, allow_none=True) + + __elements__ = ('tabColor',) + + def __init__(self, + published=None, + codeName=None, + tabColor=None, + ): + self.published = published + self.codeName = codeName + self.tabColor = tabColor diff --git a/.venv/Lib/site-packages/openpyxl/chartsheet/protection.py b/.venv/Lib/site-packages/openpyxl/chartsheet/protection.py new file mode 100644 index 00000000..f76a306b --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/chartsheet/protection.py @@ -0,0 +1,41 @@ +import hashlib + +from openpyxl.descriptors import (Bool, Integer, String) +from openpyxl.descriptors.excel import Base64Binary +from openpyxl.descriptors.serialisable import Serialisable + +from openpyxl.worksheet.protection import ( + hash_password, + _Protected +) + + +class ChartsheetProtection(Serialisable, _Protected): + tagname = "sheetProtection" + + algorithmName = String(allow_none=True) + hashValue = Base64Binary(allow_none=True) + saltValue = Base64Binary(allow_none=True) + spinCount = Integer(allow_none=True) + content = Bool(allow_none=True) + objects = Bool(allow_none=True) + + __attrs__ = ("content", "objects", "password", "hashValue", "spinCount", "saltValue", "algorithmName") + + def __init__(self, + content=None, + objects=None, + hashValue=None, + spinCount=None, + saltValue=None, + algorithmName=None, + password=None, + ): + self.content = content + self.objects = objects + self.hashValue = hashValue + self.spinCount = spinCount + self.saltValue = saltValue + self.algorithmName = algorithmName + if password is not None: + self.password = password diff --git a/.venv/Lib/site-packages/openpyxl/chartsheet/publish.py b/.venv/Lib/site-packages/openpyxl/chartsheet/publish.py new file mode 100644 index 00000000..87d941b1 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/chartsheet/publish.py @@ -0,0 +1,58 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.descriptors import ( + Bool, + Integer, + String, + Set, + Sequence +) +from openpyxl.descriptors.serialisable import Serialisable + + +class WebPublishItem(Serialisable): + tagname = "webPublishItem" + + id = Integer() + divId = String() + sourceType = Set(values=(['sheet', 'printArea', 'autoFilter', 'range', 'chart', 'pivotTable', 'query', 'label'])) + sourceRef = String() + sourceObject = String(allow_none=True) + destinationFile = String() + title = String(allow_none=True) + autoRepublish = Bool(allow_none=True) + + def __init__(self, + id=None, + divId=None, + sourceType=None, + sourceRef=None, + sourceObject=None, + destinationFile=None, + title=None, + autoRepublish=None, + ): + self.id = id + self.divId = divId + self.sourceType = sourceType + self.sourceRef = sourceRef + self.sourceObject = sourceObject + self.destinationFile = destinationFile + self.title = title + self.autoRepublish = autoRepublish + + +class WebPublishItems(Serialisable): + tagname = "WebPublishItems" + + count = Integer(allow_none=True) + webPublishItem = Sequence(expected_type=WebPublishItem, ) + + __elements__ = ('webPublishItem',) + + def __init__(self, + count=None, + webPublishItem=None, + ): + self.count = len(webPublishItem) + self.webPublishItem = webPublishItem diff --git a/.venv/Lib/site-packages/openpyxl/chartsheet/relation.py b/.venv/Lib/site-packages/openpyxl/chartsheet/relation.py new file mode 100644 index 00000000..881a1053 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/chartsheet/relation.py @@ -0,0 +1,97 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.descriptors import ( + Integer, + Alias +) +from openpyxl.descriptors.excel import Relation +from openpyxl.descriptors.serialisable import Serialisable + + +class SheetBackgroundPicture(Serialisable): + tagname = "picture" + id = Relation() + + def __init__(self, id): + self.id = id + + +class DrawingHF(Serialisable): + id = Relation() + lho = Integer(allow_none=True) + leftHeaderOddPages = Alias('lho') + lhe = Integer(allow_none=True) + leftHeaderEvenPages = Alias('lhe') + lhf = Integer(allow_none=True) + leftHeaderFirstPage = Alias('lhf') + cho = Integer(allow_none=True) + centerHeaderOddPages = Alias('cho') + che = Integer(allow_none=True) + centerHeaderEvenPages = Alias('che') + chf = Integer(allow_none=True) + centerHeaderFirstPage = Alias('chf') + rho = Integer(allow_none=True) + rightHeaderOddPages = Alias('rho') + rhe = Integer(allow_none=True) + rightHeaderEvenPages = Alias('rhe') + rhf = Integer(allow_none=True) + rightHeaderFirstPage = Alias('rhf') + lfo = Integer(allow_none=True) + leftFooterOddPages = Alias('lfo') + lfe = Integer(allow_none=True) + leftFooterEvenPages = Alias('lfe') + lff = Integer(allow_none=True) + leftFooterFirstPage = Alias('lff') + cfo = Integer(allow_none=True) + centerFooterOddPages = Alias('cfo') + cfe = Integer(allow_none=True) + centerFooterEvenPages = Alias('cfe') + cff = Integer(allow_none=True) + centerFooterFirstPage = Alias('cff') + rfo = Integer(allow_none=True) + rightFooterOddPages = Alias('rfo') + rfe = Integer(allow_none=True) + rightFooterEvenPages = Alias('rfe') + rff = Integer(allow_none=True) + rightFooterFirstPage = Alias('rff') + + def __init__(self, + id=None, + lho=None, + lhe=None, + lhf=None, + cho=None, + che=None, + chf=None, + rho=None, + rhe=None, + rhf=None, + lfo=None, + lfe=None, + lff=None, + cfo=None, + cfe=None, + cff=None, + rfo=None, + rfe=None, + rff=None, + ): + self.id = id + self.lho = lho + self.lhe = lhe + self.lhf = lhf + self.cho = cho + self.che = che + self.chf = chf + self.rho = rho + self.rhe = rhe + self.rhf = rhf + self.lfo = lfo + self.lfe = lfe + self.lff = lff + self.cfo = cfo + self.cfe = cfe + self.cff = cff + self.rfo = rfo + self.rfe = rfe + self.rff = rff diff --git a/.venv/Lib/site-packages/openpyxl/chartsheet/views.py b/.venv/Lib/site-packages/openpyxl/chartsheet/views.py new file mode 100644 index 00000000..b4666f94 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/chartsheet/views.py @@ -0,0 +1,51 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.descriptors import ( + Bool, + Integer, + Typed, + Sequence +) +from openpyxl.descriptors.excel import ExtensionList +from openpyxl.descriptors.serialisable import Serialisable + + +class ChartsheetView(Serialisable): + tagname = "sheetView" + + tabSelected = Bool(allow_none=True) + zoomScale = Integer(allow_none=True) + workbookViewId = Integer() + zoomToFit = Bool(allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = () + + def __init__(self, + tabSelected=None, + zoomScale=None, + workbookViewId=0, + zoomToFit=None, + extLst=None, + ): + self.tabSelected = tabSelected + self.zoomScale = zoomScale + self.workbookViewId = workbookViewId + self.zoomToFit = zoomToFit + + +class ChartsheetViewList(Serialisable): + tagname = "sheetViews" + + sheetView = Sequence(expected_type=ChartsheetView, ) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('sheetView',) + + def __init__(self, + sheetView=None, + extLst=None, + ): + if sheetView is None: + sheetView = [ChartsheetView()] + self.sheetView = sheetView diff --git a/.venv/Lib/site-packages/openpyxl/comments/__init__.py b/.venv/Lib/site-packages/openpyxl/comments/__init__.py new file mode 100644 index 00000000..bca142da --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/comments/__init__.py @@ -0,0 +1,4 @@ +# Copyright (c) 2010-2022 openpyxl + + +from .comments import Comment diff --git a/.venv/Lib/site-packages/openpyxl/comments/author.py b/.venv/Lib/site-packages/openpyxl/comments/author.py new file mode 100644 index 00000000..89a76ec9 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/comments/author.py @@ -0,0 +1,21 @@ +# Copyright (c) 2010-2022 openpyxl + + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Sequence, + Alias +) + + +class AuthorList(Serialisable): + + tagname = "authors" + + author = Sequence(expected_type=str) + authors = Alias("author") + + def __init__(self, + author=(), + ): + self.author = author diff --git a/.venv/Lib/site-packages/openpyxl/comments/comment_sheet.py b/.venv/Lib/site-packages/openpyxl/comments/comment_sheet.py new file mode 100644 index 00000000..9c5adf76 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/comments/comment_sheet.py @@ -0,0 +1,214 @@ +# Copyright (c) 2010-2022 openpyxl + +## Incomplete! +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Float, + Integer, + Set, + String, + Bool, +) +from openpyxl.descriptors.excel import Guid, ExtensionList +from openpyxl.descriptors.sequence import NestedSequence + +from openpyxl.utils.indexed_list import IndexedList +from openpyxl.xml.constants import SHEET_MAIN_NS +from openpyxl.xml.functions import tostring + +from openpyxl.cell.text import Text +#from openpyxl.worksheet.ole import ObjectAnchor +from .author import AuthorList +from .comments import Comment +from .shape_writer import ShapeWriter + + +class Properties(Serialisable): + + locked = Bool(allow_none=True) + defaultSize = Bool(allow_none=True) + _print = Bool(allow_none=True) + disabled = Bool(allow_none=True) + uiObject = Bool(allow_none=True) + autoFill = Bool(allow_none=True) + autoLine = Bool(allow_none=True) + altText = String(allow_none=True) + textHAlign = Set(values=(['left', 'center', 'right', 'justify', 'distributed'])) + textVAlign = Set(values=(['top', 'center', 'bottom', 'justify', 'distributed'])) + lockText = Bool(allow_none=True) + justLastX = Bool(allow_none=True) + autoScale = Bool(allow_none=True) + rowHidden = Bool(allow_none=True) + colHidden = Bool(allow_none=True) + #anchor = Typed(expected_type=ObjectAnchor, ) + + __elements__ = ('anchor',) + + def __init__(self, + locked=None, + defaultSize=None, + _print=None, + disabled=None, + uiObject=None, + autoFill=None, + autoLine=None, + altText=None, + textHAlign=None, + textVAlign=None, + lockText=None, + justLastX=None, + autoScale=None, + rowHidden=None, + colHidden=None, + anchor=None, + ): + self.locked = locked + self.defaultSize = defaultSize + self._print = _print + self.disabled = disabled + self.uiObject = uiObject + self.autoFill = autoFill + self.autoLine = autoLine + self.altText = altText + self.textHAlign = textHAlign + self.textVAlign = textVAlign + self.lockText = lockText + self.justLastX = justLastX + self.autoScale = autoScale + self.rowHidden = rowHidden + self.colHidden = colHidden + self.anchor = anchor + + +class CommentRecord(Serialisable): + + tagname = "comment" + + ref = String() + authorId = Integer() + guid = Guid(allow_none=True) + shapeId = Integer(allow_none=True) + text = Typed(expected_type=Text) + commentPr = Typed(expected_type=Properties, allow_none=True) + author = String(allow_none=True) + + __elements__ = ('text', 'commentPr') + __attrs__ = ('ref', 'authorId', 'guid', 'shapeId') + + def __init__(self, + ref="", + authorId=0, + guid=None, + shapeId=0, + text=None, + commentPr=None, + author=None, + height=79, + width=144 + ): + self.ref = ref + self.authorId = authorId + self.guid = guid + self.shapeId = shapeId + if text is None: + text = Text() + self.text = text + self.commentPr = commentPr + self.author = author + self.height = height + self.width = width + + + @classmethod + def from_cell(cls, cell): + """ + Class method to convert cell comment + """ + comment = cell._comment + ref = cell.coordinate + self = cls(ref=ref, author=comment.author) + self.text.t = comment.content + self.height = comment.height + self.width = comment.width + return self + + + @property + def content(self): + """ + Remove all inline formatting and stuff + """ + return self.text.content + + +class CommentSheet(Serialisable): + + tagname = "comments" + + authors = Typed(expected_type=AuthorList) + commentList = NestedSequence(expected_type=CommentRecord, count=0) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + _id = None + _path = "/xl/comments/comment{0}.xml" + mime_type = "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml" + _rel_type = "comments" + _rel_id = None + + __elements__ = ('authors', 'commentList') + + def __init__(self, + authors=None, + commentList=None, + extLst=None, + ): + self.authors = authors + self.commentList = commentList + + + def to_tree(self): + tree = super(CommentSheet, self).to_tree() + tree.set("xmlns", SHEET_MAIN_NS) + return tree + + + @property + def comments(self): + """ + Return a dictionary of comments keyed by coord + """ + authors = self.authors.author + + for c in self.commentList: + yield c.ref, Comment(c.content, authors[c.authorId], c.height, c.width) + + + @classmethod + def from_comments(cls, comments): + """ + Create a comment sheet from a list of comments for a particular worksheet + """ + authors = IndexedList() + + # dedupe authors and get indexes + for comment in comments: + comment.authorId = authors.add(comment.author) + + return cls(authors=AuthorList(authors), commentList=comments) + + + def write_shapes(self, vml=None): + """ + Create the VML for comments + """ + sw = ShapeWriter(self.comments) + return sw.write(vml) + + + @property + def path(self): + """ + Return path within the archive + """ + return self._path.format(self._id) diff --git a/.venv/Lib/site-packages/openpyxl/comments/comments.py b/.venv/Lib/site-packages/openpyxl/comments/comments.py new file mode 100644 index 00000000..567f4e0c --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/comments/comments.py @@ -0,0 +1,62 @@ +# Copyright (c) 2010-2022 openpyxl + + +class Comment(object): + + _parent = None + + def __init__(self, text, author, height=79, width=144): + self.content = text + self.author = author + self.height = height + self.width = width + + + @property + def parent(self): + return self._parent + + + def __eq__(self, other): + return ( + self.content == other.content + and self.author == other.author + ) + + def __repr__(self): + return "Comment: {0} by {1}".format(self.content, self.author) + + + def __copy__(self): + """Create a detached copy of this comment.""" + clone = self.__class__(self.content, self.author, self.height, self.width) + return clone + + + def bind(self, cell): + """ + Bind comment to a particular cell + """ + if cell is not None and self._parent is not None and self._parent != cell: + fmt = "Comment already assigned to {0} in worksheet {1}. Cannot assign a comment to more than one cell" + raise AttributeError(fmt.format(cell.coordinate, cell.parent.title)) + self._parent = cell + + + def unbind(self): + """ + Unbind a comment from a cell + """ + self._parent = None + + + @property + def text(self): + """ + Any comment text stripped of all formatting. + """ + return self.content + + @text.setter + def text(self, value): + self.content = value diff --git a/.venv/Lib/site-packages/openpyxl/comments/shape_writer.py b/.venv/Lib/site-packages/openpyxl/comments/shape_writer.py new file mode 100644 index 00000000..b1626380 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/comments/shape_writer.py @@ -0,0 +1,116 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.xml.functions import ( + Element, + SubElement, + tostring, + fromstring, +) + +from openpyxl.utils import ( + column_index_from_string, + coordinate_to_tuple, +) + +vmlns = "urn:schemas-microsoft-com:vml" +officens = "urn:schemas-microsoft-com:office:office" +excelns = "urn:schemas-microsoft-com:office:excel" + + +class ShapeWriter(object): + """ + Create VML for comments + """ + + vml = None + vml_path = None + + + def __init__(self, comments): + self.comments = comments + + + def add_comment_shapetype(self, root): + shape_layout = SubElement(root, "{%s}shapelayout" % officens, + {"{%s}ext" % vmlns: "edit"}) + SubElement(shape_layout, + "{%s}idmap" % officens, + {"{%s}ext" % vmlns: "edit", "data": "1"}) + shape_type = SubElement(root, + "{%s}shapetype" % vmlns, + {"id": "_x0000_t202", + "coordsize": "21600,21600", + "{%s}spt" % officens: "202", + "path": "m,l,21600r21600,l21600,xe"}) + SubElement(shape_type, "{%s}stroke" % vmlns, {"joinstyle": "miter"}) + SubElement(shape_type, + "{%s}path" % vmlns, + {"gradientshapeok": "t", + "{%s}connecttype" % officens: "rect"}) + + + def add_comment_shape(self, root, idx, coord, height, width): + row, col = coordinate_to_tuple(coord) + row -= 1 + col -= 1 + shape = _shape_factory(row, col, height, width) + + shape.set('id', "_x0000_s%04d" % idx) + root.append(shape) + + + def write(self, root): + + if not hasattr(root, "findall"): + root = Element("xml") + + # Remove any existing comment shapes + comments = root.findall("{%s}shape[@type='#_x0000_t202']" % vmlns) + for c in comments: + root.remove(c) + + # check whether comments shape type already exists + shape_types = root.find("{%s}shapetype[@id='_x0000_t202']" % vmlns) + if not shape_types: + self.add_comment_shapetype(root) + + for idx, (coord, comment) in enumerate(self.comments, 1026): + self.add_comment_shape(root, idx, coord, comment.height, comment.width) + + return tostring(root) + + +def _shape_factory(row, column, height, width): + style = ("position:absolute; " + "margin-left:59.25pt;" + "margin-top:1.5pt;" + "width:{width}px;" + "height:{height}px;" + "z-index:1;" + "visibility:hidden").format(height=height, + width=width) + attrs = { + "type": "#_x0000_t202", + "style": style, + "fillcolor": "#ffffe1", + "{%s}insetmode" % officens: "auto" + } + shape = Element("{%s}shape" % vmlns, attrs) + + SubElement(shape, "{%s}fill" % vmlns, + {"color2": "#ffffe1"}) + SubElement(shape, "{%s}shadow" % vmlns, + {"color": "black", "obscured": "t"}) + SubElement(shape, "{%s}path" % vmlns, + {"{%s}connecttype" % officens: "none"}) + textbox = SubElement(shape, "{%s}textbox" % vmlns, + {"style": "mso-direction-alt:auto"}) + SubElement(textbox, "div", {"style": "text-align:left"}) + client_data = SubElement(shape, "{%s}ClientData" % excelns, + {"ObjectType": "Note"}) + SubElement(client_data, "{%s}MoveWithCells" % excelns) + SubElement(client_data, "{%s}SizeWithCells" % excelns) + SubElement(client_data, "{%s}AutoFill" % excelns).text = "False" + SubElement(client_data, "{%s}Row" % excelns).text = str(row) + SubElement(client_data, "{%s}Column" % excelns).text = str(column) + return shape diff --git a/.venv/Lib/site-packages/openpyxl/compat/__init__.py b/.venv/Lib/site-packages/openpyxl/compat/__init__.py new file mode 100644 index 00000000..2aa2ea29 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/compat/__init__.py @@ -0,0 +1,54 @@ +# Copyright (c) 2010-2022 openpyxl + +from .numbers import NUMERIC_TYPES +from .strings import safe_string + +import warnings +from functools import wraps +import inspect + + +class DummyCode: + + pass + + +# from https://github.com/tantale/deprecated/blob/master/deprecated/__init__.py +# with an enhancement to update docstrings of deprecated functions +string_types = (type(b''), type(u'')) +def deprecated(reason): + + if isinstance(reason, string_types): + + def decorator(func1): + + if inspect.isclass(func1): + fmt1 = "Call to deprecated class {name} ({reason})." + else: + fmt1 = "Call to deprecated function {name} ({reason})." + + @wraps(func1) + def new_func1(*args, **kwargs): + #warnings.simplefilter('default', DeprecationWarning) + warnings.warn( + fmt1.format(name=func1.__name__, reason=reason), + category=DeprecationWarning, + stacklevel=2 + ) + return func1(*args, **kwargs) + + # Enhance docstring with a deprecation note + deprecationNote = "\n\n.. note::\n Deprecated: " + reason + if new_func1.__doc__: + new_func1.__doc__ += deprecationNote + else: + new_func1.__doc__ = deprecationNote + return new_func1 + + return decorator + + elif inspect.isclass(reason) or inspect.isfunction(reason): + raise TypeError("Reason for deprecation must be supplied") + + else: + raise TypeError(repr(type(reason))) diff --git a/.venv/Lib/site-packages/openpyxl/compat/abc.py b/.venv/Lib/site-packages/openpyxl/compat/abc.py new file mode 100644 index 00000000..abba166b --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/compat/abc.py @@ -0,0 +1,8 @@ +# Copyright (c) 2010-2022 openpyxl + + +try: + from abc import ABC +except ImportError: + from abc import ABCMeta + ABC = ABCMeta('ABC', (object, ), {}) diff --git a/.venv/Lib/site-packages/openpyxl/compat/numbers.py b/.venv/Lib/site-packages/openpyxl/compat/numbers.py new file mode 100644 index 00000000..b027a2d5 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/compat/numbers.py @@ -0,0 +1,43 @@ +# Copyright (c) 2010-2022 openpyxl + +from decimal import Decimal + +NUMERIC_TYPES = (int, float, Decimal) + + +try: + import numpy + NUMPY = True +except ImportError: + NUMPY = False + + +if NUMPY: + NUMERIC_TYPES = NUMERIC_TYPES + (numpy.short, + numpy.ushort, + numpy.intc, + numpy.uintc, + numpy.int_, + numpy.uint, + numpy.longlong, + numpy.ulonglong, + numpy.half, + numpy.float16, + numpy.single, + numpy.double, + numpy.longdouble, + numpy.int8, + numpy.int16, + numpy.int32, + numpy.int64, + numpy.uint8, + numpy.uint16, + numpy.uint32, + numpy.uint64, + numpy.intp, + numpy.uintp, + numpy.float32, + numpy.float64, + numpy.bool_, + numpy.floating, + numpy.integer) diff --git a/.venv/Lib/site-packages/openpyxl/compat/product.py b/.venv/Lib/site-packages/openpyxl/compat/product.py new file mode 100644 index 00000000..a6381499 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/compat/product.py @@ -0,0 +1,17 @@ +# Copyright (c) 2010-2022 openpyxl + +""" +math.prod equivalent for < Python 3.8 +""" + +import functools +import operator + +def product(sequence): + return functools.reduce(operator.mul, sequence) + + +try: + from math import prod +except ImportError: + prod = product diff --git a/.venv/Lib/site-packages/openpyxl/compat/singleton.py b/.venv/Lib/site-packages/openpyxl/compat/singleton.py new file mode 100644 index 00000000..d19c2642 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/compat/singleton.py @@ -0,0 +1,40 @@ +# Copyright (c) 2010-2022 openpyxl + +import weakref + + +class Singleton(type): + """ + Singleton metaclass + Based on Python Cookbook 3rd Edition Recipe 9.13 + Only one instance of a class can exist. Does not work with __slots__ + """ + + def __init__(self, *args, **kw): + super(Singleton, self).__init__(*args, **kw) + self.__instance = None + + def __call__(self, *args, **kw): + if self.__instance is None: + self.__instance = super(Singleton, self).__call__(*args, **kw) + return self.__instance + + +class Cached(type): + """ + Caching metaclass + Child classes will only create new instances of themselves if + one doesn't already exist. Does not work with __slots__ + """ + + def __init__(self, *args, **kw): + super(Singleton, self).__init__(*args, **kw) + self.__cache = weakref.WeakValueDictionary() + + def __call__(self, *args): + if args in self.__cache: + return self.__cache[args] + + obj = super(Singleton, self).__call__(*args) + self.__cache[args] = obj + return obj diff --git a/.venv/Lib/site-packages/openpyxl/compat/strings.py b/.venv/Lib/site-packages/openpyxl/compat/strings.py new file mode 100644 index 00000000..eb4e94c2 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/compat/strings.py @@ -0,0 +1,25 @@ +# Copyright (c) 2010-2022 openpyxl + +from datetime import datetime +from math import isnan, isinf +import sys + +VER = sys.version_info + +from .numbers import NUMERIC_TYPES + + +def safe_string(value): + """Safely and consistently format numeric values""" + if isinstance(value, NUMERIC_TYPES): + if isnan(value) or isinf(value): + value = "" + else: + value = "%.16g" % value + elif value is None: + value = "none" + elif isinstance(value, datetime): + value = value.isoformat() + elif not isinstance(value, str): + value = str(value) + return value diff --git a/.venv/Lib/site-packages/openpyxl/descriptors/__init__.py b/.venv/Lib/site-packages/openpyxl/descriptors/__init__.py new file mode 100644 index 00000000..0b794a02 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/descriptors/__init__.py @@ -0,0 +1,57 @@ +# Copyright (c) 2010-2022 openpyxl + +from .base import * +from .sequence import Sequence + + +class MetaStrict(type): + + def __new__(cls, clsname, bases, methods): + for k, v in methods.items(): + if isinstance(v, Descriptor): + v.name = k + return type.__new__(cls, clsname, bases, methods) + + +class MetaSerialisable(type): + + def __new__(cls, clsname, bases, methods): + attrs = [] + nested = [] + elements = [] + namespaced = [] + for k, v in methods.items(): + if isinstance(v, Descriptor): + ns= getattr(v, 'namespace', None) + if ns: + namespaced.append((k, "{%s}%s" % (ns, k))) + if getattr(v, 'nested', False): + nested.append(k) + elements.append(k) + elif isinstance(v, Sequence): + elements.append(k) + elif isinstance(v, Typed): + if hasattr(v.expected_type, 'to_tree'): + elements.append(k) + else: + attrs.append(k) + else: + if not isinstance(v, Alias): + attrs.append(k) + + if methods.get('__attrs__') is None: + methods['__attrs__'] = tuple(attrs) + methods['__namespaced__'] = tuple(namespaced) + if methods.get('__nested__') is None: + methods['__nested__'] = tuple(sorted(nested)) + if methods.get('__elements__') is None: + methods['__elements__'] = tuple(sorted(elements)) + return MetaStrict.__new__(cls, clsname, bases, methods) + + +Strict = MetaStrict('Strict', (object,), {}) + +_Serialiasable = MetaSerialisable('_Serialisable', (object,), {}) + +#del MetaStrict +#del MetaSerialisable diff --git a/.venv/Lib/site-packages/openpyxl/descriptors/base.py b/.venv/Lib/site-packages/openpyxl/descriptors/base.py new file mode 100644 index 00000000..5981fa42 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/descriptors/base.py @@ -0,0 +1,268 @@ +# Copyright (c) 2010-2022 openpyxl + + +""" +Based on Python Cookbook 3rd Edition, 8.13 +http://chimera.labs.oreilly.com/books/1230000000393/ch08.html#_discussiuncion_130 +""" + +import datetime +import re + +from openpyxl.utils.datetime import from_ISO8601 + +from .namespace import namespaced + +class Descriptor(object): + + def __init__(self, name=None, **kw): + self.name = name + for k, v in kw.items(): + setattr(self, k, v) + + def __set__(self, instance, value): + instance.__dict__[self.name] = value + + +class Typed(Descriptor): + """Values must of a particular type""" + + expected_type = type(None) + allow_none = False + nested = False + + def __init__(self, *args, **kw): + super(Typed, self).__init__(*args, **kw) + self.__doc__ = "Values must be of type {0}".format(self.expected_type) + + def __set__(self, instance, value): + if not isinstance(value, self.expected_type): + if (not self.allow_none + or (self.allow_none and value is not None)): + raise TypeError('expected ' + str(self.expected_type)) + super(Typed, self).__set__(instance, value) + + def __repr__(self): + return self.__doc__ + + +def _convert(expected_type, value): + """ + Check value is of or can be converted to expected type. + """ + if not isinstance(value, expected_type): + try: + value = expected_type(value) + except: + raise TypeError('expected ' + str(expected_type)) + return value + + +class Convertible(Typed): + """Values must be convertible to a particular type""" + + def __set__(self, instance, value): + if ((self.allow_none and value is not None) + or not self.allow_none): + value = _convert(self.expected_type, value) + super(Convertible, self).__set__(instance, value) + + +class Max(Convertible): + """Values must be less than a `max` value""" + + expected_type = float + allow_none = False + + def __init__(self, **kw): + if 'max' not in kw and not hasattr(self, 'max'): + raise TypeError('missing max value') + super(Max, self).__init__(**kw) + + def __set__(self, instance, value): + if ((self.allow_none and value is not None) + or not self.allow_none): + value = _convert(self.expected_type, value) + if value > self.max: + raise ValueError('Max value is {0}'.format(self.max)) + super(Max, self).__set__(instance, value) + + +class Min(Convertible): + """Values must be greater than a `min` value""" + + expected_type = float + allow_none = False + + def __init__(self, **kw): + if 'min' not in kw and not hasattr(self, 'min'): + raise TypeError('missing min value') + super(Min, self).__init__(**kw) + + def __set__(self, instance, value): + if ((self.allow_none and value is not None) + or not self.allow_none): + value = _convert(self.expected_type, value) + if value < self.min: + raise ValueError('Min value is {0}'.format(self.min)) + super(Min, self).__set__(instance, value) + + +class MinMax(Min, Max): + """Values must be greater than `min` value and less than a `max` one""" + pass + + +class Set(Descriptor): + """Value can only be from a set of know values""" + + def __init__(self, name=None, **kw): + if not 'values' in kw: + raise TypeError("missing set of values") + kw['values'] = set(kw['values']) + super(Set, self).__init__(name, **kw) + self.__doc__ = "Value must be one of {0}".format(self.values) + + def __set__(self, instance, value): + if value not in self.values: + raise ValueError(self.__doc__) + super(Set, self).__set__(instance, value) + + +class NoneSet(Set): + + """'none' will be treated as None""" + + def __init__(self, name=None, **kw): + super(NoneSet, self).__init__(name, **kw) + self.values.add(None) + + def __set__(self, instance, value): + if value == 'none': + value = None + super(NoneSet, self).__set__(instance, value) + + +class Integer(Convertible): + + expected_type = int + + +class Float(Convertible): + + expected_type = float + + +class Bool(Convertible): + + expected_type = bool + + def __set__(self, instance, value): + if isinstance(value, str): + if value in ('false', 'f', '0'): + value = False + super(Bool, self).__set__(instance, value) + + +class String(Typed): + + expected_type = str + + +class Text(String, Convertible): + + pass + + +class ASCII(Typed): + + expected_type = bytes + + +class Tuple(Typed): + + expected_type = tuple + + +class Length(Descriptor): + + def __init__(self, name=None, **kw): + if "length" not in kw: + raise TypeError("value length must be supplied") + super(Length, self).__init__(**kw) + + + def __set__(self, instance, value): + if len(value) != self.length: + raise ValueError("Value must be length {0}".format(self.length)) + super(Length, self).__set__(instance, value) + + +class Default(Typed): + """ + When called returns an instance of the expected type. + Additional default values can be passed in to the descriptor + """ + + def __init__(self, name=None, **kw): + if "defaults" not in kw: + kw['defaults'] = {} + super(Default, self).__init__(**kw) + + def __call__(self): + return self.expected_type() + + +class Alias(Descriptor): + """ + Aliases can be used when either the desired attribute name is not allowed + or confusing in Python (eg. "type") or a more descriptve name is desired + (eg. "underline" for "u") + """ + + def __init__(self, alias): + self.alias = alias + + def __set__(self, instance, value): + setattr(instance, self.alias, value) + + def __get__(self, instance, cls): + return getattr(instance, self.alias) + + +class MatchPattern(Descriptor): + """Values must match a regex pattern """ + allow_none = False + + def __init__(self, name=None, **kw): + if 'pattern' not in kw and not hasattr(self, 'pattern'): + raise TypeError('missing pattern value') + + super(MatchPattern, self).__init__(name, **kw) + self.test_pattern = re.compile(self.pattern, re.VERBOSE) + + + def __set__(self, instance, value): + + if value is None and not self.allow_none: + raise ValueError("Value must not be none") + + if ((self.allow_none and value is not None) + or not self.allow_none): + if not self.test_pattern.match(value): + raise ValueError('Value does not match pattern {0}'.format(self.pattern)) + + super(MatchPattern, self).__set__(instance, value) + + +class DateTime(Typed): + + expected_type = datetime.datetime + + def __set__(self, instance, value): + if value is not None and isinstance(value, str): + try: + value = from_ISO8601(value) + except ValueError: + raise ValueError("Value must be ISO datetime format") + super(DateTime, self).__set__(instance, value) diff --git a/.venv/Lib/site-packages/openpyxl/descriptors/excel.py b/.venv/Lib/site-packages/openpyxl/descriptors/excel.py new file mode 100644 index 00000000..319b96e3 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/descriptors/excel.py @@ -0,0 +1,112 @@ +#copyright openpyxl 2010-2018 + +""" +Excel specific descriptors +""" + +from openpyxl.xml.constants import REL_NS +from openpyxl.compat import safe_string +from openpyxl.xml.functions import Element + +from . import ( + MatchPattern, + MinMax, + Integer, + String, + Sequence, +) +from .serialisable import Serialisable + + +class HexBinary(MatchPattern): + + pattern = "[0-9a-fA-F]+$" + + +class UniversalMeasure(MatchPattern): + + pattern = r"[0-9]+(\.[0-9]+)?(mm|cm|in|pt|pc|pi)" + + +class TextPoint(MinMax): + """ + Size in hundredths of points. + In theory other units of measurement can be used but these are unbounded + """ + expected_type = int + + min = -400000 + max = 400000 + + +Coordinate = Integer + + +class Percentage(MinMax): + + pattern = r"((100)|([0-9][0-9]?))(\.[0-9][0-9]?)?%" # strict + min = -1000000 + max = 1000000 + + def __set__(self, instance, value): + if isinstance(value, str) and "%" in value: + value = value.replace("%", "") + value = int(float(value) * 1000) + super(Percentage, self).__set__(instance, value) + + +class Extension(Serialisable): + + uri = String() + + def __init__(self, + uri=None, + ): + self.uri = uri + + +class ExtensionList(Serialisable): + + ext = Sequence(expected_type=Extension) + + def __init__(self, + ext=(), + ): + self.ext = ext + + +class Relation(String): + + namespace = REL_NS + allow_none = True + + +class Base64Binary(MatchPattern): + # http://www.w3.org/TR/xmlschema11-2/#nt-Base64Binary + pattern = "^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{4})$" + + +class Guid(MatchPattern): + # https://msdn.microsoft.com/en-us/library/dd946381(v=office.12).aspx + pattern = r"{[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}\}" + + +class CellRange(MatchPattern): + + pattern = r"^[$]?([A-Za-z]{1,3})[$]?(\d+)(:[$]?([A-Za-z]{1,3})[$]?(\d+)?)?$|^[A-Za-z]{1,3}:[A-Za-z]{1,3}$" + allow_none = True + + def __set__(self, instance, value): + + if value is not None: + value = value.upper() + super(CellRange, self).__set__(instance, value) + + +def _explicit_none(tagname, value, namespace=None): + """ + Override serialisation because explicit none required + """ + if namespace is not None: + tagname = "{%s}%s" % (namespace, tagname) + return Element(tagname, val=safe_string(value)) diff --git a/.venv/Lib/site-packages/openpyxl/descriptors/namespace.py b/.venv/Lib/site-packages/openpyxl/descriptors/namespace.py new file mode 100644 index 00000000..d7b35a2c --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/descriptors/namespace.py @@ -0,0 +1,12 @@ +# copyright openpyxl 2010-2015 + + +def namespaced(obj, tagname, namespace=None): + """ + Utility to create a namespaced tag for an object + """ + + namespace = getattr(obj, "namespace", None) or namespace + if namespace is not None: + tagname = "{%s}%s" % (namespace, tagname) + return tagname diff --git a/.venv/Lib/site-packages/openpyxl/descriptors/nested.py b/.venv/Lib/site-packages/openpyxl/descriptors/nested.py new file mode 100644 index 00000000..cf1ab257 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/descriptors/nested.py @@ -0,0 +1,131 @@ +#copyright openpyxl 2010-2015 + +""" +Generic serialisable classes +""" +from .base import ( + Convertible, + Bool, + Descriptor, + NoneSet, + MinMax, + Set, + Float, + Integer, + String, + Text, + ) +from .sequence import Sequence +from openpyxl.compat import safe_string +from openpyxl.xml.functions import Element, localname, whitespace + + +class Nested(Descriptor): + + nested = True + attribute = "val" + + def __set__(self, instance, value): + if hasattr(value, "tag"): + tag = localname(value) + if tag != self.name: + raise ValueError("Tag does not match attribute") + + value = self.from_tree(value) + super(Nested, self).__set__(instance, value) + + + def from_tree(self, node): + return node.get(self.attribute) + + + def to_tree(self, tagname=None, value=None, namespace=None): + namespace = getattr(self, "namespace", namespace) + if value is not None: + if namespace is not None: + tagname = "{%s}%s" % (namespace, tagname) + value = safe_string(value) + return Element(tagname, {self.attribute:value}) + + +class NestedValue(Nested, Convertible): + """ + Nested tag storing the value on the 'val' attribute + """ + pass + + +class NestedText(NestedValue): + """ + Represents any nested tag with the value as the contents of the tag + """ + + + def from_tree(self, node): + return node.text + + + def to_tree(self, tagname=None, value=None, namespace=None): + namespace = getattr(self, "namespace", namespace) + if value is not None: + if namespace is not None: + tagname = "{%s}%s" % (namespace, tagname) + el = Element(tagname) + el.text = safe_string(value) + whitespace(el) + return el + + +class NestedFloat(NestedValue, Float): + + pass + + +class NestedInteger(NestedValue, Integer): + + pass + + +class NestedString(NestedValue, String): + + pass + + +class NestedBool(NestedValue, Bool): + + + def from_tree(self, node): + return node.get("val", True) + + +class NestedNoneSet(Nested, NoneSet): + + pass + + +class NestedSet(Nested, Set): + + pass + + +class NestedMinMax(Nested, MinMax): + + pass + + +class EmptyTag(Nested, Bool): + + """ + Boolean if a tag exists or not. + """ + + def from_tree(self, node): + return True + + + def to_tree(self, tagname=None, value=None, namespace=None): + if value: + namespace = getattr(self, "namespace", namespace) + if namespace is not None: + tagname = "{%s}%s" % (namespace, tagname) + return Element(tagname) diff --git a/.venv/Lib/site-packages/openpyxl/descriptors/sequence.py b/.venv/Lib/site-packages/openpyxl/descriptors/sequence.py new file mode 100644 index 00000000..e064fa2b --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/descriptors/sequence.py @@ -0,0 +1,127 @@ +# copyright openpyxl 2010-2015 + +from openpyxl.compat import safe_string +from openpyxl.xml.functions import Element +from openpyxl.utils.indexed_list import IndexedList + +from .base import Descriptor, Alias, _convert +from .namespace import namespaced + + +class Sequence(Descriptor): + """ + A sequence (list or tuple) that may only contain objects of the declared + type + """ + + expected_type = type(None) + seq_types = (list, tuple) + idx_base = 0 + unique = False + + + def __set__(self, instance, seq): + if not isinstance(seq, self.seq_types): + raise TypeError("Value must be a sequence") + seq = [_convert(self.expected_type, value) for value in seq] + if self.unique: + seq = IndexedList(seq) + + super(Sequence, self).__set__(instance, seq) + + + def to_tree(self, tagname, obj, namespace=None): + """ + Convert the sequence represented by the descriptor to an XML element + """ + for idx, v in enumerate(obj, self.idx_base): + if hasattr(v, "to_tree"): + el = v.to_tree(tagname, idx) + else: + tagname = namespaced(obj, tagname, namespace) + el = Element(tagname) + el.text = safe_string(v) + yield el + + +class ValueSequence(Sequence): + """ + A sequence of primitive types that are stored as a single attribute. + "val" is the default attribute + """ + + attribute = "val" + + + def to_tree(self, tagname, obj, namespace=None): + tagname = namespaced(self, tagname, namespace) + for v in obj: + yield Element(tagname, {self.attribute:safe_string(v)}) + + + def from_tree(self, node): + + return node.get(self.attribute) + + +class NestedSequence(Sequence): + """ + Wrap a sequence in an containing object + """ + + count = False + + def to_tree(self, tagname, obj, namespace=None): + tagname = namespaced(self, tagname, namespace) + container = Element(tagname) + if self.count: + container.set('count', str(len(obj))) + for v in obj: + container.append(v.to_tree()) + return container + + + def from_tree(self, node): + return [self.expected_type.from_tree(el) for el in node] + + +class MultiSequence(Sequence): + """ + Sequences can contain objects with different tags + """ + + def __set__(self, instance, seq): + if not isinstance(seq, (tuple, list)): + raise ValueError("Value must be a sequence") + seq = list(seq) + Descriptor.__set__(self, instance, seq) + + + def to_tree(self, tagname, obj, namespace=None): + """ + Convert the sequence represented by the descriptor to an XML element + """ + for v in obj: + el = v.to_tree(namespace=namespace) + yield el + + +class MultiSequencePart(Alias): + """ + Allow a multisequence to be built up from parts + + Excluded from the instance __elements__ or __attrs__ as is effectively an Alias + """ + + def __init__(self, expected_type, store): + self.expected_type = expected_type + self.store = store + + + def __set__(self, instance, value): + value = _convert(self.expected_type, value) + instance.__dict__[self.store].append(value) + + + def __get__(self, instance, cls): + return self diff --git a/.venv/Lib/site-packages/openpyxl/descriptors/serialisable.py b/.venv/Lib/site-packages/openpyxl/descriptors/serialisable.py new file mode 100644 index 00000000..9cf0127e --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/descriptors/serialisable.py @@ -0,0 +1,240 @@ +# copyright openpyxl 2010-2015 + +from copy import copy +from keyword import kwlist +KEYWORDS = frozenset(kwlist) + +from . import Descriptor +from . import _Serialiasable +from .sequence import ( + Sequence, + NestedSequence, + MultiSequencePart, +) +from .namespace import namespaced + +from openpyxl.compat import safe_string +from openpyxl.xml.functions import ( + Element, + localname, +) + +seq_types = (list, tuple) + +class Serialisable(_Serialiasable): + """ + Objects can serialise to XML their attributes and child objects. + The following class attributes are created by the metaclass at runtime: + __attrs__ = attributes + __nested__ = single-valued child treated as an attribute + __elements__ = child elements + """ + + __attrs__ = None + __nested__ = None + __elements__ = None + __namespaced__ = None + + idx_base = 0 + + @property + def tagname(self): + raise(NotImplementedError) + + namespace = None + + @classmethod + def from_tree(cls, node): + """ + Create object from XML + """ + # strip known namespaces from attributes + attrib = dict(node.attrib) + for key, ns in cls.__namespaced__: + if ns in attrib: + attrib[key] = attrib[ns] + del attrib[ns] + + # strip attributes with unknown namespaces + for key in list(attrib): + if key.startswith('{'): + del attrib[key] + elif key in KEYWORDS: + attrib["_" + key] = attrib[key] + del attrib[key] + elif "-" in key: + n = key.replace("-", "_") + attrib[n] = attrib[key] + del attrib[key] + + if node.text and "attr_text" in cls.__attrs__: + attrib["attr_text"] = node.text + + for el in node: + tag = localname(el) + if tag in KEYWORDS: + tag = "_" + tag + desc = getattr(cls, tag, None) + if desc is None or isinstance(desc, property): + continue + + if hasattr(desc, 'from_tree'): + #descriptor manages conversion + obj = desc.from_tree(el) + else: + if hasattr(desc.expected_type, "from_tree"): + #complex type + obj = desc.expected_type.from_tree(el) + else: + #primitive + obj = el.text + + if isinstance(desc, NestedSequence): + attrib[tag] = obj + elif isinstance(desc, Sequence): + attrib.setdefault(tag, []) + attrib[tag].append(obj) + elif isinstance(desc, MultiSequencePart): + attrib.setdefault(desc.store, []) + attrib[desc.store].append(obj) + else: + attrib[tag] = obj + + return cls(**attrib) + + + def to_tree(self, tagname=None, idx=None, namespace=None): + + if tagname is None: + tagname = self.tagname + + # keywords have to be masked + if tagname.startswith("_"): + tagname = tagname[1:] + + tagname = namespaced(self, tagname, namespace) + namespace = getattr(self, "namespace", namespace) + + attrs = dict(self) + for key, ns in self.__namespaced__: + if key in attrs: + attrs[ns] = attrs[key] + del attrs[key] + + el = Element(tagname, attrs) + if "attr_text" in self.__attrs__: + el.text = safe_string(getattr(self, "attr_text")) + + for child_tag in self.__elements__: + desc = getattr(self.__class__, child_tag, None) + obj = getattr(self, child_tag) + if hasattr(desc, "namespace") and hasattr(obj, 'namespace'): + obj.namespace = desc.namespace + + if isinstance(obj, seq_types): + if isinstance(desc, NestedSequence): + # wrap sequence in container + if not obj: + continue + nodes = [desc.to_tree(child_tag, obj, namespace)] + elif isinstance(desc, Sequence): + # sequence + desc.idx_base = self.idx_base + nodes = (desc.to_tree(child_tag, obj, namespace)) + else: # property + nodes = (v.to_tree(child_tag, namespace) for v in obj) + for node in nodes: + el.append(node) + else: + if child_tag in self.__nested__: + node = desc.to_tree(child_tag, obj, namespace) + elif obj is None: + continue + else: + node = obj.to_tree(child_tag) + if node is not None: + el.append(node) + return el + + + def __iter__(self): + for attr in self.__attrs__: + value = getattr(self, attr) + if attr.startswith("_"): + attr = attr[1:] + elif attr != "attr_text" and "_" in attr: + desc = getattr(self.__class__, attr) + if getattr(desc, "hyphenated", False): + attr = attr.replace("_", "-") + if attr != "attr_text" and value is not None: + yield attr, safe_string(value) + + + def __eq__(self, other): + if not self.__class__ == other.__class__: + return False + elif not dict(self) == dict(other): + return False + for el in self.__elements__: + if getattr(self, el) != getattr(other, el): + return False + return True + + + def __ne__(self, other): + return not self == other + + + def __repr__(self): + s = u"<{0}.{1} object>\nParameters:".format( + self.__module__, + self.__class__.__name__ + ) + args = [] + for k in self.__attrs__ + self.__elements__: + v = getattr(self, k) + if isinstance(v, Descriptor): + v = None + args.append(u"{0}={1}".format(k, repr(v))) + args = u", ".join(args) + + return u"\n".join([s, args]) + + + def __hash__(self): + fields = [] + for attr in self.__attrs__ + self.__elements__: + val = getattr(self, attr) + if isinstance(val, list): + val = tuple(val) + fields.append(val) + + return hash(tuple(fields)) + + + def __add__(self, other): + if type(self) != type(other): + raise TypeError("Cannot combine instances of different types") + vals = {} + for attr in self.__attrs__: + vals[attr] = getattr(self, attr) or getattr(other, attr) + for el in self.__elements__: + a = getattr(self, el) + b = getattr(other, el) + if a and b: + vals[el] = a + b + else: + vals[el] = a or b + return self.__class__(**vals) + + + def __copy__(self): + # serialise to xml and back to avoid shallow copies + xml = self.to_tree(tagname="dummy") + cp = self.__class__.from_tree(xml) + # copy any non-persisted attributed + for k in self.__dict__: + if k not in self.__attrs__ + self.__elements__: + v = copy(getattr(self, k)) + setattr(cp, k, v) + return cp diff --git a/.venv/Lib/site-packages/openpyxl/descriptors/slots.py b/.venv/Lib/site-packages/openpyxl/descriptors/slots.py new file mode 100644 index 00000000..cadc1ef3 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/descriptors/slots.py @@ -0,0 +1,18 @@ +# Metaclass for mixing slots and descriptors +# From "Programming in Python 3" by Mark Summerfield Ch.8 p. 383 + +class AutoSlotProperties(type): + + def __new__(mcl, classname, bases, dictionary): + slots = list(dictionary.get("__slots__", [])) + for getter_name in [key for key in dictionary if key.startswith("get_")]: + name = getter_name + slots.append("__" + name) + getter = dictionary.pop(getter_name) + setter = dictionary.get(setter_name, None) + if (setter is not None + and isinstance(setter, collections.Callable)): + del dictionary[setter_name] + dictionary[name] = property(getter. setter) + dictionary["__slots__"] = tuple(slots) + return super().__new__(mcl, classname, bases, dictionary) diff --git a/.venv/Lib/site-packages/openpyxl/drawing/__init__.py b/.venv/Lib/site-packages/openpyxl/drawing/__init__.py new file mode 100644 index 00000000..57aed7b1 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/drawing/__init__.py @@ -0,0 +1,4 @@ +# Copyright (c) 2010-2022 openpyxl + + +from .drawing import Drawing diff --git a/.venv/Lib/site-packages/openpyxl/drawing/colors.py b/.venv/Lib/site-packages/openpyxl/drawing/colors.py new file mode 100644 index 00000000..23171d6b --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/drawing/colors.py @@ -0,0 +1,435 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Alias, + Typed, + Integer, + Set, + MinMax, +) +from openpyxl.descriptors.excel import Percentage +from openpyxl.descriptors.nested import ( + NestedNoneSet, + NestedValue, + NestedInteger, + EmptyTag, +) + +from openpyxl.styles.colors import RGB +from openpyxl.xml.constants import DRAWING_NS + +from openpyxl.descriptors.excel import ExtensionList as OfficeArtExtensionList + +PRESET_COLORS = [ + 'aliceBlue', 'antiqueWhite', 'aqua', 'aquamarine', + 'azure', 'beige', 'bisque', 'black', 'blanchedAlmond', 'blue', + 'blueViolet', 'brown', 'burlyWood', 'cadetBlue', 'chartreuse', + 'chocolate', 'coral', 'cornflowerBlue', 'cornsilk', 'crimson', 'cyan', + 'darkBlue', 'darkCyan', 'darkGoldenrod', 'darkGray', 'darkGrey', + 'darkGreen', 'darkKhaki', 'darkMagenta', 'darkOliveGreen', 'darkOrange', + 'darkOrchid', 'darkRed', 'darkSalmon', 'darkSeaGreen', 'darkSlateBlue', + 'darkSlateGray', 'darkSlateGrey', 'darkTurquoise', 'darkViolet', + 'dkBlue', 'dkCyan', 'dkGoldenrod', 'dkGray', 'dkGrey', 'dkGreen', + 'dkKhaki', 'dkMagenta', 'dkOliveGreen', 'dkOrange', 'dkOrchid', 'dkRed', + 'dkSalmon', 'dkSeaGreen', 'dkSlateBlue', 'dkSlateGray', 'dkSlateGrey', + 'dkTurquoise', 'dkViolet', 'deepPink', 'deepSkyBlue', 'dimGray', + 'dimGrey', 'dodgerBlue', 'firebrick', 'floralWhite', 'forestGreen', + 'fuchsia', 'gainsboro', 'ghostWhite', 'gold', 'goldenrod', 'gray', + 'grey', 'green', 'greenYellow', 'honeydew', 'hotPink', 'indianRed', + 'indigo', 'ivory', 'khaki', 'lavender', 'lavenderBlush', 'lawnGreen', + 'lemonChiffon', 'lightBlue', 'lightCoral', 'lightCyan', + 'lightGoldenrodYellow', 'lightGray', 'lightGrey', 'lightGreen', + 'lightPink', 'lightSalmon', 'lightSeaGreen', 'lightSkyBlue', + 'lightSlateGray', 'lightSlateGrey', 'lightSteelBlue', 'lightYellow', + 'ltBlue', 'ltCoral', 'ltCyan', 'ltGoldenrodYellow', 'ltGray', 'ltGrey', + 'ltGreen', 'ltPink', 'ltSalmon', 'ltSeaGreen', 'ltSkyBlue', + 'ltSlateGray', 'ltSlateGrey', 'ltSteelBlue', 'ltYellow', 'lime', + 'limeGreen', 'linen', 'magenta', 'maroon', 'medAquamarine', 'medBlue', + 'medOrchid', 'medPurple', 'medSeaGreen', 'medSlateBlue', + 'medSpringGreen', 'medTurquoise', 'medVioletRed', 'mediumAquamarine', + 'mediumBlue', 'mediumOrchid', 'mediumPurple', 'mediumSeaGreen', + 'mediumSlateBlue', 'mediumSpringGreen', 'mediumTurquoise', + 'mediumVioletRed', 'midnightBlue', 'mintCream', 'mistyRose', 'moccasin', + 'navajoWhite', 'navy', 'oldLace', 'olive', 'oliveDrab', 'orange', + 'orangeRed', 'orchid', 'paleGoldenrod', 'paleGreen', 'paleTurquoise', + 'paleVioletRed', 'papayaWhip', 'peachPuff', 'peru', 'pink', 'plum', + 'powderBlue', 'purple', 'red', 'rosyBrown', 'royalBlue', 'saddleBrown', + 'salmon', 'sandyBrown', 'seaGreen', 'seaShell', 'sienna', 'silver', + 'skyBlue', 'slateBlue', 'slateGray', 'slateGrey', 'snow', 'springGreen', + 'steelBlue', 'tan', 'teal', 'thistle', 'tomato', 'turquoise', 'violet', + 'wheat', 'white', 'whiteSmoke', 'yellow', 'yellowGreen' + ] + + +SCHEME_COLORS= ['bg1', 'tx1', 'bg2', 'tx2', 'accent1', 'accent2', 'accent3', + 'accent4', 'accent5', 'accent6', 'hlink', 'folHlink', 'phClr', 'dk1', 'lt1', + 'dk2', 'lt2' + ] + + +class Transform(Serialisable): + + pass + + +class SystemColor(Serialisable): + + tagname = "sysClr" + namespace = DRAWING_NS + + # color transform options + tint = NestedInteger(allow_none=True) + shade = NestedInteger(allow_none=True) + comp = Typed(expected_type=Transform, allow_none=True) + inv = Typed(expected_type=Transform, allow_none=True) + gray = Typed(expected_type=Transform, allow_none=True) + alpha = NestedInteger(allow_none=True) + alphaOff = NestedInteger(allow_none=True) + alphaMod = NestedInteger(allow_none=True) + hue = NestedInteger(allow_none=True) + hueOff = NestedInteger(allow_none=True) + hueMod = NestedInteger(allow_none=True) + sat = NestedInteger(allow_none=True) + satOff = NestedInteger(allow_none=True) + satMod = NestedInteger(allow_none=True) + lum = NestedInteger(allow_none=True) + lumOff = NestedInteger(allow_none=True) + lumMod = NestedInteger(allow_none=True) + red = NestedInteger(allow_none=True) + redOff = NestedInteger(allow_none=True) + redMod = NestedInteger(allow_none=True) + green = NestedInteger(allow_none=True) + greenOff = NestedInteger(allow_none=True) + greenMod = NestedInteger(allow_none=True) + blue = NestedInteger(allow_none=True) + blueOff = NestedInteger(allow_none=True) + blueMod = NestedInteger(allow_none=True) + gamma = Typed(expected_type=Transform, allow_none=True) + invGamma = Typed(expected_type=Transform, allow_none=True) + + val = Set(values=( ['scrollBar', 'background', 'activeCaption', + 'inactiveCaption', 'menu', 'window', 'windowFrame', 'menuText', + 'windowText', 'captionText', 'activeBorder', 'inactiveBorder', + 'appWorkspace', 'highlight', 'highlightText', 'btnFace', 'btnShadow', + 'grayText', 'btnText', 'inactiveCaptionText', 'btnHighlight', + '3dDkShadow', '3dLight', 'infoText', 'infoBk', 'hotLight', + 'gradientActiveCaption', 'gradientInactiveCaption', 'menuHighlight', + 'menuBar'] ) + ) + lastClr = RGB(allow_none=True) + + __elements__ = ('tint', 'shade', 'comp', 'inv', 'gray', "alpha", + "alphaOff", "alphaMod", "hue", "hueOff", "hueMod", "hueOff", "sat", + "satOff", "satMod", "lum", "lumOff", "lumMod", "red", "redOff", "redMod", + "green", "greenOff", "greenMod", "blue", "blueOff", "blueMod", "gamma", + "invGamma") + + def __init__(self, + val="windowText", + lastClr=None, + tint=None, + shade=None, + comp=None, + inv=None, + gray=None, + alpha=None, + alphaOff=None, + alphaMod=None, + hue=None, + hueOff=None, + hueMod=None, + sat=None, + satOff=None, + satMod=None, + lum=None, + lumOff=None, + lumMod=None, + red=None, + redOff=None, + redMod=None, + green=None, + greenOff=None, + greenMod=None, + blue=None, + blueOff=None, + blueMod=None, + gamma=None, + invGamma=None + ): + self.val = val + self.lastClr = lastClr + self.tint = tint + self.shade = shade + self.comp = comp + self.inv = inv + self.gray = gray + self.alpha = alpha + self.alphaOff = alphaOff + self.alphaMod = alphaMod + self.hue = hue + self.hueOff = hueOff + self.hueMod = hueMod + self.sat = sat + self.satOff = satOff + self.satMod = satMod + self.lum = lum + self.lumOff = lumOff + self.lumMod = lumMod + self.red = red + self.redOff = redOff + self.redMod = redMod + self.green = green + self.greenOff = greenOff + self.greenMod = greenMod + self.blue = blue + self.blueOff = blueOff + self.blueMod = blueMod + self.gamma = gamma + self.invGamma = invGamma + + +class HSLColor(Serialisable): + + tagname = "hslClr" + + hue = Integer() + sat = MinMax(min=0, max=100) + lum = MinMax(min=0, max=100) + + #TODO add color transform options + + def __init__(self, + hue=None, + sat=None, + lum=None, + ): + self.hue = hue + self.sat = sat + self.lum = lum + + + +class RGBPercent(Serialisable): + + tagname = "rgbClr" + + r = MinMax(min=0, max=100) + g = MinMax(min=0, max=100) + b = MinMax(min=0, max=100) + + #TODO add color transform options + + def __init__(self, + r=None, + g=None, + b=None, + ): + self.r = r + self.g = g + self.b = b + + +class SchemeColor(Serialisable): + + tagname = "schemeClr" + namespace = DRAWING_NS + + tint = NestedInteger(allow_none=True) + shade = NestedInteger(allow_none=True) + comp = EmptyTag(allow_none=True) + inv = NestedInteger(allow_none=True) + gray = NestedInteger(allow_none=True) + alpha = NestedInteger(allow_none=True) + alphaOff = NestedInteger(allow_none=True) + alphaMod = NestedInteger(allow_none=True) + hue = NestedInteger(allow_none=True) + hueOff = NestedInteger(allow_none=True) + hueMod = NestedInteger(allow_none=True) + sat = NestedInteger(allow_none=True) + satOff = NestedInteger(allow_none=True) + satMod = NestedInteger(allow_none=True) + lum = NestedInteger(allow_none=True) + lumOff = NestedInteger(allow_none=True) + lumMod = NestedInteger(allow_none=True) + red = NestedInteger(allow_none=True) + redOff = NestedInteger(allow_none=True) + redMod = NestedInteger(allow_none=True) + green = NestedInteger(allow_none=True) + greenOff = NestedInteger(allow_none=True) + greenMod = NestedInteger(allow_none=True) + blue = NestedInteger(allow_none=True) + blueOff = NestedInteger(allow_none=True) + blueMod = NestedInteger(allow_none=True) + gamma = EmptyTag(allow_none=True) + invGamma = EmptyTag(allow_none=True) + val = Set(values=(['bg1', 'tx1', 'bg2', 'tx2', 'accent1', 'accent2', + 'accent3', 'accent4', 'accent5', 'accent6', 'hlink', 'folHlink', 'phClr', + 'dk1', 'lt1', 'dk2', 'lt2'])) + + __elements__ = ('tint', 'shade', 'comp', 'inv', 'gray', 'alpha', + 'alphaOff', 'alphaMod', 'hue', 'hueOff', 'hueMod', 'sat', 'satOff', + 'satMod', 'lum', 'lumMod', 'lumOff', 'red', 'redOff', 'redMod', 'green', + 'greenOff', 'greenMod', 'blue', 'blueOff', 'blueMod', 'gamma', + 'invGamma') + + def __init__(self, + tint=None, + shade=None, + comp=None, + inv=None, + gray=None, + alpha=None, + alphaOff=None, + alphaMod=None, + hue=None, + hueOff=None, + hueMod=None, + sat=None, + satOff=None, + satMod=None, + lum=None, + lumOff=None, + lumMod=None, + red=None, + redOff=None, + redMod=None, + green=None, + greenOff=None, + greenMod=None, + blue=None, + blueOff=None, + blueMod=None, + gamma=None, + invGamma=None, + val=None, + ): + self.tint = tint + self.shade = shade + self.comp = comp + self.inv = inv + self.gray = gray + self.alpha = alpha + self.alphaOff = alphaOff + self.alphaMod = alphaMod + self.hue = hue + self.hueOff = hueOff + self.hueMod = hueMod + self.sat = sat + self.satOff = satOff + self.satMod = satMod + self.lum = lum + self.lumOff = lumOff + self.lumMod = lumMod + self.red = red + self.redOff = redOff + self.redMod = redMod + self.green = green + self.greenOff = greenOff + self.greenMod = greenMod + self.blue = blue + self.blueOff = blueOff + self.blueMod = blueMod + self.gamma = gamma + self.invGamma = invGamma + self.val = val + +class ColorChoice(Serialisable): + + tagname = "colorChoice" + namespace = DRAWING_NS + + scrgbClr = Typed(expected_type=RGBPercent, allow_none=True) + RGBPercent = Alias('scrgbClr') + srgbClr = NestedValue(expected_type=str, allow_none=True) # needs pattern and can have transform + RGB = Alias('srgbClr') + hslClr = Typed(expected_type=HSLColor, allow_none=True) + sysClr = Typed(expected_type=SystemColor, allow_none=True) + schemeClr = Typed(expected_type=SchemeColor, allow_none=True) + prstClr = NestedNoneSet(values=PRESET_COLORS) + + __elements__ = ('scrgbClr', 'srgbClr', 'hslClr', 'sysClr', 'schemeClr', 'prstClr') + + def __init__(self, + scrgbClr=None, + srgbClr=None, + hslClr=None, + sysClr=None, + schemeClr=None, + prstClr=None, + ): + self.scrgbClr = scrgbClr + self.srgbClr = srgbClr + self.hslClr = hslClr + self.sysClr = sysClr + self.schemeClr = schemeClr + self.prstClr = prstClr + +_COLOR_SET = ('dk1', 'lt1', 'dk2', 'lt2', 'accent1', 'accent2', 'accent3', + 'accent4', 'accent5', 'accent6', 'hlink', 'folHlink') + + +class ColorMapping(Serialisable): + + tagname = "clrMapOvr" + + bg1 = Set(values=_COLOR_SET) + tx1 = Set(values=_COLOR_SET) + bg2 = Set(values=_COLOR_SET) + tx2 = Set(values=_COLOR_SET) + accent1 = Set(values=_COLOR_SET) + accent2 = Set(values=_COLOR_SET) + accent3 = Set(values=_COLOR_SET) + accent4 = Set(values=_COLOR_SET) + accent5 = Set(values=_COLOR_SET) + accent6 = Set(values=_COLOR_SET) + hlink = Set(values=_COLOR_SET) + folHlink = Set(values=_COLOR_SET) + extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True) + + def __init__(self, + bg1="lt1", + tx1="dk1", + bg2="lt2", + tx2="dk2", + accent1="accent1", + accent2="accent2", + accent3="accent3", + accent4="accent4", + accent5="accent5", + accent6="accent6", + hlink="hlink", + folHlink="folHlink", + extLst=None, + ): + self.bg1 = bg1 + self.tx1 = tx1 + self.bg2 = bg2 + self.tx2 = tx2 + self.accent1 = accent1 + self.accent2 = accent2 + self.accent3 = accent3 + self.accent4 = accent4 + self.accent5 = accent5 + self.accent6 = accent6 + self.hlink = hlink + self.folHlink = folHlink + self.extLst = extLst + + +class ColorChoiceDescriptor(Typed): + """ + Objects can choose from 7 different kinds of color system. + Assume RGBHex if a string is passed in. + """ + + expected_type = ColorChoice + allow_none = True + + def __set__(self, instance, value): + if isinstance(value, str): + value = ColorChoice(srgbClr=value) + else: + if hasattr(self, "namespace") and value is not None: + value.namespace = self.namespace + super(ColorChoiceDescriptor, self).__set__(instance, value) diff --git a/.venv/Lib/site-packages/openpyxl/drawing/connector.py b/.venv/Lib/site-packages/openpyxl/drawing/connector.py new file mode 100644 index 00000000..7d0b0a6b --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/drawing/connector.py @@ -0,0 +1,144 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Bool, + Integer, + String, + Alias, +) +from openpyxl.descriptors.excel import ExtensionList as OfficeArtExtensionList +from openpyxl.chart.shapes import GraphicalProperties +from openpyxl.chart.text import RichText + +from .properties import ( + NonVisualDrawingProps, + NonVisualDrawingShapeProps, +) +from .geometry import ShapeStyle + +class Connection(Serialisable): + + id = Integer() + idx = Integer() + + def __init__(self, + id=None, + idx=None, + ): + self.id = id + self.idx = idx + + +class ConnectorLocking(Serialisable): + + extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True) + + def __init__(self, + extLst=None, + ): + self.extLst = extLst + + +class NonVisualConnectorProperties(Serialisable): + + cxnSpLocks = Typed(expected_type=ConnectorLocking, allow_none=True) + stCxn = Typed(expected_type=Connection, allow_none=True) + endCxn = Typed(expected_type=Connection, allow_none=True) + extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True) + + def __init__(self, + cxnSpLocks=None, + stCxn=None, + endCxn=None, + extLst=None, + ): + self.cxnSpLocks = cxnSpLocks + self.stCxn = stCxn + self.endCxn = endCxn + self.extLst = extLst + + +class ConnectorNonVisual(Serialisable): + + cNvPr = Typed(expected_type=NonVisualDrawingProps, ) + cNvCxnSpPr = Typed(expected_type=NonVisualConnectorProperties, ) + + __elements__ = ("cNvPr", "cNvCxnSpPr",) + + def __init__(self, + cNvPr=None, + cNvCxnSpPr=None, + ): + self.cNvPr = cNvPr + self.cNvCxnSpPr = cNvCxnSpPr + + +class ConnectorShape(Serialisable): + + tagname = "cxnSp" + + nvCxnSpPr = Typed(expected_type=ConnectorNonVisual) + spPr = Typed(expected_type=GraphicalProperties) + style = Typed(expected_type=ShapeStyle, allow_none=True) + macro = String(allow_none=True) + fPublished = Bool(allow_none=True) + + def __init__(self, + nvCxnSpPr=None, + spPr=None, + style=None, + macro=None, + fPublished=None, + ): + self.nvCxnSpPr = nvCxnSpPr + self.spPr = spPr + self.style = style + self.macro = macro + self.fPublished = fPublished + + +class ShapeMeta(Serialisable): + + tagname = "nvSpPr" + + cNvPr = Typed(expected_type=NonVisualDrawingProps) + cNvSpPr = Typed(expected_type=NonVisualDrawingShapeProps) + + def __init__(self, cNvPr=None, cNvSpPr=None): + self.cNvPr = cNvPr + self.cNvSpPr = cNvSpPr + + +class Shape(Serialisable): + + macro = String(allow_none=True) + textlink = String(allow_none=True) + fPublished = Bool(allow_none=True) + fLocksText = Bool(allow_none=True) + nvSpPr = Typed(expected_type=ShapeMeta, allow_none=True) + meta = Alias("nvSpPr") + spPr = Typed(expected_type=GraphicalProperties) + graphicalProperties = Alias("spPr") + style = Typed(expected_type=ShapeStyle, allow_none=True) + txBody = Typed(expected_type=RichText, allow_none=True) + + def __init__(self, + macro=None, + textlink=None, + fPublished=None, + fLocksText=None, + nvSpPr=None, + spPr=None, + style=None, + txBody=None, + ): + self.macro = macro + self.textlink = textlink + self.fPublished = fPublished + self.fLocksText = fLocksText + self.nvSpPr = nvSpPr + self.spPr = spPr + self.style = style + self.txBody = txBody diff --git a/.venv/Lib/site-packages/openpyxl/drawing/drawing.py b/.venv/Lib/site-packages/openpyxl/drawing/drawing.py new file mode 100644 index 00000000..1657233f --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/drawing/drawing.py @@ -0,0 +1,102 @@ +from __future__ import division +# Copyright (c) 2010-2022 openpyxl + +import math + +from openpyxl.compat import deprecated + +from openpyxl.styles.colors import Color, BLACK, WHITE +from openpyxl.utils.units import ( + pixels_to_EMU, + EMU_to_pixels, + short_color, +) + + +class Drawing(object): + """ a drawing object - eg container for shapes or charts + we assume user specifies dimensions in pixels; units are + converted to EMU in the drawing part + """ + + count = 0 + + def __init__(self): + + self.name = '' + self.description = '' + self.coordinates = ((1, 2), (16, 8)) + self.left = 0 + self.top = 0 + self._width = 21 # default in px + self._height = 192 #default in px + self.resize_proportional = False + self.rotation = 0 + self.anchortype = "absolute" + self.anchorcol = 0 # left cell + self.anchorrow = 0 # top row + + + @property + def width(self): + return self._width + + @width.setter + def width(self, w): + if self.resize_proportional and w: + ratio = self._height / self._width + self._height = round(ratio * w) + self._width = w + + @property + def height(self): + return self._height + + @height.setter + def height(self, h): + if self.resize_proportional and h: + ratio = self._width / self._height + self._width = round(ratio * h) + self._height = h + + def set_dimension(self, w=0, h=0): + + xratio = w / self._width + yratio = h / self._height + + if self.resize_proportional and w and h: + if (xratio * self._height) < h: + self._height = math.ceil(xratio * self._height) + self._width = w + else: + self._width = math.ceil(yratio * self._width) + self._height = h + + @deprecated("Private method used when serialising") + def get_emu_dimensions(self): + """ return (x, y, w, h) in EMU """ + + return (pixels_to_EMU(self.left), pixels_to_EMU(self.top), + pixels_to_EMU(self._width), pixels_to_EMU(self._height)) + + + @property + def anchor(self): + from .spreadsheet_drawing import ( + OneCellAnchor, + TwoCellAnchor, + AbsoluteAnchor) + if self.anchortype == "absolute": + anchor = AbsoluteAnchor() + anchor.pos.x = pixels_to_EMU(self.left) + anchor.pos.y = pixels_to_EMU(self.top) + + elif self.anchortype == "oneCell": + anchor = OneCellAnchor() + anchor._from.col = self.anchorcol + anchor._from.row = self.anchorrow + + anchor.ext.width = pixels_to_EMU(self._width) + anchor.ext.height = pixels_to_EMU(self._height) + + return anchor diff --git a/.venv/Lib/site-packages/openpyxl/drawing/effect.py b/.venv/Lib/site-packages/openpyxl/drawing/effect.py new file mode 100644 index 00000000..043a3039 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/drawing/effect.py @@ -0,0 +1,409 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + String, + Set, + Bool, + Integer, + NoneSet, + Float, +) + + +from .colors import ColorChoice + + +class TintEffect(Serialisable): + + tagname = "tint" + + hue = Integer() + amt = Integer() + + def __init__(self, + hue=0, + amt=0, + ): + self.hue = hue + self.amt = amt + + +class LuminanceEffect(Serialisable): + + tagname = "lum" + + bright = Integer() #Pct ? + contrast = Integer() #Pct# + + def __init__(self, + bright=0, + contrast=0, + ): + self.bright = bright + self.contrast = contrast + + +class HSLEffect(Serialisable): + + hue = Integer() + sat = Integer() + lum = Integer() + + def __init__(self, + hue=None, + sat=None, + lum=None, + ): + self.hue = hue + self.sat = sat + self.lum = lum + + +class GrayscaleEffect(Serialisable): + + tagname = "grayscl" + + +class FillOverlayEffect(Serialisable): + + blend = Set(values=(['over', 'mult', 'screen', 'darken', 'lighten'])) + + def __init__(self, + blend=None, + ): + self.blend = blend + + +class DuotoneEffect(Serialisable): + + pass + +class ColorReplaceEffect(Serialisable): + + pass + +class Color(Serialisable): + + pass + +class ColorChangeEffect(Serialisable): + + useA = Bool(allow_none=True) + clrFrom = Typed(expected_type=Color, ) + clrTo = Typed(expected_type=Color, ) + + def __init__(self, + useA=None, + clrFrom=None, + clrTo=None, + ): + self.useA = useA + self.clrFrom = clrFrom + self.clrTo = clrTo + + +class BlurEffect(Serialisable): + + rad = Float() + grow = Bool(allow_none=True) + + def __init__(self, + rad=None, + grow=None, + ): + self.rad = rad + self.grow = grow + + +class BiLevelEffect(Serialisable): + + thresh = Integer() + + def __init__(self, + thresh=None, + ): + self.thresh = thresh + + +class AlphaReplaceEffect(Serialisable): + + a = Integer() + + def __init__(self, + a=None, + ): + self.a = a + + +class AlphaModulateFixedEffect(Serialisable): + + amt = Integer() + + def __init__(self, + amt=None, + ): + self.amt = amt + + +class EffectContainer(Serialisable): + + type = Set(values=(['sib', 'tree'])) + name = String(allow_none=True) + + def __init__(self, + type=None, + name=None, + ): + self.type = type + self.name = name + + +class AlphaModulateEffect(Serialisable): + + cont = Typed(expected_type=EffectContainer, ) + + def __init__(self, + cont=None, + ): + self.cont = cont + + +class AlphaInverseEffect(Serialisable): + + pass + +class AlphaFloorEffect(Serialisable): + + pass + +class AlphaCeilingEffect(Serialisable): + + pass + +class AlphaBiLevelEffect(Serialisable): + + thresh = Integer() + + def __init__(self, + thresh=None, + ): + self.thresh = thresh + + +class GlowEffect(ColorChoice): + + rad = Float() + # uses element group EG_ColorChoice + scrgbClr = ColorChoice.scrgbClr + srgbClr = ColorChoice.srgbClr + hslClr = ColorChoice.hslClr + sysClr = ColorChoice.sysClr + schemeClr = ColorChoice.schemeClr + prstClr = ColorChoice.prstClr + + __elements__ = ('scrgbClr', 'srgbClr', 'hslClr', 'sysClr', 'schemeClr', 'prstClr') + + def __init__(self, + rad=None, + **kw + ): + self.rad = rad + super(GlowEffect, self).__init__(**kw) + + +class InnerShadowEffect(ColorChoice): + + blurRad = Float() + dist = Float() + dir = Integer() + # uses element group EG_ColorChoice + scrgbClr = ColorChoice.scrgbClr + srgbClr = ColorChoice.srgbClr + hslClr = ColorChoice.hslClr + sysClr = ColorChoice.sysClr + schemeClr = ColorChoice.schemeClr + prstClr = ColorChoice.prstClr + + __elements__ = ('scrgbClr', 'srgbClr', 'hslClr', 'sysClr', 'schemeClr', 'prstClr') + + def __init__(self, + blurRad=None, + dist=None, + dir=None, + **kw + ): + self.blurRad = blurRad + self.dist = dist + self.dir = dir + super(InnerShadowEffect, self).__init__(**kw) + + +class OuterShadow(ColorChoice): + + tagname = "outerShdw" + + blurRad = Float(allow_none=True) + dist = Float(allow_none=True) + dir = Integer(allow_none=True) + sx = Integer(allow_none=True) + sy = Integer(allow_none=True) + kx = Integer(allow_none=True) + ky = Integer(allow_none=True) + algn = Set(values=['tl', 't', 'tr', 'l', 'ctr', 'r', 'bl', 'b', 'br']) + rotWithShape = Bool(allow_none=True) + # uses element group EG_ColorChoice + scrgbClr = ColorChoice.scrgbClr + srgbClr = ColorChoice.srgbClr + hslClr = ColorChoice.hslClr + sysClr = ColorChoice.sysClr + schemeClr = ColorChoice.schemeClr + prstClr = ColorChoice.prstClr + + __elements__ = ('scrgbClr', 'srgbClr', 'hslClr', 'sysClr', 'schemeClr', 'prstClr') + + def __init__(self, + blurRad=None, + dist=None, + dir=None, + sx=None, + sy=None, + kx=None, + ky=None, + algn=None, + rotWithShape=None, + **kw + ): + self.blurRad = blurRad + self.dist = dist + self.dir = dir + self.sx = sx + self.sy = sy + self.kx = kx + self.ky = ky + self.algn = algn + self.rotWithShape = rotWithShape + super(OuterShadow, self).__init__(**kw) + + +class PresetShadowEffect(ColorChoice): + + prst = Set(values=(['shdw1', 'shdw2', 'shdw3', 'shdw4', 'shdw5', 'shdw6', + 'shdw7', 'shdw8', 'shdw9', 'shdw10', 'shdw11', 'shdw12', 'shdw13', + 'shdw14', 'shdw15', 'shdw16', 'shdw17', 'shdw18', 'shdw19', 'shdw20'])) + dist = Float() + dir = Integer() + # uses element group EG_ColorChoice + scrgbClr = ColorChoice.scrgbClr + srgbClr = ColorChoice.srgbClr + hslClr = ColorChoice.hslClr + sysClr = ColorChoice.sysClr + schemeClr = ColorChoice.schemeClr + prstClr = ColorChoice.prstClr + + __elements__ = ('scrgbClr', 'srgbClr', 'hslClr', 'sysClr', 'schemeClr', 'prstClr') + + def __init__(self, + prst=None, + dist=None, + dir=None, + **kw + ): + self.prst = prst + self.dist = dist + self.dir = dir + super(PresetShadowEffect, self).__init__(**kw) + + +class ReflectionEffect(Serialisable): + + blurRad = Float() + stA = Integer() + stPos = Integer() + endA = Integer() + endPos = Integer() + dist = Float() + dir = Integer() + fadeDir = Integer() + sx = Integer() + sy = Integer() + kx = Integer() + ky = Integer() + algn = Set(values=(['tl', 't', 'tr', 'l', 'ctr', 'r', 'bl', 'b', 'br'])) + rotWithShape = Bool(allow_none=True) + + def __init__(self, + blurRad=None, + stA=None, + stPos=None, + endA=None, + endPos=None, + dist=None, + dir=None, + fadeDir=None, + sx=None, + sy=None, + kx=None, + ky=None, + algn=None, + rotWithShape=None, + ): + self.blurRad = blurRad + self.stA = stA + self.stPos = stPos + self.endA = endA + self.endPos = endPos + self.dist = dist + self.dir = dir + self.fadeDir = fadeDir + self.sx = sx + self.sy = sy + self.kx = kx + self.ky = ky + self.algn = algn + self.rotWithShape = rotWithShape + + +class SoftEdgesEffect(Serialisable): + + rad = Float() + + def __init__(self, + rad=None, + ): + self.rad = rad + + +class EffectList(Serialisable): + + blur = Typed(expected_type=BlurEffect, allow_none=True) + fillOverlay = Typed(expected_type=FillOverlayEffect, allow_none=True) + glow = Typed(expected_type=GlowEffect, allow_none=True) + innerShdw = Typed(expected_type=InnerShadowEffect, allow_none=True) + outerShdw = Typed(expected_type=OuterShadow, allow_none=True) + prstShdw = Typed(expected_type=PresetShadowEffect, allow_none=True) + reflection = Typed(expected_type=ReflectionEffect, allow_none=True) + softEdge = Typed(expected_type=SoftEdgesEffect, allow_none=True) + + __elements__ = ('blur', 'fillOverlay', 'glow', 'innerShdw', 'outerShdw', + 'prstShdw', 'reflection', 'softEdge') + + def __init__(self, + blur=None, + fillOverlay=None, + glow=None, + innerShdw=None, + outerShdw=None, + prstShdw=None, + reflection=None, + softEdge=None, + ): + self.blur = blur + self.fillOverlay = fillOverlay + self.glow = glow + self.innerShdw = innerShdw + self.outerShdw = outerShdw + self.prstShdw = prstShdw + self.reflection = reflection + self.softEdge = softEdge diff --git a/.venv/Lib/site-packages/openpyxl/drawing/fill.py b/.venv/Lib/site-packages/openpyxl/drawing/fill.py new file mode 100644 index 00000000..f064c87e --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/drawing/fill.py @@ -0,0 +1,410 @@ +# Copyright (c) 2010-2022 openpyxl + + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Alias, + Bool, + Integer, + Set, + NoneSet, + Typed, + MinMax, + Sequence, +) +from openpyxl.descriptors.excel import ( + Relation, + Percentage, +) +from openpyxl.descriptors.nested import NestedNoneSet, NestedValue +from openpyxl.descriptors.sequence import NestedSequence +from openpyxl.xml.constants import DRAWING_NS + +from .colors import ( + ColorChoice, + HSLColor, + SystemColor, + SchemeColor, + RGBPercent, + PRESET_COLORS, +) + + +from openpyxl.descriptors.excel import ExtensionList as OfficeArtExtensionList +from .effect import * + +""" +Fill elements from drawing main schema +""" + +class PatternFillProperties(Serialisable): + + tagname = "pattFill" + namespace = DRAWING_NS + + prst = NoneSet(values=(['pct5', 'pct10', 'pct20', 'pct25', 'pct30', 'pct40', + 'pct50', 'pct60', 'pct70', 'pct75', 'pct80', 'pct90', 'horz', 'vert', + 'ltHorz', 'ltVert', 'dkHorz', 'dkVert', 'narHorz', 'narVert', 'dashHorz', + 'dashVert', 'cross', 'dnDiag', 'upDiag', 'ltDnDiag', 'ltUpDiag', + 'dkDnDiag', 'dkUpDiag', 'wdDnDiag', 'wdUpDiag', 'dashDnDiag', + 'dashUpDiag', 'diagCross', 'smCheck', 'lgCheck', 'smGrid', 'lgGrid', + 'dotGrid', 'smConfetti', 'lgConfetti', 'horzBrick', 'diagBrick', + 'solidDmnd', 'openDmnd', 'dotDmnd', 'plaid', 'sphere', 'weave', 'divot', + 'shingle', 'wave', 'trellis', 'zigZag'])) + preset = Alias("prst") + fgClr = Typed(expected_type=ColorChoice, allow_none=True) + foreground = Alias("fgClr") + bgClr = Typed(expected_type=ColorChoice, allow_none=True) + background = Alias("bgClr") + + __elements__ = ("fgClr", "bgClr") + + def __init__(self, + prst=None, + fgClr=None, + bgClr=None, + ): + self.prst = prst + self.fgClr = fgClr + self.bgClr = bgClr + + +class RelativeRect(Serialisable): + + tagname = "rect" + namespace = DRAWING_NS + + l = Percentage(allow_none=True) + left = Alias('l') + t = Percentage(allow_none=True) + top = Alias('t') + r = Percentage(allow_none=True) + right = Alias('r') + b = Percentage(allow_none=True) + bottom = Alias('b') + + def __init__(self, + l=None, + t=None, + r=None, + b=None, + ): + self.l = l + self.t = t + self.r = r + self.b = b + + +class StretchInfoProperties(Serialisable): + + tagname = "stretch" + namespace = DRAWING_NS + + fillRect = Typed(expected_type=RelativeRect, allow_none=True) + + def __init__(self, + fillRect=RelativeRect(), + ): + self.fillRect = fillRect + + +class GradientStop(Serialisable): + + tagname = "gs" + namespace = DRAWING_NS + + pos = MinMax(min=0, max=100000, allow_none=True) + # Color Choice Group + scrgbClr = Typed(expected_type=RGBPercent, allow_none=True) + RGBPercent = Alias('scrgbClr') + srgbClr = NestedValue(expected_type=str, allow_none=True) # needs pattern and can have transform + RGB = Alias('srgbClr') + hslClr = Typed(expected_type=HSLColor, allow_none=True) + sysClr = Typed(expected_type=SystemColor, allow_none=True) + schemeClr = Typed(expected_type=SchemeColor, allow_none=True) + prstClr = NestedNoneSet(values=PRESET_COLORS) + + __elements__ = ('scrgbClr', 'srgbClr', 'hslClr', 'sysClr', 'schemeClr', 'prstClr') + + def __init__(self, + pos=None, + scrgbClr=None, + srgbClr=None, + hslClr=None, + sysClr=None, + schemeClr=None, + prstClr=None, + ): + if pos is None: + pos = 0 + self.pos = pos + + self.scrgbClr = scrgbClr + self.srgbClr = srgbClr + self.hslClr = hslClr + self.sysClr = sysClr + self.schemeClr = schemeClr + self.prstClr = prstClr + + +class LinearShadeProperties(Serialisable): + + tagname = "lin" + namespace = DRAWING_NS + + ang = Integer() + scaled = Bool(allow_none=True) + + def __init__(self, + ang=None, + scaled=None, + ): + self.ang = ang + self.scaled = scaled + + +class PathShadeProperties(Serialisable): + + tagname = "path" + namespace = DRAWING_NS + + path = Set(values=(['shape', 'circle', 'rect'])) + fillToRect = Typed(expected_type=RelativeRect, allow_none=True) + + def __init__(self, + path=None, + fillToRect=None, + ): + self.path = path + self.fillToRect = fillToRect + + +class GradientFillProperties(Serialisable): + + tagname = "gradFill" + namespace = DRAWING_NS + + flip = NoneSet(values=(['x', 'y', 'xy'])) + rotWithShape = Bool(allow_none=True) + + gsLst = NestedSequence(expected_type=GradientStop, count=False) + stop_list = Alias("gsLst") + + lin = Typed(expected_type=LinearShadeProperties, allow_none=True) + linear = Alias("lin") + path = Typed(expected_type=PathShadeProperties, allow_none=True) + + tileRect = Typed(expected_type=RelativeRect, allow_none=True) + + __elements__ = ('gsLst', 'lin', 'path', 'tileRect') + + def __init__(self, + flip=None, + rotWithShape=None, + gsLst=(), + lin=None, + path=None, + tileRect=None, + ): + self.flip = flip + self.rotWithShape = rotWithShape + self.gsLst = gsLst + self.lin = lin + self.path = path + self.tileRect = tileRect + + +class SolidColorFillProperties(Serialisable): + + tagname = "solidFill" + + # uses element group EG_ColorChoice + scrgbClr = Typed(expected_type=RGBPercent, allow_none=True) + RGBPercent = Alias('scrgbClr') + srgbClr = NestedValue(expected_type=str, allow_none=True) # needs pattern and can have transform + RGB = Alias('srgbClr') + hslClr = Typed(expected_type=HSLColor, allow_none=True) + sysClr = Typed(expected_type=SystemColor, allow_none=True) + schemeClr = Typed(expected_type=SchemeColor, allow_none=True) + prstClr = NestedNoneSet(values=PRESET_COLORS) + + __elements__ = ('scrgbClr', 'srgbClr', 'hslClr', 'sysClr', 'schemeClr', 'prstClr') + + def __init__(self, + scrgbClr=None, + srgbClr=None, + hslClr=None, + sysClr=None, + schemeClr=None, + prstClr=None, + ): + self.scrgbClr = scrgbClr + self.srgbClr = srgbClr + self.hslClr = hslClr + self.sysClr = sysClr + self.schemeClr = schemeClr + self.prstClr = prstClr + + +class Blip(Serialisable): + + tagname = "blip" + namespace = DRAWING_NS + + #Using attribute groupAG_Blob + cstate = NoneSet(values=(['email', 'screen', 'print', 'hqprint'])) + embed = Relation() #rId + link = Relation() #hyperlink + noGrp = Bool(allow_none=True) + noSelect = Bool(allow_none=True) + noRot = Bool(allow_none=True) + noChangeAspect = Bool(allow_none=True) + noMove = Bool(allow_none=True) + noResize = Bool(allow_none=True) + noEditPoints = Bool(allow_none=True) + noAdjustHandles = Bool(allow_none=True) + noChangeArrowheads = Bool(allow_none=True) + noChangeShapeType = Bool(allow_none=True) + # some elements are choice + extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True) + alphaBiLevel = Typed(expected_type=AlphaBiLevelEffect, allow_none=True) + alphaCeiling = Typed(expected_type=AlphaCeilingEffect, allow_none=True) + alphaFloor = Typed(expected_type=AlphaFloorEffect, allow_none=True) + alphaInv = Typed(expected_type=AlphaInverseEffect, allow_none=True) + alphaMod = Typed(expected_type=AlphaModulateEffect, allow_none=True) + alphaModFix = Typed(expected_type=AlphaModulateFixedEffect, allow_none=True) + alphaRepl = Typed(expected_type=AlphaReplaceEffect, allow_none=True) + biLevel = Typed(expected_type=BiLevelEffect, allow_none=True) + blur = Typed(expected_type=BlurEffect, allow_none=True) + clrChange = Typed(expected_type=ColorChangeEffect, allow_none=True) + clrRepl = Typed(expected_type=ColorReplaceEffect, allow_none=True) + duotone = Typed(expected_type=DuotoneEffect, allow_none=True) + fillOverlay = Typed(expected_type=FillOverlayEffect, allow_none=True) + grayscl = Typed(expected_type=GrayscaleEffect, allow_none=True) + hsl = Typed(expected_type=HSLEffect, allow_none=True) + lum = Typed(expected_type=LuminanceEffect, allow_none=True) + tint = Typed(expected_type=TintEffect, allow_none=True) + + __elements__ = ('alphaBiLevel', 'alphaCeiling', 'alphaFloor', 'alphaInv', + 'alphaMod', 'alphaModFix', 'alphaRepl', 'biLevel', 'blur', 'clrChange', + 'clrRepl', 'duotone', 'fillOverlay', 'grayscl', 'hsl', 'lum', 'tint') + + def __init__(self, + cstate=None, + embed=None, + link=None, + noGrp=None, + noSelect=None, + noRot=None, + noChangeAspect=None, + noMove=None, + noResize=None, + noEditPoints=None, + noAdjustHandles=None, + noChangeArrowheads=None, + noChangeShapeType=None, + extLst=None, + alphaBiLevel=None, + alphaCeiling=None, + alphaFloor=None, + alphaInv=None, + alphaMod=None, + alphaModFix=None, + alphaRepl=None, + biLevel=None, + blur=None, + clrChange=None, + clrRepl=None, + duotone=None, + fillOverlay=None, + grayscl=None, + hsl=None, + lum=None, + tint=None, + ): + self.cstate = cstate + self.embed = embed + self.link = link + self.noGrp = noGrp + self.noSelect = noSelect + self.noRot = noRot + self.noChangeAspect = noChangeAspect + self.noMove = noMove + self.noResize = noResize + self.noEditPoints = noEditPoints + self.noAdjustHandles = noAdjustHandles + self.noChangeArrowheads = noChangeArrowheads + self.noChangeShapeType = noChangeShapeType + self.extLst = extLst + self.alphaBiLevel = alphaBiLevel + self.alphaCeiling = alphaCeiling + self.alphaFloor = alphaFloor + self.alphaInv = alphaInv + self.alphaMod = alphaMod + self.alphaModFix = alphaModFix + self.alphaRepl = alphaRepl + self.biLevel = biLevel + self.blur = blur + self.clrChange = clrChange + self.clrRepl = clrRepl + self.duotone = duotone + self.fillOverlay = fillOverlay + self.grayscl = grayscl + self.hsl = hsl + self.lum = lum + self.tint = tint + + +class TileInfoProperties(Serialisable): + + tx = Integer(allow_none=True) + ty = Integer(allow_none=True) + sx = Integer(allow_none=True) + sy = Integer(allow_none=True) + flip = NoneSet(values=(['x', 'y', 'xy'])) + algn = Set(values=(['tl', 't', 'tr', 'l', 'ctr', 'r', 'bl', 'b', 'br'])) + + def __init__(self, + tx=None, + ty=None, + sx=None, + sy=None, + flip=None, + algn=None, + ): + self.tx = tx + self.ty = ty + self.sx = sx + self.sy = sy + self.flip = flip + self.algn = algn + + +class BlipFillProperties(Serialisable): + + tagname = "blipFill" + + dpi = Integer(allow_none=True) + rotWithShape = Bool(allow_none=True) + + blip = Typed(expected_type=Blip, allow_none=True) + srcRect = Typed(expected_type=RelativeRect, allow_none=True) + tile = Typed(expected_type=TileInfoProperties, allow_none=True) + stretch = Typed(expected_type=StretchInfoProperties, allow_none=True) + + __elements__ = ("blip", "srcRect", "tile", "stretch") + + def __init__(self, + dpi=None, + rotWithShape=None, + blip=None, + tile=None, + stretch=StretchInfoProperties(), + srcRect=None, + ): + self.dpi = dpi + self.rotWithShape = rotWithShape + self.blip = blip + self.tile = tile + self.stretch = stretch + self.srcRect = srcRect diff --git a/.venv/Lib/site-packages/openpyxl/drawing/geometry.py b/.venv/Lib/site-packages/openpyxl/drawing/geometry.py new file mode 100644 index 00000000..f580a25e --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/drawing/geometry.py @@ -0,0 +1,595 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Float, + Integer, + Bool, + MinMax, + Set, + NoneSet, + String, + Alias, +) +from openpyxl.descriptors.excel import Coordinate, Percentage + +from openpyxl.descriptors.nested import ( + EmptyTag + ) + +from openpyxl.descriptors.excel import ExtensionList as OfficeArtExtensionList +from .colors import ColorChoiceDescriptor +from .fill import ( + GradientFillProperties, + BlipFillProperties, + PatternFillProperties, + ) +from .line import LineProperties + +from openpyxl.styles.colors import Color +from openpyxl.xml.constants import DRAWING_NS + + +class Point2D(Serialisable): + + tagname = "off" + namespace = DRAWING_NS + + x = Coordinate() + y = Coordinate() + + def __init__(self, + x=None, + y=None, + ): + self.x = x + self.y = y + + +class PositiveSize2D(Serialisable): + + tagname = "ext" + namespace = DRAWING_NS + + """ + Dimensions in EMUs + """ + + cx = Integer() + width = Alias('cx') + cy = Integer() + height = Alias('cy') + + def __init__(self, + cx=None, + cy=None, + ): + self.cx = cx + self.cy = cy + + +class Transform2D(Serialisable): + + tagname = "xfrm" + namespace = DRAWING_NS + + rot = Integer(allow_none=True) + flipH = Bool(allow_none=True) + flipV = Bool(allow_none=True) + off = Typed(expected_type=Point2D, allow_none=True) + ext = Typed(expected_type=PositiveSize2D, allow_none=True) + chOff = Typed(expected_type=Point2D, allow_none=True) + chExt = Typed(expected_type=PositiveSize2D, allow_none=True) + + __elements__ = ('off', 'ext', 'chOff', 'chExt') + + def __init__(self, + rot=None, + flipH=None, + flipV=None, + off=None, + ext=None, + chOff=None, + chExt=None, + ): + self.rot = rot + self.flipH = flipH + self.flipV = flipV + self.off = off + self.ext = ext + self.chOff = chOff + self.chExt = chExt + + +class GroupTransform2D(Serialisable): + + tagname = "xfrm" + namespace = DRAWING_NS + + rot = Integer(allow_none=True) + flipH = Bool(allow_none=True) + flipV = Bool(allow_none=True) + off = Typed(expected_type=Point2D, allow_none=True) + ext = Typed(expected_type=PositiveSize2D, allow_none=True) + chOff = Typed(expected_type=Point2D, allow_none=True) + chExt = Typed(expected_type=PositiveSize2D, allow_none=True) + + __elements__ = ("off", "ext", "chOff", "chExt") + + def __init__(self, + rot=0, + flipH=None, + flipV=None, + off=None, + ext=None, + chOff=None, + chExt=None, + ): + self.rot = rot + self.flipH = flipH + self.flipV = flipV + self.off = off + self.ext = ext + self.chOff = chOff + self.chExt = chExt + + +class SphereCoords(Serialisable): + + tagname = "sphereCoords" # usually + + lat = Integer() + lon = Integer() + rev = Integer() + + def __init__(self, + lat=None, + lon=None, + rev=None, + ): + self.lat = lat + self.lon = lon + self.rev = rev + + +class Camera(Serialisable): + + tagname = "camera" + + prst = Set(values=[ + 'legacyObliqueTopLeft', 'legacyObliqueTop', 'legacyObliqueTopRight', 'legacyObliqueLeft', + 'legacyObliqueFront', 'legacyObliqueRight', 'legacyObliqueBottomLeft', + 'legacyObliqueBottom', 'legacyObliqueBottomRight', 'legacyPerspectiveTopLeft', + 'legacyPerspectiveTop', 'legacyPerspectiveTopRight', 'legacyPerspectiveLeft', + 'legacyPerspectiveFront', 'legacyPerspectiveRight', 'legacyPerspectiveBottomLeft', + 'legacyPerspectiveBottom', 'legacyPerspectiveBottomRight', 'orthographicFront', + 'isometricTopUp', 'isometricTopDown', 'isometricBottomUp', 'isometricBottomDown', + 'isometricLeftUp', 'isometricLeftDown', 'isometricRightUp', 'isometricRightDown', + 'isometricOffAxis1Left', 'isometricOffAxis1Right', 'isometricOffAxis1Top', + 'isometricOffAxis2Left', 'isometricOffAxis2Right', 'isometricOffAxis2Top', + 'isometricOffAxis3Left', 'isometricOffAxis3Right', 'isometricOffAxis3Bottom', + 'isometricOffAxis4Left', 'isometricOffAxis4Right', 'isometricOffAxis4Bottom', + 'obliqueTopLeft', 'obliqueTop', 'obliqueTopRight', 'obliqueLeft', 'obliqueRight', + 'obliqueBottomLeft', 'obliqueBottom', 'obliqueBottomRight', 'perspectiveFront', + 'perspectiveLeft', 'perspectiveRight', 'perspectiveAbove', 'perspectiveBelow', + 'perspectiveAboveLeftFacing', 'perspectiveAboveRightFacing', + 'perspectiveContrastingLeftFacing', 'perspectiveContrastingRightFacing', + 'perspectiveHeroicLeftFacing', 'perspectiveHeroicRightFacing', + 'perspectiveHeroicExtremeLeftFacing', 'perspectiveHeroicExtremeRightFacing', + 'perspectiveRelaxed', 'perspectiveRelaxedModerately']) + fov = Integer(allow_none=True) + zoom = Typed(expected_type=Percentage, allow_none=True) + rot = Typed(expected_type=SphereCoords, allow_none=True) + + + def __init__(self, + prst=None, + fov=None, + zoom=None, + rot=None, + ): + self.prst = prst + self.fov = fov + self.zoom = zoom + self.rot = rot + + +class LightRig(Serialisable): + + tagname = "lightRig" + + rig = Set(values=['legacyFlat1', 'legacyFlat2', 'legacyFlat3', 'legacyFlat4', 'legacyNormal1', + 'legacyNormal2', 'legacyNormal3', 'legacyNormal4', 'legacyHarsh1', + 'legacyHarsh2', 'legacyHarsh3', 'legacyHarsh4', 'threePt', 'balanced', + 'soft', 'harsh', 'flood', 'contrasting', 'morning', 'sunrise', 'sunset', + 'chilly', 'freezing', 'flat', 'twoPt', 'glow', 'brightRoom'] + ) + dir = Set(values=(['tl', 't', 'tr', 'l', 'r', 'bl', 'b', 'br'])) + rot = Typed(expected_type=SphereCoords, allow_none=True) + + def __init__(self, + rig=None, + dir=None, + rot=None, + ): + self.rig = rig + self.dir = dir + self.rot = rot + + +class Vector3D(Serialisable): + + tagname = "vector" + + dx = Integer() # can be in or universl measure :-/ + dy = Integer() + dz = Integer() + + def __init__(self, + dx=None, + dy=None, + dz=None, + ): + self.dx = dx + self.dy = dy + self.dz = dz + + +class Point3D(Serialisable): + + tagname = "anchor" + + x = Integer() + y = Integer() + z = Integer() + + def __init__(self, + x=None, + y=None, + z=None, + ): + self.x = x + self.y = y + self.z = z + + +class Backdrop(Serialisable): + + anchor = Typed(expected_type=Point3D, ) + norm = Typed(expected_type=Vector3D, ) + up = Typed(expected_type=Vector3D, ) + extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True) + + def __init__(self, + anchor=None, + norm=None, + up=None, + extLst=None, + ): + self.anchor = anchor + self.norm = norm + self.up = up + self.extLst = extLst + + +class Scene3D(Serialisable): + + camera = Typed(expected_type=Camera, ) + lightRig = Typed(expected_type=LightRig, ) + backdrop = Typed(expected_type=Backdrop, allow_none=True) + extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True) + + def __init__(self, + camera=None, + lightRig=None, + backdrop=None, + extLst=None, + ): + self.camera = camera + self.lightRig = lightRig + self.backdrop = backdrop + self.extLst = extLst + + +class Bevel(Serialisable): + + tagname = "bevel" + + w = Integer() + h = Integer() + prst = NoneSet(values= + ['relaxedInset', 'circle', 'slope', 'cross', 'angle', + 'softRound', 'convex', 'coolSlant', 'divot', 'riblet', + 'hardEdge', 'artDeco'] + ) + + def __init__(self, + w=None, + h=None, + prst=None, + ): + self.w = w + self.h = h + self.prst = prst + + +class Shape3D(Serialisable): + + namespace = DRAWING_NS + + z = Typed(expected_type=Coordinate, allow_none=True) + extrusionH = Integer(allow_none=True) + contourW = Integer(allow_none=True) + prstMaterial = NoneSet(values=[ + 'legacyMatte','legacyPlastic', 'legacyMetal', 'legacyWireframe', 'matte', 'plastic', + 'metal', 'warmMatte', 'translucentPowder', 'powder', 'dkEdge', + 'softEdge', 'clear', 'flat', 'softmetal'] + ) + bevelT = Typed(expected_type=Bevel, allow_none=True) + bevelB = Typed(expected_type=Bevel, allow_none=True) + extrusionClr = Typed(expected_type=Color, allow_none=True) + contourClr = Typed(expected_type=Color, allow_none=True) + extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True) + + def __init__(self, + z=None, + extrusionH=None, + contourW=None, + prstMaterial=None, + bevelT=None, + bevelB=None, + extrusionClr=None, + contourClr=None, + extLst=None, + ): + self.z = z + self.extrusionH = extrusionH + self.contourW = contourW + self.prstMaterial = prstMaterial + self.bevelT = bevelT + self.bevelB = bevelB + self.extrusionClr = extrusionClr + self.contourClr = contourClr + self.extLst = extLst + + +class Path2D(Serialisable): + + w = Float() + h = Float() + fill = NoneSet(values=(['norm', 'lighten', 'lightenLess', 'darken', 'darkenLess'])) + stroke = Bool(allow_none=True) + extrusionOk = Bool(allow_none=True) + + def __init__(self, + w=None, + h=None, + fill=None, + stroke=None, + extrusionOk=None, + ): + self.w = w + self.h = h + self.fill = fill + self.stroke = stroke + self.extrusionOk = extrusionOk + + +class Path2DList(Serialisable): + + path = Typed(expected_type=Path2D, allow_none=True) + + def __init__(self, + path=None, + ): + self.path = path + + +class GeomRect(Serialisable): + + l = Coordinate() + t = Coordinate() + r = Coordinate() + b = Coordinate() + + def __init__(self, + l=None, + t=None, + r=None, + b=None, + ): + self.l = l + self.t = t + self.r = r + self.b = b + + +class AdjPoint2D(Serialisable): + + x = Coordinate() + y = Coordinate() + + def __init__(self, + x=None, + y=None, + ): + self.x = x + self.y = y + + +class ConnectionSite(Serialisable): + + ang = MinMax(min=0, max=360) # guess work, can also be a name + pos = Typed(expected_type=AdjPoint2D, ) + + def __init__(self, + ang=None, + pos=None, + ): + self.ang = ang + self.pos = pos + + +class ConnectionSiteList(Serialisable): + + cxn = Typed(expected_type=ConnectionSite, allow_none=True) + + def __init__(self, + cxn=None, + ): + self.cxn = cxn + + +class AdjustHandleList(Serialisable): + + pass + +class GeomGuide(Serialisable): + + name = String() + fmla = String() + + def __init__(self, + name=None, + fmla=None, + ): + self.name = name + self.fmla = fmla + + +class GeomGuideList(Serialisable): + + gd = Typed(expected_type=GeomGuide, allow_none=True) + + def __init__(self, + gd=None, + ): + self.gd = gd + + +class CustomGeometry2D(Serialisable): + + avLst = Typed(expected_type=GeomGuideList, allow_none=True) + gdLst = Typed(expected_type=GeomGuideList, allow_none=True) + ahLst = Typed(expected_type=AdjustHandleList, allow_none=True) + cxnLst = Typed(expected_type=ConnectionSiteList, allow_none=True) + #rect = Typed(expected_type=GeomRect, allow_none=True) + pathLst = Typed(expected_type=Path2DList, ) + + def __init__(self, + avLst=None, + gdLst=None, + ahLst=None, + cxnLst=None, + rect=None, + pathLst=None, + ): + self.avLst = avLst + self.gdLst = gdLst + self.ahLst = ahLst + self.cxnLst = cxnLst + self.rect = None + self.pathLst = pathLst + + +class PresetGeometry2D(Serialisable): + + namespace = DRAWING_NS + + prst = Set(values=( + ['line', 'lineInv', 'triangle', 'rtTriangle', 'rect', + 'diamond', 'parallelogram', 'trapezoid', 'nonIsoscelesTrapezoid', + 'pentagon', 'hexagon', 'heptagon', 'octagon', 'decagon', 'dodecagon', + 'star4', 'star5', 'star6', 'star7', 'star8', 'star10', 'star12', + 'star16', 'star24', 'star32', 'roundRect', 'round1Rect', + 'round2SameRect', 'round2DiagRect', 'snipRoundRect', 'snip1Rect', + 'snip2SameRect', 'snip2DiagRect', 'plaque', 'ellipse', 'teardrop', + 'homePlate', 'chevron', 'pieWedge', 'pie', 'blockArc', 'donut', + 'noSmoking', 'rightArrow', 'leftArrow', 'upArrow', 'downArrow', + 'stripedRightArrow', 'notchedRightArrow', 'bentUpArrow', + 'leftRightArrow', 'upDownArrow', 'leftUpArrow', 'leftRightUpArrow', + 'quadArrow', 'leftArrowCallout', 'rightArrowCallout', 'upArrowCallout', + 'downArrowCallout', 'leftRightArrowCallout', 'upDownArrowCallout', + 'quadArrowCallout', 'bentArrow', 'uturnArrow', 'circularArrow', + 'leftCircularArrow', 'leftRightCircularArrow', 'curvedRightArrow', + 'curvedLeftArrow', 'curvedUpArrow', 'curvedDownArrow', 'swooshArrow', + 'cube', 'can', 'lightningBolt', 'heart', 'sun', 'moon', 'smileyFace', + 'irregularSeal1', 'irregularSeal2', 'foldedCorner', 'bevel', 'frame', + 'halfFrame', 'corner', 'diagStripe', 'chord', 'arc', 'leftBracket', + 'rightBracket', 'leftBrace', 'rightBrace', 'bracketPair', 'bracePair', + 'straightConnector1', 'bentConnector2', 'bentConnector3', + 'bentConnector4', 'bentConnector5', 'curvedConnector2', + 'curvedConnector3', 'curvedConnector4', 'curvedConnector5', 'callout1', + 'callout2', 'callout3', 'accentCallout1', 'accentCallout2', + 'accentCallout3', 'borderCallout1', 'borderCallout2', 'borderCallout3', + 'accentBorderCallout1', 'accentBorderCallout2', 'accentBorderCallout3', + 'wedgeRectCallout', 'wedgeRoundRectCallout', 'wedgeEllipseCallout', + 'cloudCallout', 'cloud', 'ribbon', 'ribbon2', 'ellipseRibbon', + 'ellipseRibbon2', 'leftRightRibbon', 'verticalScroll', + 'horizontalScroll', 'wave', 'doubleWave', 'plus', 'flowChartProcess', + 'flowChartDecision', 'flowChartInputOutput', + 'flowChartPredefinedProcess', 'flowChartInternalStorage', + 'flowChartDocument', 'flowChartMultidocument', 'flowChartTerminator', + 'flowChartPreparation', 'flowChartManualInput', + 'flowChartManualOperation', 'flowChartConnector', 'flowChartPunchedCard', + 'flowChartPunchedTape', 'flowChartSummingJunction', 'flowChartOr', + 'flowChartCollate', 'flowChartSort', 'flowChartExtract', + 'flowChartMerge', 'flowChartOfflineStorage', 'flowChartOnlineStorage', + 'flowChartMagneticTape', 'flowChartMagneticDisk', + 'flowChartMagneticDrum', 'flowChartDisplay', 'flowChartDelay', + 'flowChartAlternateProcess', 'flowChartOffpageConnector', + 'actionButtonBlank', 'actionButtonHome', 'actionButtonHelp', + 'actionButtonInformation', 'actionButtonForwardNext', + 'actionButtonBackPrevious', 'actionButtonEnd', 'actionButtonBeginning', + 'actionButtonReturn', 'actionButtonDocument', 'actionButtonSound', + 'actionButtonMovie', 'gear6', 'gear9', 'funnel', 'mathPlus', 'mathMinus', + 'mathMultiply', 'mathDivide', 'mathEqual', 'mathNotEqual', 'cornerTabs', + 'squareTabs', 'plaqueTabs', 'chartX', 'chartStar', 'chartPlus'])) + avLst = Typed(expected_type=GeomGuideList, allow_none=True) + + def __init__(self, + prst=None, + avLst=None, + ): + self.prst = prst + self.avLst = avLst + + +class FontReference(Serialisable): + + idx = NoneSet(values=(['major', 'minor'])) + + def __init__(self, + idx=None, + ): + self.idx = idx + + +class StyleMatrixReference(Serialisable): + + idx = Integer() + + def __init__(self, + idx=None, + ): + self.idx = idx + + +class ShapeStyle(Serialisable): + + lnRef = Typed(expected_type=StyleMatrixReference, ) + fillRef = Typed(expected_type=StyleMatrixReference, ) + effectRef = Typed(expected_type=StyleMatrixReference, ) + fontRef = Typed(expected_type=FontReference, ) + + def __init__(self, + lnRef=None, + fillRef=None, + effectRef=None, + fontRef=None, + ): + self.lnRef = lnRef + self.fillRef = fillRef + self.effectRef = effectRef + self.fontRef = fontRef diff --git a/.venv/Lib/site-packages/openpyxl/drawing/graphic.py b/.venv/Lib/site-packages/openpyxl/drawing/graphic.py new file mode 100644 index 00000000..d5411014 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/drawing/graphic.py @@ -0,0 +1,186 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.xml.functions import NS_REGEX, Element +from openpyxl.xml.constants import CHART_NS, REL_NS, DRAWING_NS + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Bool, + NoneSet, + Integer, + Set, + String, + Alias, +) +from openpyxl.descriptors.excel import ExtensionList as OfficeArtExtensionList + +from openpyxl.chart.shapes import GraphicalProperties +from openpyxl.chart.text import RichText + +from .effect import * +from .fill import RelativeRect, BlipFillProperties +from .text import Hyperlink, EmbeddedWAVAudioFile +from .geometry import ( + Scene3D, + ShapeStyle, + GroupTransform2D +) +from .picture import PictureFrame +from .properties import ( + NonVisualDrawingProps, + NonVisualDrawingShapeProps, + NonVisualGroupDrawingShapeProps, + NonVisualGroupShape, + GroupShapeProperties, +) +from .relation import ChartRelation +from .xdr import XDRTransform2D + + +class GraphicFrameLocking(Serialisable): + + noGrp = Bool(allow_none=True) + noDrilldown = Bool(allow_none=True) + noSelect = Bool(allow_none=True) + noChangeAspect = Bool(allow_none=True) + noMove = Bool(allow_none=True) + noResize = Bool(allow_none=True) + extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True) + + def __init__(self, + noGrp=None, + noDrilldown=None, + noSelect=None, + noChangeAspect=None, + noMove=None, + noResize=None, + extLst=None, + ): + self.noGrp = noGrp + self.noDrilldown = noDrilldown + self.noSelect = noSelect + self.noChangeAspect = noChangeAspect + self.noMove = noMove + self.noResize = noResize + self.extLst = extLst + + +class NonVisualGraphicFrameProperties(Serialisable): + + tagname = "cNvGraphicFramePr" + + graphicFrameLocks = Typed(expected_type=GraphicFrameLocking, allow_none=True) + extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True) + + def __init__(self, + graphicFrameLocks=None, + extLst=None, + ): + self.graphicFrameLocks = graphicFrameLocks + self.extLst = extLst + + +class NonVisualGraphicFrame(Serialisable): + + tagname = "nvGraphicFramePr" + + cNvPr = Typed(expected_type=NonVisualDrawingProps) + cNvGraphicFramePr = Typed(expected_type=NonVisualGraphicFrameProperties) + + __elements__ = ('cNvPr', 'cNvGraphicFramePr') + + def __init__(self, + cNvPr=None, + cNvGraphicFramePr=None, + ): + if cNvPr is None: + cNvPr = NonVisualDrawingProps(id=0, name="Chart 0") + self.cNvPr = cNvPr + if cNvGraphicFramePr is None: + cNvGraphicFramePr = NonVisualGraphicFrameProperties() + self.cNvGraphicFramePr = cNvGraphicFramePr + + +class GraphicData(Serialisable): + + tagname = "graphicData" + namespace = DRAWING_NS + + uri = String() + chart = Typed(expected_type=ChartRelation, allow_none=True) + + + def __init__(self, + uri=CHART_NS, + chart=None, + ): + self.uri = uri + self.chart = chart + + +class GraphicObject(Serialisable): + + tagname = "graphic" + namespace = DRAWING_NS + + graphicData = Typed(expected_type=GraphicData) + + def __init__(self, + graphicData=None, + ): + if graphicData is None: + graphicData = GraphicData() + self.graphicData = graphicData + + +class GraphicFrame(Serialisable): + + tagname = "graphicFrame" + + nvGraphicFramePr = Typed(expected_type=NonVisualGraphicFrame) + xfrm = Typed(expected_type=XDRTransform2D) + graphic = Typed(expected_type=GraphicObject) + macro = String(allow_none=True) + fPublished = Bool(allow_none=True) + + __elements__ = ('nvGraphicFramePr', 'xfrm', 'graphic', 'macro', 'fPublished') + + def __init__(self, + nvGraphicFramePr=None, + xfrm=None, + graphic=None, + macro=None, + fPublished=None, + ): + if nvGraphicFramePr is None: + nvGraphicFramePr = NonVisualGraphicFrame() + self.nvGraphicFramePr = nvGraphicFramePr + if xfrm is None: + xfrm = XDRTransform2D() + self.xfrm = xfrm + if graphic is None: + graphic = GraphicObject() + self.graphic = graphic + self.macro = macro + self.fPublished = fPublished + + +class GroupShape(Serialisable): + + nvGrpSpPr = Typed(expected_type=NonVisualGroupShape) + nonVisualProperties = Alias("nvGrpSpPr") + grpSpPr = Typed(expected_type=GroupShapeProperties) + visualProperties = Alias("grpSpPr") + pic = Typed(expected_type=PictureFrame, allow_none=True) + + __elements__ = ["nvGrpSpPr", "grpSpPr", "pic"] + + def __init__(self, + nvGrpSpPr=None, + grpSpPr=None, + pic=None, + ): + self.nvGrpSpPr = nvGrpSpPr + self.grpSpPr = grpSpPr + self.pic = pic diff --git a/.venv/Lib/site-packages/openpyxl/drawing/image.py b/.venv/Lib/site-packages/openpyxl/drawing/image.py new file mode 100644 index 00000000..a76c1fca --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/drawing/image.py @@ -0,0 +1,65 @@ +# Copyright (c) 2010-2022 openpyxl + +from io import BytesIO + +try: + from PIL import Image as PILImage +except ImportError: + PILImage = False + + +def _import_image(img): + if not PILImage: + raise ImportError('You must install Pillow to fetch image objects') + + if not isinstance(img, PILImage.Image): + img = PILImage.open(img) + + return img + + +class Image(object): + """Image in a spreadsheet""" + + _id = 1 + _path = "/xl/media/image{0}.{1}" + anchor = "A1" + + def __init__(self, img): + + self.ref = img + mark_to_close = isinstance(img, str) + image = _import_image(img) + self.width, self.height = image.size + + try: + self.format = image.format.lower() + except AttributeError: + self.format = "png" + if mark_to_close: + # PIL instances created for metadata should be closed. + image.close() + + + def _data(self): + """ + Return image data, convert to supported types if necessary + """ + img = _import_image(self.ref) + # don't convert these file formats + if self.format in ['gif', 'jpeg', 'png']: + img.fp.seek(0) + fp = img.fp + else: + fp = BytesIO() + img.save(fp, format="png") + fp.seek(0) + + data = fp.read() + fp.close() + return data + + + @property + def path(self): + return self._path.format(self._id, self.format) diff --git a/.venv/Lib/site-packages/openpyxl/drawing/line.py b/.venv/Lib/site-packages/openpyxl/drawing/line.py new file mode 100644 index 00000000..5d480bf3 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/drawing/line.py @@ -0,0 +1,151 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Float, + Integer, + Bool, + MinMax, + Set, + NoneSet, + String, + Alias, + Sequence +) +from openpyxl.descriptors.excel import Coordinate, Percentage + +from openpyxl.descriptors.nested import ( + NestedInteger, + NestedSet, + NestedNoneSet, + EmptyTag, +) +from openpyxl.compat import safe_string +from openpyxl.xml.constants import DRAWING_NS +from openpyxl.xml.functions import Element + +from .colors import ColorChoiceDescriptor +from .fill import GradientFillProperties, PatternFillProperties +from openpyxl.descriptors.excel import ExtensionList as OfficeArtExtensionList + +""" +Line elements from drawing main schema +""" + +class LineEndProperties(Serialisable): + + tagname = "end" + namespace = DRAWING_NS + + type = NoneSet(values=(['none', 'triangle', 'stealth', 'diamond', 'oval', 'arrow'])) + w = NoneSet(values=(['sm', 'med', 'lg'])) + len = NoneSet(values=(['sm', 'med', 'lg'])) + + def __init__(self, + type=None, + w=None, + len=None, + ): + self.type = type + self.w = w + self.len = len + + +class DashStop(Serialisable): + + tagname = "ds" + namespace = DRAWING_NS + + d = Integer() + length = Alias('d') + sp = Integer() + space = Alias('sp') + + def __init__(self, + d=0, + sp=0, + ): + self.d = d + self.sp = sp + + +class DashStopList(Serialisable): + + ds = Sequence(expected_type=DashStop, allow_none=True) + + def __init__(self, + ds=None, + ): + self.ds = ds + + +class LineProperties(Serialisable): + + tagname = "ln" + namespace = DRAWING_NS + + w = MinMax(min=0, max=20116800, allow_none=True) # EMU + width = Alias('w') + cap = NoneSet(values=(['rnd', 'sq', 'flat'])) + cmpd = NoneSet(values=(['sng', 'dbl', 'thickThin', 'thinThick', 'tri'])) + algn = NoneSet(values=(['ctr', 'in'])) + + noFill = EmptyTag() + solidFill = ColorChoiceDescriptor() + gradFill = Typed(expected_type=GradientFillProperties, allow_none=True) + pattFill = Typed(expected_type=PatternFillProperties, allow_none=True) + + prstDash = NestedNoneSet(values=(['solid', 'dot', 'dash', 'lgDash', 'dashDot', + 'lgDashDot', 'lgDashDotDot', 'sysDash', 'sysDot', 'sysDashDot', + 'sysDashDotDot']), namespace=namespace) + dashStyle = Alias('prstDash') + + custDash = Typed(expected_type=DashStop, allow_none=True) + + round = EmptyTag() + bevel = EmptyTag() + miter = NestedInteger(allow_none=True, attribute="lim") + + headEnd = Typed(expected_type=LineEndProperties, allow_none=True) + tailEnd = Typed(expected_type=LineEndProperties, allow_none=True) + extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True) + + __elements__ = ('noFill', 'solidFill', 'gradFill', 'pattFill', + 'prstDash', 'custDash', 'round', 'bevel', 'miter', 'headEnd', 'tailEnd') + + def __init__(self, + w=None, + cap=None, + cmpd=None, + algn=None, + noFill=None, + solidFill=None, + gradFill=None, + pattFill=None, + prstDash=None, + custDash=None, + round=None, + bevel=None, + miter=None, + headEnd=None, + tailEnd=None, + extLst=None, + ): + self.w = w + self.cap = cap + self.cmpd = cmpd + self.algn = algn + self.noFill = noFill + self.solidFill = solidFill + self.gradFill = gradFill + self.pattFill = pattFill + if prstDash is None: + prstDash = "solid" + self.prstDash = prstDash + self.custDash = custDash + self.round = round + self.bevel = bevel + self.miter = miter + self.headEnd = headEnd + self.tailEnd = tailEnd diff --git a/.venv/Lib/site-packages/openpyxl/drawing/picture.py b/.venv/Lib/site-packages/openpyxl/drawing/picture.py new file mode 100644 index 00000000..e668c4e8 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/drawing/picture.py @@ -0,0 +1,148 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.xml.constants import DRAWING_NS + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Bool, + NoneSet, + Integer, + Set, + String, + Alias, +) +from openpyxl.descriptors.excel import ExtensionList as OfficeArtExtensionList + +from openpyxl.chart.shapes import GraphicalProperties + +from .fill import RelativeRect, BlipFillProperties +from .properties import NonVisualDrawingProps, NonVisualGroupDrawingShapeProps +from .geometry import ShapeStyle + + +class PictureLocking(Serialisable): + + tagname = "picLocks" + namespace = DRAWING_NS + + #Using attribute group AG_Locking + noCrop = Bool(allow_none=True) + noGrp = Bool(allow_none=True) + noSelect = Bool(allow_none=True) + noRot = Bool(allow_none=True) + noChangeAspect = Bool(allow_none=True) + noMove = Bool(allow_none=True) + noResize = Bool(allow_none=True) + noEditPoints = Bool(allow_none=True) + noAdjustHandles = Bool(allow_none=True) + noChangeArrowheads = Bool(allow_none=True) + noChangeShapeType = Bool(allow_none=True) + extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True) + + __elements__ = () + + def __init__(self, + noCrop=None, + noGrp=None, + noSelect=None, + noRot=None, + noChangeAspect=None, + noMove=None, + noResize=None, + noEditPoints=None, + noAdjustHandles=None, + noChangeArrowheads=None, + noChangeShapeType=None, + extLst=None, + ): + self.noCrop = noCrop + self.noGrp = noGrp + self.noSelect = noSelect + self.noRot = noRot + self.noChangeAspect = noChangeAspect + self.noMove = noMove + self.noResize = noResize + self.noEditPoints = noEditPoints + self.noAdjustHandles = noAdjustHandles + self.noChangeArrowheads = noChangeArrowheads + self.noChangeShapeType = noChangeShapeType + + +class NonVisualPictureProperties(Serialisable): + + tagname = "cNvPicPr" + + preferRelativeResize = Bool(allow_none=True) + picLocks = Typed(expected_type=PictureLocking, allow_none=True) + extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True) + + __elements__ = ("picLocks",) + + def __init__(self, + preferRelativeResize=None, + picLocks=None, + extLst=None, + ): + self.preferRelativeResize = preferRelativeResize + self.picLocks = picLocks + + +class PictureNonVisual(Serialisable): + + tagname = "nvPicPr" + + cNvPr = Typed(expected_type=NonVisualDrawingProps, ) + cNvPicPr = Typed(expected_type=NonVisualPictureProperties, ) + + __elements__ = ("cNvPr", "cNvPicPr") + + def __init__(self, + cNvPr=None, + cNvPicPr=None, + ): + if cNvPr is None: + cNvPr = NonVisualDrawingProps(id=0, name="Image 1", descr="Name of file") + self.cNvPr = cNvPr + if cNvPicPr is None: + cNvPicPr = NonVisualPictureProperties() + self.cNvPicPr = cNvPicPr + + + + +class PictureFrame(Serialisable): + + tagname = "pic" + + macro = String(allow_none=True) + fPublished = Bool(allow_none=True) + nvPicPr = Typed(expected_type=PictureNonVisual, ) + blipFill = Typed(expected_type=BlipFillProperties, ) + spPr = Typed(expected_type=GraphicalProperties, ) + graphicalProperties = Alias('spPr') + style = Typed(expected_type=ShapeStyle, allow_none=True) + + __elements__ = ("nvPicPr", "blipFill", "spPr", "style") + + def __init__(self, + macro=None, + fPublished=None, + nvPicPr=None, + blipFill=None, + spPr=None, + style=None, + ): + self.macro = macro + self.fPublished = fPublished + if nvPicPr is None: + nvPicPr = PictureNonVisual() + self.nvPicPr = nvPicPr + if blipFill is None: + blipFill = BlipFillProperties() + self.blipFill = blipFill + if spPr is None: + spPr = GraphicalProperties() + self.spPr = spPr + self.style = style + diff --git a/.venv/Lib/site-packages/openpyxl/drawing/properties.py b/.venv/Lib/site-packages/openpyxl/drawing/properties.py new file mode 100644 index 00000000..f57b14dc --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/drawing/properties.py @@ -0,0 +1,174 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.xml.constants import DRAWING_NS +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Bool, + Integer, + Set, + String, + Alias, + NoneSet, +) +from openpyxl.descriptors.excel import ExtensionList as OfficeArtExtensionList + +from .geometry import GroupTransform2D, Scene3D +from .text import Hyperlink + + +class GroupShapeProperties(Serialisable): + + tagname = "grpSpPr" + + bwMode = NoneSet(values=(['clr', 'auto', 'gray', 'ltGray', 'invGray', + 'grayWhite', 'blackGray', 'blackWhite', 'black', 'white', 'hidden'])) + xfrm = Typed(expected_type=GroupTransform2D, allow_none=True) + scene3d = Typed(expected_type=Scene3D, allow_none=True) + extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True) + + def __init__(self, + bwMode=None, + xfrm=None, + scene3d=None, + extLst=None, + ): + self.bwMode = bwMode + self.xfrm = xfrm + self.scene3d = scene3d + self.extLst = extLst + + +class GroupLocking(Serialisable): + + tagname = "grpSpLocks" + namespace = DRAWING_NS + + noGrp = Bool(allow_none=True) + noUngrp = Bool(allow_none=True) + noSelect = Bool(allow_none=True) + noRot = Bool(allow_none=True) + noChangeAspect = Bool(allow_none=True) + noMove = Bool(allow_none=True) + noResize = Bool(allow_none=True) + noChangeArrowheads = Bool(allow_none=True) + noEditPoints = Bool(allow_none=True) + noAdjustHandles = Bool(allow_none=True) + noChangeArrowheads = Bool(allow_none=True) + noChangeShapeType = Bool(allow_none=True) + extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True) + + __elements__ = () + + def __init__(self, + noGrp=None, + noUngrp=None, + noSelect=None, + noRot=None, + noChangeAspect=None, + noChangeArrowheads=None, + noMove=None, + noResize=None, + noEditPoints=None, + noAdjustHandles=None, + noChangeShapeType=None, + extLst=None, + ): + self.noGrp = noGrp + self.noUngrp = noUngrp + self.noSelect = noSelect + self.noRot = noRot + self.noChangeAspect = noChangeAspect + self.noChangeArrowheads = noChangeArrowheads + self.noMove = noMove + self.noResize = noResize + self.noEditPoints = noEditPoints + self.noAdjustHandles = noAdjustHandles + self.noChangeShapeType = noChangeShapeType + + +class NonVisualGroupDrawingShapeProps(Serialisable): + + tagname = "cNvGrpSpPr" + + grpSpLocks = Typed(expected_type=GroupLocking, allow_none=True) + extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True) + + __elements__ = ("grpSpLocks",) + + def __init__(self, + grpSpLocks=None, + extLst=None, + ): + self.grpSpLocks = grpSpLocks + + +class NonVisualDrawingShapeProps(Serialisable): + + tagname = "cNvSpPr" + + spLocks = Typed(expected_type=GroupLocking, allow_none=True) + txBax = Bool(allow_none=True) + extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True) + + __elements__ = ("spLocks", "txBax") + + def __init__(self, + spLocks=None, + txBox=None, + extLst=None, + ): + self.spLocks = spLocks + self.txBox = txBox + + +class NonVisualDrawingProps(Serialisable): + + tagname = "cNvPr" + + id = Integer() + name = String() + descr = String(allow_none=True) + hidden = Bool(allow_none=True) + title = String(allow_none=True) + hlinkClick = Typed(expected_type=Hyperlink, allow_none=True) + hlinkHover = Typed(expected_type=Hyperlink, allow_none=True) + extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True) + + __elements__ = ["hlinkClick", "hlinkHover"] + + def __init__(self, + id=None, + name=None, + descr=None, + hidden=None, + title=None, + hlinkClick=None, + hlinkHover=None, + extLst=None, + ): + self.id = id + self.name = name + self.descr = descr + self.hidden = hidden + self.title = title + self.hlinkClick = hlinkClick + self.hlinkHover = hlinkHover + self.extLst = extLst + +class NonVisualGroupShape(Serialisable): + + tagname = "nvGrpSpPr" + + cNvPr = Typed(expected_type=NonVisualDrawingProps) + cNvGrpSpPr = Typed(expected_type=NonVisualGroupDrawingShapeProps) + + __elements__ = ("cNvPr", "cNvGrpSpPr") + + def __init__(self, + cNvPr=None, + cNvGrpSpPr=None, + ): + self.cNvPr = cNvPr + self.cNvGrpSpPr = cNvGrpSpPr + diff --git a/.venv/Lib/site-packages/openpyxl/drawing/relation.py b/.venv/Lib/site-packages/openpyxl/drawing/relation.py new file mode 100644 index 00000000..fb938a6f --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/drawing/relation.py @@ -0,0 +1,17 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.xml.constants import CHART_NS + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors.excel import Relation + + +class ChartRelation(Serialisable): + + tagname = "chart" + namespace = CHART_NS + + id = Relation() + + def __init__(self, id): + self.id = id diff --git a/.venv/Lib/site-packages/openpyxl/drawing/spreadsheet_drawing.py b/.venv/Lib/site-packages/openpyxl/drawing/spreadsheet_drawing.py new file mode 100644 index 00000000..87618501 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/drawing/spreadsheet_drawing.py @@ -0,0 +1,381 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Bool, + NoneSet, + Integer, + Sequence, + Alias, +) +from openpyxl.descriptors.nested import ( + NestedText, + NestedNoneSet, +) +from openpyxl.descriptors.excel import Relation + +from openpyxl.packaging.relationship import ( + Relationship, + RelationshipList, +) +from openpyxl.utils import coordinate_to_tuple +from openpyxl.utils.units import ( + cm_to_EMU, + pixels_to_EMU, +) +from openpyxl.drawing.image import Image + +from openpyxl.xml.constants import SHEET_DRAWING_NS + +from openpyxl.chart._chart import ChartBase +from .xdr import ( + XDRPoint2D, + XDRPositiveSize2D, +) +from .fill import Blip +from .connector import Shape +from .graphic import ( + GroupShape, + GraphicFrame, + ) +from .geometry import PresetGeometry2D +from .picture import PictureFrame +from .relation import ChartRelation + + +class AnchorClientData(Serialisable): + + fLocksWithSheet = Bool(allow_none=True) + fPrintsWithSheet = Bool(allow_none=True) + + def __init__(self, + fLocksWithSheet=None, + fPrintsWithSheet=None, + ): + self.fLocksWithSheet = fLocksWithSheet + self.fPrintsWithSheet = fPrintsWithSheet + + +class AnchorMarker(Serialisable): + + tagname = "marker" + + col = NestedText(expected_type=int) + colOff = NestedText(expected_type=int) + row = NestedText(expected_type=int) + rowOff = NestedText(expected_type=int) + + def __init__(self, + col=0, + colOff=0, + row=0, + rowOff=0, + ): + self.col = col + self.colOff = colOff + self.row = row + self.rowOff = rowOff + + +class _AnchorBase(Serialisable): + + #one of + sp = Typed(expected_type=Shape, allow_none=True) + shape = Alias("sp") + grpSp = Typed(expected_type=GroupShape, allow_none=True) + groupShape = Alias("grpSp") + graphicFrame = Typed(expected_type=GraphicFrame, allow_none=True) + cxnSp = Typed(expected_type=Shape, allow_none=True) + connectionShape = Alias("cxnSp") + pic = Typed(expected_type=PictureFrame, allow_none=True) + contentPart = Relation() + + clientData = Typed(expected_type=AnchorClientData) + + __elements__ = ('sp', 'grpSp', 'graphicFrame', + 'cxnSp', 'pic', 'contentPart', 'clientData') + + def __init__(self, + clientData=None, + sp=None, + grpSp=None, + graphicFrame=None, + cxnSp=None, + pic=None, + contentPart=None + ): + if clientData is None: + clientData = AnchorClientData() + self.clientData = clientData + self.sp = sp + self.grpSp = grpSp + self.graphicFrame = graphicFrame + self.cxnSp = cxnSp + self.pic = pic + self.contentPart = contentPart + + +class AbsoluteAnchor(_AnchorBase): + + tagname = "absoluteAnchor" + + pos = Typed(expected_type=XDRPoint2D) + ext = Typed(expected_type=XDRPositiveSize2D) + + sp = _AnchorBase.sp + grpSp = _AnchorBase.grpSp + graphicFrame = _AnchorBase.graphicFrame + cxnSp = _AnchorBase.cxnSp + pic = _AnchorBase.pic + contentPart = _AnchorBase.contentPart + clientData = _AnchorBase.clientData + + __elements__ = ('pos', 'ext') + _AnchorBase.__elements__ + + def __init__(self, + pos=None, + ext=None, + **kw + ): + if pos is None: + pos = XDRPoint2D(0, 0) + self.pos = pos + if ext is None: + ext = XDRPositiveSize2D(0, 0) + self.ext = ext + super(AbsoluteAnchor, self).__init__(**kw) + + +class OneCellAnchor(_AnchorBase): + + tagname = "oneCellAnchor" + + _from = Typed(expected_type=AnchorMarker) + ext = Typed(expected_type=XDRPositiveSize2D) + + sp = _AnchorBase.sp + grpSp = _AnchorBase.grpSp + graphicFrame = _AnchorBase.graphicFrame + cxnSp = _AnchorBase.cxnSp + pic = _AnchorBase.pic + contentPart = _AnchorBase.contentPart + clientData = _AnchorBase.clientData + + __elements__ = ('_from', 'ext') + _AnchorBase.__elements__ + + + def __init__(self, + _from=None, + ext=None, + **kw + ): + if _from is None: + _from = AnchorMarker() + self._from = _from + if ext is None: + ext = XDRPositiveSize2D(0, 0) + self.ext = ext + super(OneCellAnchor, self).__init__(**kw) + + +class TwoCellAnchor(_AnchorBase): + + tagname = "twoCellAnchor" + + editAs = NoneSet(values=(['twoCell', 'oneCell', 'absolute'])) + _from = Typed(expected_type=AnchorMarker) + to = Typed(expected_type=AnchorMarker) + + sp = _AnchorBase.sp + grpSp = _AnchorBase.grpSp + graphicFrame = _AnchorBase.graphicFrame + cxnSp = _AnchorBase.cxnSp + pic = _AnchorBase.pic + contentPart = _AnchorBase.contentPart + clientData = _AnchorBase.clientData + + __elements__ = ('_from', 'to') + _AnchorBase.__elements__ + + def __init__(self, + editAs=None, + _from=None, + to=None, + **kw + ): + self.editAs = editAs + if _from is None: + _from = AnchorMarker() + self._from = _from + if to is None: + to = AnchorMarker() + self.to = to + super(TwoCellAnchor, self).__init__(**kw) + + +def _check_anchor(obj): + """ + Check whether an object has an existing Anchor object + If not create a OneCellAnchor using the provided coordinate + """ + anchor = obj.anchor + if not isinstance(anchor, _AnchorBase): + row, col = coordinate_to_tuple(anchor.upper()) + anchor = OneCellAnchor() + anchor._from.row = row -1 + anchor._from.col = col -1 + if isinstance(obj, ChartBase): + anchor.ext.width = cm_to_EMU(obj.width) + anchor.ext.height = cm_to_EMU(obj.height) + elif isinstance(obj, Image): + anchor.ext.width = pixels_to_EMU(obj.width) + anchor.ext.height = pixels_to_EMU(obj.height) + return anchor + + +class SpreadsheetDrawing(Serialisable): + + tagname = "wsDr" + mime_type = "application/vnd.openxmlformats-officedocument.drawing+xml" + _rel_type = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing" + _path = PartName="/xl/drawings/drawing{0}.xml" + _id = None + + twoCellAnchor = Sequence(expected_type=TwoCellAnchor, allow_none=True) + oneCellAnchor = Sequence(expected_type=OneCellAnchor, allow_none=True) + absoluteAnchor = Sequence(expected_type=AbsoluteAnchor, allow_none=True) + + __elements__ = ("twoCellAnchor", "oneCellAnchor", "absoluteAnchor") + + def __init__(self, + twoCellAnchor=(), + oneCellAnchor=(), + absoluteAnchor=(), + ): + self.twoCellAnchor = twoCellAnchor + self.oneCellAnchor = oneCellAnchor + self.absoluteAnchor = absoluteAnchor + self.charts = [] + self.images = [] + self._rels = [] + + + def __hash__(self): + """ + Just need to check for identity + """ + return id(self) + + + def __bool__(self): + return bool(self.charts) or bool(self.images) + + + + def _write(self): + """ + create required structure and the serialise + """ + anchors = [] + for idx, obj in enumerate(self.charts + self.images, 1): + anchor = _check_anchor(obj) + if isinstance(obj, ChartBase): + rel = Relationship(type="chart", Target=obj.path) + anchor.graphicFrame = self._chart_frame(idx) + elif isinstance(obj, Image): + rel = Relationship(type="image", Target=obj.path) + child = anchor.pic or anchor.groupShape and anchor.groupShape.pic + if not child: + anchor.pic = self._picture_frame(idx) + else: + child.blipFill.blip.embed = "rId{0}".format(idx) + + anchors.append(anchor) + self._rels.append(rel) + + for a in anchors: + if isinstance(a, OneCellAnchor): + self.oneCellAnchor.append(a) + elif isinstance(a, TwoCellAnchor): + self.twoCellAnchor.append(a) + else: + self.absoluteAnchor.append(a) + + tree = self.to_tree() + tree.set('xmlns', SHEET_DRAWING_NS) + return tree + + + def _chart_frame(self, idx): + chart_rel = ChartRelation(f"rId{idx}") + frame = GraphicFrame() + nv = frame.nvGraphicFramePr.cNvPr + nv.id = idx + nv.name = "Chart {0}".format(idx) + frame.graphic.graphicData.chart = chart_rel + return frame + + + def _picture_frame(self, idx): + pic = PictureFrame() + pic.nvPicPr.cNvPr.descr = "Picture" + pic.nvPicPr.cNvPr.id = idx + pic.nvPicPr.cNvPr.name = "Image {0}".format(idx) + + pic.blipFill.blip = Blip() + pic.blipFill.blip.embed = "rId{0}".format(idx) + pic.blipFill.blip.cstate = "print" + + pic.spPr.prstGeom = PresetGeometry2D(prst="rect") + pic.spPr.ln = None + return pic + + + def _write_rels(self): + rels = RelationshipList() + rels.Relationship = self._rels + return rels.to_tree() + + + @property + def path(self): + return self._path.format(self._id) + + + @property + def _chart_rels(self): + """ + Get relationship information for each chart and bind anchor to it + """ + rels = [] + anchors = self.absoluteAnchor + self.oneCellAnchor + self.twoCellAnchor + for anchor in anchors: + if anchor.graphicFrame is not None: + graphic = anchor.graphicFrame.graphic + rel = graphic.graphicData.chart + if rel is not None: + rel.anchor = anchor + rel.anchor.graphicFrame = None + rels.append(rel) + return rels + + + @property + def _blip_rels(self): + """ + Get relationship information for each blip and bind anchor to it + + Images that are not part of the XLSX package will be ignored. + """ + rels = [] + anchors = self.absoluteAnchor + self.oneCellAnchor + self.twoCellAnchor + + for anchor in anchors: + child = anchor.pic or anchor.groupShape and anchor.groupShape.pic + if child and child.blipFill: + rel = child.blipFill.blip + if rel is not None and rel.embed: + rel.anchor = anchor + rels.append(rel) + + return rels diff --git a/.venv/Lib/site-packages/openpyxl/drawing/text.py b/.venv/Lib/site-packages/openpyxl/drawing/text.py new file mode 100644 index 00000000..3a4c6080 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/drawing/text.py @@ -0,0 +1,712 @@ +# Copyright (c) 2010-2022 openpyxl + + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Alias, + Typed, + Set, + NoneSet, + Sequence, + String, + Bool, + MinMax, + Integer +) +from openpyxl.descriptors.excel import ( + HexBinary, + TextPoint, + Coordinate, + ExtensionList, + Relation, +) +from openpyxl.descriptors.nested import ( + NestedInteger, + NestedString, + NestedText, + NestedValue, + EmptyTag +) +from openpyxl.xml.constants import DRAWING_NS + + +from .colors import ColorChoiceDescriptor +from .effect import * +from .fill import * +from .geometry import ( + LineProperties, + Color, + Scene3D +) + +from openpyxl.descriptors.excel import ExtensionList as OfficeArtExtensionList +from openpyxl.descriptors.nested import NestedBool + + +class EmbeddedWAVAudioFile(Serialisable): + + name = String(allow_none=True) + + def __init__(self, + name=None, + ): + self.name = name + + +class Hyperlink(Serialisable): + + tagname = "hlinkClick" + namespace = DRAWING_NS + + invalidUrl = String(allow_none=True) + action = String(allow_none=True) + tgtFrame = String(allow_none=True) + tooltip = String(allow_none=True) + history = Bool(allow_none=True) + highlightClick = Bool(allow_none=True) + endSnd = Bool(allow_none=True) + snd = Typed(expected_type=EmbeddedWAVAudioFile, allow_none=True) + extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True) + id = Relation(allow_none=True) + + __elements__ = ('snd',) + + def __init__(self, + invalidUrl=None, + action=None, + tgtFrame=None, + tooltip=None, + history=None, + highlightClick=None, + endSnd=None, + snd=None, + extLst=None, + id=None, + ): + self.invalidUrl = invalidUrl + self.action = action + self.tgtFrame = tgtFrame + self.tooltip = tooltip + self.history = history + self.highlightClick = highlightClick + self.endSnd = endSnd + self.snd = snd + self.id = id + + +class Font(Serialisable): + + tagname = "latin" + namespace = DRAWING_NS + + typeface = String() + panose = HexBinary(allow_none=True) + pitchFamily = MinMax(min=0, max=52, allow_none=True) + charset = Integer(allow_none=True) + + def __init__(self, + typeface=None, + panose=None, + pitchFamily=None, + charset=None, + ): + self.typeface = typeface + self.panose = panose + self.pitchFamily = pitchFamily + self.charset = charset + + +class CharacterProperties(Serialisable): + + tagname = "defRPr" + namespace = DRAWING_NS + + kumimoji = Bool(allow_none=True) + lang = String(allow_none=True) + altLang = String(allow_none=True) + sz = MinMax(allow_none=True, min=100, max=400000) # 100ths of a point + b = Bool(allow_none=True) + i = Bool(allow_none=True) + u = NoneSet(values=(['words', 'sng', 'dbl', 'heavy', 'dotted', + 'dottedHeavy', 'dash', 'dashHeavy', 'dashLong', 'dashLongHeavy', + 'dotDash', 'dotDashHeavy', 'dotDotDash', 'dotDotDashHeavy', 'wavy', + 'wavyHeavy', 'wavyDbl'])) + strike = NoneSet(values=(['noStrike', 'sngStrike', 'dblStrike'])) + kern = Integer(allow_none=True) + cap = NoneSet(values=(['small', 'all'])) + spc = Integer(allow_none=True) + normalizeH = Bool(allow_none=True) + baseline = Integer(allow_none=True) + noProof = Bool(allow_none=True) + dirty = Bool(allow_none=True) + err = Bool(allow_none=True) + smtClean = Bool(allow_none=True) + smtId = Integer(allow_none=True) + bmk = String(allow_none=True) + ln = Typed(expected_type=LineProperties, allow_none=True) + highlight = Typed(expected_type=Color, allow_none=True) + latin = Typed(expected_type=Font, allow_none=True) + ea = Typed(expected_type=Font, allow_none=True) + cs = Typed(expected_type=Font, allow_none=True) + sym = Typed(expected_type=Font, allow_none=True) + hlinkClick = Typed(expected_type=Hyperlink, allow_none=True) + hlinkMouseOver = Typed(expected_type=Hyperlink, allow_none=True) + rtl = NestedBool(allow_none=True) + extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True) + # uses element group EG_FillProperties + noFill = EmptyTag(namespace=DRAWING_NS) + solidFill = ColorChoiceDescriptor() + gradFill = Typed(expected_type=GradientFillProperties, allow_none=True) + blipFill = Typed(expected_type=BlipFillProperties, allow_none=True) + pattFill = Typed(expected_type=PatternFillProperties, allow_none=True) + grpFill = EmptyTag(namespace=DRAWING_NS) + # uses element group EG_EffectProperties + effectLst = Typed(expected_type=EffectList, allow_none=True) + effectDag = Typed(expected_type=EffectContainer, allow_none=True) + # uses element group EG_TextUnderlineLine + uLnTx = EmptyTag() + uLn = Typed(expected_type=LineProperties, allow_none=True) + # uses element group EG_TextUnderlineFill + uFillTx = EmptyTag() + uFill = EmptyTag() + + __elements__ = ('ln', 'noFill', 'solidFill', 'gradFill', 'blipFill', + 'pattFill', 'grpFill', 'effectLst', 'effectDag', 'highlight','uLnTx', + 'uLn', 'uFillTx', 'uFill', 'latin', 'ea', 'cs', 'sym', 'hlinkClick', + 'hlinkMouseOver', 'rtl', ) + + def __init__(self, + kumimoji=None, + lang=None, + altLang=None, + sz=None, + b=None, + i=None, + u=None, + strike=None, + kern=None, + cap=None, + spc=None, + normalizeH=None, + baseline=None, + noProof=None, + dirty=None, + err=None, + smtClean=None, + smtId=None, + bmk=None, + ln=None, + highlight=None, + latin=None, + ea=None, + cs=None, + sym=None, + hlinkClick=None, + hlinkMouseOver=None, + rtl=None, + extLst=None, + noFill=None, + solidFill=None, + gradFill=None, + blipFill=None, + pattFill=None, + grpFill=None, + effectLst=None, + effectDag=None, + uLnTx=None, + uLn=None, + uFillTx=None, + uFill=None, + ): + self.kumimoji = kumimoji + self.lang = lang + self.altLang = altLang + self.sz = sz + self.b = b + self.i = i + self.u = u + self.strike = strike + self.kern = kern + self.cap = cap + self.spc = spc + self.normalizeH = normalizeH + self.baseline = baseline + self.noProof = noProof + self.dirty = dirty + self.err = err + self.smtClean = smtClean + self.smtId = smtId + self.bmk = bmk + self.ln = ln + self.highlight = highlight + self.latin = latin + self.ea = ea + self.cs = cs + self.sym = sym + self.hlinkClick = hlinkClick + self.hlinkMouseOver = hlinkMouseOver + self.rtl = rtl + self.noFill = noFill + self.solidFill = solidFill + self.gradFill = gradFill + self.blipFill = blipFill + self.pattFill = pattFill + self.grpFill = grpFill + self.effectLst = effectLst + self.effectDag = effectDag + self.uLnTx = uLnTx + self.uLn = uLn + self.uFillTx = uFillTx + self.uFill = uFill + + +class TabStop(Serialisable): + + pos = Typed(expected_type=Coordinate, allow_none=True) + algn = Typed(expected_type=Set(values=(['l', 'ctr', 'r', 'dec']))) + + def __init__(self, + pos=None, + algn=None, + ): + self.pos = pos + self.algn = algn + + +class TabStopList(Serialisable): + + tab = Typed(expected_type=TabStop, allow_none=True) + + def __init__(self, + tab=None, + ): + self.tab = tab + + +class Spacing(Serialisable): + + spcPct = NestedInteger(allow_none=True) + spcPts = NestedInteger(allow_none=True) + + __elements__ = ('spcPct', 'spcPts') + + def __init__(self, + spcPct=None, + spcPts=None, + ): + self.spcPct = spcPct + self.spcPts = spcPts + + +class AutonumberBullet(Serialisable): + + type = Set(values=(['alphaLcParenBoth', 'alphaUcParenBoth', + 'alphaLcParenR', 'alphaUcParenR', 'alphaLcPeriod', 'alphaUcPeriod', + 'arabicParenBoth', 'arabicParenR', 'arabicPeriod', 'arabicPlain', + 'romanLcParenBoth', 'romanUcParenBoth', 'romanLcParenR', 'romanUcParenR', + 'romanLcPeriod', 'romanUcPeriod', 'circleNumDbPlain', + 'circleNumWdBlackPlain', 'circleNumWdWhitePlain', 'arabicDbPeriod', + 'arabicDbPlain', 'ea1ChsPeriod', 'ea1ChsPlain', 'ea1ChtPeriod', + 'ea1ChtPlain', 'ea1JpnChsDbPeriod', 'ea1JpnKorPlain', 'ea1JpnKorPeriod', + 'arabic1Minus', 'arabic2Minus', 'hebrew2Minus', 'thaiAlphaPeriod', + 'thaiAlphaParenR', 'thaiAlphaParenBoth', 'thaiNumPeriod', + 'thaiNumParenR', 'thaiNumParenBoth', 'hindiAlphaPeriod', + 'hindiNumPeriod', 'hindiNumParenR', 'hindiAlpha1Period'])) + startAt = Integer() + + def __init__(self, + type=None, + startAt=None, + ): + self.type = type + self.startAt = startAt + + +class ParagraphProperties(Serialisable): + + tagname = "pPr" + namespace = DRAWING_NS + + marL = Integer(allow_none=True) + marR = Integer(allow_none=True) + lvl = Integer(allow_none=True) + indent = Integer(allow_none=True) + algn = NoneSet(values=(['l', 'ctr', 'r', 'just', 'justLow', 'dist', 'thaiDist'])) + defTabSz = Integer(allow_none=True) + rtl = Bool(allow_none=True) + eaLnBrk = Bool(allow_none=True) + fontAlgn = NoneSet(values=(['auto', 't', 'ctr', 'base', 'b'])) + latinLnBrk = Bool(allow_none=True) + hangingPunct = Bool(allow_none=True) + + # uses element group EG_TextBulletColor + # uses element group EG_TextBulletSize + # uses element group EG_TextBulletTypeface + # uses element group EG_TextBullet + lnSpc = Typed(expected_type=Spacing, allow_none=True) + spcBef = Typed(expected_type=Spacing, allow_none=True) + spcAft = Typed(expected_type=Spacing, allow_none=True) + tabLst = Typed(expected_type=TabStopList, allow_none=True) + defRPr = Typed(expected_type=CharacterProperties, allow_none=True) + extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True) + buClrTx = EmptyTag() + buClr = Typed(expected_type=Color, allow_none=True) + buSzTx = EmptyTag() + buSzPct = NestedInteger(allow_none=True) + buSzPts = NestedInteger(allow_none=True) + buFontTx = EmptyTag() + buFont = Typed(expected_type=Font, allow_none=True) + buNone = EmptyTag() + buAutoNum = EmptyTag() + buChar = NestedValue(expected_type=str, attribute="char", allow_none=True) + buBlip = NestedValue(expected_type=Blip, attribute="blip", allow_none=True) + + __elements__ = ('lnSpc', 'spcBef', 'spcAft', 'tabLst', 'defRPr', + 'buClrTx', 'buClr', 'buSzTx', 'buSzPct', 'buSzPts', 'buFontTx', 'buFont', + 'buNone', 'buAutoNum', 'buChar', 'buBlip') + + def __init__(self, + marL=None, + marR=None, + lvl=None, + indent=None, + algn=None, + defTabSz=None, + rtl=None, + eaLnBrk=None, + fontAlgn=None, + latinLnBrk=None, + hangingPunct=None, + lnSpc=None, + spcBef=None, + spcAft=None, + tabLst=None, + defRPr=None, + extLst=None, + buClrTx=None, + buClr=None, + buSzTx=None, + buSzPct=None, + buSzPts=None, + buFontTx=None, + buFont=None, + buNone=None, + buAutoNum=None, + buChar=None, + buBlip=None, + ): + self.marL = marL + self.marR = marR + self.lvl = lvl + self.indent = indent + self.algn = algn + self.defTabSz = defTabSz + self.rtl = rtl + self.eaLnBrk = eaLnBrk + self.fontAlgn = fontAlgn + self.latinLnBrk = latinLnBrk + self.hangingPunct = hangingPunct + self.lnSpc = lnSpc + self.spcBef = spcBef + self.spcAft = spcAft + self.tabLst = tabLst + self.defRPr = defRPr + self.buClrTx = buClrTx + self.buClr = buClr + self.buSzTx = buSzTx + self.buSzPct = buSzPct + self.buSzPts = buSzPts + self.buFontTx = buFontTx + self.buFont = buFont + self.buNone = buNone + self.buAutoNum = buAutoNum + self.buChar = buChar + self.buBlip = buBlip + self.defRPr = defRPr + + +class ListStyle(Serialisable): + + tagname = "lstStyle" + namespace = DRAWING_NS + + defPPr = Typed(expected_type=ParagraphProperties, allow_none=True) + lvl1pPr = Typed(expected_type=ParagraphProperties, allow_none=True) + lvl2pPr = Typed(expected_type=ParagraphProperties, allow_none=True) + lvl3pPr = Typed(expected_type=ParagraphProperties, allow_none=True) + lvl4pPr = Typed(expected_type=ParagraphProperties, allow_none=True) + lvl5pPr = Typed(expected_type=ParagraphProperties, allow_none=True) + lvl6pPr = Typed(expected_type=ParagraphProperties, allow_none=True) + lvl7pPr = Typed(expected_type=ParagraphProperties, allow_none=True) + lvl8pPr = Typed(expected_type=ParagraphProperties, allow_none=True) + lvl9pPr = Typed(expected_type=ParagraphProperties, allow_none=True) + extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True) + + __elements__ = ("defPPr", "lvl1pPr", "lvl2pPr", "lvl3pPr", "lvl4pPr", + "lvl5pPr", "lvl6pPr", "lvl7pPr", "lvl8pPr", "lvl9pPr") + + def __init__(self, + defPPr=None, + lvl1pPr=None, + lvl2pPr=None, + lvl3pPr=None, + lvl4pPr=None, + lvl5pPr=None, + lvl6pPr=None, + lvl7pPr=None, + lvl8pPr=None, + lvl9pPr=None, + extLst=None, + ): + self.defPPr = defPPr + self.lvl1pPr = lvl1pPr + self.lvl2pPr = lvl2pPr + self.lvl3pPr = lvl3pPr + self.lvl4pPr = lvl4pPr + self.lvl5pPr = lvl5pPr + self.lvl6pPr = lvl6pPr + self.lvl7pPr = lvl7pPr + self.lvl8pPr = lvl8pPr + self.lvl9pPr = lvl9pPr + + +class RegularTextRun(Serialisable): + + tagname = "r" + namespace = DRAWING_NS + + rPr = Typed(expected_type=CharacterProperties, allow_none=True) + properties = Alias("rPr") + t = NestedText(expected_type=str) + value = Alias("t") + + __elements__ = ('rPr', 't') + + def __init__(self, + rPr=None, + t="", + ): + self.rPr = rPr + self.t = t + + +class LineBreak(Serialisable): + + tagname = "br" + namespace = DRAWING_NS + + rPr = Typed(expected_type=CharacterProperties, allow_none=True) + + __elements__ = ('rPr',) + + def __init__(self, + rPr=None, + ): + self.rPr = rPr + + +class TextField(Serialisable): + + id = String() + type = String(allow_none=True) + rPr = Typed(expected_type=CharacterProperties, allow_none=True) + pPr = Typed(expected_type=ParagraphProperties, allow_none=True) + t = String(allow_none=True) + + __elements__ = ('rPr', 'pPr') + + def __init__(self, + id=None, + type=None, + rPr=None, + pPr=None, + t=None, + ): + self.id = id + self.type = type + self.rPr = rPr + self.pPr = pPr + self.t = t + + +class Paragraph(Serialisable): + + tagname = "p" + namespace = DRAWING_NS + + # uses element group EG_TextRun + pPr = Typed(expected_type=ParagraphProperties, allow_none=True) + properties = Alias("pPr") + endParaRPr = Typed(expected_type=CharacterProperties, allow_none=True) + r = Sequence(expected_type=RegularTextRun) + text = Alias('r') + br = Typed(expected_type=LineBreak, allow_none=True) + fld = Typed(expected_type=TextField, allow_none=True) + + __elements__ = ('pPr', 'r', 'br', 'fld', 'endParaRPr') + + def __init__(self, + pPr=None, + endParaRPr=None, + r=None, + br=None, + fld=None, + ): + self.pPr = pPr + self.endParaRPr = endParaRPr + if r is None: + r = [RegularTextRun()] + self.r = r + self.br = br + self.fld = fld + + +class GeomGuide(Serialisable): + + name = String(()) + fmla = String(()) + + def __init__(self, + name=None, + fmla=None, + ): + self.name = name + self.fmla = fmla + + +class GeomGuideList(Serialisable): + + gd = Sequence(expected_type=GeomGuide, allow_none=True) + + def __init__(self, + gd=None, + ): + self.gd = gd + + +class PresetTextShape(Serialisable): + + prst = Typed(expected_type=Set(values=( + ['textNoShape', 'textPlain','textStop', 'textTriangle', 'textTriangleInverted', 'textChevron', + 'textChevronInverted', 'textRingInside', 'textRingOutside', 'textArchUp', + 'textArchDown', 'textCircle', 'textButton', 'textArchUpPour', + 'textArchDownPour', 'textCirclePour', 'textButtonPour', 'textCurveUp', + 'textCurveDown', 'textCanUp', 'textCanDown', 'textWave1', 'textWave2', + 'textDoubleWave1', 'textWave4', 'textInflate', 'textDeflate', + 'textInflateBottom', 'textDeflateBottom', 'textInflateTop', + 'textDeflateTop', 'textDeflateInflate', 'textDeflateInflateDeflate', + 'textFadeRight', 'textFadeLeft', 'textFadeUp', 'textFadeDown', + 'textSlantUp', 'textSlantDown', 'textCascadeUp', 'textCascadeDown' + ] + ))) + avLst = Typed(expected_type=GeomGuideList, allow_none=True) + + def __init__(self, + prst=None, + avLst=None, + ): + self.prst = prst + self.avLst = avLst + + +class TextNormalAutofit(Serialisable): + + fontScale = Integer() + lnSpcReduction = Integer() + + def __init__(self, + fontScale=None, + lnSpcReduction=None, + ): + self.fontScale = fontScale + self.lnSpcReduction = lnSpcReduction + + +class RichTextProperties(Serialisable): + + tagname = "bodyPr" + namespace = DRAWING_NS + + rot = Integer(allow_none=True) + spcFirstLastPara = Bool(allow_none=True) + vertOverflow = NoneSet(values=(['overflow', 'ellipsis', 'clip'])) + horzOverflow = NoneSet(values=(['overflow', 'clip'])) + vert = NoneSet(values=(['horz', 'vert', 'vert270', 'wordArtVert', + 'eaVert', 'mongolianVert', 'wordArtVertRtl'])) + wrap = NoneSet(values=(['none', 'square'])) + lIns = Integer(allow_none=True) + tIns = Integer(allow_none=True) + rIns = Integer(allow_none=True) + bIns = Integer(allow_none=True) + numCol = Integer(allow_none=True) + spcCol = Integer(allow_none=True) + rtlCol = Bool(allow_none=True) + fromWordArt = Bool(allow_none=True) + anchor = NoneSet(values=(['t', 'ctr', 'b', 'just', 'dist'])) + anchorCtr = Bool(allow_none=True) + forceAA = Bool(allow_none=True) + upright = Bool(allow_none=True) + compatLnSpc = Bool(allow_none=True) + prstTxWarp = Typed(expected_type=PresetTextShape, allow_none=True) + scene3d = Typed(expected_type=Scene3D, allow_none=True) + extLst = Typed(expected_type=OfficeArtExtensionList, allow_none=True) + noAutofit = EmptyTag() + normAutofit = EmptyTag() + spAutoFit = EmptyTag() + flatTx = NestedInteger(attribute="z", allow_none=True) + + __elements__ = ('prstTxWarp', 'scene3d', 'noAutofit', 'normAutofit', 'spAutoFit') + + def __init__(self, + rot=None, + spcFirstLastPara=None, + vertOverflow=None, + horzOverflow=None, + vert=None, + wrap=None, + lIns=None, + tIns=None, + rIns=None, + bIns=None, + numCol=None, + spcCol=None, + rtlCol=None, + fromWordArt=None, + anchor=None, + anchorCtr=None, + forceAA=None, + upright=None, + compatLnSpc=None, + prstTxWarp=None, + scene3d=None, + extLst=None, + noAutofit=None, + normAutofit=None, + spAutoFit=None, + flatTx=None, + ): + self.rot = rot + self.spcFirstLastPara = spcFirstLastPara + self.vertOverflow = vertOverflow + self.horzOverflow = horzOverflow + self.vert = vert + self.wrap = wrap + self.lIns = lIns + self.tIns = tIns + self.rIns = rIns + self.bIns = bIns + self.numCol = numCol + self.spcCol = spcCol + self.rtlCol = rtlCol + self.fromWordArt = fromWordArt + self.anchor = anchor + self.anchorCtr = anchorCtr + self.forceAA = forceAA + self.upright = upright + self.compatLnSpc = compatLnSpc + self.prstTxWarp = prstTxWarp + self.scene3d = scene3d + self.noAutofit = noAutofit + self.normAutofit = normAutofit + self.spAutoFit = spAutoFit + self.flatTx = flatTx diff --git a/.venv/Lib/site-packages/openpyxl/drawing/xdr.py b/.venv/Lib/site-packages/openpyxl/drawing/xdr.py new file mode 100644 index 00000000..e9a1534e --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/drawing/xdr.py @@ -0,0 +1,33 @@ +# Copyright (c) 2010-2022 openpyxl + +""" +Spreadsheet Drawing has some copies of Drawing ML elements +""" + +from .geometry import Point2D, PositiveSize2D, Transform2D + + +class XDRPoint2D(Point2D): + + namespace = None + x = Point2D.x + y = Point2D.y + + +class XDRPositiveSize2D(PositiveSize2D): + + namespace = None + cx = PositiveSize2D.cx + cy = PositiveSize2D.cy + + +class XDRTransform2D(Transform2D): + + namespace = None + rot = Transform2D.rot + flipH = Transform2D.flipH + flipV = Transform2D.flipV + off = Transform2D.off + ext = Transform2D.ext + chOff = Transform2D.chOff + chExt = Transform2D.chExt diff --git a/.venv/Lib/site-packages/openpyxl/formatting/__init__.py b/.venv/Lib/site-packages/openpyxl/formatting/__init__.py new file mode 100644 index 00000000..21dc0be7 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/formatting/__init__.py @@ -0,0 +1,3 @@ +# Copyright (c) 2010-2022 openpyxl + +from .rule import Rule diff --git a/.venv/Lib/site-packages/openpyxl/formatting/formatting.py b/.venv/Lib/site-packages/openpyxl/formatting/formatting.py new file mode 100644 index 00000000..9514a71f --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/formatting/formatting.py @@ -0,0 +1,118 @@ +# Copyright (c) 2010-2022 openpyxl + +from collections import OrderedDict + +from openpyxl.descriptors import ( + Bool, + String, + Sequence, + Alias, + Convertible, +) +from openpyxl.descriptors.excel import ExtensionList +from openpyxl.descriptors.serialisable import Serialisable + +from .rule import Rule + +from openpyxl.worksheet.cell_range import MultiCellRange + +class ConditionalFormatting(Serialisable): + + tagname = "conditionalFormatting" + + sqref = Convertible(expected_type=MultiCellRange) + cells = Alias("sqref") + pivot = Bool(allow_none=True) + cfRule = Sequence(expected_type=Rule) + rules = Alias("cfRule") + + + def __init__(self, sqref=(), pivot=None, cfRule=(), extLst=None): + self.sqref = sqref + self.pivot = pivot + self.cfRule = cfRule + + + def __eq__(self, other): + if not isinstance(other, self.__class__): + return False + return self.sqref == other.sqref + + + def __hash__(self): + return hash(str(self.sqref)) + + + def __repr__(self): + return "<{cls} {cells}>".format(cls=self.__class__.__name__, cells=self.sqref) + + + def __contains__(self, coord): + """ + Check whether a certain cell is affected by the formatting + """ + return coord in self.sqref + + +class ConditionalFormattingList(object): + """Conditional formatting rules.""" + + + def __init__(self): + self._cf_rules = OrderedDict() + self.max_priority = 0 + + + def add(self, range_string, cfRule): + """Add a rule such as ColorScaleRule, FormulaRule or CellIsRule + + The priority will be added automatically. + """ + cf = range_string + if isinstance(range_string, str): + cf = ConditionalFormatting(range_string) + if not isinstance(cfRule, Rule): + raise ValueError("Only instances of openpyxl.formatting.rule.Rule may be added") + rule = cfRule + self.max_priority += 1 + if not rule.priority: + rule.priority = self.max_priority + + self._cf_rules.setdefault(cf, []).append(rule) + + + def __bool__(self): + return bool(self._cf_rules) + + __nonzero = __bool__ + + + def __len__(self): + return len(self._cf_rules) + + + def __iter__(self): + for cf, rules in self._cf_rules.items(): + cf.rules = rules + yield cf + + + def __getitem__(self, key): + """ + Get the rules for a cell range + """ + if isinstance(key, str): + key = ConditionalFormatting(sqref=key) + return self._cf_rules[key] + + + def __delitem__(self, key): + key = ConditionalFormatting(sqref=key) + del self._cf_rules[key] + + + def __setitem__(self, key, rule): + """ + Add a rule for a cell range + """ + self.add(key, rule) diff --git a/.venv/Lib/site-packages/openpyxl/formatting/rule.py b/.venv/Lib/site-packages/openpyxl/formatting/rule.py new file mode 100644 index 00000000..2ed47fed --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/formatting/rule.py @@ -0,0 +1,291 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + String, + Sequence, + Bool, + NoneSet, + Set, + Integer, + Float, +) +from openpyxl.descriptors.excel import ExtensionList +from openpyxl.styles.colors import Color, ColorDescriptor +from openpyxl.styles.differential import DifferentialStyle + +from openpyxl.utils.cell import COORD_RE + + +class ValueDescriptor(Float): + """ + Expected type depends upon type attribue of parent :-( + + Most values should be numeric BUT they can also be cell references + """ + + def __set__(self, instance, value): + ref = None + if value is not None and isinstance(value, str): + ref = COORD_RE.match(value) + if instance.type == "formula" or ref: + self.expected_type = str + else: + self.expected_type = float + super(ValueDescriptor, self).__set__(instance, value) + + +class FormatObject(Serialisable): + + tagname = "cfvo" + + type = Set(values=(['num', 'percent', 'max', 'min', 'formula', 'percentile'])) + val = ValueDescriptor(allow_none=True) + gte = Bool(allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = () + + def __init__(self, + type, + val=None, + gte=None, + extLst=None, + ): + self.type = type + self.val = val + self.gte = gte + + +class RuleType(Serialisable): + + cfvo = Sequence(expected_type=FormatObject) + + +class IconSet(RuleType): + + tagname = "iconSet" + + iconSet = NoneSet(values=(['3Arrows', '3ArrowsGray', '3Flags', + '3TrafficLights1', '3TrafficLights2', '3Signs', '3Symbols', '3Symbols2', + '4Arrows', '4ArrowsGray', '4RedToBlack', '4Rating', '4TrafficLights', + '5Arrows', '5ArrowsGray', '5Rating', '5Quarters'])) + showValue = Bool(allow_none=True) + percent = Bool(allow_none=True) + reverse = Bool(allow_none=True) + + __elements__ = ("cfvo",) + + def __init__(self, + iconSet=None, + showValue=None, + percent=None, + reverse=None, + cfvo=None, + ): + self.iconSet = iconSet + self.showValue = showValue + self.percent = percent + self.reverse = reverse + self.cfvo = cfvo + + +class DataBar(RuleType): + + tagname = "dataBar" + + minLength = Integer(allow_none=True) + maxLength = Integer(allow_none=True) + showValue = Bool(allow_none=True) + color = ColorDescriptor() + + __elements__ = ('cfvo', 'color') + + def __init__(self, + minLength=None, + maxLength=None, + showValue=None, + cfvo=None, + color=None, + ): + self.minLength = minLength + self.maxLength = maxLength + self.showValue = showValue + self.cfvo = cfvo + self.color = color + + +class ColorScale(RuleType): + + tagname = "colorScale" + + color = Sequence(expected_type=Color) + + __elements__ = ('cfvo', 'color') + + def __init__(self, + cfvo=None, + color=None, + ): + self.cfvo = cfvo + self.color = color + + +class Rule(Serialisable): + + tagname = "cfRule" + + type = Set(values=(['expression', 'cellIs', 'colorScale', 'dataBar', + 'iconSet', 'top10', 'uniqueValues', 'duplicateValues', 'containsText', + 'notContainsText', 'beginsWith', 'endsWith', 'containsBlanks', + 'notContainsBlanks', 'containsErrors', 'notContainsErrors', 'timePeriod', + 'aboveAverage'])) + dxfId = Integer(allow_none=True) + priority = Integer() + stopIfTrue = Bool(allow_none=True) + aboveAverage = Bool(allow_none=True) + percent = Bool(allow_none=True) + bottom = Bool(allow_none=True) + operator = NoneSet(values=(['lessThan', 'lessThanOrEqual', 'equal', + 'notEqual', 'greaterThanOrEqual', 'greaterThan', 'between', 'notBetween', + 'containsText', 'notContains', 'beginsWith', 'endsWith'])) + text = String(allow_none=True) + timePeriod = NoneSet(values=(['today', 'yesterday', 'tomorrow', 'last7Days', + 'thisMonth', 'lastMonth', 'nextMonth', 'thisWeek', 'lastWeek', + 'nextWeek'])) + rank = Integer(allow_none=True) + stdDev = Integer(allow_none=True) + equalAverage = Bool(allow_none=True) + formula = Sequence(expected_type=str) + colorScale = Typed(expected_type=ColorScale, allow_none=True) + dataBar = Typed(expected_type=DataBar, allow_none=True) + iconSet = Typed(expected_type=IconSet, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + dxf = Typed(expected_type=DifferentialStyle, allow_none=True) + + __elements__ = ('colorScale', 'dataBar', 'iconSet', 'formula') + __attrs__ = ('type', 'rank', 'priority', 'equalAverage', 'operator', + 'aboveAverage', 'dxfId', 'stdDev', 'stopIfTrue', 'timePeriod', 'text', + 'percent', 'bottom') + + + def __init__(self, + type, + dxfId=None, + priority=0, + stopIfTrue=None, + aboveAverage=None, + percent=None, + bottom=None, + operator=None, + text=None, + timePeriod=None, + rank=None, + stdDev=None, + equalAverage=None, + formula=(), + colorScale=None, + dataBar=None, + iconSet=None, + extLst=None, + dxf=None, + ): + self.type = type + self.dxfId = dxfId + self.priority = priority + self.stopIfTrue = stopIfTrue + self.aboveAverage = aboveAverage + self.percent = percent + self.bottom = bottom + self.operator = operator + self.text = text + self.timePeriod = timePeriod + self.rank = rank + self.stdDev = stdDev + self.equalAverage = equalAverage + self.formula = formula + self.colorScale = colorScale + self.dataBar = dataBar + self.iconSet = iconSet + self.dxf = dxf + + +def ColorScaleRule(start_type=None, + start_value=None, + start_color=None, + mid_type=None, + mid_value=None, + mid_color=None, + end_type=None, + end_value=None, + end_color=None): + + """Backwards compatibility""" + formats = [] + if start_type is not None: + formats.append(FormatObject(type=start_type, val=start_value)) + if mid_type is not None: + formats.append(FormatObject(type=mid_type, val=mid_value)) + if end_type is not None: + formats.append(FormatObject(type=end_type, val=end_value)) + colors = [] + for v in (start_color, mid_color, end_color): + if v is not None: + if not isinstance(v, Color): + v = Color(v) + colors.append(v) + cs = ColorScale(cfvo=formats, color=colors) + rule = Rule(type="colorScale", colorScale=cs) + return rule + + +def FormulaRule(formula=None, stopIfTrue=None, font=None, border=None, + fill=None): + """ + Conditional formatting with custom differential style + """ + rule = Rule(type="expression", formula=formula, stopIfTrue=stopIfTrue) + rule.dxf = DifferentialStyle(font=font, border=border, fill=fill) + return rule + + +def CellIsRule(operator=None, formula=None, stopIfTrue=None, font=None, border=None, fill=None): + """ + Conditional formatting rule based on cell contents. + """ + # Excel doesn't use >, >=, etc, but allow for ease of python development + expand = {">": "greaterThan", ">=": "greaterThanOrEqual", "<": "lessThan", "<=": "lessThanOrEqual", + "=": "equal", "==": "equal", "!=": "notEqual"} + + operator = expand.get(operator, operator) + + rule = Rule(type='cellIs', operator=operator, formula=formula, stopIfTrue=stopIfTrue) + rule.dxf = DifferentialStyle(font=font, border=border, fill=fill) + + return rule + + +def IconSetRule(icon_style=None, type=None, values=None, showValue=None, percent=None, reverse=None): + """ + Convenience function for creating icon set rules + """ + cfvo = [] + for val in values: + cfvo.append(FormatObject(type, val)) + icon_set = IconSet(iconSet=icon_style, cfvo=cfvo, showValue=showValue, + percent=percent, reverse=reverse) + rule = Rule(type='iconSet', iconSet=icon_set) + + return rule + + +def DataBarRule(start_type=None, start_value=None, end_type=None, + end_value=None, color=None, showValue=None, minLength=None, maxLength=None): + start = FormatObject(start_type, start_value) + end = FormatObject(end_type, end_value) + data_bar = DataBar(cfvo=[start, end], color=color, showValue=showValue, + minLength=minLength, maxLength=maxLength) + rule = Rule(type='dataBar', dataBar=data_bar) + + return rule diff --git a/.venv/Lib/site-packages/openpyxl/formula/__init__.py b/.venv/Lib/site-packages/openpyxl/formula/__init__.py new file mode 100644 index 00000000..1f13cbec --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/formula/__init__.py @@ -0,0 +1,3 @@ +# Copyright (c) 2010-2022 openpyxl + +from .tokenizer import Tokenizer diff --git a/.venv/Lib/site-packages/openpyxl/formula/tokenizer.py b/.venv/Lib/site-packages/openpyxl/formula/tokenizer.py new file mode 100644 index 00000000..195cc79a --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/formula/tokenizer.py @@ -0,0 +1,446 @@ +""" +This module contains a tokenizer for Excel formulae. + +The tokenizer is based on the Javascript tokenizer found at +http://ewbi.blogs.com/develops/2004/12/excel_formula_p.html written by Eric +Bachtal +""" + +import re + + +class TokenizerError(Exception): + """Base class for all Tokenizer errors.""" + + +class Tokenizer(object): + + """ + A tokenizer for Excel worksheet formulae. + + Converts a str string representing an Excel formula (in A1 notation) + into a sequence of `Token` objects. + + `formula`: The str string to tokenize + + Tokenizer defines a method `._parse()` to parse the formula into tokens, + which can then be accessed through the `.items` attribute. + + """ + + SN_RE = re.compile("^[1-9](\\.[0-9]+)?[Ee]$") # Scientific notation + WSPACE_RE = re.compile(r"[ \n]+") + STRING_REGEXES = { + # Inside a string, all characters are treated as literals, except for + # the quote character used to start the string. That character, when + # doubled is treated as a single character in the string. If an + # unmatched quote appears, the string is terminated. + '"': re.compile('"(?:[^"]*"")*[^"]*"(?!")'), + "'": re.compile("'(?:[^']*'')*[^']*'(?!')"), + } + ERROR_CODES = ("#NULL!", "#DIV/0!", "#VALUE!", "#REF!", "#NAME?", + "#NUM!", "#N/A", "#GETTING_DATA") + TOKEN_ENDERS = ',;}) +-*/^&=><%' # Each of these characters, marks the + # end of an operand token + + def __init__(self, formula): + self.formula = formula + self.items = [] + self.token_stack = [] # Used to keep track of arrays, functions, and + # parentheses + self.offset = 0 # How many chars have we read + self.token = [] # Used to build up token values char by char + self._parse() + + def _parse(self): + """Populate self.items with the tokens from the formula.""" + if self.offset: + return # Already parsed! + if not self.formula: + return + elif self.formula[0] == '=': + self.offset += 1 + else: + self.items.append(Token(self.formula, Token.LITERAL)) + return + consumers = ( + ('"\'', self._parse_string), + ('[', self._parse_brackets), + ('#', self._parse_error), + (' ', self._parse_whitespace), + ('\n', self._parse_whitespace), + ('+-*/^&=><%', self._parse_operator), + ('{(', self._parse_opener), + (')}', self._parse_closer), + (';,', self._parse_separator), + ) + dispatcher = {} # maps chars to the specific parsing function + for chars, consumer in consumers: + dispatcher.update(dict.fromkeys(chars, consumer)) + while self.offset < len(self.formula): + if self.check_scientific_notation(): # May consume one character + continue + curr_char = self.formula[self.offset] + if curr_char in self.TOKEN_ENDERS: + self.save_token() + if curr_char in dispatcher: + self.offset += dispatcher[curr_char]() + else: + # TODO: this can probably be sped up using a regex to get to + # the next interesting character + self.token.append(curr_char) + self.offset += 1 + self.save_token() + + def _parse_string(self): + """ + Parse a "-delimited string or '-delimited link. + + The offset must be pointing to either a single quote ("'") or double + quote ('"') character. The strings are parsed according to Excel + rules where to escape the delimiter you just double it up. E.g., + "abc""def" in Excel is parsed as 'abc"def' in Python. + + Returns the number of characters matched. (Does not update + self.offset) + + """ + self.assert_empty_token(can_follow=':') + delim = self.formula[self.offset] + assert delim in ('"', "'") + regex = self.STRING_REGEXES[delim] + match = regex.match(self.formula[self.offset:]) + if match is None: + subtype = "string" if delim == '"' else 'link' + raise TokenizerError(f"Reached end of formula while parsing {subtype} in {self.formula}") + match = match.group(0) + if delim == '"': + self.items.append(Token.make_operand(match)) + else: + self.token.append(match) + return len(match) + + def _parse_brackets(self): + """ + Consume all the text between square brackets []. + + Returns the number of characters matched. (Does not update + self.offset) + + """ + assert self.formula[self.offset] == '[' + lefts = [(t.start(), 1) for t in + re.finditer(r"\[", self.formula[self.offset:])] + rights = [(t.start(), -1) for t in + re.finditer(r"\]", self.formula[self.offset:])] + + open_count = 0 + for idx, open_close in sorted(lefts + rights): + open_count += open_close + if open_count == 0: + outer_right = idx + 1 + self.token.append( + self.formula[self.offset:self.offset + outer_right]) + return outer_right + + raise TokenizerError(f"Encountered unmatched '[' in {self.formula}") + + def _parse_error(self): + """ + Consume the text following a '#' as an error. + + Looks for a match in self.ERROR_CODES and returns the number of + characters matched. (Does not update self.offset) + + """ + self.assert_empty_token(can_follow='!') + assert self.formula[self.offset] == '#' + subformula = self.formula[self.offset:] + for err in self.ERROR_CODES: + if subformula.startswith(err): + self.items.append(Token.make_operand(''.join(self.token) + err)) + del self.token[:] + return len(err) + raise TokenizerError(f"Invalid error code at position {self.offset} in '{self.formula}'") + + def _parse_whitespace(self): + """ + Consume a string of consecutive spaces. + + Returns the number of spaces found. (Does not update self.offset). + + """ + assert self.formula[self.offset] in (' ', '\n') + self.items.append(Token(self.formula[self.offset], Token.WSPACE)) + return self.WSPACE_RE.match(self.formula[self.offset:]).end() + + def _parse_operator(self): + """ + Consume the characters constituting an operator. + + Returns the number of characters consumed. (Does not update + self.offset) + + """ + if self.formula[self.offset:self.offset + 2] in ('>=', '<=', '<>'): + self.items.append(Token( + self.formula[self.offset:self.offset + 2], + Token.OP_IN + )) + return 2 + curr_char = self.formula[self.offset] # guaranteed to be 1 char + assert curr_char in '%*/^&=><+-' + if curr_char == '%': + token = Token('%', Token.OP_POST) + elif curr_char in "*/^&=><": + token = Token(curr_char, Token.OP_IN) + # From here on, curr_char is guaranteed to be in '+-' + elif not self.items: + token = Token(curr_char, Token.OP_PRE) + else: + prev = next((i for i in reversed(self.items) + if i.type != Token.WSPACE), None) + is_infix = prev and ( + prev.subtype == Token.CLOSE + or prev.type == Token.OP_POST + or prev.type == Token.OPERAND + ) + if is_infix: + token = Token(curr_char, Token.OP_IN) + else: + token = Token(curr_char, Token.OP_PRE) + self.items.append(token) + return 1 + + def _parse_opener(self): + """ + Consumes a ( or { character. + + Returns the number of characters consumed. (Does not update + self.offset) + + """ + assert self.formula[self.offset] in ('(', '{') + if self.formula[self.offset] == '{': + self.assert_empty_token() + token = Token.make_subexp("{") + elif self.token: + token_value = "".join(self.token) + '(' + del self.token[:] + token = Token.make_subexp(token_value) + else: + token = Token.make_subexp("(") + self.items.append(token) + self.token_stack.append(token) + return 1 + + def _parse_closer(self): + """ + Consumes a } or ) character. + + Returns the number of characters consumed. (Does not update + self.offset) + + """ + assert self.formula[self.offset] in (')', '}') + token = self.token_stack.pop().get_closer() + if token.value != self.formula[self.offset]: + raise TokenizerError( + "Mismatched ( and { pair in '%s'" % self.formula) + self.items.append(token) + return 1 + + def _parse_separator(self): + """ + Consumes a ; or , character. + + Returns the number of characters consumed. (Does not update + self.offset) + + """ + curr_char = self.formula[self.offset] + assert curr_char in (';', ',') + if curr_char == ';': + token = Token.make_separator(";") + else: + try: + top_type = self.token_stack[-1].type + except IndexError: + token = Token(",", Token.OP_IN) # Range Union operator + else: + if top_type == Token.PAREN: + token = Token(",", Token.OP_IN) # Range Union operator + else: + token = Token.make_separator(",") + self.items.append(token) + return 1 + + def check_scientific_notation(self): + """ + Consumes a + or - character if part of a number in sci. notation. + + Returns True if the character was consumed and self.offset was + updated, False otherwise. + + """ + curr_char = self.formula[self.offset] + if (curr_char in '+-' + and len(self.token) >= 1 + and self.SN_RE.match("".join(self.token))): + self.token.append(curr_char) + self.offset += 1 + return True + return False + + def assert_empty_token(self, can_follow=()): + """ + Ensure that there's no token currently being parsed. + + Or if there is a token being parsed, it must end with a character in + can_follow. + + If there are unconsumed token contents, it means we hit an unexpected + token transition. In this case, we raise a TokenizerError + + """ + if self.token and self.token[-1] not in can_follow: + raise TokenizerError(f"Unexpected character at position {self.offset} in '{self.formula}'") + + def save_token(self): + """If there's a token being parsed, add it to the item list.""" + if self.token: + self.items.append(Token.make_operand("".join(self.token))) + del self.token[:] + + def render(self): + """Convert the parsed tokens back to a string.""" + if not self.items: + return "" + elif self.items[0].type == Token.LITERAL: + return self.items[0].value + return "=" + "".join(token.value for token in self.items) + + +class Token(object): + + """ + A token in an Excel formula. + + Tokens have three attributes: + + * `value`: The string value parsed that led to this token + * `type`: A string identifying the type of token + * `subtype`: A string identifying subtype of the token (optional, and + defaults to "") + + """ + + __slots__ = ['value', 'type', 'subtype'] + + LITERAL = "LITERAL" + OPERAND = "OPERAND" + FUNC = "FUNC" + ARRAY = "ARRAY" + PAREN = "PAREN" + SEP = "SEP" + OP_PRE = "OPERATOR-PREFIX" + OP_IN = "OPERATOR-INFIX" + OP_POST = "OPERATOR-POSTFIX" + WSPACE = "WHITE-SPACE" + + def __init__(self, value, type_, subtype=""): + self.value = value + self.type = type_ + self.subtype = subtype + + # Literal operands: + # + # Literal operands are always of type 'OPERAND' and can be of subtype + # 'TEXT' (for text strings), 'NUMBER' (for all numeric types), 'LOGICAL' + # (for TRUE and FALSE), 'ERROR' (for literal error values), or 'RANGE' + # (for all range references). + + TEXT = 'TEXT' + NUMBER = 'NUMBER' + LOGICAL = 'LOGICAL' + ERROR = 'ERROR' + RANGE = 'RANGE' + + def __repr__(self): + return u"{0} {1} {2}:".format(self.type, self.subtype, self.value) + + @classmethod + def make_operand(cls, value): + """Create an operand token.""" + if value.startswith('"'): + subtype = cls.TEXT + elif value.startswith('#'): + subtype = cls.ERROR + elif value in ('TRUE', 'FALSE'): + subtype = cls.LOGICAL + else: + try: + float(value) + subtype = cls.NUMBER + except ValueError: + subtype = cls.RANGE + return cls(value, cls.OPERAND, subtype) + + + # Subexpresssions + # + # There are 3 types of `Subexpressions`: functions, array literals, and + # parentheticals. Subexpressions have 'OPEN' and 'CLOSE' tokens. 'OPEN' + # is used when parsing the initial expression token (i.e., '(' or '{') + # and 'CLOSE' is used when parsing the closing expression token ('}' or + # ')'). + + OPEN = "OPEN" + CLOSE = "CLOSE" + + @classmethod + def make_subexp(cls, value, func=False): + """ + Create a subexpression token. + + `value`: The value of the token + `func`: If True, force the token to be of type FUNC + + """ + assert value[-1] in ('{', '}', '(', ')') + if func: + assert re.match('.+\\(|\\)', value) + type_ = Token.FUNC + elif value in '{}': + type_ = Token.ARRAY + elif value in '()': + type_ = Token.PAREN + else: + type_ = Token.FUNC + subtype = cls.CLOSE if value in ')}' else cls.OPEN + return cls(value, type_, subtype) + + def get_closer(self): + """Return a closing token that matches this token's type.""" + assert self.type in (self.FUNC, self.ARRAY, self.PAREN) + assert self.subtype == self.OPEN + value = "}" if self.type == self.ARRAY else ")" + return self.make_subexp(value, func=self.type == self.FUNC) + + # Separator tokens + # + # Argument separators always have type 'SEP' and can have one of two + # subtypes: 'ARG', 'ROW'. 'ARG' is used for the ',' token, when used to + # delimit either function arguments or array elements. 'ROW' is used for + # the ';' token, which is always used to delimit rows in an array + # literal. + + ARG = "ARG" + ROW = "ROW" + + @classmethod + def make_separator(cls, value): + """Create a separator token""" + assert value in (',', ';') + subtype = cls.ARG if value == ',' else cls.ROW + return cls(value, cls.SEP, subtype) diff --git a/.venv/Lib/site-packages/openpyxl/formula/translate.py b/.venv/Lib/site-packages/openpyxl/formula/translate.py new file mode 100644 index 00000000..447a9711 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/formula/translate.py @@ -0,0 +1,166 @@ +""" +This module contains code to translate formulae across cells in a worksheet. + +The idea is that if A1 has formula "=B1+C1", then translating it to cell A2 +results in formula "=B2+C2". The algorithm relies on the formula tokenizer +to identify the parts of the formula that need to change. + +""" + +import re +from .tokenizer import Tokenizer, Token +from openpyxl.utils import ( + coordinate_to_tuple, + column_index_from_string, + get_column_letter +) + +class TranslatorError(Exception): + """ + Raised when a formula can't be translated across cells. + + This error arises when a formula's references would be translated outside + the worksheet's bounds on the top or left. Excel represents these + situations with a #REF! literal error. E.g., if the formula at B2 is + '=A1', attempting to translate the formula to B1 raises TranslatorError, + since there's no cell above A1. Similarly, translating the same formula + from B2 to A2 raises TranslatorError, since there's no cell to the left of + A1. + + """ + + +class Translator(object): + + """ + Modifies a formula so that it can be translated from one cell to another. + + `formula`: The str string to translate. Must include the leading '=' + character. + `origin`: The cell address (in A1 notation) where this formula was + defined (excluding the worksheet name). + + """ + + def __init__(self, formula, origin): + # Excel errors out when a workbook has formulae in R1C1 notation, + # regardless of the calcPr:refMode setting, so I'm assuming the + # formulae stored in the workbook must be in A1 notation. + self.row, self.col = coordinate_to_tuple(origin) + self.tokenizer = Tokenizer(formula) + + def get_tokens(self): + "Returns a list with the tokens comprising the formula." + return self.tokenizer.items + + ROW_RANGE_RE = re.compile(r"(\$?[1-9][0-9]{0,6}):(\$?[1-9][0-9]{0,6})$") + COL_RANGE_RE = re.compile(r"(\$?[A-Za-z]{1,3}):(\$?[A-Za-z]{1,3})$") + CELL_REF_RE = re.compile(r"(\$?[A-Za-z]{1,3})(\$?[1-9][0-9]{0,6})$") + + @staticmethod + def translate_row(row_str, rdelta): + """ + Translate a range row-snippet by the given number of rows. + """ + if row_str.startswith('$'): + return row_str + else: + new_row = int(row_str) + rdelta + if new_row <= 0: + raise TranslatorError("Formula out of range") + return str(new_row) + + @staticmethod + def translate_col(col_str, cdelta): + """ + Translate a range col-snippet by the given number of columns + """ + if col_str.startswith('$'): + return col_str + else: + try: + return get_column_letter( + column_index_from_string(col_str) + cdelta) + except ValueError: + raise TranslatorError("Formula out of range") + + @staticmethod + def strip_ws_name(range_str): + "Splits out the worksheet reference, if any, from a range reference." + # This code assumes that named ranges cannot contain any exclamation + # marks. Excel refuses to create these (even using VBA), and + # complains of a corrupt workbook when there are names with + # exclamation marks. The ECMA spec only states that named ranges will + # be of `ST_Xstring` type, which in theory allows '!' (char code + # 0x21) per http://www.w3.org/TR/xml/#charsets + if '!' in range_str: + sheet, range_str = range_str.rsplit('!', 1) + return sheet + "!", range_str + return "", range_str + + @classmethod + def translate_range(cls, range_str, rdelta, cdelta): + """ + Translate an A1-style range reference to the destination cell. + + `rdelta`: the row offset to add to the range + `cdelta`: the column offset to add to the range + `range_str`: an A1-style reference to a range. Potentially includes + the worksheet reference. Could also be a named range. + + """ + ws_part, range_str = cls.strip_ws_name(range_str) + match = cls.ROW_RANGE_RE.match(range_str) # e.g. `3:4` + if match is not None: + return (ws_part + cls.translate_row(match.group(1), rdelta) + ":" + + cls.translate_row(match.group(2), rdelta)) + match = cls.COL_RANGE_RE.match(range_str) # e.g. `A:BC` + if match is not None: + return (ws_part + cls.translate_col(match.group(1), cdelta) + ':' + + cls.translate_col(match.group(2), cdelta)) + if ':' in range_str: # e.g. `A1:B5` + # The check is necessarily general because range references can + # have one or both endpoints specified by named ranges. I.e., + # `named_range:C2`, `C2:named_range`, and `name1:name2` are all + # valid references. Further, Excel allows chaining multiple + # colons together (with unclear meaning) + return ws_part + ":".join( + cls.translate_range(piece, rdelta, cdelta) + for piece in range_str.split(':')) + match = cls.CELL_REF_RE.match(range_str) + if match is None: # Must be a named range + return range_str + return (ws_part + cls.translate_col(match.group(1), cdelta) + + cls.translate_row(match.group(2), rdelta)) + + def translate_formula(self, dest=None, row_delta=0, col_delta=0): + """ + Convert the formula into A1 notation, or as row and column coordinates + + The formula is converted into A1 assuming it is assigned to the cell + whose address is `dest` (no worksheet name). + + """ + tokens = self.get_tokens() + if not tokens: + return "" + elif tokens[0].type == Token.LITERAL: + return tokens[0].value + out = ['='] + # per the spec: + # A compliant producer or consumer considers a defined name in the + # range A1-XFD1048576 to be an error. All other names outside this + # range can be defined as names and overrides a cell reference if an + # ambiguity exists. (I.18.2.5) + if dest: + row, col = coordinate_to_tuple(dest) + row_delta = row - self.row + col_delta = col - self.col + for token in tokens: + if (token.type == Token.OPERAND + and token.subtype == Token.RANGE): + out.append(self.translate_range(token.value, row_delta, + col_delta)) + else: + out.append(token.value) + return "".join(out) diff --git a/.venv/Lib/site-packages/openpyxl/packaging/__init__.py b/.venv/Lib/site-packages/openpyxl/packaging/__init__.py new file mode 100644 index 00000000..c3085ee5 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/packaging/__init__.py @@ -0,0 +1,3 @@ +""" +Stuff related to Office OpenXML packaging: relationships, archive, content types. +""" diff --git a/.venv/Lib/site-packages/openpyxl/packaging/core.py b/.venv/Lib/site-packages/openpyxl/packaging/core.py new file mode 100644 index 00000000..b3da9a08 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/packaging/core.py @@ -0,0 +1,112 @@ +# Copyright (c) 2010-2022 openpyxl + +import datetime + +from openpyxl.compat import safe_string +from openpyxl.descriptors import ( + String, + DateTime, + Alias, + ) +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors.nested import NestedText +from openpyxl.xml.functions import (Element, QName, tostring) +from openpyxl.xml.constants import ( + COREPROPS_NS, + DCORE_NS, + XSI_NS, + DCTERMS_NS, + DCTERMS_PREFIX +) + +class NestedDateTime(DateTime, NestedText): + + expected_type = datetime.datetime + + def to_tree(self, tagname=None, value=None, namespace=None): + namespace = getattr(self, "namespace", namespace) + if namespace is not None: + tagname = "{%s}%s" % (namespace, tagname) + el = Element(tagname) + if value is not None: + el.text = value.isoformat(timespec="seconds") + 'Z' + return el + + +class QualifiedDateTime(NestedDateTime): + + """In certain situations Excel will complain if the additional type + attribute isn't set""" + + def to_tree(self, tagname=None, value=None, namespace=None): + el = super(QualifiedDateTime, self).to_tree(tagname, value, namespace) + el.set("{%s}type" % XSI_NS, QName(DCTERMS_NS, "W3CDTF")) + return el + + +class DocumentProperties(Serialisable): + """High-level properties of the document. + Defined in ECMA-376 Par2 Annex D + """ + + tagname = "coreProperties" + namespace = COREPROPS_NS + + category = NestedText(expected_type=str, allow_none=True) + contentStatus = NestedText(expected_type=str, allow_none=True) + keywords = NestedText(expected_type=str, allow_none=True) + lastModifiedBy = NestedText(expected_type=str, allow_none=True) + lastPrinted = NestedDateTime(allow_none=True) + revision = NestedText(expected_type=str, allow_none=True) + version = NestedText(expected_type=str, allow_none=True) + last_modified_by = Alias("lastModifiedBy") + + # Dublin Core Properties + subject = NestedText(expected_type=str, allow_none=True, namespace=DCORE_NS) + title = NestedText(expected_type=str, allow_none=True, namespace=DCORE_NS) + creator = NestedText(expected_type=str, allow_none=True, namespace=DCORE_NS) + description = NestedText(expected_type=str, allow_none=True, namespace=DCORE_NS) + identifier = NestedText(expected_type=str, allow_none=True, namespace=DCORE_NS) + language = NestedText(expected_type=str, allow_none=True, namespace=DCORE_NS) + # Dublin Core Terms + created = QualifiedDateTime(allow_none=True, namespace=DCTERMS_NS) + modified = QualifiedDateTime(allow_none=True, namespace=DCTERMS_NS) + + __elements__ = ("creator", "title", "description", "subject","identifier", + "language", "created", "modified", "lastModifiedBy", "category", + "contentStatus", "version", "revision", "keywords", "lastPrinted", + ) + + + def __init__(self, + category=None, + contentStatus=None, + keywords=None, + lastModifiedBy=None, + lastPrinted=None, + revision=None, + version=None, + created=datetime.datetime.utcnow(), + creator="openpyxl", + description=None, + identifier=None, + language=None, + modified=datetime.datetime.utcnow(), + subject=None, + title=None, + ): + self.contentStatus = contentStatus + self.lastPrinted = lastPrinted + self.revision = revision + self.version = version + self.creator = creator + self.lastModifiedBy = lastModifiedBy + self.modified = modified + self.created = created + self.title = title + self.subject = subject + self.description = description + self.identifier = identifier + self.language = language + self.keywords = keywords + self.category = category diff --git a/.venv/Lib/site-packages/openpyxl/packaging/extended.py b/.venv/Lib/site-packages/openpyxl/packaging/extended.py new file mode 100644 index 00000000..241de4e5 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/packaging/extended.py @@ -0,0 +1,141 @@ +# Copyright (c) 2010-2022 openpyxl + + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, +) +from openpyxl.descriptors.nested import ( + NestedText, +) + +from openpyxl.xml.constants import XPROPS_NS + + +def get_version(): + from openpyxl import __version__ + + VERSION = ".".join(__version__.split(".")[:2]) + return VERSION + + +class DigSigBlob(Serialisable): + + __elements__ = __attrs__ = () + + +class VectorLpstr(Serialisable): + + __elements__ = __attrs__ = () + + +class VectorVariant(Serialisable): + + __elements__ = __attrs__ = () + + +class ExtendedProperties(Serialisable): + + """ + See 22.2 + + Most of this is irrelevant + """ + + tagname = "Properties" + + Template = NestedText(expected_type=str, allow_none=True) + Manager = NestedText(expected_type=str, allow_none=True) + Company = NestedText(expected_type=str, allow_none=True) + Pages = NestedText(expected_type=int, allow_none=True) + Words = NestedText(expected_type=int,allow_none=True) + Characters = NestedText(expected_type=int, allow_none=True) + PresentationFormat = NestedText(expected_type=str, allow_none=True) + Lines = NestedText(expected_type=int, allow_none=True) + Paragraphs = NestedText(expected_type=int, allow_none=True) + Slides = NestedText(expected_type=int, allow_none=True) + Notes = NestedText(expected_type=int, allow_none=True) + TotalTime = NestedText(expected_type=int, allow_none=True) + HiddenSlides = NestedText(expected_type=int, allow_none=True) + MMClips = NestedText(expected_type=int, allow_none=True) + ScaleCrop = NestedText(expected_type=bool, allow_none=True) + HeadingPairs = Typed(expected_type=VectorVariant, allow_none=True) + TitlesOfParts = Typed(expected_type=VectorLpstr, allow_none=True) + LinksUpToDate = NestedText(expected_type=bool, allow_none=True) + CharactersWithSpaces = NestedText(expected_type=int, allow_none=True) + SharedDoc = NestedText(expected_type=bool, allow_none=True) + HyperlinkBase = NestedText(expected_type=str, allow_none=True) + HLinks = Typed(expected_type=VectorVariant, allow_none=True) + HyperlinksChanged = NestedText(expected_type=bool, allow_none=True) + DigSig = Typed(expected_type=DigSigBlob, allow_none=True) + Application = NestedText(expected_type=str, allow_none=True) + AppVersion = NestedText(expected_type=str, allow_none=True) + DocSecurity = NestedText(expected_type=int, allow_none=True) + + __elements__ = ('Application', 'AppVersion', 'DocSecurity', 'ScaleCrop', + 'LinksUpToDate', 'SharedDoc', 'HyperlinksChanged') + + def __init__(self, + Template=None, + Manager=None, + Company=None, + Pages=None, + Words=None, + Characters=None, + PresentationFormat=None, + Lines=None, + Paragraphs=None, + Slides=None, + Notes=None, + TotalTime=None, + HiddenSlides=None, + MMClips=None, + ScaleCrop=None, + HeadingPairs=None, + TitlesOfParts=None, + LinksUpToDate=None, + CharactersWithSpaces=None, + SharedDoc=None, + HyperlinkBase=None, + HLinks=None, + HyperlinksChanged=None, + DigSig=None, + Application="Microsoft Excel", + AppVersion=None, + DocSecurity=None, + ): + self.Template = Template + self.Manager = Manager + self.Company = Company + self.Pages = Pages + self.Words = Words + self.Characters = Characters + self.PresentationFormat = PresentationFormat + self.Lines = Lines + self.Paragraphs = Paragraphs + self.Slides = Slides + self.Notes = Notes + self.TotalTime = TotalTime + self.HiddenSlides = HiddenSlides + self.MMClips = MMClips + self.ScaleCrop = ScaleCrop + self.HeadingPairs = None + self.TitlesOfParts = None + self.LinksUpToDate = LinksUpToDate + self.CharactersWithSpaces = CharactersWithSpaces + self.SharedDoc = SharedDoc + self.HyperlinkBase = HyperlinkBase + self.HLinks = None + self.HyperlinksChanged = HyperlinksChanged + self.DigSig = None + self.Application = Application + if AppVersion is None: + AppVersion = get_version() + self.AppVersion = AppVersion + self.DocSecurity = DocSecurity + + + def to_tree(self): + tree = super(ExtendedProperties, self).to_tree() + tree.set("xmlns", XPROPS_NS) + return tree diff --git a/.venv/Lib/site-packages/openpyxl/packaging/interface.py b/.venv/Lib/site-packages/openpyxl/packaging/interface.py new file mode 100644 index 00000000..5204ad1a --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/packaging/interface.py @@ -0,0 +1,56 @@ +# Copyright (c) 2010-2022 openpyxl + +from abc import abstractproperty +from openpyxl.compat.abc import ABC + + +class ISerialisableFile(ABC): + + """ + Interface for Serialisable classes that represent files in the archive + """ + + + @abstractproperty + def id(self): + """ + Object id making it unique + """ + pass + + + @abstractproperty + def _path(self): + """ + File path in the archive + """ + pass + + + @abstractproperty + def _namespace(self): + """ + Qualified namespace when serialised + """ + pass + + + @abstractproperty + def _type(self): + """ + The content type for the manifest + """ + + + @abstractproperty + def _rel_type(self): + """ + The content type for relationships + """ + + + @abstractproperty + def _rel_id(self): + """ + Links object with parent + """ diff --git a/.venv/Lib/site-packages/openpyxl/packaging/manifest.py b/.venv/Lib/site-packages/openpyxl/packaging/manifest.py new file mode 100644 index 00000000..03809ec9 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/packaging/manifest.py @@ -0,0 +1,210 @@ +# Copyright (c) 2010-2022 openpyxl + +""" +File manifest +""" +from mimetypes import MimeTypes +import os.path + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import String, Sequence +from openpyxl.xml.functions import fromstring +from openpyxl.xml.constants import ( + ARC_CORE, + ARC_CONTENT_TYPES, + ARC_WORKBOOK, + ARC_APP, + ARC_THEME, + ARC_STYLE, + ARC_SHARED_STRINGS, + EXTERNAL_LINK, + THEME_TYPE, + STYLES_TYPE, + XLSX, + XLSM, + XLTM, + XLTX, + WORKSHEET_TYPE, + COMMENTS_TYPE, + SHARED_STRINGS, + DRAWING_TYPE, + CHART_TYPE, + CHARTSHAPE_TYPE, + CHARTSHEET_TYPE, + CONTYPES_NS, + ACTIVEX, + CTRL, + VBA, +) +from openpyxl.xml.functions import tostring + +# initialise mime-types +mimetypes = MimeTypes() +mimetypes.add_type('application/xml', ".xml") +mimetypes.add_type('application/vnd.openxmlformats-package.relationships+xml', ".rels") +mimetypes.add_type("application/vnd.ms-office.vbaProject", ".bin") +mimetypes.add_type("application/vnd.openxmlformats-officedocument.vmlDrawing", ".vml") +mimetypes.add_type("image/x-emf", ".emf") + + +class FileExtension(Serialisable): + + tagname = "Default" + + Extension = String() + ContentType = String() + + def __init__(self, Extension, ContentType): + self.Extension = Extension + self.ContentType = ContentType + + +class Override(Serialisable): + + tagname = "Override" + + PartName = String() + ContentType = String() + + def __init__(self, PartName, ContentType): + self.PartName = PartName + self.ContentType = ContentType + + +DEFAULT_TYPES = [ + FileExtension("rels", "application/vnd.openxmlformats-package.relationships+xml"), + FileExtension("xml", "application/xml"), +] + +DEFAULT_OVERRIDE = [ + Override("/" + ARC_STYLE, STYLES_TYPE), # Styles + Override("/" + ARC_THEME, THEME_TYPE), # Theme + Override("/docProps/core.xml", "application/vnd.openxmlformats-package.core-properties+xml"), + Override("/docProps/app.xml", "application/vnd.openxmlformats-officedocument.extended-properties+xml") +] + + +class Manifest(Serialisable): + + tagname = "Types" + + Default = Sequence(expected_type=FileExtension, unique=True) + Override = Sequence(expected_type=Override, unique=True) + path = "[Content_Types].xml" + + __elements__ = ("Default", "Override") + + def __init__(self, + Default=(), + Override=(), + ): + if not Default: + Default = DEFAULT_TYPES + self.Default = Default + if not Override: + Override = DEFAULT_OVERRIDE + self.Override = Override + + + @property + def filenames(self): + return [part.PartName for part in self.Override] + + + @property + def extensions(self): + """ + Map content types to file extensions + Skip parts without extensions + """ + exts = {os.path.splitext(part.PartName)[-1] for part in self.Override} + return [(ext[1:], mimetypes.types_map[True][ext]) for ext in sorted(exts) if ext] + + + def to_tree(self): + """ + Custom serialisation method to allow setting a default namespace + """ + defaults = [t.Extension for t in self.Default] + for ext, mime in self.extensions: + if ext not in defaults: + mime = FileExtension(ext, mime) + self.Default.append(mime) + tree = super(Manifest, self).to_tree() + tree.set("xmlns", CONTYPES_NS) + return tree + + + def __contains__(self, content_type): + """ + Check whether a particular content type is contained + """ + for t in self.Override: + if t.ContentType == content_type: + return True + + + def find(self, content_type): + """ + Find specific content-type + """ + try: + return next(self.findall(content_type)) + except StopIteration: + return + + + def findall(self, content_type): + """ + Find all elements of a specific content-type + """ + for t in self.Override: + if t.ContentType == content_type: + yield t + + + def append(self, obj): + """ + Add content object to the package manifest + # needs a contract... + """ + ct = Override(PartName=obj.path, ContentType=obj.mime_type) + self.Override.append(ct) + + + def _write(self, archive, workbook): + """ + Write manifest to the archive + """ + self.append(workbook) + self._write_vba(workbook) + self._register_mimetypes(filenames=archive.namelist()) + archive.writestr(self.path, tostring(self.to_tree())) + + + def _register_mimetypes(self, filenames): + """ + Make sure that the mime type for all file extensions is registered + """ + for fn in filenames: + ext = os.path.splitext(fn)[-1] + if not ext: + continue + mime = mimetypes.types_map[True][ext] + fe = FileExtension(ext[1:], mime) + self.Default.append(fe) + + + def _write_vba(self, workbook): + """ + Add content types from cached workbook when keeping VBA + """ + if workbook.vba_archive: + node = fromstring(workbook.vba_archive.read(ARC_CONTENT_TYPES)) + mf = Manifest.from_tree(node) + filenames = self.filenames + for override in mf.Override: + if override.PartName not in (ACTIVEX, CTRL, VBA): + continue + if override.PartName not in filenames: + self.Override.append(override) diff --git a/.venv/Lib/site-packages/openpyxl/packaging/relationship.py b/.venv/Lib/site-packages/openpyxl/packaging/relationship.py new file mode 100644 index 00000000..ad144d7c --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/packaging/relationship.py @@ -0,0 +1,176 @@ +# Copyright (c) 2010-2022 openpyxl + + +import posixpath + +from openpyxl.descriptors import ( + String, + Set, + NoneSet, + Alias, + Sequence, +) +from openpyxl.descriptors.serialisable import Serialisable + +from openpyxl.xml.constants import REL_NS, PKG_REL_NS +from openpyxl.xml.functions import ( + Element, + fromstring, + tostring +) + + +class Relationship(Serialisable): + """Represents many kinds of relationships.""" + + tagname = "Relationship" + + Type = String() + Target = String() + target = Alias("Target") + TargetMode = String(allow_none=True) + Id = String(allow_none=True) + id = Alias("Id") + + + def __init__(self, + Id=None, + Type=None, + type=None, + Target=None, + TargetMode=None + ): + """ + `type` can be used as a shorthand with the default relationships namespace + otherwise the `Type` must be a fully qualified URL + """ + if type is not None: + Type = "{0}/{1}".format(REL_NS, type) + self.Type = Type + self.Target = Target + self.TargetMode = TargetMode + self.Id = Id + + +class RelationshipList(Serialisable): + + tagname = "Relationships" + + Relationship = Sequence(expected_type=Relationship) + + + def __init__(self, Relationship=()): + self.Relationship = Relationship + + + def append(self, value): + values = self.Relationship[:] + values.append(value) + if not value.Id: + value.Id = "rId{0}".format((len(values))) + self.Relationship = values + + + def __len__(self): + return len(self.Relationship) + + + def __bool__(self): + return bool(self.Relationship) + + + def find(self, content_type): + """ + Find relationships by content-type + NB. these content-types namespaced objects and different to the MIME-types + in the package manifest :-( + """ + for r in self.Relationship: + if r.Type == content_type: + yield r + + + def __getitem__(self, key): + for r in self.Relationship: + if r.Id == key: + return r + raise KeyError("Unknown relationship: {0}".format(key)) + + + def to_tree(self): + tree = Element("Relationships", xmlns=PKG_REL_NS) + for idx, rel in enumerate(self.Relationship, 1): + if not rel.Id: + rel.Id = "rId{0}".format(idx) + tree.append(rel.to_tree()) + + return tree + + +def get_rels_path(path): + """ + Convert relative path to absolutes that can be loaded from a zip + archive. + The path to be passed in is that of containing object (workbook, + worksheet, etc.) + """ + folder, obj = posixpath.split(path) + filename = posixpath.join(folder, '_rels', '{0}.rels'.format(obj)) + return filename + + +from warnings import warn + +def get_dependents(archive, filename): + """ + Normalise dependency file paths to absolute ones + + Relative paths are relative to parent object + """ + src = archive.read(filename) + node = fromstring(src) + try: + rels = RelationshipList.from_tree(node) + except TypeError: + msg = "{0} contains invalid dependency definitions".format(filename) + warn(msg) + rels = RelationshipList() + folder = posixpath.dirname(filename) + parent = posixpath.split(folder)[0] + for r in rels.Relationship: + if r.TargetMode == "External": + continue + elif r.target.startswith("/"): + r.target = r.target[1:] + else: + pth = posixpath.join(parent, r.target) + r.target = posixpath.normpath(pth) + return rels + + +def get_rel(archive, deps, id=None, cls=None): + """ + Get related object based on id or rel_type + """ + if not any([id, cls]): + raise ValueError("Either the id or the content type are required") + if id is not None: + rel = deps[id] + else: + try: + rel = next(deps.find(cls.rel_type)) + except StopIteration: # no known dependency + return + + path = rel.target + src = archive.read(path) + tree = fromstring(src) + obj = cls.from_tree(tree) + + rels_path = get_rels_path(path) + try: + obj.deps = get_dependents(archive, rels_path) + except KeyError: + obj.deps = [] + + return obj diff --git a/.venv/Lib/site-packages/openpyxl/packaging/workbook.py b/.venv/Lib/site-packages/openpyxl/packaging/workbook.py new file mode 100644 index 00000000..79784515 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/packaging/workbook.py @@ -0,0 +1,204 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Alias, + Typed, + String, + Integer, + Bool, + NoneSet, + Set, + Sequence, +) +from openpyxl.descriptors.excel import ExtensionList, Relation +from openpyxl.descriptors.sequence import NestedSequence +from openpyxl.descriptors.nested import NestedString + +from openpyxl.xml.constants import SHEET_MAIN_NS + +from openpyxl.workbook.defined_name import DefinedName, DefinedNameList +from openpyxl.workbook.external_reference import ExternalReference +from openpyxl.workbook.function_group import FunctionGroupList +from openpyxl.workbook.properties import WorkbookProperties, CalcProperties, FileVersion +from openpyxl.workbook.protection import WorkbookProtection, FileSharing +from openpyxl.workbook.smart_tags import SmartTagList, SmartTagProperties +from openpyxl.workbook.views import CustomWorkbookView, BookView +from openpyxl.workbook.web import WebPublishing, WebPublishObjectList + + +class FileRecoveryProperties(Serialisable): + + tagname = "fileRecoveryPr" + + autoRecover = Bool(allow_none=True) + crashSave = Bool(allow_none=True) + dataExtractLoad = Bool(allow_none=True) + repairLoad = Bool(allow_none=True) + + def __init__(self, + autoRecover=None, + crashSave=None, + dataExtractLoad=None, + repairLoad=None, + ): + self.autoRecover = autoRecover + self.crashSave = crashSave + self.dataExtractLoad = dataExtractLoad + self.repairLoad = repairLoad + + +class ChildSheet(Serialisable): + """ + Represents a reference to a worksheet or chartsheet in workbook.xml + + It contains the title, order and state but only an indirect reference to + the objects themselves. + """ + + tagname = "sheet" + + name = String() + sheetId = Integer() + state = NoneSet(values=(['visible', 'hidden', 'veryHidden'])) + id = Relation() + + def __init__(self, + name=None, + sheetId=None, + state="visible", + id=None, + ): + self.name = name + self.sheetId = sheetId + self.state = state + self.id = id + + +class PivotCache(Serialisable): + + tagname = "pivotCache" + + cacheId = Integer() + id = Relation() + + def __init__(self, + cacheId=None, + id=None + ): + self.cacheId = cacheId + self.id = id + + +class WorkbookPackage(Serialisable): + + """ + Represent the workbook file in the archive + """ + + tagname = "workbook" + + conformance = NoneSet(values=['strict', 'transitional']) + fileVersion = Typed(expected_type=FileVersion, allow_none=True) + fileSharing = Typed(expected_type=FileSharing, allow_none=True) + workbookPr = Typed(expected_type=WorkbookProperties, allow_none=True) + properties = Alias("workbookPr") + workbookProtection = Typed(expected_type=WorkbookProtection, allow_none=True) + bookViews = NestedSequence(expected_type=BookView) + sheets = NestedSequence(expected_type=ChildSheet) + functionGroups = Typed(expected_type=FunctionGroupList, allow_none=True) + externalReferences = NestedSequence(expected_type=ExternalReference) + definedNames = Typed(expected_type=DefinedNameList, allow_none=True) + calcPr = Typed(expected_type=CalcProperties, allow_none=True) + oleSize = NestedString(allow_none=True, attribute="ref") + customWorkbookViews = NestedSequence(expected_type=CustomWorkbookView) + pivotCaches = NestedSequence(expected_type=PivotCache, allow_none=True) + smartTagPr = Typed(expected_type=SmartTagProperties, allow_none=True) + smartTagTypes = Typed(expected_type=SmartTagList, allow_none=True) + webPublishing = Typed(expected_type=WebPublishing, allow_none=True) + fileRecoveryPr = Typed(expected_type=FileRecoveryProperties, allow_none=True) + webPublishObjects = Typed(expected_type=WebPublishObjectList, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + Ignorable = NestedString(namespace="http://schemas.openxmlformats.org/markup-compatibility/2006", allow_none=True) + + __elements__ = ('fileVersion', 'fileSharing', 'workbookPr', + 'workbookProtection', 'bookViews', 'sheets', 'functionGroups', + 'externalReferences', 'definedNames', 'calcPr', 'oleSize', + 'customWorkbookViews', 'pivotCaches', 'smartTagPr', 'smartTagTypes', + 'webPublishing', 'fileRecoveryPr', 'webPublishObjects') + + def __init__(self, + conformance=None, + fileVersion=None, + fileSharing=None, + workbookPr=None, + workbookProtection=None, + bookViews=(), + sheets=(), + functionGroups=None, + externalReferences=(), + definedNames=None, + calcPr=None, + oleSize=None, + customWorkbookViews=(), + pivotCaches=(), + smartTagPr=None, + smartTagTypes=None, + webPublishing=None, + fileRecoveryPr=None, + webPublishObjects=None, + extLst=None, + Ignorable=None, + ): + self.conformance = conformance + self.fileVersion = fileVersion + self.fileSharing = fileSharing + if workbookPr is None: + workbookPr = WorkbookProperties() + self.workbookPr = workbookPr + self.workbookProtection = workbookProtection + self.bookViews = bookViews + self.sheets = sheets + self.functionGroups = functionGroups + self.externalReferences = externalReferences + self.definedNames = definedNames + self.calcPr = calcPr + self.oleSize = oleSize + self.customWorkbookViews = customWorkbookViews + self.pivotCaches = pivotCaches + self.smartTagPr = smartTagPr + self.smartTagTypes = smartTagTypes + self.webPublishing = webPublishing + self.fileRecoveryPr = fileRecoveryPr + self.webPublishObjects = webPublishObjects + + + def to_tree(self): + tree = super(WorkbookPackage, self).to_tree() + tree.set("xmlns", SHEET_MAIN_NS) + return tree + + + @property + def active(self): + for view in self.bookViews: + if view.activeTab is not None: + return view.activeTab + return 0 + + + @property + def pivot_caches(self): + """ + Get PivotCache objects + """ + d = {} + for c in self.caches: + cache = get_rel(self.archive, self.rels, id=c.id, cls=CacheDefinition) + if cache.deps: + records = get_rel(self.archive, cache.deps, cache.id, RecordList) + else: + records = None + cache.records = records + d[c.cacheId] = cache + return d diff --git a/.venv/Lib/site-packages/openpyxl/pivot/__init__.py b/.venv/Lib/site-packages/openpyxl/pivot/__init__.py new file mode 100644 index 00000000..a5a5eaa0 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/pivot/__init__.py @@ -0,0 +1 @@ +# Copyright (c) 2010-2022 openpyxl diff --git a/.venv/Lib/site-packages/openpyxl/pivot/cache.py b/.venv/Lib/site-packages/openpyxl/pivot/cache.py new file mode 100644 index 00000000..cb172276 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/pivot/cache.py @@ -0,0 +1,1119 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Bool, + Float, + Set, + NoneSet, + String, + Integer, + DateTime, + Sequence, +) + +from openpyxl.descriptors.excel import ( + HexBinary, + ExtensionList, + Relation, +) +from openpyxl.descriptors.nested import NestedInteger +from openpyxl.descriptors.sequence import ( + NestedSequence, + MultiSequence, + MultiSequencePart, +) +from openpyxl.xml.constants import SHEET_MAIN_NS +from openpyxl.xml.functions import tostring +from openpyxl.packaging.relationship import ( + RelationshipList, + Relationship, + get_rels_path +) + +from .table import ( + PivotArea, + Reference, +) +from .fields import ( + Boolean, + Error, + Missing, + Number, + Text, + TupleList, + DateTimeField, +) + +class MeasureDimensionMap(Serialisable): + + tagname = "map" + + measureGroup = Integer(allow_none=True) + dimension = Integer(allow_none=True) + + def __init__(self, + measureGroup=None, + dimension=None, + ): + self.measureGroup = measureGroup + self.dimension = dimension + + +class MeasureGroup(Serialisable): + + tagname = "measureGroup" + + name = String() + caption = String() + + def __init__(self, + name=None, + caption=None, + ): + self.name = name + self.caption = caption + + +class PivotDimension(Serialisable): + + tagname = "dimension" + + measure = Bool() + name = String() + uniqueName = String() + caption = String() + + def __init__(self, + measure=None, + name=None, + uniqueName=None, + caption=None, + ): + self.measure = measure + self.name = name + self.uniqueName = uniqueName + self.caption = caption + + +class CalculatedMember(Serialisable): + + tagname = "calculatedMember" + + name = String() + mdx = String() + memberName = String() + hierarchy = String() + parent = String() + solveOrder = Integer() + set = Bool() + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = () + + def __init__(self, + name=None, + mdx=None, + memberName=None, + hierarchy=None, + parent=None, + solveOrder=None, + set=None, + extLst=None, + ): + self.name = name + self.mdx = mdx + self.memberName = memberName + self.hierarchy = hierarchy + self.parent = parent + self.solveOrder = solveOrder + self.set = set + #self.extLst = extLst + + +class CalculatedItem(Serialisable): + + tagname = "calculatedItem" + + field = Integer(allow_none=True) + formula = String() + pivotArea = Typed(expected_type=PivotArea, ) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('pivotArea', 'extLst') + + def __init__(self, + field=None, + formula=None, + pivotArea=None, + extLst=None, + ): + self.field = field + self.formula = formula + self.pivotArea = pivotArea + self.extLst = extLst + + +class ServerFormat(Serialisable): + + tagname = "serverFormat" + + culture = String(allow_none=True) + format = String(allow_none=True) + + def __init__(self, + culture=None, + format=None, + ): + self.culture = culture + self.format = format + + +class ServerFormatList(Serialisable): + + tagname = "serverFormats" + + serverFormat = Sequence(expected_type=ServerFormat, allow_none=True) + + __elements__ = ('serverFormat',) + __attrs__ = ('count',) + + def __init__(self, + count=None, + serverFormat=None, + ): + self.serverFormat = serverFormat + + + @property + def count(self): + return len(self.serverFormat) + + +class Query(Serialisable): + + tagname = "query" + + mdx = String() + tpls = Typed(expected_type=TupleList, allow_none=True) + + __elements__ = ('tpls',) + + def __init__(self, + mdx=None, + tpls=None, + ): + self.mdx = mdx + self.tpls = tpls + + +class QueryCache(Serialisable): + + tagname = "queryCache" + + count = Integer() + query = Typed(expected_type=Query, ) + + __elements__ = ('query',) + + def __init__(self, + count=None, + query=None, + ): + self.count = count + self.query = query + + +class OLAPSet(Serialisable): + + tagname = "set" + + count = Integer() + maxRank = Integer() + setDefinition = String() + sortType = NoneSet(values=(['ascending', 'descending', 'ascendingAlpha', + 'descendingAlpha', 'ascendingNatural', 'descendingNatural'])) + queryFailed = Bool() + tpls = Typed(expected_type=TupleList, allow_none=True) + sortByTuple = Typed(expected_type=TupleList, allow_none=True) + + __elements__ = ('tpls', 'sortByTuple') + + def __init__(self, + count=None, + maxRank=None, + setDefinition=None, + sortType=None, + queryFailed=None, + tpls=None, + sortByTuple=None, + ): + self.count = count + self.maxRank = maxRank + self.setDefinition = setDefinition + self.sortType = sortType + self.queryFailed = queryFailed + self.tpls = tpls + self.sortByTuple = sortByTuple + + +class OLAPSets(Serialisable): + + count = Integer() + set = Typed(expected_type=OLAPSet, ) + + __elements__ = ('set',) + + def __init__(self, + count=None, + set=None, + ): + self.count = count + self.set = set + + +class PCDSDTCEntries(Serialisable): + + tagname = "pCDSDTCEntries" + + count = Integer() + # some elements are choice + m = Typed(expected_type=Missing, ) + n = Typed(expected_type=Number, ) + e = Typed(expected_type=Error, ) + s = Typed(expected_type=Text) + + __elements__ = ('m', 'n', 'e', 's') + + def __init__(self, + count=None, + m=None, + n=None, + e=None, + s=None, + ): + self.count = count + self.m = m + self.n = n + self.e = e + self.s = s + + +class TupleCache(Serialisable): + + tagname = "tupleCache" + + entries = Typed(expected_type=PCDSDTCEntries, allow_none=True) + sets = Typed(expected_type=OLAPSets, allow_none=True) + queryCache = Typed(expected_type=QueryCache, allow_none=True) + serverFormats = Typed(expected_type=ServerFormatList, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('entries', 'sets', 'queryCache', 'serverFormats', 'extLst') + + def __init__(self, + entries=None, + sets=None, + queryCache=None, + serverFormats=None, + extLst=None, + ): + self.entries = entries + self.sets = sets + self.queryCache = queryCache + self.serverFormats = serverFormats + self.extLst = extLst + + +class PCDKPI(Serialisable): + + tagname = "pCDKPI" + + uniqueName = String() + caption = String(allow_none=True) + displayFolder = String() + measureGroup = String() + parent = String() + value = String() + goal = String() + status = String() + trend = String() + weight = String() + time = String() + + def __init__(self, + uniqueName=None, + caption=None, + displayFolder=None, + measureGroup=None, + parent=None, + value=None, + goal=None, + status=None, + trend=None, + weight=None, + time=None, + ): + self.uniqueName = uniqueName + self.caption = caption + self.displayFolder = displayFolder + self.measureGroup = measureGroup + self.parent = parent + self.value = value + self.goal = goal + self.status = status + self.trend = trend + self.weight = weight + self.time = time + + +class GroupMember(Serialisable): + + tagname = "groupMember" + + uniqueName = String() + group = Bool() + + def __init__(self, + uniqueName=None, + group=None, + ): + self.uniqueName = uniqueName + self.group = group + + +class GroupMembers(Serialisable): + + count = Integer() + groupMember = Typed(expected_type=GroupMember, ) + + __elements__ = ('groupMember',) + + def __init__(self, + count=None, + groupMember=None, + ): + self.count = count + self.groupMember = groupMember + + +class LevelGroup(Serialisable): + + tagname = "levelGroup" + + name = String() + uniqueName = String() + caption = String() + uniqueParent = String() + id = Integer() + groupMembers = Typed(expected_type=GroupMembers, ) + + __elements__ = ('groupMembers',) + + def __init__(self, + name=None, + uniqueName=None, + caption=None, + uniqueParent=None, + id=None, + groupMembers=None, + ): + self.name = name + self.uniqueName = uniqueName + self.caption = caption + self.uniqueParent = uniqueParent + self.id = id + self.groupMembers = groupMembers + + +class Groups(Serialisable): + + tagname = "groups" + + count = Integer() + group = Typed(expected_type=LevelGroup, ) + + __elements__ = ('group',) + + def __init__(self, + count=None, + group=None, + ): + self.count = count + self.group = group + + +class GroupLevel(Serialisable): + + tagname = "groupLevel" + + uniqueName = String() + caption = String() + user = Bool() + customRollUp = Bool() + groups = Typed(expected_type=Groups, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('groups', 'extLst') + + def __init__(self, + uniqueName=None, + caption=None, + user=None, + customRollUp=None, + groups=None, + extLst=None, + ): + self.uniqueName = uniqueName + self.caption = caption + self.user = user + self.customRollUp = customRollUp + self.groups = groups + self.extLst = extLst + + +class GroupLevels(Serialisable): + + count = Integer() + groupLevel = Typed(expected_type=GroupLevel, ) + + __elements__ = ('groupLevel',) + + def __init__(self, + count=None, + groupLevel=None, + ): + self.count = count + self.groupLevel = groupLevel + + +class FieldUsage(Serialisable): + + tagname = "fieldUsage" + + x = Integer() + + def __init__(self, + x=None, + ): + self.x = x + + +class FieldsUsage(Serialisable): + + count = Integer() + fieldUsage = Typed(expected_type=FieldUsage, allow_none=True) + + __elements__ = ('fieldUsage',) + + def __init__(self, + count=None, + fieldUsage=None, + ): + self.count = count + self.fieldUsage = fieldUsage + + +class CacheHierarchy(Serialisable): + + tagname = "cacheHierarchy" + + uniqueName = String() + caption = String(allow_none=True) + measure = Bool() + set = Bool() + parentSet = Integer(allow_none=True) + iconSet = Integer() + attribute = Bool() + time = Bool() + keyAttribute = Bool() + defaultMemberUniqueName = String(allow_none=True) + allUniqueName = String(allow_none=True) + allCaption = String(allow_none=True) + dimensionUniqueName = String(allow_none=True) + displayFolder = String(allow_none=True) + measureGroup = String(allow_none=True) + measures = Bool() + count = Integer() + oneField = Bool() + memberValueDatatype = Integer(allow_none=True) + unbalanced = Bool(allow_none=True) + unbalancedGroup = Bool(allow_none=True) + hidden = Bool() + fieldsUsage = Typed(expected_type=FieldsUsage, allow_none=True) + groupLevels = Typed(expected_type=GroupLevels, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('fieldsUsage', 'groupLevels') + + def __init__(self, + uniqueName="", + caption=None, + measure=None, + set=None, + parentSet=None, + iconSet=0, + attribute=None, + time=None, + keyAttribute=None, + defaultMemberUniqueName=None, + allUniqueName=None, + allCaption=None, + dimensionUniqueName=None, + displayFolder=None, + measureGroup=None, + measures=None, + count=None, + oneField=None, + memberValueDatatype=None, + unbalanced=None, + unbalancedGroup=None, + hidden=None, + fieldsUsage=None, + groupLevels=None, + extLst=None, + ): + self.uniqueName = uniqueName + self.caption = caption + self.measure = measure + self.set = set + self.parentSet = parentSet + self.iconSet = iconSet + self.attribute = attribute + self.time = time + self.keyAttribute = keyAttribute + self.defaultMemberUniqueName = defaultMemberUniqueName + self.allUniqueName = allUniqueName + self.allCaption = allCaption + self.dimensionUniqueName = dimensionUniqueName + self.displayFolder = displayFolder + self.measureGroup = measureGroup + self.measures = measures + self.count = count + self.oneField = oneField + self.memberValueDatatype = memberValueDatatype + self.unbalanced = unbalanced + self.unbalancedGroup = unbalancedGroup + self.hidden = hidden + self.fieldsUsage = fieldsUsage + self.groupLevels = groupLevels + self.extLst = extLst + + +class GroupItems(Serialisable): + + tagname = "groupItems" + + m = Sequence(expected_type=Missing) + n = Sequence(expected_type=Number) + b = Sequence(expected_type=Boolean) + e = Sequence(expected_type=Error) + s = Sequence(expected_type=Text) + d = Sequence(expected_type=DateTimeField,) + + __elements__ = ('m', 'n', 'b', 'e', 's', 'd') + __attrs__ = ("count", ) + + def __init__(self, + count=None, + m=(), + n=(), + b=(), + e=(), + s=(), + d=(), + ): + self.m = m + self.n = n + self.b = b + self.e = e + self.s = s + self.d = d + + + @property + def count(self): + return len(self.m + self.n + self.b + self.e + self.s + self.d) + + +class DiscretePr(Serialisable): + + tagname = "discretePr" + + count = Integer() + x = NestedInteger(allow_none=True) + + __elements__ = ('x',) + + def __init__(self, + count=None, + x=None, + ): + self.count = count + self.x = x + + +class RangePr(Serialisable): + + tagname = "rangePr" + + autoStart = Bool(allow_none=True) + autoEnd = Bool(allow_none=True) + groupBy = Set(values=(['range', 'seconds', 'minutes', 'hours', 'days', + 'months', 'quarters', 'years'])) + startNum = Float(allow_none=True) + endNum = Float(allow_none=True) + startDate = DateTime(allow_none=True) + endDate = DateTime(allow_none=True) + groupInterval = Float(allow_none=True) + + def __init__(self, + autoStart=True, + autoEnd=True, + groupBy="range", + startNum=None, + endNum=None, + startDate=None, + endDate=None, + groupInterval=1, + ): + self.autoStart = autoStart + self.autoEnd = autoEnd + self.groupBy = groupBy + self.startNum = startNum + self.endNum = endNum + self.startDate = startDate + self.endDate = endDate + self.groupInterval = groupInterval + + +class FieldGroup(Serialisable): + + tagname = "fieldGroup" + + par = Integer(allow_none=True) + base = Integer(allow_none=True) + rangePr = Typed(expected_type=RangePr, allow_none=True) + discretePr = Typed(expected_type=DiscretePr, allow_none=True) + groupItems = Typed(expected_type=GroupItems, allow_none=True) + + __elements__ = ('rangePr', 'discretePr', 'groupItems') + + def __init__(self, + par=None, + base=None, + rangePr=None, + discretePr=None, + groupItems=None, + ): + self.par = par + self.base = base + self.rangePr = rangePr + self.discretePr = discretePr + self.groupItems = groupItems + + +class SharedItems(Serialisable): + + tagname = "sharedItems" + + _fields = MultiSequence() + m = MultiSequencePart(expected_type=Missing, store="_fields") + n = MultiSequencePart(expected_type=Number, store="_fields") + b = MultiSequencePart(expected_type=Boolean, store="_fields") + e = MultiSequencePart(expected_type=Error, store="_fields") + s = MultiSequencePart(expected_type=Text, store="_fields") + d = MultiSequencePart(expected_type=DateTimeField, store="_fields") + # attributes are optional and must be derived from associated cache records + containsSemiMixedTypes = Bool(allow_none=True) + containsNonDate = Bool(allow_none=True) + containsDate = Bool(allow_none=True) + containsString = Bool(allow_none=True) + containsBlank = Bool(allow_none=True) + containsMixedTypes = Bool(allow_none=True) + containsNumber = Bool(allow_none=True) + containsInteger = Bool(allow_none=True) + minValue = Float(allow_none=True) + maxValue = Float(allow_none=True) + minDate = DateTime(allow_none=True) + maxDate = DateTime(allow_none=True) + longText = Bool(allow_none=True) + + __attrs__ = ('count', 'containsBlank', 'containsDate', 'containsInteger', + 'containsMixedTypes', 'containsNonDate', 'containsNumber', + 'containsSemiMixedTypes', 'containsString', 'minValue', 'maxValue', + 'minDate', 'maxDate', 'longText') + + def __init__(self, + _fields=(), + containsSemiMixedTypes=None, + containsNonDate=None, + containsDate=None, + containsString=None, + containsBlank=None, + containsMixedTypes=None, + containsNumber=None, + containsInteger=None, + minValue=None, + maxValue=None, + minDate=None, + maxDate=None, + count=None, + longText=None, + ): + self._fields = _fields + self.containsBlank = containsBlank + self.containsDate = containsDate + self.containsNonDate = containsNonDate + self.containsString = containsString + self.containsMixedTypes = containsMixedTypes + self.containsSemiMixedTypes = containsSemiMixedTypes + self.containsNumber = containsNumber + self.containsInteger = containsInteger + self.minValue = minValue + self.maxValue = maxValue + self.minDate = minDate + self.maxDate = maxDate + self.longText = longText + + + @property + def count(self): + return len(self._fields) + + +class CacheField(Serialisable): + + tagname = "cacheField" + + sharedItems = Typed(expected_type=SharedItems, allow_none=True) + fieldGroup = Typed(expected_type=FieldGroup, allow_none=True) + mpMap = NestedInteger(allow_none=True, attribute="v") + extLst = Typed(expected_type=ExtensionList, allow_none=True) + name = String() + caption = String(allow_none=True) + propertyName = String(allow_none=True) + serverField = Bool(allow_none=True) + uniqueList = Bool(allow_none=True) + numFmtId = Integer(allow_none=True) + formula = String(allow_none=True) + sqlType = Integer(allow_none=True) + hierarchy = Integer(allow_none=True) + level = Integer(allow_none=True) + databaseField = Bool(allow_none=True) + mappingCount = Integer(allow_none=True) + memberPropertyField = Bool(allow_none=True) + + __elements__ = ('sharedItems', 'fieldGroup', 'mpMap') + + def __init__(self, + sharedItems=None, + fieldGroup=None, + mpMap=None, + extLst=None, + name=None, + caption=None, + propertyName=None, + serverField=None, + uniqueList=True, + numFmtId=None, + formula=None, + sqlType=0, + hierarchy=0, + level=0, + databaseField=True, + mappingCount=None, + memberPropertyField=None, + ): + self.sharedItems = sharedItems + self.fieldGroup = fieldGroup + self.mpMap = mpMap + self.extLst = extLst + self.name = name + self.caption = caption + self.propertyName = propertyName + self.serverField = serverField + self.uniqueList = uniqueList + self.numFmtId = numFmtId + self.formula = formula + self.sqlType = sqlType + self.hierarchy = hierarchy + self.level = level + self.databaseField = databaseField + self.mappingCount = mappingCount + self.memberPropertyField = memberPropertyField + + +class RangeSet(Serialisable): + + tagname = "rangeSet" + + i1 = Integer(allow_none=True) + i2 = Integer(allow_none=True) + i3 = Integer(allow_none=True) + i4 = Integer(allow_none=True) + ref = String() + name = String(allow_none=True) + sheet = String(allow_none=True) + + def __init__(self, + i1=None, + i2=None, + i3=None, + i4=None, + ref=None, + name=None, + sheet=None, + ): + self.i1 = i1 + self.i2 = i2 + self.i3 = i3 + self.i4 = i4 + self.ref = ref + self.name = name + self.sheet = sheet + + +class PageItem(Serialisable): + + tagname = "pageItem" + + name = String() + + def __init__(self, + name=None, + ): + self.name = name + + +class Page(Serialisable): + + # PCDSCPage + tagname = "PCDSCPage" + + pageItem = Sequence(expected_type=PageItem) + + __elements__ = ('pageItem',) + + def __init__(self, + count=None, + pageItem=None, + ): + self.pageItem = pageItem + + + @property + def count(self): + return len(self.pageItem) + + +class Consolidation(Serialisable): + + tagname = "consolidation" + + autoPage = Bool(allow_none=True) + pages = NestedSequence(expected_type=Page, count=True) + rangeSets = NestedSequence(expected_type=RangeSet, count=True) + + __elements__ = ('pages', 'rangeSets') + + def __init__(self, + autoPage=None, + pages=(), + rangeSets=(), + ): + self.autoPage = autoPage + self.pages = pages + self.rangeSets = rangeSets + + +class WorksheetSource(Serialisable): + + tagname = "worksheetSource" + + ref = String(allow_none=True) + name = String(allow_none=True) + sheet = String(allow_none=True) + + def __init__(self, + ref=None, + name=None, + sheet=None, + ): + self.ref = ref + self.name = name + self.sheet = sheet + + +class CacheSource(Serialisable): + + tagname = "cacheSource" + + type = Set(values=(['worksheet', 'external', 'consolidation', 'scenario'])) + connectionId = Integer(allow_none=True) + # some elements are choice + worksheetSource = Typed(expected_type=WorksheetSource, allow_none=True) + consolidation = Typed(expected_type=Consolidation, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('worksheetSource', 'consolidation',) + + def __init__(self, + type=None, + connectionId=None, + worksheetSource=None, + consolidation=None, + extLst=None, + ): + self.type = type + self.connectionId = connectionId + self.worksheetSource = worksheetSource + self.consolidation = consolidation + + +class CacheDefinition(Serialisable): + + mime_type = "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheDefinition+xml" + rel_type = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotCacheDefinition" + _id = 1 + _path = "/xl/pivotCache/pivotCacheDefinition{0}.xml" + records = None + + tagname = "pivotCacheDefinition" + + invalid = Bool(allow_none=True) + saveData = Bool(allow_none=True) + refreshOnLoad = Bool(allow_none=True) + optimizeMemory = Bool(allow_none=True) + enableRefresh = Bool(allow_none=True) + refreshedBy = String(allow_none=True) + refreshedDate = Float(allow_none=True) + refreshedDateIso = DateTime(allow_none=True) + backgroundQuery = Bool(allow_none=True) + missingItemsLimit = Integer(allow_none=True) + createdVersion = Integer(allow_none=True) + refreshedVersion = Integer(allow_none=True) + minRefreshableVersion = Integer(allow_none=True) + recordCount = Integer(allow_none=True) + upgradeOnRefresh = Bool(allow_none=True) + tupleCache = Bool(allow_none=True) + supportSubquery = Bool(allow_none=True) + supportAdvancedDrill = Bool(allow_none=True) + cacheSource = Typed(expected_type=CacheSource) + cacheFields = NestedSequence(expected_type=CacheField, count=True) + cacheHierarchies = NestedSequence(expected_type=CacheHierarchy, allow_none=True) + kpis = NestedSequence(expected_type=PCDKPI, allow_none=True) + tupleCache = Typed(expected_type=TupleCache, allow_none=True) + calculatedItems = NestedSequence(expected_type=CalculatedItem, count=True) + calculatedMembers = NestedSequence(expected_type=CalculatedMember, count=True) + dimensions = NestedSequence(expected_type=PivotDimension, allow_none=True) + measureGroups = NestedSequence(expected_type=MeasureGroup, count=True) + maps = NestedSequence(expected_type=MeasureDimensionMap, count=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + id = Relation() + + __elements__ = ('cacheSource', 'cacheFields', 'cacheHierarchies', 'kpis', + 'tupleCache', 'calculatedItems', 'calculatedMembers', 'dimensions', + 'measureGroups', 'maps',) + + def __init__(self, + invalid=None, + saveData=None, + refreshOnLoad=None, + optimizeMemory=None, + enableRefresh=None, + refreshedBy=None, + refreshedDate=None, + refreshedDateIso=None, + backgroundQuery=None, + missingItemsLimit=None, + createdVersion=None, + refreshedVersion=None, + minRefreshableVersion=None, + recordCount=None, + upgradeOnRefresh=None, + tupleCache=None, + supportSubquery=None, + supportAdvancedDrill=None, + cacheSource=None, + cacheFields=(), + cacheHierarchies=(), + kpis=(), + calculatedItems=(), + calculatedMembers=(), + dimensions=(), + measureGroups=(), + maps=(), + extLst=None, + id = None, + ): + self.invalid = invalid + self.saveData = saveData + self.refreshOnLoad = refreshOnLoad + self.optimizeMemory = optimizeMemory + self.enableRefresh = enableRefresh + self.refreshedBy = refreshedBy + self.refreshedDate = refreshedDate + self.refreshedDateIso = refreshedDateIso + self.backgroundQuery = backgroundQuery + self.missingItemsLimit = missingItemsLimit + self.createdVersion = createdVersion + self.refreshedVersion = refreshedVersion + self.minRefreshableVersion = minRefreshableVersion + self.recordCount = recordCount + self.upgradeOnRefresh = upgradeOnRefresh + self.tupleCache = tupleCache + self.supportSubquery = supportSubquery + self.supportAdvancedDrill = supportAdvancedDrill + self.cacheSource = cacheSource + self.cacheFields = cacheFields + self.cacheHierarchies = cacheHierarchies + self.kpis = kpis + self.tupleCache = tupleCache + self.calculatedItems = calculatedItems + self.calculatedMembers = calculatedMembers + self.dimensions = dimensions + self.measureGroups = measureGroups + self.maps = maps + self.id = id + + + def to_tree(self): + node = super(CacheDefinition, self).to_tree() + node.set("xmlns", SHEET_MAIN_NS) + return node + + + @property + def path(self): + return self._path.format(self._id) + + + def _write(self, archive, manifest): + """ + Add to zipfile and update manifest + """ + self._write_rels(archive, manifest) + xml = tostring(self.to_tree()) + archive.writestr(self.path[1:], xml) + manifest.append(self) + + + def _write_rels(self, archive, manifest): + """ + Write the relevant child objects and add links + """ + if self.records is None: + return + + rels = RelationshipList() + r = Relationship(Type=self.records.rel_type, Target=self.records.path) + rels.append(r) + self.id = r.id + self.records._id = self._id + self.records._write(archive, manifest) + + path = get_rels_path(self.path) + xml = tostring(rels.to_tree()) + archive.writestr(path[1:], xml) diff --git a/.venv/Lib/site-packages/openpyxl/pivot/fields.py b/.venv/Lib/site-packages/openpyxl/pivot/fields.py new file mode 100644 index 00000000..ac8be1f7 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/pivot/fields.py @@ -0,0 +1,322 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + DateTime, + Bool, + Float, + String, + Integer, + Sequence, +) +from openpyxl.descriptors.excel import HexBinary + +class Index(Serialisable): + + tagname = "x" + + v = Integer(allow_none=True) + + def __init__(self, + v=0, + ): + self.v = v + + +class Tuple(Serialisable): + + fld = Integer() + hier = Integer() + item = Integer() + + def __init__(self, + fld=None, + hier=None, + item=None, + ): + self.fld = fld + self.hier = hier + self.item = item + + +class TupleList(Serialisable): + + c = Integer(allow_none=True) + tpl = Typed(expected_type=Tuple, ) + + __elements__ = ('tpl',) + + def __init__(self, + c=None, + tpl=None, + ): + self.c = c + self.tpl = tpl + + +class Missing(Serialisable): + + tagname = "m" + + tpls = Sequence(expected_type=TupleList) + x = Sequence(expected_type=Index) + u = Bool(allow_none=True) + f = Bool(allow_none=True) + c = String(allow_none=True) + cp = Integer(allow_none=True) + _in = Integer(allow_none=True) + bc = HexBinary(allow_none=True) + fc = HexBinary(allow_none=True) + i = Bool(allow_none=True) + un = Bool(allow_none=True) + st = Bool(allow_none=True) + b = Bool(allow_none=True) + + __elements__ = ('tpls', 'x') + + def __init__(self, + tpls=(), + x=(), + u=None, + f=None, + c=None, + cp=None, + _in=None, + bc=None, + fc=None, + i=None, + un=None, + st=None, + b=None, + ): + self.tpls = tpls + self.x = x + self.u = u + self.f = f + self.c = c + self.cp = cp + self._in = _in + self.bc = bc + self.fc = fc + self.i = i + self.un = un + self.st = st + self.b = b + + +class Number(Serialisable): + + tagname = "n" + + tpls = Sequence(expected_type=TupleList) + x = Sequence(expected_type=Index) + v = Float() + u = Bool(allow_none=True) + f = Bool(allow_none=True) + c = String(allow_none=True) + cp = Integer(allow_none=True) + _in = Integer(allow_none=True) + bc = HexBinary(allow_none=True) + fc = HexBinary(allow_none=True) + i = Bool(allow_none=True) + un = Bool(allow_none=True) + st = Bool(allow_none=True) + b = Bool(allow_none=True) + + __elements__ = ('tpls', 'x') + + def __init__(self, + tpls=(), + x=(), + v=None, + u=None, + f=None, + c=None, + cp=None, + _in=None, + bc=None, + fc=None, + i=None, + un=None, + st=None, + b=None, + ): + self.tpls = tpls + self.x = x + self.v = v + self.u = u + self.f = f + self.c = c + self.cp = cp + self._in = _in + self.bc = bc + self.fc = fc + self.i = i + self.un = un + self.st = st + self.b = b + + +class Error(Serialisable): + + tagname = "e" + + tpls = Typed(expected_type=TupleList, allow_none=True) + x = Sequence(expected_type=Index) + v = String() + u = Bool(allow_none=True) + f = Bool(allow_none=True) + c = String(allow_none=True) + cp = Integer(allow_none=True) + _in = Integer(allow_none=True) + bc = HexBinary(allow_none=True) + fc = HexBinary(allow_none=True) + i = Bool(allow_none=True) + un = Bool(allow_none=True) + st = Bool(allow_none=True) + b = Bool(allow_none=True) + + __elements__ = ('tpls', 'x') + + def __init__(self, + tpls=None, + x=(), + v=None, + u=None, + f=None, + c=None, + cp=None, + _in=None, + bc=None, + fc=None, + i=None, + un=None, + st=None, + b=None, + ): + self.tpls = tpls + self.x = x + self.v = v + self.u = u + self.f = f + self.c = c + self.cp = cp + self._in = _in + self.bc = bc + self.fc = fc + self.i = i + self.un = un + self.st = st + self.b = b + + +class Boolean(Serialisable): + + tagname = "b" + + x = Sequence(expected_type=Index) + v = Bool() + u = Bool(allow_none=True) + f = Bool(allow_none=True) + c = String(allow_none=True) + cp = Integer(allow_none=True) + + __elements__ = ('x',) + + def __init__(self, + x=(), + v=None, + u=None, + f=None, + c=None, + cp=None, + ): + self.x = x + self.v = v + self.u = u + self.f = f + self.c = c + self.cp = cp + + +class Text(Serialisable): + + tagname = "s" + + tpls = Sequence(expected_type=TupleList) + x = Sequence(expected_type=Index) + v = String() + u = Bool(allow_none=True) + f = Bool(allow_none=True) + c = String(allow_none=True) + cp = Integer(allow_none=True) + _in = Integer(allow_none=True) + bc = HexBinary(allow_none=True) + fc = HexBinary(allow_none=True) + i = Bool(allow_none=True) + un = Bool(allow_none=True) + st = Bool(allow_none=True) + b = Bool(allow_none=True) + + __elements__ = ('tpls', 'x') + + def __init__(self, + tpls=(), + x=(), + v=None, + u=None, + f=None, + c=None, + cp=None, + _in=None, + bc=None, + fc=None, + i=None, + un=None, + st=None, + b=None, + ): + self.tpls = tpls + self.x = x + self.v = v + self.u = u + self.f = f + self.c = c + self.cp = cp + self._in = _in + self.bc = bc + self.fc = fc + self.i = i + self.un = un + self.st = st + self.b = b + + +class DateTimeField(Serialisable): + + tagname = "d" + + x = Sequence(expected_type=Index) + v = DateTime() + u = Bool(allow_none=True) + f = Bool(allow_none=True) + c = String(allow_none=True) + cp = Integer(allow_none=True) + + __elements__ = ('x',) + + def __init__(self, + x=(), + v=None, + u=None, + f=None, + c=None, + cp=None, + ): + self.x = x + self.v = v + self.u = u + self.f = f + self.c = c + self.cp = cp diff --git a/.venv/Lib/site-packages/openpyxl/pivot/record.py b/.venv/Lib/site-packages/openpyxl/pivot/record.py new file mode 100644 index 00000000..db3f8f7f --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/pivot/record.py @@ -0,0 +1,111 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Integer, + Sequence, +) +from openpyxl.descriptors.sequence import ( + MultiSequence, + MultiSequencePart, +) +from openpyxl.descriptors.excel import ExtensionList +from openpyxl.descriptors.nested import ( + NestedInteger, + NestedBool, +) + +from openpyxl.xml.constants import SHEET_MAIN_NS +from openpyxl.xml.functions import tostring + +from .fields import ( + Boolean, + Error, + Missing, + Number, + Text, + TupleList, + DateTimeField, + Index, +) + + +class Record(Serialisable): + + tagname = "r" + + _fields = MultiSequence() + m = MultiSequencePart(expected_type=Missing, store="_fields") + n = MultiSequencePart(expected_type=Number, store="_fields") + b = MultiSequencePart(expected_type=Boolean, store="_fields") + e = MultiSequencePart(expected_type=Error, store="_fields") + s = MultiSequencePart(expected_type=Text, store="_fields") + d = MultiSequencePart(expected_type=DateTimeField, store="_fields") + x = MultiSequencePart(expected_type=Index, store="_fields") + + + def __init__(self, + _fields=(), + m=None, + n=None, + b=None, + e=None, + s=None, + d=None, + x=None, + ): + self._fields = _fields + + +class RecordList(Serialisable): + + mime_type = "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheRecords+xml" + rel_type = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotCacheRecords" + _id = 1 + _path = "/xl/pivotCache/pivotCacheRecords{0}.xml" + + tagname ="pivotCacheRecords" + + r = Sequence(expected_type=Record, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('r', ) + __attrs__ = ('count', ) + + def __init__(self, + count=None, + r=(), + extLst=None, + ): + self.r = r + self.extLst = extLst + + + @property + def count(self): + return len(self.r) + + + def to_tree(self): + tree = super(RecordList, self).to_tree() + tree.set("xmlns", SHEET_MAIN_NS) + return tree + + + @property + def path(self): + return self._path.format(self._id) + + + def _write(self, archive, manifest): + """ + Write to zipfile and update manifest + """ + xml = tostring(self.to_tree()) + archive.writestr(self.path[1:], xml) + manifest.append(self) + + + def _write_rels(self, archive, manifest): + pass diff --git a/.venv/Lib/site-packages/openpyxl/pivot/table.py b/.venv/Lib/site-packages/openpyxl/pivot/table.py new file mode 100644 index 00000000..10045816 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/pivot/table.py @@ -0,0 +1,1178 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Integer, + NoneSet, + Set, + Float, + Bool, + DateTime, + String, + Alias, + Bool, + Sequence, +) + +from openpyxl.descriptors.excel import ExtensionList, Relation +from openpyxl.descriptors.nested import NestedInteger +from openpyxl.descriptors.sequence import NestedSequence +from openpyxl.xml.constants import SHEET_MAIN_NS +from openpyxl.xml.functions import tostring +from openpyxl.packaging.relationship import ( + RelationshipList, + Relationship, + get_rels_path +) +from .fields import Index + +from openpyxl.worksheet.filters import ( + AutoFilter, + CellRange, + ColorFilter, + CustomFilter, + CustomFilters, + DateGroupItem, + DynamicFilter, + FilterColumn, + Filters, + IconFilter, + SortCondition, + SortState, + Top10, +) + + +class HierarchyUsage(Serialisable): + + tagname = "hierarchyUsage" + + hierarchyUsage = Integer() + + def __init__(self, + hierarchyUsage=None, + ): + self.hierarchyUsage = hierarchyUsage + + +class ColHierarchiesUsage(Serialisable): + + tagname = "colHierarchiesUsage" + + colHierarchyUsage = Sequence(expected_type=HierarchyUsage, ) + + __elements__ = ('colHierarchyUsage',) + __attrs__ = ('count', ) + + def __init__(self, + count=None, + colHierarchyUsage=(), + ): + self.colHierarchyUsage = colHierarchyUsage + + + @property + def count(self): + return len(self.colHierarchyUsage) + + +class RowHierarchiesUsage(Serialisable): + + tagname = "rowHierarchiesUsage" + + rowHierarchyUsage = Sequence(expected_type=HierarchyUsage, ) + + __elements__ = ('rowHierarchyUsage',) + __attrs__ = ('count', ) + + def __init__(self, + count=None, + rowHierarchyUsage=(), + ): + self.rowHierarchyUsage = rowHierarchyUsage + + @property + def count(self): + return len(self.rowHierarchyUsage) + + +class PivotFilter(Serialisable): + + tagname = "filter" + + fld = Integer() + mpFld = Integer(allow_none=True) + type = Set(values=(['unknown', 'count', 'percent', 'sum', 'captionEqual', + 'captionNotEqual', 'captionBeginsWith', 'captionNotBeginsWith', + 'captionEndsWith', 'captionNotEndsWith', 'captionContains', + 'captionNotContains', 'captionGreaterThan', 'captionGreaterThanOrEqual', + 'captionLessThan', 'captionLessThanOrEqual', 'captionBetween', + 'captionNotBetween', 'valueEqual', 'valueNotEqual', 'valueGreaterThan', + 'valueGreaterThanOrEqual', 'valueLessThan', 'valueLessThanOrEqual', + 'valueBetween', 'valueNotBetween', 'dateEqual', 'dateNotEqual', + 'dateOlderThan', 'dateOlderThanOrEqual', 'dateNewerThan', + 'dateNewerThanOrEqual', 'dateBetween', 'dateNotBetween', 'tomorrow', + 'today', 'yesterday', 'nextWeek', 'thisWeek', 'lastWeek', 'nextMonth', + 'thisMonth', 'lastMonth', 'nextQuarter', 'thisQuarter', 'lastQuarter', + 'nextYear', 'thisYear', 'lastYear', 'yearToDate', 'Q1', 'Q2', 'Q3', 'Q4', + 'M1', 'M2', 'M3', 'M4', 'M5', 'M6', 'M7', 'M8', 'M9', 'M10', 'M11', + 'M12'])) + evalOrder = Integer(allow_none=True) + id = Integer() + iMeasureHier = Integer(allow_none=True) + iMeasureFld = Integer(allow_none=True) + name = String(allow_none=True) + description = String(allow_none=True) + stringValue1 = String(allow_none=True) + stringValue2 = String(allow_none=True) + autoFilter = Typed(expected_type=AutoFilter, ) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('autoFilter',) + + def __init__(self, + fld=None, + mpFld=None, + type=None, + evalOrder=None, + id=None, + iMeasureHier=None, + iMeasureFld=None, + name=None, + description=None, + stringValue1=None, + stringValue2=None, + autoFilter=None, + extLst=None, + ): + self.fld = fld + self.mpFld = mpFld + self.type = type + self.evalOrder = evalOrder + self.id = id + self.iMeasureHier = iMeasureHier + self.iMeasureFld = iMeasureFld + self.name = name + self.description = description + self.stringValue1 = stringValue1 + self.stringValue2 = stringValue2 + self.autoFilter = autoFilter + + +class PivotFilters(Serialisable): + + count = Integer() + filter = Typed(expected_type=PivotFilter, allow_none=True) + + __elements__ = ('filter',) + + def __init__(self, + count=None, + filter=None, + ): + self.filter = filter + + +class PivotTableStyle(Serialisable): + + tagname = "pivotTableStyleInfo" + + name = String(allow_none=True) + showRowHeaders = Bool() + showColHeaders = Bool() + showRowStripes = Bool() + showColStripes = Bool() + showLastColumn = Bool() + + def __init__(self, + name=None, + showRowHeaders=None, + showColHeaders=None, + showRowStripes=None, + showColStripes=None, + showLastColumn=None, + ): + self.name = name + self.showRowHeaders = showRowHeaders + self.showColHeaders = showColHeaders + self.showRowStripes = showRowStripes + self.showColStripes = showColStripes + self.showLastColumn = showLastColumn + + +class MemberList(Serialisable): + + tagname = "members" + + level = Integer(allow_none=True) + member = NestedSequence(expected_type=String, attribute="name") + + __elements__ = ('member',) + + def __init__(self, + count=None, + level=None, + member=(), + ): + self.level = level + self.member = member + + @property + def count(self): + return len(self.member) + + +class MemberProperty(Serialisable): + + tagname = "mps" + + name = String(allow_none=True) + showCell = Bool(allow_none=True) + showTip = Bool(allow_none=True) + showAsCaption = Bool(allow_none=True) + nameLen = Integer(allow_none=True) + pPos = Integer(allow_none=True) + pLen = Integer(allow_none=True) + level = Integer(allow_none=True) + field = Integer() + + def __init__(self, + name=None, + showCell=None, + showTip=None, + showAsCaption=None, + nameLen=None, + pPos=None, + pLen=None, + level=None, + field=None, + ): + self.name = name + self.showCell = showCell + self.showTip = showTip + self.showAsCaption = showAsCaption + self.nameLen = nameLen + self.pPos = pPos + self.pLen = pLen + self.level = level + self.field = field + + +class PivotHierarchy(Serialisable): + + tagname = "pivotHierarchy" + + outline = Bool() + multipleItemSelectionAllowed = Bool() + subtotalTop = Bool() + showInFieldList = Bool() + dragToRow = Bool() + dragToCol = Bool() + dragToPage = Bool() + dragToData = Bool() + dragOff = Bool() + includeNewItemsInFilter = Bool() + caption = String(allow_none=True) + mps = NestedSequence(expected_type=MemberProperty, count=True) + members = Typed(expected_type=MemberList, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('mps', 'members',) + + def __init__(self, + outline=None, + multipleItemSelectionAllowed=None, + subtotalTop=None, + showInFieldList=None, + dragToRow=None, + dragToCol=None, + dragToPage=None, + dragToData=None, + dragOff=None, + includeNewItemsInFilter=None, + caption=None, + mps=(), + members=None, + extLst=None, + ): + self.outline = outline + self.multipleItemSelectionAllowed = multipleItemSelectionAllowed + self.subtotalTop = subtotalTop + self.showInFieldList = showInFieldList + self.dragToRow = dragToRow + self.dragToCol = dragToCol + self.dragToPage = dragToPage + self.dragToData = dragToData + self.dragOff = dragOff + self.includeNewItemsInFilter = includeNewItemsInFilter + self.caption = caption + self.mps = mps + self.members = members + self.extLst = extLst + + +class Reference(Serialisable): + + tagname = "reference" + + field = Integer(allow_none=True) + selected = Bool(allow_none=True) + byPosition = Bool(allow_none=True) + relative = Bool(allow_none=True) + defaultSubtotal = Bool(allow_none=True) + sumSubtotal = Bool(allow_none=True) + countASubtotal = Bool(allow_none=True) + avgSubtotal = Bool(allow_none=True) + maxSubtotal = Bool(allow_none=True) + minSubtotal = Bool(allow_none=True) + productSubtotal = Bool(allow_none=True) + countSubtotal = Bool(allow_none=True) + stdDevSubtotal = Bool(allow_none=True) + stdDevPSubtotal = Bool(allow_none=True) + varSubtotal = Bool(allow_none=True) + varPSubtotal = Bool(allow_none=True) + x = NestedInteger(allow_none=True, attribute="v") + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('x',) + + def __init__(self, + field=None, + count=None, + selected=None, + byPosition=None, + relative=None, + defaultSubtotal=None, + sumSubtotal=None, + countASubtotal=None, + avgSubtotal=None, + maxSubtotal=None, + minSubtotal=None, + productSubtotal=None, + countSubtotal=None, + stdDevSubtotal=None, + stdDevPSubtotal=None, + varSubtotal=None, + varPSubtotal=None, + x=None, + extLst=None, + ): + self.field = field + self.selected = selected + self.byPosition = byPosition + self.relative = relative + self.defaultSubtotal = defaultSubtotal + self.sumSubtotal = sumSubtotal + self.countASubtotal = countASubtotal + self.avgSubtotal = avgSubtotal + self.maxSubtotal = maxSubtotal + self.minSubtotal = minSubtotal + self.productSubtotal = productSubtotal + self.countSubtotal = countSubtotal + self.stdDevSubtotal = stdDevSubtotal + self.stdDevPSubtotal = stdDevPSubtotal + self.varSubtotal = varSubtotal + self.varPSubtotal = varPSubtotal + self.x = x + + + @property + def count(self): + return len(self.field) + + +class PivotArea(Serialisable): + + tagname = "pivotArea" + + references = NestedSequence(expected_type=Reference, count=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + field = Integer(allow_none=True) + type = NoneSet(values=(['normal', 'data', 'all', 'origin', 'button', + 'topEnd', 'topRight'])) + dataOnly = Bool(allow_none=True) + labelOnly = Bool(allow_none=True) + grandRow = Bool(allow_none=True) + grandCol = Bool(allow_none=True) + cacheIndex = Bool(allow_none=True) + outline = Bool(allow_none=True) + offset = String(allow_none=True) + collapsedLevelsAreSubtotals = Bool(allow_none=True) + axis = NoneSet(values=(['axisRow', 'axisCol', 'axisPage', 'axisValues'])) + fieldPosition = Integer(allow_none=True) + + __elements__ = ('references',) + + def __init__(self, + references=(), + extLst=None, + field=None, + type="normal", + dataOnly=True, + labelOnly=None, + grandRow=None, + grandCol=None, + cacheIndex=None, + outline=True, + offset=None, + collapsedLevelsAreSubtotals=None, + axis=None, + fieldPosition=None, + ): + self.references = references + self.extLst = extLst + self.field = field + self.type = type + self.dataOnly = dataOnly + self.labelOnly = labelOnly + self.grandRow = grandRow + self.grandCol = grandCol + self.cacheIndex = cacheIndex + self.outline = outline + self.offset = offset + self.collapsedLevelsAreSubtotals = collapsedLevelsAreSubtotals + self.axis = axis + self.fieldPosition = fieldPosition + + +class ChartFormat(Serialisable): + + tagname = "chartFormat" + + chart = Integer() + format = Integer() + series = Bool() + pivotArea = Typed(expected_type=PivotArea, ) + + __elements__ = ('pivotArea',) + + def __init__(self, + chart=None, + format=None, + series=None, + pivotArea=None, + ): + self.chart = chart + self.format = format + self.series = series + self.pivotArea = pivotArea + + +class ConditionalFormat(Serialisable): + + tagname = "conditionalFormat" + + scope = Set(values=(['selection', 'data', 'field'])) + type = NoneSet(values=(['all', 'row', 'column'])) + priority = Integer() + pivotAreas = NestedSequence(expected_type=PivotArea) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('pivotAreas',) + + def __init__(self, + scope="selection", + type=None, + priority=None, + pivotAreas=(), + extLst=None, + ): + self.scope = scope + self.type = type + self.priority = priority + self.pivotAreas = pivotAreas + self.extLst = extLst + + +class Format(Serialisable): + + tagname = "format" + + action = NoneSet(values=(['blank', 'formatting', 'drill', 'formula'])) + dxfId = Integer(allow_none=True) + pivotArea = Typed(expected_type=PivotArea, ) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('pivotArea',) + + def __init__(self, + action="formatting", + dxfId=None, + pivotArea=None, + extLst=None, + ): + self.action = action + self.dxfId = dxfId + self.pivotArea = pivotArea + self.extLst = extLst + + +class DataField(Serialisable): + + tagname = "dataField" + + name = String(allow_none=True) + fld = Integer() + subtotal = Set(values=(['average', 'count', 'countNums', 'max', 'min', + 'product', 'stdDev', 'stdDevp', 'sum', 'var', 'varp'])) + showDataAs = Set(values=(['normal', 'difference', 'percent', + 'percentDiff', 'runTotal', 'percentOfRow', 'percentOfCol', + 'percentOfTotal', 'index'])) + baseField = Integer() + baseItem = Integer() + numFmtId = Integer(allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = () + + + def __init__(self, + name=None, + fld=None, + subtotal="sum", + showDataAs="normal", + baseField=-1, + baseItem=1048832, + numFmtId=None, + extLst=None, + ): + self.name = name + self.fld = fld + self.subtotal = subtotal + self.showDataAs = showDataAs + self.baseField = baseField + self.baseItem = baseItem + self.numFmtId = numFmtId + self.extLst = extLst + + +class PageField(Serialisable): + + tagname = "pageField" + + fld = Integer() + item = Integer(allow_none=True) + hier = Integer(allow_none=True) + name = String(allow_none=True) + cap = String(allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = () + + def __init__(self, + fld=None, + item=None, + hier=None, + name=None, + cap=None, + extLst=None, + ): + self.fld = fld + self.item = item + self.hier = hier + self.name = name + self.cap = cap + self.extLst = extLst + + +class RowColItem(Serialisable): + + tagname = "i" + + t = Set(values=(['data', 'default', 'sum', 'countA', 'avg', 'max', 'min', + 'product', 'count', 'stdDev', 'stdDevP', 'var', 'varP', 'grand', + 'blank'])) + r = Integer() + i = Integer() + x = Sequence(expected_type=Index, attribute="v") + + __elements__ = ('x',) + + def __init__(self, + t="data", + r=0, + i=0, + x=(), + ): + self.t = t + self.r = r + self.i = i + self.x = x + + +class RowColField(Serialisable): + + tagname = "field" + + x = Integer() + + def __init__(self, + x=None, + ): + self.x = x + + +class AutoSortScope(Serialisable): + + pivotArea = Typed(expected_type=PivotArea, ) + + __elements__ = ('pivotArea',) + + def __init__(self, + pivotArea=None, + ): + self.pivotArea = pivotArea + + +class FieldItem(Serialisable): + + tagname = "item" + + n = String(allow_none=True) + t = Set(values=(['data', 'default', 'sum', 'countA', 'avg', 'max', 'min', + 'product', 'count', 'stdDev', 'stdDevP', 'var', 'varP', 'grand', + 'blank'])) + h = Bool(allow_none=True) + s = Bool(allow_none=True) + sd = Bool(allow_none=True) + f = Bool(allow_none=True) + m = Bool(allow_none=True) + c = Bool(allow_none=True) + x = Integer(allow_none=True) + d = Bool(allow_none=True) + e = Bool(allow_none=True) + + def __init__(self, + n=None, + t="data", + h=None, + s=None, + sd=True, + f=None, + m=None, + c=None, + x=None, + d=None, + e=None, + ): + self.n = n + self.t = t + self.h = h + self.s = s + self.sd = sd + self.f = f + self.m = m + self.c = c + self.x = x + self.d = d + self.e = e + + +class PivotField(Serialisable): + + tagname = "pivotField" + + items = NestedSequence(expected_type=FieldItem, count=True) + autoSortScope = Typed(expected_type=AutoSortScope, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + name = String(allow_none=True) + axis = NoneSet(values=(['axisRow', 'axisCol', 'axisPage', 'axisValues'])) + dataField = Bool(allow_none=True) + subtotalCaption = String(allow_none=True) + showDropDowns = Bool(allow_none=True) + hiddenLevel = Bool(allow_none=True) + uniqueMemberProperty = String(allow_none=True) + compact = Bool(allow_none=True) + allDrilled = Bool(allow_none=True) + numFmtId = Integer(allow_none=True) + outline = Bool(allow_none=True) + subtotalTop = Bool(allow_none=True) + dragToRow = Bool(allow_none=True) + dragToCol = Bool(allow_none=True) + multipleItemSelectionAllowed = Bool(allow_none=True) + dragToPage = Bool(allow_none=True) + dragToData = Bool(allow_none=True) + dragOff = Bool(allow_none=True) + showAll = Bool(allow_none=True) + insertBlankRow = Bool(allow_none=True) + serverField = Bool(allow_none=True) + insertPageBreak = Bool(allow_none=True) + autoShow = Bool(allow_none=True) + topAutoShow = Bool(allow_none=True) + hideNewItems = Bool(allow_none=True) + measureFilter = Bool(allow_none=True) + includeNewItemsInFilter = Bool(allow_none=True) + itemPageCount = Integer(allow_none=True) + sortType = Set(values=(['manual', 'ascending', 'descending'])) + dataSourceSort = Bool(allow_none=True) + nonAutoSortDefault = Bool(allow_none=True) + rankBy = Integer(allow_none=True) + defaultSubtotal = Bool(allow_none=True) + sumSubtotal = Bool(allow_none=True) + countASubtotal = Bool(allow_none=True) + avgSubtotal = Bool(allow_none=True) + maxSubtotal = Bool(allow_none=True) + minSubtotal = Bool(allow_none=True) + productSubtotal = Bool(allow_none=True) + countSubtotal = Bool(allow_none=True) + stdDevSubtotal = Bool(allow_none=True) + stdDevPSubtotal = Bool(allow_none=True) + varSubtotal = Bool(allow_none=True) + varPSubtotal = Bool(allow_none=True) + showPropCell = Bool(allow_none=True) + showPropTip = Bool(allow_none=True) + showPropAsCaption = Bool(allow_none=True) + defaultAttributeDrillState = Bool(allow_none=True) + + __elements__ = ('items', 'autoSortScope',) + + def __init__(self, + items=(), + autoSortScope=None, + name=None, + axis=None, + dataField=None, + subtotalCaption=None, + showDropDowns=True, + hiddenLevel=None, + uniqueMemberProperty=None, + compact=True, + allDrilled=None, + numFmtId=None, + outline=True, + subtotalTop=True, + dragToRow=True, + dragToCol=True, + multipleItemSelectionAllowed=None, + dragToPage=True, + dragToData=True, + dragOff=True, + showAll=True, + insertBlankRow=None, + serverField=None, + insertPageBreak=None, + autoShow=None, + topAutoShow=True, + hideNewItems=None, + measureFilter=None, + includeNewItemsInFilter=None, + itemPageCount=10, + sortType="manual", + dataSourceSort=None, + nonAutoSortDefault=None, + rankBy=None, + defaultSubtotal=True, + sumSubtotal=None, + countASubtotal=None, + avgSubtotal=None, + maxSubtotal=None, + minSubtotal=None, + productSubtotal=None, + countSubtotal=None, + stdDevSubtotal=None, + stdDevPSubtotal=None, + varSubtotal=None, + varPSubtotal=None, + showPropCell=None, + showPropTip=None, + showPropAsCaption=None, + defaultAttributeDrillState=None, + extLst=None, + ): + self.items = items + self.autoSortScope = autoSortScope + self.name = name + self.axis = axis + self.dataField = dataField + self.subtotalCaption = subtotalCaption + self.showDropDowns = showDropDowns + self.hiddenLevel = hiddenLevel + self.uniqueMemberProperty = uniqueMemberProperty + self.compact = compact + self.allDrilled = allDrilled + self.numFmtId = numFmtId + self.outline = outline + self.subtotalTop = subtotalTop + self.dragToRow = dragToRow + self.dragToCol = dragToCol + self.multipleItemSelectionAllowed = multipleItemSelectionAllowed + self.dragToPage = dragToPage + self.dragToData = dragToData + self.dragOff = dragOff + self.showAll = showAll + self.insertBlankRow = insertBlankRow + self.serverField = serverField + self.insertPageBreak = insertPageBreak + self.autoShow = autoShow + self.topAutoShow = topAutoShow + self.hideNewItems = hideNewItems + self.measureFilter = measureFilter + self.includeNewItemsInFilter = includeNewItemsInFilter + self.itemPageCount = itemPageCount + self.sortType = sortType + self.dataSourceSort = dataSourceSort + self.nonAutoSortDefault = nonAutoSortDefault + self.rankBy = rankBy + self.defaultSubtotal = defaultSubtotal + self.sumSubtotal = sumSubtotal + self.countASubtotal = countASubtotal + self.avgSubtotal = avgSubtotal + self.maxSubtotal = maxSubtotal + self.minSubtotal = minSubtotal + self.productSubtotal = productSubtotal + self.countSubtotal = countSubtotal + self.stdDevSubtotal = stdDevSubtotal + self.stdDevPSubtotal = stdDevPSubtotal + self.varSubtotal = varSubtotal + self.varPSubtotal = varPSubtotal + self.showPropCell = showPropCell + self.showPropTip = showPropTip + self.showPropAsCaption = showPropAsCaption + self.defaultAttributeDrillState = defaultAttributeDrillState + + +class Location(Serialisable): + + tagname = "location" + + ref = String() + firstHeaderRow = Integer() + firstDataRow = Integer() + firstDataCol = Integer() + rowPageCount = Integer(allow_none=True) + colPageCount = Integer(allow_none=True) + + def __init__(self, + ref=None, + firstHeaderRow=None, + firstDataRow=None, + firstDataCol=None, + rowPageCount=None, + colPageCount=None, + ): + self.ref = ref + self.firstHeaderRow = firstHeaderRow + self.firstDataRow = firstDataRow + self.firstDataCol = firstDataCol + self.rowPageCount = rowPageCount + self.colPageCount = colPageCount + + +class TableDefinition(Serialisable): + + mime_type = "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotTable+xml" + rel_type = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotTable" + _id = 1 + _path = "/xl/pivotTables/pivotTable{0}.xml" + + tagname = "pivotTableDefinition" + cache = None + + name = String() + cacheId = Integer() + dataOnRows = Bool() + dataPosition = Integer(allow_none=True) + dataCaption = String() + grandTotalCaption = String(allow_none=True) + errorCaption = String(allow_none=True) + showError = Bool() + missingCaption = String(allow_none=True) + showMissing = Bool() + pageStyle = String(allow_none=True) + pivotTableStyle = String(allow_none=True) + vacatedStyle = String(allow_none=True) + tag = String(allow_none=True) + updatedVersion = Integer() + minRefreshableVersion = Integer() + asteriskTotals = Bool() + showItems = Bool() + editData = Bool() + disableFieldList = Bool() + showCalcMbrs = Bool() + visualTotals = Bool() + showMultipleLabel = Bool() + showDataDropDown = Bool() + showDrill = Bool() + printDrill = Bool() + showMemberPropertyTips = Bool() + showDataTips = Bool() + enableWizard = Bool() + enableDrill = Bool() + enableFieldProperties = Bool() + preserveFormatting = Bool() + useAutoFormatting = Bool() + pageWrap = Integer() + pageOverThenDown = Bool() + subtotalHiddenItems = Bool() + rowGrandTotals = Bool() + colGrandTotals = Bool() + fieldPrintTitles = Bool() + itemPrintTitles = Bool() + mergeItem = Bool() + showDropZones = Bool() + createdVersion = Integer() + indent = Integer() + showEmptyRow = Bool() + showEmptyCol = Bool() + showHeaders = Bool() + compact = Bool() + outline = Bool() + outlineData = Bool() + compactData = Bool() + published = Bool() + gridDropZones = Bool() + immersive = Bool() + multipleFieldFilters = Bool() + chartFormat = Integer() + rowHeaderCaption = String(allow_none=True) + colHeaderCaption = String(allow_none=True) + fieldListSortAscending = Bool() + mdxSubqueries = Bool() + customListSort = Bool(allow_none=True) + autoFormatId = Integer(allow_none=True) + applyNumberFormats = Bool() + applyBorderFormats = Bool() + applyFontFormats = Bool() + applyPatternFormats = Bool() + applyAlignmentFormats = Bool() + applyWidthHeightFormats = Bool() + location = Typed(expected_type=Location, ) + pivotFields = NestedSequence(expected_type=PivotField, count=True) + rowFields = NestedSequence(expected_type=RowColField, count=True) + rowItems = NestedSequence(expected_type=RowColItem, count=True) + colFields = NestedSequence(expected_type=RowColField, count=True) + colItems = NestedSequence(expected_type=RowColItem, count=True) + pageFields = NestedSequence(expected_type=PageField, count=True) + dataFields = NestedSequence(expected_type=DataField, count=True) + formats = NestedSequence(expected_type=Format, count=True) + conditionalFormats = NestedSequence(expected_type=ConditionalFormat, count=True) + chartFormats = NestedSequence(expected_type=ChartFormat, count=True) + pivotHierarchies = NestedSequence(expected_type=PivotHierarchy, count=True) + pivotTableStyleInfo = Typed(expected_type=PivotTableStyle, allow_none=True) + filters = NestedSequence(expected_type=PivotFilter, count=True) + rowHierarchiesUsage = Typed(expected_type=RowHierarchiesUsage, allow_none=True) + colHierarchiesUsage = Typed(expected_type=ColHierarchiesUsage, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + id = Relation() + + __elements__ = ('location', 'pivotFields', 'rowFields', 'rowItems', + 'colFields', 'colItems', 'pageFields', 'dataFields', 'formats', + 'conditionalFormats', 'chartFormats', 'pivotHierarchies', + 'pivotTableStyleInfo', 'filters', 'rowHierarchiesUsage', + 'colHierarchiesUsage',) + + def __init__(self, + name=None, + cacheId=None, + dataOnRows=False, + dataPosition=None, + dataCaption=None, + grandTotalCaption=None, + errorCaption=None, + showError=False, + missingCaption=None, + showMissing=True, + pageStyle=None, + pivotTableStyle=None, + vacatedStyle=None, + tag=None, + updatedVersion=0, + minRefreshableVersion=0, + asteriskTotals=False, + showItems=True, + editData=False, + disableFieldList=False, + showCalcMbrs=True, + visualTotals=True, + showMultipleLabel=True, + showDataDropDown=True, + showDrill=True, + printDrill=False, + showMemberPropertyTips=True, + showDataTips=True, + enableWizard=True, + enableDrill=True, + enableFieldProperties=True, + preserveFormatting=True, + useAutoFormatting=False, + pageWrap=0, + pageOverThenDown=False, + subtotalHiddenItems=False, + rowGrandTotals=True, + colGrandTotals=True, + fieldPrintTitles=False, + itemPrintTitles=False, + mergeItem=False, + showDropZones=True, + createdVersion=0, + indent=1, + showEmptyRow=False, + showEmptyCol=False, + showHeaders=True, + compact=True, + outline=False, + outlineData=False, + compactData=True, + published=False, + gridDropZones=False, + immersive=True, + multipleFieldFilters=None, + chartFormat=0, + rowHeaderCaption=None, + colHeaderCaption=None, + fieldListSortAscending=None, + mdxSubqueries=None, + customListSort=None, + autoFormatId=None, + applyNumberFormats=False, + applyBorderFormats=False, + applyFontFormats=False, + applyPatternFormats=False, + applyAlignmentFormats=False, + applyWidthHeightFormats=False, + location=None, + pivotFields=(), + rowFields=(), + rowItems=(), + colFields=(), + colItems=(), + pageFields=(), + dataFields=(), + formats=(), + conditionalFormats=(), + chartFormats=(), + pivotHierarchies=(), + pivotTableStyleInfo=None, + filters=(), + rowHierarchiesUsage=None, + colHierarchiesUsage=None, + extLst=None, + id=None, + ): + self.name = name + self.cacheId = cacheId + self.dataOnRows = dataOnRows + self.dataPosition = dataPosition + self.dataCaption = dataCaption + self.grandTotalCaption = grandTotalCaption + self.errorCaption = errorCaption + self.showError = showError + self.missingCaption = missingCaption + self.showMissing = showMissing + self.pageStyle = pageStyle + self.pivotTableStyle = pivotTableStyle + self.vacatedStyle = vacatedStyle + self.tag = tag + self.updatedVersion = updatedVersion + self.minRefreshableVersion = minRefreshableVersion + self.asteriskTotals = asteriskTotals + self.showItems = showItems + self.editData = editData + self.disableFieldList = disableFieldList + self.showCalcMbrs = showCalcMbrs + self.visualTotals = visualTotals + self.showMultipleLabel = showMultipleLabel + self.showDataDropDown = showDataDropDown + self.showDrill = showDrill + self.printDrill = printDrill + self.showMemberPropertyTips = showMemberPropertyTips + self.showDataTips = showDataTips + self.enableWizard = enableWizard + self.enableDrill = enableDrill + self.enableFieldProperties = enableFieldProperties + self.preserveFormatting = preserveFormatting + self.useAutoFormatting = useAutoFormatting + self.pageWrap = pageWrap + self.pageOverThenDown = pageOverThenDown + self.subtotalHiddenItems = subtotalHiddenItems + self.rowGrandTotals = rowGrandTotals + self.colGrandTotals = colGrandTotals + self.fieldPrintTitles = fieldPrintTitles + self.itemPrintTitles = itemPrintTitles + self.mergeItem = mergeItem + self.showDropZones = showDropZones + self.createdVersion = createdVersion + self.indent = indent + self.showEmptyRow = showEmptyRow + self.showEmptyCol = showEmptyCol + self.showHeaders = showHeaders + self.compact = compact + self.outline = outline + self.outlineData = outlineData + self.compactData = compactData + self.published = published + self.gridDropZones = gridDropZones + self.immersive = immersive + self.multipleFieldFilters = multipleFieldFilters + self.chartFormat = chartFormat + self.rowHeaderCaption = rowHeaderCaption + self.colHeaderCaption = colHeaderCaption + self.fieldListSortAscending = fieldListSortAscending + self.mdxSubqueries = mdxSubqueries + self.customListSort = customListSort + self.autoFormatId = autoFormatId + self.applyNumberFormats = applyNumberFormats + self.applyBorderFormats = applyBorderFormats + self.applyFontFormats = applyFontFormats + self.applyPatternFormats = applyPatternFormats + self.applyAlignmentFormats = applyAlignmentFormats + self.applyWidthHeightFormats = applyWidthHeightFormats + self.location = location + self.pivotFields = pivotFields + self.rowFields = rowFields + self.rowItems = rowItems + self.colFields = colFields + self.colItems = colItems + self.pageFields = pageFields + self.dataFields = dataFields + self.formats = formats + self.conditionalFormats = conditionalFormats + self.chartFormats = chartFormats + self.pivotHierarchies = pivotHierarchies + self.pivotTableStyleInfo = pivotTableStyleInfo + self.filters = filters + self.rowHierarchiesUsage = rowHierarchiesUsage + self.colHierarchiesUsage = colHierarchiesUsage + self.extLst = extLst + self.id = id + + + def to_tree(self): + tree = super(TableDefinition, self).to_tree() + tree.set("xmlns", SHEET_MAIN_NS) + return tree + + + @property + def path(self): + return self._path.format(self._id) + + + def _write(self, archive, manifest): + """ + Add to zipfile and update manifest + """ + self._write_rels(archive, manifest) + xml = tostring(self.to_tree()) + archive.writestr(self.path[1:], xml) + manifest.append(self) + + + def _write_rels(self, archive, manifest): + """ + Write the relevant child objects and add links + """ + if self.cache is None: + return + + rels = RelationshipList() + r = Relationship(Type=self.cache.rel_type, Target=self.cache.path) + rels.append(r) + self.id = r.id + if self.cache.path[1:] not in archive.namelist(): + self.cache._write(archive, manifest) + + path = get_rels_path(self.path) + xml = tostring(rels.to_tree()) + archive.writestr(path[1:], xml) diff --git a/.venv/Lib/site-packages/openpyxl/reader/__init__.py b/.venv/Lib/site-packages/openpyxl/reader/__init__.py new file mode 100644 index 00000000..a5a5eaa0 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/reader/__init__.py @@ -0,0 +1 @@ +# Copyright (c) 2010-2022 openpyxl diff --git a/.venv/Lib/site-packages/openpyxl/reader/drawings.py b/.venv/Lib/site-packages/openpyxl/reader/drawings.py new file mode 100644 index 00000000..531231c0 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/reader/drawings.py @@ -0,0 +1,63 @@ + +# Copyright (c) 2010-2022 openpyxl + + +from io import BytesIO +from warnings import warn + +from openpyxl.xml.functions import fromstring +from openpyxl.xml.constants import IMAGE_NS +from openpyxl.packaging.relationship import get_rel, get_rels_path, get_dependents +from openpyxl.drawing.spreadsheet_drawing import SpreadsheetDrawing +from openpyxl.drawing.image import Image, PILImage +from openpyxl.chart.chartspace import ChartSpace +from openpyxl.chart.reader import read_chart + + +def find_images(archive, path): + """ + Given the path to a drawing file extract charts and images + + Ingore errors due to unsupported parts of DrawingML + """ + + src = archive.read(path) + tree = fromstring(src) + try: + drawing = SpreadsheetDrawing.from_tree(tree) + except TypeError: + warn("DrawingML support is incomplete and limited to charts and images only. Shapes and drawings will be lost.") + return [], [] + + rels_path = get_rels_path(path) + deps = [] + if rels_path in archive.namelist(): + deps = get_dependents(archive, rels_path) + + charts = [] + for rel in drawing._chart_rels: + cs = get_rel(archive, deps, rel.id, ChartSpace) + chart = read_chart(cs) + chart.anchor = rel.anchor + charts.append(chart) + + images = [] + if not PILImage: # Pillow not installed, drop images + return charts, images + + for rel in drawing._blip_rels: + dep = deps[rel.embed] + if dep.Type == IMAGE_NS: + try: + image = Image(BytesIO(archive.read(dep.target))) + except OSError: + msg = "The image {0} will be removed because it cannot be read".format(dep.target) + warn(msg) + continue + if image.format.upper() == "WMF": # cannot save + msg = "{0} image format is not supported so the image is being dropped".format(image.format) + warn(msg) + continue + image.anchor = rel.anchor + images.append(image) + return charts, images diff --git a/.venv/Lib/site-packages/openpyxl/reader/excel.py b/.venv/Lib/site-packages/openpyxl/reader/excel.py new file mode 100644 index 00000000..d2272c92 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/reader/excel.py @@ -0,0 +1,318 @@ +# Copyright (c) 2010-2022 openpyxl + + +"""Read an xlsx file into Python""" + +# Python stdlib imports +from zipfile import ZipFile, ZIP_DEFLATED, BadZipfile +from sys import exc_info +from io import BytesIO +import os.path +import warnings + +from openpyxl.pivot.table import TableDefinition + +# Allow blanket setting of KEEP_VBA for testing +try: + from ..tests import KEEP_VBA +except ImportError: + KEEP_VBA = False + + +# package imports +from openpyxl.utils.exceptions import InvalidFileException +from openpyxl.xml.constants import ( + ARC_SHARED_STRINGS, + ARC_CORE, + ARC_CONTENT_TYPES, + ARC_WORKBOOK, + ARC_THEME, + COMMENTS_NS, + SHARED_STRINGS, + EXTERNAL_LINK, + XLTM, + XLTX, + XLSM, + XLSX, +) +from openpyxl.cell import MergedCell +from openpyxl.comments.comment_sheet import CommentSheet + +from .strings import read_string_table +from .workbook import WorkbookParser +from openpyxl.styles.stylesheet import apply_stylesheet + +from openpyxl.packaging.core import DocumentProperties +from openpyxl.packaging.manifest import Manifest, Override + +from openpyxl.packaging.relationship import ( + RelationshipList, + get_dependents, + get_rels_path, +) + +from openpyxl.worksheet._read_only import ReadOnlyWorksheet +from openpyxl.worksheet._reader import WorksheetReader +from openpyxl.chartsheet import Chartsheet +from openpyxl.worksheet.table import Table +from openpyxl.drawing.spreadsheet_drawing import SpreadsheetDrawing + +from openpyxl.xml.functions import fromstring + +from .drawings import find_images + + +SUPPORTED_FORMATS = ('.xlsx', '.xlsm', '.xltx', '.xltm') + +def _validate_archive(filename): + """ + Does a first check whether filename is a string or a file-like + object. If it is a string representing a filename, a check is done + for supported formats by checking the given file-extension. If the + file-extension is not in SUPPORTED_FORMATS an InvalidFileException + will raised. Otherwise the filename (resp. file-like object) will + forwarded to zipfile.ZipFile returning a ZipFile-Instance. + """ + is_file_like = hasattr(filename, 'read') + if not is_file_like: + file_format = os.path.splitext(filename)[-1].lower() + if file_format not in SUPPORTED_FORMATS: + if file_format == '.xls': + msg = ('openpyxl does not support the old .xls file format, ' + 'please use xlrd to read this file, or convert it to ' + 'the more recent .xlsx file format.') + elif file_format == '.xlsb': + msg = ('openpyxl does not support binary format .xlsb, ' + 'please convert this file to .xlsx format if you want ' + 'to open it with openpyxl') + else: + msg = ('openpyxl does not support %s file format, ' + 'please check you can open ' + 'it with Excel first. ' + 'Supported formats are: %s') % (file_format, + ','.join(SUPPORTED_FORMATS)) + raise InvalidFileException(msg) + + archive = ZipFile(filename, 'r') + return archive + + +def _find_workbook_part(package): + workbook_types = [XLTM, XLTX, XLSM, XLSX] + for ct in workbook_types: + part = package.find(ct) + if part: + return part + + # some applications reassign the default for application/xml + defaults = {p.ContentType for p in package.Default} + workbook_type = defaults & set(workbook_types) + if workbook_type: + return Override("/" + ARC_WORKBOOK, workbook_type.pop()) + + raise IOError("File contains no valid workbook part") + + +class ExcelReader: + + """ + Read an Excel package and dispatch the contents to the relevant modules + """ + + def __init__(self, fn, read_only=False, keep_vba=KEEP_VBA, + data_only=False, keep_links=True): + self.archive = _validate_archive(fn) + self.valid_files = self.archive.namelist() + self.read_only = read_only + self.keep_vba = keep_vba + self.data_only = data_only + self.keep_links = keep_links + self.shared_strings = [] + + + def read_manifest(self): + src = self.archive.read(ARC_CONTENT_TYPES) + root = fromstring(src) + self.package = Manifest.from_tree(root) + + + def read_strings(self): + ct = self.package.find(SHARED_STRINGS) + if ct is not None: + strings_path = ct.PartName[1:] + with self.archive.open(strings_path,) as src: + self.shared_strings = read_string_table(src) + + + def read_workbook(self): + wb_part = _find_workbook_part(self.package) + self.parser = WorkbookParser(self.archive, wb_part.PartName[1:], keep_links=self.keep_links) + self.parser.parse() + wb = self.parser.wb + wb._sheets = [] + wb._data_only = self.data_only + wb._read_only = self.read_only + wb.template = wb_part.ContentType in (XLTX, XLTM) + + # If are going to preserve the vba then attach a copy of the archive to the + # workbook so that is available for the save. + if self.keep_vba: + wb.vba_archive = ZipFile(BytesIO(), 'a', ZIP_DEFLATED) + for name in self.valid_files: + wb.vba_archive.writestr(name, self.archive.read(name)) + + if self.read_only: + wb._archive = self.archive + + self.wb = wb + + + def read_properties(self): + if ARC_CORE in self.valid_files: + src = fromstring(self.archive.read(ARC_CORE)) + self.wb.properties = DocumentProperties.from_tree(src) + + + def read_theme(self): + if ARC_THEME in self.valid_files: + self.wb.loaded_theme = self.archive.read(ARC_THEME) + + + def read_chartsheet(self, sheet, rel): + sheet_path = rel.target + rels_path = get_rels_path(sheet_path) + rels = [] + if rels_path in self.valid_files: + rels = get_dependents(self.archive, rels_path) + + with self.archive.open(sheet_path, "r") as src: + xml = src.read() + node = fromstring(xml) + cs = Chartsheet.from_tree(node) + cs._parent = self.wb + cs.title = sheet.name + self.wb._add_sheet(cs) + + drawings = rels.find(SpreadsheetDrawing._rel_type) + for rel in drawings: + charts, images = find_images(self.archive, rel.target) + for c in charts: + cs.add_chart(c) + + + def read_worksheets(self): + comment_warning = """Cell '{0}':{1} is part of a merged range but has a comment which will be removed because merged cells cannot contain any data.""" + for sheet, rel in self.parser.find_sheets(): + if rel.target not in self.valid_files: + continue + + if "chartsheet" in rel.Type: + self.read_chartsheet(sheet, rel) + continue + + rels_path = get_rels_path(rel.target) + rels = RelationshipList() + if rels_path in self.valid_files: + rels = get_dependents(self.archive, rels_path) + + if self.read_only: + ws = ReadOnlyWorksheet(self.wb, sheet.name, rel.target, self.shared_strings) + ws.sheet_state = sheet.state + self.wb._sheets.append(ws) + continue + else: + fh = self.archive.open(rel.target) + ws = self.wb.create_sheet(sheet.name) + ws._rels = rels + ws_parser = WorksheetReader(ws, fh, self.shared_strings, self.data_only) + ws_parser.bind_all() + + # assign any comments to cells + for r in rels.find(COMMENTS_NS): + src = self.archive.read(r.target) + comment_sheet = CommentSheet.from_tree(fromstring(src)) + for ref, comment in comment_sheet.comments: + try: + ws[ref].comment = comment + except AttributeError: + c = ws[ref] + if isinstance(c, MergedCell): + warnings.warn(comment_warning.format(ws.title, c.coordinate)) + continue + + # preserve link to VML file if VBA + if self.wb.vba_archive and ws.legacy_drawing: + ws.legacy_drawing = rels[ws.legacy_drawing].target + else: + ws.legacy_drawing = None + + for t in ws_parser.tables: + src = self.archive.read(t) + xml = fromstring(src) + table = Table.from_tree(xml) + ws.add_table(table) + + drawings = rels.find(SpreadsheetDrawing._rel_type) + for rel in drawings: + charts, images = find_images(self.archive, rel.target) + for c in charts: + ws.add_chart(c, c.anchor) + for im in images: + ws.add_image(im, im.anchor) + + pivot_rel = rels.find(TableDefinition.rel_type) + for r in pivot_rel: + pivot_path = r.Target + src = self.archive.read(pivot_path) + tree = fromstring(src) + pivot = TableDefinition.from_tree(tree) + pivot.cache = self.parser.pivot_caches[pivot.cacheId] + ws.add_pivot(pivot) + + ws.sheet_state = sheet.state + + + def read(self): + self.read_manifest() + self.read_strings() + self.read_workbook() + self.read_properties() + self.read_theme() + apply_stylesheet(self.archive, self.wb) + self.read_worksheets() + self.parser.assign_names() + if not self.read_only: + self.archive.close() + + +def load_workbook(filename, read_only=False, keep_vba=KEEP_VBA, + data_only=False, keep_links=True): + """Open the given filename and return the workbook + + :param filename: the path to open or a file-like object + :type filename: string or a file-like object open in binary mode c.f., :class:`zipfile.ZipFile` + + :param read_only: optimised for reading, content cannot be edited + :type read_only: bool + + :param keep_vba: preseve vba content (this does NOT mean you can use it) + :type keep_vba: bool + + :param data_only: controls whether cells with formulae have either the formula (default) or the value stored the last time Excel read the sheet + :type data_only: bool + + :param keep_links: whether links to external workbooks should be preserved. The default is True + :type keep_links: bool + + :rtype: :class:`openpyxl.workbook.Workbook` + + .. note:: + + When using lazy load, all worksheets will be :class:`openpyxl.worksheet.iter_worksheet.IterableWorksheet` + and the returned workbook will be read-only. + + """ + reader = ExcelReader(filename, read_only, keep_vba, + data_only, keep_links) + reader.read() + return reader.wb diff --git a/.venv/Lib/site-packages/openpyxl/reader/strings.py b/.venv/Lib/site-packages/openpyxl/reader/strings.py new file mode 100644 index 00000000..99018015 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/reader/strings.py @@ -0,0 +1,23 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.cell.text import Text + +from openpyxl.xml.functions import iterparse +from openpyxl.xml.constants import SHEET_MAIN_NS + + +def read_string_table(xml_source): + """Read in all shared strings in the table""" + + strings = [] + STRING_TAG = '{%s}si' % SHEET_MAIN_NS + + for _, node in iterparse(xml_source): + if node.tag == STRING_TAG: + text = Text.from_tree(node).content + text = text.replace('x005F_', '') + node.clear() + + strings.append(text) + + return strings diff --git a/.venv/Lib/site-packages/openpyxl/reader/workbook.py b/.venv/Lib/site-packages/openpyxl/reader/workbook.py new file mode 100644 index 00000000..009aabf1 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/reader/workbook.py @@ -0,0 +1,126 @@ +# Copyright (c) 2010-2022 openpyxl + +import posixpath +from warnings import warn + +from openpyxl.xml.functions import fromstring + +from openpyxl.packaging.relationship import ( + get_dependents, + get_rels_path, + get_rel, +) +from openpyxl.packaging.manifest import Manifest +from openpyxl.packaging.workbook import WorkbookPackage +from openpyxl.workbook import Workbook +from openpyxl.workbook.defined_name import ( + _unpack_print_area, + _unpack_print_titles, +) +from openpyxl.workbook.external_link.external import read_external_link +from openpyxl.pivot.cache import CacheDefinition +from openpyxl.pivot.record import RecordList + +from openpyxl.utils.datetime import CALENDAR_MAC_1904 + + +class WorkbookParser: + + _rels = None + + def __init__(self, archive, workbook_part_name, keep_links=True): + self.archive = archive + self.workbook_part_name = workbook_part_name + self.wb = Workbook() + self.keep_links = keep_links + self.sheets = [] + + + @property + def rels(self): + if self._rels is None: + self._rels = get_dependents(self.archive, get_rels_path(self.workbook_part_name)) + return self._rels + + + def parse(self): + src = self.archive.read(self.workbook_part_name) + node = fromstring(src) + package = WorkbookPackage.from_tree(node) + if package.properties.date1904: + self.wb.epoch = CALENDAR_MAC_1904 + + self.wb.code_name = package.properties.codeName + self.wb.active = package.active + self.wb.views = package.bookViews + self.sheets = package.sheets + self.wb.calculation = package.calcPr + self.caches = package.pivotCaches + + #external links contain cached worksheets and can be very big + if not self.keep_links: + package.externalReferences = [] + + for ext_ref in package.externalReferences: + rel = self.rels[ext_ref.id] + self.wb._external_links.append( + read_external_link(self.archive, rel.Target) + ) + + if package.definedNames: + package.definedNames._cleanup() + self.wb.defined_names = package.definedNames + + self.wb.security = package.workbookProtection + + + def find_sheets(self): + """ + Find all sheets in the workbook and return the link to the source file. + + Older XLSM files sometimes contain invalid sheet elements. + Warn user when these are removed. + """ + + for sheet in self.sheets: + if not sheet.id: + msg = "File contains an invalid specification for {0}. This will be removed".format(sheet.name) + warn(msg) + continue + yield sheet, self.rels[sheet.id] + + + def assign_names(self): + """ + Bind reserved names to parsed worksheets + """ + defns = [] + + for defn in self.wb.defined_names.definedName: + reserved = defn.is_reserved + if reserved in ("Print_Titles", "Print_Area"): + sheet = self.wb._sheets[defn.localSheetId] + if reserved == "Print_Titles": + rows, cols = _unpack_print_titles(defn) + sheet.print_title_rows = rows + sheet.print_title_cols = cols + elif reserved == "Print_Area": + sheet.print_area = _unpack_print_area(defn) + else: + defns.append(defn) + self.wb.defined_names.definedName = defns + + + @property + def pivot_caches(self): + """ + Get PivotCache objects + """ + d = {} + for c in self.caches: + cache = get_rel(self.archive, self.rels, id=c.id, cls=CacheDefinition) + if cache.deps: + records = get_rel(self.archive, cache.deps, cache.id, RecordList) + cache.records = records + d[c.cacheId] = cache + return d diff --git a/.venv/Lib/site-packages/openpyxl/styles/__init__.py b/.venv/Lib/site-packages/openpyxl/styles/__init__.py new file mode 100644 index 00000000..76ee7186 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/styles/__init__.py @@ -0,0 +1,11 @@ +# Copyright (c) 2010-2022 openpyxl + + +from .alignment import Alignment +from .borders import Border, Side +from .colors import Color +from .fills import PatternFill, GradientFill, Fill +from .fonts import Font, DEFAULT_FONT +from .numbers import NumberFormatDescriptor, is_date_format, is_builtin +from .protection import Protection +from .named_styles import NamedStyle diff --git a/.venv/Lib/site-packages/openpyxl/styles/alignment.py b/.venv/Lib/site-packages/openpyxl/styles/alignment.py new file mode 100644 index 00000000..f6ec7576 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/styles/alignment.py @@ -0,0 +1,72 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.compat import safe_string + +from openpyxl.descriptors import Bool, MinMax, Min, Alias, NoneSet +from openpyxl.descriptors.serialisable import Serialisable + + +horizontal_alignments = ( + "general", "left", "center", "right", "fill", "justify", "centerContinuous", + "distributed", ) +vertical_aligments = ( + "top", "center", "bottom", "justify", "distributed", +) + +class Alignment(Serialisable): + """Alignment options for use in styles.""" + + tagname = "alignment" + + __fields__ = ('horizontal', + 'vertical', + 'textRotation', + 'wrapText', + 'shrinkToFit', + 'indent', + 'relativeIndent', + 'justifyLastLine', + 'readingOrder', + ) + horizontal = NoneSet(values=horizontal_alignments) + vertical = NoneSet(values=vertical_aligments) + textRotation = NoneSet(values=range(181)) + textRotation.values.add(255) + text_rotation = Alias('textRotation') + wrapText = Bool(allow_none=True) + wrap_text = Alias('wrapText') + shrinkToFit = Bool(allow_none=True) + shrink_to_fit = Alias('shrinkToFit') + indent = MinMax(min=0, max=255) + relativeIndent = MinMax(min=-255, max=255) + justifyLastLine = Bool(allow_none=True) + readingOrder = Min(min=0) + + def __init__(self, horizontal=None, vertical=None, + textRotation=0, wrapText=None, shrinkToFit=None, indent=0, relativeIndent=0, + justifyLastLine=None, readingOrder=0, text_rotation=None, + wrap_text=None, shrink_to_fit=None, mergeCell=None): + self.horizontal = horizontal + self.vertical = vertical + self.indent = indent + self.relativeIndent = relativeIndent + self.justifyLastLine = justifyLastLine + self.readingOrder = readingOrder + if text_rotation is not None: + textRotation = text_rotation + if textRotation is not None: + self.textRotation = int(textRotation) + if wrap_text is not None: + wrapText = wrap_text + self.wrapText = wrapText + if shrink_to_fit is not None: + shrinkToFit = shrink_to_fit + self.shrinkToFit = shrinkToFit + # mergeCell is vestigial + + + def __iter__(self): + for attr in self.__attrs__: + value = getattr(self, attr) + if value is not None and value != 0: + yield attr, safe_string(value) diff --git a/.venv/Lib/site-packages/openpyxl/styles/borders.py b/.venv/Lib/site-packages/openpyxl/styles/borders.py new file mode 100644 index 00000000..e75185d9 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/styles/borders.py @@ -0,0 +1,113 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.compat import safe_string +from openpyxl.descriptors import ( + NoneSet, + Typed, + Bool, + Alias, + Sequence, + Integer, +) +from openpyxl.descriptors.serialisable import Serialisable + +from .colors import ColorDescriptor + + +BORDER_NONE = None +BORDER_DASHDOT = 'dashDot' +BORDER_DASHDOTDOT = 'dashDotDot' +BORDER_DASHED = 'dashed' +BORDER_DOTTED = 'dotted' +BORDER_DOUBLE = 'double' +BORDER_HAIR = 'hair' +BORDER_MEDIUM = 'medium' +BORDER_MEDIUMDASHDOT = 'mediumDashDot' +BORDER_MEDIUMDASHDOTDOT = 'mediumDashDotDot' +BORDER_MEDIUMDASHED = 'mediumDashed' +BORDER_SLANTDASHDOT = 'slantDashDot' +BORDER_THICK = 'thick' +BORDER_THIN = 'thin' + + +class Side(Serialisable): + + """Border options for use in styles. + Caution: if you do not specify a border_style, other attributes will + have no effect !""" + + __fields__ = ('style', + 'color') + + color = ColorDescriptor(allow_none=True) + style = NoneSet(values=('dashDot','dashDotDot', 'dashed','dotted', + 'double','hair', 'medium', 'mediumDashDot', 'mediumDashDotDot', + 'mediumDashed', 'slantDashDot', 'thick', 'thin') + ) + border_style = Alias('style') + + def __init__(self, style=None, color=None, border_style=None): + if border_style is not None: + style = border_style + self.style = style + self.color = color + + +class Border(Serialisable): + """Border positioning for use in styles.""" + + tagname = "border" + + __fields__ = ('left', + 'right', + 'top', + 'bottom', + 'diagonal', + 'diagonal_direction', + 'vertical', + 'horizontal') + __elements__ = ('start', 'end', 'left', 'right', 'top', 'bottom', + 'diagonal', 'vertical', 'horizontal') + + # child elements + start = Typed(expected_type=Side, allow_none=True) + end = Typed(expected_type=Side, allow_none=True) + left = Typed(expected_type=Side, allow_none=True) + right = Typed(expected_type=Side, allow_none=True) + top = Typed(expected_type=Side, allow_none=True) + bottom = Typed(expected_type=Side, allow_none=True) + diagonal = Typed(expected_type=Side, allow_none=True) + vertical = Typed(expected_type=Side, allow_none=True) + horizontal = Typed(expected_type=Side, allow_none=True) + # attributes + outline = Bool() + diagonalUp = Bool() + diagonalDown = Bool() + + def __init__(self, left=None, right=None, top=None, + bottom=None, diagonal=None, diagonal_direction=None, + vertical=None, horizontal=None, diagonalUp=False, diagonalDown=False, + outline=True, start=None, end=None): + self.left = left + self.right = right + self.top = top + self.bottom = bottom + self.diagonal = diagonal + self.vertical = vertical + self.horizontal = horizontal + self.diagonal_direction = diagonal_direction + self.diagonalUp = diagonalUp + self.diagonalDown = diagonalDown + self.outline = outline + self.start = start + self.end = end + + def __iter__(self): + for attr in self.__attrs__: + value = getattr(self, attr) + if value and attr != "outline": + yield attr, safe_string(value) + elif attr == "outline" and not value: + yield attr, safe_string(value) + +DEFAULT_BORDER = Border(left=Side(), right=Side(), top=Side(), bottom=Side(), diagonal=Side()) diff --git a/.venv/Lib/site-packages/openpyxl/styles/builtins.py b/.venv/Lib/site-packages/openpyxl/styles/builtins.py new file mode 100644 index 00000000..b4ee8d46 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/styles/builtins.py @@ -0,0 +1,1397 @@ +# Copyright (c) 2010-2022 openpyxl + +# Builtins styles as defined in Part 4 Annex G.2 + +from .named_styles import NamedStyle +from openpyxl.xml.functions import fromstring + + +normal = """ + + + + + + + + + + + + + + + + + + + + +""" + +comma = """ + + + _-* #,##0.00\\ _$_-;\\-* #,##0.00\\ _$_-;_-* "-"??\\ _$_-;_-@_- + + + + + + + + + + + + + + + + + + +""" + +comma_0 = """ + + + _-* #,##0\\ _$_-;\\-* #,##0\\ _$_-;_-* "-"\\ _$_-;_-@_- + + + + + + + + + + + + + + + + + + +""" + +currency = """ + + + _-* #,##0.00\\ "$"_-;\\-* #,##0.00\\ "$"_-;_-* "-"??\\ "$"_-;_-@_- + + + + + + + + + + + + + + + + + + +""" + +currency_0 = """ + + + _-* #,##0\\ "$"_-;\\-* #,##0\\ "$"_-;_-* "-"\\ "$"_-;_-@_- + + + + + + + + + + + + + + + + + + +""" + +percent = """ + + + 0% + + + + + + + + + + + + + + + + + + +""" + +hyperlink = """ + + + + + + + + + + + + + + + + + + + + """ + +followed_hyperlink = """ + + + + + + + + + + + + + + + + + + + + """ + +title = """ + + + + + + + + + + + + + + + + + + + + + +""" + +headline_1 = """ + + + + + + + + + + + + + + + + + + + + + + + +""" + +headline_2 = """ + + + + + + + + + + + + + + + + + + + + + + + +""" + +headline_3 = """ + + + + + + + + + + + + + + + + + + + + + + + + +""" + +headline_4 = """ + + + + + + + + + + + + + + + + + + + + + +""" + +good = """ + + + + + + + + + + + + + + + + + + + + + + +""" + +bad = """ + + + + + + + + + + + + + + + + + + + + + + +""" + +neutral = """ + + + + + + + + + + + + + + + + + + + + + + +""" + +input = """ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +""" + +output = """ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +""" + +calculation = """ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +""" + +linked_cell = """ + + + + + + + + + + + + + + + + + + + + + + +""" + +check_cell = """ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +""" + +warning = """ + + + + + + + + + + + + + + + + + + + + +""" + +note = """ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +""" + +explanatory = """ + + + + + + + + + + + + + + + + + + + + + +""" + +total = """ + + + + + + + + + + + + + + + + + + + + + + + + + +""" + +accent_1 = """ + + + + + + + + + + + + + + + + + + + + + + +""" + +accent_1_20 = """ + + + + + + + + + + + + + + + + + + + + + + + +""" + +accent_1_40 = """ + + + + + + + + + + + + + + + + + + + + + + + +""" + +accent_1_60 = """ + + + + + + + + + + + + + + + + + + + + + + + +""" + +accent_2 = """ + + + + + + + + + + + + + + + + + + + + + """ + +accent_2_20 = """ + + + + + + + + + + + + + + + + + + + + + + + """ + +accent_2_40 = """ + + + + + + + + + + + + + + + + + + + + + + + """ + +accent_2_60 = """ + + + + + + + + + + + + + + + + + + + + + + + """ + +accent_3 = """ + + + + + + + + + + + + + + + + + + + + + + """ + +accent_3_20 = """ + + + + + + + + + + + + + + + + + + + + + + + """ + +accent_3_40 = """ + + + + + + + + + + + + + + + + + + + + + + + +""" +accent_3_60 = """ + + + + + + + + + + + + + + + + + + + + + + + +""" +accent_4 = """ + + + + + + + + + + + + + + + + + + + + + + +""" + +accent_4_20 = """ + + + + + + + + + + + + + + + + + + + + + + + +""" + +accent_4_40 = """ + + + + + + + + + + + + + + + + + + + + + + + +""" + +accent_4_60 = """ + + + + + + + + + + + + + + + + + + + + + + + +""" + +accent_5 = """ + + + + + + + + + + + + + + + + + + + + + + +""" + +accent_5_20 = """ + + + + + + + + + + + + + + + + + + + + + + + +""" + +accent_5_40 = """ + + + + + + + + + + + + + + + + + + + + + + + +""" + +accent_5_60 = """ + + + + + + + + + + + + + + + + + + + + + + + +""" + +accent_6 = """ + + + + + + + + + + + + + + + + + + + + + + +""" + +accent_6_20 = """ + + + + + + + + + + + + + + + + + + + + + + + +""" + +accent_6_40 = """ + + + + + + + + + + + + + + + + + + + + + + + +""" + +accent_6_60 = """ + + + + + + + + + + + + + + + + + + + + + + + +""" + +pandas_highlight = """ + +""" + +styles = dict( + [ + ('Normal', NamedStyle.from_tree(fromstring(normal))), + ('Comma', NamedStyle.from_tree(fromstring(comma))), + ('Currency', NamedStyle.from_tree(fromstring(currency))), + ('Percent', NamedStyle.from_tree(fromstring(percent))), + ('Comma [0]', NamedStyle.from_tree(fromstring(comma_0))), + ('Currency [0]', NamedStyle.from_tree(fromstring(currency_0))), + ('Hyperlink', NamedStyle.from_tree(fromstring(hyperlink))), + ('Followed Hyperlink', NamedStyle.from_tree(fromstring(followed_hyperlink))), + ('Note', NamedStyle.from_tree(fromstring(note))), + ('Warning Text', NamedStyle.from_tree(fromstring(warning))), + ('Title', NamedStyle.from_tree(fromstring(title))), + ('Headline 1', NamedStyle.from_tree(fromstring(headline_1))), + ('Headline 2', NamedStyle.from_tree(fromstring(headline_2))), + ('Headline 3', NamedStyle.from_tree(fromstring(headline_3))), + ('Headline 4', NamedStyle.from_tree(fromstring(headline_4))), + ('Input', NamedStyle.from_tree(fromstring(input))), + ('Output', NamedStyle.from_tree(fromstring(output))), + ('Calculation',NamedStyle.from_tree(fromstring(calculation))), + ('Check Cell', NamedStyle.from_tree(fromstring(check_cell))), + ('Linked Cell', NamedStyle.from_tree(fromstring(linked_cell))), + ('Total', NamedStyle.from_tree(fromstring(total))), + ('Good', NamedStyle.from_tree(fromstring(good))), + ('Bad', NamedStyle.from_tree(fromstring(bad))), + ('Neutral', NamedStyle.from_tree(fromstring(neutral))), + ('Accent1', NamedStyle.from_tree(fromstring(accent_1))), + ('20 % - Accent1', NamedStyle.from_tree(fromstring(accent_1_20))), + ('40 % - Accent1', NamedStyle.from_tree(fromstring(accent_1_40))), + ('60 % - Accent1', NamedStyle.from_tree(fromstring(accent_1_60))), + ('Accent2', NamedStyle.from_tree(fromstring(accent_2))), + ('20 % - Accent2', NamedStyle.from_tree(fromstring(accent_2_20))), + ('40 % - Accent2', NamedStyle.from_tree(fromstring(accent_2_40))), + ('60 % - Accent2', NamedStyle.from_tree(fromstring(accent_2_60))), + ('Accent3', NamedStyle.from_tree(fromstring(accent_3))), + ('20 % - Accent3', NamedStyle.from_tree(fromstring(accent_3_20))), + ('40 % - Accent3', NamedStyle.from_tree(fromstring(accent_3_40))), + ('60 % - Accent3', NamedStyle.from_tree(fromstring(accent_3_60))), + ('Accent4', NamedStyle.from_tree(fromstring(accent_4))), + ('20 % - Accent4', NamedStyle.from_tree(fromstring(accent_4_20))), + ('40 % - Accent4', NamedStyle.from_tree(fromstring(accent_4_40))), + ('60 % - Accent4', NamedStyle.from_tree(fromstring(accent_4_60))), + ('Accent5', NamedStyle.from_tree(fromstring(accent_5))), + ('20 % - Accent5', NamedStyle.from_tree(fromstring(accent_5_20))), + ('40 % - Accent5', NamedStyle.from_tree(fromstring(accent_5_40))), + ('60 % - Accent5', NamedStyle.from_tree(fromstring(accent_5_60))), + ('Accent6', NamedStyle.from_tree(fromstring(accent_6))), + ('20 % - Accent6', NamedStyle.from_tree(fromstring(accent_6_20))), + ('40 % - Accent6', NamedStyle.from_tree(fromstring(accent_6_40))), + ('60 % - Accent6', NamedStyle.from_tree(fromstring(accent_6_60))), + ('Explanatory Text', NamedStyle.from_tree(fromstring(explanatory))), + ('Pandas', NamedStyle.from_tree(fromstring(pandas_highlight))) + ] +) diff --git a/.venv/Lib/site-packages/openpyxl/styles/cell_style.py b/.venv/Lib/site-packages/openpyxl/styles/cell_style.py new file mode 100644 index 00000000..87d0603a --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/styles/cell_style.py @@ -0,0 +1,202 @@ +# Copyright (c) 2010-2022 openpyxl + +from array import array + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Float, + Bool, + Integer, + Sequence, +) +from openpyxl.descriptors.excel import ExtensionList +from openpyxl.utils.indexed_list import IndexedList + + +from .alignment import Alignment +from .protection import Protection + + +class ArrayDescriptor(object): + + def __init__(self, key): + self.key = key + + def __get__(self, instance, cls): + return instance[self.key] + + def __set__(self, instance, value): + instance[self.key] = value + + +class StyleArray(array): + """ + Simplified named tuple with an array + """ + + __slots__ = () + tagname = 'xf' + + fontId = ArrayDescriptor(0) + fillId = ArrayDescriptor(1) + borderId = ArrayDescriptor(2) + numFmtId = ArrayDescriptor(3) + protectionId = ArrayDescriptor(4) + alignmentId = ArrayDescriptor(5) + pivotButton = ArrayDescriptor(6) + quotePrefix = ArrayDescriptor(7) + xfId = ArrayDescriptor(8) + + + def __new__(cls, args=[0]*9): + return array.__new__(cls, 'i', args) + + + def __hash__(self): + return hash(tuple(self)) + + + def __copy__(self): + return StyleArray((self)) + + + def __deepcopy__(self, memo): + return StyleArray((self)) + + +class CellStyle(Serialisable): + + tagname = "xf" + + numFmtId = Integer() + fontId = Integer() + fillId = Integer() + borderId = Integer() + xfId = Integer(allow_none=True) + quotePrefix = Bool(allow_none=True) + pivotButton = Bool(allow_none=True) + applyNumberFormat = Bool(allow_none=True) + applyFont = Bool(allow_none=True) + applyFill = Bool(allow_none=True) + applyBorder = Bool(allow_none=True) + applyAlignment = Bool(allow_none=True) + applyProtection = Bool(allow_none=True) + alignment = Typed(expected_type=Alignment, allow_none=True) + protection = Typed(expected_type=Protection, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('alignment', 'protection') + __attrs__ = ("numFmtId", "fontId", "fillId", "borderId", + "applyAlignment", "applyProtection", "pivotButton", "quotePrefix", "xfId") + + def __init__(self, + numFmtId=0, + fontId=0, + fillId=0, + borderId=0, + xfId=None, + quotePrefix=None, + pivotButton=None, + applyNumberFormat=None, + applyFont=None, + applyFill=None, + applyBorder=None, + applyAlignment=None, + applyProtection=None, + alignment=None, + protection=None, + extLst=None, + ): + self.numFmtId = numFmtId + self.fontId = fontId + self.fillId = fillId + self.borderId = borderId + self.xfId = xfId + self.quotePrefix = quotePrefix + self.pivotButton = pivotButton + self.applyNumberFormat = applyNumberFormat + self.applyFont = applyFont + self.applyFill = applyFill + self.applyBorder = applyBorder + self.alignment = alignment + self.protection = protection + + + def to_array(self): + """ + Convert to StyleArray + """ + style = StyleArray() + for k in ("fontId", "fillId", "borderId", "numFmtId", "pivotButton", + "quotePrefix", "xfId"): + v = getattr(self, k, 0) + if v is not None: + setattr(style, k, v) + return style + + + @classmethod + def from_array(cls, style): + """ + Convert from StyleArray + """ + return cls(numFmtId=style.numFmtId, fontId=style.fontId, + fillId=style.fillId, borderId=style.borderId, xfId=style.xfId, + quotePrefix=style.quotePrefix, pivotButton=style.pivotButton,) + + + @property + def applyProtection(self): + return self.protection is not None or None + + + @property + def applyAlignment(self): + return self.alignment is not None or None + + +class CellStyleList(Serialisable): + + tagname = "cellXfs" + + __attrs__ = ("count",) + + count = Integer(allow_none=True) + xf = Sequence(expected_type=CellStyle) + alignment = Sequence(expected_type=Alignment) + protection = Sequence(expected_type=Protection) + + __elements__ = ('xf',) + + def __init__(self, + count=None, + xf=(), + ): + self.xf = xf + + + @property + def count(self): + return len(self.xf) + + + def __getitem__(self, idx): + return self.xf[idx] + + + def _to_array(self): + """ + Extract protection and alignments, convert to style array + """ + self.prots = IndexedList([Protection()]) + self.alignments = IndexedList([Alignment()]) + styles = [] # allow duplicates + for xf in self.xf: + style = xf.to_array() + if xf.alignment is not None: + style.alignmentId = self.alignments.add(xf.alignment) + if xf.protection is not None: + style.protectionId = self.prots.add(xf.protection) + styles.append(style) + return IndexedList(styles) diff --git a/.venv/Lib/site-packages/openpyxl/styles/colors.py b/.venv/Lib/site-packages/openpyxl/styles/colors.py new file mode 100644 index 00000000..4db7422f --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/styles/colors.py @@ -0,0 +1,172 @@ +# Copyright (c) 2010-2022 openpyxl + +import re +from openpyxl.compat import safe_string +from openpyxl.descriptors import ( + String, + Bool, + MinMax, + Integer, + Typed, +) +from openpyxl.descriptors.sequence import NestedSequence +from openpyxl.descriptors.serialisable import Serialisable + +# Default Color Index as per 18.8.27 of ECMA Part 4 +COLOR_INDEX = ( + '00000000', '00FFFFFF', '00FF0000', '0000FF00', '000000FF', #0-4 + '00FFFF00', '00FF00FF', '0000FFFF', '00000000', '00FFFFFF', #5-9 + '00FF0000', '0000FF00', '000000FF', '00FFFF00', '00FF00FF', #10-14 + '0000FFFF', '00800000', '00008000', '00000080', '00808000', #15-19 + '00800080', '00008080', '00C0C0C0', '00808080', '009999FF', #20-24 + '00993366', '00FFFFCC', '00CCFFFF', '00660066', '00FF8080', #25-29 + '000066CC', '00CCCCFF', '00000080', '00FF00FF', '00FFFF00', #30-34 + '0000FFFF', '00800080', '00800000', '00008080', '000000FF', #35-39 + '0000CCFF', '00CCFFFF', '00CCFFCC', '00FFFF99', '0099CCFF', #40-44 + '00FF99CC', '00CC99FF', '00FFCC99', '003366FF', '0033CCCC', #45-49 + '0099CC00', '00FFCC00', '00FF9900', '00FF6600', '00666699', #50-54 + '00969696', '00003366', '00339966', '00003300', '00333300', #55-59 + '00993300', '00993366', '00333399', '00333333', #60-63 +) +# indices 64 and 65 are reserved for the system foreground and background colours respectively + +# Will remove these definitions in a future release +BLACK = COLOR_INDEX[0] +WHITE = COLOR_INDEX[1] +#RED = COLOR_INDEX[2] +#DARKRED = COLOR_INDEX[8] +BLUE = COLOR_INDEX[4] +#DARKBLUE = COLOR_INDEX[12] +#GREEN = COLOR_INDEX[3] +#DARKGREEN = COLOR_INDEX[9] +#YELLOW = COLOR_INDEX[5] +#DARKYELLOW = COLOR_INDEX[19] + + +aRGB_REGEX = re.compile("^([A-Fa-f0-9]{8}|[A-Fa-f0-9]{6})$") + + +class RGB(Typed): + """ + Descriptor for aRGB values + If not supplied alpha is 00 + """ + + expected_type = str + + def __set__(self, instance, value): + if not self.allow_none: + m = aRGB_REGEX.match(value) + if m is None: + raise ValueError("Colors must be aRGB hex values") + if len(value) == 6: + value = "00" + value + super(RGB, self).__set__(instance, value) + + +class Color(Serialisable): + """Named colors for use in styles.""" + + tagname = "color" + + rgb = RGB() + indexed = Integer() + auto = Bool() + theme = Integer() + tint = MinMax(min=-1, max=1, expected_type=float) + type = String() + + + def __init__(self, rgb=BLACK, indexed=None, auto=None, theme=None, tint=0.0, index=None, type='rgb'): + if index is not None: + indexed = index + if indexed is not None: + self.type = 'indexed' + self.indexed = indexed + elif theme is not None: + self.type = 'theme' + self.theme = theme + elif auto is not None: + self.type = 'auto' + self.auto = auto + else: + self.rgb = rgb + self.type = 'rgb' + self.tint = tint + + @property + def value(self): + return getattr(self, self.type) + + @value.setter + def value(self, value): + setattr(self, self.type, value) + + def __iter__(self): + attrs = [(self.type, self.value)] + if self.tint != 0: + attrs.append(('tint', self.tint)) + for k, v in attrs: + yield k, safe_string(v) + + @property + def index(self): + # legacy + return self.value + + + def __add__(self, other): + """ + Adding colours is undefined behaviour best do nothing + """ + if not isinstance(other, Color): + return super(Color, self).__add__(other) + return self + + +class ColorDescriptor(Typed): + + expected_type = Color + + def __set__(self, instance, value): + if isinstance(value, str): + value = Color(rgb=value) + super(ColorDescriptor, self).__set__(instance, value) + + +class RgbColor(Serialisable): + + tagname = "rgbColor" + + rgb = RGB() + + def __init__(self, + rgb=None, + ): + self.rgb = rgb + + +class ColorList(Serialisable): + + tagname = "colors" + + indexedColors = NestedSequence(expected_type=RgbColor) + mruColors = NestedSequence(expected_type=Color) + + __elements__ = ('indexedColors', 'mruColors') + + def __init__(self, + indexedColors=(), + mruColors=(), + ): + self.indexedColors = indexedColors + self.mruColors = mruColors + + + def __bool__(self): + return bool(self.indexedColors) or bool(self.mruColors) + + + @property + def index(self): + return [val.rgb for val in self.indexedColors] diff --git a/.venv/Lib/site-packages/openpyxl/styles/differential.py b/.venv/Lib/site-packages/openpyxl/styles/differential.py new file mode 100644 index 00000000..730a7e3c --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/styles/differential.py @@ -0,0 +1,95 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.descriptors import ( + Typed, + Sequence, + Alias, +) +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.styles import ( + Font, + Fill, + Border, + Alignment, + Protection, + ) +from .numbers import NumberFormat + + +class DifferentialStyle(Serialisable): + + tagname = "dxf" + + __elements__ = ("font", "numFmt", "fill", "alignment", "border", "protection") + + font = Typed(expected_type=Font, allow_none=True) + numFmt = Typed(expected_type=NumberFormat, allow_none=True) + fill = Typed(expected_type=Fill, allow_none=True) + alignment = Typed(expected_type=Alignment, allow_none=True) + border = Typed(expected_type=Border, allow_none=True) + protection = Typed(expected_type=Protection, allow_none=True) + + def __init__(self, + font=None, + numFmt=None, + fill=None, + alignment=None, + border=None, + protection=None, + extLst=None, + ): + self.font = font + self.numFmt = numFmt + self.fill = fill + self.alignment = alignment + self.border = border + self.protection = protection + self.extLst = extLst + + +class DifferentialStyleList(Serialisable): + """ + Dedupable container for differential styles. + """ + + tagname = "dxfs" + + dxf = Sequence(expected_type=DifferentialStyle) + styles = Alias("dxf") + __attrs__ = ("count",) + + + def __init__(self, dxf=(), count=None): + self.dxf = dxf + + + def append(self, dxf): + """ + Check to see whether style already exists and append it if does not. + """ + if not isinstance(dxf, DifferentialStyle): + raise TypeError('expected ' + str(DifferentialStyle)) + if dxf in self.styles: + return + self.styles.append(dxf) + + + def add(self, dxf): + """ + Add a differential style and return its index + """ + self.append(dxf) + return self.styles.index(dxf) + + + def __bool__(self): + return bool(self.styles) + + + def __getitem__(self, idx): + return self.styles[idx] + + + @property + def count(self): + return len(self.dxf) diff --git a/.venv/Lib/site-packages/openpyxl/styles/fills.py b/.venv/Lib/site-packages/openpyxl/styles/fills.py new file mode 100644 index 00000000..f327004c --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/styles/fills.py @@ -0,0 +1,224 @@ +from __future__ import division +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.descriptors import ( + Float, + Set, + Alias, + NoneSet, + Sequence, + Integer, + MinMax, +) +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.compat import safe_string + +from .colors import ColorDescriptor, Color + +from openpyxl.xml.functions import Element, localname +from openpyxl.xml.constants import SHEET_MAIN_NS + + +FILL_NONE = 'none' +FILL_SOLID = 'solid' +FILL_PATTERN_DARKDOWN = 'darkDown' +FILL_PATTERN_DARKGRAY = 'darkGray' +FILL_PATTERN_DARKGRID = 'darkGrid' +FILL_PATTERN_DARKHORIZONTAL = 'darkHorizontal' +FILL_PATTERN_DARKTRELLIS = 'darkTrellis' +FILL_PATTERN_DARKUP = 'darkUp' +FILL_PATTERN_DARKVERTICAL = 'darkVertical' +FILL_PATTERN_GRAY0625 = 'gray0625' +FILL_PATTERN_GRAY125 = 'gray125' +FILL_PATTERN_LIGHTDOWN = 'lightDown' +FILL_PATTERN_LIGHTGRAY = 'lightGray' +FILL_PATTERN_LIGHTGRID = 'lightGrid' +FILL_PATTERN_LIGHTHORIZONTAL = 'lightHorizontal' +FILL_PATTERN_LIGHTTRELLIS = 'lightTrellis' +FILL_PATTERN_LIGHTUP = 'lightUp' +FILL_PATTERN_LIGHTVERTICAL = 'lightVertical' +FILL_PATTERN_MEDIUMGRAY = 'mediumGray' + +fills = (FILL_SOLID, FILL_PATTERN_DARKDOWN, FILL_PATTERN_DARKGRAY, + FILL_PATTERN_DARKGRID, FILL_PATTERN_DARKHORIZONTAL, FILL_PATTERN_DARKTRELLIS, + FILL_PATTERN_DARKUP, FILL_PATTERN_DARKVERTICAL, FILL_PATTERN_GRAY0625, + FILL_PATTERN_GRAY125, FILL_PATTERN_LIGHTDOWN, FILL_PATTERN_LIGHTGRAY, + FILL_PATTERN_LIGHTGRID, FILL_PATTERN_LIGHTHORIZONTAL, + FILL_PATTERN_LIGHTTRELLIS, FILL_PATTERN_LIGHTUP, FILL_PATTERN_LIGHTVERTICAL, + FILL_PATTERN_MEDIUMGRAY) + + +class Fill(Serialisable): + + """Base class""" + + tagname = "fill" + + @classmethod + def from_tree(cls, el): + children = [c for c in el] + if not children: + return + child = children[0] + if "patternFill" in child.tag: + return PatternFill._from_tree(child) + return super(Fill, GradientFill).from_tree(child) + + +class PatternFill(Fill): + """Area fill patterns for use in styles. + Caution: if you do not specify a fill_type, other attributes will have + no effect !""" + + tagname = "patternFill" + + __elements__ = ('fgColor', 'bgColor') + + patternType = NoneSet(values=fills) + fill_type = Alias("patternType") + fgColor = ColorDescriptor() + start_color = Alias("fgColor") + bgColor = ColorDescriptor() + end_color = Alias("bgColor") + + def __init__(self, patternType=None, fgColor=Color(), bgColor=Color(), + fill_type=None, start_color=None, end_color=None): + if fill_type is not None: + patternType = fill_type + self.patternType = patternType + if start_color is not None: + fgColor = start_color + self.fgColor = fgColor + if end_color is not None: + bgColor = end_color + self.bgColor = bgColor + + @classmethod + def _from_tree(cls, el): + attrib = dict(el.attrib) + for child in el: + desc = localname(child) + attrib[desc] = Color.from_tree(child) + return cls(**attrib) + + + def to_tree(self, tagname=None, idx=None): + parent = Element("fill") + el = Element(self.tagname) + if self.patternType is not None: + el.set('patternType', self.patternType) + for c in self.__elements__: + value = getattr(self, c) + if value != Color(): + el.append(value.to_tree(c)) + parent.append(el) + return parent + + +DEFAULT_EMPTY_FILL = PatternFill() +DEFAULT_GRAY_FILL = PatternFill(patternType='gray125') + + +class Stop(Serialisable): + + tagname = "stop" + + position = MinMax(min=0, max=1) + color = ColorDescriptor() + + def __init__(self, color, position): + self.position = position + self.color = color + + +def _assign_position(values): + """ + Automatically assign positions if a list of colours is provided. + + It is not permitted to mix colours and stops + """ + n_values = len(values) + n_stops = sum(isinstance(value, Stop) for value in values) + + if n_stops == 0: + interval = 1 + if n_values > 2: + interval = 1 / (n_values - 1) + values = [Stop(value, i * interval) + for i, value in enumerate(values)] + + elif n_stops < n_values: + raise ValueError('Cannot interpret mix of Stops and Colors in GradientFill') + + pos = set() + for stop in values: + if stop.position in pos: + raise ValueError("Duplicate position {0}".format(stop.position)) + pos.add(stop.position) + + return values + + +class StopList(Sequence): + + expected_type = Stop + + def __set__(self, obj, values): + values = _assign_position(values) + super(StopList, self).__set__(obj, values) + + +class GradientFill(Fill): + """Fill areas with gradient + + Two types of gradient fill are supported: + + - A type='linear' gradient interpolates colours between + a set of specified Stops, across the length of an area. + The gradient is left-to-right by default, but this + orientation can be modified with the degree + attribute. A list of Colors can be provided instead + and they will be positioned with equal distance between them. + + - A type='path' gradient applies a linear gradient from each + edge of the area. Attributes top, right, bottom, left specify + the extent of fill from the respective borders. Thus top="0.2" + will fill the top 20% of the cell. + + """ + + tagname = "gradientFill" + + type = Set(values=('linear', 'path')) + fill_type = Alias("type") + degree = Float() + left = Float() + right = Float() + top = Float() + bottom = Float() + stop = StopList() + + + def __init__(self, type="linear", degree=0, left=0, right=0, top=0, + bottom=0, stop=()): + self.degree = degree + self.left = left + self.right = right + self.top = top + self.bottom = bottom + self.stop = stop + self.type = type + + + def __iter__(self): + for attr in self.__attrs__: + value = getattr(self, attr) + if value: + yield attr, safe_string(value) + + + def to_tree(self, tagname=None, namespace=None, idx=None): + parent = Element("fill") + el = super(GradientFill, self).to_tree() + parent.append(el) + return parent diff --git a/.venv/Lib/site-packages/openpyxl/styles/fonts.py b/.venv/Lib/site-packages/openpyxl/styles/fonts.py new file mode 100644 index 00000000..07cd90fe --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/styles/fonts.py @@ -0,0 +1,113 @@ +# Copyright (c) 2010-2022 openpyxl + + +from openpyxl.descriptors import ( + Alias, + Sequence, + Integer +) +from openpyxl.descriptors.serialisable import Serialisable + +from openpyxl.descriptors.nested import ( + NestedValue, + NestedBool, + NestedNoneSet, + NestedMinMax, + NestedString, + NestedInteger, + NestedFloat, +) +from .colors import ColorDescriptor, Color, BLACK + +from openpyxl.compat import safe_string +from openpyxl.xml.functions import Element, SubElement +from openpyxl.xml.constants import SHEET_MAIN_NS + + +def _no_value(tagname, value, namespace=None): + if value: + return Element(tagname, val=safe_string(value)) + + +class Font(Serialisable): + """Font options used in styles.""" + + UNDERLINE_DOUBLE = 'double' + UNDERLINE_DOUBLE_ACCOUNTING = 'doubleAccounting' + UNDERLINE_SINGLE = 'single' + UNDERLINE_SINGLE_ACCOUNTING = 'singleAccounting' + + name = NestedString(allow_none=True) + charset = NestedInteger(allow_none=True) + family = NestedMinMax(min=0, max=14, allow_none=True) + sz = NestedFloat(allow_none=True) + size = Alias("sz") + b = NestedBool(to_tree=_no_value) + bold = Alias("b") + i = NestedBool(to_tree=_no_value) + italic = Alias("i") + strike = NestedBool(allow_none=True) + strikethrough = Alias("strike") + outline = NestedBool(allow_none=True) + shadow = NestedBool(allow_none=True) + condense = NestedBool(allow_none=True) + extend = NestedBool(allow_none=True) + u = NestedNoneSet(values=('single', 'double', 'singleAccounting', + 'doubleAccounting')) + underline = Alias("u") + vertAlign = NestedNoneSet(values=('superscript', 'subscript', 'baseline')) + color = ColorDescriptor(allow_none=True) + scheme = NestedNoneSet(values=("major", "minor")) + + tagname = "font" + + __elements__ = ('name', 'charset', 'family', 'b', 'i', 'strike', 'outline', + 'shadow', 'condense', 'color', 'extend', 'sz', 'u', 'vertAlign', + 'scheme') + + + def __init__(self, name=None, sz=None, b=None, i=None, charset=None, + u=None, strike=None, color=None, scheme=None, family=None, size=None, + bold=None, italic=None, strikethrough=None, underline=None, + vertAlign=None, outline=None, shadow=None, condense=None, + extend=None): + self.name = name + self.family = family + if size is not None: + sz = size + self.sz = sz + if bold is not None: + b = bold + self.b = b + if italic is not None: + i = italic + self.i = i + if underline is not None: + u = underline + self.u = u + if strikethrough is not None: + strike = strikethrough + self.strike = strike + self.color = color + self.vertAlign = vertAlign + self.charset = charset + self.outline = outline + self.shadow = shadow + self.condense = condense + self.extend = extend + self.scheme = scheme + + + @classmethod + def from_tree(cls, node): + """ + Set default value for underline if child element is present + """ + underline = node.find("{%s}u" % SHEET_MAIN_NS) + if underline is not None and underline.get('val') is None: + underline.set("val", "single") + return super(Font, cls).from_tree(node) + + +DEFAULT_FONT = Font(name="Calibri", sz=11, family=2, b=False, i=False, + color=Color(theme=1), scheme="minor") diff --git a/.venv/Lib/site-packages/openpyxl/styles/named_styles.py b/.venv/Lib/site-packages/openpyxl/styles/named_styles.py new file mode 100644 index 00000000..d4c3eed1 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/styles/named_styles.py @@ -0,0 +1,291 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.compat import safe_string + +from openpyxl.descriptors import ( + Typed, + Integer, + Bool, + String, + Sequence, +) +from openpyxl.descriptors.excel import ExtensionList +from openpyxl.descriptors.serialisable import Serialisable + +from .fills import PatternFill, Fill +from .fonts import Font +from .borders import Border +from .alignment import Alignment +from .protection import Protection +from .numbers import ( + NumberFormatDescriptor, + BUILTIN_FORMATS_MAX_SIZE, + BUILTIN_FORMATS_REVERSE, +) +from .cell_style import ( + StyleArray, + CellStyle, +) + + +class NamedStyle(Serialisable): + + """ + Named and editable styles + """ + + font = Typed(expected_type=Font) + fill = Typed(expected_type=Fill) + border = Typed(expected_type=Border) + alignment = Typed(expected_type=Alignment) + number_format = NumberFormatDescriptor() + protection = Typed(expected_type=Protection) + builtinId = Integer(allow_none=True) + hidden = Bool(allow_none=True) + xfId = Integer(allow_none=True) + name = String() + _wb = None + _style = StyleArray() + + + def __init__(self, + name="Normal", + font=Font(), + fill=PatternFill(), + border=Border(), + alignment=Alignment(), + number_format=None, + protection=Protection(), + builtinId=None, + hidden=False, + xfId=None, + ): + self.name = name + self.font = font + self.fill = fill + self.border = border + self.alignment = alignment + self.number_format = number_format + self.protection = protection + self.builtinId = builtinId + self.hidden = hidden + self._wb = None + self._style = StyleArray() + + + def __setattr__(self, attr, value): + super(NamedStyle, self).__setattr__(attr, value) + if getattr(self, '_wb', None) and attr in ( + 'font', 'fill', 'border', 'alignment', 'number_format', 'protection', + ): + self._recalculate() + + + def __iter__(self): + for key in ('name', 'builtinId', 'hidden', 'xfId'): + value = getattr(self, key, None) + if value is not None: + yield key, safe_string(value) + + + @property + def xfId(self): + """ + Index of the style in the list of named styles + """ + return self._style.xfId + + + def _set_index(self, idx): + """ + Allow the containing list to set the index + """ + self._style.xfId = idx + + + def bind(self, wb): + """ + Bind a named style to a workbook + """ + self._wb = wb + self._recalculate() + + + def _recalculate(self): + self._style.fontId = self._wb._fonts.add(self.font) + self._style.borderId = self._wb._borders.add(self.border) + self._style.fillId = self._wb._fills.add(self.fill) + self._style.protectionId = self._wb._protections.add(self.protection) + self._style.alignmentId = self._wb._alignments.add(self.alignment) + fmt = self.number_format + if fmt in BUILTIN_FORMATS_REVERSE: + fmt = BUILTIN_FORMATS_REVERSE[fmt] + else: + fmt = self._wb._number_formats.add(self.number_format) + ( + BUILTIN_FORMATS_MAX_SIZE) + self._style.numFmtId = fmt + + + def as_tuple(self): + """Return a style array representing the current style""" + return self._style + + + def as_xf(self): + """ + Return equivalent XfStyle + """ + xf = CellStyle.from_array(self._style) + xf.xfId = None + xf.pivotButton = None + xf.quotePrefix = None + if self.alignment != Alignment(): + xf.alignment = self.alignment + if self.protection != Protection(): + xf.protection = self.protection + return xf + + + def as_name(self): + """ + Return relevant named style + + """ + named = _NamedCellStyle( + name=self.name, + builtinId=self.builtinId, + hidden=self.hidden, + xfId=self.xfId + ) + return named + + +class NamedStyleList(list): + """ + Named styles are editable and can be applied to multiple objects + + As only the index is stored in referencing objects the order mus + be preserved. + """ + + @property + def names(self): + return [s.name for s in self] + + + def __getitem__(self, key): + if isinstance(key, int): + return super(NamedStyleList, self).__getitem__(key) + + names = self.names + if key not in names: + raise KeyError("No named style with the name{0} exists".format(key)) + + for idx, name in enumerate(names): + if name == key: + return self[idx] + + + def append(self, style): + if not isinstance(style, NamedStyle): + raise TypeError("""Only NamedStyle instances can be added""") + elif style.name in self.names: + raise ValueError("""Style {0} exists already""".format(style.name)) + style._set_index(len(self)) + super(NamedStyleList, self).append(style) + + +class _NamedCellStyle(Serialisable): + + """ + Pointer-based representation of named styles in XML + xfId refers to the corresponding CellStyleXfs + + Not used in client code. + """ + + tagname = "cellStyle" + + name = String() + xfId = Integer() + builtinId = Integer(allow_none=True) + iLevel = Integer(allow_none=True) + hidden = Bool(allow_none=True) + customBuiltin = Bool(allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = () + + + def __init__(self, + name=None, + xfId=None, + builtinId=None, + iLevel=None, + hidden=None, + customBuiltin=None, + extLst=None, + ): + self.name = name + self.xfId = xfId + self.builtinId = builtinId + self.iLevel = iLevel + self.hidden = hidden + self.customBuiltin = customBuiltin + + +class _NamedCellStyleList(Serialisable): + """ + Container for named cell style objects + + Not used in client code + """ + + tagname = "cellStyles" + + count = Integer(allow_none=True) + cellStyle = Sequence(expected_type=_NamedCellStyle) + + __attrs__ = ("count",) + + def __init__(self, + count=None, + cellStyle=(), + ): + self.cellStyle = cellStyle + + + @property + def count(self): + return len(self.cellStyle) + + + @property + def names(self): + """ + Convert to NamedStyle objects and remove duplicates. + + In theory the highest xfId wins but in practice they are duplicates + so it doesn't matter. + """ + + def sort_fn(v): + return v.xfId + + styles = [] + names = set() + + for ns in sorted(self.cellStyle, key=sort_fn): + if ns.name in names: + continue + + style = NamedStyle( + name=ns.name, + hidden=ns.hidden, + builtinId = ns.builtinId + ) + names.add(ns.name) + style._set_index(len(styles)) # assign xfId + styles.append(style) + + return NamedStyleList(styles) diff --git a/.venv/Lib/site-packages/openpyxl/styles/numbers.py b/.venv/Lib/site-packages/openpyxl/styles/numbers.py new file mode 100644 index 00000000..f4bf780b --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/styles/numbers.py @@ -0,0 +1,200 @@ +# Copyright (c) 2010-2022 openpyxl + +import re + +from openpyxl.descriptors import ( + String, + Sequence, + Integer, +) +from openpyxl.descriptors.serialisable import Serialisable + + +BUILTIN_FORMATS = { + 0: 'General', + 1: '0', + 2: '0.00', + 3: '#,##0', + 4: '#,##0.00', + 5: '"$"#,##0_);("$"#,##0)', + 6: '"$"#,##0_);[Red]("$"#,##0)', + 7: '"$"#,##0.00_);("$"#,##0.00)', + 8: '"$"#,##0.00_);[Red]("$"#,##0.00)', + 9: '0%', + 10: '0.00%', + 11: '0.00E+00', + 12: '# ?/?', + 13: '# ??/??', + 14: 'mm-dd-yy', + 15: 'd-mmm-yy', + 16: 'd-mmm', + 17: 'mmm-yy', + 18: 'h:mm AM/PM', + 19: 'h:mm:ss AM/PM', + 20: 'h:mm', + 21: 'h:mm:ss', + 22: 'm/d/yy h:mm', + + 37: '#,##0_);(#,##0)', + 38: '#,##0_);[Red](#,##0)', + 39: '#,##0.00_);(#,##0.00)', + 40: '#,##0.00_);[Red](#,##0.00)', + + 41: r'_(* #,##0_);_(* \(#,##0\);_(* "-"_);_(@_)', + 42: r'_("$"* #,##0_);_("$"* \(#,##0\);_("$"* "-"_);_(@_)', + 43: r'_(* #,##0.00_);_(* \(#,##0.00\);_(* "-"??_);_(@_)', + + 44: r'_("$"* #,##0.00_)_("$"* \(#,##0.00\)_("$"* "-"??_)_(@_)', + 45: 'mm:ss', + 46: '[h]:mm:ss', + 47: 'mmss.0', + 48: '##0.0E+0', + 49: '@', } + +BUILTIN_FORMATS_MAX_SIZE = 164 +BUILTIN_FORMATS_REVERSE = dict( + [(value, key) for key, value in BUILTIN_FORMATS.items()]) + +FORMAT_GENERAL = BUILTIN_FORMATS[0] +FORMAT_TEXT = BUILTIN_FORMATS[49] +FORMAT_NUMBER = BUILTIN_FORMATS[1] +FORMAT_NUMBER_00 = BUILTIN_FORMATS[2] +FORMAT_NUMBER_COMMA_SEPARATED1 = BUILTIN_FORMATS[4] +FORMAT_NUMBER_COMMA_SEPARATED2 = '#,##0.00_-' +FORMAT_PERCENTAGE = BUILTIN_FORMATS[9] +FORMAT_PERCENTAGE_00 = BUILTIN_FORMATS[10] +FORMAT_DATE_YYYYMMDD2 = 'yyyy-mm-dd' +FORMAT_DATE_YYMMDD = 'yy-mm-dd' +FORMAT_DATE_DDMMYY = 'dd/mm/yy' +FORMAT_DATE_DMYSLASH = 'd/m/y' +FORMAT_DATE_DMYMINUS = 'd-m-y' +FORMAT_DATE_DMMINUS = 'd-m' +FORMAT_DATE_MYMINUS = 'm-y' +FORMAT_DATE_XLSX14 = BUILTIN_FORMATS[14] +FORMAT_DATE_XLSX15 = BUILTIN_FORMATS[15] +FORMAT_DATE_XLSX16 = BUILTIN_FORMATS[16] +FORMAT_DATE_XLSX17 = BUILTIN_FORMATS[17] +FORMAT_DATE_XLSX22 = BUILTIN_FORMATS[22] +FORMAT_DATE_DATETIME = 'yyyy-mm-dd h:mm:ss' +FORMAT_DATE_TIME1 = BUILTIN_FORMATS[18] +FORMAT_DATE_TIME2 = BUILTIN_FORMATS[19] +FORMAT_DATE_TIME3 = BUILTIN_FORMATS[20] +FORMAT_DATE_TIME4 = BUILTIN_FORMATS[21] +FORMAT_DATE_TIME5 = BUILTIN_FORMATS[45] +FORMAT_DATE_TIME6 = BUILTIN_FORMATS[21] +FORMAT_DATE_TIME7 = 'i:s.S' +FORMAT_DATE_TIME8 = 'h:mm:ss@' +FORMAT_DATE_TIMEDELTA = '[hh]:mm:ss' +FORMAT_DATE_YYMMDDSLASH = 'yy/mm/dd@' +FORMAT_CURRENCY_USD_SIMPLE = '"$"#,##0.00_-' +FORMAT_CURRENCY_USD = '$#,##0_-' +FORMAT_CURRENCY_EUR_SIMPLE = '[$EUR ]#,##0.00_-' + + +COLORS = r"\[(BLACK|BLUE|CYAN|GREEN|MAGENTA|RED|WHITE|YELLOW)\]" +LITERAL_GROUP = r'".*?"' # anything in quotes +LOCALE_GROUP = r'\[(?!hh?\]|mm?\]|ss?\])[^\]]*\]' # anything in square brackets, except hours or minutes or seconds +STRIP_RE = re.compile(f"{LITERAL_GROUP}|{LOCALE_GROUP}") +TIMEDELTA_RE = re.compile(r'\[hh?\](:mm(:ss(\.0*)?)?)?|\[mm?\](:ss(\.0*)?)?|\[ss?\](\.0*)?', re.I) + + +# Spec 18.8.31 numFmts +# +ve;-ve;zero;text + +def is_date_format(fmt): + if fmt is None: + return False + fmt = fmt.split(";")[0] # only look at the first format + fmt = STRIP_RE.sub("", fmt) # ignore some formats + return re.search(r"[^\\][dmhysDMHYS]", fmt) is not None + + +def is_timedelta_format(fmt): + if fmt is None: + return False + fmt = fmt.split(";")[0] # only look at the first format + return TIMEDELTA_RE.search(fmt) is not None + + +def is_datetime(fmt): + """ + Return date, time or datetime + """ + if not is_date_format(fmt): + return + + DATE = TIME = False + + if any((x in fmt for x in 'dy')): + DATE = True + if any((x in fmt for x in 'hs')): + TIME = True + + if DATE and TIME: + return "datetime" + if DATE: + return "date" + return "time" + + +def is_builtin(fmt): + return fmt in BUILTIN_FORMATS.values() + + +def builtin_format_code(index): + """Return one of the standard format codes by index.""" + try: + fmt = BUILTIN_FORMATS[index] + except KeyError: + fmt = None + return fmt + + +def builtin_format_id(fmt): + """Return the id of a standard style.""" + return BUILTIN_FORMATS_REVERSE.get(fmt) + + +class NumberFormatDescriptor(String): + + def __set__(self, instance, value): + if value is None: + value = FORMAT_GENERAL + super(NumberFormatDescriptor, self).__set__(instance, value) + + +class NumberFormat(Serialisable): + + numFmtId = Integer() + formatCode = String() + + def __init__(self, + numFmtId=None, + formatCode=None, + ): + self.numFmtId = numFmtId + self.formatCode = formatCode + + +class NumberFormatList(Serialisable): + + count = Integer(allow_none=True) + numFmt = Sequence(expected_type=NumberFormat) + + __elements__ = ('numFmt',) + __attrs__ = ("count",) + + def __init__(self, + count=None, + numFmt=(), + ): + self.numFmt = numFmt + + + @property + def count(self): + return len(self.numFmt) + + + def __getitem__(self, idx): + return self.numFmt[idx] diff --git a/.venv/Lib/site-packages/openpyxl/styles/protection.py b/.venv/Lib/site-packages/openpyxl/styles/protection.py new file mode 100644 index 00000000..d420856c --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/styles/protection.py @@ -0,0 +1,17 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.descriptors import Bool +from openpyxl.descriptors.serialisable import Serialisable + + +class Protection(Serialisable): + """Protection options for use in styles.""" + + tagname = "protection" + + locked = Bool() + hidden = Bool() + + def __init__(self, locked=True, hidden=False): + self.locked = locked + self.hidden = hidden diff --git a/.venv/Lib/site-packages/openpyxl/styles/proxy.py b/.venv/Lib/site-packages/openpyxl/styles/proxy.py new file mode 100644 index 00000000..6a4567ce --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/styles/proxy.py @@ -0,0 +1,62 @@ +# Copyright (c) 2010-2022 openpyxl + +from copy import copy + +from openpyxl.compat import deprecated + + +class StyleProxy(object): + """ + Proxy formatting objects so that they cannot be altered + """ + + __slots__ = ('__target') + + def __init__(self, target): + self.__target = target + + + def __repr__(self): + return repr(self.__target) + + + def __getattr__(self, attr): + return getattr(self.__target, attr) + + + def __setattr__(self, attr, value): + if attr != "_StyleProxy__target": + raise AttributeError("Style objects are immutable and cannot be changed." + "Reassign the style with a copy") + super(StyleProxy, self).__setattr__(attr, value) + + + def __copy__(self): + """ + Return a copy of the proxied object. + """ + return copy(self.__target) + + + def __add__(self, other): + """ + Add proxied object to another instance and return the combined object + """ + return self.__target + other + + + @deprecated("Use copy(obj) or cell.obj = cell.obj + other") + def copy(self, **kw): + """Return a copy of the proxied object. Keyword args will be passed through""" + cp = copy(self.__target) + for k, v in kw.items(): + setattr(cp, k, v) + return cp + + + def __eq__(self, other): + return self.__target == other + + + def __ne__(self, other): + return not self == other diff --git a/.venv/Lib/site-packages/openpyxl/styles/styleable.py b/.venv/Lib/site-packages/openpyxl/styles/styleable.py new file mode 100644 index 00000000..f89de63f --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/styles/styleable.py @@ -0,0 +1,152 @@ +# Copyright (c) 2010-2022 openpyxl + +from copy import copy +from warnings import warn + +from .numbers import ( + BUILTIN_FORMATS, + BUILTIN_FORMATS_MAX_SIZE, + BUILTIN_FORMATS_REVERSE, +) +from .proxy import StyleProxy +from .cell_style import StyleArray +from .named_styles import NamedStyle +from .builtins import styles + + +class StyleDescriptor(object): + + def __init__(self, collection, key): + self.collection = collection + self.key = key + + def __set__(self, instance, value): + coll = getattr(instance.parent.parent, self.collection) + if not getattr(instance, "_style"): + instance._style = StyleArray() + setattr(instance._style, self.key, coll.add(value)) + + + def __get__(self, instance, cls): + coll = getattr(instance.parent.parent, self.collection) + if not getattr(instance, "_style"): + instance._style = StyleArray() + idx = getattr(instance._style, self.key) + return StyleProxy(coll[idx]) + + +class NumberFormatDescriptor(object): + + key = "numFmtId" + collection = '_number_formats' + + def __set__(self, instance, value): + coll = getattr(instance.parent.parent, self.collection) + if value in BUILTIN_FORMATS_REVERSE: + idx = BUILTIN_FORMATS_REVERSE[value] + else: + idx = coll.add(value) + BUILTIN_FORMATS_MAX_SIZE + + if not getattr(instance, "_style"): + instance._style = StyleArray() + setattr(instance._style, self.key, idx) + + + def __get__(self, instance, cls): + if not getattr(instance, "_style"): + instance._style = StyleArray() + idx = getattr(instance._style, self.key) + if idx < BUILTIN_FORMATS_MAX_SIZE: + return BUILTIN_FORMATS.get(idx, "General") + coll = getattr(instance.parent.parent, self.collection) + return coll[idx - BUILTIN_FORMATS_MAX_SIZE] + + +class NamedStyleDescriptor(object): + + key = "xfId" + collection = "_named_styles" + + + def __set__(self, instance, value): + if not getattr(instance, "_style"): + instance._style = StyleArray() + coll = getattr(instance.parent.parent, self.collection) + if isinstance(value, NamedStyle): + style = value + if style not in coll: + instance.parent.parent.add_named_style(style) + elif value not in coll.names: + if value in styles: # is it builtin? + style = styles[value] + if style not in coll: + instance.parent.parent.add_named_style(style) + else: + raise ValueError("{0} is not a known style".format(value)) + else: + style = coll[value] + instance._style = copy(style.as_tuple()) + + + def __get__(self, instance, cls): + if not getattr(instance, "_style"): + instance._style = StyleArray() + idx = getattr(instance._style, self.key) + coll = getattr(instance.parent.parent, self.collection) + return coll.names[idx] + + +class StyleArrayDescriptor(object): + + def __init__(self, key): + self.key = key + + def __set__(self, instance, value): + if instance._style is None: + instance._style = StyleArray() + setattr(instance._style, self.key, value) + + + def __get__(self, instance, cls): + if instance._style is None: + return False + return bool(getattr(instance._style, self.key)) + + +class StyleableObject(object): + """ + Base class for styleble objects implementing proxy and lookup functions + """ + + font = StyleDescriptor('_fonts', "fontId") + fill = StyleDescriptor('_fills', "fillId") + border = StyleDescriptor('_borders', "borderId") + number_format = NumberFormatDescriptor() + protection = StyleDescriptor('_protections', "protectionId") + alignment = StyleDescriptor('_alignments', "alignmentId") + style = NamedStyleDescriptor() + quotePrefix = StyleArrayDescriptor('quotePrefix') + pivotButton = StyleArrayDescriptor('pivotButton') + + __slots__ = ('parent', '_style') + + def __init__(self, sheet, style_array=None): + self.parent = sheet + if style_array is not None: + style_array = StyleArray(style_array) + self._style = style_array + + + @property + def style_id(self): + if self._style is None: + self._style = StyleArray() + return self.parent.parent._cell_styles.add(self._style) + + + @property + def has_style(self): + if self._style is None: + return False + return any(self._style) + diff --git a/.venv/Lib/site-packages/openpyxl/styles/stylesheet.py b/.venv/Lib/site-packages/openpyxl/styles/stylesheet.py new file mode 100644 index 00000000..b5741f17 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/styles/stylesheet.py @@ -0,0 +1,263 @@ +# Copyright (c) 2010-2022 openpyxl + +from warnings import warn + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, +) +from openpyxl.descriptors.sequence import NestedSequence +from openpyxl.descriptors.excel import ExtensionList +from openpyxl.utils.indexed_list import IndexedList +from openpyxl.xml.constants import ARC_STYLE, SHEET_MAIN_NS +from openpyxl.xml.functions import fromstring + +from .builtins import styles +from .colors import ColorList, COLOR_INDEX +from .differential import DifferentialStyle +from .table import TableStyleList +from .borders import Border +from .fills import Fill +from .fonts import Font +from .numbers import ( + NumberFormatList, + BUILTIN_FORMATS, + BUILTIN_FORMATS_MAX_SIZE, + BUILTIN_FORMATS_REVERSE, + is_date_format, + is_timedelta_format, + builtin_format_code +) +from .named_styles import ( + _NamedCellStyleList +) +from .cell_style import CellStyle, CellStyleList + + +class Stylesheet(Serialisable): + + tagname = "styleSheet" + + numFmts = Typed(expected_type=NumberFormatList) + fonts = NestedSequence(expected_type=Font, count=True) + fills = NestedSequence(expected_type=Fill, count=True) + borders = NestedSequence(expected_type=Border, count=True) + cellStyleXfs = Typed(expected_type=CellStyleList) + cellXfs = Typed(expected_type=CellStyleList) + cellStyles = Typed(expected_type=_NamedCellStyleList) + dxfs = NestedSequence(expected_type=DifferentialStyle, count=True) + tableStyles = Typed(expected_type=TableStyleList, allow_none=True) + colors = Typed(expected_type=ColorList, allow_none=True) + extLst = Typed(expected_type=ExtensionList, allow_none=True) + + __elements__ = ('numFmts', 'fonts', 'fills', 'borders', 'cellStyleXfs', + 'cellXfs', 'cellStyles', 'dxfs', 'tableStyles', 'colors') + + def __init__(self, + numFmts=None, + fonts=(), + fills=(), + borders=(), + cellStyleXfs=None, + cellXfs=None, + cellStyles=None, + dxfs=(), + tableStyles=None, + colors=None, + extLst=None, + ): + if numFmts is None: + numFmts = NumberFormatList() + self.numFmts = numFmts + self.number_formats = IndexedList() + self.fonts = fonts + self.fills = fills + self.borders = borders + if cellStyleXfs is None: + cellStyleXfs = CellStyleList() + self.cellStyleXfs = cellStyleXfs + if cellXfs is None: + cellXfs = CellStyleList() + self.cellXfs = cellXfs + if cellStyles is None: + cellStyles = _NamedCellStyleList() + self.cellStyles = cellStyles + + self.dxfs = dxfs + self.tableStyles = tableStyles + self.colors = colors + + self.cell_styles = self.cellXfs._to_array() + self.alignments = self.cellXfs.alignments + self.protections = self.cellXfs.prots + self._normalise_numbers() + self.named_styles = self._merge_named_styles() + + + @classmethod + def from_tree(cls, node): + # strip all attribs + attrs = dict(node.attrib) + for k in attrs: + del node.attrib[k] + return super(Stylesheet, cls).from_tree(node) + + + def _merge_named_styles(self): + """ + Merge named style names "cellStyles" with their associated styles + "cellStyleXfs" + """ + named_styles = self.cellStyles.names + + for style in named_styles: + self._expand_named_style(style) + + return named_styles + + + def _expand_named_style(self, named_style): + """ + Bind format definitions for a named style from the associated style + record + """ + xf = self.cellStyleXfs[named_style.xfId] + named_style.font = self.fonts[xf.fontId] + named_style.fill = self.fills[xf.fillId] + named_style.border = self.borders[xf.borderId] + if xf.numFmtId < BUILTIN_FORMATS_MAX_SIZE: + formats = BUILTIN_FORMATS + else: + formats = self.custom_formats + if xf.numFmtId in formats: + named_style.number_format = formats[xf.numFmtId] + if xf.alignment: + named_style.alignment = xf.alignment + if xf.protection: + named_style.protection = xf.protection + + + def _split_named_styles(self, wb): + """ + Convert NamedStyle into separate CellStyle and Xf objects + """ + for style in wb._named_styles: + self.cellStyles.cellStyle.append(style.as_name()) + self.cellStyleXfs.xf.append(style.as_xf()) + + + @property + def custom_formats(self): + return dict([(n.numFmtId, n.formatCode) for n in self.numFmts.numFmt]) + + + def _normalise_numbers(self): + """ + Rebase custom numFmtIds with a floor of 164 when reading stylesheet + And index datetime formats + """ + date_formats = set() + timedelta_formats = set() + custom = self.custom_formats + formats = self.number_formats + for idx, style in enumerate(self.cell_styles): + if style.numFmtId in custom: + fmt = custom[style.numFmtId] + if fmt in BUILTIN_FORMATS_REVERSE: # remove builtins + style.numFmtId = BUILTIN_FORMATS_REVERSE[fmt] + else: + style.numFmtId = formats.add(fmt) + BUILTIN_FORMATS_MAX_SIZE + else: + fmt = builtin_format_code(style.numFmtId) + if is_date_format(fmt): + # Create an index of which styles refer to datetimes + date_formats.add(idx) + if is_timedelta_format(fmt): + # Create an index of which styles refer to timedeltas + timedelta_formats.add(idx) + self.date_formats = date_formats + self.timedelta_formats = timedelta_formats + + + def to_tree(self, tagname=None, idx=None, namespace=None): + tree = super(Stylesheet, self).to_tree(tagname, idx, namespace) + tree.set("xmlns", SHEET_MAIN_NS) + return tree + + +def apply_stylesheet(archive, wb): + """ + Add styles to workbook if present + """ + try: + src = archive.read(ARC_STYLE) + except KeyError: + return wb + + node = fromstring(src) + stylesheet = Stylesheet.from_tree(node) + + if stylesheet.cell_styles: + + wb._borders = IndexedList(stylesheet.borders) + wb._fonts = IndexedList(stylesheet.fonts) + wb._fills = IndexedList(stylesheet.fills) + wb._differential_styles.styles = stylesheet.dxfs + wb._number_formats = stylesheet.number_formats + wb._protections = stylesheet.protections + wb._alignments = stylesheet.alignments + wb._table_styles = stylesheet.tableStyles + + # need to overwrite openpyxl defaults in case workbook has different ones + wb._cell_styles = stylesheet.cell_styles + wb._named_styles = stylesheet.named_styles + wb._date_formats = stylesheet.date_formats + wb._timedelta_formats = stylesheet.timedelta_formats + + for ns in wb._named_styles: + ns.bind(wb) + + else: + warn("Workbook contains no stylesheet, using openpyxl's defaults") + + if not wb._named_styles: + normal = styles['Normal'] + wb.add_named_style(normal) + warn("Workbook contains no default style, apply openpyxl's default") + + if stylesheet.colors is not None: + wb._colors = stylesheet.colors.index + + +def write_stylesheet(wb): + stylesheet = Stylesheet() + stylesheet.fonts = wb._fonts + stylesheet.fills = wb._fills + stylesheet.borders = wb._borders + stylesheet.dxfs = wb._differential_styles.styles + stylesheet.colors = ColorList(indexedColors=wb._colors) + + from .numbers import NumberFormat + fmts = [] + for idx, code in enumerate(wb._number_formats, BUILTIN_FORMATS_MAX_SIZE): + fmt = NumberFormat(idx, code) + fmts.append(fmt) + + stylesheet.numFmts.numFmt = fmts + + xfs = [] + for style in wb._cell_styles: + xf = CellStyle.from_array(style) + + if style.alignmentId: + xf.alignment = wb._alignments[style.alignmentId] + + if style.protectionId: + xf.protection = wb._protections[style.protectionId] + xfs.append(xf) + stylesheet.cellXfs = CellStyleList(xf=xfs) + + stylesheet._split_named_styles(wb) + stylesheet.tableStyles = wb._table_styles + + return stylesheet.to_tree() diff --git a/.venv/Lib/site-packages/openpyxl/styles/table.py b/.venv/Lib/site-packages/openpyxl/styles/table.py new file mode 100644 index 00000000..27906e32 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/styles/table.py @@ -0,0 +1,94 @@ +# Copyright (c) 2010-2022 openpyxl + +from openpyxl.descriptors.serialisable import Serialisable +from openpyxl.descriptors import ( + Typed, + Float, + Bool, + Set, + Integer, + NoneSet, + String, + Sequence +) + +from .colors import Color + + +class TableStyleElement(Serialisable): + + tagname = "tableStyleElement" + + type = Set(values=(['wholeTable', 'headerRow', 'totalRow', 'firstColumn', + 'lastColumn', 'firstRowStripe', 'secondRowStripe', 'firstColumnStripe', + 'secondColumnStripe', 'firstHeaderCell', 'lastHeaderCell', + 'firstTotalCell', 'lastTotalCell', 'firstSubtotalColumn', + 'secondSubtotalColumn', 'thirdSubtotalColumn', 'firstSubtotalRow', + 'secondSubtotalRow', 'thirdSubtotalRow', 'blankRow', + 'firstColumnSubheading', 'secondColumnSubheading', + 'thirdColumnSubheading', 'firstRowSubheading', 'secondRowSubheading', + 'thirdRowSubheading', 'pageFieldLabels', 'pageFieldValues'])) + size = Integer(allow_none=True) + dxfId = Integer(allow_none=True) + + def __init__(self, + type=None, + size=None, + dxfId=None, + ): + self.type = type + self.size = size + self.dxfId = dxfId + + +class TableStyle(Serialisable): + + tagname = "tableStyle" + + name = String() + pivot = Bool(allow_none=True) + table = Bool(allow_none=True) + count = Integer(allow_none=True) + tableStyleElement = Sequence(expected_type=TableStyleElement, allow_none=True) + + __elements__ = ('tableStyleElement',) + + def __init__(self, + name=None, + pivot=None, + table=None, + count=None, + tableStyleElement=(), + ): + self.name = name + self.pivot = pivot + self.table = table + self.count = count + self.tableStyleElement = tableStyleElement + + +class TableStyleList(Serialisable): + + tagname = "tableStyles" + + defaultTableStyle = String(allow_none=True) + defaultPivotStyle = String(allow_none=True) + tableStyle = Sequence(expected_type=TableStyle, allow_none=True) + + __elements__ = ('tableStyle',) + __attrs__ = ("count", "defaultTableStyle", "defaultPivotStyle") + + def __init__(self, + count=None, + defaultTableStyle="TableStyleMedium9", + defaultPivotStyle="PivotStyleLight16", + tableStyle=(), + ): + self.defaultTableStyle = defaultTableStyle + self.defaultPivotStyle = defaultPivotStyle + self.tableStyle = tableStyle + + + @property + def count(self): + return len(self.tableStyle) diff --git a/.venv/Lib/site-packages/openpyxl/utils/__init__.py b/.venv/Lib/site-packages/openpyxl/utils/__init__.py new file mode 100644 index 00000000..a324b006 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/utils/__init__.py @@ -0,0 +1,17 @@ +# Copyright (c) 2010-2022 openpyxl + + +from .cell import ( + absolute_coordinate, + cols_from_range, + column_index_from_string, + coordinate_to_tuple, + get_column_letter, + get_column_interval, + quote_sheetname, + range_boundaries, + range_to_tuple, + rows_from_range, +) + +from .formulas import FORMULAE diff --git a/.venv/Lib/site-packages/openpyxl/utils/bound_dictionary.py b/.venv/Lib/site-packages/openpyxl/utils/bound_dictionary.py new file mode 100644 index 00000000..c9fe27ab --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/utils/bound_dictionary.py @@ -0,0 +1,26 @@ +# Copyright (c) 2010-2022 openpyxl + +from collections import defaultdict + + +class BoundDictionary(defaultdict): + """ + A default dictionary where elements are tightly coupled. + + The factory method is responsible for binding the parent object to the child. + + If a reference attribute is assigned then child objects will have the key assigned to this. + + Otherwise it's just a defaultdict. + """ + + def __init__(self, reference=None, *args, **kw): + self.reference = reference + super(BoundDictionary, self).__init__(*args, **kw) + + + def __getitem__(self, key): + value = super(BoundDictionary, self).__getitem__(key) + if self.reference is not None: + setattr(value, self.reference, key) + return value diff --git a/.venv/Lib/site-packages/openpyxl/utils/cell.py b/.venv/Lib/site-packages/openpyxl/utils/cell.py new file mode 100644 index 00000000..04359409 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/utils/cell.py @@ -0,0 +1,227 @@ +# Copyright (c) 2010-2022 openpyxl + +""" +Collection of utilities used within the package and also available for client code +""" +import re +from string import digits + +from .exceptions import CellCoordinatesException + +# constants +COORD_RE = re.compile(r'^[$]?([A-Za-z]{1,3})[$]?(\d+)$') +COL_RANGE = """[A-Z]{1,3}:[A-Z]{1,3}:""" +ROW_RANGE = r"""\d+:\d+:""" +RANGE_EXPR = r""" +[$]?(?P[A-Za-z]{1,3})? +[$]?(?P\d+)? +(:[$]?(?P[A-Za-z]{1,3})? +[$]?(?P\d+)?)? +""" +ABSOLUTE_RE = re.compile('^' + RANGE_EXPR +'$', re.VERBOSE) +SHEET_TITLE = r""" +(('(?P([^']|'')*)')|(?P[^'^ ^!]*))!""" +SHEETRANGE_RE = re.compile("""{0}(?P{1})(?=,?)""".format( + SHEET_TITLE, RANGE_EXPR), re.VERBOSE) + + +def get_column_interval(start, end): + """ + Given the start and end columns, return all the columns in the series. + + The start and end columns can be either column letters or 1-based + indexes. + """ + if isinstance(start, str): + start = column_index_from_string(start) + if isinstance(end, str): + end = column_index_from_string(end) + return [get_column_letter(x) for x in range(start, end + 1)] + + +def coordinate_from_string(coord_string): + """Convert a coordinate string like 'B12' to a tuple ('B', 12)""" + match = COORD_RE.match(coord_string) + if not match: + msg = f"Invalid cell coordinates ({coord_string})" + raise CellCoordinatesException(msg) + column, row = match.groups() + row = int(row) + if not row: + msg = f"There is no row 0 ({coord_string})" + raise CellCoordinatesException(msg) + return column, row + + +def absolute_coordinate(coord_string): + """Convert a coordinate to an absolute coordinate string (B12 -> $B$12)""" + m = ABSOLUTE_RE.match(coord_string) + if not m: + raise ValueError(f"{coord_string} is not a valid coordinate range") + + d = m.groupdict('') + for k, v in d.items(): + if v: + d[k] = f"${v}" + + if d['max_col'] or d['max_row']: + fmt = "{min_col}{min_row}:{max_col}{max_row}" + else: + fmt = "{min_col}{min_row}" + return fmt.format(**d) + + +def _get_column_letter(col_idx): + """Convert a column number into a column letter (3 -> 'C') + + Right shift the column col_idx by 26 to find column letters in reverse + order. These numbers are 1-based, and can be converted to ASCII + ordinals by adding 64. + + """ + # these indicies corrospond to A -> ZZZ and include all allowed + # columns + if not 1 <= col_idx <= 18278: + raise ValueError("Invalid column index {0}".format(col_idx)) + letters = [] + while col_idx > 0: + col_idx, remainder = divmod(col_idx, 26) + # check for exact division and borrow if needed + if remainder == 0: + remainder = 26 + col_idx -= 1 + letters.append(chr(remainder+64)) + return ''.join(reversed(letters)) + + +_COL_STRING_CACHE = {} +_STRING_COL_CACHE = {} +for i in range(1, 18279): + col = _get_column_letter(i) + _STRING_COL_CACHE[i] = col + _COL_STRING_CACHE[col] = i + + +def get_column_letter(idx,): + """Convert a column index into a column letter + (3 -> 'C') + """ + try: + return _STRING_COL_CACHE[idx] + except KeyError: + raise ValueError("Invalid column index {0}".format(idx)) + + +def column_index_from_string(str_col): + """Convert a column name into a numerical index + ('A' -> 1) + """ + # we use a function argument to get indexed name lookup + try: + return _COL_STRING_CACHE[str_col.upper()] + except KeyError: + raise ValueError("{0} is not a valid column name".format(str_col)) + + +def range_boundaries(range_string): + """ + Convert a range string into a tuple of boundaries: + (min_col, min_row, max_col, max_row) + Cell coordinates will be converted into a range with the cell at both end + """ + msg = "{0} is not a valid coordinate or range".format(range_string) + m = ABSOLUTE_RE.match(range_string) + if not m: + raise ValueError(msg) + + min_col, min_row, sep, max_col, max_row = m.groups() + + if sep: + cols = min_col, max_col + rows = min_row, max_row + + if not ( + all(cols + rows) or + all(cols) and not any(rows) or + all(rows) and not any(cols) + ): + raise ValueError(msg) + + if min_col is not None: + min_col = column_index_from_string(min_col) + + if min_row is not None: + min_row = int(min_row) + + if max_col is not None: + max_col = column_index_from_string(max_col) + else: + max_col = min_col + + if max_row is not None: + max_row = int(max_row) + else: + max_row = min_row + + return min_col, min_row, max_col, max_row + + +def rows_from_range(range_string): + """ + Get individual addresses for every cell in a range. + Yields one row at a time. + """ + min_col, min_row, max_col, max_row = range_boundaries(range_string) + rows = range(min_row, max_row + 1) + cols = [get_column_letter(col) for col in range(min_col, max_col + 1)] + for row in rows: + yield tuple('{0}{1}'.format(col, row) for col in cols) + + +def cols_from_range(range_string): + """ + Get individual addresses for every cell in a range. + Yields one row at a time. + """ + min_col, min_row, max_col, max_row = range_boundaries(range_string) + rows = range(min_row, max_row+1) + cols = (get_column_letter(col) for col in range(min_col, max_col+1)) + for col in cols: + yield tuple('{0}{1}'.format(col, row) for row in rows) + + +def coordinate_to_tuple(coordinate): + """ + Convert an Excel style coordinate to (row, colum) tuple + """ + for idx, c in enumerate(coordinate): + if c in digits: + break + col = coordinate[:idx].upper() + row = coordinate[idx:] + return int(row), _COL_STRING_CACHE[col] + + +def range_to_tuple(range_string): + """ + Convert a worksheet range to the sheetname and maximum and minimum + coordinate indices + """ + m = SHEETRANGE_RE.match(range_string) + if m is None: + raise ValueError("Value must be of the form sheetname!A1:E4") + sheetname = m.group("quoted") or m.group("notquoted") + cells = m.group("cells") + boundaries = range_boundaries(cells) + return sheetname, boundaries + + +def quote_sheetname(sheetname): + """ + Add quotes around sheetnames if they contain spaces. + """ + if "'" in sheetname: + sheetname = sheetname.replace("'", "''") + + sheetname = u"'{0}'".format(sheetname) + return sheetname diff --git a/.venv/Lib/site-packages/openpyxl/utils/dataframe.py b/.venv/Lib/site-packages/openpyxl/utils/dataframe.py new file mode 100644 index 00000000..a65c3207 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/utils/dataframe.py @@ -0,0 +1,92 @@ +# Copyright (c) 2010-2022 openpyxl + +from itertools import accumulate +import operator + +from openpyxl.compat.product import prod + + +def dataframe_to_rows(df, index=True, header=True): + """ + Convert a Pandas dataframe into something suitable for passing into a worksheet. + If index is True then the index will be included, starting one row below the header. + If header is True then column headers will be included starting one column to the right. + Formatting should be done by client code. + """ + import numpy + from pandas import Timestamp + blocks = df._data.blocks + ncols = sum(b.shape[0] for b in blocks) + data = [None] * ncols + + for b in blocks: + values = b.values + + if b.dtype.type == numpy.datetime64: + values = numpy.array([Timestamp(v) for v in values.ravel()]) + values = values.reshape(b.shape) + + result = values.tolist() + + for col_loc, col in zip(b.mgr_locs, result): + data[col_loc] = col + + if header: + if df.columns.nlevels > 1: + rows = expand_index(df.columns, header) + else: + rows = [list(df.columns.values)] + for row in rows: + n = [] + for v in row: + if isinstance(v, numpy.datetime64): + v = Timestamp(v) + n.append(v) + row = n + if index: + row = [None]*df.index.nlevels + row + yield row + + if index: + yield df.index.names + + expanded = ([v] for v in df.index) + if df.index.nlevels > 1: + expanded = expand_index(df.index) + + for idx, v in enumerate(expanded): + row = [data[j][idx] for j in range(ncols)] + if index: + row = v + row + yield row + + +def expand_index(index, header=False): + """ + Expand axis or column Multiindex + For columns use header = True + For axes use header = False (default) + """ + + shape = index.levshape + depth = prod(shape) + row = [None] * index.nlevels + lengths = [depth / size for size in accumulate(shape, operator.mul)] # child index lengths + columns = [ [] for l in index.names] # avoid copied list gotchas + + for idx, entry in enumerate(index): + row = [None] * index.nlevels + for level, v in enumerate(entry): + length = lengths[level] + if idx % length: + v = None + row[level] = v + if header: + columns[level].append(v) + + if not header: + yield row + + if header: + for row in columns: + yield row diff --git a/.venv/Lib/site-packages/openpyxl/utils/datetime.py b/.venv/Lib/site-packages/openpyxl/utils/datetime.py new file mode 100644 index 00000000..2cf3a904 --- /dev/null +++ b/.venv/Lib/site-packages/openpyxl/utils/datetime.py @@ -0,0 +1,140 @@ +# Copyright (c) 2010-2022 openpyxl + +"""Manage Excel date weirdness.""" + +# Python stdlib imports +import datetime +from math import isnan +import re + + +# constants +MAC_EPOCH = datetime.datetime(1904, 1, 1) +WINDOWS_EPOCH = datetime.datetime(1899, 12, 30) +CALENDAR_WINDOWS_1900 = 2415018.5 # Julian date of WINDOWS_EPOCH +CALENDAR_MAC_1904 = 2416480.5 # Julian date of MAC_EPOCH +CALENDAR_WINDOWS_1900 = WINDOWS_EPOCH +CALENDAR_MAC_1904 = MAC_EPOCH +SECS_PER_DAY = 86400 + +ISO_FORMAT = '%Y-%m-%dT%H:%M:%SZ' +ISO_REGEX = re.compile(r''' +(?P(?P\d{4})-(?P\d{2})-(?P\d{2}))?T? +(?P