From 2bfb976e3658d88c14f0f80c3c7fd59b7c50ac1a Mon Sep 17 00:00:00 2001 From: NishiPhalke Date: Fri, 9 Oct 2020 12:33:19 -0400 Subject: [PATCH] added snp search and coordinates suggestions --- debug.log | 1 + package.json | 2 +- src/components/customtrack/addtrack.tsx | 5 +- src/components/search/queries.ts | 14 +++ src/components/search/refseqsearchbox.tsx | 2 +- src/components/search/searchbox.tsx | 103 ++++++++++++++++------ src/components/search/types.ts | 2 +- src/components/search/utils.ts | 1 + src/genomes/default/default.tsx | 8 +- src/genomes/hg19/browser.tsx | 4 +- src/genomes/hg38/browser.tsx | 10 +-- src/genomes/mm10/browser.tsx | 2 +- src/genomes/page/page.tsx | 15 +++- 13 files changed, 120 insertions(+), 49 deletions(-) create mode 100644 debug.log diff --git a/debug.log b/debug.log new file mode 100644 index 0000000..4e5dba4 --- /dev/null +++ b/debug.log @@ -0,0 +1 @@ +[1009/083544.836:ERROR:directory_reader_win.cc(43)] FindFirstFile: The system cannot find the path specified. (0x3) diff --git a/package.json b/package.json index 0c6fa2f..2b58275 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gbwebapp", - "version": "2.0.11", + "version": "2.0.12", "private": true, "dependencies": { "bigwig-reader": "^1.3.1", diff --git a/src/components/customtrack/addtrack.tsx b/src/components/customtrack/addtrack.tsx index 1c5d393..68fb5c7 100644 --- a/src/components/customtrack/addtrack.tsx +++ b/src/components/customtrack/addtrack.tsx @@ -1,8 +1,5 @@ import React, { useState } from 'react'; -import { - AddTrackProps, - TrackType, -} from './types'; +import { AddTrackProps, TrackType } from './types'; import { Button, Modal, Message, Input, Radio, Dropdown } from 'semantic-ui-react'; import ColorPicker from './colorpicker'; import { TEST_QUERY } from './queries'; diff --git a/src/components/search/queries.ts b/src/components/search/queries.ts index b1c0305..cf25421 100644 --- a/src/components/search/queries.ts +++ b/src/components/search/queries.ts @@ -1,3 +1,15 @@ +export const SNP_AUTOCOMPLETE_QUERY = ` + query suggestions($assembly: String!, $snpid: String!) { + snpAutocompleteQuery(assembly: $assembly, snpid: $snpid) { + rsId + coordinates { + chromosome + start + end + } + } + }`; + export const GENE_AUTOCOMPLETE_QUERY = ` query Genes( $id: [String] @@ -8,6 +20,7 @@ export const GENE_AUTOCOMPLETE_QUERY = ` $end: Int $gene_type: String $havana_id: String + $orderby: String $name_prefix: String $limit: Int $assembly: String! @@ -20,6 +33,7 @@ export const GENE_AUTOCOMPLETE_QUERY = ` start: $start end: $end gene_type: $gene_type + orderby: $orderby havana_id: $havana_id name_prefix: $name_prefix limit: $limit diff --git a/src/components/search/refseqsearchbox.tsx b/src/components/search/refseqsearchbox.tsx index 88e4d69..c08d849 100644 --- a/src/components/search/refseqsearchbox.tsx +++ b/src/components/search/refseqsearchbox.tsx @@ -52,7 +52,7 @@ const RefSeqSearchBox: React.FC = (props) => {
= (props) => { const [searchVal, setSearchVal] = useState(); - const [selectedsearchVal, setSelectedsearchVal] = useState(); + const [selectedSearchVal, setSelectedsearchVal] = useState(); const [results, setResults] = useState(); const onSubmit = useCallback(() => { @@ -14,44 +14,89 @@ const SearchBox: React.FC = (props) => { props.onSearchSubmit && props.onSearchSubmit(searchVal); return; } - let gene = selectedsearchVal ? selectedsearchVal : results && results[0]; + let gene = selectedSearchVal ? selectedSearchVal : results && results[0]; + if (gene === undefined) return; + const coords = gene.description.split('\n'); props.onSearchSubmit && - isCoordinate(gene.description.split('\n')[1]) && - props.onSearchSubmit(gene.description.split('\n')[1]); - }, [searchVal, results, props, selectedsearchVal]); + isCoordinate(coords.length === 2 ? coords[1] : coords[0]) && + props.onSearchSubmit(coords.length === 2 ? coords[1] : coords[0], gene.title, !(coords.length === 2)); + }, [searchVal, results, props, selectedSearchVal]); const onSearchChange = useCallback( async (e, { value }) => { + let val: string = value.toLowerCase(); + let rs: Result[] = []; + setSearchVal(value); + if (val.startsWith('rs') && props.assembly === 'GRCh38') { + const response = await fetch('https://snps.staging.wenglab.org/graphql', { + method: 'POST', + body: JSON.stringify({ + query: SNP_AUTOCOMPLETE_QUERY, + variables: { snpid: value, assembly: 'hg38', limit: 3 }, + }), + headers: { 'Content-Type': 'application/json' }, + }); + let rst = (await response.json()).data?.snpAutocompleteQuery + ?.slice(0, 3) + .map( + (result: { + rsId: string; + coordinates: { chromosome: string; start: number; end: number }; + }) => ({ + title: result.rsId, + description: + result.coordinates.chromosome + + ':' + + result.coordinates.start + + '-' + + result.coordinates.end, + }) + ); + rs = uniq(rst, value); + } + if ( + value.toLowerCase().match(/^chr[0-9x-y]+$/g) && + value.toLowerCase().match(/^chr[0-9x-y]+$/g).length === 1 && + value.length <= 5 + ) { + rs = [ + { title: value + ':' + '1' + '-100000', description: '' + '\n' + value + ':' + '1' + '-100000' }, + { title: value + ':' + '1' + '-1000000', description: '' + '\n' + value + ':' + '1' + '-1000000' }, + { + title: value + ':' + '1' + '-10000000', + description: '' + '\n' + value + ':' + '1' + '-10000000', + }, + ]; + } const response = await fetch('https://ga.staging.wenglab.org/graphql', { method: 'POST', body: JSON.stringify({ query: GENE_AUTOCOMPLETE_QUERY, - variables: { name_prefix: value, assembly: props.assembly, limit: 3 }, + variables: { name_prefix: value, assembly: props.assembly, orderby: 'name', limit: 3 }, }), headers: { 'Content-Type': 'application/json' }, }); - setSearchVal(value); - let res: Result[] = uniq( - (await response.json()).data?.gene?.map( - (result: { - name: string; - id: string; - coordinates: { chromosome: string; start: number; end: number }; - }) => ({ - title: result.name, - description: - result.id + - '\n' + - result.coordinates.chromosome + - ':' + - result.coordinates.start + - '-' + - result.coordinates.end, - }) - ), - value + let genesRes = (await response.json()).data?.gene?.map( + (result: { + name: string; + id: string; + coordinates: { chromosome: string; start: number; end: number }; + }) => ({ + title: result.name, + description: + result.id + + '\n' + + result.coordinates.chromosome + + ':' + + result.coordinates.start + + '-' + + result.coordinates.end, + }) ); - setResults(res); + + let res: Result[] | undefined = + genesRes && genesRes.length === 0 && rs.length > 0 ? undefined : uniq(genesRes, value); + setResults(rs ? (res ? [...rs, ...res] : rs) : res); }, [props.assembly] ); @@ -64,7 +109,7 @@ const SearchBox: React.FC = (props) => { void; + onSearchSubmit: (domain: string, name?: string, isSnp?: boolean) => void; }; export type RefSeqSearchBoxProps = { diff --git a/src/components/search/utils.ts b/src/components/search/utils.ts index 72af0a8..06f2b8f 100644 --- a/src/components/search/utils.ts +++ b/src/components/search/utils.ts @@ -15,6 +15,7 @@ export const uniq = (results: Result[], d: string): Result[] => { }); if (!found) r.push(result); }); + return r.length ? r : [{ title: d, description: '' }]; }; diff --git a/src/genomes/default/default.tsx b/src/genomes/default/default.tsx index 27176d8..3cd5023 100644 --- a/src/genomes/default/default.tsx +++ b/src/genomes/default/default.tsx @@ -24,11 +24,11 @@ import { GenomicRange } from 'ts-bedkit'; const BamTrack_Limit = 50000; const transcriptPack_Limit = 3000000; const transcript_Limit = 10000000; -const bigbed_Limit = 10000000 +const bigbed_Limit = 10000000; const DefaultBrowser: React.FC = (props) => { let defaultTracksModes: Record = {}; - + console.log('props.domain',props.domain) defaultTracksModes['refseqgenes_transcript_track'] = ''; defaultTracksModes['refseqxeno_transcript_track'] = ''; @@ -373,7 +373,7 @@ const DefaultBrowser: React.FC = (props) => { id={peak.title} /> - ) : props.domain.end - props.domain.start > bigbed_Limit && !peak.displayMode ? ( + ) : props.domain.end - props.domain.start > bigbed_Limit && !peak.displayMode ? ( = (props) => { titleSize={12} trackMargin={12} /> - ): ( + ) : ( [ const BamTrack_Limit = 50000; const transcriptPack_Limit = 3000000; const transcript_Limit = 10000000; -const bigbed_Limit = 10000000 +const bigbed_Limit = 10000000; const Hg19Browser: React.FC = (props) => { let defaultTracksModes: Record = {}; @@ -369,7 +369,7 @@ const Hg19Browser: React.FC = (props) => { titleSize={12} trackMargin={12} /> - ): ( + ) : ( = (props) => { let defaultTracksModes: Record = {}; @@ -65,12 +65,12 @@ const Hg38Browser: React.FC = (props) => { defaultTracksModes['AFR_LdTrack'] = 'dense'; defaultTracksModes['EUR_LdTrack'] = 'dense'; defaultTracksModes['AMR_LdTrack'] = 'dense'; - + let noOfRows = +(+Math.round((tracks(props.domain).length + 5) / 5)).toFixed() + 1; const [defaultTracks, setDefaultTracks] = useState>(defaultTracksModes); const customTracks = props.customTracks && Object.values(props.customTracks).filter((ct) => !ct.track.baiUrl); const bamCustomTracks = props.customTracks && Object.values(props.customTracks).filter((ct) => ct.track.baiUrl); - + let pks: Record< string, { peaks: { chr: string; start: number; end: number }[] | []; title: string; displayMode?: string } @@ -130,8 +130,8 @@ const Hg38Browser: React.FC = (props) => { } }} > - - + + {defaultTracks['transcript'] === 'hide' ? ( diff --git a/src/genomes/mm10/browser.tsx b/src/genomes/mm10/browser.tsx index 31c8132..8f5eebf 100644 --- a/src/genomes/mm10/browser.tsx +++ b/src/genomes/mm10/browser.tsx @@ -353,7 +353,7 @@ const Mm10Browser: React.FC = (props) => { titleSize={12} trackMargin={12} /> - ): ( + ) : ( = (props) => { } }, [props.assembly]); useEffect(() => { + console.log(chromLength,domain?.chromosome) if ((!genomeConfig[props.assembly] || !genomeConfig[props.assembly].domain) && chromLength) { if (transcriptCoordinates) { let genelength = transcriptCoordinates.coordinates.end - transcriptCoordinates.coordinates.start; @@ -354,7 +355,19 @@ const GenomeBrowserPage: React.FC = (props) => {
onDomainChanged(parseDomain(domain))} + onSearchSubmit={(domain: string, name?: string, isSnp?: boolean) => { + let d: Domain = parseDomain(domain); + console.log(d, 'd'); + if (isSnp) { + d = { + ...d, + start: d.start - 10000, + end: d.end + 10000, + }; + setAnchor(name); + } + onDomainChanged(d); + }} assembly={props.assembly === 'hg38' ? 'GRCh38' : props.assembly} />