From 88503d5a0cd03b84a6c9d872c0811f80952ab59a Mon Sep 17 00:00:00 2001 From: Katie Stahl Date: Wed, 31 Jul 2024 18:16:34 -0400 Subject: [PATCH] fixing tests and updating data with new models, bug fixes --- .../Pages/Structure/Builder/Builder.tsx | 38 +++---- .../GeneElementInput/GeneElementInput.tsx | 2 +- .../Input/StructuralElementInputAccordion.tsx | 34 +++--- .../TemplatedSequenceElementInput.tsx | 15 ++- .../TxSegmentElementInput.tsx | 4 +- client/src/services/ResponseModels.ts | 16 +-- server/src/curfu/routers/utilities.py | 18 ++- server/src/curfu/schemas.py | 16 +-- server/tests/conftest.py | 104 ++++++------------ server/tests/integration/test_constructors.py | 80 +++++--------- 10 files changed, 131 insertions(+), 196 deletions(-) diff --git a/client/src/components/Pages/Structure/Builder/Builder.tsx b/client/src/components/Pages/Structure/Builder/Builder.tsx index 01946796..40e28cc3 100644 --- a/client/src/components/Pages/Structure/Builder/Builder.tsx +++ b/client/src/components/Pages/Structure/Builder/Builder.tsx @@ -53,7 +53,7 @@ const ELEMENT_TEMPLATE = [ { type: ElementType.geneElement, nomenclature: "", - element_id: uuid(), + elementId: uuid(), gene: { id: "", type: "", @@ -63,11 +63,11 @@ const ELEMENT_TEMPLATE = [ { type: ElementType.transcriptSegmentElement, nomenclature: "", - element_id: uuid(), - exon_start: null, - exon_start_offset: null, - exon_end: null, - exon_end_offset: null, + elementId: uuid(), + exonStart: null, + exonStartOffset: null, + exonEnd: null, + exonEndOffset: null, gene: { id: "", type: "", @@ -77,12 +77,12 @@ const ELEMENT_TEMPLATE = [ { nomenclature: "", type: ElementType.linkerSequenceElement, - element_id: uuid(), + elementId: uuid(), }, { nomenclature: "", type: ElementType.templatedSequenceElement, - element_id: uuid(), + elementId: uuid(), id: "", location: { sequence_id: "", @@ -102,18 +102,18 @@ const ELEMENT_TEMPLATE = [ }, { type: ElementType.unknownGeneElement, - element_id: uuid(), + elementId: uuid(), nomenclature: "?", }, { type: ElementType.multiplePossibleGenesElement, - element_id: uuid(), + elementId: uuid(), nomenclature: "v", }, { type: ElementType.regulatoryElement, nomenclature: "", - element_id: uuid(), + elementId: uuid(), }, ]; @@ -148,7 +148,7 @@ const Builder: React.FC = () => { const sourceClone = Array.from(ELEMENT_TEMPLATE); const item = sourceClone[source.index]; const newItem = Object.assign({}, item); - newItem.element_id = uuid(); + newItem.elementId = uuid(); if (draggableId.includes("RegulatoryElement")) { setFusion({ ...fusion, ...{ regulatoryElement: newItem } }); @@ -190,7 +190,7 @@ const Builder: React.FC = () => { const handleDelete = (uuid: string) => { let items: Array = Array.from(fusion.structure); - items = items.filter((item) => item?.element_id !== uuid); + items = items.filter((item) => item?.elementId !== uuid); setFusion({ ...fusion, ...{ structure: items } }); }; @@ -414,7 +414,7 @@ const Builder: React.FC = () => { style={{ display: "flex" }} > - {ELEMENT_TEMPLATE.map(({ element_id, type }, index) => { + {ELEMENT_TEMPLATE.map(({ elementId, type }, index) => { if ( (fusion.type === "AssayedFusion" && type !== ElementType.multiplePossibleGenesElement) || @@ -423,8 +423,8 @@ const Builder: React.FC = () => { ) { return ( { {snapshot.isDragging && ( {elementNameMap[type].icon}{" "} @@ -526,8 +526,8 @@ const Builder: React.FC = () => { return ( element && ( {(provided) => ( diff --git a/client/src/components/Pages/Structure/Input/GeneElementInput/GeneElementInput.tsx b/client/src/components/Pages/Structure/Input/GeneElementInput/GeneElementInput.tsx index 24180c9c..43a9ae9f 100644 --- a/client/src/components/Pages/Structure/Input/GeneElementInput/GeneElementInput.tsx +++ b/client/src/components/Pages/Structure/Input/GeneElementInput/GeneElementInput.tsx @@ -10,6 +10,7 @@ import { getGeneNomenclature, } from "../../../../../services/main"; import StructuralElementInputAccordion from "../StructuralElementInputAccordion"; +import React from "react"; interface GeneElementInputProps extends StructuralElementInputProps { element: ClientGeneElement; @@ -70,7 +71,6 @@ const GeneElementInput: React.FC = ({ setGene={setGene} geneText={geneText} setGeneText={setGeneText} - style={{ width: 125 }} tooltipDirection="left" /> ); diff --git a/client/src/components/Pages/Structure/Input/StructuralElementInputAccordion.tsx b/client/src/components/Pages/Structure/Input/StructuralElementInputAccordion.tsx index 05b4fe92..24fcf0b9 100644 --- a/client/src/components/Pages/Structure/Input/StructuralElementInputAccordion.tsx +++ b/client/src/components/Pages/Structure/Input/StructuralElementInputAccordion.tsx @@ -72,7 +72,7 @@ const StructuralElementInputAccordion: React.FC< inputElements, validated, icon, - pendingResponse + pendingResponse, }) => { const classes = useStyles(); @@ -81,20 +81,24 @@ const StructuralElementInputAccordion: React.FC< : - - { - event.stopPropagation(); - handleDelete(element.element_id); - }} - onFocus={(event) => event.stopPropagation()} - > - - - + pendingResponse ? ( + + ) : ( + + { + event.stopPropagation(); + handleDelete(element.elementId); + }} + onFocus={(event) => event.stopPropagation()} + > + + + + ) } title={element.nomenclature ? element.nomenclature : null} classes={{ diff --git a/client/src/components/Pages/Structure/Input/TemplatedSequenceElementInput/TemplatedSequenceElementInput.tsx b/client/src/components/Pages/Structure/Input/TemplatedSequenceElementInput/TemplatedSequenceElementInput.tsx index 6ec4d5a5..b8e1ab69 100644 --- a/client/src/components/Pages/Structure/Input/TemplatedSequenceElementInput/TemplatedSequenceElementInput.tsx +++ b/client/src/components/Pages/Structure/Input/TemplatedSequenceElementInput/TemplatedSequenceElementInput.tsx @@ -21,12 +21,14 @@ const TemplatedSequenceElementInput: React.FC< const [chromosome, setChromosome] = useState( element.inputChromosome || "" ); - const [strand, setStrand] = useState(element.strand || "+"); + const [strand, setStrand] = useState( + element.strand === 1 ? "+" : "-" + ); const [startPosition, setStartPosition] = useState( - element.inputStart || "" + element.inputStart !== null ? `${element.inputStart}` : "" ); const [endPosition, setEndPosition] = useState( - element.inputEnd || "" + element.inputEnd !== null ? `${element.inputEnd}` : "" ); const [inputError, setInputError] = useState(""); @@ -74,13 +76,14 @@ const TemplatedSequenceElementInput: React.FC< templatedSequenceResponse.element ).then((nomenclatureResponse) => { if (nomenclatureResponse.nomenclature) { + console.log(templatedSequenceResponse.element); const templatedSequenceElement: ClientTemplatedSequenceElement = { ...templatedSequenceResponse.element, - element_id: element.element_id, + elementId: element.elementId, nomenclature: nomenclatureResponse.nomenclature, inputChromosome: chromosome, - input_start: startPosition, - input_end: endPosition, + inputStart: Number(startPosition), + inputEnd: Number(endPosition), }; handleSave(index, templatedSequenceElement); } diff --git a/client/src/components/Pages/Structure/Input/TxSegmentElementInput/TxSegmentElementInput.tsx b/client/src/components/Pages/Structure/Input/TxSegmentElementInput/TxSegmentElementInput.tsx index 45cf9a67..cabeb193 100644 --- a/client/src/components/Pages/Structure/Input/TxSegmentElementInput/TxSegmentElementInput.tsx +++ b/client/src/components/Pages/Structure/Input/TxSegmentElementInput/TxSegmentElementInput.tsx @@ -58,7 +58,9 @@ const TxSegmentCompInput: React.FC = ({ const [txGene, setTxGene] = useState(element.inputGene || ""); const [txGeneText, setTxGeneText] = useState(""); - const [txStrand, setTxStrand] = useState(element.inputStrand || "+"); + const [txStrand, setTxStrand] = useState( + element.inputStrand === 1 ? "+" : "-" + ); const [txChrom, setTxChrom] = useState(element.inputChr || ""); const [txChromText, setTxChromText] = useState(""); diff --git a/client/src/services/ResponseModels.ts b/client/src/services/ResponseModels.ts index 7815730c..925427be 100644 --- a/client/src/services/ResponseModels.ts +++ b/client/src/services/ResponseModels.ts @@ -534,12 +534,12 @@ export interface ClientTranscriptSegmentElement { inputStrand?: Strand | null; inputGene?: string | null; inputChr?: string | null; - inputGenomicStart?: number | null; - inputGenomicEnd?: number | null; - inputExonStart?: number | null; - inputExonStartOffset?: number | null; - inputExonEnd?: number | null; - inputExonEndOffset?: number | null; + inputGenomicStart?: string | null; + inputGenomicEnd?: string | null; + inputExonStart?: string | null; + inputExonStartOffset?: string | null; + inputExonEnd?: string | null; + inputExonEndOffset?: string | null; } /** * Gene element used client-side. @@ -560,8 +560,8 @@ export interface ClientTemplatedSequenceElement { region: SequenceLocation; strand: Strand; inputChromosome: string | null; - inputStart: number | null; - inputEnd: number | null; + inputStart: string | null; + inputEnd: string | null; } /** * Linker element class used client-side. diff --git a/server/src/curfu/routers/utilities.py b/server/src/curfu/routers/utilities.py index 1c9dbe17..2b4a27b0 100644 --- a/server/src/curfu/routers/utilities.py +++ b/server/src/curfu/routers/utilities.py @@ -107,16 +107,14 @@ async def get_genome_coords( if exon_end is not None and exon_end_offset is None: exon_end_offset = 0 - response = ( - await request.app.state.fusor.cool_seq_tool.transcript_to_genomic_coordinates( - gene=gene, - transcript=transcript, - exon_start=exon_start, - exon_end=exon_end, - exon_start_offset=exon_start_offset, - exon_end_offset=exon_end_offset, - residue_mode="inter-residue", - ) + response = await request.app.state.fusor.cool_seq_tool.ex_g_coords_mapper.transcript_to_genomic_coordinates( + gene=gene, + transcript=transcript, + exon_start=exon_start, + exon_end=exon_end, + exon_start_offset=exon_start_offset, + exon_end_offset=exon_end_offset, + residue_mode="inter-residue", ) warnings = response.warnings if warnings: diff --git a/server/src/curfu/schemas.py b/server/src/curfu/schemas.py index f26b7f9d..6e8b1e88 100644 --- a/server/src/curfu/schemas.py +++ b/server/src/curfu/schemas.py @@ -53,12 +53,12 @@ class ClientTranscriptSegmentElement(TranscriptSegmentElement, ClientStructuralE inputStrand: Strand | None = None inputGene: str | None = None inputChr: str | None = None - inputGenomicStart: int | None = None - inputGenomicEnd: int | None = None - inputExonStart: int | None = None - inputExonStartOffset: int | None = None - inputExonEnd: int | None = None - inputExonEndOffset: int | None = None + inputGenomicStart: str | None = None + inputGenomicEnd: str | None = None + inputExonStart: str | None = None + inputExonStartOffset: str | None = None + inputExonEnd: str | None = None + inputExonEndOffset: str | None = None class ClientLinkerElement(LinkerElement, ClientStructuralElement): @@ -69,8 +69,8 @@ class ClientTemplatedSequenceElement(TemplatedSequenceElement, ClientStructuralE """Templated sequence element used client-side.""" inputChromosome: str | None - inputStart: int | None - inputEnd: int | None + inputStart: str | None + inputEnd: str | None class ClientGeneElement(GeneElement, ClientStructuralElement): diff --git a/server/tests/conftest.py b/server/tests/conftest.py index 74a7f769..66ac022b 100644 --- a/server/tests/conftest.py +++ b/server/tests/conftest.py @@ -4,6 +4,7 @@ from collections.abc import Callable import pytest +import pytest_asyncio from curfu.main import app, get_domain_services, get_gene_services, start_fusor from httpx import AsyncClient @@ -16,7 +17,7 @@ def event_loop(request): loop.close() -@pytest.fixture(scope="session") +@pytest_asyncio.fixture(scope="session") async def async_client(): """Provide httpx async client fixture.""" app.state.fusor = await start_fusor() @@ -30,7 +31,7 @@ async def async_client(): response_callback_type = Callable[[dict, dict], None] -@pytest.fixture(scope="session") +@pytest_asyncio.fixture(scope="session") async def check_response(async_client): """Provide base response check function. Use in individual tests.""" @@ -66,10 +67,9 @@ async def check_response( def alk_descriptor(): """Gene descriptor for ALK gene""" return { - "id": "normalize.gene:hgnc%3A427", "type": "Gene", "label": "ALK", - "gene_id": "hgnc:427", + "id": "hgnc:427", } @@ -77,10 +77,9 @@ def alk_descriptor(): def tpm3_descriptor(): """Gene descriptor for TPM3 gene""" return { - "id": "normalize.gene:TPM3", "type": "Gene", "label": "TPM3", - "gene_id": "hgnc:12012", + "id": "hgnc:12012", } @@ -88,10 +87,9 @@ def tpm3_descriptor(): def ntrk1_descriptor(): """Gene descriptor for NTRK1 gene""" return { - "id": "normalize.gene:NTRK1", "type": "Gene", "label": "NTRK1", - "gene_id": "hgnc:8031", + "id": "hgnc:8031", } @@ -109,22 +107,14 @@ def ntrk1_tx_element_start(ntrk1_descriptor): return { "type": "TranscriptSegmentElement", "transcript": "refseq:NM_002529.3", - "exon_start": 2, - "exon_start_offset": 1, + "exonStart": 2, + "exonStartOffset": 1, "gene": ntrk1_descriptor, - "element_genomic_start": { + "elementGenomicStart": { "id": "fusor.location_descriptor:NC_000001.11", "type": "SequenceLocation", - "label": "NC_000001.11", - "location": { - "type": "SequenceLocation", - "sequence_id": "refseq:NC_000001.11", - "interval": { - "type": "SequenceInterval", - "start": {"type": "Number", "value": 156864429}, - "end": {"type": "Number", "value": 156864430}, - }, - }, + "start": 156864429, + "end": 156864430, }, } @@ -137,38 +127,22 @@ def tpm3_tx_t_element(tpm3_descriptor): return { "type": "TranscriptSegmentElement", "transcript": "refseq:NM_152263.4", - "exon_start": 6, - "exon_start_offset": 72, - "exon_end": 6, - "exon_end_offset": -5, + "exonStart": 6, + "exonStartOffset": 72, + "exonEnd": 6, + "exonEndOffset": -5, "gene": tpm3_descriptor, - "element_genomic_start": { + "elementGenomicStart": { "id": "fusor.location_descriptor:NC_000001.11", "type": "SequenceLocation", - "label": "NC_000001.11", - "location": { - "type": "SequenceLocation", - "sequence_id": "refseq:NC_000001.11", - "interval": { - "type": "SequenceInterval", - "start": {"type": "Number", "value": 154171416}, - "end": {"type": "Number", "value": 154171417}, - }, - }, + "start": 154171416, + "end": 154171417, }, - "element_genomic_end": { + "elementGenomicEnd": { "id": "fusor.location_descriptor:NC_000001.11", "type": "SequenceLocation", - "label": "NC_000001.11", - "location": { - "type": "SequenceLocation", - "sequence_id": "refseq:NC_000001.11", - "interval": { - "type": "SequenceInterval", - "start": {"type": "Number", "value": 154171417}, - "end": {"type": "Number", "value": 154171418}, - }, - }, + "start": 154171417, + "end": 154171418, }, } @@ -181,37 +155,21 @@ def tpm3_tx_g_element(tpm3_descriptor): return { "type": "TranscriptSegmentElement", "transcript": "refseq:NM_152263.4", - "exon_start": 6, - "exon_start_offset": 5, - "exon_end": 6, - "exon_end_offset": -70, + "exonStart": 6, + "exonStartOffset": 5, + "exonEnd": 6, + "exonEndOffset": -70, "gene": tpm3_descriptor, - "element_genomic_start": { + "elementGenomicStart": { "id": "fusor.location_descriptor:NC_000001.11", "type": "SequenceLocation", - "label": "NC_000001.11", - "location": { - "type": "SequenceLocation", - "sequence_id": "refseq:NC_000001.11", - "interval": { - "type": "SequenceInterval", - "start": {"type": "Number", "value": 154171483}, - "end": {"type": "Number", "value": 154171484}, - }, - }, + "start": 154171483, + "end": 154171484, }, - "element_genomic_end": { + "elementGenomicEnd": { "id": "fusor.location_descriptor:NC_000001.11", "type": "SequenceLocation", - "label": "NC_000001.11", - "location": { - "type": "SequenceLocation", - "sequence_id": "refseq:NC_000001.11", - "interval": { - "type": "SequenceInterval", - "start": {"type": "Number", "value": 154171482}, - "end": {"type": "Number", "value": 154171483}, - }, - }, + "start": 154171482, + "end": 154171483, }, } diff --git a/server/tests/integration/test_constructors.py b/server/tests/integration/test_constructors.py index 89e03cc4..c9f1fb3a 100644 --- a/server/tests/integration/test_constructors.py +++ b/server/tests/integration/test_constructors.py @@ -19,7 +19,6 @@ def check_gene_element_response( assert response_gd["id"] == expected_id assert response_gd["type"] == expected_gd["type"] assert response_gd["label"] == expected_gd["label"] - assert response_gd["gene_id"] == expected_gd["gene_id"] alk_gene_response = {"warnings": [], "element": alk_gene_element} @@ -27,13 +26,13 @@ def check_gene_element_response( "/api/construct/structural_element/gene?term=hgnc:427", alk_gene_response, check_gene_element_response, - expected_id="normalize.gene:hgnc%3A427", + expected_id="hgnc:427", ) await check_response( "/api/construct/structural_element/gene?term=ALK", alk_gene_response, check_gene_element_response, - expected_id="normalize.gene:ALK", + expected_id="hgnc:427", ) fake_id = "hgnc:99999999" await check_response( @@ -57,19 +56,19 @@ def check_tx_element_response(response: dict, expected_response: dict): expected_element = expected_response["element"] assert response_element["transcript"] == expected_element["transcript"] assert response_element["gene"] == expected_element["gene"] - assert response_element.get("exon_start") == expected_element.get("exon_start") - assert response_element.get("exon_start_offset") == expected_element.get( - "exon_start_offset" + assert response_element.get("exonStart") == expected_element.get("exonStart") + assert response_element.get("exonStartOffset") == expected_element.get( + "exonStartOffset" ) - assert response_element.get("exon_end") == expected_element.get("exon_end") - assert response_element.get("exon_end_offset") == expected_element.get( - "exon_end_offset" + assert response_element.get("exonEnd") == expected_element.get("exonEnd") + assert response_element.get("exonEndOffset") == expected_element.get( + "exonEndOffset" ) - assert response_element.get("element_genomic_start") == expected_element.get( - "element_genomic_start" + assert response_element.get("elementGenomicStart") == expected_element.get( + "elementGenomicStart" ) - assert response_element.get("element_genomic_end") == expected_element.get( - "element_genomic_end" + assert response_element.get("elementGenomicEnd") == expected_element.get( + "elementGenomicEnd" ) return check_tx_element_response @@ -92,13 +91,11 @@ def check_re_response(response: dict, expected_response: dict): response_re = response["regulatoryElement"] expected_re = expected_response["regulatoryElement"] assert response_re["type"] == expected_re["type"] - assert response_re.get("regulatory_class") == expected_re.get( - "regulatory_class" - ) + assert response_re.get("regulatoryClass") == expected_re.get("regulatoryClass") assert response_re.get("featureId") == expected_re.get("featureId") assert response_re.get("associatedGene") == expected_re.get("associatedGene") - assert response_re.get("location_descriptor") == expected_re.get( - "location_descriptor" + assert response_re.get("sequenceLocation") == expected_re.get( + "sequenceLocation" ) return check_re_response @@ -121,37 +118,11 @@ def check_temp_seq_response(response: dict, expected_response: dict): assert response_elem["region"]["id"] == expected_elem["region"]["id"] assert response_elem["region"]["type"] == expected_elem["region"]["type"] assert ( - response_elem["region"]["location_id"] - == expected_elem["region"]["location_id"] - ) - assert ( - response_elem["region"]["location"]["type"] - == expected_elem["region"]["location"]["type"] - ) - assert ( - response_elem["region"]["location"]["sequence_id"] - == expected_elem["region"]["location"]["sequence_id"] - ) - assert ( - response_elem["region"]["location"]["interval"]["type"] - == expected_elem["region"]["location"]["interval"]["type"] - ) - assert ( - response_elem["region"]["location"]["interval"]["start"]["type"] - == expected_elem["region"]["location"]["interval"]["start"]["type"] - ) - assert ( - response_elem["region"]["location"]["interval"]["start"]["value"] - == expected_elem["region"]["location"]["interval"]["start"]["value"] - ) - assert ( - response_elem["region"]["location"]["interval"]["end"]["type"] - == expected_elem["region"]["location"]["interval"]["end"]["type"] - ) - assert ( - response_elem["region"]["location"]["interval"]["end"]["value"] - == expected_elem["region"]["location"]["interval"]["end"]["value"] + response_elem["region"]["sequenceReference"]["id"] + == expected_elem["region"]["sequenceReference"]["id"] ) + assert response_elem["region"]["start"] == expected_elem["region"]["start"] + assert response_elem["region"]["end"] == expected_elem["region"]["end"] return check_temp_seq_response @@ -164,21 +135,21 @@ async def test_build_tx_segment_ect( coordinates and transcript. """ await check_response( - "/api/construct/structural_element/tx_segment_ect?transcript=NM_002529.3&exon_start=2&exon_start_offset=1", + "/api/construct/structural_element/tx_segment_ect?transcript=NM_002529.3&exonStart=2&exonStartOffset=1", {"element": ntrk1_tx_element_start}, check_tx_element_response, ) - # test require exon_start or exon_end + # test require exonStart or exonEnd await check_response( "/api/construct/structural_element/tx_segment_ect?transcript=NM_002529.3", - {"warnings": ["Must provide either `exon_start` or `exon_end`"]}, + {"warnings": ["Must provide either `exonStart` or `exonEnd`"]}, check_tx_element_response, ) # test handle invalid transcript await check_response( - "/api/construct/structural_element/tx_segment_ect?transcript=NM_0012529.3&exon_start=3", + "/api/construct/structural_element/tx_segment_ect?transcript=NM_0012529.3&exonStart=3", {"warnings": ["Unable to get exons for NM_0012529.3"]}, check_tx_element_response, ) @@ -225,12 +196,11 @@ async def test_build_reg_element(check_response, check_reg_element_response): { "regulatoryElement": { "associatedGene": { - "gene_id": "hgnc:1097", - "id": "normalize.gene:braf", + "id": "hgnc:1097", "label": "BRAF", "type": "Gene", }, - "regulatory_class": "promoter", + "regulatoryClass": "promoter", "type": "RegulatoryElement", } },