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 #71

Merged
merged 10 commits into from
Sep 4, 2024
50 changes: 15 additions & 35 deletions dashboard-react/components/CheckboxMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,22 +39,6 @@ export default function CheckboxMenu({
const allSelected = selectedOptions.length === options.length;
const noneSelected = selectedOptions.length === 0;

const [canScrollUp, setCanScrollUp] = useState(false);
const [canScrollDown, setCanScrollDown] = useState(false);
const scrollRef = useRef(null);

const checkScrollPosition = () => {
const element = scrollRef.current;
setCanScrollUp(element.scrollTop > 0);
setCanScrollDown(
element.scrollTop < element.scrollHeight - element.clientHeight
);
};

useEffect(() => {
checkScrollPosition(); // Initial check
}, [options]); // Re-check when options change

return (
<div className="my-4 border border-gray-300 rounded-md shadow-sm">
<div className="flex justify-center mx-auto">
Expand Down Expand Up @@ -89,19 +73,15 @@ export default function CheckboxMenu({
</button>
</div>
<div className="my-1 border-t border-gray-300"></div>
<div>
{canScrollUp ? (
<div className="scroll-arrow up text-center text-primary shadow-bottom">
</div>
) : (
<div className="h-6 shadow-bottom"></div>
)}
<div className="relative">
<div
className="overflow-auto flex-grow px-0 h-[150px]"
ref={scrollRef}
onScroll={checkScrollPosition}
>
className="absolute top-0 left-0 right-0 h-6 pointer-events-none z-10"
style={{
background:
"linear-gradient(to bottom, rgba(255, 255, 255, 1), rgba(255, 255, 255, 0))",
}}
></div>{" "}
<div className="overflow-auto flex-grow px-0 h-[150px]">
<ul className="menu dropdown-content rounded-box z-[1] mx-auto">
{options?.map((option) => (
<li key={option}>
Expand Down Expand Up @@ -132,13 +112,13 @@ export default function CheckboxMenu({
))}
</ul>
</div>
{canScrollDown ? (
<div className="scroll-arrow down text-center h-6 text-primary shadow-top">
</div>
) : (
<div className="h-6 shadow-top"></div>
)}
<div
className="absolute bottom-0 left-0 right-0 h-6 pointer-events-none z-10"
style={{
background:
"linear-gradient(to bottom, rgba(255, 255, 255, 0), rgba(255, 255, 255, 1))",
}}
></div>{" "}
</div>
</div>
);
Expand Down
3 changes: 3 additions & 0 deletions dashboard-react/components/Dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,9 @@ export default function Dashboard() {
tickangle: xTickAngle,
ticklen: 10,
automargin: true,
tickfont: {
weight: "bold",
},
},
hovermode: "x",
}}
Expand Down
4 changes: 2 additions & 2 deletions dashboard-react/components/DashboardFilterControls.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,14 @@ export default function DashboardFilterControls() {
options={Object.keys(temperatureFilterDict)}
selectedOptions={snap.filters.selectedTemperatureLevels}
filterKey="selectedTemperatureLevels"
title="Average Temperature"
title="Mean Temperature"
infoText="Select one or more options to filter the data by the average temperature range during the field test."
/>
<CheckboxMenu
options={Object.keys(moistureFilterDict)}
selectedOptions={snap.filters.selectedMoistureLevels}
filterKey="selectedMoistureLevels"
title="% Moisture"
title="Mean % Moisture"
infoText="Select one or more options to filter the data by the average % moisture."
/>
<CheckboxMenu
Expand Down
117 changes: 91 additions & 26 deletions dashboard-react/components/OperatingConditionsDashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,17 @@ export default function OperatingConditionsDashboard({
const [plotData, setPlotData] = useState([]);
const [errorMessage, setErrorMessage] = useState("");
const [selectedMetric, setSelectedMetric] = useState("Temperature");
const [ignoreMaxDays, setIgnoreMaxDays] = useState(false);
const [applyMovingAverage, setApplyMovingAverage] = useState(true);

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

const [effectiveMaxDays, setEffectiveMaxDays] = useState(maxDays);

useEffect(() => {
csv("/data/operating_conditions.csv")
.then((data) => {
const formattedData = [];

const selectedColumn =
selectedMetric === "Temperature"
? "Temperature"
Expand All @@ -29,21 +32,41 @@ export default function OperatingConditionsDashboard({
? "Oxygen"
: null;

const filteredData = data.filter(
let 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",
];

filteredData = filteredData.filter((row) => {
// Check if all trial columns are empty (null, undefined, or empty string)
return Object.keys(row).some(
(col) =>
!nonTrialColumns.includes(col) &&
row[col] !== null &&
row[col] !== undefined &&
row[col] !== ""
);
});

console.log("Filtered Data:", filteredData);

let timeSteps = filteredData.map((d) => d["Time Step"]);
if (selectedMetric !== "Temperature") {
timeSteps = timeSteps.map((d) => d * 7); // Convert weeks to days
}

const maxDaysFromData = Math.max(...timeSteps);
const calculatedEffectiveMaxDays = ignoreMaxDays
? maxDaysFromData + 5
: Math.min(maxDays, maxDaysFromData);

setEffectiveMaxDays(calculatedEffectiveMaxDays); // Update state

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

Object.keys(data[0]).forEach((column) => {
Expand All @@ -53,18 +76,34 @@ export default function OperatingConditionsDashboard({
if (selectedMetric !== "Temperature") {
windowSize = 3; // Reduce window size for non-temperature metrics
}
yData = movingAverage(yData, windowSize);
if (applyMovingAverage) {
yData = movingAverage(yData, windowSize);
}
const trialName = mapTrialName(column, trialCount);

formattedData.push({
x: timeSteps,
y: yData,
mode: "lines",
mode: "lines+markers",
name: trialName,
});
}
});

if (selectedMetric === "Temperature") {
formattedData.push({
x: [0, 45],
y: [131, 131],
mode: "lines",
name: "PFRP",
line: {
dash: "dot",
color: "red",
width: 2,
},
});
}

formattedData.sort((a, b) => a.name.localeCompare(b.name));
setPlotData(formattedData);
setDataLoaded(true);
Expand All @@ -73,7 +112,7 @@ export default function OperatingConditionsDashboard({
console.error("Error loading CSV data:", error);
setErrorMessage("Failed to load data.");
});
}, [windowSize, selectedMetric]);
}, [windowSize, selectedMetric, ignoreMaxDays, applyMovingAverage]);

const mapTrialName = (trialName, trialCount) => {
const mappings = {
Expand Down Expand Up @@ -154,8 +193,8 @@ export default function OperatingConditionsDashboard({

const yMax =
plotData.length > 0
? Math.max(...plotData.flatMap((d) => d.y.map((y) => y + 0.05)), 1.05)
: 1.05;
? Math.max(...plotData.flatMap((d) => d.y.map((y) => y + 0.05)))
: null;

const xTickAngle = plotData.length > 6 ? 90 : 0;

Expand All @@ -167,19 +206,6 @@ export default function OperatingConditionsDashboard({
</div>
) : (
<>
<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={{
Expand All @@ -197,6 +223,7 @@ export default function OperatingConditionsDashboard({
text: `<b>${yAxisTitle}</b>`,
},
range: [0, yMax],
showline: true,
},
xaxis: {
title: {
Expand All @@ -205,7 +232,8 @@ export default function OperatingConditionsDashboard({
tickangle: xTickAngle,
ticklen: 10,
automargin: true,
range: [0, maxDays], // Cap x-axis at maxDays
range: [0, effectiveMaxDays],
// range: ignoreMaxDays ? null : [0, effectiveMaxDays],
showline: true,
},
hovermode: "x",
Expand All @@ -214,6 +242,43 @@ export default function OperatingConditionsDashboard({
displayModeBar: false,
}}
/>
<div className="flex justify-center my-4">
<div className="w-1/3 flex justify-center">
<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>
<div className="w-1/3 flex justify-center">
<label className="flex items-center">
<input
type="checkbox"
checked={!ignoreMaxDays}
onChange={(e) => setIgnoreMaxDays(!e.target.checked)}
/>
<span className="ml-2">Cap at 45 Days</span>
</label>
</div>
<div className="w-1/3 flex justify-center">
<label className="flex items-center">
<input
type="checkbox"
checked={!applyMovingAverage}
onChange={(e) => setApplyMovingAverage(!e.target.checked)}
/>
<span className="ml-2">
Display Raw Data (No Moving Average)
</span>
</label>
</div>
</div>
</>
)}
</>
Expand Down
24 changes: 24 additions & 0 deletions scripts/pipeline-template.py
Original file line number Diff line number Diff line change
Expand Up @@ -596,8 +596,32 @@ def preprocess_data(self, data: pd.DataFrame) -> pd.DataFrame:
# Map Trial IDs to the technology used in the trial
all_trials["Technology"] = all_trials["Trial ID"].apply(map_technology)

# Anonymize brand names
brand_mapping = {"BÉSICS®": "BÉSICS®"} # Note: no anonymization for BÉSICS®
brand_counter = 0


def anonymize_brand(brand: str) -> str:
"""Anonymizes brand names by mapping them to a generic brand. Sorry for the global variable.

Args:
brand: The brand name

Returns:
The anonymized brand name (eg "Brand A")
"""
global brand_counter
if brand not in brand_mapping:
brand_mapping[brand] = f"Brand {chr(65 + brand_counter)}"
brand_counter += 1
return brand_mapping[brand]


all_trials["Item Brand"] = all_trials["Item Brand"].apply(anonymize_brand)

all_trials.to_csv(output_filepath, index=False)


# Make sure all trial IDs are represented in operating conditions
unique_trial_ids = pd.DataFrame(all_trials["Trial ID"].unique(), columns=["Trial ID"]).set_index("Trial ID")
df_operating_conditions_avg = unique_trial_ids.merge(
Expand Down
Loading