diff options
Diffstat (limited to 'wqflask')
-rw-r--r-- | wqflask/db/webqtlDatabaseFunction.py | 12 | ||||
-rw-r--r-- | wqflask/tests/unit/wqflask/api/test_gen_menu.py | 17 | ||||
-rw-r--r-- | wqflask/wqflask/api/gen_menu.py | 18 | ||||
-rw-r--r-- | wqflask/wqflask/database.py | 8 | ||||
-rw-r--r-- | wqflask/wqflask/decorators.py | 14 | ||||
-rw-r--r-- | wqflask/wqflask/templates/edit_trait.html | 232 | ||||
-rw-r--r-- | wqflask/wqflask/templates/show_trait_details.html | 2 | ||||
-rw-r--r-- | wqflask/wqflask/views.py | 95 |
8 files changed, 265 insertions, 133 deletions
diff --git a/wqflask/db/webqtlDatabaseFunction.py b/wqflask/db/webqtlDatabaseFunction.py index 29112949..9ec650a4 100644 --- a/wqflask/db/webqtlDatabaseFunction.py +++ b/wqflask/db/webqtlDatabaseFunction.py @@ -21,15 +21,6 @@ # This module is used by GeneNetwork project (www.genenetwork.org) from db.call import fetch1 -from utility.tools import USE_GN_SERVER - -from utility.logger import getLogger -logger = getLogger(__name__) - -########################################################################### -# output: cursor instance -# function: connect to database and return cursor instance -########################################################################### def retrieve_species(group): @@ -38,13 +29,10 @@ def retrieve_species(group): """ result = fetch1("select Species.Name from Species, InbredSet where InbredSet.Name = '%s' and InbredSet.SpeciesId = Species.Id" % ( group), "/cross/" + group + ".json", lambda r: (r["species"],))[0] - logger.debug("retrieve_species result:", result) return result def retrieve_species_id(group): - result = fetch1("select SpeciesId from InbredSet where Name = '%s'" % ( group), "/cross/" + group + ".json", lambda r: (r["species_id"],))[0] - logger.debug("retrieve_species_id result:", result) return result diff --git a/wqflask/tests/unit/wqflask/api/test_gen_menu.py b/wqflask/tests/unit/wqflask/api/test_gen_menu.py index 743b3bde..7e477da2 100644 --- a/wqflask/tests/unit/wqflask/api/test_gen_menu.py +++ b/wqflask/tests/unit/wqflask/api/test_gen_menu.py @@ -3,7 +3,6 @@ import unittest from unittest import mock from wqflask.api.gen_menu import gen_dropdown_json -from wqflask.api.gen_menu import get_species from wqflask.api.gen_menu import get_groups from wqflask.api.gen_menu import get_types from wqflask.api.gen_menu import get_datasets @@ -67,20 +66,6 @@ class TestGenMenu(unittest.TestCase): } } - def test_get_species(self): - """Test that assertion is raised when dataset and dataset_name - are defined""" - db_mock = mock.MagicMock() - with db_mock.cursor() as cursor: - cursor.fetchall.return_value = ( - ('human', 'Human'), - ('mouse', 'Mouse')) - self.assertEqual(get_species(db_mock), - [['human', 'Human'], ['mouse', 'Mouse']]) - cursor.execute.assert_called_once_with( - "SELECT Name, MenuName FROM Species ORDER BY OrderId" - ) - def test_get_groups(self): """Test that species groups are grouped correctly""" db_mock = mock.MagicMock() @@ -411,7 +396,7 @@ class TestGenMenu(unittest.TestCase): @mock.patch('wqflask.api.gen_menu.get_datasets') @mock.patch('wqflask.api.gen_menu.get_types') @mock.patch('wqflask.api.gen_menu.get_groups') - @mock.patch('wqflask.api.gen_menu.get_species') + @mock.patch('wqflask.api.gen_menu.get_all_species') def test_gen_dropdown_json(self, species_mock, groups_mock, diff --git a/wqflask/wqflask/api/gen_menu.py b/wqflask/wqflask/api/gen_menu.py index 0c1120ab..a699a484 100644 --- a/wqflask/wqflask/api/gen_menu.py +++ b/wqflask/wqflask/api/gen_menu.py @@ -1,29 +1,17 @@ +from gn3.db.species import get_all_species def gen_dropdown_json(conn): """Generates and outputs (as json file) the data for the main dropdown menus on the home page """ - - species = get_species(conn) + species = get_all_species(conn) groups = get_groups(species, conn) types = get_types(groups, conn) datasets = get_datasets(types, conn) - - data = dict(species=species, + return dict(species=species, groups=groups, types=types, datasets=datasets) - return data - - -def get_species(conn): - """Build species list""" - with conn.cursor() as cursor: - cursor.execute("SELECT Name, MenuName FROM Species " - "ORDER BY OrderId") - results = cursor.fetchall() - return [[name, menu_name] for name, menu_name in results] - def get_groups(species, conn): """Build groups list""" diff --git a/wqflask/wqflask/database.py b/wqflask/wqflask/database.py index 38c37d2c..11f8d287 100644 --- a/wqflask/wqflask/database.py +++ b/wqflask/wqflask/database.py @@ -14,9 +14,5 @@ db_session = scoped_session(sessionmaker(autocommit=False, Base = declarative_base() Base.query = db_session.query_property() - -def init_db(): - Base.metadata.create_all(bind=engine) - - -init_db() +# Initialise the db +Base.metadata.create_all(bind=engine) diff --git a/wqflask/wqflask/decorators.py b/wqflask/wqflask/decorators.py new file mode 100644 index 00000000..f0978fd3 --- /dev/null +++ b/wqflask/wqflask/decorators.py @@ -0,0 +1,14 @@ +"""This module contains gn2 decorators""" +from flask import g +from functools import wraps + + +def admin_login_required(f): + """Use this for endpoints where admins are required""" + @wraps(f) + def wrap(*args, **kwargs): + if g.user_session.record.get(b"user_email_address") not in [ + b"labwilliams@gmail.com"]: + return "You need to be admin", 401 + return f(*args, **kwargs) + return wrap diff --git a/wqflask/wqflask/templates/edit_trait.html b/wqflask/wqflask/templates/edit_trait.html index b9a31fc6..2cd8a6fd 100644 --- a/wqflask/wqflask/templates/edit_trait.html +++ b/wqflask/wqflask/templates/edit_trait.html @@ -2,85 +2,165 @@ {% block title %}Trait Submission{% endblock %} {% block content %} <!-- Start of body --> - <form method="post" action="/show_temp_trait"> - <div class="container-fluid"> +Edit Trait for Published Database +Submit Trait | Reset - {{ flash_me() }} +{% if publish_xref.comments %} +<h2>Update History:</h2> - <div class="row" style="width: 1400px !important;"> - <div class="col-xs-3"> - <section id="description"> - <div> - <h2 style="color: #5a5a5a;">Introduction</h2> - <hr> - <p>The trait values that you enter are statistically compared with verified genotypes collected at a set of microsatellite markers in each RI set. The markers are drawn from a set of over 750, but for each set redundant markers have been removed, preferentially retaining those that are most informative.</p> - <p>These error-checked RI mapping data match theoretical expectations for RI strain sets. The cumulative adjusted length of the RI maps are approximately 1400 cM, a value that matches those of both MIT maps and Chromosome Committee Report maps. See our <a target="_blank" href="http://www.nervenet.org/papers/BXN.html">full description</a> of the genetic data collected as part of the WebQTL project.</p> - </div> - </section> - <br> - <section id="description"> - <div> - <h2 style="color: #5a5a5a;">About Your Data</h2> - <hr> - <p>You can open a separate window giving the number of strains for each data set and sample data.</p> - <p>None of your submitted data is copied or stored by this system except during the actual processing of your submission. By the time the reply page displays in your browser, your submission has been cleared from this system.</p> - </div> - </section> - </div> - <div style="padding-left:20px" class="col-xs-6" style="width: 600px !important;"> - <section id="submission_form"> - <div class="form-group"> - <h2 style="color: #5a5a5a;">Edit Trait Form</h2> - <hr> - <div style="margin-bottom: 150px;" class="form-horizontal"> - <h3>Choose Dataset to Edit:</h3> - <br> - <div class="col-xs-10"> - <!-- Species --> - <div class="form-group"> - <label for="species" class="col-xs-2 control-label">Species: </label> - <div class="col-xs-4 controls"> - <select name="species" id="species" class="form-control span3" style="width: 280px !important;"></select> - </div> - </div> - <!-- Group --> - <div class="form-group"> - <label for="group" class="col-xs-2 control-label">Group: </label> - <div class="col-xs-4 controls"> - <select name="group" id="group" class="form-control span3" style="width: 280px !important;"></select> - </div> - </div> - <!-- Type --> - <div class="form-group"> - <label for="group" class="col-xs-2 control-label">Type: </label> - <div class="col-xs-4 controls"> - <select name="type" id="type" class="form-control span3" style="width: 280px !important;"></select> - </div> - </div> - <!-- Dataset --> - <div class="form-group"> - <label for="group" class="col-xs-2 control-label">DataSet: </label> - <div class="col-xs-4 controls"> - <select name="dataset" id="dataset" class="form-control span3" style="width: 280px !important;"></select> - </div> - </div> - </div> - <div class="controls" style="display:block; margin-left: 40%; margin-right: 20%;"> - <input type="submit" style="width: 110px; margin-right: 25px;" class="btn btn-primary form-control col-xs-2" value="Submit Trait"> - <input type="reset" style="width: 110px;" class="btn btn-primary form-control col-xs-2" value="Reset"> - </div> - </div> - </section> - </div> - </div> - </div> - </form> +{{ publish_xref.comments }} + +{% endif %} + +<form id="edit-form" class="form-horizontal" method="post" action="/trait/update"> + <h2 class="text-center">Trait Information:</h2> + <div class="form-group"> + <label for="pubmed-id" class="col-sm-2 control-label">Pubmed ID:</label> + <!-- Do not enter PubMed_ID if this trait has not been Published. + If the PubMed_ID you entered is alreday stored in our + database, all the following fields except Post Publication + Description will be ignored. Do not enter any non-digit + 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> + </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> + </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 + Pre Publication Description is entered, only you and + authorized users can see the Post Publication Description --> + </div> + <div class="form-group"> + <label for="post-pub-desc" class="col-sm-2 control-label">Post Publication Description:</label> + <div class="col-sm-8"> + <textarea name="pre-pub-desc" class="form-control" rows="4">{{ phenotype.post_pub_description |default('', true) }}</textarea> + </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> + </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> + </div> + </div> + <div class="form-group"> + <label for="pre-pub-abbrev" class="col-sm-2 control-label"> + Pre Publication Abbreviation: + </label> + <div class="col-sm-8"> + <textarea name="pre-pub-abbrev" class="form-control" rows="1">{{ phenotype.pre_pub_abbreviation |default('', true) }}</textarea> + </div> + </div> + <div class="form-group"> + <label for="post-pub-abbrev" class="col-sm-2 control-label"> + Post Publication Abbreviation: + </label> + <div class="col-sm-8"> + <textarea name="post-pub-abbrev" class="form-control" rows="1">{{ phenotype.post_pub_abbreviation |default('', true) }}</textarea> + </div> + </div> + <div class="form-group"> + <label for="labcode" class="col-sm-2 control-label"> + Lab Code: + </label> + <div class="col-sm-8"> + <textarea name="labcode" class="form-control" rows="1">{{ phenotype.lab_code |default('', true) }}</textarea> + </div> + </div> + <div class="form-group"> + <label for="submitter" class="col-sm-2 control-label"> + Submitter: + </label> + <div class="col-sm-8"> + <textarea name="submitter" class="form-control" rows="1">{{ phenotype.submitter |default('', true) }}</textarea> + </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> + </div> + </div> + <div class="form-group"> + <label for="owner" 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> + </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> + </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> + </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> + </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> + </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> + </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> + </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> + </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> + </div> + </div> + <div class="controls" style="display:block; margin-left: 40%; margin-right: 20%;"> + <input name="dataset-name" type="hidden" value="{{ publish_xref.id_ }}"/> + <input name="phenotype-id" type="hidden" value="{{ publish_xref.phenotype_id }}"/> + <input name="inbred-set-id" type="hidden" value="{{ publish_xref.inbred_set_id }}"/> + <input name="comments" type="hidden" value="{{ publish_xref.comments }}"/> + <input type="submit" style="width: 125px; margin-right: 25px;" class="btn btn-primary form-control col-xs-2" value="Submit Change"> + <input type="reset" style="width: 110px;" class="btn btn-primary form-control col-xs-2" onClick="window.location.reload();" value="Reset"> + </div> +</form> {%endblock%} - {% block js %} - <script src="/static/new/javascript/dataset_select_menu_edit_trait.js"></script> - <script> - gn_server_url = "{{ gn_server_url }}"; - </script> +{% block js %} +<script> + gn_server_url = "{{ gn_server_url }}"; +</script> {% endblock %} diff --git a/wqflask/wqflask/templates/show_trait_details.html b/wqflask/wqflask/templates/show_trait_details.html index d2999eef..83f7b0ac 100644 --- a/wqflask/wqflask/templates/show_trait_details.html +++ b/wqflask/wqflask/templates/show_trait_details.html @@ -235,7 +235,7 @@ {% endif %} <button type="button" id="view_in_gn1" class="btn btn-primary" title="View Trait in GN1" onclick="window.open('http://gn1.genenetwork.org/webqtl/main.py?cmd=show&db={{ this_trait.dataset.name }}&probeset={{ this_trait.name }}', '_blank')">Go to GN1</button> {% if admin_status == "owner" or admin_status == "edit-admins" or admin_status == "edit-access" %} - <button type="button" id="edit_resource" class="btn btn-success" title="Edit Resource" onclick="window.open('./resources/manage?resource_id={{ resource_id }}', '_blank')">Edit</button> + <button type="button" id="edit_resource" class="btn btn-success" title="Edit Resource" onclick="window.open('/trait/{{ this_trait.name }}/edit/{{ this_trait.dataset.id }}', '_blank')">Edit</button> {% endif %} </div> </div> diff --git a/wqflask/wqflask/views.py b/wqflask/wqflask/views.py index 4834ee63..ee69900e 100644 --- a/wqflask/wqflask/views.py +++ b/wqflask/wqflask/views.py @@ -9,6 +9,7 @@ import csv import simplejson as json import xlsxwriter import io # Todo: Use cStringIO? +import MySQLdb from zipfile import ZipFile, ZIP_DEFLATED @@ -21,6 +22,15 @@ import base64 import array import sqlalchemy from wqflask import app + +from gn3.db import fetchone +from gn3.db import update +from gn3.db.phenotypes import Phenotype +from gn3.db.phenotypes import PublishXRef +from gn3.db.phenotypes import Publication + + +from flask import current_app from flask import g from flask import Response from flask import request @@ -31,10 +41,8 @@ from flask import redirect from flask import url_for from flask import send_file -from wqflask import collect from wqflask import search_results from wqflask import server_side -from wqflask.submit_bnw import get_bnw_input from base.data_set import create_dataset # Used by YAML in marker_regression from wqflask.show_trait import show_trait from wqflask.show_trait import export_trait_data @@ -58,6 +66,7 @@ from wqflask.export_traits import export_search_results_csv from wqflask.gsearch import GSearch from wqflask.update_search_results import GSearch as UpdateGSearch from wqflask.docs import Docs, update_text +from wqflask.decorators import admin_login_required from wqflask.db_info import InfoPage from utility import temp_data @@ -414,16 +423,88 @@ def submit_trait_form(): version=GN_VERSION) -@app.route("/edit_trait_form") -def edit_trait_page(): - species_and_groups = get_species_groups() +@app.route("/trait/<name>/edit/<inbred_set_id>") +@admin_login_required +def edit_trait(name, inbred_set_id): + conn = MySQLdb.Connect(db=current_app.config.get("DB_NAME"), + user=current_app.config.get("DB_USER"), + passwd=current_app.config.get("DB_PASS"), + host=current_app.config.get("DB_HOST")) + publish_xref = fetchone( + conn=conn, + table="PublishXRef", + where=PublishXRef(id_=name, + inbred_set_id=inbred_set_id)) + phenotype_ = fetchone( + conn=conn, + table="Phenotype", + where=Phenotype(id_=publish_xref.phenotype_id)) + publication_ = fetchone( + conn=conn, + table="Publication", + where=Publication(id_=publish_xref.publication_id)) return render_template( "edit_trait.html", - species_and_groups=species_and_groups, - gn_server_url=GN_SERVER_URL, + publish_xref=publish_xref, + phenotype=phenotype_, + publication=publication_, version=GN_VERSION) +@app.route("/trait/update", methods=["POST"]) +def update_trait(): + conn = MySQLdb.Connect(db=current_app.config.get("DB_NAME"), + 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 ''} + + # Run updates: + 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"), + ), + where=Phenotype(id_=data_.get("phenotype-id"))) + 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"))) + return redirect("/trait/10007/edit/1") + + @app.route("/create_temp_trait", methods=('POST',)) def create_temp_trait(): logger.info(request.url) |