Version 0.1

Added sidebar, Dashboard, Line Graph
This commit is contained in:
Ayxan
2022-05-23 04:12:37 +04:00
parent d660f2a4ca
commit 9e90b4a150
204 changed files with 29218 additions and 109 deletions

View File

@ -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

View File

@ -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

View File

@ -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<min_col>[A-Za-z]{1,3})?
[$]?(?P<min_row>\d+)?
(:[$]?(?P<max_col>[A-Za-z]{1,3})?
[$]?(?P<max_row>\d+)?)?
"""
ABSOLUTE_RE = re.compile('^' + RANGE_EXPR +'$', re.VERBOSE)
SHEET_TITLE = r"""
(('(?P<quoted>([^']|'')*)')|(?P<notquoted>[^'^ ^!]*))!"""
SHEETRANGE_RE = re.compile("""{0}(?P<cells>{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

View File

@ -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

View File

@ -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<date>(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2}))?T?
(?P<time>(?P<hour>\d{2}):(?P<minute>\d{2})(:(?P<second>\d{2})(?P<microsecond>\.\d{1,3})?)?)?Z?''',
re.VERBOSE)
ISO_DURATION = re.compile(r'PT((?P<hours>\d+)H)?((?P<minutes>\d+)M)?((?P<seconds>\d+(\.\d{1,3})?)S)?')
def to_ISO8601(dt):
"""Convert from a datetime to a timestamp string."""
if hasattr(dt, "microsecond") and dt.microsecond:
return dt.isoformat(timespec="milliseconds")
return dt.isoformat()
def from_ISO8601(formatted_string):
"""Convert from a timestamp string to a datetime object. According to
18.17.4 in the specification the following ISO 8601 formats are
supported.
Dates B.1.1 and B.2.1
Times B.1.2 and B.2.2
Datetimes B.1.3 and B.2.3
There is no concept of timedeltas in the specification, but Excel
writes them (in strict OOXML mode), so these are also understood.
"""
if not formatted_string:
return None
match = ISO_REGEX.match(formatted_string)
if match and any(match.groups()):
parts = match.groupdict(0)
for key in ["year", "month", "day", "hour", "minute", "second"]:
if parts[key]:
parts[key] = int(parts[key])
if parts["microsecond"]:
parts["microsecond"] = int(float(parts['microsecond']) * 1_000_000)
if not parts["date"]:
dt = datetime.time(parts['hour'], parts['minute'], parts['second'], parts["microsecond"])
elif not parts["time"]:
dt = datetime.date(parts['year'], parts['month'], parts['day'])
else:
del parts["time"]
del parts["date"]
dt = datetime.datetime(**parts)
return dt
match = ISO_DURATION.match(formatted_string)
if match and any(match.groups()):
parts = match.groupdict(0)
for key, val in parts.items():
if val:
parts[key] = float(val)
return datetime.timedelta(**parts)
raise ValueError("Invalid datetime value {}".format(formatted_string))
def to_excel(dt, epoch=WINDOWS_EPOCH):
"""Convert Python datetime to Excel serial"""
if isinstance(dt, datetime.time):
return time_to_days(dt)
if isinstance(dt, datetime.timedelta):
return timedelta_to_days(dt)
if isnan(dt.year): # Pandas supports Not a Date
return
if not hasattr(dt, "date"):
dt = datetime.datetime.combine(dt, datetime.time())
# rebase on epoch and adjust for < 1900-03-01
days = (dt - epoch).days
if 0 < days <= 60 and epoch == WINDOWS_EPOCH:
days -= 1
return days + time_to_days(dt)
def from_excel(value, epoch=WINDOWS_EPOCH, timedelta=False):
"""Convert Excel serial to Python datetime"""
if value is None:
return
if timedelta:
td = datetime.timedelta(days=value)
if td.microseconds:
# round to millisecond precision
td = datetime.timedelta(seconds=td.total_seconds() // 1,
microseconds=round(td.microseconds, -3))
return td
day, fraction = divmod(value, 1)
diff = datetime.timedelta(milliseconds=round(fraction * SECS_PER_DAY * 1000))
if 0 <= value < 1 and diff.days == 0:
return days_to_time(diff)
if 0 < value < 60 and epoch == WINDOWS_EPOCH:
day += 1
return epoch + datetime.timedelta(days=day) + diff
def time_to_days(value):
"""Convert a time value to fractions of day"""
return (
(value.hour * 3600)
+ (value.minute * 60)
+ value.second
+ value.microsecond / 10**6
) / SECS_PER_DAY
def timedelta_to_days(value):
"""Convert a timedelta value to fractions of a day"""
return value.total_seconds() / SECS_PER_DAY
def days_to_time(value):
mins, seconds = divmod(value.seconds, 60)
hours, mins = divmod(mins, 60)
return datetime.time(hours, mins, seconds, value.microseconds)

View File

@ -0,0 +1,43 @@
# Copyright (c) 2010-2022 openpyxl
"""
OOXML has non-standard escaping for characters < \031
"""
import re
def escape(value):
r"""
Convert ASCII < 31 to OOXML: \n == _x + hex(ord(\n)) + _
"""
CHAR_REGEX = re.compile(r"[\001-\031]")
def _sub(match):
"""
Callback to escape chars
"""
return "_x{:0>4x}_".format(ord(match.group(0)))
return CHAR_REGEX.sub(_sub, value)
def unescape(value):
r"""
Convert escaped strings to ASCIII: _x000a_ == \n
"""
ESCAPED_REGEX = re.compile("_x([0-9A-Fa-f]{4})_")
def _sub(match):
"""
Callback to unescape chars
"""
return chr(int(match.group(1), 16))
if "_x" in value:
value = ESCAPED_REGEX.sub(_sub, value)
return value

View File

@ -0,0 +1,34 @@
# Copyright (c) 2010-2022 openpyxl
"""Definitions for openpyxl shared exception classes."""
class CellCoordinatesException(Exception):
"""Error for converting between numeric and A1-style cell references."""
class IllegalCharacterError(Exception):
"""The data submitted which cannot be used directly in Excel files. It
must be removed or escaped."""
class NamedRangeException(Exception):
"""Error for badly formatted named ranges."""
class SheetTitleException(Exception):
"""Error for bad sheet names."""
class InvalidFileException(Exception):
"""Error for trying to open a non-ooxml file."""
class ReadOnlyWorkbookException(Exception):
"""Error for trying to modify a read-only workbook"""
class WorkbookAlreadySaved(Exception):
"""Error when attempting to perform operations on a dump workbook
while it has already been dumped once"""

View File

@ -0,0 +1,9 @@
# Copyright (c) 2010-2022 openpyxl
"""
List of builtin formulae
"""
FORMULAE = ("CUBEKPIMEMBER", "CUBEMEMBER", "CUBEMEMBERPROPERTY", "CUBERANKEDMEMBER", "CUBESET", "CUBESETCOUNT", "CUBEVALUE", "DAVERAGE", "DCOUNT", "DCOUNTA", "DGET", "DMAX", "DMIN", "DPRODUCT", "DSTDEV", "DSTDEVP", "DSUM", "DVAR", "DVARP", "DATE", "DATEDIF", "DATEVALUE", "DAY", "DAYS360", "EDATE", "EOMONTH", "HOUR", "MINUTE", "MONTH", "NETWORKDAYS", "NETWORKDAYS.INTL", "NOW", "SECOND", "TIME", "TIMEVALUE", "TODAY", "WEEKDAY", "WEEKNUM", "WORKDAY ", "WORKDAY.INTL", "YEAR", "YEARFRAC", "BESSELI", "BESSELJ", "BESSELK", "BESSELY", "BIN2DEC", "BIN2HEX", "BIN2OCT", "COMPLEX", "CONVERT", "DEC2BIN", "DEC2HEX", "DEC2OCT", "DELTA", "ERF", "ERFC", "GESTEP", "HEX2BIN", "HEX2DEC", "HEX2OCT", "IMABS", "IMAGINARY", "IMARGUMENT", "IMCONJUGATE", "IMCOS", "IMDIV", "IMEXP", "IMLN", "IMLOG10", "IMLOG2", "IMPOWER", "IMPRODUCT", "IMREAL", "IMSIN", "IMSQRT", "IMSUB", "IMSUM", "OCT2BIN", "OCT2DEC", "OCT2HEX", "ACCRINT", "ACCRINTM", "AMORDEGRC", "AMORLINC", "COUPDAYBS", "COUPDAYS", "COUPDAYSNC", "COUPNCD", "COUPNUM", "COUPPCD", "CUMIPMT", "CUMPRINC", "DB", "DDB", "DISC", "DOLLARDE", "DOLLARFR", "DURATION", "EFFECT", "FV", "FVSCHEDULE", "INTRATE", "IPMT", "IRR", "ISPMT", "MDURATION", "MIRR", "NOMINAL", "NPER", "NPV", "ODDFPRICE", "ODDFYIELD", "ODDLPRICE", "ODDLYIELD", "PMT", "PPMT", "PRICE", "PRICEDISC", "PRICEMAT", "PV", "RATE", "RECEIVED", "SLN", "SYD", "TBILLEQ", "TBILLPRICE", "TBILLYIELD", "VDB", "XIRR", "XNPV", "YIELD", "YIELDDISC", "YIELDMAT", "CELL", "ERROR.TYPE", "INFO", "ISBLANK", "ISERR", "ISERROR", "ISEVEN", "ISLOGICAL", "ISNA", "ISNONTEXT", "ISNUMBER", "ISODD", "ISREF", "ISTEXT", "N", "NA", "TYPE", "AND", "FALSE", "IF", "IFERROR", "NOT", "OR", "TRUE ADDRESS", "AREAS", "CHOOSE", "COLUMN", "COLUMNS", "GETPIVOTDATA", "HLOOKUP", "HYPERLINK", "INDEX", "INDIRECT", "LOOKUP", "MATCH", "OFFSET", "ROW", "ROWS", "RTD", "TRANSPOSE", "VLOOKUP", "ABS", "ACOS", "ACOSH", "ASIN", "ASINH", "ATAN", "ATAN2", "ATANH", "CEILING", "COMBIN", "COS", "COSH", "DEGREES", "ECMA.CEILING", "EVEN", "EXP", "FACT", "FACTDOUBLE", "FLOOR", "GCD", "INT", "ISO.CEILING", "LCM", "LN", "LOG", "LOG10", "MDETERM", "MINVERSE", "MMULT", "MOD", "MROUND", "MULTINOMIAL", "ODD", "PI", "POWER", "PRODUCT", "QUOTIENT", "RADIANS", "RAND", "RANDBETWEEN", "ROMAN", "ROUND", "ROUNDDOWN", "ROUNDUP", "SERIESSUM", "SIGN", "SIN", "SINH", "SQRT", "SQRTPI", "SUBTOTAL", "SUM", "SUMIF", "SUMIFS", "SUMPRODUCT", "SUMSQ", "SUMX2MY2", "SUMX2PY2", "SUMXMY2", "TAN", "TANH", "TRUNC", "AVEDEV", "AVERAGE", "AVERAGEA", "AVERAGEIF", "AVERAGEIFS", "BETADIST", "BETAINV", "BINOMDIST", "CHIDIST", "CHIINV", "CHITEST", "CONFIDENCE", "CORREL", "COUNT", "COUNTA", "COUNTBLANK", "COUNTIF", "COUNTIFS", "COVAR", "CRITBINOM", "DEVSQ", "EXPONDIST", "FDIST", "FINV", "FISHER", "FISHERINV", "FORECAST", "FREQUENCY", "FTEST", "GAMMADIST", "GAMMAINV", "GAMMALN", "GEOMEAN", "GROWTH", "HARMEAN", "HYPGEOMDIST", "INTERCEPT", "KURT", "LARGE", "LINEST", "LOGEST", "LOGINV", "LOGNORMDIST", "MAX", "MAXA", "MEDIAN", "MIN", "MINA", "MODE", "NEGBINOMDIST", "NORMDIST", "NORMINV", "NORMSDIST", "NORMSINV", "PEARSON", "PERCENTILE", "PERCENTRANK", "PERMUT", "POISSON", "PROB", "QUARTILE", "RANK", "RSQ", "SKEW", "SLOPE", "SMALL", "STANDARDIZE", "STDEV STDEVA", "STDEVP", "STDEVPA STEYX", "TDIST", "TINV", "TREND", "TRIMMEAN", "TTEST", "VAR", "VARA", "VARP", "VARPA", "WEIBULL", "ZTEST", "ASC", "BAHTTEXT", "CHAR", "CLEAN", "CODE", "CONCATENATE", "DOLLAR", "EXACT", "FIND", "FINDB", "FIXED", "JIS", "LEFT", "LEFTB", "LEN", "LENB", "LOWER", "MID", "MIDB", "PHONETIC", "PROPER", "REPLACE", "REPLACEB", "REPT", "RIGHT", "RIGHTB", "SEARCH", "SEARCHB", "SUBSTITUTE", "T", "TEXT", "TRIM", "UPPER", "VALUE")
FORMULAE = frozenset(FORMULAE)

View File

@ -0,0 +1,49 @@
# Copyright (c) 2010-2022 openpyxl
class IndexedList(list):
"""
List with optimised access by value
Based on Alex Martelli's recipe
http://code.activestate.com/recipes/52303-the-auxiliary-dictionary-idiom-for-sequences-with-/
"""
_dict = {}
def __init__(self, iterable=None):
self.clean = True
self._dict = {}
if iterable is not None:
self.clean = False
for idx, val in enumerate(iterable):
self._dict[val] = idx
list.append(self, val)
def _rebuild_dict(self):
self._dict = {}
idx = 0
for value in self:
if value not in self._dict:
self._dict[value] = idx
idx += 1
self.clean = True
def __contains__(self, value):
if not self.clean:
self._rebuild_dict()
return value in self._dict
def index(self, value):
if value in self:
return self._dict[value]
raise ValueError
def append(self, value):
if value not in self._dict:
self._dict[value] = len(self)
list.append(self, value)
def add(self, value):
self.append(value)
return self._dict[value]

View File

@ -0,0 +1,60 @@
# Copyright (c) 2010-2022 openpyxl
"""
Type inference functions
"""
import datetime
import re
from openpyxl.styles import numbers
PERCENT_REGEX = re.compile(r'^(?P<number>\-?[0-9]*\.?[0-9]*\s?)\%$')
TIME_REGEX = re.compile(r"""
^(?: # HH:MM and HH:MM:SS
(?P<hour>[0-1]{0,1}[0-9]{2}):
(?P<minute>[0-5][0-9]):?
(?P<second>[0-5][0-9])?$)
|
^(?: # MM:SS.
([0-5][0-9]):
([0-5][0-9])?\.
(?P<microsecond>\d{1,6}))
""", re.VERBOSE)
NUMBER_REGEX = re.compile(r'^-?([\d]|[\d]+\.[\d]*|\.[\d]+|[1-9][\d]+\.?[\d]*)((E|e)[-+]?[\d]+)?$')
def cast_numeric(value):
"""Explicity convert a string to a numeric value"""
if NUMBER_REGEX.match(value):
try:
return int(value)
except ValueError:
return float(value)
def cast_percentage(value):
"""Explicitly convert a string to numeric value and format as a
percentage"""
match = PERCENT_REGEX.match(value)
if match:
return float(match.group('number')) / 100
def cast_time(value):
"""Explicitly convert a string to a number and format as datetime or
time"""
match = TIME_REGEX.match(value)
if match:
if match.group("microsecond") is not None:
value = value[:12]
pattern = "%M:%S.%f"
#fmt = numbers.FORMAT_DATE_TIME5
elif match.group('second') is None:
#fmt = numbers.FORMAT_DATE_TIME3
pattern = "%H:%M"
else:
pattern = "%H:%M:%S"
#fmt = numbers.FORMAT_DATE_TIME6
value = datetime.datetime.strptime(value, pattern)
return value.time()

View File

@ -0,0 +1,22 @@
# Copyright (c) 2010-2022 openpyxl
def hash_password(plaintext_password=''):
"""
Create a password hash from a given string for protecting a worksheet
only. This will not work for encrypting a workbook.
This method is based on the algorithm provided by
Daniel Rentz of OpenOffice and the PEAR package
Spreadsheet_Excel_Writer by Xavier Noguer <xnoguer@rezebra.com>.
See also http://blogs.msdn.com/b/ericwhite/archive/2008/02/23/the-legacy-hashing-algorithm-in-open-xml.aspx
"""
password = 0x0000
for idx, char in enumerate(plaintext_password, 1):
value = ord(char) << idx
rotated_bits = value >> 15
value &= 0x7fff
password ^= (value | rotated_bits)
password ^= len(plaintext_password)
password ^= 0xCE4B
return str(hex(password)).upper()[2:]

View File

@ -0,0 +1,108 @@
from __future__ import division
# Copyright (c) 2010-2022 openpyxl
import math
#constants
DEFAULT_ROW_HEIGHT = 15. # Default row height measured in point size.
BASE_COL_WIDTH = 8 # in characters
DEFAULT_COLUMN_WIDTH = BASE_COL_WIDTH + 5
# = baseColumnWidth + {margin padding (2 pixels on each side, totalling 4 pixels)} + {gridline (1pixel)}
DEFAULT_LEFT_MARGIN = 0.7 # in inches, = right margin
DEFAULT_TOP_MARGIN = 0.7874 # in inches = bottom margin
DEFAULT_HEADER = 0.3 # in inches
# Conversion functions
"""
From the ECMA Spec (4th Edition part 1)
Page setup: "Left Page Margin in inches" p. 1647
Docs from
http://startbigthinksmall.wordpress.com/2010/01/04/points-inches-and-emus-measuring-units-in-office-open-xml/
See also http://msdn.microsoft.com/en-us/library/dd560821(v=office.12).aspx
dxa: The main unit in OOXML is a twentieth of a point. Also called twips.
pt: point. In Excel there are 72 points to an inch
hp: half-points are used to specify font sizes. A font-size of 12pt equals 24 half points
pct: Half-points are used to specify font sizes. A font-size of 12pt equals 24 half points
EMU: English Metric Unit, EMUs are used for coordinates in vector-based
drawings and embedded pictures. One inch equates to 914400 EMUs and a
centimeter is 360000. For bitmaps the default resolution is 96 dpi (known as
PixelsPerInch in Excel). Spec p. 1122
For radial geometry Excel uses integert units of 1/60000th of a degree.
"""
def inch_to_dxa(value):
"""1 inch = 72 * 20 dxa"""
return int(value * 20 * 72)
def dxa_to_inch(value):
return value / 72 / 20
def dxa_to_cm(value):
return 2.54 * dxa_to_inch(value)
def cm_to_dxa(value):
emu = cm_to_EMU(value)
inch = EMU_to_inch(emu)
return inch_to_dxa(inch)
def pixels_to_EMU(value):
"""1 pixel = 9525 EMUs"""
return int(value * 9525)
def EMU_to_pixels(value):
return round(value / 9525)
def cm_to_EMU(value):
"""1 cm = 360000 EMUs"""
return int(value * 360000)
def EMU_to_cm(value):
return round(value / 360000, 4)
def inch_to_EMU(value):
"""1 inch = 914400 EMUs"""
return int(value * 914400)
def EMU_to_inch(value):
return round(value / 914400, 4)
def pixels_to_points(value, dpi=96):
"""96 dpi, 72i"""
return value * 72 / dpi
def points_to_pixels(value, dpi=96):
return int(math.ceil(value * dpi / 72))
def degrees_to_angle(value):
"""1 degree = 60000 angles"""
return int(round(value * 60000))
def angle_to_degrees(value):
return round(value / 60000, 2)
def short_color(color):
""" format a color to its short size """
if len(color) > 6:
return color[2:]
return color