From 25c4d04febb256ad328671c285826017c32f8be8 Mon Sep 17 00:00:00 2001 From: ClaudiaGivan Date: Thu, 14 Dec 2023 17:09:07 +0200 Subject: [PATCH] [TRON-18102] Enhance Observation Logging with Dynamic State Capture and Cycle Time Selection - capture the cycle times for all the states - add a select menu for the cycle times by states inside the form instead the input --- examples/example.html | 14 ++-- examples/sidebars.js | 24 ++++++- src/graphs/cfd/CFDRenderer.js | 126 +++++++++++++++++++--------------- 3 files changed, 99 insertions(+), 65 deletions(-) diff --git a/examples/example.html b/examples/example.html index cd12d0f..cc518c0 100644 --- a/examples/example.html +++ b/examples/example.html @@ -82,14 +82,14 @@

Observation

- - + +
+
@@ -148,7 +148,7 @@

Observation

-
+

CFD

diff --git a/examples/sidebars.js b/examples/sidebars.js index 174a00a..8b78aaf 100644 --- a/examples/sidebars.js +++ b/examples/sidebars.js @@ -22,12 +22,13 @@ const submitButton = document.getElementById("form-button"); const scatterplotDiv = document.getElementById("scatterplot-form-fields-div"); const cfdDiv = document.getElementById("cfd-form-fields-div"); -const avgCycleTimeInput = document.getElementById("average-cycle-time-input"); +const cycleTimesByStateSelect = document.getElementById("cycle-times-select"); const avgLeadTimeInput = document.getElementById("average-lead-time-input"); const leadTimeInput = document.getElementById("lead-time-input"); const throughputInput = document.getElementById("throughput-input"); const wipInput = document.getElementById("wip-input"); export const warningField = document.getElementById("warning-p"); +let cycleTimesByState = {} let isLeftSidebarOpen = false; @@ -45,7 +46,8 @@ submitButton?.addEventListener("click", async () => { body: observationTextarea.value, }; if (observation.chart_type === "CFD") { - avgCycleTimeInput.value !== "-" && (observation.avg_cycle_time = parseInt(avgCycleTimeInput.value.split(" ")[0], 10)); + observation.avg_cycle_time = parseInt(cycleTimesByStateSelect.value.split(" ")[0], 10); + observation.cycleTimesByState = cycleTimesByState avgLeadTimeInput.value !== "-" && (observation.avg_lead_time = parseInt(avgLeadTimeInput.value.split(" ")[0], 10)); throughputInput.value !== "-" && (observation.throughput = parseFloat(throughputInput.value.split(" ")[0])); wipInput.value !== "-" && (observation.wip = parseInt(wipInput.value.split(" ")[0], 10)); @@ -132,11 +134,27 @@ function clearObservationForm() { } export function initializeForm(data) { + clearObservationForm(); if (data.chartType === "CFD") { cfdDiv.classList.remove("hidden"); scatterplotDiv.classList.add("hidden"); - avgCycleTimeInput.value = data.metrics.averageCycleTime ? data.metrics.averageCycleTime + " days" : "-"; + let selectedState; + cycleTimesByState = data.metrics.cycleTimesByState + cycleTimesByStateSelect.innerHTML='' + for (const state in data.metrics.cycleTimesByState) { + const option = document.createElement('option'); + option.textContent = `${data.metrics.cycleTimesByState[state]} days - ${state}`; + option.value = state; + cycleTimesByStateSelect.appendChild(option); + if(data.metrics.cycleTimesByState[state] === data.metrics.biggestCycleTime){ + option.selected = true + selectedState = state + } + } + cycleTimesByStateSelect.addEventListener('change', (event) => { + cycleTimesByStateSelect.value = selectedState; + }); avgLeadTimeInput.value = data.metrics.averageLeadTime ? data.metrics.averageLeadTime + " days" : "-"; throughputInput.value = data.metrics.throughput ? data.metrics.throughput + " items" : "-"; wipInput.value = data.metrics.wip ? data.metrics.wip + " items" : "-"; diff --git a/src/graphs/cfd/CFDRenderer.js b/src/graphs/cfd/CFDRenderer.js index 396206a..d801b48 100644 --- a/src/graphs/cfd/CFDRenderer.js +++ b/src/graphs/cfd/CFDRenderer.js @@ -438,7 +438,7 @@ class CFDRenderer extends UIControlsRenderer { */ #positionTooltip(left, top, width) { this.tooltip?.transition().duration(100).style('opacity', 0.9).style('pointer-events', 'auto'); - this.tooltip?.style('left', left - width + 'px').style('top', top + 50 + 'px'); + this.tooltip?.style('left', left - width + 'px').style('top', top + 30 + 'px'); } /** @@ -448,20 +448,19 @@ class CFDRenderer extends UIControlsRenderer { */ #populateTooltip(event) { this.tooltip?.append('p').text(formatDateToLocalString(event.date)).attr('class', 'text-center'); - const gridContainer = this.tooltip?.append('div').attr('class', 'grid grid-cols-2 gap-2'); - if (event.metrics.averageCycleTime > 0) { - gridContainer - .append('span') - .text('Cycle time:') - .attr('class', 'pr-1') - .style('text-align', 'start') - .style('color', this.#cycleTimeColor); + const gridContainer = this.tooltip?.append('div').attr('class', 'grid grid-cols-2'); + if (event.metrics.throughput > 0) { + gridContainer.append('span').text('Throughput:').attr('class', 'pr-1').style('text-align', 'start'); + gridContainer.append('span').text(`${event.metrics.throughput} items`).attr('class', 'pl-1').style('text-align', 'start'); + } + if (event.metrics.wip > 0) { + gridContainer.append('span').text('WIP:').attr('class', 'pr-1').style('text-align', 'start').style('color', this.#wipColor); gridContainer .append('span') - .text(`${event.metrics.averageCycleTime} days`) + .text(`${event.metrics.wip} items`) .attr('class', 'pl-1') .style('text-align', 'start') - .style('color', this.#cycleTimeColor); + .style('color', this.#wipColor); } if (event.metrics.averageLeadTime > 0) { gridContainer @@ -477,18 +476,33 @@ class CFDRenderer extends UIControlsRenderer { .style('text-align', 'start') .style('color', this.#leadTimeColor); } - if (event.metrics.wip > 0) { - gridContainer.append('span').text('WIP:').attr('class', 'pr-1').style('text-align', 'start').style('color', this.#wipColor); + if (event.metrics.averageCycleTime > 0) { gridContainer .append('span') - .text(`${event.metrics.wip} items`) + .text('Cycle time:') + .attr('class', 'pr-1') + .style('text-align', 'start') + .style('color', this.#cycleTimeColor); + gridContainer + .append('span') + .text(`${event.metrics.averageCycleTime} days`) .attr('class', 'pl-1') .style('text-align', 'start') - .style('color', this.#wipColor); + .style('color', this.#cycleTimeColor); } - if (event.metrics.throughput > 0) { - gridContainer.append('span').text('Throughput:').attr('class', 'pr-1').style('text-align', 'start'); - gridContainer.append('span').text(`${event.metrics.throughput} items`).attr('class', 'pl-1').style('text-align', 'start'); + if (event.metrics.currentState) { + gridContainer + .append('span') + .text('State:') + .attr('class', 'pr-1') + .style('text-align', 'start') + .style('color', this.#statesColors(event.metrics.currentState)); + gridContainer + .append('span') + .text(`${event.metrics.currentState}`) + .attr('class', 'pl-1') + .style('text-align', 'start') + .style('color', this.#statesColors(event.metrics.currentState)); } if (event.observationBody) { gridContainer.append('span').text('Observation:').attr('class', 'pr-1').style('text-align', 'start'); @@ -576,22 +590,33 @@ class CFDRenderer extends UIControlsRenderer { */ #computeMetrics(currentDate, currentCumulativeCount, excludeCycleTime = false) { currentDate = new Date(currentDate); + currentDate.setHours(0, 0, 0, 0); const currentDataEntry = this.data.find((d) => areDatesEqual(new Date(d.date), currentDate)); if (currentDataEntry) { + const filteredData = this.data.filter((d) => d.date <= currentDate).reverse(); const currentStateIndex = this.#getCurrentStateIndex(currentCumulativeCount, currentDataEntry); - const currentStateCumulativeCount = - currentStateIndex >= 0 ? this.#getNoOfItems(currentDataEntry, this.states[currentStateIndex]) : -1; const currentDeliveredItems = currentDataEntry.delivered; - const { cycleTimeDateBefore, leadTimeDateBefore } = this.#computeCycleAndLeadTimeDates( - currentDate, - currentStateCumulativeCount, - currentDeliveredItems, - currentStateIndex - ); - - let averageCycleTime = cycleTimeDateBefore ? Math.floor(calculateDaysBetweenDates(cycleTimeDateBefore, currentDate)) : null; + const leadTimeDateBefore = this.#computeLeadTimeDate(currentDate, currentDeliveredItems, filteredData); + let cycleTimeDateBefore = null; + let averageCycleTime = null; + let biggestCycleTime = 0; + let currentStateCumulativeCount = null; + let cycleTimesByState = {}; + cycleTimesByState[this.states[0]] = 0; + for (let i = 0; i < this.states.length - 1; i++) { + let stateCumulativeCount = this.#getNoOfItems(currentDataEntry, this.states[i]); + let cycleTimeDate = this.#computeCycleTimeDate(currentDate, stateCumulativeCount, i, filteredData); + cycleTimesByState[this.states[i + 1]] = cycleTimeDate ? Math.floor(calculateDaysBetweenDates(cycleTimeDate, currentDate)) : null; + if (cycleTimesByState[this.states[i + 1]] > biggestCycleTime) { + biggestCycleTime = cycleTimesByState[this.states[i + 1]]; + } + if (currentStateIndex > 0 && i + 1 === currentStateIndex) { + cycleTimeDateBefore = cycleTimeDate; + averageCycleTime = cycleTimesByState[this.states[i + 1]]; + currentStateCumulativeCount = stateCumulativeCount; + } + } const averageLeadTime = leadTimeDateBefore ? Math.floor(calculateDaysBetweenDates(leadTimeDateBefore, currentDate)) : null; - const noOfItemsBefore = this.#getNoOfItems(currentDataEntry, this.states[this.states.indexOf('delivered')]); const noOfItemsAfter = this.#getNoOfItems(currentDataEntry, this.states[this.states.indexOf('analysis_active')]); @@ -610,10 +635,9 @@ class CFDRenderer extends UIControlsRenderer { return { currentState: this.states[currentStateIndex], - cycleTimeDateBefore: formatDateToLocalString(cycleTimeDateBefore), - leadTimeDateBefore: formatDateToLocalString(leadTimeDateBefore), wip, - averageCycleTime, + cycleTimesByState, + biggestCycleTime, averageLeadTime, throughput, }; @@ -621,32 +645,24 @@ class CFDRenderer extends UIControlsRenderer { return {}; } - /** - * Computes the dates for cycle time and lead time based on the current state of the data. - * @param {Date} currentDate - The current date for which the computation is made. - * @param {number} currentStateCumulativeCount - The cumulative count of items in the current state. - * @param {number} currentDeliveredItems - The count of delivered items up to the current date. - * @param {number} currentStateIndex - The index of the current state in the states array. - * @returns {{cycleTimeDateBefore: Date | null, leadTimeDateBefore: Date | null}} - * An object containing the computed cycle time date and lead time date prior to the current date. - */ - - #computeCycleAndLeadTimeDates(currentDate, currentStateCumulativeCount, currentDeliveredItems, currentStateIndex) { - let cycleTimeDateBefore = null; - let leadTimeDateBefore = null; - for (const entry of this.data) { + #computeLeadTimeDate(currentDate, currentDeliveredItems, filteredData) { + for (const entry of filteredData) { const entryDate = new Date(entry.date); - if (entryDate >= currentDate) continue; + const leadTimeCumulativeCount = this.#getNoOfItems(entry, this.states[this.states.length - 1]); + if (leadTimeCumulativeCount <= currentDeliveredItems) return entryDate; + } + return null; + } - if (currentStateCumulativeCount >= 0) { - const cycleTimeCumulativeCount = this.#getNoOfItems(entry, this.states[currentStateIndex + 1]); - if (cycleTimeCumulativeCount <= currentStateCumulativeCount) cycleTimeDateBefore = entryDate; + #computeCycleTimeDate(currentDate, currentStateCumulativeCount, currentStateIndex, filteredData) { + for (const entry of filteredData) { + const entryDate = new Date(entry.date); + const cycleTimeCumulativeCount = this.#getNoOfItems(entry, this.states[currentStateIndex + 1]); + if (cycleTimeCumulativeCount <= currentStateCumulativeCount) { + return entryDate; } - - const leadTimeCumulativeCount = this.#getNoOfItems(entry, this.states[this.states.length - 1]); - if (leadTimeCumulativeCount <= currentDeliveredItems) leadTimeDateBefore = entryDate; } - return { cycleTimeDateBefore, leadTimeDateBefore }; + return null; } /** @@ -781,7 +797,7 @@ class CFDRenderer extends UIControlsRenderer { let cumulativeCount = 0; for (let stateIndex = 0; stateIndex < this.states.length; stateIndex++) { cumulativeCount += currentDataEntry[this.states[stateIndex]]; - if (currentCumulativeCount <= cumulativeCount) { + if (currentCumulativeCount < cumulativeCount) { return stateIndex; } }