diff options
author | Frederick Muriuki Muriithi | 2025-01-27 15:58:42 -0600 |
---|---|---|
committer | Frederick Muriuki Muriithi | 2025-01-27 16:04:00 -0600 |
commit | b2a5013e192c2116442253bf274f80fa2ae8dd58 (patch) | |
tree | 1c130138aca1a4a6b92bf7b78c94d30a2b66fabe /uploader | |
parent | 046cf17b9981fc1e75c35acf547cb143a52dc9d9 (diff) | |
download | gn-uploader-b2a5013e192c2116442253bf274f80fa2ae8dd58.tar.gz |
Enable editing of a phenotype's numeric values.
Diffstat (limited to 'uploader')
-rw-r--r-- | uploader/phenotypes/views.py | 115 | ||||
-rw-r--r-- | uploader/templates/phenotypes/edit-phenotype.html | 41 |
2 files changed, 135 insertions, 21 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"], diff --git a/uploader/templates/phenotypes/edit-phenotype.html b/uploader/templates/phenotypes/edit-phenotype.html index ddb5aae..260d032 100644 --- a/uploader/templates/phenotypes/edit-phenotype.html +++ b/uploader/templates/phenotypes/edit-phenotype.html @@ -129,7 +129,15 @@ <div class="row"> <h3 class="subheading">phenotype data</h3> - <form id="frm-edit-phenotype-data" class="form-horizontal"> + <form id="frm-edit-phenotype-data" + class="form-horizontal" + method="POST" + action="{{url_for( + 'species.populations.phenotypes.edit_phenotype_data', + species_id=species.SpeciesId, + population_id=population.Id, + dataset_id=dataset.Id, + xref_id=xref_id)}}"> <div style="max-height: 23.37em;overflow-y: scroll;"> <table class="table table-striped table-responsive table-form-table"> <thead style="position: sticky; top: 0;"> @@ -151,22 +159,31 @@ <td>{{item.StrainName}}</td> <td> <input type="text" - name="value::{{item.DataId}}::{{item.StrainId}}" + name="value-new::{{item.DataId}}::{{item.StrainId}}" value="{{item.value}}" - data-original-value="{{item.value}}" - class="form-control" /></td> + class="form-control" /> + <input type="hidden" + name="value-original::{{item.DataId}}::{{item.StrainId}}" + value="{{item.value}}" /></td> {%if population.Family in families_with_se_and_n%} <td> <input type="text" - name="se::{{item.DataId}}::{{item.StrainId}}" + name="se-new::{{item.DataId}}::{{item.StrainId}}" value="{{item.error or ''}}" data-original-value="{{item.error or ''}}" - class="form-control" /></td> - <td><input type="text" - name="n::{{item.DataId}}::{{item.StrainId}}" - value="{{item.count or ''}}" - data-original-value="{{item.count or "-"}}" - class="form-control" /></td> + class="form-control" /> + <input type="hidden" + name="se-original::{{item.DataId}}::{{item.StrainId}}" + value="{{item.error or ''}}" /></td> + <td> + <input type="text" + name="n-new::{{item.DataId}}::{{item.StrainId}}" + value="{{item.count or ''}}" + data-original-value="{{item.count or "-"}}" + class="form-control" /> + <input type="hidden" + name="n-original::{{item.DataId}}::{{item.StrainId}}" + value="{{item.count or ''}}" /></td> {%endif%} </tr> {%endfor%} @@ -177,7 +194,7 @@ <div class="col-sm-offset-2 col-sm-10"> <input type="submit" name="submit" - class="btn btn-primary not-implemented" + class="btn btn-primary" value="update data" /> </div> </div> |