Skip to content

Commit

Permalink
[TRON-18102] Enhance Observation Logging with Dynamic State Capture a…
Browse files Browse the repository at this point in the history
…nd 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
  • Loading branch information
ClaudiaGivan committed Dec 15, 2023
1 parent c6274f6 commit 25c4d04
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 65 deletions.
14 changes: 7 additions & 7 deletions examples/example.html
Original file line number Diff line number Diff line change
Expand Up @@ -82,14 +82,14 @@ <h2 class="text-center text-xl text-white">Observation</h2>
<div class="grid grid-cols-2 gap-4">
<!-- Avg. cycle time -->
<div>
<label class="block text-black font-medium" for="average-cycle-time-input">Avg. cycle time</label>
<input
class="shadow appearance-none bg-gray-300 px-4 w-11/12 cursor-not-allowed border py-1 rounded text-blue-500 leading-tight focus:outline-none focus:shadow-outline"
id="average-cycle-time-input"
type="text"
readonly />
<label class="block text-black font-medium" for="cycle-times-select">Avg. cycle time-state</label>
<select
class="block appearance-none shadow bg-gray-300 px-4 w-11/12 cursor-pointer border py-1 rounded text-blue-500 leading-tight focus:outline-none focus:shadow-outline"
id="cycle-times-select">
</select>
</div>


<!-- Avg. lead time -->
<div>
<label class="block text-black font-medium" for="average-lead-time-input">Avg. lead time</label>
Expand Down Expand Up @@ -148,7 +148,7 @@ <h2 class="text-center text-xl text-white">Observation</h2>
</div>
</div>
<div class="h-full ml-8 -z-10" id="page-content">
<div class="mx-auto mt-6 w-10/12">
<div class="mx-auto mt-12 w-10/12">
<h1 class="my-2 text-center font-bold">CFD</h1>
<div id="cfd-area-div"></div>
<div id="cfd-brush-div"></div>
Expand Down
24 changes: 21 additions & 3 deletions examples/sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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));
Expand Down Expand Up @@ -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" : "-";
Expand Down
126 changes: 71 additions & 55 deletions src/graphs/cfd/CFDRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
}

/**
Expand All @@ -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
Expand All @@ -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');
Expand Down Expand Up @@ -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')]);

Expand All @@ -610,43 +635,34 @@ class CFDRenderer extends UIControlsRenderer {

return {
currentState: this.states[currentStateIndex],
cycleTimeDateBefore: formatDateToLocalString(cycleTimeDateBefore),
leadTimeDateBefore: formatDateToLocalString(leadTimeDateBefore),
wip,
averageCycleTime,
cycleTimesByState,
biggestCycleTime,
averageLeadTime,
throughput,
};
}
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) {

Check failure on line 648 in src/graphs/cfd/CFDRenderer.js

View workflow job for this annotation

GitHub Actions / build

'currentDate' is defined but never used. Allowed unused args must match /^_/u

Check failure on line 648 in src/graphs/cfd/CFDRenderer.js

View workflow job for this annotation

GitHub Actions / build

'currentDate' is defined but never used. Allowed unused args must match /^_/u
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) {

Check failure on line 657 in src/graphs/cfd/CFDRenderer.js

View workflow job for this annotation

GitHub Actions / build

'currentDate' is defined but never used. Allowed unused args must match /^_/u

Check failure on line 657 in src/graphs/cfd/CFDRenderer.js

View workflow job for this annotation

GitHub Actions / build

'currentDate' is defined but never used. Allowed unused args must match /^_/u
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;
}

/**
Expand Down Expand Up @@ -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;
}
}
Expand Down

0 comments on commit 25c4d04

Please sign in to comment.