from copy import deepcopy
from collections.abc import Mapping
from plotly import graph_objects as go
def merge_themes(theme_1: dict, theme_2: dict) -> dict:
"""
Recursively merges theme_2 into theme_1.
"""
merged = deepcopy(
theme_1
) # Start with a copy of theme_1 to avoid modifying original
for key, value in theme_2.items():
if (
key in merged
and isinstance(merged[key], Mapping)
and isinstance(value, Mapping)
):
# Recursively merge dictionaries
merged[key] = merge_themes(merged[key], value)
else:
# Overwrite or add new key-value pairs
merged[key] = value
return merged
# Creating color object based on Material Specs for each of our company colors
# https://m2.material.io/design/color/the-color-system.html#tools-for-picking-colors
[docs]
COLORS = {
# Dark Blue in Ernie
"brand-blue": {
"default": "#0d4753",
900: "#00323c",
800: "#0d4753",
700: "#1c5b67",
600: "#2b6f7d",
500: "#377e8d",
400: "#5591a0",
300: "#70a6b3",
200: "#93c1cc",
100: "#b4dce4",
50: "#d4f3fe",
},
"light-blue": {
"default": "#abd0d9",
900: "#002c58",
800: "#004775",
700: "#005786",
600: "#006796",
500: "#0072a1",
400: "#0084aa",
300: "#2896b2",
200: "#70b2c4",
100: "#abd0d9",
50: "#ddecf0",
},
# Orange in Ernie
"accent": {
"default": "#f48d5c",
900: "#b23d00",
800: "#ca4a00",
700: "#d85200",
600: "#e55904",
500: "#f05f09",
400: "#f27437",
300: "#f48d5c",
200: "#f7ac8b",
100: "#faccb8",
50: "#fae9e6",
},
"green": {
"default": "#288b6f",
900: "#144f38",
800: "#1d6b52",
700: "#237b60",
600: "#288b6f",
500: "#2c987b",
400: "#3ea88d",
300: "#5bb8a0",
200: "#87ccba",
100: "#b6e0d5",
50: "#e1f2ef",
},
"yellow": {
"default": "#f4bb01",
900: "#f46c00",
800: "#f48b00",
700: "#f49b00",
600: "#f4ae00",
500: "#f4bb01",
400: "#f5c525",
300: "#f7d14d",
200: "#f9dd80",
100: "#fbeab2",
50: "#fdf7e1",
},
"red": {
"default": "#e9514e",
900: "#b11a1b",
800: "#c02626",
700: "#cd2d2d",
600: "#de3633",
500: "#ed4034",
400: "#e9514e",
300: "#e07171",
200: "#eb9898",
100: "#fdccd1",
50: "#feebed",
},
"medium-blue": {
"default": "#19889F",
900: "#0a6672",
800: "#19889F",
700: "#209db8",
600: "#209db8",
500: "#2fc2e7",
400: "#3bcbed",
300: "#59d4f3",
200: "#87e1f8",
100: "#b6edfb",
50: "#e2f8fe",
},
"light-orange": {
"default": "#f8bca0",
900: "#8f0400",
800: "#a61800",
700: "#b32100",
600: "#bf2800",
500: "#c82e00",
400: "#d24d00",
300: "#dc6a2f",
200: "#eb9267",
100: "#f8bca0",
50: "#fbe2dc",
},
"light-green": {
"default": "#61d1b1",
900: "#005129",
800: "#006d44",
700: "#007e51",
600: "#008e60",
500: "#009b6c",
400: "#00ac7f",
300: "#00bc93",
200: "#61d1b1",
100: "#a4e3d0",
50: "#daf4ed",
},
"light-yellow": {
"default": "#fedd71",
900: "#ff6500",
800: "#ff8700",
700: "#fe9900",
600: "#fdad00",
500: "#fbbb00",
400: "#fcc500",
300: "#fdd132",
200: "#fedd71",
100: "#ffeaaa",
50: "#fff7dd",
},
"light-red": {
"default": "#ef8280",
900: "#c5362b",
800: "#d44037",
700: "#e1473e",
600: "#f45145",
500: "#ff5845",
400: "#fb665f",
300: "#ef8280",
200: "#f6a4a4",
100: "#ffd3d8",
50: "#ffeef0",
},
"ultra-light-blue": {
"default": "#daeff2",
900: "#003136",
800: "#00515d",
700: "#006374",
600: "#00768b",
500: "#00839b",
400: "#0093a8",
300: "#00a3b6",
200: "#62bdca",
100: "#a2d7df",
50: "#daeff2",
},
"black": {
"default": "#000000",
900: "#000000",
800: "#262626",
700: "#434343",
600: "#555555",
500: "#7b7b7b",
400: "#9d9d9d",
300: "#c4c4c4",
200: "#d9d9d9",
100: "#e9e9e9",
50: "#f5f5f5",
},
# Gray in Ernie
"light-grey": {
"default": "#999999",
900: "#1d1d1d",
800: "#3e3e3e",
700: "#5d5d5d",
600: "#707070",
500: "#999999",
400: "#b8b8b8",
300: "#dcdcdc",
200: "#ebebeb",
100: "#f3f3f3",
50: "#f9f9f9",
},
# Not in Ernie but in our company colors
"blue": {
"default": "#1788b0",
900: "#0a577c",
800: "#16769d",
700: "#1788b0",
600: "#239ac2",
500: "#2ca7cf",
400: "#43b4d4",
300: "#5ec1d8",
200: "#86d2e3",
100: "#b4e4ed",
50: "#e1f5f8",
},
"dark-grey": {
"default": "#444444",
900: "#232323",
800: "#444444",
700: "#636363",
600: "#777777",
500: "#a0a0a0",
400: "#bfbfbf",
300: "#e2e2e2",
200: "#efefef",
100: "#f5f5f5",
50: "#fafafa",
},
}
"""
Standard colors used in Plotly chart components.
"""
COLORS["gray"] = deepcopy(COLORS["light-grey"])
COLORS["orange"] = deepcopy(COLORS["accent"])
COLORS["dark-blue"] = deepcopy(COLORS["brand-blue"])
POSITIVE_COLORSCALE = [
[0.0, "#ffffff"],
[0.1, "#dcf5f9"],
[0.2, "#b8eaf4"],
[0.3, "#95e0ee"],
[0.4, "#72d5e9"],
[0.5, "#2bc0de"],
[0.6, "#1ea7c2"],
[0.7, "#19889f"],
[0.8, "#19889f"],
[0.9, "#136a7c"],
[1.0, "#0d4753"],
]
NEGATIVE_COLORSCALE = [
[1.0, "#ffffff"],
[0.9, "#FBE4E4"],
[0.8, "#f8c9c9"],
[0.7, "#f4a6a4"],
[0.6, "#ef8280"],
[0.5, "#e9514e"],
[0.4, "#e63a37"],
[0.3, "#da1e1b"],
[0.2, "#b61916"],
[0.1, "#911412"],
[0.0, "#6d0f0d"],
]
POSITIVE_AND_NEGATIVE_COLORSCALE = [
[1.0, "#0d4753"],
[0.9, "#19889f"],
[0.8, "#2bc0de"],
[0.7, "#95e0ee"],
[0.6, "#dcf5f9"],
[0.5, "#ffffff"],
[0.4, "#f8c9c9"],
[0.3, "#ef8280"],
[0.2, "#e63a37"],
[0.1, "#b61916"],
[0.0, "#6d0f0d"],
]
DEFAULT_COLORS = [
COLORS[scale]["default"]
for scale in [
"dark-blue",
"light-blue",
"orange",
"green",
"yellow",
"red",
"medium-blue",
"light-orange",
"light-green",
"light-yellow",
"light-red",
"ultra-light-blue",
"black",
"gray",
]
]
TRANSPARENT = "rgba(255,255,255,0)"
WHITE = "rgba(255,255,255,1)"
BACKGROUND_SECONDARY = "#F5F5F5"
TITLE_TO_AXIS_MARGIN = 24
MARGIN_TOP = 64
MARGIN_BOTTOM = 80
MARGIN_RIGHT = 64
MARGIN_LEFT = 64
FONT_URLS = [
"https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap"
]
FONT_FAMILY = "Roboto,system-ui,sans-serif"
TICK_FORMAT_WHOLE_NUMBER = ","
TICK_FORMAT_DECIMAL_NUMBER = ",.2f"
FONT_COLOR = COLORS["dark-grey"]["default"]
LEGEND_FONT_COLOR = COLORS["dark-grey"]["default"]
ANNOTATION_FONT_COLOR = COLORS["dark-grey"]["default"]
AXIS_FONT_COLOR = COLORS["dark-grey"][500]
FONT_SIZE = 16
TITLE_FONT_SIZE = 18
LEGEND_FONT_SIZE = 16
ANNOTATION_FONT_SIZE = 16
SERIES_LINE_WIDTH = 3
BACKGROUND_COLOR = TRANSPARENT
LEGEND_BORDER_COLOR = TRANSPARENT
AXIS_COLOR = COLORS["light-grey"]["default"]
AXIS_GRID_COLOR = COLORS["light-grey"][100]
AXIS_LINE_COLOR = COLORS["light-grey"][200]
AXIS_ZERO_LINE_COLOR = COLORS["dark-grey"][400]
ANNOTATION_LINE_COLOR = COLORS["dark-grey"]["default"]
HOVER_LABEL_BACKGROUND_COLOR = "rgba(255, 255, 255, 0.9)"
ANNOTATION_LINE_WIDTH = 1
SHADE_DEFAULT_COLOR = COLORS["light-grey"]["default"]
SHADE_OPACITY = 0.15
SHADE_LINE_WIDTH = 0
LEGEND_ORIENTATION = "h"
LEGEND_X_POSITION = 0.5
LEGEND_X_ANCHOR = "center"
LEGEND_Y_POSITION = 0
LEGEND_MEDIUM_Y_POSITION = 0 # Use if legend has multiple series
LEGEND_LARGE_Y_POSITION = 0 # Use if legend + x-axis title is visible
LEGEND_Y_ANCHOR = "bottom"
CHART_LOGO_X_POSITION = 1.07875
CHART_LOGO_MEDIUM_X_POSITION = 1.1125
CHART_LOGO_X_SIZE = 0.15
CHART_LOGO_Y_SIZE = 0.15
# Properties here: https://plotly.com/python/reference/layout/
[docs]
YD_CLASSIC_THEME_CONFIG = dict(
height=610,
title={
"font": {
"size": TITLE_FONT_SIZE,
}
},
font={
"family": FONT_FAMILY,
"color": FONT_COLOR,
"size": FONT_SIZE,
},
separators=".,",
paper_bgcolor=BACKGROUND_COLOR,
plot_bgcolor=BACKGROUND_COLOR,
colorway=DEFAULT_COLORS,
legend={
"bordercolor": LEGEND_BORDER_COLOR,
"font": {
"family": FONT_FAMILY,
"color": LEGEND_FONT_COLOR,
"size": LEGEND_FONT_SIZE,
},
"orientation": LEGEND_ORIENTATION,
"x": LEGEND_X_POSITION,
"y": LEGEND_Y_POSITION,
"xref": "container",
"yref": "container",
"xanchor": LEGEND_X_ANCHOR,
"yanchor": LEGEND_Y_ANCHOR,
"title": {
"font": {
"family": FONT_FAMILY,
"color": LEGEND_FONT_COLOR,
"size": LEGEND_FONT_SIZE,
},
"side": "top",
"text": "",
},
"grouptitlefont": {
"family": FONT_FAMILY,
"color": LEGEND_FONT_COLOR,
"size": LEGEND_FONT_SIZE,
},
},
xaxis={
"color": AXIS_COLOR,
"gridcolor": AXIS_GRID_COLOR,
"tickfont": {
"family": FONT_FAMILY,
"size": LEGEND_FONT_SIZE,
"color": LEGEND_FONT_COLOR,
},
"title": {
"font": {
"family": FONT_FAMILY,
"size": LEGEND_FONT_SIZE,
"color": LEGEND_FONT_COLOR,
},
"standoff": TITLE_TO_AXIS_MARGIN,
},
"automargin": True,
"showline": True,
"linecolor": AXIS_LINE_COLOR,
},
yaxis={
"color": AXIS_COLOR,
"gridcolor": AXIS_GRID_COLOR,
"tickfont": {
"family": FONT_FAMILY,
"size": LEGEND_FONT_SIZE,
"color": LEGEND_FONT_COLOR,
},
"title": {
"font": {
"family": FONT_FAMILY,
"size": LEGEND_FONT_SIZE,
"color": LEGEND_FONT_COLOR,
},
"standoff": TITLE_TO_AXIS_MARGIN,
},
"automargin": True,
"showline": True,
"linecolor": AXIS_LINE_COLOR,
"zerolinecolor": AXIS_ZERO_LINE_COLOR,
},
yaxis2={
"color": AXIS_COLOR,
"gridcolor": TRANSPARENT,
"tickfont": {
"family": FONT_FAMILY,
"size": LEGEND_FONT_SIZE,
"color": LEGEND_FONT_COLOR,
},
"title": {
"font": {
"family": FONT_FAMILY,
"size": LEGEND_FONT_SIZE,
"color": LEGEND_FONT_COLOR,
},
"standoff": TITLE_TO_AXIS_MARGIN,
},
"automargin": True,
"showline": True,
"linecolor": AXIS_LINE_COLOR,
"zerolinecolor": AXIS_ZERO_LINE_COLOR,
"overlaying": "y",
},
margin={
"t": MARGIN_TOP,
"b": MARGIN_BOTTOM,
"r": MARGIN_LEFT,
"l": MARGIN_RIGHT,
},
annotations=[
{
"align": "left",
"arrowcolor": ANNOTATION_LINE_COLOR,
"arrowhead": 0,
"arrowsize": 0.5,
"arrowwidth": ANNOTATION_LINE_WIDTH,
"bgcolor": TRANSPARENT,
"bordercolor": TRANSPARENT,
"borderwidth": 0,
"font": {
"family": FONT_FAMILY,
"size": ANNOTATION_FONT_SIZE,
"color": ANNOTATION_FONT_COLOR,
},
"showarrow": False,
"valign": "middle",
}
],
hoverlabel={
"bgcolor": WHITE,
"bordercolor": BACKGROUND_SECONDARY,
"font": {
"family": FONT_FAMILY,
"color": FONT_COLOR,
"size": FONT_SIZE,
},
"align": "left",
},
# TODO: not being respected
shapes=[
{
"type": "line",
"line": {
"color": ANNOTATION_LINE_COLOR,
"dash": "dot",
"width": ANNOTATION_LINE_WIDTH,
},
},
],
)
YD_CLASSIC_THEME = {
"layout": go.Layout(**YD_CLASSIC_THEME_CONFIG),
}
"""
Standard template used when creating Plotly figure objects that are returned from the toolkit. Styling is indended for emailed reports.
"""
ATLAS_FONT_SIZE = 14
ATLAS_AXIS_TITLE_COLOR = "rgba(0, 0, 0, 0.87)"
ATLAS_TICK_COLOR = "rgb(158, 158, 158)"
ATLAS_THEME_CONFIG = merge_themes(
YD_CLASSIC_THEME_CONFIG,
{
"margin": {"t": 16},
"font": {
"color": ATLAS_AXIS_TITLE_COLOR,
"size": ATLAS_FONT_SIZE,
},
"legend": {
"font": {
"color": ATLAS_AXIS_TITLE_COLOR,
"size": ATLAS_FONT_SIZE,
}
},
"yaxis": {
"title": {
"font": {
"color": ATLAS_TICK_COLOR,
"size": ATLAS_FONT_SIZE,
}
},
"tickfont": {
"color": ATLAS_TICK_COLOR,
"size": ATLAS_FONT_SIZE,
},
},
"yaxis2": {
"title": {
"font": {
"color": ATLAS_TICK_COLOR,
"size": ATLAS_FONT_SIZE,
}
},
"tickfont": {
"color": ATLAS_TICK_COLOR,
"size": ATLAS_FONT_SIZE,
},
},
"xaxis": {
"title": {
"font": {
"color": ATLAS_TICK_COLOR,
"size": ATLAS_FONT_SIZE,
}
},
"tickfont": {
"color": ATLAS_TICK_COLOR,
"size": ATLAS_FONT_SIZE,
},
},
"hoverlabel": {
"font": {
"size": ATLAS_FONT_SIZE,
},
},
},
)
ATLAS_THEME = {
"layout": go.Layout(**ATLAS_THEME_CONFIG),
}
"""
New template to be used when creating Plotly figure objects that are embedded in interactive experiences
"""