diff options
author | Alexander Kabui | 2021-11-23 14:13:25 +0300 |
---|---|---|
committer | Alexander Kabui | 2021-11-23 14:13:25 +0300 |
commit | bda4e460667909d47fd3a167391ca6fbe34cd61b (patch) | |
tree | 6d7c21869e2783073446d674958e3cbaf40d7801 | |
parent | aa9a06d927bdc2b5221e58559f24921a0ff72cd8 (diff) | |
parent | fcfd7be522ce914b0aa11cd4555aeab2d2141428 (diff) | |
download | genenetwork2-bda4e460667909d47fd3a167391ca6fbe34cd61b.tar.gz |
resolve merge conflicts
25 files changed, 362 insertions, 473 deletions
diff --git a/doc/API_readme.md b/doc/API_readme.md index be6668dc..17d10e44 100644 --- a/doc/API_readme.md +++ b/doc/API_readme.md @@ -1,169 +1,3 @@ # API Query Documentation # ---- -# Fetching Dataset/Trait info/data # ---- -## Fetch Species List ## -To get a list of species with data available in GN (and their associated names and ids): -``` -curl http://genenetwork.org/api/v_pre1/species -[ { "FullName": "Mus musculus", "Id": 1, "Name": "mouse", "TaxonomyId": 10090 }, ... { "FullName": "Populus trichocarpa", "Id": 10, "Name": "poplar", "TaxonomyId": 3689 } ] -``` - -Or to get a single species info: -``` -curl http://genenetwork.org/api/v_pre1/species/mouse -``` -OR -``` -curl http://genenetwork.org/api/v_pre1/species/mouse.json -``` - -*For all queries where the last field is a user-specified name/ID, there will be the option to append a file format type. Currently there is only JSON (and it will default to JSON if none is provided), but other formats will be added later* - -## Fetch Groups/RISets ## - -This query can optionally filter by species: - -``` -curl http://genenetwork.org/api/v_pre1/groups (for all species) -``` -OR -``` -curl http://genenetwork.org/api/v_pre1/groups/mouse (for just mouse groups/RISets) -[ { "DisplayName": "BXD", "FullName": "BXD RI Family", "GeneticType": "riset", "Id": 1, "MappingMethodId": "1", "Name": "BXD", "SpeciesId": 1, "public": 2 }, ... { "DisplayName": "AIL LGSM F34 and F39-43 (GBS)", "FullName": "AIL LGSM F34 and F39-43 (GBS)", "GeneticType": "intercross", "Id": 72, "MappingMethodId": "2", "Name": "AIL-LGSM-F34-F39-43-GBS", "SpeciesId": 1, "public": 2 } ] -``` - -## Fetch Genotypes for Group/RISet ## -``` -curl http://genenetwork.org/api/v_pre1/genotypes/bimbam/BXD -curl http://genenetwork.org/api/v_pre1/genotypes/BXD.bimbam -``` -Returns a group's genotypes in one of several formats - bimbam, rqtl2, or geno (a format used by qtlreaper which is just a CSV file consisting of marker positions and genotypes) - -Rqtl2 genotype queries can also include the dataset name and will return a zip of the genotypes, phenotypes, and gene map (marker names/positions). For example: -``` -curl http://genenetwork.org/api/v_pre1/genotypes/rqtl2/BXD/HC_M2_0606_P.zip -``` - -## Fetch Datasets ## -``` -curl http://genenetwork.org/api/v_pre1/datasets/bxd -``` -OR -``` -curl http://genenetwork.org/api/v_pre1/datasets/mouse/bxd -[ { "AvgID": 1, "CreateTime": "Fri, 01 Aug 2003 00:00:00 GMT", "DataScale": "log2", "FullName": "UTHSC/ETHZ/EPFL BXD Liver Polar Metabolites Extraction A, CD Cohorts (Mar 2017) log2", "Id": 1, "Long_Abbreviation": "BXDMicroArray_ProbeSet_August03", "ProbeFreezeId": 3, "ShortName": "Brain U74Av2 08/03 MAS5", "Short_Abbreviation": "Br_U_0803_M", "confidentiality": 0, "public": 0 }, ... { "AvgID": 3, "CreateTime": "Tue, 14 Aug 2018 00:00:00 GMT", "DataScale": "log2", "FullName": "EPFL/LISP BXD CD Liver Affy Mouse Gene 1.0 ST (Aug18) RMA", "Id": 859, "Long_Abbreviation": "EPFLMouseLiverCDRMAApr18", "ProbeFreezeId": 181, "ShortName": "EPFL/LISP BXD CD Liver Affy Mouse Gene 1.0 ST (Aug18) RMA", "Short_Abbreviation": "EPFLMouseLiverCDRMA0818", "confidentiality": 0, "public": 1 } ] -``` -(I added the option to specify species just in case we end up with the same group name across multiple species at some point, though it's currently unnecessary) - -## Fetch Individual Dataset Info ## -### For mRNA Assay/"ProbeSet" ### - -``` -curl http://genenetwork.org/api/v_pre1/dataset/HC_M2_0606_P -``` -OR -``` -curl http://genenetwork.org/api/v_pre1/dataset/bxd/HC_M2_0606_P``` -{ "confidential": 0, "data_scale": "log2", "dataset_type": "mRNA expression", "full_name": "Hippocampus Consortium M430v2 (Jun06) PDNN", "id": 112, "name": "HC_M2_0606_P", "public": 2, "short_name": "Hippocampus M430v2 BXD 06/06 PDNN", "tissue": "Hippocampus mRNA", "tissue_id": 9 } -``` -(This also has the option to specify group/riset) - -### For "Phenotypes" (basically non-mRNA Expression; stuff like weight, sex, etc) ### -For these traits, the query fetches publication info and takes the group and phenotype 'ID' as input. For example: -``` -curl http://genenetwork.org/api/v_pre1/dataset/bxd/10001 -{ "dataset_type": "phenotype", "description": "Central nervous system, morphology: Cerebellum weight, whole, bilateral in adults of both sexes [mg]", "id": 10001, "name": "CBLWT2", "pubmed_id": 11438585, "title": "Genetic control of the mouse cerebellum: identification of quantitative trait loci modulating size and architecture", "year": "2001" } -``` - -## Fetch Sample Data for Dataset ## -``` -curl http://genenetwork.org/api/v_pre1/sample_data/HSNIH-PalmerPublish.csv -``` - -Returns a CSV file with sample/strain names as the columns and trait IDs as rows - -## Fetch Sample Data for Single Trait ## -``` -curl http://genenetwork.org/api/v_pre1/sample_data/HC_M2_0606_P/1436869_at -[ { "data_id": 23415463, "sample_name": "129S1/SvImJ", "sample_name_2": "129S1/SvImJ", "se": 0.123, "value": 8.201 }, { "data_id": 23415463, "sample_name": "A/J", "sample_name_2": "A/J", "se": 0.046, "value": 8.413 }, { "data_id": 23415463, "sample_name": "AKR/J", "sample_name_2": "AKR/J", "se": 0.134, "value": 8.856 }, ... ] -``` - -## Fetch Trait List for Dataset ## -``` -curl http://genenetwork.org/api/v_pre1/traits/HXBBXHPublish.json -[ { "Additive": 0.0499967532467532, "Id": 10001, "LRS": 16.2831307029479, "Locus": "rs106114574", "PhenotypeId": 1449, "PublicationId": 319, "Sequence": 1 }, ... ] -``` - -Both JSON and CSV formats can be specified, with JSON as default. There is also an optional "ids_only" and "names_only" parameter that will only return a list of trait IDs or names, respectively. - -## Fetch Trait Info (Name, Description, Location, etc) ## -### For mRNA Expression/"ProbeSet" ### -``` -curl http://genenetwork.org/api/v_pre1/trait/HC_M2_0606_P/1436869_at -{ "additive": -0.214087568058076, "alias": "HHG1; HLP3; HPE3; SMMCI; Dsh; Hhg1", "chr": "5", "description": "sonic hedgehog (hedgehog)", "id": 99602, "locus": "rs8253327", "lrs": 12.7711275309832, "mb": 28.457155, "mean": 9.27909090909091, "name": "1436869_at", "p_value": 0.306, "se": null, "symbol": "Shh" } -``` - -### For "Phenotypes" ### -For phenotypes this just gets the max LRS, its location, and additive effect (as calculated by qtlreaper) - -Since each group/riset only has one phenotype "dataset", this query takes either the group/riset name or the group/riset name + "Publish" (for example "BXDPublish", which is the dataset name in the DB) as input -``` -curl http://genenetwork.org/api/v_pre1/trait/BXD/10001 -{ "additive": 2.39444435069444, "id": 4, "locus": "rs48756159", "lrs": 13.4974911471087 } -``` - ---- - -# Analyses # ---- -## Mapping ## -Currently two mapping tools can be used - GEMMA and R/qtl. qtlreaper will be added later with Christian Fischer's RUST implementation - https://github.com/chfi/rust-qtlreaper - -Each method's query takes the following parameters respectively (more will be added): -### GEMMA ### -* trait_id (*required*) - ID for trait being mapped -* db (*required*) - DB name for trait above (Short_Abbreviation listed when you query for datasets) -* use_loco - Whether to use LOCO (leave one chromosome out) method (default = false) -* maf - minor allele frequency (default = 0.01) - -Example query: -``` -curl http://genenetwork.org/api/v_pre1/mapping?trait_id=10015&db=BXDPublish&method=gemma&use_loco=true -``` - -### R/qtl ### -(See the R/qtl guide for information on some of these options - http://www.rqtl.org/manual/qtl-manual.pdf) -* trait_id (*required*) - ID for trait being mapped -* db (*required*) - DB name for trait above (Short_Abbreviation listed when you query for datasets) -* rqtl_method - hk (default) | ehk | em | imp | mr | mr-imp | mr-argmax ; Corresponds to the "method" option for the R/qtl scanone function. -* rqtl_model - normal (default) | binary | 2-part | np ; corresponds to the "model" option for the R/qtl scanone function -* num_perm - number of permutations; 0 by default -* control_marker - Name of marker to use as control; this relies on the user knowing the name of the marker they want to use as a covariate -* interval_mapping - Whether to use interval mapping; "false" by default -* pair_scan - *NYI* - -Example query: -``` -curl http://genenetwork.org/api/v_pre1/mapping?trait_id=1418701_at&db=HC_M2_0606_P&method=rqtl&num_perm=100 -``` - -Some combinations of methods/models may not make sense. The R/qtl manual should be referred to for any questions on its use (specifically the scanone function in this case) - -## Calculate Correlation ## -Currently only Sample and Tissue correlations are implemented - -This query currently takes the following parameters (though more will be added): -* trait_id (*required*) - ID for trait used for correlation -* db (*required*) - DB name for the trait above (this is the Short_Abbreviation listed when you query for datasets) -* target_db (*required*) - Target DB name to be correlated against -* type - sample (default) | tissue -* method - pearson (default) | spearman -* return - Number of results to return (default = 500) - -Example query: -``` -curl http://genenetwork.org/api/v_pre1/correlation?trait_id=1427571_at&db=HC_M2_0606_P&target_db=BXDPublish&type=sample&return_count=100 -[ { "#_strains": 6, "p_value": 0.004804664723032055, "sample_r": -0.942857142857143, "trait": 20511 }, { "#_strains": 6, "p_value": 0.004804664723032055, "sample_r": -0.942857142857143, "trait": 20724 }, { "#_strains": 12, "p_value": 1.8288943424888848e-05, "sample_r": -0.9233615170820528, "trait": 13536 }, { "#_strains": 7, "p_value": 0.006807187408935392, "sample_r": 0.8928571428571429, "trait": 10157 }, { "#_strains": 7, "p_value": 0.006807187408935392, "sample_r": -0.8928571428571429, "trait": 20392 }, ... ] -``` +This document has moved to [gn-docs](https://github.com/genenetwork/gn-docs/blob/master/api/GN2-REST-API.md)! diff --git a/doc/README.org b/doc/README.org index 8839aefc..e1c6b614 100644 --- a/doc/README.org +++ b/doc/README.org @@ -26,7 +26,7 @@ * Introduction -Large system deployments can get very [[http://biogems.info/contrib/genenetwork/gn2.svg ][complex]]. In this document we +Large system deployments can get very [[http://genenetwork.org/environments/][complex]]. In this document we explain the GeneNetwork version 2 (GN2) reproducible deployment system which is based on GNU Guix (see also [[https://github.com/pjotrp/guix-notes/blob/master/README.md][Guix-notes]]). The Guix system can be used to install GN with all its files and dependencies. diff --git a/wqflask/base/data_set.py b/wqflask/base/data_set.py index 4d75e7ee..49ece9dd 100644 --- a/wqflask/base/data_set.py +++ b/wqflask/base/data_set.py @@ -757,6 +757,7 @@ class DataSet: # Postgres doesn't have that limit, so we can get rid of this after we transition chunk_size = 50 number_chunks = int(math.ceil(len(sample_ids) / chunk_size)) + cached_results = fetch_cached_results(self.name, self.type) if cached_results is None: trait_sample_data = [] @@ -814,7 +815,6 @@ class DataSet: cache_dataset_results( self.name, self.type, self.trait_data) - else: self.trait_data = cached_results diff --git a/wqflask/wqflask/__init__.py b/wqflask/wqflask/__init__.py index fbf1c9f5..05e040ed 100644 --- a/wqflask/wqflask/__init__.py +++ b/wqflask/wqflask/__init__.py @@ -10,8 +10,9 @@ from urllib.parse import urlparse from utility import formatting from gn3.authentication import DataRole, AdminRole -from wqflask.resource_manager import resource_management +from wqflask.group_manager import group_management +from wqflask.resource_manager import resource_management from wqflask.metadata_edits import metadata_edit from wqflask.api.markdown import glossary_blueprint @@ -65,8 +66,8 @@ app.register_blueprint(news_blueprint, url_prefix="/news") app.register_blueprint(jupyter_notebooks, url_prefix="/jupyter_notebooks") app.register_blueprint(resource_management, url_prefix="/resource-management") - app.register_blueprint(metadata_edit, url_prefix="/datasets/") +app.register_blueprint(group_management, url_prefix="/group-management") @app.before_request def before_request(): diff --git a/wqflask/wqflask/correlation/correlation_gn3_api.py b/wqflask/wqflask/correlation/correlation_gn3_api.py index 32a55b44..c2acd648 100644 --- a/wqflask/wqflask/correlation/correlation_gn3_api.py +++ b/wqflask/wqflask/correlation/correlation_gn3_api.py @@ -64,7 +64,7 @@ def test_process_data(this_trait, dataset, start_vars): return sample_data -def process_samples(start_vars,sample_names = [],excluded_samples = []): +def process_samples(start_vars, sample_names=[], excluded_samples=[]): """code to fetch correct samples""" sample_data = {} sample_vals_dict = json.loads(start_vars["sample_vals"]) @@ -84,7 +84,6 @@ def process_samples(start_vars,sample_names = [],excluded_samples = []): return sample_data - def merge_correlation_results(correlation_results, target_correlation_results): corr_dict = {} @@ -167,7 +166,7 @@ def fetch_sample_data(start_vars, this_trait, this_dataset, target_dataset): elif corr_samples_group == "samples_other": sample_data = process_samples( - start_vars, excluded_samples = this_dataset.group.samplelist) + start_vars, excluded_samples=this_dataset.group.samplelist) else: sample_data = process_samples(start_vars) @@ -316,6 +315,7 @@ def get_tissue_correlation_input(this_trait, trait_symbol_dict): primary_trait_tissue_vals_dict = correlation_functions.get_trait_symbol_and_tissue_values( symbol_list=[this_trait.symbol]) if this_trait.symbol and this_trait.symbol.lower() in primary_trait_tissue_vals_dict: + primary_trait_tissue_values = primary_trait_tissue_vals_dict[this_trait.symbol.lower( )] corr_result_tissue_vals_dict = correlation_functions.get_trait_symbol_and_tissue_values( diff --git a/wqflask/wqflask/correlation/show_corr_results.py b/wqflask/wqflask/correlation/show_corr_results.py index f5600f13..2c820658 100644 --- a/wqflask/wqflask/correlation/show_corr_results.py +++ b/wqflask/wqflask/correlation/show_corr_results.py @@ -101,11 +101,11 @@ def correlation_json_for_table(correlation_data, this_trait, this_dataset, targe target_trait = dataset_metadata.get(trait_name) if target_trait is None: + target_trait_ob = create_trait(dataset=target_dataset_ob, name=trait_name, get_qtl_info=True) target_trait = jsonable(target_trait_ob, target_dataset_ob) - new_traits_metadata[trait_name] = target_trait if target_trait['view'] == False: continue results_dict = {} @@ -183,6 +183,7 @@ def correlation_json_for_table(correlation_data, this_trait, this_dataset, targe cache_new_traits_metadata(dataset_metadata, new_traits_metadata, file_path) + return json.dumps(results_list) diff --git a/wqflask/wqflask/group_manager.py b/wqflask/wqflask/group_manager.py index 04a100ba..3936e36e 100644 --- a/wqflask/wqflask/group_manager.py +++ b/wqflask/wqflask/group_manager.py @@ -1,175 +1,157 @@ -import random -import string - -from flask import (Flask, g, render_template, url_for, request, make_response, - redirect, flash) - -from wqflask import app -from wqflask.user_login import send_verification_email, send_invitation_email, basic_info, set_password - -from utility.redis_tools import get_user_groups, get_group_info, save_user, create_group, delete_group, add_users_to_group, remove_users_from_group, \ - change_group_name, save_verification_code, check_verification_code, get_user_by_unique_column, get_resources, get_resource_info - -from utility.logger import getLogger -logger = getLogger(__name__) - - -@app.route("/groups/manage", methods=('GET', 'POST')) -def manage_groups(): - params = request.form if request.form else request.args - if "add_new_group" in params: - return redirect(url_for('add_group')) - else: - admin_groups, member_groups = get_user_groups(g.user_session.user_id) - return render_template("admin/group_manager.html", admin_groups=admin_groups, member_groups=member_groups) - - -@app.route("/groups/view", methods=('GET', 'POST')) -def view_group(): - params = request.form if request.form else request.args - group_id = params['id'] - group_info = get_group_info(group_id) - admins_info = [] - user_is_admin = False - if g.user_session.user_id in group_info['admins']: - user_is_admin = True - for user_id in group_info['admins']: - if user_id: - user_info = get_user_by_unique_column("user_id", user_id) - admins_info.append(user_info) - members_info = [] - for user_id in group_info['members']: - if user_id: - user_info = get_user_by_unique_column("user_id", user_id) - members_info.append(user_info) - - # ZS: This whole part might not scale well with many resources - resources_info = [] - all_resources = get_resources() - for resource_id in all_resources: - resource_info = get_resource_info(resource_id) - group_masks = resource_info['group_masks'] - if group_id in group_masks: - this_resource = {} - privileges = group_masks[group_id] - this_resource['id'] = resource_id - this_resource['name'] = resource_info['name'] - this_resource['data'] = privileges['data'] - this_resource['metadata'] = privileges['metadata'] - this_resource['admin'] = privileges['admin'] - resources_info.append(this_resource) - - return render_template("admin/view_group.html", group_info=group_info, admins=admins_info, members=members_info, user_is_admin=user_is_admin, resources=resources_info) - - -@app.route("/groups/remove", methods=('POST',)) -def remove_groups(): - group_ids_to_remove = request.form['selected_group_ids'] - for group_id in group_ids_to_remove.split(":"): - delete_group(g.user_session.user_id, group_id) - - return redirect(url_for('manage_groups')) - - -@app.route("/groups/remove_users", methods=('POST',)) -def remove_users(): - group_id = request.form['group_id'] - admin_ids_to_remove = request.form['selected_admin_ids'] - member_ids_to_remove = request.form['selected_member_ids'] - - remove_users_from_group(g.user_session.user_id, admin_ids_to_remove.split( - ":"), group_id, user_type="admins") - remove_users_from_group(g.user_session.user_id, member_ids_to_remove.split( - ":"), group_id, user_type="members") - - return redirect(url_for('view_group', id=group_id)) - - -@app.route("/groups/add_<path:user_type>", methods=('POST',)) -def add_users(user_type='members'): - group_id = request.form['group_id'] - if user_type == "admins": - user_emails = request.form['admin_emails_to_add'].split(",") - add_users_to_group(g.user_session.user_id, group_id, - user_emails, admins=True) - elif user_type == "members": - user_emails = request.form['member_emails_to_add'].split(",") - add_users_to_group(g.user_session.user_id, group_id, - user_emails, admins=False) - - return redirect(url_for('view_group', id=group_id)) - - -@app.route("/groups/change_name", methods=('POST',)) -def change_name(): - group_id = request.form['group_id'] - new_name = request.form['new_name'] - group_info = change_group_name(g.user_session.user_id, group_id, new_name) - - return new_name - - -@app.route("/groups/create", methods=('GET', 'POST')) -def add_or_edit_group(): - params = request.form if request.form else request.args - if "group_name" in params: - member_user_ids = set() - admin_user_ids = set() - # ZS: Always add the user creating the group as an admin - admin_user_ids.add(g.user_session.user_id) - if "admin_emails_to_add" in params: - admin_emails = params['admin_emails_to_add'].split(",") - for email in admin_emails: - user_details = get_user_by_unique_column( - "email_address", email) - if user_details: - admin_user_ids.add(user_details['user_id']) - #send_group_invites(params['group_id'], user_email_list = admin_emails, user_type="admins") - if "member_emails_to_add" in params: - member_emails = params['member_emails_to_add'].split(",") - for email in member_emails: - user_details = get_user_by_unique_column( - "email_address", email) - if user_details: - member_user_ids.add(user_details['user_id']) - #send_group_invites(params['group_id'], user_email_list = user_emails, user_type="members") - - create_group(list(admin_user_ids), list( - member_user_ids), params['group_name']) - return redirect(url_for('manage_groups')) - else: - return render_template("admin/create_group.html") - -# ZS: Will integrate this later, for now just letting users be added directly - - -def send_group_invites(group_id, user_email_list=[], user_type="members"): - for user_email in user_email_list: - user_details = get_user_by_unique_column("email_address", user_email) - if user_details: - group_info = get_group_info(group_id) - # ZS: Probably not necessary since the group should normally always exist if group_id is being passed here, - # but it's technically possible to hit it if Redis is cleared out before submitting the new users or something - if group_info: - # ZS: Don't add user if they're already an admin or if they're being added a regular user and are already a regular user, - # but do add them if they're a regular user and are added as an admin - if (user_details['user_id'] in group_info['admins']) or \ - ((user_type == "members") and (user_details['user_id'] in group_info['members'])): - continue - else: - send_verification_email(user_details, template_name="email/group_verification.txt", - key_prefix="verification_code", subject="You've been invited to join a GeneNetwork user group") - else: - temp_password = ''.join(random.choice( - string.ascii_uppercase + string.digits) for _ in range(6)) - user_details = { - 'user_id': str(uuid.uuid4()), - 'email_address': user_email, - 'registration_info': basic_info(), - 'password': set_password(temp_password), - 'confirmed': 0 - } - save_user(user_details, user_details['user_id']) - send_invitation_email(user_email, temp_password) - -# @app.route() +import json +import redis +import datetime + +from flask import current_app +from flask import Blueprint +from flask import g +from flask import render_template +from flask import request +from flask import redirect +from flask import url_for +from gn3.authentication import get_groups_by_user_uid +from gn3.authentication import get_user_info_by_key +from gn3.authentication import create_group +from wqflask.decorators import login_required + +group_management = Blueprint("group_management", __name__) + + +@group_management.route("/groups") +@login_required +def display_groups(): + groups = get_groups_by_user_uid( + user_uid=(g.user_session.record.get(b"user_id", + b"").decode("utf-8") or + g.user_session.record.get("user_id", "")), + conn=redis.from_url( + current_app.config["REDIS_URL"], + decode_responses=True)) + return render_template("admin/group_manager.html", + admin_groups=groups.get("admin"), + member_groups=groups.get("member")) + + +@group_management.route("/groups/create", methods=("GET",)) +@login_required +def view_create_group_page(): + return render_template("admin/create_group.html") + + +@group_management.route("/groups/create", methods=("POST",)) +@login_required +def create_new_group(): + conn = redis.from_url(current_app.config["REDIS_URL"], + decode_responses=True) + if group_name := request.form.get("group_name"): + members_uid, admins_uid = set(), set() + admins_uid.add(user_uid := ( + g.user_session.record.get( + b"user_id", + b"").decode("utf-8") or + g.user_session.record.get("user_id", ""))) + if admin_string := request.form.get("admin_emails_to_add"): + for email in admin_string.split(","): + user_info = get_user_info_by_key(key="email_address", + value=email, + conn=conn) + if user_uid := user_info.get("user_id"): + admins_uid.add(user_uid) + if member_string := request.form.get("member_emails_to_add"): + for email in member_string.split(","): + user_info = get_user_info_by_key(key="email_address", + value=email, + conn=conn) + if user_uid := user_info.get("user_id"): + members_uid.add(user_uid) + + # Create the new group: + create_group(conn=conn, + group_name=group_name, + member_user_uids=list(members_uid), + admin_user_uids=list(admins_uid)) + return redirect(url_for('group_management.display_groups')) + return redirect(url_for('group_management.create_groups')) + + +@group_management.route("/groups/delete", methods=("POST",)) +@login_required +def delete_groups(): + conn = redis.from_url(current_app.config["REDIS_URL"], + decode_responses=True) + user_uid = (g.user_session.record.get(b"user_id", b"").decode("utf-8") or + g.user_session.record.get("user_id", "")) + current_app.logger.info(request.form.get("selected_group_ids")) + for group_uid in request.form.get("selected_group_ids", "").split(":"): + if group_info := conn.hget("groups", group_uid): + group_info = json.loads(group_info) + # A user who is an admin can delete things + if user_uid in group_info.get("admins"): + conn.hdel("groups", group_uid) + return redirect(url_for('group_management.display_groups')) + + +@group_management.route("/groups/<group_id>") +@login_required +def view_group(group_id: str): + conn = redis.from_url(current_app.config["REDIS_URL"], + decode_responses=True) + user_uid = (g.user_session.record.get(b"user_id", b"").decode("utf-8") or + g.user_session.record.get("user_id", "")) + + resource_info = [] + for resource_uid, resource in conn.hgetall("resources").items(): + resource = json.loads(resource) + if group_id in (group_mask := resource.get("group_masks")): + __dict = {} + for val in group_mask.values(): + __dict.update(val) + __dict.update({ + "id": resource_uid, + "name": resource.get("name"), + }) + resource_info.append(__dict) + group_info = json.loads(conn.hget("groups", + group_id)) + group_info["guid"] = group_id + + return render_template( + "admin/view_group.html", + group_info=group_info, + admins=[get_user_info_by_key(key="user_id", + value=user_id, + conn=conn) + for user_id in group_info.get("admins")], + members=[get_user_info_by_key(key="user_id", + value=user_id, + conn=conn) + for user_id in group_info.get("members")], + is_admin = (True if user_uid in group_info.get("admins") else False), + resources=resource_info) + + +@group_management.route("/groups/<group_id>", methods=("POST",)) +def update_group(group_id: str): + conn = redis.from_url(current_app.config["REDIS_URL"], + decode_responses=True) + user_uid = (g.user_session.record.get(b"user_id", b"").decode("utf-8") or + g.user_session.record.get("user_id", "")) + group = json.loads(conn.hget("groups", group_id)) + timestamp = group["changed_timestamp"] + timestamp_ = datetime.datetime.utcnow().strftime('%b %d %Y %I:%M%p') + if user_uid in group.get("admins"): + if name := request.form.get("new_name"): + group["name"] = name + group["changed_timestamp"] = timestamp_ + if admins := request.form.get("admin_emails_to_add"): + group["admins"] = list(set(admins.split(":") + + group.get("admins"))) + group["changed_timestamp"] = timestamp_ + if members := request.form.get("member_emails_to_add"): + print(f"\n+++++\n{members}\n+++++\n") + group["members"] = list(set(members.split(":") + + group.get("members"))) + group["changed_timestamp"] = timestamp_ + conn.hset("groups", group_id, json.dumps(group)) + return redirect(url_for('group_management.view_group', + group_id=group_id)) diff --git a/wqflask/wqflask/gsearch.py b/wqflask/wqflask/gsearch.py index 2516e4fb..31f3305c 100644 --- a/wqflask/wqflask/gsearch.py +++ b/wqflask/wqflask/gsearch.py @@ -82,13 +82,14 @@ class GSearch: this_trait['species'] = line[0] this_trait['group'] = line[1] this_trait['tissue'] = line[2] - this_trait['symbol'] = line[6] + this_trait['symbol'] = "N/A" + if line[6]: + this_trait['symbol'] = line[6] + this_trait['description'] = "N/A" if line[7]: this_trait['description'] = line[7].decode( 'utf-8', 'replace') - else: - this_trait['description'] = "N/A" - this_trait['location_repr'] = 'N/A' + this_trait['location_repr'] = "N/A" if (line[8] != "NULL" and line[8] != "") and (line[9] != 0): this_trait['location_repr'] = 'Chr%s: %.6f' % ( line[8], float(line[9])) diff --git a/wqflask/wqflask/jupyter_notebooks.py b/wqflask/wqflask/jupyter_notebooks.py index dbea04dd..7d76828e 100644 --- a/wqflask/wqflask/jupyter_notebooks.py +++ b/wqflask/wqflask/jupyter_notebooks.py @@ -6,16 +6,12 @@ jupyter_notebooks = Blueprint('jupyter_notebooks', __name__) def launcher(): links = ( { - "main_url": "http://notebook.genenetwork.org/51091/tree?", - "notebook_name": "COVID-19 in mybinder.org federation", - "src_link_url": "https://github.com/jgarte/covid19_in_binder"}, + "main_url": "http://notebook.genenetwork.org/34301/notebooks/genenetwork-api-using-r.ipynb", + "notebook_name": "R notebook showing how to query the GeneNetwork API.", + "src_link_url": "https://github.com/jgarte/genenetwork-api-r-jupyter-notebook"}, { - "main_url": "http://notebook.genenetwork.org/35639/tree?", - "notebook_name": "Simple requirements.txt based example", - "src_link_url": "https://github.com/jgarte/requirements"}, - { - "main_url": "http://notebook.genenetwork.org/40733/tree?", - "notebook_name": "Guile Jupyter Notebook Querying GeneNetwork API", - "src_link_url": "https://github.com/jgarte/guile-notebook-genenetwork-api"}) + "main_url": "http://notebook.genenetwork.org/57675/notebooks/genenetwork.ipynb", + "notebook_name": "Querying the GeneNetwork API declaratively with python.", + "src_link_url": "https://github.com/jgarte/genenetwork-jupyter-notebook-example"}) return render_template("jupyter_notebooks.html", links=links) diff --git a/wqflask/wqflask/marker_regression/run_mapping.py b/wqflask/wqflask/marker_regression/run_mapping.py index 769b9240..9d70bb15 100644 --- a/wqflask/wqflask/marker_regression/run_mapping.py +++ b/wqflask/wqflask/marker_regression/run_mapping.py @@ -556,7 +556,8 @@ def export_mapping_results(dataset, trait, markers, results_path, mapping_method transform_text = "" output_file.write(transform_text + "\n") if dataset.type == "ProbeSet": - output_file.write("Gene Symbol: " + trait.symbol + "\n") + if trait.symbol: + output_file.write("Gene Symbol: " + trait.symbol + "\n") output_file.write("Location: " + str(trait.chr) + \ " @ " + str(trait.mb) + " Mb\n") if len(covariates) > 0: diff --git a/wqflask/wqflask/search_results.py b/wqflask/wqflask/search_results.py index 5ca1f9ca..33f9319c 100644 --- a/wqflask/wqflask/search_results.py +++ b/wqflask/wqflask/search_results.py @@ -62,8 +62,10 @@ class SearchResultPage: self.search_term_exists = True self.results = [] + max_result_count = 100000 # max number of results to display type = kw.get('type') if type == "Phenotypes": # split datatype on type field + max_result_count = 50000 dataset_type = "Publish" elif type == "Genotypes": dataset_type = "Geno" @@ -81,7 +83,7 @@ class SearchResultPage: self.too_many_results = False if self.search_term_exists: - if len(self.results) > 50000: + if len(self.results) > max_result_count: self.trait_list = [] self.too_many_results = True else: diff --git a/wqflask/wqflask/show_trait/show_trait.py b/wqflask/wqflask/show_trait/show_trait.py index 6020bc16..93f95852 100644 --- a/wqflask/wqflask/show_trait/show_trait.py +++ b/wqflask/wqflask/show_trait/show_trait.py @@ -40,10 +40,17 @@ ONE_YEAR = 60 * 60 * 24 * 365 class ShowTrait: def __init__(self, user_id, kw): + self.admin_status = None if 'trait_id' in kw and kw['dataset'] != "Temp": self.temp_trait = False self.trait_id = kw['trait_id'] helper_functions.get_species_dataset_trait(self, kw) + self.resource_id = get_resource_id(self.dataset, + self.trait_id) + self.admin_status = get_highest_user_access_role( + user_id=user_id, + resource_id=(self.resource_id or ""), + gn_proxy_url=GN_PROXY_URL) elif 'group' in kw: self.temp_trait = True self.trait_id = "Temp_" + kw['species'] + "_" + kw['group'] + \ @@ -71,12 +78,7 @@ class ShowTrait: name=self.trait_id, cellid=None) self.trait_vals = Redis.get(self.trait_id).split() - self.resource_id = get_resource_id(self.dataset, - self.trait_id) - self.admin_status = get_highest_user_access_role( - user_id=user_id, - resource_id=(self.resource_id or ""), - gn_proxy_url=GN_PROXY_URL) + # ZS: Get verify/rna-seq link URLs try: blatsequence = self.this_trait.blatseq diff --git a/wqflask/wqflask/static/new/css/jupyter_notebooks.css b/wqflask/wqflask/static/new/css/jupyter_notebooks.css new file mode 100644 index 00000000..db972a17 --- /dev/null +++ b/wqflask/wqflask/static/new/css/jupyter_notebooks.css @@ -0,0 +1,16 @@ +.jupyter-links { + padding: 1.5em; +} + +.jupyter-links:nth-of-type(2n) { + background: #EEEEEE; +} + +.jupyter-links .main-link { + font-size: larger; + display: block; +} + +.jupyter-links .src-link { + font-size: smaller; +} diff --git a/wqflask/wqflask/static/new/javascript/group_manager.js b/wqflask/wqflask/static/new/javascript/group_manager.js index 4c172cbf..cd56133a 100644 --- a/wqflask/wqflask/static/new/javascript/group_manager.js +++ b/wqflask/wqflask/static/new/javascript/group_manager.js @@ -16,23 +16,22 @@ $('#clear_members').click(function(){ function add_emails(user_type){ - var email_address = $('input[name=user_email]').val(); - var email_list_string = $('input[name=' + user_type + '_emails_to_add]').val().trim() - console.log(email_list_string) + let email_address = $('input[name=user_email]').val(); + let email_list_string = $('input[name=' + user_type + '_emails_to_add]').val().trim() if (email_list_string == ""){ - var email_set = new Set(); + let email_set = new Set(); } else { - var email_set = new Set(email_list_string.split(",")) + let email_set = new Set(email_list_string.split(",")) } email_set.add(email_address) $('input[name=' + user_type + '_emails_to_add]').val(Array.from(email_set).join(',')) - var emails_display_string = Array.from(email_set).join('\n') + let emails_display_string = Array.from(email_set).join('\n') $('.added_' + user_type + 's').val(emails_display_string) } function clear_emails(user_type){ $('input[name=' + user_type + '_emails_to_add]').val("") $('.added_' + user_type + 's').val("") -}
\ No newline at end of file +} diff --git a/wqflask/wqflask/templates/admin/create_group.html b/wqflask/wqflask/templates/admin/create_group.html index 21ef5653..b1d214ea 100644 --- a/wqflask/wqflask/templates/admin/create_group.html +++ b/wqflask/wqflask/templates/admin/create_group.html @@ -6,7 +6,8 @@ <div class="page-header"> <h1>Create Group</h1> </div> - <form action="/groups/create" method="POST"> + <form action="{{ url_for('group_management.create_new_group') }}" + method="POST"> <input type="hidden" name="admin_emails_to_add" value=""> <input type="hidden" name="member_emails_to_add" value=""> <fieldset> @@ -73,17 +74,11 @@ </form> </div> - - <!-- End of body --> - {% endblock %} {% block js %} <script language="javascript" type="text/javascript" src="{{ url_for('js', filename='DataTables/js/jquery.js') }}"></script> <script language="javascript" type="text/javascript" src="/static/new/javascript/group_manager.js"></script> <script language="javascript" type="text/javascript" src="{{ url_for('js', filename='js_alt/underscore.min.js') }}"></script> - - <script type="text/javascript" charset="utf-8"> - </script> {% endblock %} diff --git a/wqflask/wqflask/templates/admin/group_manager.html b/wqflask/wqflask/templates/admin/group_manager.html index 692a7abc..eedfe138 100644 --- a/wqflask/wqflask/templates/admin/group_manager.html +++ b/wqflask/wqflask/templates/admin/group_manager.html @@ -12,8 +12,15 @@ <h1>Manage Groups</h1> {% if admin_groups|length != 0 or member_groups|length != 0 %} <div style="display: inline;"> - <button type="button" id="create_group" class="btn btn-primary" data-url="/groups/create">Create Group</button> - <button type="button" id="remove_groups" class="btn btn-primary" data-url="/groups/remove">Remove Selected Groups</button> + <a href="{{ url_for('group_management.view_create_group_page') }}" target="_blank"> + <button type="button" class="btn btn-primary"> + Create Group + </button> + </a> + <button type="button" id="remove_groups" class="btn btn-primary" + data-url="{{ url_for('group_management.delete_groups') }}"> + Remove Selected Groups + </button> </div> {% endif %} </div> @@ -23,7 +30,11 @@ {% if admin_groups|length == 0 and member_groups|length == 0 %} <h4>You currently aren't a member or admin of any groups.</h4> <br> - <button type="button" id="create_group" class="btn btn-primary" data-url="/groups/create">Create a new group</button> + <a href="{{ url_for('group_management.view_create_group_page') }}" target="_blank"> + <button type="button" class="btn btn-primary"> + Create Group + </button> + </a> {% else %} <div style="margin-top: 20px;"><h2>Admin Groups</h2></div> <hr> @@ -47,11 +58,12 @@ <tr> <td><input type="checkbox" name="group_id" value="{{ group.id }}"></td> <td align="right">{{ loop.index }}</td> - <td><a href="/groups/view?id={{ group.id }}">{{ group.name }}</a></td> + {% set group_url = url_for('group_management.view_group', group_id=group.uuid) %} + <td><a href="{{ group_url }}">{{ group.name }}</a></td> <td align="right">{{ group.admins|length + group.members|length }}</td> <td>{{ group.created_timestamp }}</td> <td>{{ group.changed_timestamp }}</td> - <td>{{ group.id }}</td> + <td>{{ group.uuid }}</td> </tr> {% endfor %} </tbody> @@ -81,7 +93,8 @@ <tr> <td><input type="checkbox" name="read" value="{{ group.id }}"></td> <td>{{ loop.index }}</td> - <td><a href="/groups/view?id={{ group.id }}">{{ group.name }}</a></td> + {% set group_url = url_for('group_management.view_group', group_id=group.uuid) %} + <td><a href="{{ group_url }}">{{ group.name }}</a></td> <td>{{ group.admins|length + group.members|length }}</td> <td>{{ group.created_timestamp }}</td> <td>{{ group.changed_timestamp }}</td> @@ -119,11 +132,6 @@ return $("#groups_form").submit(); }; - $("#create_group").on("click", function() { - url = $(this).data("url") - return submit_special(url) - }); - $("#remove_groups").on("click", function() { url = $(this).data("url") groups = [] diff --git a/wqflask/wqflask/templates/admin/view_group.html b/wqflask/wqflask/templates/admin/view_group.html index 26692fe8..c88ce0e7 100644 --- a/wqflask/wqflask/templates/admin/view_group.html +++ b/wqflask/wqflask/templates/admin/view_group.html @@ -1,26 +1,30 @@ {% extends "base.html" %} {% block title %}View and Edit Group{% endblock %} {% block css %} - <link rel="stylesheet" type="text/css" href="{{ url_for('css', filename='DataTables/css/jquery.dataTables.css') }}" /> - <link rel="stylesheet" type="text/css" href="{{ url_for('css', filename='DataTablesExtensions/buttonStyles/css/buttons.dataTables.min.css') }}" /> - <link rel="stylesheet" type="text/css" href="/static/new/css/show_trait.css" /> +<link rel="stylesheet" type="text/css" href="{{ url_for('css', filename='DataTables/css/jquery.dataTables.css') }}" /> +<link rel="stylesheet" type="text/css" href="{{ url_for('css', filename='DataTablesExtensions/buttonStyles/css/buttons.dataTables.min.css') }}" /> +<link rel="stylesheet" type="text/css" href="/static/new/css/show_trait.css" /> {% endblock %} {% block content %} <!-- Start of body --> - <div class="container"> +{% set GROUP_URL = url_for('group_management.view_group', group_id=group_info.guid) %} +{% set UPDATE_GROUP_URL = url_for('group_management.update_group', group_id=group_info.guid) %} +<div class="container"> <div class="page-header"> <h1> - <span id="group_name">{{ group_info.name }}</span> - <input type="text" name="new_group_name" style="font-size: 20px; display: none; width: 500px;" class="form-control" placeholder="{{ group_info.name }}"> + <span id="group_name">Name: {{ group_info.name }}</span> + <input type="text" name="new_group_name" style="font-size: 20px; display: none; width: 500px;" class="form-control" placeholder="{{ group_info.name }}"> + {% if is_admin %} <button class="btn btn-default" style="display: inline;" id="change_group_name">Change Group Name</button> + {% endif %} </h1> - {% if user_is_admin == true %} + {% if is_admin %} <div style="display: inline;"> <button type="button" id="remove_users" class="btn btn-danger" data-url="/groups/remove_users">Remove Selected Users from Group</button> </div> {% endif %} </div> - <form id="group_form" action="/groups/view" method="POST"> + <form id="group_form" action="{{ UPDATE_GROUP_URL }}" method="POST"> <input type="hidden" name="group_id" value="{{ group_info.id }}"> <input type="hidden" name="selected_admin_ids" value=""> <input type="hidden" name="selected_member_ids" value=""> @@ -37,6 +41,9 @@ <th>Name</th> <th>Email Address</th> <th>Organization</th> + {% if is_admin %} + <th>UID</th> + {% endif %} </tr> </thead> <tbody> @@ -47,17 +54,20 @@ <td>{% if 'full_name' in admin %}{{ admin.full_name }}{% elif 'name' in admin %}{{ admin.name }}{% else %}N/A{% endif %}</td> <td>{% if 'email_address' in admin %}{{ admin.email_address }}{% else %}N/A{% endif %}</td> <td>{% if 'organization' in admin %}{{ admin.organization }}{% else %}N/A{% endif %}</td> + {% if is_admin %} + <td>{{admin.user_id}}</td> + {% endif %} </tr> {% endfor %} </tbody> </table> - {% if user_is_admin == true %} + {% if is_admin %} <div style="margin-top: 20px;"> <span>E-mail of user to add to admins (multiple e-mails can be added separated by commas):</span> <input type="text" size="60" name="admin_emails_to_add" placeholder="Enter E-mail(s)" value=""> </div> <div style="margin-bottom: 30px; margin-top: 20px;"> - <button type="button" id="add_admins" class="btn btn-primary" data-usertype="admin" data-url="/groups/add_admins">Add Admin(s)</button> + <button type="button" id="add_admins" class="btn btn-primary" data-usertype="admin" data-url="{{ UPDATE_GROUP_URL }}">Add Admin(s)</button> </div> {% endif %} </div> @@ -74,38 +84,50 @@ <th>Name</th> <th>Email Address</th> <th>Organization</th> + {% if is_admin %} + <th>UID</th> + {% endif %} </tr> </thead> <tbody> {% for member in members %} <tr> - <td style="text-align: center; padding: 0px 10px 2px 10px;"><input type="checkbox" name="member_id" value="{{ member.user_id }}"></td> + + <td style="text-align: center; padding: 0px 10px 2px 10px;"> + {% if is_admin %} + <input type="checkbox" name="member_id" value="{{ member.user_id }}"> + {% endif %} + </td> <td align="right">{{ loop.index }}</td> <td>{% if 'full_name' in member %}{{ member.full_name }}{% elif 'name' in admin %}{{ admin.name }}{% else %}N/A{% endif %}</td> <td>{% if 'email_address' in member %}{{ member.email_address }}{% else %}N/A{% endif %}</td> <td>{% if 'organization' in member %}{{ member.organization }}{% else %}N/A{% endif %}</td> + {% if is_admin %} + <td>{{ member }}</td> + {% endif %} + </tr> {% endfor %} </tbody> </table> - {% if user_is_admin == true %} + {% if is_admin %} <div style="margin-top: 20px;"> <span>E-mail of user to add to members (multiple e-mails can be added separated by commas):</span> <input type="text" size="60" name="member_emails_to_add" placeholder="Enter E-mail(s)" value=""> </div> <div style="margin-bottom: 30px; margin-top: 20px;"> - <button type="button" id="add_members" class="btn btn-primary" data-usertype="member" data-url="/groups/add_members">Add Member(s)</button> + <button type="button" id="add_members" class="btn btn-primary" data-usertype="member" data-url="{{ GROUP_URL }}">Add Member(s)</button> </div> {% endif %} {% else %} There are currently no members in this group. - {% if user_is_admin == true %} + {% if is_admin %} <div style="margin-top: 20px;"> <span>E-mail of user to add to members (multiple e-mails can be added separated by commas):</span> <input type="text" size="60" name="member_emails_to_add" placeholder="Enter E-mail(s)" value=""> </div> <div style="margin-bottom: 30px; margin-top: 20px;"> - <button type="button" id="add_members" class="btn btn-primary" data-usertype="member" data-url="/groups/add_members">Add Member(s)</button> + <button type="button" id="add_members" class="btn btn-primary" data-usertype="member" data-url="{{ GROUP_URL }}">Add Member(s)</button> </div> {% endif %} {% endif %} @@ -219,6 +241,7 @@ $("#add_admins, #add_members").on("click", function() { url = $(this).data("url"); + console.log(url) return submit_special(url) }); @@ -230,7 +253,7 @@ new_name = $('input[name=new_group_name]').val() $.ajax({ type: "POST", - url: "/groups/change_name", + url: "{{ GROUP_URL }} ", data: { group_id: $('input[name=group_id]').val(), new_name: new_name diff --git a/wqflask/wqflask/templates/base.html b/wqflask/wqflask/templates/base.html index ab8b644f..c17409d0 100644 --- a/wqflask/wqflask/templates/base.html +++ b/wqflask/wqflask/templates/base.html @@ -22,6 +22,12 @@ <!--<link rel="stylesheet" type="text/css" href="/static/new/css/main.css" />--> <link rel="stylesheet" type="text/css" href="/static/new/css/parsley.css" /> <link rel="stylesheet" type="text/css" href="/static/new/css/broken_links.css" /> + <!-- UIkit CSS --> + <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/uikit@3.7.4/dist/css/uikit.min.css" /> + + <!-- UIkit JS --> + <script src="https://cdn.jsdelivr.net/npm/uikit@3.7.4/dist/js/uikit.min.js"></script> + <script src="https://cdn.jsdelivr.net/npm/uikit@3.7.4/dist/js/uikit-icons.min.js"></script> {% block css %} @@ -87,7 +93,7 @@ <li><a href="https://systems-genetics.org/">Systems Genetics PheWAS</a></li> <li><a href="http://ucscbrowser.genenetwork.org/">Genome Browser</a></li> <li><a href="http://power.genenetwork.org">BXD Power Calculator</a></li> - <li><a href="url_for('jupyter_notebooks.launcher')">Jupyter Notebook Launcher</a></li> + <li><a href="{{url_for('jupyter_notebooks.launcher')}}">Jupyter Notebooks</a></li> <li><a href="http://datafiles.genenetwork.org">Interplanetary File System</a></li> </ul> </li> @@ -227,6 +233,7 @@ <a href="http://www.neuinfo.org" target="_blank"> <img src="/static/new/images/Nif.png" alt="Registered with Nif" border="0"> </a> + <script type="text/javascript" src="//rf.revolvermaps.com/0/0/8.js?i=526mdlpknyd&m=0&c=ff0000&cr1=ffffff&f=arial&l=33" async="async"></script> </div> </div> </footer> diff --git a/wqflask/wqflask/templates/index_page.html b/wqflask/wqflask/templates/index_page.html index 7b103305..3e2f424d 100755 --- a/wqflask/wqflask/templates/index_page.html +++ b/wqflask/wqflask/templates/index_page.html @@ -193,10 +193,40 @@ </ul> </section> </div> + <!--<div class="uk-section uk-section-muted">--> <div class="col-xs-4" style="width: 600px !important;"> - <section id="affiliates"> - <div class="page-header"> + <section id="tutorials"> + <div class="page-header"> + <h1>Tutorials</h1> + <!-- <div class="uk-section uk-section-muted"> + <div class="uk-container">--> + <div class="uk-grid-match uk-child-width-1-3@m" uk-grid> + <div> + <strong><a class="uk-link-text" href="/tutorials">Webinars & Courses</a></strong> + <p>In-person courses, live webinars and webinar recordings + <p> + <!--<span uk-icon="icon: laptop; ratio: 2"></span>--> + <a href="/tutorials" class="uk-icon-link" uk-icon="laptop" ratio="2"></a></p> + </div> + + <div> + <strong><a class="uk-link-text" href="/tutorials">Tutorials</a></strong> + <p>Tutorials: Training materials in HTML, PDF and video formats + <p><a href="/tutorials" class="uk-icon-link" uk-icon="file-text" ratio="2"></a></p> + </div> + + <div> + <strong><a class="uk-link-text" href="/tutorials">Documentation</a></strong> + <p>Online manuals, handbooks, fact sheets and FAQs + <p><a href="/tutorials" class="uk-icon-link" uk-icon="album" ratio="2"></a></p> + </div> + + </div> + </div> + <!--</div> + </div>--> + <!--<div class="page-header"> <h1>Affiliates</h1> <ul> <li><b><a href="http://gn1.genenetwork.org">GeneNetwork 1</a> at UTHSC</b></li> @@ -206,7 +236,7 @@ <li><a href="https://phenogen.org/">PhenoGen</a> at University of Colorado</li> <li><a href="http://www.webgestalt.org/">WebGestalt</a> at Baylor</li> </ul> - </div> + </div>--> </section> <section id="news-section"> <div class="page-header"> @@ -241,8 +271,22 @@ <li><span class="broken_link" href="http://artemis.uthsc.edu/">Time Machine</span>: Full GN versions from 2009 to 2016 (mm9)</li> Cloud (EC2)</a></li> </ul> - <script type="text/javascript" src="//rf.revolvermaps.com/0/0/8.js?i=526mdlpknyd&m=0&c=ff0000&cr1=ffffff&f=arial&l=33" async="async"></script> + <!--<script type="text/javascript" src="//rf.revolvermaps.com/0/0/8.js?i=526mdlpknyd&m=0&c=ff0000&cr1=ffffff&f=arial&l=33" async="async"></script>--> </section> + <section id="affiliates"> + <div class="page-header"> + <h1>Affiliates</h1> + <ul> + <li><b><a href="http://gn1.genenetwork.org">GeneNetwork 1</a> at UTHSC</b></li> + <li><a href="https://systems-genetics.org/">Systems Genetics</a> at EPFL</li> + <li><a href="http://bnw.genenetwork.org/">Bayesian Network Web Server</a> at UTHSC</li> + <li><a href="https://www.geneweaver.org/">GeneWeaver</a></li> + <li><a href="https://phenogen.org/">PhenoGen</a> at University of Colorado</li> + <li><a href="http://www.webgestalt.org/">WebGestalt</a> at Baylor</li> + </ul> + </div> + + </section> </div> </div> </div> diff --git a/wqflask/wqflask/templates/jupyter_notebooks.html b/wqflask/wqflask/templates/jupyter_notebooks.html index 4dce0f27..afc95a15 100644 --- a/wqflask/wqflask/templates/jupyter_notebooks.html +++ b/wqflask/wqflask/templates/jupyter_notebooks.html @@ -4,6 +4,10 @@ Jupyter Notebooks {%endblock%} +{%block css%} +<link rel="stylesheet" type="text/css" href="/static/new/css/jupyter_notebooks.css" /> +{%endblock%} + {%block content%} <div class="container"> diff --git a/wqflask/wqflask/templates/search_result_page.html b/wqflask/wqflask/templates/search_result_page.html index 95842316..dade6ba5 100644 --- a/wqflask/wqflask/templates/search_result_page.html +++ b/wqflask/wqflask/templates/search_result_page.html @@ -176,7 +176,7 @@ return params; }; - {% if results|count > 0 %} + {% if results|count > 0 and not too_many_results %} var tableId = "trait_table"; var width_change = 0; //ZS: For storing the change in width so overall table width can be adjusted by that amount diff --git a/wqflask/wqflask/templates/show_trait_details.html b/wqflask/wqflask/templates/show_trait_details.html index 3e59a3ee..4e9ea0fb 100644 --- a/wqflask/wqflask/templates/show_trait_details.html +++ b/wqflask/wqflask/templates/show_trait_details.html @@ -234,9 +234,9 @@ {% endif %} {% 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.get('metadata', DataRole.VIEW) > DataRole.VIEW %} + {% if admin_status != None and admin_status.get('metadata', DataRole.VIEW) > DataRole.VIEW %} {% if this_trait.dataset.type == 'Publish' %} - <button type="button" id="edit_resource" class="btn btn-success" title="Edit Resource" onclick="window.open('/datasets/{{ this_trait.dataset.id }}/traits/{{ this_trait.name }}?resource-id={{ resource_id }}', '_blank')">Edit</button> + <button type="button" id="edit_resource" class="btn btn-success" title="Edit Resource" onclick="window.open('/datasets/{{ this_trait.dataset.group.id }}/traits/{{ this_trait.name }}?resource-id={{ resource_id }}', '_blank')">Edit</button> {% endif %} {% if this_trait.dataset.type == 'ProbeSet' %} diff --git a/wqflask/wqflask/templates/tutorials.html b/wqflask/wqflask/templates/tutorials.html index 8f3447e3..aa6a818d 100644 --- a/wqflask/wqflask/templates/tutorials.html +++ b/wqflask/wqflask/templates/tutorials.html @@ -56,9 +56,7 @@ </thead> <tbody> <tr> - <td><p><h3>Webinar #01 - Introduction to Quantitative Trait Loci (QTL) Analysis</h3> - <p><i>Friday, May 8th, 2020<br> - 10am PDT/ 11am MDT/ 12pm CDT/ 1pm EDT</i></p> + <td><p><h3>Introduction to Quantitative Trait Loci (QTL) Analysis</h3> <p>Goals of this webinar (trait variance to QTL):</p> <ul> <li>Define quantitative trait locus (QTL)</li> @@ -76,10 +74,7 @@ University of Tennessee Health Science Center <iframe width="560" height="315" src="https://www.youtube.com/embed/leY3kPmnLaI" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></td> </tr> <tr> - <td><p><h3>Webinar #02 - Mapping Addiction and Behavioral Traits and Getting at Causal Gene Variants with GeneNetwork</h3> - <p><i>Friday, May 22nd. 2020 - 10am PDT/ 11am MDT/ 12pm CDT/ 1pm EDT</i> -</p> + <td><p><h3>Mapping Addiction and Behavioral Traits and Getting at Causal Gene Variants with GeneNetwork</h3> <p>Goals of this webinar (QTL to gene variant):</p> <ul> <li>Demonstrate mapping a quantitative trait using GeneNetwork (GN)</li> @@ -98,8 +93,6 @@ University of Tennessee Health Science Center <!--NEW WEBINAR STARTS HERE--> <tr> <td><p><h3>Data structure, disease risk, GXE, and causal modeling</h3> - <p><i>Friday, November 20th at 9am PDT/ 11pm CDT/ 12pm EDT<br> - 1-hour presentation followed by 30 minutes of discussion</i> <p>Human disease is mainly due to complex interactions between genetic and environmental factors (GXE). We need to acquire the right "smart" data types—coherent and multiplicative data—required to make accurate predictions about risk and outcome for n = 1 individuals—a daunting task. We have developed large families of fully sequenced mice that mirror the genetic complexity of humans. We are using these Reference Populations to generate multiplicatively useful data and to build and test causal quantitative models of disease mechanisms with a special focus on diseases of aging, addiction, and neurological and psychiatric disease. @@ -141,7 +134,7 @@ $('#myTable').dataTable( { <!--NEW WEBINAR STARTS HERE--> <tr> - <td><p><h3>#01- Introduction to Gene Network</h3> + <td><p><h3>Introduction to Gene Network</h3> <p><i>Please note that this tutorial is based on GeneNetwork v1</i> <p>GeneNetwork is a group of linked data sets and tools used to study complex networks of genes, molecules, and higher order gene function and phenotypes. GeneNetwork combines more than 25 years of legacy data generated by hundreds of scientists together with sequence data (SNPs) and massive transcriptome data sets (expression genetic or eQTL data sets). The quantitative trait locus (QTL) mapping module that is built into GN is optimized for fast on-line analysis of traits that are controlled by combinations of gene variants and environmental factors. GeneNetwork can be used to study humans, mice (BXD, AXB, LXS, etc.), rats (HXB), Drosophila, and plant species (barley and Arabidopsis). Most of these population data sets are linked with dense genetic maps (genotypes) that can be used to locate the genetic modifiers that cause differences in expression and phenotypes, including disease susceptibility. @@ -159,25 +152,27 @@ Registration: <a href="https://bit.ly/osga_2020-11-20">https://bit.ly/osga_2020- <!--<p>This webinar series is sponsored by the NIDA Center of Excellence in Omics, Systems Genetics, and the Addictome (P30 DA044223). --> </td> <!--<td><strong>After the presentation, the recording will be made available here.</strong></td>--> - <td><iframe width="560" height="315" src="https://www.youtube.com/embed/B3g_0q-ldJ8" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></iframe> + <td> + <iframe width="560" height="315" src="https://www.youtube.com/embed/B3g_0q-ldJ8" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> </tr> <!--WEBINAR ENDS HERE--> <!--NEW WEBINAR STARTS HERE--> <tr> - <td><p><h3>#02 - How to search in GeneNetwork</h3> -</td> + <td><p><h3>How to search in GeneNetwork</h3><br>Presented by Rob Williams University of Tennessee Health Science Center</td> + <!--<td><p><h3>How to search in GeneNetwork</h3> +</td>--> <td> - <iframe width="560" height="315" src="https://www.youtube.com/embed/5exnkka5Tso" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></td> + <iframe width="560" height="315" src="https://www.youtube.com/embed/5exnkka5Tso" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> </td> </tr> <!--WEBINAR ENDS HERE--> <!--NEW WEBINAR STARTS HERE--> <tr> - <td><p><h3>#03 - GeneNetwork.org: genetic analysis for all neuroscientists</h3><br>Presented by David G. Ashbrook Assistant Professor University of Tennessee Health Science Center + <td><p><h3>GeneNetwork.org: genetic analysis for all neuroscientists</h3><br>Presented by David G. Ashbrook Assistant Professor University of Tennessee Health Science Center </td> <td> - <iframe width="560" height="315" src="https://www.youtube.com/embed/JmlVLki09Q8" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></td> + <iframe width="560" height="315" src="https://www.youtube.com/embed/JmlVLki09Q8" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></td> </tr> <!--WEBINAR ENDS HERE--> </tbody> @@ -223,7 +218,8 @@ $('#myTable3').dataTable( { </thead> <tbody> <tr><td><a href="https://www.biorxiv.org/content/10.1101/2020.12.23.424047v1">GeneNetwork: a continuously updated tool for systems genetics analyses</a></td></tr> - <tr><td><a href="https://www.biorxiv.org/content/10.1101/2021.05.24.445383v1">Old data and friends improve with age: Advancements with the updated tools of GeneNetwork</a></td></tr> + <tr><td><a href="https://www.biorxiv.org/content/10.1101/2021.05.24.445383v1">Old data and friends improve with age: Advancements with the updated tools of GeneNetwork</a></td> + <tr><td><a href="https://www.opar.io/pdf/Rat_HRDP_Brain_Proteomics_Wang_WIlliams_08Oct2021.pdf">A Primer on Brain Proteomics and protein-QTL Analysis for Substance Use Disorders</a></td></tr> </tbody> </table> diff --git a/wqflask/wqflask/templates/wgcna_setup.html b/wqflask/wqflask/templates/wgcna_setup.html index 86d9fa10..d7acd5f2 100644 --- a/wqflask/wqflask/templates/wgcna_setup.html +++ b/wqflask/wqflask/templates/wgcna_setup.html @@ -9,7 +9,8 @@ } </style> -<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm@4.14.1/css/xterm.css"> + +<link rel="stylesheet" type="text/css" href="{{ url_for('css', filename='xterm/xterm.min.css') }}" /> <div class="container"> <div class="col-md-5"> @@ -80,19 +81,12 @@ </div> </div> -<script src="https://cdn.socket.io/4.2.0/socket.io.min.js" integrity="sha384-PiBR5S00EtOj2Lto9Uu81cmoyZqR57XcOna1oAuVuIEjzj0wpqDVfD0JA9eXlRsj" crossorigin="anonymous"></script> - -<script src="https://cdn.jsdelivr.net/npm/xterm@4.14.1/lib/xterm.min.js"></script> - -<script src="https://cdn.jsdelivr.net/npm/xterm-addon-attach@0.6.0/lib/xterm-addon-attach.min.js"></script> - - +<script src="{{ url_for('js', filename='xterm/xterm.min.js') }}" type="text/javascript"></script> +<script src="{{ url_for('js', filename='jquery/jquery.min.js') }}" type="text/javascript"></script> <script src="https://cdn.jsdelivr.net/npm/xterm-addon-fit@0.5.0/lib/xterm-addon-fit.min.js"></script> - <script src="https://code.jquery.com/jquery-3.5.1.js" - integrity="sha256-QWo7LDvxbWT2tbbQ97B53yJnYU3WhH/C8ycbRAkjPDc=" - crossorigin="anonymous"></script> + <script> -// document.addEventListener('DOMContentLoaded', function() { +document.addEventListener('DOMContentLoaded', function() { let term = new Terminal({ cursorBlink: true, lineHeight: 1.3, @@ -117,11 +111,6 @@ term.writeln(termDebugs.general) wgcnaForm = document.querySelector("#wgcna_form") -const socket = io("http://127.0.0.1:8081") //issue gn3 private -const attachAddon = new AttachAddon.AttachAddon(socket); - -term.loadAddon(attachAddon); - fitAddon.fit() term.onData((data) => { term.write(data) @@ -133,16 +122,6 @@ if (wgcnaForm) { term.writeln(termDebugs.fail) } -socket.on("connect", () => { - $("#wgcna_form").append(`<input type="hidden" name="socket_id" value=${socket.id}>`); -}) - -socket.on("output", ({ - data -}) => { - term.writeln(data) -}) - $(document).on('submit', '#wgcna_form', function(e) { term.writeln(termDebugs.success) @@ -156,6 +135,8 @@ $(document).on('submit', '#wgcna_form', function(e) { document.write(data) } }) -}); +}) +}) + </script> {% endblock %}
\ No newline at end of file diff --git a/wqflask/wqflask/wgcna/gn3_wgcna.py b/wqflask/wqflask/wgcna/gn3_wgcna.py index c4cc2e7f..15728f22 100644 --- a/wqflask/wqflask/wgcna/gn3_wgcna.py +++ b/wqflask/wqflask/wgcna/gn3_wgcna.py @@ -4,7 +4,9 @@ and process data to be rendered by datatables import requests from types import SimpleNamespace + from utility.helper_functions import get_trait_db_obs +from utility.tools import GN_SERVER_URL def fetch_trait_data(requestform): @@ -24,7 +26,6 @@ def process_dataset(trait_list): traits = [] strains = [] - # xtodo unique traits and strains for trait in trait_list: traits.append(trait[0].name) @@ -33,9 +34,6 @@ def process_dataset(trait_list): for strain in trait[0].data: strains.append(strain) input_data[trait[0].name][strain] = trait[0].data[strain].value - # "sample_names": list(set(strains)), - # "trait_names": form_traits, - # "trait_sample_data": form_strains, return { "input": input_data, @@ -77,9 +75,7 @@ def process_image(response): def run_wgcna(form_data): """function to run wgcna""" - GN3_URL = "http://127.0.0.1:8081" - - wgcna_api = f"{GN3_URL}/api/wgcna/run_wgcna" + wgcna_api = f"{GN_SERVER_URL}api/wgcna/run_wgcna" # parse form data |