Skip to content

Commit

Permalink
workflow selection refactors
Browse files Browse the repository at this point in the history
  • Loading branch information
v-rocheleau committed Aug 9, 2023
1 parent 960fd5f commit aca3586
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 52 deletions.
50 changes: 50 additions & 0 deletions src/components/manager/DataTypeSelect.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React, { useCallback, useEffect, useMemo, useState } from "react";
import { Select, Spin } from "antd";
import PropTypes from "prop-types";
import { useSelector } from "react-redux";

const DataTypeSelect = ({value, workflows, onChange}) => {
const [selected, setSelected] = useState(value ?? undefined);
const servicesFetching = useSelector((state) => state.services.isFetchingAll);
const workflowsFetching = useSelector((state) => state.serviceWorkflows.isFetchingAll);

useEffect(() => {
setSelected(value);
}, [value])

const onChangeInner = useCallback((newSelected) => {
if (!value) setSelected(newSelected);
if (onChange) {
onChange(newSelected);
}
}, [value, onChange, selected]);

const options = useMemo(() => {
if (Array.isArray(workflows)) {
const dataTypes = new Set(workflows.map((w) => w.data_type));
return Array.from(dataTypes).map((dt) =>
<Select.Option value={dt} key={dt}>
{dt}
</Select.Option>
)
}
return [];
}, [workflows]);

return (
<Spin spinning={servicesFetching || workflowsFetching}>
<Select value={selected} onChange={onChangeInner}>
{options}
</Select>
</Spin>

);
};

DataTypeSelect.propTypes = {
workflows: PropTypes.array,
onChange: PropTypes.func,
value: PropTypes.string
};

export default DataTypeSelect;
24 changes: 2 additions & 22 deletions src/components/manager/DatasetTreeSelect.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,9 @@ const DatasetTreeSelect = ({value, onChange, style}) => {

const onChangeInner = useCallback((newSelected) => {
if (!value) setSelected(newSelected);

// Update the change handler bound to the component if one exists
if (onChange) {
onChange(newSelected);
const [project, dataset] = newSelected.split(":");
onChange(project, dataset);
}
}, [value, onChange, selected]);

Expand All @@ -32,32 +31,13 @@ const DatasetTreeSelect = ({value, onChange, style}) => {
title: d.title,
key: `${p.identifier}:${d.identifier}`,
value: `${p.identifier}:${d.identifier}`,
children: [
{
title: `${p.title}:${d.title}:phenopacket`,
key: `${p.identifier}:${d.identifier}:phenopacket`,
value: `${p.identifier}:${d.identifier}:phenopacket`,
},
{
title: `${p.title}:${d.title}:experiment`,
key: `${p.identifier}:${d.identifier}:experiment`,
value: `${p.identifier}:${d.identifier}:experiment`,
}
]
})),
})), [projectItems]);

return <Spin spinning={servicesFetching || projectsFetching}>
<TreeSelect
style={style ?? {}}
showSearch={true}
// filterTreeNode={(v, n) => {
// const filter = v.toLocaleLowerCase().trim();
// if (filter === "") return true;
// return n.key.toLocaleLowerCase().includes(filter)
// || n.props.data.title.toLocaleLowerCase().includes(filter)
// || (n.props.data.dataType || "").toLocaleLowerCase().includes(filter);
// }}
onChange={onChangeInner}
value={selected}
treeData={selectTreeData}
Expand Down
62 changes: 36 additions & 26 deletions src/components/manager/ManagerIngestionContent.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useMemo } from "react";
import React, { useCallback, useMemo } from "react";
import {useDispatch, useSelector} from "react-redux";
import {useHistory} from "react-router-dom";
import PropTypes from "prop-types";
Expand All @@ -17,17 +17,18 @@ import {

import DatasetTreeSelect from "./DatasetTreeSelect";

import {workflowsStateToPropsMixin} from "../../propTypes";
import {workflowTarget, workflowsStateToPropsMixin} from "../../propTypes";
import RunSetupWizard from "./RunSetupWizard";
import RunSetupInputsTable from "./RunSetupInputsTable";
import DataTypeSelect from "./DataTypeSelect";


const IngestWorkflowSelection = ({values, setValues, handleWorkflowClick}) => {
const {workflows, workflowsLoading} = useSelector(workflowsStateToPropsMixin);
const {selectedDataset} = values;
const {selectedProject, selectedDataset, selectedDataType} = values;

const workflowItems = workflows.ingestion
.filter(w => w.data_type === (selectedDataset ? selectedDataset.split(":")[2] : null))
.filter(w => selectedDataset && selectedDataType && w.data_type === selectedDataType)
.map(w =>
<WorkflowListItem
key={w.id}
Expand All @@ -36,37 +37,49 @@ const IngestWorkflowSelection = ({values, setValues, handleWorkflowClick}) => {
/>,
);

const onChange = useCallback(({project=selectedProject, dataset=selectedDataset, dataType=selectedDataType}) => {
setValues({
selectedProject: project,
selectedDataset: dataset,
selectedDataType: dataType,
});
}, [selectedDataset, selectedDataType, setValues]);

return <Form labelCol={FORM_LABEL_COL} wrapperCol={FORM_WRAPPER_COL}>
<Form.Item label="Dataset">
<DatasetTreeSelect
onChange={d => setValues({selectedDataset: d})}
onChange={(p, d) => onChange({project: p, dataset: d})}
value={selectedDataset}
/>
</Form.Item>
<Form.Item label="Data-Type">
<DataTypeSelect
workflows={workflows?.ingestion ?? []}
onChange={dt => onChange({dataType: dt})}
value={selectedDataType}
/>
</Form.Item>
<Form.Item label="Workflows">
{selectedDataset
{selectedDataset && selectedDataType
? <Spin spinning={workflowsLoading}>
{workflowsLoading
? <Skeleton/>
: <List itemLayout="vertical">{workflowItems}</List>}
</Spin>
: <Empty image={Empty.PRESENTED_IMAGE_SIMPLE}
description="Select a dataset to see available workflows"/>
description="Select a dataset and data type to see available workflows"/>
}
</Form.Item>
</Form>;
};
IngestWorkflowSelection.propTypes = {
values: PropTypes.shape({
selectedDataset: PropTypes.string,
}),
values: workflowTarget,
setValues: PropTypes.func,
handleWorkflowClick: PropTypes.func,
};

const IngestConfirmDisplay = ({selectedDataset, selectedWorkflow, inputs, handleRunWorkflow}) => {
const IngestConfirmDisplay = ({target, selectedWorkflow, inputs, handleRunWorkflow}) => {
const projectsByID = useSelector(state => state.projects.itemsByID);

const isSubmittingIngestionRun = useSelector(state => state.runs.isSubmittingIngestionRun);
const datasetsByID = useSelector((state) =>
Object.fromEntries(
Expand All @@ -76,18 +89,16 @@ const IngestConfirmDisplay = ({selectedDataset, selectedWorkflow, inputs, handle

const formatWithNameIfPossible = (name, id) => name ? `${name} (${id})` : id;

const [projectID, datasetID, dataType] = selectedDataset.split(":");

const projectTitle = projectsByID[projectID]?.title || null;
const datasetTitle = datasetsByID[datasetID]?.title || null;
const projectTitle = projectsByID[target.selectedProject]?.title || null;
const datasetTitle = datasetsByID[target.selectedDataset]?.title || null;

return (
<Form labelCol={FORM_LABEL_COL} wrapperCol={FORM_WRAPPER_COL}>
<Form.Item label="Project">
{formatWithNameIfPossible(projectTitle, projectID)}
{formatWithNameIfPossible(projectTitle, target.selectedProject)}
</Form.Item>
<Form.Item label="Dataset">
{formatWithNameIfPossible(datasetTitle, datasetID)}
{formatWithNameIfPossible(datasetTitle, target.selectedDataset)}
</Form.Item>
<Form.Item label="Workflow">
<List itemLayout="vertical" style={{marginBottom: "14px"}}>
Expand All @@ -110,7 +121,7 @@ const IngestConfirmDisplay = ({selectedDataset, selectedWorkflow, inputs, handle
);
};
IngestConfirmDisplay.propTypes = {
selectedDataset: PropTypes.string, // format: <project id>:<dataset id>
target: workflowTarget,
selectedWorkflow: PropTypes.object,
inputs: PropTypes.object,
handleRunWorkflow: PropTypes.func,
Expand All @@ -136,29 +147,28 @@ const ManagerIngestionContent = () => {
}
confirmDisplay={({selectedWorkflow, workflowSelectionValues, inputs, handleRunWorkflow}) => (
<IngestConfirmDisplay
selectedDataset={workflowSelectionValues.selectedDataset}
// selectedDataset={workflowSelectionValues.selectedDataset}
target={workflowSelectionValues}
selectedWorkflow={selectedWorkflow}
inputs={inputs}
handleRunWorkflow={handleRunWorkflow}
/>
)}
onSubmit={({workflowSelectionValues, selectedWorkflow, inputs}) => {
console.log(workflowSelectionValues);
const {selectedDataset} = workflowSelectionValues;
const {selectedProject, selectedDataset, selectedDataType} = workflowSelectionValues;

if (!selectedDataset || !selectedWorkflow) {
message.error(`Missing ${selectedDataset ? "workflow" : "dataset"} selection; cannot submit run!`);
return;
}

const serviceInfo = servicesByID[selectedWorkflow.serviceID];
const [projectID, datasetID, dataType] = selectedDataset.split(":");

dispatch(submitIngestionWorkflowRun(
serviceInfo,
projectID,
datasetID,
dataType,
selectedProject,
selectedDataset,
selectedDataType,
selectedWorkflow,
inputs,
"/admin/data/manager/runs",
Expand Down
13 changes: 9 additions & 4 deletions src/components/manager/projects/RoutedProject.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class RoutedProject extends Component {
this.showDatasetAdditionModal = this.showDatasetAdditionModal.bind(this);
this.hideDatasetAdditionModal = this.hideDatasetAdditionModal.bind(this);
this.hideDatasetEditModal = this.hideDatasetEditModal.bind(this);
this.ingestIntoTable = this.ingestIntoTable.bind(this);
this.ingestIntoTable = this.ingestIntoDataset.bind(this);
this.handleDeleteProject = this.handleDeleteProject.bind(this);
}

Expand All @@ -40,9 +40,14 @@ class RoutedProject extends Component {
}
}

ingestIntoTable(p, d, dt) {
ingestIntoDataset(p, d, dt) {
this.props.history.push("/admin/data/manager/ingestion",
{workflowSelectionValues: {selectedDataset: `${p.identifier}:${d.identifier}:${dt.id}`}});
{workflowSelectionValues: {
selectedProject: p.identifier,
selectedDataset: d.identifier,
selectedDataType: dt.id,
}
});
}

handleProjectSave(project) {
Expand Down Expand Up @@ -138,7 +143,7 @@ class RoutedProject extends Component {
datasetEditModal: true,
})}
onAddJsonSchema={() => this.setJsonSchemaModalVisible(true)}
onDatasetIngest={(p, d, dt) => this.ingestIntoTable(p, d, dt)}/>
onDatasetIngest={(p, d, dt) => this.ingestIntoDataset(p, d, dt)}/>
</>;
}
}
Expand Down
7 changes: 7 additions & 0 deletions src/propTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,13 @@ export const runPropTypesShape = PropTypes.shape({
// Prop types object shape for a single table summary object.
export const summaryPropTypesShape = PropTypes.object;

// Prop types object shape describing the target of a workflow (project, dataset and data-type)
export const workflowTarget = PropTypes.shape({
selectedProject: PropTypes.string,
selectedDataset: PropTypes.string,
selectedDataType: PropTypes.string,
})

// Gives components which include this in their state to props connection access to workflows and loading status.
export const workflowsStateToPropsMixin = state => {
const workflowsByType = {
Expand Down

0 comments on commit aca3586

Please sign in to comment.