From 60aaf93fd1f51ef01e48b45f4a148f18550de615 Mon Sep 17 00:00:00 2001 From: Jordan Dialpuri <44945647+Dialpuri@users.noreply.github.com> Date: Wed, 6 Mar 2024 11:20:27 +0000 Subject: [PATCH 01/25] Refactor TorsionPlot components and improve database fetching Reorganized the TorsionPlot and TorsionMultiPlot components to a shared directory for better code management. Enhanced database fetching in Database.tsx by replacing 'NaN' values with 'null' to prevent parsing errors. Added a new TorsionGraph component for detailed torsions analysis. These changes enhance the functionality while improving code maintainability and efficiency. --- .../DatabaseComponents/TorsionGraph.tsx | 99 +++++++++++++++++++ .../DatabaseResult/DatabaseResult.tsx | 18 +++- .../GlycanDetail/GlycanDetailTorsionPlot.tsx | 7 +- webapp/src/routes/Database/Database.tsx | 16 ++- .../TorsionPlot/TorsionMultiPlot.tsx | 24 ++++- .../TorsionPlot/TorsionPlot.tsx | 11 ++- 6 files changed, 157 insertions(+), 18 deletions(-) create mode 100644 webapp/src/database/DatabaseComponents/TorsionGraph.tsx rename webapp/src/{main/GlycanDetail => shared}/TorsionPlot/TorsionMultiPlot.tsx (82%) rename webapp/src/{main/GlycanDetail => shared}/TorsionPlot/TorsionPlot.tsx (93%) diff --git a/webapp/src/database/DatabaseComponents/TorsionGraph.tsx b/webapp/src/database/DatabaseComponents/TorsionGraph.tsx new file mode 100644 index 00000000..6dd13a6a --- /dev/null +++ b/webapp/src/database/DatabaseComponents/TorsionGraph.tsx @@ -0,0 +1,99 @@ +import React, { useEffect, useState } from "react" +import TorsionMultiPlot from "../../shared/TorsionPlot/TorsionMultiPlot.tsx" + +function TorsionGraphTabs(props): React.JSX.Element[] { + return props.keys.map((item, index) => { + return ( +
  • + +
  • + ); + }); +} + + +export default function TorsionGraph(data) { + const [torsions, setTorsions] = useState() + const [torsionTab, setTorsionTab] = useState(0) + const [glycanTab, setGlycanTab] = useState(0) + + useEffect(() => { + const glycans = data.data.glycans; + let torsionList: Record[]> = {}; + + for (const key in glycans) { + const glycanType = glycans[key]; + for (let i = 0; i < glycanType.length; i++) { + const chainID = glycanType[i].rootSugarChainId; + const linkages = glycanType[i].linkages; + + for (const linkage in linkages) { + for (let j = 0; j < linkages[linkage].length; j++) { + const data = { + "sugar_1": linkages[linkage][j].first_residue, + "sugar_2": linkages[linkage][j].second_residue, + "atom_number_1": linkages[linkage][j].donor_atom.slice(-1), + "atom_number_2": linkages[linkage][j].acceptor_atom.slice(-1), + "phi" : linkages[linkage][j].phi, + "psi": linkages[linkage][j].psi + } + if (chainID in torsionList) { + torsionList[chainID].push(data) + } + else { + torsionList[chainID] = [data] + } + } + } + } + } + // torsionList.sort; + + let sortedTorsionList = {} + + Object.keys(torsionList) + .sort() + .forEach(function(k, v) { + sortedTorsionList[k] = torsionList[k]; + }) + + setTorsions(sortedTorsionList) + }, [data]) + + useEffect(() => { + setTorsionTab(0) + }, [glycanTab]) + + return ( + +
    + {torsions !== undefined ? + <> + Linkage Torsion Analysis +
    + Chain: +
      + +
    +
    + + + : <>} +
    + ) +} \ No newline at end of file diff --git a/webapp/src/database/DatabaseResult/DatabaseResult.tsx b/webapp/src/database/DatabaseResult/DatabaseResult.tsx index 47bb03f4..e664a32f 100644 --- a/webapp/src/database/DatabaseResult/DatabaseResult.tsx +++ b/webapp/src/database/DatabaseResult/DatabaseResult.tsx @@ -4,6 +4,8 @@ import CremerPopleGraph from '../DatabaseComponents/CremerPopleGraph.tsx'; import BFactorVsRSCC from '../DatabaseComponents/BFactorVsRSCC.tsx'; import SNFGList from '../DatabaseComponents/SNFGList.tsx'; import SugarList from '../DatabaseComponents/SugarList.tsx'; +import TorsionGraph from "../DatabaseComponents/TorsionGraph.tsx" +import BorderElement from "../../layouts/BorderElement.tsx" export default function DatabaseResult(props: DatabaseResultProps) { const [pdbShown, setPDBShown] = useState(true); @@ -18,7 +20,6 @@ export default function DatabaseResult(props: DatabaseResultProps) { if (props.pdbResults === null) return; if (props.pdbRedoResults === null) return; - console.log('running use effect'); setSelectedData(props.pdbResults); }, []); @@ -43,6 +44,21 @@ export default function DatabaseResult(props: DatabaseResultProps) {
    + + + +
    + +
    +
    diff --git a/webapp/src/main/GlycanDetail/GlycanDetailTorsionPlot.tsx b/webapp/src/main/GlycanDetail/GlycanDetailTorsionPlot.tsx index 1e2b25db..a632d942 100644 --- a/webapp/src/main/GlycanDetail/GlycanDetailTorsionPlot.tsx +++ b/webapp/src/main/GlycanDetail/GlycanDetailTorsionPlot.tsx @@ -1,12 +1,12 @@ -import { type TableDataEntry } from '../../interfaces/types.ts'; +import { type ResultsEntry } from '../../interfaces/types.ts'; import React, { lazy, useEffect, useState } from 'react'; const TorsionMultiPlot = lazy( - async () => await import('./TorsionPlot/TorsionMultiPlot.tsx') + async () => await import('../../shared/TorsionPlot/TorsionMultiPlot.tsx') ); export function GlycanDetailTorsionPlot(props: { key: string; - tableDataEntries: TableDataEntry[]; + tableDataEntries: ResultsEntry []; rowID: number; tab: number; tab1: (value: ((prevState: number) => number) | number) => void; @@ -33,6 +33,7 @@ export function GlycanDetailTorsionPlot(props: { tab={props.tab} setTab={props.tab1} size={dimension} + background={'#D6D9E5'} /> ); diff --git a/webapp/src/routes/Database/Database.tsx b/webapp/src/routes/Database/Database.tsx index ee6f2401..8fff1978 100644 --- a/webapp/src/routes/Database/Database.tsx +++ b/webapp/src/routes/Database/Database.tsx @@ -33,9 +33,12 @@ export default function Database(props: { try { const response = await fetch(pdbUrl); - const data: string = await response.json(); - setPDBResults(data); - } catch { + const text = await response.text() + const replacedText = text.replace(/\bNaN\b/g, "null") + const result = JSON.parse(replacedText) + // const data: string = await response.json(); + setPDBResults(result); + } catch (e) { setFallBack(true); setFailureText('This PDB is not in the database'); } @@ -44,9 +47,12 @@ export default function Database(props: { try { const response = await fetch(pdbRedoUrl); - const redoData: string = await response.json(); - setPDBRedoResults(redoData); + const text = await response.text() + const replacedText = text.replace(/\bNaN\b/g, "null") + const result = JSON.parse(replacedText) + setPDBRedoResults(result); } catch { + console.log("PDB REDO Failed") // setFallBack(true); // setFailureText('This PDB is not in the database'); } diff --git a/webapp/src/main/GlycanDetail/TorsionPlot/TorsionMultiPlot.tsx b/webapp/src/shared/TorsionPlot/TorsionMultiPlot.tsx similarity index 82% rename from webapp/src/main/GlycanDetail/TorsionPlot/TorsionMultiPlot.tsx rename to webapp/src/shared/TorsionPlot/TorsionMultiPlot.tsx index 6f8184d4..aba18a67 100644 --- a/webapp/src/main/GlycanDetail/TorsionPlot/TorsionMultiPlot.tsx +++ b/webapp/src/shared/TorsionPlot/TorsionMultiPlot.tsx @@ -1,10 +1,9 @@ -import React, { useEffect, lazy, type ReactElement } from 'react'; +import React, { useEffect, lazy, type ReactElement, useState } from "react" const TorsionPlot = lazy(async () => await import('./TorsionPlot.tsx')); function sortTorsions(torsions): [string[], any] { const linkageSet = new Set(); - torsions.forEach( (torsion: { sugar_1: string; @@ -84,14 +83,15 @@ function sortTorsions(torsions): [string[], any] { return [linkageArray, sortedLinkageMap]; } -function TorsionMultiPlotTabs({ torsions, setTab }): React.JSX.Element[] { +function TorsionMultiPlotTabs({ torsions, currentTab, setTab }): React.JSX.Element[] { const [linkageArray, _] = sortTorsions(torsions); return linkageArray.map((item, index) => { return (
  • +
  • + ); }); } @@ -114,18 +116,28 @@ export default function TorsionMultiPlot({ tab, setTab, size, + background }: { torsions: any; tab: string; setTab: any; size: any; + background: string }): ReactElement { - const [linkageArray, sortedLinkageArray] = sortTorsions(torsions); + + const [linkageArray, setLinkageArray] = useState([]) + const [sortedLinkageArray, setSortedLinkageArray] = useState() useEffect(() => { setTab(0); }, []); + useEffect(() => { + const [linkageArray_, sortedLinkageArray_] = sortTorsions(torsions) + setLinkageArray(linkageArray_) + setSortedLinkageArray(sortedLinkageArray_) + }, [torsions]) + return ( <> {linkageArray.length === 0 ? ( @@ -137,6 +149,7 @@ export default function TorsionMultiPlot({ @@ -145,6 +158,7 @@ export default function TorsionMultiPlot({ linkageType={linkageArray[tab]} sortedTorsionList={sortedLinkageArray} size={size} + background={background} /> )} diff --git a/webapp/src/main/GlycanDetail/TorsionPlot/TorsionPlot.tsx b/webapp/src/shared/TorsionPlot/TorsionPlot.tsx similarity index 93% rename from webapp/src/main/GlycanDetail/TorsionPlot/TorsionPlot.tsx rename to webapp/src/shared/TorsionPlot/TorsionPlot.tsx index 696ee25f..4c09c18c 100644 --- a/webapp/src/main/GlycanDetail/TorsionPlot/TorsionPlot.tsx +++ b/webapp/src/shared/TorsionPlot/TorsionPlot.tsx @@ -1,22 +1,25 @@ import React, { useEffect, useState, lazy, type ReactElement } from 'react'; -import { linkageDB, binDB } from '../../../data/Constants.tsx'; +import { linkageDB, binDB } from '../../data/Constants.tsx'; const Plot = lazy(async () => await import('react-plotly.js')); export default function TorsionPlot({ linkageType, sortedTorsionList, size, + background }: { linkageType: string; sortedTorsionList: any; size: any; + background: string; }): ReactElement { const [trace, setTrace] = useState({}); const [overlay, setOverlay] = useState({}); const [linkageFound, setLinkageFound] = useState(false); useEffect(() => { + console.log(linkageDB[linkageType]) fetch(linkageDB[linkageType]) .then(async (response) => await response.json()) .then((responseJson) => { @@ -64,7 +67,6 @@ export default function TorsionPlot({ overlayPhi.push(sortedTorsionList[linkageType][i].phi as number); overlayPsi.push(sortedTorsionList[linkageType][i].psi as number); } - setOverlay({ x: overlayPhi, y: overlayPsi, @@ -76,7 +78,7 @@ export default function TorsionPlot({ symbol: ['x'], }, }); - }, [linkageType]); + }, [linkageType, sortedTorsionList]); return !linkageFound ? (

    @@ -91,7 +93,8 @@ export default function TorsionPlot({ height: size, title: linkageType, plot_bgcolor: '#FFFFFF', - paper_bgcolor: '#D6D9E5', + // paper_bgcolor: , + paper_bgcolor: background , yaxis: { title: { text: 'ψ / °', From a1c584318fc49276d5c0dafcc9558d3d43f53f4f Mon Sep 17 00:00:00 2001 From: Jordan Dialpuri <44945647+Dialpuri@users.noreply.github.com> Date: Wed, 6 Mar 2024 11:23:03 +0000 Subject: [PATCH 02/25] Refactor component code and enhance database handling Cleaned up various TorsionPlot components and organized them under a shared directory for easier navigation. Improved data handling in Database.tsx by replacing 'NaN' values with 'null', preventing potential parsing issues. Introduced a TorsionGraph component for more detailed torsion analysis, overall enhancing code maintainability and application functionality. --- .../DatabaseComponents/TorsionGraph.tsx | 90 +++++++++++-------- .../DatabaseResult/DatabaseResult.tsx | 6 +- webapp/src/routes/Database/Database.tsx | 18 ++-- .../shared/TorsionPlot/TorsionMultiPlot.tsx | 34 +++---- webapp/src/shared/TorsionPlot/TorsionPlot.tsx | 6 +- 5 files changed, 87 insertions(+), 67 deletions(-) diff --git a/webapp/src/database/DatabaseComponents/TorsionGraph.tsx b/webapp/src/database/DatabaseComponents/TorsionGraph.tsx index 6dd13a6a..046a13c4 100644 --- a/webapp/src/database/DatabaseComponents/TorsionGraph.tsx +++ b/webapp/src/database/DatabaseComponents/TorsionGraph.tsx @@ -1,13 +1,16 @@ -import React, { useEffect, useState } from "react" -import TorsionMultiPlot from "../../shared/TorsionPlot/TorsionMultiPlot.tsx" +import React, { useEffect, useState } from 'react'; +import TorsionMultiPlot from '../../shared/TorsionPlot/TorsionMultiPlot.tsx'; function TorsionGraphTabs(props): React.JSX.Element[] { return props.keys.map((item, index) => { return ( -
  • +
  • -
  • - ); }); } @@ -116,27 +121,26 @@ export default function TorsionMultiPlot({ tab, setTab, size, - background + background, }: { torsions: any; tab: string; setTab: any; size: any; - background: string + background: string; }): ReactElement { - - const [linkageArray, setLinkageArray] = useState([]) - const [sortedLinkageArray, setSortedLinkageArray] = useState() + const [linkageArray, setLinkageArray] = useState([]); + const [sortedLinkageArray, setSortedLinkageArray] = useState(); useEffect(() => { setTab(0); }, []); useEffect(() => { - const [linkageArray_, sortedLinkageArray_] = sortTorsions(torsions) - setLinkageArray(linkageArray_) - setSortedLinkageArray(sortedLinkageArray_) - }, [torsions]) + const [linkageArray_, sortedLinkageArray_] = sortTorsions(torsions); + setLinkageArray(linkageArray_); + setSortedLinkageArray(sortedLinkageArray_); + }, [torsions]); return ( <> diff --git a/webapp/src/shared/TorsionPlot/TorsionPlot.tsx b/webapp/src/shared/TorsionPlot/TorsionPlot.tsx index 4c09c18c..7a2a907f 100644 --- a/webapp/src/shared/TorsionPlot/TorsionPlot.tsx +++ b/webapp/src/shared/TorsionPlot/TorsionPlot.tsx @@ -7,7 +7,7 @@ export default function TorsionPlot({ linkageType, sortedTorsionList, size, - background + background, }: { linkageType: string; sortedTorsionList: any; @@ -19,7 +19,7 @@ export default function TorsionPlot({ const [linkageFound, setLinkageFound] = useState(false); useEffect(() => { - console.log(linkageDB[linkageType]) + console.log(linkageDB[linkageType]); fetch(linkageDB[linkageType]) .then(async (response) => await response.json()) .then((responseJson) => { @@ -94,7 +94,7 @@ export default function TorsionPlot({ title: linkageType, plot_bgcolor: '#FFFFFF', // paper_bgcolor: , - paper_bgcolor: background , + paper_bgcolor: background, yaxis: { title: { text: 'ψ / °', From 5056964f2cbabc6e8cd3659accadca5f1dbe9683 Mon Sep 17 00:00:00 2001 From: Jordan Dialpuri <44945647+Dialpuri@users.noreply.github.com> Date: Wed, 6 Mar 2024 11:24:38 +0000 Subject: [PATCH 03/25] Refactor GlycanDetailTorsionPlot and TorsionPlot code Fixed formatting issue in GlycanDetailTorsionPlot and removed unnecessary console log in TorsionPlot. The refactoring leads to better readability, easier navigation, and improved maintainability of the code. --- webapp/src/main/GlycanDetail/GlycanDetailTorsionPlot.tsx | 2 +- webapp/src/shared/TorsionPlot/TorsionPlot.tsx | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/webapp/src/main/GlycanDetail/GlycanDetailTorsionPlot.tsx b/webapp/src/main/GlycanDetail/GlycanDetailTorsionPlot.tsx index a632d942..e2f96813 100644 --- a/webapp/src/main/GlycanDetail/GlycanDetailTorsionPlot.tsx +++ b/webapp/src/main/GlycanDetail/GlycanDetailTorsionPlot.tsx @@ -6,7 +6,7 @@ const TorsionMultiPlot = lazy( export function GlycanDetailTorsionPlot(props: { key: string; - tableDataEntries: ResultsEntry []; + tableDataEntries: ResultsEntry[]; rowID: number; tab: number; tab1: (value: ((prevState: number) => number) | number) => void; diff --git a/webapp/src/shared/TorsionPlot/TorsionPlot.tsx b/webapp/src/shared/TorsionPlot/TorsionPlot.tsx index 7a2a907f..5040499f 100644 --- a/webapp/src/shared/TorsionPlot/TorsionPlot.tsx +++ b/webapp/src/shared/TorsionPlot/TorsionPlot.tsx @@ -19,7 +19,6 @@ export default function TorsionPlot({ const [linkageFound, setLinkageFound] = useState(false); useEffect(() => { - console.log(linkageDB[linkageType]); fetch(linkageDB[linkageType]) .then(async (response) => await response.json()) .then((responseJson) => { From de708c71a8ebec047295fb9c903b449812e1cdee Mon Sep 17 00:00:00 2001 From: Jordan Dialpuri <44945647+Dialpuri@users.noreply.github.com> Date: Thu, 7 Mar 2024 17:49:01 +0000 Subject: [PATCH 04/25] Added New Database Seaching Customized scrollbar appearance and replaced DatabaseFetch import with DatabaseInput in several components. Added a new implementation for the DatabaseSearchTable and modified related dependencies. Also introduced changes for handling 'NaN' values in TorsionPlot component. --- webapp/.eslintrc.json | 2 + webapp/package.json | 1 + .../database/DatabaseInput/DatabaseInput.tsx | 47 ++ .../DatabaseSearch/DatabaseSearch.tsx | 714 ++++++++++++++++++ .../DatabaseSearchTable.tsx | 338 +++++++++ webapp/src/database/Header/DatabaseHeader.tsx | 6 +- webapp/src/index.css | 21 + webapp/src/routes/Database/Database.tsx | 2 +- webapp/src/shared/TorsionPlot/TorsionPlot.tsx | 5 +- webapp/src/utils/loadGlytoucan.ts | 15 +- 10 files changed, 1141 insertions(+), 10 deletions(-) create mode 100644 webapp/src/database/DatabaseInput/DatabaseInput.tsx create mode 100644 webapp/src/database/DatabaseSearch/DatabaseSearch.tsx create mode 100644 webapp/src/database/DatabaseSearchTable/DatabaseSearchTable.tsx diff --git a/webapp/.eslintrc.json b/webapp/.eslintrc.json index 248d5ca8..9ee4c7b2 100644 --- a/webapp/.eslintrc.json +++ b/webapp/.eslintrc.json @@ -16,6 +16,8 @@ "@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/ban-ts-comment": "off", "no-tabs": "off", + "@typescript-eslint/strict-boolean-expressions": "warn", + "no-prototype-builtins": "warn", "@typescript-eslint/no-unused-vars": [ "warn", // or "error" { diff --git a/webapp/package.json b/webapp/package.json index e5ca028f..fb9d4bb2 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -13,6 +13,7 @@ "testserver": "vite --port 5173" }, "dependencies": { + "@tanstack/react-table": "^8.13.2", "@types/jest": "^29.5.6", "@types/node": "^20.8.9", "jest": "^29.7.0", diff --git a/webapp/src/database/DatabaseInput/DatabaseInput.tsx b/webapp/src/database/DatabaseInput/DatabaseInput.tsx new file mode 100644 index 00000000..d8b0f3dd --- /dev/null +++ b/webapp/src/database/DatabaseInput/DatabaseInput.tsx @@ -0,0 +1,47 @@ +import React, { useState } from 'react'; +import DatabaseFetch from '../DatabaseFetch/DatabaseFetch'; +import UploadButton from '../../shared/Upload/UploadButton.tsx'; +import Submit from '../../shared/Submit/Submit.tsx'; +import PDBFetch from '../../shared/PDBFetch/PDBFetch.tsx'; +import DatabaseSearch from '../DatabaseSearch/DatabaseSearch.tsx'; + +export default function DatabaseInput(props: { + PDBCode: string; + setPDBCode: any; + submitPressed: boolean; +}) { + const [showSearchBegin, setSearchBegin] = useState(false); + + return ( + <> +
    + { + + } + + {!showSearchBegin ? ( +
    + OR +
    + ) : ( + <> + )} + {!showSearchBegin ? ( + + ) : ( + <> + )} +
    + + ); +} diff --git a/webapp/src/database/DatabaseSearch/DatabaseSearch.tsx b/webapp/src/database/DatabaseSearch/DatabaseSearch.tsx new file mode 100644 index 00000000..903f60eb --- /dev/null +++ b/webapp/src/database/DatabaseSearch/DatabaseSearch.tsx @@ -0,0 +1,714 @@ +import React, { + type Dispatch, + type SetStateAction, + useEffect, + useState, +} from 'react'; +import DatabaseSearchTable from '../DatabaseSearchTable/DatabaseSearchTable.tsx'; +import Loading from '../../shared/Loading/Loading.tsx'; + +function ViewAllEntriesButton(props: { + text: string; + label: any; + onClickMethod: any; + secondLabel: string | undefined; + onSecondClick: any; +}) { + return ( +
    + + + +

    + {props.text} +

    +
    + + {props.secondLabel !== undefined ? ( + + ) : ( + <> + )} +
    +
    + ); +} + +function TypeFilterBox(props: { selected: string; onClickMethod: any }) { + const sugars = ['N-glycans', 'O-glycans', 'S-glycans', 'C-glycans']; + return ( +
    + + + + +

    + Filter Sugars +

    + +
    + {sugars.map((item, index) => { + return ( + + ); + })} +
    +
    + ); +} + +function LinkageFilterBox(props: { + selected: string; + sugars: string[]; + onClickMethod: Dispatch>; +}) { + return ( +
    + + + + +

    + Filter Linkages +

    + +
    + {props.sugars.map((item, index) => { + return ( + + ); + })} +
    +
    + ); +} + +function FilterZone(props: { setSearchBegin: any }) { + const [search, setSearch] = useState(false); + const [linkage, setLinkage] = useState('NAG-1,2-ASN'); + const [type, setType] = useState('N-glycans'); + const [text, setText] = useState(''); + const sugars = { + 'N-glycans': [ + 'NAG-1,2-ASN', + 'NAG-1,4-NAG', + 'BMA-1,4-NAG', + 'MAN-1,3-BMA', + 'MAN-1,6-BMA', + 'FUC-1,6-NAG', + 'MAN-1,3-MAN', + 'MAN-1,6-MAN', + 'MAN-1,2-MAN', + 'MAN-1,4-NAG', + 'NAG-1,2-MAN', + 'FUC-1,3-NAG', + 'BMA-1,3-BMA', + 'GAL-1,4-NAG', + 'FUL-1,6-NAG', + 'BMA-1,6-BMA', + 'NDG-1,4-NAG', + 'NAG-1,1-ASN', + 'XYP-1,2-BMA', + 'NAG-1,3-NAG', + 'MAN-1,4-BMA', + 'BMA-1,6-MAN', + 'MAN-1,4-MAN', + 'BMA-1,3-NAG', + 'FUL-1,3-NAG', + 'BMA-1,4-NDG', + 'BMA-1,4-MAN', + 'BMA-1,3-MAN', + 'NAG-1,6-NAG', + 'BMA-1,4-BMA', + 'NAG-1,4-MAN', + 'MAN-1,3-NAG', + 'MAN-1,4-NDG', + 'SIA-2,6-GAL', + 'MAN-1,6-NAG', + 'BMA-1,2-MAN', + 'NDG-1,2-ASN', + 'NAG-1,2-BMA', + 'MAN-1,2-BMA', + 'NAG-1,4-BMA', + 'FCA-1,3-NAG', + 'NAG-1,2-ARG', + 'GLC-1,3-MAN', + 'NAG-1,2-LYS', + 'NAG-1,4-NDG', + 'NDG-1,2-MAN', + 'NAG-1,6-MAN', + 'MAN-2,6-BMA', + 'NAG-3,4-NAG', + 'GAL-1,4-NDG', + 'FUC-1,4-NAG', + 'FCA-1,6-NAG', + 'NAG-5,4-NAG', + 'BMA-1,6-NAG', + 'BMA-1,2-BMA', + 'MAN-1,2-ASN', + 'BGC-1,2-ASN', + 'XYS-1,2-BMA', + 'SIA-2,3-GAL', + 'NAG-1,3-MAN', + 'NAG-4,4-NAG', + 'GLA-1,4-NAG', + 'GAL-1,4-MAN', + 'NDG-1,4-NDG', + 'NDG-1,2-BMA', + 'HSQ-1,2-ASN', + 'BMA-1,2-ASN', + 'MAN-2,3-BMA', + 'NAG-1,G-ASN', + 'MAN-1,3-XXR', + 'KDO-2,1-ASN', + 'RM4-1,4-XYP', + 'GLC-1,4-GLC', + 'FUL-1,6-NDG', + 'NAG-1,3-BMA', + 'M6D-1,2-MAN', + 'MAN-3,4-NAG', + 'NDG-1,1-ASN', + 'GLA-1,2-FUC', + 'MAN-3,3-MAN', + 'NAG-8,6-NAG', + 'B6D-1,2-ASN', + 'TGY-2,6-NAG', + 'NAG-1,B-ASN', + 'GAL-1,4-FUC', + 'XYP-1,4-FUC', + 'GLC-1,4-NAG', + 'XYP-1,4-BGC', + 'FUC-5,6-NAG', + 'NAG-1,1-MAN', + 'XXR-1,3-FUC', + 'FUC-1,3-BGC', + 'XYS-1,2-MAN', + 'NAG-1,Z-LYS', + 'NGK-1,4-NAG', + 'NAG-7,8-NAG', + 'FUC-1,2-GAL', + 'FRU-2,2-LYS', + 'NAG-4,1-NAG', + 'BMA-4,4-NAG', + '7CV-1,2-RM4', + 'XYL-1,2-BMA', + 'BGC-1,4-NAG', + 'NAG-1,4-ARG', + 'BMA-1,3-SHD', + 'GMH-1,5-KDO', + 'XYP-1,2-MAN', + 'GL0-1,4-NGA', + 'MAN-3,3-BMA', + 'NGZ-1,3-B6D', + 'NAG-A,A-ASN', + 'GLA-1,6-BMA', + 'MAN-1,3-XYS', + 'GUP-1,4-NAG', + 'MAN-1,3-GLC', + 'NAG-1,4-GAL', + 'BMA-1,6-SHD', + 'NAA-1,3-NAG', + 'MAN-1,1-ASN', + 'BMA-5,4-NAG', + 'G6D-1,6-BGC', + 'FUC-1,6-BMA', + 'NDG-1,4-MAN', + 'Z9N-1,6-NAG', + 'GLC-1,2-ASN', + 'NAA-1,2-ASN', + 'MAN-1,1-ARG', + 'GUP-1,3-BMA', + 'BMA-1,6-GLC', + 'BMA-1,3-NDG', + 'MAN-3,6-BMA', + 'MAN-1,3-ARG', + 'GCS-1,1-ARG', + 'MAN-1,4-ARG', + 'LXZ-1,2-ASN', + 'MAN-2,3-NAG', + 'JHM-1,4-IDS', + 'NGZ-1,4-LXB', + 'NAG-6,6-NAG', + 'SHD-1,4-NAG', + 'GLC-1,3-GLC', + 'BGC-1,3-A2G', + 'GXL-1,6-LXB', + 'RIB-1,1-ARG', + 'NAG-2,4-NAG', + 'NDG-1,6-BMA', + 'BGC-1,6-BGC', + 'BMA-5,6-MAN', + 'NAG-3,3-NAG', + 'MAN-1,3-GAL', + 'MAN-1,6-GUP', + 'SGN-1,4-IDS', + 'GLC-1,6-GL0', + 'JHM-1,1-LYS', + 'BGC-1,2-BGC', + 'FUC-1,6-NDG', + 'GLC-1,4-NDG', + 'XYS-1,6-BMA', + 'NAG-1,1-ARG', + 'BGC-1,3-LXB', + 'IDS-1,4-JHM', + 'IDS-4,1-SGN', + 'XYZ-1,6-MAN', + 'GAL-1,3-GAL', + 'RIP-1,6-NAG', + 'BGC-1,3-LXZ', + 'MAN-1,3-LGU', + 'BGC-1,3-BGC', + 'A2G-1,4-A2G', + 'XYS-1,3-BMA', + 'NGA-1,4-LXZ', + 'NAG-1,2-GUP', + 'GLC-4,1-GLC', + 'XYP-1,2-ASN', + 'LGU-1,4-NAG', + 'SIA-2,6-GLA', + 'MAN-4,6-MAN', + 'IDS-1,4-SGN', + 'MAN-1,4-BGC', + 'BGC-1,1-LYS', + 'NAG-2,3-NAG', + 'MAN-1,3-GUP', + 'BDF-2,2-LYS', + 'A2G-1,3-B6D', + 'GAL-1,2-ASN', + 'GAL-1,3-MAN', + 'KDO-2,6-GCS', + 'LGU-1,6-LGU', + 'GUP-1,2-BMA', + 'NAG-4,3-MAN', + 'MAN-4,2-MAN', + 'GAL-1,6-NAG', + 'NAG-7,7-NAG', + 'FUC-4,3-NAG', + 'GLA-1,3-GAL', + 'SGN-4,1-IDS', + 'GLC-1,4-ASN', + 'MAN-1,Z-LYS', + 'GLC-1,6-LXZ', + 'NAG-1,4-HSQ', + 'BMA-6,3-BMA', + 'BMA-2,4-NAG', + 'SGN-1,4-ARG', + 'MAN-1,3-NDG', + 'MAN-1,3-ASN', + 'GL0-1,4-NGZ', + 'BGC-1,2-BMA', + 'BGC-1,3-GL0', + 'IDS-1,4-ARG', + 'BGS-1,1-LYS', + 'NDG-8,3-NDG', + 'LXB-1,2-ASN', + 'BGC-1,4-BGC', + 'GUP-1,2-MAN', + 'BGC-1,3-BMA', + 'KDO-2,4-KDO', + 'BMA-1,6-ARG', + ], + 'C-glycans': [ + 'MAN-1,1-TRP', + 'BMA-1,1-TRP', + 'GAL-1,1-TRP', + 'NAG-1,4-TRP', + 'BGC-1,1-TRP', + 'NAG-1,1-TRP', + 'NAG-1,2-TRP', + 'NAG-4,1-NAG', + ], + 'O-glycans': [ + 'MAN-1,G-SER', + 'MAN-1,1-THR', + 'A2G-1,1-THR', + 'BGC-1,G-SER', + 'FUC-1,1-THR', + 'NAG-1,G-SER', + 'FUC-1,G-SER', + 'NAG-1,1-THR', + 'A2G-1,G-SER', + 'GAL-1,3-A2G', + 'BGC-1,3-FUC', + 'NGA-1,1-THR', + 'GLC-1,G-SER', + 'RAM-1,4-MAN', + 'G2F-1,2-GLU', + 'NAG-1,2-GLU', + 'BGC-1,4-BGC', + 'MAN-1,2-MAN', + 'G2F-1,1-GLU', + 'GCU-1,2-MAN', + 'NAG-1,2-ASP', + 'XYS-1,3-BGC', + 'NGA-1,G-SER', + 'XYP-1,4-GCU', + 'BGC-1,4-G2F', + 'BGC-1,2-ASP', + 'NDG-1,1-THR', + 'RIP-1,G-SER', + 'BMA-1,4-NAG', + 'GLC-1,4-GLC', + 'NAG-1,2-THR', + 'SIA-2,6-A2G', + 'B9D-1,1-ASP', + 'NAG-1,2-SER', + 'XYS-1,3-XYS', + 'MXY-1,4-XYP', + 'NAG-1,2-TYR', + 'BMA-1,G-SER', + 'GAL-1,3-NGA', + 'NAG-1,4-NAG', + 'SIA-2,3-GAL', + 'MAN-A,A-SER', + 'DFX-1,2-GLU', + 'BMA-1,1-THR', + '2DG-1,2-GLU', + 'BGC-1,3-BGC', + 'XYP-1,4-DFX', + 'BGC-1,H-TYR', + 'GLC-1,2-GLU', + 'XYP-1,4-X2F', + 'BGC-1,4-GLC', + 'GLC-1,1-GLU', + 'GLC-1,1-ASP', + 'GLC-1,4-BGC', + 'SIA-2,6-NDG', + 'X2F-1,2-GLU', + 'MAN-A,A-THR', + 'AC1-1,4-GLC', + 'NBG-1,1-GLU', + 'XYS-1,6-BGC', + 'GLA-1,3-DT6', + 'MAN-A,G-SER', + 'FUL-1,G-SER', + 'GLA-1,3-MXZ', + 'MXZ-1,4-XYP', + 'FUL-1,1-THR', + 'AC1-1,4-ASO', + 'NAG-1,4-ASP', + 'BGC-1,4-MXZ', + 'MAN-1,A-SER', + 'NAG-1,6-A2G', + '7JZ-1,2-ASP', + 'NAG-1,3-FUC', + '2FG-1,2-GLU', + 'XYS-1,G-SER', + 'ASO-1,2-ASP', + 'BGC-1,4-MXY', + 'GLC-1,2-ASP', + 'BGC-1,2-B9D', + 'MAN-A,1-THR', + 'GLC-4,1-GLC', + 'RIP-1,1-THR', + 'GAL-1,3-NDG', + 'EPG-1,1-GLU', + 'G4D-1,3-MXY', + 'GLA-1,3-NAG', + 'X2F-1,1-GLU', + 'XYS-1,1-THR', + 'BGC-1,4-NBG', + 'BGC-1,3-G2F', + 'DT6-1,G-SER', + 'G2F-1,1-ASP', + 'GLC-1,H-TYR', + 'XYS-1,2-GLU', + 'XYS-1,4-GCU', + 'NAG-1,H-TYR', + 'XYF-1,5-ASP', + 'NGA-1,3-NGA', + 'MFU-1,4-XYP', + 'GAL-1,1-THR', + 'XYP-1,3-BXF', + 'NAG-1,4-G2F', + 'GLC-1,4-THR', + 'XYP-1,3-XYP', + 'GAL-1,H-TYR', + 'GAF-1,2-GLU', + 'BDP-1,1-SER', + 'G2F-1,2-ASP', + 'GAL-1,4-GLC', + 'MAN-1,6-BMA', + 'XYP-2,2-XYS', + 'GLC-1,6-BGC', + 'GAL-1,3-NAG', + 'GLC-1,4-ASP', + '8B9-1,1-THR', + 'EBG-1,1-GLU', + 'GLC-1,4-SHG', + 'NGA-1,H-TYR', + 'NAG-1,2-MAN', + 'SHG-4,1-BGC', + 'FUC-1,3-NAG', + 'SHG-1,1-ASP', + 'BMA-1,4-GLU', + 'A2G-1,1-GLU', + 'MAN-1,1-GLU', + 'BGC-1,1-SER', + 'BMA-2,1-MAN', + 'BXF-1,1-GLU', + 'XYS-1,1-GLU', + 'RAM-1,1-THR', + 'RIB-1,2-GLU', + 'BGC-1,3-NBG', + 'XYS-1,4-XYS', + '3HD-1,1-THR', + 'NAG-1,4-MUB', + 'GLA-1,3-B6D', + 'XYP-2,1-GCV', + 'GLA-1,G-SER', + 'XYP-1,3-BGC', + 'AHR-1,1-GLU', + 'GCV-1,2-SER', + '289-1,G-SER', + 'MAN-1,4-MAN', + 'AMP-1,9-ASP', + 'B8D-1,4-BGC', + 'SIA-2,6-8B9', + 'NAG-1,4-GLU', + 'BGC-4,1-BGC', + 'NAG-1,6-NGA', + 'GCU-1,1-SER', + 'GXL-1,4-NDG', + 'C3X-1,1-GLU', + 'SIA-2,1-THR', + 'XYL-4,1-XYP', + 'BGC-1,3-GLC', + 'GAL-1,4-NAG', + 'GXL-1,2-GAL', + 'MAN-1,3-GLU', + 'MAN-1,2-ASP', + 'XYP-1,4-XYS', + 'C5X-1,1-GLU', + 'ARB-1,2-MAN', + 'NGA-1,1-SER', + 'SHG-1,G-SER', + 'G4D-1,4-GLC', + 'BGC-1,4-GLU', + 'NDG-1,6-A2G', + 'ARA-1,1-THR', + 'GLC-1,2-FRU', + 'BGC-1,1-THR', + 'NAG-1,3-A2G', + 'GAL-1,4-BGC', + 'DFX-1,1-GLU', + 'B6D-1,G-SER', + 'NDG-1,H-TYR', + 'FRU-2,2-GLU', + 'XYP-4,1-XYP', + 'GLC-1,1-THR', + 'GAL-1,1-GLU', + 'MAN-1,2-BMA', + 'MUB-1,2-GLU', + ], + 'S-glycans': [ + 'NAG-1,G-CYS', + 'KDO-2,G-CYS', + 'BGC-1,G-CYS', + 'MAN-1,G-CYS', + 'A2G-1,G-CYS', + 'MAN-1,2-MAN', + ], + Ligands: [ + 'NAG-1,2-ASN', + 'NAG-1,4-NAG', + 'NAG-1,4-BMA', + 'NAG-1,4-NAG', + 'NAG-1,4-NAG', + ], + }; + + useEffect(() => { + let textString = 'Find'; + + if (type === 'Any') { + textString += ' any carbohydrate'; + } else { + textString += ' ' + type; + } + textString += ' with '; + if (linkage === 'Any') { + textString += ' any linkage type'; + } else { + textString += ' ' + linkage + ' linkages'; + } + + setText(textString); + }, [linkage, type]); + + useEffect(() => { + if (!search) { + return; + } + + let url = + 'https://raw.githubusercontent.com/Dialpuri/PrivateerDatabase/master/linkages/'; + if (type === 'N-glycans') { + url += 'n-glycan/'; + } + if (type === 'O-glycans') { + url += 'o-glycan/'; + } + if (type === 'S-glycans') { + url += 's-glycan/'; + } + if (type === 'C-glycans') { + url += 'c-glycan/'; + } + if (type === 'Ligands') { + url += 'ligand/'; + } + url += linkage.replace(',', '%2C'); + url += '.json'; + + void fetch(url) + .then(async (response) => await response.json()) + .then((json) => { + const formattedData: Record = json.map( + (item) => { + return { + pdb: item.pdb, + count: item.count, + resolution: item.resolution, + type: 'n-glycan', + link: + 'https://privateer.york.ac.uk/database?pdb=' + + item.pdb, + }; + } + ); + setData(formattedData); + }) + .catch(() => {}); + }, [search]); + + const [data, setData] = useState | null>( + null + ); + + return !search ? ( + <> + {ViewAllEntriesButton({ + label: 'Back', + text, + onClickMethod: props.setSearchBegin, + secondLabel: 'Search', + onSecondClick: setSearch, + })} + {TypeFilterBox({ onClickMethod: setType, selected: type })} + {LinkageFilterBox({ + onClickMethod: setLinkage, + sugars: sugars[type], + selected: linkage, + })} + + ) : data === null ? ( + + ) : ( +
    + Query: {text} + + +
    + ); +} + +export default function DatabaseSearch(props: { + setSearchBegin: any; + searchBegin: boolean; +}) { + return !props.searchBegin + ? ViewAllEntriesButton({ + label: 'View', + text: ( + <> + View all entries in the Privateer Database + + ), + onClickMethod: props.setSearchBegin, + }) + : FilterZone({ setSearchBegin: props.setSearchBegin }); +} diff --git a/webapp/src/database/DatabaseSearchTable/DatabaseSearchTable.tsx b/webapp/src/database/DatabaseSearchTable/DatabaseSearchTable.tsx new file mode 100644 index 00000000..662cc194 --- /dev/null +++ b/webapp/src/database/DatabaseSearchTable/DatabaseSearchTable.tsx @@ -0,0 +1,338 @@ +import React, { useState, useEffect, useMemo } from 'react'; +import { + type PaginationState, + useReactTable, + getCoreRowModel, + getFilteredRowModel, + getPaginationRowModel, + flexRender, + getSortedRowModel, + type Column, +} from '@tanstack/react-table'; +import styled from 'styled-components'; + +const Styles = styled.div` + table { + border-collapse: collapse; + border-spacing: 0; + width: 100%; + // border: 0px solid #ddd; + } + + table th { + text-align: left; + padding: 16px; + // border: 1px solid #ddd; + } + + table td { + text-align: center; + padding: 16px; + border: 1px solid #ddd; + border-style: solid none; + } + + table td { + border-style: none none; + } + + table tr:nth-child(even) { + background-color: #f6f6f6; + } + + table tr:nth-child(even) { + background-color: #f4f9ff; + // color: #000000 + } + + table th { + padding-top: 12px; + padding-bottom: 12px; + text-align: center; + background-color: #f4f9ff; + color: black; + } + + table th:first-of-type { + border-top-left-radius: 30px; + } + + table th:last-of-type { + border-top-right-radius: 30px; + } + + table tr:last-of-type td:first-of-type { + border-bottom-left-radius: 30px; + } + + table tr:last-of-type td:last-of-type { + border-bottom-right-radius: 30px; + } + + #row:hover { + scale: 101%; + cursor: grab; + } +`; + +function Filter({ column, table }: { column: Column; table: any }) { + const firstValue = table + .getPreFilteredRowModel() + .flatRows[0]?.getValue(column.id); + + const columnFilterValue = column.getFilterValue(); + + return typeof firstValue === 'number' ? ( +
    + { + column.setFilterValue((old: [number, number]) => [ + e.target.value, + old?.[1], + ]); + }} + placeholder={' Min'} + className="w-24 border shadow rounded" + /> + { + column.setFilterValue((old: [number, number]) => [ + old?.[0], + e.target.value, + ]); + }} + placeholder={' Max'} + className="w-24 border shadow rounded" + /> +
    + ) : ( + { + column.setFilterValue(e.target.value); + }} + placeholder={'Search...'} + className="w-36 border shadow rounded" + /> + ); +} +function Table({ data }: { data: any }) { + const COLUMNS = [ + { + header: 'Type', + accessorKey: 'type', + }, + { + header: 'PDB', + accessorKey: 'pdb', + }, + { + header: 'Count', + accessorKey: 'count', + }, + { + header: 'Resolution', + accessorKey: 'resolution', + }, + { + header: 'Link', + accessorKey: 'link', + enableColumnFilter: false, + cell: (props: { getValue: () => string }) => { + return ( + + + + + + ); + }, + }, + ]; + + useEffect(() => { + console.warn(data); + }, []); + + const columns = useMemo(() => COLUMNS, []); + const [pagination, setPagination] = useState({ + pageIndex: 0, + pageSize: 10, + }); + const table = useReactTable({ + columns, + data, + debugTable: true, + getCoreRowModel: getCoreRowModel(), + getSortedRowModel: getSortedRowModel(), + getFilteredRowModel: getFilteredRowModel(), + getPaginationRowModel: getPaginationRowModel(), + onPaginationChange: setPagination, + state: { pagination }, + }); + + return ( + <> + + + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + ); + })} + + ))} + + + {table.getRowModel().rows.map((row) => { + return ( + + {row.getVisibleCells().map((cell) => { + return ( + + ); + })} + + ); + })} + +
    +
    + {flexRender( + header.column.columnDef + .header, + header.getContext() + )} + {{ + asc: ' ↑', + desc: ' ↓', + }[ + header.column.getIsSorted() as string + ] ?? null} + {header.column.getCanFilter() ? ( +
    + +
    + ) : null} +
    +
    + {flexRender( + cell.column.columnDef.cell, + cell.getContext() + )} +
    +
    +
    + + + + + +
    Page
    + + {table.getState().pagination.pageIndex + 1} of{' '} + {table.getPageCount().toLocaleString()} + +
    + + | Go to page: + { + const page = e.target.value + ? Number(e.target.value) - 1 + : 0; + table.setPageIndex(page); + }} + className="border p-1 rounded w-16" + /> + + +
    +
    + Showing {table.getRowModel().rows.length.toLocaleString()}{' '} + of {table.getRowCount().toLocaleString()} Rows +
    + + + ); +} + +export default function DatabaseSearchTable(props: { data: any }) { + return ; +} diff --git a/webapp/src/database/Header/DatabaseHeader.tsx b/webapp/src/database/Header/DatabaseHeader.tsx index 6e33a376..b8bd5e42 100644 --- a/webapp/src/database/Header/DatabaseHeader.tsx +++ b/webapp/src/database/Header/DatabaseHeader.tsx @@ -7,8 +7,8 @@ const NavBar = lazy(async () => await import('../../layouts/NavBar.tsx')); const NoGlycans = lazy( async () => await import('../../shared/NoGlycans/NoGlycans.tsx') ); -const DatabaseFetch = lazy( - async () => await import('../DatabaseFetch/DatabaseFetch.jsx') +const DatabaseInput = lazy( + async () => await import('../DatabaseInput/DatabaseInput.jsx') ); const DatabaseResult = lazy( async () => await import('../DatabaseResult/DatabaseResult.tsx') @@ -26,7 +26,7 @@ export function DatabaseHeader(props: DatabaseHeaderProps): ReactElement { } > {props.pdbResults === '' ? ( - { diff --git a/webapp/src/shared/TorsionPlot/TorsionPlot.tsx b/webapp/src/shared/TorsionPlot/TorsionPlot.tsx index 5040499f..5ccd0a65 100644 --- a/webapp/src/shared/TorsionPlot/TorsionPlot.tsx +++ b/webapp/src/shared/TorsionPlot/TorsionPlot.tsx @@ -20,7 +20,10 @@ export default function TorsionPlot({ useEffect(() => { fetch(linkageDB[linkageType]) - .then(async (response) => await response.json()) + .then(async (response) => await response.text()) + .then((text) => { + return JSON.parse(text.replace(/\bNaN\b/g, 'null')); + }) .then((responseJson) => { const xData: number[] = []; const yData: number[] = []; diff --git a/webapp/src/utils/loadGlytoucan.ts b/webapp/src/utils/loadGlytoucan.ts index cc7b44c5..71574f1d 100644 --- a/webapp/src/utils/loadGlytoucan.ts +++ b/webapp/src/utils/loadGlytoucan.ts @@ -53,13 +53,18 @@ export async function loadGlytoucanFromFile( tableData.forEach((data, index) => { const glycomicsResult = glycomicsData[data.wurcs]; + if (!glycomicsResult) { + return; + } // Neaten up NotFound -> Not Found - if (glycomicsResult.GlyConnect === 'NotFound') { - glycomicsResult.GlyConnect = 'Not Found'; - } + if (glycomicsResult.hasOwnProperty('GlyConnect')) { + if (glycomicsResult.GlyConnect === 'NotFound') { + glycomicsResult.GlyConnect = 'Not Found'; + } - tableData[index].glytoucan_id = glycomicsResult.GlyToucan; - tableData[index].glyconnect_id = glycomicsResult.GlyConnect; + tableData[index].glytoucan_id = glycomicsResult.GlyToucan; + tableData[index].glyconnect_id = glycomicsResult.GlyConnect; + } }); } From 757fa2bc4071262c31e83cf9f38519d0e2315a83 Mon Sep 17 00:00:00 2001 From: Jordan Dialpuri <44945647+Dialpuri@users.noreply.github.com> Date: Thu, 7 Mar 2024 17:58:22 +0000 Subject: [PATCH 05/25] Add sugarLinkageMap to Constants and DatabaseSearch Expanded Constants.tsx to include definition of sugarLinkageMap that defines several sugar linkages. Implemented use of this new constant in DatabaseSearch.tsx, replacing the previous inline definition of sugars. --- webapp/src/data/Constants.tsx | 430 +++++++++++++++++ .../DatabaseSearch/DatabaseSearch.tsx | 451 +----------------- 2 files changed, 448 insertions(+), 433 deletions(-) diff --git a/webapp/src/data/Constants.tsx b/webapp/src/data/Constants.tsx index de1d69ab..a63e876c 100644 --- a/webapp/src/data/Constants.tsx +++ b/webapp/src/data/Constants.tsx @@ -223,3 +223,433 @@ export const binDB = { 'MAN-1,3-MAN': { start: -180, end: 180, size: 4 }, 'MAN-1,2-MAN': { start: -180, end: 180, size: 4 }, }; + +export const sugarLinkageMap = { + 'N-glycans': [ + 'NAG-1,2-ASN', + 'NAG-1,4-NAG', + 'BMA-1,4-NAG', + 'MAN-1,3-BMA', + 'MAN-1,6-BMA', + 'FUC-1,6-NAG', + 'MAN-1,3-MAN', + 'MAN-1,6-MAN', + 'MAN-1,2-MAN', + 'MAN-1,4-NAG', + 'NAG-1,2-MAN', + 'FUC-1,3-NAG', + 'BMA-1,3-BMA', + 'GAL-1,4-NAG', + 'FUL-1,6-NAG', + 'BMA-1,6-BMA', + 'NDG-1,4-NAG', + 'NAG-1,1-ASN', + 'XYP-1,2-BMA', + 'NAG-1,3-NAG', + 'MAN-1,4-BMA', + 'BMA-1,6-MAN', + 'MAN-1,4-MAN', + 'BMA-1,3-NAG', + 'FUL-1,3-NAG', + 'BMA-1,4-NDG', + 'BMA-1,4-MAN', + 'BMA-1,3-MAN', + 'NAG-1,6-NAG', + 'BMA-1,4-BMA', + 'NAG-1,4-MAN', + 'MAN-1,3-NAG', + 'MAN-1,4-NDG', + 'SIA-2,6-GAL', + 'MAN-1,6-NAG', + 'BMA-1,2-MAN', + 'NDG-1,2-ASN', + 'NAG-1,2-BMA', + 'MAN-1,2-BMA', + 'NAG-1,4-BMA', + 'FCA-1,3-NAG', + 'NAG-1,2-ARG', + 'GLC-1,3-MAN', + 'NAG-1,2-LYS', + 'NAG-1,4-NDG', + 'NDG-1,2-MAN', + 'NAG-1,6-MAN', + 'MAN-2,6-BMA', + 'NAG-3,4-NAG', + 'GAL-1,4-NDG', + 'FUC-1,4-NAG', + 'FCA-1,6-NAG', + 'NAG-5,4-NAG', + 'BMA-1,6-NAG', + 'BMA-1,2-BMA', + 'MAN-1,2-ASN', + 'BGC-1,2-ASN', + 'XYS-1,2-BMA', + 'SIA-2,3-GAL', + 'NAG-1,3-MAN', + 'NAG-4,4-NAG', + 'GLA-1,4-NAG', + 'GAL-1,4-MAN', + 'NDG-1,4-NDG', + 'NDG-1,2-BMA', + 'HSQ-1,2-ASN', + 'BMA-1,2-ASN', + 'MAN-2,3-BMA', + 'NAG-1,G-ASN', + 'MAN-1,3-XXR', + 'KDO-2,1-ASN', + 'RM4-1,4-XYP', + 'GLC-1,4-GLC', + 'FUL-1,6-NDG', + 'NAG-1,3-BMA', + 'M6D-1,2-MAN', + 'MAN-3,4-NAG', + 'NDG-1,1-ASN', + 'GLA-1,2-FUC', + 'MAN-3,3-MAN', + 'NAG-8,6-NAG', + 'B6D-1,2-ASN', + 'TGY-2,6-NAG', + 'NAG-1,B-ASN', + 'GAL-1,4-FUC', + 'XYP-1,4-FUC', + 'GLC-1,4-NAG', + 'XYP-1,4-BGC', + 'FUC-5,6-NAG', + 'NAG-1,1-MAN', + 'XXR-1,3-FUC', + 'FUC-1,3-BGC', + 'XYS-1,2-MAN', + 'NAG-1,Z-LYS', + 'NGK-1,4-NAG', + 'NAG-7,8-NAG', + 'FUC-1,2-GAL', + 'FRU-2,2-LYS', + 'NAG-4,1-NAG', + 'BMA-4,4-NAG', + '7CV-1,2-RM4', + 'XYL-1,2-BMA', + 'BGC-1,4-NAG', + 'NAG-1,4-ARG', + 'BMA-1,3-SHD', + 'GMH-1,5-KDO', + 'XYP-1,2-MAN', + 'GL0-1,4-NGA', + 'MAN-3,3-BMA', + 'NGZ-1,3-B6D', + 'NAG-A,A-ASN', + 'GLA-1,6-BMA', + 'MAN-1,3-XYS', + 'GUP-1,4-NAG', + 'MAN-1,3-GLC', + 'NAG-1,4-GAL', + 'BMA-1,6-SHD', + 'NAA-1,3-NAG', + 'MAN-1,1-ASN', + 'BMA-5,4-NAG', + 'G6D-1,6-BGC', + 'FUC-1,6-BMA', + 'NDG-1,4-MAN', + 'Z9N-1,6-NAG', + 'GLC-1,2-ASN', + 'NAA-1,2-ASN', + 'MAN-1,1-ARG', + 'GUP-1,3-BMA', + 'BMA-1,6-GLC', + 'BMA-1,3-NDG', + 'MAN-3,6-BMA', + 'MAN-1,3-ARG', + 'GCS-1,1-ARG', + 'MAN-1,4-ARG', + 'LXZ-1,2-ASN', + 'MAN-2,3-NAG', + 'JHM-1,4-IDS', + 'NGZ-1,4-LXB', + 'NAG-6,6-NAG', + 'SHD-1,4-NAG', + 'GLC-1,3-GLC', + 'BGC-1,3-A2G', + 'GXL-1,6-LXB', + 'RIB-1,1-ARG', + 'NAG-2,4-NAG', + 'NDG-1,6-BMA', + 'BGC-1,6-BGC', + 'BMA-5,6-MAN', + 'NAG-3,3-NAG', + 'MAN-1,3-GAL', + 'MAN-1,6-GUP', + 'SGN-1,4-IDS', + 'GLC-1,6-GL0', + 'JHM-1,1-LYS', + 'BGC-1,2-BGC', + 'FUC-1,6-NDG', + 'GLC-1,4-NDG', + 'XYS-1,6-BMA', + 'NAG-1,1-ARG', + 'BGC-1,3-LXB', + 'IDS-1,4-JHM', + 'IDS-4,1-SGN', + 'XYZ-1,6-MAN', + 'GAL-1,3-GAL', + 'RIP-1,6-NAG', + 'BGC-1,3-LXZ', + 'MAN-1,3-LGU', + 'BGC-1,3-BGC', + 'A2G-1,4-A2G', + 'XYS-1,3-BMA', + 'NGA-1,4-LXZ', + 'NAG-1,2-GUP', + 'GLC-4,1-GLC', + 'XYP-1,2-ASN', + 'LGU-1,4-NAG', + 'SIA-2,6-GLA', + 'MAN-4,6-MAN', + 'IDS-1,4-SGN', + 'MAN-1,4-BGC', + 'BGC-1,1-LYS', + 'NAG-2,3-NAG', + 'MAN-1,3-GUP', + 'BDF-2,2-LYS', + 'A2G-1,3-B6D', + 'GAL-1,2-ASN', + 'GAL-1,3-MAN', + 'KDO-2,6-GCS', + 'LGU-1,6-LGU', + 'GUP-1,2-BMA', + 'NAG-4,3-MAN', + 'MAN-4,2-MAN', + 'GAL-1,6-NAG', + 'NAG-7,7-NAG', + 'FUC-4,3-NAG', + 'GLA-1,3-GAL', + 'SGN-4,1-IDS', + 'GLC-1,4-ASN', + 'MAN-1,Z-LYS', + 'GLC-1,6-LXZ', + 'NAG-1,4-HSQ', + 'BMA-6,3-BMA', + 'BMA-2,4-NAG', + 'SGN-1,4-ARG', + 'MAN-1,3-NDG', + 'MAN-1,3-ASN', + 'GL0-1,4-NGZ', + 'BGC-1,2-BMA', + 'BGC-1,3-GL0', + 'IDS-1,4-ARG', + 'BGS-1,1-LYS', + 'NDG-8,3-NDG', + 'LXB-1,2-ASN', + 'BGC-1,4-BGC', + 'GUP-1,2-MAN', + 'BGC-1,3-BMA', + 'KDO-2,4-KDO', + 'BMA-1,6-ARG', + ], + 'C-glycans': [ + 'MAN-1,1-TRP', + 'BMA-1,1-TRP', + 'GAL-1,1-TRP', + 'NAG-1,4-TRP', + 'BGC-1,1-TRP', + 'NAG-1,1-TRP', + 'NAG-1,2-TRP', + 'NAG-4,1-NAG', + ], + 'O-glycans': [ + 'MAN-1,G-SER', + 'MAN-1,1-THR', + 'A2G-1,1-THR', + 'BGC-1,G-SER', + 'FUC-1,1-THR', + 'NAG-1,G-SER', + 'FUC-1,G-SER', + 'NAG-1,1-THR', + 'A2G-1,G-SER', + 'GAL-1,3-A2G', + 'BGC-1,3-FUC', + 'NGA-1,1-THR', + 'GLC-1,G-SER', + 'RAM-1,4-MAN', + 'G2F-1,2-GLU', + 'NAG-1,2-GLU', + 'BGC-1,4-BGC', + 'MAN-1,2-MAN', + 'G2F-1,1-GLU', + 'GCU-1,2-MAN', + 'NAG-1,2-ASP', + 'XYS-1,3-BGC', + 'NGA-1,G-SER', + 'XYP-1,4-GCU', + 'BGC-1,4-G2F', + 'BGC-1,2-ASP', + 'NDG-1,1-THR', + 'RIP-1,G-SER', + 'BMA-1,4-NAG', + 'GLC-1,4-GLC', + 'NAG-1,2-THR', + 'SIA-2,6-A2G', + 'B9D-1,1-ASP', + 'NAG-1,2-SER', + 'XYS-1,3-XYS', + 'MXY-1,4-XYP', + 'NAG-1,2-TYR', + 'BMA-1,G-SER', + 'GAL-1,3-NGA', + 'NAG-1,4-NAG', + 'SIA-2,3-GAL', + 'MAN-A,A-SER', + 'DFX-1,2-GLU', + 'BMA-1,1-THR', + '2DG-1,2-GLU', + 'BGC-1,3-BGC', + 'XYP-1,4-DFX', + 'BGC-1,H-TYR', + 'GLC-1,2-GLU', + 'XYP-1,4-X2F', + 'BGC-1,4-GLC', + 'GLC-1,1-GLU', + 'GLC-1,1-ASP', + 'GLC-1,4-BGC', + 'SIA-2,6-NDG', + 'X2F-1,2-GLU', + 'MAN-A,A-THR', + 'AC1-1,4-GLC', + 'NBG-1,1-GLU', + 'XYS-1,6-BGC', + 'GLA-1,3-DT6', + 'MAN-A,G-SER', + 'FUL-1,G-SER', + 'GLA-1,3-MXZ', + 'MXZ-1,4-XYP', + 'FUL-1,1-THR', + 'AC1-1,4-ASO', + 'NAG-1,4-ASP', + 'BGC-1,4-MXZ', + 'MAN-1,A-SER', + 'NAG-1,6-A2G', + '7JZ-1,2-ASP', + 'NAG-1,3-FUC', + '2FG-1,2-GLU', + 'XYS-1,G-SER', + 'ASO-1,2-ASP', + 'BGC-1,4-MXY', + 'GLC-1,2-ASP', + 'BGC-1,2-B9D', + 'MAN-A,1-THR', + 'GLC-4,1-GLC', + 'RIP-1,1-THR', + 'GAL-1,3-NDG', + 'EPG-1,1-GLU', + 'G4D-1,3-MXY', + 'GLA-1,3-NAG', + 'X2F-1,1-GLU', + 'XYS-1,1-THR', + 'BGC-1,4-NBG', + 'BGC-1,3-G2F', + 'DT6-1,G-SER', + 'G2F-1,1-ASP', + 'GLC-1,H-TYR', + 'XYS-1,2-GLU', + 'XYS-1,4-GCU', + 'NAG-1,H-TYR', + 'XYF-1,5-ASP', + 'NGA-1,3-NGA', + 'MFU-1,4-XYP', + 'GAL-1,1-THR', + 'XYP-1,3-BXF', + 'NAG-1,4-G2F', + 'GLC-1,4-THR', + 'XYP-1,3-XYP', + 'GAL-1,H-TYR', + 'GAF-1,2-GLU', + 'BDP-1,1-SER', + 'G2F-1,2-ASP', + 'GAL-1,4-GLC', + 'MAN-1,6-BMA', + 'XYP-2,2-XYS', + 'GLC-1,6-BGC', + 'GAL-1,3-NAG', + 'GLC-1,4-ASP', + '8B9-1,1-THR', + 'EBG-1,1-GLU', + 'GLC-1,4-SHG', + 'NGA-1,H-TYR', + 'NAG-1,2-MAN', + 'SHG-4,1-BGC', + 'FUC-1,3-NAG', + 'SHG-1,1-ASP', + 'BMA-1,4-GLU', + 'A2G-1,1-GLU', + 'MAN-1,1-GLU', + 'BGC-1,1-SER', + 'BMA-2,1-MAN', + 'BXF-1,1-GLU', + 'XYS-1,1-GLU', + 'RAM-1,1-THR', + 'RIB-1,2-GLU', + 'BGC-1,3-NBG', + 'XYS-1,4-XYS', + '3HD-1,1-THR', + 'NAG-1,4-MUB', + 'GLA-1,3-B6D', + 'XYP-2,1-GCV', + 'GLA-1,G-SER', + 'XYP-1,3-BGC', + 'AHR-1,1-GLU', + 'GCV-1,2-SER', + '289-1,G-SER', + 'MAN-1,4-MAN', + 'AMP-1,9-ASP', + 'B8D-1,4-BGC', + 'SIA-2,6-8B9', + 'NAG-1,4-GLU', + 'BGC-4,1-BGC', + 'NAG-1,6-NGA', + 'GCU-1,1-SER', + 'GXL-1,4-NDG', + 'C3X-1,1-GLU', + 'SIA-2,1-THR', + 'XYL-4,1-XYP', + 'BGC-1,3-GLC', + 'GAL-1,4-NAG', + 'GXL-1,2-GAL', + 'MAN-1,3-GLU', + 'MAN-1,2-ASP', + 'XYP-1,4-XYS', + 'C5X-1,1-GLU', + 'ARB-1,2-MAN', + 'NGA-1,1-SER', + 'SHG-1,G-SER', + 'G4D-1,4-GLC', + 'BGC-1,4-GLU', + 'NDG-1,6-A2G', + 'ARA-1,1-THR', + 'GLC-1,2-FRU', + 'BGC-1,1-THR', + 'NAG-1,3-A2G', + 'GAL-1,4-BGC', + 'DFX-1,1-GLU', + 'B6D-1,G-SER', + 'NDG-1,H-TYR', + 'FRU-2,2-GLU', + 'XYP-4,1-XYP', + 'GLC-1,1-THR', + 'GAL-1,1-GLU', + 'MAN-1,2-BMA', + 'MUB-1,2-GLU', + ], + 'S-glycans': [ + 'NAG-1,G-CYS', + 'KDO-2,G-CYS', + 'BGC-1,G-CYS', + 'MAN-1,G-CYS', + 'A2G-1,G-CYS', + 'MAN-1,2-MAN', + ], + 'Ligands': [ + 'NAG-1,2-ASN', + 'NAG-1,4-NAG', + 'NAG-1,4-BMA', + 'NAG-1,4-NAG', + 'NAG-1,4-NAG', + ], +}; \ No newline at end of file diff --git a/webapp/src/database/DatabaseSearch/DatabaseSearch.tsx b/webapp/src/database/DatabaseSearch/DatabaseSearch.tsx index 903f60eb..3e7b9f37 100644 --- a/webapp/src/database/DatabaseSearch/DatabaseSearch.tsx +++ b/webapp/src/database/DatabaseSearch/DatabaseSearch.tsx @@ -6,7 +6,7 @@ import React, { } from 'react'; import DatabaseSearchTable from '../DatabaseSearchTable/DatabaseSearchTable.tsx'; import Loading from '../../shared/Loading/Loading.tsx'; - +import {sugarLinkageMap} from "../../data/Constants.tsx" function ViewAllEntriesButton(props: { text: string; label: any; @@ -164,435 +164,11 @@ function FilterZone(props: { setSearchBegin: any }) { const [linkage, setLinkage] = useState('NAG-1,2-ASN'); const [type, setType] = useState('N-glycans'); const [text, setText] = useState(''); - const sugars = { - 'N-glycans': [ - 'NAG-1,2-ASN', - 'NAG-1,4-NAG', - 'BMA-1,4-NAG', - 'MAN-1,3-BMA', - 'MAN-1,6-BMA', - 'FUC-1,6-NAG', - 'MAN-1,3-MAN', - 'MAN-1,6-MAN', - 'MAN-1,2-MAN', - 'MAN-1,4-NAG', - 'NAG-1,2-MAN', - 'FUC-1,3-NAG', - 'BMA-1,3-BMA', - 'GAL-1,4-NAG', - 'FUL-1,6-NAG', - 'BMA-1,6-BMA', - 'NDG-1,4-NAG', - 'NAG-1,1-ASN', - 'XYP-1,2-BMA', - 'NAG-1,3-NAG', - 'MAN-1,4-BMA', - 'BMA-1,6-MAN', - 'MAN-1,4-MAN', - 'BMA-1,3-NAG', - 'FUL-1,3-NAG', - 'BMA-1,4-NDG', - 'BMA-1,4-MAN', - 'BMA-1,3-MAN', - 'NAG-1,6-NAG', - 'BMA-1,4-BMA', - 'NAG-1,4-MAN', - 'MAN-1,3-NAG', - 'MAN-1,4-NDG', - 'SIA-2,6-GAL', - 'MAN-1,6-NAG', - 'BMA-1,2-MAN', - 'NDG-1,2-ASN', - 'NAG-1,2-BMA', - 'MAN-1,2-BMA', - 'NAG-1,4-BMA', - 'FCA-1,3-NAG', - 'NAG-1,2-ARG', - 'GLC-1,3-MAN', - 'NAG-1,2-LYS', - 'NAG-1,4-NDG', - 'NDG-1,2-MAN', - 'NAG-1,6-MAN', - 'MAN-2,6-BMA', - 'NAG-3,4-NAG', - 'GAL-1,4-NDG', - 'FUC-1,4-NAG', - 'FCA-1,6-NAG', - 'NAG-5,4-NAG', - 'BMA-1,6-NAG', - 'BMA-1,2-BMA', - 'MAN-1,2-ASN', - 'BGC-1,2-ASN', - 'XYS-1,2-BMA', - 'SIA-2,3-GAL', - 'NAG-1,3-MAN', - 'NAG-4,4-NAG', - 'GLA-1,4-NAG', - 'GAL-1,4-MAN', - 'NDG-1,4-NDG', - 'NDG-1,2-BMA', - 'HSQ-1,2-ASN', - 'BMA-1,2-ASN', - 'MAN-2,3-BMA', - 'NAG-1,G-ASN', - 'MAN-1,3-XXR', - 'KDO-2,1-ASN', - 'RM4-1,4-XYP', - 'GLC-1,4-GLC', - 'FUL-1,6-NDG', - 'NAG-1,3-BMA', - 'M6D-1,2-MAN', - 'MAN-3,4-NAG', - 'NDG-1,1-ASN', - 'GLA-1,2-FUC', - 'MAN-3,3-MAN', - 'NAG-8,6-NAG', - 'B6D-1,2-ASN', - 'TGY-2,6-NAG', - 'NAG-1,B-ASN', - 'GAL-1,4-FUC', - 'XYP-1,4-FUC', - 'GLC-1,4-NAG', - 'XYP-1,4-BGC', - 'FUC-5,6-NAG', - 'NAG-1,1-MAN', - 'XXR-1,3-FUC', - 'FUC-1,3-BGC', - 'XYS-1,2-MAN', - 'NAG-1,Z-LYS', - 'NGK-1,4-NAG', - 'NAG-7,8-NAG', - 'FUC-1,2-GAL', - 'FRU-2,2-LYS', - 'NAG-4,1-NAG', - 'BMA-4,4-NAG', - '7CV-1,2-RM4', - 'XYL-1,2-BMA', - 'BGC-1,4-NAG', - 'NAG-1,4-ARG', - 'BMA-1,3-SHD', - 'GMH-1,5-KDO', - 'XYP-1,2-MAN', - 'GL0-1,4-NGA', - 'MAN-3,3-BMA', - 'NGZ-1,3-B6D', - 'NAG-A,A-ASN', - 'GLA-1,6-BMA', - 'MAN-1,3-XYS', - 'GUP-1,4-NAG', - 'MAN-1,3-GLC', - 'NAG-1,4-GAL', - 'BMA-1,6-SHD', - 'NAA-1,3-NAG', - 'MAN-1,1-ASN', - 'BMA-5,4-NAG', - 'G6D-1,6-BGC', - 'FUC-1,6-BMA', - 'NDG-1,4-MAN', - 'Z9N-1,6-NAG', - 'GLC-1,2-ASN', - 'NAA-1,2-ASN', - 'MAN-1,1-ARG', - 'GUP-1,3-BMA', - 'BMA-1,6-GLC', - 'BMA-1,3-NDG', - 'MAN-3,6-BMA', - 'MAN-1,3-ARG', - 'GCS-1,1-ARG', - 'MAN-1,4-ARG', - 'LXZ-1,2-ASN', - 'MAN-2,3-NAG', - 'JHM-1,4-IDS', - 'NGZ-1,4-LXB', - 'NAG-6,6-NAG', - 'SHD-1,4-NAG', - 'GLC-1,3-GLC', - 'BGC-1,3-A2G', - 'GXL-1,6-LXB', - 'RIB-1,1-ARG', - 'NAG-2,4-NAG', - 'NDG-1,6-BMA', - 'BGC-1,6-BGC', - 'BMA-5,6-MAN', - 'NAG-3,3-NAG', - 'MAN-1,3-GAL', - 'MAN-1,6-GUP', - 'SGN-1,4-IDS', - 'GLC-1,6-GL0', - 'JHM-1,1-LYS', - 'BGC-1,2-BGC', - 'FUC-1,6-NDG', - 'GLC-1,4-NDG', - 'XYS-1,6-BMA', - 'NAG-1,1-ARG', - 'BGC-1,3-LXB', - 'IDS-1,4-JHM', - 'IDS-4,1-SGN', - 'XYZ-1,6-MAN', - 'GAL-1,3-GAL', - 'RIP-1,6-NAG', - 'BGC-1,3-LXZ', - 'MAN-1,3-LGU', - 'BGC-1,3-BGC', - 'A2G-1,4-A2G', - 'XYS-1,3-BMA', - 'NGA-1,4-LXZ', - 'NAG-1,2-GUP', - 'GLC-4,1-GLC', - 'XYP-1,2-ASN', - 'LGU-1,4-NAG', - 'SIA-2,6-GLA', - 'MAN-4,6-MAN', - 'IDS-1,4-SGN', - 'MAN-1,4-BGC', - 'BGC-1,1-LYS', - 'NAG-2,3-NAG', - 'MAN-1,3-GUP', - 'BDF-2,2-LYS', - 'A2G-1,3-B6D', - 'GAL-1,2-ASN', - 'GAL-1,3-MAN', - 'KDO-2,6-GCS', - 'LGU-1,6-LGU', - 'GUP-1,2-BMA', - 'NAG-4,3-MAN', - 'MAN-4,2-MAN', - 'GAL-1,6-NAG', - 'NAG-7,7-NAG', - 'FUC-4,3-NAG', - 'GLA-1,3-GAL', - 'SGN-4,1-IDS', - 'GLC-1,4-ASN', - 'MAN-1,Z-LYS', - 'GLC-1,6-LXZ', - 'NAG-1,4-HSQ', - 'BMA-6,3-BMA', - 'BMA-2,4-NAG', - 'SGN-1,4-ARG', - 'MAN-1,3-NDG', - 'MAN-1,3-ASN', - 'GL0-1,4-NGZ', - 'BGC-1,2-BMA', - 'BGC-1,3-GL0', - 'IDS-1,4-ARG', - 'BGS-1,1-LYS', - 'NDG-8,3-NDG', - 'LXB-1,2-ASN', - 'BGC-1,4-BGC', - 'GUP-1,2-MAN', - 'BGC-1,3-BMA', - 'KDO-2,4-KDO', - 'BMA-1,6-ARG', - ], - 'C-glycans': [ - 'MAN-1,1-TRP', - 'BMA-1,1-TRP', - 'GAL-1,1-TRP', - 'NAG-1,4-TRP', - 'BGC-1,1-TRP', - 'NAG-1,1-TRP', - 'NAG-1,2-TRP', - 'NAG-4,1-NAG', - ], - 'O-glycans': [ - 'MAN-1,G-SER', - 'MAN-1,1-THR', - 'A2G-1,1-THR', - 'BGC-1,G-SER', - 'FUC-1,1-THR', - 'NAG-1,G-SER', - 'FUC-1,G-SER', - 'NAG-1,1-THR', - 'A2G-1,G-SER', - 'GAL-1,3-A2G', - 'BGC-1,3-FUC', - 'NGA-1,1-THR', - 'GLC-1,G-SER', - 'RAM-1,4-MAN', - 'G2F-1,2-GLU', - 'NAG-1,2-GLU', - 'BGC-1,4-BGC', - 'MAN-1,2-MAN', - 'G2F-1,1-GLU', - 'GCU-1,2-MAN', - 'NAG-1,2-ASP', - 'XYS-1,3-BGC', - 'NGA-1,G-SER', - 'XYP-1,4-GCU', - 'BGC-1,4-G2F', - 'BGC-1,2-ASP', - 'NDG-1,1-THR', - 'RIP-1,G-SER', - 'BMA-1,4-NAG', - 'GLC-1,4-GLC', - 'NAG-1,2-THR', - 'SIA-2,6-A2G', - 'B9D-1,1-ASP', - 'NAG-1,2-SER', - 'XYS-1,3-XYS', - 'MXY-1,4-XYP', - 'NAG-1,2-TYR', - 'BMA-1,G-SER', - 'GAL-1,3-NGA', - 'NAG-1,4-NAG', - 'SIA-2,3-GAL', - 'MAN-A,A-SER', - 'DFX-1,2-GLU', - 'BMA-1,1-THR', - '2DG-1,2-GLU', - 'BGC-1,3-BGC', - 'XYP-1,4-DFX', - 'BGC-1,H-TYR', - 'GLC-1,2-GLU', - 'XYP-1,4-X2F', - 'BGC-1,4-GLC', - 'GLC-1,1-GLU', - 'GLC-1,1-ASP', - 'GLC-1,4-BGC', - 'SIA-2,6-NDG', - 'X2F-1,2-GLU', - 'MAN-A,A-THR', - 'AC1-1,4-GLC', - 'NBG-1,1-GLU', - 'XYS-1,6-BGC', - 'GLA-1,3-DT6', - 'MAN-A,G-SER', - 'FUL-1,G-SER', - 'GLA-1,3-MXZ', - 'MXZ-1,4-XYP', - 'FUL-1,1-THR', - 'AC1-1,4-ASO', - 'NAG-1,4-ASP', - 'BGC-1,4-MXZ', - 'MAN-1,A-SER', - 'NAG-1,6-A2G', - '7JZ-1,2-ASP', - 'NAG-1,3-FUC', - '2FG-1,2-GLU', - 'XYS-1,G-SER', - 'ASO-1,2-ASP', - 'BGC-1,4-MXY', - 'GLC-1,2-ASP', - 'BGC-1,2-B9D', - 'MAN-A,1-THR', - 'GLC-4,1-GLC', - 'RIP-1,1-THR', - 'GAL-1,3-NDG', - 'EPG-1,1-GLU', - 'G4D-1,3-MXY', - 'GLA-1,3-NAG', - 'X2F-1,1-GLU', - 'XYS-1,1-THR', - 'BGC-1,4-NBG', - 'BGC-1,3-G2F', - 'DT6-1,G-SER', - 'G2F-1,1-ASP', - 'GLC-1,H-TYR', - 'XYS-1,2-GLU', - 'XYS-1,4-GCU', - 'NAG-1,H-TYR', - 'XYF-1,5-ASP', - 'NGA-1,3-NGA', - 'MFU-1,4-XYP', - 'GAL-1,1-THR', - 'XYP-1,3-BXF', - 'NAG-1,4-G2F', - 'GLC-1,4-THR', - 'XYP-1,3-XYP', - 'GAL-1,H-TYR', - 'GAF-1,2-GLU', - 'BDP-1,1-SER', - 'G2F-1,2-ASP', - 'GAL-1,4-GLC', - 'MAN-1,6-BMA', - 'XYP-2,2-XYS', - 'GLC-1,6-BGC', - 'GAL-1,3-NAG', - 'GLC-1,4-ASP', - '8B9-1,1-THR', - 'EBG-1,1-GLU', - 'GLC-1,4-SHG', - 'NGA-1,H-TYR', - 'NAG-1,2-MAN', - 'SHG-4,1-BGC', - 'FUC-1,3-NAG', - 'SHG-1,1-ASP', - 'BMA-1,4-GLU', - 'A2G-1,1-GLU', - 'MAN-1,1-GLU', - 'BGC-1,1-SER', - 'BMA-2,1-MAN', - 'BXF-1,1-GLU', - 'XYS-1,1-GLU', - 'RAM-1,1-THR', - 'RIB-1,2-GLU', - 'BGC-1,3-NBG', - 'XYS-1,4-XYS', - '3HD-1,1-THR', - 'NAG-1,4-MUB', - 'GLA-1,3-B6D', - 'XYP-2,1-GCV', - 'GLA-1,G-SER', - 'XYP-1,3-BGC', - 'AHR-1,1-GLU', - 'GCV-1,2-SER', - '289-1,G-SER', - 'MAN-1,4-MAN', - 'AMP-1,9-ASP', - 'B8D-1,4-BGC', - 'SIA-2,6-8B9', - 'NAG-1,4-GLU', - 'BGC-4,1-BGC', - 'NAG-1,6-NGA', - 'GCU-1,1-SER', - 'GXL-1,4-NDG', - 'C3X-1,1-GLU', - 'SIA-2,1-THR', - 'XYL-4,1-XYP', - 'BGC-1,3-GLC', - 'GAL-1,4-NAG', - 'GXL-1,2-GAL', - 'MAN-1,3-GLU', - 'MAN-1,2-ASP', - 'XYP-1,4-XYS', - 'C5X-1,1-GLU', - 'ARB-1,2-MAN', - 'NGA-1,1-SER', - 'SHG-1,G-SER', - 'G4D-1,4-GLC', - 'BGC-1,4-GLU', - 'NDG-1,6-A2G', - 'ARA-1,1-THR', - 'GLC-1,2-FRU', - 'BGC-1,1-THR', - 'NAG-1,3-A2G', - 'GAL-1,4-BGC', - 'DFX-1,1-GLU', - 'B6D-1,G-SER', - 'NDG-1,H-TYR', - 'FRU-2,2-GLU', - 'XYP-4,1-XYP', - 'GLC-1,1-THR', - 'GAL-1,1-GLU', - 'MAN-1,2-BMA', - 'MUB-1,2-GLU', - ], - 'S-glycans': [ - 'NAG-1,G-CYS', - 'KDO-2,G-CYS', - 'BGC-1,G-CYS', - 'MAN-1,G-CYS', - 'A2G-1,G-CYS', - 'MAN-1,2-MAN', - ], - Ligands: [ - 'NAG-1,2-ASN', - 'NAG-1,4-NAG', - 'NAG-1,4-BMA', - 'NAG-1,4-NAG', - 'NAG-1,4-NAG', - ], - }; + + useEffect(() => { + setLinkage(sugarLinkageMap[type][0]) + }, [type]) + useEffect(() => { let textString = 'Find'; @@ -616,23 +192,32 @@ function FilterZone(props: { setSearchBegin: any }) { if (!search) { return; } - + let formattedType = "" let url = 'https://raw.githubusercontent.com/Dialpuri/PrivateerDatabase/master/linkages/'; if (type === 'N-glycans') { url += 'n-glycan/'; + formattedType = "n-glycan" } if (type === 'O-glycans') { url += 'o-glycan/'; + formattedType = "o-glycan" + } if (type === 'S-glycans') { url += 's-glycan/'; + formattedType = "s-glycan" + } if (type === 'C-glycans') { url += 'c-glycan/'; + formattedType = "c-glycan" + } if (type === 'Ligands') { url += 'ligand/'; + formattedType = "ligand" + } url += linkage.replace(',', '%2C'); url += '.json'; @@ -646,7 +231,7 @@ function FilterZone(props: { setSearchBegin: any }) { pdb: item.pdb, count: item.count, resolution: item.resolution, - type: 'n-glycan', + type: formattedType, link: 'https://privateer.york.ac.uk/database?pdb=' + item.pdb, @@ -674,7 +259,7 @@ function FilterZone(props: { setSearchBegin: any }) { {TypeFilterBox({ onClickMethod: setType, selected: type })} {LinkageFilterBox({ onClickMethod: setLinkage, - sugars: sugars[type], + sugars: sugarLinkageMap[type], selected: linkage, })} From 572b34d33a1853116de6336d728e74148d039ac1 Mon Sep 17 00:00:00 2001 From: Jordan Dialpuri <44945647+Dialpuri@users.noreply.github.com> Date: Thu, 7 Mar 2024 18:02:41 +0000 Subject: [PATCH 06/25] Implement sorting functionality in DatabaseSearchTable Sorting feature is added to the DatabaseSearchTable. 'SortingState' from '@tanstack/react-table' was used to add state for the sorting criteria. The initial state is set to sort by the "count" column in descending order. The debugTable property is also changed to false. --- .../DatabaseSearchTable/DatabaseSearchTable.tsx | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/webapp/src/database/DatabaseSearchTable/DatabaseSearchTable.tsx b/webapp/src/database/DatabaseSearchTable/DatabaseSearchTable.tsx index 662cc194..d10a4d7d 100644 --- a/webapp/src/database/DatabaseSearchTable/DatabaseSearchTable.tsx +++ b/webapp/src/database/DatabaseSearchTable/DatabaseSearchTable.tsx @@ -7,8 +7,8 @@ import { getPaginationRowModel, flexRender, getSortedRowModel, - type Column, -} from '@tanstack/react-table'; + type Column, SortingState, +} from "@tanstack/react-table" import styled from 'styled-components'; const Styles = styled.div` @@ -168,16 +168,22 @@ function Table({ data }: { data: any }) { pageIndex: 0, pageSize: 10, }); + const [sorting, setSorting] = useState([ + { + id: "count", // Must be equal to the accessorKey of the coulmn you want sorted by default + desc: true, + }, + ]) const table = useReactTable({ columns, data, - debugTable: true, + debugTable: false, getCoreRowModel: getCoreRowModel(), getSortedRowModel: getSortedRowModel(), getFilteredRowModel: getFilteredRowModel(), getPaginationRowModel: getPaginationRowModel(), onPaginationChange: setPagination, - state: { pagination }, + state: { pagination, sorting }, }); return ( From 1cca1f9f82bdbb7154edf6e06d94f07ce4c86158 Mon Sep 17 00:00:00 2001 From: Jordan Dialpuri <44945647+Dialpuri@users.noreply.github.com> Date: Thu, 7 Mar 2024 18:03:43 +0000 Subject: [PATCH 07/25] Remove debug console warning from DatabaseSearchTable The debug 'console.warn(data)' has been removed from DatabaseSearchTable.tsx. This console output was unnecessary for the production code and its removal will enhance performance --- .../src/database/DatabaseSearchTable/DatabaseSearchTable.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/webapp/src/database/DatabaseSearchTable/DatabaseSearchTable.tsx b/webapp/src/database/DatabaseSearchTable/DatabaseSearchTable.tsx index d10a4d7d..ff3784a3 100644 --- a/webapp/src/database/DatabaseSearchTable/DatabaseSearchTable.tsx +++ b/webapp/src/database/DatabaseSearchTable/DatabaseSearchTable.tsx @@ -159,10 +159,6 @@ function Table({ data }: { data: any }) { }, ]; - useEffect(() => { - console.warn(data); - }, []); - const columns = useMemo(() => COLUMNS, []); const [pagination, setPagination] = useState({ pageIndex: 0, From 8615394e99f98e17f8090242d20355ac1857ec77 Mon Sep 17 00:00:00 2001 From: Jordan Dialpuri <44945647+Dialpuri@users.noreply.github.com> Date: Thu, 7 Mar 2024 18:07:50 +0000 Subject: [PATCH 08/25] Refactor state assignment in DatabaseSearchTable The 'state' assignment in DatabaseSearchTable.tsx has been refactored to improve code readability. The 'sorting' property is now clearly commented, making it easier to understand the purpose of this piece of code. --- .../src/database/DatabaseSearchTable/DatabaseSearchTable.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/webapp/src/database/DatabaseSearchTable/DatabaseSearchTable.tsx b/webapp/src/database/DatabaseSearchTable/DatabaseSearchTable.tsx index ff3784a3..847b195a 100644 --- a/webapp/src/database/DatabaseSearchTable/DatabaseSearchTable.tsx +++ b/webapp/src/database/DatabaseSearchTable/DatabaseSearchTable.tsx @@ -179,7 +179,10 @@ function Table({ data }: { data: any }) { getFilteredRowModel: getFilteredRowModel(), getPaginationRowModel: getPaginationRowModel(), onPaginationChange: setPagination, - state: { pagination, sorting }, + state: { + pagination, + // sorting + }, }); return ( From 830dab6e2a65ad01ea8aeed9cb1699933a37e5a5 Mon Sep 17 00:00:00 2001 From: Jordan Dialpuri <44945647+Dialpuri@users.noreply.github.com> Date: Thu, 7 Mar 2024 18:22:55 +0000 Subject: [PATCH 09/25] Add tooltip to sugar list in database components A tooltip was added to the sugar list component to provide in-depth explanation of the sugar concepts. A refactored rendering for 'monosaccharide validation data' text span and an svg icon were also included, improving user interactivity and understanding of the data presented. --- .../database/DatabaseComponents/SugarList.tsx | 107 +++++++++++------- 1 file changed, 68 insertions(+), 39 deletions(-) diff --git a/webapp/src/database/DatabaseComponents/SugarList.tsx b/webapp/src/database/DatabaseComponents/SugarList.tsx index 515aa074..e66af376 100644 --- a/webapp/src/database/DatabaseComponents/SugarList.tsx +++ b/webapp/src/database/DatabaseComponents/SugarList.tsx @@ -1,7 +1,8 @@ -import React, { useMemo, useEffect, useState } from 'react'; +import React, { useMemo, useEffect, useState, useRef } from "react" import { useTable } from 'react-table'; import { SugarListColumns } from '../../data/Constants.tsx'; import styled from 'styled-components'; +import { Tooltip, TooltipRefProps } from "react-tooltip" function customSort( a: Record, @@ -135,54 +136,82 @@ export default function SugarList(props) { setData(results); }, [props]); + const tooltipRef = useRef(null); + + const tooltipContent: string = + `A “spherical” coordinate system is used to describe six-membered rings, where the total puckering amplitude of the ring, Q, is defined, as well as
    + the distortion-type which is specified by two angles, phi and theta:

    + Radius, Q: describes the overall distortion or shape of the puckered ring,
    + measuring the deviation from a perfectly flat six-membered ring (Q = 0). This is calculated using out-of-plane deviations of puckered rings using the
    + z-coordinates of the ring atoms relative to a mean plane cutting through the ring.

    + Azimuthal angle, theta: describes the orientation of the puckering plane around the rings circumference. This parameter indicates the direction
    + in which the puckering occurs along the ring, providing information about the spatial distribution of the distortion.

    + Meridian angle, phi: describes the orientation of the out-of-plane puckering with respect to the rings mean plane. This provides information about
    + the directionality of the puckering, indicating whether the distortion of the ring is primarily upward or downward with respect to the mean plane.`; + return (
    - + + +
    + Detailed monosaccharide validation data - + + + +
    - {headerGroups.map((headerGroup) => ( + {headerGroups.map((headerGroup) => ( + + {headerGroup.headers.map((column) => ( + + ))} + + ))} + + + {rows.map((row) => { + prepareRow(row); + return ( - {headerGroup.headers.map((column) => ( - - ))} + {row.cells.map((cell) => { + return ( + + ); + })} - ))} - - - {rows.map((row) => { - prepareRow(row); - return ( - - {row.cells.map((cell) => { - return ( - - ); - })} - - ); - })} + ); + })}
    + {column.render('Header')} +
    - {column.render('Header')} - + {cell.render('Cell')} +
    - {cell.render('Cell')} -
    From 1e4d25071518b74b553b234b23cade9903c34128 Mon Sep 17 00:00:00 2001 From: Jordan Dialpuri <44945647+Dialpuri@users.noreply.github.com> Date: Fri, 8 Mar 2024 11:47:56 +0000 Subject: [PATCH 10/25] Added any type to linkages and fixed scrollbar The scrollbar colors in index.css have been updated for improved visual appeal. The linkage search functionality in the database component has been expanded to include an 'Any' option, allowing users to search without specifying a particularlinkage. --- webapp/src/data/Constants.tsx | 5 ++ .../DatabaseSearch/DatabaseSearch.tsx | 54 ++++++++++++++----- .../DatabaseSearchTable.tsx | 4 ++ webapp/src/index.css | 8 +-- 4 files changed, 53 insertions(+), 18 deletions(-) diff --git a/webapp/src/data/Constants.tsx b/webapp/src/data/Constants.tsx index a63e876c..a94507fa 100644 --- a/webapp/src/data/Constants.tsx +++ b/webapp/src/data/Constants.tsx @@ -226,6 +226,7 @@ export const binDB = { export const sugarLinkageMap = { 'N-glycans': [ + "Any", 'NAG-1,2-ASN', 'NAG-1,4-NAG', 'BMA-1,4-NAG', @@ -445,6 +446,7 @@ export const sugarLinkageMap = { 'BMA-1,6-ARG', ], 'C-glycans': [ + "Any", 'MAN-1,1-TRP', 'BMA-1,1-TRP', 'GAL-1,1-TRP', @@ -455,6 +457,7 @@ export const sugarLinkageMap = { 'NAG-4,1-NAG', ], 'O-glycans': [ + "Any", 'MAN-1,G-SER', 'MAN-1,1-THR', 'A2G-1,1-THR', @@ -638,6 +641,7 @@ export const sugarLinkageMap = { 'MUB-1,2-GLU', ], 'S-glycans': [ + "Any", 'NAG-1,G-CYS', 'KDO-2,G-CYS', 'BGC-1,G-CYS', @@ -646,6 +650,7 @@ export const sugarLinkageMap = { 'MAN-1,2-MAN', ], 'Ligands': [ + "Any", 'NAG-1,2-ASN', 'NAG-1,4-NAG', 'NAG-1,4-BMA', diff --git a/webapp/src/database/DatabaseSearch/DatabaseSearch.tsx b/webapp/src/database/DatabaseSearch/DatabaseSearch.tsx index 3e7b9f37..a12bd887 100644 --- a/webapp/src/database/DatabaseSearch/DatabaseSearch.tsx +++ b/webapp/src/database/DatabaseSearch/DatabaseSearch.tsx @@ -219,25 +219,51 @@ function FilterZone(props: { setSearchBegin: any }) { formattedType = "ligand" } - url += linkage.replace(',', '%2C'); + if (linkage === "Any") { + url += "any"; + } else { + url += linkage.replace(',', '%2C') + } url += '.json'; void fetch(url) .then(async (response) => await response.json()) .then((json) => { - const formattedData: Record = json.map( - (item) => { - return { - pdb: item.pdb, - count: item.count, - resolution: item.resolution, - type: formattedType, - link: - 'https://privateer.york.ac.uk/database?pdb=' + - item.pdb, - }; - } - ); + let formattedData; + if (linkage === "Any") { + formattedData = Object.keys(json).flatMap( + (item) => { + return json[item].map((element) => { + return { + pdb: element.pdb, + count: element.count, + resolution: element.resolution, + linkage: item, + type: formattedType, + link: + 'https://privateer.york.ac.uk/database?pdb=' + + element.pdb, + }; + }) + } + ); + } + else { + formattedData = json.map( + (item) => { + return { + pdb: item.pdb, + count: item.count, + resolution: item.resolution, + linkage: linkage, + type: formattedType, + link: + 'https://privateer.york.ac.uk/database?pdb=' + + item.pdb, + }; + } + ); + } setData(formattedData); }) .catch(() => {}); diff --git a/webapp/src/database/DatabaseSearchTable/DatabaseSearchTable.tsx b/webapp/src/database/DatabaseSearchTable/DatabaseSearchTable.tsx index 847b195a..2e339dbf 100644 --- a/webapp/src/database/DatabaseSearchTable/DatabaseSearchTable.tsx +++ b/webapp/src/database/DatabaseSearchTable/DatabaseSearchTable.tsx @@ -131,6 +131,10 @@ function Table({ data }: { data: any }) { header: 'PDB', accessorKey: 'pdb', }, + { + header: 'Linkage', + accessorKey: 'linkage', + }, { header: 'Count', accessorKey: 'count', diff --git a/webapp/src/index.css b/webapp/src/index.css index 38abee90..2b69aa59 100644 --- a/webapp/src/index.css +++ b/webapp/src/index.css @@ -6,7 +6,7 @@ * { scrollbar-width: thin; - scrollbar-color: #a6a99e #d6d9e5; + scrollbar-color: #B6BAC6 #D6D9E5; } /* Chrome, Edge, and Safari */ @@ -15,14 +15,14 @@ } *::-webkit-scrollbar-track { - background-color: #0b5a4b; + background-color: #D6D9E5; border-radius: 5px; } *::-webkit-scrollbar-thumb { - background-color: #0b5a4b; + background-color: #B6BAC6; border-radius: 14px; - border: 3px solid #a6a99e; + border: 3px solid #B6BAC6; } :root { From 6ad79a795238ed3a33ce705c7daf7826c4e84c07 Mon Sep 17 00:00:00 2001 From: Jordan Dialpuri <44945647+Dialpuri@users.noreply.github.com> Date: Fri, 8 Mar 2024 12:12:13 +0000 Subject: [PATCH 11/25] Refactor code and enhance search functionality Code has been refactored to use consistent quote types and formatting, and enhance search functionality in the database component. The sugarLinkageMap constant's type has been explicitly specified, and the default linkage search value has been changed to 'Any' to improve user flexibility --- webapp/src/data/Constants.tsx | 16 +-- .../database/DatabaseComponents/SugarList.tsx | 107 +++++++++--------- .../DatabaseSearch/DatabaseSearch.tsx | 86 +++++++------- .../DatabaseSearchTable.tsx | 9 +- 4 files changed, 103 insertions(+), 115 deletions(-) diff --git a/webapp/src/data/Constants.tsx b/webapp/src/data/Constants.tsx index a94507fa..f19f683f 100644 --- a/webapp/src/data/Constants.tsx +++ b/webapp/src/data/Constants.tsx @@ -224,9 +224,9 @@ export const binDB = { 'MAN-1,2-MAN': { start: -180, end: 180, size: 4 }, }; -export const sugarLinkageMap = { +export const sugarLinkageMap: Record = { 'N-glycans': [ - "Any", + 'Any', 'NAG-1,2-ASN', 'NAG-1,4-NAG', 'BMA-1,4-NAG', @@ -446,7 +446,7 @@ export const sugarLinkageMap = { 'BMA-1,6-ARG', ], 'C-glycans': [ - "Any", + 'Any', 'MAN-1,1-TRP', 'BMA-1,1-TRP', 'GAL-1,1-TRP', @@ -457,7 +457,7 @@ export const sugarLinkageMap = { 'NAG-4,1-NAG', ], 'O-glycans': [ - "Any", + 'Any', 'MAN-1,G-SER', 'MAN-1,1-THR', 'A2G-1,1-THR', @@ -641,7 +641,7 @@ export const sugarLinkageMap = { 'MUB-1,2-GLU', ], 'S-glycans': [ - "Any", + 'Any', 'NAG-1,G-CYS', 'KDO-2,G-CYS', 'BGC-1,G-CYS', @@ -649,12 +649,12 @@ export const sugarLinkageMap = { 'A2G-1,G-CYS', 'MAN-1,2-MAN', ], - 'Ligands': [ - "Any", + Ligands: [ + 'Any', 'NAG-1,2-ASN', 'NAG-1,4-NAG', 'NAG-1,4-BMA', 'NAG-1,4-NAG', 'NAG-1,4-NAG', ], -}; \ No newline at end of file +}; diff --git a/webapp/src/database/DatabaseComponents/SugarList.tsx b/webapp/src/database/DatabaseComponents/SugarList.tsx index e66af376..ffcf45da 100644 --- a/webapp/src/database/DatabaseComponents/SugarList.tsx +++ b/webapp/src/database/DatabaseComponents/SugarList.tsx @@ -1,8 +1,8 @@ -import React, { useMemo, useEffect, useState, useRef } from "react" +import React, { useMemo, useEffect, useState, useRef } from 'react'; import { useTable } from 'react-table'; import { SugarListColumns } from '../../data/Constants.tsx'; import styled from 'styled-components'; -import { Tooltip, TooltipRefProps } from "react-tooltip" +import { Tooltip, type TooltipRefProps } from 'react-tooltip'; function customSort( a: Record, @@ -138,8 +138,7 @@ export default function SugarList(props) { const tooltipRef = useRef(null); - const tooltipContent: string = - `A “spherical” coordinate system is used to describe six-membered rings, where the total puckering amplitude of the ring, Q, is defined, as well as
    + const tooltipContent: string = `A “spherical” coordinate system is used to describe six-membered rings, where the total puckering amplitude of the ring, Q, is defined, as well as
    the distortion-type which is specified by two angles, phi and theta:

    Radius, Q: describes the overall distortion or shape of the puckered ring,
    measuring the deviation from a perfectly flat six-membered ring (Q = 0). This is calculated using out-of-plane deviations of puckered rings using the
    @@ -154,64 +153,62 @@ export default function SugarList(props) {
    - - Detailed monosaccharide validation data - - - - + + Detailed monosaccharide validation data + + + +
    - {headerGroups.map((headerGroup) => ( - - {headerGroup.headers.map((column) => ( - - ))} - - ))} - - - {rows.map((row) => { - prepareRow(row); - return ( + {headerGroups.map((headerGroup) => ( - {row.cells.map((cell) => { - return ( - - ); - })} + {headerGroup.headers.map((column) => ( + + ))} - ); - })} + ))} + + + {rows.map((row) => { + prepareRow(row); + return ( + + {row.cells.map((cell) => { + return ( + + ); + })} + + ); + })}
    - {column.render('Header')} -
    - {cell.render('Cell')} - + {column.render('Header')} +
    + {cell.render('Cell')} +
    diff --git a/webapp/src/database/DatabaseSearch/DatabaseSearch.tsx b/webapp/src/database/DatabaseSearch/DatabaseSearch.tsx index a12bd887..fdad5211 100644 --- a/webapp/src/database/DatabaseSearch/DatabaseSearch.tsx +++ b/webapp/src/database/DatabaseSearch/DatabaseSearch.tsx @@ -6,7 +6,7 @@ import React, { } from 'react'; import DatabaseSearchTable from '../DatabaseSearchTable/DatabaseSearchTable.tsx'; import Loading from '../../shared/Loading/Loading.tsx'; -import {sugarLinkageMap} from "../../data/Constants.tsx" +import { sugarLinkageMap } from '../../data/Constants.tsx'; function ViewAllEntriesButton(props: { text: string; label: any; @@ -161,14 +161,13 @@ function LinkageFilterBox(props: { function FilterZone(props: { setSearchBegin: any }) { const [search, setSearch] = useState(false); - const [linkage, setLinkage] = useState('NAG-1,2-ASN'); + const [linkage, setLinkage] = useState('Any'); const [type, setType] = useState('N-glycans'); const [text, setText] = useState(''); useEffect(() => { - setLinkage(sugarLinkageMap[type][0]) - }, [type]) - + setLinkage(sugarLinkageMap[type][0]); + }, [type]); useEffect(() => { let textString = 'Find'; @@ -192,77 +191,68 @@ function FilterZone(props: { setSearchBegin: any }) { if (!search) { return; } - let formattedType = "" + let formattedType = ''; let url = 'https://raw.githubusercontent.com/Dialpuri/PrivateerDatabase/master/linkages/'; if (type === 'N-glycans') { url += 'n-glycan/'; - formattedType = "n-glycan" + formattedType = 'n-glycan'; } if (type === 'O-glycans') { url += 'o-glycan/'; - formattedType = "o-glycan" - + formattedType = 'o-glycan'; } if (type === 'S-glycans') { url += 's-glycan/'; - formattedType = "s-glycan" - + formattedType = 's-glycan'; } if (type === 'C-glycans') { url += 'c-glycan/'; - formattedType = "c-glycan" - + formattedType = 'c-glycan'; } if (type === 'Ligands') { url += 'ligand/'; - formattedType = "ligand" - + formattedType = 'ligand'; } - if (linkage === "Any") { - url += "any"; + if (linkage === 'Any') { + url += 'any'; } else { - url += linkage.replace(',', '%2C') + url += linkage.replace(',', '%2C'); } url += '.json'; void fetch(url) .then(async (response) => await response.json()) - .then((json) => { - let formattedData; - if (linkage === "Any") { - formattedData = Object.keys(json).flatMap( - (item) => { - return json[item].map((element) => { - return { - pdb: element.pdb, - count: element.count, - resolution: element.resolution, - linkage: item, - type: formattedType, - link: - 'https://privateer.york.ac.uk/database?pdb=' + - element.pdb, - }; - }) - } - ); - } - else { - formattedData = json.map( - (item) => { + .then((json: Record) => { + let formattedData: Record; + if (linkage === 'Any') { + formattedData = Object.keys(json).flatMap((item) => { + return json[item].map((element) => { return { - pdb: item.pdb, - count: item.count, - resolution: item.resolution, - linkage: linkage, + pdb: element.pdb, + count: element.count, + resolution: element.resolution, + linkage: item, type: formattedType, link: 'https://privateer.york.ac.uk/database?pdb=' + - item.pdb, + element.pdb, }; - } - ); + }); + }); + } else { + formattedData = json.map((item) => { + return { + pdb: item.pdb, + count: item.count, + resolution: item.resolution, + linkage, + type: formattedType, + link: + 'https://privateer.york.ac.uk/database?pdb=' + + item.pdb, + }; + }); } setData(formattedData); }) diff --git a/webapp/src/database/DatabaseSearchTable/DatabaseSearchTable.tsx b/webapp/src/database/DatabaseSearchTable/DatabaseSearchTable.tsx index 2e339dbf..d8d21f72 100644 --- a/webapp/src/database/DatabaseSearchTable/DatabaseSearchTable.tsx +++ b/webapp/src/database/DatabaseSearchTable/DatabaseSearchTable.tsx @@ -7,8 +7,9 @@ import { getPaginationRowModel, flexRender, getSortedRowModel, - type Column, SortingState, -} from "@tanstack/react-table" + type Column, + type SortingState, +} from '@tanstack/react-table'; import styled from 'styled-components'; const Styles = styled.div` @@ -170,10 +171,10 @@ function Table({ data }: { data: any }) { }); const [sorting, setSorting] = useState([ { - id: "count", // Must be equal to the accessorKey of the coulmn you want sorted by default + id: 'count', // Must be equal to the accessorKey of the coulmn you want sorted by default desc: true, }, - ]) + ]); const table = useReactTable({ columns, data, From 1e62f054497e7ceddb334bbecfb3888d34d5d5e6 Mon Sep 17 00:00:00 2001 From: Jordan Dialpuri <44945647+Dialpuri@users.noreply.github.com> Date: Fri, 8 Mar 2024 12:25:33 +0000 Subject: [PATCH 12/25] Normalize color values in CSS files CSS color values in the index.css file have been revised to maintain consistency across the code. All hex color values are now in the same format, lowercased, contributing to improved code readability and maintenance. --- webapp/src/index.css | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/webapp/src/index.css b/webapp/src/index.css index 2b69aa59..cab4e7b9 100644 --- a/webapp/src/index.css +++ b/webapp/src/index.css @@ -6,7 +6,7 @@ * { scrollbar-width: thin; - scrollbar-color: #B6BAC6 #D6D9E5; + scrollbar-color: #b6bac6 #d6d9e5; } /* Chrome, Edge, and Safari */ @@ -15,14 +15,14 @@ } *::-webkit-scrollbar-track { - background-color: #D6D9E5; + background-color: #d6d9e5; border-radius: 5px; } *::-webkit-scrollbar-thumb { - background-color: #B6BAC6; + background-color: #b6bac6; border-radius: 14px; - border: 3px solid #B6BAC6; + border: 3px solid #b6bac6; } :root { From 6d63e651e28afb399dea43b107d5e84ae6df4652 Mon Sep 17 00:00:00 2001 From: Jordan Dialpuri <44945647+Dialpuri@users.noreply.github.com> Date: Sat, 9 Mar 2024 09:53:11 +0000 Subject: [PATCH 13/25] Add Google Analytics tracking with web vitals Incorporated Google Analytics 4 into the project to track page views and web vital metrics. Updates include initializing GA4 in `main.tsx` with `ReactGA.initialize` and capturing CLS, LCP, FID metrics via web-vitals library. Added dependency for `react-ga4` and `web-vitals` in `package.json`. --- webapp/package.json | 2 ++ webapp/src/main.tsx | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/webapp/package.json b/webapp/package.json index fb9d4bb2..448f904b 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -25,6 +25,7 @@ "react": "^18.2.0", "react-collapsed": "^4.1.2", "react-dom": "^18.2.0", + "react-ga4": "^2.1.0", "react-grid-layout": "^1.4.4", "react-icons": "^4.11.0", "react-inlinesvg": "^3.0.2", @@ -38,6 +39,7 @@ "serve": "^14.2.1", "styled-components": "^6.0.6", "vite-plugin-top-level-await": "^1.3.1", + "web-vitals": "^3.5.2", "zlib": "^1.0.5" }, "devDependencies": { diff --git a/webapp/src/main.tsx b/webapp/src/main.tsx index 08568ceb..6ec9557c 100644 --- a/webapp/src/main.tsx +++ b/webapp/src/main.tsx @@ -3,7 +3,10 @@ import ReactDOM from 'react-dom/client'; import App from './App'; import './index.css'; import { BrowserRouter } from 'react-router-dom'; +import ReactGA from "react-ga4"; +import {onCLS, onFID, onLCP} from 'web-vitals'; +ReactGA.initialize("G-PGPMR0MEYT"); ReactDOM.createRoot(document.getElementById('root')).render( // @@ -12,3 +15,18 @@ ReactDOM.createRoot(document.getElementById('root')).render( // , ); +const SendAnalytics = ()=> { + ReactGA.send({ + hitType: "pageview", + page: window.location.pathname, + }); +} + +try { + onCLS(SendAnalytics); + onLCP(SendAnalytics); + onFID(SendAnalytics); +} +catch (err) { + console.error(err) +} From 14a97e77b693d019c1e03cd7d2acf5c8c7d46d61 Mon Sep 17 00:00:00 2001 From: Jordan Dialpuri <44945647+Dialpuri@users.noreply.github.com> Date: Sat, 9 Mar 2024 19:07:32 +0000 Subject: [PATCH 14/25] Update color and legend in CremerPopleGraph component Adjusted the color setting in the 'scatter' type and added legend configuration in the 'layout' part of CremerPopleGraph.tsx. The scatter plot color updated from green to blue and the legend was made visible with additional settings such as position and border color. --- .../DatabaseComponents/CremerPopleGraph.tsx | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/webapp/src/database/DatabaseComponents/CremerPopleGraph.tsx b/webapp/src/database/DatabaseComponents/CremerPopleGraph.tsx index fcb49db6..fcfca3a1 100644 --- a/webapp/src/database/DatabaseComponents/CremerPopleGraph.tsx +++ b/webapp/src/database/DatabaseComponents/CremerPopleGraph.tsx @@ -52,7 +52,7 @@ export default function CremerPopleGraph(props: any) { type: 'scatter', marker: { size: 8, - color: 'green', + color: 'blue', symbol: ['o'], }, name: 'No Issues', @@ -82,7 +82,17 @@ export default function CremerPopleGraph(props: any) { Date: Mon, 11 Mar 2024 20:46:40 +0000 Subject: [PATCH 15/25] Refine UI and conditional rendering across various components This commit enhances UI and implements conditional rendering in multiple components. Notably, CremerPopleGraph's scatter plots receive color and legend updates, while DatabaseHeader and DatabaseResult's flex containers are widened. Improvements are made to SugarList and BFactorVsRSCC visual elements, and an extraneous hover title in SNFGList is removed. Additionally, a new data extraction function is included in Constants.tsx for better data accessibility and presentation. --- webapp/src/data/Constants.tsx | 64 +++++++++++++++++-- .../DatabaseComponents/BFactorVsRSCC.tsx | 6 +- .../DatabaseComponents/CremerPopleGraph.tsx | 8 +-- .../database/DatabaseComponents/SNFGList.tsx | 1 - .../database/DatabaseComponents/SugarList.tsx | 1 - .../DatabaseResult/DatabaseResult.tsx | 2 +- webapp/src/database/Header/DatabaseHeader.tsx | 2 +- webapp/src/main.tsx | 17 +++-- 8 files changed, 76 insertions(+), 25 deletions(-) diff --git a/webapp/src/data/Constants.tsx b/webapp/src/data/Constants.tsx index f19f683f..29c15ef2 100644 --- a/webapp/src/data/Constants.tsx +++ b/webapp/src/data/Constants.tsx @@ -139,46 +139,102 @@ export const DatabaseColumns = [ }, ]; +function extracted(props, accessor) { + const typeValue = props.row.original[accessor]; + const diagnostic = props.row.original.diagnostic; + + const colour = diagnostic === "yes" ? "text-red-600" : (diagnostic === "check" ? "text-yellow-600": "") + return ( + + {typeValue} + + ); +} + export const SugarListColumns = [ { Header: 'Sugar ID', accessor: 'sugarId', + Cell: (props) => extracted(props, 'sugarId'), + }, + { + Header: 'Conformation', + accessor: 'conformation', + Cell: (props: { + row: { original: { conformation: any; diagnostic: any } }; + }) => { + const typeValue = props.row.original.conformation; + const diagnostic = props.row.original.diagnostic; + const colour = diagnostic === "yes" ? "text-red-400" : (diagnostic === "check" ? "text-yellow-600": "") + + const regex = /([a-zA-Z]*?\d*)([a-zA-Z])(\d*)/; + const formattedString = typeValue.replace( + regex, + (_, before, letter, after) => { + let string = ''; + if (before) { + string += '' + before + ''; + } + string += letter; + if (after) { + string += '' + after + ''; + } + return string; + } + ); + return ( +
    + ); + }, }, { Header: 'Q', accessor: 'q', + Cell: (props) => extracted(props, 'q'), }, { Header: 'Phi', accessor: 'phi', + Cell: (props) => extracted(props, 'phi'), }, { Header: 'Theta', accessor: 'theta', + Cell: (props) => extracted(props, 'theta'), }, + { Header: 'RSCC', accessor: 'rscc', + Cell: (props) => extracted(props, 'rscc'), }, { Header: 'B Factor', accessor: 'bFactor', + Cell: (props) => extracted(props, 'bFactor'), }, { Header: 'Detected Type', accessor: 'detectedType', + Cell: (props) => extracted(props, 'detectedType'), }, - { - Header: 'mFo', - accessor: 'mFo', - }, + // { + // Header: 'mFo', + // accessor: 'mFo', + // }, { Header: 'Type', accessor: 'type', + Cell: (props) => extracted(props, 'type'), }, { Header: 'Diagnostic', accessor: 'diagnostic', + Cell: (props) => extracted(props, 'diagnostic'), }, ]; diff --git a/webapp/src/database/DatabaseComponents/BFactorVsRSCC.tsx b/webapp/src/database/DatabaseComponents/BFactorVsRSCC.tsx index 4e4da786..35c1bd02 100644 --- a/webapp/src/database/DatabaseComponents/BFactorVsRSCC.tsx +++ b/webapp/src/database/DatabaseComponents/BFactorVsRSCC.tsx @@ -42,7 +42,7 @@ export default function BFactorVsRSCC(props) { marker: { size: 1, color: 'rgba(173,181,189,0.5)', - symbol: ['o'], + symbol: 'x', }, }); @@ -55,8 +55,8 @@ export default function BFactorVsRSCC(props) { type: 'scatter', marker: { size: 8, - color: 'green', - symbol: ['o'], + color: 'blue', + symbol: 'o', }, }); }, [props]); diff --git a/webapp/src/database/DatabaseComponents/CremerPopleGraph.tsx b/webapp/src/database/DatabaseComponents/CremerPopleGraph.tsx index fcfca3a1..1b046964 100644 --- a/webapp/src/database/DatabaseComponents/CremerPopleGraph.tsx +++ b/webapp/src/database/DatabaseComponents/CremerPopleGraph.tsx @@ -53,7 +53,7 @@ export default function CremerPopleGraph(props: any) { marker: { size: 8, color: 'blue', - symbol: ['o'], + symbol: 'o', }, name: 'No Issues', }); @@ -67,7 +67,7 @@ export default function CremerPopleGraph(props: any) { marker: { size: 8, color: 'red', - symbol: ['o'], + symbol: 'x', }, name: 'Issues', }); @@ -89,9 +89,7 @@ export default function CremerPopleGraph(props: any) { y: 0.5, bgcolor: 'rgba(0,0,0,0)', borderwidth: 0.2, - bordercolor: "gray" - - + bordercolor: 'gray', }, width: 500, height: 400, diff --git a/webapp/src/database/DatabaseComponents/SNFGList.tsx b/webapp/src/database/DatabaseComponents/SNFGList.tsx index 740a014a..205f440f 100644 --- a/webapp/src/database/DatabaseComponents/SNFGList.tsx +++ b/webapp/src/database/DatabaseComponents/SNFGList.tsx @@ -140,7 +140,6 @@ export default function SNFGList(props) { return ( diff --git a/webapp/src/database/DatabaseComponents/SugarList.tsx b/webapp/src/database/DatabaseComponents/SugarList.tsx index ffcf45da..9344662e 100644 --- a/webapp/src/database/DatabaseComponents/SugarList.tsx +++ b/webapp/src/database/DatabaseComponents/SugarList.tsx @@ -192,7 +192,6 @@ export default function SugarList(props) { return ( diff --git a/webapp/src/database/DatabaseResult/DatabaseResult.tsx b/webapp/src/database/DatabaseResult/DatabaseResult.tsx index 72855b5f..0575824c 100644 --- a/webapp/src/database/DatabaseResult/DatabaseResult.tsx +++ b/webapp/src/database/DatabaseResult/DatabaseResult.tsx @@ -36,7 +36,7 @@ export default function DatabaseResult(props: DatabaseResultProps) { return ( <> {selectedData !== undefined ? ( -
    +

    Validation Report - {props.PDBCode} {props.pdbRedoResults !== '' ? toggleSwitch() : <>} diff --git a/webapp/src/database/Header/DatabaseHeader.tsx b/webapp/src/database/Header/DatabaseHeader.tsx index b8bd5e42..8045fb0e 100644 --- a/webapp/src/database/Header/DatabaseHeader.tsx +++ b/webapp/src/database/Header/DatabaseHeader.tsx @@ -18,7 +18,7 @@ export function DatabaseHeader(props: DatabaseHeaderProps): ReactElement { return (
    -
    +
    {!props.fallback ? ( @@ -15,18 +15,17 @@ ReactDOM.createRoot(document.getElementById('root')).render( // , ); -const SendAnalytics = ()=> { +const SendAnalytics = () => { ReactGA.send({ - hitType: "pageview", + hitType: 'pageview', page: window.location.pathname, }); -} +}; try { onCLS(SendAnalytics); onLCP(SendAnalytics); onFID(SendAnalytics); -} -catch (err) { - console.error(err) +} catch (err) { + console.error(err); } From cca8ae537e1a4ca3ddf14bbac217b8865170e5a7 Mon Sep 17 00:00:00 2001 From: Jordan Dialpuri <44945647+Dialpuri@users.noreply.github.com> Date: Wed, 13 Mar 2024 09:08:51 +0000 Subject: [PATCH 16/25] Update package-lock.json with new dependencies In this commit, new dependencies "@tanstack/react-table", "react-ga4" and "web-vitals" have been added to the project's package-lock.json file. The version of "es5-ext" and "ip" have also been updated and necessary information about these updated modules have been added as well. --- webapp/package-lock.json | 91 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 82 insertions(+), 9 deletions(-) diff --git a/webapp/package-lock.json b/webapp/package-lock.json index 6f81cd98..08c984c5 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -8,6 +8,7 @@ "name": "privateer", "version": "0.0.0", "dependencies": { + "@tanstack/react-table": "^8.13.2", "@types/jest": "^29.5.6", "@types/node": "^20.8.9", "jest": "^29.7.0", @@ -19,6 +20,7 @@ "react": "^18.2.0", "react-collapsed": "^4.1.2", "react-dom": "^18.2.0", + "react-ga4": "^2.1.0", "react-grid-layout": "^1.4.4", "react-icons": "^4.11.0", "react-inlinesvg": "^3.0.2", @@ -32,6 +34,7 @@ "serve": "^14.2.1", "styled-components": "^6.0.6", "vite-plugin-top-level-await": "^1.3.1", + "web-vitals": "^3.5.2", "zlib": "^1.0.5" }, "devDependencies": { @@ -3377,6 +3380,37 @@ "node": ">=10" } }, + "node_modules/@tanstack/react-table": { + "version": "8.13.2", + "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.13.2.tgz", + "integrity": "sha512-b6mR3mYkjRtJ443QZh9sc7CvGTce81J35F/XMr0OoWbx0KIM7TTTdyNP2XKObvkLpYnLpCrYDwI3CZnLezWvpg==", + "dependencies": { + "@tanstack/table-core": "8.13.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": ">=16", + "react-dom": ">=16" + } + }, + "node_modules/@tanstack/table-core": { + "version": "8.13.2", + "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.13.2.tgz", + "integrity": "sha512-/2saD1lWBUV6/uNAwrsg2tw58uvMJ07bO2F1IWMxjFRkJiXKQRuc3Oq2aufeobD3873+4oIM/DRySIw7+QsPPw==", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, "node_modules/@tootallnate/quickjs-emscripten": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", @@ -5993,13 +6027,14 @@ } }, "node_modules/es5-ext": { - "version": "0.10.62", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", - "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", + "version": "0.10.64", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", "hasInstallScript": true, "dependencies": { "es6-iterator": "^2.0.3", "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", "next-tick": "^1.1.0" }, "engines": { @@ -6597,6 +6632,25 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esniff/node_modules/type": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" + }, "node_modules/espree": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", @@ -6666,6 +6720,15 @@ "node": ">=0.10.0" } }, + "node_modules/event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -7971,9 +8034,9 @@ } }, "node_modules/ip": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz", - "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==" + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.9.tgz", + "integrity": "sha512-cyRxvOEpNHNtchU3Ln9KC/auJgup87llfQpQ+t5ghoC/UhL16SWzbueiCsdTnWmqAWl7LadfuwhlqmtOaqMHdQ==" }, "node_modules/is-array-buffer": { "version": "3.0.2", @@ -10888,6 +10951,11 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/react-ga4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/react-ga4/-/react-ga4-2.1.0.tgz", + "integrity": "sha512-ZKS7PGNFqqMd3PJ6+C2Jtz/o1iU9ggiy8Y8nUeksgVuvNISbmrQtJiZNvC/TjDsqD0QlU5Wkgs7i+w9+OjHhhQ==" + }, "node_modules/react-grid-layout": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/react-grid-layout/-/react-grid-layout-1.4.4.tgz", @@ -11795,9 +11863,9 @@ } }, "node_modules/socks/node_modules/ip": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", - "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.1.tgz", + "integrity": "sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ==" }, "node_modules/source-map": { "version": "0.6.1", @@ -12964,6 +13032,11 @@ "resolved": "https://registry.npmjs.org/weak-map/-/weak-map-1.0.8.tgz", "integrity": "sha512-lNR9aAefbGPpHO7AEnY0hCFjz1eTkWCXYvkTRrTHs9qv8zJp+SkVYpzfLIFXQQiG3tVvbNFQgVg2bQS8YGgxyw==" }, + "node_modules/web-vitals": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-3.5.2.tgz", + "integrity": "sha512-c0rhqNcHXRkY/ogGDJQxZ9Im9D19hDihbzSQJrsioex+KnFgmMzBiy57Z1EjkhX/+OjyBpclDCzz2ITtjokFmg==" + }, "node_modules/webgl-context": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/webgl-context/-/webgl-context-2.2.0.tgz", From 19de3b7aba7a69a8d770def775bababadb916922 Mon Sep 17 00:00:00 2001 From: Jordan Dialpuri <44945647+Dialpuri@users.noreply.github.com> Date: Wed, 13 Mar 2024 09:47:22 +0000 Subject: [PATCH 17/25] Extract color determining logic to function The color determination logic based on diagnostic values has been extracted into a separate function called `getColour`. This function is then utilized in two different places, which enhances code readability and maintainability. --- webapp/src/data/Constants.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/webapp/src/data/Constants.tsx b/webapp/src/data/Constants.tsx index 29c15ef2..a1edd93b 100644 --- a/webapp/src/data/Constants.tsx +++ b/webapp/src/data/Constants.tsx @@ -139,11 +139,17 @@ export const DatabaseColumns = [ }, ]; + +function getColour(diagnostic) { + return diagnostic !== "yes" ? "text-red-400" : (diagnostic === "check" ? "text-yellow-600" : "") +} + + function extracted(props, accessor) { const typeValue = props.row.original[accessor]; const diagnostic = props.row.original.diagnostic; - const colour = diagnostic === "yes" ? "text-red-600" : (diagnostic === "check" ? "text-yellow-600": "") + const colour = getColour(diagnostic) return ( @@ -166,7 +172,7 @@ export const SugarListColumns = [ }) => { const typeValue = props.row.original.conformation; const diagnostic = props.row.original.diagnostic; - const colour = diagnostic === "yes" ? "text-red-400" : (diagnostic === "check" ? "text-yellow-600": "") + const colour = getColour(diagnostic) const regex = /([a-zA-Z]*?\d*)([a-zA-Z])(\d*)/; const formattedString = typeValue.replace( From 02bf71a8c93c18b1b305d55db02cd3acbe80912b Mon Sep 17 00:00:00 2001 From: Jordan Dialpuri <44945647+Dialpuri@users.noreply.github.com> Date: Wed, 13 Mar 2024 10:17:26 +0000 Subject: [PATCH 18/25] Update color logic in getColour function The color logic within the `getColour` function in Constants.tsx has been modified. The color for the 'yes' diagnostic is updated from "text-red-400" to "text-red-600" in order to provide more optic distinction in the application's UI. --- webapp/src/data/Constants.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/src/data/Constants.tsx b/webapp/src/data/Constants.tsx index a1edd93b..7bb50d3f 100644 --- a/webapp/src/data/Constants.tsx +++ b/webapp/src/data/Constants.tsx @@ -141,7 +141,7 @@ export const DatabaseColumns = [ function getColour(diagnostic) { - return diagnostic !== "yes" ? "text-red-400" : (diagnostic === "check" ? "text-yellow-600" : "") + return diagnostic !== "yes" ? "text-red-600" : (diagnostic === "check" ? "text-yellow-600" : "") } From b2806593ee676b948386b9538871a71cc81126b5 Mon Sep 17 00:00:00 2001 From: Jordan Dialpuri <44945647+Dialpuri@users.noreply.github.com> Date: Wed, 13 Mar 2024 10:44:55 +0000 Subject: [PATCH 19/25] Display appropriate message for empty torsions data The code has been updated to handle cases where 'torsions' data is undefined. If the torsions data is undefined, the code now shows a relevant message to the user. By adding checks for empty data and appropriate response mechanisms, the system's usability improves and the potential for confusion in such scenarios is minimized. --- .../database/DatabaseComponents/TorsionGraph.tsx | 13 +++++++++++-- webapp/src/shared/TorsionPlot/TorsionMultiPlot.tsx | 6 ++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/webapp/src/database/DatabaseComponents/TorsionGraph.tsx b/webapp/src/database/DatabaseComponents/TorsionGraph.tsx index 046a13c4..6798681a 100644 --- a/webapp/src/database/DatabaseComponents/TorsionGraph.tsx +++ b/webapp/src/database/DatabaseComponents/TorsionGraph.tsx @@ -83,7 +83,7 @@ export default function TorsionGraph(data) { return (
    - {torsions !== undefined ? ( + {torsions[Object.keys(torsions)[glycanTab]] !== undefined ? ( <> Linkage Torsion Analysis
    @@ -108,7 +108,16 @@ export default function TorsionGraph(data) { /> ) : ( - <> + <> + + Linkage Torsion Analysis +
    + + There are no linkages detected in this model. + + +
    + )}
    ); diff --git a/webapp/src/shared/TorsionPlot/TorsionMultiPlot.tsx b/webapp/src/shared/TorsionPlot/TorsionMultiPlot.tsx index 8443bf31..c1f7283c 100644 --- a/webapp/src/shared/TorsionPlot/TorsionMultiPlot.tsx +++ b/webapp/src/shared/TorsionPlot/TorsionMultiPlot.tsx @@ -134,9 +134,11 @@ export default function TorsionMultiPlot({ useEffect(() => { setTab(0); + console.log(torsions) }, []); useEffect(() => { + if (torsions === undefined) {return} const [linkageArray_, sortedLinkageArray_] = sortTorsions(torsions); setLinkageArray(linkageArray_); setSortedLinkageArray(sortedLinkageArray_); @@ -144,8 +146,8 @@ export default function TorsionMultiPlot({ return ( <> - {linkageArray.length === 0 ? ( - <> + {torsions === undefined ? ( + <>There are no linkages to be displayed. ) : (
    From 704b9efe9c9e2a62dca10bbff81666ca73b9d413 Mon Sep 17 00:00:00 2001 From: Jordan Dialpuri <44945647+Dialpuri@users.noreply.github.com> Date: Wed, 13 Mar 2024 10:51:13 +0000 Subject: [PATCH 20/25] Refactor code to handle empty 'torsions' data more precisely This commit replaces the null state for 'torsions' with an explicit undefined state when 'sortedTorsionList' has no entries. It also improves readability of the code by altering the condition for rendering 'Linkage Torsion Analysis', making it dependent on 'torsions' being defined, not the existence of keys in 'torsions'. This makes the execution path clearer. --- .../DatabaseComponents/TorsionGraph.tsx | 20 ++++++++++--------- .../shared/TorsionPlot/TorsionMultiPlot.tsx | 5 +++-- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/webapp/src/database/DatabaseComponents/TorsionGraph.tsx b/webapp/src/database/DatabaseComponents/TorsionGraph.tsx index 6798681a..c0f90f7f 100644 --- a/webapp/src/database/DatabaseComponents/TorsionGraph.tsx +++ b/webapp/src/database/DatabaseComponents/TorsionGraph.tsx @@ -29,7 +29,7 @@ function TorsionGraphTabs(props): React.JSX.Element[] { } export default function TorsionGraph(data) { - const [torsions, setTorsions] = useState(); + const [torsions, setTorsions] = useState | undefined>(); const [torsionTab, setTorsionTab] = useState(0); const [glycanTab, setGlycanTab] = useState(0); @@ -74,7 +74,11 @@ export default function TorsionGraph(data) { sortedTorsionList[k] = torsionList[k]; }); - setTorsions(sortedTorsionList); + if (Object.keys(sortedTorsionList).length === 0) { + setTorsions(undefined); + } else { + setTorsions(sortedTorsionList); + } }, [data]); useEffect(() => { @@ -83,7 +87,7 @@ export default function TorsionGraph(data) { return (
    - {torsions[Object.keys(torsions)[glycanTab]] !== undefined ? ( + {torsions !== undefined ? ( <> Linkage Torsion Analysis
    @@ -109,15 +113,13 @@ export default function TorsionGraph(data) { ) : ( <> - - Linkage Torsion Analysis -
    + Linkage Torsion Analysis +
    There are no linkages detected in this model. - -
    - +
    + )}
    ); diff --git a/webapp/src/shared/TorsionPlot/TorsionMultiPlot.tsx b/webapp/src/shared/TorsionPlot/TorsionMultiPlot.tsx index c1f7283c..8b0ff530 100644 --- a/webapp/src/shared/TorsionPlot/TorsionMultiPlot.tsx +++ b/webapp/src/shared/TorsionPlot/TorsionMultiPlot.tsx @@ -134,11 +134,12 @@ export default function TorsionMultiPlot({ useEffect(() => { setTab(0); - console.log(torsions) }, []); useEffect(() => { - if (torsions === undefined) {return} + if (torsions === undefined) { + return; + } const [linkageArray_, sortedLinkageArray_] = sortTorsions(torsions); setLinkageArray(linkageArray_); setSortedLinkageArray(sortedLinkageArray_); From 982447e3053929b5487b95edd2fdd933e5954ccf Mon Sep 17 00:00:00 2001 From: Jordan Dialpuri <44945647+Dialpuri@users.noreply.github.com> Date: Wed, 13 Mar 2024 10:51:32 +0000 Subject: [PATCH 21/25] Refactor 'getColour' and 'extracted' functions for cleaner code The changes in this commit refactor 'getColour' and 'extracted' functions in Constants.tsx file for cleaner, more readable code. Specifically, the ternary operator in 'getColour' function was simplified and the unnecessary return parentheses in 'extracted' function were removed. Also, missing semicolons were added after function calls for better coding practices. --- webapp/src/data/Constants.tsx | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/webapp/src/data/Constants.tsx b/webapp/src/data/Constants.tsx index 7bb50d3f..7282c216 100644 --- a/webapp/src/data/Constants.tsx +++ b/webapp/src/data/Constants.tsx @@ -139,23 +139,20 @@ export const DatabaseColumns = [ }, ]; - function getColour(diagnostic) { - return diagnostic !== "yes" ? "text-red-600" : (diagnostic === "check" ? "text-yellow-600" : "") + return diagnostic !== 'yes' + ? 'text-red-600' + : diagnostic === 'check' + ? 'text-yellow-600' + : ''; } - function extracted(props, accessor) { const typeValue = props.row.original[accessor]; const diagnostic = props.row.original.diagnostic; - const colour = getColour(diagnostic) - return ( - - {typeValue} - - ); + const colour = getColour(diagnostic); + return {typeValue}; } export const SugarListColumns = [ @@ -172,7 +169,7 @@ export const SugarListColumns = [ }) => { const typeValue = props.row.original.conformation; const diagnostic = props.row.original.diagnostic; - const colour = getColour(diagnostic) + const colour = getColour(diagnostic); const regex = /([a-zA-Z]*?\d*)([a-zA-Z])(\d*)/; const formattedString = typeValue.replace( From 8d009c6d074cf8aeb198457e7bbedccbebd1ecf4 Mon Sep 17 00:00:00 2001 From: Jordan Dialpuri <44945647+Dialpuri@users.noreply.github.com> Date: Wed, 13 Mar 2024 11:08:34 +0000 Subject: [PATCH 22/25] Implement early return in TorsionPlot effect Included an early return statement in TorsionPlot.tsx's useEffect to prevent execution when sortedTorsionList is undefined. Also introduced a return statement in the promise error catch block, although its effect would generally be limited as it is the last action in the block. --- webapp/src/shared/TorsionPlot/TorsionPlot.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/webapp/src/shared/TorsionPlot/TorsionPlot.tsx b/webapp/src/shared/TorsionPlot/TorsionPlot.tsx index 5ccd0a65..71a11f99 100644 --- a/webapp/src/shared/TorsionPlot/TorsionPlot.tsx +++ b/webapp/src/shared/TorsionPlot/TorsionPlot.tsx @@ -19,6 +19,11 @@ export default function TorsionPlot({ const [linkageFound, setLinkageFound] = useState(false); useEffect(() => { + + if (sortedTorsionList === undefined) { + return; + } + fetch(linkageDB[linkageType]) .then(async (response) => await response.text()) .then((text) => { @@ -60,6 +65,7 @@ export default function TorsionPlot({ .catch((error) => { console.error(error); setLinkageFound(false); + return }); const overlayPhi: number[] = []; From f08108e4df0b121c8a47bda4f3f1f02a796f8fcc Mon Sep 17 00:00:00 2001 From: Jordan Dialpuri <44945647+Dialpuri@users.noreply.github.com> Date: Wed, 13 Mar 2024 11:17:00 +0000 Subject: [PATCH 23/25] Improve sugar data diagnostics in BFactorVsRSCC.tsx Modified the calculatePoints function to clearly distinguish and handle 'issue' and 'no issue' sugars. Altered data plotting to accurately reflect this improved diagnostic handling, and implemented a comprehensive legend for user-friendly data distinction. --- .../DatabaseComponents/BFactorVsRSCC.tsx | 59 +++++++++++++++---- 1 file changed, 49 insertions(+), 10 deletions(-) diff --git a/webapp/src/database/DatabaseComponents/BFactorVsRSCC.tsx b/webapp/src/database/DatabaseComponents/BFactorVsRSCC.tsx index 35c1bd02..79b0da07 100644 --- a/webapp/src/database/DatabaseComponents/BFactorVsRSCC.tsx +++ b/webapp/src/database/DatabaseComponents/BFactorVsRSCC.tsx @@ -2,40 +2,53 @@ import React, { useEffect, useState, lazy } from 'react'; const Plot = lazy(async () => await import('react-plotly.js')); -function calculatePoints(data): [number[], number[], string[]] { +function calculatePoints(data): [number[], number[], string[], number[], number[], string[]] { const glycans = data.data.glycans; const xAxis: number[] = []; const yAxis: number[] = []; const text: string[] = []; + const errorXAxis: number[] = []; + const errorYAxis: number[] = []; + const errorText: string[] = []; + for (const key in glycans) { const glycanType = glycans[key]; for (let i = 0; i < glycanType.length; i++) { const sugars = glycanType[i].sugars; for (let j = 0; j < sugars.length; j++) { - xAxis.push(sugars[j].bFactor as number); - yAxis.push(sugars[j].rscc as number); - text.push(sugars[j]['Sugar ID'] as string); + if (sugars[j].diagnostic !== 'yes') { + errorXAxis.push(sugars[j].bFactor as number); + errorYAxis.push(sugars[j].rscc as number); + errorText.push(sugars[j].sugarId as string); + } else { + xAxis.push(sugars[j].bFactor as number); + yAxis.push(sugars[j].rscc as number); + text.push(sugars[j].sugarId as string); + } } } } - return [xAxis, yAxis, text]; + return [xAxis, yAxis, text, errorXAxis, errorYAxis, errorText]; } export default function BFactorVsRSCC(props) { const [trace, setTrace] = useState({}); const [corrTrace, setCorrTrace] = useState({}); + const [badTrace, setBadTrace] = useState({}); useEffect(() => { - const [xAxis, yAxis, text] = calculatePoints(props); - - const maxX = Math.max(...xAxis) + 5; + const [xAxis, yAxis, text, errorXAxis, errorYAxis, errorText] = calculatePoints(props); + const maxX = Math.max(Math.max(...xAxis), Math.max(...errorXAxis)) + 5; + console.log(errorXAxis, xAxis) setCorrTrace({ x: [0, maxX], y: [0.7, 0.7], + text, + hoverinfo: 'text', fill: 'tozeroy', fillcolor: 'rgba(173,181,189,0.3)', fillopacity: 0.1, @@ -44,6 +57,7 @@ export default function BFactorVsRSCC(props) { color: 'rgba(173,181,189,0.5)', symbol: 'x', }, + showlegend: false, }); setTrace({ @@ -58,6 +72,23 @@ export default function BFactorVsRSCC(props) { color: 'blue', symbol: 'o', }, + name: 'No Issues', + + }); + + setBadTrace({ + x: errorXAxis, + y: errorYAxis, + text: errorText, + hoverinfo: 'text', + mode: 'markers', + type: 'scatter', + marker: { + size: 8, + color: 'red', + symbol: 'x', + }, + name: 'Issues', }); }, [props]); @@ -65,12 +96,20 @@ export default function BFactorVsRSCC(props) {
    BFactor vs RSCC Date: Wed, 13 Mar 2024 11:18:58 +0000 Subject: [PATCH 24/25] Remove unnecessary return and blank lines in TorsionPlot This commit simplifies the TorsionPlot.tsx file by eliminating a return statement in the error catch block that was superfluous. Also, it removes unnecessary blank lines to improve code readability. --- webapp/src/database/DatabaseComponents/TorsionGraph.tsx | 2 +- webapp/src/shared/TorsionPlot/TorsionPlot.tsx | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/webapp/src/database/DatabaseComponents/TorsionGraph.tsx b/webapp/src/database/DatabaseComponents/TorsionGraph.tsx index c0f90f7f..7ca84613 100644 --- a/webapp/src/database/DatabaseComponents/TorsionGraph.tsx +++ b/webapp/src/database/DatabaseComponents/TorsionGraph.tsx @@ -70,7 +70,7 @@ export default function TorsionGraph(data) { Object.keys(torsionList) .sort() - .forEach(function (k, v) { + .forEach(function (k, _) { sortedTorsionList[k] = torsionList[k]; }); diff --git a/webapp/src/shared/TorsionPlot/TorsionPlot.tsx b/webapp/src/shared/TorsionPlot/TorsionPlot.tsx index 71a11f99..cf7d1d66 100644 --- a/webapp/src/shared/TorsionPlot/TorsionPlot.tsx +++ b/webapp/src/shared/TorsionPlot/TorsionPlot.tsx @@ -19,7 +19,6 @@ export default function TorsionPlot({ const [linkageFound, setLinkageFound] = useState(false); useEffect(() => { - if (sortedTorsionList === undefined) { return; } @@ -65,7 +64,6 @@ export default function TorsionPlot({ .catch((error) => { console.error(error); setLinkageFound(false); - return }); const overlayPhi: number[] = []; From 455d7b2c7c72d715a6c5341f5d4a82c045c1b899 Mon Sep 17 00:00:00 2001 From: Jordan Dialpuri <44945647+Dialpuri@users.noreply.github.com> Date: Wed, 13 Mar 2024 11:19:22 +0000 Subject: [PATCH 25/25] Refactor BFactorVsRSCC.tsx for better readability This commit cleans up the BFactorVsRSCC.tsx file. It modifies the calculatePoints function declaration layout and improves console.log formatting. It also eliminates unnecessary blank lines and corrects syntax to enhance code readability. --- webapp/src/database/DatabaseComponents/BFactorVsRSCC.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/webapp/src/database/DatabaseComponents/BFactorVsRSCC.tsx b/webapp/src/database/DatabaseComponents/BFactorVsRSCC.tsx index 79b0da07..1cf9b4d9 100644 --- a/webapp/src/database/DatabaseComponents/BFactorVsRSCC.tsx +++ b/webapp/src/database/DatabaseComponents/BFactorVsRSCC.tsx @@ -2,7 +2,9 @@ import React, { useEffect, useState, lazy } from 'react'; const Plot = lazy(async () => await import('react-plotly.js')); -function calculatePoints(data): [number[], number[], string[], number[], number[], string[]] { +function calculatePoints( + data +): [number[], number[], string[], number[], number[], string[]] { const glycans = data.data.glycans; const xAxis: number[] = []; @@ -40,10 +42,10 @@ export default function BFactorVsRSCC(props) { const [badTrace, setBadTrace] = useState({}); useEffect(() => { - const [xAxis, yAxis, text, errorXAxis, errorYAxis, errorText] = calculatePoints(props); + const [xAxis, yAxis, text, errorXAxis, errorYAxis, errorText] = + calculatePoints(props); const maxX = Math.max(Math.max(...xAxis), Math.max(...errorXAxis)) + 5; - console.log(errorXAxis, xAxis) setCorrTrace({ x: [0, maxX], y: [0.7, 0.7], @@ -73,7 +75,6 @@ export default function BFactorVsRSCC(props) { symbol: 'o', }, name: 'No Issues', - }); setBadTrace({