697 lines
25 KiB
Python
697 lines
25 KiB
Python
|
###############################################################################
|
||
|
#
|
||
|
# Worksheet - A class for writing Excel Worksheets.
|
||
|
#
|
||
|
# Copyright 2013-2019, John McNamara, jmcnamara@cpan.org
|
||
|
#
|
||
|
import re
|
||
|
import datetime
|
||
|
from warnings import warn
|
||
|
|
||
|
COL_NAMES = {}
|
||
|
range_parts = re.compile(r'(\$?)([A-Z]{1,3})(\$?)(\d+)')
|
||
|
|
||
|
|
||
|
def xl_rowcol_to_cell(row, col, row_abs=False, col_abs=False):
|
||
|
"""
|
||
|
Convert a zero indexed row and column cell reference to a A1 style string.
|
||
|
|
||
|
Args:
|
||
|
row: The cell row. Int.
|
||
|
col: The cell column. Int.
|
||
|
row_abs: Optional flag to make the row absolute. Bool.
|
||
|
col_abs: Optional flag to make the column absolute. Bool.
|
||
|
|
||
|
Returns:
|
||
|
A1 style string.
|
||
|
|
||
|
"""
|
||
|
if row < 0:
|
||
|
warn("Row number %d must be >= 0" % row)
|
||
|
return None
|
||
|
|
||
|
if col < 0:
|
||
|
warn("Col number %d must be >= 0" % col)
|
||
|
return None
|
||
|
|
||
|
row += 1 # Change to 1-index.
|
||
|
row_abs = '$' if row_abs else ''
|
||
|
|
||
|
col_str = xl_col_to_name(col, col_abs)
|
||
|
|
||
|
return col_str + row_abs + str(row)
|
||
|
|
||
|
|
||
|
def xl_rowcol_to_cell_fast(row, col):
|
||
|
"""
|
||
|
Optimized version of the xl_rowcol_to_cell function. Only used internally.
|
||
|
|
||
|
Args:
|
||
|
row: The cell row. Int.
|
||
|
col: The cell column. Int.
|
||
|
|
||
|
Returns:
|
||
|
A1 style string.
|
||
|
|
||
|
"""
|
||
|
if col in COL_NAMES:
|
||
|
col_str = COL_NAMES[col]
|
||
|
else:
|
||
|
col_str = xl_col_to_name(col)
|
||
|
COL_NAMES[col] = col_str
|
||
|
|
||
|
return col_str + str(row + 1)
|
||
|
|
||
|
|
||
|
def xl_col_to_name(col, col_abs=False):
|
||
|
"""
|
||
|
Convert a zero indexed column cell reference to a string.
|
||
|
|
||
|
Args:
|
||
|
col: The cell column. Int.
|
||
|
col_abs: Optional flag to make the column absolute. Bool.
|
||
|
|
||
|
Returns:
|
||
|
Column style string.
|
||
|
|
||
|
"""
|
||
|
col_num = col
|
||
|
if col_num < 0:
|
||
|
warn("Col number %d must be >= 0" % col_num)
|
||
|
return None
|
||
|
|
||
|
col_num += 1 # Change to 1-index.
|
||
|
col_str = ''
|
||
|
col_abs = '$' if col_abs else ''
|
||
|
|
||
|
while col_num:
|
||
|
# Set remainder from 1 .. 26
|
||
|
remainder = col_num % 26
|
||
|
|
||
|
if remainder == 0:
|
||
|
remainder = 26
|
||
|
|
||
|
# Convert the remainder to a character.
|
||
|
col_letter = chr(ord('A') + remainder - 1)
|
||
|
|
||
|
# Accumulate the column letters, right to left.
|
||
|
col_str = col_letter + col_str
|
||
|
|
||
|
# Get the next order of magnitude.
|
||
|
col_num = int((col_num - 1) / 26)
|
||
|
|
||
|
return col_abs + col_str
|
||
|
|
||
|
|
||
|
def xl_cell_to_rowcol(cell_str):
|
||
|
"""
|
||
|
Convert a cell reference in A1 notation to a zero indexed row and column.
|
||
|
|
||
|
Args:
|
||
|
cell_str: A1 style string.
|
||
|
|
||
|
Returns:
|
||
|
row, col: Zero indexed cell row and column indices.
|
||
|
|
||
|
"""
|
||
|
if not cell_str:
|
||
|
return 0, 0
|
||
|
|
||
|
match = range_parts.match(cell_str)
|
||
|
col_str = match.group(2)
|
||
|
row_str = match.group(4)
|
||
|
|
||
|
# Convert base26 column string to number.
|
||
|
expn = 0
|
||
|
col = 0
|
||
|
for char in reversed(col_str):
|
||
|
col += (ord(char) - ord('A') + 1) * (26 ** expn)
|
||
|
expn += 1
|
||
|
|
||
|
# Convert 1-index to zero-index
|
||
|
row = int(row_str) - 1
|
||
|
col -= 1
|
||
|
|
||
|
return row, col
|
||
|
|
||
|
|
||
|
def xl_cell_to_rowcol_abs(cell_str):
|
||
|
"""
|
||
|
Convert an absolute cell reference in A1 notation to a zero indexed
|
||
|
row and column, with True/False values for absolute rows or columns.
|
||
|
|
||
|
Args:
|
||
|
cell_str: A1 style string.
|
||
|
|
||
|
Returns:
|
||
|
row, col, row_abs, col_abs: Zero indexed cell row and column indices.
|
||
|
|
||
|
"""
|
||
|
if not cell_str:
|
||
|
return 0, 0, False, False
|
||
|
|
||
|
match = range_parts.match(cell_str)
|
||
|
|
||
|
col_abs = match.group(1)
|
||
|
col_str = match.group(2)
|
||
|
row_abs = match.group(3)
|
||
|
row_str = match.group(4)
|
||
|
|
||
|
if col_abs:
|
||
|
col_abs = True
|
||
|
else:
|
||
|
col_abs = False
|
||
|
|
||
|
if row_abs:
|
||
|
row_abs = True
|
||
|
else:
|
||
|
row_abs = False
|
||
|
|
||
|
# Convert base26 column string to number.
|
||
|
expn = 0
|
||
|
col = 0
|
||
|
for char in reversed(col_str):
|
||
|
col += (ord(char) - ord('A') + 1) * (26 ** expn)
|
||
|
expn += 1
|
||
|
|
||
|
# Convert 1-index to zero-index
|
||
|
row = int(row_str) - 1
|
||
|
col -= 1
|
||
|
|
||
|
return row, col, row_abs, col_abs
|
||
|
|
||
|
|
||
|
def xl_range(first_row, first_col, last_row, last_col):
|
||
|
"""
|
||
|
Convert zero indexed row and col cell references to a A1:B1 range string.
|
||
|
|
||
|
Args:
|
||
|
first_row: The first cell row. Int.
|
||
|
first_col: The first cell column. Int.
|
||
|
last_row: The last cell row. Int.
|
||
|
last_col: The last cell column. Int.
|
||
|
|
||
|
Returns:
|
||
|
A1:B1 style range string.
|
||
|
|
||
|
"""
|
||
|
range1 = xl_rowcol_to_cell(first_row, first_col)
|
||
|
range2 = xl_rowcol_to_cell(last_row, last_col)
|
||
|
|
||
|
if range1 is None or range2 is None:
|
||
|
warn("Row and column numbers must be >= 0")
|
||
|
return None
|
||
|
|
||
|
return range1 + ':' + range2
|
||
|
|
||
|
|
||
|
def xl_range_abs(first_row, first_col, last_row, last_col):
|
||
|
"""
|
||
|
Convert zero indexed row and col cell references to a $A$1:$B$1 absolute
|
||
|
range string.
|
||
|
|
||
|
Args:
|
||
|
first_row: The first cell row. Int.
|
||
|
first_col: The first cell column. Int.
|
||
|
last_row: The last cell row. Int.
|
||
|
last_col: The last cell column. Int.
|
||
|
|
||
|
Returns:
|
||
|
$A$1:$B$1 style range string.
|
||
|
|
||
|
"""
|
||
|
range1 = xl_rowcol_to_cell(first_row, first_col, True, True)
|
||
|
range2 = xl_rowcol_to_cell(last_row, last_col, True, True)
|
||
|
|
||
|
if range1 is None or range2 is None:
|
||
|
warn("Row and column numbers must be >= 0")
|
||
|
return None
|
||
|
|
||
|
return range1 + ':' + range2
|
||
|
|
||
|
|
||
|
def xl_range_formula(sheetname, first_row, first_col, last_row, last_col):
|
||
|
"""
|
||
|
Convert worksheet name and zero indexed row and col cell references to
|
||
|
a Sheet1!A1:B1 range formula string.
|
||
|
|
||
|
Args:
|
||
|
sheetname: The worksheet name. String.
|
||
|
first_row: The first cell row. Int.
|
||
|
first_col: The first cell column. Int.
|
||
|
last_row: The last cell row. Int.
|
||
|
last_col: The last cell column. Int.
|
||
|
|
||
|
Returns:
|
||
|
A1:B1 style range string.
|
||
|
|
||
|
"""
|
||
|
cell_range = xl_range_abs(first_row, first_col, last_row, last_col)
|
||
|
sheetname = quote_sheetname(sheetname)
|
||
|
|
||
|
return sheetname + '!' + cell_range
|
||
|
|
||
|
|
||
|
def quote_sheetname(sheetname):
|
||
|
"""
|
||
|
Convert a worksheet name to a quoted name if it contains spaces or
|
||
|
special characters.
|
||
|
|
||
|
Args:
|
||
|
sheetname: The worksheet name. String.
|
||
|
|
||
|
Returns:
|
||
|
A quoted worksheet string.
|
||
|
|
||
|
"""
|
||
|
|
||
|
# TODO. Possibly extend this to quote sheetnames that look like ranges.
|
||
|
if not sheetname.isalnum() and not sheetname.startswith("'"):
|
||
|
# Double quote any single quotes.
|
||
|
sheetname = sheetname.replace("'", "''")
|
||
|
|
||
|
# Singe quote the sheet name.
|
||
|
sheetname = "'%s'" % sheetname
|
||
|
|
||
|
return sheetname
|
||
|
|
||
|
|
||
|
def xl_color(color):
|
||
|
# Used in conjunction with the XlsxWriter *color() methods to convert
|
||
|
# a color name into an RGB formatted string. These colors are for
|
||
|
# backward compatibility with older versions of Excel.
|
||
|
named_colors = {
|
||
|
'black': '#000000',
|
||
|
'blue': '#0000FF',
|
||
|
'brown': '#800000',
|
||
|
'cyan': '#00FFFF',
|
||
|
'gray': '#808080',
|
||
|
'green': '#008000',
|
||
|
'lime': '#00FF00',
|
||
|
'magenta': '#FF00FF',
|
||
|
'navy': '#000080',
|
||
|
'orange': '#FF6600',
|
||
|
'pink': '#FF00FF',
|
||
|
'purple': '#800080',
|
||
|
'red': '#FF0000',
|
||
|
'silver': '#C0C0C0',
|
||
|
'white': '#FFFFFF',
|
||
|
'yellow': '#FFFF00',
|
||
|
}
|
||
|
|
||
|
if color in named_colors:
|
||
|
color = named_colors[color]
|
||
|
|
||
|
if not re.match('#[0-9a-fA-F]{6}', color):
|
||
|
warn("Color '%s' isn't a valid Excel color" % color)
|
||
|
|
||
|
# Convert the RGB color to the Excel ARGB format.
|
||
|
return "FF" + color.lstrip('#').upper()
|
||
|
|
||
|
|
||
|
def get_rgb_color(color):
|
||
|
# Convert the user specified color to an RGB color.
|
||
|
rgb_color = xl_color(color)
|
||
|
|
||
|
# Remove leading FF from RGB color for charts.
|
||
|
rgb_color = re.sub(r'^FF', '', rgb_color)
|
||
|
|
||
|
return rgb_color
|
||
|
|
||
|
|
||
|
def get_sparkline_style(style_id):
|
||
|
styles = [
|
||
|
{'series': {'theme': "4", 'tint': "-0.499984740745262"},
|
||
|
'negative': {'theme': "5"},
|
||
|
'markers': {'theme': "4", 'tint': "-0.499984740745262"},
|
||
|
'first': {'theme': "4", 'tint': "0.39997558519241921"},
|
||
|
'last': {'theme': "4", 'tint': "0.39997558519241921"},
|
||
|
'high': {'theme': "4"},
|
||
|
'low': {'theme': "4"},
|
||
|
}, # 0
|
||
|
{'series': {'theme': "4", 'tint': "-0.499984740745262"},
|
||
|
'negative': {'theme': "5"},
|
||
|
'markers': {'theme': "4", 'tint': "-0.499984740745262"},
|
||
|
'first': {'theme': "4", 'tint': "0.39997558519241921"},
|
||
|
'last': {'theme': "4", 'tint': "0.39997558519241921"},
|
||
|
'high': {'theme': "4"},
|
||
|
'low': {'theme': "4"},
|
||
|
}, # 1
|
||
|
{'series': {'theme': "5", 'tint': "-0.499984740745262"},
|
||
|
'negative': {'theme': "6"},
|
||
|
'markers': {'theme': "5", 'tint': "-0.499984740745262"},
|
||
|
'first': {'theme': "5", 'tint': "0.39997558519241921"},
|
||
|
'last': {'theme': "5", 'tint': "0.39997558519241921"},
|
||
|
'high': {'theme': "5"},
|
||
|
'low': {'theme': "5"},
|
||
|
}, # 2
|
||
|
{'series': {'theme': "6", 'tint': "-0.499984740745262"},
|
||
|
'negative': {'theme': "7"},
|
||
|
'markers': {'theme': "6", 'tint': "-0.499984740745262"},
|
||
|
'first': {'theme': "6", 'tint': "0.39997558519241921"},
|
||
|
'last': {'theme': "6", 'tint': "0.39997558519241921"},
|
||
|
'high': {'theme': "6"},
|
||
|
'low': {'theme': "6"},
|
||
|
}, # 3
|
||
|
{'series': {'theme': "7", 'tint': "-0.499984740745262"},
|
||
|
'negative': {'theme': "8"},
|
||
|
'markers': {'theme': "7", 'tint': "-0.499984740745262"},
|
||
|
'first': {'theme': "7", 'tint': "0.39997558519241921"},
|
||
|
'last': {'theme': "7", 'tint': "0.39997558519241921"},
|
||
|
'high': {'theme': "7"},
|
||
|
'low': {'theme': "7"},
|
||
|
}, # 4
|
||
|
{'series': {'theme': "8", 'tint': "-0.499984740745262"},
|
||
|
'negative': {'theme': "9"},
|
||
|
'markers': {'theme': "8", 'tint': "-0.499984740745262"},
|
||
|
'first': {'theme': "8", 'tint': "0.39997558519241921"},
|
||
|
'last': {'theme': "8", 'tint': "0.39997558519241921"},
|
||
|
'high': {'theme': "8"},
|
||
|
'low': {'theme': "8"},
|
||
|
}, # 5
|
||
|
{'series': {'theme': "9", 'tint': "-0.499984740745262"},
|
||
|
'negative': {'theme': "4"},
|
||
|
'markers': {'theme': "9", 'tint': "-0.499984740745262"},
|
||
|
'first': {'theme': "9", 'tint': "0.39997558519241921"},
|
||
|
'last': {'theme': "9", 'tint': "0.39997558519241921"},
|
||
|
'high': {'theme': "9"},
|
||
|
'low': {'theme': "9"},
|
||
|
}, # 6
|
||
|
{'series': {'theme': "4", 'tint': "-0.249977111117893"},
|
||
|
'negative': {'theme': "5"},
|
||
|
'markers': {'theme': "5", 'tint': "-0.249977111117893"},
|
||
|
'first': {'theme': "5", 'tint': "-0.249977111117893"},
|
||
|
'last': {'theme': "5", 'tint': "-0.249977111117893"},
|
||
|
'high': {'theme': "5", 'tint': "-0.249977111117893"},
|
||
|
'low': {'theme': "5", 'tint': "-0.249977111117893"},
|
||
|
}, # 7
|
||
|
{'series': {'theme': "5", 'tint': "-0.249977111117893"},
|
||
|
'negative': {'theme': "6"},
|
||
|
'markers': {'theme': "6", 'tint': "-0.249977111117893"},
|
||
|
'first': {'theme': "6", 'tint': "-0.249977111117893"},
|
||
|
'last': {'theme': "6", 'tint': "-0.249977111117893"},
|
||
|
'high': {'theme': "6", 'tint': "-0.249977111117893"},
|
||
|
'low': {'theme': "6", 'tint': "-0.249977111117893"},
|
||
|
}, # 8
|
||
|
{'series': {'theme': "6", 'tint': "-0.249977111117893"},
|
||
|
'negative': {'theme': "7"},
|
||
|
'markers': {'theme': "7", 'tint': "-0.249977111117893"},
|
||
|
'first': {'theme': "7", 'tint': "-0.249977111117893"},
|
||
|
'last': {'theme': "7", 'tint': "-0.249977111117893"},
|
||
|
'high': {'theme': "7", 'tint': "-0.249977111117893"},
|
||
|
'low': {'theme': "7", 'tint': "-0.249977111117893"},
|
||
|
}, # 9
|
||
|
{'series': {'theme': "7", 'tint': "-0.249977111117893"},
|
||
|
'negative': {'theme': "8"},
|
||
|
'markers': {'theme': "8", 'tint': "-0.249977111117893"},
|
||
|
'first': {'theme': "8", 'tint': "-0.249977111117893"},
|
||
|
'last': {'theme': "8", 'tint': "-0.249977111117893"},
|
||
|
'high': {'theme': "8", 'tint': "-0.249977111117893"},
|
||
|
'low': {'theme': "8", 'tint': "-0.249977111117893"},
|
||
|
}, # 10
|
||
|
{'series': {'theme': "8", 'tint': "-0.249977111117893"},
|
||
|
'negative': {'theme': "9"},
|
||
|
'markers': {'theme': "9", 'tint': "-0.249977111117893"},
|
||
|
'first': {'theme': "9", 'tint': "-0.249977111117893"},
|
||
|
'last': {'theme': "9", 'tint': "-0.249977111117893"},
|
||
|
'high': {'theme': "9", 'tint': "-0.249977111117893"},
|
||
|
'low': {'theme': "9", 'tint': "-0.249977111117893"},
|
||
|
}, # 11
|
||
|
{'series': {'theme': "9", 'tint': "-0.249977111117893"},
|
||
|
'negative': {'theme': "4"},
|
||
|
'markers': {'theme': "4", 'tint': "-0.249977111117893"},
|
||
|
'first': {'theme': "4", 'tint': "-0.249977111117893"},
|
||
|
'last': {'theme': "4", 'tint': "-0.249977111117893"},
|
||
|
'high': {'theme': "4", 'tint': "-0.249977111117893"},
|
||
|
'low': {'theme': "4", 'tint': "-0.249977111117893"},
|
||
|
}, # 12
|
||
|
{'series': {'theme': "4"},
|
||
|
'negative': {'theme': "5"},
|
||
|
'markers': {'theme': "4", 'tint': "-0.249977111117893"},
|
||
|
'first': {'theme': "4", 'tint': "-0.249977111117893"},
|
||
|
'last': {'theme': "4", 'tint': "-0.249977111117893"},
|
||
|
'high': {'theme': "4", 'tint': "-0.249977111117893"},
|
||
|
'low': {'theme': "4", 'tint': "-0.249977111117893"},
|
||
|
}, # 13
|
||
|
{'series': {'theme': "5"},
|
||
|
'negative': {'theme': "6"},
|
||
|
'markers': {'theme': "5", 'tint': "-0.249977111117893"},
|
||
|
'first': {'theme': "5", 'tint': "-0.249977111117893"},
|
||
|
'last': {'theme': "5", 'tint': "-0.249977111117893"},
|
||
|
'high': {'theme': "5", 'tint': "-0.249977111117893"},
|
||
|
'low': {'theme': "5", 'tint': "-0.249977111117893"},
|
||
|
}, # 14
|
||
|
{'series': {'theme': "6"},
|
||
|
'negative': {'theme': "7"},
|
||
|
'markers': {'theme': "6", 'tint': "-0.249977111117893"},
|
||
|
'first': {'theme': "6", 'tint': "-0.249977111117893"},
|
||
|
'last': {'theme': "6", 'tint': "-0.249977111117893"},
|
||
|
'high': {'theme': "6", 'tint': "-0.249977111117893"},
|
||
|
'low': {'theme': "6", 'tint': "-0.249977111117893"},
|
||
|
}, # 15
|
||
|
{'series': {'theme': "7"},
|
||
|
'negative': {'theme': "8"},
|
||
|
'markers': {'theme': "7", 'tint': "-0.249977111117893"},
|
||
|
'first': {'theme': "7", 'tint': "-0.249977111117893"},
|
||
|
'last': {'theme': "7", 'tint': "-0.249977111117893"},
|
||
|
'high': {'theme': "7", 'tint': "-0.249977111117893"},
|
||
|
'low': {'theme': "7", 'tint': "-0.249977111117893"},
|
||
|
}, # 16
|
||
|
{'series': {'theme': "8"},
|
||
|
'negative': {'theme': "9"},
|
||
|
'markers': {'theme': "8", 'tint': "-0.249977111117893"},
|
||
|
'first': {'theme': "8", 'tint': "-0.249977111117893"},
|
||
|
'last': {'theme': "8", 'tint': "-0.249977111117893"},
|
||
|
'high': {'theme': "8", 'tint': "-0.249977111117893"},
|
||
|
'low': {'theme': "8", 'tint': "-0.249977111117893"},
|
||
|
}, # 17
|
||
|
{'series': {'theme': "9"},
|
||
|
'negative': {'theme': "4"},
|
||
|
'markers': {'theme': "9", 'tint': "-0.249977111117893"},
|
||
|
'first': {'theme': "9", 'tint': "-0.249977111117893"},
|
||
|
'last': {'theme': "9", 'tint': "-0.249977111117893"},
|
||
|
'high': {'theme': "9", 'tint': "-0.249977111117893"},
|
||
|
'low': {'theme': "9", 'tint': "-0.249977111117893"},
|
||
|
}, # 18
|
||
|
{'series': {'theme': "4", 'tint': "0.39997558519241921"},
|
||
|
'negative': {'theme': "0", 'tint': "-0.499984740745262"},
|
||
|
'markers': {'theme': "4", 'tint': "0.79998168889431442"},
|
||
|
'first': {'theme': "4", 'tint': "-0.249977111117893"},
|
||
|
'last': {'theme': "4", 'tint': "-0.249977111117893"},
|
||
|
'high': {'theme': "4", 'tint': "-0.499984740745262"},
|
||
|
'low': {'theme': "4", 'tint': "-0.499984740745262"},
|
||
|
}, # 19
|
||
|
{'series': {'theme': "5", 'tint': "0.39997558519241921"},
|
||
|
'negative': {'theme': "0", 'tint': "-0.499984740745262"},
|
||
|
'markers': {'theme': "5", 'tint': "0.79998168889431442"},
|
||
|
'first': {'theme': "5", 'tint': "-0.249977111117893"},
|
||
|
'last': {'theme': "5", 'tint': "-0.249977111117893"},
|
||
|
'high': {'theme': "5", 'tint': "-0.499984740745262"},
|
||
|
'low': {'theme': "5", 'tint': "-0.499984740745262"},
|
||
|
}, # 20
|
||
|
{'series': {'theme': "6", 'tint': "0.39997558519241921"},
|
||
|
'negative': {'theme': "0", 'tint': "-0.499984740745262"},
|
||
|
'markers': {'theme': "6", 'tint': "0.79998168889431442"},
|
||
|
'first': {'theme': "6", 'tint': "-0.249977111117893"},
|
||
|
'last': {'theme': "6", 'tint': "-0.249977111117893"},
|
||
|
'high': {'theme': "6", 'tint': "-0.499984740745262"},
|
||
|
'low': {'theme': "6", 'tint': "-0.499984740745262"},
|
||
|
}, # 21
|
||
|
{'series': {'theme': "7", 'tint': "0.39997558519241921"},
|
||
|
'negative': {'theme': "0", 'tint': "-0.499984740745262"},
|
||
|
'markers': {'theme': "7", 'tint': "0.79998168889431442"},
|
||
|
'first': {'theme': "7", 'tint': "-0.249977111117893"},
|
||
|
'last': {'theme': "7", 'tint': "-0.249977111117893"},
|
||
|
'high': {'theme': "7", 'tint': "-0.499984740745262"},
|
||
|
'low': {'theme': "7", 'tint': "-0.499984740745262"},
|
||
|
}, # 22
|
||
|
{'series': {'theme': "8", 'tint': "0.39997558519241921"},
|
||
|
'negative': {'theme': "0", 'tint': "-0.499984740745262"},
|
||
|
'markers': {'theme': "8", 'tint': "0.79998168889431442"},
|
||
|
'first': {'theme': "8", 'tint': "-0.249977111117893"},
|
||
|
'last': {'theme': "8", 'tint': "-0.249977111117893"},
|
||
|
'high': {'theme': "8", 'tint': "-0.499984740745262"},
|
||
|
'low': {'theme': "8", 'tint': "-0.499984740745262"},
|
||
|
}, # 23
|
||
|
{'series': {'theme': "9", 'tint': "0.39997558519241921"},
|
||
|
'negative': {'theme': "0", 'tint': "-0.499984740745262"},
|
||
|
'markers': {'theme': "9", 'tint': "0.79998168889431442"},
|
||
|
'first': {'theme': "9", 'tint': "-0.249977111117893"},
|
||
|
'last': {'theme': "9", 'tint': "-0.249977111117893"},
|
||
|
'high': {'theme': "9", 'tint': "-0.499984740745262"},
|
||
|
'low': {'theme': "9", 'tint': "-0.499984740745262"},
|
||
|
}, # 24
|
||
|
{'series': {'theme': "1", 'tint': "0.499984740745262"},
|
||
|
'negative': {'theme': "1", 'tint': "0.249977111117893"},
|
||
|
'markers': {'theme': "1", 'tint': "0.249977111117893"},
|
||
|
'first': {'theme': "1", 'tint': "0.249977111117893"},
|
||
|
'last': {'theme': "1", 'tint': "0.249977111117893"},
|
||
|
'high': {'theme': "1", 'tint': "0.249977111117893"},
|
||
|
'low': {'theme': "1", 'tint': "0.249977111117893"},
|
||
|
}, # 25
|
||
|
{'series': {'theme': "1", 'tint': "0.34998626667073579"},
|
||
|
'negative': {'theme': "0", 'tint': "-0.249977111117893"},
|
||
|
'markers': {'theme': "0", 'tint': "-0.249977111117893"},
|
||
|
'first': {'theme': "0", 'tint': "-0.249977111117893"},
|
||
|
'last': {'theme': "0", 'tint': "-0.249977111117893"},
|
||
|
'high': {'theme': "0", 'tint': "-0.249977111117893"},
|
||
|
'low': {'theme': "0", 'tint': "-0.249977111117893"},
|
||
|
}, # 26
|
||
|
{'series': {'rgb': "FF323232"},
|
||
|
'negative': {'rgb': "FFD00000"},
|
||
|
'markers': {'rgb': "FFD00000"},
|
||
|
'first': {'rgb': "FFD00000"},
|
||
|
'last': {'rgb': "FFD00000"},
|
||
|
'high': {'rgb': "FFD00000"},
|
||
|
'low': {'rgb': "FFD00000"},
|
||
|
}, # 27
|
||
|
{'series': {'rgb': "FF000000"},
|
||
|
'negative': {'rgb': "FF0070C0"},
|
||
|
'markers': {'rgb': "FF0070C0"},
|
||
|
'first': {'rgb': "FF0070C0"},
|
||
|
'last': {'rgb': "FF0070C0"},
|
||
|
'high': {'rgb': "FF0070C0"},
|
||
|
'low': {'rgb': "FF0070C0"},
|
||
|
}, # 28
|
||
|
{'series': {'rgb': "FF376092"},
|
||
|
'negative': {'rgb': "FFD00000"},
|
||
|
'markers': {'rgb': "FFD00000"},
|
||
|
'first': {'rgb': "FFD00000"},
|
||
|
'last': {'rgb': "FFD00000"},
|
||
|
'high': {'rgb': "FFD00000"},
|
||
|
'low': {'rgb': "FFD00000"},
|
||
|
}, # 29
|
||
|
{'series': {'rgb': "FF0070C0"},
|
||
|
'negative': {'rgb': "FF000000"},
|
||
|
'markers': {'rgb': "FF000000"},
|
||
|
'first': {'rgb': "FF000000"},
|
||
|
'last': {'rgb': "FF000000"},
|
||
|
'high': {'rgb': "FF000000"},
|
||
|
'low': {'rgb': "FF000000"},
|
||
|
}, # 30
|
||
|
{'series': {'rgb': "FF5F5F5F"},
|
||
|
'negative': {'rgb': "FFFFB620"},
|
||
|
'markers': {'rgb': "FFD70077"},
|
||
|
'first': {'rgb': "FF5687C2"},
|
||
|
'last': {'rgb': "FF359CEB"},
|
||
|
'high': {'rgb': "FF56BE79"},
|
||
|
'low': {'rgb': "FFFF5055"},
|
||
|
}, # 31
|
||
|
{'series': {'rgb': "FF5687C2"},
|
||
|
'negative': {'rgb': "FFFFB620"},
|
||
|
'markers': {'rgb': "FFD70077"},
|
||
|
'first': {'rgb': "FF777777"},
|
||
|
'last': {'rgb': "FF359CEB"},
|
||
|
'high': {'rgb': "FF56BE79"},
|
||
|
'low': {'rgb': "FFFF5055"},
|
||
|
}, # 32
|
||
|
{'series': {'rgb': "FFC6EFCE"},
|
||
|
'negative': {'rgb': "FFFFC7CE"},
|
||
|
'markers': {'rgb': "FF8CADD6"},
|
||
|
'first': {'rgb': "FFFFDC47"},
|
||
|
'last': {'rgb': "FFFFEB9C"},
|
||
|
'high': {'rgb': "FF60D276"},
|
||
|
'low': {'rgb': "FFFF5367"},
|
||
|
}, # 33
|
||
|
{'series': {'rgb': "FF00B050"},
|
||
|
'negative': {'rgb': "FFFF0000"},
|
||
|
'markers': {'rgb': "FF0070C0"},
|
||
|
'first': {'rgb': "FFFFC000"},
|
||
|
'last': {'rgb': "FFFFC000"},
|
||
|
'high': {'rgb': "FF00B050"},
|
||
|
'low': {'rgb': "FFFF0000"},
|
||
|
}, # 34
|
||
|
{'series': {'theme': "3"},
|
||
|
'negative': {'theme': "9"},
|
||
|
'markers': {'theme': "8"},
|
||
|
'first': {'theme': "4"},
|
||
|
'last': {'theme': "5"},
|
||
|
'high': {'theme': "6"},
|
||
|
'low': {'theme': "7"},
|
||
|
}, # 35
|
||
|
{'series': {'theme': "1"},
|
||
|
'negative': {'theme': "9"},
|
||
|
'markers': {'theme': "8"},
|
||
|
'first': {'theme': "4"},
|
||
|
'last': {'theme': "5"},
|
||
|
'high': {'theme': "6"},
|
||
|
'low': {'theme': "7"},
|
||
|
}, # 36
|
||
|
]
|
||
|
|
||
|
return styles[style_id]
|
||
|
|
||
|
|
||
|
def supported_datetime(dt):
|
||
|
# Determine is an argument is a supported datetime object.
|
||
|
return(isinstance(dt, (datetime.datetime,
|
||
|
datetime.date,
|
||
|
datetime.time,
|
||
|
datetime.timedelta)))
|
||
|
|
||
|
|
||
|
def remove_datetime_timezone(dt_obj, remove_timezone):
|
||
|
# Excel doesn't support timezones in datetimes/times so we remove the
|
||
|
# tzinfo from the object if the user has specified that option in the
|
||
|
# constructor.
|
||
|
if remove_timezone:
|
||
|
dt_obj = dt_obj.replace(tzinfo=None)
|
||
|
else:
|
||
|
if dt_obj.tzinfo:
|
||
|
raise TypeError(
|
||
|
"Excel doesn't support timezones in datetimes. "
|
||
|
"Set the tzinfo in the datetime/time object to None or "
|
||
|
"use the 'remove_timezone' Workbook() option")
|
||
|
|
||
|
return dt_obj
|
||
|
|
||
|
|
||
|
def datetime_to_excel_datetime(dt_obj, date_1904, remove_timezone):
|
||
|
# Convert a datetime object to an Excel serial date and time. The integer
|
||
|
# part of the number stores the number of days since the epoch and the
|
||
|
# fractional part stores the percentage of the day.
|
||
|
date_type = dt_obj
|
||
|
|
||
|
if date_1904:
|
||
|
# Excel for Mac date epoch.
|
||
|
epoch = datetime.datetime(1904, 1, 1)
|
||
|
else:
|
||
|
# Default Excel epoch.
|
||
|
epoch = datetime.datetime(1899, 12, 31)
|
||
|
|
||
|
# We handle datetime .datetime, .date and .time objects but convert
|
||
|
# them to datetime.datetime objects and process them in the same way.
|
||
|
if isinstance(dt_obj, datetime.datetime):
|
||
|
dt_obj = remove_datetime_timezone(dt_obj, remove_timezone)
|
||
|
delta = dt_obj - epoch
|
||
|
elif isinstance(dt_obj, datetime.date):
|
||
|
dt_obj = datetime.datetime.fromordinal(dt_obj.toordinal())
|
||
|
delta = dt_obj - epoch
|
||
|
elif isinstance(dt_obj, datetime.time):
|
||
|
dt_obj = datetime.datetime.combine(epoch, dt_obj)
|
||
|
dt_obj = remove_datetime_timezone(dt_obj, remove_timezone)
|
||
|
delta = dt_obj - epoch
|
||
|
elif isinstance(dt_obj, datetime.timedelta):
|
||
|
delta = dt_obj
|
||
|
else:
|
||
|
raise TypeError("Unknown or unsupported datetime type")
|
||
|
|
||
|
# Convert a Python datetime.datetime value to an Excel date number.
|
||
|
excel_time = (delta.days
|
||
|
+ (float(delta.seconds)
|
||
|
+ float(delta.microseconds) / 1E6)
|
||
|
/ (60 * 60 * 24))
|
||
|
|
||
|
# The following is a workaround for the fact that in Excel a time only
|
||
|
# value is represented as 1899-12-31+time whereas in datetime.datetime()
|
||
|
# it is 1900-1-1+time so we need to subtract the 1 day difference.
|
||
|
if (isinstance(date_type, datetime.datetime)
|
||
|
and dt_obj.isocalendar() == (1900, 1, 1)):
|
||
|
excel_time -= 1
|
||
|
|
||
|
# Account for Excel erroneously treating 1900 as a leap year.
|
||
|
if not date_1904 and excel_time > 59:
|
||
|
excel_time += 1
|
||
|
|
||
|
return excel_time
|