Skip to content

Commit

Permalink
bycon 2.n alignment
Browse files Browse the repository at this point in the history
  • Loading branch information
mbaudis committed Nov 26, 2024
1 parent c4687bc commit e65f814
Show file tree
Hide file tree
Showing 21 changed files with 1,528 additions and 264 deletions.
914 changes: 914 additions & 0 deletions src/components/Cytobands.js

Large diffs are not rendered by default.

42 changes: 42 additions & 0 deletions src/components/SubsetsHierarchyLoader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { useFilterTreesByType } from "../hooks/api"
import { WithData } from "./Loader"
import React from "react"
import { keyBy } from "lodash"
import { buildTree, TreePanel } from "./classificationTree/TreePanel"

export default function SubsetsHierarchyLoader({ collationTypes, datasetIds, defaultTreeDepth }) {
const collationsHierarchyReply = useFilterTreesByType({
datasetIds,
collationTypes
})

return (
<WithData
apiReply={collationsHierarchyReply}
background
render={(collationsHierarchyReply) => (
<SubsetsResponse
collationsHierarchies={collationsHierarchyReply.response.filteringTerms}
datasetIds={datasetIds}
defaultTreeDepth={defaultTreeDepth}
/>
)}
/>
)
}

function SubsetsResponse({ collationsHierarchies, datasetIds, defaultTreeDepth }) {
const subsetById = keyBy(collationsHierarchies, "id")
const { tree, size } = buildTree(collationsHierarchies, subsetById)

return (
<TreePanel
datasetIds={datasetIds}
subsetById={subsetById}
tree={tree}
size={size}
defaultTreeDepth={defaultTreeDepth}
sampleFilterScope="allTermsFilters"
/>
)
}
153 changes: 95 additions & 58 deletions src/components/SubsetsLoader.js
Original file line number Diff line number Diff line change
@@ -1,74 +1,111 @@
import { useCollationsByType, useCollationsById } from "../hooks/api"
import { WithData } from "./Loader"
import React from "react"
import { keyBy, merge } from "lodash"
import {
SITE_DEFAULTS,
useServiceItemDelivery,
sampleSearchPageFiltersLink,
NoResultsHelp
} from "../hooks/api"
import { Loader } from "./Loader"
import { SubsetHistogram } from "./SVGloaders"
import { buildTree, buildTreeForDetails, TreePanel } from "./classificationTree/TreePanel"
// import { ShowJSON } from "./RawData"
import { ExternalLink, InternalLink } from "./helpersShared/linkHelpers"

export default function SubsetsLoader({ collationTypes, datasetIds, defaultTreeDepth }) {
const bioSubsetsHierarchiesReply = useCollationsByType({
datasetIds,
method: "paths",
collationTypes
})
const service = "collations"

const allBioSubsetsReply = useCollationsById({
export function SubsetLoader({ id, datasetIds }) {
const { data, error, isLoading } = useServiceItemDelivery(
id,
service,
datasetIds
})

)
return (
<WithData
apiReply={bioSubsetsHierarchiesReply}
background
render={(bioSubsetsHierarchiesResponse) => (
<WithData
apiReply={allBioSubsetsReply}
background
render={(allBioSubsetsResponse) => (
<SubsetsResponse
bioSubsetsHierarchies={bioSubsetsHierarchiesResponse.response.results}
allBioSubsets={allBioSubsetsResponse.response.results}
datasetIds={datasetIds}
defaultTreeDepth={defaultTreeDepth}
/>
)}
/>
<Loader isLoading={isLoading} hasError={error} background>
{data && (
<SubsetResponse response={data} id={id} datasetIds={datasetIds} />
)}
/>
</Loader>
)
}

function SubsetsResponse({ bioSubsetsHierarchies, allBioSubsets, datasetIds, defaultTreeDepth }) {
const isDetailPage = bioSubsetsHierarchies.length === 1
// We merge both subsets from hierarchies and subsets from allSubsets.
// This is because some children in the bioSubsetsHierarchies don't have labels or sample count information.
const subsetById = merge(keyBy(bioSubsetsHierarchies, "id"), allBioSubsets)
const { tree, size } = isDetailPage
? buildTreeForDetails(bioSubsetsHierarchies, subsetById)
: buildTree(bioSubsetsHierarchies, subsetById)
function SubsetResponse({ response, datasetIds }) {
if (!response.response.results[0]) {
return NoResultsHelp("subsetdetails")
}
return <Subset subset={response.response.results[0]} datasetIds={datasetIds} />
}

function Subset({ subset, datasetIds }) {

const filters = subset.id
const sampleFilterScope = "allTermsFilters"

return (
<section className="content">
<h2>
{subset.label} ({subset.id})
</h2>

{subset.type && (
<>
{isDetailPage && (
<div className="mb-3">
<SubsetHistogram
id={bioSubsetsHierarchies[0].id}
datasetIds={datasetIds}
loaderProps={{
background: true,
colored: true
}}
<h5>Subset Type</h5>
<ul>
<li>
{subset.type}{" "}
<ExternalLink
href={subset.reference}
label={subset.id}
/>
</div>
)}
<TreePanel
datasetIds={datasetIds}
subsetById={subsetById}
tree={tree}
size={size}
defaultTreeDepth={defaultTreeDepth}
sampleFilterScope="allTermsFilters"
/>
</li>
</ul>
</>
)}

<h5>Sample Counts</h5>
<ul>
<li>{subset.count} samples</li>
<li>{subset.codeMatches} direct <i>{subset.id}</i> code matches</li>
{subset.cnvAnalyses && (
<li>{subset.cnvAnalyses} CNV analyses</li>
)}
{subset.frequencymapCnvAnalyses && (
<li>
{subset.frequencymapCnvAnalyses} {" CNV analyses used in frequency calculations"}
</li>
)}
</ul>
<h5>CNV Histogram</h5>
<div className="mb-3">
<SubsetHistogram
id={subset.id}
datasetIds={datasetIds}
loaderProps={{background: true, colored: true}}
/>
</div>

<h5>
<InternalLink
href={`${SITE_DEFAULTS.API_PATH}services/intervalFrequencies/${subset.id}/?output=pgxfreq`}
label="Download CNV frequencies"
/>
</h5>
<p>
Download CNV frequency data for genomic 1Mb bins.
</p>

<h5>
<InternalLink
href={ sampleSearchPageFiltersLink({datasetIds, sampleFilterScope, filters}) }
label={`Search for ${subset.id} Samples`}
rel="noreferrer"
target="_blank"
/>
</h5>
<p>
Select samples through the search form, e.g. by querying for specific
genomic variants or phenotypes.
</p>

{/*<ShowJSON data={subset} />*/}

</section>
)
}
52 changes: 27 additions & 25 deletions src/components/classificationTree/SubsetsTree.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import { FaAngleDown, FaAngleRight } from "react-icons/fa"
import Tippy from "@tippyjs/react"
import { FixedSizeTree as VTree } from "react-vtree"
import useDebounce from "../../hooks/debounce"
import { min } from "lodash"
// import { min } from "lodash"
import { filterNode } from "./tree"

const ROW_HEIGHT = 30
const ROW_HEIGHT = 28

export function SubsetsTree({
tree,
Expand All @@ -18,16 +18,15 @@ export function SubsetsTree({
datasetIds,
checkedSubsets,
checkboxClicked,
sampleFilterScope,
defaultTreeDepth
sampleFilterScope
}) {
const {
searchInput,
setSearchInput,
filteredTree,
debouncedSearchInput
} = useFilterTree(tree)
const [levelSelector, setLevelSelector] = useState(defaultTreeDepth)
const [levelSelector, setLevelSelector] = useState(3)

// console.log(filteredTree)

Expand All @@ -43,7 +42,7 @@ export function SubsetsTree({
<div className="field">
<input
className="input "
placeholder="Type text to filter ..."
placeholder="Filter subsets e.g. by prefix ..."
value={searchInput}
onChange={(e) => setSearchInput(e.target.value)}
/>
Expand Down Expand Up @@ -87,7 +86,7 @@ export function SubsetsTree({
)}
{checkedSubsets.map((subset) => (
<li className="tag is-primary" key={subset.id}>
{subset.label} ({subset.count})
{subset.label ? subset.label : subset.id} ({subset.count})
</li>
))}
</ul>
Expand Down Expand Up @@ -166,7 +165,6 @@ function Tree({
// Node component receives all the data we created in the `treeWalker` +
// internal openness state (`isOpen`), function to change internal openness
// state (`toggle`) and `style` parameter that should be added to the root div.

function Node({
data: { isLeaf, subsetId, subset, nestingLevel },
treeData: {
Expand All @@ -180,9 +178,9 @@ function Node({
style,
setOpen
}) {
const isSearchPossible = subset && canSearch(subset)
const isSearchPossible = true // subset && canSearch(subset)
const even = index % 2 === 0
const detailsPage = subsetId.match("cellosaurus") ? "cellline" : "subset"
const detailsPage = "subset"
return (
<div
style={{
Expand Down Expand Up @@ -222,17 +220,21 @@ function Node({
</span>
)}
<Tippy content={`Show data for subset "${subset.label}"`}>
<a
href={`/${detailsPage}/?id=${subsetId}&datasetIds=${datasetIds}`}
>
<>
{(subset?.label && (
<span className="Subsets__tree__label" title={subset.label}>
{subset.label}
<a href={`/${detailsPage}/?id=${subsetId}&datasetIds=${datasetIds}`}>
{subset.label}</a>: {subsetId}
</span>
))
||
<span className="Subsets__tree__label" title={subsetId}>
<a href={`/${detailsPage}/?id=${subsetId}&datasetIds=${datasetIds}`}>
{subsetId}</a>: {subsetId}
</span>
)) || <span>&nbsp;</span>}
</a>
}
</>
</Tippy>
<span>: {subsetId}</span>
{isSearchPossible ? (
<Tippy content={`Click to retrieve samples for ${subsetId}`}>
<a
Expand Down Expand Up @@ -353,11 +355,11 @@ function sampleSelectUrl({ subsets, datasetIds, sampleFilterScope }) {
return sampleSearchPageFiltersLink({ datasetIds, sampleFilterScope, filters })
}

function canSearch(subset) {
// Only necessary for NCIT
if (!subset.id.includes("NCIT:")) return true
const minDepth = subset.hierarchyPaths
? min(subset.hierarchyPaths?.map((hp) => hp.depth))
: 999
return minDepth >= 2
}
// function canSearch(subset) {
// // Only necessary for NCIT
// if (!subset.id.includes("NCIT:")) return true
// const minDepth = subset.hierarchyPaths
// ? min(subset.hierarchyPaths?.map((hp) => hp.depth))
// : 999
// return minDepth >= 2
// }
22 changes: 1 addition & 21 deletions src/components/classificationTree/TreePanel.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import React, { useState } from "react"
import { SubsetsTree } from "./SubsetsTree"
import { sortBy } from "lodash"
import { getOrMakeChild, getOrMakeNode } from "./tree"
import { getOrMakeNode } from "./tree"

export function TreePanel({
tree,
size,
subsetById,
datasetIds,
sampleFilterScope,
defaultTreeDepth,
isFlat
}) {
const [checkedIds, setCheckedIds] = useState({})
Expand All @@ -34,7 +33,6 @@ export function TreePanel({
checkedSubsets={checkedSubsets}
checkboxClicked={checkboxClicked}
sampleFilterScope={sampleFilterScope}
defaultTreeDepth={defaultTreeDepth}
isFlat={isFlat}
/>
</div>
Expand Down Expand Up @@ -63,23 +61,5 @@ export function buildTree(response, subsetById) {
return { tree, size }
}

export function buildTreeForDetails(response, subsetById) {
const rootSubset = response[0]
const tree = { id: "root", children: [], path: ["root"] }
let size = 1
const rootNode = getOrMakeChild(tree, rootSubset.id)
rootNode.subset = rootSubset
const childTerms = rootSubset.childTerms
childTerms.forEach((c) => {
// some subsets have themselves in the children list
if (rootSubset.code !== c) {
const node = getOrMakeChild(rootNode, c, randomStringGenerator)
node.subset = subsetById[node.id]
size++
}
})
return { tree, size }
}

// We generate random UID because a tree contains several nodes with the same ids
const randomStringGenerator = () => Math.random().toString(36).substring(2, 15)
1 change: 0 additions & 1 deletion src/components/searchForm/BiosamplesSearchForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -720,7 +720,6 @@ function useFilteringTerms(watchForm, ct) {
const datasetIds = watchForm("datasetIds")
return useFiltersByType({
datasetIds,
method: "counts",
collationTypes: ct
})
}
Expand Down
Loading

0 comments on commit e65f814

Please sign in to comment.