diff --git a/screen2.0/package.json b/screen2.0/package.json index 9631c1a5..be152630 100644 --- a/screen2.0/package.json +++ b/screen2.0/package.json @@ -45,7 +45,7 @@ "@types/node": "^20.5.0", "@types/react": "^18.2.20", "@types/react-dom": "18.2.7", - "@weng-lab/psychscreen-ui-components": "^0.7.8", + "@weng-lab/psychscreen-ui-components": "^0.8.0-a.2", "@weng-lab/ts-ztable": "^4.0.1", "autoprefixer": "10.4.15", "eslint": "8.47.0", @@ -58,6 +58,7 @@ "postcss": "8.4.27", "react": "18.2.0", "react-dom": "18.2.0", + "react-dropzone": "^14.2.3", "server": "^1.0.38", "sharp": "^0.32.4", "tailwindcss": "3.3.3", diff --git a/screen2.0/src/app/applets/multi-region-search/page.tsx b/screen2.0/src/app/applets/multi-region-search/page.tsx index 037eb7e3..257811c2 100644 --- a/screen2.0/src/app/applets/multi-region-search/page.tsx +++ b/screen2.0/src/app/applets/multi-region-search/page.tsx @@ -1,10 +1,12 @@ "use client" import { Typography } from "@mui/material" +import BedUpload from "../../../common/components/BedUpload" export default function MultiRegionSearch() { return (
- This is the Multi-Region Search page + Find Intersecting cCREs from BED file +
) } diff --git a/screen2.0/src/app/page.tsx b/screen2.0/src/app/page.tsx index ddbd87ee..bc4209e7 100644 --- a/screen2.0/src/app/page.tsx +++ b/screen2.0/src/app/page.tsx @@ -2,15 +2,13 @@ "use client" import { Typography } from "@mui/material" - import MainSearch from "../common/components/MainSearch" // Grid v2 isn't declared stable yet, but using it now as it's what MUI is currently developing out import Grid2 from "@mui/material/Unstable_Grid2/Grid2" - import homeImage from "../../public/homeImage.png" - import Image from "next/image" +import BedUpload from "../common/components/BedUpload" export default function Home() { return ( diff --git a/screen2.0/src/app/search/ccredetails/ccredetails.tsx b/screen2.0/src/app/search/ccredetails/ccredetails.tsx index 1932978b..1dacf0ea 100644 --- a/screen2.0/src/app/search/ccredetails/ccredetails.tsx +++ b/screen2.0/src/app/search/ccredetails/ccredetails.tsx @@ -1,7 +1,7 @@ "use client" import React from "react" import { Tabs, Typography, Paper, ThemeProvider, AppBar, Toolbar, IconButton, Drawer, Box } from "@mui/material" -import { GenomicRegion } from "../types" +import { GenomicRegion, LinkedGenesData } from "../types" import Grid2 from "@mui/material/Unstable_Grid2/Grid2" import { StyledTab } from "../ccresearch" import { InSpecificBiosamples } from "./inspecificbiosample" @@ -22,16 +22,7 @@ type CcreDetailsProps = { assembly: string region: GenomicRegion globals: any - genes: { - pc: { - name: string - __typename: string - }[] - all: { - name: string - __typename: string - }[] - } + genes: LinkedGenesData } export const CcreDetails: React.FC = ({ accession, region, globals, assembly, genes }) => { @@ -154,8 +145,8 @@ export const CcreDetails: React.FC = ({ accession, region, glo assembly={assembly} coordinates={{ chromosome: region.chrom, - start: +region.start.toString().replace(/\D/g, ""), - end: +region.end.toString().replace(/\D/g, ""), + start: +region.start.replace(/\D/g, ""), + end: +region.end.replace(/\D/g, ""), }} /> )} diff --git a/screen2.0/src/app/search/ccredetails/gene-expression.tsx b/screen2.0/src/app/search/ccredetails/gene-expression.tsx index b86a8893..e5191ee2 100644 --- a/screen2.0/src/app/search/ccredetails/gene-expression.tsx +++ b/screen2.0/src/app/search/ccredetails/gene-expression.tsx @@ -28,11 +28,12 @@ import { OptionsScale, } from "../../applets/gene-expression/options" import { GeneExpressionInfoTooltip } from "../../applets/gene-expression/const" +import { LinkedGenesData } from "../types" export function GeneExpression(props: { accession: string assembly: string - genes: { pc: { name: string; __typename: string }[]; all: { name: string; __typename: string }[] } + genes: LinkedGenesData hamburger: boolean }) { const pathname = usePathname() @@ -43,7 +44,7 @@ export function GeneExpression(props: { const [open, setState] = useState(true) const [current_assembly, setAssembly] = useState(props.assembly ? props.assembly : "GRCh38") - const [current_gene, setGene] = useState(props.genes.pc[0].name) + const [current_gene, setGene] = useState(props.genes.distancePC[0].name) const [biosamples, setBiosamples] = useState(["cell line", "in vitro differentiated cells", "primary cell", "tissue"]) const [cell_components, setCellComponents] = useState(["cell"]) @@ -88,8 +89,8 @@ export function GeneExpression(props: { .then((data) => { setData(data) let geneList: string[] = [] - for (let g of props.genes.pc) if (!geneList.includes(g.name)) geneList.push(g.name) - for (let g of props.genes.all) if (!geneList.includes(g.name)) geneList.push(g.name) + for (let g of props.genes.distancePC) if (!geneList.includes(g.name)) geneList.push(g.name) + for (let g of props.genes.distanceAll) if (!geneList.includes(g.name)) geneList.push(g.name) setOptions(geneList) setLoading(false) }) diff --git a/screen2.0/src/app/search/ccresearch.tsx b/screen2.0/src/app/search/ccresearch.tsx index 42b42d7d..14a37190 100644 --- a/screen2.0/src/app/search/ccresearch.tsx +++ b/screen2.0/src/app/search/ccresearch.tsx @@ -7,10 +7,11 @@ import { CcreDetails } from "./ccredetails/ccredetails" import Grid2 from "../../common/mui-client-wrappers/Grid2" import { ReadonlyURLSearchParams, useSearchParams } from "next/navigation" import styled from "@emotion/styled" +import { MainResultTableRows } from "./types" export const StyledTab = styled(Tab)(() => ({ textTransform: "none", })) -export const CcreSearch = ({ mainQueryParams, ccrerows, globals, assembly }) => { +export const CcreSearch = (props: { mainQueryParams, ccrerows: MainResultTableRows, globals, assembly }) => { const searchParams: ReadonlyURLSearchParams = useSearchParams()! const [value, setValue] = React.useState(searchParams.get("accession") ? 1 : 0) @@ -18,15 +19,14 @@ export const CcreSearch = ({ mainQueryParams, ccrerows, globals, assembly }) => setValue(newValue) } - //This needs to be changed, this will switch to the details tab anytime filter is updated useEffect(() => { if (searchParams.get("accession")) { setValue(1) } }, [searchParams]) - //Need meaningful variable names please - let f = ccrerows.find((c) => c.accession === searchParams.get("accession")) + //Need meaningful variable names please, is showing that this is undefined and throwing an error when using back button on details page since accession is undefined + let f = props.ccrerows.find((c) => c.accession === searchParams.get("accession")) const region = { start: f?.start, chrom: f?.chromosome, end: f?.end } return ( @@ -42,14 +42,14 @@ export const CcreSearch = ({ mainQueryParams, ccrerows, globals, assembly }) => {value === 0 && ( - + @@ -61,8 +61,8 @@ export const CcreSearch = ({ mainQueryParams, ccrerows, globals, assembly }) => diff --git a/screen2.0/src/app/search/page.tsx b/screen2.0/src/app/search/page.tsx index 77715c09..28832146 100644 --- a/screen2.0/src/app/search/page.tsx +++ b/screen2.0/src/app/search/page.tsx @@ -1,9 +1,10 @@ // Search Results Page import { CcreSearch } from "./ccresearch" -import { getGlobals, MainQuery } from "../../common/lib/queries" +import { MainQuery, getGlobals, linkedGenesQuery } from "../../common/lib/queries" import { ApolloQueryResult } from "@apollo/client" -import { cCREData, CellTypeData, MainQueryParams } from "./types" +import { cCREData, CellTypeData, MainQueryParams, MainResultTableRow, MainResultTableRows } from "./types" import { checkTrueFalse, passesCriteria } from "../../common/lib/filter-helpers" +import { LinkedGenes } from "./ccredetails/linkedgenes" export default async function Search({ // Object from URL, see https://nextjs.org/docs/app/api-reference/file-conventions/page#searchparams-optional @@ -26,11 +27,11 @@ export default async function Search({ InVitro: searchParams.InVitro ? checkTrueFalse(searchParams.InVitro) : true, Biosample: searchParams.Biosample ? { - selected: true, - biosample: searchParams.Biosample, - tissue: searchParams.BiosampleTissue, - summaryName: searchParams.BiosampleSummary, - } + selected: true, + biosample: searchParams.Biosample, + tissue: searchParams.BiosampleTissue, + summaryName: searchParams.BiosampleSummary, + } : { selected: false, biosample: null, tissue: null, summaryName: null }, // Chromatin Filters // "[...]_s" = start, "[...]_e" = end. @@ -55,8 +56,8 @@ export default async function Search({ TF: searchParams.TF ? checkTrueFalse(searchParams.TF) : true, } - //Main query. Returns -1 if query returns an error - const mainQueryResult: ApolloQueryResult | -1 = await MainQuery( + //Main query + const mainQueryResult: ApolloQueryResult = await MainQuery( mainQueryParams.assembly, mainQueryParams.chromosome, mainQueryParams.start, @@ -72,26 +73,14 @@ export default async function Search({ * @param QueryResult Result from Main Query * @returns rows usable by the DataTable component */ - const generateRows = (QueryResult: ApolloQueryResult, biosample: string | null) => { - const rows: { - //atac will need to be changed from string to number when that data is available - accession: string - class: string - chromosome: string - start: string - end: string - dnase?: number - atac: string - h3k4me3?: number - h3k27ac?: number - ctcf?: number - linkedGenes: { pc: { name: string }[]; all: { name: string }[] } - }[] = [] + async function generateRows(QueryResult: ApolloQueryResult, biosample: string | null) { const cCRE_data: cCREData[] = QueryResult.data.cCRESCREENSearch - let offset = 0 - cCRE_data.forEach((currentElement, index) => { + const rows: MainResultTableRows = [] + // Used for Linked Gene Query + const accessions: string[] = [] + cCRE_data.forEach((currentElement) => { if (passesCriteria(currentElement, biosample, mainQueryParams)) { - rows[index - offset] = { + rows.push({ accession: currentElement.info.accession, class: currentElement.pct, chromosome: currentElement.chrom, @@ -103,14 +92,27 @@ export default async function Search({ h3k4me3: biosample ? currentElement.ctspecific.h3k4me3_zscore : currentElement.promoter_zscore, h3k27ac: biosample ? currentElement.ctspecific.h3k27ac_zscore : currentElement.enhancer_zscore, ctcf: biosample ? currentElement.ctspecific.ctcf_zscore : currentElement.ctcf_zscore, - linkedGenes: { pc: currentElement.genesallpc.pc.intersecting_genes, all: currentElement.genesallpc.all.intersecting_genes }, - } - } - // Offset incremented to account for missing rows which do not meet filter criteria - else { - offset += 1 + linkedGenes: { distancePC: currentElement.genesallpc.pc.intersecting_genes, distanceAll: currentElement.genesallpc.all.intersecting_genes, CTCF_ChIAPET: [], RNAPII_ChIAPET: [] }, + }) + accessions.push(currentElement.info.accession) } }) + const otherLinked = await linkedGenesQuery(mainQueryParams.assembly, accessions) + //Need to add in biosample information for the hover + rows.forEach((row: MainResultTableRow) => { + const accession = row.accession + const genesToAdd = otherLinked[accession] ?? null + + genesToAdd && genesToAdd.genes.forEach(gene => { + if (gene.linkedBy === "CTCF-ChIAPET") { + row.linkedGenes.CTCF_ChIAPET.push({name: gene.geneName, biosample: gene.biosample}) + } + else if (gene.linkedBy === "RNAPII-ChIAPET"){ + row.linkedGenes.RNAPII_ChIAPET.push({name: gene.geneName, biosample: gene.biosample}) + } + }); + + }) return rows } @@ -120,7 +122,7 @@ export default async function Search({ diff --git a/screen2.0/src/app/search/types.ts b/screen2.0/src/app/search/types.ts index 668c27e2..36a10f71 100644 --- a/screen2.0/src/app/search/types.ts +++ b/screen2.0/src/app/search/types.ts @@ -1,7 +1,7 @@ export type GenomicRegion = { chrom: string - start: number - end: number + start: string + end: string } export type cCREData = { @@ -110,3 +110,27 @@ export type FilteredBiosampleData = [ } }[], ][] + +export type MainResultTableRows = MainResultTableRow[] + +export type MainResultTableRow = { + //atac will need to be changed from string to number when that data is available + accession: string + class: string + chromosome: string + start: string + end: string + dnase?: number + atac: string + h3k4me3?: number + h3k27ac?: number + ctcf?: number + linkedGenes: LinkedGenesData +} + +export type LinkedGenesData = { + distancePC: { name: string }[], + distanceAll: { name: string }[], + CTCF_ChIAPET: { name: string, biosample: string }[], + RNAPII_ChIAPET: { name: string, biosample: string }[] +} diff --git a/screen2.0/src/common/components/BedUpload.tsx b/screen2.0/src/common/components/BedUpload.tsx new file mode 100644 index 00000000..d49f5a90 --- /dev/null +++ b/screen2.0/src/common/components/BedUpload.tsx @@ -0,0 +1,42 @@ +"use client" + +import React, { useState } from "react" +import { Button, Typography, Box } from "@mui/material" +import Dropzone from "react-dropzone" + + +/** + * Things to improve upon old upload: + * - Prevent upload of non BED files + * - Clear files + * - Convert byte size to mb/gb + */ +const BedUpload = () => { + const [files, setFiles] = useState([]) + + function handleFileUploads(uploads){ + setFiles([...files, ...uploads]) + } + + return ( + + handleFileUploads(acceptedFiles)}> + {({ getRootProps, getInputProps }) => ( +
+
+ + +
+
+ )} +
+
+ {files.map((file: File, index: number) => { + return {file.name} - {file.size} bytes + })} + {files.length > 0 && } +
+ ) +} + +export default BedUpload \ No newline at end of file diff --git a/screen2.0/src/common/components/MainResultsTable.tsx b/screen2.0/src/common/components/MainResultsTable.tsx index de38be95..3e37ad5e 100644 --- a/screen2.0/src/common/components/MainResultsTable.tsx +++ b/screen2.0/src/common/components/MainResultsTable.tsx @@ -1,141 +1,223 @@ "use client" +import { DataTable, DataTableProps, DataTableColumn } from "@weng-lab/psychscreen-ui-components" +import React, { useCallback, useEffect, useState } from "react" +import { Box, Typography, Menu, Checkbox, Stack, MenuItem, Container, FormControlLabel, FormGroup } from "@mui/material" +import { MainResultTableRow } from "../../app/search/types" +import ArrowRightIcon from '@mui/icons-material/ArrowRight'; import { usePathname, useRouter, useSearchParams } from "next/navigation" -import { DataTable, DataTableProps, DataTableColumn } from "@weng-lab/psychscreen-ui-components" -import React from "react" -import { Box, Button, Typography } from "@mui/material" -import Link from "next/link" -import { ObjectFlags } from "typescript" +function MainResultsTable(props: Partial>) { + const [distance, setDistance] = useState(true) + const [CTCF_ChIAPET, setCTCF_ChIAPET] = useState(false) + const [RNAPII_ChIAPET, setRNAPII_ChIAPET] = useState(false) -let COLUMNS = (rows) => { - // can prob just use link instead here const router = useRouter() const pathname = usePathname() const searchParams: any = useSearchParams()! - const createQueryString = React.useCallback( + const createQueryString = useCallback( (name: string, value: string) => { const params = new URLSearchParams(searchParams) params.set(name, value) return params.toString() - }, - [searchParams] - ) + }, [searchParams]) - let col: DataTableColumn[] = [ - { - header: "Accession", - value: (row: { accession: string }) => row.accession, - }, - { - header: "Class", - value: (row: { class: string }) => row.class, - }, - { - header: "Chr", - value: (row: { chromosome: any }) => row.chromosome, - }, - { - header: "Start", - value: (row: { start: number }) => row.start, - }, - { - header: "End", - value: (row: { end: number }) => row.end, - }, - { - header: "ATAC", - value: (row: { atac: number }) => row.atac, - }, - ] + const columns = (funcSetDistance: React.Dispatch>, funcSetCTCF_ChIAPET: React.Dispatch>, funcSetRNAPII_ChIAPET: React.Dispatch>) => { + let cols: DataTableColumn[] = [ + { + header: "Accession", + value: (row: { accession: string }) => row.accession, + }, + { + header: "Class", + value: (row: { class: string }) => row.class, + }, + { + header: "Chr", + value: (row: { chromosome: any }) => row.chromosome, + }, + { + header: "Start", + value: (row: { start: string }) => row.start, + }, + { + header: "End", + value: (row: { end: string }) => row.end, + }, + { + header: "ATAC", + //Atac is a string because the data does not exist and is "TBD" for now + value: (row: { atac: string }) => row.atac, + }, + ] - if (rows[0] && rows[0].dnase !== null) { - col.push({ - header: "DNase", - value: (row) => (row.dnase && row.dnase.toFixed(2)) || 0, - }) - } - if (rows[0] && rows[0].ctcf !== null) { - col.push({ - header: "CTCF", - value: (row) => (row.ctcf && row.ctcf.toFixed(2)) || 0, - }) - } - if (rows[0] && rows[0].h3k27ac != null) { - col.push({ - header: "H3K27ac", - value: (row) => (row.h3k27ac && row.h3k27ac.toFixed(2)) || 0, - }) - } - if (rows[0] && rows[0].h3k4me3 != null) { - col.push({ - header: "H3K4me3", - value: (row) => (row.h3k4me3 && row.h3k4me3.toFixed(2)) || 0, - }) - } + if (props.rows[0] && props.rows[0].dnase !== null) { + cols.push({ + header: "DNase", + value: (row) => (row.dnase && row.dnase.toFixed(2)) || 0, + }) + } + if (props.rows[0] && props.rows[0].ctcf !== null) { + cols.push({ + header: "CTCF", + value: (row) => (row.ctcf && row.ctcf.toFixed(2)) || 0, + }) + } + if (props.rows[0] && props.rows[0].h3k27ac != null) { + cols.push({ + header: "H3K27ac", + value: (row) => (row.h3k27ac && row.h3k27ac.toFixed(2)) || 0, + }) + } + if (props.rows[0] && props.rows[0].h3k4me3 != null) { + cols.push({ + header: "H3K4me3", + value: (row) => (row.h3k4me3 && row.h3k4me3.toFixed(2)) || 0, + }) + } - //Is there a good way to sort linked genes? Set to "" because I'm not sure - col.push({ - header: "Linked\u00A0Genes\u00A0(Distance)", - value: (row) => "", - render: (row) => ( - - - {`PC: `} - - - {Object.values(row.linkedGenes.pc).map((gene: { name: string; __typename: string }, i: number) => ( - - {i < row.linkedGenes.all.length - 1 ? `\u00A0${gene.name}, ` : `\u00A0${gene.name}`} - - ))} - - - - {`All: `} - - - {Object.values(row.linkedGenes.all).map((gene: { name: string; __typename: string }, i: number) => ( - - {i < row.linkedGenes.all.length - 1 ? `\u00A0${gene.name}, ` : `\u00A0${gene.name}`} - - ))} - - - ), - }) + //Whenever the state of the checkboxes conflicts with the state of the main component, it triggers a rerender + cols.push({ + header: "Linked\u00A0Genes\u00A0(Distance)", + value: () => "", + unsortable: true, + HeaderRender: () => { + const [checkedState, setCheckedState] = useState([distance, CTCF_ChIAPET, RNAPII_ChIAPET]) + const [anchorEl, setAnchorEl] = useState(null) - return col -} + var open = Boolean(anchorEl); -function MainResultsTable(props: Partial>) { - const router = useRouter() - const pathname = usePathname() - const searchParams: any = useSearchParams()! + const handleClose = () => { + console.log("closed properly") + funcSetDistance(checkedState[0]) + funcSetCTCF_ChIAPET(checkedState[1]) + funcSetRNAPII_ChIAPET(checkedState[2]) + setAnchorEl(null); + }; - const createQueryString = React.useCallback( - (name: string, value: string) => { - const params = new URLSearchParams(searchParams) - params.set(name, value) + const handleClick = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; - return params.toString() - }, - [searchParams] - ) + const handleCheckboxChange = (event: React.ChangeEvent, value: 0 | 1 | 2) => { + setCheckedState(checkedState.map((prevValue, index) => { + if (index === value) { + return event.target.checked + } else { + return prevValue + } + })) + }; + + return ( + + + + Linked Genes + + + + + handleCheckboxChange(event, 0)} />} label={`Distance`} /> + + + handleCheckboxChange(event, 1)} />} label={`CTCF-ChIAPET`} /> + + + handleCheckboxChange(event, 2)} />} label={`RNAPII-ChIAPET`} /> + + + + + + ) + }, + render: (row) => { + return ( + <> + {distance && + + {`PC:\u00A0`} + + + {Object.values(row.linkedGenes.distancePC).map((gene: { name: string }, i: number) => ( + + {i < row.linkedGenes.distancePC.length - 1 ? `\u00A0${gene.name},\u00A0` : `\u00A0${gene.name}`} + + ))} + + } + {distance && + + {`All:\u00A0`} + + + {Object.values(row.linkedGenes.distanceAll).map((gene: { name: string }, i: number) => ( + + {i < row.linkedGenes.distanceAll.length - 1 ? `\u00A0${gene.name},\u00A0` : `\u00A0${gene.name}`} + + ))} + + } + {CTCF_ChIAPET && + + {`CTCF-ChIAPET:\u00A0`} + + + {row.linkedGenes.CTCF_ChIAPET.length == 0 ? "none" : Object.values(row.linkedGenes.CTCF_ChIAPET).map((gene: { name: string, biosample: string }, i: number) => ( + + {i < row.linkedGenes.CTCF_ChIAPET.length - 1 ? `\u00A0${gene.name},\u00A0` : `\u00A0${gene.name}`} + + ))} + + } + {RNAPII_ChIAPET && + + {`RNAPII-ChIAPET:\u00A0`} + + + {row.linkedGenes.RNAPII_ChIAPET.length == 0 ? "none" : Object.values(row.linkedGenes.RNAPII_ChIAPET).map((gene: { name: string, biosample: string }, i: number) => ( + + {i < row.linkedGenes.RNAPII_ChIAPET.length - 1 ? `\u00A0${gene.name},\u00A0` : `\u00A0${gene.name}`} + + ))} + + } + + ) + }, + }) + console.log("columns recalculated") + return cols + } return ( { router.push(pathname + "?" + createQueryString("accession", r.accession)) }} tableTitle={props.tableTitle} - sortColumn={6} + sortColumn={0} + sortDescending /> ) } diff --git a/screen2.0/src/common/components/ResponsiveAppBar.tsx b/screen2.0/src/common/components/ResponsiveAppBar.tsx index 3522d534..df6d8838 100644 --- a/screen2.0/src/common/components/ResponsiveAppBar.tsx +++ b/screen2.0/src/common/components/ResponsiveAppBar.tsx @@ -194,7 +194,7 @@ function ResponsiveAppBar() { {/* Wrap in next/link to enable dyanic link changing from basePath in next.config.js */} - {page.pageName} + {page.pageName} ))} diff --git a/screen2.0/src/common/lib/queries.ts b/screen2.0/src/common/lib/queries.ts index f56260a8..31c354f8 100644 --- a/screen2.0/src/common/lib/queries.ts +++ b/screen2.0/src/common/lib/queries.ts @@ -299,6 +299,69 @@ function cCRE_QUERY_VARIABLES(assembly: string, chromosome: string, start: numbe } } +const LINKED_GENES_QUERY = gql` + query ($assembly: String!, $accession: [String]!) { + linkedGenesQuery(assembly: $assembly, accession: $accession) { + assay + accession + celltype + gene + } + } +` + +const GENE_QUERY = gql` + query($assembly: String!, $name_prefix: [String!]) { + gene(assembly: $assembly, name_prefix: $name_prefix) { + name + id + } + } +` + +export async function linkedGenesQuery(assembly: "GRCh38" | "mm10", accession: string[]) { + let returnData: {[key: string]: {genes: {geneName: string, linkedBy: "CTCF-ChIAPET" | "RNAPII-ChIAPET", biosample: string}[]}} = {} + let geneIDs: string[] = [] + let linkedGenes: ApolloQueryResult + let geneNames: ApolloQueryResult + //Attempt first linked genes query + try { + linkedGenes = await getClient().query({ + query: LINKED_GENES_QUERY, + variables: { assembly, accession }, + }) + linkedGenes.data.linkedGenesQuery.forEach((entry) => { + !geneIDs.includes(entry.gene.split(".")[0]) && geneIDs.push(entry.gene.split(".")[0]) + }) + //Attempt to lookup gene names + try { + geneNames = await getClient().query({ + query: GENE_QUERY, + variables: { assembly: assembly, name_prefix: geneIDs }, + }) + //If both queries are successful, go through each of linkedGenes.data.linkedGenesQuery, find the accession and (if doesnt exist) add to linkedGenesData along with any gene names matching the ID in queryRes2 + linkedGenes.data.linkedGenesQuery.forEach((entry) => { + // if returnData does not have an entry for that accession, and if there is a gene in query2 with an id that matches + if (geneNames.data && (!Object.hasOwn(returnData, entry.accession)) && (geneNames.data.gene.find((x) => x.id === entry.gene)!== undefined)){ + Object.defineProperty(returnData, entry.accession, {value: {genes: [{geneName: geneNames.data.gene.find((x) => x.id === entry.gene).name, linkedBy: entry.assay, biosample: entry.celltype}]}, writable: true, enumerable: true, configurable: true}) + } + // if returnData does already have a linked gene for that accession, add the linked gene to the existing data + else if (geneNames.data && (Object.hasOwn(returnData, entry.accession)) && (geneNames.data.gene.find((x) => x.id === entry.gene)!== undefined)){ + Object.defineProperty(returnData[entry.accession], "genes", {value: [...returnData[entry.accession].genes, {geneName: geneNames.data.gene.find((x) => x.id === entry.gene).name, linkedBy: entry.assay, biosample: entry.celltype}], writable: true, enumerable: true, configurable: true}) + } + }) + } catch (error) { + console.log("Gene Name Lookup Failed") + console.log(error) + } + } catch (error) { + console.log(error) + } + //for some reason, the formatting of the data (newlines) aren't consistent. Don't think this has any effect though + return returnData +} + + /** * * @param assembly string, "GRCh38" or "mm10" @@ -309,9 +372,8 @@ function cCRE_QUERY_VARIABLES(assembly: string, chromosome: string, start: numbe * @returns cCREs matching the search */ export async function MainQuery(assembly: string, chromosome: string, start: number, end: number, biosample: string = null) { - console.log("queried with: " + assembly, chromosome, start, end, biosample) - - var data: ApolloQueryResult | -1 + // console.log("queried with: " + assembly, chromosome, start, end, biosample) + let data: ApolloQueryResult try { data = await getClient().query({ query: biosample ? cCRE_QUERY_WITH_BIOSAMPLES : cCRE_QUERY, @@ -319,7 +381,6 @@ export async function MainQuery(assembly: string, chromosome: string, start: num }) } catch (error) { console.log(error) - data = -1 } finally { return data } @@ -333,7 +394,6 @@ export async function biosampleQuery() { }) } catch (error) { console.log(error) - data = -1 } finally { return data } @@ -363,8 +423,8 @@ export async function UMAPQuery(assembly: "grch38" | "mm10", assay: "DNase" | "H * @returns the shortened byCellType file from https://downloads.wenglab.org/databyct.json */ export const getGlobals = async (assembly: "GRCh38" | "mm10") => { - console.log(assembly) - var res: Response + // console.log(assembly) + let res: Response if (assembly === "GRCh38") { res = await fetch("https://downloads.wenglab.org/databyct.json") } else if (assembly === "mm10") { diff --git a/screen2.0/yarn.lock b/screen2.0/yarn.lock index 040626ed..b6f44525 100644 --- a/screen2.0/yarn.lock +++ b/screen2.0/yarn.lock @@ -1418,12 +1418,12 @@ __metadata: languageName: node linkType: hard -"@weng-lab/psychscreen-ui-components@npm:^0.7.8": - version: 0.7.8 - resolution: "@weng-lab/psychscreen-ui-components@npm:0.7.8" +"@weng-lab/psychscreen-ui-components@npm:^0.8.0-a.2": + version: 0.8.0-a.2 + resolution: "@weng-lab/psychscreen-ui-components@npm:0.8.0-a.2" peerDependencies: react: ">=16" - checksum: 516eed3de77f161f65f7c3e2af230faa9cc6218456d8d6123d1226da1c745f2b2458d1c9a0562ccd9c0bbc5ba7dd9b657ee8d9aac6532fdf10fcb728036f6e92 + checksum: d70fd09ad78cfa3558348f261172fb694e864c694b7bc6e6f2aacdba4b186f80991d1046cfb0e53780a1be9d4abbcbbf895e5dbe865041b7027dce85b0f38e34 languageName: node linkType: hard @@ -1795,6 +1795,13 @@ __metadata: languageName: node linkType: hard +"attr-accept@npm:^2.2.2": + version: 2.2.2 + resolution: "attr-accept@npm:2.2.2" + checksum: 496f7249354ab53e522510c1dc8f67a1887382187adde4dc205507d2f014836a247073b05e9d9ea51e2e9c7f71b0d2aa21730af80efa9af2d68303e5f0565c4d + languageName: node + linkType: hard + "autoprefixer@npm:10.4.15": version: 10.4.15 resolution: "autoprefixer@npm:10.4.15" @@ -3585,6 +3592,15 @@ __metadata: languageName: node linkType: hard +"file-selector@npm:^0.6.0": + version: 0.6.0 + resolution: "file-selector@npm:0.6.0" + dependencies: + tslib: ^2.4.0 + checksum: 7d051b6e5d793f3c6e2ab287ba5e7c2c6a0971bccc9d56e044c8047ba483e18f60fc0b5771c951dc707c0d15f4f36ccb4f1f1aaf385d21ec8f7700dadf8325ba + languageName: node + linkType: hard + "fill-range@npm:^7.0.1": version: 7.0.1 resolution: "fill-range@npm:7.0.1" @@ -6345,6 +6361,19 @@ __metadata: languageName: node linkType: hard +"react-dropzone@npm:^14.2.3": + version: 14.2.3 + resolution: "react-dropzone@npm:14.2.3" + dependencies: + attr-accept: ^2.2.2 + file-selector: ^0.6.0 + prop-types: ^15.8.1 + peerDependencies: + react: ">= 16.8 || 18.0.0" + checksum: 174b744d5ca898cf3d84ec1aeb6cef5211c446697e45dc8ece8287a03d291f8d07253206d5a1247ef156fd385d65e7de666d4d5c2986020b8543b8f2434e8b40 + languageName: node + linkType: hard + "react-is@npm:^16.13.1, react-is@npm:^16.7.0": version: 16.13.1 resolution: "react-is@npm:16.13.1" @@ -6694,7 +6723,7 @@ __metadata: "@types/node": ^20.5.0 "@types/react": ^18.2.20 "@types/react-dom": 18.2.7 - "@weng-lab/psychscreen-ui-components": ^0.7.8 + "@weng-lab/psychscreen-ui-components": ^0.8.0-a.2 "@weng-lab/ts-ztable": ^4.0.1 autoprefixer: 10.4.15 eslint: 8.47.0 @@ -6711,6 +6740,7 @@ __metadata: prettier-plugin-tailwindcss: ^0.4.1 react: 18.2.0 react-dom: 18.2.0 + react-dropzone: ^14.2.3 server: ^1.0.38 sharp: ^0.32.4 tailwindcss: 3.3.3 @@ -7703,11 +7733,11 @@ __metadata: "typescript@patch:typescript@5.1.6#~builtin": version: 5.1.6 - resolution: "typescript@patch:typescript@npm%3A5.1.6#~builtin::version=5.1.6&hash=5da071" + resolution: "typescript@patch:typescript@npm%3A5.1.6#~builtin::version=5.1.6&hash=85af82" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: f53bfe97f7c8b2b6d23cf572750d4e7d1e0c5fff1c36d859d0ec84556a827b8785077bc27676bf7e71fae538e517c3ecc0f37e7f593be913d884805d931bc8be + checksum: 21e88b0a0c0226f9cb9fd25b9626fb05b4c0f3fddac521844a13e1f30beb8f14e90bd409a9ac43c812c5946d714d6e0dee12d5d02dfc1c562c5aacfa1f49b606 languageName: node linkType: hard