Why are the panels in my Grafana dashboard overlapping? - grafana

I am attempting to create a Grafana Scripted Dashboard consisting of panels that indicate whether a server is UP or not. The reason for this is that we constantly have a list of agents being added and making a new panel every time is becoming repetitive.
Currently, I have this as my script (Note my JavaScript/JQuery skills are abysmal..):
'use strict';
// accessible variables in this scope
var window, document, ARGS, $, jQuery, moment, kbn;
return function(callback) {
// Setup some variables
var dashboard;
// Initialize a skeleton with nothing but a rows array and service object
dashboard = {
annotations: {
list: [
{
builtIn: 1,
datasource: "-- Grafana --",
enable: true,
hide: false,
iconColor: "rgba(0, 211, 255, 1)",
name: "Annotations & Alerts",
type: "dashboard"
}
]
},
editable: true,
gnetId: null,
graphTooltip: 0,
id: 15,
links: [],
panels : [],
refresh: false,
schemaVersion: 16,
style: "dark",
tags: [],
templating: {
list: []
},
time: {
from: "now-5m",
to: "now"
},
timepicker: {
refresh_intervals: [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
],
time_options: [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
timezone: "",
title: "My Agents",
//uid: "000000019",
version: 2
};
// Set a title
dashboard.title = 'Scripted dash';
// Set default time
// time can be overridden in the url using from/to parameters, but this is
// handled automatically in grafana core during dashboard initialization
dashboard.time = {
from: "now-6h",
to: "now"
};
var rows = 1;
var seriesName = 'argName';
if(!_.isUndefined(ARGS.rows)) {
rows = parseInt(ARGS.rows, 10);
}
if(!_.isUndefined(ARGS.name)) {
seriesName = ARGS.name;
}
$.ajax({
method: 'GET',
url: 'http://my_server/api/my_rest_call_to_get_agents'
})
.done(function(result) {
//console.log(JSON.stringify(result));
var x = 0;
var y = 0;
var id = 0;
$.each(result.data.result, function(index, value) {
//console.log(value.metric.instance);
var panel = {
clusterName: "My Agent",
colorMode: "Panel",
colors: {
crit: "rgba(245, 54, 54, 0.9)",
disable: "rgba(128, 128, 128, 0.9)",
ok: "rgba(50, 128, 45, 0.9)",
warn: "rgba(237, 129, 40, 0.9)"
},
cornerRadius: 0,
//datasource: "Prometheus",
displayName: "Agent_Name",
flipCard: true,
flipTime: "2",
fontFormat: "Bold",
gridPos: {
h: 3,
w: 2,
x: x,
y: y
},
id: id,
isGrayOnNoData: true,
isHideAlertsOnDisable: false,
isIgnoreOKColors: false,
links: [],
targets: [
{
aggregation: "Last",
alias: value.metric.instance,
crit: 0,
decimals: 2,
displayAliasType: "Warning / Critical",
displayType: "Regular",
displayValueWithAlias: "Never",
expr: 'expression_to_query_service',
format: "time_series",
instant: true,
intervalFactor: 1,
legendFormat: value.metric.instance,
refId: "A",
units: "short",
valueHandler: "Number Threshold"
}
],
title: value.metric.instance,
transparent: true,
type: "vonage-status-panel"
}
dashboard.panels.push(panel);
x += 2;
});
callback(dashboard);
});
}
The strangest part about all of the panels being overlapping is that every panel has different coordinates if I look at each panel's JSON.
Any help with this would be appreciated!

Related

How to place label on top of each horizontal bar in echarts?

I'm trying to make an horizontal histogram with y labels on top of each bar with the really nice libray echarts. Here is an example:
Here is where I am with this jsfiddle https://jsfiddle.net/795f84o0/6/ :
Echarts documentation is really good but I did not found a way to put these labels (sankey, funnel, gauge....) on top on each bar :/
Do you have any idea how I can do it? Thank you for your help!
var chartDom = document.getElementById('main');
var myChart = echarts.init(chartDom);
var option;
var builderJson = {
"all": 10887,
"charts": {
"map": 3237,
"lines": 2164,
"bar": 7561,
"line": 7778,
"pie": 7355,
"scatter": 2405,
"candlestick": 1842,
"radar": 2090,
"heatmap": 1762,
"treemap": 1593,
"graph": 2060,
"boxplot": 1537,
"parallel": 1908,
"gauge": 2107,
"funnel": 1692,
"sankey": 1568
},
"components": {
"geo": 2788,
"title": 9575,
"legend": 9400,
"tooltip": 9466,
"grid": 9266,
"markPoint": 3419,
"markLine": 2984,
"timeline": 2739,
"dataZoom": 2744,
"visualMap": 2466,
"toolbox": 3034,
"polar": 1945
},
"ie": 9743
};
option = {
xAxis: [{
type: 'value',
max: builderJson.all,
}],
yAxis: [{
data: Object.keys(builderJson.charts),
axisLabel: {
show: false,
},
},
{
data: Object.keys(builderJson.charts),
axisLabel: {
show: true,
},
},
],
series: [{
type: 'bar',
data: Object.keys(builderJson.charts).map(function (key) {
return builderJson.charts[key];
})
}]
};
option && myChart.setOption(option);
All right, I got it after two hours...
Just posting a screenshot to show the result:
The fiddle and the code :
var chartDom = document.getElementById('main');
var myChart = echarts.init(chartDom);
var option;
var builderJson = {
"all": 100,
"charts": {
"pie": 1,
"scatter": 1,
"candlestick": 1,
"radar": 2,
"heatmap": 3,
"treemap": 6,
"graph": 7,
"boxplot": 7,
"parallel": 8,
"gauge": 9,
"funnel": 15,
"sankey": 30
},
};
option = {
xAxis: [{
type: 'value',
max: builderJson.all,
axisLabel: {
show: false,
},
splitLine: {
show: false
}
},
],
yAxis: [{
data: Object.keys(builderJson.charts),
axisLabel: {
show: false,
},
splitLine: {
show: false
},
axisLine: {
show: false
},
axisTick: {
show: false,
}
},
],
series: [{
type: 'bar',
stack: 'chart',
barCategoryGap: 30,
barWidth: 20,
label: {
position: [0, -14],
formatter: '{b}',
show: true
},
itemStyle: {
borderRadius: [0, 2, 2, 0],
},
data: Object.keys(builderJson.charts).map(function (key) {
return builderJson.charts[key];
})
},
{
type: 'bar',
stack: 'chart',
barCategoryGap: 30,
barWidth: 20,
itemStyle: {
color: 'whitesmoke'
},
label: {
position: 'insideRight',
formatter: function(params) { return 100 - params.value + '%'},
show: true
},
data: Object.keys(builderJson.charts).map(function (key) {
return builderJson.all - builderJson.charts[key];
})
}
]
};
option && myChart.setOption(option);

In Slick Grid inline edit I can't able to get the entire object

I am using angular slickgrid for showing my data. When I am trying to edit the slick grid record, I will get the changed fields only I need the entire object. I have given the sample data.
Columndefinition :
this.columnDefinitions = [
{
id: 'title', name: 'Title', field: 'title', width: 220, cssClass: 'cell-title',
filterable: true, sortable: true,
queryFieldSorter: 'id', type: FieldType.string,
formatter: Formatters.tree,
editor: {
model: Editors.longText,
required: true,
},
},
{ id: 'duration', name: 'Duration', field: 'duration', minWidth: 90, filterable: true },
{
id: 'child.0.percentComplete', name: '% Complete', field: 'child.0.percentComplete', minWidth: 120, maxWidth: 200,
sortable: true, filterable: true, filter: { model: Filters.slider, operator: '>=' },
formatter: Formatters.percentCompleteBar, type: FieldType.number,
editor: {
model: Editors.slider,
minValue: 0,
maxValue: 100,
params: { hideSliderNumber: false },
},
},
];
SlickGrid input data set structure:
const data = [
{
'id': 0,
'indent': 0,
'parentId': null,
'title': 'Task 0',
'duration': '5 days',
'percentComplete': 73,
'start': '2003-03-21T18:30:00.000Z',
'finish': '2003-04-21T18:30:00.000Z',
'effortDriven': true,
'child' : [{
'id': 2,
'indent': 0,
'parentId': 1,
'title': 'Task 0',
'duration': '5 days',
'percentComplete': 73,
'start': '2003-03-21T18:30:00.000Z',
'finish': '2003-04-21T18:30:00.000Z',
'effortDriven': true
}]
},
{
'id': 1,
'indent': 0,
'parentId': null,
'title': 'Task 1',
'duration': '5 days',
'percentComplete': 4,
'start': '2004-04-24T18:30:00.000Z',
'finish': '2004-05-24T18:30:00.000Z',
'effortDriven': false
}
];
When I start to change the employee field oncellchanged called and I got arg.Item
Current behaviour
onCellChanged(e, args) {
this.angularGrid.gridService.updateItemById(args.item['id'], args.item);
console.log(args.item);
}
Log
{
"id": 0,
"indent": 0,
"parentId": null,
"title": "Task 0",
"duration": "5 days",
"percentComplete": 73,
"start": "2003-03-21T18:30:00.000Z",
"finish": "2003-04-21T18:30:00.000Z",
"effortDriven": true,
"child": {
"0": {
"percentComplete": 25
}
}
}
Expected output:
{
"id": 0,
"indent": 0,
"parentId": null,
"title": "Task 0",
"duration": "5 days",
"percentComplete": 73,
"start": "2003-03-21T18:30:00.000Z",
"finish": "2003-04-21T18:30:00.000Z",
"effortDriven": true,
"child": [
{
"id": 2,
"indent": 0,
"parentId": 1,
"title": "Task 0",
"duration": "5 days",
"percentComplete": 25,
"start": "2003-03-21T18:30:00.000Z",
"finish": "2003-04-21T18:30:00.000Z",
"effortDriven": true
}
]
}
Software versions
Angular : 7.3.5
Angular-Slickgrid : 2.17.10
TypeScript : 3.1.6
Node : 10.16.3
The issue due to the utils files used in angular slickgrid library. If you want to fix this issue, have two solutions.
The library itself handle the function logic.
Need to implement custom editor based on your requirement.
Issue area
In each editor, applyValue method sets the object value to respective path. In the method, the array value not parsed properly. You can extend the editor class and override the applyValue method. Here I shared the sample code for your reference. Especially, go through the setDeepValue method which I have mentioned below.
import { EditorArguments, InputEditor } from 'angular-slickgrid';
export class CustomInputEditor extends InputEditor {
constructor(protected readonly args: EditorArguments, inputType: string) {
super(args, inputType);
}
applyValue(item: any, state: any) {
const fieldName = this.columnDef && this.columnDef.field;
if (fieldName !== undefined) {
const isComplexObject = fieldName?.indexOf('.') > 0; // is the field a complex object, "address.streetNumber"
// is the field a complex object having array value you need to specify the index position in path (Here I used 0th index), "address.0.streetNumber"
// validate the value before applying it (if not valid we'll set an empty string)
const validation = this.validate(state);
const newValue = validation?.valid ? state : '';
// set the new value to the item datacontext
if (isComplexObject) {
// when it's a complex object, user could override the object path (where the editable object is located)
// else we use the path provided in the Field Column Definition
const objectPath =
this.columnEditor?.complexObjectPath ?? fieldName ?? '';
this.setDeepValue(item, objectPath, newValue);
} else if (fieldName) {
item[fieldName] = newValue;
}
}
}
setDeepValue<T = any>(obj: T, path: string | string[], value: any) { // Customized the set value method to handle the array data
if (typeof path === 'string') {
path = path.split('.');
}
if (path.length > 1) {
const e = path.shift();
if (obj && e !== undefined) {
let innerObject;
if (!Array.isArray(obj[e]) && typeof obj[e] != 'object') {
obj[e] = {};
}
this.setDeepValue(obj[e], path, value);
}
} else if (obj && path[0]) {
(obj as any)[(path as any)[0]] = value;
}
}
}
I hope this may helpful for you.
Kind information to the library author, If possible update the setDeepValue method in latest release.

Apache ECharts: Rotated labels get cropped

I'm trying to use rotated labels in my graph. However, the labels are getting cropped. How can I make more space in the bottom so that the labels can fit?
options = {
xAxis: {
type: "category",
data: axis,
axisLabel: {
show: true,
interval: 0,
rotate: 45,
},
axisTick: {
show: true,
interval: 0
}
},
yAxis: {
type: "value",
data: axis,
min: 1,
minInterval: 1,
maxInterval: 1,
max: 15,
axisTick: {
interval: 1
}
},
series: [
{
type: "bar",
stack: "minmax",
itemStyle: {
normal: {
color: "rgba(0,0,0,0)"
}
},
data: maxs
},
{
type: "bar",
stack: "minmax",
data: mins,
itemStyle: { color: "#63869e" }
},
{
symbolSize: 20,
data: scatter,
type: "scatter",
itemStyle: { color: "black" },
},
]
};
To contain the label you can use:
options = {
grid: {
containLabel: true,
}
}
Space can be added with grid option bottom:
options = {
grid: {
bottom: 100,
}
}

How to use ECharts to manipulate nodes in a relationship graph?

I wish to use ECharts to implement a page. In this page, the user can use some buttons to add a node(or edge) or edit the name of a node(or edge). So I want to manipulate the nodes of a relation graph such as the force layout which is shown here:
https://echarts.apache.org/examples/en/editor.html?c=graph-force
Now my question is, since the data of the graph in the example is provided by a gexf file, is there any way to control them from javascript? Like adding a node, deleting a node, or editing a node(or edge).
The data used in the example is in XML format which is later converted to an array of objects, you can look at that format and generate your own using javascript. For instance,
var nodes = [
{
id: "0",
name: "",
itemStyle: null,
symbolSize: 50,
attributes: {
modularity_class: 0
},
value: 28.685715,
label: {
show: false
},
category: 0
},
{
id: "1",
name: "",
itemStyle: null,
symbolSize: 10,
attributes: {
modularity_class: 0
},
value: 4,
label: {
show: false
},
category: 0
},
{
id: "2",
name: "",
itemStyle: null,
symbolSize: 10,
attributes: {
modularity_class: 0
},
value: 9.485714,
label: {
show: false
},
category: 0
}];
var links = [
{
id: "0",
name: null,
source: "0",
target: "1",
lineStyle: {
normal: {}
}
},
{
id: "1",
name: null,
source: "0",
target: "2",
lineStyle: {
normal: {}
}];

Dojo-DataGrid :: How to dynamically fetch values as options for a select box in Dojo DataGrid

I have a Dojo-DataGrid which is programatically populated as below :
var jsonStore = new dojo.data.ItemFileWriteStore({ url: "json/gaskets.json" });
var layout= [
{ field: "description", width: "auto", name: "Tier/Description", editable:true },
{ field: "billingMethod", width: "auto", name: "Billing Method", editable: true,
type: dojox.grid.cells.Select, options: [ '0', '1' ] },
{ field: "offeringComponents", width: "auto", name: "Offering Component", editable: true,
type: dojox.grid.cells.Select, options: [ '0', '1' ] },
{ field: "serviceActivity", width: "auto", name: "Service Activity", editable: true,
type: dojox.grid.cells.Select, options: [ '0', '1' ] },
{ field: "hours", width: "auto", name: "Hours" },
{ field: "rate", width: "auto", name: "Rate <br/> (EUR)" },
{ field: "cost", width: "auto", name: "Cost <br/> (EUR)" },
{ field: "price", width: "auto", name: "Price <br/> (EUR)" },
{ field: "gvn", width: "auto", name: "Gvn" }
];
grid = new dojox.grid.DataGrid({
query: { description: '*' },
store: jsonStore,
structure: layout,
rowsPerPage: 20
}, 'gridNode');
The options for the field billingMethod (Currently defined as dojox.grid.cells.Select) are hard coded right now, but I would like to get those values dynamically from the backend as JSON. But dojox.grid.cells.Select currently(I am using Dojo 1.5) does not have a option to define a "store".
I am trying to use dijit.form.FilteringSelect, but this needs a id(of a Div) for its constructor and I cannot specify one as this select box has to go with in the grid, rather than a separate DIV.
Thanks
Sandeep
Your answer works fine, the issue is that in the combo the user can select A, but once the combo lose the focus, the value 1 will be shown. Some months ago I had the same problem, and I got a solution from KGF on #dojo. The idea is to have a formatter on the cell that just creates a SPAN element, and then it invokes a query over the store to get the label of the selected element and put it on the SPAN. I modified your example to get that working.
dojo.require("dojo.data.ItemFileWriteStore");
dojo.require("dojo.data.ItemFileReadStore");
dojo.require("dojox.grid.cells.dijit");
dojo.require("dojox.grid.DataGrid");
dojo.require("dijit.form.Select");
dojo.require('dojox.grid.cells.dijit');
dojo.require('dijit.form.FilteringSelect');
var grid;
var jsonStore;
dojo.addOnLoad(function() {
jsonStore = new dojo.data.ItemFileWriteStore({
data: {
"identifier": "identify",
"label": "description",
"items": [
{
"identify": 123,
"description": "Project Manager"},
{
"identify": 234,
"description": "Developer"},
{
"identify": 536,
"description": "Developer",
"billingMethod":2}
]
}
});
var myStore = new dojo.data.ItemFileReadStore({
data: {
identifier: 'value',
label: 'name',
items: [{
value: 1,
name: 'A',
label: 'A'},
{
value: 2,
name: 'B',
label: 'B'},
{
value: 3,
name: 'C',
label: 'C'}]
}
});
//[kgf] callback referenced by formatter for FilteringSelect cell
function displayValue(nodeId, store, attr, item) {
if (item != null) { //if it's null, it wasn't found!
dojo.byId(nodeId).innerHTML = store.getValue(item, attr);
}
}
var layout = [
{
field: "identify",
width: "auto",
name: "Id 2 Hide",
hidden: true},
{
field: "description",
width: "auto",
name: "Tier/Description",
editable: true},
{
field: 'billingMethod',
name: 'Billing Method',
editable: true,
required: true,
width: '150px',
type: dojox.grid.cells._Widget,
widgetClass: dijit.form.FilteringSelect,
widgetProps: {
store: myStore
},
formatter: function(data, rowIndex) { //[kgf]
//alert("data "+data)
var genId = 'title' + rowIndex;
var store = this.widgetProps.store;
var attr = "label";
setTimeout(function() {
store.fetchItemByIdentity({
identity: data,
onItem: dojo.partial(displayValue, genId, store, attr)
});
}, 50);
//for now return a span with a predetermined id for us to populate.
return '<span id="' + genId + '"></span>';
}
}
];
grid = new dojox.grid.DataGrid({
query: {
description: '*'
},
store: jsonStore,
singleClickEdit: true,
structure: layout,
rowsPerPage: 20
}, 'gridNode');
grid.startup();
});
I was finally able to figure this out..Incase someone wants to implement same kind of stuff using DOJO Datagrid+FilteringSelect.
Sample Code
dojo.require("dojo.data.ItemFileWriteStore");
dojo.require("dojo.data.ItemFileReadStore");
dojo.require("dojox.grid.cells.dijit");
dojo.require("dojox.grid.DataGrid");
dojo.require("dijit.form.Select");
dojo.require('dojox.grid.cells.dijit');
dojo.require('dijit.form.FilteringSelect');
var grid;
var jsonStore;
dojo.addOnLoad(function() {
jsonStore = new dojo.data.ItemFileWriteStore({
data: {
"identifier": "identify",
"label": "description",
"items": [
{
"identify": 123,
"description": "Project Manager"},
{
"identify": 234,
"description": "Developer"},
{
"identify": 536,
"description": "Developer"}
]
}
});
var myStore = new dojo.data.ItemFileReadStore({
data: {
identifier: 'value',
label: 'name',
items: [{
value: 1,
name: 'A',
label: 'A'},
{
value: 2,
name: 'B',
label: 'Y'},
{
value: 3,
name: 'C',
label: 'C'}]
}
});
var layout = [
{
field: "identify",
width: "auto",
name: "Id 2 Hide",
hidden: true},
{
field: "description",
width: "auto",
name: "Tier/Description",
editable: true},
{
field: 'billingMethod',
name: 'Billing Method',
editable: true,
required: true,
width: '150px',
type: dojox.grid.cells._Widget,
widgetClass: dijit.form.FilteringSelect,
widgetProps: {
store: myStore
}}
];
grid = new dojox.grid.DataGrid({
query: {
description: '*'
},
store: jsonStore,
singleClickEdit: true,
structure: layout,
rowsPerPage: 20
}, 'gridNode');
grid.startup();
});