forked from Alsan/Post_finder
292 lines
9.1 KiB
Python
292 lines
9.1 KiB
Python
# Copyright (c) 2010-2024 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 attribute 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().__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
|