You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

385 lines
13 KiB

# Copyright (C) 2005-2024 Splunk Inc. All Rights Reserved.
from .utils import dict_merge, normalize_integer, normalize_color
from .vis.icon_collection import get_icons_collection
from .vis.drilldowns import transform_drilldowns, set_off_custom_drilldown_on
from .vis.thresholds import transform_thresholds_deprecated, transform_thresholds_singlevalue, \
transform_thresholds_singlevalueicon, transform_thresholds_line
from ui_utils.css_utils import (DEFAULT_TEXT_COLOR)
DEFAULT_FONT_SIZE = 12
DEFAULT_STROKE = 1
DEFAULT_STROKE_FOR_LINE_WITH_DECORATOR = 2
class VizConverter(object):
def __init__(self, session_key=None):
"""
Visualization converter
:param session_key: splunkd session key
"""
self.item = None
self.session_key = session_key
self.icon_collection = []
def convert(self, item, scale_factor):
"""
Takes an original widget data and produces a UDF vis object
:param item: original widget
:param scale_factor: scale factor applied to the classic GT
:return: UDF visualization object
"""
self.item = item
self.scale_factor = scale_factor
set_off_custom_drilldown_on(item)
name = item.get('name')
func_map = {
'Text': self.create_text,
'Rectangle': self.create_rectangle,
'Ellipse': self.create_ellipse,
'Connection': self.create_connection,
'Line': self.create_line,
'PolyIcon': self.create_icon,
'PolyImage': self.create_image,
'SingleValue': self.create_single_value,
'Sparkline': self.create_sparkline,
'SingleValueDelta': self.create_single_value_delta,
'Gauge': self.create_gauge,
'SquareWidget': self.upgrade_to_rectangle,
'CircularWidget': self.upgrade_to_ellipse
}
func = func_map.get(name)
if not func:
raise Exception('Unknown viz: type=%s name=%s' % (self.item.get('vizType'), self.item.get('name')))
return func()
def create_text(self):
bg_color = normalize_color(self.item.get('bgColor'))
font_color = normalize_color(self.item.get('fontColor'))
return {
'type': 'splunk.markdown',
'options': {
'markdown': self.item.get('text'),
'fontColor': font_color,
'backgroundColor': bg_color
}
}
def create_image(self):
return {
'type': 'splunk.image',
'options': {
'src': 'splunk-enterprise-kvstore://%s' % self.item.get('fileModel').get('_key')
}
}
def create_icon(self):
icon_id = self.item.get('iconId')
color = self.item.get('color')
bg_color = normalize_color(self.item.get('bgColor'))
color_calc = self.item.get('colorCalc')
svg_path = self.item.get('svgPath')
thresholds = None
if icon_id.endswith('Icon'):
# retrieve kvstore id for icons referred by name (old classic gt style)
if not self.icon_collection:
self.icon_collection = get_icons_collection(self.session_key)
for icon_obj in self.icon_collection:
if icon_obj.get('svg_path') == svg_path:
icon_id = icon_obj.get('_key')
break
options = {
'icon': 'splunk-enterprise-kvstore://' + icon_id,
'backgroundColor': bg_color,
'showValue': False
}
if color and (not color_calc or color_calc == 'static'):
options['iconColor'] = color
elif color_calc == 'adhoc' or color_calc == 'kpi':
thresholds = transform_thresholds_singlevalueicon(self.item)
res = {
'type': 'splunk.singlevalueicon',
'options': options
}
if thresholds:
dict_merge(res, thresholds)
drilldowns = transform_drilldowns(self.item, self.session_key)
if drilldowns:
dict_merge(res, drilldowns)
return res
def create_single_value(self):
new_type = 'splunk.singlevalue'
unit = self.item.get('unit')
digit_precision = self.item.get('digitPrecision')
options = {
'trendDisplay': 'off',
'sparklineDisplay': 'off',
'showSparklineTooltip': True
}
if unit:
options['unit'] = unit
if digit_precision:
options['numberPrecision'] = digit_precision
res = {
'type': new_type,
'options': options
}
thresholds = transform_thresholds_singlevalue(self.item)
if thresholds:
dict_merge(res, thresholds)
drilldowns = transform_drilldowns(self.item, self.session_key)
if drilldowns:
dict_merge(res, drilldowns)
return res
def create_sparkline(self):
new_type = 'splunk.singlevalue'
unit = self.item.get('unit')
digit_precision = self.item.get('digitPrecision')
options = {
'sparklineDisplay': 'before',
'trendDisplay': 'off',
'showSparklineTooltip': True
}
if unit:
options['unit'] = unit
if digit_precision:
options['numberPrecision'] = digit_precision
res = {
'type': new_type,
'options': options
}
thresholds = transform_thresholds_singlevalue(self.item)
if thresholds:
dict_merge(res, thresholds)
drilldowns = transform_drilldowns(self.item, self.session_key)
if drilldowns:
dict_merge(res, drilldowns)
return res
def create_single_value_delta(self):
new_type = 'splunk.singlevalue'
unit = self.item.get('unit')
digit_precision = self.item.get('digitPrecision')
options = {
'trendDisplay': 'percent',
'sparklineDisplay': 'off'
}
if unit:
options['unit'] = unit
if digit_precision:
options['numberPrecision'] = digit_precision
res = {
'type': new_type,
'options': options
}
thresholds = transform_thresholds_singlevalue(self.item)
if thresholds:
dict_merge(res, thresholds)
drilldowns = transform_drilldowns(self.item, self.session_key)
if drilldowns:
dict_merge(res, drilldowns)
return res
def create_gauge(self):
unit = self.item.get('unit')
digit_precision = self.item.get('digitPrecision')
options = {}
if unit:
options['unit'] = unit
if digit_precision:
options['numberPrecision'] = digit_precision
res = {
'type': 'splunk.singlevalueradial',
'options': options
}
thresholds = transform_thresholds_deprecated(self.item)
if thresholds:
dict_merge(res, thresholds)
drilldowns = transform_drilldowns(self.item, self.session_key)
if drilldowns:
dict_merge(res, drilldowns)
return res
def create_rectangle(self):
color = normalize_color(self.item.get('color'))
bg_color = normalize_color(self.item.get('bgColor'))
stroke_width = normalize_integer(self.item.get('stroke', DEFAULT_STROKE), DEFAULT_STROKE)
res = {
'type': 'splunk.rectangle',
'options': {
'strokeColor': color,
'strokeWidth': int(stroke_width)
}
}
if self.item.get('statModel'):
# Indicating that the rectangle is associated with some search
thresholds = transform_thresholds_deprecated(self.item)
if thresholds:
if thresholds.get('options', {}).get('backgroundColor'):
# rectangle uses fill instead of backgroundColor
thresholds['options']['fill'] = thresholds['options'].pop('backgroundColor')
dict_merge(res, thresholds)
else:
res['options']['fill'] = bg_color
drilldowns = transform_drilldowns(self.item, self.session_key)
if drilldowns:
dict_merge(res, drilldowns)
return res
def create_ellipse(self):
color = normalize_color(self.item.get('color'))
bg_color = normalize_color(self.item.get('bgColor'))
stroke_width = normalize_integer(self.item.get('stroke', DEFAULT_STROKE), DEFAULT_STROKE)
res = {
'type': 'splunk.ellipse',
'options': {
'strokeColor': color,
'strokeWidth': int(stroke_width)
}
}
if self.item.get('statModel'):
# Indicating that the ellipse is associated with some search
thresholds = transform_thresholds_deprecated(self.item)
if thresholds:
if thresholds.get('options', {}).get('backgroundColor'):
# ellipse uses fill instead of backgroundColor
thresholds['options']['fill'] = thresholds['options'].pop('backgroundColor')
dict_merge(res, thresholds)
else:
res['options']['fill'] = bg_color
drilldowns = transform_drilldowns(self.item, self.session_key)
if drilldowns:
dict_merge(res, drilldowns)
return res
def upgrade_to_rectangle(self):
self.item['stroke'] = 1
self.item['bgColor'] = '#FFFFFF'
self.item['color'] = DEFAULT_TEXT_COLOR
return self.create_rectangle()
def upgrade_to_ellipse(self):
self.item['stroke'] = 1
self.item['bgColor'] = '#FFFFFF'
self.item['color'] = DEFAULT_TEXT_COLOR
return self.create_ellipse()
def create_connection(self):
stroke_width = normalize_integer(self.item.get('stroke', DEFAULT_STROKE), DEFAULT_STROKE)
# UDF doesn't support transparent line so default to white
color = '#FFFFFF' if self.item.get('color') == 'none' else self.item.get('color')
options = {}
if stroke_width and stroke_width == DEFAULT_STROKE:
stroke_width = normalize_integer('', DEFAULT_STROKE_FOR_LINE_WITH_DECORATOR)
source_decorator = self.item.get('sourceDecorator')
target_decorator = self.item.get('targetDecorator')
options['fromArrow'] = True if source_decorator else False
options['toArrow'] = True if target_decorator else False
if color:
options['strokeColor'] = color
if stroke_width:
options['strokeWidth'] = int(stroke_width)
return {
'type': 'abslayout.line',
'options': options
}
def create_line(self):
stroke_width = normalize_integer(self.item.get('stroke', DEFAULT_STROKE), DEFAULT_STROKE)
# UDF doesn't support transparent line so default to white
color = '#FFFFFF' if self.item.get('color') == 'none' else self.item.get('color')
options = {}
classic_decorator_types = ['simple', 'triangle', 'diamond']
start_point_decorator_type = self.item.get('startPointDecoratorType')
end_point_decorator_type = self.item.get('endPointDecoratorType')
options['fromArrow'] = False
options['toArrow'] = False
if start_point_decorator_type in classic_decorator_types:
options['fromArrow'] = True
if end_point_decorator_type in classic_decorator_types:
options['toArrow'] = True
if options['fromArrow'] or options['toArrow']:
if stroke_width and stroke_width == DEFAULT_STROKE:
stroke_width = normalize_integer('', DEFAULT_STROKE_FOR_LINE_WITH_DECORATOR)
if stroke_width:
options['strokeWidth'] = int(stroke_width)
if color: # if static color
options['strokeColor'] = color
res = {
'type': 'abslayout.line',
'options': options
}
if self.item.get('statModel'):
thresholds = transform_thresholds_line(self.item)
if thresholds:
dict_merge(res, thresholds)
return res
def create_label(self, item):
label = item.get('label')
font_family = item.get('fontFamily') # pick first font from the list
font_family = [font.strip() for font in font_family.split(',')][0] if font_family else ''
bg_color = item.get('bgColor')
font_color = item.get('labelFontColor')
label_font_size = normalize_integer(self.item.get('labelFontSize', DEFAULT_FONT_SIZE), DEFAULT_FONT_SIZE)
label_font_size = int(label_font_size * self.scale_factor)
options = {
'content': label,
'fontSize': label_font_size
}
if font_family:
options['fontFamily'] = font_family
if bg_color:
options['backgroundColor'] = bg_color
if font_color:
options['textColor'] = font_color
return {
'type': 'splunk.markdown',
'options': options
}