123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362 |
- from __future__ import absolute_import
- # Copyright (c) 2010-2019 openpyxl
- from openpyxl.descriptors.serialisable import Serialisable
- from openpyxl.descriptors import (
- Descriptor,
- Alias,
- Typed,
- Bool,
- Integer,
- NoneSet,
- String,
- Sequence,
- )
- from openpyxl.descriptors.excel import ExtensionList, CellRange
- from openpyxl.descriptors.sequence import NestedSequence
- from openpyxl.xml.constants import SHEET_MAIN_NS, REL_NS
- from openpyxl.xml.functions import tostring
- from openpyxl.utils import range_boundaries
- from openpyxl.utils.escape import escape, unescape
- from .related import Related
- from .filters import (
- AutoFilter,
- SortState,
- )
- TABLESTYLES = tuple(
- ["TableStyleMedium{0}".format(i) for i in range(1, 29)]
- + ["TableStyleLight{0}".format(i) for i in range(1, 22)]
- + ["TableStyleDark{0}".format(i) for i in range(1, 12)]
- )
- PIVOTSTYLES = tuple(
- ["PivotStyleMedium{0}".format(i) for i in range(1, 29)]
- + ["PivotStyleLight{0}".format(i) for i in range(1, 29)]
- + ["PivotStyleDark{0}".format(i) for i in range(1, 29)]
- )
- class TableStyleInfo(Serialisable):
- tagname = "tableStyleInfo"
- name = String(allow_none=True)
- showFirstColumn = Bool(allow_none=True)
- showLastColumn = Bool(allow_none=True)
- showRowStripes = Bool(allow_none=True)
- showColumnStripes = Bool(allow_none=True)
- def __init__(self,
- name=None,
- showFirstColumn=None,
- showLastColumn=None,
- showRowStripes=None,
- showColumnStripes=None,
- ):
- self.name = name
- self.showFirstColumn = showFirstColumn
- self.showLastColumn = showLastColumn
- self.showRowStripes = showRowStripes
- self.showColumnStripes = showColumnStripes
- class XMLColumnProps(Serialisable):
- tagname = "xmlColumnPr"
- mapId = Integer()
- xpath = String()
- denormalized = Bool(allow_none=True)
- xmlDataType = String()
- extLst = Typed(expected_type=ExtensionList, allow_none=True)
- __elements__ = ()
- def __init__(self,
- mapId=None,
- xpath=None,
- denormalized=None,
- xmlDataType=None,
- extLst=None,
- ):
- self.mapId = mapId
- self.xpath = xpath
- self.denormalized = denormalized
- self.xmlDataType = xmlDataType
- class TableFormula(Serialisable):
- tagname = "tableFormula"
- ## Note formula is stored as the text value
- array = Bool(allow_none=True)
- attr_text = Descriptor()
- text = Alias('attr_text')
- def __init__(self,
- array=None,
- attr_text=None,
- ):
- self.array = array
- self.attr_text = attr_text
- class TableColumn(Serialisable):
- tagname = "tableColumn"
- id = Integer()
- uniqueName = String(allow_none=True)
- name = String()
- totalsRowFunction = NoneSet(values=(['sum', 'min', 'max', 'average',
- 'count', 'countNums', 'stdDev', 'var', 'custom']))
- totalsRowLabel = String(allow_none=True)
- queryTableFieldId = Integer(allow_none=True)
- headerRowDxfId = Integer(allow_none=True)
- dataDxfId = Integer(allow_none=True)
- totalsRowDxfId = Integer(allow_none=True)
- headerRowCellStyle = String(allow_none=True)
- dataCellStyle = String(allow_none=True)
- totalsRowCellStyle = String(allow_none=True)
- calculatedColumnFormula = Typed(expected_type=TableFormula, allow_none=True)
- totalsRowFormula = Typed(expected_type=TableFormula, allow_none=True)
- xmlColumnPr = Typed(expected_type=XMLColumnProps, allow_none=True)
- extLst = Typed(expected_type=ExtensionList, allow_none=True)
- __elements__ = ('calculatedColumnFormula', 'totalsRowFormula',
- 'xmlColumnPr', 'extLst')
- def __init__(self,
- id=None,
- uniqueName=None,
- name=None,
- totalsRowFunction=None,
- totalsRowLabel=None,
- queryTableFieldId=None,
- headerRowDxfId=None,
- dataDxfId=None,
- totalsRowDxfId=None,
- headerRowCellStyle=None,
- dataCellStyle=None,
- totalsRowCellStyle=None,
- calculatedColumnFormula=None,
- totalsRowFormula=None,
- xmlColumnPr=None,
- extLst=None,
- ):
- self.id = id
- self.uniqueName = uniqueName
- self.name = name
- self.totalsRowFunction = totalsRowFunction
- self.totalsRowLabel = totalsRowLabel
- self.queryTableFieldId = queryTableFieldId
- self.headerRowDxfId = headerRowDxfId
- self.dataDxfId = dataDxfId
- self.totalsRowDxfId = totalsRowDxfId
- self.headerRowCellStyle = headerRowCellStyle
- self.dataCellStyle = dataCellStyle
- self.totalsRowCellStyle = totalsRowCellStyle
- self.calculatedColumnFormula = calculatedColumnFormula
- self.totalsRowFormula = totalsRowFormula
- self.xmlColumnPr = xmlColumnPr
- self.extLst = extLst
- def __iter__(self):
- for k, v in super(TableColumn, self).__iter__():
- if k == 'name':
- v = escape(v)
- yield k, v
- @classmethod
- def from_tree(cls, node):
- self = super(TableColumn, cls).from_tree(node)
- self.name = unescape(self.name)
- return self
- class TableNameDescriptor(String):
- """
- Table names cannot have spaces in them
- """
- def __set__(self, instance, value):
- if value is not None and " " in value:
- raise ValueError("Table names cannot have spaces")
- super(TableNameDescriptor, self).__set__(instance, value)
- class Table(Serialisable):
- _path = "/tables/table{0}.xml"
- mime_type = "application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml"
- _rel_type = REL_NS + "/table"
- _rel_id = None
- tagname = "table"
- id = Integer()
- name = String(allow_none=True)
- displayName = TableNameDescriptor()
- comment = String(allow_none=True)
- ref = CellRange()
- tableType = NoneSet(values=(['worksheet', 'xml', 'queryTable']))
- headerRowCount = Integer(allow_none=True)
- insertRow = Bool(allow_none=True)
- insertRowShift = Bool(allow_none=True)
- totalsRowCount = Integer(allow_none=True)
- totalsRowShown = Bool(allow_none=True)
- published = Bool(allow_none=True)
- headerRowDxfId = Integer(allow_none=True)
- dataDxfId = Integer(allow_none=True)
- totalsRowDxfId = Integer(allow_none=True)
- headerRowBorderDxfId = Integer(allow_none=True)
- tableBorderDxfId = Integer(allow_none=True)
- totalsRowBorderDxfId = Integer(allow_none=True)
- headerRowCellStyle = String(allow_none=True)
- dataCellStyle = String(allow_none=True)
- totalsRowCellStyle = String(allow_none=True)
- connectionId = Integer(allow_none=True)
- autoFilter = Typed(expected_type=AutoFilter, allow_none=True)
- sortState = Typed(expected_type=SortState, allow_none=True)
- tableColumns = NestedSequence(expected_type=TableColumn, count=True)
- tableStyleInfo = Typed(expected_type=TableStyleInfo, allow_none=True)
- extLst = Typed(expected_type=ExtensionList, allow_none=True)
- __elements__ = ('autoFilter', 'sortState', 'tableColumns',
- 'tableStyleInfo')
- def __init__(self,
- id=1,
- displayName=None,
- ref=None,
- name=None,
- comment=None,
- tableType=None,
- headerRowCount=1,
- insertRow=None,
- insertRowShift=None,
- totalsRowCount=None,
- totalsRowShown=None,
- published=None,
- headerRowDxfId=None,
- dataDxfId=None,
- totalsRowDxfId=None,
- headerRowBorderDxfId=None,
- tableBorderDxfId=None,
- totalsRowBorderDxfId=None,
- headerRowCellStyle=None,
- dataCellStyle=None,
- totalsRowCellStyle=None,
- connectionId=None,
- autoFilter=None,
- sortState=None,
- tableColumns=(),
- tableStyleInfo=None,
- extLst=None,
- ):
- self.id = id
- self.displayName = displayName
- if name is None:
- name = displayName
- self.name = name
- self.comment = comment
- self.ref = ref
- self.tableType = tableType
- self.headerRowCount = headerRowCount
- self.insertRow = insertRow
- self.insertRowShift = insertRowShift
- self.totalsRowCount = totalsRowCount
- self.totalsRowShown = totalsRowShown
- self.published = published
- self.headerRowDxfId = headerRowDxfId
- self.dataDxfId = dataDxfId
- self.totalsRowDxfId = totalsRowDxfId
- self.headerRowBorderDxfId = headerRowBorderDxfId
- self.tableBorderDxfId = tableBorderDxfId
- self.totalsRowBorderDxfId = totalsRowBorderDxfId
- self.headerRowCellStyle = headerRowCellStyle
- self.dataCellStyle = dataCellStyle
- self.totalsRowCellStyle = totalsRowCellStyle
- self.connectionId = connectionId
- self.autoFilter = autoFilter
- self.sortState = sortState
- self.tableColumns = tableColumns
- self.tableStyleInfo = tableStyleInfo
- def to_tree(self):
- tree = super(Table, self).to_tree()
- tree.set("xmlns", SHEET_MAIN_NS)
- return tree
- @property
- def path(self):
- """
- Return path within the archive
- """
- return "/xl" + self._path.format(self.id)
- def _write(self, archive):
- """
- Serialise to XML and write to archive
- """
- xml = self.to_tree()
- archive.writestr(self.path[1:], tostring(xml))
- def _initialise_columns(self):
- """
- Create a list of table columns from a cell range
- Always set a ref if we have headers (the default)
- Column headings must be strings and must match cells in the worksheet.
- """
- min_col, min_row, max_col, max_row = range_boundaries(self.ref)
- for idx in range(min_col, max_col+1):
- col = TableColumn(id=idx, name="Column{0}".format(idx))
- self.tableColumns.append(col)
- if self.headerRowCount:
- self.autoFilter = AutoFilter(ref=self.ref)
- class TablePartList(Serialisable):
- tagname = "tableParts"
- count = Integer(allow_none=True)
- tablePart = Sequence(expected_type=Related)
- __elements__ = ('tablePart',)
- __attrs__ = ('count',)
- def __init__(self,
- count=None,
- tablePart=(),
- ):
- self.tablePart = tablePart
- def append(self, part):
- self.tablePart.append(part)
- @property
- def count(self):
- return len(self.tablePart)
- def __bool__(self):
- return bool(self.tablePart)
- __nonzero__ = __bool__
|