graph.vue 8.43 KB
Newer Older
1 2
<script>
  import d3 from 'd3';
3 4 5
  import GraphLegend from './graph/legend.vue';
  import GraphFlag from './graph/flag.vue';
  import GraphDeployment from './graph/deployment.vue';
Jose Ivan Vargas's avatar
Jose Ivan Vargas committed
6
  import GraphPath from './graph_path.vue';
7 8 9
  import MonitoringMixin from '../mixins/monitoring_mixins';
  import eventHub from '../event_hub';
  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 14 15 16 17

  const bisectDate = d3.bisector(d => d.time).left;

  export default {
    props: {
18
      graphData: {
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
        type: Object,
        required: true,
      },
      updateAspectRatio: {
        type: Boolean,
        required: true,
      },
      deploymentData: {
        type: Array,
        required: true,
      },
    },

    mixins: [MonitoringMixin],

    data() {
      return {
36 37
        baseGraphHeight: 450,
        baseGraphWidth: 600,
38
        graphHeight: 450,
39 40 41 42 43 44 45 46 47 48 49 50
        graphWidth: 600,
        graphHeightOffset: 120,
        margin: {},
        unitOfDisplay: '',
        yAxisLabel: '',
        legendTitle: '',
        reducedDeploymentData: [],
        measurements: measurements.large,
        currentData: {
          time: new Date(),
          value: 0,
        },
51
        currentDataIndex: 0,
52 53 54 55
        currentXCoordinate: 0,
        currentFlagPosition: 0,
        showFlag: false,
        showDeployInfo: true,
56
        timeSeries: [],
57 58 59 60
      };
    },

    components: {
61 62 63
      GraphLegend,
      GraphFlag,
      GraphDeployment,
Jose Ivan Vargas's avatar
Jose Ivan Vargas committed
64
      GraphPath,
65 66 67 68
    },

    computed: {
      outterViewBox() {
69
        return `0 0 ${this.baseGraphWidth} ${this.baseGraphHeight}`;
70 71 72
      },

      innerViewBox() {
73 74
        if ((this.baseGraphWidth - 150) > 0) {
          return `0 0 ${this.baseGraphWidth - 150} ${this.baseGraphHeight}`;
75 76 77 78 79 80 81 82 83
        }
        return '0 0 0 0';
      },

      axisTransform() {
        return `translate(70, ${this.graphHeight - 100})`;
      },

      paddingBottomRootSvg() {
84
        return {
85
          paddingBottom: `${(Math.ceil(this.baseGraphHeight * 100) / this.baseGraphWidth) || 0}%`,
86
        };
87 88 89 90 91
      },
    },

    methods: {
      draw() {
92
        const breakpointSize = bp.getBreakpointSize();
93
        const query = this.graphData.queries[0];
94 95 96 97 98 99
        this.margin = measurements.large.margin;
        if (breakpointSize === 'xs' || breakpointSize === 'sm') {
          this.graphHeight = 300;
          this.margin = measurements.small.margin;
          this.measurements = measurements.small;
        }
100
        this.unitOfDisplay = query.unit || '';
101
        this.yAxisLabel = this.graphData.y_label || 'Values';
102
        this.legendTitle = query.label || 'Average';
103 104 105
        this.graphWidth = this.$refs.baseSvg.clientWidth -
                     this.margin.left - this.margin.right;
        this.graphHeight = this.graphHeight - this.margin.top - this.margin.bottom;
106 107
        this.baseGraphHeight = this.graphHeight;
        this.baseGraphWidth = this.graphWidth;
108 109
        this.renderAxesPaths();
        this.formatDeployments();
110 111 112 113 114 115 116 117
      },

      handleMouseOverGraph(e) {
        let point = this.$refs.graphData.createSVGPoint();
        point.x = e.clientX;
        point.y = e.clientY;
        point = point.matrixTransform(this.$refs.graphData.getScreenCTM().inverse());
        point.x = point.x += 7;
118 119 120 121 122
        const firstTimeSeries = this.timeSeries[0];
        const timeValueOverlay = firstTimeSeries.timeSeriesScaleX.invert(point.x);
        const overlayIndex = bisectDate(firstTimeSeries.values, timeValueOverlay, 1);
        const d0 = firstTimeSeries.values[overlayIndex - 1];
        const d1 = firstTimeSeries.values[overlayIndex];
123 124 125
        if (d0 === undefined || d1 === undefined) return;
        const evalTime = timeValueOverlay - d0[0] > d1[0] - timeValueOverlay;
        this.currentData = evalTime ? d1 : d0;
126 127
        this.currentDataIndex = evalTime ? overlayIndex : (overlayIndex - 1);
        this.currentXCoordinate = Math.floor(firstTimeSeries.timeSeriesScaleX(this.currentData.time));
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
        const currentDeployXPos = this.mouseOverDeployInfo(point.x);

        if (this.currentXCoordinate > (this.graphWidth - 200)) {
          this.currentFlagPosition = this.currentXCoordinate - 103;
        } else {
          this.currentFlagPosition = this.currentXCoordinate;
        }

        if (currentDeployXPos) {
          this.showFlag = false;
        } else {
          this.showFlag = true;
        }
      },

      renderAxesPaths() {
Mike Greiling's avatar
Mike Greiling committed
144 145 146 147 148 149
        this.timeSeries = createTimeSeries(
          this.graphData.queries[0],
          this.graphWidth,
          this.graphHeight,
          this.graphHeightOffset
        );
150

151 152
        if (this.timeSeries.length > 3) {
          this.baseGraphHeight = this.baseGraphHeight += (this.timeSeries.length - 3) * 20;
153 154
        }

155 156
        const axisXScale = d3.time.scale()
          .range([0, this.graphWidth]);
157
        const axisYScale = d3.scale.linear()
158
          .range([this.graphHeight - this.graphHeightOffset, 0]);
159 160 161

        axisXScale.domain(d3.extent(this.timeSeries[0].values, d => d.time));
        axisYScale.domain([0, d3.max(this.timeSeries[0].values.map(d => d.value))]);
162 163 164

        const xAxis = d3.svg.axis()
          .scale(axisXScale)
165
          .ticks(d3.time.minute, 60)
166
          .tickFormat(timeScaleFormat)
167 168 169
          .orient('bottom');

        const yAxis = d3.svg.axis()
170
          .scale(axisYScale)
171
          .ticks(measurements.yTicks)
172 173 174 175 176 177 178
          .orient('left');

        d3.select(this.$refs.baseSvg).select('.x-axis').call(xAxis);

        const width = this.graphWidth;
        d3.select(this.$refs.baseSvg).select('.y-axis').call(yAxis)
          .selectAll('.tick')
179 180 181 182 183 184
          .each(function createTickLines(d, i) {
            if (i > 0) {
              d3.select(this).select('line')
                .attr('x2', width)
                .attr('class', 'axis-tick');
            } // Avoid adding the class to the first tick, to prevent coloring
185 186 187 188 189 190 191
          }); // This will select all of the ticks once they're rendered
      },
    },

    watch: {
      updateAspectRatio() {
        if (this.updateAspectRatio) {
192
          this.graphHeight = 450;
193 194 195 196 197 198 199 200 201 202 203 204 205
          this.graphWidth = 600;
          this.measurements = measurements.large;
          this.draw();
          eventHub.$emit('toggleAspectRatio');
        }
      },
    },

    mounted() {
      this.draw();
    },
  };
</script>
206

207
<template>
208 209 210
  <div class="prometheus-graph">
    <h5 class="text-center graph-title">
      {{graphData.title}}
211
    </h5>
212 213 214
    <div
      class="prometheus-svg-container"
      :style="paddingBottomRootSvg">
215
      <svg
216 217 218 219 220 221 222 223 224 225
        :viewBox="outterViewBox"
        ref="baseSvg">
        <g
          class="x-axis"
          :transform="axisTransform">
        </g>
        <g
          class="y-axis"
          transform="translate(70, 20)">
        </g>
226
        <graph-legend
227 228 229 230 231 232
          :graph-width="graphWidth"
          :graph-height="graphHeight"
          :margin="margin"
          :measurements="measurements"
          :legend-title="legendTitle"
          :y-axis-label="yAxisLabel"
233 234 235
          :time-series="timeSeries"
          :unit-of-display="unitOfDisplay"
          :current-data-index="currentDataIndex"
236
        />
237
        <svg
238 239 240
          class="graph-data"
          :viewBox="innerViewBox"
          ref="graphData">
Jose Ivan Vargas's avatar
Jose Ivan Vargas committed
241
            <graph-path
242 243 244 245
              v-for="(path, index) in timeSeries"
              :key="index"
              :generated-line-path="path.linePath"
              :generated-area-path="path.areaPath"
246 247
              :line-color="path.lineColor"
              :area-color="path.areaColor"
248
            />
Jose Ivan Vargas's avatar
Jose Ivan Vargas committed
249
            <graph-deployment
250 251 252 253 254
              :show-deploy-info="showDeployInfo"
              :deployment-data="reducedDeploymentData"
              :graph-height="graphHeight"
              :graph-height-offset="graphHeightOffset"
            />
255
            <graph-flag
256 257 258 259 260 261 262
              v-if="showFlag"
              :current-x-coordinate="currentXCoordinate"
              :current-data="currentData"
              :current-flag-position="currentFlagPosition"
              :graph-height="graphHeight"
              :graph-height-offset="graphHeightOffset"
            />
263 264 265 266 267 268 269 270
            <rect
              class="prometheus-graph-overlay"
              :width="(graphWidth - 70)"
              :height="(graphHeight - 100)"
              transform="translate(-5, 20)"
              ref="graphOverlay"
              @mousemove="handleMouseOverGraph($event)">
            </rect>
271 272 273 274 275
        </svg>
      </svg>
    </div>
  </div>
</template>