PageRenderTime 37ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 0ms

/app/assets/javascripts/monitoring/components/graph.vue

https://gitlab.com/certik/gitlab-ce
Vue | 273 lines | 249 code | 24 blank | 0 comment | 16 complexity | 37783911f224bedbbb6dd01e44c6e5be MD5 | raw file
  1. <script>
  2. import d3 from 'd3';
  3. import GraphLegend from './graph/legend.vue';
  4. import GraphFlag from './graph/flag.vue';
  5. import GraphDeployment from './graph/deployment.vue';
  6. import GraphPath from './graph_path.vue';
  7. import MonitoringMixin from '../mixins/monitoring_mixins';
  8. import eventHub from '../event_hub';
  9. import measurements from '../utils/measurements';
  10. import { timeScaleFormat } from '../utils/date_time_formatters';
  11. import createTimeSeries from '../utils/multiple_time_series';
  12. import bp from '../../breakpoints';
  13. const bisectDate = d3.bisector(d => d.time).left;
  14. export default {
  15. props: {
  16. graphData: {
  17. type: Object,
  18. required: true,
  19. },
  20. updateAspectRatio: {
  21. type: Boolean,
  22. required: true,
  23. },
  24. deploymentData: {
  25. type: Array,
  26. required: true,
  27. },
  28. },
  29. mixins: [MonitoringMixin],
  30. data() {
  31. return {
  32. baseGraphHeight: 450,
  33. baseGraphWidth: 600,
  34. graphHeight: 450,
  35. graphWidth: 600,
  36. graphHeightOffset: 120,
  37. margin: {},
  38. unitOfDisplay: '',
  39. yAxisLabel: '',
  40. legendTitle: '',
  41. reducedDeploymentData: [],
  42. measurements: measurements.large,
  43. currentData: {
  44. time: new Date(),
  45. value: 0,
  46. },
  47. currentDataIndex: 0,
  48. currentXCoordinate: 0,
  49. currentFlagPosition: 0,
  50. showFlag: false,
  51. showDeployInfo: true,
  52. timeSeries: [],
  53. };
  54. },
  55. components: {
  56. GraphLegend,
  57. GraphFlag,
  58. GraphDeployment,
  59. GraphPath,
  60. },
  61. computed: {
  62. outterViewBox() {
  63. return `0 0 ${this.baseGraphWidth} ${this.baseGraphHeight}`;
  64. },
  65. innerViewBox() {
  66. if ((this.baseGraphWidth - 150) > 0) {
  67. return `0 0 ${this.baseGraphWidth - 150} ${this.baseGraphHeight}`;
  68. }
  69. return '0 0 0 0';
  70. },
  71. axisTransform() {
  72. return `translate(70, ${this.graphHeight - 100})`;
  73. },
  74. paddingBottomRootSvg() {
  75. return {
  76. paddingBottom: `${(Math.ceil(this.baseGraphHeight * 100) / this.baseGraphWidth) || 0}%`,
  77. };
  78. },
  79. },
  80. methods: {
  81. draw() {
  82. const breakpointSize = bp.getBreakpointSize();
  83. const query = this.graphData.queries[0];
  84. this.margin = measurements.large.margin;
  85. if (breakpointSize === 'xs' || breakpointSize === 'sm') {
  86. this.graphHeight = 300;
  87. this.margin = measurements.small.margin;
  88. this.measurements = measurements.small;
  89. }
  90. this.unitOfDisplay = query.unit || '';
  91. this.yAxisLabel = this.graphData.y_label || 'Values';
  92. this.legendTitle = query.label || 'Average';
  93. this.graphWidth = this.$refs.baseSvg.clientWidth -
  94. this.margin.left - this.margin.right;
  95. this.graphHeight = this.graphHeight - this.margin.top - this.margin.bottom;
  96. this.baseGraphHeight = this.graphHeight;
  97. this.baseGraphWidth = this.graphWidth;
  98. this.renderAxesPaths();
  99. this.formatDeployments();
  100. },
  101. handleMouseOverGraph(e) {
  102. let point = this.$refs.graphData.createSVGPoint();
  103. point.x = e.clientX;
  104. point.y = e.clientY;
  105. point = point.matrixTransform(this.$refs.graphData.getScreenCTM().inverse());
  106. point.x = point.x += 7;
  107. const firstTimeSeries = this.timeSeries[0];
  108. const timeValueOverlay = firstTimeSeries.timeSeriesScaleX.invert(point.x);
  109. const overlayIndex = bisectDate(firstTimeSeries.values, timeValueOverlay, 1);
  110. const d0 = firstTimeSeries.values[overlayIndex - 1];
  111. const d1 = firstTimeSeries.values[overlayIndex];
  112. if (d0 === undefined || d1 === undefined) return;
  113. const evalTime = timeValueOverlay - d0[0] > d1[0] - timeValueOverlay;
  114. this.currentData = evalTime ? d1 : d0;
  115. this.currentDataIndex = evalTime ? overlayIndex : (overlayIndex - 1);
  116. this.currentXCoordinate = Math.floor(firstTimeSeries.timeSeriesScaleX(this.currentData.time));
  117. const currentDeployXPos = this.mouseOverDeployInfo(point.x);
  118. if (this.currentXCoordinate > (this.graphWidth - 200)) {
  119. this.currentFlagPosition = this.currentXCoordinate - 103;
  120. } else {
  121. this.currentFlagPosition = this.currentXCoordinate;
  122. }
  123. if (currentDeployXPos) {
  124. this.showFlag = false;
  125. } else {
  126. this.showFlag = true;
  127. }
  128. },
  129. renderAxesPaths() {
  130. this.timeSeries = createTimeSeries(this.graphData.queries[0],
  131. this.graphWidth,
  132. this.graphHeight,
  133. this.graphHeightOffset);
  134. if (this.timeSeries.length > 3) {
  135. this.baseGraphHeight = this.baseGraphHeight += (this.timeSeries.length - 3) * 20;
  136. }
  137. const axisXScale = d3.time.scale()
  138. .range([0, this.graphWidth]);
  139. const axisYScale = d3.scale.linear()
  140. .range([this.graphHeight - this.graphHeightOffset, 0]);
  141. axisXScale.domain(d3.extent(this.timeSeries[0].values, d => d.time));
  142. axisYScale.domain([0, d3.max(this.timeSeries[0].values.map(d => d.value))]);
  143. const xAxis = d3.svg.axis()
  144. .scale(axisXScale)
  145. .ticks(d3.time.minute, 60)
  146. .tickFormat(timeScaleFormat)
  147. .orient('bottom');
  148. const yAxis = d3.svg.axis()
  149. .scale(axisYScale)
  150. .ticks(measurements.yTicks)
  151. .orient('left');
  152. d3.select(this.$refs.baseSvg).select('.x-axis').call(xAxis);
  153. const width = this.graphWidth;
  154. d3.select(this.$refs.baseSvg).select('.y-axis').call(yAxis)
  155. .selectAll('.tick')
  156. .each(function createTickLines(d, i) {
  157. if (i > 0) {
  158. d3.select(this).select('line')
  159. .attr('x2', width)
  160. .attr('class', 'axis-tick');
  161. } // Avoid adding the class to the first tick, to prevent coloring
  162. }); // This will select all of the ticks once they're rendered
  163. },
  164. },
  165. watch: {
  166. updateAspectRatio() {
  167. if (this.updateAspectRatio) {
  168. this.graphHeight = 450;
  169. this.graphWidth = 600;
  170. this.measurements = measurements.large;
  171. this.draw();
  172. eventHub.$emit('toggleAspectRatio');
  173. }
  174. },
  175. },
  176. mounted() {
  177. this.draw();
  178. },
  179. };
  180. </script>
  181. <template>
  182. <div class="prometheus-graph">
  183. <h5 class="text-center graph-title">
  184. {{graphData.title}}
  185. </h5>
  186. <div
  187. class="prometheus-svg-container"
  188. :style="paddingBottomRootSvg">
  189. <svg
  190. :viewBox="outterViewBox"
  191. ref="baseSvg">
  192. <g
  193. class="x-axis"
  194. :transform="axisTransform">
  195. </g>
  196. <g
  197. class="y-axis"
  198. transform="translate(70, 20)">
  199. </g>
  200. <graph-legend
  201. :graph-width="graphWidth"
  202. :graph-height="graphHeight"
  203. :margin="margin"
  204. :measurements="measurements"
  205. :legend-title="legendTitle"
  206. :y-axis-label="yAxisLabel"
  207. :time-series="timeSeries"
  208. :unit-of-display="unitOfDisplay"
  209. :current-data-index="currentDataIndex"
  210. />
  211. <svg
  212. class="graph-data"
  213. :viewBox="innerViewBox"
  214. ref="graphData">
  215. <graph-path
  216. v-for="(path, index) in timeSeries"
  217. :key="index"
  218. :generated-line-path="path.linePath"
  219. :generated-area-path="path.areaPath"
  220. :line-color="path.lineColor"
  221. :area-color="path.areaColor"
  222. />
  223. <graph-deployment
  224. :show-deploy-info="showDeployInfo"
  225. :deployment-data="reducedDeploymentData"
  226. :graph-height="graphHeight"
  227. :graph-height-offset="graphHeightOffset"
  228. />
  229. <graph-flag
  230. v-if="showFlag"
  231. :current-x-coordinate="currentXCoordinate"
  232. :current-data="currentData"
  233. :current-flag-position="currentFlagPosition"
  234. :graph-height="graphHeight"
  235. :graph-height-offset="graphHeightOffset"
  236. />
  237. <rect
  238. class="prometheus-graph-overlay"
  239. :width="(graphWidth - 70)"
  240. :height="(graphHeight - 100)"
  241. transform="translate(-5, 20)"
  242. ref="graphOverlay"
  243. @mousemove="handleMouseOverGraph($event)">
  244. </rect>
  245. </svg>
  246. </svg>
  247. </div>
  248. </div>
  249. </template>