about summary refs log tree commit diff
diff options
context:
space:
mode:
authorBonfaceKilz2022-05-25 21:36:03 +0300
committerBonfaceKilz2022-05-27 15:02:25 +0300
commit9fa398d57e26c8ece01bce7a4f67545449e94821 (patch)
tree0c283c3e6b32ea02bd363ceb54151eeca5de887f
parent99a7781b6aab87c76e5bb2a67b421e3240d7c562 (diff)
downloadgenenetwork3-9fa398d57e26c8ece01bce7a4f67545449e94821.tar.gz
Move sql for modifying case-attributes from gn2 to gn3
-rw-r--r--gn3/db/case_attributes.py132
-rw-r--r--gn3/db/sample_data.py10
-rw-r--r--tests/unit/db/test_case_attributes.py205
3 files changed, 338 insertions, 9 deletions
diff --git a/gn3/db/case_attributes.py b/gn3/db/case_attributes.py
new file mode 100644
index 0000000..8eb2bf6
--- /dev/null
+++ b/gn3/db/case_attributes.py
@@ -0,0 +1,132 @@
+"""Module that contains functions for editing case-attribute data"""
+from typing import Any, Optional, Tuple
+
+import json
+import MySQLdb
+
+
+def get_case_attributes(conn) -> Optional[Tuple]:
+    """Get all the case attributes from the database."""
+    with conn.cursor() as cursor:
+        cursor.execute("SELECT Id, Name, Description FROM CaseAttribute")
+        return cursor.fetchall()
+
+
+def get_unreviewed_diffs(conn: Any) -> Optional[tuple]:
+    """Fetch all case attributes in GN"""
+    with conn.cursor() as cursor:
+        cursor.execute(
+            "SELECT id, editor, json_diff_data FROM "
+            "caseattributes_audit WHERE status = 'review'"
+        )
+        return cursor.fetchall()
+
+
+def insert_case_attribute_audit(
+    conn: Any, status: str, author: str, data: str
+) -> int:
+    """Update the case_attribute_audit table"""
+    rowcount = 0
+    try:
+        with conn.cursor() as cursor:
+            cursor.execute(
+                "INSERT INTO caseattributes_audit "
+                "(status, editor, json_diff_data) "
+                "VALUES (%s, %s, %s)",
+                (status, author, data),
+            )
+            rowcount = cursor.rowcount
+            conn.commit()
+    except Exception as _e:
+        conn.rollback()
+        raise MySQLdb.Error(_e) from _e
+    return rowcount
+
+
+def reject_case_attribute(conn: Any, case_attr_audit_id: int) -> int:
+    """Given the id of the json_diff in the case_attribute_audit table, reject
+    it"""
+    rowcount = 0
+    try:
+        with conn.cursor() as cursor:
+            cursor.execute(
+                "UPDATE caseattributes_audit SET "
+                "status = 'rejected' WHERE id = %s",
+                (case_attr_audit_id,),
+            )
+            rowcount = cursor.rowcount
+            conn.commit()
+    except Exception as _e:
+        conn.rollback()
+        raise MySQLdb.Error(_e) from _e
+    return rowcount
+
+
+def approve_case_attribute(conn: Any, case_attr_audit_id: int) -> int:
+    """Given the id of the json_diff in the case_attribute_audit table,
+    approve it
+
+    """
+    rowcount = 0
+    try:
+        with conn.cursor() as cursor:
+            cursor.execute(
+                "SELECT json_diff_data FROM caseattributes_audit "
+                "WHERE id = %s",
+                (case_attr_audit_id,),
+            )
+            diff_data = cursor.fetchone()
+            if diff_data:
+                diff_data = json.loads(diff_data[0])
+                # Insert (Most Important)
+                if diff_data.get("Insert"):
+                    data = diff_data.get("Insert")
+                    cursor.execute(
+                        "INSERT INTO CaseAttribute "
+                        "(Name, Description) VALUES "
+                        "(%s, %s)",
+                        (
+                            data.get("name").strip(),
+                            data.get("description").strip(),
+                        ),
+                    )
+                # Delete
+                elif diff_data.get("Deletion"):
+                    data = diff_data.get("Deletion")
+                    cursor.execute(
+                        "DELETE FROM CaseAttribute " "WHERE Id = %s",
+                        (data.get("id"),),
+                    )
+                # Modification
+                elif diff_data.get("Modification"):
+                    data = diff_data.get("Modification")
+                    if desc_ := data.get("description"):
+                        cursor.execute(
+                            "UPDATE CaseAttribute SET "
+                            "Description = %s WHERE Id = %s",
+                            (
+                                desc_.get("Current"),
+                                diff_data.get("id"),
+                            ),
+                        )
+                    if name_ := data.get("name"):
+                        cursor.execute(
+                            "UPDATE CaseAttribute SET "
+                            "Name = %s WHERE Id = %s",
+                            (
+                                name_.get("Current"),
+                                diff_data.get("id"),
+                            ),
+                        )
+                if cursor.rowcount:
+                    cursor.execute(
+                        "UPDATE caseattributes_audit SET "
+                        "status = 'approved' WHERE id = %s",
+                        (case_attr_audit_id,),
+                    )
+            rowcount = cursor.rowcount
+            conn.commit()
+    except Exception as _e:
+        conn.rollback()
+        raise MySQLdb.Error(_e) from _e
+    return rowcount
diff --git a/gn3/db/sample_data.py b/gn3/db/sample_data.py
index f79ce97..d29f6c8 100644
--- a/gn3/db/sample_data.py
+++ b/gn3/db/sample_data.py
@@ -1,8 +1,7 @@
 """Module containing functions that work with sample data"""
-from typing import Any, Tuple, Dict, Callable, Optional
+from typing import Any, Tuple, Dict, Callable
 
 import re
-import collections
 import MySQLdb
 
 from gn3.csvcmp import extract_strain_name
@@ -423,10 +422,3 @@ def insert_sample_data(
     except Exception as _e:
         conn.rollback()
         raise MySQLdb.Error(_e) from _e
-
-
-def get_case_attributes(conn) -> Optional[Tuple]:
-    """Get all the case attributes from the database."""
-    with conn.cursor() as cursor:
-        cursor.execute("SELECT Id, Name, Description FROM CaseAttribute")
-        return cursor.fetchall()
diff --git a/tests/unit/db/test_case_attributes.py b/tests/unit/db/test_case_attributes.py
new file mode 100644
index 0000000..175249b
--- /dev/null
+++ b/tests/unit/db/test_case_attributes.py
@@ -0,0 +1,205 @@
+"""Test cases for gn3.db.case_attributes.py"""
+
+import pytest
+from pytest_mock import MockFixture
+from gn3.db.case_attributes import get_unreviewed_diffs
+from gn3.db.case_attributes import get_case_attributes
+from gn3.db.case_attributes import insert_case_attribute_audit
+from gn3.db.case_attributes import approve_case_attribute
+from gn3.db.case_attributes import reject_case_attribute
+
+
+@pytest.mark.unit_test
+def test_get_case_attributes(mocker: MockFixture) -> None:
+    """Test that all the case attributes are fetched correctly"""
+    mock_conn = mocker.MagicMock()
+    with mock_conn.cursor() as cursor:
+        cursor.fetchall.return_value = (
+            (1, "Condition", None),
+            (2, "Tissue", None),
+            (3, "Age", "Cum sociis natoque penatibus et magnis dis"),
+            (4, "Condition", "Description A"),
+            (5, "Condition", "Description B"),
+        )
+        results = get_case_attributes(mock_conn)
+        cursor.execute.assert_called_once_with(
+            "SELECT Id, Name, Description FROM CaseAttribute"
+        )
+        assert results == (
+            (1, "Condition", None),
+            (2, "Tissue", None),
+            (3, "Age", "Cum sociis natoque penatibus et magnis dis"),
+            (4, "Condition", "Description A"),
+            (5, "Condition", "Description B"),
+        )
+
+
+@pytest.mark.unit_test
+def test_get_unreviewed_diffs(mocker: MockFixture) -> None:
+    """Test that the correct query is called when fetching unreviewed
+    case-attributes diff"""
+    mock_conn = mocker.MagicMock()
+    with mock_conn.cursor() as cursor:
+        _ = get_unreviewed_diffs(mock_conn)
+        cursor.fetchall.return_value = ((1, "editor", "diff_data_1"),)
+        cursor.execute.assert_called_once_with(
+            "SELECT id, editor, json_diff_data FROM "
+            "caseattributes_audit WHERE status = 'review'"
+        )
+
+
+@pytest.mark.unit_test
+def test_insert_case_attribute_audit(mocker: MockFixture) -> None:
+    """Test that the updating case attributes uses the correct query"""
+    mock_conn = mocker.MagicMock()
+    with mock_conn.cursor() as cursor:
+        _ = insert_case_attribute_audit(
+            mock_conn, status="review", author="Author", data="diff_data"
+        )
+        cursor.execute.assert_called_once_with(
+            "INSERT INTO caseattributes_audit "
+            "(status, editor, json_diff_data) "
+            "VALUES (%s, %s, %s)",
+            ("review", "Author", "diff_data"),
+        )
+
+
+@pytest.mark.unit_test
+def test_reject_case_attribute(mocker: MockFixture) -> None:
+    """Test rejecting a case-attribute"""
+    mock_conn = mocker.MagicMock()
+    with mock_conn.cursor() as cursor:
+        _ = reject_case_attribute(
+            mock_conn,
+            case_attr_audit_id=1,
+        )
+        cursor.execute.assert_called_once_with(
+            "UPDATE caseattributes_audit SET "
+            "status = 'rejected' WHERE id = %s",
+            (1,),
+        )
+
+
+@pytest.mark.unit_test
+def test_approve_inserting_case_attribute(mocker: MockFixture) -> None:
+    """Test approving inserting a case-attribute"""
+    mock_conn = mocker.MagicMock()
+    with mock_conn.cursor() as cursor:
+        type(cursor).rowcount = 1
+        cursor.fetchone.return_value = (
+            """
+        {"Insert": {"name": "test", "description": "Random Description"}}
+        """,
+        )
+        _ = approve_case_attribute(
+            mock_conn,
+            case_attr_audit_id=3,
+        )
+        calls = [
+            mocker.call(
+                "SELECT json_diff_data FROM caseattributes_audit "
+                "WHERE id = %s",
+                (3,),
+            ),
+            mocker.call(
+                "INSERT INTO CaseAttribute "
+                "(Name, Description) VALUES "
+                "(%s, %s)",
+                (
+                    "test",
+                    "Random Description",
+                ),
+            ),
+            mocker.call(
+                "UPDATE caseattributes_audit SET "
+                "status = 'approved' WHERE id = %s",
+                (3,),
+            ),
+        ]
+        cursor.execute.assert_has_calls(calls, any_order=False)
+
+
+@pytest.mark.unit_test
+def test_approve_deleting_case_attribute(mocker: MockFixture) -> None:
+    """Test deleting a case-attribute"""
+    mock_conn = mocker.MagicMock()
+    with mock_conn.cursor() as cursor:
+        type(cursor).rowcount = 1
+        cursor.fetchone.return_value = (
+            """
+        {"Deletion": {"id": "12", "name": "test", "description": ""}}
+        """,
+        )
+        _ = approve_case_attribute(
+            mock_conn,
+            case_attr_audit_id=3,
+        )
+        calls = [
+            mocker.call(
+                "SELECT json_diff_data FROM caseattributes_audit "
+                "WHERE id = %s",
+                (3,),
+            ),
+            mocker.call("DELETE FROM CaseAttribute " "WHERE Id = %s", ("12",)),
+            mocker.call(
+                "UPDATE caseattributes_audit SET "
+                "status = 'approved' WHERE id = %s",
+                (3,),
+            ),
+        ]
+        cursor.execute.assert_has_calls(calls, any_order=False)
+
+
+@pytest.mark.unit_test
+def test_approve_modifying_case_attribute(mocker: MockFixture) -> None:
+    """Test modifying a case-attribute"""
+    mock_conn = mocker.MagicMock()
+    with mock_conn.cursor() as cursor:
+        type(cursor).rowcount = 1
+        cursor.fetchone.return_value = (
+            """
+{
+  "id": "12",
+  "Modification": {
+    "description": {
+      "Current": "Test",
+      "Original": "A"
+    },
+    "name": {
+      "Current": "Height (A)",
+      "Original": "Height"
+    }
+  }
+}""",
+        )
+        _ = approve_case_attribute(
+            mock_conn,
+            case_attr_audit_id=3,
+        )
+        calls = [
+            mocker.call(
+                "SELECT json_diff_data FROM caseattributes_audit "
+                "WHERE id = %s",
+                (3,),
+            ),
+            mocker.call(
+                "UPDATE CaseAttribute SET " "Description = %s WHERE Id = %s",
+                (
+                    "Test",
+                    "12",
+                ),
+            ),
+            mocker.call(
+                "UPDATE CaseAttribute SET " "Name = %s WHERE Id = %s",
+                (
+                    "Height (A)",
+                    "12",
+                ),
+            ),
+            mocker.call(
+                "UPDATE caseattributes_audit SET "
+                "status = 'approved' WHERE id = %s",
+                (3,),
+            ),
+        ]
+        cursor.execute.assert_has_calls(calls, any_order=False)