"""Handle linking data to groups.""" import sys import json import uuid from datetime import datetime from urllib.parse import urljoin from redis import Redis from flask import ( flash, request, jsonify, url_for, redirect, Response, Blueprint, current_app as app) from gn2.wqflask.oauth2.request_utils import with_flash_error from gn2.jobs import jobs from .ui import render_ui from .request_utils import process_error from .client import oauth2_get, oauth2_post, authserver_uri data = Blueprint("data", __name__) def __search_mrna__(query, template, **kwargs): species_name = kwargs["species_name"] search_uri = urljoin(authserver_uri(), "auth/data/search") datasets = oauth2_get( "auth/data/search", json = { "query": query, "dataset_type": "mrna", "species_name": species_name, "selected": __selected_datasets__() }).either( lambda err: {"datasets_error": process_error(err)}, lambda datasets: {"datasets": datasets}) return render_ui(template, search_uri=search_uri, **datasets, **kwargs) def __selected_datasets__(): if bool(request.json): return request.json.get( "selected", request.args.get("selected", request.form.get("selected", []))) return request.args.get("selected", request.form.get("selected", [])) def __search_genotypes__(query, template, **kwargs): species_name = kwargs["species_name"] search_uri = urljoin(authserver_uri(), "auth/data/search") datasets = oauth2_get( "auth/data/search", json = { "query": query, "dataset_type": "genotype", "species_name": species_name, "selected": __selected_datasets__() }).either( lambda err: {"datasets_error": process_error(err)}, lambda datasets: {"datasets": datasets}) return render_ui(template, search_uri=search_uri, **datasets, **kwargs) def __search_phenotypes__(query, template, **kwargs): from gn2.utility.tools import GN_SERVER_URL page = int(request.args.get("page", 1)) per_page = int(request.args.get("per_page", 50)) selected_traits = request.form.getlist("selected_traits") def __search_success__(search_results): job_id = uuid.UUID(search_results["job_id"]) return render_ui( template, traits=[], per_page=per_page, query=query, selected_traits=selected_traits, search_results=search_results, search_endpoint=urljoin( request.host_url, "oauth2/data/phenotype/search"), auth_server_url=authserver_uri(), pheno_results_template=urljoin( authserver_uri(), "auth/data/search/phenotype/<jobid>"), results_endpoint=urljoin( authserver_uri(), f"auth/data/search/phenotype/{job_id}"), **kwargs) return oauth2_get("auth/data/search", json={ "dataset_type": "phenotype", "species_name": kwargs["species_name"], "per_page": per_page, "page": page, "gn3_server_uri": GN_SERVER_URL }).either( with_flash_error(redirect(url_for('oauth2.data.list_data'))), __search_success__) @data.route("/genotype/search", methods=["POST"]) def json_search_genotypes() -> Response: def __handle_error__(err): error = process_error(err) return jsonify(error), error["status_code"] return oauth2_get( "auth/data/search", json = { "query": request.json["query"], "dataset_type": "genotype", "species_name": request.json["species_name"], "selected": __selected_datasets__() }).either( __handle_error__, lambda datasets: jsonify(datasets)) @data.route("/mrna/search", methods=["POST"]) def json_search_mrna() -> Response: def __handle_error__(err): error = process_error(err) return jsonify(error), error["status_code"] return oauth2_get( "auth/data/search", json = { "query": request.json["query"], "dataset_type": "mrna", "species_name": request.json["species_name"], "selected": __selected_datasets__() }).either( __handle_error__, lambda datasets: jsonify(datasets)) @data.route("/phenotype/search", methods=["POST"]) def json_search_phenotypes() -> Response: """Search for phenotypes.""" from gn2.utility.tools import GN_SERVER_URL form = request.json def __handle_error__(err): error = process_error(err) return jsonify(error), error["status_code"] return oauth2_get( "auth/data/search", json={ "dataset_type": "phenotype", "species_name": form["species_name"], "query": form.get("query", ""), "per_page": int(form.get("per_page", 50)), "page": int(form.get("page", 1)), "auth_server_uri": authserver_uri(), "gn3_server_uri": GN_SERVER_URL, "selected_traits": form.get("selected_traits", []) }).either(__handle_error__, jsonify) @data.route("/<string:species_name>/<string:dataset_type>/list", methods=["GET", "POST"]) def list_data_by_species_and_dataset( species_name: str, dataset_type: str) -> Response: templates = { "mrna": "oauth2/data-list-mrna.html", "genotype": "oauth2/data-list-genotype.html", "phenotype": "oauth2/data-list-phenotype.html" } search_fns = { "mrna": __search_mrna__, "genotype": __search_genotypes__, "phenotype": __search_phenotypes__ } groups = oauth2_get("auth/group/list").either( lambda err: {"groups_error": process_error(err)}, lambda grps: {"groups": grps}) query = request.args.get("query", "") return search_fns[dataset_type]( query, templates[dataset_type], **groups, species_name=species_name, dataset_type=dataset_type) @data.route("/list", methods=["GET", "POST"]) def list_data(): """List ungrouped data.""" def __render__(**kwargs): roles = kwargs.get("roles", []) user_privileges = tuple( privilege["privilege_id"] for role in roles for privilege in role["privileges"]) return render_ui( "oauth2/data-list.html", groups=kwargs.get("groups", []), data_items=kwargs.get("data_items", []), user_privileges=user_privileges, **{key:val for key,val in kwargs.items() if key not in ("groups", "data_items", "user_privileges")}) groups = oauth2_get("auth/group/list").either( lambda err: {"groups_error": process_error(err)}, lambda grp: {"groups": grp}) roles = oauth2_get("auth/system/roles").either( lambda err: {"roles_error": process_error(err)}, lambda roles: {"roles": roles}) species = oauth2_get("auth/data/species").either( lambda err: {"species_error": process_error(err)}, lambda species: {"species": species}) if request.method == "GET": return __render__(**{**groups, **roles, **species}) species_name = request.form["species_name"] dataset_type = request.form["dataset_type"] if dataset_type not in ("mrna", "genotype", "phenotype"): flash("InvalidDatasetType: An invalid dataset type was provided", "alert-danger") return __render__(**{**groups, **roles, **species}) return redirect(url_for( "oauth2.data.list_data_by_species_and_dataset", species_name=species_name, dataset_type=dataset_type)) @data.route("/link", methods=["POST"]) def link_data(): """Link the selected data to a specific group.""" def __error__(err, form_data): error = process_error(err) flash(f"{error['error']}: {error['error_description']}", "alert-danger") return redirect(url_for("oauth2.data.list_data", **form_data), code=307) def __success__(success, form_data): flash("Data successfully linked!", "alert-success") return redirect(url_for("oauth2.data.list_data", **form_data), code=307) form = request.form try: keys = ("dataset_type", "group_id") assert all(item in form for item in keys) assert all(bool(form[item]) for item in keys) state_data = { "dataset_type": form["dataset_type"], "offset": form.get("offset", 0)} dataset_ids = form.getlist("dataset_ids") if len(dataset_ids) == 0: flash("You must select at least one item to link", "alert-danger") return redirect(url_for( "oauth2.data.list_data", **state_data)) return oauth2_post( "auth/group/data/link", data={ "dataset_type": form["dataset_type"], "dataset_ids": dataset_ids, "group_id": form["group_id"] }).either(lambda err: __error__(err, state_data), lambda success: __success__(success, state_data)) except AssertionError as aserr: flash("You must provide all the expected data.", "alert-danger") return redirect(url_for("oauth2.data.list_data")) @data.route("/link/genotype", methods=["POST"]) def link_genotype_data(): """Link genotype data to a group.""" form = request.form link_source_url = redirect(url_for("oauth2.data.list_data")) if bool(form.get("species_name")): link_source_url = redirect(url_for( "oauth2.data.list_data_by_species_and_dataset", species_name=form["species_name"], dataset_type="genotype")) def __link_error__(err): flash(f"{err['error']}: {err['error_description']}", "alert-danger") return link_source_url def __link_success__(success): flash(success["description"], "alert-success") return link_source_url return oauth2_post("auth/data/link/genotype", json={ "species_name": form.get("species_name"), "group_id": form.get("group_id"), "selected": tuple(json.loads(dataset) for dataset in form.getlist("selected")) }).either(lambda err: __link_error__(process_error(err)), __link_success__) @data.route("/link/mrna", methods=["POST"]) def link_mrna_data(): """Link mrna data to a group.""" form = request.form link_source_url = redirect(url_for("oauth2.data.list_data")) if bool(form.get("species_name")): link_source_url = redirect(url_for( "oauth2.data.list_data_by_species_and_dataset", species_name=form["species_name"], dataset_type="mrna")) def __link_error__(err): error = process_error(err) flash(f"{err['error']}: {err['error_description']}", "alert-danger") return link_source_url def __link_success__(success): flash(success["description"], "alert-success") return link_source_url return oauth2_post("auth/data/link/mrna", json={ "species_name": form.get("species_name"), "group_id": form.get("group_id"), "selected": tuple(json.loads(dataset) for dataset in form.getlist("selected")) }).either(lambda err: __link_error__(process_error(err)), __link_success__) @data.route("/link/phenotype", methods=["POST"]) def link_phenotype_data(): """Link phenotype data to a group.""" form = request.form link_source_url = redirect(url_for("oauth2.data.list_data")) if bool(form.get("species_name")): link_source_url = redirect(url_for( "oauth2.data.list_data_by_species_and_dataset", species_name=form["species_name"], dataset_type="phenotype")) def __link_error__(err): error = process_error(err) flash(f"{error['error']}: {error['error_description']}", "alert-danger") return link_source_url def __link_success__(success): flash(success["description"], "alert-success") return link_source_url return oauth2_post("auth/data/link/phenotype", json={ "species_name": form.get("species_name"), "group_id": form.get("group_id"), "selected": tuple( json.loads(trait) for trait in form.getlist("selected"))}).either( __link_error__, __link_success__)