dashboard_model.ts 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969
  1. import moment from 'moment';
  2. import _ from 'lodash';
  3. import {GRID_COLUMN_COUNT, GRID_CELL_HEIGHT, REPEAT_DIR_VERTICAL} from 'app/core/constants';
  4. import {DEFAULT_ANNOTATION_COLOR} from 'app/core/utils/colors';
  5. import {Emitter} from 'app/core/utils/emitter';
  6. import {contextSrv} from 'app/core/services/context_srv';
  7. import sortByKeys from 'app/core/utils/sort_by_keys';
  8. import {PanelModel} from './panel_model';
  9. export class DashboardModel {
  10. id: any;
  11. title: any;
  12. autoUpdate: any;
  13. description: any;
  14. tags: any;
  15. style: any;
  16. timezone: any;
  17. editable: any;
  18. graphTooltip: any;
  19. time: any;
  20. timepicker: any;
  21. hideControls: any;
  22. templating: any;
  23. annotations: any;
  24. refresh: any;
  25. snapshot: any;
  26. schemaVersion: number;
  27. version: number;
  28. revision: number;
  29. links: any;
  30. gnetId: any;
  31. folderId: number;
  32. panels: PanelModel[];
  33. // ------------------
  34. // not persisted
  35. // ------------------
  36. // repeat process cycles
  37. iteration: number;
  38. meta: any;
  39. events: Emitter;
  40. static nonPersistedProperties: {[str: string]: boolean} = {
  41. events: true,
  42. meta: true,
  43. panels: true, // needs special handling
  44. templating: true, // needs special handling
  45. };
  46. constructor(data, meta?) {
  47. if (!data) {
  48. data = {};
  49. }
  50. this.events = new Emitter();
  51. this.id = data.id || null;
  52. this.revision = data.revision;
  53. this.title = data.title || 'No Title';
  54. this.autoUpdate = data.autoUpdate;
  55. this.description = data.description;
  56. this.tags = data.tags || [];
  57. this.style = data.style || 'dark';
  58. this.timezone = data.timezone || '';
  59. this.editable = data.editable !== false;
  60. this.graphTooltip = data.graphTooltip || 0;
  61. this.hideControls = data.hideControls || false;
  62. this.time = data.time || {from: 'now-6h', to: 'now'};
  63. this.timepicker = data.timepicker || {};
  64. this.templating = this.ensureListExist(data.templating);
  65. this.annotations = this.ensureListExist(data.annotations);
  66. this.refresh = data.refresh;
  67. this.snapshot = data.snapshot;
  68. this.schemaVersion = data.schemaVersion || 0;
  69. this.version = data.version || 0;
  70. this.links = data.links || [];
  71. this.gnetId = data.gnetId || null;
  72. this.folderId = data.folderId || null;
  73. this.panels = _.map(data.panels || [], panelData => new PanelModel(panelData));
  74. this.initMeta(meta);
  75. this.updateSchema(data);
  76. this.addBuiltInAnnotationQuery();
  77. this.sortPanelsByGridPos();
  78. }
  79. addBuiltInAnnotationQuery() {
  80. let found = false;
  81. for (let item of this.annotations.list) {
  82. if (item.builtIn === 1) {
  83. found = true;
  84. break;
  85. }
  86. }
  87. if (found) {
  88. return;
  89. }
  90. this.annotations.list.unshift({
  91. datasource: '-- Grafana --',
  92. name: 'Annotations & Alerts',
  93. type: 'dashboard',
  94. iconColor: DEFAULT_ANNOTATION_COLOR,
  95. enable: true,
  96. hide: true,
  97. builtIn: 1,
  98. });
  99. }
  100. private initMeta(meta) {
  101. meta = meta || {};
  102. meta.canShare = meta.canShare !== false;
  103. meta.canSave = meta.canSave !== false;
  104. meta.canStar = meta.canStar !== false;
  105. meta.canEdit = meta.canEdit !== false;
  106. if (!this.editable) {
  107. meta.canEdit = false;
  108. meta.canDelete = false;
  109. meta.canSave = false;
  110. }
  111. this.meta = meta;
  112. }
  113. // cleans meta data and other non peristent state
  114. getSaveModelClone() {
  115. // make clone
  116. var copy: any = {};
  117. for (var property in this) {
  118. if (DashboardModel.nonPersistedProperties[property] || !this.hasOwnProperty(property)) {
  119. continue;
  120. }
  121. copy[property] = _.cloneDeep(this[property]);
  122. }
  123. // get variable save models
  124. copy.templating = {
  125. list: _.map(this.templating.list, variable => (variable.getSaveModel ? variable.getSaveModel() : variable)),
  126. };
  127. // get panel save models
  128. copy.panels = _.map(this.panels, panel => panel.getSaveModel());
  129. // sort by keys
  130. copy = sortByKeys(copy);
  131. return copy;
  132. }
  133. setViewMode(panel: PanelModel, fullscreen: boolean, isEditing: boolean) {
  134. this.meta.fullscreen = fullscreen;
  135. this.meta.isEditing = isEditing && this.meta.canEdit;
  136. panel.setViewMode(fullscreen, this.meta.isEditing);
  137. this.events.emit('view-mode-changed', panel);
  138. }
  139. private ensureListExist(data) {
  140. if (!data) {
  141. data = {};
  142. }
  143. if (!data.list) {
  144. data.list = [];
  145. }
  146. return data;
  147. }
  148. getNextPanelId() {
  149. let max = 0;
  150. for (let panel of this.panels) {
  151. if (panel.id > max) {
  152. max = panel.id;
  153. }
  154. }
  155. return max + 1;
  156. }
  157. forEachPanel(callback) {
  158. for (let i = 0; i < this.panels.length; i++) {
  159. callback(this.panels[i], i);
  160. }
  161. }
  162. getPanelById(id) {
  163. for (let panel of this.panels) {
  164. if (panel.id === id) {
  165. return panel;
  166. }
  167. }
  168. return null;
  169. }
  170. addPanel(panelData) {
  171. panelData.id = this.getNextPanelId();
  172. let panel = new PanelModel(panelData);
  173. this.panels.unshift(panel);
  174. this.sortPanelsByGridPos();
  175. this.events.emit('panel-added', panel);
  176. }
  177. sortPanelsByGridPos() {
  178. this.panels.sort(function(panelA, panelB) {
  179. if (panelA.gridPos.y === panelB.gridPos.y) {
  180. return panelA.gridPos.x - panelB.gridPos.x;
  181. } else {
  182. return panelA.gridPos.y - panelB.gridPos.y;
  183. }
  184. });
  185. }
  186. cleanUpRepeats() {
  187. this.processRepeats(true);
  188. }
  189. processRepeats(cleanUpOnly?: boolean) {
  190. if (this.snapshot || this.templating.list.length === 0) {
  191. return;
  192. }
  193. this.iteration = (this.iteration || new Date().getTime()) + 1;
  194. let panelsToRemove = [];
  195. // cleanup scopedVars
  196. for (let panel of this.panels) {
  197. delete panel.scopedVars;
  198. }
  199. for (let i = 0; i < this.panels.length; i++) {
  200. let panel = this.panels[i];
  201. if (panel.repeat) {
  202. if (!cleanUpOnly) {
  203. this.repeatPanel(panel, i);
  204. }
  205. } else if (panel.repeatPanelId && panel.repeatIteration !== this.iteration) {
  206. panelsToRemove.push(panel);
  207. }
  208. }
  209. // for (let panel of this.panels) {
  210. // if (panel.repeat) {
  211. // if (!cleanUpOnly) {
  212. // this.repeatPanel(panel);
  213. // }
  214. // } else if (panel.repeatPanelId && panel.repeatIteration !== this.iteration) {
  215. // panelsToRemove.push(panel);
  216. // }
  217. // }
  218. // remove panels
  219. _.pull(this.panels, ...panelsToRemove);
  220. this.sortPanelsByGridPos();
  221. this.events.emit('repeats-processed');
  222. }
  223. getPanelRepeatClone(sourcePanel, valueIndex, sourcePanelIndex) {
  224. // if first clone return source
  225. if (valueIndex === 0) {
  226. return sourcePanel;
  227. }
  228. var clone = new PanelModel(sourcePanel.getSaveModel());
  229. clone.id = this.getNextPanelId();
  230. if (sourcePanel.type === 'row') {
  231. // for row clones we need to figure out panels under row to clone and where to insert clone
  232. let rowPanels = this.getRowPanels(sourcePanelIndex);
  233. clone.panels = _.map(rowPanels, panel => panel.getSaveModel());
  234. // insert after preceding row's panels
  235. let insertPos = sourcePanelIndex + ((rowPanels.length + 1)*valueIndex);
  236. this.panels.splice(insertPos, 0, clone);
  237. } else {
  238. // insert after source panel + value index
  239. this.panels.splice(sourcePanelIndex+valueIndex, 0, clone);
  240. }
  241. clone.repeatIteration = this.iteration;
  242. clone.repeatPanelId = sourcePanel.id;
  243. clone.repeat = null;
  244. return clone;
  245. }
  246. getBottomYForRow() {
  247. }
  248. repeatPanel(panel: PanelModel, panelIndex: number) {
  249. var variable = _.find(this.templating.list, {name: panel.repeat});
  250. if (!variable) {
  251. return;
  252. }
  253. var selected;
  254. if (variable.current.text === 'All') {
  255. selected = variable.options.slice(1, variable.options.length);
  256. } else {
  257. selected = _.filter(variable.options, {selected: true});
  258. }
  259. let minWidth = panel.minSpan || 6;
  260. let xPos = 0;
  261. let yPos = panel.gridPos.y;
  262. for (let index = 0; index < selected.length; index++) {
  263. var option = selected[index];
  264. var copy = this.getPanelRepeatClone(panel, index, panelIndex);
  265. copy.scopedVars = {};
  266. copy.scopedVars[variable.name] = option;
  267. if (copy.type === 'row') {
  268. // place row below row panels
  269. }
  270. if (panel.repeatDirection === REPEAT_DIR_VERTICAL) {
  271. copy.gridPos.y = yPos;
  272. yPos += copy.gridPos.h;
  273. } else {
  274. // set width based on how many are selected
  275. // assumed the repeated panels should take up full row width
  276. copy.gridPos.w = Math.max(GRID_COLUMN_COUNT / selected.length, minWidth);
  277. copy.gridPos.x = xPos;
  278. copy.gridPos.y = yPos;
  279. xPos += copy.gridPos.w;
  280. // handle overflow by pushing down one row
  281. if (xPos + copy.gridPos.w > GRID_COLUMN_COUNT) {
  282. xPos = 0;
  283. yPos += copy.gridPos.h;
  284. }
  285. }
  286. }
  287. }
  288. removePanel(panel: PanelModel) {
  289. var index = _.indexOf(this.panels, panel);
  290. this.panels.splice(index, 1);
  291. this.events.emit('panel-removed', panel);
  292. }
  293. setPanelFocus(id) {
  294. this.meta.focusPanelId = id;
  295. }
  296. updateSubmenuVisibility() {
  297. this.meta.submenuEnabled = (() => {
  298. if (this.links.length > 0) {
  299. return true;
  300. }
  301. var visibleVars = _.filter(this.templating.list, variable => variable.hide !== 2);
  302. if (visibleVars.length > 0) {
  303. return true;
  304. }
  305. var visibleAnnotations = _.filter(this.annotations.list, annotation => annotation.hide !== true);
  306. if (visibleAnnotations.length > 0) {
  307. return true;
  308. }
  309. return false;
  310. })();
  311. }
  312. getPanelInfoById(panelId) {
  313. for (let i = 0; i < this.panels.length; i++) {
  314. if (this.panels[i].id === panelId) {
  315. return {
  316. panel: this.panels[i],
  317. index: i,
  318. };
  319. }
  320. }
  321. return null;
  322. }
  323. duplicatePanel(panel) {
  324. const newPanel = panel.getSaveModel();
  325. newPanel.id = this.getNextPanelId();
  326. delete newPanel.repeat;
  327. delete newPanel.repeatIteration;
  328. delete newPanel.repeatPanelId;
  329. delete newPanel.scopedVars;
  330. if (newPanel.alert) {
  331. delete newPanel.thresholds;
  332. }
  333. delete newPanel.alert;
  334. // does it fit to the right?
  335. if (panel.gridPos.x + panel.gridPos.w * 2 <= GRID_COLUMN_COUNT) {
  336. newPanel.gridPos.x += panel.gridPos.w;
  337. } else {
  338. // add bellow
  339. newPanel.gridPos.y += panel.gridPos.h;
  340. }
  341. this.addPanel(newPanel);
  342. return newPanel;
  343. }
  344. formatDate(date, format?) {
  345. date = moment.isMoment(date) ? date : moment(date);
  346. format = format || 'YYYY-MM-DD HH:mm:ss';
  347. let timezone = this.getTimezone();
  348. return timezone === 'browser' ? moment(date).format(format) : moment.utc(date).format(format);
  349. }
  350. destroy() {
  351. this.events.removeAllListeners();
  352. for (let panel of this.panels) {
  353. panel.destroy();
  354. }
  355. }
  356. toggleRow(row: PanelModel) {
  357. let rowIndex = _.indexOf(this.panels, row);
  358. if (row.collapsed) {
  359. row.collapsed = false;
  360. if (row.panels.length > 0) {
  361. // Use first panel to figure out if it was moved or pushed
  362. let firstPanel = row.panels[0];
  363. let yDiff = firstPanel.gridPos.y - (row.gridPos.y + row.gridPos.h);
  364. // start inserting after row
  365. let insertPos = rowIndex+1;
  366. // y max will represent the bottom y pos after all panels have been added
  367. // needed to know home much panels below should be pushed down
  368. let yMax = row.gridPos.y;
  369. for (let panel of row.panels) {
  370. // make sure y is adjusted (in case row moved while collapsed)
  371. panel.gridPos.y -= yDiff;
  372. // insert after row
  373. this.panels.splice(insertPos, 0, new PanelModel(panel));
  374. // update insert post and y max
  375. insertPos += 1;
  376. yMax = Math.max(yMax, panel.gridPos.y + panel.gridPos.h);
  377. }
  378. const pushDownAmount = yMax - row.gridPos.y;
  379. // push panels below down
  380. for (let panelIndex = insertPos; panelIndex < this.panels.length; panelIndex++) {
  381. this.panels[panelIndex].gridPos.y += pushDownAmount;
  382. }
  383. row.panels = [];
  384. }
  385. // sort panels
  386. this.sortPanelsByGridPos();
  387. // emit change event
  388. this.events.emit('row-expanded');
  389. return;
  390. }
  391. let rowPanels = this.getRowPanels(rowIndex);
  392. // remove panels
  393. _.pull(this.panels, ...rowPanels);
  394. // save panel models inside row panel
  395. row.panels = _.map(rowPanels, panel => panel.getSaveModel());
  396. row.collapsed = true;
  397. // emit change event
  398. this.events.emit('row-collapsed');
  399. }
  400. /**
  401. * Will return all panels after rowIndex until it encounters another row
  402. */
  403. getRowPanels(rowIndex: number): PanelModel[] {
  404. let rowPanels = [];
  405. for (let index = rowIndex+1; index < this.panels.length; index++) {
  406. let panel = this.panels[index];
  407. // break when encountering another row
  408. if (panel.type === 'row') {
  409. break;
  410. }
  411. // this panel must belong to row
  412. rowPanels.push(panel);
  413. }
  414. return rowPanels;
  415. }
  416. on(eventName, callback) {
  417. this.events.on(eventName, callback);
  418. }
  419. off(eventName, callback?) {
  420. this.events.off(eventName, callback);
  421. }
  422. cycleGraphTooltip() {
  423. this.graphTooltip = (this.graphTooltip + 1) % 3;
  424. }
  425. sharedTooltipModeEnabled() {
  426. return this.graphTooltip > 0;
  427. }
  428. sharedCrosshairModeOnly() {
  429. return this.graphTooltip === 1;
  430. }
  431. getRelativeTime(date) {
  432. date = moment.isMoment(date) ? date : moment(date);
  433. return this.timezone === 'browser' ? moment(date).fromNow() : moment.utc(date).fromNow();
  434. }
  435. getNextQueryLetter(panel) {
  436. var letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
  437. return _.find(letters, function(refId) {
  438. return _.every(panel.targets, function(other) {
  439. return other.refId !== refId;
  440. });
  441. });
  442. }
  443. isTimezoneUtc() {
  444. return this.getTimezone() === 'utc';
  445. }
  446. getTimezone() {
  447. return this.timezone ? this.timezone : contextSrv.user.timezone;
  448. }
  449. private updateSchema(old) {
  450. var i, j, k;
  451. var oldVersion = this.schemaVersion;
  452. var panelUpgrades = [];
  453. this.schemaVersion = 16;
  454. if (oldVersion === this.schemaVersion) {
  455. return;
  456. }
  457. // version 2 schema changes
  458. if (oldVersion < 2) {
  459. if (old.services) {
  460. if (old.services.filter) {
  461. this.time = old.services.filter.time;
  462. this.templating.list = old.services.filter.list || [];
  463. }
  464. }
  465. panelUpgrades.push(function(panel) {
  466. // rename panel type
  467. if (panel.type === 'graphite') {
  468. panel.type = 'graph';
  469. }
  470. if (panel.type !== 'graph') {
  471. return;
  472. }
  473. if (_.isBoolean(panel.legend)) {
  474. panel.legend = {show: panel.legend};
  475. }
  476. if (panel.grid) {
  477. if (panel.grid.min) {
  478. panel.grid.leftMin = panel.grid.min;
  479. delete panel.grid.min;
  480. }
  481. if (panel.grid.max) {
  482. panel.grid.leftMax = panel.grid.max;
  483. delete panel.grid.max;
  484. }
  485. }
  486. if (panel.y_format) {
  487. panel.y_formats[0] = panel.y_format;
  488. delete panel.y_format;
  489. }
  490. if (panel.y2_format) {
  491. panel.y_formats[1] = panel.y2_format;
  492. delete panel.y2_format;
  493. }
  494. });
  495. }
  496. // schema version 3 changes
  497. if (oldVersion < 3) {
  498. // ensure panel ids
  499. var maxId = this.getNextPanelId();
  500. panelUpgrades.push(function(panel) {
  501. if (!panel.id) {
  502. panel.id = maxId;
  503. maxId += 1;
  504. }
  505. });
  506. }
  507. // schema version 4 changes
  508. if (oldVersion < 4) {
  509. // move aliasYAxis changes
  510. panelUpgrades.push(function(panel) {
  511. if (panel.type !== 'graph') {
  512. return;
  513. }
  514. _.each(panel.aliasYAxis, function(value, key) {
  515. panel.seriesOverrides = [{alias: key, yaxis: value}];
  516. });
  517. delete panel.aliasYAxis;
  518. });
  519. }
  520. if (oldVersion < 6) {
  521. // move pulldowns to new schema
  522. var annotations = _.find(old.pulldowns, {type: 'annotations'});
  523. if (annotations) {
  524. this.annotations = {
  525. list: annotations.annotations || [],
  526. };
  527. }
  528. // update template variables
  529. for (i = 0; i < this.templating.list.length; i++) {
  530. var variable = this.templating.list[i];
  531. if (variable.datasource === void 0) {
  532. variable.datasource = null;
  533. }
  534. if (variable.type === 'filter') {
  535. variable.type = 'query';
  536. }
  537. if (variable.type === void 0) {
  538. variable.type = 'query';
  539. }
  540. if (variable.allFormat === void 0) {
  541. variable.allFormat = 'glob';
  542. }
  543. }
  544. }
  545. if (oldVersion < 7) {
  546. if (old.nav && old.nav.length) {
  547. this.timepicker = old.nav[0];
  548. }
  549. // ensure query refIds
  550. panelUpgrades.push(function(panel) {
  551. _.each(
  552. panel.targets,
  553. function(target) {
  554. if (!target.refId) {
  555. target.refId = this.getNextQueryLetter(panel);
  556. }
  557. }.bind(this),
  558. );
  559. });
  560. }
  561. if (oldVersion < 8) {
  562. panelUpgrades.push(function(panel) {
  563. _.each(panel.targets, function(target) {
  564. // update old influxdb query schema
  565. if (target.fields && target.tags && target.groupBy) {
  566. if (target.rawQuery) {
  567. delete target.fields;
  568. delete target.fill;
  569. } else {
  570. target.select = _.map(target.fields, function(field) {
  571. var parts = [];
  572. parts.push({type: 'field', params: [field.name]});
  573. parts.push({type: field.func, params: []});
  574. if (field.mathExpr) {
  575. parts.push({type: 'math', params: [field.mathExpr]});
  576. }
  577. if (field.asExpr) {
  578. parts.push({type: 'alias', params: [field.asExpr]});
  579. }
  580. return parts;
  581. });
  582. delete target.fields;
  583. _.each(target.groupBy, function(part) {
  584. if (part.type === 'time' && part.interval) {
  585. part.params = [part.interval];
  586. delete part.interval;
  587. }
  588. if (part.type === 'tag' && part.key) {
  589. part.params = [part.key];
  590. delete part.key;
  591. }
  592. });
  593. if (target.fill) {
  594. target.groupBy.push({type: 'fill', params: [target.fill]});
  595. delete target.fill;
  596. }
  597. }
  598. }
  599. });
  600. });
  601. }
  602. // schema version 9 changes
  603. if (oldVersion < 9) {
  604. // move aliasYAxis changes
  605. panelUpgrades.push(function(panel) {
  606. if (panel.type !== 'singlestat' && panel.thresholds !== '') {
  607. return;
  608. }
  609. if (panel.thresholds) {
  610. var k = panel.thresholds.split(',');
  611. if (k.length >= 3) {
  612. k.shift();
  613. panel.thresholds = k.join(',');
  614. }
  615. }
  616. });
  617. }
  618. // schema version 10 changes
  619. if (oldVersion < 10) {
  620. // move aliasYAxis changes
  621. panelUpgrades.push(function(panel) {
  622. if (panel.type !== 'table') {
  623. return;
  624. }
  625. _.each(panel.styles, function(style) {
  626. if (style.thresholds && style.thresholds.length >= 3) {
  627. var k = style.thresholds;
  628. k.shift();
  629. style.thresholds = k;
  630. }
  631. });
  632. });
  633. }
  634. if (oldVersion < 12) {
  635. // update template variables
  636. _.each(this.templating.list, function(templateVariable) {
  637. if (templateVariable.refresh) {
  638. templateVariable.refresh = 1;
  639. }
  640. if (!templateVariable.refresh) {
  641. templateVariable.refresh = 0;
  642. }
  643. if (templateVariable.hideVariable) {
  644. templateVariable.hide = 2;
  645. } else if (templateVariable.hideLabel) {
  646. templateVariable.hide = 1;
  647. }
  648. });
  649. }
  650. if (oldVersion < 12) {
  651. // update graph yaxes changes
  652. panelUpgrades.push(function(panel) {
  653. if (panel.type !== 'graph') {
  654. return;
  655. }
  656. if (!panel.grid) {
  657. return;
  658. }
  659. if (!panel.yaxes) {
  660. panel.yaxes = [
  661. {
  662. show: panel['y-axis'],
  663. min: panel.grid.leftMin,
  664. max: panel.grid.leftMax,
  665. logBase: panel.grid.leftLogBase,
  666. format: panel.y_formats[0],
  667. label: panel.leftYAxisLabel,
  668. },
  669. {
  670. show: panel['y-axis'],
  671. min: panel.grid.rightMin,
  672. max: panel.grid.rightMax,
  673. logBase: panel.grid.rightLogBase,
  674. format: panel.y_formats[1],
  675. label: panel.rightYAxisLabel,
  676. },
  677. ];
  678. panel.xaxis = {
  679. show: panel['x-axis'],
  680. };
  681. delete panel.grid.leftMin;
  682. delete panel.grid.leftMax;
  683. delete panel.grid.leftLogBase;
  684. delete panel.grid.rightMin;
  685. delete panel.grid.rightMax;
  686. delete panel.grid.rightLogBase;
  687. delete panel.y_formats;
  688. delete panel.leftYAxisLabel;
  689. delete panel.rightYAxisLabel;
  690. delete panel['y-axis'];
  691. delete panel['x-axis'];
  692. }
  693. });
  694. }
  695. if (oldVersion < 13) {
  696. // update graph yaxes changes
  697. panelUpgrades.push(function(panel) {
  698. if (panel.type !== 'graph') {
  699. return;
  700. }
  701. if (!panel.grid) {
  702. return;
  703. }
  704. panel.thresholds = [];
  705. var t1: any = {},
  706. t2: any = {};
  707. if (panel.grid.threshold1 !== null) {
  708. t1.value = panel.grid.threshold1;
  709. if (panel.grid.thresholdLine) {
  710. t1.line = true;
  711. t1.lineColor = panel.grid.threshold1Color;
  712. t1.colorMode = 'custom';
  713. } else {
  714. t1.fill = true;
  715. t1.fillColor = panel.grid.threshold1Color;
  716. t1.colorMode = 'custom';
  717. }
  718. }
  719. if (panel.grid.threshold2 !== null) {
  720. t2.value = panel.grid.threshold2;
  721. if (panel.grid.thresholdLine) {
  722. t2.line = true;
  723. t2.lineColor = panel.grid.threshold2Color;
  724. t2.colorMode = 'custom';
  725. } else {
  726. t2.fill = true;
  727. t2.fillColor = panel.grid.threshold2Color;
  728. t2.colorMode = 'custom';
  729. }
  730. }
  731. if (_.isNumber(t1.value)) {
  732. if (_.isNumber(t2.value)) {
  733. if (t1.value > t2.value) {
  734. t1.op = t2.op = 'lt';
  735. panel.thresholds.push(t1);
  736. panel.thresholds.push(t2);
  737. } else {
  738. t1.op = t2.op = 'gt';
  739. panel.thresholds.push(t1);
  740. panel.thresholds.push(t2);
  741. }
  742. } else {
  743. t1.op = 'gt';
  744. panel.thresholds.push(t1);
  745. }
  746. }
  747. delete panel.grid.threshold1;
  748. delete panel.grid.threshold1Color;
  749. delete panel.grid.threshold2;
  750. delete panel.grid.threshold2Color;
  751. delete panel.grid.thresholdLine;
  752. });
  753. }
  754. if (oldVersion < 14) {
  755. this.graphTooltip = old.sharedCrosshair ? 1 : 0;
  756. }
  757. if (oldVersion < 16) {
  758. this.upgradeToGridLayout(old);
  759. }
  760. if (panelUpgrades.length === 0) {
  761. return;
  762. }
  763. for (j = 0; j < this.panels.length; j++) {
  764. for (k = 0; k < panelUpgrades.length; k++) {
  765. panelUpgrades[k].call(this, this.panels[j]);
  766. }
  767. }
  768. }
  769. upgradeToGridLayout(old) {
  770. let yPos = 0;
  771. let widthFactor = GRID_COLUMN_COUNT / 12;
  772. //let rowIds = 1000;
  773. //
  774. if (!old.rows) {
  775. return;
  776. }
  777. for (let row of old.rows) {
  778. let xPos = 0;
  779. let height: any = row.height || 250;
  780. // if (this.meta.keepRows) {
  781. // this.panels.push({
  782. // id: rowIds++,
  783. // type: 'row',
  784. // title: row.title,
  785. // x: 0,
  786. // y: yPos,
  787. // height: 1,
  788. // width: 12
  789. // });
  790. //
  791. // yPos += 1;
  792. // }
  793. if (_.isString(height)) {
  794. height = parseInt(height.replace('px', ''), 10);
  795. }
  796. const rowGridHeight = Math.ceil(height / GRID_CELL_HEIGHT);
  797. for (let panel of row.panels) {
  798. const panelWidth = Math.floor(panel.span) * widthFactor;
  799. // should wrap to next row?
  800. if (xPos + panelWidth >= GRID_COLUMN_COUNT) {
  801. yPos += rowGridHeight;
  802. }
  803. panel.gridPos = {x: xPos, y: yPos, w: panelWidth, h: rowGridHeight};
  804. delete panel.span;
  805. xPos += panel.gridPos.w;
  806. this.panels.push(new PanelModel(panel));
  807. }
  808. yPos += rowGridHeight;
  809. }
  810. }
  811. }