dashboard_model.ts 23 KB

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