Skip to content

Commit

Permalink
feat: add better error messaging (#335)
Browse files Browse the repository at this point in the history
* feat: add better error messaging

* feat: add better error messaging

* feat: print all validation errors for fusion

* update fusor version with error message fixes
  • Loading branch information
katiestahl authored Sep 26, 2024
1 parent d15a82c commit 100bb39
Show file tree
Hide file tree
Showing 11 changed files with 79 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@ const GeneElementInput: React.FC<GeneElementInputProps> = ({
handleDelete,
icon,
}) => {
const [errors, setErrors] = useState<string[]>([]);
const [gene, setGene] = useState<string>(element.gene?.label || "");
const [geneText, setGeneText] = useState<string>("");
const validated = gene !== "" && geneText == "";
const [expanded, setExpanded] = useState<boolean>(!validated);
const [pendingResponse, setPendingResponse] = useState(false);
const geneNotFound = "Gene not found";

useEffect(() => {
if (validated) buildGeneElement();
Expand All @@ -39,11 +41,14 @@ const GeneElementInput: React.FC<GeneElementInputProps> = ({
geneElementResponse.warnings &&
geneElementResponse.warnings.length > 0
) {
setGeneText("Gene not found");
setGeneText(geneNotFound);
setErrors([geneNotFound]);
setPendingResponse(false);
} else if (
geneElementResponse.element &&
geneElementResponse.element.gene
) {
setErrors([]);
getGeneNomenclature(geneElementResponse.element).then(
(nomenclatureResponse: NomenclatureResponse) => {
if (
Expand Down Expand Up @@ -81,6 +86,7 @@ const GeneElementInput: React.FC<GeneElementInputProps> = ({
handleDelete,
inputElements,
validated,
errors,
icon,
pendingResponse,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ const LinkerElementInput: React.FC<LinkerElementInputProps> = ({
handleDelete,
inputElements,
validated,
errors: linkerError ? ["Invalid linker sequence"] : [],
icon,
});
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ const RegulatoryElementInput: React.FC<RegulatoryElementInputProps> = ({
const validated = gene !== "" && geneText == "" && elementClass !== "default";
const [expanded, setExpanded] = useState<boolean>(!validated);

const [errors, setErrors] = useState<string[]>([]);

useEffect(() => {
if (validated) handleAdd();
}, [gene, geneText, elementClass]);
Expand All @@ -73,16 +75,19 @@ const RegulatoryElementInput: React.FC<RegulatoryElementInputProps> = ({
if (elementClass === "default") return;
getRegulatoryElement(elementClass, gene).then((reResponse) => {
if (reResponse.warnings && reResponse.warnings.length > 0) {
throw new Error(reResponse.warnings[0]);
setErrors(reResponse.warnings);
return;
}
getRegElementNomenclature(reResponse.regulatoryElement).then(
(nomenclatureResponse) => {
if (
nomenclatureResponse.warnings &&
nomenclatureResponse.warnings.length > 0
) {
throw new Error(nomenclatureResponse.warnings[0]);
setErrors(nomenclatureResponse.warnings);
return;
}
setErrors([]);
const newRegElement: ClientRegulatoryElement = {
...reResponse.regulatoryElement,
elementId: element.elementId,
Expand All @@ -104,6 +109,7 @@ const RegulatoryElementInput: React.FC<RegulatoryElementInputProps> = ({
setElementClass("default");
setGene("");
setGeneText("");
setErrors([]);
};

const inputElements = (
Expand All @@ -127,6 +133,7 @@ const RegulatoryElementInput: React.FC<RegulatoryElementInputProps> = ({
handleDelete: handleDeleteElement,
inputElements,
validated,
errors,
icon,
});
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { Tooltip, makeStyles, CircularProgress } from "@material-ui/core";
import { styled } from "@mui/material/styles";
import IconButton, { IconButtonProps } from "@mui/material/IconButton";
import Card from "@mui/material/Card";
Expand All @@ -8,11 +7,13 @@ import CardActions from "@mui/material/CardActions";
import Collapse from "@mui/material/Collapse";
import DeleteIcon from "@material-ui/icons/Delete";
import CheckCircleIcon from "@mui/icons-material/CheckCircle";
import ErrorIcon from "@mui/icons-material/Error";
import EditIcon from "@material-ui/icons/Edit";
import { red, green } from "@material-ui/core/colors";
import "./StructuralElementInputAccordion.scss";
import { BaseStructuralElementProps } from "./StructuralElementInputProps";
import React from "react";
import { Alert, CircularProgress, Tooltip } from "@mui/material";
import { makeStyles, Typography } from "@material-ui/core";

const useStyles = makeStyles(() => ({
cardHeaderAction: {
Expand Down Expand Up @@ -42,6 +43,7 @@ interface StructuralElementInputAccordionProps
setExpanded?: React.Dispatch<React.SetStateAction<boolean>>;
inputElements?: JSX.Element;
validated: boolean;
errors?: string[];
icon: JSX.Element;
pendingResponse?: boolean;
}
Expand Down Expand Up @@ -71,6 +73,7 @@ const StructuralElementInputAccordion: React.FC<
handleDelete,
inputElements,
validated,
errors,
icon,
pendingResponse,
}) => {
Expand Down Expand Up @@ -121,20 +124,25 @@ const StructuralElementInputAccordion: React.FC<
root: classes.cardActionsRoot,
}}
>
{validated ? (
{errors?.length === 0 && validated ? (
<Tooltip title="Validation successful">
<CheckCircleIcon
className="input-correct"
style={{ color: green[500] }}
/>
</Tooltip>
) : (
<Tooltip title="Invalid component">
<ErrorIcon
className="input-incorrect"
style={{ color: red[500] }}
/>
</Tooltip>
<>
{errors && errors.length ? (
<Alert severity="error">
{errors?.map((e) => {
return <Typography variant="body2">{e}</Typography>;
})}
</Alert>
) : (
<></>
)}
</>
)}
{inputElements ? (
<ExpandMore
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ interface TemplatedSequenceElementInputProps
const TemplatedSequenceElementInput: React.FC<
TemplatedSequenceElementInputProps
> = ({ element, handleSave, handleDelete, icon }) => {
const [errors, setErrors] = useState<string[]>([]);
const [chromosome, setChromosome] = useState<string>(
element.inputChromosome || ""
);
Expand Down Expand Up @@ -72,10 +73,12 @@ const TemplatedSequenceElementInput: React.FC<
) {
// TODO visible error handling
setInputError("element validation unsuccessful");
setErrors(templatedSequenceResponse.warnings);
setPendingResponse(false);
return;
} else if (templatedSequenceResponse.element) {
setInputError("");
setErrors([]);
getTemplatedSequenceNomenclature(
templatedSequenceResponse.element
).then((nomenclatureResponse) => {
Expand Down Expand Up @@ -175,6 +178,7 @@ const TemplatedSequenceElementInput: React.FC<
handleDelete,
inputElements,
validated,
errors,
icon,
pendingResponse,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ interface TxSegmentElementInputProps extends StructuralElementInputProps {

const TxSegmentCompInput: React.FC<TxSegmentElementInputProps> = ({
element,
index,
handleSave,
handleDelete,
icon,
Expand All @@ -51,6 +50,8 @@ const TxSegmentCompInput: React.FC<TxSegmentElementInputProps> = ({
: null
);

const [errors, setErrors] = useState<string[]>([]);

// "Text" variables refer to helper or warning text to set under input fields
// TODO: this needs refactored so badly
const [txAc, setTxAc] = useState(element.inputTx || "");
Expand Down Expand Up @@ -234,6 +235,7 @@ const TxSegmentCompInput: React.FC<TxSegmentElementInputProps> = ({
txSegmentResponse.warnings &&
txSegmentResponse.warnings?.length > 0
) {
setErrors(txSegmentResponse.warnings);
CheckGenomicCoordWarning(txSegmentResponse.warnings);
} else {
const inputParams = {
Expand All @@ -244,6 +246,7 @@ const TxSegmentCompInput: React.FC<TxSegmentElementInputProps> = ({
inputGenomicStart: txStartingGenomic,
inputGenomicEnd: txEndingGenomic,
};
setErrors([]);
handleTxElementResponse(txSegmentResponse, inputParams);
}
});
Expand Down Expand Up @@ -278,10 +281,13 @@ const TxSegmentCompInput: React.FC<TxSegmentElementInputProps> = ({
setEndingExonText("Invalid");
}
}
setPendingResponse(false);
setErrors(txSegmentResponse.warnings);
} else {
setTxAcText("");
setStartingExonText("");
setEndingExonText("");
setErrors([]);
const inputParams = {
inputType: txInputType,
inputTx: txAc,
Expand Down Expand Up @@ -678,6 +684,7 @@ const TxSegmentCompInput: React.FC<TxSegmentElementInputProps> = ({
handleDelete,
inputElements,
validated,
errors,
icon,
pendingResponse,
});
Expand Down
7 changes: 3 additions & 4 deletions client/src/components/Pages/Summary/Invalid/Invalid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,9 @@ export const Invalid: React.FC<Props> = ({

const unknownError = (
<>
<ListItemText>
We were unable to parse the validation failure for this fusion:
</ListItemText>
<blockquote>{validationErrors}</blockquote>
{validationErrors.map((error) => (
<blockquote>{error}</blockquote>
))}
</>
);

Expand Down
8 changes: 1 addition & 7 deletions client/src/components/Pages/Summary/Main/Summary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,7 @@ export const Summary: React.FC<Props> = ({ setVisibleTab }) => {
// make request
validateFusion(formattedFusion).then((response) => {
if (response.warnings && response.warnings?.length > 0) {
if (
validationErrors !== null &&
JSON.stringify(response.warnings.sort()) !==
JSON.stringify(validationErrors.sort())
) {
setValidationErrors(response.warnings);
}
setValidationErrors(response.warnings);
} else {
setValidationErrors([]);
setValidatedFusion(
Expand Down
30 changes: 27 additions & 3 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,46 +8,69 @@ asyncpg==0.29.0
attrs==24.2.0
biocommons.seqrepo==0.6.9
bioutils==0.5.8.post1
boto3==1.35.4
botocore==1.35.4
black==24.8.0
boto3==1.35.3
botocore==1.35.3
canonicaljson==2.0.0
certifi==2024.7.4
cfgv==3.4.0
charset-normalizer==3.3.2
click==8.1.7
coloredlogs==15.0.1
configparser==7.1.0
cool_seq_tool==0.7.0
coverage==7.6.1
decorator==5.1.1
distlib==0.3.8
executing==2.0.1
fastapi==0.112.1
fusor==0.4.2
filelock==3.15.4
fusor==0.4.3
ga4gh.vrs==2.0.0a10
gene-normalizer==0.4.0
h11==0.14.0
hgvs==1.5.4
httpcore==1.0.5
httpx==0.27.0
humanfriendly==10.0
identify==2.6.0
idna==3.7
importlib_metadata==8.4.0
iniconfig==2.0.0
ipython==8.26.0
jedi==0.19.1
Jinja2==3.1.4
jmespath==1.0.1
MarkupSafe==2.1.5
matplotlib-inline==0.1.7
mypy-extensions==1.0.0
nodeenv==1.9.1
packaging==24.1
Parsley==1.3
parso==0.8.4
pathspec==0.12.1
pexpect==4.9.0
platformdirs==4.2.2
pluggy==1.5.0
polars==1.5.0
pre-commit==3.8.0
prompt_toolkit==3.0.47
psycopg2==2.9.9
psycopg2-binary==2.9.9
ptyprocess==0.7.0
pure_eval==0.2.3
pydantic==2.4.2
pydantic-to-typescript2==1.0.4
pydantic_core==2.10.1
Pygments==2.18.0
pysam==0.22.1
pytest==8.3.2
pytest-asyncio==0.24.0
pytest-cov==5.0.0
python-dateutil==2.9.0.post0
PyYAML==6.0.2
requests==2.32.3
ruff==0.5.0
s3transfer==0.10.2
six==1.16.0
sniffio==1.3.1
Expand All @@ -60,6 +83,7 @@ traitlets==5.14.3
typing_extensions==4.12.2
urllib3==1.26.19
uvicorn==0.30.6
virtualenv==20.26.3
wags_tails==0.1.4
wcwidth==0.2.13
yoyo-migrations==8.2.0
Expand Down
2 changes: 1 addition & 1 deletion server/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ dependencies = [
"click",
"boto3",
"botocore",
"fusor ~= 0.4.2",
"fusor ~= 0.4.3",
"cool-seq-tool ~= 0.7.0",
"pydantic == 2.4.2", # validation errors with more recent versions, so don't remove this specific pin
"gene-normalizer ~= 0.4.0",
Expand Down
2 changes: 1 addition & 1 deletion server/src/curfu/routers/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def validate_fusion(request: Request, fusion: dict = Body()) -> ResponseDict:
try:
verified_fusion = fusor.fusion(**fusion)
except FUSORParametersException as e:
response["warnings"] = [str(e)]
response["warnings"] = str(e).split("\n")
else:
response["fusion"] = verified_fusion
return response

0 comments on commit 100bb39

Please sign in to comment.