diff options
-rw-r--r-- | wqflask/wqflask/server_side.py | 4 | ||||
-rw-r--r-- | wqflask/wqflask/templates/edit_trait.html | 72 | ||||
-rw-r--r-- | wqflask/wqflask/views.py | 155 |
3 files changed, 168 insertions, 63 deletions
diff --git a/wqflask/wqflask/server_side.py b/wqflask/wqflask/server_side.py index 7f68efad..e661c407 100644 --- a/wqflask/wqflask/server_side.py +++ b/wqflask/wqflask/server_side.py @@ -9,13 +9,11 @@ class ServerSideTable: the client-side and reduces the size of data interchanged. Usage: - ServerSideTable(table_data, request_values) + ServerSideTable(rows_count, table_rows, header_data_names, request_values) where, - `table_data` must have data members `rows_count` as number of rows in the table, `table_rows` as data rows of the table, `header_data_names` as headers names of the table. - `request_values` must have request arguments values including the DataTables server-side processing arguments. diff --git a/wqflask/wqflask/templates/edit_trait.html b/wqflask/wqflask/templates/edit_trait.html index f01c497c..c6668683 100644 --- a/wqflask/wqflask/templates/edit_trait.html +++ b/wqflask/wqflask/templates/edit_trait.html @@ -5,10 +5,53 @@ Edit Trait for Published Database Submit Trait | Reset -{% if publish_xref.comments %} -<h2>Update History:</h2> +{% if diff %} -{{ publish_xref.comments }} +<div class="container"> + <details class="col-sm-12 col-md-10 col-lg-12"> + <summary> + <h2>Update History</h2> + </summary> + <table class="table"> + <tbody> + <tr> + <th>Timestamp</th> + <th>Editor</th> + <th>Field</th> + <th>Diff</th> + </tr> + {% set ns = namespace(display_cell=True) %} + + {% for timestamp, group in diff %} + {% set ns.display_cell = True %} + {% for i in group %} + <tr> + {% if ns.display_cell and i.timestamp == timestamp %} + + {% set author = i.author %} + {% set field = i.diff.field %} + {% set timestamp_ = i.timestamp %} + + {% else %} + + {% set author = "" %} + {% set field = "" %} + {% set timestamp_ = "" %} + + {% endif %} + <td>{{ timestamp_ }}</td> + <td>{{ author }} {{ display_cell }}</td> + <td>{{ field }}</td> + <td><pre>{{ i.diff.diff }}</pre></td> + {% set ns.display_cell = False %} + </tr> + {% endfor %} + {% endfor %} + </tbody> + </table> + </details> + +</div> {% endif %} @@ -23,12 +66,14 @@ Submit Trait | Reset character in this field. --> <div class="col-sm-8"> <textarea name="pubmed-id" class="form-control" rows="1">{{ publish_xref.publication_id |default('', true) }}</textarea> + <input name="old_id_" class="changed" type="hidden" value="{{ publish_xref.publication_id |default('', true) }}"/> </div> </div> <div class="form-group"> <label for="pre-pub-desc" class="col-sm-2 control-label">Pre Publication Description:</label> <div class="col-sm-8"> <textarea name="pre-pub-desc" class="form-control" rows="4">{{ phenotype.pre_pub_description |default('', true) }}</textarea> + <input name="old_pre_pub_description" class="changed" type="hidden" value="{{ phenotype.pre_pub_description |default('', true) }}"/> </div> <!-- If the PubMed ID is entered, the Post Publication Description will be shown to all users. If there is no PubMed ID, and the @@ -39,18 +84,21 @@ Submit Trait | Reset <label for="post-pub-desc" class="col-sm-2 control-label">Post Publication Description:</label> <div class="col-sm-8"> <textarea name="post-pub-desc" class="form-control" rows="4">{{ phenotype.post_pub_description |default('', true) }}</textarea> + <input name="old_post_pub_description" class="changed" type="hidden" value="{{ phenotype.post_pub_description |default('', true) }}"/> </div> </div> <div class="form-group"> <label for="orig-desc" class="col-sm-2 control-label">Original Description:</label> <div class="col-sm-8"> <textarea name="orig-desc" class="form-control" rows="4">{{ phenotype.original_description |default('', true) }}</textarea> + <input name="old_original_description" class="changed" type="hidden" value="{{ phenotype.original_description |default('', true) }}"/> </div> </div> <div class="form-group"> <label for="units" class="col-sm-2 control-label">Units:</label> <div class="col-sm-8"> <textarea name="units" class="form-control" rows="1">{{ phenotype.units |default('', true) }}</textarea> + <input name="old_units" class="changed" type="hidden" value="{{ phenotype.units |default('', true) }}"/> </div> </div> <div class="form-group"> @@ -59,6 +107,7 @@ Submit Trait | Reset </label> <div class="col-sm-8"> <textarea name="pre-pub-abbrev" class="form-control" rows="1">{{ phenotype.pre_pub_abbreviation |default('', true) }}</textarea> + <input name="old_pre_pub_abbreviation" class="changed" type="hidden" value="{{ phenotype.pre_pub_abbreviation |default('', true) }}"/> </div> </div> <div class="form-group"> @@ -67,6 +116,7 @@ Submit Trait | Reset </label> <div class="col-sm-8"> <textarea name="post-pub-abbrev" class="form-control" rows="1">{{ phenotype.post_pub_abbreviation |default('', true) }}</textarea> + <input name="old_post_pub_abbreviation" class="changed" type="hidden" value="{{ phenotype.post_pub_abbreviation |default('', true) }}"/> </div> </div> <div class="form-group"> @@ -75,6 +125,7 @@ Submit Trait | Reset </label> <div class="col-sm-8"> <textarea name="labcode" class="form-control" rows="1">{{ phenotype.lab_code |default('', true) }}</textarea> + <input name="old_lab_code" class="changed" type="hidden" value="{{ phenotype.lab_code |default('', true) }}"/> </div> </div> <div class="form-group"> @@ -83,68 +134,79 @@ Submit Trait | Reset </label> <div class="col-sm-8"> <textarea name="submitter" class="form-control" rows="1">{{ phenotype.submitter |default('', true) }}</textarea> + <input name="old_submitter" class="changed" type="hidden" value="{{ phenotype.submitter |default('', true) }}"/> </div> </div> <div class="form-group"> <label for="owner" class="col-sm-2 control-label">Owner:</label> <div class="col-sm-8"> <textarea name="owner" class="form-control" rows="1">{{ phenotype.owner |default('', true) }}</textarea> + <input name="old_owner" class="changed" type="hidden" value="{{ phenotype.owner |default('', true) }}"/> </div> </div> <div class="form-group"> - <label for="owner" class="col-sm-2 control-label"> + <label for="authorized-users" class="col-sm-2 control-label"> Authorized Users: </label> <div class="col-sm-8"> - <textarea name="owner" class="form-control" rows="1">{{ phenotype.authorized_users |default('', true) }}</textarea> + <textarea name="authorized-users" class="form-control" rows="1">{{ phenotype.authorized_users |default('', true) }}</textarea> + <input name="old_authorized_users" class="changed" type="hidden" value="{{ phenotype.authorized_users |default('', true) }}"/> </div> </div> <div class="form-group"> <label for="authors" class="col-sm-2 control-label">Authors:</label> <div class="col-sm-8"> <textarea name="authors" class="form-control" rows="2">{{ publication.authors |default('', true) }}</textarea> + <input name="old_authors" class="changed" type="hidden" value="{{ publication.authors |default('', true) }}"/> </div> </div> <div class="form-group"> <label for="title" class="col-sm-2 control-label">Title:</label> <div class="col-sm-8"> <textarea name="title" class="form-control" rows="2">{{ publication.title |default('', true) }}</textarea> + <input name="old_title" class="changed" type="hidden" value="{{ publication.title |default('', true) }}"/> </div> </div> <div class="form-group"> <label for="abstract" class="col-sm-2 control-label">Abstract:</label> <div class="col-sm-8"> <textarea name="abstract" class="form-control" rows="6">{{ publication.abstract |default('', true) }}</textarea> + <input name="old_abstract" class="changed" type="hidden" value="{{ publication.abstract |default('', true) }}"/> </div> </div> <div class="form-group"> <label for="journal" class="col-sm-2 control-label">Journal:</label> <div class="col-sm-8"> <textarea name="journal" class="form-control" rows="1">{{ publication.journal |default('', true) }}</textarea> + <input name="old_journal" class="changed" type="hidden" value="{{ publication.journal_ |default('', true) }}"/> </div> </div> <div class="form-group"> <label for="pages" class="col-sm-2 control-label">Pages:</label> <div class="col-sm-8"> <textarea name="pages" class="form-control" rows="1">{{ publication.pages |default('', true) }}</textarea> + <input name="old_pages" class="changed" type="hidden" value="{{ publication.pages |default('', true) }}"/> </div> </div> <div class="form-group"> <label for="month" class="col-sm-2 control-label">Month:</label> <div class="col-sm-8"> <textarea name="month" class="form-control" rows="1">{{ publication.month |default('', true) }}</textarea> + <input name="old_month" class="changed" type="hidden" value="{{ publication.month |default('', true) }}"/> </div> </div> <div class="form-group"> <label for="year" class="col-sm-2 control-label">Year:</label> <div class="col-sm-8"> <textarea name="year" class="form-control" rows="1">{{ publication.year |default('', true) }}</textarea> + <input name="old_year" class="changed" type="hidden" value="{{ publication.year |default('', true) }}"/> </div> </div> <div class="form-group"> <label for="sequence" class="col-sm-2 control-label">Sequence:</label> <div class="col-sm-8"> <textarea name="sequence" class="form-control" rows="6">{{ publish_xref.sequence |default('', true) }}</textarea> + <input name="old_sequence" class="changed" type="hidden" value="{{ publication.sequence |default('', true) }}"/> </div> </div> <div class="controls" style="display:block; margin-left: 40%; margin-right: 20%;"> diff --git a/wqflask/wqflask/views.py b/wqflask/wqflask/views.py index 2c5b6c6b..ebe5303a 100644 --- a/wqflask/wqflask/views.py +++ b/wqflask/wqflask/views.py @@ -1,33 +1,41 @@ """Main routing table for GN2""" -import traceback # for error page -import os # for error gifs -import random # for random error gif -import datetime # for errors -import sys +import MySQLdb +import array +import base64 import csv -import simplejson as json -import xlsxwriter +import difflib +import datetime +import flask import io # Todo: Use cStringIO? -import MySQLdb - -from zipfile import ZipFile, ZIP_DEFLATED +import json import numpy as np +import os import pickle as pickle +import random +import sqlalchemy +import sys +import traceback import uuid +import xlsxwriter + +from itertools import groupby +from collections import namedtuple +from zipfile import ZipFile +from zipfile import ZIP_DEFLATED -import flask -import base64 -import array -import sqlalchemy from wqflask import app +from gn3.db import diff_from_dict +from gn3.db import fetchall from gn3.db import fetchone +from gn3.db import insert from gn3.db import update +from gn3.db.metadata_audit import MetadataAudit from gn3.db.phenotypes import Phenotype -from gn3.db.phenotypes import PublishXRef from gn3.db.phenotypes import Publication +from gn3.db.phenotypes import PublishXRef from flask import current_app @@ -446,12 +454,41 @@ def edit_trait(name, inbred_set_id): conn=conn, table="Publication", where=Publication(id_=publish_xref.publication_id)) + json_data = fetchall( + conn, + "metadata_audit", + where=MetadataAudit(dataset_id=publish_xref.id_)) + + Edit = namedtuple("Edit", ["field", "old", "new", "diff"]) + Diff = namedtuple("Diff", ["author", "diff", "timestamp"]) + diff_data = [] + for data in json_data: + json_ = json.loads(data.json_data) + timestamp = json_.get("timestamp") + author = json_.get("author") + for key, value in json_.items(): + if isinstance(value, dict): + for field, data_ in value.items(): + diff_data.append( + Diff(author=author, + diff=Edit(field, + data_.get("old"), + data_.get("new"), + "\n".join(difflib.ndiff( + [data_.get("old")], + [data_.get("new")]))), + timestamp=timestamp)) + diff_data_ = None + if len(diff_data) > 0: + diff_data_ = groupby(diff_data, lambda x: x.timestamp) return render_template( "edit_trait.html", + diff=diff_data_, publish_xref=publish_xref, phenotype=phenotype_, publication=publication_, - version=GN_VERSION) + version=GN_VERSION, + ) @app.route("/trait/update", methods=["POST"]) @@ -460,51 +497,59 @@ def update_trait(): user=current_app.config.get("DB_USER"), passwd=current_app.config.get("DB_PASS"), host=current_app.config.get("DB_HOST")) - # Filter out empty values - data_ = {k: v for k, v in request.form.items() if v is not ''} - + data_ = request.form.to_dict() # Run updates: + phenotype_ = { + "pre_pub_description": data_.get("pre-pub-desc"), + "post_pub_description": data_.get("post-pub-desc"), + "original_description": data_.get("orig-desc"), + "units": data_.get("units"), + "pre_pub_abbreviation": data_.get("pre-pub-abbrev"), + "post_pub_abbreviation": data_.get("post-pub-abbrev"), + "lab_code": data_.get("labcode"), + "submitter": data_.get("submitter"), + "owner": data_.get("owner"), + "authorized_users": data_.get("authorized-users"), + } updated_phenotypes = update( conn, "Phenotype", - data=Phenotype( - pre_pub_description=data_.get("pre-pub-desc"), - post_pub_description=data_.get("post-pub-desc"), - original_description=data_.get("orig-desc"), - units=data_.get("units"), - pre_pub_abbreviation=data_.get("pre-pub-abbrev"), - post_pub_abbreviation=data_.get("post-pub-abbrev"), - lab_code=data_.get("labcode"), - submitter=data_.get("submitter"), - owner=data_.get("owner"), - ), + data=Phenotype(**phenotype_), where=Phenotype(id_=data_.get("phenotype-id"))) + diff_data = {} + if updated_phenotypes: + diff_data.update({"Phenotype": diff_from_dict(old={ + k: data_.get(f"old_{k}") for k, v in phenotype_.items() + if v is not None}, new=phenotype_)}) + publication_ = { + "abstract": data_.get("abstract"), + "authors": data_.get("authors"), + "title": data_.get("title"), + "journal": data_.get("journal"), + "volume": data_.get("volume"), + "pages": data_.get("pages"), + "month": data_.get("month"), + "year": data_.get("year") + } updated_publications = update( conn, "Publication", - data=Publication( - abstract=data_.get("abstract"), - authors=data_.get("authors"), - title=data_.get("title"), - journal=data_.get("journal"), - volume=data_.get("volume"), - pages=data_.get("pages"), - month=data_.get("month"), - year=data_.get("year")), - where=Publication(id_=data_.get("pubmed-id"))) - if updated_phenotypes or updated_publications: - comments = data_.get("comments") - if comments: - comments = (f"{comments}\r\n" - f"{g.user_session.record.get(b'user_name')}") - update(conn, "PublishXRef", - data=PublishXRef( - comments=(f"{data_.get('comments')}\r\n" - "Modified by: " - f"{g.user_session.record.get(b'user_name').decode('utf-8')} " - f"on {str(datetime.datetime.now())}"), - publication_id=data_.get("pubmed-id")), - where=PublishXRef( - id_=data_.get("dataset-name"), - inbred_set_id=data_.get("inbred-set-id"))) + data=Publication(**publication_), + where=Publication(id_=data_.get("pubmed-id", + data_.get("old_id_")))) + if updated_publications: + diff_data.update({"Publication": diff_from_dict(old={ + k: data_.get(f"old_{k}") for k, v in publication_.items() + if v is not None}, new=publication_)}) + author = g.user_session.record.get(b'user_name') + if diff_data: + diff_data.update({"dataset_id": data_.get("dataset-name")}) + diff_data.update({"author": author.decode('utf-8')}) + diff_data.update({"timestamp": datetime.datetime.now().strftime( + "%Y-%m-%d %H:%M:%S")}) + insert(conn, + table="metadata_audit", + data=MetadataAudit(dataset_id=data_.get("dataset-name"), + editor=author.decode("utf-8"), + json_data=json.dumps(diff_data))) return redirect("/trait/10007/edit/1") |