Skip to content

Commit e4e3dc2

Browse files
committed
bug fixes and feature additions
1 parent a6932ef commit e4e3dc2

File tree

13 files changed

+1084
-579
lines changed

13 files changed

+1084
-579
lines changed

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,6 @@ node_modules
22
.next
33
out
44

5-
*storybook.log
5+
*storybook.log
6+
7+
.DS_Store

app/tools/layout.tsx

Lines changed: 61 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -55,42 +55,67 @@ export default function DashboardLayout({
5555
}, [notifications]);
5656

5757

58-
if (rdkit) {
59-
rdkit.onmessage = (event) => {
60-
const message = event.data;
61-
if (typeof message === 'string') {
62-
pushNotification({ message: message, autoClose: true, duration: 2 });
63-
} else if (message.data) {
64-
switch (message.function) {
65-
case 'fingerprint':
66-
if (message.settings) {
67-
localStorage.setItem("fingerprint", message.settings.fingerprint);
68-
localStorage.setItem("path", message.settings.radius.toString());
69-
localStorage.setItem("nBits", message.settings.nBits.toString());
70-
}
71-
pushNotification({ message: "Molecule Pre-processing Done! Going to Activity Distribution Tool..." });
72-
setTimeout(() => {
73-
setLigand(message.data);
74-
setTarget({ ...target, activity_columns: message.activity_columns, pre_processed: true });
75-
router.push("/tools/activity")
76-
}, 200);
77-
break;
78-
case 'mma':
79-
pushNotification({ message: "Massive Molecular Analysis Done! Going to Scaffold Analysis Tool..." });
80-
setTarget({ ...target, scaffCores: message.data });
81-
break;
82-
case 'tanimoto':
83-
pushNotification({ message: "Tanimoto Similarity Calculation Done!" });
84-
setLigand(message.data);
85-
break;
86-
default:
87-
break;
88-
}
89-
} else if (message.error) {
90-
rdkit.terminate();
58+
if (rdkit) {
59+
rdkit.onmessage = (event) => {
60+
const { message, id, error, ...data } = event.data;
61+
62+
// Handle simple string messages (progress, etc.)
63+
if (message && typeof message === 'string') {
64+
pushNotification({ message, autoClose: true, duration: 2000, id: id || undefined });
65+
return;
66+
}
67+
68+
// Handle errors
69+
if (error) {
70+
pushNotification({ message: `Error: ${error}`, type: 'error' });
71+
rdkit.terminate();
72+
return;
73+
}
74+
75+
// Handle function results
76+
if (id && data.function) {
77+
switch (data.function) {
78+
case 'fingerprint':
79+
if (data.settings) {
80+
localStorage.setItem("fingerprint", data.settings.fingerprint);
81+
localStorage.setItem("path", data.settings.radius.toString());
82+
localStorage.setItem("nBits", data.settings.nBits.toString());
83+
}
84+
pushNotification({ message: "Molecule Pre-processing Done! Going to Activity Distribution Tool..." });
85+
setTimeout(() => {
86+
setLigand(data.data);
87+
setTarget({ ...target, activity_columns: data.activity_columns, pre_processed: true });
88+
router.push("/tools/activity");
89+
}, 200);
90+
break;
91+
92+
case 'mma':
93+
pushNotification({ message: "Massive Molecular Analysis Done! Going to Scaffold Analysis Tool..." });
94+
setTarget({ ...target, scaffCores: data.data });
95+
break;
96+
97+
case 'tanimoto':
98+
pushNotification({ message: "Tanimoto Similarity Calculation Done!" });
99+
setLigand(data.data);
100+
break;
101+
102+
case 'only_fingerprint':
103+
setLigand(data.results);
104+
pushNotification({ message: "Fingerprints generated successfully!" });
105+
break;
106+
107+
case 'substructure_search':
108+
setLigand(data.results);
109+
pushNotification({ message: `Found ${data.results.length} matching substructures` });
110+
break;
111+
112+
default:
113+
console.warn('Unknown function:', data.function);
91114
}
92-
};
93-
}
115+
}
116+
};
117+
}
118+
94119

95120
if (pyodide) {
96121
pyodide.onmessage = (event) => {
@@ -178,6 +203,7 @@ export default function DashboardLayout({
178203
<Notification
179204
key={n.id}
180205
radius="lg"
206+
color={n.type === 'error' ? 'red' : n.type || 'blue'} //
181207
withCloseButton
182208
onClose={() => removeNotification(n.id)}
183209
>

app/tools/ml/layout.tsx

Lines changed: 90 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import JSME from "../../../components/tools/toolViz/JSMEComp";
1111
import Dropdown from "../../../components/tools/toolViz/DropDown";
1212
import { Button, Group, Input, Select } from "@mantine/core";
1313
import FoldMetricBarplot from "../../../components/tools/toolViz/BarChart";
14+
import DiscreteScatterplot from "../../../components/tools/toolViz/DiscreteScatterPlot";
15+
import { round } from "mathjs";
1416

1517
export default function MLLayout({ children }) {
1618
const [screenData, setScreenData] = useState([]);
@@ -22,7 +24,6 @@ export default function MLLayout({ children }) {
2224
const { rdkit } = useContext(RDKitContext);
2325
const { pyodide } = useContext(PyodideContext);
2426
const { setTarget, target } = useContext(TargetContext);
25-
2627
const pathname = usePathname();
2728

2829
// -----------------------------
@@ -64,14 +65,16 @@ export default function MLLayout({ children }) {
6465

6566
pyodide.postMessage({
6667
id: "job-123",
67-
opts: 0,
68+
opts: target.machine_learning_inference_type === "regression" ? 1 : 2,
6869
fp: [mol_fp],
6970
func: "ml-screen",
7071
});
7172

7273
pyodide.onmessage = (event) => {
7374
if (event.data.success === "ok") {
7475
setOneOffSmilesResult(event.data.results[0]);
76+
console.log(oneOffSMILESResult)
77+
console.log(typeof (oneOffSMILESResult))
7578
}
7679
};
7780
}
@@ -90,12 +93,12 @@ export default function MLLayout({ children }) {
9093
// Merge fold scatter data
9194
// -----------------------------
9295
const mergedData: { x: number; y: number }[] = [];
93-
const foldColorProperty: number[] = [];
96+
const foldColorProperty: string[] = [];
9497

9598
perFoldPreds.forEach((fold, foldIndex) => {
9699
fold.forEach((point) => {
97100
mergedData.push({ x: point.x, y: point.y });
98-
foldColorProperty.push(foldIndex + 1);
101+
foldColorProperty.push(`Fold ${foldIndex + 1}`);
99102
});
100103
});
101104

@@ -106,63 +109,100 @@ export default function MLLayout({ children }) {
106109
<div className="tools-container">
107110
<MLResultsContext.Provider value={setScreenData}>
108111
{/* ---------------- Task Type Selector ---------------- */}
109-
<Select
110-
label="ML Task Type"
111-
value={target.machine_learning_inference_type || "regression"}
112-
onChange={(v) => setTarget({ ...target, machine_learning_inference_type: v }) }
113-
data={[
114-
{ value: "regression", label: "Regression" },
115-
{ value: "classification", label: "Classification" },
116-
]}
117-
mb="md"
118-
/>
119-
120-
{/* ---------------- Children get taskType prop ---------------- */}
121-
{children}
122-
123-
{/* ---------------- Single molecule prediction ---------------- */}
112+
{/* Task Type Selector + Clear Button */}
124113
<Group>
125-
<h2>Predict the activity of a single molecule</h2>
126-
127-
<Input
128-
ref={inputRef}
129-
style={{ width: "20%" }}
130-
onChange={(e) => setOneOffSmiles(e.target.value)}
131-
placeholder="Input your SMILES string"
114+
<h3>ML Task Type</h3>
115+
<Select
116+
value={target.machine_learning_inference_type}
117+
onChange={(v) => setTarget({ ...target, machine_learning_inference_type: v })}
118+
data={[
119+
{ value: "regression", label: "Regression" },
120+
{ value: "classification", label: "Classification" },
121+
]}
122+
style={{ flex: 1 }}
123+
disabled={hasResults} // Still lock after results
132124
/>
133125

134-
<Dropdown buttonText="Draw the molecule">
135-
<JSME
136-
width="300px"
137-
height="300px"
138-
onChange={(smiles) => setOneOffSmiles(smiles)}
139-
/>
140-
</Dropdown>
141-
142-
<Button onClick={oneOffPred}>Predict Activity</Button>
143-
144-
{oneOffSMILESResult !== undefined && (
145-
<span>
146-
Predicted {target.activity_columns[0]}: <b>{oneOffSMILESResult}</b>
147-
</span>
126+
{hasResults && (
127+
<Button
128+
variant="light"
129+
color="gray"
130+
onClick={() => {
131+
setTarget({
132+
...target,
133+
machine_learning: [],
134+
machine_learning_inference_type: "regression" // Reset to allow re-selection
135+
});
136+
setOneOffSmilesResult(undefined);
137+
}}
138+
>
139+
Clear Results
140+
</Button>
148141
)}
149142
</Group>
150143

144+
145+
{/* ---------------- Children get taskType prop ---------------- */}
146+
{children}
147+
151148
{/* ---------------- Scatterplot or barplot ---------------- */}
152149
{hasResults && (
153150
<>
154-
{target.machine_learning_inference_type === "regression" ? (
155-
<Scatterplot
156-
data={mergedData}
157-
discreteColor={true}
158-
colorProperty={foldColorProperty}
159-
xAxisTitle="Experimental Activity"
160-
yAxisTitle="Predicted Activity"
151+
{/* ---------------- Single molecule prediction ---------------- */}
152+
<Group>
153+
<h2>Predict the activity of a single molecule</h2>
154+
155+
<Input
156+
ref={inputRef}
157+
style={{ width: "20%" }}
158+
onChange={(e) => setOneOffSmiles(e.target.value)}
159+
placeholder="Input your SMILES string"
161160
/>
162-
) : (
163-
<FoldMetricBarplot metricName="R2" data={metric2} color="#f59e0b" />
161+
162+
<Dropdown buttonText="Draw the molecule">
163+
<JSME
164+
width="300px"
165+
height="300px"
166+
onChange={(smiles) => setOneOffSmiles(smiles)}
167+
/>
168+
</Dropdown>
169+
170+
<Button onClick={oneOffPred}>Predict Activity</Button>
171+
172+
{oneOffSMILESResult !== undefined && (
173+
<span>
174+
<b>{target.machine_learning_inference_type === "regression" && <>
175+
Predicted {target.activity_columns[0]}: {round(oneOffSMILESResult, 2)}
176+
</>}
177+
</b>
178+
<b>{target.machine_learning_inference_type === "classification" &&
179+
<Group style={{ marginLeft: "10px" }}>
180+
<span>Active Probability: {oneOffSMILESResult[0]}</span>
181+
<span>Inactive Probability: {oneOffSMILESResult[1]}</span>
182+
</Group>
183+
}
184+
</b>
185+
</span>
186+
)}
187+
</Group>
188+
{target.machine_learning_inference_type === "regression" && (
189+
<>
190+
<DiscreteScatterplot
191+
data={mergedData}
192+
discreteColor={true}
193+
colorLabels={foldColorProperty}
194+
xAxisTitle="Experimental Activity"
195+
yAxisTitle="Predicted Activity"
196+
/>
197+
<FoldMetricBarplot metricName="Mean Absolute Error" data={metric1} color="#3b82f6" />
198+
</>
199+
)}
200+
{target.machine_learning_inference_type === "classification" && (
201+
<Group align="flex-start">
202+
<FoldMetricBarplot metricName="Accuracy" data={metric1} color="#3b82f6" />
203+
<FoldMetricBarplot metricName="ROC-AUC Score" data={metric2} color="#f59e0b" />
204+
</Group>
164205
)}
165-
<FoldMetricBarplot metricName="Accuracy" data={metric1} color="#3b82f6" />
166206
</>
167207
)}
168208
</MLResultsContext.Provider>

app/tools/screen/layout.tsx

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import PyodideContext from "../../../context/PyodideContext";
66
import Loader from "../../../components/ui-comps/Loader";
77
import CSVLoader from "../../../components/dataloader/CSVLoader";
88
import NotificationContext from "../../../context/NotificationContext";
9+
import TargetContext from "../../../context/TargetContext";
910

1011
// Create a new context for screenData
1112
export const ScreenDataContext = createContext([]);
@@ -18,16 +19,19 @@ export default function ScreenLayout({ children }) {
1819

1920
const { rdkit } = useContext(RDKitContext);
2021
const { pyodide } = useContext(PyodideContext);
22+
const { target } = useContext(TargetContext);
23+
let newScreenData = screenData;
24+
const requestId = `fingerprint_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
2125

2226
async function callofScreenFunction(data) {
2327
pushNotification({ message: "Running ML Model on Ligands" });
24-
let newScreenData = screenData
28+
2529
newScreenData.forEach(obj => {
2630
obj["canonical_smiles"] = obj[data.smi_column];
2731
delete obj[data.smi_column];
2832
});
2933

30-
const requestId = `fingerprint_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
34+
3135
rdkit.postMessage({
3236
function: 'only_fingerprint',
3337
id: requestId,
@@ -38,27 +42,28 @@ export default function ScreenLayout({ children }) {
3842
nBits: parseInt(localStorage.getItem("nBits")),
3943
}
4044
});
41-
rdkit.onmessage = async (event) => {
42-
console.log("Received message from RDKit:", event.data);
43-
if (event.data.id === requestId) {
44-
let mol_fp = event.data.data.map(x => x["fingerprint"]);
45-
pyodide.postMessage({
46-
id: "job-123",
47-
opts: 0,
48-
fp: mol_fp,
49-
func: "ml-screen"
50-
})
51-
pyodide.onmessage = async (event) => {
52-
console.log("Received message from Pyodide:", event.data);
53-
if (event.data.success == "ok") {
54-
let fp_mols = event.data.results;
55-
newScreenData = await newScreenData.map((x, i) => {
56-
x["predictions"] = fp_mols[i];
57-
return x
58-
});
59-
setScreenData(newScreenData);
60-
pushNotification({ message: "ML Model run complete" });
61-
}
45+
}
46+
47+
48+
rdkit.onmessage = async (event) => {
49+
if (event.data.function === "only_fingerprint") {
50+
let mol_fp = event.data.results.map(x => x["fingerprint"]);
51+
pyodide.postMessage({
52+
id: "job-123",
53+
opts: target.machine_learning_inference_type === "regression" ? 1 : 2,
54+
fp: mol_fp,
55+
func: "ml-screen"
56+
})
57+
pyodide.onmessage = async (event) => {
58+
console.log("Received message from Pyodide:", event.data);
59+
if (event.data.success == "ok") {
60+
let fp_mols = event.data.results;
61+
newScreenData = await newScreenData.map((x, i) => {
62+
x["predictions"] = fp_mols[i];
63+
return x
64+
});
65+
setScreenData(newScreenData);
66+
pushNotification({ message: "ML Model run complete" });
6267
}
6368
}
6469
}

0 commit comments

Comments
 (0)