From 735adc012cc5d974eb8eabb26eda6c483af0da1a Mon Sep 17 00:00:00 2001 From: Frederick Muriuki Muriithi Date: Wed, 19 Apr 2023 17:49:58 +0300 Subject: oauth2: Link the phenotype traits to user groups. --- gn3/auth/authorisation/data/phenotypes.py | 69 +++++++++++++++++++++++++++++-- gn3/auth/authorisation/data/views.py | 29 +++++++++++++ 2 files changed, 94 insertions(+), 4 deletions(-) (limited to 'gn3/auth/authorisation/data') diff --git a/gn3/auth/authorisation/data/phenotypes.py b/gn3/auth/authorisation/data/phenotypes.py index a330d4a..ff98295 100644 --- a/gn3/auth/authorisation/data/phenotypes.py +++ b/gn3/auth/authorisation/data/phenotypes.py @@ -1,11 +1,14 @@ """Handle linking of Phenotype data to the Auth(entic|oris)ation system.""" +import uuid from typing import Any, Iterable from MySQLdb.cursors import DictCursor import gn3.auth.db as authdb import gn3.db_utils as gn3db +from gn3.auth.dictify import dictify from gn3.auth.authorisation.checks import authorised_p +from gn3.auth.authorisation.groups.models import Group def linked_phenotype_data( authconn: authdb.DbConnection, gn3conn: gn3db.Connection, @@ -19,9 +22,9 @@ def linked_phenotype_data( for row in authcursor.fetchall()) if len(linked) <= 0: return iter(()) - paramstr = "".join(["(%s, %s, %s, %s)"] * len(linked)) + paramstr = ", ".join(["(%s, %s, %s, %s)"] * len(linked)) query = ( - "SELECT spc.SpeciesId, spc.SpeciesName, iset.InbredSetId, " + "SELECT spc.SpeciesId, spc.Name AS SpeciesName, iset.InbredSetId, " "iset.InbredSetName, pf.Id AS PublishFreezeId, " "pf.Name AS dataset_name, pf.FullName AS dataset_fullname, " "pf.ShortName AS dataset_shortname, pxr.Id AS PublishXRefId " @@ -35,10 +38,11 @@ def linked_phenotype_data( "ON pf.InbredSetId=pxr.InbredSetId") + ( " WHERE" if (len(linked) > 0 or bool(species)) else "") + ( (" (spc.SpeciesId, iset.InbredSetId, pf.Id, pxr.Id) " - f"NOT IN ({paramstr})") if len(linked) > 0 else "") + ( + f"IN ({paramstr})") if len(linked) > 0 else "") + ( " AND"if len(linked) > 0 else "") + ( " spc.SpeciesName=%s" if bool(species) else "") - params = linked + ((species,) if bool(species) else tuple()) + params = tuple(item for sublist in linked for item in sublist) + ( + (species,) if bool(species) else tuple()) gn3cursor.execute(query, params) return (item for item in gn3cursor.fetchall()) @@ -77,3 +81,60 @@ def ungrouped_phenotype_data( return tuple(dict(row) for row in cursor.fetchall()) return tuple() + +def __traits__(gn3conn: gn3db.Connection, params: tuple[dict, ...]) -> tuple[dict, ...]: + """An internal utility function. Don't use outside of this module.""" + if len(params) < 1: + return tuple() + paramstr = ", ".join(["(%s, %s, %s, %s)"] * len(params)) + with gn3conn.cursor(DictCursor) as cursor: + cursor.execute( + "SELECT spc.SpeciesId, iset.InbredSetId, pf.Id AS PublishFreezeId, " + "pf.Name AS dataset_name, pf.FullName AS dataset_fullname, " + "pf.ShortName AS dataset_shortname, pxr.Id AS PublishXRefId " + "FROM " + "Species AS spc " + "INNER JOIN InbredSet AS iset " + "ON spc.SpeciesId=iset.SpeciesId " + "INNER JOIN PublishFreeze AS pf " + "ON iset.InbredSetId=pf.InbredSetId " + "INNER JOIN PublishXRef AS pxr " + "ON pf.InbredSetId=pxr.InbredSetId " + "WHERE (spc.SpeciesName, iset.InbredSetName, pf.Name, pxr.Id) " + f"IN ({paramstr})", + tuple( + itm for sublist in ( + (item["species"], item["group"], item["dataset"], item["name"]) + for item in params) + for itm in sublist)) + return cursor.fetchall() + +@authorised_p(("system:data:link-to-group",), + error_description=( + "You do not have sufficient privileges to link data to (a) " + "group(s)."), + oauth2_scope="profile group resource") +def link_phenotype_data( + authconn:authdb.DbConnection, gn3conn: gn3db.Connection, group: Group, + traits: tuple[dict, ...]) -> dict: + """Link phenotype traits to a user group.""" + with authdb.cursor(authconn) as cursor: + params = tuple({ + "data_link_id": str(uuid.uuid4()), + "group_id": str(group.group_id), + **item + } for item in __traits__(gn3conn, traits)) + cursor.executemany( + "INSERT INTO linked_phenotype_data " + "VALUES (" + ":data_link_id, :group_id, :SpeciesId, :InbredSetId, " + ":PublishFreezeId, :dataset_name, :dataset_fullname, " + ":dataset_shortname, :PublishXRefId" + ")", + params) + return { + "description": ( + f"Successfully linked {len(traits)} traits to group."), + "group": dictify(group), + "traits": params + } diff --git a/gn3/auth/authorisation/data/views.py b/gn3/auth/authorisation/data/views.py index a9861d2..81b3e2f 100644 --- a/gn3/auth/authorisation/data/views.py +++ b/gn3/auth/authorisation/data/views.py @@ -28,6 +28,7 @@ from gn3.auth.authorisation.resources.models import ( from gn3.auth.authentication.oauth2.resource_server import require_oauth +from gn3.auth.authorisation.data.phenotypes import link_phenotype_data from gn3.auth.authorisation.data.mrna import link_mrna_data, ungrouped_mrna_data from gn3.auth.authorisation.data.genotypes import ( link_genotype_data, ungrouped_genotype_data) @@ -246,3 +247,31 @@ def link_mrna() -> Response: return jsonify(with_db_connection( partial(__link__, **__values__(request.json)))) + +@data.route("/link/phenotype", methods=["POST"]) +def link_phenotype() -> Response: + """Link phenotype data to group.""" + def __values__(form): + if not bool(form.get("species_name", "").strip()): + raise InvalidData("Expected 'species_name' not provided.") + if not bool(form.get("group_id")): + raise InvalidData("Expected 'group_id' not provided.",) + try: + _group_id = uuid.UUID(form.get("group_id")) + except TypeError as terr: + raise InvalidData("Expected a UUID for 'group_id' value.") from terr + if not bool(form.get("selected")): + raise InvalidData("Expected at least one dataset to be provided.") + return { + "group_id": uuid.UUID(form["group_id"]), + "traits": form["selected"] + } + + with gn3db.database_connection(app.config["SQL_URI"]) as gn3conn: + def __link__(conn: db.DbConnection, group_id: uuid.UUID, + traits: tuple[dict, ...]) -> dict: + return link_phenotype_data( + conn, gn3conn, group_by_id(conn, group_id), traits) + + return jsonify(with_db_connection( + partial(__link__, **__values__(request.json)))) -- cgit v1.2.3