From 878ea00e90023d7215567d460adc9489581de948 Mon Sep 17 00:00:00 2001 From: ClaudiaGivan Date: Fri, 6 Sep 2024 21:35:23 +0200 Subject: [PATCH] Emit events with the work items info when clicking on a data point in the moving range chart --- src/graphs/cfd/CFDRenderer.js | 4 +- src/graphs/control-chart/ControlRenderer.js | 9 ++++ src/graphs/moving-range/MovingRangeGraph.js | 5 ++- .../moving-range/MovingRangeRenderer.js | 19 +++++++-- src/graphs/scatterplot/ScatterplotGraph.js | 5 +-- src/graphs/scatterplot/ScatterplotRenderer.js | 25 ++++++----- src/index.js | 2 + .../ScatterplotGraphExpectedOutput.js | 41 +++++++++++++------ 8 files changed, 75 insertions(+), 35 deletions(-) diff --git a/src/graphs/cfd/CFDRenderer.js b/src/graphs/cfd/CFDRenderer.js index 27a7751..9e074fa 100644 --- a/src/graphs/cfd/CFDRenderer.js +++ b/src/graphs/cfd/CFDRenderer.js @@ -641,7 +641,7 @@ class CFDRenderer extends UIControlsRenderer { let { cycleTimeDateBefore, averageCycleTime, biggestCycleTime, currentStateCumulativeCount, cycleTimesByState } = this.computeCycleTimeAndLeadTimeMetrics(currentDataEntry, filteredData, currentDate, currentStateIndex); const averageLeadTime = leadTimeDateBefore - ? Math.floor(calculateDaysBetweenDates(leadTimeDateBefore, currentDate).roundedDays) + ? Math.floor(calculateDaysBetweenDates(leadTimeDateBefore, currentDate).exactTimeDiff) : null; const noOfItemsBefore = this.#getNoOfItems(currentDataEntry, this.states[this.states.indexOf('delivered')]); const noOfItemsAfter = this.#getNoOfItems(currentDataEntry, this.states[this.states.indexOf('analysis_active')]); @@ -682,7 +682,7 @@ class CFDRenderer extends UIControlsRenderer { let stateCumulativeCount = this.#getNoOfItems(currentDataEntry, this.states[i]); let cycleTimeDate = this.#computeCycleTimeDate(stateCumulativeCount, i, filteredData); cycleTimesByState[this.states[i + 1]] = cycleTimeDate - ? Math.floor(calculateDaysBetweenDates(cycleTimeDate, currentDate).roundedDays) + ? Math.floor(calculateDaysBetweenDates(cycleTimeDate, currentDate).exactTimeDiff) : null; if (cycleTimesByState[this.states[i + 1]] > biggestCycleTime) { biggestCycleTime = cycleTimesByState[this.states[i + 1]]; diff --git a/src/graphs/control-chart/ControlRenderer.js b/src/graphs/control-chart/ControlRenderer.js index c345eb5..b4d87a5 100644 --- a/src/graphs/control-chart/ControlRenderer.js +++ b/src/graphs/control-chart/ControlRenderer.js @@ -47,6 +47,15 @@ class ControlRenderer extends ScatterplotRenderer { this.y.domain([minY, maxY]); } + populateTooltip(event) { + this.tooltip + .style('pointer-events', 'auto') + .style('opacity', 0.9) + .append('p') + .style('text-decoration', 'underline') + .text(`${event.deliveredDate}`); + } + drawScatterplot(chartArea, data, x, y) { chartArea .selectAll(`.${this.dotClass}`) diff --git a/src/graphs/moving-range/MovingRangeGraph.js b/src/graphs/moving-range/MovingRangeGraph.js index 5b6ca19..9642ace 100644 --- a/src/graphs/moving-range/MovingRangeGraph.js +++ b/src/graphs/moving-range/MovingRangeGraph.js @@ -12,8 +12,9 @@ class MovingRangeGraph { this.dataSet.push({ fromDate: new Date(this.data[i - 1].deliveredDate), deliveredDate: new Date(this.data[i].deliveredTimestamp * 1000), - // leadTime: Math.floor(Math.abs(Number(this.data[i].exactLeadTime) - Number(this.data[i - 1].exactLeadTime))), - leadTime: Math.floor(Math.abs(Number(this.data[i].leadTime) - Number(this.data[i - 1].leadTime))), + leadTime: Math.abs(Number(this.data[i].leadTime) - Number(this.data[i - 1].leadTime)), + workItem1: this.data[i - 1].ticketId, + workItem2: this.data[i].ticketId, }); } } diff --git a/src/graphs/moving-range/MovingRangeRenderer.js b/src/graphs/moving-range/MovingRangeRenderer.js index c398256..fddd73d 100644 --- a/src/graphs/moving-range/MovingRangeRenderer.js +++ b/src/graphs/moving-range/MovingRangeRenderer.js @@ -5,9 +5,10 @@ class MovingRangeRenderer extends ScatterplotRenderer { color = '#0ea5e9'; timeScale = 'linear'; - constructor(data, avgMovingRange, chartName) { + constructor(data, avgMovingRange, workTicketsURL, chartName) { super(data); this.avgMovingRange = avgMovingRange; + this.workTicketsURL = workTicketsURL; this.chartName = chartName; this.chartType = 'MOVING_RANGE'; this.dotClass = 'moving-range-dot'; @@ -38,9 +39,19 @@ class MovingRangeRenderer extends ScatterplotRenderer { this.tooltip .style('pointer-events', 'auto') .style('opacity', 0.9) - .append('p') + .append('div') + .append('a') .style('text-decoration', 'underline') - .text(`${event.date}`); + .attr('href', `${this.workTicketsURL}/${event.workItem1}`) + .text(event.workItem1) + .attr('target', '_blank'); + this.tooltip + .append('div') + .append('a') + .style('text-decoration', 'underline') + .attr('href', `${this.workTicketsURL}/${event.workItem2}`) + .text(event.workItem1) + .attr('target', '_blank'); } drawScatterplot(chartArea, data, x, y) { @@ -55,7 +66,7 @@ class MovingRangeRenderer extends ScatterplotRenderer { .attr('cy', (d) => this.applyYScale(y, d.leadTime)) .style('cursor', 'pointer') .attr('fill', this.color) - .on('mouseover', (event, d) => this.handleMouseClickEvent(event, { date: d.deliveredDate })); + .on('click', (event, d) => this.handleMouseClickEvent(event, { ...d, date: d.deliveredDate })); // Define the line generator const line = d3 diff --git a/src/graphs/scatterplot/ScatterplotGraph.js b/src/graphs/scatterplot/ScatterplotGraph.js index fbd60d0..c34a2e3 100644 --- a/src/graphs/scatterplot/ScatterplotGraph.js +++ b/src/graphs/scatterplot/ScatterplotGraph.js @@ -65,19 +65,16 @@ class ScatterplotGraph { this.data.forEach((ticket) => { if (ticket.delivered) { const deliveredDate = new Date(ticket.delivered * 1000); - deliveredDate.setHours(0, 0, 0, 0); const scatterplotTicket = { deliveredDate: deliveredDate, deliveredTimestamp: ticket.delivered, leadTime: 0, - exactLeadTime: 0, ticketId: ticket.work_id, }; for (const state of this.states) { if (ticket[state]) { const diff = calculateDaysBetweenDates(ticket[state], ticket.delivered); - scatterplotTicket.leadTime = diff.roundedDays; - scatterplotTicket.exactLeadTime = diff.exactTimeDiff; + scatterplotTicket.leadTime = diff.exactTimeDiff; break; } } diff --git a/src/graphs/scatterplot/ScatterplotRenderer.js b/src/graphs/scatterplot/ScatterplotRenderer.js index a68af50..8f07e1b 100644 --- a/src/graphs/scatterplot/ScatterplotRenderer.js +++ b/src/graphs/scatterplot/ScatterplotRenderer.js @@ -250,7 +250,7 @@ class ScatterplotRenderer extends UIControlsRenderer { computeYScale() { // Start domain from a small positive value: 0.6 to avoid log(0) issues - const yDomain = [0.6, d3.max(this.data, (d) => d.leadTime)]; + const yDomain = [0.5, d3.max(this.data, (d) => d.leadTime)]; if (this.timeScale === 'logarithmic') { this.y = d3 @@ -265,9 +265,9 @@ class ScatterplotRenderer extends UIControlsRenderer { } applyYScale(yScale, value) { - if (this.timeScale === 'logarithmic' && value <= 0) { + if (this.timeScale === 'logarithmic' && value <= 0.5) { // Handle zero or negative values explicitly - return yScale(0.6); + return yScale(0.5); } else { return yScale(value); } @@ -302,7 +302,7 @@ class ScatterplotRenderer extends UIControlsRenderer { .append('rect') .attr('class', 'axis-background') .attr('x', 0) - .attr('y', 0) + .attr('y', 4) .attr('width', this.width) .attr('height', axisHeight) .attr('fill', 'gray') @@ -312,7 +312,11 @@ class ScatterplotRenderer extends UIControlsRenderer { const xAxisGroup = g.attr('class', 'x-axis-group').attr('transform', `translate(0, ${height})`); xAxisGroup.call(axis); xAxisGroup.selectAll('.tick line').attr('stroke', 'black').attr('opacity', 0.3).attr('y1', 15).attr('y2', -this.height); - xAxisGroup.selectAll('.tick text').attr('fill', 'black').attr('y', axisHeight).attr('dy', '10px'); + xAxisGroup + .selectAll('.tick text') + .attr('fill', 'black') + .attr('y', axisHeight + 6) + .attr('dy', '10px'); xAxisGroup.select('.domain').remove(); grayBand.on('mouseover', function () { d3.select(this) @@ -343,10 +347,10 @@ class ScatterplotRenderer extends UIControlsRenderer { gy.call(yAxis).selectAll('.tick line').attr('opacity', 0.1); if (this.timeScale === 'logarithmic') { - // Manually add tick for 0.6 value which is rendered as value 0 + // Manually add tick for 0.5 value which is rendered as value 0 gy.append('g') .attr('class', 'tick') - .attr('transform', `translate(0, ${y(0.6)})`) // Position tick line at y(0.6) + .attr('transform', `translate(0, ${y(0.5)})`) // Position tick line at y(0.5) .append('line') .attr('x2', this.width) .attr('stroke', 'black') @@ -355,7 +359,7 @@ class ScatterplotRenderer extends UIControlsRenderer { // Manually add text label for 0.6 value is rendered as value 0 gy.append('g') .attr('class', 'tick') - .attr('transform', `translate(0, ${y(0.6)})`) // Position text at y(0.6) + .attr('transform', `translate(0, ${y(0.5)})`) // Position text at y(0.5) .append('text') .attr('x', -4) .attr('dy', '.32em') @@ -515,8 +519,7 @@ class ScatterplotRenderer extends UIControlsRenderer { */ handleMouseClickEvent(event, d) { let data = { - date: d.date, - ticketId: d.ticketId, + ...d, tooltipLeft: event.pageX, tooltipTop: event.pageY, }; @@ -531,8 +534,8 @@ class ScatterplotRenderer extends UIControlsRenderer { observationBody: observation?.body, observationId: observation?.id, }; - this.eventBus?.emitEvents(`${this.chartName}-click`, data); } + this.eventBus?.emitEvents(`${this.chartName}-click`, data); this.showTooltip(data); } diff --git a/src/index.js b/src/index.js index 806ba55..973cf84 100644 --- a/src/index.js +++ b/src/index.js @@ -8,6 +8,7 @@ import MovingRangeRenderer from './graphs/moving-range/MovingRangeRenderer.js'; import ControlRenderer from './graphs/control-chart/ControlRenderer.js'; import HistogramRenderer from './graphs/histogram/HistogramRenderer.js'; import { eventBus } from './utils/EventBus.js'; +import { processServiceData } from './data-processor.js'; import ObservationLoggingService from './graphs/ObservationLoggingService.js'; export { @@ -22,4 +23,5 @@ export { HistogramRenderer, ObservationLoggingService, eventBus, + processServiceData, }; diff --git a/test/testData/ScatterplotGraphExpectedOutput.js b/test/testData/ScatterplotGraphExpectedOutput.js index 25dd035..a4e64db 100644 --- a/test/testData/ScatterplotGraphExpectedOutput.js +++ b/test/testData/ScatterplotGraphExpectedOutput.js @@ -1,25 +1,42 @@ const scatterplotGraphOutput = [ { - "deliveredDate": "2023-03-20T22:00:00.000Z", - "leadTime": 42, - "ticketId": "T-91730136" + deliveredDate: "2023-03-21T15:09:35.000Z", + deliveredTimestamp: '1679411375', + leadTime: 42.04, + ticketId: 'T-91730136' }, { - "deliveredDate": "2023-04-03T21:00:00.000Z", - "leadTime": 4, - "ticketId": "T-91730806" + deliveredDate: "2023-04-04T15:04:38.000Z", + deliveredTimestamp: '1680620678', + leadTime: 0.14, + ticketId: 'T-91730847' }, { - "deliveredDate": "2023-04-04T21:00:00.000Z", - "leadTime": 1, - "ticketId": "T-91730832" + deliveredDate: "2023-04-04T15:26:07.000Z", + deliveredTimestamp: '1680621967', + leadTime: 4.61, + ticketId: 'T-91730806' }, { - "deliveredDate": "2023-04-13T21:00:00.000Z", - "leadTime": 7, - "ticketId": "T-91720964" + deliveredDate: "2023-04-05T13:12:22.000Z", + deliveredTimestamp: '1680700342', + leadTime: 2, + ticketId: 'T-91730832' + }, + { + deliveredDate: "2023-04-06T11:26:07.000Z", + deliveredTimestamp: '1680780367', + leadTime: 0.82, + ticketId: 'T-91730873' + }, + { + deliveredDate: "2023-04-14T07:07:04.000Z", + deliveredTimestamp: '1681456024', + leadTime: 7.54, + ticketId: 'T-91720964' } ] + export default scatterplotGraphOutput;