Repository: AgnostiqHQ/covalent
Pull request #1565 (2023-03-30 21:16:20)

Postprocess bug fix
<!--
⚠️ Your pull request title should be short detailed and understandable for all.
⚠️ Increment the version number in the /VERSION file. If this is a bugfix, increment the patch version (the rightmost number), for example 0.18.2 becomes 0.18.3. If it's a new feature, increment the minor version (the middle number), for example 0.18.2 becomes 0.19.0.
⚠️ Add a note to /CHANGELOG.md with your version number and the date, summarizing the changes.
⚠️ If your pull request fixes an open issue, please link to the issue.
⚠️ Rebase the latest changes from the develop branch. Assuming origin points to https://github.com/AgnostiqHQ/covalent, you should run git rebase origin/develop.
-->

- [ ] I have added the tests to cover my changes.
- [ ] I have updated the documentation and CHANGELOG accordingly.
- [ ] I have read the CONTRIBUTING document.


codecov[bot]: ## [Codecov](https://codecov.io/gh/AgnostiqHQ/covalent/pull/1565?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=AgnostiqHQ) Report
> Merging [#1565](https://codecov.io/gh/AgnostiqHQ/covalent/pull/1565?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=AgnostiqHQ) (2ae3e0c) into [develop](https://codecov.io/gh/AgnostiqHQ/covalent/commit/501f82e5c81a62949cc6a588efa9d6db6fbb61e9?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=AgnostiqHQ) (501f82e) will **increase** coverage by `0.32%`.
> The diff coverage is `98.93%`.

<details><summary>Additional details and impacted files</summary>


[![Impacted file tree graph](https://codecov.io/gh/AgnostiqHQ/covalent/pull/1565/graphs/tree.svg?width=650&height=150&src=pr&token=YGHCB3DE4P&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=AgnostiqHQ)](https://codecov.io/gh/AgnostiqHQ/covalent/pull/1565?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=AgnostiqHQ)

```diff
@@             Coverage Diff             @@
##           develop    #1565      +/-   ##
===========================================
+ Coverage    85.87%   86.19%   +0.32%     
===========================================
  Files          157       82      -75     
  Lines         6476     4809    -1667     
  Branches       145        0     -145     
===========================================
- Hits          5561     4145    -1416     
+ Misses         813      664     -149     
+ Partials       102        0     -102     
```

| Flag | Coverage Δ | |
|---|---|---|
| Dispatcher | `88.48% <100.00%> (-0.46%)` | :arrow_down: |
| Functional_Tests | `?` | |
| SDK | `84.71% <98.80%> (+0.95%)` | :arrow_up: |
| UI_Frontend | `?` | |

</details>

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4e188603..03c23b1c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ## [UNRELEASED]
 
+### Fixed
+
+- Doubling of nodes that are added to the transport graph.
+- Ensure postprocessing node end time is added as the workflow end time.
+
 ### Added
 
 - Postprocessing as electrons.
diff --git a/covalent/_workflow/lattice.py b/covalent/_workflow/lattice.py
index 5c257c87..d738d2d9 100644
--- a/covalent/_workflow/lattice.py
+++ b/covalent/_workflow/lattice.py
@@ -239,7 +239,6 @@ class Lattice:
         with redirect_stdout(open(os.devnull, "w")):
             with active_lattice_manager.claim(self):
                 try:
-                    workflow_function(*new_args, **new_kwargs)
                     retval = workflow_function(*new_args, **new_kwargs)
                 except Exception:
                     warnings.warn(
diff --git a/covalent_dispatcher/_db/update.py b/covalent_dispatcher/_db/update.py
index aa8054ac..58bb60fa 100644
--- a/covalent_dispatcher/_db/update.py
+++ b/covalent_dispatcher/_db/update.py
@@ -112,6 +112,7 @@ def _node(
     if node_name.startswith(postprocess_prefix):
         result._result = output
         result._status = status
+        result._end_time = end_time
         upsert.lattice_data(result)
 
 


Test patch:
diff --git a/tests/covalent_dispatcher_tests/_core/dispatcher_test.py b/tests/covalent_dispatcher_tests/_core/dispatcher_test.py
index 74b04283..6a2fa006 100644
--- a/tests/covalent_dispatcher_tests/_core/dispatcher_test.py
+++ b/tests/covalent_dispatcher_tests/_core/dispatcher_test.py
@@ -181,7 +181,7 @@ def test_get_abstract_task_inputs():
     result_object = Result(lattice=received_lattice, dispatch_id="asdf")
     tg = received_lattice.transport_graph
 
-    assert list(tg._graph.nodes) == list(range(17))
+    assert list(tg._graph.nodes) == list(range(9))
     tg.set_node_value(0, "output", ct.TransportableObject(1))
     tg.set_node_value(2, "output", ct.TransportableObject(2))
 
@@ -264,8 +264,8 @@ async def test_get_initial_tasks_and_deps(mocker):
     result_object = get_mock_result()
     num_tasks, initial_nodes, pending_parents = await _get_initial_tasks_and_deps(result_object)
 
-    assert initial_nodes == [1, 4]
-    assert pending_parents == {0: 1, 1: 0, 2: 1, 3: 1, 4: 0, 5: 1, 6: 4}
+    assert initial_nodes == [1]
+    assert pending_parents == {0: 1, 1: 0, 2: 1, 3: 2}
     assert num_tasks == len(result_object.lattice.transport_graph._graph.nodes)
 
 
diff --git a/tests/covalent_dispatcher_tests/_core/execution_test.py b/tests/covalent_dispatcher_tests/_core/execution_test.py
index 319dc7e9..9783f70c 100644
--- a/tests/covalent_dispatcher_tests/_core/execution_test.py
+++ b/tests/covalent_dispatcher_tests/_core/execution_test.py
@@ -155,7 +155,7 @@ def test_get_task_inputs():
     result_object = Result(lattice=received_lattice, dispatch_id="asdf")
     tg = received_lattice.transport_graph
 
-    assert list(tg._graph.nodes) == list(range(17))
+    assert list(tg._graph.nodes) == list(range(9))
     tg.set_node_value(0, "output", ct.TransportableObject(1))
     tg.set_node_value(2, "output", ct.TransportableObject(2))
 
@@ -375,7 +375,7 @@ async def test_run_workflow_with_client_side_postprocess(test_db, mocker):
     result_object = await run_workflow(result_object)
     mock_unregister.assert_called_with(result_object.dispatch_id)
     assert result_object.status == Result.RUNNING
-    assert mock_run_abstract_task.call_count == 2
+    assert mock_run_abstract_task.call_count == 1
 
 
 @pytest.mark.asyncio
@@ -421,7 +421,7 @@ async def test_run_workflow_with_failed_postprocess(test_db, mocker):
     mock_unregister.assert_called_with(result_object.dispatch_id)
 
     assert result_object.status == Result.RUNNING
-    assert mock_run_abstract_task.call_count == 4
+    assert mock_run_abstract_task.call_count == 2
 
 
 @pytest.mark.asyncio
diff --git a/tests/covalent_dispatcher_tests/_core/runner_test.py b/tests/covalent_dispatcher_tests/_core/runner_test.py
index 2535ef72..49c401dd 100644
--- a/tests/covalent_dispatcher_tests/_core/runner_test.py
+++ b/tests/covalent_dispatcher_tests/_core/runner_test.py
@@ -262,7 +262,7 @@ def test_build_sublattice_graph():
     json_lattice = _build_sublattice_graph(workflow, parent_metadata, 1)
     lattice = Lattice.deserialize_from_json(json_lattice)
 
-    assert list(lattice.transport_graph._graph.nodes) == list(range(5))
+    assert list(lattice.transport_graph._graph.nodes) == list(range(3))
     for k in lattice.metadata.keys():
         # results_dir will be deprecated soon
         if k != "results_dir":
diff --git a/tests/covalent_dispatcher_tests/_db/update_test.py b/tests/covalent_dispatcher_tests/_db/update_test.py
index 8e10dd88..88dd1cb0 100644
--- a/tests/covalent_dispatcher_tests/_db/update_test.py
+++ b/tests/covalent_dispatcher_tests/_db/update_test.py
@@ -151,7 +151,7 @@ def test_update_node(test_db, result_1, mocker):
             == "test_sublattice"
         )
 
-        assert lattice_record.electron_num == 11
+        assert lattice_record.electron_num == 6
         assert lattice_record.completed_electron_num == 0
         assert lattice_record.updated_at is not None
     update._node(
@@ -177,7 +177,7 @@ def test_update_node(test_db, result_1, mocker):
         )
         assert result == 5
 
-        assert lattice_record.electron_num == 11
+        assert lattice_record.electron_num == 6
         assert lattice_record.completed_electron_num == 1
         assert lattice_record.updated_at is not None
 
@@ -281,7 +281,7 @@ def test_result_persist_workflow_1(test_db, result_1, mocker):
                 assert executor_data["attributes"] == le.__dict__
 
         # Check that there are the appropriate amount of electron dependency records
-        assert len(electron_dependency_rows) == 12
+        assert len(electron_dependency_rows) == 6
 
         # Update some node / lattice statuses
         cur_time = dt.now(timezone.utc)
@@ -289,7 +289,7 @@ def test_result_persist_workflow_1(test_db, result_1, mocker):
         result_1._status = "COMPLETED"
         result_1._result = ct.TransportableObject({"helo": 1, "world": 2})
 
-        for node_id in range(10):
+        for node_id in range(5):
             update._node(
                 result_1,
                 node_id=node_id,
diff --git a/tests/covalent_tests/workflow/electron_test.py b/tests/covalent_tests/workflow/electron_test.py
index 248de8de..e9678bef 100644
--- a/tests/covalent_tests/workflow/electron_test.py
+++ b/tests/covalent_tests/workflow/electron_test.py
@@ -160,8 +160,8 @@ def test_injected_inputs_are_not_in_tg():
     workflow.build_graph(2)
     g = workflow.transport_graph._graph
 
-    assert list(g.nodes) == list(range(5))
-    assert list(g.edges) == [(0, 4, 0), (1, 0, 0), (2, 4, 0), (3, 2, 0)]
+    assert list(g.nodes) == list(range(3))
+    assert list(g.edges) == [(0, 2, 0), (1, 0, 0)]
 
 
 def test_metadata_in_electron_list():
@@ -233,22 +233,13 @@ def test_autogen_list_electrons():
 
     g = workflow.transport_graph._graph
 
-    assert list(g.nodes) == list(range(9))
+    assert list(g.nodes) == list(range(5))
     fn = g.nodes[1]["function"].get_deserialized()
     assert fn(2, 5, 7) == [2, 5, 7]
 
     assert g.nodes[2]["value"].get_deserialized() == 5
     assert g.nodes[3]["value"].get_deserialized() == 7
-    assert set(g.edges) == {
-        (0, 8, 0),
-        (1, 0, 0),
-        (2, 1, 0),
-        (3, 1, 0),
-        (4, 8, 0),
-        (5, 4, 0),
-        (6, 5, 0),
-        (7, 5, 0),
-    }
+    assert set(g.edges) == {(1, 0, 0), (3, 1, 0), (2, 1, 0), (0, 4, 0)}
 
 
 def test_autogen_dict_electrons():
@@ -264,21 +255,12 @@ def test_autogen_dict_electrons():
 
     g = workflow.transport_graph._graph
 
-    assert list(g.nodes) == [0, 1, 2, 3, 4, 5, 6, 7, 8]
+    assert list(g.nodes) == list(range(5))
     fn = g.nodes[1]["function"].get_deserialized()
     assert fn(x=2, y=5, z=7) == {"x": 2, "y": 5, "z": 7}
     assert g.nodes[2]["value"].get_deserialized() == 5
     assert g.nodes[3]["value"].get_deserialized() == 7
-    assert set(g.edges) == {
-        (0, 8, 0),
-        (1, 0, 0),
-        (2, 1, 0),
-        (3, 1, 0),
-        (4, 8, 0),
-        (5, 4, 0),
-        (6, 5, 0),
-        (7, 5, 0),
-    }
+    assert set(g.edges) == {(1, 0, 0), (3, 1, 0), (2, 1, 0), (0, 4, 0)}
 
 
 def test_as_transportable_dict(mocker):
diff --git a/tests/covalent_tests/workflow/postprocessing_test.py b/tests/covalent_tests/workflow/postprocessing_test.py
index 30859855..7af8f071 100644
--- a/tests/covalent_tests/workflow/postprocessing_test.py
+++ b/tests/covalent_tests/workflow/postprocessing_test.py
@@ -82,7 +82,7 @@ def test_filter_electrons(postprocessor):
     tg = postprocessor.lattice.transport_graph
     mock_bound_electrons = {i: f"mock_electron_{i}" for i in range(12)}
     assert postprocessor._filter_electrons(tg, mock_bound_electrons) == [
-        f"mock_electron_{i}" for i in [0, 2, 4, 6, 8, 10]
+        f"mock_electron_{i}" for i in [0, 2, 4]
     ]