diff --git a/src/components/SubsetsLoader.js b/src/components/SubsetsLoader.js index 94730bf4..227f23f6 100644 --- a/src/components/SubsetsLoader.js +++ b/src/components/SubsetsLoader.js @@ -67,7 +67,7 @@ function SubsetsResponse({ bioSubsetsHierarchies, allBioSubsets, datasetIds, def tree={tree} size={size} defaultTreeDepth={defaultTreeDepth} - sampleFilterScope="referenceid" + sampleFilterScope="allTermsFilters" /> ) diff --git a/src/components/SubsetsLoader.textClipping b/src/components/SubsetsLoader.textClipping deleted file mode 100644 index 74a98f0e..00000000 Binary files a/src/components/SubsetsLoader.textClipping and /dev/null differ diff --git a/src/components/classificationTree/SubsetsTree.js b/src/components/classificationTree/SubsetsTree.js index 979effd2..c0c1ee92 100644 --- a/src/components/classificationTree/SubsetsTree.js +++ b/src/components/classificationTree/SubsetsTree.js @@ -183,7 +183,6 @@ function Node({ const isSearchPossible = subset && canSearch(subset) const even = index % 2 === 0 const detailsPage = subsetId.match("cellosaurus") ? "cellline" : "subset" - const searchPage = subsetId.match("cellosaurus") ? "filterSearch" : "search" return (
)} - + @@ -241,7 +240,6 @@ function Node({ href={sampleSelectUrl({ subsets: [subset], datasetIds, - searchPage, sampleFilterScope })} > @@ -349,10 +347,10 @@ function useFilterTree(tree) { return { searchInput, debouncedSearchInput, setSearchInput, filteredTree } } -function sampleSelectUrl({ subsets, datasetIds, searchPage, sampleFilterScope }) { +function sampleSelectUrl({ subsets, datasetIds, sampleFilterScope }) { const filters = subsets.map(({ id }) => id).join(",") // here the `bioontology` parameter has to be used instead of `filters` for transfer to the search form - return sampleSearchPageFiltersLink({ datasetIds, searchPage, sampleFilterScope, filters }) + return sampleSearchPageFiltersLink({ datasetIds, sampleFilterScope, filters }) } function canSearch(subset) { diff --git a/src/components/searchForm/BiosamplesSearchForm.js b/src/components/searchForm/BiosamplesSearchForm.js index bd72a5cc..35415620 100644 --- a/src/components/searchForm/BiosamplesSearchForm.js +++ b/src/components/searchForm/BiosamplesSearchForm.js @@ -1,17 +1,14 @@ -import React, { useMemo, useState } from "react" //useEffect, -// import React, { useEffect, useMemo, useRef, useState } from "react" //useEffect, -import cn from "classnames" import { checkIntegerRange, makeFilters, - useCollationsByType, + useFiltersByType, validateBeaconQuery } from "../../hooks/api" +import React, { useMemo, useState } from "react" //useEffect, import { markdownToReact } from "../../utils/md" import { useForm } from "react-hook-form" import { CytoBandsUtility, - FormUtilitiesButtons, GeneSpansUtility, useFormUtilities } from "./BiosamplesFormUtilities" @@ -24,10 +21,12 @@ import { withUrlQuery } from "../../hooks/url-query" import { GeoCitySelector } from "./GeoCitySelector" import { GeneSymbolSelector } from "./GeneSymbolSelector" import ChromosomePreview from "./ChromosomePreview" +import { FaCogs } from "react-icons/fa" +import cn from "classnames" export const BiosamplesSearchForm = withUrlQuery( ({ urlQuery, setUrlQuery, ...props }) => ( -
+ ) ) export default BiosamplesSearchForm @@ -36,13 +35,13 @@ BiosamplesSearchForm.propTypes = { cytoBands: PropTypes.object.isRequired, isQuerying: PropTypes.bool.isRequired, setSearchQuery: PropTypes.func.isRequired, - requestTypeConfig: PropTypes.object.isRequired, + beaconQueryTypes: PropTypes.object.isRequired, requestTypeExamples: PropTypes.object.isRequired, parametersConfig: PropTypes.object.isRequired } function urlQueryToFormParam(urlQuery, key, parametersConfig) { - const isMulti = !!parametersConfig[key]?.isMulti ?? false + const isMulti = !!parametersConfig.parameters[key]?.isMulti ?? false const value = urlQuery[key] if (value == null) return value if (isMulti) { @@ -56,11 +55,11 @@ function useIsFilterlogicWarningVisible(watch) { const referenceid = watch("referenceid") const clinicalClasses = watch("clinicalClasses") const cohorts = watch("cohorts") - const freeFilters = watch("freeFilters") + const allTermsFilters = watch("allTermsFilters") const sex = watch("sex") const materialtype = watch("materialtype") const filters = makeFilters({ - freeFilters, + allTermsFilters, bioontology, referenceid, clinicalClasses, @@ -71,30 +70,23 @@ function useIsFilterlogicWarningVisible(watch) { return filterLogic === "AND" && filters.length > 1 } -export function Form({ - cytoBands, - isQuerying, - setSearchQuery, - requestTypeConfig, - requestTypeExamples, - parametersConfig, - urlQuery, - setUrlQuery, - useUtilitiesButtons = true, - useExamplesButtons = true -}) { +export function BeaconSearchForm({ + cytoBands, + isQuerying, + setSearchQuery, + beaconQueryTypes, + requestTypeExamples, + parametersConfig, + urlQuery, + setUrlQuery + }) { // const autoExecuteSearch = urlQuery.executeSearch === "true" - // const formRef = useRef(null); - // useEffect(() => { - // formRef.current.submit(); - // }, []); - const [example, setExample] = useState(null) let parameters = useMemo( () => - makeParameters(parametersConfig, requestTypeConfig, example), - [example, parametersConfig, requestTypeConfig] + makeParameters(parametersConfig, example), + [example, parametersConfig] ) const initialValues = transform(parameters, (r, v, k) => { @@ -121,48 +113,64 @@ export function Form({ // reset form when default values changes useDeepCompareEffect(() => reset(initialValues), [initialValues]) - // biosubsets lookup - const { data: biosubsetsResponse, isLoading: isBioSubsetsDataLoading } = useBioSubsets( - watch - ) - - const biosubsetsOptions = biosubsetsResponse?.response.results.map((value) => ({ + // all subsets lookup ----------------------------------------------------- // + var ct = "" + const { + data: allsubsetsResponse, + isLoading: isAllSubsetsDataLoading + } = useFilteringTerms( watch, ct ) + const allsubsetsOptions = allsubsetsResponse?.response?.filteringTerms?.map((value) => ({ + value: value.id, + label: `${value.id}: ${value.label} (${value.count})` + })) + parameters = merge({}, parameters, { + allTermsFilters: { options: allsubsetsOptions } + }) + + // biosubsets lookup ------------------------------------------------------ // + ct = "NCIT,pgx:icdom,pgx:icdot,UBERON" + const { + data: biosubsetsResponse, + isLoading: isBioSubsetsDataLoading + } = useFilteringTerms( watch, ct ) + const biosubsetsOptions = biosubsetsResponse?.response?.filteringTerms?.map((value) => ({ value: value.id, label: `${value.id}: ${value.label} (${value.count})` })) - parameters = merge({}, parameters, { bioontology: { options: biosubsetsOptions } }) - // referenceid lookup - const { data: refsubsetsResponse, isLoading: isRefSubsetsDataLoading } = useReferencesSubsets( - watch - ) - - const refsubsetsOptions = refsubsetsResponse?.response.results.map((value) => ({ + // referenceid lookup ----------------------------------------------------- // + ct = "PMID,GEOseries,GEOplatform,cellosaurus" + const { + data: refsubsetsResponse, + isLoading: isRefSubsetsDataLoading + } = useFilteringTerms( watch, ct ) + const refsubsetsOptions = refsubsetsResponse?.response?.filteringTerms?.map((value) => ({ value: value.id, label: `${value.id}: ${value.label} (${value.count})` - })) - + })) parameters = merge({}, parameters, { referenceid: { options: refsubsetsOptions } }) - // clinical lookup - const { data: clinicalResponse, isLoading: isClinicalDataLoading } = useClinicalSubsets( - watch - ) - - const clinicalOptions = clinicalResponse?.response.results.map((value) => ({ + // clinical lookup -------------------------------------------------------- // + ct = "TNM,NCITgrade,NCITstage,EFOfus" + const { + data: clinicalResponse, + isLoading: isClinicalDataLoading + } = useFilteringTerms( watch, ct ) + const clinicalOptions = clinicalResponse?.response?.filteringTerms?.map((value) => ({ value: value.id, label: `${value.id}: ${value.label} (${value.count})` })) - parameters = merge({}, parameters, { clinicalClasses: { options: clinicalOptions } }) + // ======================================================================== // + const { cytoBandPanelOpen, onCytoBandClick, @@ -189,177 +197,129 @@ export function Form({ const geoCity = watch("geoCity") const showGeoDistance = !parameters.geoCity.isHidden && geoCity != null - const [collapsedSections, setCollapsedSections] = useState({ - location: true, - filters: true - }); - - const toggleSection = (section) => { - setCollapsedSections((prevSections) => ({ - ...prevSections, - [section]: !prevSections[section] - })); - }; - - const handleExampleClicked = (reset, setExample, setUrlQuery, section) => (example) => { - // Reset URL query and set example - setUrlQuery({}, { replace: true }); - setExample(example); - - // Update the state to expand the specified section - setCollapsedSections((prevSections) => ({ - ...prevSections, - [section]: false, // Set the section to be expanded - })); - }; - - return ( <>
-
- { useExamplesButtons && ( - - + - )} -
- - {cytoBandPanelOpen && ( - - )} - {geneSpansPanelOpen && ( - - )} + beaconQueryTypes={beaconQueryTypes} + /> {errors?.global?.message && ( -
- {errors.global.message} -
+
+ {errors.global.message} +
)} - - + {!parameters.datasetIds.isHidden && ( + + )} +
+ + +
{!parameters.geneId.isHidden && ( - + )} - - {/*
*/} - -
toggleSection("location")} - style={{ padding: "10px 0" }} // Add padding before and after the title - > - Query by Position {collapsedSections.location ? Show : Hide} - {/*
*/} - {!collapsedSections.location && ( - <> -
e.stopPropagation()}> - - -
-
e.stopPropagation()}> - - -
-
e.stopPropagation()}> - - -
-
e.stopPropagation()}> - - -
- - )} +
+ + +
+
+ + +
+
+ + + +
- -
- - -
toggleSection("filters")} - style={{ padding: "10px 0" }} // Add padding before and after the title - > - Filtering Options {collapsedSections.filters ? Show : Hide} - {/*
*/} - {!collapsedSections.filters && ( - <> -
e.stopPropagation()}> - - +
+ + {parameters.filterLogic.label} - } - /> - + } + /> + {parameters.includeDescendantTerms.label} - } - /> -
-
e.stopPropagation()}> - - -
- - )} + } + />
- - {!parameters.geoCity.isHidden && (
@@ -503,12 +424,36 @@ export function Form({
)} +
+ + +
+
+ {geneSpansPanelOpen && ( + + )} + {cytoBandPanelOpen && ( + + )} +
+ + +
+
+ +
+ +
+ {example?.img && ( +
+ +
+ )} ) } +function QuerytypesTabs({ beaconQueryTypes, onQuerytypeClicked }) { + // console.log(beaconQueryTypes) + const startType = beaconQueryTypes[0] + const [selectedTab, setSelectedTab] = useState(startType) + // onQuerytypeClicked(selectedTab) + return ( +
+ +
+ ) +} + +export function InfodotTab(short, full) { + return ( + + {short} + + ) +} + function ExamplesButtons({ requestTypeExamples, onExampleClicked }) { return ( -
- {Object.entries(requestTypeExamples || []).map(([id, value]) => ( - - ))} +
+
+
+ Query Examples +
+
+ {Object.entries(requestTypeExamples || []).map(([id, value]) => ( + + ))} +
+
+
+ ) +} + +function FormUtilitiesButtons({ + onGeneSpansClick, + geneSpansPanelOpen, + onCytoBandClick, + cytoBandPanelOpen +}) { + return ( +
+
+
+ Form Utilities +
+
+ + +
+
) } @@ -550,21 +610,18 @@ function ExampleDescription({ example }) { function makeParameters( parametersConfig, - requestTypeConfig, example ) { // merge base parameters config and request config const mergedConfigs = merge( {}, // important to not mutate the object - parametersConfig, - requestTypeConfig?.parameters, + parametersConfig.parameters, example?.parameters ?? {} ) // add name the list let parameters = transform(mergedConfigs, (r, v, k) => { r[k] = { name: k, ...v } }) - return parameters } @@ -590,18 +647,20 @@ function validateForm(formValues) { start, end, geneId, + aminoacidChange, + genomicAlleleShortForm, bioontology, clinicalClasses, referenceid, cohorts, - freeFilters + allTermsFilters } = formValues const errors = [] const setMissing = (name) => errors.push([name, { type: "manual", message: "Parameter is missing" }]) - if (!referenceName && !referenceBases && !alternateBases && !start && !end && !variantType && !geneId && !bioontology && !referenceid && !freeFilters && !clinicalClasses && !cohorts) { + if (!referenceName && !referenceBases && !alternateBases && !start && !end && !variantType && !geneId && !aminoacidChange && !genomicAlleleShortForm && !bioontology && !referenceid && !allTermsFilters && !clinicalClasses && !cohorts) { !referenceName && setMissing("referenceName") !referenceBases && setMissing("referenceBases") !alternateBases && setMissing("alternateBases") @@ -612,8 +671,8 @@ function validateForm(formValues) { !bioontology && setMissing("bioontology") !clinicalClasses && setMissing("clinicalClasses") !referenceid && setMissing("referenceid") - !freeFilters && setMissing("freeFilters") - !cohorts && setMissing("freeFilters") + !allTermsFilters && setMissing("allTermsFilters") + !cohorts && setMissing("cohorts") } const queryError = validateBeaconQuery(formValues) @@ -624,37 +683,26 @@ function validateForm(formValues) { } errors.push(["global", error]) } - return errors } - - -// Maps FilteringTerms hook to apiReply usable by DataFetchSelect -function useBioSubsets(watchForm) { - const datasetIds = watchForm("datasetIds") - return useCollationsByType({ - datasetIds, - method: "counts", - collationTypes: "NCIT,pgx:icdom,pgx:icdot,UBERON" - }) +const handleExampleClicked = (reset, setExample, setUrlQuery) => (example) => { + setUrlQuery({}, { replace: true }) + setExample(example) } -function useReferencesSubsets(watchForm) { - const datasetIds = watchForm("datasetIds") - return useCollationsByType({ - datasetIds, - method: "counts", - collationTypes: "PMID,GEOseries,GEOplatform,cellosaurus" - }) +const handleQuerytypeClicked = (reset, setExample, setUrlQuery) => (example) => { + setUrlQuery({}, { replace: true }) + setExample(example) } -function useClinicalSubsets(watchForm) { +// Maps FilteringTerms hook to apiReply usable by DataFetchSelect +function useFilteringTerms(watchForm, ct) { const datasetIds = watchForm("datasetIds") - return useCollationsByType({ + return useFiltersByType({ datasetIds, method: "counts", - collationTypes: "TNM,NCITgrade,NCITstage,EFOfus" + collationTypes: ct }) } @@ -671,4 +719,3 @@ function FilterLogicWarning({ isVisible }) { ) } - diff --git a/src/components/searchForm/BiosamplesSearchPanel.js b/src/components/searchForm/BiosamplesSearchPanel.js index 1c42281d..b4e12933 100644 --- a/src/components/searchForm/BiosamplesSearchPanel.js +++ b/src/components/searchForm/BiosamplesSearchPanel.js @@ -1,27 +1,24 @@ import React, { useState } from "react" import { useBeaconQuery } from "../../hooks/api" import Panel from "../Panel" -import cn from "classnames" -// import { FaSlidersH } from "react-icons/fa" import { BiosamplesSearchForm } from "./BiosamplesSearchForm" import { BiosamplesResults } from "../searchResults/BiosamplesResults" import PropTypes from "prop-types" // import cn from "classnames" BiosamplesSearchPanel.propTypes = { - requestTypeConfig: PropTypes.object.isRequired, - requestTypeExamples: PropTypes.object.isRequired, parametersConfig: PropTypes.object.isRequired, + beaconQueryTypes: PropTypes.object.isRequired, + requestTypeExamples: PropTypes.object.isRequired, collapsed: false } export default function BiosamplesSearchPanel({ parametersConfig, - requestTypeConfig, + beaconQueryTypes, requestTypeExamples, cytoBands, - collapsed, - useUtilitiesButtons = true + collapsed }) { const [query, setQuery] = useState(null) // actual valid query const [searchCollapsed, setSearchCollapsed] = useState(collapsed) @@ -37,7 +34,6 @@ export default function BiosamplesSearchPanel({ setQuery(null) mutateQuery(null) } - const isLoading = isQueryLoading && !!query const onValidFormQuery = (formValues) => { setSearchCollapsed(true) @@ -45,9 +41,6 @@ export default function BiosamplesSearchPanel({ setQuery(formValues) } - // button className="button ml-3" - // className="icon has-text-info" - return ( <> {(searchCollapsed && ( - <>
- )) - || - null + || +
Search Samples
}
} > {query && ( diff --git a/src/config/beaconQueryTypes.yaml b/src/config/beaconQueryTypes.yaml new file mode 100644 index 00000000..82f3b862 --- /dev/null +++ b/src/config/beaconQueryTypes.yaml @@ -0,0 +1,309 @@ +--- +- label: "Search Core" + infoText: "Display main form fields" + img: false + parameters: + datasetIds: + defaultValue: ["cellz"] + aminoacidChange: + isHidden: true + geneId: + isHidden: false + genomicAlleleShortForm: + isHidden: true + referenceName: + isHidden: false + start: + isHidden: false + end: + isHidden: false + variantMinLength: + isHidden: true + variantMaxLength: + isHidden: true + variantType: + isHidden: false + alternateBases: + isHidden: false + referenceBases: + isHidden: false + bioontology: + isHidden: true + clinicalClasses: + isHidden: true + accessid: + isHidden: true + allTermsFilters: + isHidden: false + filterLogic: + isHidden: true + includeDescendantTerms: + isHidden: false + geoCity: + isHidden: true + geodistanceKm: + isHidden: true + sex: + isHidden: false + materialtype: + isHidden: false + referenceid: + isHidden: true + cohorts: + isHidden: true + limit: + isHidden: false + skip: + isHidden: false + examples: [] + +- label: "CNV (Bracket)" + infoText: "Query usein 2 `start` and 2 `end` values to find (structural) variants with some fuzziness for their start and end." + description: | + Typically CNV Queries are performed as **Beacon Bracket Queries**. These allow + the specification of sequence _ranges_ for both `start` and `end` positions of a + genomic variation since one usually wants to query for _similar_ structural + variants affecting a genomic region but potentially differing in their exact + size. + See also the [Beacon documentation](https://docs.genomebeacons.org/variant-queries/#beacon-bracket-queries) for more information. + img: /img/BeaconBracketQuery-limited-match-graphics.png + parameters: + aminoacidChange: + isHidden: true + geneId: + isHidden: true + genomicAlleleShortForm: + isHidden: true + referenceName: + placeholder: "refseq:NC_000009.12" + isHidden: false + start: + placeholder: "21000001-21975098" + isHidden: false + end: + placeholder: "21967753-23000000" + isHidden: false + variantType: + placeholder: "EFO:0030067 (DEL)" + alternateBases: + isHidden: true + referenceBases: + isHidden: true + variantMinLength: + isHidden: true + variantMaxLength: + isHidden: true + allTermsFilters: + placeholder: [NCIT:C3058] + isHidden: false + examples: [] + +- label: "Genomic Range" + infoText: "Query for variants with overlap to a genomic range" + description: | + **Beacon Range Queries** are supposed to return matches of any variant with + at least partial overlap of the _sequence range_ specified by `reference_name`, + `start` and `end` parameters. Additionally results might be restricted to + variants matching a given size range, variant type or base composition. + See also the [Beacon documentation](https://docs.genomebeacons.org/variant-queries/#beacon-range-queries) for more information. + img: /img/BeaconRangeQuery-graphics.png + parameters: + datasetIds: + placeholder: ["cellz"] + referenceName: + isHidden: false + geneId: + isHidden: true + genomicAlleleShortForm: + isHidden: true + aminoacidChange: + isHidden: true + start: + isHidden: false + end: + isHidden: false + variantType: + isHidden: false + alternateBases: + isHidden: false + referenceBases: + isHidden: false + examples: [] + +- label: "Gene ID" + infoText: "" + description: | + **Gene Queries** are in essence a variation of Range Queries in which the + coordinates are replaced by the HGNC gene symbol. It is left to the implementation + if the matching is done on variants annotated for the gene symbol or if a + positional translation is being applied (and to which parameters - e.g. extend + of coding region ...). + See also the [Beacon documentation](https://docs.genomebeacons.org/variant-queries/#beacon-geneid-queries) for more information. + img: /img/BeaconGeneQuery-graphics.png + parameters: + datasetIds: + defaultValue: ["cellz"] + aminoacidChange: + isHidden: true + geneId: + isHidden: false + genomicAlleleShortForm: + isHidden: true + referenceName: + isHidden: true + start: + isHidden: true + end: + isHidden: true + variantMinLength: + isHidden: false + variantMaxLength: + isHidden: false + variantType: + isHidden: false + alternateBases: + isHidden: false + referenceBases: + isHidden: false + examples: [] + +- label: "Sequence" + infoText: 'This represents the inputs for a "classic" Beacon SNV query' + description: | + **Sequence Queries** explore the existence of a specified sequence at a given + genomic position. Such queries correspond to the original Beacon queries + ("Allele Requests") and are used to match short, precisely defined genomic + variants such as SNVs and INDELs. + See also the [Beacon documentation](https://docs.genomebeacons.org/variant-queries/#beacon-sequence-queries) for more information. + parameters: + datasetIds: + placeholder: ["cellz"] + referenceName: + isHidden: false + geneId: + isHidden: true + genomicAlleleShortForm: + isHidden: true + aminoacidChange: + isHidden: true + start: + isHidden: false + end: + isHidden: true + variantType: + defaultValue: "SO:0001059" + alternateBases: + isHidden: false + referenceBases: + isHidden: false + variantMinLength: + isHidden: true + variantMaxLength: + isHidden: true + examples: [] + img: false + +- label: "Metadata" + infoText: "" + description: | + This template provides a variety of fields _excluding genomic variant parameters_ + for the purpose to identify samples matchinng a single or several phenotypic + or other properties. While here multiple (autocomplete) input fields are presented for convenience + to facilitate complex queries, in the end all values are combined as a list of Beacon `filters` + attributes (_i.e._ one can just add all of them to teh "Filters" field). + See also the [Beacon `filters` documentation](https://docs.genomebeacons.org/filters/#using-filters-in-queries) for more information. + img: false + parameters: + datasetIds: + defaultValue: ["cellz"] + referenceName: + isHidden: true + start: + isHidden: true + end: + isHidden: true + variantType: + isHidden: true + alternateBases: + isHidden: true + referenceBases: + isHidden: true + bioontology: + isHidden: false + clinicalClasses: + isHidden: false + accessid: + isHidden: true + allTermsFilters: + isHidden: false + filterLogic: + isHidden: true + includeDescendantTerms: + isHidden: false + sex: + isHidden: false + materialtype: + isHidden: false + referenceid: + isHidden: false + cohorts: + isHidden: true + examples: [] + +- label: "All" + infoText: "Display all form fields" + img: false + parameters: + datasetIds: + defaultValue: ["progenetix"] + aminoacidChange: + isHidden: false + geneId: + isHidden: false + genomicAlleleShortForm: + isHidden: false + referenceName: + isHidden: false + start: + isHidden: false + end: + isHidden: false + variantMinLength: + isHidden: false + variantMaxLength: + isHidden: false + variantType: + isHidden: false + alternateBases: + isHidden: false + referenceBases: + isHidden: false + bioontology: + isHidden: false + clinicalClasses: + isHidden: false + accessid: + isHidden: false + allTermsFilters: + isHidden: false + filterLogic: + isHidden: true + includeDescendantTerms: + isHidden: false + geoCity: + isHidden: false + geodistanceKm: + isHidden: false + sex: + isHidden: false + materialtype: + isHidden: false + referenceid: + isHidden: false + cohorts: + isHidden: false + limit: + isHidden: false + skip: + isHidden: false + examples: [] diff --git a/src/config/beaconSearchParameters.yaml b/src/config/beaconSearchParameters.yaml new file mode 100644 index 00000000..db65eb70 --- /dev/null +++ b/src/config/beaconSearchParameters.yaml @@ -0,0 +1,322 @@ +--- +parameters: + datasetIds: + label: Dataset + defaultValue: ["cellz"] + options: + - value: cellz + label: Cancer Cell Lines Collection + - value: progenetix + label: Progenetix + isMulti: true + assemblyId: + label: Genome Assembly + infoText: | + The genome reference assembly. + defaultValue: "GRCh38" + isHidden: true + options: + - GRCh38 + geneId: + label: Gene Symbol + placeholder: MYCN + infoText: The gene symbol (HUGO), e.g. "TP53". + isHidden: true + aminoacidChange: + label: Aminoacid alteration + placeholder: V600E + infoText: Aminoacid alteration in 1 letter format (e.g. "V600E"). + isHidden: true + genomicAlleleShortForm: + label: Genomic HGVS ID + placeholder: NC_000017.11:g.7674232C>G + infoText: Genomic HGVSId descriptor. + isHidden: true + referenceName: + label: Chromosome + infoText: The chromosome (transmitted as refseq id). + options: + - label: "(none)" + value: "" + - label: "1 (NC_000001.11)" + value: "refseq:NC_0000001.11" + # value: "1" + - label: "2 (NC_000002.12)" + value: "refseq:NC_000002.12" + # value: "2" + - label: "3 (NC_000003.12)" + value: "refseq:NC_000003.12" + # value: "3" + - label: "4 (NC_000004.12)" + value: "refseq:NC_000004.12" + # value: "4" + - label: "5 (NC_000005.10)" + value: "refseq:NC_000005.10" + # value: "5" + - label: "6 (NC_000006.12)" + value: "refseq:NC_000006.12" + # value: "6" + - label: "7 (NC_000007.14)" + value: "refseq:NC_000007.14" + # value: "7" + - label: "8 (NC_000008.11)" + value: "refseq:NC_000008.11" + # value: "8" + - label: "9 (NC_000009.12)" + value: "refseq:NC_000009.12" + # value: "9" + - label: "10 (NC_000010.11)" + value: "refseq:NC_000010.11" + # value: "10" + - label: "11 (NC_000011.10)" + value: "refseq:NC_000011.10" + # value: "11" + - label: "12 (NC_000012.12)" + value: "refseq:NC_000012.12" + # value: "12" + - label: "13 (NC_000013.11)" + value: "refseq:NC_000013.11" + # value: "13" + - label: "14 (NC_000014.9)" + value: "refseq:NC_000014.9" + # value: "14" + - label: "15 (NC_000015.10)" + value: "refseq:NC_000015.10" + # value: "15" + - label: "16 (NC_000016.10)" + value: "refseq:NC_000016.10" + # value: "16" + - label: "17 (NC_000017.11)" + value: "refseq:NC_000017.11" + # value: "17" + - label: "18 (NC_000018.10)" + value: "refseq:NC_000018.10" + # value: "18" + - label: "19 (NC_000019.10)" + value: "refseq:NC_000019.10" + # value: "19" + - label: "20 (NC_000020.11)" + value: "refseq:NC_000020.11" + # value: "20" + - label: "21 (NC_000021.9)" + value: "refseq:NC_000021.9" + # value: "21" + - label: "22 (NC_000022.11)" + value: "refseq:NC_000022.11" + # value: "22" + - label: "X (NC_000023.11)" + value: "refseq:NC_000023.11" + # value: "23" + - label: "Y (NC_000024.10)" + value: "refseq:NC_000024.10" + # value: "24" + variantType: + label: "Variant Type" + infoText: >- + The type of a structural variant for e.g. CNV searches. + defaultValue: "" + options: + - value: "(none)" + label: "" + - value: "EFO:0030067" + label: "EFO:0030067 (copy number deletion)" + - value: "EFO:0030070" + label: "EFO:0030070 (copy number gain)" + - value: "EFO:0020073" + label: "EFO:0020073 (high-level copy number loss)" + - value: "EFO:0030072" + label: "EFO:0030072 (high-level copy number gain)" + - value: "EFO:0030066" + label: "EFO:0030066 (any relative CN variation)" + - value: "SO:0001059" + label: "SO:0001059 (any sequence alteration - SNV, INDEL...)" + start: + label: Start or Position + placeholder: "19000001-21975098" + infoText: | + The 1-based genomic position of the variant, or the start of a range query, + specified through a single value. + However, in case of fuzzy requests for e.g. copy number variants, the start + position will usually be provided as a range for the possible occurrence of + the CNV start (e.g. "21500001-21975098"). + end: + label: End (Range or Structural Var.) + placeholder: "21967753-24000000" + infoText: | + The 1-based genomic end position of a range query, specified through a + single value, or in case of fuzzy requests for e.g. copy number variants + the end provided as a range for the possible occurrence of + the CNV end (e.g. "21967753-22500000"). + variantMinLength: + label: Min Variant Length + type: number + infoText: | + The minimal length, in bases, of a genomic variant. This parameter can be + applied to e.g. CNV queries. + isHidden: true + variantMaxLength: + label: Max Variant Length + type: number + infoText: | + The maximal length, in bases, of a genomic variant. This parameter can be + applied to e.g. CNV queries. + isHidden: true + referenceBases: + infoText: | + The minimal length, in bases, of a genomic variant. This parameter can be + applied to e.g. CNV queries. + placeholder: "N" + label: Reference Base(s) + alternateBases: + placeholder: "A" + label: Alternate Base(s) + cohorts: + label: Cohorts + infoText: > + An aggregation of samples by origin (e.g. study, external resource), shared features or use in a publication. + isMulti: true + isHidden: true + bioontology: + label: Cancer Classification(s) + infoLink: "https://info.progenetix.org/doc/filters-documentation.html" + infoText: | + Special filter type for disease classification codes. If using multiple + codes please be aware that probably use of an "OR" filter logic will be appropriate. + This field relies on the existence of the term in the Beacon `filteringTermsResponse`. + isMulti: true + isHidden: true + options: + - value: "" + label: "(no selection)" + referenceid: + label: Reference ID(s) + infoText: | + Special filter type for external reference codes such as PMIDs or cellosaurus + id values. + If using multiple codes please be aware that probably use of an "OR" filter + logic will be appropriate. + This field relies on the existence of the term in the Beacon `filteringTermsResponse`. + isMulti: true + isHidden: true + options: + - value: "" + label: "(no selection)" + clinicalClasses: + label: Clinical Classes + infoText: | + Clinical and diagnostic support classifications, such as tumor grading or staging. + isMulti: true + isHidden: true + options: + - value: "" + label: "(no selection)" + materialtype: + label: Biosample Type + infoText: | + Special filter type, to e.g. limit responses to neoplastic or reference + samples, respectively, when not using specific cancer classification codes. + defaultValue: "" + isHidden: true + options: + - value: "" + label: "(no selection)" + - value: EFO:0009656 + label: neoplastic sample + - value: EFO:0030035 + label: cancer cell line sample + - value: EFO:0009654 + label: reference sample + sex: + label: Genotypic Sex + infoText: | + Genotypic sex of the individual. + defaultValue: "" + isHidden: true + options: + - value: "" + label: "(no selection)" + - value: PATO:0020002 + label: Female genotypic Sex + - value: PATO:0020001 + label: Male genotypic Sex + allTermsFilters: + label: Select Filters + isMulti: true + infoLink: "https://info.progenetix.org/doc/filters-documentation.html" + infoText: | + Add any **filters** to the search. These will usually + correspond to CURIE values supported by the resource (e.g. + "cellosaurus:CVCL_0004"; "NCIT:C3222"; "PMID:18559507"). + This field relies on the existence of the term in the Beacon `filteringTermsResponse`. + options: + - value: [""] + label: "(no selection)" + accessid: + label: Accession Prefilter + isHidden: true + filterLogic: + label: Filter Logic + isHidden: true + infoText: | + Boolean logic for filter values; the default **AND** assumes different + logical scopes for used filters (e.g. collision occurr if multiple cancer + diagnoses are used with AND). Please be aware that "Filters" here include + the "Cancer Classification", "Biosample Type" and "Filters" fields. + defaultValue: "AND" + options: + - value: AND + label: AND + - value: OR + label: OR + includeDescendantTerms: + label: Include Child Terms + infoText: >- + As standard, any selected filter will also include matches on its child + terms; i.e. NCIT:C3052 - Digestive System Neoplasm will include results + from gastric, esophagus, colon ... cancer. However, this parameter allows + to limit searches to the exact code match. + defaultValue: "True" + isHidden: true + options: + - value: "true" + label: always match child terms + - value: "false" + label: only match exact code(s) + # filterPrecision: + # label: Filter Precision + # infoText: | + # Logic for the matching of filter terms. The use of "start" will e.g. + # allow to match all "icdom-8500_" values (i.e. ductal breast carcinomas + # and precursors - "...85000, ...85001, ...85002, ...85003") + # defaultValue: "exact" + # options: + # - value: exact + # label: Exact term match + # - value: start + # label: Start-anchored term match + geoCity: + label: City + infoText: | + Selector for a city for which the samples have been annotated, usually corresponding to origin of the data. + isHidden: true + geodistanceKm: + infoText: | + Proximity of the data matches to the selected city, to allow for fuzzy + annotations or large geographic area searches (e.g. "European samples" through + "Zurich + 2000km"). + isHidden: true + label: Range (km) + type: number + defaultValue: 100 + skip: + label: Skip Pages + infoText: > + The number of result pages to skip when retrieving data. + defaultValue: 0 + isHidden: true + limit: + label: Response Limit / Page Size + infoText: > + The maximum number of biosamples to retrieve per page and also the size of response pages. + defaultValue: 100 + isHidden: true \ No newline at end of file diff --git a/src/hooks/api.js b/src/hooks/api.js index 9bdc190d..4e88d4f6 100644 --- a/src/hooks/api.js +++ b/src/hooks/api.js @@ -121,7 +121,7 @@ export function mkGeneParams(gene) { } export function makeFilters({ - freeFilters, + allTermsFilters, clinicalClasses, bioontology, referenceid, @@ -129,20 +129,14 @@ export function makeFilters({ sex, materialtype }) { - const parsedFreeFilters = - freeFilters - ?.split(",") - .map((ff) => ff.trim()) - .filter((v) => v != null && v.length !== 0) ?? [] - return [ + ...(allTermsFilters ?? []), ...(bioontology ?? []), ...(clinicalClasses ?? []), ...(referenceid ?? []), ...(cohorts ? [cohorts] : []), ...(sex ? [sex] : []), - ...(materialtype ? [materialtype] : []), - ...parsedFreeFilters + ...(materialtype ? [materialtype] : []) ] } @@ -155,7 +149,7 @@ export function buildQueryParameters(queryData) { cohorts, sex, materialtype, - freeFilters, + allTermsFilters, clinicalClasses, geneId, geoCity, @@ -163,6 +157,7 @@ export function buildQueryParameters(queryData) { ...otherParams } = queryData // positions from the form have to be -1 adjusted (only first value if interval) + const starts = [] if (start) { const match = INTEGER_RANGE_REGEX.exec(start) @@ -182,7 +177,7 @@ export function buildQueryParameters(queryData) { end1 && ends.push(end1) } const filters = makeFilters({ - freeFilters, + allTermsFilters, clinicalClasses, bioontology, referenceid, @@ -358,7 +353,12 @@ export function useCollationsById({ datasetIds }) { } export function useCollations({ datasetIds, method, filters }) { - const url = `${SITE_DEFAULTS.API_PATH}services/collations/?datasetIds=${datasetIds}&method=${method}&filters=${filters}` + const url = `${SITE_DEFAULTS.API_PATH}beacon/filtering_terms/?datasetIds=${datasetIds}&method=${method}&filters=${filters}` + return useProgenetixApi(url) +} + +export function useFiltersByType({ datasetIds, method, collationTypes }) { + const url = `${SITE_DEFAULTS.API_PATH}beacon/filtering_terms/?datasetIds=${datasetIds}&method=${method}&collationTypes=${collationTypes}` return useProgenetixApi(url) } @@ -369,11 +369,10 @@ export function useCollationsByType({ datasetIds, method, collationTypes }) { export function sampleSearchPageFiltersLink({ datasetIds, - searchPage, sampleFilterScope, filters }) { - return `/${searchPage}/?${sampleFilterScope}=${filters}&datasetIds=${datasetIds}` + return `/search/?${sampleFilterScope}=${filters}&datasetIds=${datasetIds}` } export function useGeoCity({ city }) { diff --git a/src/hooks/api.test.js b/src/hooks/api.test.js deleted file mode 100644 index da68bd13..00000000 --- a/src/hooks/api.test.js +++ /dev/null @@ -1,73 +0,0 @@ -import { SITE_DEFAULTS, replaceWithProxy } from "./api" - -const { buildQueryParameters } = require("./api") - -test("build a complete query", () => { - expect( - buildQueryParameters({ - datasetIds: [SITE_DEFAULTS.DATASETID], - assemblyId: "GRCh38", - referenceName: "9", - variantType: "EFO:0030067", - start: "20000001", - end: "21967753-23000000", - referenceBases: "N", - alternateBases: "G", - bioontology: ["NCIT:C102872", "NCIT:C102873"], - sex: "PATO:0020002", - materialtype: "EFO:0009656", - freeFilters: "geolat:49,geolong:8.69,geodist:2000000" - }) - ).toBe( - "datasetIds=cellz&assemblyId=GRCh38&referenceName=9&variantType=EFO%3A0030067&referenceBases=N&alternateBases=G&start=20000000&end=21967753&end=23000000&filters=NCIT%3AC102872&filters=NCIT%3AC102873&filters=PATO%3A0020002&filters=EFO%3A0009656&filters=geolat%3A49&filters=geolong%3A8.69&filters=geodist%3A2000000" - ) -}) - -test("build a query with start", () => { - expect( - buildQueryParameters({ - start: "20000001" - }) - ).toBe("start=20000000") -}) - -test("build a query with start range", () => { - expect( - buildQueryParameters({ - start: "20000001,20000003" - }) - ).toBe("start=20000000&start=20000003") -}) - -test("build a query with end", () => { - expect( - buildQueryParameters({ - end: "21967753" - }) - ).toBe("end=21967753") -}) - -test("build a query with end range", () => { - expect( - buildQueryParameters({ - end: "21967753-23000000" - }) - ).toBe("end=21967753&end=23000000") -}) - -test("replaceWithProxy", () => { - expect( - replaceWithProxy( - "http://test.progenetix.org:3000/test?query=a", - true, - "api/" - ) - ).toBe("api/test?query=a") - expect( - replaceWithProxy( - new URL("http://test.progenetix.org:3000/test?query=a"), - true, - "api/" - ) - ).toBe("api/test?query=a") -}) diff --git a/src/modules/data-pages/celllines_dataPage.js b/src/modules/data-pages/celllines_dataPage.js index 5e7159d2..f7a19224 100644 --- a/src/modules/data-pages/celllines_dataPage.js +++ b/src/modules/data-pages/celllines_dataPage.js @@ -1,23 +1,18 @@ import React from "react" import { Layout } from "../../components/Layout" -import parametersConfig from "../../config//searchParameters.yaml" -import requestTypeConfig from "../../config/celllines_searchParameters.yaml" +import parametersConfig from "../../config/beaconSearchParameters.yaml" +import beaconQueryTypes from "../../config/beaconQueryTypes.yaml" import requestTypeExamples from "../../config/celllines_searchExamples.yaml" import BiosamplesSearchPanel from "../../components/searchForm/BiosamplesSearchPanel" export default function cellLines_dataPage({ cytoBands }) { - return ( -{/*
- The Cancer Cell Lines site is under development. Stay tuned! -
-*/}
diff --git a/src/modules/details-pages/celllinePage.js b/src/modules/details-pages/celllinePage.js index 6905395a..515529b9 100644 --- a/src/modules/details-pages/celllinePage.js +++ b/src/modules/details-pages/celllinePage.js @@ -133,7 +133,7 @@ function SubsetResponse({ id, response, individual, datasetIds }) { function Subset({ id, subset, individual, datasetIds }) { const filters = id - const sampleFilterScope = "referenceid" + const sampleFilterScope = "allTermsFilters" const [showAll, setShowAll] = useState(false); // console.log(individual); // console.log(Object.keys(individual)); diff --git a/src/modules/service-pages/dataVisualizationPage.js b/src/modules/service-pages/dataVisualizationPage.js index 96fac7ef..5225c6c5 100644 --- a/src/modules/service-pages/dataVisualizationPage.js +++ b/src/modules/service-pages/dataVisualizationPage.js @@ -31,7 +31,7 @@ const DataVisualizationPage = withUrlQuery(({ urlQuery }) => { return ( {!accessid && !fileId ? ( @@ -109,7 +109,6 @@ function DataVisualizationPanel({ datasetIds, accessid, fileId, skip, limit, wid } function DataVisualizationForm({ isQuerying, onSubmit }) { - const defaultValues = { "plotRegionLabels": null, "plotGeneSymbols": null @@ -119,13 +118,6 @@ function DataVisualizationForm({ isQuerying, onSubmit }) {
-