Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev #70

Merged
merged 7 commits into from
Aug 29, 2024
Merged

Dev #70

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions dashboard-react/app/api/data/route.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ const getIntersectingTrialIDs = (...sets) => {
};

const prepareData = async (searchParams) => {
console.log("searchParams", searchParams);
// console.log("searchParams", searchParams);
// Display params
const aggCol = searchParams.get("aggcol") || "Material Class I";
const displayCol = searchParams.get("displaycol") || "% Residuals (Mass)";
Expand Down Expand Up @@ -175,6 +175,18 @@ const prepareData = async (searchParams) => {
// filter data based on selected filters
filteredData = filterData(filteredData, "Test Method", [testMethod]);
filteredData = filterData(filteredData, "Technology", technologies);
// Return empty object to preserve privacy if not enough trials (Except for Bulk Dose)
const technologyTrialIDs = new Set(filteredData.map((d) => d["Trial ID"]));
// console.log("technologyTrialIDs", technologyTrialIDs);
// console.log(technologies);
const trialThreshold = testMethod === "Bulk Dose" ? 1 : 3;
if (technologyTrialIDs.size < trialThreshold && testMethod !== "Bulk Dose") {
return {
message:
"There are not enough trials for the selected technology. Please select more options.",
};
}

filteredData = filterData(filteredData, "Material Class II", materials);
filteredData = filterData(
filteredData,
Expand Down Expand Up @@ -242,8 +254,8 @@ const prepareData = async (searchParams) => {

console.log("filteredData.length", filteredData.length);

// Not enough data - return empty object (ignore for bulk dose since methodology is different)
const dataThreshold = testMethod === "Bulk Dose" ? 1 : 5;
// Not enough data - return empty object
const dataThreshold = 1;
if (filteredData.length < dataThreshold) {
return {
message:
Expand Down Expand Up @@ -283,7 +295,7 @@ const prepareData = async (searchParams) => {
);
});

console.log("sortedGrouped", sortedGrouped);
// console.log("sortedGrouped", sortedGrouped);

return {
data: sortedGrouped,
Expand Down
17 changes: 15 additions & 2 deletions dashboard-react/components/CheckboxMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export default function CheckboxMenu({
filterKey,
title,
infoText,
showInfoIcon = false,
}) {
const snap = useSnapshot(state);

Expand Down Expand Up @@ -97,15 +98,27 @@ export default function CheckboxMenu({
<div className="h-6 shadow-bottom"></div>
)}
<div
className="overflow-auto flex-grow px-3 h-[150px]"
className="overflow-auto flex-grow px-0 h-[150px]"
ref={scrollRef}
onScroll={checkScrollPosition}
>
<ul className="menu dropdown-content rounded-box z-[1] mx-auto">
{options?.map((option) => (
<li key={option}>
<label className="label cursor-pointer">
<span className="label-text text-[.8rem] px-0">{option}</span>
<span className="label-text text-[.85rem] px-0">
{option}
</span>
{showInfoIcon && (
<div
className="tooltip tooltip-primary tooltip-bottom ml-2 inline-flex"
data-tip={infoText}
>
<span className="cursor-pointer text-primary">
<InformationCircleIcon className="h-5 w-5 text-primary" />
</span>
</div>
)}
<input
type="checkbox"
id={`option-${option}`}
Expand Down
1 change: 1 addition & 0 deletions dashboard-react/components/DashboardFilterControls.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export default function DashboardFilterControls() {
filterKey="selectedTechnologies"
title="Technology"
infoText="Select one or more options to filter the data by the primary technology used by the facility conducting the field test."
showInfoIcon={false}
/>
<CheckboxMenu
options={Object.keys(temperatureFilterDict)}
Expand Down
136 changes: 91 additions & 45 deletions dashboard-react/components/OperatingConditionsDashboard.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";
import React, { useState, useEffect } from "react";
import Plot from "react-plotly.js";
import { csv } from "d3-fetch";
import { useEffect, useState } from "react";
import Plot from "react-plotly.js";

export default function OperatingConditionsDashboard({
maxDays = 45,
Expand All @@ -11,40 +11,69 @@ export default function OperatingConditionsDashboard({
const [dataLoaded, setDataLoaded] = useState(false);
const [plotData, setPlotData] = useState([]);
const [errorMessage, setErrorMessage] = useState("");
const [selectedMetric, setSelectedMetric] = useState("Temperature");

const metrics = ["Temperature", "% Moisture", "O2 in Field"];

useEffect(() => {
csv("/data/temperature_data.csv")
csv("/data/operating_conditions.csv")
.then((data) => {
const formattedData = [];
const days = data.map((d) => d["Day #"]);

const selectedColumn =
selectedMetric === "Temperature"
? "Temperature"
: selectedMetric === "% Moisture"
? "Moisture"
: selectedMetric === "O2 in Field"
? "Oxygen"
: null;

const filteredData = data.filter(
(d) => d["Operating Condition"] === selectedColumn
);
let timeSteps = filteredData.map((d) => d["Time Step"]);

if (selectedMetric !== "Temperature") {
timeSteps = timeSteps.map((d) => d * 7); // Convert weeks to days
}

const nonTrialColumns = [
"Time Step",
"Operating Condition",
"Time Unit",
];

const trialCount = {}; // Reset trial count each time data is processed

Object.keys(data[0]).forEach((column) => {
if (column !== "Day #") {
let yData = data.map((d) => parseFloat(d[column]) || null);
yData = interpolateData(yData); // Perform interpolation
yData = movingAverage(yData, windowSize); // Smooth using moving average

const trialName = mapTrialName(column, trialCount); // Pass trialCount to mapTrialName
if (!nonTrialColumns.includes(column)) {
let yData = filteredData.map((d) => parseFloat(d[column]) || null);
yData = interpolateData(yData);
if (selectedMetric !== "Temperature") {
windowSize = 3; // Reduce window size for non-temperature metrics
}
yData = movingAverage(yData, windowSize);
const trialName = mapTrialName(column, trialCount);

formattedData.push({
x: days,
x: timeSteps,
y: yData,
mode: "lines",
name: trialName, // Use the mapped trial name
name: trialName,
});
}
});

formattedData.sort((a, b) => a.name.localeCompare(b.name));
setPlotData(formattedData);
setDataLoaded(true);
})
.catch((error) => {
console.error("Error loading CSV data:", error);
setErrorMessage("Failed to load data.");
});
}, [windowSize]);
}, [windowSize, selectedMetric]);

const mapTrialName = (trialName, trialCount) => {
const mappings = {
Expand Down Expand Up @@ -119,9 +148,9 @@ export default function OperatingConditionsDashboard({
});
}

const yAxisTitle = "Temperature";
const yAxisTitle = `${selectedMetric}`;

const title = "Temperature Over Time";
const title = `${selectedMetric} Over Time`;

const yMax =
plotData.length > 0
Expand All @@ -137,38 +166,55 @@ export default function OperatingConditionsDashboard({
<p>{errorMessage}</p>
</div>
) : (
<Plot
data={plotData}
layout={{
width: 1280,
height: 600,
title: {
text: `<b>${title}</b>`,
x: 0.5,
xanchor: "center",
yanchor: "top",
},
showlegend: true,
yaxis: {
<>
<div className="flex justify-center my-4">
<select
className="select select-bordered"
value={selectedMetric}
onChange={(e) => setSelectedMetric(e.target.value)}
>
{metrics.map((metric) => (
<option key={metric} value={metric}>
{metric}
</option>
))}
</select>
</div>
<Plot
data={plotData}
layout={{
width: 1280,
height: 600,
title: {
text: `<b>${yAxisTitle}</b>`,
text: `<b>${title}</b>`,
x: 0.5,
xanchor: "center",
yanchor: "top",
},
showlegend: true,
yaxis: {
title: {
text: `<b>${yAxisTitle}</b>`,
},
range: [0, yMax],
},
xaxis: {
title: {
text: "<b>Days</b>",
},
tickangle: xTickAngle,
ticklen: 10,
automargin: true,
range: [0, maxDays], // Cap x-axis at maxDays
showline: true,
},
range: [0, yMax],
linewidth: 2, // Set y-axis line thickness
},
xaxis: {
tickangle: xTickAngle,
ticklen: 10,
automargin: true,
range: [0, maxDays], // Cap x-axis at maxDays
linewidth: 2, // Set x-axis line thickness
},
hovermode: "x",
}}
config={{
displayModeBar: false,
}}
/>
hovermode: "x",
}}
config={{
displayModeBar: false,
}}
/>
</>
)}
</>
);
Expand Down
Loading
Loading