From 64cbe7f8a513a5271c777ef3044a4fc4248f5838 Mon Sep 17 00:00:00 2001 From: Samir Reddigari Date: Sun, 3 May 2020 16:33:15 -0400 Subject: [PATCH 01/21] fix: remove data input ports from flow nodes --- pyworkflow/pyworkflow/nodes/flow_control/integer_input.py | 2 +- pyworkflow/pyworkflow/nodes/flow_control/string_input.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyworkflow/pyworkflow/nodes/flow_control/integer_input.py b/pyworkflow/pyworkflow/nodes/flow_control/integer_input.py index 086eb89..e917d07 100644 --- a/pyworkflow/pyworkflow/nodes/flow_control/integer_input.py +++ b/pyworkflow/pyworkflow/nodes/flow_control/integer_input.py @@ -8,7 +8,7 @@ class IntegerNode(FlowNode): Allows for Strings to replace 'string' fields in Nodes """ name = "Integer Input" - num_in = 1 + num_in = 0 num_out = 1 color = 'purple' diff --git a/pyworkflow/pyworkflow/nodes/flow_control/string_input.py b/pyworkflow/pyworkflow/nodes/flow_control/string_input.py index eaade18..a5714a1 100644 --- a/pyworkflow/pyworkflow/nodes/flow_control/string_input.py +++ b/pyworkflow/pyworkflow/nodes/flow_control/string_input.py @@ -8,7 +8,7 @@ class StringNode(FlowNode): Allows for Strings to replace 'string' fields in Nodes """ name = "String Input" - num_in = 1 + num_in = 0 num_out = 1 color = 'purple' From c7edc96e59628a5720151b84caaf4ebc998238ed Mon Sep 17 00:00:00 2001 From: Samir Reddigari Date: Sun, 3 May 2020 16:35:35 -0400 Subject: [PATCH 02/21] fix: add flow control checks in workflow behavior --- pyworkflow/pyworkflow/workflow.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyworkflow/pyworkflow/workflow.py b/pyworkflow/pyworkflow/workflow.py index 6d29a07..a9515a8 100644 --- a/pyworkflow/pyworkflow/workflow.py +++ b/pyworkflow/pyworkflow/workflow.py @@ -172,7 +172,7 @@ def get_all_flow_var_options(self, node_id): for predecessor_id in self.get_node_predecessors(node_id): node = self.get_node(predecessor_id) - if node.node_type == 'FlowNode': + if node.node_type == 'flow_control': flow_variables.append(node.to_json()) return flow_variables @@ -314,7 +314,7 @@ def execute(self, node_id): except NodeException as e: raise e - if node_to_execute.data is None: + if node_to_execute.data is None and node_to_execute.node_type != "flow_control": raise WorkflowException('execute', 'There was a problem saving node output.') return node_to_execute @@ -384,7 +384,7 @@ def load_input_data(self, node_id): if node_to_retrieve is None: raise WorkflowException('retrieve node data', 'The workflow does not contain node %s' % predecessor_id) - if node_to_retrieve.node_type != 'FlowNode': + if node_to_retrieve.node_type != 'flow_control': input_data.append(self.retrieve_node_data(node_to_retrieve)) except WorkflowException: From d7977138703e6cb6c27589281d1fd3e4dfe629ab Mon Sep 17 00:00:00 2001 From: Samir Reddigari Date: Sun, 3 May 2020 16:36:46 -0400 Subject: [PATCH 03/21] feat(ui): add API function to retrieve node by ID --- front-end/src/API.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/front-end/src/API.js b/front-end/src/API.js index a61e6e8..a184885 100644 --- a/front-end/src/API.js +++ b/front-end/src/API.js @@ -26,6 +26,16 @@ function fetchWrapper(endpoint, options = {}) { } +/** + * Retrieve node info from server side workflow + * @param {string} nodeId - ID of node to retrieve + * @returns {Promise} - server response (node info and flow variables) + */ +export async function getNode(nodeId) { + return fetchWrapper(`/node/${nodeId}`); +} + + /** * Add node to server-side workflow * @param {CustomNodeModel} node - JS node to add From ad5b87200b89323fb7b1894bca953003629c8677 Mon Sep 17 00:00:00 2001 From: Samir Reddigari Date: Sun, 3 May 2020 16:38:46 -0400 Subject: [PATCH 04/21] refactor(ui): layout node action icons with flex --- .../components/CustomNode/CustomNodeWidget.js | 29 ++++++++++--------- front-end/src/styles/CustomNode.css | 16 +++++----- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/front-end/src/components/CustomNode/CustomNodeWidget.js b/front-end/src/components/CustomNode/CustomNodeWidget.js index a5a8e2b..5c442e9 100644 --- a/front-end/src/components/CustomNode/CustomNodeWidget.js +++ b/front-end/src/components/CustomNode/CustomNodeWidget.js @@ -75,19 +75,22 @@ export default class CustomNodeWidget extends React.Component {
{this.props.node.options.name}
-
{String.fromCharCode(this.icon)}
- - {graphView} - +
+
+ {String.fromCharCode(this.icon)} +
+ + {graphView} + +
{ portWidgets["in"] }
diff --git a/front-end/src/styles/CustomNode.css b/front-end/src/styles/CustomNode.css index 0787cb9..97da783 100644 --- a/front-end/src/styles/CustomNode.css +++ b/front-end/src/styles/CustomNode.css @@ -40,21 +40,21 @@ border-left-color: mediumpurple; } +.custom-node-icons { + width: 100%; + position: absolute; + display: flex; + justify-content: space-evenly; + align-items: center; +} + .custom-node-configure { font-size: 1.5rem; cursor: pointer; - position: absolute; - left: 25%; - top: 50%; - transform: translate(-50%, -50%); } .custom-node-tabular { cursor: pointer; - position: absolute; - right: 25%; - top: 25%; - transform: translate(50%, -25%); } .custom-node-description { From aea81ac7c072297a7ac03849d08b88f66e4bb121 Mon Sep 17 00:00:00 2001 From: Samir Reddigari Date: Sun, 3 May 2020 16:40:26 -0400 Subject: [PATCH 05/21] feat(ui): add input flow port to node model --- front-end/src/components/CustomNode/CustomNodeModel.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/front-end/src/components/CustomNode/CustomNodeModel.js b/front-end/src/components/CustomNode/CustomNodeModel.js index c382e3a..0b0c890 100644 --- a/front-end/src/components/CustomNode/CustomNodeModel.js +++ b/front-end/src/components/CustomNode/CustomNodeModel.js @@ -13,6 +13,14 @@ export default class CustomNodeModel extends NodeModel { this.configParams = options.option_types; this.options.status = options.status || "unconfigured"; + // add flow control input port + this.addPort( + new VPPortModel({ + in: true, + type: 'vp-port', + name: 'flow-in' + }) + ); const nIn = options.num_in === undefined ? 1 : options.num_in; const nOut = options.num_out === undefined ? 1 : options.num_out; // setup in and out ports From af9df7cefe05e26a98390285796f9940262bf119 Mon Sep 17 00:00:00 2001 From: Samir Reddigari Date: Sun, 3 May 2020 16:41:05 -0400 Subject: [PATCH 06/21] feat(ui): render flow input port in node widget --- .../components/CustomNode/CustomNodeWidget.js | 15 +++++++++++++-- front-end/src/styles/CustomNode.css | 17 +++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/front-end/src/components/CustomNode/CustomNodeWidget.js b/front-end/src/components/CustomNode/CustomNodeWidget.js index 5c442e9..6ee902f 100644 --- a/front-end/src/components/CustomNode/CustomNodeWidget.js +++ b/front-end/src/components/CustomNode/CustomNodeWidget.js @@ -47,7 +47,9 @@ export default class CustomNodeWidget extends React.Component { render() { const engine = this.props.engine; - const ports = _.values(this.props.node.getPorts()); + const allPorts = _.values(this.props.node.getPorts()); + const ports = allPorts.filter(p => p.options.name !== "flow-in"); + const flowPort = allPorts.find(p => p.options.name === "flow-in"); // group ports by type (in/out) const sortedPorts = _.groupBy(ports, p => p.options.in === true ? "in" : "out"); // create PortWidget array for each type @@ -55,11 +57,19 @@ export default class CustomNodeWidget extends React.Component { for (let portType in sortedPorts) { portWidgets[portType] = sortedPorts[portType].map(port => -
+
); } + const flowPortWidget = flowPort ? + +
+ + : null; + + let graphView; let width = 40; if (this.props.node.options.node_type !== "flow_control") { @@ -91,6 +101,7 @@ export default class CustomNodeWidget extends React.Component { onDelete={this.handleDelete} onSubmit={this.acceptConfiguration} />
+ {flowPortWidget}
{ portWidgets["in"] }
diff --git a/front-end/src/styles/CustomNode.css b/front-end/src/styles/CustomNode.css index 97da783..90c06e3 100644 --- a/front-end/src/styles/CustomNode.css +++ b/front-end/src/styles/CustomNode.css @@ -14,6 +14,10 @@ position: relative; } +.custom-node-name { + margin-bottom: 3px; +} + .port-col { display: flex; flex-direction: column; @@ -40,6 +44,19 @@ border-left-color: mediumpurple; } +.flow-port-div { + position: absolute; + top: -5px; + left: -5px; +} + +.flow-port { + width: 10px; + height: 10px; + border-radius: 5px; + background-color: purple; +} + .custom-node-icons { width: 100%; position: absolute; From c12c39e8b69ffcaa5f46fcdd271e9271b5c883ae Mon Sep 17 00:00:00 2001 From: Samir Reddigari Date: Sun, 3 May 2020 16:47:30 -0400 Subject: [PATCH 07/21] feat(ui): retrieve flow nodes from server Fetches only when the node config form goes from hidden to displayed, so flow node options are up-to-date. --- .../src/components/CustomNode/NodeConfig.js | 28 +++++++++++++------ front-end/src/components/Workspace.js | 1 - 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/front-end/src/components/CustomNode/NodeConfig.js b/front-end/src/components/CustomNode/NodeConfig.js index 67c8f94..a30df87 100644 --- a/front-end/src/components/CustomNode/NodeConfig.js +++ b/front-end/src/components/CustomNode/NodeConfig.js @@ -12,13 +12,25 @@ export default class NodeConfig extends React.Component { this.state = { disabled: false, data: {}, - flowData: {} + flowData: {}, + flowNodes: [] }; this.updateData = this.updateData.bind(this); this.handleDelete = this.handleDelete.bind(this); this.handleSubmit = this.handleSubmit.bind(this); } + getFlowNodes() { + if (!this.props.node) return; + API.getNode(this.props.node.options.id) + .then(node => this.setState({flowNodes: node.flow_variables})) + .catch(err => console.log(err)); + } + + componentDidUpdate(prevProps) { + if (!prevProps.show && this.props.show) this.getFlowNodes(); + } + // callback to update form data in state; // resulting state will be sent to node config callback updateData(key, value, flow = false) { @@ -82,7 +94,7 @@ export default class NodeConfig extends React.Component { value={this.props.node.config[key]} flowValue={this.props.node.options.option_replace ? this.props.node.options.option_replace[key] : null} - globals={this.props.globals} + flowNodes={this.state.flowNodes} disableFunc={(v) => this.setState({disabled: v})}/> )} @@ -149,7 +161,7 @@ function OptionInput(props) { } const hideFlow = props.node.options.is_global - || props.type === "file" || props.globals.length === 0 + || props.type === "file" || props.flowNodes.length === 0; return ( {props.label} @@ -159,7 +171,7 @@ function OptionInput(props) { {hideFlow ? null : @@ -290,7 +302,7 @@ function FlowVariableOverride(props) { const handleSelect = (event) => { const uuid = event.target.value; - const flow = props.flowNodes.find(d => d.id === uuid); + const flow = props.flowNodes.find(d => d.node_id === uuid); const obj = { node_id: uuid, is_global: flow.is_global @@ -308,9 +320,9 @@ function FlowVariableOverride(props) { )} diff --git a/front-end/src/components/Workspace.js b/front-end/src/components/Workspace.js index d12410b..1d06abb 100644 --- a/front-end/src/components/Workspace.js +++ b/front-end/src/components/Workspace.js @@ -53,7 +53,6 @@ class Workspace extends React.Component { API.getGlobalVars() .then(vars => { this.setState({globals: vars}); - this.model.globals = vars; }) .catch(err => console.log(err)); } From 79a9cfb9d5eb26aaaa7796e4b9a726b44b5b4fd8 Mon Sep 17 00:00:00 2001 From: Samir Reddigari Date: Sun, 3 May 2020 16:47:43 -0400 Subject: [PATCH 08/21] refactor(ui): tweak link style --- front-end/src/components/VPLink/VPLinkModel.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/front-end/src/components/VPLink/VPLinkModel.js b/front-end/src/components/VPLink/VPLinkModel.js index a29e2bb..832c6fd 100644 --- a/front-end/src/components/VPLink/VPLinkModel.js +++ b/front-end/src/components/VPLink/VPLinkModel.js @@ -5,8 +5,8 @@ export default class VPLinkModel extends DefaultLinkModel { constructor() { super({ type: 'default', - width: 5, - color: 'orange' + width: 2, + color: 'black' }); this.registerListener({ targetPortChanged: event => { From 9ecea00187eb1d7c9d666e0a0eb40cef972be2d5 Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Tue, 5 May 2020 02:01:16 -0500 Subject: [PATCH 09/21] Back-end file migration --- {CLI => back-end/CLI}/__init__.py | 0 {CLI => back-end/CLI}/cli.py | 0 {CLI => back-end/CLI}/setup.py | 0 {CLI => back-end/CLI}/test.py | 0 Pipfile => back-end/Pipfile | 0 Pipfile.lock => back-end/Pipfile.lock | 0 .../pyworkflow}/pyworkflow/.coveragerc | 0 .../pyworkflow}/pyworkflow/__init__.py | 0 {pyworkflow => back-end/pyworkflow}/pyworkflow/node.py | 0 .../pyworkflow}/pyworkflow/node_factory.py | 0 .../pyworkflow}/pyworkflow/nodes/__init__.py | 0 .../pyworkflow/nodes/custom_nodes/table_creator.py | 0 .../pyworkflow/nodes/flow_control/__init__.py | 0 .../pyworkflow/nodes/flow_control/integer_input.py | 0 .../pyworkflow/nodes/flow_control/string_input.py | 0 .../pyworkflow}/pyworkflow/nodes/io/__init__.py | 0 .../pyworkflow}/pyworkflow/nodes/io/read_csv.py | 0 .../pyworkflow}/pyworkflow/nodes/io/write_csv.py | 0 .../pyworkflow/nodes/manipulation/__init__.py | 0 .../pyworkflow}/pyworkflow/nodes/manipulation/filter.py | 0 .../pyworkflow}/pyworkflow/nodes/manipulation/join.py | 0 .../pyworkflow}/pyworkflow/nodes/manipulation/pivot.py | 0 .../pyworkflow/nodes/visualization/__init__.py | 0 .../pyworkflow}/pyworkflow/nodes/visualization/graph.py | 0 .../pyworkflow}/pyworkflow/parameters.py | 0 .../pyworkflow}/pyworkflow/tests/sample_test_data.py | 0 .../pyworkflow}/pyworkflow/tests/test_node.py | 0 .../pyworkflow}/pyworkflow/tests/test_parameters.py | 0 .../pyworkflow}/pyworkflow/tests/test_pyworkflow.py | 0 .../pyworkflow}/pyworkflow/tests/test_workflow.py | 0 .../pyworkflow}/pyworkflow/workflow.py | 0 {pyworkflow => back-end/pyworkflow}/setup.py | 0 {vp => back-end/vp}/manage.py | 0 {vp => back-end/vp}/node/__init__.py | 0 {vp => back-end/vp}/node/admin.py | 0 {vp => back-end/vp}/node/apps.py | 0 {vp => back-end/vp}/node/migrations/__init__.py | 0 {vp => back-end/vp}/node/models.py | 0 {vp => back-end/vp}/node/tests.py | 0 {vp => back-end/vp}/node/urls.py | 0 {vp => back-end/vp}/node/views.py | 0 {vp => back-end/vp}/vp/__init__.py | 0 {vp => back-end/vp}/vp/asgi.py | 0 {vp => back-end/vp}/vp/settings.py | 9 +++++++-- {vp => back-end/vp}/vp/urls.py | 0 {vp => back-end/vp}/vp/views.py | 0 {vp => back-end/vp}/vp/wsgi.py | 0 {vp => back-end/vp}/workflow/__init__.py | 0 {vp => back-end/vp}/workflow/admin.py | 0 {vp => back-end/vp}/workflow/apps.py | 0 {vp => back-end/vp}/workflow/middleware.py | 0 {vp => back-end/vp}/workflow/migrations/__init__.py | 0 {vp => back-end/vp}/workflow/models.py | 0 {vp => back-end/vp}/workflow/tests.py | 0 {vp => back-end/vp}/workflow/urls.py | 0 {vp => back-end/vp}/workflow/views.py | 0 vp/db.sqlite3 | 0 57 files changed, 7 insertions(+), 2 deletions(-) rename {CLI => back-end/CLI}/__init__.py (100%) rename {CLI => back-end/CLI}/cli.py (100%) rename {CLI => back-end/CLI}/setup.py (100%) rename {CLI => back-end/CLI}/test.py (100%) rename Pipfile => back-end/Pipfile (100%) rename Pipfile.lock => back-end/Pipfile.lock (100%) rename {pyworkflow => back-end/pyworkflow}/pyworkflow/.coveragerc (100%) rename {pyworkflow => back-end/pyworkflow}/pyworkflow/__init__.py (100%) rename {pyworkflow => back-end/pyworkflow}/pyworkflow/node.py (100%) rename {pyworkflow => back-end/pyworkflow}/pyworkflow/node_factory.py (100%) rename {pyworkflow => back-end/pyworkflow}/pyworkflow/nodes/__init__.py (100%) rename {pyworkflow => back-end/pyworkflow}/pyworkflow/nodes/custom_nodes/table_creator.py (100%) rename {pyworkflow => back-end/pyworkflow}/pyworkflow/nodes/flow_control/__init__.py (100%) rename {pyworkflow => back-end/pyworkflow}/pyworkflow/nodes/flow_control/integer_input.py (100%) rename {pyworkflow => back-end/pyworkflow}/pyworkflow/nodes/flow_control/string_input.py (100%) rename {pyworkflow => back-end/pyworkflow}/pyworkflow/nodes/io/__init__.py (100%) rename {pyworkflow => back-end/pyworkflow}/pyworkflow/nodes/io/read_csv.py (100%) rename {pyworkflow => back-end/pyworkflow}/pyworkflow/nodes/io/write_csv.py (100%) rename {pyworkflow => back-end/pyworkflow}/pyworkflow/nodes/manipulation/__init__.py (100%) rename {pyworkflow => back-end/pyworkflow}/pyworkflow/nodes/manipulation/filter.py (100%) rename {pyworkflow => back-end/pyworkflow}/pyworkflow/nodes/manipulation/join.py (100%) rename {pyworkflow => back-end/pyworkflow}/pyworkflow/nodes/manipulation/pivot.py (100%) rename {pyworkflow => back-end/pyworkflow}/pyworkflow/nodes/visualization/__init__.py (100%) rename {pyworkflow => back-end/pyworkflow}/pyworkflow/nodes/visualization/graph.py (100%) rename {pyworkflow => back-end/pyworkflow}/pyworkflow/parameters.py (100%) rename {pyworkflow => back-end/pyworkflow}/pyworkflow/tests/sample_test_data.py (100%) rename {pyworkflow => back-end/pyworkflow}/pyworkflow/tests/test_node.py (100%) rename {pyworkflow => back-end/pyworkflow}/pyworkflow/tests/test_parameters.py (100%) rename {pyworkflow => back-end/pyworkflow}/pyworkflow/tests/test_pyworkflow.py (100%) rename {pyworkflow => back-end/pyworkflow}/pyworkflow/tests/test_workflow.py (100%) rename {pyworkflow => back-end/pyworkflow}/pyworkflow/workflow.py (100%) rename {pyworkflow => back-end/pyworkflow}/setup.py (100%) rename {vp => back-end/vp}/manage.py (100%) rename {vp => back-end/vp}/node/__init__.py (100%) rename {vp => back-end/vp}/node/admin.py (100%) rename {vp => back-end/vp}/node/apps.py (100%) rename {vp => back-end/vp}/node/migrations/__init__.py (100%) rename {vp => back-end/vp}/node/models.py (100%) rename {vp => back-end/vp}/node/tests.py (100%) rename {vp => back-end/vp}/node/urls.py (100%) rename {vp => back-end/vp}/node/views.py (100%) rename {vp => back-end/vp}/vp/__init__.py (100%) rename {vp => back-end/vp}/vp/asgi.py (100%) rename {vp => back-end/vp}/vp/settings.py (94%) rename {vp => back-end/vp}/vp/urls.py (100%) rename {vp => back-end/vp}/vp/views.py (100%) rename {vp => back-end/vp}/vp/wsgi.py (100%) rename {vp => back-end/vp}/workflow/__init__.py (100%) rename {vp => back-end/vp}/workflow/admin.py (100%) rename {vp => back-end/vp}/workflow/apps.py (100%) rename {vp => back-end/vp}/workflow/middleware.py (100%) rename {vp => back-end/vp}/workflow/migrations/__init__.py (100%) rename {vp => back-end/vp}/workflow/models.py (100%) rename {vp => back-end/vp}/workflow/tests.py (100%) rename {vp => back-end/vp}/workflow/urls.py (100%) rename {vp => back-end/vp}/workflow/views.py (100%) delete mode 100644 vp/db.sqlite3 diff --git a/CLI/__init__.py b/back-end/CLI/__init__.py similarity index 100% rename from CLI/__init__.py rename to back-end/CLI/__init__.py diff --git a/CLI/cli.py b/back-end/CLI/cli.py similarity index 100% rename from CLI/cli.py rename to back-end/CLI/cli.py diff --git a/CLI/setup.py b/back-end/CLI/setup.py similarity index 100% rename from CLI/setup.py rename to back-end/CLI/setup.py diff --git a/CLI/test.py b/back-end/CLI/test.py similarity index 100% rename from CLI/test.py rename to back-end/CLI/test.py diff --git a/Pipfile b/back-end/Pipfile similarity index 100% rename from Pipfile rename to back-end/Pipfile diff --git a/Pipfile.lock b/back-end/Pipfile.lock similarity index 100% rename from Pipfile.lock rename to back-end/Pipfile.lock diff --git a/pyworkflow/pyworkflow/.coveragerc b/back-end/pyworkflow/pyworkflow/.coveragerc similarity index 100% rename from pyworkflow/pyworkflow/.coveragerc rename to back-end/pyworkflow/pyworkflow/.coveragerc diff --git a/pyworkflow/pyworkflow/__init__.py b/back-end/pyworkflow/pyworkflow/__init__.py similarity index 100% rename from pyworkflow/pyworkflow/__init__.py rename to back-end/pyworkflow/pyworkflow/__init__.py diff --git a/pyworkflow/pyworkflow/node.py b/back-end/pyworkflow/pyworkflow/node.py similarity index 100% rename from pyworkflow/pyworkflow/node.py rename to back-end/pyworkflow/pyworkflow/node.py diff --git a/pyworkflow/pyworkflow/node_factory.py b/back-end/pyworkflow/pyworkflow/node_factory.py similarity index 100% rename from pyworkflow/pyworkflow/node_factory.py rename to back-end/pyworkflow/pyworkflow/node_factory.py diff --git a/pyworkflow/pyworkflow/nodes/__init__.py b/back-end/pyworkflow/pyworkflow/nodes/__init__.py similarity index 100% rename from pyworkflow/pyworkflow/nodes/__init__.py rename to back-end/pyworkflow/pyworkflow/nodes/__init__.py diff --git a/pyworkflow/pyworkflow/nodes/custom_nodes/table_creator.py b/back-end/pyworkflow/pyworkflow/nodes/custom_nodes/table_creator.py similarity index 100% rename from pyworkflow/pyworkflow/nodes/custom_nodes/table_creator.py rename to back-end/pyworkflow/pyworkflow/nodes/custom_nodes/table_creator.py diff --git a/pyworkflow/pyworkflow/nodes/flow_control/__init__.py b/back-end/pyworkflow/pyworkflow/nodes/flow_control/__init__.py similarity index 100% rename from pyworkflow/pyworkflow/nodes/flow_control/__init__.py rename to back-end/pyworkflow/pyworkflow/nodes/flow_control/__init__.py diff --git a/pyworkflow/pyworkflow/nodes/flow_control/integer_input.py b/back-end/pyworkflow/pyworkflow/nodes/flow_control/integer_input.py similarity index 100% rename from pyworkflow/pyworkflow/nodes/flow_control/integer_input.py rename to back-end/pyworkflow/pyworkflow/nodes/flow_control/integer_input.py diff --git a/pyworkflow/pyworkflow/nodes/flow_control/string_input.py b/back-end/pyworkflow/pyworkflow/nodes/flow_control/string_input.py similarity index 100% rename from pyworkflow/pyworkflow/nodes/flow_control/string_input.py rename to back-end/pyworkflow/pyworkflow/nodes/flow_control/string_input.py diff --git a/pyworkflow/pyworkflow/nodes/io/__init__.py b/back-end/pyworkflow/pyworkflow/nodes/io/__init__.py similarity index 100% rename from pyworkflow/pyworkflow/nodes/io/__init__.py rename to back-end/pyworkflow/pyworkflow/nodes/io/__init__.py diff --git a/pyworkflow/pyworkflow/nodes/io/read_csv.py b/back-end/pyworkflow/pyworkflow/nodes/io/read_csv.py similarity index 100% rename from pyworkflow/pyworkflow/nodes/io/read_csv.py rename to back-end/pyworkflow/pyworkflow/nodes/io/read_csv.py diff --git a/pyworkflow/pyworkflow/nodes/io/write_csv.py b/back-end/pyworkflow/pyworkflow/nodes/io/write_csv.py similarity index 100% rename from pyworkflow/pyworkflow/nodes/io/write_csv.py rename to back-end/pyworkflow/pyworkflow/nodes/io/write_csv.py diff --git a/pyworkflow/pyworkflow/nodes/manipulation/__init__.py b/back-end/pyworkflow/pyworkflow/nodes/manipulation/__init__.py similarity index 100% rename from pyworkflow/pyworkflow/nodes/manipulation/__init__.py rename to back-end/pyworkflow/pyworkflow/nodes/manipulation/__init__.py diff --git a/pyworkflow/pyworkflow/nodes/manipulation/filter.py b/back-end/pyworkflow/pyworkflow/nodes/manipulation/filter.py similarity index 100% rename from pyworkflow/pyworkflow/nodes/manipulation/filter.py rename to back-end/pyworkflow/pyworkflow/nodes/manipulation/filter.py diff --git a/pyworkflow/pyworkflow/nodes/manipulation/join.py b/back-end/pyworkflow/pyworkflow/nodes/manipulation/join.py similarity index 100% rename from pyworkflow/pyworkflow/nodes/manipulation/join.py rename to back-end/pyworkflow/pyworkflow/nodes/manipulation/join.py diff --git a/pyworkflow/pyworkflow/nodes/manipulation/pivot.py b/back-end/pyworkflow/pyworkflow/nodes/manipulation/pivot.py similarity index 100% rename from pyworkflow/pyworkflow/nodes/manipulation/pivot.py rename to back-end/pyworkflow/pyworkflow/nodes/manipulation/pivot.py diff --git a/pyworkflow/pyworkflow/nodes/visualization/__init__.py b/back-end/pyworkflow/pyworkflow/nodes/visualization/__init__.py similarity index 100% rename from pyworkflow/pyworkflow/nodes/visualization/__init__.py rename to back-end/pyworkflow/pyworkflow/nodes/visualization/__init__.py diff --git a/pyworkflow/pyworkflow/nodes/visualization/graph.py b/back-end/pyworkflow/pyworkflow/nodes/visualization/graph.py similarity index 100% rename from pyworkflow/pyworkflow/nodes/visualization/graph.py rename to back-end/pyworkflow/pyworkflow/nodes/visualization/graph.py diff --git a/pyworkflow/pyworkflow/parameters.py b/back-end/pyworkflow/pyworkflow/parameters.py similarity index 100% rename from pyworkflow/pyworkflow/parameters.py rename to back-end/pyworkflow/pyworkflow/parameters.py diff --git a/pyworkflow/pyworkflow/tests/sample_test_data.py b/back-end/pyworkflow/pyworkflow/tests/sample_test_data.py similarity index 100% rename from pyworkflow/pyworkflow/tests/sample_test_data.py rename to back-end/pyworkflow/pyworkflow/tests/sample_test_data.py diff --git a/pyworkflow/pyworkflow/tests/test_node.py b/back-end/pyworkflow/pyworkflow/tests/test_node.py similarity index 100% rename from pyworkflow/pyworkflow/tests/test_node.py rename to back-end/pyworkflow/pyworkflow/tests/test_node.py diff --git a/pyworkflow/pyworkflow/tests/test_parameters.py b/back-end/pyworkflow/pyworkflow/tests/test_parameters.py similarity index 100% rename from pyworkflow/pyworkflow/tests/test_parameters.py rename to back-end/pyworkflow/pyworkflow/tests/test_parameters.py diff --git a/pyworkflow/pyworkflow/tests/test_pyworkflow.py b/back-end/pyworkflow/pyworkflow/tests/test_pyworkflow.py similarity index 100% rename from pyworkflow/pyworkflow/tests/test_pyworkflow.py rename to back-end/pyworkflow/pyworkflow/tests/test_pyworkflow.py diff --git a/pyworkflow/pyworkflow/tests/test_workflow.py b/back-end/pyworkflow/pyworkflow/tests/test_workflow.py similarity index 100% rename from pyworkflow/pyworkflow/tests/test_workflow.py rename to back-end/pyworkflow/pyworkflow/tests/test_workflow.py diff --git a/pyworkflow/pyworkflow/workflow.py b/back-end/pyworkflow/pyworkflow/workflow.py similarity index 100% rename from pyworkflow/pyworkflow/workflow.py rename to back-end/pyworkflow/pyworkflow/workflow.py diff --git a/pyworkflow/setup.py b/back-end/pyworkflow/setup.py similarity index 100% rename from pyworkflow/setup.py rename to back-end/pyworkflow/setup.py diff --git a/vp/manage.py b/back-end/vp/manage.py similarity index 100% rename from vp/manage.py rename to back-end/vp/manage.py diff --git a/vp/node/__init__.py b/back-end/vp/node/__init__.py similarity index 100% rename from vp/node/__init__.py rename to back-end/vp/node/__init__.py diff --git a/vp/node/admin.py b/back-end/vp/node/admin.py similarity index 100% rename from vp/node/admin.py rename to back-end/vp/node/admin.py diff --git a/vp/node/apps.py b/back-end/vp/node/apps.py similarity index 100% rename from vp/node/apps.py rename to back-end/vp/node/apps.py diff --git a/vp/node/migrations/__init__.py b/back-end/vp/node/migrations/__init__.py similarity index 100% rename from vp/node/migrations/__init__.py rename to back-end/vp/node/migrations/__init__.py diff --git a/vp/node/models.py b/back-end/vp/node/models.py similarity index 100% rename from vp/node/models.py rename to back-end/vp/node/models.py diff --git a/vp/node/tests.py b/back-end/vp/node/tests.py similarity index 100% rename from vp/node/tests.py rename to back-end/vp/node/tests.py diff --git a/vp/node/urls.py b/back-end/vp/node/urls.py similarity index 100% rename from vp/node/urls.py rename to back-end/vp/node/urls.py diff --git a/vp/node/views.py b/back-end/vp/node/views.py similarity index 100% rename from vp/node/views.py rename to back-end/vp/node/views.py diff --git a/vp/vp/__init__.py b/back-end/vp/vp/__init__.py similarity index 100% rename from vp/vp/__init__.py rename to back-end/vp/vp/__init__.py diff --git a/vp/vp/asgi.py b/back-end/vp/vp/asgi.py similarity index 100% rename from vp/vp/asgi.py rename to back-end/vp/vp/asgi.py diff --git a/vp/vp/settings.py b/back-end/vp/vp/settings.py similarity index 94% rename from vp/vp/settings.py rename to back-end/vp/vp/settings.py index 49453f9..79ff0ec 100644 --- a/vp/vp/settings.py +++ b/back-end/vp/vp/settings.py @@ -25,7 +25,12 @@ # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True -ALLOWED_HOSTS = [] +ALLOWED_HOSTS = ['back-end:8000', + 'back-end', + 'localhost', + '127.0.0.1', + '0.0.0.0', + '[::1]'] # Application definition @@ -81,7 +86,7 @@ # Database # https://docs.djangoproject.com/en/3.0/ref/settings/#databases -### Not yet setup +# Not yet setup DATABASES = {} MEDIA_ROOT = '/tmp' diff --git a/vp/vp/urls.py b/back-end/vp/vp/urls.py similarity index 100% rename from vp/vp/urls.py rename to back-end/vp/vp/urls.py diff --git a/vp/vp/views.py b/back-end/vp/vp/views.py similarity index 100% rename from vp/vp/views.py rename to back-end/vp/vp/views.py diff --git a/vp/vp/wsgi.py b/back-end/vp/vp/wsgi.py similarity index 100% rename from vp/vp/wsgi.py rename to back-end/vp/vp/wsgi.py diff --git a/vp/workflow/__init__.py b/back-end/vp/workflow/__init__.py similarity index 100% rename from vp/workflow/__init__.py rename to back-end/vp/workflow/__init__.py diff --git a/vp/workflow/admin.py b/back-end/vp/workflow/admin.py similarity index 100% rename from vp/workflow/admin.py rename to back-end/vp/workflow/admin.py diff --git a/vp/workflow/apps.py b/back-end/vp/workflow/apps.py similarity index 100% rename from vp/workflow/apps.py rename to back-end/vp/workflow/apps.py diff --git a/vp/workflow/middleware.py b/back-end/vp/workflow/middleware.py similarity index 100% rename from vp/workflow/middleware.py rename to back-end/vp/workflow/middleware.py diff --git a/vp/workflow/migrations/__init__.py b/back-end/vp/workflow/migrations/__init__.py similarity index 100% rename from vp/workflow/migrations/__init__.py rename to back-end/vp/workflow/migrations/__init__.py diff --git a/vp/workflow/models.py b/back-end/vp/workflow/models.py similarity index 100% rename from vp/workflow/models.py rename to back-end/vp/workflow/models.py diff --git a/vp/workflow/tests.py b/back-end/vp/workflow/tests.py similarity index 100% rename from vp/workflow/tests.py rename to back-end/vp/workflow/tests.py diff --git a/vp/workflow/urls.py b/back-end/vp/workflow/urls.py similarity index 100% rename from vp/workflow/urls.py rename to back-end/vp/workflow/urls.py diff --git a/vp/workflow/views.py b/back-end/vp/workflow/views.py similarity index 100% rename from vp/workflow/views.py rename to back-end/vp/workflow/views.py diff --git a/vp/db.sqlite3 b/vp/db.sqlite3 deleted file mode 100644 index e69de29..0000000 From c0d0c71abf2525a9a00879ec1401ff5eb7553d5c Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Tue, 5 May 2020 02:02:27 -0500 Subject: [PATCH 10/21] Adding docker files --- .dockerignore | 24 ++++++++++++++++++++++++ back-end/Dockerfile | 19 +++++++++++++++++++ docker-compose.yml | 26 ++++++++++++++++++++++++++ front-end/Dockerfile | 14 ++++++++++++++ 4 files changed, 83 insertions(+) create mode 100644 .dockerignore create mode 100644 back-end/Dockerfile create mode 100644 docker-compose.yml create mode 100644 front-end/Dockerfile diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..8604b6c --- /dev/null +++ b/.dockerignore @@ -0,0 +1,24 @@ +.git +.vscode + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# production +/build + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/back-end/Dockerfile b/back-end/Dockerfile new file mode 100644 index 0000000..1fb0106 --- /dev/null +++ b/back-end/Dockerfile @@ -0,0 +1,19 @@ +FROM python:3.8 + +WORKDIR /visual-programming/back-end + +COPY Pipfile Pipfile.lock ./ +COPY CLI/ ./CLI/ +COPY pyworkflow/ ./pyworkflow/ + +RUN pip install pipenv +RUN pipenv install --dev --ignore-pipfile + +COPY vp/ ./vp +RUN echo "SECRET_KEY=tmp" > vp/.environment + +EXPOSE 8000 + +WORKDIR /visual-programming/back-end/vp + +CMD pipenv run python manage.py runserver 0.0.0.0:8000 diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..2df6ef4 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,26 @@ +version: "3.3" +services: + back-end: + build: ./back-end + volumes: + - ./back-end:/visual-programming/back-end + ports: + - "8000:8000" + command: bash -c "pipenv run python manage.py runserver 0.0.0.0:8000" + environment: + - DJANGO_ENV=development + front-end: + build: ./front-end + volumes: + - ./front-end:/visual-programming/front-end + # One-way volume to use node_modules from inside image + - /visual-programming/front-end/node_modules + ports: + - "3000:3000" + environment: + - NODE_ENV=development + depends_on: + - back-end + links: + - back-end + command: npm start diff --git a/front-end/Dockerfile b/front-end/Dockerfile new file mode 100644 index 0000000..2e7842e --- /dev/null +++ b/front-end/Dockerfile @@ -0,0 +1,14 @@ +FROM node:14-alpine + +WORKDIR /visual-programming/front-end + +COPY package.json . + +RUN npm install + +COPY src/ ./src/ +COPY public/ ./public/ + +EXPOSE 3000 + +CMD npm start From a7d6772029f8fc0773b5e03bf7be51fc946280ef Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Tue, 5 May 2020 02:02:54 -0500 Subject: [PATCH 11/21] GitHub Actions fix & README updates --- .github/workflows/main.yml | 2 +- README.md | 28 ++++++++++++++++++++++++++-- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index cba5dcb..7be4877 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -31,6 +31,7 @@ jobs: run: | python3 -m pip install --upgrade pip pip3 install pipenv + cd back-end pipenv install pipenv run echo "SECRET_KEY='TEMPORARY SECRET KEY'" > vp/.environment @@ -61,4 +62,3 @@ jobs: with: collection: Postman/Visual\ Programming-Tests.postman_collection.json environment: Postman/Local\ Testing.postman_environment.json - \ No newline at end of file diff --git a/README.md b/README.md index 90d589e..fb50303 100644 --- a/README.md +++ b/README.md @@ -64,11 +64,11 @@ data from `localhost:8000` **where the Django app must be running**. --- ## CLI 1. Run pipenv shell. -2. Create a workflow using UI and save it. +2. Create a workflow using UI and save it. 3. Run it as: pyworkflow execute workflow-file Also accepts reading input from std (i.e < file.csv) and writing to sdt out (i.e > output.csv) - + --- @@ -94,3 +94,27 @@ directory, you can run - `coverage run -m unittest tests/*.py` - `coverage report` (to see a report via the CLI) - `coverage html && open /htmlcov/index.html` (to view interactive coverage) + + + +--- +## Docker +The Docker container for PyWorkflow is built from 2 images: the `front-end` and the `back-end`. The `docker-compose.yml` defines how to combine and run the two. + +In order to build each image individually, from the root of the application: +- `docker build front-end --tag FE_IMAGE[:TAG]` +- `docker build back-end --tag BE_IMAGE[:TAG]` + ex. - `docker build back-end --tag backendtest:2.0` + +Each individual image can be run by changing to the `front-end` or `back-end` directory and running: +- `docker run -p 3000:3000 --name FE_CONTAINER_NAME FE_IMAGE[:TAG]` +- `docker run -p 8000:8000 --name BE_CONTAINER_NAME BE_IMAGE[:TAG]` + ex. - `docker run -p 8000:8000 --name pyworkflow-be backendtest:2.0` + +To compose and run the entire application container, from the root of the application: +- `docker-compose up` + +You can then kill the container gracefully with: +- `docker-compose down` + +NOTE: For development, change ./front-end/package.json from "proxy": "http://back-end:8000" to "http://localhost:8000" to work. From d4ae51c734607a65d2aa8edb1dd86e64566ed1b8 Mon Sep 17 00:00:00 2001 From: Matt Thomas Date: Tue, 5 May 2020 07:44:28 -0400 Subject: [PATCH 12/21] fix: Add `stdin_open` to fix known `react-scripts` bug --- docker-compose.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/docker-compose.yml b/docker-compose.yml index 2df6ef4..8bd1061 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -15,6 +15,7 @@ services: - ./front-end:/visual-programming/front-end # One-way volume to use node_modules from inside image - /visual-programming/front-end/node_modules + stdin_open: true ports: - "3000:3000" environment: From bf6d50c7178141bb1c15bc51b1b1dc08e67efeee Mon Sep 17 00:00:00 2001 From: Matt Thomas Date: Tue, 5 May 2020 07:45:01 -0400 Subject: [PATCH 13/21] fix: Default proxy to `back-end` for working with both containers --- front-end/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/front-end/package.json b/front-end/package.json index 5cd81a3..64507d6 100644 --- a/front-end/package.json +++ b/front-end/package.json @@ -67,7 +67,7 @@ "last 1 safari version" ] }, - "proxy": "http://localhost:8000", + "proxy": "http://back-end:8000", "devDependencies": { "@babel/preset-env": "^7.9.5", "@babel/preset-react": "^7.9.4", From 55c1352661d2ec20c79746227e52c2c427fbabce Mon Sep 17 00:00:00 2001 From: Matt Thomas Date: Tue, 5 May 2020 07:49:43 -0400 Subject: [PATCH 14/21] docs: Add note about running front-end solo in Docker --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index fb50303..5e258b5 100644 --- a/README.md +++ b/README.md @@ -111,6 +111,10 @@ Each individual image can be run by changing to the `front-end` or `back-end` di - `docker run -p 8000:8000 --name BE_CONTAINER_NAME BE_IMAGE[:TAG]` ex. - `docker run -p 8000:8000 --name pyworkflow-be backendtest:2.0` +Note: there [is a known issue with `react-scripts` v3.4.1](https://github.com/facebook/create-react-app/issues/8688) +that may cause the front-end container to exit with code 0. If this happens, +you can add `-e CI=true` to the `docker-run` command above for the front-end. + To compose and run the entire application container, from the root of the application: - `docker-compose up` From b4b4d1daa0b4e0dce7cb5bb551bbb9de64d7c744 Mon Sep 17 00:00:00 2001 From: Samir Reddigari Date: Tue, 5 May 2020 19:57:05 -0400 Subject: [PATCH 15/21] fix: use replacement values from flow nodes --- pyworkflow/pyworkflow/node.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pyworkflow/pyworkflow/node.py b/pyworkflow/pyworkflow/node.py index 0a3bc20..88153ae 100644 --- a/pyworkflow/pyworkflow/node.py +++ b/pyworkflow/pyworkflow/node.py @@ -59,6 +59,8 @@ def get_execution_options(self, workflow, flow_nodes): if key == 'file': option.set_value(workflow.path(replacement_value)) + else: + option.set_value(replacement_value) execution_options[key] = option From e7a7dd585a5a606dd925b0589a6aa262a874d153 Mon Sep 17 00:00:00 2001 From: Samir Reddigari Date: Tue, 5 May 2020 20:26:47 -0400 Subject: [PATCH 16/21] refactor: don't replace option values unnecessarily --- pyworkflow/pyworkflow/node.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pyworkflow/pyworkflow/node.py b/pyworkflow/pyworkflow/node.py index 88153ae..898fe1c 100644 --- a/pyworkflow/pyworkflow/node.py +++ b/pyworkflow/pyworkflow/node.py @@ -54,13 +54,12 @@ def get_execution_options(self, workflow, flow_nodes): if key in flow_nodes: replacement_value = flow_nodes[key].get_replacement_value() + option.set_value(replacement_value) else: replacement_value = option.get_value() if key == 'file': option.set_value(workflow.path(replacement_value)) - else: - option.set_value(replacement_value) execution_options[key] = option From edfe3aff97221724d0e1c03e06a02a043939e254 Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Tue, 5 May 2020 22:07:36 -0500 Subject: [PATCH 17/21] Removing volume declarations in `docker-compose` --- docker-compose.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 8bd1061..7b2a9a0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,8 +2,6 @@ version: "3.3" services: back-end: build: ./back-end - volumes: - - ./back-end:/visual-programming/back-end ports: - "8000:8000" command: bash -c "pipenv run python manage.py runserver 0.0.0.0:8000" @@ -11,10 +9,6 @@ services: - DJANGO_ENV=development front-end: build: ./front-end - volumes: - - ./front-end:/visual-programming/front-end - # One-way volume to use node_modules from inside image - - /visual-programming/front-end/node_modules stdin_open: true ports: - "3000:3000" From 19f65edad27e11c208a91dc45ea734283c705df7 Mon Sep 17 00:00:00 2001 From: Samir Reddigari Date: Wed, 6 May 2020 17:06:15 -0400 Subject: [PATCH 18/21] feat(ui): add flow out port to node model (only on flow nodes) --- front-end/src/components/CustomNode/CustomNodeModel.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/front-end/src/components/CustomNode/CustomNodeModel.js b/front-end/src/components/CustomNode/CustomNodeModel.js index 0b0c890..a3d05ff 100644 --- a/front-end/src/components/CustomNode/CustomNodeModel.js +++ b/front-end/src/components/CustomNode/CustomNodeModel.js @@ -21,6 +21,16 @@ export default class CustomNodeModel extends NodeModel { name: 'flow-in' }) ); + // if flow node, add flow control output port + if (this.options.node_type === "flow_control") { + this.addPort( + new VPPortModel({ + in: false, + type: 'vp-port', + name: 'flow-out' + }) + ); + } const nIn = options.num_in === undefined ? 1 : options.num_in; const nOut = options.num_out === undefined ? 1 : options.num_out; // setup in and out ports From 88e6e34237c07aa9e6c5c87835cdfacbca3f235f Mon Sep 17 00:00:00 2001 From: Samir Reddigari Date: Wed, 6 May 2020 17:06:46 -0400 Subject: [PATCH 19/21] feat(ui): render flow out port like flow in port --- .../src/components/CustomNode/CustomNodeWidget.js | 15 ++++++++------- front-end/src/styles/CustomNode.css | 7 +++++++ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/front-end/src/components/CustomNode/CustomNodeWidget.js b/front-end/src/components/CustomNode/CustomNodeWidget.js index 6ee902f..6bc38eb 100644 --- a/front-end/src/components/CustomNode/CustomNodeWidget.js +++ b/front-end/src/components/CustomNode/CustomNodeWidget.js @@ -48,8 +48,9 @@ export default class CustomNodeWidget extends React.Component { render() { const engine = this.props.engine; const allPorts = _.values(this.props.node.getPorts()); - const ports = allPorts.filter(p => p.options.name !== "flow-in"); - const flowPort = allPorts.find(p => p.options.name === "flow-in"); + const ports = allPorts.filter(p => !p.options.name.includes("flow")); + const flowInPort = allPorts.find(p => p.options.name === "flow-in"); + const flowOutPort = allPorts.find(p => p.options.name === "flow-out"); // group ports by type (in/out) const sortedPorts = _.groupBy(ports, p => p.options.in === true ? "in" : "out"); // create PortWidget array for each type @@ -62,12 +63,12 @@ export default class CustomNodeWidget extends React.Component { ); } - const flowPortWidget = flowPort ? - + const flowPortWidgets = [flowInPort, flowOutPort].filter(p => p).map(port => +
- : null; + ); let graphView; @@ -101,7 +102,7 @@ export default class CustomNodeWidget extends React.Component { onDelete={this.handleDelete} onSubmit={this.acceptConfiguration} />
- {flowPortWidget} + {flowPortWidgets}
{ portWidgets["in"] }
diff --git a/front-end/src/styles/CustomNode.css b/front-end/src/styles/CustomNode.css index 90c06e3..2060c0b 100644 --- a/front-end/src/styles/CustomNode.css +++ b/front-end/src/styles/CustomNode.css @@ -47,9 +47,16 @@ .flow-port-div { position: absolute; top: -5px; +} + +.flow-port-div-in { left: -5px; } +.flow-port-div-out { + left: 85%; +} + .flow-port { width: 10px; height: 10px; From 56a089240bf5d51fec0cdd96d3590e4d7d8050bd Mon Sep 17 00:00:00 2001 From: Samir Reddigari Date: Wed, 6 May 2020 17:07:25 -0400 Subject: [PATCH 20/21] fix(ui): improve logic in canLinkToPort method --- front-end/src/components/VPPort/VPPortModel.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/front-end/src/components/VPPort/VPPortModel.js b/front-end/src/components/VPPort/VPPortModel.js index 4c0897a..c72778d 100644 --- a/front-end/src/components/VPPort/VPPortModel.js +++ b/front-end/src/components/VPPort/VPPortModel.js @@ -9,8 +9,16 @@ export default class VPPortModel extends DefaultPortModel { } canLinkToPort(port) { - // can't both be in or out ports - return port instanceof VPPortModel - && this.options.in !== port.options.in; + // if connecting to flow port, make sure this is a flow port + // and opposite of other's direction + if (port.options.name.includes("flow")) { + return this.options.name.includes("flow") + && this.options.in !== port.options.in + // otherwise, make sure this is NOT a flow port, and ensure + // in/out compatibility + } else { + return !this.options.name.includes("flow") + && this.options.in !== port.options.in + } } } From 0ff3d9d5868eb613df47fed7e80c58eba34dc619 Mon Sep 17 00:00:00 2001 From: Samir Reddigari Date: Wed, 6 May 2020 17:07:47 -0400 Subject: [PATCH 21/21] fix: remove data out port on flow nodes --- .../pyworkflow/pyworkflow/nodes/flow_control/integer_input.py | 2 +- .../pyworkflow/pyworkflow/nodes/flow_control/string_input.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/back-end/pyworkflow/pyworkflow/nodes/flow_control/integer_input.py b/back-end/pyworkflow/pyworkflow/nodes/flow_control/integer_input.py index e917d07..0ecb0cf 100644 --- a/back-end/pyworkflow/pyworkflow/nodes/flow_control/integer_input.py +++ b/back-end/pyworkflow/pyworkflow/nodes/flow_control/integer_input.py @@ -9,7 +9,7 @@ class IntegerNode(FlowNode): """ name = "Integer Input" num_in = 0 - num_out = 1 + num_out = 0 color = 'purple' OPTIONS = { diff --git a/back-end/pyworkflow/pyworkflow/nodes/flow_control/string_input.py b/back-end/pyworkflow/pyworkflow/nodes/flow_control/string_input.py index a5714a1..81a785b 100644 --- a/back-end/pyworkflow/pyworkflow/nodes/flow_control/string_input.py +++ b/back-end/pyworkflow/pyworkflow/nodes/flow_control/string_input.py @@ -9,7 +9,7 @@ class StringNode(FlowNode): """ name = "String Input" num_in = 0 - num_out = 1 + num_out = 0 color = 'purple' OPTIONS = {