flowviz
Version:
A framework which provides seamless integration with other phylogenetic tools and frameworks, while allowing workflow scheduling and execution, through the Apache Airflow workflow system.
253 lines (226 loc) • 7.63 kB
JavaScript
import {
Button,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
} from "@material-ui/core";
import { Grid } from "@mui/material";
import * as React from "react";
import { useState } from "react";
import ChipContainer from "../../../common/chipContainer";
import ToolInfo from "../info/toolInfo";
import VariableContainer from "../io/variable";
import CommandPreview from "./library/commandPreview";
import ToolSetupLibraryCommand from "./library/toolSetupLibraryCommand";
import RelayedData from "./relayedData";
import ToolSetupRow from "./toolSetupRow";
import ToolSetupStack from "./toolSetupStack";
const reservedValues = ["file", "str"];
export default function ToolSetupDialog({
nodeId,
open,
tool,
relayedOuts,
scroll,
onSetupDialogApply,
onSetupDialogClose,
children,
}) {
const descriptionElementRef = React.useRef(null);
// For tools that provide both setup methods
const [setupMethod, setSetupMethod] = useState("library");
const relayedOutputs = relayedOuts.flatMap((elem) => elem);
const [inputs, setInputs] = useState(relayedOutputs);
const [outputs, setOutputs] = useState([]);
// Obtains the first command group name and name to fill the selectors
const [commandGroups, cmdGroup, firstCmdName] = getLibrary(tool);
// A clean command when adding new commands inside the task
const cleanCommand = {
groupName: cmdGroup.name,
name: "",
value: "",
io: "",
};
// The library configuration state
const [inputCommands, setInputCommands] = useState([cleanCommand]);
const [endpoints, setEndpoints] = useState([
{
groupName: cmdGroup.name,
name: firstCmdName,
value: "",
io: "",
},
]);
const onAddElement = (collection, keyValuePair, setter) => {
const key = keyValuePair.key;
const hasChip = collection.find((elem) => elem === key);
if (!hasChip) {
setter((collection) => [
...collection,
{ name: nodeId, key: key, value: keyValuePair.value },
]);
}
};
const onRemoveElement = (key, setter) => {
setter((col) => col.filter((elem) => elem.key !== key));
};
const onAddCommand = (event) => {
setInputCommands([...inputCommands, cleanCommand]);
};
const onRemoveCommand = (event, i) => {
if (inputCommands.length <= 1 || i === 0) return;
const cmds = [...inputCommands];
cmds.splice(i, 1);
setInputCommands(cmds);
};
const onUpdateCommand = (event, i, prop, nextProps) => {
const value = event.target.value;
const cmds = [...inputCommands];
cmds[i][prop] = value;
// Clean next dependent properties if they exist
if (nextProps && nextProps.length > 0) {
nextProps.forEach((nextProp) => {
cmds[i][nextProp] = "";
});
}
setInputCommands(cmds);
};
//TODO
const cmdPreview = inputCommands
.map((cmd) => {
return `${getInvocationFromCmd(tool.library, cmd.groupName, cmd.name)} ${
reservedValues.includes(cmd.value) ? "" : cmd.value
} ${!cmd.io ? "" : cmd.io}`;
})
.toString()
.replace(",", "")
.replace(",", " ");
const onCancel = (event) => {
setInputCommands([cleanCommand]);
onSetupDialogClose();
};
const onApply = (config) => {
onSetupDialogApply(config);
onSetupDialogClose();
};
React.useEffect(() => {
if (open) {
const { current: descriptionElement } = descriptionElementRef;
if (descriptionElement !== null) {
descriptionElement.focus();
}
}
}, [open]);
return (
<>
<Dialog
fullWidth
maxWidth="lg"
open={open}
onClose={onSetupDialogClose}
scroll={scroll}
aria-labelledby="scroll-dialog-title"
aria-describedby="scroll-dialog-description"
>
<DialogTitle id="scroll-dialog-title">Task Setup</DialogTitle>
<DialogContent dividers={scroll === "paper"}>
<ToolSetupStack>
{relayedOutputs && relayedOutputs.length > 0 ? (
<ToolSetupRow title={`Relayed data`}>
<RelayedData relayedOutputs={relayedOutputs} />
</ToolSetupRow>
) : (
<></>
)}
<ToolSetupRow title="Tool information">
<ToolInfo tool={tool} />
</ToolSetupRow>
<ToolSetupRow title="I/O variables">
<Grid fullWidth container>
<Grid container item xs={6}>
<ToolSetupRow title="Input">
<VariableContainer
id="Input"
keyTooltip="The input variable key to assign a task input."
valueTooltip="The input variable value to assign a value to the key variable."
collection={inputs}
collectionSetter={setInputs}
onAddElement={onAddElement}
onRemoveElement={onRemoveElement}
/>
</ToolSetupRow>
</Grid>
<Grid container item xs={6}>
<ToolSetupRow title="Output">
<VariableContainer
id="Output"
keyTooltip="The output key variable to assign the output of this task. This key will be used by the next task to get the result of this one."
valueTooltip="The output variable value to assign a value to the key variable."
collection={outputs}
collectionSetter={setOutputs}
onAddElement={onAddElement}
onRemoveElement={onRemoveElement}
/>
</ToolSetupRow>
</Grid>
</Grid>
</ToolSetupRow>
<ToolSetupRow title="Setup">
<CommandPreview cmdPreview={cmdPreview} />
{inputCommands.map((cmd, i) => (
<ToolSetupLibraryCommand
key={i}
index={i}
commandGroups={commandGroups}
state={cmd}
onParentUpdate={onUpdateCommand}
onRemove={onRemoveCommand}
relayedOutputs={relayedOutputs}
inputs={inputs}
outputs={outputs}
/>
))}
<Button onClick={onAddCommand}>Add command</Button>
</ToolSetupRow>
</ToolSetupStack>
</DialogContent>
<DialogActions>
{children}
<Button onClick={onCancel}>Cancel</Button>
<Button
onClick={() =>
onApply({
inputs: inputs,
outputs: outputs,
setup: inputCommands,
action: cmdPreview,
})
}
>
Apply
</Button>
</DialogActions>
</Dialog>
</>
);
}
const getInvocationFromCmd = (library, cmdGroup, cmdName) => {
const cmdGroupCommands = library.find(
(commandGroup) => commandGroup.name === cmdGroup
).commands;
const cmd =
cmdName !== ""
? cmdGroupCommands.find((cmd) => cmd.name === cmdName)
: cmdGroupCommands[0];
return cmd.invocation[0];
};
function getLibrary(tool) {
const library = tool.library;
if (!library) return [];
const commandGroups = library;
const cmdGroup = commandGroups.find((cmdGroup) => cmdGroup.order === 0);
const firstCmd = cmdGroup.commands[0];
const firstCmdName = firstCmd.name;
return [commandGroups, cmdGroup, firstCmdName];
}