Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .infra/rdev/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ stack:
services:
explorer:
image:
tag: sha-99bd33b
tag: sha-0b0ec44
replicaCount: 1
env:
# env vars common to all deployment stages
Expand Down
20 changes: 9 additions & 11 deletions client/src/common/queries/coverage.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {
UndefinedInitialDataOptions,
useQueries,
useQuery,
UseQueryResult,
} from "@tanstack/react-query";
import { fetchJson } from "util/fetch";
Expand All @@ -17,11 +17,11 @@ export interface GeneInfo {
}

export interface FetchCoverageResponse {
cellType: string;
coveragePlot: CoveragePlotData;
chromosome: string;
coverage: [number, number, number][];
geneInfo: GeneInfo[];
coverageByCellType: {
[cellType: string]: [number, number, number][];
};
}

async function fetchCoverage(
Expand Down Expand Up @@ -54,12 +54,10 @@ export function useCoverageQuery({
genomeVersion,
cellTypes,
options,
}: UseCoverageQueryOptions): UseQueryResult<FetchCoverageResponse>[] {
return useQueries({
queries: cellTypes.map((cellType) => ({
queryKey: [USE_COVERAGE, geneName, genomeVersion, cellType],
queryFn: () => fetchCoverage(geneName, genomeVersion, cellType),
...options,
})),
}: UseCoverageQueryOptions): UseQueryResult<FetchCoverageResponse> {
return useQuery({
queryKey: [USE_COVERAGE, geneName, genomeVersion, cellTypes],
queryFn: () => fetchCoverage(geneName, genomeVersion, cellTypes.join(",")),
...options,
});
}
4 changes: 1 addition & 3 deletions client/src/components/BottomPanel/BottomPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,7 @@ const BottomSideBar = ({
return (
<BottomPanelWrapper isHidden={bottomPanelHidden}>
<BottomPanelHeader>
<BottomPanelHeaderTitle>
Chromatin Accessibility Viewer
</BottomPanelHeaderTitle>
<BottomPanelHeaderTitle>Chromatin Accessibility</BottomPanelHeaderTitle>

<BottomPanelHeaderActions>
<GeneSelect />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,73 +26,82 @@ export const ChromosomeMap = () => {
const formatSelectedGenes =
parts.length <= 1 ? selectedGene : parts.slice(0, -1).join("_");

const coverageQueries = useCoverageQuery({
const coverageQuery = useCoverageQuery({
cellTypes: selectedCellTypes,
geneName: formatSelectedGenes,
genomeVersion: "hg38", // TODO: (smccanny) make this dynamic
options: {
enabled: !bottomPanelHidden,
enabled: !bottomPanelHidden && selectedCellTypes.length > 0,
retry: 3,
},
});

const isLoading = coverageQueries.some((q) => q.isLoading);
const isError = coverageQueries.some((q) => q.isError);
const { isLoading, isError } = coverageQuery;

const totalBasePairs = useMemo(
() =>
Math.max(...coverageQueries.map((q) => q.data?.coverage.length ?? 0), 0),
[coverageQueries]
);
const totalBasePairs = useMemo(() => {
const coverageByCellType = coverageQuery.data?.coverageByCellType;
if (!coverageByCellType) return 0;

return Math.max(
...Object.values(coverageByCellType).map((coverage) => coverage.length),
0
);
}, [coverageQuery.data?.coverageByCellType]);

const startBasePair = useMemo(() => {
for (const q of coverageQueries) {
const plot = q.data?.coverage;
if (plot && plot.length > 0) {
const start = plot[0][1]; // startBasePair
return start;
const coverageByCellType = coverageQuery.data?.coverageByCellType;
if (!coverageByCellType) return 0;

for (const coverage of Object.values(coverageByCellType)) {
if (coverage.length > 0) {
return coverage[0][1]; // the second element in [value, start, end]
}
}

return 0;
}, [coverageQueries]);
}, [coverageQuery.data?.coverageByCellType]);

const endBasePair = useMemo(() => {
for (const q of coverageQueries) {
const plot = q.data?.coverage;
if (plot && plot.length > 0) {
const end = plot[plot.length - 1][2]; // endBasePair
return end;
const coverageByCellType = coverageQuery.data?.coverageByCellType;
if (!coverageByCellType) return 0;

for (const coverage of Object.values(coverageByCellType)) {
if (coverage.length > 0) {
return coverage[coverage.length - 1][2]; // third value = end base pair
}
}

return 0;
}, [coverageQueries]);
}, [coverageQuery.data?.coverageByCellType]);

const binSize = useMemo(() => {
for (const q of coverageQueries) {
const plot = q.data?.coverage;
if (plot && plot.length > 1) {
const start = plot[0][1]; // startBasePair
const end = plot[0][2]; // endBasePair
const coverageByCellType = coverageQuery.data?.coverageByCellType;
if (!coverageByCellType) return 0;

for (const coverage of Object.values(coverageByCellType)) {
if (coverage.length > 0) {
// Destructure the first coverage tuple to get start and end base pairs
const [, start, end] = coverage[0];
return end - start;
}
}
return 0;
}, [coverageQueries]);
}, [coverageQuery.data?.coverageByCellType]);

const totalBPAtScale = (totalBasePairs * binSize) / 1_000; // this gives us a scale in kb

// Find the selected gene info and calculate its position
// Find the selected gene info from the query result
const selectedGeneInfo = useMemo(() => {
for (const q of coverageQueries) {
if (q.data?.geneInfo) {
const gene = q.data.geneInfo.find(
(g) => g.geneName.toLowerCase() === selectedGene.toLowerCase()
);
if (gene) return gene;
}
const geneInfoArray = coverageQuery.data?.geneInfo;

if (geneInfoArray) {
const gene = geneInfoArray.find(
(g) => g.geneName.toLowerCase() === selectedGene.toLowerCase()
);
if (gene) return gene;
}
return null;
}, [coverageQueries, selectedGene]);
}, [coverageQuery.data?.geneInfo, selectedGene]);

useEffect(() => {
if (selectedGeneInfo && !isLoading && totalBasePairs > 0) {
Expand All @@ -116,18 +125,22 @@ export const ChromosomeMap = () => {
return () => {};
}, [selectedGeneInfo, isLoading, totalBasePairs]);

const yMax = useMemo(
() =>
Math.max(
...coverageQueries.map((q) => {
const coverage = q.data?.coverage;
if (!coverage || coverage.length === 0) return 0;
return Math.ceil(Math.max(...coverage.map((c) => c[0]))); // Get the max y value
}),
0 // Ensure we return at least 0
),
[coverageQueries]
);
const yMax = useMemo(() => {
const coverageByCellType = coverageQuery.data?.coverageByCellType;
if (!coverageByCellType) return 0;

let maxY = 0;

for (const coverage of Object.values(coverageByCellType)) {
for (const [value] of coverage) {
if (value > maxY) {
maxY = value;
}
}
}

return Math.ceil(maxY);
}, [coverageQuery.data?.coverageByCellType]);

if (isLoading) {
return (
Expand Down Expand Up @@ -158,7 +171,7 @@ export const ChromosomeMap = () => {
);
}

const chromosome = coverageQueries[0]?.data?.chromosome;
const chromosome = coverageQuery?.data?.chromosome;
if (!chromosome) {
return (
<div
Expand Down Expand Up @@ -198,14 +211,15 @@ export const ChromosomeMap = () => {
barWidth={BAR_WIDTH}
yMax={yMax}
cellType={cellType}
coverageQuery={coverageQueries.find(
(q) => q.data?.cellType === cellType
)}
coverageQuery={
coverageQuery.data?.coverageByCellType?.[cellType] ?? []
}
/>
))}

<GeneMap
svgWidth={totalBasePairs * BAR_WIDTH}
geneInfo={coverageQueries[0]?.data?.geneInfo ?? undefined}
geneInfo={coverageQuery?.data?.geneInfo ?? undefined}
startBasePair={startBasePair}
endBasePair={endBasePair}
formatSelectedGenes={formatSelectedGenes}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,7 @@ import React, {
} from "react";
import ReactDOM from "react-dom";
import memoize from "memoize-one";
import { UseQueryResult } from "@tanstack/react-query";
import {
CoveragePlotData,
FetchCoverageResponse,
} from "common/queries/coverage";
import { CoveragePlotData } from "common/queries/coverage";
import { useDispatch, useSelector } from "react-redux";
import { RootState } from "reducers";
import { Button } from "@czi-sds/components";
Expand Down Expand Up @@ -65,7 +61,7 @@ export function CoveragePlot({
chromosome: string;
cellType: string;
barWidth: number;
coverageQuery: UseQueryResult<FetchCoverageResponse> | undefined;
coverageQuery: [number, number, number][];
yMax: number;
}) {
const [histogramTooltipLocation, setHistogramTooltipLocation] = useState<{
Expand All @@ -86,16 +82,16 @@ export function CoveragePlot({
return [index + 1, barIndex, chromosomeId, cellType, binSize];
});
}
if (!coverageQuery?.data?.coverage) {
if (!coverageQuery) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this!

return {};
}
const coverage = transformData(coverageQuery.data.coverage);
const coverage = transformData(coverageQuery);
return {
coverage_bin_size: 1, // this is required for the histogram to render correctly - there should be a better name for this.
total_length: coverageQuery.data.coverage.length,
total_length: coverageQuery.length,
coverage,
};
}, [coverageQuery?.data?.coverage, chromosome, cellType]);
}, [coverageQuery, chromosome, cellType]);

const handleHistogramBarEnter = useCallback(
(hoverData: [number, number]) => {
Expand Down
Loading