about summary refs log tree commit diff
path: root/uploader/phenotypes
diff options
context:
space:
mode:
authorFrederick Muriuki Muriithi2025-01-27 15:58:42 -0600
committerFrederick Muriuki Muriithi2025-01-27 16:04:00 -0600
commitb2a5013e192c2116442253bf274f80fa2ae8dd58 (patch)
tree1c130138aca1a4a6b92bf7b78c94d30a2b66fabe /uploader/phenotypes
parent046cf17b9981fc1e75c35acf547cb143a52dc9d9 (diff)
downloadgn-uploader-b2a5013e192c2116442253bf274f80fa2ae8dd58.tar.gz
Enable editing of a phenotype's numeric values.
Diffstat (limited to 'uploader/phenotypes')
-rw-r--r--uploader/phenotypes/views.py115
1 files changed, 106 insertions, 9 deletions
diff --git a/uploader/phenotypes/views.py b/uploader/phenotypes/views.py
index b53b296..d283e47 100644
--- a/uploader/phenotypes/views.py
+++ b/uploader/phenotypes/views.py
@@ -622,6 +622,88 @@ def update_phenotype_metadata(conn, metadata: dict):
         return cursor.rowcount
 
 
+def update_phenotype_values(conn, values):
+    with conn.cursor() as cursor:
+        cursor.executemany(
+            "UPDATE PublishData SET value=%(new)s "
+            "WHERE Id=%(data_id)s AND StrainId=%(strain_id)s",
+            tuple(item for item in values if item["new"] is not None))
+        cursor.executemany(
+            "DELETE FROM PublishData "
+            "WHERE Id=%(data_id)s AND StrainId=%(strain_id)s",
+            tuple(item for item in values if item["new"] is None))
+        return len(values)
+    return 0
+
+
+def update_phenotype_se(conn, serrs):
+    with conn.cursor() as cursor:
+        cursor.executemany(
+            "INSERT INTO PublishSE(DataId, StrainId, error) "
+            "VALUES(%(data_id)s, %(strain_id)s, %(new)s) "
+            "ON DUPLICATE KEY UPDATE error=VALUES(error)",
+            tuple(item for item in serrs if item["new"] is not None))
+        cursor.executemany(
+            "DELETE FROM PublishSE "
+            "WHERE DataId=%(data_id)s AND StrainId=%(strain_id)s",
+            tuple(item for item in serrs if item["new"] is None))
+        return len(serrs)
+    return 0
+
+
+def update_phenotype_n(conn, counts):
+    with conn.cursor() as cursor:
+        cursor.executemany(
+            "INSERT INTO NStrain(DataId, StrainId, count) "
+            "VALUES(%(data_id)s, %(strain_id)s, %(new)s) "
+            "ON DUPLICATE KEY UPDATE count=VALUES(count)",
+            tuple(item for item in counts if item["new"] is not None))
+        cursor.executemany(
+            "DELETE FROM NStrain "
+            "WHERE DataId=%(data_id)s AND StrainId=%(strain_id)s",
+            tuple(item for item in counts if item["new"] is None))
+        return len(counts)
+
+    return 0
+
+
+def update_phenotype_data(conn, data: dict):
+    """Update the numeric data for a phenotype."""
+    def __organise_by_dataid_and_strainid__(acc, current):
+        _key, dataid, strainid = current[0].split("::")
+        _keysrc, _keytype = _key.split("-")
+        newkey = f"{dataid}::{strainid}"
+        newitem = acc.get(newkey, {})
+        newitem[_keysrc] = newitem.get(_keysrc, {})
+        newitem[_keysrc][_keytype] = current[1]
+        return {**acc, newkey: newitem}
+
+    def __separate_items__(acc, row):
+        key, val = row
+        return ({**acc[0], key: {**val["value"], "changed?": (not val["value"]["new"] == val["value"]["original"])}},
+                {**acc[1], key: {**val["se"]   , "changed?": (not val["se"]["new"] == val["se"]["original"])}},
+                {**acc[2], key: {**val["n"]    , "changed?": (not val["n"]["new"] == val["n"]["original"])}})
+
+    values, serrs, counts = tuple(
+        tuple({
+            "data_id": row[0].split("::")[0],
+            "strain_id": row[0].split("::")[1],
+            "new": row[1]["new"]
+        } for row in item)
+        for item in (
+                filter(lambda val: val[1]["changed?"], item.items())
+                for item in reduce(
+                        __separate_items__,
+                        reduce(__organise_by_dataid_and_strainid__,
+                               data.items(),
+                               {}).items(),
+                        ({}, {}, {}))))
+
+    return (update_phenotype_values(conn, values),
+            update_phenotype_se(conn, serrs),
+            update_phenotype_n(conn, counts))
+
+
 @phenotypesbp.route(
     "<int:species_id>/populations/<int:population_id>/phenotypes/datasets"
     "/<int:dataset_id>/phenotype/<int:xref_id>/edit",
@@ -706,26 +788,41 @@ def edit_phenotype_data(
             ).either(__fail__, lambda args: __render__(**args))
 
         ## POST
+        _change = False
         match request.form.get("submit", "invalid-action"):
             case "update basic metadata":
-                if update_phenotype_metadata(conn, {
+                _change = update_phenotype_metadata(conn, {
+                    key: value.strip() if bool(value.strip()) else None
+                    for key, value in request.form.items()
+                    if key not in ("submit",)
+                })
+                msg = "Basic metadata was updated successfully."
+            case "update data":
+                _update = update_phenotype_data(conn, {
                         key: value.strip() if bool(value.strip()) else None
                         for key, value in request.form.items()
                         if key not in ("submit",)
-                }):
-                    flash("Basic metadata updated.", "alert-success")
-                else:
-                    flash("There were no changes to the basic metadata",
-                          "alert-info")
-            case "update data":
-                flash("NOT IMPLEMENTED: Would update data", "alert-success")
+                })
+                msg = (f"{_update[0]} value rows, {_update[1]} standard-error "
+                       f"rows and {_update[2]} 'N' rows were updated.")
+                _change = any(item != 0 for item in _update)
             case "update publication":
                 flash("NOT IMPLEMENTED: Would update publication data.", "alert-success")
             case _:
                 flash("Invalid phenotype editing action.", "alert-danger")
 
+        if _change:
+            flash(msg, "alert-success")
+            return redirect(url_for(
+                "species.populations.phenotypes.view_phenotype",
+                species_id=species["SpeciesId"],
+                population_id=population["Id"],
+                dataset_id=dataset["Id"],
+                xref_id=xref_id))
+
+        flash("No change was made by the user.", "alert-info")
         return redirect(url_for(
-            "species.populations.phenotypes.view_phenotype",
+            "species.populations.phenotypes.edit_phenotype_data",
             species_id=species["SpeciesId"],
             population_id=population["Id"],
             dataset_id=dataset["Id"],