From de83ee90b0f97755a15b77223817c54d982ac48e Mon Sep 17 00:00:00 2001 From: Katie Stahl Date: Tue, 30 Jul 2024 15:38:18 -0400 Subject: [PATCH] pass around formatted fusion to reduce repeated code --- .../Pages/Summary/JSON/SummaryJSON.tsx | 107 +----------------- .../Pages/Summary/Success/Success.tsx | 2 +- client/src/services/ResponseModels.ts | 86 +++++--------- server/src/curfu/schemas.py | 4 +- 4 files changed, 37 insertions(+), 162 deletions(-) diff --git a/client/src/components/Pages/Summary/JSON/SummaryJSON.tsx b/client/src/components/Pages/Summary/JSON/SummaryJSON.tsx index 1e4a170e..21b6f5a3 100644 --- a/client/src/components/Pages/Summary/JSON/SummaryJSON.tsx +++ b/client/src/components/Pages/Summary/JSON/SummaryJSON.tsx @@ -1,118 +1,23 @@ import copy from "clipboard-copy"; import React, { useEffect, useState } from "react"; +import { validateFusion } from "../../../../services/main"; import { - ClientElementUnion, - ElementUnion, - validateFusion, -} from "../../../../services/main"; -import { - AssayedFusion, - CategoricalFusion, - FunctionalDomain, - GeneElement, - LinkerElement, - MultiplePossibleGenesElement, - TemplatedSequenceElement, - TranscriptSegmentElement, - UnknownGeneElement, + FormattedAssayedFusion, + FormattedCategoricalFusion, } from "../../../../services/ResponseModels"; -import { FusionType } from "../Main/Summary"; import "./SummaryJSON.scss"; interface Props { - fusion: FusionType; + formattedFusion: FormattedAssayedFusion | FormattedCategoricalFusion; } -export const SummaryJSON: React.FC = ({ fusion }) => { +export const SummaryJSON: React.FC = ({ formattedFusion }) => { const [isDown, setIsDown] = useState(false); const [isCopied, setIsCopied] = useState(false); const [printedFusion, setPrintedFusion] = useState(""); const [validationErrors, setValidationErrors] = useState([]); - /** - * On component render, restructure fusion to drop properties used for client state purposes, - * transmit to validation endpoint, and update local copy. - */ useEffect(() => { - const structuralElements: ElementUnion[] = fusion.structure?.map( - (element: ClientElementUnion) => { - switch (element.type) { - case "GeneElement": - const geneElement: GeneElement = { - type: element.type, - gene: element.gene, - }; - return geneElement; - case "LinkerSequenceElement": - const linkerElement: LinkerElement = { - type: element.type, - linker_sequence: element.linker_sequence, - }; - return linkerElement; - case "TemplatedSequenceElement": - const templatedSequenceElement: TemplatedSequenceElement = { - type: element.type, - region: element.region, - strand: element.strand, - }; - return templatedSequenceElement; - case "TranscriptSegmentElement": - const txSegmentElement: TranscriptSegmentElement = { - type: element.type, - transcript: element.transcript, - exon_start: element.exon_start, - exon_start_offset: element.exon_start_offset, - exon_end: element.exon_end, - exon_end_offset: element.exon_end_offset, - gene: element.gene, - element_genomic_start: element.element_genomic_start, - element_genomic_end: element.element_genomic_end, - }; - return txSegmentElement; - case "MultiplePossibleGenesElement": - case "UnknownGeneElement": - const newElement: - | MultiplePossibleGenesElement - | UnknownGeneElement = { - type: element.type, - }; - return newElement; - default: - throw new Error("Unrecognized element type"); - } - } - ); - const regulatoryElements = fusion.regulatoryElements?.map((re) => ({ - type: re.type, - associated_gene: re.associated_gene, - regulatory_class: re.regulatory_class, - featureId: re.featureId, - genomic_location: re.genomic_location, - })); - let formattedFusion: AssayedFusion | CategoricalFusion; - if (fusion.type === "AssayedFusion") { - formattedFusion = { - ...fusion, - structure: structuralElements, - regulatoryElements: regulatoryElements, - }; - } else { - const criticalDomains: FunctionalDomain[] = - fusion.criticalFunctionalDomains?.map((domain) => ({ - _id: domain._id, - label: domain.label, - status: domain.status, - associated_gene: domain.associated_gene, - sequence_location: domain.sequence_location, - })); - formattedFusion = { - ...fusion, - structure: structuralElements, - regulatoryElements: regulatoryElements, - criticalFunctionalDomains: criticalDomains, - }; - } - // make request validateFusion(formattedFusion).then((response) => { if (response.warnings && response.warnings?.length > 0) { @@ -126,7 +31,7 @@ export const SummaryJSON: React.FC = ({ fusion }) => { setPrintedFusion(JSON.stringify(response.fusion, null, 2)); } }); - }, [fusion]); // should be blank? + }, [formattedFusion]); const handleCopy = () => { copy(printedFusion); diff --git a/client/src/components/Pages/Summary/Success/Success.tsx b/client/src/components/Pages/Summary/Success/Success.tsx index c35e2b4e..04e6b218 100644 --- a/client/src/components/Pages/Summary/Success/Success.tsx +++ b/client/src/components/Pages/Summary/Success/Success.tsx @@ -60,7 +60,7 @@ export const Success: React.FC = ({ fusion }) => {
- {fusion && } + {fusion && }
diff --git a/client/src/services/ResponseModels.ts b/client/src/services/ResponseModels.ts index 23a00384..50425eb8 100644 --- a/client/src/services/ResponseModels.ts +++ b/client/src/services/ResponseModels.ts @@ -6,9 +6,9 @@ */ /** - * Specify possible Fusion types. + * Form of evidence supporting identification of the fusion. */ -export type FusionType = "CategoricalFusion" | "AssayedFusion"; +export type Evidence = "observed" | "inferred"; /** * Define possible classes of Regulatory Elements. Options are the possible values * for /regulatory_class value property in the INSDC controlled vocabulary: @@ -66,20 +66,6 @@ export type Range = [number | null, number | null]; * A character string of Residues that represents a biological sequence using the conventional sequence order (5'-to-3' for nucleic acid sequences, and amino-to-carboxyl for amino acid sequences). IUPAC ambiguity codes are permitted in Sequence Strings. */ export type SequenceString = string; -/** - * Define possible structural element type values. - */ -export type StructuralElementType = - | "TranscriptSegmentElement" - | "TemplatedSequenceElement" - | "LinkerSequenceElement" - | "GeneElement" - | "UnknownGeneElement" - | "MultiplePossibleGenesElement"; -/** - * Form of evidence supporting identification of the fusion. - */ -export type Evidence = "observed" | "inferred"; /** * Create enum for positive and negative strand */ @@ -95,13 +81,34 @@ export type EventType = "rearrangement" | "read-through" | "trans-splicing"; export type DomainStatus = "lost" | "preserved"; /** - * Define Fusion class + * Information pertaining to the assay used in identifying the fusion. + */ +export interface Assay { + type?: "Assay"; + assayName?: string | null; + assayId?: string | null; + methodUri?: string | null; + fusionDetection?: Evidence | null; +} +/** + * Assayed gene fusions from biological specimens are directly detected using + * RNA-based gene fusion assays, or alternatively may be inferred from genomic + * rearrangements detected by whole genome sequencing or by coarser-scale cytogenomic + * assays. Example: an EWSR1 fusion inferred from a breakapart FISH assay. */ -export interface AbstractFusion { - type: FusionType; +export interface AssayedFusion { + type?: "AssayedFusion"; regulatoryElement?: RegulatoryElement | null; - structure: BaseStructuralElement[]; + structure: ( + | TranscriptSegmentElement + | GeneElement + | TemplatedSequenceElement + | LinkerElement + | UnknownGeneElement + )[]; readingFramePreserved?: boolean | null; + causativeEvent?: CausativeEvent | null; + assay?: Assay | null; } /** * Define RegulatoryElement class. @@ -318,43 +325,6 @@ export interface SequenceReference { circular?: boolean | null; [k: string]: unknown; } -/** - * Define base structural element class. - */ -export interface BaseStructuralElement { - type: StructuralElementType; - [k: string]: unknown; -} -/** - * Information pertaining to the assay used in identifying the fusion. - */ -export interface Assay { - type?: "Assay"; - assayName?: string | null; - assayId?: string | null; - methodUri?: string | null; - fusionDetection?: Evidence | null; -} -/** - * Assayed gene fusions from biological specimens are directly detected using - * RNA-based gene fusion assays, or alternatively may be inferred from genomic - * rearrangements detected by whole genome sequencing or by coarser-scale cytogenomic - * assays. Example: an EWSR1 fusion inferred from a breakapart FISH assay. - */ -export interface AssayedFusion { - type?: "AssayedFusion"; - regulatoryElement?: RegulatoryElement | null; - structure: ( - | TranscriptSegmentElement - | GeneElement - | TemplatedSequenceElement - | LinkerElement - | UnknownGeneElement - )[]; - readingFramePreserved?: boolean | null; - causativeEvent?: CausativeEvent | null; - assay?: Assay | null; -} /** * Define TranscriptSegment class */ @@ -849,5 +819,5 @@ export interface TxSegmentElementResponse { */ export interface ValidateFusionResponse { warnings?: string[] | null; - fusion: AbstractFusion | null; + fusion: CategoricalFusion | AssayedFusion | null; } diff --git a/server/src/curfu/schemas.py b/server/src/curfu/schemas.py index 5a699131..be75dcf4 100644 --- a/server/src/curfu/schemas.py +++ b/server/src/curfu/schemas.py @@ -5,7 +5,6 @@ from cool_seq_tool.schemas import GenomicData from fusor.models import ( - AbstractFusion, Assay, AssayedFusion, AssayedFusionElements, @@ -13,6 +12,7 @@ CategoricalFusionElements, CausativeEvent, FunctionalDomain, + Fusion, GeneElement, LinkerElement, MultiplePossibleGenesElement, @@ -180,7 +180,7 @@ class AssociatedDomainResponse(Response): class ValidateFusionResponse(Response): """Response model for Fusion validation endpoint.""" - fusion: AbstractFusion | None + fusion: Fusion | None class ExonCoordsRequest(BaseModel):