Skip to content

Commit 9334549

Browse files
committed
tanimoto works!
1 parent 96cbd68 commit 9334549

File tree

14 files changed

+201
-114
lines changed

14 files changed

+201
-114
lines changed

app/tools/activity/page.tsx

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,24 @@ import { mean, median, mode, round } from "mathjs";
77
import TargetContext from "../../../context/TargetContext";
88

99
export default function Activity() {
10-
const { ligand } = useContext(LigandContext);
11-
const { target } = useContext(TargetContext);
12-
var data = ligand.map((obj) => obj[target.activity_columns[0]]);
10+
const { ligand } = useContext(LigandContext) || { ligand: [] };
11+
const { target } = useContext(TargetContext) || { target: { activity_columns: [] } };
12+
13+
const data = Array.isArray(ligand) && ligand.length > 0 && target?.activity_columns?.length > 0
14+
? ligand.map((obj) => obj[target.activity_columns[0]])
15+
: [];
1316

1417

1518
return (
1619
<div className="tools-container">
17-
<Histogram data={data} xLabel={target.activity_columns[0]} yLabel="Count" toolTipData={ligand}>
18-
<span>Mean : {round(mean(data), 2) || ""} || Median : {round(median(data), 2) || ""} || Mode : {round(mode(data), 2) || ""}</span>
20+
<Histogram data={data} xLabel={target?.activity_columns?.[0] || ""} yLabel="Count" toolTipData={ligand}>
21+
<span>
22+
{data.length > 0 ? (
23+
<>Mean : {round(mean(data), 2) || ""} || Median : {round(median(data), 2) || ""} || Mode : {round(mode(data), 2) || ""}</>
24+
) : (
25+
<>No activity data available</>
26+
)}
27+
</span>
1928
</Histogram>
2029
</div>
2130
)

app/tools/dim-red/pca/page.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ import TargetContext from "../../../../context/TargetContext";
88
import { Button } from "@mantine/core";
99

1010
export default function PCA() {
11-
const { ligand } = useContext(LigandContext);
12-
const { target } = useContext(TargetContext);
13-
const { pyodide } = useContext(PyodideContext);
11+
const { ligand } = useContext(LigandContext) || { ligand: [] };
12+
const { target } = useContext(TargetContext) || { target: { activity_columns: [] } };
13+
const { pyodide } = useContext(PyodideContext) || { pyodide: null };
1414
const containerRef = useRef(null);
1515

1616
async function runDimRed() {
@@ -33,7 +33,7 @@ export default function PCA() {
3333
<div className="tools-container" ref={containerRef}>
3434
<h1>Principal Component Analysis</h1>
3535
<Button onClick={() => runDimRed()}>Run PCA</Button>
36-
{ligand[0].pca && (
36+
{Array.isArray(ligand) && ligand.length > 0 && ligand[0] && ligand[0].pca && (
3737
<>
3838
{/* <p>Explained Variance by first 2 Principal Components: {globalThis.explain_variance.toFixed(2)}</p> */}
3939
<br></br>
@@ -42,7 +42,7 @@ export default function PCA() {
4242
const [x, y] = obj.pca;
4343
return { x, y };
4444
})}
45-
colorProperty={ligand.map((obj) => obj[target.activity_columns[0]])}
45+
colorProperty={ligand.map((obj) => obj[target?.activity_columns?.[0]])}
4646
hoverProp={ligand.map((obj) => obj.canonical_smiles)}
4747
xAxisTitle={"Principal Component 1"}
4848
yAxisTitle={"Principal Component 2"}

app/tools/dim-red/tsne/page.tsx

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,20 @@ type tsneType = {
1717
}
1818

1919
export default function TSNE() {
20-
const { ligand, setLigand } = useContext(LigandContext);
21-
const { target } = useContext(TargetContext)
22-
const { pyodide } = useContext(PyodideContext);
20+
const { ligand, setLigand } = useContext(LigandContext) || { ligand: [], setLigand: () => {} };
21+
const { target } = useContext(TargetContext) || { target: { activity_columns: [] } };
22+
const { pyodide } = useContext(PyodideContext) || { pyodide: null };
2323
const [pca, setPCA] = useState<any[]>([]);
2424
const containerRef = useRef(null);
2525
const [loaded, setLoaded] = useState(false);
2626

2727
const { register, handleSubmit, watch, formState: { errors }, } = useForm<tsneType>()
2828

29-
globalThis.fp = ligand.map((obj) => obj.fingerprint);
29+
if (Array.isArray(ligand)) {
30+
globalThis.fp = ligand.map((obj) => obj.fingerprint);
31+
} else {
32+
globalThis.fp = [];
33+
}
3034

3135
async function runDimRed(formStuff: tsneType) {
3236
setLoaded(false);
@@ -82,7 +86,7 @@ export default function TSNE() {
8286
<input type="submit" className="button" value={"Run tSNE"} />
8387
</form>
8488
</details>
85-
{ligand[0].tsne && (
89+
{Array.isArray(ligand) && ligand.length > 0 && ligand[0] && ligand[0].tsne && (
8690
<>
8791
{/* <p>Explained Variance by first 2 Principal Components: {globalThis.explain_variance.toFixed(2)}</p> */}
8892
<br></br>

app/tools/layout.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ export default function DashboardLayout({
7171
setNotificationText("Massive Molecular Analysis Done! Going to Scaffold Analysis Tool...");
7272
setTarget({ ...target, scaffCores: message.data });
7373
break;
74+
case 'tanimoto':
75+
setNotificationText("Tanimoto Similarity Calculation Done!");
76+
setLigand(message.data);
77+
break;
7478
default:
7579
break;
7680
}
@@ -84,6 +88,10 @@ export default function DashboardLayout({
8488
pyodide.onmessage = (event) => {
8589
setShowNotification(true);
8690
const message = event.data;
91+
if (typeof message === 'string') {
92+
setNotificationText(message);
93+
setShowNotification(true);
94+
}
8795
if (message.func === "dim_red") {
8896
if (message.opts === 2 || message.opts === 3) {
8997
setNotificationText("tSNE Processing Done!");

app/tools/tanimoto/page.tsx

Lines changed: 42 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,15 @@ import { useState, useEffect, useRef, useContext } from "react";
44
import LigandContext from "../../../context/LigandContext";
55
import Histogram from "../../../components/tools/toolViz/Histogram";
66
import RDKitContext from "../../../context/RDKitContext";
7-
import TanimotoSimilarity from "../../../components/utils/tanimoto";
8-
import fpSorter from "../../../components/utils/fp_sorter";
97
import ErrorContext from "../../../context/ErrorContext";
108
import JSME from "../../../components/tools/toolViz/JSMEComp";
11-
import Dropdown from "../../../components/tools/toolViz/DropDown";
9+
import { Select } from "@mantine/core";
1210

1311
export default function Tanimoto() {
14-
const { ligand } = useContext(LigandContext);
12+
const { ligand } = useContext(LigandContext) || { ligand: [] };
1513
const { rdkit } = useContext(RDKitContext);
1614
const { setErrors } = useContext(ErrorContext);
17-
15+
const [selectedAnchorMol, setSelectedAnchorMol] = useState<string | null>(null);
1816
const inputRef = useRef(null);
1917
const containerRef = useRef(null);
2018
const [taniData, setTaniData] = useState([]);
@@ -27,29 +25,21 @@ export default function Tanimoto() {
2725
}, [anchorMol]);
2826

2927
function tanimotoDist() {
30-
try {
31-
const mol_fp = fpSorter(
32-
localStorage.getItem("fingerprint"),
33-
anchorMol,
34-
rdkit,
35-
parseInt(localStorage.getItem("path")),
36-
parseInt(localStorage.getItem("nBits")),
37-
);
38-
const data = ligand.map((x) => {
39-
const tanimoto = TanimotoSimilarity(x.fingerprint, mol_fp);
40-
return tanimoto;
41-
});
42-
setTaniData(data);
43-
} catch (e) {
44-
console.error(e);
45-
setErrors("Most probably there is problem with your SMILES string");
46-
}
28+
const requestId = `fingerprint_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
29+
rdkit.postMessage({
30+
function: 'tanimoto',
31+
id: requestId,
32+
mol_data: ligand,
33+
anchorMol: anchorMol,
34+
fp_dets: {
35+
type: localStorage.getItem("fingerprint") || "Morgan",
36+
radius: parseInt(localStorage.getItem("path")) || 2,
37+
nBits: parseInt(localStorage.getItem("nBits")) || 2048,
38+
}
39+
});
40+
setTaniData([...taniData, anchorMol]);
4741
}
4842

49-
useEffect(() => {
50-
tanimotoDist();
51-
}, []);
52-
5343
return (
5444
<div className="tools-container" ref={containerRef}>
5545
<details open={false}>
@@ -70,18 +60,37 @@ export default function Tanimoto() {
7060
type="text"
7161
className="input"
7262
onChange={(e) => setAnchorMol(e.target.value)}
73-
style = {{width: "40%"}}
74-
ref={inputRef}
63+
style={{ width: "40%" }}
64+
ref={inputRef}
7565
/>
7666
&nbsp;
77-
<Dropdown buttonText = "Draw the molecule">
78-
<JSME width="400px" height="300px" onChange={(smiles) => setAnchorMol(smiles)} />
79-
</Dropdown>
67+
<JSME width="400px" height="300px" onChange={(smiles) => setAnchorMol(smiles)} />
8068
&nbsp;
8169
<button className="button" onClick={tanimotoDist}>
8270
Generate Graph
8371
</button>
84-
{taniData.length != 0 && (
72+
<Select
73+
label="Your favorite library"
74+
placeholder="Pick value"
75+
value = {selectedAnchorMol}
76+
data={taniData}
77+
onChange={setSelectedAnchorMol}
78+
/>
79+
{
80+
selectedAnchorMol &&
81+
Array.isArray(ligand) &&
82+
ligand.length > 0 &&
83+
ligand[0] &&
84+
ligand[0][`${selectedAnchorMol}_tanimoto`] !== undefined && (
85+
<Histogram
86+
data={ligand.map(mol => mol[`${selectedAnchorMol}_tanimoto`])}
87+
toolTipData={ligand}
88+
xLabel="Tanimoto Scores"
89+
yLabel="Count"
90+
/>
91+
)
92+
}
93+
{/* {taniData.length != 0 && (
8594
<>
8695
<Histogram
8796
data={taniData}
@@ -90,7 +99,7 @@ export default function Tanimoto() {
9099
yLabel="Count"
91100
/>
92101
</>
93-
)}
102+
)} */}
94103
</div>
95104
);
96105
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// TabWrapper.jsx
2+
import React, { useState, useEffect } from 'react';
3+
import PropTypes from 'prop-types';
4+
5+
const TabWrapper = ({ children, defaultTab = 0 }) => {
6+
const [activeTab, setActiveTab] = useState(defaultTab);
7+
8+
const handleTabChange = (index) => {
9+
setActiveTab(index);
10+
};
11+
12+
return (
13+
<div className="tab-wrapper">
14+
<div className="tab-header">
15+
{React.Children.map(children, (child, index) =>
16+
React.cloneElement(child as React.ReactElement<any>, {
17+
isActive: index === activeTab,
18+
onTabClick: () => handleTabChange(index),
19+
})
20+
)}
21+
</div>
22+
<div className="tab-content">
23+
{(React.Children.toArray(children)[activeTab] as React.ReactElement<any>).props.children}
24+
</div>
25+
</div>
26+
);
27+
};
28+
29+
export default TabWrapper;
30+
31+
export const Tabs = ({ title, isActive, onTabClick, children }) => {
32+
return (
33+
<div className={`tab ${isActive ? 'active' : ''}`} onClick={onTabClick}>
34+
{title}
35+
</div>
36+
);
37+
};
38+
39+
Tabs.propTypes = {
40+
title: PropTypes.string.isRequired,
41+
isActive: PropTypes.bool,
42+
onTabClick: PropTypes.func,
43+
content: PropTypes.node,
44+
};

package-lock.json

Lines changed: 40 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
"next": "^15.5.5",
3737
"prop-types": "^15.8.1",
3838
"react": "^19.2.0",
39+
"react-chemdoodle": "^0.0.7",
3940
"react-dom": "^19.2.0",
4041
"react-force-graph-3d": "^1.29.0",
4142
"react-hook-form": "^7.65.0",

public/moon.png

-5.81 KB
Binary file not shown.

public/nav_arrow.svg

Lines changed: 0 additions & 29 deletions
This file was deleted.

0 commit comments

Comments
 (0)