Updated script that can be controled by Nodejs web app

This commit is contained in:
mac OS
2024-11-25 12:24:18 +07:00
parent c440eda1f4
commit 8b0ab2bd3a
8662 changed files with 1803808 additions and 34 deletions

View File

@ -0,0 +1,4 @@
# Copyright (c) 2010-2024 openpyxl
from .cell import Cell, WriteOnlyCell, MergedCell
from .read_only import ReadOnlyCell

View File

@ -0,0 +1,136 @@
# Copyright (c) 2010-2024 openpyxl
from openpyxl.compat import safe_string
from openpyxl.xml.functions import Element, SubElement, whitespace, XML_NS
from openpyxl import LXML
from openpyxl.utils.datetime import to_excel, to_ISO8601
from datetime import timedelta
from openpyxl.worksheet.formula import DataTableFormula, ArrayFormula
from openpyxl.cell.rich_text import CellRichText
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':
attrib = {}
if isinstance(value, ArrayFormula):
attrib = dict(value)
value = value.text
elif isinstance(value, DataTableFormula):
attrib = dict(value)
value = None
formula = SubElement(el, 'f', attrib)
if value is not None and not attrib.get('t') == "dataTable":
formula.text = value[1:]
value = None
if cell.data_type == 's':
if isinstance(value, CellRichText):
el.append(value.to_tree())
else:
inline_string = Element("is")
text = Element('t')
text.text = value
whitespace(text)
inline_string.append(text)
el.append(inline_string)
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':
attrib = {}
if isinstance(value, ArrayFormula):
attrib = dict(value)
value = value.text
elif isinstance(value, DataTableFormula):
attrib = dict(value)
value = None
with xf.element('f', attrib):
if value is not None and not attrib.get('t') == "dataTable":
xf.write(value[1:])
value = None
if cell.data_type == 's':
if isinstance(value, CellRichText):
el = value.to_tree()
xf.write(el)
else:
with xf.element("is"):
if isinstance(value, str):
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)
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

View File

@ -0,0 +1,332 @@
# Copyright (c) 2010-2024 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,
)
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
from openpyxl.worksheet.formula import DataTableFormula, ArrayFormula
from openpyxl.cell.rich_text import CellRichText
# 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, CellRichText)
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'
elif isinstance(value, (DataTableFormula, ArrayFormula)):
dt = 'f'
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().__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 "<Cell {0!r}.{1}>".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(f"{value} cannot be used in worksheets.")
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" and not isinstance(value, CellRichText):
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().__init__(worksheet)
self.row = row
self.column = column
def __repr__(self):
return "<MergedCell {0!r}.{1}>".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)

View File

@ -0,0 +1,136 @@
# Copyright (c) 2010-2024 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:
__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 "<ReadOnlyCell {0!r}.{1}>".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:
__slots__ = ()
value = None
is_date = False
font = None
border = None
fill = None
number_format = None
alignment = None
data_type = 'n'
def __repr__(self):
return "<EmptyCell>"
EMPTY_CELL = EmptyCell()

View File

@ -0,0 +1,202 @@
# Copyright (c) 2010-2024 openpyxl
"""
RichText definition
"""
from copy import copy
from openpyxl.compat import NUMERIC_TYPES
from openpyxl.cell.text import InlineFont, Text
from openpyxl.descriptors import (
Strict,
String,
Typed
)
from openpyxl.xml.functions import Element, whitespace
class TextBlock(Strict):
""" Represents text string in a specific format
This class is used as part of constructing a rich text strings.
"""
font = Typed(expected_type=InlineFont)
text = String()
def __init__(self, font, text):
self.font = font
self.text = text
def __eq__(self, other):
return self.text == other.text and self.font == other.font
def __str__(self):
"""Just retun the text"""
return self.text
def __repr__(self):
font = self.font != InlineFont() and self.font or "default"
return f"{self.__class__.__name__} text={self.text}, font={font}"
def to_tree(self):
el = Element("r")
el.append(self.font.to_tree(tagname="rPr"))
t = Element("t")
t.text = self.text
whitespace(t)
el.append(t)
return el
#
# Rich Text class.
# This class behaves just like a list whose members are either simple strings, or TextBlock() instances.
# In addition, it can be initialized in several ways:
# t = CellRFichText([...]) # initialize with a list.
# t = CellRFichText((...)) # initialize with a tuple.
# t = CellRichText(node) # where node is an Element() from either lxml or xml.etree (has a 'tag' element)
class CellRichText(list):
"""Represents a rich text string.
Initialize with a list made of pure strings or :class:`TextBlock` elements
Can index object to access or modify individual rich text elements
it also supports the + and += operators between rich text strings
There are no user methods for this class
operations which modify the string will generally call an optimization pass afterwards,
that merges text blocks with identical formats, consecutive pure text strings,
and remove empty strings and empty text blocks
"""
def __init__(self, *args):
if len(args) == 1:
args = args[0]
if isinstance(args, (list, tuple)):
CellRichText._check_rich_text(args)
else:
CellRichText._check_element(args)
args = [args]
else:
CellRichText._check_rich_text(args)
super().__init__(args)
@classmethod
def _check_element(cls, value):
if not isinstance(value, (str, TextBlock, NUMERIC_TYPES)):
raise TypeError(f"Illegal CellRichText element {value}")
@classmethod
def _check_rich_text(cls, rich_text):
for t in rich_text:
CellRichText._check_element(t)
@classmethod
def from_tree(cls, node):
text = Text.from_tree(node)
if text.t:
return (text.t.replace('x005F_', ''),)
s = []
for r in text.r:
t = ""
if r.t:
t = r.t.replace('x005F_', '')
if r.rPr:
s.append(TextBlock(r.rPr, t))
else:
s.append(t)
return cls(s)
# Merge TextBlocks with identical formatting
# remove empty elements
def _opt(self):
last_t = None
l = CellRichText(tuple())
for t in self:
if isinstance(t, str):
if not t:
continue
elif not t.text:
continue
if type(last_t) == type(t):
if isinstance(t, str):
last_t += t
continue
elif last_t.font == t.font:
last_t.text += t.text
continue
if last_t:
l.append(last_t)
last_t = t
if last_t:
# Add remaining TextBlock at end of rich text
l.append(last_t)
super().__setitem__(slice(None), l)
return self
def __iadd__(self, arg):
# copy used here to create new TextBlock() so we don't modify the right hand side in _opt()
CellRichText._check_rich_text(arg)
super().__iadd__([copy(e) for e in list(arg)])
return self._opt()
def __add__(self, arg):
return CellRichText([copy(e) for e in list(self) + list(arg)])._opt()
def __setitem__(self, indx, val):
CellRichText._check_element(val)
super().__setitem__(indx, val)
self._opt()
def append(self, arg):
CellRichText._check_element(arg)
super().append(arg)
def extend(self, arg):
CellRichText._check_rich_text(arg)
super().extend(arg)
def __repr__(self):
return "CellRichText([{}])".format(', '.join((repr(s) for s in self)))
def __str__(self):
return ''.join([str(s) for s in self])
def as_list(self):
"""
Returns a list of the strings contained.
The main reason for this is to make editing easier.
"""
return [str(s) for s in self]
def to_tree(self):
"""
Return the full XML representation
"""
container = Element("is")
for obj in self:
if isinstance(obj, TextBlock):
container.append(obj.to_tree())
else:
el = Element("r")
t = Element("t")
t.text = obj
whitespace(t)
el.append(t)
container.append(el)
return container

View File

@ -0,0 +1,184 @@
# Copyright (c) 2010-2024 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)