Repository: AgnostiqHQ/covalent (python)

Issue:
Electron dependencies are now saved twice in the db
As of v0.200.0, each transport graph edge now appears twice in the DB. This occurs because [some logic to check](https://github.com/AgnostiqHQ/covalent/blob/79952e6ea409cb5db359c688c0810bf57742b08b/covalent/_results_manager/result.py#L762) for existing edge entries in the DB was accidentally removed during the recent reorg of DB logic.

Pull request:
Fix dup electron dependencies in the DB
<!--
⚠️ 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.
-->

- [x] I have added the tests to cover my changes.
- [x] I have updated the documentation and CHANGELOG accordingly.
- [x] I have read the CONTRIBUTING document.

Closes #1327 .

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ddb11d30..a27436a3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 - Ability to view sublattices list as part of the main lattice
 - Ability to view subalattices graph as part of main lattice
 
+
+### Fixed
+
+- Electron dependencies are no longer written twice to the DB during a workflow
+
 ## [0.201.0] - 2022-10-09
 
 ### Authors
diff --git a/covalent_dispatcher/_db/update.py b/covalent_dispatcher/_db/update.py
index 1b2b2a6b..102142c9 100644
--- a/covalent_dispatcher/_db/update.py
+++ b/covalent_dispatcher/_db/update.py
@@ -30,7 +30,7 @@ from covalent._workflow.lattice import Lattice
 from covalent._workflow.transport import _TransportGraph
 
 from . import upsert
-from .write_result_to_db import insert_electron_dependency_data
+from .write_result_to_db import upsert_electron_dependency_data
 
 app_log = logger.app_log
 
@@ -50,7 +50,7 @@ def persist(record: Union[Result, Lattice, _TransportGraph], electron_id: int =
         upsert._lattice_data(record, electron_id=electron_id)
         upsert._electron_data(record)
         app_log.debug("upsert complete")
-        insert_electron_dependency_data(record.dispatch_id, record.lattice)
+        upsert_electron_dependency_data(record.dispatch_id, record.lattice)
         app_log.debug("persist complete")
     if isinstance(record, Lattice):
         persist(record.transport_graph)
diff --git a/covalent_dispatcher/_db/write_result_to_db.py b/covalent_dispatcher/_db/write_result_to_db.py
index 35b57eae..b5ad3fff 100644
--- a/covalent_dispatcher/_db/write_result_to_db.py
+++ b/covalent_dispatcher/_db/write_result_to_db.py
@@ -270,6 +270,26 @@ def insert_electron_dependency_data(dispatch_id: str, lattice: LatticeClass):
     return electron_dependency_ids
 
 
+def upsert_electron_dependency_data(dispatch_id: str, lattice: LatticeClass):
+    """Update electron dependency data"""
+
+    # Insert electron dependency records if they don't exist
+    with workflow_db.session() as session:
+        electron_dependencies_exist = (
+            session.query(ElectronDependency, Electron, Lattice)
+            .where(
+                Electron.id == ElectronDependency.electron_id,
+                Electron.parent_lattice_id == Lattice.id,
+                Lattice.dispatch_id == dispatch_id,
+            )
+            .first()
+            is not None
+        )
+    app_log.debug("electron_dependencies_exist is " + str(electron_dependencies_exist))
+    if not electron_dependencies_exist:
+        insert_electron_dependency_data(dispatch_id=dispatch_id, lattice=lattice)
+
+
 def update_lattices_data(dispatch_id: str, **kwargs) -> None:
     """This function updates the lattices record."""
 


Test patch:
diff --git a/tests/covalent_dispatcher_tests/_db/write_result_to_db_test.py b/tests/covalent_dispatcher_tests/_db/write_result_to_db_test.py
index dbfcf39e..67420afd 100644
--- a/tests/covalent_dispatcher_tests/_db/write_result_to_db_test.py
+++ b/tests/covalent_dispatcher_tests/_db/write_result_to_db_test.py
@@ -54,6 +54,7 @@ from covalent_dispatcher._db.write_result_to_db import (
     update_electrons_data,
     update_lattice_completed_electron_num,
     update_lattices_data,
+    upsert_electron_dependency_data,
 )
 
 STORAGE_TYPE = "local"
@@ -447,6 +448,76 @@ def test_insert_electron_dependency_data(test_db, workflow_lattice, mocker):
             assert electron_dependency.updated_at is not None
 
 
+def test_upsert_electron_dependency_data(test_db, workflow_lattice, mocker):
+    """Test that upsert_electron_dependency_data is idempotent"""
+
+    mocker.patch("covalent_dispatcher._db.write_result_to_db.workflow_db", test_db)
+    cur_time = dt.now(timezone.utc)
+    insert_lattices_data(
+        **get_lattice_kwargs(created_at=cur_time, updated_at=cur_time, started_at=cur_time)
+    )
+
+    electron_ids = []
+    cur_time = dt.now(timezone.utc)
+    for (name, node_id) in [
+        ("task_1", 0),
+        (":parameter:1", 1),
+        (":parameter:2", 2),
+        (":sublattice:task_2", 3),
+        (":parameter:2", 4),
+    ]:
+        electron_kwargs = get_electron_kwargs(
+            name=name,
+            transport_graph_node_id=node_id,
+            created_at=cur_time,
+            updated_at=cur_time,
+        )
+        electron_ids.append(insert_electrons_data(**electron_kwargs))
+
+    mock_insert = mocker.patch(
+        "covalent_dispatcher._db.write_result_to_db.insert_electron_dependency_data"
+    )
+
+    upsert_electron_dependency_data(dispatch_id="dispatch_1", lattice=workflow_lattice)
+
+    mock_insert.assert_called_once_with(dispatch_id="dispatch_1", lattice=workflow_lattice)
+
+
+def test_upsert_electron_dependency_data_idempotent(test_db, workflow_lattice, mocker):
+    """Test that upsert_electron_dependency_data is idempotent"""
+
+    mocker.patch("covalent_dispatcher._db.write_result_to_db.workflow_db", test_db)
+    cur_time = dt.now(timezone.utc)
+    insert_lattices_data(
+        **get_lattice_kwargs(created_at=cur_time, updated_at=cur_time, started_at=cur_time)
+    )
+
+    electron_ids = []
+    cur_time = dt.now(timezone.utc)
+    for (name, node_id) in [
+        ("task_1", 0),
+        (":parameter:1", 1),
+        (":parameter:2", 2),
+        (":sublattice:task_2", 3),
+        (":parameter:2", 4),
+    ]:
+        electron_kwargs = get_electron_kwargs(
+            name=name,
+            transport_graph_node_id=node_id,
+            created_at=cur_time,
+            updated_at=cur_time,
+        )
+        electron_ids.append(insert_electrons_data(**electron_kwargs))
+
+    insert_electron_dependency_data(dispatch_id="dispatch_1", lattice=workflow_lattice)
+
+    mock_insert = mocker.patch(
+        "covalent_dispatcher._db.write_result_to_db.insert_electron_dependency_data"
+    )
+    upsert_electron_dependency_data(dispatch_id="dispatch_1", lattice=workflow_lattice)
+    mock_insert.assert_not_called()
+
+
 def test_update_lattices_data(test_db, mocker):
     """Test the function that updates the lattice data."""