module.ts 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. import './graph';
  2. import './series_overrides_ctrl';
  3. import './thresholds_form';
  4. import './time_regions_form';
  5. import template from './template';
  6. import _ from 'lodash';
  7. import { MetricsPanelCtrl } from 'app/plugins/sdk';
  8. import { DataProcessor } from './data_processor';
  9. import { axesEditorComponent } from './axes_editor';
  10. import config from 'app/core/config';
  11. import TimeSeries from 'app/core/time_series2';
  12. import { DataFrame, DataLink, DateTimeInput } from '@grafana/data';
  13. import { getColorFromHexRgbOrName, VariableSuggestion } from '@grafana/ui';
  14. import { getProcessedDataFrames } from 'app/features/dashboard/state/runRequest';
  15. import { GraphContextMenuCtrl } from './GraphContextMenuCtrl';
  16. import { getDataLinksVariableSuggestions } from 'app/features/panel/panellinks/link_srv';
  17. import { auto } from 'angular';
  18. import { AnnotationsSrv } from 'app/features/annotations/all';
  19. class GraphCtrl extends MetricsPanelCtrl {
  20. static template = template;
  21. renderError: boolean;
  22. hiddenSeries: any = {};
  23. seriesList: TimeSeries[] = [];
  24. dataList: DataFrame[] = [];
  25. annotations: any = [];
  26. alertState: any;
  27. annotationsPromise: any;
  28. dataWarning: any;
  29. colors: any = [];
  30. subTabIndex: number;
  31. processor: DataProcessor;
  32. contextMenuCtrl: GraphContextMenuCtrl;
  33. linkVariableSuggestions: VariableSuggestion[] = [];
  34. panelDefaults: any = {
  35. // datasource name, null = default datasource
  36. datasource: null,
  37. // sets client side (flot) or native graphite png renderer (png)
  38. renderer: 'flot',
  39. yaxes: [
  40. {
  41. label: null,
  42. show: true,
  43. logBase: 1,
  44. min: null,
  45. max: null,
  46. format: 'short',
  47. },
  48. {
  49. label: null,
  50. show: true,
  51. logBase: 1,
  52. min: null,
  53. max: null,
  54. format: 'short',
  55. },
  56. ],
  57. xaxis: {
  58. show: true,
  59. mode: 'time',
  60. name: null,
  61. values: [],
  62. buckets: null,
  63. },
  64. yaxis: {
  65. align: false,
  66. alignLevel: null,
  67. },
  68. // show/hide lines
  69. lines: true,
  70. // fill factor
  71. fill: 1,
  72. // fill factor
  73. fillGradient: 0,
  74. // line width in pixels
  75. linewidth: 1,
  76. // show/hide dashed line
  77. dashes: false,
  78. // length of a dash
  79. dashLength: 10,
  80. // length of space between two dashes
  81. spaceLength: 10,
  82. // show hide points
  83. points: false,
  84. // point radius in pixels
  85. pointradius: 2,
  86. // show hide bars
  87. bars: false,
  88. // enable/disable stacking
  89. stack: false,
  90. // stack percentage mode
  91. percentage: false,
  92. // legend options
  93. legend: {
  94. show: true, // disable/enable legend
  95. values: false, // disable/enable legend values
  96. min: false,
  97. max: false,
  98. current: false,
  99. total: false,
  100. avg: false,
  101. },
  102. // how null points should be handled
  103. nullPointMode: 'null',
  104. // staircase line mode
  105. steppedLine: false,
  106. // tooltip options
  107. tooltip: {
  108. value_type: 'individual',
  109. shared: true,
  110. sort: 0,
  111. },
  112. // time overrides
  113. timeFrom: null,
  114. timeShift: null,
  115. // metric queries
  116. targets: [{}],
  117. // series color overrides
  118. aliasColors: {},
  119. // other style overrides
  120. seriesOverrides: [],
  121. thresholds: [],
  122. timeRegions: [],
  123. options: {
  124. dataLinks: [],
  125. },
  126. };
  127. /** @ngInject */
  128. constructor($scope: any, $injector: auto.IInjectorService, private annotationsSrv: AnnotationsSrv) {
  129. super($scope, $injector);
  130. _.defaults(this.panel, this.panelDefaults);
  131. _.defaults(this.panel.tooltip, this.panelDefaults.tooltip);
  132. _.defaults(this.panel.legend, this.panelDefaults.legend);
  133. _.defaults(this.panel.xaxis, this.panelDefaults.xaxis);
  134. _.defaults(this.panel.options, this.panelDefaults.options);
  135. this.useDataFrames = true;
  136. this.processor = new DataProcessor(this.panel);
  137. this.contextMenuCtrl = new GraphContextMenuCtrl($scope);
  138. this.events.on('render', this.onRender.bind(this));
  139. this.events.on('data-frames-received', this.onDataFramesReceived.bind(this));
  140. this.events.on('data-error', this.onDataError.bind(this));
  141. this.events.on('data-snapshot-load', this.onDataSnapshotLoad.bind(this));
  142. this.events.on('init-edit-mode', this.onInitEditMode.bind(this));
  143. this.events.on('init-panel-actions', this.onInitPanelActions.bind(this));
  144. this.onDataLinksChange = this.onDataLinksChange.bind(this);
  145. }
  146. onInitEditMode() {
  147. this.addEditorTab('Display options', 'public/app/plugins/panel/graph/tab_display.html');
  148. this.addEditorTab('Axes', axesEditorComponent);
  149. this.addEditorTab('Legend', 'public/app/plugins/panel/graph/tab_legend.html');
  150. this.addEditorTab('Thresholds & Time Regions', 'public/app/plugins/panel/graph/tab_thresholds_time_regions.html');
  151. this.addEditorTab('Data links', 'public/app/plugins/panel/graph/tab_drilldown_links.html');
  152. this.subTabIndex = 0;
  153. }
  154. onInitPanelActions(actions: any[]) {
  155. actions.push({ text: 'Export CSV', click: 'ctrl.exportCsv()' });
  156. actions.push({ text: 'Toggle legend', click: 'ctrl.toggleLegend()', shortcut: 'p l' });
  157. }
  158. issueQueries(datasource: any) {
  159. this.annotationsPromise = this.annotationsSrv.getAnnotations({
  160. dashboard: this.dashboard,
  161. panel: this.panel,
  162. range: this.range,
  163. });
  164. /* Wait for annotationSrv requests to get datasources to
  165. * resolve before issuing queries. This allows the annotations
  166. * service to fire annotations queries before graph queries
  167. * (but not wait for completion). This resolves
  168. * issue 11806.
  169. */
  170. return this.annotationsSrv.datasourcePromises.then((r: any) => {
  171. return super.issueQueries(datasource);
  172. });
  173. }
  174. zoomOut(evt: any) {
  175. this.publishAppEvent('zoom-out', 2);
  176. }
  177. onDataSnapshotLoad(snapshotData: any) {
  178. this.annotationsPromise = this.annotationsSrv.getAnnotations({
  179. dashboard: this.dashboard,
  180. panel: this.panel,
  181. range: this.range,
  182. });
  183. const frames = getProcessedDataFrames(snapshotData);
  184. this.onDataFramesReceived(frames);
  185. }
  186. onDataError(err: any) {
  187. this.seriesList = [];
  188. this.annotations = [];
  189. this.render([]);
  190. }
  191. onDataFramesReceived(data: DataFrame[]) {
  192. this.dataList = data;
  193. this.seriesList = this.processor.getSeriesList({
  194. dataList: this.dataList,
  195. range: this.range,
  196. });
  197. this.linkVariableSuggestions = getDataLinksVariableSuggestions(data);
  198. this.dataWarning = null;
  199. const datapointsCount = this.seriesList.reduce((prev, series) => {
  200. return prev + series.datapoints.length;
  201. }, 0);
  202. if (datapointsCount === 0) {
  203. this.dataWarning = {
  204. title: 'No data points',
  205. tip: 'No datapoints returned from data query',
  206. };
  207. } else {
  208. for (const series of this.seriesList) {
  209. if (series.isOutsideRange) {
  210. this.dataWarning = {
  211. title: 'Data points outside time range',
  212. tip: 'Can be caused by timezone mismatch or missing time filter in query',
  213. };
  214. break;
  215. }
  216. }
  217. }
  218. this.annotationsPromise.then(
  219. (result: { alertState: any; annotations: any }) => {
  220. this.loading = false;
  221. this.alertState = result.alertState;
  222. this.annotations = result.annotations;
  223. this.render(this.seriesList);
  224. },
  225. () => {
  226. this.loading = false;
  227. this.render(this.seriesList);
  228. }
  229. );
  230. }
  231. onRender() {
  232. if (!this.seriesList) {
  233. return;
  234. }
  235. for (const series of this.seriesList) {
  236. series.applySeriesOverrides(this.panel.seriesOverrides);
  237. if (series.unit) {
  238. this.panel.yaxes[series.yaxis - 1].format = series.unit;
  239. }
  240. }
  241. }
  242. onColorChange = (series: any, color: string) => {
  243. series.setColor(getColorFromHexRgbOrName(color, config.theme.type));
  244. this.panel.aliasColors[series.alias] = color;
  245. this.render();
  246. };
  247. onToggleSeries = (hiddenSeries: any) => {
  248. this.hiddenSeries = hiddenSeries;
  249. this.render();
  250. };
  251. onToggleSort = (sortBy: any, sortDesc: any) => {
  252. this.panel.legend.sort = sortBy;
  253. this.panel.legend.sortDesc = sortDesc;
  254. this.render();
  255. };
  256. onToggleAxis = (info: { alias: any; yaxis: any }) => {
  257. let override: any = _.find(this.panel.seriesOverrides, { alias: info.alias });
  258. if (!override) {
  259. override = { alias: info.alias };
  260. this.panel.seriesOverrides.push(override);
  261. }
  262. override.yaxis = info.yaxis;
  263. this.render();
  264. };
  265. onDataLinksChange(dataLinks: DataLink[]) {
  266. this.panel.updateOptions({
  267. ...this.panel.options,
  268. dataLinks,
  269. });
  270. }
  271. addSeriesOverride(override: any) {
  272. this.panel.seriesOverrides.push(override || {});
  273. }
  274. removeSeriesOverride(override: any) {
  275. this.panel.seriesOverrides = _.without(this.panel.seriesOverrides, override);
  276. this.render();
  277. }
  278. toggleLegend() {
  279. this.panel.legend.show = !this.panel.legend.show;
  280. this.render();
  281. }
  282. legendValuesOptionChanged() {
  283. const legend = this.panel.legend;
  284. legend.values = legend.min || legend.max || legend.avg || legend.current || legend.total;
  285. this.render();
  286. }
  287. exportCsv() {
  288. const scope = this.$scope.$new(true);
  289. scope.seriesList = this.seriesList;
  290. this.publishAppEvent('show-modal', {
  291. templateHtml: '<export-data-modal data="seriesList"></export-data-modal>',
  292. scope,
  293. modalClass: 'modal--narrow',
  294. });
  295. }
  296. onContextMenuClose = () => {
  297. this.contextMenuCtrl.toggleMenu();
  298. };
  299. formatDate = (date: DateTimeInput, format?: string) => {
  300. return this.dashboard.formatDate.apply(this.dashboard, [date, format]);
  301. };
  302. getDataFrameByRefId = (refId: string) => {
  303. return this.dataList.filter(dataFrame => dataFrame.refId === refId)[0];
  304. };
  305. }
  306. export { GraphCtrl, GraphCtrl as PanelCtrl };