about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--gn3/case_attributes.py124
1 files changed, 116 insertions, 8 deletions
diff --git a/gn3/case_attributes.py b/gn3/case_attributes.py
index e3e6dc6..cec4850 100644
--- a/gn3/case_attributes.py
+++ b/gn3/case_attributes.py
@@ -305,8 +305,97 @@ def __load_diff__(diff_filename):
     with open(diff_filename, encoding="utf8") as diff_file:
         return __parse_diff_json__(diff_file.read())
 
+def __apply_additions__(
+        cursor, inbredset_id: int, additions_diff) -> None:
+    """Apply additions: creates new case attributes."""
+    # TODO: Not tested... will probably fail.
+    cursor.execute(
+        "INSERT INTO CaseAttribute(InbredSetId, Name, Description) "
+        "VALUES (:inbredset_id, :name, :desc)",
+        tuple({
+            "inbredset_id": inbredset_id,
+            "name": diff["name"],
+            "desc": diff["description"]
+        } for diff in additions_diff))
+
+def __apply_modifications__(
+        cursor, inbredset_id: int, modifications_diff, fieldnames) -> None:
+    """Apply modifications: changes values of existing case attributes."""
+    cattrs = tuple(field for field in fieldnames if field != "Strain")
+
+    def __retrieve_changes__(acc, row):
+        orig = dict(zip(fieldnames, row["Original"].split(",")))
+        new = dict(zip(fieldnames, row["Current"].split(",")))
+        return acc + tuple({
+            "Strain": new["Strain"],
+            cattr: new[cattr]
+        } for cattr in cattrs if new[cattr] != orig[cattr])
+
+    new_rows = reduce(__retrieve_changes__, modifications_diff, tuple())
+    strain_names = tuple({row["Strain"] for row in new_rows})
+    cursor.execute("SELECT Id AS StrainId, Name AS StrainName FROM Strain "
+                   f"WHERE Name IN ({', '.join(['%s'] * len(strain_names))})",
+                   strain_names)
+    strain_ids = {
+        row["StrainName"]: int(row["StrainId"])
+        for row in cursor.fetchall()}
+
+    cursor.execute("SELECT CaseAttributeId, Name AS CaseAttributeName "
+                   "FROM CaseAttribute WHERE InbredSetId=%s "
+                   f"AND Name IN ({', '.join(['%s'] * len(cattrs))})",
+                   (inbredset_id,) + cattrs)
+    cattr_ids = {
+        row["CaseAttributeName"]: row["CaseAttributeId"]
+        for row in cursor.fetchall()
+    }
+
+    cursor.executemany(
+        "INSERT INTO CaseAttributeXRefNew"
+        "(InbredSetId, StrainId, CaseAttributeId, Value) "
+        "VALUES(%(isetid)s, %(strainid)s, %(cattrid)s, %(value)s) "
+        "ON DUPLICATE KEY UPDATE Value=VALUES(value)",
+        tuple(
+            {
+                "isetid": inbredset_id,
+                "strainid": strain_ids[row["Strain"]],
+                "cattrid": cattr_ids[cattr],
+                "value": row[cattr]
+            }
+            for cattr in cattrs for row in new_rows
+            if bool(row.get(cattr, "").strip())))
+    cursor.executemany(
+        "DELETE FROM CaseAttributeXRefNew WHERE "
+        "InbredSetId=%(isetid)s AND StrainId=%(strainid)s "
+        "AND CaseAttributeId=%(cattrid)s",
+        tuple(
+            {
+                "isetid": inbredset_id,
+                "strainid": strain_ids[row["Strain"]],
+                "cattrid": cattr_ids[cattr]
+            }
+            for row in new_rows
+            for cattr in (key for key in row.keys() if key != "Strain")
+            if not bool(row[cattr].strip())))
+
+def __apply_deletions__(
+        cursor, inbredset_id: int, deletions_diff) -> None:
+    """Apply deletions: delete existing case attributes and their values."""
+    # TODO: Not tested... will probably fail.
+    params = tuple({
+        "inbredset_id": inbredset_id,
+        "case_attribute_id": diff["case_attribute_id"]
+    } for diff in deletions_diff)
+    cursor.executemany(
+        "DELETE FROM CaseAttributeXRefNew WHERE "
+        "InbredSetId=:inbredset_id AND CaseAttributeId=:case_attribute_id",
+        params)
+    cursor.executemany(
+        "DELETE FROM CaseAttribute WHERE "
+        "InbredSetId=:inbredset_id AND CaseAttributeId=:case_attribute_id",
+        params)
+
 def __apply_diff__(
-        conn: Connection, inbredset_id: int, user: User, diff_filename) -> None:
+        conn: Connection, inbredset_id: int, user: User, diff_filename, the_diff) -> None:
     """
     Apply the changes in the diff at `diff_filename` to the data in the database
     if the user has appropriate privileges.
@@ -314,7 +403,17 @@ def __apply_diff__(
     required_access(
         inbredset_id, ("system:inbredset:edit-case-attribute",
                        "system:inbredset:apply-case-attribute-edit"))
-    raise NotImplementedError
+    diffs = the_diff["diff"]
+    with conn.cursor(cursorclass=DictCursor) as cursor:
+        # __apply_additions__(cursor, inbredset_id, diffs["Additions"])
+        __apply_modifications__(
+            cursor, inbredset_id, diffs["Modifications"], the_diff["fieldnames"])
+        # __apply_deletions__(cursor, inbredset_id, diffs["Deletions"])
+        __save_diff__(conn, the_diff, EditStatus.approved)
+        new_path = Path(
+            diff_filename.parent,
+            f"{diff_filename.stem}-approved{diff_filename.suffix}")
+        os.rename(diff_filename, new_path)
 
 def __reject_diff__(conn: Connection,
                     inbredset_id: int,
@@ -370,9 +469,11 @@ def edit_case_attributes(inbredset_id: int) -> Response:
                         fieldnames,
                         __process_orig_data__(
                             fieldnames,
-                            __case_attribute_values_by_inbred_set__(conn, inbredset_id),
+                            __case_attribute_values_by_inbred_set__(
+                                conn, inbredset_id),
                             __inbredset_strains__(conn, inbredset_id)),
-                        __process_edit_data__(fieldnames, request.json["edit-data"]))
+                        __process_edit_data__(
+                            fieldnames, request.json["edit-data"]))
                 },
                 Path(current_app.config.get("TMPDIR"), CATTR_DIFFS_DIR))
         except NoDiffError as _nde:
@@ -385,7 +486,8 @@ def edit_case_attributes(inbredset_id: int) -> Response:
             return response
 
         try:
-            __apply_diff__(conn, user, diff_filename)
+            __apply_diff__(
+                conn, inbredset_id, user, diff_filename, __load_diff__(diff_filename))
             return jsonify({
                 "diff-status": "applied",
                 "message": ("The changes to the case-attributes have been "
@@ -451,12 +553,18 @@ def list_diffs(inbredset_id: int) -> Response:
     return resp
 
 @caseattr.route("/approve/<path:filename>", methods=["POST"])
-def approve_case_attributes_diff(inbredset_id: int) -> Response:
+def approve_case_attributes_diff(filename: str) -> Response:
     """Approve the changes to the case attributes in the diff."""
+    diff_dir = Path(current_app.config.get("TMPDIR"), CATTR_DIFFS_DIR)
+    diff_filename = Path(diff_dir, filename)
+    the_diff = __load_diff__(diff_filename)
     with (require_oauth.acquire("profile resource") as the_token,
           database_connection(current_app.config["SQL_URI"]) as conn):
-        __apply_diff__(conn, inbredset_id, the_token.user, diff_filename)
-        raise NotImplementedError
+        __apply_diff__(conn, the_diff["inbredset_id"], the_token.user, diff_filename, the_diff)
+        return jsonify({
+            "message": "Applied the diff successfully.",
+            "diff_filename": diff_filename.name
+        })
 
 @caseattr.route("/reject/<path:filename>", methods=["POST"])
 def reject_case_attributes_diff(filename: str) -> Response: