diff --git a/dashboard-react/components/CheckboxMenu.js b/dashboard-react/components/CheckboxMenu.js
index 094dd5e..bfaece2 100644
--- a/dashboard-react/components/CheckboxMenu.js
+++ b/dashboard-react/components/CheckboxMenu.js
@@ -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 (
@@ -89,19 +73,15 @@ export default function CheckboxMenu({
-
- {canScrollUp ? (
-
- ▲
-
- ) : (
-
- )}
+
+ 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))",
+ }}
+ >
{" "}
+
{options?.map((option) => (
-
@@ -132,13 +112,13 @@ export default function CheckboxMenu({
))}
- {canScrollDown ? (
-
- ▼
-
- ) : (
-
- )}
+
{" "}
);
diff --git a/dashboard-react/components/Dashboard.js b/dashboard-react/components/Dashboard.js
index 7748e14..0b403cb 100644
--- a/dashboard-react/components/Dashboard.js
+++ b/dashboard-react/components/Dashboard.js
@@ -129,6 +129,9 @@ export default function Dashboard() {
tickangle: xTickAngle,
ticklen: 10,
automargin: true,
+ tickfont: {
+ weight: "bold",
+ },
},
hovermode: "x",
}}
diff --git a/dashboard-react/components/DashboardFilterControls.js b/dashboard-react/components/DashboardFilterControls.js
index 802211b..a035c60 100644
--- a/dashboard-react/components/DashboardFilterControls.js
+++ b/dashboard-react/components/DashboardFilterControls.js
@@ -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."
/>
{
csv("/data/operating_conditions.csv")
.then((data) => {
const formattedData = [];
-
const selectedColumn =
selectedMetric === "Temperature"
? "Temperature"
@@ -29,14 +32,9 @@ 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",
@@ -44,6 +42,31 @@ export default function OperatingConditionsDashboard({
"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) => {
@@ -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);
@@ -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 = {
@@ -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;
@@ -167,19 +206,6 @@ export default function OperatingConditionsDashboard({
) : (
<>
-
-
-
${yAxisTitle}`,
},
range: [0, yMax],
+ showline: true,
},
xaxis: {
title: {
@@ -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",
@@ -214,6 +242,43 @@ export default function OperatingConditionsDashboard({
displayModeBar: false,
}}
/>
+
+
+
+
+
+
+
+
+
+
+
>
)}
>
diff --git a/scripts/pipeline-template.py b/scripts/pipeline-template.py
index cd564da..a082e7d 100644
--- a/scripts/pipeline-template.py
+++ b/scripts/pipeline-template.py
@@ -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(