Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -3,51 +3,91 @@ import { StepLabel } from "@databiosphere/findable-ui/lib/components/Stepper/com
import { Icon } from "@databiosphere/findable-ui/lib/components/Stepper/components/Step/components/StepLabel/components/Label/components/Icon/icon";
import { StepProps } from "../types";
import { Step } from "@databiosphere/findable-ui/lib/components/Stepper/components/Step/step";
import { useUCSCFiles } from "./hooks/UseUCSCFiles/useUCSCFiles";
import { FormControlLabel, Radio, RadioGroup, Typography } from "@mui/material";
import { useRadioGroup } from "../hooks/UseRadioGroup/hook";
import {
Button,
FormControlLabel,
Radio,
RadioGroup,
Typography,
} from "@mui/material";
import { StyledGrid2 } from "./gtfStep.styles";
import { TYPOGRAPHY_PROPS } from "./constants";
import { getGeneModelLabel, getInitialValue } from "./utils";
import { TYPOGRAPHY_PROPS as MUI_TYPOGRAPHY_PROPS } from "@databiosphere/findable-ui/lib/styles/common/mui/typography";
import { configureGTFStep } from "./utils";
import { BUTTON_PROPS } from "../components/Button/constants";
import { useUCSCFiles } from "./hooks/UseUCSCFiles/hook";
import { useRadioGroup } from "./hooks/UseRadioGroup/hook";
import { useEffect } from "react";

export const GTFStep = ({
active,
completed,
description,
entryKey,
entryLabel,
genome,
index,
label,
launchStatus,
onConfigure,
onLaunch,
}: StepProps): JSX.Element => {
const { geneModelUrls } = useUCSCFiles(genome);
const { onChange, onValueChange, value } = useRadioGroup("");
const hasGeneModels = geneModelUrls.length > 0;
const { controls, onChange, onValueChange, value } =
useRadioGroup(geneModelUrls);

useEffect(() => {
onValueChange(getInitialValue(geneModelUrls));
}, [geneModelUrls, onValueChange]);
configureGTFStep(
geneModelUrls,
entryKey,
entryLabel,
onConfigure,
onValueChange
);
}, [geneModelUrls, entryKey, entryLabel, onConfigure, onValueChange]);

return (
<Step active={active && hasGeneModels} completed={completed} index={index}>
<Step
active={active && !!geneModelUrls}
completed={completed}
index={index}
>
<StepLabel>
{label}
{entryLabel}
<Icon slotProps={{ tooltip: { title: description } }} />
</StepLabel>
<StepContent>
<StyledGrid2>
<Typography {...TYPOGRAPHY_PROPS}>
Genes and Gene Predictions
</Typography>
<RadioGroup onChange={onChange} value={value}>
{geneModelUrls.map((url, index) => (
<FormControlLabel
control={<Radio />}
key={index}
label={getGeneModelLabel(url)}
value={url}
/>
))}
</RadioGroup>
{controls.length > 0 ? (
<RadioGroup onChange={onChange} value={value}>
{controls.map(({ label, value }, i) => (
<FormControlLabel
control={<Radio />}
key={i}
onChange={() =>
onConfigure(entryKey, entryLabel, [
{ key: value, value: label },
])
}
label={label}
value={value}
/>
))}
</RadioGroup>
) : (
<Typography variant={MUI_TYPOGRAPHY_PROPS.VARIANT.TEXT_BODY_400}>
No gene models found.
</Typography>
)}
<Button
{...BUTTON_PROPS}
disabled={launchStatus.disabled || launchStatus.loading}
onClick={onLaunch}
>
Launch Galaxy
</Button>
</StyledGrid2>
</StepContent>
</Step>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { useRadioGroup as useBaseRadioGroup } from "../../../hooks/UseRadioGroup/hook";
import { useMemo } from "react";
import { UseRadioGroup } from "../../../hooks/UseRadioGroup/types";
import { mapControl } from "./utils";

export const useRadioGroup = (
geneModelUrls: string[] | undefined
): UseRadioGroup & {
controls: { label: string; value: string }[];
} => {
const { onChange, onValueChange, value } = useBaseRadioGroup("");

const controls = useMemo(() => {
return (geneModelUrls || []).map(mapControl);
}, [geneModelUrls]);

return { controls, onChange, onValueChange, value };
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { getGeneModelLabel } from "../../utils";

/**
* Maps a gene model URL to a form control with display label and value.
* @param value - The gene model URL.
* @returns The form control with label and value.
*/
export function mapControl(value: string): { label: string; value: string } {
return {
label: getGeneModelLabel(value),
value,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { UseUCSCFiles } from "./types";

export const useUCSCFiles = (genome: BRCDataCatalogGenome): UseUCSCFiles => {
const assemblyId = genome.accession;
const [geneModelUrls, setGeneModelUrls] = useState<string[]>([]);
const [geneModelUrls, setGeneModelUrls] = useState<string[] | undefined>();

useEffect(() => {
fetch(`${UCSC_FILES_ENDPOINT}?genome=${assemblyId}`)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ export interface UrlList {
}

export interface UseUCSCFiles {
geneModelUrls: string[];
geneModelUrls: string[] | undefined;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { Result } from "./types";
import { DOWNLOAD_BASE_URL } from "./constants";

/**
* Parses the result from the UCSC files API to extract GTF file URLs.
* @param response - Response.
* @returns A list of GTF file URLs.
*/
export function parseUCSCFilesResult(response: Result): string[] {
const { urlList } = response;
return urlList
Expand All @@ -9,6 +14,11 @@ export function parseUCSCFilesResult(response: Result): string[] {
.map((url) => `${DOWNLOAD_BASE_URL}/${url}`);
}

/**
* Filters the GTF file URLs from the UCSC files API response.
* @param url - URL.
* @returns True if the URL ends with ".gtf.gz".
*/
function filterGFTFile(url: string): boolean {
return url.endsWith(".gtf.gz");
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ import { GTFStep } from "./gtfStep";
export const STEP: StepConfig = {
Step: GTFStep,
description: "Select the GTF files for the workflow",
key: "gtf-files",
key: "geneModelUrl",
label: "GTF Files",
};
Original file line number Diff line number Diff line change
@@ -1,30 +1,81 @@
import { LABEL } from "@databiosphere/findable-ui/lib/apis/azul/common/entities";
import { ConfiguredValue } from "../hooks/UseLaunchGalaxy/types";
import { UseRadioGroup } from "../hooks/UseRadioGroup/types";
import { OnConfigure } from "../../../../../../../../../../../views/WorkflowInputsView/hooks/UseConfigureInputs/types";

/**
* Configures the GTF step.
* @param geneModelUrls - Gene model URLs.
* @param entryKey - Configured value key.
* @param entryLabel - Configured value display label.
* @param onConfigure - Callback function to configure the step.
* @param onValueChange - Callback function to handle value changes.
*/
export const configureGTFStep = (
geneModelUrls: string[] | undefined,
entryKey: keyof ConfiguredValue,
entryLabel: string,
onConfigure: OnConfigure,
onValueChange: UseRadioGroup["onValueChange"]
): void => {
// Gene model URLs are not yet loaded.
if (!geneModelUrls) return;

// Gene model URLs are not available for this workflow.
if (geneModelUrls.length === 0) {
onConfigure(entryKey, entryLabel, [{ key: null, value: LABEL.NONE }]);
return;
}

// Get the preferred gene model.
const value = getPreferredGeneModel(geneModelUrls);

// Update radio state.
onValueChange(value);

// If no preferred gene model is found, do nothing.
if (!value) return;

// Otherwise, use the gene model to configure the step.
onConfigure(entryKey, entryLabel, [
{ key: value, value: getGeneModelLabel(value) },
]);
};

/**
* Maps a gene model URL to a display label.
* @param url - Gene model URL.
* @returns The display label for the gene model.
*/
export const getGeneModelLabel = (url: string): string => {
const filename = url.split("/").pop() || "";
if (filename.includes(".augustus.")) {
return "Augustus";
} else if (filename.includes(".ncbiRefSeq.")) {
return "NCBI RefSeq";
} else {
return filename;

const labelPatterns: [RegExp, string][] = [
[/\.augustus\./, "Augustus"],
[/\.ncbiRefSeq\./, "NCBI RefSeq"],
[/\.ncbiGene\./, "NCBI Gene"],
];

for (const [pattern, label] of labelPatterns) {
if (pattern.test(filename)) {
return label;
}
}

return filename;
};

/**
* Returns the initial value for the radio group based on the provided gene model URLs.
* Returns the preferred gene model based on the order of preference.
* Preference is given to NCBI RefSeq, NCBI Gene, then Augustus, otherwise empty string.
* @param geneModelUrls - Gene model URLs.
* @returns The initial value for the radio group.
* @returns The preferred gene model.
*/
export const getInitialValue = (geneModelUrls: string[]): string => {
const priorityTypes = ["ncbiRefSeq", "ncbiGene", "augustus"];
for (const priorityType of priorityTypes) {
export const getPreferredGeneModel = (geneModelUrls: string[]): string => {
const preferredTypes = ["ncbiRefSeq", "ncbiGene", "augustus"];
for (const preferredType of preferredTypes) {
for (const url of geneModelUrls) {
if (url.includes(priorityType)) {
if (url.includes(preferredType)) {
return url;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,29 @@ import { Step } from "@databiosphere/findable-ui/lib/components/Stepper/componen
import { StepContent } from "@databiosphere/findable-ui/lib/components/Stepper/components/Step/components/StepContent/stepContent";
import { StepLabel } from "@databiosphere/findable-ui/lib/components/Stepper/components/Step/components/StepLabel/stepLabel";
import { StepProps } from "../types";
import { Optional } from "@databiosphere/findable-ui/lib/components/Stepper/components/Step/components/StepLabel/components/Optional/optional";
import { useEffect } from "react";

export const ReferenceAssemblyStep = ({
active,
completed,
entryKey,
entryLabel,
genome,
index,
label,
onConfigure,
}: StepProps): JSX.Element => {
const { accession } = genome;

useEffect(() => {
onConfigure(entryKey, entryLabel, [{ key: accession, value: accession }]);
}, [accession, entryKey, entryLabel, onConfigure]);

return (
<Step active={active} completed={completed} index={index}>
<StepLabel>{label}</StepLabel>
<StepLabel optional={<Optional>{accession}</Optional>}>
{entryLabel}
</StepLabel>
<StepContent>None</StepContent>
</Step>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ import { ReferenceAssemblyStep } from "./referenceAssemblyStep";
export const STEP: StepConfig = {
Step: ReferenceAssemblyStep,
disabled: true,
key: "reference-assembly",
key: "referenceAssembly",
label: "Reference Assembly",
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import {
COLOR,
VARIANT,
} from "@databiosphere/findable-ui/lib/styles/common/mui/button";
import { ButtonProps } from "@mui/material";

export const BUTTON_PROPS: ButtonProps = {
color: COLOR.PRIMARY,
variant: VARIANT.CONTAINED,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { ConfiguredValue } from "./types";

export const CONFIGURED_VALUE_KEYS: (keyof ConfiguredValue)[] = [
"geneModelUrl",
"referenceAssembly",
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Workflow } from "../../../../../../../../../../../../apis/catalog/brc-analytics-catalog/common/entities";
import { ConfiguredInput } from "../../../../../../../../../../../../views/WorkflowInputsView/hooks/UseConfigureInputs/types";

export interface ConfiguredValue {
geneModelUrl: string | null;
referenceAssembly: string;
}

export interface LaunchStatus {
disabled: boolean;
loading: boolean;
}

export type OnLaunch = () => Promise<void>;

export interface Props {
configuredInput: ConfiguredInput;
workflow: Workflow;
}

export interface UseLaunchGalaxy {
launchStatus: LaunchStatus;
onLaunch: OnLaunch;
}
Loading