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.

283 lines
8.8 KiB

define(["pm/contrib/d3/d3.amd",
"/static/app/DA-ITSI-CP-vmware-dashboards/swc-vmware-cp/index.js",
'/static/app/DA-ITSI-CP-vmware-dashboards/libs/underscore.js'
],
function (d3, SWCVMware, _) {
const Messages = SWCVMware.MVCMessages;
const SimpleSplunkView = SWCVMware.SimpleSplunkView;
//Define custom messages, override all core messages to be compact
var custom_messages = {
'cancelled': {
icon: "info-circle",
level: "info",
message: "Search was cancelled.",
compact: true
},
'empty': {
icon: "blank",
level: "info",
message: "",
compact: true
},
'unresolved-search': {
icon: "warning-sign",
level: "error",
message: "Search query is not fully resolved.",
compact: true
},
'no-events': {
icon: "info-circle",
level: "info",
message: "Search did not return any events.",
compact: true
},
'no-results': {
icon: "blank",
level: "info",
message: "No results found.",
compact: true
},
'no-search': {
icon: "info-circle",
level: "info",
message: "No search set.",
compact: true
},
'no-stats': {
icon: "warning-sign",
level: "error",
message: "Search isn't generating any statistical results.",
compact: true
},
'not-started': {
icon: "info-circle",
level: "info",
message: "No search started.",
compact: true
},
'waiting': {
icon: "blank",
level: "info",
message: "Waiting for data...",
compact: true
},
'waiting-queued': {
icon: "info-circle",
level: "info",
message: "Waiting for search to start: job is queued.",
compact: true
},
'waiting-preparing': {
icon: "info-circle",
level: "info",
message: "Waiting for search to start: job is preparing.",
compact: true
}
};
var DistributionGraph = SimpleSplunkView.extend({
className: "proactive-monitoring-distribution-graph",
tagName: "g",
output_mode: "json_rows",
resultOptions: { output_time_format: "%s.%Q" },
options: {
data: "preview",
//The distribution search manager id
managerid: undefined,
//If overloaded will set the default message container (default is this.$el)
message_container: undefined,
color_scheme: undefined,
//This is a reference to our chart controller
chart_controller: undefined
},
colors: {
dark: {
inner_fill: "#909090",
outer_fill: "#7A7A7A"
},
light: {
inner_fill: "#D4D3D4",
outer_fill: "#ECECEC"
}
},
time_extent: undefined,
y_extent: undefined,
initialize: function(options) {
//Default the color scheme to dark if it is not passed in
this.color_scheme = options.color_scheme === "light" ? "light" : "dark";
this.message_container = options.message_container;
this.chart_controller = options.chart_controller;
SimpleSplunkView.prototype.initialize.apply(this, arguments);
},
/*
* We overload displayMessage to include our own custom messages
* with message text overloading. If you wish to use a message
* template you must pass a text object with keys equal to the
* template tokens to replace.
*
* In addition we allow for the control of the message container
* with a default of this.$el. this._viz will not be destroyed if
* container is specified.
*/
displayMessage: function(info, text, container) {
if (container === null || container === undefined) {
container = this.$el;
this._viz = null;
}
if (custom_messages.hasOwnProperty(info)) {
var info_obj = _.clone(custom_messages[info]);
if (text !== null && text !== undefined) {
info_obj.message = info_obj.message_template(text, {variable: "text"});
}
Messages.render(info_obj, container);
}
else {
Messages.render(info, container);
}
return this;
},
/*
* We overload _displayMessage to allow for a default container
* other than our own $el.
*/
_displayMessage: function(info, text, container) {
if ((container === undefined || container === null) && this.message_container !== undefined) {
container = this.message_container;
}
return this.displayMessage(info, text, container);
},
formatResults: function(resultsModel) {
if (!resultsModel) {
return {fields: [],
rows: [[]],
parse_error: true
};
}
// First try the legacy one, and if it isn't there, use the real one.
var outputMode = this.output_mode || this.outputMode;
var data_type = this.data_types[outputMode];
var data = resultsModel.data();
//override to return fields as well, thus our data looks like: {fields: [fieldname1, fieldname2, ...], rows: [row1_array, row2_array, ...]}
return this.formatData({
fields: data.fields,
rows: data[data_type],
parse_error: false
});
},
/*
* formatData gives us our area shape makers
*/
formatData: function(data) {
var transformed_data = {parse_error: false};
/*
* Our data must have the following fields:
* -> lower_quartile - defines the lower boundary of the inner stream
* -> upper_quartile - defines the upper boundary of the inner stream
* -> lower_extreme - defines the lower boundary of the outer stream
* -> upper_extreme - defines the upper boundary of the outer stream
* -> _time - defines the time axis/domain of the data
*/
//Get the fields we care about
var time_index = _.indexOf(data.fields, "_time");
var upper_quartile_index = _.indexOf(data.fields, "upper_quartile");
var lower_quartile_index = _.indexOf(data.fields, "lower_quartile");
var upper_extreme_index = _.indexOf(data.fields, "upper_extreme");
var lower_extreme_index = _.indexOf(data.fields, "lower_extreme");
var inner_stream_data = [];
var outer_stream_data = [];
var ii, row, datum, t, y0, y1;
for (ii = 0; ii < data.rows.length; ii++) {
row = data.rows[ii];
t = row[time_index];
y0 = row[lower_quartile_index];
y1 = row[upper_quartile_index];
if (_.isFinite(t) && _.isFinite(y0) && _.isFinite(y1)) {
inner_stream_data.push([Number(t), Number(y0), Number(y1)]);
}
y0 = row[lower_extreme_index];
y1 = row[upper_extreme_index];
if (_.isFinite(t) && _.isFinite(y0) && _.isFinite(y1)) {
outer_stream_data.push([Number(t), Number(y0), Number(y1)]);
}
}
this.time_extent = d3.extent(outer_stream_data, function(d) { return d[0]; });
this.y_extent = [_.min(outer_stream_data, function(d) { return d[1]; })[1], _.max(outer_stream_data, function(d) { return d[2]; })[2]];
this.chart_controller.updateScales();
transformed_data.inner_stream_data = inner_stream_data;
transformed_data.outer_stream_data = outer_stream_data;
return transformed_data;
},
/*
* createView sets up a d3 selector for the container g
*/
createView: function() {
var d3stage = d3.select(this.$el.get(0));
d3stage.append("path")
.attr("class", "pm-distribution-outer-stream")
.attr("fill", this.colors[this.color_scheme].outer_fill);
d3stage.append("path")
.attr("class", "pm-distribution-inner-stream")
.attr("fill", this.colors[this.color_scheme].inner_fill);
return {d3stage: d3stage};
},
updateView: function(viz, data) {
if (data.parse_error) {
return;
}
this._displayMessage("empty");
//RENDER THE THINGS!
this.chart_controller.renderGraphs();
},
/*
* OVERLOADED!
* This method was overloaded to allow for the hiding of the old global chart as a new one is populating.
*/
_onSearchStart: function() {
if (this._viz !== null && this._viz !== undefined) {
this._viz.d3stage.select("path.pm-distribution-outer-stream").attr("opacity", 1e-6);
this._viz.d3stage.select("path.pm-distribution-inner-stream").attr("opacity", 1e-6);
}
this._isJobDone = false;
this._displayMessage('waiting');
},
/*
* Method called by the chart controller when it is time to render
* the data
*/
renderGraph: function() {
if (this._data === undefined || this._data === null || this._viz === undefined || this._viz === null){
return;
}
var chart_controller = this.chart_controller;
var area = d3.svg.area()
.x(function(d) { return chart_controller.time_scale(d[0]); })
.y0(function(d) {return chart_controller.y_scale(d[1]); })
.y1(function(d) {return chart_controller.y_scale(d[2]); });
this._viz.d3stage.select("path.pm-distribution-outer-stream")
.datum(this._data.outer_stream_data)
.attr("d", area)
.attr("opacity", 1);
this._viz.d3stage.select("path.pm-distribution-inner-stream")
.datum(this._data.inner_stream_data)
.attr("d", area)
.attr("opacity", 1);
}
});
//Note you must return your view at the end
return DistributionGraph;
}
);