about summary refs log tree commit diff
"""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__)