about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--wqflask/wqflask/server_side.py4
-rw-r--r--wqflask/wqflask/templates/edit_trait.html72
-rw-r--r--wqflask/wqflask/views.py155
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")