From b2c1f093c280be0da5a2fd2c7e71dc39fcc9ddac Mon Sep 17 00:00:00 2001 From: Sam Park Date: Wed, 15 Jan 2025 12:09:10 -0500 Subject: [PATCH 1/5] add reference genome compatibility modal --- ui/bedbase-types.d.ts | 93 ++++++++++++++++++- .../bed-splash-components/header.tsx | 58 ++++++++++-- .../bed-splash-components/refgenome-modal.tsx | 52 +++++++++++ .../bed2bed/b2b-search-results-table.tsx | 4 +- .../text2bed/t2b-search-results-table.tsx | 6 +- ui/src/pages/bed-splash.tsx | 10 +- ui/src/queries/useBedGenomeStats.ts | 29 ++++++ 7 files changed, 235 insertions(+), 17 deletions(-) create mode 100644 ui/src/components/bed-splash-components/refgenome-modal.tsx create mode 100644 ui/src/queries/useBedGenomeStats.ts diff --git a/ui/bedbase-types.d.ts b/ui/bedbase-types.d.ts index fbf47a7..bece057 100644 --- a/ui/bedbase-types.d.ts +++ b/ui/bedbase-types.d.ts @@ -467,6 +467,27 @@ export interface paths { patch?: never; trace?: never; }; + "/v1/bed/{bed_id}/genome-stats": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get reference genome validation results + * @description Return reference genome validation results for a bed file + * Example: bed: 0dcdf8986a72a3d85805bbc9493a1302 + */ + get: operations["get_ref_gen_results_v1_bed__bed_id__genome_stats_get"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/v1/bedset/example": { parameters: { query?: never; @@ -909,7 +930,10 @@ export interface components { }; /** BedPEPHub */ BedPEPHub: { - /** Sample Name */ + /** + * Sample Name + * @default + */ sample_name: string; /** * Genome @@ -1001,7 +1025,10 @@ export interface components { }; /** BedPEPHubRestrict */ BedPEPHubRestrict: { - /** Sample Name */ + /** + * Sample Name + * @default + */ sample_name: string; /** * Genome @@ -1305,9 +1332,38 @@ export interface components { /** Payload */ payload?: Record; /** Score */ - score: number; + score?: number; metadata?: components["schemas"]["BedMetadataBasic"] | null; }; + /** RefGenValidModel */ + RefGenValidModel: { + /** Provided Genome */ + provided_genome: string; + /** Compared Genome */ + compared_genome: string; + /** + * Xs + * @default 0 + */ + xs: number; + /** Oobr */ + oobr?: number | null; + /** Sequence Fit */ + sequence_fit?: number | null; + /** Assigned Points */ + assigned_points: number; + /** Tier Ranking */ + tier_ranking: number; + }; + /** RefGenValidReturnModel */ + RefGenValidReturnModel: { + /** Id */ + id: string; + /** Provided Genome */ + provided_genome?: string | null; + /** Compared Genome */ + compared_genome: components["schemas"]["RefGenValidModel"][]; + }; /** ServiceInfoResponse */ ServiceInfoResponse: { /** Id */ @@ -2174,6 +2230,37 @@ export interface operations { }; }; }; + get_ref_gen_results_v1_bed__bed_id__genome_stats_get: { + parameters: { + query?: never; + header?: never; + path: { + bed_id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["RefGenValidReturnModel"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; get_example_bedset_record_v1_bedset_example_get: { parameters: { query?: never; diff --git a/ui/src/components/bed-splash-components/header.tsx b/ui/src/components/bed-splash-components/header.tsx index c872647..9e8015c 100644 --- a/ui/src/components/bed-splash-components/header.tsx +++ b/ui/src/components/bed-splash-components/header.tsx @@ -5,23 +5,28 @@ import { components } from '../../../bedbase-types'; import { useCopyToClipboard } from '@uidotdev/usehooks'; import { bytesToSize, formatDateTime } from '../../utils'; import { Dropdown, OverlayTrigger } from 'react-bootstrap'; +import { RefGenomeModal } from './refgenome-modal'; const API_BASE = import.meta.env.VITE_API_BASE || ''; type BedMetadata = components['schemas']['BedMetadataAll']; +type BedGenomeStats = components['schemas']['RefGenValidReturnModel']; type Props = { metadata: BedMetadata; record_identifier: string | undefined; + genomeStats: BedGenomeStats; }; export const BedSplashHeader = (props: Props) => { - const { metadata, record_identifier } = props; + const { metadata, record_identifier, genomeStats } = props; const [, copyToClipboard] = useCopyToClipboard(); const { cart, addBedToCart, removeBedFromCart } = useBedCart(); const [addedToCart, setAddedToCart] = useState(false); const [copiedId, setCopiedId] = useState(false); + const [showRefGenomeModal, setShowRefGenomeModal] = useState(false); + const noFilesToDownload = !metadata.files?.bed_file && !metadata.files?.bigbed_file; @@ -158,19 +163,47 @@ export const BedSplashHeader = (props: Props) => { } > {metadata?.genome_digest ? ( - -
+ <> + +
+ + {metadata.genome_alias || 'No assembly available'} +
+
+ + ) : ( + <> +
{metadata.genome_alias || 'No assembly available'}
- - ) : ( -
- - {metadata.genome_alias || 'No assembly available'} -
+ )} + {genomeStats?.compared_genome && + +
+
Genome compatibility
+
+ } + > +
{ + // conditional required to prevent double click + if (showRefGenomeModal !== true) { + setShowRefGenomeModal(true); + } + }} + > + +
+
+ }

@@ -271,6 +304,13 @@ export const BedSplashHeader = (props: Props) => {
+ { + setShowRefGenomeModal(false); + }} + genomeStats={genomeStats} + /> ); }; diff --git a/ui/src/components/bed-splash-components/refgenome-modal.tsx b/ui/src/components/bed-splash-components/refgenome-modal.tsx new file mode 100644 index 0000000..bad88e2 --- /dev/null +++ b/ui/src/components/bed-splash-components/refgenome-modal.tsx @@ -0,0 +1,52 @@ +import { Modal } from 'react-bootstrap'; +import { components } from '../../../bedbase-types'; + +type BedGenomeStats = components['schemas']['RefGenValidReturnModel']; + +type Props = { + show: boolean; + onHide: () => void; + genomeStats: BedGenomeStats; +}; + +export const RefGenomeModal = (props: Props) => { + const { show, onHide, genomeStats } = props; + + console.log(genomeStats?.compared_genome) + return ( + onHide()} + size="lg" + aria-labelledby="contained-modal-title-vcenter" + centered + > + Reference Genome Compatibility + +
+
+ + + + + + + + + {genomeStats?.compared_genome + ?.sort((a, b) => a.tier_ranking - b.tier_ranking) + .map(item => ( + + + + + ))} + +
GenomeCompatibility Ranking (1 is best)
{item.compared_genome}{item.tier_ranking}
+
+
+
+
+ ); +}; diff --git a/ui/src/components/search/bed2bed/b2b-search-results-table.tsx b/ui/src/components/search/bed2bed/b2b-search-results-table.tsx index e5802e5..9e1be03 100644 --- a/ui/src/components/search/bed2bed/b2b-search-results-table.tsx +++ b/ui/src/components/search/bed2bed/b2b-search-results-table.tsx @@ -130,8 +130,8 @@ export const Bed2BedSearchResultsTable = (props: Props) => { diff --git a/ui/src/components/search/text2bed/t2b-search-results-table.tsx b/ui/src/components/search/text2bed/t2b-search-results-table.tsx index a19a5f4..07181f2 100644 --- a/ui/src/components/search/text2bed/t2b-search-results-table.tsx +++ b/ui/src/components/search/text2bed/t2b-search-results-table.tsx @@ -49,6 +49,8 @@ export const Text2BedSearchResultsTable = (props: Props) => { } }; + console.log(results.results) + return (
@@ -122,8 +124,8 @@ export const Text2BedSearchResultsTable = (props: Props) => { diff --git a/ui/src/pages/bed-splash.tsx b/ui/src/pages/bed-splash.tsx index db18767..c240722 100644 --- a/ui/src/pages/bed-splash.tsx +++ b/ui/src/pages/bed-splash.tsx @@ -1,5 +1,6 @@ import { useParams } from 'react-router-dom'; import { useBedMetadata } from '../queries/useBedMetadata'; +import { useBedGenomeStats } from '../queries/useBedGenomeStats'; import { Layout } from '../components/layout'; import { Col, Row } from 'react-bootstrap'; import { BedSplashHeader } from '../components/bed-splash-components/header'; @@ -34,6 +35,13 @@ export const BedSplash = () => { full: true, }); + const { + data: genomeStats, + } = useBedGenomeStats({ + md5: bedId, + autoRun: true, + }); + const { data: neighbours } = useBedNeighbours({ md5: bedId, limit: 10, @@ -131,7 +139,7 @@ export const BedSplash = () => {
- {metadata !== undefined ? : null} + {metadata !== undefined && genomeStats !== undefined ? : null} diff --git a/ui/src/queries/useBedGenomeStats.ts b/ui/src/queries/useBedGenomeStats.ts new file mode 100644 index 0000000..18dc3c8 --- /dev/null +++ b/ui/src/queries/useBedGenomeStats.ts @@ -0,0 +1,29 @@ +import type { components } from '../../bedbase-types.d.ts'; +import { useQuery } from '@tanstack/react-query'; +import { useBedbaseApi } from '../contexts/api-context'; + +type BedGenomeStatsResponse = components['schemas']['RefGenValidReturnModel']; + +type BedGenomeStatsQuery = { + md5?: string; + autoRun?: boolean; +}; + +export const useBedGenomeStats = (query: BedGenomeStatsQuery) => { + const { api } = useBedbaseApi(); + const { md5, autoRun } = query; + let enabled = false; + if (autoRun !== undefined && autoRun && md5) { + enabled = true; + } + + return useQuery({ + queryKey: ['bed-genome-stats', md5], + queryFn: async () => { + const { data } = await api.get(`/bed/${md5}/genome-stats`); + return data; + }, + enabled: enabled, + staleTime: 0, + }); +}; From 126cf378950905f564f4c6108eb218782f1f207d Mon Sep 17 00:00:00 2001 From: Sam Park Date: Wed, 15 Jan 2025 12:56:17 -0500 Subject: [PATCH 2/5] remove console.logs --- ui/src/components/bed-splash-components/refgenome-modal.tsx | 1 - ui/src/components/search/text2bed/t2b-search-results-table.tsx | 2 -- 2 files changed, 3 deletions(-) diff --git a/ui/src/components/bed-splash-components/refgenome-modal.tsx b/ui/src/components/bed-splash-components/refgenome-modal.tsx index bad88e2..6dbe8d7 100644 --- a/ui/src/components/bed-splash-components/refgenome-modal.tsx +++ b/ui/src/components/bed-splash-components/refgenome-modal.tsx @@ -12,7 +12,6 @@ type Props = { export const RefGenomeModal = (props: Props) => { const { show, onHide, genomeStats } = props; - console.log(genomeStats?.compared_genome) return ( { } }; - console.log(results.results) - return (
From 8b79d92a5739a7924246873e289e38e52b3e40f0 Mon Sep 17 00:00:00 2001 From: Sam Park Date: Wed, 22 Jan 2025 16:21:42 -0500 Subject: [PATCH 3/5] reference genome modal update --- .../bed-splash-components/refgenome-modal.tsx | 62 ++++++++++++------- 1 file changed, 40 insertions(+), 22 deletions(-) diff --git a/ui/src/components/bed-splash-components/refgenome-modal.tsx b/ui/src/components/bed-splash-components/refgenome-modal.tsx index 6dbe8d7..218b063 100644 --- a/ui/src/components/bed-splash-components/refgenome-modal.tsx +++ b/ui/src/components/bed-splash-components/refgenome-modal.tsx @@ -12,6 +12,8 @@ type Props = { export const RefGenomeModal = (props: Props) => { const { show, onHide, genomeStats } = props; + console.log(genomeStats.compared_genome); + return ( { > Reference Genome Compatibility -
-
-
- - - - - - - - {genomeStats?.compared_genome - ?.sort((a, b) => a.tier_ranking - b.tier_ranking) - .map(item => ( - - - - - ))} - -
GenomeCompatibility Ranking (1 is best)
{item.compared_genome}{item.tier_ranking}
-
- +

+ Ranking and score breakdown; the closer the compatibility rank is to 1, the more compatible the genome is with the given reference genome. +

+ + {genomeStats?.compared_genome?.sort((a, b) => a.tier_ranking - b.tier_ranking) + .map(genome => ( +
+
+
{genome.compared_genome}
+ Compatibility Rank: {genome.tier_ranking} + +

xs

+
+
+
+ +

oobr

+
+
+
+ +

sequence fit

+
+
+
+ +
+
+ ))} ); From 10b0321af39bacf2dbb682aca02db6376169705e Mon Sep 17 00:00:00 2001 From: Sam Park Date: Thu, 23 Jan 2025 17:03:14 -0500 Subject: [PATCH 4/5] modal ui updates --- .../bed-splash-components/refgenome-modal.tsx | 69 ++++++++++++------- 1 file changed, 45 insertions(+), 24 deletions(-) diff --git a/ui/src/components/bed-splash-components/refgenome-modal.tsx b/ui/src/components/bed-splash-components/refgenome-modal.tsx index 218b063..1f450a8 100644 --- a/ui/src/components/bed-splash-components/refgenome-modal.tsx +++ b/ui/src/components/bed-splash-components/refgenome-modal.tsx @@ -26,38 +26,59 @@ export const RefGenomeModal = (props: Props) => { Reference Genome Compatibility

- Ranking and score breakdown; the closer the compatibility rank is to 1, the more compatible the genome is with the given reference genome. + Note: Below is a ranking of the compatibility various reference genomes to this BED file (rank 1 is best).

+
+
+
+

Genome

+

xs

+

oobr

+

sequence fit

+

Rank

+
+
+
+ {genomeStats?.compared_genome?.sort((a, b) => a.tier_ranking - b.tier_ranking) .map(genome => ( -
+
-
{genome.compared_genome}
- Compatibility Rank: {genome.tier_ranking} -

xs

-
-
-
+
+
+
+

{genome.compared_genome}

-

oobr

-
-
-
+
+ 30 ? 'text-white' : 'text-dark'}`}> + {((genome.xs || 0) * 100).toFixed(2) + '%'} + +
+
+ +
+ 30 ? 'text-white' : 'text-dark'}`}> + {((genome.oobr || 0) * 100).toFixed(2) + '%'} + +
+
+ +
+ 30 ? 'text-white' : 'text-dark'}`}> + {((genome.sequence_fit || 0) * 100).toFixed(2) + '%'} + +
+
-

sequence fit

-
-
+

Rank {genome.tier_ranking}

+
+
From 0eaf7f8c45c87038b38090f2f7fe5e92e9c1f6cb Mon Sep 17 00:00:00 2001 From: Sam Park Date: Tue, 28 Jan 2025 13:16:01 -0500 Subject: [PATCH 5/5] modal ui --- .../components/bed-splash-components/refgenome-modal.tsx | 8 ++++---- ui/src/custom.scss | 5 +++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/ui/src/components/bed-splash-components/refgenome-modal.tsx b/ui/src/components/bed-splash-components/refgenome-modal.tsx index 1f450a8..2c61e57 100644 --- a/ui/src/components/bed-splash-components/refgenome-modal.tsx +++ b/ui/src/components/bed-splash-components/refgenome-modal.tsx @@ -44,7 +44,7 @@ export const RefGenomeModal = (props: Props) => { {genomeStats?.compared_genome?.sort((a, b) => a.tier_ranking - b.tier_ranking) .map(genome => (
@@ -55,21 +55,21 @@ export const RefGenomeModal = (props: Props) => {

{genome.compared_genome}

-
+
30 ? 'text-white' : 'text-dark'}`}> {((genome.xs || 0) * 100).toFixed(2) + '%'}
-
+
30 ? 'text-white' : 'text-dark'}`}> {((genome.oobr || 0) * 100).toFixed(2) + '%'}
-
+
30 ? 'text-white' : 'text-dark'}`}> {((genome.sequence_fit || 0) * 100).toFixed(2) + '%'} diff --git a/ui/src/custom.scss b/ui/src/custom.scss index 7be2237..d5c409e 100644 --- a/ui/src/custom.scss +++ b/ui/src/custom.scss @@ -311,3 +311,8 @@ td { color: #212529; background: #e9ecef; } + +.genome-card:hover { + border: 1px solid $primary; + filter: brightness(98%); +}